diff options
41 files changed, 457 insertions, 1539 deletions
diff --git a/configure.ac b/configure.ac index a4a23474c8..94abb0dfec 100644 --- a/configure.ac +++ b/configure.ac @@ -710,6 +710,9 @@ case $host in fi CORE_CPPFLAGS="$CORE_CPPFLAGS -D_MT -DWIN32 -D_WINDOWS -D_WIN32_WINNT=0x0601 -D_WIN32_IE=0x0501 -DWIN32_LEAN_AND_MEAN" + dnl Prevent the definition of min/max macros. + dnl We always want to use the standard library. + CORE_CPPFLAGS="$CORE_CPPFLAGS -DNOMINMAX" dnl libtool insists upon adding -nostdlib and a list of objects/libs to link against. dnl That breaks our ability to build dll's with static libgcc/libstdc++/libssp. Override @@ -1261,8 +1264,8 @@ AC_LINK_IFELSE( AC_MSG_CHECKING([for ::_wsystem]) AC_LINK_IFELSE( [ AC_LANG_PROGRAM( - [[ ]], - [[ int nErr = ::_wsystem(""); ]] + [[ #include <stdlib.h> ]], + [[ int nErr = ::_wsystem(NULL); ]] )], [ AC_MSG_RESULT([yes]); have_any_system=yes], [ AC_MSG_RESULT([no]) ] diff --git a/contrib/devtools/symbol-check.py b/contrib/devtools/symbol-check.py index a419e392ee..23d29af3f1 100755 --- a/contrib/devtools/symbol-check.py +++ b/contrib/devtools/symbol-check.py @@ -50,9 +50,8 @@ MAX_VERSIONS = { # Ignore symbols that are exported as part of every executable IGNORE_EXPORTS = { -'_edata', '_end', '__end__', '_init', '__bss_start', '__bss_start__', '_bss_end__', -'__bss_end__', '_fini', '_IO_stdin_used', 'stdin', 'stdout', 'stderr', -'environ', '_environ', '__environ', +'environ', '_environ', '__environ', '_fini', '_init', 'stdin', +'stdout', 'stderr', } # Expected linker-loader names can be found here: diff --git a/contrib/devtools/test-symbol-check.py b/contrib/devtools/test-symbol-check.py index b4c112b266..2881e3efac 100755 --- a/contrib/devtools/test-symbol-check.py +++ b/contrib/devtools/test-symbol-check.py @@ -39,7 +39,7 @@ class TestSymbolChecks(unittest.TestCase): cc = determine_wellknown_cmd('CC', 'gcc') # there's no way to do this test for RISC-V at the moment; we build for - # RISC-V in a glibc 2.27 envinonment and we allow all symbols from 2.27. + # RISC-V in a glibc 2.27 environment and we allow all symbols from 2.27. if 'riscv' in get_machine(cc): self.skipTest("test not available for RISC-V") diff --git a/contrib/seeds/asmap.py b/contrib/seeds/asmap.py index 7a605d0b9e..e28e5cf532 100644 --- a/contrib/seeds/asmap.py +++ b/contrib/seeds/asmap.py @@ -168,7 +168,7 @@ class _Instruction(Enum): # Not an actual instruction, but a way to encode the empty program that fails. In the # encoder, it is used more generally to represent the failure case inside MATCH instructions, # which may (if used inside the context of a DEFAULT instruction) actually correspond to - # a succesful return. In this usage, they're always converted to an actual MATCH or RETURN + # a successful return. In this usage, they're always converted to an actual MATCH or RETURN # before the top level is reached (see make_default below). END = 4 diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk index 2bc3a81430..7e268fe9a5 100644 --- a/depends/packages/qt.mk +++ b/depends/packages/qt.mk @@ -171,7 +171,7 @@ $(package)_config_opts_mingw32 += -no-freetype $(package)_config_opts_mingw32 += -xplatform win32-g++ $(package)_config_opts_mingw32 += "QMAKE_CFLAGS = '$($(package)_cflags) $($(package)_cppflags)'" $(package)_config_opts_mingw32 += "QMAKE_CXX = '$($(package)_cxx)'" -$(package)_config_opts_mingw32 += "QMAKE_CXXFLAGS = '$($(package)_cflags) $($(package)_cppflags)'" +$(package)_config_opts_mingw32 += "QMAKE_CXXFLAGS = '$($(package)_cxxflags) $($(package)_cppflags)'" $(package)_config_opts_mingw32 += "QMAKE_LFLAGS = '$($(package)_ldflags)'" $(package)_config_opts_mingw32 += -device-option CROSS_COMPILE="$(host)-" $(package)_config_opts_mingw32 += -pch diff --git a/depends/packages/sqlite.mk b/depends/packages/sqlite.mk index 126781ceeb..820d724214 100644 --- a/depends/packages/sqlite.mk +++ b/depends/packages/sqlite.mk @@ -1,8 +1,8 @@ package=sqlite -$(package)_version=3320100 -$(package)_download_path=https://sqlite.org/2020/ +$(package)_version=3380500 +$(package)_download_path=https://sqlite.org/2022/ $(package)_file_name=sqlite-autoconf-$($(package)_version).tar.gz -$(package)_sha256_hash=486748abfb16abd8af664e3a5f03b228e5f124682b0c942e157644bf6fff7d10 +$(package)_sha256_hash=5af07de982ba658fd91a03170c945f99c971f6955bc79df3266544373e39869c define $(package)_set_vars $(package)_config_opts=--disable-shared --disable-readline --disable-dynamic-extensions --enable-option-checking diff --git a/depends/packages/systemtap.mk b/depends/packages/systemtap.mk index 833e75b978..a57f1b6d36 100644 --- a/depends/packages/systemtap.mk +++ b/depends/packages/systemtap.mk @@ -1,8 +1,8 @@ package=systemtap -$(package)_version=4.5 +$(package)_version=4.7 $(package)_download_path=https://sourceware.org/systemtap/ftp/releases/ $(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=75078ed37e0dd2a769c9d1f9394170b2d9f4d7daa425f43ca80c13bad6cfc925 +$(package)_sha256_hash=43a0a3db91aa4d41e28015b39a65e62059551f3cc7377ebf3a3a5ca7339e7b1f $(package)_patches=remove_SDT_ASM_SECTION_AUTOGROUP_SUPPORT_check.patch define $(package)_preprocess_cmds diff --git a/depends/patches/systemtap/remove_SDT_ASM_SECTION_AUTOGROUP_SUPPORT_check.patch b/depends/patches/systemtap/remove_SDT_ASM_SECTION_AUTOGROUP_SUPPORT_check.patch index eae0cf72d6..c115bc43e8 100644 --- a/depends/patches/systemtap/remove_SDT_ASM_SECTION_AUTOGROUP_SUPPORT_check.patch +++ b/depends/patches/systemtap/remove_SDT_ASM_SECTION_AUTOGROUP_SUPPORT_check.patch @@ -1,19 +1,15 @@ -commit b92d4c121486f3c6e8a2cea537c53eb09894479a -Author: 0xb10c <0xb10c@gmail.com> -Date: Tue Dec 7 11:02:07 2021 +0100 +Remove _SDT_ASM_SECTION_AUTOGROUP_SUPPORT check - Remove _SDT_ASM_SECTION_AUTOGROUP_SUPPORT check - - We assume that the assembler supports "?" in .pushsection directives. - This enables us to skip configure and make. - - See https://github.com/bitcoin/bitcoin/issues/23297. +We assume that the assembler supports "?" in .pushsection directives. +This enables us to skip configure and make. + +See https://github.com/bitcoin/bitcoin/issues/23297. diff --git a/includes/sys/sdt.h b/includes/sys/sdt.h -index 97766e710..352b4ee25 100644 +index ca0162b..f96e0ee 100644 --- a/includes/sys/sdt.h +++ b/includes/sys/sdt.h -@@ -230,12 +230,10 @@ __extension__ extern unsigned long long __sdt_unsp; +@@ -241,12 +241,10 @@ __extension__ extern unsigned long long __sdt_unsp; nice with code in COMDAT sections, which comes up in C++ code. Without that assembler support, some combinations of probe placements in certain kinds of C++ code may produce link-time errors. */ @@ -27,5 +23,5 @@ index 97766e710..352b4ee25 100644 -# define _SDT_ASM_AUTOGROUP "" -#endif - #define _SDT_ASM_BODY(provider, name, pack_args, args) \ - _SDT_ASM_1(990: _SDT_NOP) \ + #define _SDT_DEF_MACROS \ + _SDT_ASM_1(.altmacro) \ diff --git a/src/Makefile.am b/src/Makefile.am index f90c14bab8..fa716af619 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -29,7 +29,6 @@ LIBBITCOIN_NODE=libbitcoin_node.a LIBBITCOIN_COMMON=libbitcoin_common.a LIBBITCOIN_CONSENSUS=libbitcoin_consensus.a LIBBITCOIN_CLI=libbitcoin_cli.a -LIBBITCOIN_KERNEL=libbitcoin_kernel.a LIBBITCOIN_UTIL=libbitcoin_util.a LIBBITCOIN_CRYPTO_BASE=crypto/libbitcoin_crypto_base.la LIBBITCOINQT=qt/libbitcoinqt.a diff --git a/src/compat.h b/src/compat.h index f41c501c84..0a44b98b4e 100644 --- a/src/compat.h +++ b/src/compat.h @@ -11,9 +11,6 @@ #endif #ifdef WIN32 -#ifndef NOMINMAX -#define NOMINMAX -#endif #ifdef FD_SETSIZE #undef FD_SETSIZE // prevent redefinition compiler warning #endif diff --git a/src/fs.cpp b/src/fs.cpp index b61115bf01..74b167e313 100644 --- a/src/fs.cpp +++ b/src/fs.cpp @@ -12,9 +12,6 @@ #include <sys/utsname.h> #include <unistd.h> #else -#ifndef NOMINMAX -#define NOMINMAX -#endif #include <codecvt> #include <limits> #include <windows.h> diff --git a/src/node/blockstorage.h b/src/node/blockstorage.h index 2e52716649..e017f3f427 100644 --- a/src/node/blockstorage.h +++ b/src/node/blockstorage.h @@ -49,7 +49,7 @@ extern std::atomic_bool fReindex; /** Pruning-related variables and constants */ /** True if we're running in -prune mode. */ extern bool fPruneMode; -/** Number of MiB of block files that we're trying to stay below. */ +/** Number of bytes of block files that we're trying to stay below. */ extern uint64_t nPruneTarget; // Because validation code takes pointers to the map's CBlockIndex objects, if diff --git a/src/policy/packages.h b/src/policy/packages.h index ba6a3a9a06..564ff50d29 100644 --- a/src/policy/packages.h +++ b/src/policy/packages.h @@ -19,6 +19,15 @@ static constexpr uint32_t MAX_PACKAGE_COUNT{25}; static constexpr uint32_t MAX_PACKAGE_SIZE{101}; static_assert(MAX_PACKAGE_SIZE * WITNESS_SCALE_FACTOR * 1000 >= MAX_STANDARD_TX_WEIGHT); +// If a package is submitted, it must be within the mempool's ancestor/descendant limits. Since a +// submitted package must be child-with-unconfirmed-parents (all of the transactions are an ancestor +// of the child), package limits are ultimately bounded by mempool package limits. Ensure that the +// defaults reflect this constraint. +static_assert(DEFAULT_DESCENDANT_LIMIT >= MAX_PACKAGE_COUNT); +static_assert(DEFAULT_ANCESTOR_LIMIT >= MAX_PACKAGE_COUNT); +static_assert(DEFAULT_ANCESTOR_SIZE_LIMIT >= MAX_PACKAGE_SIZE); +static_assert(DEFAULT_DESCENDANT_SIZE_LIMIT >= MAX_PACKAGE_SIZE); + /** A "reason" why a package was invalid. It may be that one or more of the included * transactions is invalid or the package itself violates our rules. * We don't distinguish between consensus and policy violations right now. diff --git a/src/policy/policy.h b/src/policy/policy.h index 94f9623b8a..cd46652efc 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -20,49 +20,63 @@ class CFeeRate; class CScript; /** Default for -blockmaxweight, which controls the range of block weights the mining code will create **/ -static const unsigned int DEFAULT_BLOCK_MAX_WEIGHT = MAX_BLOCK_WEIGHT - 4000; +static constexpr unsigned int DEFAULT_BLOCK_MAX_WEIGHT{MAX_BLOCK_WEIGHT - 4000}; /** Default for -blockmintxfee, which sets the minimum feerate for a transaction in blocks created by mining code **/ -static const unsigned int DEFAULT_BLOCK_MIN_TX_FEE = 1000; +static constexpr unsigned int DEFAULT_BLOCK_MIN_TX_FEE{1000}; /** The maximum weight for transactions we're willing to relay/mine */ -static const unsigned int MAX_STANDARD_TX_WEIGHT = 400000; +static constexpr unsigned int MAX_STANDARD_TX_WEIGHT{400000}; /** The minimum non-witness size for transactions we're willing to relay/mine (1 segwit input + 1 P2WPKH output = 82 bytes) */ -static const unsigned int MIN_STANDARD_TX_NONWITNESS_SIZE = 82; +static constexpr unsigned int MIN_STANDARD_TX_NONWITNESS_SIZE{82}; /** Maximum number of signature check operations in an IsStandard() P2SH script */ -static const unsigned int MAX_P2SH_SIGOPS = 15; +static constexpr unsigned int MAX_P2SH_SIGOPS{15}; /** The maximum number of sigops we're willing to relay/mine in a single tx */ -static const unsigned int MAX_STANDARD_TX_SIGOPS_COST = MAX_BLOCK_SIGOPS_COST/5; +static constexpr unsigned int MAX_STANDARD_TX_SIGOPS_COST{MAX_BLOCK_SIGOPS_COST/5}; /** Default for -maxmempool, maximum megabytes of mempool memory usage */ -static const unsigned int DEFAULT_MAX_MEMPOOL_SIZE = 300; +static constexpr unsigned int DEFAULT_MAX_MEMPOOL_SIZE{300}; /** Default for -incrementalrelayfee, which sets the minimum feerate increase for mempool limiting or BIP 125 replacement **/ -static const unsigned int DEFAULT_INCREMENTAL_RELAY_FEE = 1000; +static constexpr unsigned int DEFAULT_INCREMENTAL_RELAY_FEE{1000}; /** Default for -bytespersigop */ -static const unsigned int DEFAULT_BYTES_PER_SIGOP = 20; +static constexpr unsigned int DEFAULT_BYTES_PER_SIGOP{20}; /** Default for -permitbaremultisig */ -static const bool DEFAULT_PERMIT_BAREMULTISIG = true; +static constexpr bool DEFAULT_PERMIT_BAREMULTISIG{true}; /** The maximum number of witness stack items in a standard P2WSH script */ -static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEMS = 100; +static constexpr unsigned int MAX_STANDARD_P2WSH_STACK_ITEMS{100}; /** The maximum size in bytes of each witness stack item in a standard P2WSH script */ -static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEM_SIZE = 80; +static constexpr unsigned int MAX_STANDARD_P2WSH_STACK_ITEM_SIZE{80}; /** The maximum size in bytes of each witness stack item in a standard BIP 342 script (Taproot, leaf version 0xc0) */ -static const unsigned int MAX_STANDARD_TAPSCRIPT_STACK_ITEM_SIZE = 80; +static constexpr unsigned int MAX_STANDARD_TAPSCRIPT_STACK_ITEM_SIZE{80}; /** The maximum size in bytes of a standard witnessScript */ -static const unsigned int MAX_STANDARD_P2WSH_SCRIPT_SIZE = 3600; +static constexpr unsigned int MAX_STANDARD_P2WSH_SCRIPT_SIZE{3600}; /** The maximum size of a standard ScriptSig */ -static const unsigned int MAX_STANDARD_SCRIPTSIG_SIZE = 1650; +static constexpr unsigned int MAX_STANDARD_SCRIPTSIG_SIZE{1650}; /** Min feerate for defining dust. Historically this has been based on the * minRelayTxFee, however changing the dust limit changes which transactions are * standard and should be done with care and ideally rarely. It makes sense to * only increase the dust limit after prior releases were already not creating * outputs below the new threshold */ -static const unsigned int DUST_RELAY_TX_FEE = 3000; +static constexpr unsigned int DUST_RELAY_TX_FEE{3000}; /** Default for -minrelaytxfee, minimum relay fee for transactions */ -static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000; +static constexpr unsigned int DEFAULT_MIN_RELAY_TX_FEE{1000}; +/** Default for -limitancestorcount, max number of in-mempool ancestors */ +static constexpr unsigned int DEFAULT_ANCESTOR_LIMIT{25}; +/** Default for -limitancestorsize, maximum kilobytes of tx + all in-mempool ancestors */ +static constexpr unsigned int DEFAULT_ANCESTOR_SIZE_LIMIT{101}; +/** Default for -limitdescendantcount, max number of in-mempool descendants */ +static constexpr unsigned int DEFAULT_DESCENDANT_LIMIT{25}; +/** Default for -limitdescendantsize, maximum kilobytes of in-mempool descendants */ +static constexpr unsigned int DEFAULT_DESCENDANT_SIZE_LIMIT{101}; +/** + * An extra transaction can be added to a package, as long as it only has one + * ancestor and is no larger than this. Not really any reason to make this + * configurable as it doesn't materially change DoS parameters. + */ +static constexpr unsigned int EXTRA_DESCENDANT_TX_SIZE_LIMIT{10000}; /** * Standard script verification flags that standard transactions will comply * with. However scripts violating these flags may still be present in valid * blocks and we must accept those blocks. */ -static constexpr unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY_FLAGS | +static constexpr unsigned int STANDARD_SCRIPT_VERIFY_FLAGS{MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_STRICTENC | SCRIPT_VERIFY_MINIMALDATA | @@ -81,14 +95,14 @@ static constexpr unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VE SCRIPT_VERIFY_TAPROOT | SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION | SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS | - SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE; + SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE}; /** For convenience, standard but not mandatory verify flags. */ -static constexpr unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS; +static constexpr unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS{STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS}; /** Used as the flags parameter to sequence and nLocktime checks in non-consensus code. */ -static constexpr unsigned int STANDARD_LOCKTIME_VERIFY_FLAGS = LOCKTIME_VERIFY_SEQUENCE | - LOCKTIME_MEDIAN_TIME_PAST; +static constexpr unsigned int STANDARD_LOCKTIME_VERIFY_FLAGS{LOCKTIME_VERIFY_SEQUENCE | + LOCKTIME_MEDIAN_TIME_PAST}; CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFee); diff --git a/src/psbt.h b/src/psbt.h index 8fda889bb4..4a6d41076f 100644 --- a/src/psbt.h +++ b/src/psbt.h @@ -54,7 +54,7 @@ static constexpr uint8_t PSBT_SEPARATOR = 0x00; // BIP 174 does not specify a maximum file size, but we set a limit anyway // to prevent reading a stream indefinitely and running out of memory. -const std::streamsize MAX_FILE_SIZE_PSBT = 100000000; // 100 MiB +const std::streamsize MAX_FILE_SIZE_PSBT = 100000000; // 100 MB // PSBT version number static constexpr uint32_t PSBT_HIGHEST_VERSION = 0; diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui index 934363af1f..ffebc316b1 100644 --- a/src/qt/forms/sendcoinsentry.ui +++ b/src/qt/forms/sendcoinsentry.ui @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>SendCoinsEntry</class> - <widget class="QStackedWidget" name="SendCoinsEntry"> + <widget class="QWidget" name="SendCoinsEntry"> <property name="geometry"> <rect> <x>0</x> @@ -16,1244 +16,204 @@ <property name="autoFillBackground"> <bool>false</bool> </property> - <widget class="QFrame" name="SendCoins"> - <property name="frameShape"> - <enum>QFrame::NoFrame</enum> + <layout class="QGridLayout" name="gridLayout"> + <property name="topMargin"> + <number>8</number> </property> - <layout class="QGridLayout" name="gridLayout"> - <property name="topMargin"> - <number>8</number> - </property> - <property name="bottomMargin"> - <number>4</number> - </property> - <property name="horizontalSpacing"> - <number>12</number> - </property> - <property name="verticalSpacing"> - <number>8</number> - </property> - <item row="0" column="0"> - <widget class="QLabel" name="payToLabel"> - <property name="text"> - <string>Pay &To:</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - <property name="buddy"> - <cstring>payTo</cstring> - </property> - </widget> - </item> - <item row="0" column="1"> - <layout class="QHBoxLayout" name="payToLayout"> - <property name="spacing"> - <number>0</number> - </property> - <item> - <widget class="QValidatedLineEdit" name="payTo"> - <property name="toolTip"> - <string>The Bitcoin address to send the payment to</string> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="addressBookButton"> - <property name="toolTip"> - <string>Choose previously used address</string> - </property> - <property name="text"> - <string/> - </property> - <property name="icon"> - <iconset resource="../bitcoin.qrc"> - <normaloff>:/icons/address-book</normaloff>:/icons/address-book</iconset> - </property> - <property name="iconSize"> - <size> - <width>22</width> - <height>22</height> - </size> - </property> - <property name="shortcut"> - <string>Alt+A</string> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="pasteButton"> - <property name="toolTip"> - <string>Paste address from clipboard</string> - </property> - <property name="text"> - <string/> - </property> - <property name="icon"> - <iconset resource="../bitcoin.qrc"> - <normaloff>:/icons/editpaste</normaloff>:/icons/editpaste</iconset> - </property> - <property name="iconSize"> - <size> - <width>22</width> - <height>22</height> - </size> - </property> - <property name="shortcut"> - <string>Alt+P</string> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="deleteButton"> - <property name="toolTip"> - <string>Remove this entry</string> - </property> - <property name="text"> - <string/> - </property> - <property name="icon"> - <iconset resource="../bitcoin.qrc"> - <normaloff>:/icons/remove</normaloff>:/icons/remove</iconset> - </property> - <property name="iconSize"> - <size> - <width>22</width> - <height>22</height> - </size> - </property> - </widget> - </item> - </layout> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="labellLabel"> - <property name="text"> - <string>&Label:</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - <property name="buddy"> - <cstring>addAsLabel</cstring> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QLineEdit" name="addAsLabel"> - <property name="toolTip"> - <string>Enter a label for this address to add it to the list of used addresses</string> - </property> - <property name="placeholderText"> - <string>Enter a label for this address to add it to the list of used addresses</string> - </property> - </widget> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="amountLabel"> - <property name="text"> - <string>A&mount:</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - <property name="buddy"> - <cstring>payAmount</cstring> - </property> - </widget> - </item> - <item row="2" column="1"> - <layout class="QHBoxLayout" name="horizontalLayoutAmount" stretch="0,1,0"> - <item> - <widget class="BitcoinAmountField" name="payAmount"> - <property name="toolTip"> - <string>The amount to send in the selected unit</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="checkboxSubtractFeeFromAmount"> - <property name="toolTip"> - <string>The fee will be deducted from the amount being sent. The recipient will receive less bitcoins than you enter in the amount field. If multiple recipients are selected, the fee is split equally.</string> - </property> - <property name="text"> - <string>S&ubtract fee from amount</string> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="useAvailableBalanceButton"> - <property name="text"> - <string>Use available balance</string> - </property> - </widget> - </item> - </layout> - </item> - <item row="3" column="0"> - <widget class="QLabel" name="messageLabel"> - <property name="text"> - <string>Message:</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item row="3" column="1"> - <widget class="QLabel" name="messageTextLabel"> - <property name="toolTip"> - <string>A message that was attached to the bitcoin: URI which will be stored with the transaction for your reference. Note: This message will not be sent over the Bitcoin network.</string> - </property> - <property name="textFormat"> - <enum>Qt::PlainText</enum> - </property> - </widget> - </item> - <item row="4" column="0" colspan="2"> - <widget class="Line" name="line"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - </widget> - </item> - </layout> - </widget> - <widget class="QFrame" name="SendCoins_UnauthenticatedPaymentRequest"> - <property name="palette"> - <palette> - <active> - <colorrole role="WindowText"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>0</red> - <green>0</green> - <blue>0</blue> - </color> - </brush> - </colorrole> - <colorrole role="Button"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>255</red> - <green>255</green> - <blue>127</blue> - </color> - </brush> - </colorrole> - <colorrole role="Light"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>255</red> - <green>255</green> - <blue>255</blue> - </color> - </brush> - </colorrole> - <colorrole role="Midlight"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>255</red> - <green>255</green> - <blue>191</blue> - </color> - </brush> - </colorrole> - <colorrole role="Dark"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>127</red> - <green>127</green> - <blue>63</blue> - </color> - </brush> - </colorrole> - <colorrole role="Mid"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>170</red> - <green>170</green> - <blue>84</blue> - </color> - </brush> - </colorrole> - <colorrole role="Text"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>0</red> - <green>0</green> - <blue>0</blue> - </color> - </brush> - </colorrole> - <colorrole role="BrightText"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>255</red> - <green>255</green> - <blue>255</blue> - </color> - </brush> - </colorrole> - <colorrole role="ButtonText"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>0</red> - <green>0</green> - <blue>0</blue> - </color> - </brush> - </colorrole> - <colorrole role="Base"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>255</red> - <green>255</green> - <blue>255</blue> - </color> - </brush> - </colorrole> - <colorrole role="Window"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>255</red> - <green>255</green> - <blue>127</blue> - </color> - </brush> - </colorrole> - <colorrole role="Shadow"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>0</red> - <green>0</green> - <blue>0</blue> - </color> - </brush> - </colorrole> - <colorrole role="AlternateBase"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>255</red> - <green>255</green> - <blue>191</blue> - </color> - </brush> - </colorrole> - <colorrole role="ToolTipBase"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>255</red> - <green>255</green> - <blue>220</blue> - </color> - </brush> - </colorrole> - <colorrole role="ToolTipText"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>0</red> - <green>0</green> - <blue>0</blue> - </color> - </brush> - </colorrole> - </active> - <inactive> - <colorrole role="WindowText"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>0</red> - <green>0</green> - <blue>0</blue> - </color> - </brush> - </colorrole> - <colorrole role="Button"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>255</red> - <green>255</green> - <blue>127</blue> - </color> - </brush> - </colorrole> - <colorrole role="Light"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>255</red> - <green>255</green> - <blue>255</blue> - </color> - </brush> - </colorrole> - <colorrole role="Midlight"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>255</red> - <green>255</green> - <blue>191</blue> - </color> - </brush> - </colorrole> - <colorrole role="Dark"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>127</red> - <green>127</green> - <blue>63</blue> - </color> - </brush> - </colorrole> - <colorrole role="Mid"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>170</red> - <green>170</green> - <blue>84</blue> - </color> - </brush> - </colorrole> - <colorrole role="Text"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>0</red> - <green>0</green> - <blue>0</blue> - </color> - </brush> - </colorrole> - <colorrole role="BrightText"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>255</red> - <green>255</green> - <blue>255</blue> - </color> - </brush> - </colorrole> - <colorrole role="ButtonText"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>0</red> - <green>0</green> - <blue>0</blue> - </color> - </brush> - </colorrole> - <colorrole role="Base"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>255</red> - <green>255</green> - <blue>255</blue> - </color> - </brush> - </colorrole> - <colorrole role="Window"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>255</red> - <green>255</green> - <blue>127</blue> - </color> - </brush> - </colorrole> - <colorrole role="Shadow"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>0</red> - <green>0</green> - <blue>0</blue> - </color> - </brush> - </colorrole> - <colorrole role="AlternateBase"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>255</red> - <green>255</green> - <blue>191</blue> - </color> - </brush> - </colorrole> - <colorrole role="ToolTipBase"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>255</red> - <green>255</green> - <blue>220</blue> - </color> - </brush> - </colorrole> - <colorrole role="ToolTipText"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>0</red> - <green>0</green> - <blue>0</blue> - </color> - </brush> - </colorrole> - </inactive> - <disabled> - <colorrole role="WindowText"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>127</red> - <green>127</green> - <blue>63</blue> - </color> - </brush> - </colorrole> - <colorrole role="Button"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>255</red> - <green>255</green> - <blue>127</blue> - </color> - </brush> - </colorrole> - <colorrole role="Light"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>255</red> - <green>255</green> - <blue>255</blue> - </color> - </brush> - </colorrole> - <colorrole role="Midlight"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>255</red> - <green>255</green> - <blue>191</blue> - </color> - </brush> - </colorrole> - <colorrole role="Dark"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>127</red> - <green>127</green> - <blue>63</blue> - </color> - </brush> - </colorrole> - <colorrole role="Mid"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>170</red> - <green>170</green> - <blue>84</blue> - </color> - </brush> - </colorrole> - <colorrole role="Text"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>127</red> - <green>127</green> - <blue>63</blue> - </color> - </brush> - </colorrole> - <colorrole role="BrightText"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>255</red> - <green>255</green> - <blue>255</blue> - </color> - </brush> - </colorrole> - <colorrole role="ButtonText"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>127</red> - <green>127</green> - <blue>63</blue> - </color> - </brush> - </colorrole> - <colorrole role="Base"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>255</red> - <green>255</green> - <blue>127</blue> - </color> - </brush> - </colorrole> - <colorrole role="Window"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>255</red> - <green>255</green> - <blue>127</blue> - </color> - </brush> - </colorrole> - <colorrole role="Shadow"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>0</red> - <green>0</green> - <blue>0</blue> - </color> - </brush> - </colorrole> - <colorrole role="AlternateBase"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>255</red> - <green>255</green> - <blue>127</blue> - </color> - </brush> - </colorrole> - <colorrole role="ToolTipBase"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>255</red> - <green>255</green> - <blue>220</blue> - </color> - </brush> - </colorrole> - <colorrole role="ToolTipText"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>0</red> - <green>0</green> - <blue>0</blue> - </color> - </brush> - </colorrole> - </disabled> - </palette> + <property name="bottomMargin"> + <number>4</number> </property> - <property name="toolTip"> - <string>This is an unauthenticated payment request.</string> + <property name="horizontalSpacing"> + <number>12</number> </property> - <property name="autoFillBackground"> - <bool>true</bool> + <property name="verticalSpacing"> + <number>8</number> </property> - <property name="frameShape"> - <enum>QFrame::NoFrame</enum> - </property> - <layout class="QGridLayout" name="gridLayout_is"> - <property name="spacing"> - <number>12</number> - </property> - <item row="0" column="0"> - <widget class="QLabel" name="payToLabel_is"> - <property name="text"> - <string>Pay To:</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item row="0" column="1"> - <layout class="QHBoxLayout" name="payToLayout_is"> - <property name="spacing"> - <number>0</number> - </property> - <item> - <widget class="QLabel" name="payTo_is"/> - </item> - <item> - <widget class="QToolButton" name="deleteButton_is"> - <property name="toolTip"> - <string>Remove this entry</string> - </property> - <property name="text"> - <string/> - </property> - <property name="icon"> - <iconset resource="../bitcoin.qrc"> - <normaloff>:/icons/remove</normaloff>:/icons/remove</iconset> - </property> - </widget> - </item> - </layout> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="memoLabel_is"> - <property name="text"> - <string>Memo:</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QLabel" name="memoTextLabel_is"> - <property name="textFormat"> - <enum>Qt::PlainText</enum> - </property> - </widget> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="amountLabel_is"> - <property name="text"> - <string>A&mount:</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - <property name="buddy"> - <cstring>payAmount_is</cstring> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="BitcoinAmountField" name="payAmount_is"> - <property name="acceptDrops"> - <bool>false</bool> - </property> - </widget> - </item> - </layout> - </widget> - <widget class="QFrame" name="SendCoins_AuthenticatedPaymentRequest"> - <property name="palette"> - <palette> - <active> - <colorrole role="WindowText"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>0</red> - <green>0</green> - <blue>0</blue> - </color> - </brush> - </colorrole> - <colorrole role="Button"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>140</red> - <green>232</green> - <blue>119</blue> - </color> - </brush> - </colorrole> - <colorrole role="Light"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>230</red> - <green>255</green> - <blue>224</blue> - </color> - </brush> - </colorrole> - <colorrole role="Midlight"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>185</red> - <green>243</green> - <blue>171</blue> - </color> - </brush> - </colorrole> - <colorrole role="Dark"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>70</red> - <green>116</green> - <blue>59</blue> - </color> - </brush> - </colorrole> - <colorrole role="Mid"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>93</red> - <green>155</green> - <blue>79</blue> - </color> - </brush> - </colorrole> - <colorrole role="Text"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>0</red> - <green>0</green> - <blue>0</blue> - </color> - </brush> - </colorrole> - <colorrole role="BrightText"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>155</red> - <green>255</green> - <blue>147</blue> - </color> - </brush> - </colorrole> - <colorrole role="ButtonText"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>0</red> - <green>0</green> - <blue>0</blue> - </color> - </brush> - </colorrole> - <colorrole role="Base"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>119</red> - <green>255</green> - <blue>233</blue> - </color> - </brush> - </colorrole> - <colorrole role="Window"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>140</red> - <green>232</green> - <blue>119</blue> - </color> - </brush> - </colorrole> - <colorrole role="Shadow"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>0</red> - <green>0</green> - <blue>0</blue> - </color> - </brush> - </colorrole> - <colorrole role="AlternateBase"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>197</red> - <green>243</green> - <blue>187</blue> - </color> - </brush> - </colorrole> - <colorrole role="NoRole"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>125</red> - <green>194</green> - <blue>122</blue> - </color> - </brush> - </colorrole> - <colorrole role="ToolTipBase"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>255</red> - <green>255</green> - <blue>220</blue> - </color> - </brush> - </colorrole> - <colorrole role="ToolTipText"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>0</red> - <green>0</green> - <blue>0</blue> - </color> - </brush> - </colorrole> - </active> - <inactive> - <colorrole role="WindowText"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>0</red> - <green>0</green> - <blue>0</blue> - </color> - </brush> - </colorrole> - <colorrole role="Button"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>140</red> - <green>232</green> - <blue>119</blue> - </color> - </brush> - </colorrole> - <colorrole role="Light"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>230</red> - <green>255</green> - <blue>224</blue> - </color> - </brush> - </colorrole> - <colorrole role="Midlight"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>185</red> - <green>243</green> - <blue>171</blue> - </color> - </brush> - </colorrole> - <colorrole role="Dark"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>70</red> - <green>116</green> - <blue>59</blue> - </color> - </brush> - </colorrole> - <colorrole role="Mid"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>93</red> - <green>155</green> - <blue>79</blue> - </color> - </brush> - </colorrole> - <colorrole role="Text"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>0</red> - <green>0</green> - <blue>0</blue> - </color> - </brush> - </colorrole> - <colorrole role="BrightText"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>155</red> - <green>255</green> - <blue>147</blue> - </color> - </brush> - </colorrole> - <colorrole role="ButtonText"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>0</red> - <green>0</green> - <blue>0</blue> - </color> - </brush> - </colorrole> - <colorrole role="Base"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>119</red> - <green>255</green> - <blue>233</blue> - </color> - </brush> - </colorrole> - <colorrole role="Window"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>140</red> - <green>232</green> - <blue>119</blue> - </color> - </brush> - </colorrole> - <colorrole role="Shadow"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>0</red> - <green>0</green> - <blue>0</blue> - </color> - </brush> - </colorrole> - <colorrole role="AlternateBase"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>197</red> - <green>243</green> - <blue>187</blue> - </color> - </brush> - </colorrole> - <colorrole role="NoRole"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>125</red> - <green>194</green> - <blue>122</blue> - </color> - </brush> - </colorrole> - <colorrole role="ToolTipBase"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>255</red> - <green>255</green> - <blue>220</blue> - </color> - </brush> - </colorrole> - <colorrole role="ToolTipText"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>0</red> - <green>0</green> - <blue>0</blue> - </color> - </brush> - </colorrole> - </inactive> - <disabled> - <colorrole role="WindowText"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>70</red> - <green>116</green> - <blue>59</blue> - </color> - </brush> - </colorrole> - <colorrole role="Button"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>140</red> - <green>232</green> - <blue>119</blue> - </color> - </brush> - </colorrole> - <colorrole role="Light"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>230</red> - <green>255</green> - <blue>224</blue> - </color> - </brush> - </colorrole> - <colorrole role="Midlight"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>185</red> - <green>243</green> - <blue>171</blue> - </color> - </brush> - </colorrole> - <colorrole role="Dark"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>70</red> - <green>116</green> - <blue>59</blue> - </color> - </brush> - </colorrole> - <colorrole role="Mid"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>93</red> - <green>155</green> - <blue>79</blue> - </color> - </brush> - </colorrole> - <colorrole role="Text"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>70</red> - <green>116</green> - <blue>59</blue> - </color> - </brush> - </colorrole> - <colorrole role="BrightText"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>155</red> - <green>255</green> - <blue>147</blue> - </color> - </brush> - </colorrole> - <colorrole role="ButtonText"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>70</red> - <green>116</green> - <blue>59</blue> - </color> - </brush> - </colorrole> - <colorrole role="Base"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>140</red> - <green>232</green> - <blue>119</blue> - </color> - </brush> - </colorrole> - <colorrole role="Window"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>140</red> - <green>232</green> - <blue>119</blue> - </color> - </brush> - </colorrole> - <colorrole role="Shadow"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>0</red> - <green>0</green> - <blue>0</blue> - </color> - </brush> - </colorrole> - <colorrole role="AlternateBase"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>140</red> - <green>232</green> - <blue>119</blue> - </color> - </brush> - </colorrole> - <colorrole role="NoRole"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>125</red> - <green>194</green> - <blue>122</blue> - </color> - </brush> - </colorrole> - <colorrole role="ToolTipBase"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>255</red> - <green>255</green> - <blue>220</blue> - </color> - </brush> - </colorrole> - <colorrole role="ToolTipText"> - <brush brushstyle="SolidPattern"> - <color alpha="255"> - <red>0</red> - <green>0</green> - <blue>0</blue> - </color> - </brush> - </colorrole> - </disabled> - </palette> - </property> - <property name="toolTip"> - <string>This is an authenticated payment request.</string> - </property> - <property name="autoFillBackground"> - <bool>true</bool> - </property> - <property name="frameShape"> - <enum>QFrame::NoFrame</enum> - </property> - <layout class="QGridLayout" name="gridLayout_s"> - <property name="spacing"> - <number>12</number> - </property> - <item row="0" column="0"> - <widget class="QLabel" name="payToLabel_s"> - <property name="text"> - <string>Pay To:</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item row="0" column="1"> - <layout class="QHBoxLayout" name="payToLayout_s"> - <property name="spacing"> - <number>0</number> - </property> - <item> - <widget class="QLabel" name="payTo_s"> - <property name="textFormat"> - <enum>Qt::PlainText</enum> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="deleteButton_s"> - <property name="toolTip"> - <string>Remove this entry</string> - </property> - <property name="text"> - <string/> - </property> - <property name="icon"> - <iconset resource="../bitcoin.qrc"> - <normaloff>:/icons/remove</normaloff>:/icons/remove</iconset> - </property> - </widget> - </item> - </layout> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="memoLabel_s"> - <property name="text"> - <string>Memo:</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QLabel" name="memoTextLabel_s"> - <property name="textFormat"> - <enum>Qt::PlainText</enum> - </property> - </widget> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="amountLabel_s"> - <property name="text"> - <string>A&mount:</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - <property name="buddy"> - <cstring>payAmount_s</cstring> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="BitcoinAmountField" name="payAmount_s"> - <property name="acceptDrops"> - <bool>false</bool> - </property> - </widget> - </item> - </layout> - </widget> + <item row="0" column="0"> + <widget class="QLabel" name="payToLabel"> + <property name="text"> + <string>Pay &To:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="buddy"> + <cstring>payTo</cstring> + </property> + </widget> + </item> + <item row="0" column="1"> + <layout class="QHBoxLayout" name="payToLayout"> + <property name="spacing"> + <number>0</number> + </property> + <item> + <widget class="QValidatedLineEdit" name="payTo"> + <property name="toolTip"> + <string>The Bitcoin address to send the payment to</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="addressBookButton"> + <property name="toolTip"> + <string>Choose previously used address</string> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset resource="../bitcoin.qrc"> + <normaloff>:/icons/address-book</normaloff>:/icons/address-book</iconset> + </property> + <property name="iconSize"> + <size> + <width>22</width> + <height>22</height> + </size> + </property> + <property name="shortcut"> + <string>Alt+A</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="pasteButton"> + <property name="toolTip"> + <string>Paste address from clipboard</string> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset resource="../bitcoin.qrc"> + <normaloff>:/icons/editpaste</normaloff>:/icons/editpaste</iconset> + </property> + <property name="iconSize"> + <size> + <width>22</width> + <height>22</height> + </size> + </property> + <property name="shortcut"> + <string>Alt+P</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="deleteButton"> + <property name="toolTip"> + <string>Remove this entry</string> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset resource="../bitcoin.qrc"> + <normaloff>:/icons/remove</normaloff>:/icons/remove</iconset> + </property> + <property name="iconSize"> + <size> + <width>22</width> + <height>22</height> + </size> + </property> + </widget> + </item> + </layout> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="labellLabel"> + <property name="text"> + <string>&Label:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="buddy"> + <cstring>addAsLabel</cstring> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="addAsLabel"> + <property name="toolTip"> + <string>Enter a label for this address to add it to the list of used addresses</string> + </property> + <property name="placeholderText"> + <string>Enter a label for this address to add it to the list of used addresses</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="amountLabel"> + <property name="text"> + <string>A&mount:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="buddy"> + <cstring>payAmount</cstring> + </property> + </widget> + </item> + <item row="2" column="1"> + <layout class="QHBoxLayout" name="horizontalLayoutAmount" stretch="0,1,0"> + <item> + <widget class="BitcoinAmountField" name="payAmount" native="true"> + <property name="toolTip"> + <string>The amount to send in the selected unit</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="checkboxSubtractFeeFromAmount"> + <property name="toolTip"> + <string>The fee will be deducted from the amount being sent. The recipient will receive less bitcoins than you enter in the amount field. If multiple recipients are selected, the fee is split equally.</string> + </property> + <property name="text"> + <string>S&ubtract fee from amount</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="useAvailableBalanceButton"> + <property name="text"> + <string>Use available balance</string> + </property> + </widget> + </item> + </layout> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="messageLabel"> + <property name="text"> + <string>Message:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QLabel" name="messageTextLabel"> + <property name="toolTip"> + <string>A message that was attached to the bitcoin: URI which will be stored with the transaction for your reference. Note: This message will not be sent over the Bitcoin network.</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + </widget> + </item> + <item row="4" column="0" colspan="2"> + <widget class="Line" name="line"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + </layout> </widget> <customwidgets> <customwidget> @@ -1263,8 +223,9 @@ </customwidget> <customwidget> <class>BitcoinAmountField</class> - <extends>QLineEdit</extends> + <extends>QWidget</extends> <header>qt/bitcoinamountfield.h</header> + <container>1</container> </customwidget> </customwidgets> <tabstops> @@ -1274,10 +235,6 @@ <tabstop>deleteButton</tabstop> <tabstop>addAsLabel</tabstop> <tabstop>payAmount</tabstop> - <tabstop>payAmount_is</tabstop> - <tabstop>deleteButton_is</tabstop> - <tabstop>payAmount_s</tabstop> - <tabstop>deleteButton_s</tabstop> </tabstops> <resources> <include location="../bitcoin.qrc"/> diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index e3c6d8a624..186ed4f644 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -24,9 +24,6 @@ #include <util/time.h> #ifdef WIN32 -#ifndef NOMINMAX -#define NOMINMAX -#endif #include <shellapi.h> #include <shlobj.h> #include <shlwapi.h> diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index f4928951fe..4c8b33bf28 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -287,7 +287,7 @@ void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable ui->freeSpace->setText(""); } else { m_bytes_available = bytesAvailable; - if (ui->prune->isEnabled()) { + if (ui->prune->isEnabled() && !(gArgs.IsArgSet("-prune") && gArgs.GetIntArg("-prune", 0) == 0)) { ui->prune->setChecked(m_bytes_available < (m_blockchain_size_gb + m_chain_state_size_gb + 10) * GB_BYTES); } UpdateFreeSpaceLabel(); diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index 339ac580d8..af514d5a43 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -20,7 +20,7 @@ #include <QClipboard> SendCoinsEntry::SendCoinsEntry(const PlatformStyle *_platformStyle, QWidget *parent) : - QStackedWidget(parent), + QWidget(parent), ui(new Ui::SendCoinsEntry), model(nullptr), platformStyle(_platformStyle) @@ -30,25 +30,16 @@ SendCoinsEntry::SendCoinsEntry(const PlatformStyle *_platformStyle, QWidget *par ui->addressBookButton->setIcon(platformStyle->SingleColorIcon(":/icons/address-book")); ui->pasteButton->setIcon(platformStyle->SingleColorIcon(":/icons/editpaste")); ui->deleteButton->setIcon(platformStyle->SingleColorIcon(":/icons/remove")); - ui->deleteButton_is->setIcon(platformStyle->SingleColorIcon(":/icons/remove")); - ui->deleteButton_s->setIcon(platformStyle->SingleColorIcon(":/icons/remove")); - - setCurrentWidget(ui->SendCoins); if (platformStyle->getUseExtraSpacing()) ui->payToLayout->setSpacing(4); - // normal bitcoin address field GUIUtil::setupAddressWidget(ui->payTo, this); - // just a label for displaying bitcoin address(es) - ui->payTo_is->setFont(GUIUtil::fixedPitchFont()); // Connect signals connect(ui->payAmount, &BitcoinAmountField::valueChanged, this, &SendCoinsEntry::payAmountChanged); connect(ui->checkboxSubtractFeeFromAmount, &QCheckBox::toggled, this, &SendCoinsEntry::subtractFeeFromAmountChanged); connect(ui->deleteButton, &QPushButton::clicked, this, &SendCoinsEntry::deleteClicked); - connect(ui->deleteButton_is, &QPushButton::clicked, this, &SendCoinsEntry::deleteClicked); - connect(ui->deleteButton_s, &QPushButton::clicked, this, &SendCoinsEntry::deleteClicked); connect(ui->useAvailableBalanceButton, &QPushButton::clicked, this, &SendCoinsEntry::useAvailableBalanceClicked); } @@ -103,14 +94,6 @@ void SendCoinsEntry::clear() ui->messageTextLabel->clear(); ui->messageTextLabel->hide(); ui->messageLabel->hide(); - // clear UI elements for unauthenticated payment request - ui->payTo_is->clear(); - ui->memoTextLabel_is->clear(); - ui->payAmount_is->clear(); - // clear UI elements for authenticated payment request - ui->payTo_s->clear(); - ui->memoTextLabel_s->clear(); - ui->payAmount_s->clear(); // update the display unit, to not use the default ("BTC") updateDisplayUnit(); @@ -219,7 +202,7 @@ void SendCoinsEntry::setAmount(const CAmount &amount) bool SendCoinsEntry::isClear() { - return ui->payTo->text().isEmpty() && ui->payTo_is->text().isEmpty() && ui->payTo_s->text().isEmpty(); + return ui->payTo->text().isEmpty(); } void SendCoinsEntry::setFocus() @@ -229,12 +212,8 @@ void SendCoinsEntry::setFocus() void SendCoinsEntry::updateDisplayUnit() { - if(model && model->getOptionsModel()) - { - // Update payAmount with the current unit + if (model && model->getOptionsModel()) { ui->payAmount->setDisplayUnit(model->getOptionsModel()->getDisplayUnit()); - ui->payAmount_is->setDisplayUnit(model->getOptionsModel()->getDisplayUnit()); - ui->payAmount_s->setDisplayUnit(model->getOptionsModel()->getDisplayUnit()); } } @@ -244,11 +223,9 @@ void SendCoinsEntry::changeEvent(QEvent* e) ui->addressBookButton->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/address-book"))); ui->pasteButton->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/editpaste"))); ui->deleteButton->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/remove"))); - ui->deleteButton_is->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/remove"))); - ui->deleteButton_s->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/remove"))); } - QStackedWidget::changeEvent(e); + QWidget::changeEvent(e); } bool SendCoinsEntry::updateLabel(const QString &address) diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h index e8db1e3a5c..ea9d58fbf8 100644 --- a/src/qt/sendcoinsentry.h +++ b/src/qt/sendcoinsentry.h @@ -7,7 +7,7 @@ #include <qt/sendcoinsrecipient.h> -#include <QStackedWidget> +#include <QWidget> class WalletModel; class PlatformStyle; @@ -22,10 +22,8 @@ namespace Ui { /** * A single entry in the dialog for sending bitcoins. - * Stacked widget, with different UIs for payment requests - * with a strong payee identity. */ -class SendCoinsEntry : public QStackedWidget +class SendCoinsEntry : public QWidget { Q_OBJECT diff --git a/src/support/lockedpool.cpp b/src/support/lockedpool.cpp index b7ef479675..e48accf0a4 100644 --- a/src/support/lockedpool.cpp +++ b/src/support/lockedpool.cpp @@ -10,9 +10,6 @@ #endif #ifdef WIN32 -#ifndef NOMINMAX -#define NOMINMAX -#endif #include <windows.h> #else #include <sys/mman.h> // for mmap diff --git a/src/util/system.cpp b/src/util/system.cpp index 6e2638a5d6..1ae82fbc96 100644 --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -62,9 +62,6 @@ #pragma warning(disable:4717) #endif -#ifndef NOMINMAX -#define NOMINMAX -#endif #include <codecvt> #include <io.h> /* for _commit */ diff --git a/src/validation.cpp b/src/validation.cpp index 26ce286c63..b775c85912 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -81,12 +81,6 @@ using node::UnlinkPrunedFiles; #define MICRO 0.000001 #define MILLI 0.001 -/** - * An extra transaction can be added to a package, as long as it only has one - * ancestor and is no larger than this. Not really any reason to make this - * configurable as it doesn't materially change DoS parameters. - */ -static const unsigned int EXTRA_DESCENDANT_TX_SIZE_LIMIT = 10000; /** Maximum kilobytes for transactions to store for processing during reorg */ static const unsigned int MAX_DISCONNECTED_TX_POOL_SIZE = 20000; /** Time to wait between writing blocks/block index to disk. */ diff --git a/src/validation.h b/src/validation.h index 70b6c072e6..3b6cd509c6 100644 --- a/src/validation.h +++ b/src/validation.h @@ -21,6 +21,7 @@ #include <node/blockstorage.h> #include <policy/feerate.h> #include <policy/packages.h> +#include <policy/policy.h> #include <script/script_error.h> #include <sync.h> #include <txdb.h> @@ -58,24 +59,6 @@ namespace Consensus { struct Params; } // namespace Consensus -/** Default for -limitancestorcount, max number of in-mempool ancestors */ -static const unsigned int DEFAULT_ANCESTOR_LIMIT = 25; -/** Default for -limitancestorsize, maximum kilobytes of tx + all in-mempool ancestors */ -static const unsigned int DEFAULT_ANCESTOR_SIZE_LIMIT = 101; -/** Default for -limitdescendantcount, max number of in-mempool descendants */ -static const unsigned int DEFAULT_DESCENDANT_LIMIT = 25; -/** Default for -limitdescendantsize, maximum kilobytes of in-mempool descendants */ -static const unsigned int DEFAULT_DESCENDANT_SIZE_LIMIT = 101; - -// If a package is submitted, it must be within the mempool's ancestor/descendant limits. Since a -// submitted package must be child-with-unconfirmed-parents (all of the transactions are an ancestor -// of the child), package limits are ultimately bounded by mempool package limits. Ensure that the -// defaults reflect this constraint. -static_assert(DEFAULT_DESCENDANT_LIMIT >= MAX_PACKAGE_COUNT); -static_assert(DEFAULT_ANCESTOR_LIMIT >= MAX_PACKAGE_COUNT); -static_assert(DEFAULT_ANCESTOR_SIZE_LIMIT >= MAX_PACKAGE_SIZE); -static_assert(DEFAULT_DESCENDANT_SIZE_LIMIT >= MAX_PACKAGE_SIZE); - /** Default for -mempoolexpiry, expiration time for mempool transactions in hours */ static const unsigned int DEFAULT_MEMPOOL_EXPIRY = 336; /** Maximum number of dedicated script-checking threads allowed */ diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h index 65a5c83366..d08d3664c4 100644 --- a/src/wallet/coincontrol.h +++ b/src/wallet/coincontrol.h @@ -33,12 +33,11 @@ public: CTxDestination destChange = CNoDestination(); //! Override the default change type if set, ignored if destChange is set std::optional<OutputType> m_change_type; - //! If false, only selected inputs are used - bool m_add_inputs = true; //! If false, only safe inputs will be used bool m_include_unsafe_inputs = false; - //! If false, allows unselected inputs, but requires all selected inputs be used - bool fAllowOtherInputs = false; + //! If true, the selection process can add extra unselected inputs from the wallet + //! while requires all selected inputs be used + bool m_allow_other_inputs = false; //! Includes watch only addresses which are solvable bool fAllowWatchOnly = false; //! Override automatic min/max checks on fee, m_feerate must be set if true diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index afd2b83971..5e70ed4a30 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -213,7 +213,7 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo for (const auto& inputs : wtx.tx->vin) { new_coin_control.Select(COutPoint(inputs.prevout)); } - new_coin_control.fAllowOtherInputs = true; + new_coin_control.m_allow_other_inputs = true; // We cannot source new unconfirmed inputs(bip125 rule 2) new_coin_control.m_min_depth = 1; diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp index e1203817e0..f54b2c83d2 100644 --- a/src/wallet/interfaces.cpp +++ b/src/wallet/interfaces.cpp @@ -110,7 +110,7 @@ WalletTxOut MakeWalletTxOut(const CWallet& wallet, result.txout = wtx.tx->vout[n]; result.time = wtx.GetTxTime(); result.depth_in_main_chain = depth; - result.is_spent = wallet.IsSpent(wtx.GetHash(), n); + result.is_spent = wallet.IsSpent(COutPoint(wtx.GetHash(), n)); return result; } @@ -121,7 +121,7 @@ WalletTxOut MakeWalletTxOut(const CWallet& wallet, result.txout = output.txout; result.time = output.time; result.depth_in_main_chain = output.depth; - result.is_spent = wallet.IsSpent(output.outpoint.hash, output.outpoint.n); + result.is_spent = wallet.IsSpent(output.outpoint); return result; } @@ -245,7 +245,7 @@ public: bool isLockedCoin(const COutPoint& output) override { LOCK(m_wallet->cs_wallet); - return m_wallet->IsLockedCoin(output.hash, output.n); + return m_wallet->IsLockedCoin(output); } void listLockedCoins(std::vector<COutPoint>& outputs) override { diff --git a/src/wallet/receive.cpp b/src/wallet/receive.cpp index 8cce07b921..8de4017371 100644 --- a/src/wallet/receive.cpp +++ b/src/wallet/receive.cpp @@ -204,10 +204,9 @@ CAmount CachedTxGetAvailableCredit(const CWallet& wallet, const CWalletTx& wtx, bool allow_used_addresses = (filter & ISMINE_USED) || !wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE); CAmount nCredit = 0; uint256 hashTx = wtx.GetHash(); - for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) - { - if (!wallet.IsSpent(hashTx, i) && (allow_used_addresses || !wallet.IsSpentKey(hashTx, i))) { - const CTxOut &txout = wtx.tx->vout[i]; + for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) { + const CTxOut& txout = wtx.tx->vout[i]; + if (!wallet.IsSpent(COutPoint(hashTx, i)) && (allow_used_addresses || !wallet.IsSpentKey(txout.scriptPubKey))) { nCredit += OutputGetCredit(wallet, txout, filter); if (!MoneyRange(nCredit)) throw std::runtime_error(std::string(__func__) + " : value out of range"); @@ -371,15 +370,15 @@ std::map<CTxDestination, CAmount> GetAddressBalances(const CWallet& wallet) if (nDepth < (CachedTxIsFromMe(wallet, wtx, ISMINE_ALL) ? 0 : 1)) continue; - for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) - { + for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) { + const auto& output = wtx.tx->vout[i]; CTxDestination addr; - if (!wallet.IsMine(wtx.tx->vout[i])) + if (!wallet.IsMine(output)) continue; - if(!ExtractDestination(wtx.tx->vout[i].scriptPubKey, addr)) + if(!ExtractDestination(output.scriptPubKey, addr)) continue; - CAmount n = wallet.IsSpent(walletEntry.first, i) ? 0 : wtx.tx->vout[i].nValue; + CAmount n = wallet.IsSpent(COutPoint(walletEntry.first, i)) ? 0 : output.nValue; balances[addr] += n; } } diff --git a/src/wallet/rpc/coins.cpp b/src/wallet/rpc/coins.cpp index 2649fa586c..ad59cc94ff 100644 --- a/src/wallet/rpc/coins.cpp +++ b/src/wallet/rpc/coins.cpp @@ -341,11 +341,11 @@ RPCHelpMan lockunspent() throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout index out of bounds"); } - if (pwallet->IsSpent(outpt.hash, outpt.n)) { + if (pwallet->IsSpent(outpt)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected unspent output"); } - const bool is_locked = pwallet->IsLockedCoin(outpt.hash, outpt.n); + const bool is_locked = pwallet->IsLockedCoin(outpt); if (fUnlock && !is_locked) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected locked output"); @@ -638,7 +638,7 @@ RPCHelpMan listunspent() cctl.m_max_depth = nMaxDepth; cctl.m_include_unsafe_inputs = include_unsafe; LOCK(pwallet->cs_wallet); - AvailableCoinsListUnspent(*pwallet, vecOutputs, &cctl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount); + vecOutputs = AvailableCoinsListUnspent(*pwallet, &cctl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount).coins; } LOCK(pwallet->cs_wallet); @@ -649,7 +649,7 @@ RPCHelpMan listunspent() CTxDestination address; const CScript& scriptPubKey = out.txout.scriptPubKey; bool fValidAddress = ExtractDestination(scriptPubKey, address); - bool reused = avoid_reuse && pwallet->IsSpentKey(out.outpoint.hash, out.outpoint.n); + bool reused = avoid_reuse && pwallet->IsSpentKey(scriptPubKey); if (destinations.size() && (!fValidAddress || !destinations.count(address))) continue; diff --git a/src/wallet/rpc/spend.cpp b/src/wallet/rpc/spend.cpp index 2520cd2532..0e8cee5db8 100644 --- a/src/wallet/rpc/spend.cpp +++ b/src/wallet/rpc/spend.cpp @@ -535,8 +535,8 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out, }, true, true); - if (options.exists("add_inputs") ) { - coinControl.m_add_inputs = options["add_inputs"].get_bool(); + if (options.exists("add_inputs")) { + coinControl.m_allow_other_inputs = options["add_inputs"].get_bool(); } if (options.exists("changeAddress") || options.exists("change_address")) { @@ -823,7 +823,7 @@ RPCHelpMan fundrawtransaction() int change_position; CCoinControl coin_control; // Automatically select (additional) coins. Can be overridden by options.add_inputs. - coin_control.m_add_inputs = true; + coin_control.m_allow_other_inputs = true; FundTransaction(*pwallet, tx, fee, change_position, request.params[1], coin_control, /*override_min_fee=*/true); UniValue result(UniValue::VOBJ); @@ -1225,7 +1225,7 @@ RPCHelpMan send() CCoinControl coin_control; // Automatically select coins, unless at least one is manually selected. Can // be overridden by options.add_inputs. - coin_control.m_add_inputs = rawTx.vin.size() == 0; + coin_control.m_allow_other_inputs = rawTx.vin.size() == 0; SetOptionsInputWeights(options["inputs"], options); FundTransaction(*pwallet, rawTx, fee, change_position, options, coin_control, /*override_min_fee=*/false); @@ -1367,7 +1367,6 @@ RPCHelpMan sendall() CMutableTransaction rawTx{ConstructTransaction(options["inputs"], recipient_key_value_pairs, options["locktime"], rbf)}; LOCK(pwallet->cs_wallet); - std::vector<COutput> all_the_utxos; CAmount total_input_value(0); bool send_max{options.exists("send_max") ? options["send_max"].get_bool() : false}; @@ -1375,7 +1374,7 @@ RPCHelpMan sendall() throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot combine send_max with specific inputs."); } else if (options.exists("inputs")) { for (const CTxIn& input : rawTx.vin) { - if (pwallet->IsSpent(input.prevout.hash, input.prevout.n)) { + if (pwallet->IsSpent(input.prevout)) { throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Input not available. UTXO (%s:%d) was already spent.", input.prevout.hash.ToString(), input.prevout.n)); } const CWalletTx* tx{pwallet->GetWalletTx(input.prevout.hash)}; @@ -1385,8 +1384,7 @@ RPCHelpMan sendall() total_input_value += tx->tx->vout[input.prevout.n].nValue; } } else { - AvailableCoins(*pwallet, all_the_utxos, &coin_control, fee_rate, /*nMinimumAmount=*/0); - for (const COutput& output : all_the_utxos) { + for (const COutput& output : AvailableCoins(*pwallet, &coin_control, fee_rate, /*nMinimumAmount=*/0).coins) { CHECK_NONFATAL(output.input_bytes > 0); if (send_max && fee_rate.GetFee(output.input_bytes) > output.txout.nValue) { continue; @@ -1651,7 +1649,7 @@ RPCHelpMan walletcreatefundedpsbt() CCoinControl coin_control; // Automatically select coins, unless at least one is manually selected. Can // be overridden by options.add_inputs. - coin_control.m_add_inputs = rawTx.vin.size() == 0; + coin_control.m_allow_other_inputs = rawTx.vin.size() == 0; SetOptionsInputWeights(request.params[0], options); FundTransaction(wallet, rawTx, fee, change_position, options, coin_control, /*override_min_fee=*/true); diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp index ce99aeefe5..5799a9ff2a 100644 --- a/src/wallet/spend.cpp +++ b/src/wallet/spend.cpp @@ -84,12 +84,18 @@ TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *walle return CalculateMaximumSignedTxSize(tx, wallet, txouts, coin_control); } -void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl, std::optional<CFeeRate> feerate, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount) +CoinsResult AvailableCoins(const CWallet& wallet, + const CCoinControl* coinControl, + std::optional<CFeeRate> feerate, + const CAmount& nMinimumAmount, + const CAmount& nMaximumAmount, + const CAmount& nMinimumSumAmount, + const uint64_t nMaximumCount, + bool only_spendable) { AssertLockHeld(wallet.cs_wallet); - vCoins.clear(); - CAmount nTotal = 0; + CoinsResult result; // Either the WALLET_FLAG_AVOID_REUSE flag is not set (in which case we always allow), or we default to avoiding, and only in the case where // a coin control object is provided, and has the avoid address reuse flag set to false, do we allow already used addresses bool allow_used_addresses = !wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE) || (coinControl && !coinControl->m_avoid_address_reuse); @@ -159,76 +165,75 @@ void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const C bool tx_from_me = CachedTxIsFromMe(wallet, wtx, ISMINE_ALL); for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) { - // Only consider selected coins if add_inputs is false - if (coinControl && !coinControl->m_add_inputs && !coinControl->IsSelected(COutPoint(entry.first, i))) { - continue; - } + const CTxOut& output = wtx.tx->vout[i]; + const COutPoint outpoint(wtxid, i); - if (wtx.tx->vout[i].nValue < nMinimumAmount || wtx.tx->vout[i].nValue > nMaximumAmount) + if (output.nValue < nMinimumAmount || output.nValue > nMaximumAmount) continue; - if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs && !coinControl->IsSelected(COutPoint(entry.first, i))) + if (coinControl && coinControl->HasSelected() && !coinControl->m_allow_other_inputs && !coinControl->IsSelected(outpoint)) continue; - if (wallet.IsLockedCoin(entry.first, i)) + if (wallet.IsLockedCoin(outpoint)) continue; - if (wallet.IsSpent(wtxid, i)) + if (wallet.IsSpent(outpoint)) continue; - isminetype mine = wallet.IsMine(wtx.tx->vout[i]); + isminetype mine = wallet.IsMine(output); if (mine == ISMINE_NO) { continue; } - if (!allow_used_addresses && wallet.IsSpentKey(wtxid, i)) { + if (!allow_used_addresses && wallet.IsSpentKey(output.scriptPubKey)) { continue; } - std::unique_ptr<SigningProvider> provider = wallet.GetSolvingProvider(wtx.tx->vout[i].scriptPubKey); + std::unique_ptr<SigningProvider> provider = wallet.GetSolvingProvider(output.scriptPubKey); - bool solvable = provider ? IsSolvable(*provider, wtx.tx->vout[i].scriptPubKey) : false; + bool solvable = provider ? IsSolvable(*provider, output.scriptPubKey) : false; bool spendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable)); - int input_bytes = GetTxSpendSize(wallet, wtx, i, (coinControl && coinControl->fAllowWatchOnly)); - vCoins.emplace_back(COutPoint(wtx.GetHash(), i), wtx.tx->vout.at(i), nDepth, input_bytes, spendable, solvable, safeTx, wtx.GetTxTime(), tx_from_me, feerate); + // Filter by spendable outputs only + if (!spendable && only_spendable) continue; + + int input_bytes = GetTxSpendSize(wallet, wtx, i, (coinControl && coinControl->fAllowWatchOnly)); + result.coins.emplace_back(outpoint, output, nDepth, input_bytes, spendable, solvable, safeTx, wtx.GetTxTime(), tx_from_me, feerate); + result.total_amount += output.nValue; // Checks the sum amount of all UTXO's. if (nMinimumSumAmount != MAX_MONEY) { - nTotal += wtx.tx->vout[i].nValue; - - if (nTotal >= nMinimumSumAmount) { - return; + if (result.total_amount >= nMinimumSumAmount) { + return result; } } // Checks the maximum number of UTXO's. - if (nMaximumCount > 0 && vCoins.size() >= nMaximumCount) { - return; + if (nMaximumCount > 0 && result.coins.size() >= nMaximumCount) { + return result; } } } + + return result; } -void AvailableCoinsListUnspent(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount) +CoinsResult AvailableCoinsListUnspent(const CWallet& wallet, const CCoinControl* coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount) { - AvailableCoins(wallet, vCoins, coinControl, /*feerate=*/ std::nullopt, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount); + return AvailableCoins(wallet, coinControl, /*feerate=*/ std::nullopt, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, /*only_spendable=*/false); } CAmount GetAvailableBalance(const CWallet& wallet, const CCoinControl* coinControl) { LOCK(wallet.cs_wallet); - - CAmount balance = 0; - std::vector<COutput> vCoins; - AvailableCoinsListUnspent(wallet, vCoins, coinControl); - for (const COutput& out : vCoins) { - if (out.spendable) { - balance += out.txout.nValue; - } - } - return balance; + return AvailableCoins(wallet, coinControl, + /*feerate=*/ std::nullopt, + /*nMinimumAmount=*/ 1, + /*nMaximumAmount=*/ MAX_MONEY, + /*nMinimumSumAmount=*/ MAX_MONEY, + /*nMaximumCount=*/ 0 + ).total_amount; } const CTxOut& FindNonChangeParentOutput(const CWallet& wallet, const CTransaction& tx, int output) @@ -260,11 +265,8 @@ std::map<CTxDestination, std::vector<COutput>> ListCoins(const CWallet& wallet) AssertLockHeld(wallet.cs_wallet); std::map<CTxDestination, std::vector<COutput>> result; - std::vector<COutput> availableCoins; - - AvailableCoinsListUnspent(wallet, availableCoins); - for (const COutput& coin : availableCoins) { + for (const COutput& coin : AvailableCoinsListUnspent(wallet).coins) { CTxDestination address; if ((coin.spendable || (wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && coin.solvable)) && ExtractDestination(FindNonChangeParentOutput(wallet, coin.outpoint).scriptPubKey, address)) { @@ -431,23 +433,6 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, const std::vec OutputGroup preset_inputs(coin_selection_params); - // coin control -> return all selected outputs (we want all selected to go into the transaction for sure) - if (coin_control.HasSelected() && !coin_control.fAllowOtherInputs) - { - for (const COutput& out : vCoins) { - if (!out.spendable) continue; - /* Set ancestors and descendants to 0 as these don't matter for preset inputs as no actual selection is being done. - * positive_only is set to false because we want to include all preset inputs, even if they are dust. - */ - preset_inputs.Insert(out, /*ancestors=*/ 0, /*descendants=*/ 0, /*positive_only=*/ false); - } - SelectionResult result(nTargetValue, SelectionAlgorithm::MANUAL); - result.AddInput(preset_inputs); - if (result.GetSelectedValue() < nTargetValue) return std::nullopt; - result.ComputeAndSetWaste(coin_selection_params.m_cost_of_change); - return result; - } - // calculate value from preset inputs and store them std::set<COutPoint> preset_coins; @@ -456,15 +441,14 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, const std::vec for (const COutPoint& outpoint : vPresetInputs) { int input_bytes = -1; CTxOut txout; - std::map<uint256, CWalletTx>::const_iterator it = wallet.mapWallet.find(outpoint.hash); - if (it != wallet.mapWallet.end()) { - const CWalletTx& wtx = it->second; + auto ptr_wtx = wallet.GetWalletTx(outpoint.hash); + if (ptr_wtx) { // Clearly invalid input, fail - if (wtx.tx->vout.size() <= outpoint.n) { + if (ptr_wtx->tx->vout.size() <= outpoint.n) { return std::nullopt; } - input_bytes = GetTxSpendSize(wallet, wtx, outpoint.n, false); - txout = wtx.tx->vout.at(outpoint.n); + input_bytes = GetTxSpendSize(wallet, *ptr_wtx, outpoint.n, false); + txout = ptr_wtx->tx->vout.at(outpoint.n); } else { // The input is external. We did not find the tx in mapWallet. if (!coin_control.GetExternalOutput(outpoint, txout)) { @@ -495,6 +479,15 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, const std::vec preset_inputs.Insert(output, /*ancestors=*/ 0, /*descendants=*/ 0, /*positive_only=*/ false); } + // coin control -> return all selected outputs (we want all selected to go into the transaction for sure) + if (coin_control.HasSelected() && !coin_control.m_allow_other_inputs) { + SelectionResult result(nTargetValue, SelectionAlgorithm::MANUAL); + result.AddInput(preset_inputs); + if (result.GetSelectedValue() < nTargetValue) return std::nullopt; + result.ComputeAndSetWaste(coin_selection_params.m_cost_of_change); + return result; + } + // remove preset inputs from vCoins so that Coin Selection doesn't pick them. for (std::vector<COutput>::iterator it = vCoins.begin(); it != vCoins.end() && coin_control.HasSelected();) { @@ -791,11 +784,16 @@ static std::optional<CreatedTransactionResult> CreateTransactionInternal( CAmount selection_target = recipients_sum + not_input_fees; // Get available coins - std::vector<COutput> vAvailableCoins; - AvailableCoins(wallet, vAvailableCoins, &coin_control, coin_selection_params.m_effective_feerate, 1, MAX_MONEY, MAX_MONEY, 0); + auto res_available_coins = AvailableCoins(wallet, + &coin_control, + coin_selection_params.m_effective_feerate, + 1, /*nMinimumAmount*/ + MAX_MONEY, /*nMaximumAmount*/ + MAX_MONEY, /*nMinimumSumAmount*/ + 0); /*nMaximumCount*/ // Choose coins to use - std::optional<SelectionResult> result = SelectCoins(wallet, vAvailableCoins, /*nTargetValue=*/selection_target, coin_control, coin_selection_params); + std::optional<SelectionResult> result = SelectCoins(wallet, res_available_coins.coins, /*nTargetValue=*/selection_target, coin_control, coin_selection_params); if (!result) { error = _("Insufficient funds"); return std::nullopt; @@ -1021,8 +1019,6 @@ bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet, vecSend.push_back(recipient); } - coinControl.fAllowOtherInputs = true; - // Acquire the locks to prevent races to the new locked unspents between the // CreateTransaction call and LockCoin calls (when lockUnspents is true). LOCK(wallet.cs_wallet); diff --git a/src/wallet/spend.h b/src/wallet/spend.h index 988058a25a..cba42d6fae 100644 --- a/src/wallet/spend.h +++ b/src/wallet/spend.h @@ -34,16 +34,29 @@ struct TxSize { TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, const std::vector<CTxOut>& txouts, const CCoinControl* coin_control = nullptr); TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, const CCoinControl* coin_control = nullptr) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet); +struct CoinsResult { + std::vector<COutput> coins; + // Sum of all the coins amounts + CAmount total_amount{0}; +}; /** - * populate vCoins with vector of available COutputs. + * Return vector of available COutputs. + * By default, returns only the spendable coins. */ -void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl = nullptr, std::optional<CFeeRate> feerate = std::nullopt, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet); +CoinsResult AvailableCoins(const CWallet& wallet, + const CCoinControl* coinControl = nullptr, + std::optional<CFeeRate> feerate = std::nullopt, + const CAmount& nMinimumAmount = 1, + const CAmount& nMaximumAmount = MAX_MONEY, + const CAmount& nMinimumSumAmount = MAX_MONEY, + const uint64_t nMaximumCount = 0, + bool only_spendable = true) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet); /** * Wrapper function for AvailableCoins which skips the `feerate` parameter. Use this function * to list all available coins (e.g. listunspent RPC) while not intending to fund a transaction. */ -void AvailableCoinsListUnspent(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet); +CoinsResult AvailableCoinsListUnspent(const CWallet& wallet, const CCoinControl* coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet); CAmount GetAvailableBalance(const CWallet& wallet, const CCoinControl* coinControl = nullptr); diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp index 76f28917a4..d6f47e9954 100644 --- a/src/wallet/test/coinselector_tests.cpp +++ b/src/wallet/test/coinselector_tests.cpp @@ -336,7 +336,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test) add_coin(coins, *wallet, 3 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true); add_coin(coins, *wallet, 2 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true); CCoinControl coin_control; - coin_control.fAllowOtherInputs = true; + coin_control.m_allow_other_inputs = true; coin_control.Select(coins.at(0).outpoint); coin_selection_params_bnb.m_effective_feerate = CFeeRate(0); const auto result10 = SelectCoins(*wallet, coins, 10 * CENT, coin_control, coin_selection_params_bnb); @@ -392,7 +392,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test) expected_result.Clear(); add_coin(9 * CENT, 2, expected_result); add_coin(1 * CENT, 2, expected_result); - coin_control.fAllowOtherInputs = true; + coin_control.m_allow_other_inputs = true; coin_control.Select(coins.at(1).outpoint); // pre select 9 coin const auto result13 = SelectCoins(*wallet, coins, 10 * CENT, coin_control, coin_selection_params_bnb); BOOST_CHECK(EquivalentResult(expected_result, *result13)); diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 70863f5464..0c03b2f4a2 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -583,9 +583,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoinsTest, ListCoinsTestingSetup) // Lock both coins. Confirm number of available coins drops to 0. { LOCK(wallet->cs_wallet); - std::vector<COutput> available; - AvailableCoinsListUnspent(*wallet, available); - BOOST_CHECK_EQUAL(available.size(), 2U); + BOOST_CHECK_EQUAL(AvailableCoinsListUnspent(*wallet).coins.size(), 2U); } for (const auto& group : list) { for (const auto& coin : group.second) { @@ -595,9 +593,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoinsTest, ListCoinsTestingSetup) } { LOCK(wallet->cs_wallet); - std::vector<COutput> available; - AvailableCoinsListUnspent(*wallet, available); - BOOST_CHECK_EQUAL(available.size(), 0U); + BOOST_CHECK_EQUAL(AvailableCoinsListUnspent(*wallet).coins.size(), 0U); } // Confirm ListCoins still returns same result as before, despite coins // being locked. diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 910562e669..ea306183ef 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -629,14 +629,12 @@ void CWallet::SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator> ran * Outpoint is spent if any non-conflicted transaction * spends it: */ -bool CWallet::IsSpent(const uint256& hash, unsigned int n) const +bool CWallet::IsSpent(const COutPoint& outpoint) const { - const COutPoint outpoint(hash, n); std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range; range = mapTxSpends.equal_range(outpoint); - for (TxSpends::const_iterator it = range.first; it != range.second; ++it) - { + for (TxSpends::const_iterator it = range.first; it != range.second; ++it) { const uint256& wtxid = it->second; std::map<uint256, CWalletTx>::const_iterator mit = mapWallet.find(wtxid); if (mit != mapWallet.end()) { @@ -908,35 +906,31 @@ void CWallet::SetSpentKeyState(WalletBatch& batch, const uint256& hash, unsigned } } -bool CWallet::IsSpentKey(const uint256& hash, unsigned int n) const +bool CWallet::IsSpentKey(const CScript& scriptPubKey) const { AssertLockHeld(cs_wallet); - const CWalletTx* srctx = GetWalletTx(hash); - if (srctx) { - assert(srctx->tx->vout.size() > n); - CTxDestination dest; - if (!ExtractDestination(srctx->tx->vout[n].scriptPubKey, dest)) { - return false; - } - if (IsAddressUsed(dest)) { - return true; - } - if (IsLegacy()) { - LegacyScriptPubKeyMan* spk_man = GetLegacyScriptPubKeyMan(); - assert(spk_man != nullptr); - for (const auto& keyid : GetAffectedKeys(srctx->tx->vout[n].scriptPubKey, *spk_man)) { - WitnessV0KeyHash wpkh_dest(keyid); - if (IsAddressUsed(wpkh_dest)) { - return true; - } - ScriptHash sh_wpkh_dest(GetScriptForDestination(wpkh_dest)); - if (IsAddressUsed(sh_wpkh_dest)) { - return true; - } - PKHash pkh_dest(keyid); - if (IsAddressUsed(pkh_dest)) { - return true; - } + CTxDestination dest; + if (!ExtractDestination(scriptPubKey, dest)) { + return false; + } + if (IsAddressUsed(dest)) { + return true; + } + if (IsLegacy()) { + LegacyScriptPubKeyMan* spk_man = GetLegacyScriptPubKeyMan(); + assert(spk_man != nullptr); + for (const auto& keyid : GetAffectedKeys(scriptPubKey, *spk_man)) { + WitnessV0KeyHash wpkh_dest(keyid); + if (IsAddressUsed(wpkh_dest)) { + return true; + } + ScriptHash sh_wpkh_dest(GetScriptForDestination(wpkh_dest)); + if (IsAddressUsed(sh_wpkh_dest)) { + return true; + } + PKHash pkh_dest(keyid); + if (IsAddressUsed(pkh_dest)) { + return true; } } } @@ -2450,12 +2444,10 @@ bool CWallet::UnlockAllCoins() return success; } -bool CWallet::IsLockedCoin(uint256 hash, unsigned int n) const +bool CWallet::IsLockedCoin(const COutPoint& output) const { AssertLockHeld(cs_wallet); - COutPoint outpt(hash, n); - - return (setLockedCoins.count(outpt) > 0); + return setLockedCoins.count(output) > 0; } void CWallet::ListLockedCoins(std::vector<COutPoint>& vOutpts) const diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 7da601c3b7..a75b6981e1 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -441,16 +441,16 @@ public: //! check whether we support the named feature bool CanSupportFeature(enum WalletFeature wf) const override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); return IsFeatureSupported(nWalletVersion, wf); } - bool IsSpent(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + bool IsSpent(const COutPoint& outpoint) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - // Whether this or any known UTXO with the same single key has been spent. - bool IsSpentKey(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + // Whether this or any known scriptPubKey with the same single key has been spent. + bool IsSpentKey(const CScript& scriptPubKey) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void SetSpentKeyState(WalletBatch& batch, const uint256& hash, unsigned int n, bool used, std::set<CTxDestination>& tx_destinations) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); /** Display address on an external signer. Returns false if external signer support is not compiled */ bool DisplayAddress(const CTxDestination& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - bool IsLockedCoin(uint256 hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + bool IsLockedCoin(const COutPoint& output) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool LockCoin(const COutPoint& output, WalletBatch* batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool UnlockCoin(const COutPoint& output, WalletBatch* batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool UnlockAllCoins() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); diff --git a/test/functional/rpc_createmultisig.py b/test/functional/rpc_createmultisig.py index 716ee8f7ef..1ad3fc24c9 100755 --- a/test/functional/rpc_createmultisig.py +++ b/test/functional/rpc_createmultisig.py @@ -43,7 +43,7 @@ class RpcCreateMultiSigTest(BitcoinTestFramework): if self.is_bdb_compiled(): self.final = node2.getnewaddress() else: - self.final = getnewdestination()[2] + self.final = getnewdestination('bech32')[2] def run_test(self): node0, node1, node2 = self.nodes @@ -66,9 +66,7 @@ class RpcCreateMultiSigTest(BitcoinTestFramework): # Test mixed compressed and uncompressed pubkeys self.log.info('Mixed compressed and uncompressed multisigs are not allowed') - pk0 = getnewdestination()[0].hex() - pk1 = getnewdestination()[0].hex() - pk2 = getnewdestination()[0].hex() + pk0, pk1, pk2 = [getnewdestination('bech32')[0].hex() for _ in range(3)] # decompress pk2 pk_obj = ECPubKey() diff --git a/test/functional/test_framework/address.py b/test/functional/test_framework/address.py index fcea24655b..92244b5ed8 100644 --- a/test/functional/test_framework/address.py +++ b/test/functional/test_framework/address.py @@ -47,8 +47,7 @@ def create_deterministic_address_bcrt1_p2tr_op_true(): Returns a tuple with the generated address and the internal key. """ internal_key = (1).to_bytes(32, 'big') - scriptPubKey = taproot_construct(internal_key, [(None, CScript([OP_TRUE]))]).scriptPubKey - address = encode_segwit_address("bcrt", 1, scriptPubKey[2:]) + address = output_key_to_p2tr(taproot_construct(internal_key, [(None, CScript([OP_TRUE]))]).output_pubkey) assert_equal(address, 'bcrt1p9yfmy5h72durp7zrhlw9lf7jpwjgvwdg0jr0lqmmjtgg83266lqsekaqka') return (address, internal_key) @@ -141,6 +140,10 @@ def script_to_p2sh_p2wsh(script, main=False): p2shscript = CScript([OP_0, sha256(script)]) return script_to_p2sh(p2shscript, main) +def output_key_to_p2tr(key, main=False): + assert len(key) == 32 + return program_to_witness(1, key, main) + def check_key(key): if (type(key) is str): key = bytes.fromhex(key) # Assuming this is hex string diff --git a/test/functional/test_framework/script_util.py b/test/functional/test_framework/script_util.py index f7d8422eee..b114002145 100755 --- a/test/functional/test_framework/script_util.py +++ b/test/functional/test_framework/script_util.py @@ -105,6 +105,11 @@ def script_to_p2sh_p2wsh_script(script): return script_to_p2sh_script(p2shscript) +def output_key_to_p2tr_script(key): + assert len(key) == 32 + return program_to_witness_script(1, key) + + def check_key(key): if isinstance(key, str): key = bytes.fromhex(key) # Assuming this is hex string diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py index 93f3ea9e81..7249e53676 100644 --- a/test/functional/test_framework/wallet.py +++ b/test/functional/test_framework/wallet.py @@ -19,9 +19,13 @@ from test_framework.address import ( key_to_p2pkh, key_to_p2sh_p2wpkh, key_to_p2wpkh, + output_key_to_p2tr, ) from test_framework.descriptors import descsum_create -from test_framework.key import ECKey +from test_framework.key import ( + ECKey, + compute_xonly_pubkey, +) from test_framework.messages import ( COIN, COutPoint, @@ -38,6 +42,7 @@ from test_framework.script import ( OP_NOP, OP_TRUE, SIGHASH_ALL, + taproot_construct, ) from test_framework.script_util import ( key_to_p2pk_script, @@ -81,8 +86,7 @@ class MiniWallet: def __init__(self, test_node, *, mode=MiniWalletMode.ADDRESS_OP_TRUE): self._test_node = test_node self._utxos = [] - self._priv_key = None - self._address = None + self._mode = mode assert isinstance(mode, MiniWalletMode) if mode == MiniWalletMode.RAW_OP_TRUE: @@ -116,7 +120,7 @@ class MiniWallet: def sign_tx(self, tx, fixed_length=True): """Sign tx that has been created by MiniWallet in P2PK mode""" - assert self._priv_key is not None + assert_equal(self._mode, MiniWalletMode.RAW_P2PK) (sighash, err) = LegacySignatureHash(CScript(self._scriptPubKey), tx, 0, SIGHASH_ALL) assert err is None # for exact fee calculation, create only signatures with fixed size by default (>49.89% probability): @@ -146,6 +150,7 @@ class MiniWallet: return descsum_create(f'raw({self._scriptPubKey.hex()})') def get_address(self): + assert_equal(self._mode, MiniWalletMode.ADDRESS_OP_TRUE) return self._address def get_utxo(self, *, txid: str = '', vout: Optional[int] = None, mark_as_spent=True) -> dict: @@ -252,10 +257,12 @@ class MiniWallet: """Create and return a tx with the specified fee_rate. Fee may be exact or at most one satoshi higher than needed.""" from_node = from_node or self._test_node utxo_to_spend = utxo_to_spend or self.get_utxo() - if self._priv_key is None: + if self._mode in (MiniWalletMode.RAW_OP_TRUE, MiniWalletMode.ADDRESS_OP_TRUE): vsize = Decimal(104) # anyone-can-spend - else: + elif self._mode == MiniWalletMode.RAW_P2PK: vsize = Decimal(168) # P2PK (73 bytes scriptSig + 35 bytes scriptPubKey + 60 bytes other) + else: + assert False send_value = int(COIN * (utxo_to_spend['value'] - fee_rate * (vsize / 1000))) assert send_value > 0 @@ -263,17 +270,15 @@ class MiniWallet: tx.vin = [CTxIn(COutPoint(int(utxo_to_spend['txid'], 16), utxo_to_spend['vout']), nSequence=sequence)] tx.vout = [CTxOut(send_value, self._scriptPubKey)] tx.nLockTime = locktime - if not self._address: - # raw script - if self._priv_key is not None: - # P2PK, need to sign - self.sign_tx(tx) - else: - # anyone-can-spend - tx.vin[0].scriptSig = CScript([OP_NOP] * 43) # pad to identical size - else: + if self._mode == MiniWalletMode.RAW_P2PK: + self.sign_tx(tx) + elif self._mode == MiniWalletMode.RAW_OP_TRUE: + tx.vin[0].scriptSig = CScript([OP_NOP] * 43) # pad to identical size + elif self._mode == MiniWalletMode.ADDRESS_OP_TRUE: tx.wit.vtxinwit = [CTxInWitness()] tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE]), bytes([LEAF_VERSION_TAPSCRIPT]) + self._internal_key] + else: + assert False tx_hex = tx.serialize().hex() assert_equal(tx.get_vsize(), vsize) @@ -286,10 +291,10 @@ class MiniWallet: return txid -def getnewdestination(address_type='bech32'): +def getnewdestination(address_type='bech32m'): """Generate a random destination of the specified type and return the corresponding public key, scriptPubKey and address. Supported types are - 'legacy', 'p2sh-segwit' and 'bech32'. Can be used when a random + 'legacy', 'p2sh-segwit', 'bech32' and 'bech32m'. Can be used when a random destination is needed, but no compiled wallet is available (e.g. as replacement to the getnewaddress/getaddressinfo RPCs).""" key = ECKey() @@ -304,7 +309,11 @@ def getnewdestination(address_type='bech32'): elif address_type == 'bech32': scriptpubkey = key_to_p2wpkh_script(pubkey) address = key_to_p2wpkh(pubkey) - # TODO: also support bech32m (need to generate x-only-pubkey) + elif address_type == 'bech32m': + tap = taproot_construct(compute_xonly_pubkey(key.get_bytes())[0]) + pubkey = tap.output_pubkey + scriptpubkey = tap.scriptPubKey + address = output_key_to_p2tr(pubkey) else: assert False return pubkey, scriptpubkey, address diff --git a/test/functional/wallet_taproot.py b/test/functional/wallet_taproot.py index a4d836c8fe..d238c50bca 100755 --- a/test/functional/wallet_taproot.py +++ b/test/functional/wallet_taproot.py @@ -7,19 +7,18 @@ import random from decimal import Decimal +from test_framework.address import output_key_to_p2tr from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal from test_framework.descriptors import descsum_create from test_framework.script import ( CScript, MAX_PUBKEYS_PER_MULTI_A, - OP_1, OP_CHECKSIG, OP_CHECKSIGADD, OP_NUMEQUAL, taproot_construct, ) -from test_framework.segwit_addr import encode_segwit_address # xprvs/xpubs, and m/* derived x-only pubkeys (created using independent implementation) KEYS = [ @@ -183,10 +182,7 @@ def multi_a(k, hex_keys, sort=False): def compute_taproot_address(pubkey, scripts): """Compute the address for a taproot output with given inner key and scripts.""" - tap = taproot_construct(pubkey, scripts) - assert tap.scriptPubKey[0] == OP_1 - assert tap.scriptPubKey[1] == 0x20 - return encode_segwit_address("bcrt", 1, tap.scriptPubKey[2:]) + return output_key_to_p2tr(taproot_construct(pubkey, scripts).output_pubkey) class WalletTaprootTest(BitcoinTestFramework): """Test generation and spending of P2TR address outputs.""" |