aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--COPYING4
-rw-r--r--configure.ac10
-rw-r--r--contrib/debian/copyright2
-rw-r--r--doc/bips.md3
-rw-r--r--doc/release-notes/release-notes-0.15.0.md4
-rw-r--r--src/compat.h2
-rw-r--r--src/keystore.cpp57
-rw-r--r--src/keystore.h5
-rw-r--r--src/policy/policy.h36
-rw-r--r--src/qt/addresstablemodel.cpp3
-rw-r--r--src/qt/bitcoingui.cpp6
-rw-r--r--src/qt/bitcoinunits.cpp21
-rw-r--r--src/qt/bitcoinunits.h4
-rw-r--r--src/qt/paymentserver.cpp35
-rw-r--r--src/qt/recentrequeststablemodel.cpp2
-rw-r--r--src/qt/sendcoinsdialog.cpp2
-rw-r--r--src/qt/test/wallettests.cpp5
-rw-r--r--src/qt/transactionview.cpp2
-rw-r--r--src/rpc/blockchain.cpp4
-rw-r--r--src/rpc/misc.cpp92
-rw-r--r--src/script/sign.cpp19
-rw-r--r--src/script/sign.h6
-rw-r--r--src/script/standard.cpp11
-rw-r--r--src/script/standard.h22
-rw-r--r--src/test/data/tx_valid.json6
-rw-r--r--src/test/mempool_tests.cpp29
-rw-r--r--src/test/script_standard_tests.cpp7
-rw-r--r--src/test/util_tests.cpp2
-rw-r--r--src/txmempool.cpp4
-rw-r--r--src/txmempool.h8
-rw-r--r--src/wallet/crypter.cpp1
-rw-r--r--src/wallet/init.cpp12
-rw-r--r--src/wallet/rpcdump.cpp18
-rw-r--r--src/wallet/rpcwallet.cpp82
-rw-r--r--src/wallet/test/wallet_test_fixture.cpp2
-rw-r--r--src/wallet/test/wallet_tests.cpp2
-rw-r--r--src/wallet/wallet.cpp123
-rw-r--r--src/wallet/wallet.h47
-rw-r--r--test/functional/README.md18
-rwxr-xr-xtest/functional/address_types.py199
-rwxr-xr-xtest/functional/bip68-112-113-p2p.py2
-rwxr-xr-xtest/functional/bip68-sequence.py3
-rwxr-xr-xtest/functional/bumpfee.py5
-rwxr-xr-xtest/functional/create_cache.py1
-rwxr-xr-xtest/functional/import-rescan.py2
-rwxr-xr-xtest/functional/importmulti.py1
-rwxr-xr-xtest/functional/mempool_limit.py12
-rwxr-xr-xtest/functional/multiwallet.py48
-rwxr-xr-xtest/functional/nulldummy.py2
-rwxr-xr-xtest/functional/p2p-fullblocktest.py7
-rwxr-xr-xtest/functional/p2p-segwit.py16
-rwxr-xr-xtest/functional/prioritise_transaction.py19
-rwxr-xr-xtest/functional/rawtransactions.py5
-rwxr-xr-xtest/functional/segwit.py102
-rwxr-xr-xtest/functional/signmessages.py1
-rw-r--r--test/functional/test_framework/blocktools.py64
-rw-r--r--test/functional/test_framework/messages.py25
-rw-r--r--test/functional/test_framework/script.py2
-rwxr-xr-xtest/functional/test_framework/test_framework.py7
-rwxr-xr-xtest/functional/test_framework/test_node.py68
-rwxr-xr-xtest/functional/test_runner.py26
-rwxr-xr-xtest/functional/txn_clone.py15
-rwxr-xr-xtest/functional/wallet-dump.py2
-rwxr-xr-xtest/functional/wallet.py14
64 files changed, 1020 insertions, 346 deletions
diff --git a/COPYING b/COPYING
index 45d51c3c78..9704b9c94c 100644
--- a/COPYING
+++ b/COPYING
@@ -1,7 +1,7 @@
The MIT License (MIT)
-Copyright (c) 2009-2017 The Bitcoin Core developers
-Copyright (c) 2009-2017 Bitcoin Developers
+Copyright (c) 2009-2018 The Bitcoin Core developers
+Copyright (c) 2009-2018 Bitcoin Developers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/configure.ac b/configure.ac
index f7de4c9f9a..256aed40b2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -5,7 +5,7 @@ define(_CLIENT_VERSION_MINOR, 15)
define(_CLIENT_VERSION_REVISION, 99)
define(_CLIENT_VERSION_BUILD, 0)
define(_CLIENT_VERSION_IS_RELEASE, false)
-define(_COPYRIGHT_YEAR, 2017)
+define(_COPYRIGHT_YEAR, 2018)
define(_COPYRIGHT_HOLDERS,[The %s developers])
define(_COPYRIGHT_HOLDERS_SUBSTITUTION,[[Bitcoin Core]])
AC_INIT([Bitcoin Core],[_CLIENT_VERSION_MAJOR._CLIENT_VERSION_MINOR._CLIENT_VERSION_REVISION],[https://github.com/bitcoin/bitcoin/issues],[bitcoin],[https://bitcoincore.org/])
@@ -164,7 +164,7 @@ AC_ARG_ENABLE([lcov],
[enable lcov testing (default is no)])],
[use_lcov=$enableval],
[use_lcov=no])
-
+
AC_ARG_ENABLE([lcov-branch-coverage],
[AS_HELP_STRING([--enable-lcov-branch-coverage],
[enable lcov testing branch coverage (default is no)])],
@@ -1313,7 +1313,7 @@ case ${OS} in
;;
esac
-echo
+echo
echo "Options used to compile and link:"
echo " with wallet = $enable_wallet"
echo " with gui / qt = $bitcoin_enable_qt"
@@ -1328,7 +1328,7 @@ echo " with upnp = $use_upnp"
echo " use asm = $use_asm"
echo " debug enabled = $enable_debug"
echo " werror = $enable_werror"
-echo
+echo
echo " target os = $TARGET_OS"
echo " build os = $BUILD_OS"
echo
@@ -1339,4 +1339,4 @@ echo " CXX = $CXX"
echo " CXXFLAGS = $CXXFLAGS"
echo " LDFLAGS = $LDFLAGS"
echo " ARFLAGS = $ARFLAGS"
-echo
+echo
diff --git a/contrib/debian/copyright b/contrib/debian/copyright
index 8e715bd9d8..c6484157a5 100644
--- a/contrib/debian/copyright
+++ b/contrib/debian/copyright
@@ -5,7 +5,7 @@ Upstream-Contact: Satoshi Nakamoto <satoshin@gmx.com>
Source: https://github.com/bitcoin/bitcoin
Files: *
-Copyright: 2009-2017, Bitcoin Core Developers
+Copyright: 2009-2018, Bitcoin Core Developers
License: Expat
Comment: The Bitcoin Core Developers encompasses the current developers listed on bitcoin.org,
as well as the numerous contributors to the project.
diff --git a/doc/bips.md b/doc/bips.md
index fbff94a329..e587275f0f 100644
--- a/doc/bips.md
+++ b/doc/bips.md
@@ -33,4 +33,5 @@ BIPs that are implemented by Bitcoin Core (up-to-date up to **v0.13.0**):
* [`BIP 145`](https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki): getblocktemplate updates for Segregated Witness as of **v0.13.0** ([PR 8149](https://github.com/bitcoin/bitcoin/pull/8149)).
* [`BIP 147`](https://github.com/bitcoin/bips/blob/master/bip-0147.mediawiki): NULLDUMMY softfork as of **v0.13.1** ([PR 8636](https://github.com/bitcoin/bitcoin/pull/8636) and [PR 8937](https://github.com/bitcoin/bitcoin/pull/8937)).
* [`BIP 152`](https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki): Compact block transfer and related optimizations are used as of **v0.13.0** ([PR 8068](https://github.com/bitcoin/bitcoin/pull/8068)).
-* [`BIP 159`](https://github.com/bitcoin/bips/blob/master/bip-0159.mediawiki): NODE_NETWORK_LIMITED service bit [signaling only] is supported as of **v0.16.0** ([PR 10740](https://github.com/bitcoin/bitcoin/pull/10740)).
+* [`BIP 159`](https://github.com/bitcoin/bips/blob/master/bip-0159.mediawiki): NODE_NETWORK_LIMITED service bit [signaling only] is supported as of **v0.16.0** ([PR 11740](https://github.com/bitcoin/bitcoin/pull/11740)).
+* [`BIP 176`](https://github.com/bitcoin/bips/blob/master/bip-0176.mediawiki): Bits Denomination [QT only] is supported as of **v0.16.0** ([PR 12035](https://github.com/bitcoin/bitcoin/pull/12035)).
diff --git a/doc/release-notes/release-notes-0.15.0.md b/doc/release-notes/release-notes-0.15.0.md
index 29816cacf7..3d000e94d5 100644
--- a/doc/release-notes/release-notes-0.15.0.md
+++ b/doc/release-notes/release-notes-0.15.0.md
@@ -151,8 +151,8 @@ Multi-wallet is enabled by using more than one `-wallet` argument when starting
Bitcoin Core 0.15.0 contains the following changes to the RPC interface and `bitcoin-cli` for multi-wallet:
* When running Bitcoin Core with a single wallet, there are **no** changes to the RPC interface or `bitcoin-cli`. All RPC calls and `bitcoin-cli` commands continue to work as before.
-* When running Bitcoin Core with multi-wallet, all *node-level* RPC methods continue to work as before. HTTP RPC requests should be send to the normal `<RPC IP address>:<RPC port>/` endpoint, and `bitcoin-cli` commands should be run as before. A *node-level* RPC method is any method which does not require access to the wallet.
-* When running Bitcoin Core with multi-wallet, *wallet-level* RPC methods must specify the wallet for which they're intended in every request. HTTP RPC requests should be send to the `<RPC IP address>:<RPC port>/wallet/<wallet name>/` endpoint, for example `127.0.0.1:8332/wallet/wallet1.dat/`. `bitcoin-cli` commands should be run with a `-rpcwallet` option, for example `bitcoin-cli -rpcwallet=wallet1.dat getbalance`.
+* When running Bitcoin Core with multi-wallet, all *node-level* RPC methods continue to work as before. HTTP RPC requests should be send to the normal `<RPC IP address>:<RPC port>` endpoint, and `bitcoin-cli` commands should be run as before. A *node-level* RPC method is any method which does not require access to the wallet.
+* When running Bitcoin Core with multi-wallet, *wallet-level* RPC methods must specify the wallet for which they're intended in every request. HTTP RPC requests should be send to the `<RPC IP address>:<RPC port>/wallet/<wallet name>` endpoint, for example `127.0.0.1:8332/wallet/wallet1.dat`. `bitcoin-cli` commands should be run with a `-rpcwallet` option, for example `bitcoin-cli -rpcwallet=wallet1.dat getbalance`.
* A new *node-level* `listwallets` RPC method is added to display which wallets are currently loaded. The names returned by this method are the same as those used in the HTTP endpoint and for the `rpcwallet` argument.
Note that while multi-wallet is now fully supported, the RPC multi-wallet interface should be considered unstable for version 0.15.0, and there may backwards-incompatible changes in future versions.
diff --git a/src/compat.h b/src/compat.h
index 65e9683e2f..aae84b1181 100644
--- a/src/compat.h
+++ b/src/compat.h
@@ -33,7 +33,7 @@
#include <ws2tcpip.h>
#include <stdint.h>
#else
-#include <sys/fcntl.h>
+#include <fcntl.h>
#include <sys/mman.h>
#include <sys/select.h>
#include <sys/socket.h>
diff --git a/src/keystore.cpp b/src/keystore.cpp
index 55c9656438..fab1b81c9a 100644
--- a/src/keystore.cpp
+++ b/src/keystore.cpp
@@ -11,6 +11,31 @@ bool CKeyStore::AddKey(const CKey &key) {
return AddKeyPubKey(key, key.GetPubKey());
}
+void CBasicKeyStore::ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey)
+{
+ AssertLockHeld(cs_KeyStore);
+ CKeyID key_id = pubkey.GetID();
+ // We must actually know about this key already.
+ assert(HaveKey(key_id) || mapWatchKeys.count(key_id));
+ // This adds the redeemscripts necessary to detect P2WPKH and P2SH-P2WPKH
+ // outputs. Technically P2WPKH outputs don't have a redeemscript to be
+ // spent. However, our current IsMine logic requires the corresponding
+ // P2SH-P2WPKH redeemscript to be present in the wallet in order to accept
+ // payment even to P2WPKH outputs.
+ // Also note that having superfluous scripts in the keystore never hurts.
+ // They're only used to guide recursion in signing and IsMine logic - if
+ // a script is present but we can't do anything with it, it has no effect.
+ // "Implicitly" refers to fact that scripts are derived automatically from
+ // existing keys, and are present in memory, even without being explicitly
+ // loaded (e.g. from a file).
+ if (pubkey.IsCompressed()) {
+ CScript script = GetScriptForDestination(WitnessV0KeyHash(key_id));
+ // This does not use AddCScript, as it may be overridden.
+ CScriptID id(script);
+ mapScripts[id] = std::move(script);
+ }
+}
+
bool CBasicKeyStore::GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const
{
CKey key;
@@ -31,6 +56,7 @@ bool CBasicKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey)
{
LOCK(cs_KeyStore);
mapKeys[pubkey.GetID()] = key;
+ ImplicitlyLearnRelatedKeyScripts(pubkey);
return true;
}
@@ -120,8 +146,10 @@ bool CBasicKeyStore::AddWatchOnly(const CScript &dest)
LOCK(cs_KeyStore);
setWatchOnly.insert(dest);
CPubKey pubKey;
- if (ExtractPubKey(dest, pubKey))
+ if (ExtractPubKey(dest, pubKey)) {
mapWatchKeys[pubKey.GetID()] = pubKey;
+ ImplicitlyLearnRelatedKeyScripts(pubKey);
+ }
return true;
}
@@ -130,8 +158,11 @@ bool CBasicKeyStore::RemoveWatchOnly(const CScript &dest)
LOCK(cs_KeyStore);
setWatchOnly.erase(dest);
CPubKey pubKey;
- if (ExtractPubKey(dest, pubKey))
+ if (ExtractPubKey(dest, pubKey)) {
mapWatchKeys.erase(pubKey.GetID());
+ }
+ // Related CScripts are not removed; having superfluous scripts around is
+ // harmless (see comment in ImplicitlyLearnRelatedKeyScripts).
return true;
}
@@ -146,3 +177,25 @@ bool CBasicKeyStore::HaveWatchOnly() const
LOCK(cs_KeyStore);
return (!setWatchOnly.empty());
}
+
+CKeyID GetKeyForDestination(const CKeyStore& store, const CTxDestination& dest)
+{
+ // Only supports destinations which map to single public keys, i.e. P2PKH,
+ // P2WPKH, and P2SH-P2WPKH.
+ if (auto id = boost::get<CKeyID>(&dest)) {
+ return *id;
+ }
+ if (auto witness_id = boost::get<WitnessV0KeyHash>(&dest)) {
+ return CKeyID(*witness_id);
+ }
+ if (auto script_id = boost::get<CScriptID>(&dest)) {
+ CScript script;
+ CTxDestination inner_dest;
+ if (store.GetCScript(*script_id, script) && ExtractDestination(script, inner_dest)) {
+ if (auto inner_witness_id = boost::get<WitnessV0KeyHash>(&inner_dest)) {
+ return CKeyID(*inner_witness_id);
+ }
+ }
+ }
+ return CKeyID();
+}
diff --git a/src/keystore.h b/src/keystore.h
index 8ce6a076c5..ffd3238fd6 100644
--- a/src/keystore.h
+++ b/src/keystore.h
@@ -60,6 +60,8 @@ protected:
ScriptMap mapScripts;
WatchOnlySet setWatchOnly;
+ void ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey);
+
public:
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override;
bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override;
@@ -80,4 +82,7 @@ public:
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial;
typedef std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char> > > CryptedKeyMap;
+/** Return the CKeyID of the key involved in a script (if there is a unique one). */
+CKeyID GetKeyForDestination(const CKeyStore& store, const CTxDestination& dest);
+
#endif // BITCOIN_KEYSTORE_H
diff --git a/src/policy/policy.h b/src/policy/policy.h
index d670f2d8f0..3d96406bbc 100644
--- a/src/policy/policy.h
+++ b/src/policy/policy.h
@@ -49,28 +49,28 @@ static const unsigned int DUST_RELAY_TX_FEE = 3000;
* with. However scripts violating these flags may still be present in valid
* blocks and we must accept those blocks.
*/
-static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY_FLAGS |
- SCRIPT_VERIFY_DERSIG |
- SCRIPT_VERIFY_STRICTENC |
- SCRIPT_VERIFY_MINIMALDATA |
- SCRIPT_VERIFY_NULLDUMMY |
- SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS |
- SCRIPT_VERIFY_CLEANSTACK |
- SCRIPT_VERIFY_MINIMALIF |
- SCRIPT_VERIFY_NULLFAIL |
- SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY |
- SCRIPT_VERIFY_CHECKSEQUENCEVERIFY |
- SCRIPT_VERIFY_LOW_S |
- SCRIPT_VERIFY_WITNESS |
- SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM |
- SCRIPT_VERIFY_WITNESS_PUBKEYTYPE;
+static constexpr unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY_FLAGS |
+ SCRIPT_VERIFY_DERSIG |
+ SCRIPT_VERIFY_STRICTENC |
+ SCRIPT_VERIFY_MINIMALDATA |
+ SCRIPT_VERIFY_NULLDUMMY |
+ SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS |
+ SCRIPT_VERIFY_CLEANSTACK |
+ SCRIPT_VERIFY_MINIMALIF |
+ SCRIPT_VERIFY_NULLFAIL |
+ SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY |
+ SCRIPT_VERIFY_CHECKSEQUENCEVERIFY |
+ SCRIPT_VERIFY_LOW_S |
+ SCRIPT_VERIFY_WITNESS |
+ SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM |
+ SCRIPT_VERIFY_WITNESS_PUBKEYTYPE;
/** For convenience, standard but not mandatory verify flags. */
-static const 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 const 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/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp
index c672486984..a2521a1e9e 100644
--- a/src/qt/addresstablemodel.cpp
+++ b/src/qt/addresstablemodel.cpp
@@ -384,7 +384,8 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
return QString();
}
}
- strAddress = EncodeDestination(newKey.GetID());
+ wallet->LearnRelatedScripts(newKey, g_address_type);
+ strAddress = EncodeDestination(GetDestinationForKey(newKey, g_address_type));
}
else
{
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 8efa009460..afd90a3bc6 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -1206,7 +1206,7 @@ UnitDisplayStatusBarControl::UnitDisplayStatusBarControl(const PlatformStyle *pl
const QFontMetrics fm(font());
for (const BitcoinUnits::Unit unit : units)
{
- max_width = qMax(max_width, fm.width(BitcoinUnits::name(unit)));
+ max_width = qMax(max_width, fm.width(BitcoinUnits::longName(unit)));
}
setMinimumSize(max_width, 0);
setAlignment(Qt::AlignRight | Qt::AlignVCenter);
@@ -1225,7 +1225,7 @@ void UnitDisplayStatusBarControl::createContextMenu()
menu = new QMenu(this);
for (BitcoinUnits::Unit u : BitcoinUnits::availableUnits())
{
- QAction *menuAction = new QAction(QString(BitcoinUnits::name(u)), this);
+ QAction *menuAction = new QAction(QString(BitcoinUnits::longName(u)), this);
menuAction->setData(QVariant(u));
menu->addAction(menuAction);
}
@@ -1250,7 +1250,7 @@ void UnitDisplayStatusBarControl::setOptionsModel(OptionsModel *_optionsModel)
/** When Display Units are changed on OptionsModel it will refresh the display text of the control on the status bar */
void UnitDisplayStatusBarControl::updateDisplayUnit(int newUnits)
{
- setText(BitcoinUnits::name(newUnits));
+ setText(BitcoinUnits::longName(newUnits));
}
/** Shows context menu with Display Unit options by the mouse coordinates */
diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp
index 55811458a9..9df05d2a13 100644
--- a/src/qt/bitcoinunits.cpp
+++ b/src/qt/bitcoinunits.cpp
@@ -36,24 +36,33 @@ bool BitcoinUnits::valid(int unit)
}
}
-QString BitcoinUnits::name(int unit)
+QString BitcoinUnits::longName(int unit)
{
switch(unit)
{
case BTC: return QString("BTC");
case mBTC: return QString("mBTC");
- case uBTC: return QString::fromUtf8("μBTC");
+ case uBTC: return QString::fromUtf8("µBTC (bits)");
default: return QString("???");
}
}
+QString BitcoinUnits::shortName(int unit)
+{
+ switch(unit)
+ {
+ case uBTC: return QString::fromUtf8("bits");
+ default: return longName(unit);
+ }
+}
+
QString BitcoinUnits::description(int unit)
{
switch(unit)
{
case BTC: return QString("Bitcoins");
case mBTC: return QString("Milli-Bitcoins (1 / 1" THIN_SP_UTF8 "000)");
- case uBTC: return QString("Micro-Bitcoins (1 / 1" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)");
+ case uBTC: return QString("Micro-Bitcoins (bits) (1 / 1" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)");
default: return QString("???");
}
}
@@ -121,7 +130,7 @@ QString BitcoinUnits::format(int unit, const CAmount& nIn, bool fPlus, Separator
QString BitcoinUnits::formatWithUnit(int unit, const CAmount& amount, bool plussign, SeparatorStyle separators)
{
- return format(unit, amount, plussign, separators) + QString(" ") + name(unit);
+ return format(unit, amount, plussign, separators) + QString(" ") + shortName(unit);
}
QString BitcoinUnits::formatHtmlWithUnit(int unit, const CAmount& amount, bool plussign, SeparatorStyle separators)
@@ -176,7 +185,7 @@ QString BitcoinUnits::getAmountColumnTitle(int unit)
QString amountTitle = QObject::tr("Amount");
if (BitcoinUnits::valid(unit))
{
- amountTitle += " ("+BitcoinUnits::name(unit) + ")";
+ amountTitle += " ("+BitcoinUnits::shortName(unit) + ")";
}
return amountTitle;
}
@@ -197,7 +206,7 @@ QVariant BitcoinUnits::data(const QModelIndex &index, int role) const
{
case Qt::EditRole:
case Qt::DisplayRole:
- return QVariant(name(unit));
+ return QVariant(longName(unit));
case Qt::ToolTipRole:
return QVariant(description(unit));
case UnitRole:
diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h
index d94b33acde..310f651815 100644
--- a/src/qt/bitcoinunits.h
+++ b/src/qt/bitcoinunits.h
@@ -76,8 +76,10 @@ public:
static QList<Unit> availableUnits();
//! Is unit ID valid?
static bool valid(int unit);
+ //! Long name
+ static QString longName(int unit);
//! Short name
- static QString name(int unit);
+ static QString shortName(int unit);
//! Longer description
static QString description(int unit);
//! Number of Satoshis (1e-8) per unit
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index 8a83b513e1..dc729649b8 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -636,27 +636,24 @@ void PaymentServer::fetchPaymentACK(CWallet* wallet, const SendCoinsRecipient& r
// Create a new refund address, or re-use:
QString account = tr("Refund from %1").arg(recipient.authenticatedMerchant);
std::string strAccount = account.toStdString();
- std::set<CTxDestination> refundAddresses = wallet->GetAccountAddresses(strAccount);
- if (!refundAddresses.empty()) {
- CScript s = GetScriptForDestination(*refundAddresses.begin());
+ CPubKey newKey;
+ if (wallet->GetKeyFromPool(newKey)) {
+ // BIP70 requests encode the scriptPubKey directly, so we are not restricted to address
+ // types supported by the receiver. As a result, we choose the address format we also
+ // use for change. Despite an actual payment and not change, this is a close match:
+ // it's the output type we use subject to privacy issues, but not restricted by what
+ // other software supports.
+ wallet->LearnRelatedScripts(newKey, g_change_type);
+ CTxDestination dest = GetDestinationForKey(newKey, g_change_type);
+ wallet->SetAddressBook(dest, strAccount, "refund");
+
+ CScript s = GetScriptForDestination(dest);
payments::Output* refund_to = payment.add_refund_to();
refund_to->set_script(&s[0], s.size());
- }
- else {
- CPubKey newKey;
- if (wallet->GetKeyFromPool(newKey)) {
- CKeyID keyID = newKey.GetID();
- wallet->SetAddressBook(keyID, strAccount, "refund");
-
- CScript s = GetScriptForDestination(keyID);
- payments::Output* refund_to = payment.add_refund_to();
- refund_to->set_script(&s[0], s.size());
- }
- else {
- // This should never happen, because sending coins should have
- // just unlocked the wallet and refilled the keypool.
- qWarning() << "PaymentServer::fetchPaymentACK: Error getting refund key, refund_to not set";
- }
+ } else {
+ // This should never happen, because sending coins should have
+ // just unlocked the wallet and refilled the keypool.
+ qWarning() << "PaymentServer::fetchPaymentACK: Error getting refund key, refund_to not set";
}
int length = payment.ByteSize();
diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp
index c8c2cb76a3..0dd7d46960 100644
--- a/src/qt/recentrequeststablemodel.cpp
+++ b/src/qt/recentrequeststablemodel.cpp
@@ -123,7 +123,7 @@ void RecentRequestsTableModel::updateAmountColumnTitle()
/** Gets title for amount column including current display unit if optionsModel reference available. */
QString RecentRequestsTableModel::getAmountTitle()
{
- return (this->walletModel->getOptionsModel() != nullptr) ? tr("Requested") + " ("+BitcoinUnits::name(this->walletModel->getOptionsModel()->getDisplayUnit()) + ")" : "";
+ return (this->walletModel->getOptionsModel() != nullptr) ? tr("Requested") + " ("+BitcoinUnits::shortName(this->walletModel->getOptionsModel()->getDisplayUnit()) + ")" : "";
}
QModelIndex RecentRequestsTableModel::index(int row, int column, const QModelIndex &parent) const
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 56b62f97fb..9fd61db70e 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -336,7 +336,7 @@ void SendCoinsDialog::on_sendButton_clicked()
}
questionString.append(tr("Total Amount %1")
.arg(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), totalAmount)));
- questionString.append(QString("<span style='font-size:10pt;font-weight:normal;'><br />(=%2)</span>")
+ questionString.append(QString("<span style='font-size:10pt;font-weight:normal;'><br />(=%1)</span>")
.arg(alternativeUnits.join(" " + tr("or") + "<br />")));
questionString.append("<hr /><span>");
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index 4b7c3bd726..a270e5de59 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -149,6 +149,9 @@ void BumpFee(TransactionView& view, const uint256& txid, bool expectDisabled, st
// src/qt/test/test_bitcoin-qt -platform cocoa # macOS
void TestGUI()
{
+ g_address_type = OUTPUT_TYPE_P2SH_SEGWIT;
+ g_change_type = OUTPUT_TYPE_P2SH_SEGWIT;
+
// Set up wallet and chain with 105 blocks (5 mature blocks for spending).
TestChain100Setup test;
for (int i = 0; i < 5; ++i) {
@@ -161,7 +164,7 @@ void TestGUI()
wallet.LoadWallet(firstRun);
{
LOCK(wallet.cs_wallet);
- wallet.SetAddressBook(test.coinbaseKey.GetPubKey().GetID(), "", "receive");
+ wallet.SetAddressBook(GetDestinationForKey(test.coinbaseKey.GetPubKey(), g_address_type), "", "receive");
wallet.AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey());
}
{
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index 868934288b..fa43ab750a 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -365,7 +365,7 @@ void TransactionView::exportClicked()
// name, column, role
writer.setModel(transactionProxyModel);
writer.addColumn(tr("Confirmed"), 0, TransactionTableModel::ConfirmedRole);
- if (model && model->haveWatchOnly())
+ if (model->haveWatchOnly())
writer.addColumn(tr("Watch-only"), TransactionTableModel::Watchonly);
writer.addColumn(tr("Date"), 0, TransactionTableModel::DateRole);
writer.addColumn(tr("Type"), TransactionTableModel::Type, Qt::EditRole);
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index e7f9657603..1b2c71c4a4 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -1358,6 +1358,7 @@ UniValue mempoolInfoToJSON()
size_t maxmempool = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
ret.push_back(Pair("maxmempool", (int64_t) maxmempool));
ret.push_back(Pair("mempoolminfee", ValueFromAmount(std::max(mempool.GetMinFee(maxmempool), ::minRelayTxFee).GetFeePerK())));
+ ret.push_back(Pair("minrelaytxfee", ValueFromAmount(::minRelayTxFee.GetFeePerK())));
return ret;
}
@@ -1374,7 +1375,8 @@ UniValue getmempoolinfo(const JSONRPCRequest& request)
" \"bytes\": xxxxx, (numeric) Sum of all virtual transaction sizes as defined in BIP 141. Differs from actual serialized size because witness data is discounted\n"
" \"usage\": xxxxx, (numeric) Total memory usage for the mempool\n"
" \"maxmempool\": xxxxx, (numeric) Maximum memory usage for the mempool\n"
- " \"mempoolminfee\": xxxxx (numeric) Minimum fee rate in " + CURRENCY_UNIT + "/kB for tx to be accepted\n"
+ " \"mempoolminfee\": xxxxx (numeric) Minimum fee rate in " + CURRENCY_UNIT + "/kB for tx to be accepted. Is the maximum of minrelaytxfee and minimum mempool fee\n"
+ " \"minrelaytxfee\": xxxxx (numeric) Current minimum relay fee for transactions\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getmempoolinfo", "")
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 1c8a24ed36..95a27d474b 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -40,6 +40,46 @@ public:
explicit DescribeAddressVisitor(CWallet *_pwallet) : pwallet(_pwallet) {}
+ void ProcessSubScript(const CScript& subscript, UniValue& obj, bool include_addresses = false) const
+ {
+ // Always present: script type and redeemscript
+ txnouttype which_type;
+ std::vector<std::vector<unsigned char>> solutions_data;
+ Solver(subscript, which_type, solutions_data);
+ obj.pushKV("script", GetTxnOutputType(which_type));
+ obj.pushKV("hex", HexStr(subscript.begin(), subscript.end()));
+
+ CTxDestination embedded;
+ UniValue a(UniValue::VARR);
+ if (ExtractDestination(subscript, embedded)) {
+ // Only when the script corresponds to an address.
+ UniValue subobj = boost::apply_visitor(*this, embedded);
+ subobj.pushKV("address", EncodeDestination(embedded));
+ subobj.pushKV("scriptPubKey", HexStr(subscript.begin(), subscript.end()));
+ // Always report the pubkey at the top level, so that `getnewaddress()['pubkey']` always works.
+ if (subobj.exists("pubkey")) obj.pushKV("pubkey", subobj["pubkey"]);
+ obj.pushKV("embedded", std::move(subobj));
+ if (include_addresses) a.push_back(EncodeDestination(embedded));
+ } else if (which_type == TX_MULTISIG) {
+ // Also report some information on multisig scripts (which do not have a corresponding address).
+ // TODO: abstract out the common functionality between this logic and ExtractDestinations.
+ obj.pushKV("sigsrequired", solutions_data[0][0]);
+ UniValue pubkeys(UniValue::VARR);
+ for (size_t i = 1; i < solutions_data.size() - 1; ++i) {
+ CPubKey key(solutions_data[i].begin(), solutions_data[i].end());
+ if (include_addresses) a.push_back(EncodeDestination(key.GetID()));
+ pubkeys.push_back(HexStr(key.begin(), key.end()));
+ }
+ obj.pushKV("pubkeys", std::move(pubkeys));
+ }
+
+ // The "addresses" field is confusing because it refers to public keys using their P2PKH address.
+ // For that reason, only add the 'addresses' field when needed for backward compatibility. New applications
+ // can use the 'embedded'->'address' field for P2SH or P2WSH wrapped addresses, and 'pubkeys' for
+ // inspecting multisig participants.
+ if (include_addresses) obj.pushKV("addresses", std::move(a));
+ }
+
UniValue operator()(const CNoDestination &dest) const { return UniValue(UniValue::VOBJ); }
UniValue operator()(const CKeyID &keyID) const {
@@ -60,19 +100,7 @@ public:
obj.push_back(Pair("isscript", true));
obj.push_back(Pair("iswitness", false));
if (pwallet && pwallet->GetCScript(scriptID, subscript)) {
- std::vector<CTxDestination> addresses;
- txnouttype whichType;
- int nRequired;
- ExtractDestinations(subscript, whichType, addresses, nRequired);
- obj.push_back(Pair("script", GetTxnOutputType(whichType)));
- obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
- UniValue a(UniValue::VARR);
- for (const CTxDestination& addr : addresses) {
- a.push_back(EncodeDestination(addr));
- }
- obj.push_back(Pair("addresses", a));
- if (whichType == TX_MULTISIG)
- obj.push_back(Pair("sigsrequired", nRequired));
+ ProcessSubScript(subscript, obj, true);
}
return obj;
}
@@ -103,7 +131,7 @@ public:
uint160 hash;
hasher.Write(id.begin(), 32).Finalize(hash.begin());
if (pwallet && pwallet->GetCScript(CScriptID(hash), subscript)) {
- obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
+ ProcessSubScript(subscript, obj);
}
return obj;
}
@@ -131,23 +159,32 @@ UniValue validateaddress(const JSONRPCRequest& request)
"\nResult:\n"
"{\n"
" \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n"
- " \"address\" : \"address\", (string) The bitcoin address validated\n"
+ " \"address\" : \"address\", (string) The bitcoin address validated\n"
" \"scriptPubKey\" : \"hex\", (string) The hex encoded scriptPubKey generated by the address\n"
" \"ismine\" : true|false, (boolean) If the address is yours or not\n"
" \"iswatchonly\" : true|false, (boolean) If the address is watchonly\n"
- " \"isscript\" : true|false, (boolean) If the key is a script\n"
- " \"script\" : \"type\" (string, optional) The output script type. Possible types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash, witness_v0_scripthash\n"
- " \"hex\" : \"hex\", (string, optional) The redeemscript for the p2sh address\n"
- " \"addresses\" (string, optional) Array of addresses associated with the known redeemscript\n"
+ " \"isscript\" : true|false, (boolean, optional) If the address is P2SH or P2WSH. Not included for unknown witness types.\n"
+ " \"iswitness\" : true|false, (boolean) If the address is P2WPKH, P2WSH, or an unknown witness version\n"
+ " \"witness_version\" : version (number, optional) For all witness output types, gives the version number.\n"
+ " \"witness_program\" : \"hex\" (string, optional) For all witness output types, gives the script or key hash present in the address.\n"
+ " \"script\" : \"type\" (string, optional) The output script type. Only if \"isscript\" is true and the redeemscript is known. Possible types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash, witness_v0_scripthash, witness_unknown\n"
+ " \"hex\" : \"hex\", (string, optional) The redeemscript for the P2SH or P2WSH address\n"
+ " \"addresses\" (string, optional) Array of addresses associated with the known redeemscript (only if \"iswitness\" is false). This field is superseded by the \"pubkeys\" field and the address inside \"embedded\".\n"
" [\n"
" \"address\"\n"
" ,...\n"
" ]\n"
- " \"sigsrequired\" : xxxxx (numeric, optional) Number of signatures required to spend multisig output\n"
- " \"pubkey\" : \"publickeyhex\", (string) The hex value of the raw public key\n"
+ " \"pubkeys\" (string, optional) Array of pubkeys associated with the known redeemscript (only if \"script\" is \"multisig\")\n"
+ " [\n"
+ " \"pubkey\"\n"
+ " ,...\n"
+ " ]\n"
+ " \"sigsrequired\" : xxxxx (numeric, optional) Number of signatures required to spend multisig output (only if \"script\" is \"multisig\")\n"
+ " \"pubkey\" : \"publickeyhex\", (string, optional) The hex value of the raw public key, for single-key addresses (possibly embedded in P2SH or P2WSH)\n"
+ " \"embedded\" : {...}, (object, optional) information about the address embedded in P2SH or P2WSH, if relevant and known. It includes all validateaddress output fields for the embedded address, excluding \"isvalid\", metadata (\"timestamp\", \"hdkeypath\", \"hdmasterkeyid\") and relation to the wallet (\"ismine\", \"iswatchonly\", \"account\").\n"
" \"iscompressed\" : true|false, (boolean) If the address is compressed\n"
" \"account\" : \"account\" (string) DEPRECATED. The account associated with the address, \"\" is the default account\n"
- " \"timestamp\" : timestamp, (number, optional) The creation time of the key if available in seconds since epoch (Jan 1 1970 GMT)\n"
+ " \"timestamp\" : timestamp, (number, optional) The creation time of the key if available in seconds since epoch (Jan 1 1970 GMT)\n"
" \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath if the key is HD and available\n"
" \"hdmasterkeyid\" : \"<hash160>\" (string, optional) The Hash160 of the HD master pubkey\n"
"}\n"
@@ -188,8 +225,9 @@ UniValue validateaddress(const JSONRPCRequest& request)
}
if (pwallet) {
const CKeyMetadata* meta = nullptr;
- if (const CKeyID* key_id = boost::get<CKeyID>(&dest)) {
- auto it = pwallet->mapKeyMetadata.find(*key_id);
+ CKeyID key_id = GetKeyForDestination(*pwallet, dest);
+ if (!key_id.IsNull()) {
+ auto it = pwallet->mapKeyMetadata.find(key_id);
if (it != pwallet->mapKeyMetadata.end()) {
meta = &it->second;
}
@@ -242,12 +280,12 @@ CScript _createmultisig_redeemScript(CWallet * const pwallet, const UniValue& pa
// Case 1: Bitcoin address and we have full public key:
CTxDestination dest = DecodeDestination(ks);
if (pwallet && IsValidDestination(dest)) {
- const CKeyID *keyID = boost::get<CKeyID>(&dest);
- if (!keyID) {
+ CKeyID key = GetKeyForDestination(*pwallet, dest);
+ if (key.IsNull()) {
throw std::runtime_error(strprintf("%s does not refer to a key", ks));
}
CPubKey vchPubKey;
- if (!pwallet->GetPubKey(*keyID, vchPubKey)) {
+ if (!pwallet->GetPubKey(key, vchPubKey)) {
throw std::runtime_error(strprintf("no full public key for address %s", ks));
}
if (!vchPubKey.IsFullyValid())
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index f1ec5e2d13..838e502a0a 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -422,3 +422,22 @@ bool DummySignatureCreator::CreateSig(std::vector<unsigned char>& vchSig, const
vchSig[6 + 33 + 32] = SIGHASH_ALL;
return true;
}
+
+bool IsSolvable(const CKeyStore& store, const CScript& script)
+{
+ // This check is to make sure that the script we created can actually be solved for and signed by us
+ // if we were to have the private keys. This is just to make sure that the script is valid and that,
+ // if found in a transaction, we would still accept and relay that transaction. In particular,
+ // it will reject witness outputs that require signing with an uncompressed public key.
+ DummySignatureCreator creator(&store);
+ SignatureData sigs;
+ // Make sure that STANDARD_SCRIPT_VERIFY_FLAGS includes SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, the most
+ // important property this function is designed to test for.
+ static_assert(STANDARD_SCRIPT_VERIFY_FLAGS & SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, "IsSolvable requires standard script flags to include WITNESS_PUBKEYTYPE");
+ if (ProduceSignature(creator, script, sigs)) {
+ // VerifyScript check is just defensive, and should never fail.
+ assert(VerifyScript(sigs.scriptSig, script, &sigs.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker()));
+ return true;
+ }
+ return false;
+}
diff --git a/src/script/sign.h b/src/script/sign.h
index 0fe97a80e8..97c0014cd0 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -81,4 +81,10 @@ SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignature
SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn);
void UpdateTransaction(CMutableTransaction& tx, unsigned int nIn, const SignatureData& data);
+/* Check whether we know how to sign for an output like this, assuming we
+ * have all private keys. While this function does not need private keys, the passed
+ * keystore is used to look up public keys and redeemscripts by hash.
+ * Solvability is unrelated to whether we consider this output to be ours. */
+bool IsSolvable(const CKeyStore& store, const CScript& script);
+
#endif // BITCOIN_SCRIPT_SIGN_H
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index 43961d0572..cfb3c58588 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -348,19 +348,14 @@ CScript GetScriptForWitness(const CScript& redeemscript)
std::vector<std::vector<unsigned char> > vSolutions;
if (Solver(redeemscript, typ, vSolutions)) {
if (typ == TX_PUBKEY) {
- unsigned char h160[20];
- CHash160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(h160);
- ret << OP_0 << std::vector<unsigned char>(&h160[0], &h160[20]);
- return ret;
+ return GetScriptForDestination(WitnessV0KeyHash(Hash160(vSolutions[0].begin(), vSolutions[0].end())));
} else if (typ == TX_PUBKEYHASH) {
- ret << OP_0 << vSolutions[0];
- return ret;
+ return GetScriptForDestination(WitnessV0KeyHash(vSolutions[0]));
}
}
uint256 hash;
CSHA256().Write(&redeemscript[0], redeemscript.size()).Finalize(hash.begin());
- ret << OP_0 << ToByteVector(hash);
- return ret;
+ return GetScriptForDestination(WitnessV0ScriptHash(hash));
}
bool IsValidDestination(const CTxDestination& dest) {
diff --git a/src/script/standard.h b/src/script/standard.h
index 5925f2a512..3b2838a5bb 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -73,8 +73,19 @@ public:
friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; }
};
-struct WitnessV0ScriptHash : public uint256 {};
-struct WitnessV0KeyHash : public uint160 {};
+struct WitnessV0ScriptHash : public uint256
+{
+ WitnessV0ScriptHash() : uint256() {}
+ explicit WitnessV0ScriptHash(const uint256& hash) : uint256(hash) {}
+ using uint256::uint256;
+};
+
+struct WitnessV0KeyHash : public uint160
+{
+ WitnessV0KeyHash() : uint160() {}
+ explicit WitnessV0KeyHash(const uint160& hash) : uint160(hash) {}
+ using uint160::uint160;
+};
//! CTxDestination subtype to encode any future Witness version
struct WitnessUnknown
@@ -144,6 +155,10 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
* addressRet is populated with a single value and nRequiredRet is set to 1.
* Returns true if successful. Currently does not extract address from
* pay-to-witness scripts.
+ *
+ * Note: this function confuses destinations (a subset of CScripts that are
+ * encodable as an address) with key identifiers (of keys involved in a
+ * CScript), and its use should be phased out.
*/
bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet);
@@ -164,6 +179,9 @@ CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys);
* Generate a pay-to-witness script for the given redeem script. If the redeem
* script is P2PK or P2PKH, this returns a P2WPKH script, otherwise it returns a
* P2WSH script.
+ *
+ * TODO: replace calls to GetScriptForWitness with GetScriptForDestination using
+ * the various witness-specific CTxDestination subtypes.
*/
CScript GetScriptForWitness(const CScript& redeemscript);
diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json
index ad74b7cf1b..a845083636 100644
--- a/src/test/data/tx_valid.json
+++ b/src/test/data/tx_valid.json
@@ -56,6 +56,12 @@
[[["0000000000000000000000000000000000000000000000000000000000000200", 0, "1"], ["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0xe52b482f2faa8ecbf0db344f93c84ac908557f33 EQUALVERIFY CHECKSIG"]],
"01000000020002000000000000000000000000000000000000000000000000000000000000000000000151ffffffff0001000000000000000000000000000000000000000000000000000000000000000000006b483045022100c9cdd08798a28af9d1baf44a6c77bcc7e279f47dc487c8c899911bc48feaffcc0220503c5c50ae3998a733263c5c0f7061b483e2b56c4c41b456e7d2f5a78a74c077032102d5c25adb51b61339d2b05315791e21bbe80ea470a49db0135720983c905aace0ffffffff010000000000000000015100000000", "P2SH"],
+["The following tests SIGHASH_SINGLE|SIGHASHANYONECANPAY inputs"],
+[[["437a1002eb125dec0f93f635763e0ae45f28ff8e81d82945753d0107611cd390", 1, "DUP HASH160 0x14 0x383fb81cb0a3fc724b5e08cf8bbd404336d711f6 EQUALVERIFY CHECKSIG"],
+ ["2d48d32ccad087bcda0ac5b31555bd58d1d2568184cbc8e752dd2be2684af03f", 1, "DUP HASH160 0x14 0x275ec2a233e5b23d43fa19e7bf9beb0cb3996117 EQUALVERIFY CHECKSIG"],
+ ["c76168ef1a272a4f176e55e73157ecfce040cfad16a5272f6296eb7089dca846", 1, "DUP HASH160 0x14 0x34fea2c5a75414fd945273ae2d029ce1f28dafcf EQUALVERIFY CHECKSIG"]],
+"010000000390d31c6107013d754529d8818eff285fe40a3e7635f6930fec5d12eb02107a43010000006b483045022100f40815ae3c81a0dd851cc8d376d6fd226c88416671346a9033468cca2cdcc6c202204f764623903e6c4bed1b734b75d82c40f1725e4471a55ad4f51218f86130ac038321033d710ab45bb54ac99618ad23b3c1da661631aa25f23bfe9d22b41876f1d46e4effffffff3ff04a68e22bdd52e7c8cb848156d2d158bd5515b3c50adabc87d0ca2cd3482d010000006a4730440220598d263c107004008e9e26baa1e770be30fd31ee55ded1898f7c00da05a75977022045536bead322ca246779698b9c3df3003377090f41afeca7fb2ce9e328ec4af2832102b738b531def73020bd637f32935924cc88549c8206976226d968edd3a42fc2d7ffffffff46a8dc8970eb96622f27a516adcf40e0fcec5731e7556e174f2a271aef6861c7010000006b483045022100c5b90a777a9fdc90c208dbef7290d1fc1be651f47151ee4ccff646872a454cf90220640cfbc4550446968fbbe9d12528f3adf7d87b31541569c59e790db8a220482583210391332546e22bbe8fe3af54addfad6f8b83d05fa4f5e047593d4c07ae938795beffffffff028036be26000000001976a914ddfb29efad43a667465ac59ff14dc6442a1adfca88ac3d5cba01000000001976a914b64dde7a505a13ca986c40e86e984a8dc81368b688ac00000000", "P2SH"],
+
["An invalid P2SH Transaction"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7a052c840ba73af26755de42cf01cc9e0a49fef0 EQUAL"]],
"010000000100010000000000000000000000000000000000000000000000000000000000000000000009085768617420697320ffffffff010000000000000000015100000000", "NONE"],
diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp
index e6234c5abf..1766c6a093 100644
--- a/src/test/mempool_tests.cpp
+++ b/src/test/mempool_tests.cpp
@@ -287,35 +287,6 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
pool.removeRecursive(pool.mapTx.find(tx9.GetHash())->GetTx());
pool.removeRecursive(pool.mapTx.find(tx8.GetHash())->GetTx());
- /* Now check the sort on the mining score index.
- * Final order should be:
- *
- * tx7 (2M)
- * tx2 (20k)
- * tx4 (15000)
- * tx1/tx5 (10000)
- * tx3/6 (0)
- * (Ties resolved by hash)
- */
- sortedOrder.clear();
- sortedOrder.push_back(tx7.GetHash().ToString());
- sortedOrder.push_back(tx2.GetHash().ToString());
- sortedOrder.push_back(tx4.GetHash().ToString());
- if (tx1.GetHash() < tx5.GetHash()) {
- sortedOrder.push_back(tx5.GetHash().ToString());
- sortedOrder.push_back(tx1.GetHash().ToString());
- } else {
- sortedOrder.push_back(tx1.GetHash().ToString());
- sortedOrder.push_back(tx5.GetHash().ToString());
- }
- if (tx3.GetHash() < tx6.GetHash()) {
- sortedOrder.push_back(tx6.GetHash().ToString());
- sortedOrder.push_back(tx3.GetHash().ToString());
- } else {
- sortedOrder.push_back(tx3.GetHash().ToString());
- sortedOrder.push_back(tx6.GetHash().ToString());
- }
- CheckSort<mining_score>(pool, sortedOrder);
}
BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp
index 19060eccc9..cd30fbeda7 100644
--- a/src/test/script_standard_tests.cpp
+++ b/src/test/script_standard_tests.cpp
@@ -508,12 +508,7 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
scriptPubKey.clear();
scriptPubKey << OP_0 << ToByteVector(pubkeys[0].GetID());
- // Keystore has key, but no P2SH redeemScript
- result = IsMine(keystore, scriptPubKey, isInvalid);
- BOOST_CHECK_EQUAL(result, ISMINE_NO);
- BOOST_CHECK(!isInvalid);
-
- // Keystore has key and P2SH redeemScript
+ // Keystore implicitly has key and P2SH redeemScript
keystore.AddCScript(scriptPubKey);
result = IsMine(keystore, scriptPubKey, isInvalid);
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 6a02ccb5c1..55d60d95e9 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -121,7 +121,7 @@ BOOST_AUTO_TEST_CASE(util_ParseParameters)
testArgs.ParseParameters(1, (char**)argv_test);
BOOST_CHECK(testArgs.GetMapArgs().empty() && testArgs.GetMapMultiArgs().empty());
- testArgs.ParseParameters(5, (char**)argv_test);
+ testArgs.ParseParameters(7, (char**)argv_test);
// expectation: -ignored is ignored (program name argument),
// -a, -b and -ccc end up in map, -d ignored because it is after
// a non-option argument (non-GNU option parsing)
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index ffb024aef9..d1edde284f 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -906,8 +906,8 @@ bool CCoinsViewMemPool::GetCoin(const COutPoint &outpoint, Coin &coin) const {
size_t CTxMemPool::DynamicMemoryUsage() const {
LOCK(cs);
- // Estimate the overhead of mapTx to be 15 pointers + an allocation, as no exact formula for boost::multi_index_contained is implemented.
- return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 15 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(mapLinks) + memusage::DynamicUsage(vTxHashes) + cachedInnerUsage;
+ // Estimate the overhead of mapTx to be 12 pointers + an allocation, as no exact formula for boost::multi_index_contained is implemented.
+ return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 12 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(mapLinks) + memusage::DynamicUsage(vTxHashes) + cachedInnerUsage;
}
void CTxMemPool::RemoveStaged(setEntries &stage, bool updateDescendants, MemPoolRemovalReason reason) {
diff --git a/src/txmempool.h b/src/txmempool.h
index d32131d7a8..d6f8e7094b 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -313,7 +313,6 @@ public:
// Multi_index tag names
struct descendant_score {};
struct entry_time {};
-struct mining_score {};
struct ancestor_score {};
class CBlockPolicyEstimator;
@@ -382,7 +381,6 @@ public:
* - transaction hash
* - feerate [we use max(feerate of tx, feerate of tx with all descendants)]
* - time in mempool
- * - mining score (feerate modified by any fee deltas from PrioritiseTransaction)
*
* Note: the term "descendant" refers to in-mempool transactions that depend on
* this one, while "ancestor" refers to in-mempool transactions that a given
@@ -472,12 +470,6 @@ public:
boost::multi_index::identity<CTxMemPoolEntry>,
CompareTxMemPoolEntryByEntryTime
>,
- // sorted by score (for mining prioritization)
- boost::multi_index::ordered_unique<
- boost::multi_index::tag<mining_score>,
- boost::multi_index::identity<CTxMemPoolEntry>,
- CompareTxMemPoolEntryByScore
- >,
// sorted by fee rate with ancestors
boost::multi_index::ordered_non_unique<
boost::multi_index::tag<ancestor_score>,
diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp
index fd7e1bb56c..6ad18721fd 100644
--- a/src/wallet/crypter.cpp
+++ b/src/wallet/crypter.cpp
@@ -245,6 +245,7 @@ bool CCryptoKeyStore::AddCryptedKey(const CPubKey &vchPubKey, const std::vector<
}
mapCryptedKeys[vchPubKey.GetID()] = make_pair(vchPubKey, vchCryptedSecret);
+ ImplicitlyLearnRelatedKeyScripts(vchPubKey);
return true;
}
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 71e7111b1a..0f453f154f 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -16,6 +16,8 @@
std::string GetWalletHelpString(bool showDebug)
{
std::string strUsage = HelpMessageGroup(_("Wallet options:"));
+ strUsage += HelpMessageOpt("-addresstype", strprintf(_("What type of addresses to use (\"legacy\", \"p2sh-segwit\", or \"bech32\", default: \"%s\")"), FormatOutputType(OUTPUT_TYPE_DEFAULT)));
+ strUsage += HelpMessageOpt("-changetype", _("What type of change to use (\"legacy\", \"p2sh-segwit\", or \"bech32\", default is same as -addresstype)"));
strUsage += HelpMessageOpt("-disablewallet", _("Do not load the wallet and disable wallet RPC calls"));
strUsage += HelpMessageOpt("-keypool=<n>", strprintf(_("Set key pool size to <n> (default: %u)"), DEFAULT_KEYPOOL_SIZE));
strUsage += HelpMessageOpt("-fallbackfee=<amt>", strprintf(_("A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s)"),
@@ -175,6 +177,16 @@ bool WalletParameterInteraction()
bSpendZeroConfChange = gArgs.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE);
fWalletRbf = gArgs.GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF);
+ g_address_type = ParseOutputType(gArgs.GetArg("-addresstype", ""));
+ if (g_address_type == OUTPUT_TYPE_NONE) {
+ return InitError(strprintf(_("Unknown address type '%s'"), gArgs.GetArg("-addresstype", "")));
+ }
+
+ g_change_type = ParseOutputType(gArgs.GetArg("-changetype", ""), g_address_type);
+ if (g_change_type == OUTPUT_TYPE_NONE) {
+ return InitError(strprintf(_("Unknown change type '%s'"), gArgs.GetArg("-changetype", "")));
+ }
+
return true;
}
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index fc84fe6468..645e776b68 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -131,7 +131,11 @@ UniValue importprivkey(const JSONRPCRequest& request)
CKeyID vchAddress = pubkey.GetID();
{
pwallet->MarkDirty();
- pwallet->SetAddressBook(vchAddress, strLabel, "receive");
+
+ // We don't know which corresponding address will be used; label them all
+ for (const auto& dest : GetAllDestinationsForKey(pubkey)) {
+ pwallet->SetAddressBook(dest, strLabel, "receive");
+ }
// Don't throw error in case a key is already there
if (pwallet->HaveKey(vchAddress)) {
@@ -143,6 +147,7 @@ UniValue importprivkey(const JSONRPCRequest& request)
if (!pwallet->AddKeyPubKey(key, pubkey)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
}
+ pwallet->LearnAllRelatedScripts(pubkey);
// whenever a key is imported, we need to scan the whole chain
pwallet->UpdateTimeFirstKey(1);
@@ -433,8 +438,11 @@ UniValue importpubkey(const JSONRPCRequest& request)
LOCK2(cs_main, pwallet->cs_wallet);
- ImportAddress(pwallet, pubKey.GetID(), strLabel);
+ for (const auto& dest : GetAllDestinationsForKey(pubKey)) {
+ ImportAddress(pwallet, dest, strLabel);
+ }
ImportScript(pwallet, GetScriptForRawPubKey(pubKey), strLabel, false);
+ pwallet->LearnAllRelatedScripts(pubKey);
if (fRescan)
{
@@ -595,12 +603,12 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
}
- const CKeyID *keyID = boost::get<CKeyID>(&dest);
- if (!keyID) {
+ auto keyid = GetKeyForDestination(*pwallet, dest);
+ if (keyid.IsNull()) {
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key");
}
CKey vchSecret;
- if (!pwallet->GetKey(*keyID, vchSecret)) {
+ if (!pwallet->GetKey(keyid, vchSecret)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known");
}
return CBitcoinSecret(vchSecret).ToString();
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 873fbd1bab..e307623fd5 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -136,14 +136,15 @@ UniValue getnewaddress(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() > 1)
+ if (request.fHelp || request.params.size() > 2)
throw std::runtime_error(
- "getnewaddress ( \"account\" )\n"
+ "getnewaddress ( \"account\" \"address_type\" )\n"
"\nReturns a new Bitcoin address for receiving payments.\n"
"If 'account' is specified (DEPRECATED), it is added to the address book \n"
"so payments received with the address will be credited to 'account'.\n"
"\nArguments:\n"
"1. \"account\" (string, optional) DEPRECATED. The account name for the address to be linked to. If not provided, the default account \"\" is used. It can also be set to the empty string \"\" to represent the default account. The account does not need to exist, it will be created if there is no account by the given name.\n"
+ "2. \"address_type\" (string, optional) The address type to use. Options are \"legacy\", \"p2sh\", and \"bech32\". Default is set by -addresstype.\n"
"\nResult:\n"
"\"address\" (string) The new bitcoin address\n"
"\nExamples:\n"
@@ -158,6 +159,14 @@ UniValue getnewaddress(const JSONRPCRequest& request)
if (!request.params[0].isNull())
strAccount = AccountFromValue(request.params[0]);
+ OutputType output_type = g_address_type;
+ if (!request.params[1].isNull()) {
+ output_type = ParseOutputType(request.params[1].get_str(), g_address_type);
+ if (output_type == OUTPUT_TYPE_NONE) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[1].get_str()));
+ }
+ }
+
if (!pwallet->IsLocked()) {
pwallet->TopUpKeyPool();
}
@@ -167,22 +176,23 @@ UniValue getnewaddress(const JSONRPCRequest& request)
if (!pwallet->GetKeyFromPool(newKey)) {
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
}
- CKeyID keyID = newKey.GetID();
+ pwallet->LearnRelatedScripts(newKey, output_type);
+ CTxDestination dest = GetDestinationForKey(newKey, output_type);
- pwallet->SetAddressBook(keyID, strAccount, "receive");
+ pwallet->SetAddressBook(dest, strAccount, "receive");
- return EncodeDestination(keyID);
+ return EncodeDestination(dest);
}
-CTxDestination GetAccountAddress(CWallet* const pwallet, std::string strAccount, bool bForceNew=false)
+CTxDestination GetAccountDestination(CWallet* const pwallet, std::string strAccount, bool bForceNew=false)
{
- CPubKey pubKey;
- if (!pwallet->GetAccountPubkey(pubKey, strAccount, bForceNew)) {
+ CTxDestination dest;
+ if (!pwallet->GetAccountDestination(dest, strAccount, bForceNew)) {
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
}
- return pubKey.GetID();
+ return dest;
}
UniValue getaccountaddress(const JSONRPCRequest& request)
@@ -214,7 +224,7 @@ UniValue getaccountaddress(const JSONRPCRequest& request)
UniValue ret(UniValue::VSTR);
- ret = EncodeDestination(GetAccountAddress(pwallet, strAccount));
+ ret = EncodeDestination(GetAccountDestination(pwallet, strAccount));
return ret;
}
@@ -226,11 +236,13 @@ UniValue getrawchangeaddress(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() > 0)
+ if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
- "getrawchangeaddress\n"
+ "getrawchangeaddress ( \"address_type\" )\n"
"\nReturns a new Bitcoin address, for receiving change.\n"
"This is for use with raw transactions, NOT normal use.\n"
+ "\nArguments:\n"
+ "1. \"address_type\" (string, optional) The address type to use. Options are \"legacy\", \"p2sh\", and \"bech32\". Default is set by -changetype.\n"
"\nResult:\n"
"\"address\" (string) The address\n"
"\nExamples:\n"
@@ -244,6 +256,14 @@ UniValue getrawchangeaddress(const JSONRPCRequest& request)
pwallet->TopUpKeyPool();
}
+ OutputType output_type = g_change_type;
+ if (!request.params[0].isNull()) {
+ output_type = ParseOutputType(request.params[0].get_str(), g_change_type);
+ if (output_type == OUTPUT_TYPE_NONE) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[0].get_str()));
+ }
+ }
+
CReserveKey reservekey(pwallet);
CPubKey vchPubKey;
if (!reservekey.GetReservedKey(vchPubKey, true))
@@ -251,9 +271,10 @@ UniValue getrawchangeaddress(const JSONRPCRequest& request)
reservekey.KeepKey();
- CKeyID keyID = vchPubKey.GetID();
+ pwallet->LearnRelatedScripts(vchPubKey, output_type);
+ CTxDestination dest = GetDestinationForKey(vchPubKey, output_type);
- return EncodeDestination(keyID);
+ return EncodeDestination(dest);
}
@@ -292,8 +313,8 @@ UniValue setaccount(const JSONRPCRequest& request)
// Detect when changing the account of an address that is the 'unused current key' of another account:
if (pwallet->mapAddressBook.count(dest)) {
std::string strOldAccount = pwallet->mapAddressBook[dest].name;
- if (dest == GetAccountAddress(pwallet, strOldAccount)) {
- GetAccountAddress(pwallet, strOldAccount, true);
+ if (dest == GetAccountDestination(pwallet, strOldAccount)) {
+ GetAccountDestination(pwallet, strOldAccount, true);
}
}
pwallet->SetAddressBook(dest, strAccount, "receive");
@@ -1155,6 +1176,8 @@ UniValue addmultisigaddress(const JSONRPCRequest& request)
std::string msg = "addmultisigaddress nrequired [\"key\",...] ( \"account\" )\n"
"\nAdd a nrequired-to-sign multisignature address to the wallet. Requires a new wallet backup.\n"
"Each key is a Bitcoin address or hex-encoded public key.\n"
+ "This functionality is only intended for use with non-watchonly addresses.\n"
+ "See `importaddress` for watchonly p2sh address support.\n"
"If 'account' is specified (DEPRECATED), assign address to that account.\n"
"\nArguments:\n"
@@ -1186,11 +1209,12 @@ UniValue addmultisigaddress(const JSONRPCRequest& request)
// Construct using pay-to-script-hash:
CScript inner = _createmultisig_redeemScript(pwallet, request.params);
- CScriptID innerID(inner);
pwallet->AddCScript(inner);
- pwallet->SetAddressBook(innerID, strAccount, "send");
- return EncodeDestination(innerID);
+ CTxDestination dest = pwallet->AddAndGetDestinationForScript(inner, g_address_type);
+
+ pwallet->SetAddressBook(dest, strAccount, "send");
+ return EncodeDestination(dest);
}
class Witnessifier : public boost::static_visitor<bool>
@@ -1206,12 +1230,7 @@ public:
if (pwallet) {
CScript basescript = GetScriptForDestination(keyID);
CScript witscript = GetScriptForWitness(basescript);
- SignatureData sigs;
- // This check is to make sure that the script we created can actually be solved for and signed by us
- // if we were to have the private keys. This is just to make sure that the script is valid and that,
- // if found in a transaction, we would still accept and relay that transaction.
- if (!ProduceSignature(DummySignatureCreator(pwallet), witscript, sigs) ||
- !VerifyScript(sigs.scriptSig, witscript, &sigs.scriptWitness, MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, DummySignatureCreator(pwallet).Checker())) {
+ if (!IsSolvable(*pwallet, witscript)) {
return false;
}
return ExtractDestination(witscript, result);
@@ -1230,12 +1249,7 @@ public:
return true;
}
CScript witscript = GetScriptForWitness(subscript);
- SignatureData sigs;
- // This check is to make sure that the script we created can actually be solved for and signed by us
- // if we were to have the private keys. This is just to make sure that the script is valid and that,
- // if found in a transaction, we would still accept and relay that transaction.
- if (!ProduceSignature(DummySignatureCreator(pwallet), witscript, sigs) ||
- !VerifyScript(sigs.scriptSig, witscript, &sigs.scriptWitness, MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, DummySignatureCreator(pwallet).Checker())) {
+ if (!IsSolvable(*pwallet, witscript)) {
return false;
}
return ExtractDestination(witscript, result);
@@ -1319,7 +1333,7 @@ UniValue addwitnessaddress(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot convert between witness address types");
}
} else {
- pwallet->AddCScript(witprogram);
+ pwallet->AddCScript(witprogram); // Implicit for single-key now, but necessary for multisig and for compatibility with older software
pwallet->SetAddressBook(w.result, "", "receive");
}
@@ -3464,8 +3478,8 @@ static const CRPCCommand commands[] =
{ "wallet", "getaccount", &getaccount, {"address"} },
{ "wallet", "getaddressesbyaccount", &getaddressesbyaccount, {"account"} },
{ "wallet", "getbalance", &getbalance, {"account","minconf","include_watchonly"} },
- { "wallet", "getnewaddress", &getnewaddress, {"account"} },
- { "wallet", "getrawchangeaddress", &getrawchangeaddress, {} },
+ { "wallet", "getnewaddress", &getnewaddress, {"account","address_type"} },
+ { "wallet", "getrawchangeaddress", &getrawchangeaddress, {"address_type"} },
{ "wallet", "getreceivedbyaccount", &getreceivedbyaccount, {"account","minconf"} },
{ "wallet", "getreceivedbyaddress", &getreceivedbyaddress, {"address","minconf"} },
{ "wallet", "gettransaction", &gettransaction, {"txid","include_watchonly"} },
diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp
index b2e297755e..7797f85f07 100644
--- a/src/wallet/test/wallet_test_fixture.cpp
+++ b/src/wallet/test/wallet_test_fixture.cpp
@@ -13,6 +13,8 @@ WalletTestingSetup::WalletTestingSetup(const std::string& chainName):
bitdb.MakeMock();
bool fFirstRun;
+ g_address_type = OUTPUT_TYPE_DEFAULT;
+ g_change_type = OUTPUT_TYPE_DEFAULT;
std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, "wallet_test.dat"));
pwalletMain = MakeUnique<CWallet>(std::move(dbw));
pwalletMain->LoadWallet(fFirstRun);
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index e18ae4b3b7..7b3c283f37 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -602,6 +602,8 @@ public:
{
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
::bitdb.MakeMock();
+ g_address_type = OUTPUT_TYPE_DEFAULT;
+ g_change_type = OUTPUT_TYPE_DEFAULT;
wallet.reset(new CWallet(std::unique_ptr<CWalletDBWrapper>(new CWalletDBWrapper(&bitdb, "wallet_test.dat"))));
bool firstRun;
wallet->LoadWallet(firstRun);
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index e8b21b3d6f..da721ea008 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -42,6 +42,8 @@ CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE);
unsigned int nTxConfirmTarget = DEFAULT_TX_CONFIRM_TARGET;
bool bSpendZeroConfChange = DEFAULT_SPEND_ZEROCONF_CHANGE;
bool fWalletRbf = DEFAULT_WALLET_RBF;
+OutputType g_address_type = OUTPUT_TYPE_NONE;
+OutputType g_change_type = OUTPUT_TYPE_NONE;
const char * DEFAULT_WALLET_DAT = "wallet.dat";
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
@@ -821,7 +823,7 @@ bool CWallet::AccountMove(std::string strFrom, std::string strTo, CAmount nAmoun
return true;
}
-bool CWallet::GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bForceNew)
+bool CWallet::GetAccountDestination(CTxDestination &dest, std::string strAccount, bool bForceNew)
{
CWalletDB walletdb(*dbw);
@@ -832,8 +834,8 @@ bool CWallet::GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bFo
if (!account.vchPubKey.IsValid())
bForceNew = true;
else {
- // Check if the current key has been used
- CScript scriptPubKey = GetScriptForDestination(account.vchPubKey.GetID());
+ // Check if the current key has been used (TODO: check other addresses with the same key)
+ CScript scriptPubKey = GetScriptForDestination(GetDestinationForKey(account.vchPubKey, g_address_type));
for (std::map<uint256, CWalletTx>::iterator it = mapWallet.begin();
it != mapWallet.end() && account.vchPubKey.IsValid();
++it)
@@ -850,12 +852,14 @@ bool CWallet::GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bFo
if (!GetKeyFromPool(account.vchPubKey, false))
return false;
- SetAddressBook(account.vchPubKey.GetID(), strAccount, "receive");
+ LearnRelatedScripts(account.vchPubKey, g_address_type);
+ dest = GetDestinationForKey(account.vchPubKey, g_address_type);
+ SetAddressBook(dest, strAccount, "receive");
walletdb.WriteAccount(strAccount, account);
+ } else {
+ dest = GetDestinationForKey(account.vchPubKey, g_address_type);
}
- pubKey = account.vchPubKey;
-
return true;
}
@@ -2735,7 +2739,8 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
return false;
}
- scriptChange = GetScriptForDestination(vchPubKey.GetID());
+ LearnRelatedScripts(vchPubKey, g_change_type);
+ scriptChange = GetScriptForDestination(GetDestinationForKey(vchPubKey, g_change_type));
}
CTxOut change_prototype_txout(0, scriptChange);
size_t change_prototype_size = GetSerializeSize(change_prototype_txout, SER_DISK, 0);
@@ -3624,6 +3629,7 @@ void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id)
if (walletdb.ReadPool(index, keypool)) { //TODO: This should be unnecessary
m_pool_key_to_index.erase(keypool.vchPubKey.GetID());
}
+ LearnAllRelatedScripts(keypool.vchPubKey);
walletdb.ErasePool(index);
LogPrintf("keypool index %d removed\n", index);
it = setKeyPool->erase(it);
@@ -4128,3 +4134,106 @@ bool CWalletTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState&
fInMempool = ret;
return ret;
}
+
+static const std::string OUTPUT_TYPE_STRING_LEGACY = "legacy";
+static const std::string OUTPUT_TYPE_STRING_P2SH_SEGWIT = "p2sh-segwit";
+static const std::string OUTPUT_TYPE_STRING_BECH32 = "bech32";
+
+OutputType ParseOutputType(const std::string& type, OutputType default_type)
+{
+ if (type.empty()) {
+ return default_type;
+ } else if (type == OUTPUT_TYPE_STRING_LEGACY) {
+ return OUTPUT_TYPE_LEGACY;
+ } else if (type == OUTPUT_TYPE_STRING_P2SH_SEGWIT) {
+ return OUTPUT_TYPE_P2SH_SEGWIT;
+ } else if (type == OUTPUT_TYPE_STRING_BECH32) {
+ return OUTPUT_TYPE_BECH32;
+ } else {
+ return OUTPUT_TYPE_NONE;
+ }
+}
+
+const std::string& FormatOutputType(OutputType type)
+{
+ switch (type) {
+ case OUTPUT_TYPE_LEGACY: return OUTPUT_TYPE_STRING_LEGACY;
+ case OUTPUT_TYPE_P2SH_SEGWIT: return OUTPUT_TYPE_STRING_P2SH_SEGWIT;
+ case OUTPUT_TYPE_BECH32: return OUTPUT_TYPE_STRING_BECH32;
+ default: assert(false);
+ }
+}
+
+void CWallet::LearnRelatedScripts(const CPubKey& key, OutputType type)
+{
+ if (key.IsCompressed() && (type == OUTPUT_TYPE_P2SH_SEGWIT || type == OUTPUT_TYPE_BECH32)) {
+ CTxDestination witdest = WitnessV0KeyHash(key.GetID());
+ CScript witprog = GetScriptForDestination(witdest);
+ // Make sure the resulting program is solvable.
+ assert(IsSolvable(*this, witprog));
+ AddCScript(witprog);
+ }
+}
+
+void CWallet::LearnAllRelatedScripts(const CPubKey& key)
+{
+ // OUTPUT_TYPE_P2SH_SEGWIT always adds all necessary scripts for all types.
+ LearnRelatedScripts(key, OUTPUT_TYPE_P2SH_SEGWIT);
+}
+
+CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type)
+{
+ switch (type) {
+ case OUTPUT_TYPE_LEGACY: return key.GetID();
+ case OUTPUT_TYPE_P2SH_SEGWIT:
+ case OUTPUT_TYPE_BECH32: {
+ if (!key.IsCompressed()) return key.GetID();
+ CTxDestination witdest = WitnessV0KeyHash(key.GetID());
+ CScript witprog = GetScriptForDestination(witdest);
+ if (type == OUTPUT_TYPE_P2SH_SEGWIT) {
+ return CScriptID(witprog);
+ } else {
+ return witdest;
+ }
+ }
+ default: assert(false);
+ }
+}
+
+std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key)
+{
+ CKeyID keyid = key.GetID();
+ if (key.IsCompressed()) {
+ CTxDestination segwit = WitnessV0KeyHash(keyid);
+ CTxDestination p2sh = CScriptID(GetScriptForDestination(segwit));
+ return std::vector<CTxDestination>{std::move(keyid), std::move(p2sh), std::move(segwit)};
+ } else {
+ return std::vector<CTxDestination>{std::move(keyid)};
+ }
+}
+
+CTxDestination CWallet::AddAndGetDestinationForScript(const CScript& script, OutputType type)
+{
+ // Note that scripts over 520 bytes are not yet supported.
+ switch (type) {
+ case OUTPUT_TYPE_LEGACY:
+ return CScriptID(script);
+ case OUTPUT_TYPE_P2SH_SEGWIT:
+ case OUTPUT_TYPE_BECH32: {
+ WitnessV0ScriptHash hash;
+ CSHA256().Write(script.data(), script.size()).Finalize(hash.begin());
+ CTxDestination witdest = hash;
+ CScript witprog = GetScriptForDestination(witdest);
+ // Check if the resulting program is solvable (i.e. doesn't use an uncompressed key)
+ if (!IsSolvable(*this, witprog)) return CScriptID(script);
+ // Add the redeemscript, so that P2WSH and P2SH-P2WSH outputs are recognized as ours.
+ AddCScript(witprog);
+ if (type == OUTPUT_TYPE_BECH32) {
+ return witdest;
+ } else {
+ return CScriptID(witprog);
+ }
+ }
+ default: assert(false);
+ }
+}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 08950869b7..53a2c6b9d5 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -99,6 +99,19 @@ enum WalletFeature
FEATURE_LATEST = FEATURE_COMPRPUBKEY // HD is optional, use FEATURE_COMPRPUBKEY as latest version
};
+enum OutputType
+{
+ OUTPUT_TYPE_NONE,
+ OUTPUT_TYPE_LEGACY,
+ OUTPUT_TYPE_P2SH_SEGWIT,
+ OUTPUT_TYPE_BECH32,
+
+ OUTPUT_TYPE_DEFAULT = OUTPUT_TYPE_P2SH_SEGWIT
+};
+
+extern OutputType g_address_type;
+extern OutputType g_change_type;
+
/** A key pool entry */
class CKeyPool
@@ -923,7 +936,7 @@ public:
int64_t IncOrderPosNext(CWalletDB *pwalletdb = nullptr);
DBErrors ReorderTransactions();
bool AccountMove(std::string strFrom, std::string strTo, CAmount nAmount, std::string strComment = "");
- bool GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bForceNew = false);
+ bool GetAccountDestination(CTxDestination &dest, std::string strAccount, bool bForceNew = false);
void MarkDirty();
bool AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose=true);
@@ -1129,6 +1142,26 @@ public:
* deadlock
*/
void BlockUntilSyncedToCurrentChain();
+
+ /**
+ * Explicitly make the wallet learn the related scripts for outputs to the
+ * given key. This is purely to make the wallet file compatible with older
+ * software, as CBasicKeyStore automatically does this implicitly for all
+ * keys now.
+ */
+ void LearnRelatedScripts(const CPubKey& key, OutputType);
+
+ /**
+ * Same as LearnRelatedScripts, but when the OutputType is not known (and could
+ * be anything).
+ */
+ void LearnAllRelatedScripts(const CPubKey& key);
+
+ /**
+ * Get a destination of the requested type (if possible) to the specified script.
+ * This function will automatically add the necessary scripts to the wallet.
+ */
+ CTxDestination AddAndGetDestinationForScript(const CScript& script, OutputType);
};
/** A key allocated from the key pool. */
@@ -1218,4 +1251,16 @@ bool CWallet::DummySignTx(CMutableTransaction &txNew, const ContainerType &coins
return true;
}
+OutputType ParseOutputType(const std::string& str, OutputType default_type = OUTPUT_TYPE_DEFAULT);
+const std::string& FormatOutputType(OutputType type);
+
+/**
+ * Get a destination of the requested type (if possible) to the specified key.
+ * The caller must make sure LearnRelatedScripts has been called beforehand.
+ */
+CTxDestination GetDestinationForKey(const CPubKey& key, OutputType);
+
+/** Get all destinations (potentially) supported by the wallet for the given key. */
+std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key);
+
#endif // BITCOIN_WALLET_WALLET_H
diff --git a/test/functional/README.md b/test/functional/README.md
index 6be4d9cfab..d6ce490ab3 100644
--- a/test/functional/README.md
+++ b/test/functional/README.md
@@ -17,7 +17,7 @@ don't have test cases for.
#### Style guidelines
-- Where possible, try to adhere to [PEP-8 guidelines]([https://www.python.org/dev/peps/pep-0008/)
+- Where possible, try to adhere to [PEP-8 guidelines](https://www.python.org/dev/peps/pep-0008/)
- Use a python linter like flake8 before submitting PRs to catch common style
nits (eg trailing whitespace, unused imports, etc)
- Avoid wildcard imports where possible
@@ -27,6 +27,20 @@ don't have test cases for.
`set_test_params()`, `add_options()` and `setup_xxxx()` methods at the top of
the subclass, then locally-defined helper methods, then the `run_test()` method.
+#### Naming guidelines
+
+- Name the test `<area>_test.py`, where area can be one of the following:
+ - `feature` for tests for full features that aren't wallet/mining/mempool, eg `feature_rbf.py`
+ - `interface` for tests for other interfaces (REST, ZMQ, etc), eg `interface_rest.py`
+ - `mempool` for tests for mempool behaviour, eg `mempool_reorg.py`
+ - `mining` for tests for mining features, eg `mining_prioritisetransaction.py`
+ - `p2p` for tests that explicitly test the p2p interface, eg `p2p_disconnect_ban.py`
+ - `rpc` for tests for individual RPC methods or features, eg `rpc_listtransactions.py`
+ - `wallet` for tests for wallet features, eg `wallet_keypool.py`
+- use an underscore to separate words
+ - exception: for tests for specific RPCs or command line options which don't include underscores, name the test after the exact RPC or argument name, eg `rpc_decodescript.py`, not `rpc_decode_script.py`
+- Don't use the redundant word `test` in the name, eg `interface_zmq.py`, not `interface_zmq_test.py`
+
#### General test-writing advice
- Set `self.num_nodes` to the minimum number of nodes necessary for the test.
@@ -73,7 +87,7 @@ start the networking thread. (Continue with the test logic in your existing
thread.)
- Can be used to write tests where specific P2P protocol behavior is tested.
-Examples tests are `p2p-accept-block.py`, `p2p-compactblocks.py`.
+Examples tests are `p2p-acceptblock.py`, `p2p-compactblocks.py`.
#### Comptool
diff --git a/test/functional/address_types.py b/test/functional/address_types.py
new file mode 100755
index 0000000000..cb119c04b0
--- /dev/null
+++ b/test/functional/address_types.py
@@ -0,0 +1,199 @@
+#!/usr/bin/env python3
+# Copyright (c) 2017 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test that the wallet can send and receive using all combinations of address types.
+
+There are 4 nodes-under-test:
+ - node0 uses legacy addresses
+ - node1 uses p2sh/segwit addresses
+ - node2 uses p2sh/segwit addresses and bech32 addresses for change
+ - node3 uses bech32 addresses
+
+node4 exists to generate new blocks.
+
+The script is a series of tests, iterating over the 4 nodes. In each iteration
+of the test, one node sends:
+ - 10/101th of its balance to itself (using getrawchangeaddress for single key addresses)
+ - 20/101th to the next node
+ - 30/101th to the node after that
+ - 40/101th to the remaining node
+ - 1/101th remains as fee+change
+
+Iterate over each node for single key addresses, and then over each node for
+multisig addresses. In a second iteration, the same is done, but with explicit address_type
+parameters passed to getnewaddress and getrawchangeaddress. Node0 and node3 send to p2sh,
+node 1 sends to bech32, and node2 sends to legacy. As every node sends coins after receiving,
+this also verifies that spending coins sent to all these address types works."""
+
+from decimal import Decimal
+import itertools
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal, assert_greater_than, connect_nodes_bi, sync_blocks, sync_mempools
+
+class AddressTypeTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 5
+ self.extra_args = [["-addresstype=legacy"], ["-addresstype=p2sh-segwit"], ["-addresstype=p2sh-segwit", "-changetype=bech32"], ["-addresstype=bech32"], []]
+
+ def setup_network(self):
+ self.setup_nodes()
+
+ # Fully mesh-connect nodes for faster mempool sync
+ for i, j in itertools.product(range(self.num_nodes), repeat=2):
+ if i > j:
+ connect_nodes_bi(self.nodes, i, j)
+ self.sync_all()
+
+ def get_balances(self, confirmed=True):
+ """Return a list of confirmed or unconfirmed balances."""
+ if confirmed:
+ return [self.nodes[i].getbalance() for i in range(4)]
+ else:
+ return [self.nodes[i].getunconfirmedbalance() for i in range(4)]
+
+ def test_address(self, node, address, multisig, typ):
+ """Run sanity checks on an address."""
+ info = self.nodes[node].validateaddress(address)
+ assert(info['isvalid'])
+ if not multisig and typ == 'legacy':
+ # P2PKH
+ assert(not info['isscript'])
+ assert(not info['iswitness'])
+ assert('pubkey' in info)
+ elif not multisig and typ == 'p2sh-segwit':
+ # P2SH-P2WPKH
+ assert(info['isscript'])
+ assert(not info['iswitness'])
+ assert_equal(info['script'], 'witness_v0_keyhash')
+ assert('pubkey' in info)
+ elif not multisig and typ == 'bech32':
+ # P2WPKH
+ assert(not info['isscript'])
+ assert(info['iswitness'])
+ assert_equal(info['witness_version'], 0)
+ assert_equal(len(info['witness_program']), 40)
+ assert('pubkey' in info)
+ elif typ == 'legacy':
+ # P2SH-multisig
+ assert(info['isscript'])
+ assert_equal(info['script'], 'multisig')
+ assert(not info['iswitness'])
+ assert('pubkeys' in info)
+ elif typ == 'p2sh-segwit':
+ # P2SH-P2WSH-multisig
+ assert(info['isscript'])
+ assert_equal(info['script'], 'witness_v0_scripthash')
+ assert(not info['iswitness'])
+ assert(info['embedded']['isscript'])
+ assert_equal(info['embedded']['script'], 'multisig')
+ assert(info['embedded']['iswitness'])
+ assert_equal(info['embedded']['witness_version'], 0)
+ assert_equal(len(info['embedded']['witness_program']), 64)
+ assert('pubkeys' in info['embedded'])
+ elif typ == 'bech32':
+ # P2WSH-multisig
+ assert(info['isscript'])
+ assert_equal(info['script'], 'multisig')
+ assert(info['iswitness'])
+ assert_equal(info['witness_version'], 0)
+ assert_equal(len(info['witness_program']), 64)
+ assert('pubkeys' in info)
+ else:
+ # Unknown type
+ assert(False)
+
+ def run_test(self):
+ # Mine 101 blocks on node4 to bring nodes out of IBD and make sure that
+ # no coinbases are maturing for the nodes-under-test during the test
+ self.nodes[4].generate(101)
+ sync_blocks(self.nodes)
+
+ uncompressed_1 = "0496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858ee"
+ uncompressed_2 = "047211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073dee6c89064984f03385237d92167c13e236446b417ab79a0fcae412ae3316b77"
+ compressed_1 = "0296b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52"
+ compressed_2 = "037211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073"
+
+ # addmultisigaddress with at least 1 uncompressed key should return a legacy address.
+ for node in range(4):
+ self.test_address(node, self.nodes[node].addmultisigaddress(2, [uncompressed_1, uncompressed_2]), True, 'legacy')
+ self.test_address(node, self.nodes[node].addmultisigaddress(2, [compressed_1, uncompressed_2]), True, 'legacy')
+ self.test_address(node, self.nodes[node].addmultisigaddress(2, [uncompressed_1, compressed_2]), True, 'legacy')
+ # addmultisigaddress with all compressed keys should return the appropriate address type (even when the keys are not ours).
+ self.test_address(0, self.nodes[0].addmultisigaddress(2, [compressed_1, compressed_2]), True, 'legacy')
+ self.test_address(1, self.nodes[1].addmultisigaddress(2, [compressed_1, compressed_2]), True, 'p2sh-segwit')
+ self.test_address(2, self.nodes[2].addmultisigaddress(2, [compressed_1, compressed_2]), True, 'p2sh-segwit')
+ self.test_address(3, self.nodes[3].addmultisigaddress(2, [compressed_1, compressed_2]), True, 'bech32')
+
+ for explicit_type, multisig, from_node in itertools.product([False, True], [False, True], range(4)):
+ address_type = None
+ if explicit_type and not multisig:
+ if from_node == 1:
+ address_type = 'bech32'
+ elif from_node == 0 or from_node == 3:
+ address_type = 'p2sh-segwit'
+ else:
+ address_type = 'legacy'
+ self.log.info("Sending from node {} ({}) with{} multisig using {}".format(from_node, self.extra_args[from_node], "" if multisig else "out", "default" if address_type is None else address_type))
+ old_balances = self.get_balances()
+ self.log.debug("Old balances are {}".format(old_balances))
+ to_send = (old_balances[from_node] / 101).quantize(Decimal("0.00000001"))
+ sends = {}
+
+ self.log.debug("Prepare sends")
+ for n, to_node in enumerate(range(from_node, from_node + 4)):
+ to_node %= 4
+ change = False
+ if not multisig:
+ if from_node == to_node:
+ # When sending non-multisig to self, use getrawchangeaddress
+ address = self.nodes[to_node].getrawchangeaddress(address_type=address_type)
+ change = True
+ else:
+ address = self.nodes[to_node].getnewaddress(address_type=address_type)
+ else:
+ addr1 = self.nodes[to_node].getnewaddress()
+ addr2 = self.nodes[to_node].getnewaddress()
+ address = self.nodes[to_node].addmultisigaddress(2, [addr1, addr2])
+
+ # Do some sanity checking on the created address
+ if address_type is not None:
+ typ = address_type
+ elif to_node == 0:
+ typ = 'legacy'
+ elif to_node == 1 or (to_node == 2 and not change):
+ typ = 'p2sh-segwit'
+ else:
+ typ = 'bech32'
+ self.test_address(to_node, address, multisig, typ)
+
+ # Output entry
+ sends[address] = to_send * 10 * (1 + n)
+
+ self.log.debug("Sending: {}".format(sends))
+ self.nodes[from_node].sendmany("", sends)
+ sync_mempools(self.nodes)
+
+ unconf_balances = self.get_balances(False)
+ self.log.debug("Check unconfirmed balances: {}".format(unconf_balances))
+ assert_equal(unconf_balances[from_node], 0)
+ for n, to_node in enumerate(range(from_node + 1, from_node + 4)):
+ to_node %= 4
+ assert_equal(unconf_balances[to_node], to_send * 10 * (2 + n))
+
+ # node4 collects fee and block subsidy to keep accounting simple
+ self.nodes[4].generate(1)
+ sync_blocks(self.nodes)
+
+ new_balances = self.get_balances()
+ self.log.debug("Check new balances: {}".format(new_balances))
+ # We don't know what fee was set, so we can only check bounds on the balance of the sending node
+ assert_greater_than(new_balances[from_node], to_send * 10)
+ assert_greater_than(to_send * 11, new_balances[from_node])
+ for n, to_node in enumerate(range(from_node + 1, from_node + 4)):
+ to_node %= 4
+ assert_equal(new_balances[to_node], old_balances[to_node] + to_send * 10 * (2 + n))
+
+if __name__ == '__main__':
+ AddressTypeTest().main()
diff --git a/test/functional/bip68-112-113-p2p.py b/test/functional/bip68-112-113-p2p.py
index 30ceed3be7..82aa0ff891 100755
--- a/test/functional/bip68-112-113-p2p.py
+++ b/test/functional/bip68-112-113-p2p.py
@@ -95,7 +95,7 @@ class BIP68_112_113Test(ComparisonTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
- self.extra_args = [['-whitelist=127.0.0.1', '-blockversion=4']]
+ self.extra_args = [['-whitelist=127.0.0.1', '-blockversion=4', '-addresstype=legacy']]
def run_test(self):
test = TestManager(self, self.options.tmpdir)
diff --git a/test/functional/bip68-sequence.py b/test/functional/bip68-sequence.py
index 2acdd72aff..94b13653b9 100755
--- a/test/functional/bip68-sequence.py
+++ b/test/functional/bip68-sequence.py
@@ -362,9 +362,10 @@ class BIP68Test(BitcoinTestFramework):
block.vtx.extend([tx1, tx2, tx3])
block.hashMerkleRoot = block.calc_merkle_root()
block.rehash()
+ add_witness_commitment(block)
block.solve()
- self.nodes[0].submitblock(ToHex(block))
+ self.nodes[0].submitblock(bytes_to_hex_str(block.serialize(True)))
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
def activateCSV(self):
diff --git a/test/functional/bumpfee.py b/test/functional/bumpfee.py
index ffd5ac197b..1e5620736b 100755
--- a/test/functional/bumpfee.py
+++ b/test/functional/bumpfee.py
@@ -14,7 +14,7 @@ added in the future, they should try to follow the same convention and not
make assumptions about execution order.
"""
-from segwit import send_to_witness
+from test_framework.blocktools import send_to_witness
from test_framework.test_framework import BitcoinTestFramework
from test_framework import blocktools
from test_framework.mininode import CTransaction
@@ -194,7 +194,7 @@ def test_settxfee(rbf_node, dest_address):
requested_feerate = Decimal("0.00025000")
rbf_node.settxfee(requested_feerate)
bumped_tx = rbf_node.bumpfee(rbfid)
- actual_feerate = bumped_tx["fee"] * 1000 / rbf_node.getrawtransaction(bumped_tx["txid"], True)["size"]
+ actual_feerate = bumped_tx["fee"] * 1000 / rbf_node.getrawtransaction(bumped_tx["txid"], True)["vsize"]
# Assert that the difference between the requested feerate and the actual
# feerate of the bumped transaction is small.
assert_greater_than(Decimal("0.00001000"), abs(requested_feerate - actual_feerate))
@@ -290,6 +290,7 @@ def submit_block_with_tx(node, tx):
block.vtx.append(ctx)
block.rehash()
block.hashMerkleRoot = block.calc_merkle_root()
+ blocktools.add_witness_commitment(block)
block.solve()
node.submitblock(bytes_to_hex_str(block.serialize(True)))
return block
diff --git a/test/functional/create_cache.py b/test/functional/create_cache.py
index 4c79814a26..9665c50a92 100755
--- a/test/functional/create_cache.py
+++ b/test/functional/create_cache.py
@@ -16,6 +16,7 @@ class CreateCache(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 0
+ self.supports_cli = True
def setup_network(self):
pass
diff --git a/test/functional/import-rescan.py b/test/functional/import-rescan.py
index 7d5fbb8d65..d193a99d5b 100755
--- a/test/functional/import-rescan.py
+++ b/test/functional/import-rescan.py
@@ -119,7 +119,7 @@ class ImportRescanTest(BitcoinTestFramework):
self.num_nodes = 2 + len(IMPORT_NODES)
def setup_network(self):
- extra_args = [[] for _ in range(self.num_nodes)]
+ extra_args = [["-addresstype=legacy"] for _ in range(self.num_nodes)]
for i, import_node in enumerate(IMPORT_NODES, 2):
if import_node.prune:
extra_args[i] += ["-prune=1"]
diff --git a/test/functional/importmulti.py b/test/functional/importmulti.py
index 35cb5b6768..ab4ca48e48 100755
--- a/test/functional/importmulti.py
+++ b/test/functional/importmulti.py
@@ -9,6 +9,7 @@ from test_framework.util import *
class ImportMultiTest (BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
+ self.extra_args = [["-addresstype=legacy"], ["-addresstype=legacy"]]
self.setup_clean_chain = True
def setup_network(self):
diff --git a/test/functional/mempool_limit.py b/test/functional/mempool_limit.py
index b2f718eb01..e7ce3820d2 100755
--- a/test/functional/mempool_limit.py
+++ b/test/functional/mempool_limit.py
@@ -17,10 +17,14 @@ class MempoolLimitTest(BitcoinTestFramework):
txouts = gen_return_txouts()
relayfee = self.nodes[0].getnetworkinfo()['relayfee']
+ self.log.info('Check that mempoolminfee is minrelytxfee')
+ assert_equal(self.nodes[0].getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000'))
+ assert_equal(self.nodes[0].getmempoolinfo()['mempoolminfee'], Decimal('0.00001000'))
+
txids = []
utxos = create_confirmed_utxos(relayfee, self.nodes[0], 91)
- #create a mempool tx that will be evicted
+ self.log.info('Create a mempool tx that will be evicted')
us0 = utxos.pop()
inputs = [{ "txid" : us0["txid"], "vout" : us0["vout"]}]
outputs = {self.nodes[0].getnewaddress() : 0.0001}
@@ -37,10 +41,14 @@ class MempoolLimitTest(BitcoinTestFramework):
txids.append([])
txids[i] = create_lots_of_big_transactions(self.nodes[0], txouts, utxos[30*i:30*i+30], 30, (i+1)*base_fee)
- # by now, the tx should be evicted, check confirmation state
+ self.log.info('The tx should be evicted by now')
assert(txid not in self.nodes[0].getrawmempool())
txdata = self.nodes[0].gettransaction(txid)
assert(txdata['confirmations'] == 0) #confirmation should still be 0
+ self.log.info('Check that mempoolminfee is larger than minrelytxfee')
+ assert_equal(self.nodes[0].getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000'))
+ assert_greater_than(self.nodes[0].getmempoolinfo()['mempoolminfee'], Decimal('0.00001000'))
+
if __name__ == '__main__':
MempoolLimitTest().main()
diff --git a/test/functional/multiwallet.py b/test/functional/multiwallet.py
index d0c40e5446..12d9e9f48d 100755
--- a/test/functional/multiwallet.py
+++ b/test/functional/multiwallet.py
@@ -17,9 +17,16 @@ class MultiWalletTest(BitcoinTestFramework):
self.setup_clean_chain = True
self.num_nodes = 1
self.extra_args = [['-wallet=w1', '-wallet=w2', '-wallet=w3', '-wallet=w']]
+ self.supports_cli = True
def run_test(self):
- assert_equal(set(self.nodes[0].listwallets()), {"w1", "w2", "w3", "w"})
+ node = self.nodes[0]
+
+ data_dir = lambda *p: os.path.join(node.datadir, 'regtest', *p)
+ wallet_dir = lambda *p: data_dir('wallets', *p)
+ wallet = lambda name: node.get_wallet_rpc(name)
+
+ assert_equal(set(node.listwallets()), {"w1", "w2", "w3", "w"})
self.stop_node(0)
@@ -27,39 +34,38 @@ class MultiWalletTest(BitcoinTestFramework):
self.assert_start_raises_init_error(0, ['-wallet=w1', '-wallet=w1'], 'Error loading wallet w1. Duplicate -wallet filename specified.')
# should not initialize if wallet file is a directory
- wallet_dir = os.path.join(self.options.tmpdir, 'node0', 'regtest', 'wallets')
- os.mkdir(os.path.join(wallet_dir, 'w11'))
+ os.mkdir(wallet_dir('w11'))
self.assert_start_raises_init_error(0, ['-wallet=w11'], 'Error loading wallet w11. -wallet filename must be a regular file.')
# should not initialize if one wallet is a copy of another
- shutil.copyfile(os.path.join(wallet_dir, 'w2'), os.path.join(wallet_dir, 'w22'))
+ shutil.copyfile(wallet_dir('w2'), wallet_dir('w22'))
self.assert_start_raises_init_error(0, ['-wallet=w2', '-wallet=w22'], 'duplicates fileid')
# should not initialize if wallet file is a symlink
- os.symlink(os.path.join(wallet_dir, 'w1'), os.path.join(wallet_dir, 'w12'))
+ os.symlink(wallet_dir('w1'), wallet_dir('w12'))
self.assert_start_raises_init_error(0, ['-wallet=w12'], 'Error loading wallet w12. -wallet filename must be a regular file.')
# should not initialize if the specified walletdir does not exist
self.assert_start_raises_init_error(0, ['-walletdir=bad'], 'Error: Specified -walletdir "bad" does not exist')
# should not initialize if the specified walletdir is not a directory
- not_a_dir = os.path.join(wallet_dir, 'notadir')
+ not_a_dir = wallet_dir('notadir')
open(not_a_dir, 'a').close()
- self.assert_start_raises_init_error(0, ['-walletdir='+not_a_dir], 'Error: Specified -walletdir "' + not_a_dir + '" is not a directory')
+ self.assert_start_raises_init_error(0, ['-walletdir=' + not_a_dir], 'Error: Specified -walletdir "' + not_a_dir + '" is not a directory')
# if wallets/ doesn't exist, datadir should be the default wallet dir
- wallet_dir2 = os.path.join(self.options.tmpdir, 'node0', 'regtest', 'walletdir')
- os.rename(wallet_dir, wallet_dir2)
+ wallet_dir2 = data_dir('walletdir')
+ os.rename(wallet_dir(), wallet_dir2)
self.start_node(0, ['-wallet=w4', '-wallet=w5'])
- assert_equal(set(self.nodes[0].listwallets()), {"w4", "w5"})
- w5 = self.nodes[0].get_wallet_rpc("w5")
+ assert_equal(set(node.listwallets()), {"w4", "w5"})
+ w5 = wallet("w5")
w5.generate(1)
self.stop_node(0)
# now if wallets/ exists again, but the rootdir is specified as the walletdir, w4 and w5 should still be loaded
- os.rename(wallet_dir2, wallet_dir)
- self.start_node(0, ['-wallet=w4', '-wallet=w5', '-walletdir=' + os.path.join(self.options.tmpdir, 'node0', 'regtest')])
- assert_equal(set(self.nodes[0].listwallets()), {"w4", "w5"})
- w5 = self.nodes[0].get_wallet_rpc("w5")
+ os.rename(wallet_dir2, wallet_dir())
+ self.start_node(0, ['-wallet=w4', '-wallet=w5', '-walletdir=' + data_dir()])
+ assert_equal(set(node.listwallets()), {"w4", "w5"})
+ w5 = wallet("w5")
w5_info = w5.getwalletinfo()
assert_equal(w5_info['immature_balance'], 50)
@@ -67,11 +73,11 @@ class MultiWalletTest(BitcoinTestFramework):
self.start_node(0, self.extra_args[0])
- w1 = self.nodes[0].get_wallet_rpc("w1")
- w2 = self.nodes[0].get_wallet_rpc("w2")
- w3 = self.nodes[0].get_wallet_rpc("w3")
- w4 = self.nodes[0].get_wallet_rpc("w")
- wallet_bad = self.nodes[0].get_wallet_rpc("bad")
+ w1 = wallet("w1")
+ w2 = wallet("w2")
+ w3 = wallet("w3")
+ w4 = wallet("w")
+ wallet_bad = wallet("bad")
w1.generate(1)
@@ -79,7 +85,7 @@ class MultiWalletTest(BitcoinTestFramework):
assert_raises_rpc_error(-18, "Requested wallet does not exist or is not loaded", wallet_bad.getwalletinfo)
# accessing wallet RPC without using wallet endpoint fails
- assert_raises_rpc_error(-19, "Wallet file not specified", self.nodes[0].getwalletinfo)
+ assert_raises_rpc_error(-19, "Wallet file not specified", node.getwalletinfo)
# check w1 wallet balance
w1_info = w1.getwalletinfo()
diff --git a/test/functional/nulldummy.py b/test/functional/nulldummy.py
index a94fe8204f..110b7a655e 100755
--- a/test/functional/nulldummy.py
+++ b/test/functional/nulldummy.py
@@ -42,7 +42,7 @@ class NULLDUMMYTest(BitcoinTestFramework):
self.setup_clean_chain = True
# This script tests NULLDUMMY activation, which is part of the 'segwit' deployment, so we go through
# normal segwit activation here (and don't use the default always-on behaviour).
- self.extra_args = [['-whitelist=127.0.0.1', '-walletprematurewitness', '-vbparams=segwit:0:999999999999']]
+ self.extra_args = [['-whitelist=127.0.0.1', '-walletprematurewitness', '-vbparams=segwit:0:999999999999', '-addresstype=legacy']]
def run_test(self):
self.address = self.nodes[0].getnewaddress()
diff --git a/test/functional/p2p-fullblocktest.py b/test/functional/p2p-fullblocktest.py
index d08318a96b..fe9bbda14b 100755
--- a/test/functional/p2p-fullblocktest.py
+++ b/test/functional/p2p-fullblocktest.py
@@ -36,12 +36,15 @@ class CBrokenBlock(CBlock):
self.vtx = copy.deepcopy(base_block.vtx)
self.hashMerkleRoot = self.calc_merkle_root()
- def serialize(self):
+ def serialize(self, with_witness=False):
r = b""
r += super(CBlock, self).serialize()
r += struct.pack("<BQ", 255, len(self.vtx))
for tx in self.vtx:
- r += tx.serialize()
+ if with_witness:
+ r += tx.serialize_with_witness()
+ else:
+ r += tx.serialize_without_witness()
return r
def normal_serialize(self):
diff --git a/test/functional/p2p-segwit.py b/test/functional/p2p-segwit.py
index 2487fdc239..20e4805df0 100755
--- a/test/functional/p2p-segwit.py
+++ b/test/functional/p2p-segwit.py
@@ -25,7 +25,7 @@ MAX_SIGOP_COST = 80000
# Calculate the virtual size of a witness block:
# (base + witness/4)
def get_virtual_size(witness_block):
- base_size = len(witness_block.serialize())
+ base_size = len(witness_block.serialize(with_witness=False))
total_size = len(witness_block.serialize(with_witness=True))
# the "+3" is so we round up
vsize = int((3*base_size + total_size + 3)/4)
@@ -1396,18 +1396,20 @@ class SegWitTest(BitcoinTestFramework):
temp_utxos.pop(0)
- # Update self.utxos for later tests. Just spend everything in
- # temp_utxos to a corresponding entry in self.utxos
+ # Update self.utxos for later tests by creating two outputs
+ # that consolidate all the coins in temp_utxos.
+ output_value = sum(i.nValue for i in temp_utxos) // 2
+
tx = CTransaction()
index = 0
+ # Just spend to our usual anyone-can-spend output
+ tx.vout = [CTxOut(output_value, CScript([OP_TRUE]))] * 2
for i in temp_utxos:
- # Just spend to our usual anyone-can-spend output
- # Use SIGHASH_SINGLE|SIGHASH_ANYONECANPAY so we can build up
+ # Use SIGHASH_ALL|SIGHASH_ANYONECANPAY so we can build up
# the signatures as we go.
tx.vin.append(CTxIn(COutPoint(i.sha256, i.n), b""))
- tx.vout.append(CTxOut(i.nValue, CScript([OP_TRUE])))
tx.wit.vtxinwit.append(CTxInWitness())
- sign_P2PK_witness_input(witness_program, tx, index, SIGHASH_SINGLE|SIGHASH_ANYONECANPAY, i.nValue, key)
+ sign_P2PK_witness_input(witness_program, tx, index, SIGHASH_ALL|SIGHASH_ANYONECANPAY, i.nValue, key)
index += 1
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx])
diff --git a/test/functional/prioritise_transaction.py b/test/functional/prioritise_transaction.py
index c10042ddb2..57954ce321 100755
--- a/test/functional/prioritise_transaction.py
+++ b/test/functional/prioritise_transaction.py
@@ -15,6 +15,25 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
self.extra_args = [["-printpriority=1"], ["-printpriority=1"]]
def run_test(self):
+ # Test `prioritisetransaction` required parameters
+ assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction)
+ assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction, '')
+ assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction, '', 0)
+
+ # Test `prioritisetransaction` invalid extra parameters
+ assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction, '', 0, 0, 0)
+
+ # Test `prioritisetransaction` invalid `txid`
+ assert_raises_rpc_error(-1, "txid must be hexadecimal string", self.nodes[0].prioritisetransaction, txid='foo', fee_delta=0)
+
+ # Test `prioritisetransaction` invalid `dummy`
+ txid = '1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000'
+ assert_raises_rpc_error(-1, "JSON value is not a number as expected", self.nodes[0].prioritisetransaction, txid, 'foo', 0)
+ assert_raises_rpc_error(-8, "Priority is no longer supported, dummy argument to prioritisetransaction must be 0.", self.nodes[0].prioritisetransaction, txid, 1, 0)
+
+ # Test `prioritisetransaction` invalid `fee_delta`
+ assert_raises_rpc_error(-1, "JSON value is not an integer as expected", self.nodes[0].prioritisetransaction, txid=txid, fee_delta='foo')
+
self.txouts = gen_return_txouts()
self.relayfee = self.nodes[0].getnetworkinfo()['relayfee']
diff --git a/test/functional/rawtransactions.py b/test/functional/rawtransactions.py
index 073ed394c1..fe749adb49 100755
--- a/test/functional/rawtransactions.py
+++ b/test/functional/rawtransactions.py
@@ -39,6 +39,7 @@ class RawTransactionsTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 3
+ self.extra_args = [["-addresstype=legacy"], ["-addresstype=legacy"], ["-addresstype=legacy"]]
def setup_network(self, split=False):
super().setup_network()
@@ -189,7 +190,7 @@ class RawTransactionsTest(BitcoinTestFramework):
break
bal = self.nodes[0].getbalance()
- inputs = [{ "txid" : txId, "vout" : vout['n'], "scriptPubKey" : vout['scriptPubKey']['hex']}]
+ inputs = [{ "txid" : txId, "vout" : vout['n'], "scriptPubKey" : vout['scriptPubKey']['hex'], "amount" : vout['value']}]
outputs = { self.nodes[0].getnewaddress() : 2.19 }
rawTx = self.nodes[2].createrawtransaction(inputs, outputs)
rawTxPartialSigned = self.nodes[1].signrawtransaction(rawTx, inputs)
@@ -234,7 +235,7 @@ class RawTransactionsTest(BitcoinTestFramework):
break
bal = self.nodes[0].getbalance()
- inputs = [{ "txid" : txId, "vout" : vout['n'], "scriptPubKey" : vout['scriptPubKey']['hex'], "redeemScript" : mSigObjValid['hex']}]
+ inputs = [{ "txid" : txId, "vout" : vout['n'], "scriptPubKey" : vout['scriptPubKey']['hex'], "redeemScript" : mSigObjValid['hex'], "amount" : vout['value']}]
outputs = { self.nodes[0].getnewaddress() : 2.19 }
rawTx2 = self.nodes[2].createrawtransaction(inputs, outputs)
rawTxPartialSigned1 = self.nodes[1].signrawtransaction(rawTx2, inputs)
diff --git a/test/functional/segwit.py b/test/functional/segwit.py
index dbf4a86e38..7d5c760ad9 100755
--- a/test/functional/segwit.py
+++ b/test/functional/segwit.py
@@ -4,10 +4,18 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the SegWit changeover logic."""
+from test_framework.address import (
+ key_to_p2sh_p2wpkh,
+ key_to_p2wpkh,
+ program_to_witness,
+ script_to_p2sh_p2wsh,
+ script_to_p2wsh,
+)
+from test_framework.blocktools import witness_script, send_to_witness
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
from test_framework.mininode import sha256, CTransaction, CTxIn, COutPoint, CTxOut, COIN, ToHex, FromHex
-from test_framework.address import script_to_p2sh, key_to_p2pkh, key_to_p2sh_p2wpkh, key_to_p2wpkh, script_to_p2sh_p2wsh, script_to_p2wsh, program_to_witness
+from test_framework.address import script_to_p2sh, key_to_p2pkh
from test_framework.script import CScript, OP_HASH160, OP_CHECKSIG, OP_0, hash160, OP_EQUAL, OP_DUP, OP_EQUALVERIFY, OP_1, OP_2, OP_CHECKMULTISIG, OP_TRUE
from io import BytesIO
@@ -16,52 +24,6 @@ NODE_2 = 2
WIT_V0 = 0
WIT_V1 = 1
-# Create a scriptPubKey corresponding to either a P2WPKH output for the
-# given pubkey, or a P2WSH output of a 1-of-1 multisig for the given
-# pubkey. Returns the hex encoding of the scriptPubKey.
-def witness_script(use_p2wsh, pubkey):
- if (use_p2wsh == False):
- # P2WPKH instead
- pubkeyhash = hash160(hex_str_to_bytes(pubkey))
- pkscript = CScript([OP_0, pubkeyhash])
- else:
- # 1-of-1 multisig
- witness_program = CScript([OP_1, hex_str_to_bytes(pubkey), OP_1, OP_CHECKMULTISIG])
- scripthash = sha256(witness_program)
- pkscript = CScript([OP_0, scripthash])
- return bytes_to_hex_str(pkscript)
-
-# Return a transaction (in hex) that spends the given utxo to a segwit output,
-# optionally wrapping the segwit output using P2SH.
-def create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount):
- if use_p2wsh:
- program = CScript([OP_1, hex_str_to_bytes(pubkey), OP_1, OP_CHECKMULTISIG])
- addr = script_to_p2sh_p2wsh(program) if encode_p2sh else script_to_p2wsh(program)
- else:
- addr = key_to_p2sh_p2wpkh(pubkey) if encode_p2sh else key_to_p2wpkh(pubkey)
- if not encode_p2sh:
- assert_equal(node.validateaddress(addr)['scriptPubKey'], witness_script(use_p2wsh, pubkey))
- return node.createrawtransaction([utxo], {addr: amount})
-
-# Create a transaction spending a given utxo to a segwit output corresponding
-# to the given pubkey: use_p2wsh determines whether to use P2WPKH or P2WSH;
-# encode_p2sh determines whether to wrap in P2SH.
-# sign=True will have the given node sign the transaction.
-# insert_redeem_script will be added to the scriptSig, if given.
-def send_to_witness(use_p2wsh, node, utxo, pubkey, encode_p2sh, amount, sign=True, insert_redeem_script=""):
- tx_to_witness = create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount)
- if (sign):
- signed = node.signrawtransaction(tx_to_witness)
- assert("errors" not in signed or len(["errors"]) == 0)
- return node.sendrawtransaction(signed["hex"])
- else:
- if (insert_redeem_script):
- tx = FromHex(CTransaction(), tx_to_witness)
- tx.vin[0].scriptSig += CScript([hex_str_to_bytes(insert_redeem_script)])
- tx_to_witness = ToHex(tx)
-
- return node.sendrawtransaction(tx_to_witness)
-
def getutxo(txid):
utxo = {}
utxo["vout"] = 0
@@ -78,9 +40,9 @@ class SegWitTest(BitcoinTestFramework):
self.setup_clean_chain = True
self.num_nodes = 3
# This test tests SegWit both pre and post-activation, so use the normal BIP9 activation.
- self.extra_args = [["-walletprematurewitness", "-rpcserialversion=0", "-vbparams=segwit:0:999999999999"],
- ["-blockversion=4", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness", "-rpcserialversion=1", "-vbparams=segwit:0:999999999999"],
- ["-blockversion=536870915", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness", "-vbparams=segwit:0:999999999999"]]
+ self.extra_args = [["-walletprematurewitness", "-rpcserialversion=0", "-vbparams=segwit:0:999999999999", "-addresstype=legacy"],
+ ["-blockversion=4", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness", "-rpcserialversion=1", "-vbparams=segwit:0:999999999999", "-addresstype=legacy"],
+ ["-blockversion=536870915", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness", "-vbparams=segwit:0:999999999999", "-addresstype=legacy"]]
def setup_network(self):
super().setup_network()
@@ -135,9 +97,9 @@ class SegWitTest(BitcoinTestFramework):
self.pubkey.append(self.nodes[i].validateaddress(newaddress)["pubkey"])
multiaddress = self.nodes[i].addmultisigaddress(1, [self.pubkey[-1]])
multiscript = CScript([OP_1, hex_str_to_bytes(self.pubkey[-1]), OP_1, OP_CHECKMULTISIG])
- p2sh_addr = self.nodes[i].addwitnessaddress(newaddress, True)
+ p2sh_addr = self.nodes[i].addwitnessaddress(newaddress)
bip173_addr = self.nodes[i].addwitnessaddress(newaddress, False)
- p2sh_ms_addr = self.nodes[i].addwitnessaddress(multiaddress, True)
+ p2sh_ms_addr = self.nodes[i].addwitnessaddress(multiaddress)
bip173_ms_addr = self.nodes[i].addwitnessaddress(multiaddress, False)
assert_equal(p2sh_addr, key_to_p2sh_p2wpkh(self.pubkey[-1]))
assert_equal(bip173_addr, key_to_p2wpkh(self.pubkey[-1]))
@@ -356,8 +318,10 @@ class SegWitTest(BitcoinTestFramework):
[p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
# normal P2PKH and P2PK with compressed keys should always be spendable
spendable_anytime.extend([p2pkh, p2pk])
- # P2SH_P2PK, P2SH_P2PKH, and witness with compressed keys are spendable after direct importaddress
- spendable_after_importaddress.extend([p2wpkh, p2sh_p2wpkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
+ # P2SH_P2PK, P2SH_P2PKH with compressed keys are spendable after direct importaddress
+ spendable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
+ # P2WPKH and P2SH_P2WPKH with compressed keys should always be spendable
+ spendable_anytime.extend([p2wpkh, p2sh_p2wpkh])
for i in uncompressed_spendable_address:
v = self.nodes[0].validateaddress(i)
@@ -373,7 +337,7 @@ class SegWitTest(BitcoinTestFramework):
spendable_anytime.extend([p2pkh, p2pk])
# P2SH_P2PK and P2SH_P2PKH are spendable after direct importaddress
spendable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh])
- # witness with uncompressed keys are never seen
+ # Witness output types with uncompressed keys are never seen
unseen_anytime.extend([p2wpkh, p2sh_p2wpkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
for i in compressed_solvable_address:
@@ -384,10 +348,10 @@ class SegWitTest(BitcoinTestFramework):
solvable_after_importaddress.extend([bare, p2sh, p2wsh, p2sh_p2wsh])
else:
[p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
- # normal P2PKH and P2PK with compressed keys should always be seen
- solvable_anytime.extend([p2pkh, p2pk])
- # P2SH_P2PK, P2SH_P2PKH, and witness with compressed keys are seen after direct importaddress
- solvable_after_importaddress.extend([p2wpkh, p2sh_p2wpkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
+ # normal P2PKH, P2PK, P2WPKH and P2SH_P2WPKH with compressed keys should always be seen
+ solvable_anytime.extend([p2pkh, p2pk, p2wpkh, p2sh_p2wpkh])
+ # P2SH_P2PK, P2SH_P2PKH with compressed keys are seen after direct importaddress
+ solvable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
for i in uncompressed_solvable_address:
v = self.nodes[0].validateaddress(i)
@@ -403,7 +367,7 @@ class SegWitTest(BitcoinTestFramework):
solvable_anytime.extend([p2pkh, p2pk])
# P2SH_P2PK, P2SH_P2PKH with uncompressed keys are seen after direct importaddress
solvable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh])
- # witness with uncompressed keys are never seen
+ # Witness output types with uncompressed keys are never seen
unseen_anytime.extend([p2wpkh, p2sh_p2wpkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
op1 = CScript([OP_1])
@@ -496,6 +460,8 @@ class SegWitTest(BitcoinTestFramework):
spendable_after_addwitnessaddress = [] # These outputs should be seen after importaddress
solvable_after_addwitnessaddress=[] # These outputs should be seen after importaddress but not spendable
unseen_anytime = [] # These outputs should never be seen
+ solvable_anytime = [] # These outputs should be solvable after importpubkey
+ unseen_anytime = [] # These outputs should never be seen
uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], compressed_spendable_address[0]]))
uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], uncompressed_spendable_address[0]]))
@@ -514,9 +480,8 @@ class SegWitTest(BitcoinTestFramework):
premature_witaddress.append(script_to_p2sh(p2wsh))
else:
[p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
- # P2WPKH, P2SH_P2WPKH are spendable after addwitnessaddress
- spendable_after_addwitnessaddress.extend([p2wpkh, p2sh_p2wpkh])
- premature_witaddress.append(script_to_p2sh(p2wpkh))
+ # P2WPKH, P2SH_P2WPKH are always spendable
+ spendable_anytime.extend([p2wpkh, p2sh_p2wpkh])
for i in uncompressed_spendable_address + uncompressed_solvable_address:
v = self.nodes[0].validateaddress(i)
@@ -538,10 +503,11 @@ class SegWitTest(BitcoinTestFramework):
premature_witaddress.append(script_to_p2sh(p2wsh))
else:
[p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
- # P2SH_P2PK, P2SH_P2PKH with compressed keys are seen after addwitnessaddress
- solvable_after_addwitnessaddress.extend([p2wpkh, p2sh_p2wpkh])
- premature_witaddress.append(script_to_p2sh(p2wpkh))
+ # P2SH_P2PK, P2SH_P2PKH with compressed keys are always solvable
+ solvable_anytime.extend([p2wpkh, p2sh_p2wpkh])
+ self.mine_and_test_listunspent(spendable_anytime, 2)
+ self.mine_and_test_listunspent(solvable_anytime, 1)
self.mine_and_test_listunspent(spendable_after_addwitnessaddress + solvable_after_addwitnessaddress + unseen_anytime, 0)
# addwitnessaddress should refuse to return a witness address if an uncompressed key is used
@@ -558,8 +524,8 @@ class SegWitTest(BitcoinTestFramework):
witaddress = self.nodes[0].addwitnessaddress(i)
assert_equal(witaddress, self.nodes[0].addwitnessaddress(witaddress))
- spendable_txid.append(self.mine_and_test_listunspent(spendable_after_addwitnessaddress, 2))
- solvable_txid.append(self.mine_and_test_listunspent(solvable_after_addwitnessaddress, 1))
+ spendable_txid.append(self.mine_and_test_listunspent(spendable_after_addwitnessaddress + spendable_anytime, 2))
+ solvable_txid.append(self.mine_and_test_listunspent(solvable_after_addwitnessaddress + solvable_anytime, 1))
self.mine_and_test_listunspent(unseen_anytime, 0)
# Check that createrawtransaction/decoderawtransaction with non-v0 Bech32 works
diff --git a/test/functional/signmessages.py b/test/functional/signmessages.py
index c1939b432b..5b6935ceea 100755
--- a/test/functional/signmessages.py
+++ b/test/functional/signmessages.py
@@ -11,6 +11,7 @@ class SignMessagesTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
+ self.extra_args = [["-addresstype=legacy"]]
def run_test(self):
message = 'This is just a test message'
diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py
index 93af0037e9..642ef98a27 100644
--- a/test/functional/test_framework/blocktools.py
+++ b/test/functional/test_framework/blocktools.py
@@ -4,8 +4,24 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Utilities for manipulating blocks and transactions."""
+from .address import (
+ key_to_p2sh_p2wpkh,
+ key_to_p2wpkh,
+ script_to_p2sh_p2wsh,
+ script_to_p2wsh,
+)
from .mininode import *
-from .script import CScript, OP_TRUE, OP_CHECKSIG, OP_RETURN
+from .script import (
+ CScript,
+ OP_0,
+ OP_1,
+ OP_CHECKMULTISIG,
+ OP_CHECKSIG,
+ OP_RETURN,
+ OP_TRUE,
+ hash160,
+)
+from .util import assert_equal
# Create a block (with regtest difficulty)
def create_block(hashprev, coinbase, nTime=None):
@@ -108,3 +124,49 @@ def get_legacy_sigopcount_tx(tx, fAccurate=True):
# scriptSig might be of type bytes, so convert to CScript for the moment
count += CScript(j.scriptSig).GetSigOpCount(fAccurate)
return count
+
+# Create a scriptPubKey corresponding to either a P2WPKH output for the
+# given pubkey, or a P2WSH output of a 1-of-1 multisig for the given
+# pubkey. Returns the hex encoding of the scriptPubKey.
+def witness_script(use_p2wsh, pubkey):
+ if (use_p2wsh == False):
+ # P2WPKH instead
+ pubkeyhash = hash160(hex_str_to_bytes(pubkey))
+ pkscript = CScript([OP_0, pubkeyhash])
+ else:
+ # 1-of-1 multisig
+ witness_program = CScript([OP_1, hex_str_to_bytes(pubkey), OP_1, OP_CHECKMULTISIG])
+ scripthash = sha256(witness_program)
+ pkscript = CScript([OP_0, scripthash])
+ return bytes_to_hex_str(pkscript)
+
+# Return a transaction (in hex) that spends the given utxo to a segwit output,
+# optionally wrapping the segwit output using P2SH.
+def create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount):
+ if use_p2wsh:
+ program = CScript([OP_1, hex_str_to_bytes(pubkey), OP_1, OP_CHECKMULTISIG])
+ addr = script_to_p2sh_p2wsh(program) if encode_p2sh else script_to_p2wsh(program)
+ else:
+ addr = key_to_p2sh_p2wpkh(pubkey) if encode_p2sh else key_to_p2wpkh(pubkey)
+ if not encode_p2sh:
+ assert_equal(node.validateaddress(addr)['scriptPubKey'], witness_script(use_p2wsh, pubkey))
+ return node.createrawtransaction([utxo], {addr: amount})
+
+# Create a transaction spending a given utxo to a segwit output corresponding
+# to the given pubkey: use_p2wsh determines whether to use P2WPKH or P2WSH;
+# encode_p2sh determines whether to wrap in P2SH.
+# sign=True will have the given node sign the transaction.
+# insert_redeem_script will be added to the scriptSig, if given.
+def send_to_witness(use_p2wsh, node, utxo, pubkey, encode_p2sh, amount, sign=True, insert_redeem_script=""):
+ tx_to_witness = create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount)
+ if (sign):
+ signed = node.signrawtransaction(tx_to_witness)
+ assert("errors" not in signed or len(["errors"]) == 0)
+ return node.sendrawtransaction(signed["hex"])
+ else:
+ if (insert_redeem_script):
+ tx = FromHex(CTransaction(), tx_to_witness)
+ tx.vin[0].scriptSig += CScript([hex_str_to_bytes(insert_redeem_script)])
+ tx_to_witness = ToHex(tx)
+
+ return node.sendrawtransaction(tx_to_witness)
diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py
index d8032e4430..a54a0299c7 100644
--- a/test/functional/test_framework/messages.py
+++ b/test/functional/test_framework/messages.py
@@ -453,10 +453,10 @@ class CTransaction():
r += struct.pack("<I", self.nLockTime)
return r
- # Regular serialization is without witness -- must explicitly
- # call serialize_with_witness to include witness data.
+ # Regular serialization is with witness -- must explicitly
+ # call serialize_without_witness to exclude witness data.
def serialize(self):
- return self.serialize_without_witness()
+ return self.serialize_with_witness()
# Recalculate the txid (transaction hash without witness)
def rehash(self):
@@ -472,7 +472,7 @@ class CTransaction():
if self.sha256 is None:
self.sha256 = uint256_from_str(hash256(self.serialize_without_witness()))
- self.hash = encode(hash256(self.serialize())[::-1], 'hex_codec').decode('ascii')
+ self.hash = encode(hash256(self.serialize_without_witness())[::-1], 'hex_codec').decode('ascii')
def is_valid(self):
self.calc_sha256()
@@ -569,7 +569,7 @@ class CBlock(CBlockHeader):
if with_witness:
r += ser_vector(self.vtx, "serialize_with_witness")
else:
- r += ser_vector(self.vtx)
+ r += ser_vector(self.vtx, "serialize_without_witness")
return r
# Calculate the merkle root given a vector of transaction hashes
@@ -636,7 +636,7 @@ class PrefilledTransaction():
self.tx = CTransaction()
self.tx.deserialize(f)
- def serialize(self, with_witness=False):
+ def serialize(self, with_witness=True):
r = b""
r += ser_compact_size(self.index)
if with_witness:
@@ -645,6 +645,9 @@ class PrefilledTransaction():
r += self.tx.serialize_without_witness()
return r
+ def serialize_without_witness(self):
+ return self.serialize(with_witness=False)
+
def serialize_with_witness(self):
return self.serialize(with_witness=True)
@@ -684,7 +687,7 @@ class P2PHeaderAndShortIDs():
if with_witness:
r += ser_vector(self.prefilled_txn, "serialize_with_witness")
else:
- r += ser_vector(self.prefilled_txn)
+ r += ser_vector(self.prefilled_txn, "serialize_without_witness")
return r
def __repr__(self):
@@ -815,13 +818,13 @@ class BlockTransactions():
self.blockhash = deser_uint256(f)
self.transactions = deser_vector(f, CTransaction)
- def serialize(self, with_witness=False):
+ def serialize(self, with_witness=True):
r = b""
r += ser_uint256(self.blockhash)
if with_witness:
r += ser_vector(self.transactions, "serialize_with_witness")
else:
- r += ser_vector(self.transactions)
+ r += ser_vector(self.transactions, "serialize_without_witness")
return r
def __repr__(self):
@@ -1021,7 +1024,7 @@ class msg_block():
self.block.deserialize(f)
def serialize(self):
- return self.block.serialize()
+ return self.block.serialize(with_witness=False)
def __repr__(self):
return "msg_block(block=%s)" % (repr(self.block))
@@ -1292,7 +1295,7 @@ class msg_blocktxn():
def serialize(self):
r = b""
- r += self.block_transactions.serialize()
+ r += self.block_transactions.serialize(with_witness=False)
return r
def __repr__(self):
diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py
index 8429f34c41..dae8a4e569 100644
--- a/test/functional/test_framework/script.py
+++ b/test/functional/test_framework/script.py
@@ -641,7 +641,7 @@ def SignatureHash(script, txTo, inIdx, hashtype):
txtmp.vin = []
txtmp.vin.append(tmp)
- s = txtmp.serialize()
+ s = txtmp.serialize_without_witness()
s += struct.pack(b"<I", hashtype)
hash = hash256(s)
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index e42f3e60c2..5d4f8e6720 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -62,6 +62,7 @@ class BitcoinTestFramework():
self.setup_clean_chain = False
self.nodes = []
self.mocktime = 0
+ self.supports_cli = False
self.set_test_params()
assert hasattr(self, "num_nodes"), "Test must set self.num_nodes in set_test_params()"
@@ -91,6 +92,8 @@ class BitcoinTestFramework():
help="Location of the test framework config file")
parser.add_option("--pdbonfailure", dest="pdbonfailure", default=False, action="store_true",
help="Attach a python debugger if test fails")
+ parser.add_option("--usecli", dest="usecli", default=False, action="store_true",
+ help="use bitcoin-cli instead of RPC for all commands")
self.add_options(parser)
(self.options, self.args) = parser.parse_args()
@@ -113,6 +116,8 @@ class BitcoinTestFramework():
success = TestStatus.FAILED
try:
+ if self.options.usecli and not self.supports_cli:
+ raise SkipTest("--usecli specified but test does not support using CLI")
self.setup_chain()
self.setup_network()
self.run_test()
@@ -213,7 +218,7 @@ class BitcoinTestFramework():
assert_equal(len(extra_args), num_nodes)
assert_equal(len(binary), num_nodes)
for i in range(num_nodes):
- self.nodes.append(TestNode(i, self.options.tmpdir, extra_args[i], rpchost, timewait=timewait, binary=binary[i], stderr=None, mocktime=self.mocktime, coverage_dir=self.options.coveragedir))
+ self.nodes.append(TestNode(i, self.options.tmpdir, extra_args[i], rpchost, timewait=timewait, binary=binary[i], stderr=None, mocktime=self.mocktime, coverage_dir=self.options.coveragedir, use_cli=self.options.usecli))
def start_node(self, i, extra_args=None, stderr=None):
"""Start a bitcoind"""
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index a9248c764e..589a8f3969 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -10,6 +10,7 @@ import http.client
import json
import logging
import os
+import re
import subprocess
import time
@@ -22,6 +23,9 @@ from .util import (
p2p_port,
)
+# For Python 3.4 compatibility
+JSONDecodeError = getattr(json, "JSONDecodeError", ValueError)
+
BITCOIND_PROC_WAIT_TIMEOUT = 60
class TestNode():
@@ -38,7 +42,7 @@ class TestNode():
To make things easier for the test writer, any unrecognised messages will
be dispatched to the RPC connection."""
- def __init__(self, i, dirname, extra_args, rpchost, timewait, binary, stderr, mocktime, coverage_dir):
+ def __init__(self, i, dirname, extra_args, rpchost, timewait, binary, stderr, mocktime, coverage_dir, use_cli=False):
self.index = i
self.datadir = os.path.join(dirname, "node" + str(i))
self.rpchost = rpchost
@@ -58,6 +62,7 @@ class TestNode():
self.args = [self.binary, "-datadir=" + self.datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-logtimemicros", "-debug", "-debugexclude=libevent", "-debugexclude=leveldb", "-mocktime=" + str(mocktime), "-uacomment=testnode%d" % i]
self.cli = TestNodeCLI(os.getenv("BITCOINCLI", "bitcoin-cli"), self.datadir)
+ self.use_cli = use_cli
self.running = False
self.process = None
@@ -69,9 +74,12 @@ class TestNode():
self.p2ps = []
def __getattr__(self, name):
- """Dispatches any unrecognised messages to the RPC connection."""
- assert self.rpc_connected and self.rpc is not None, "Error: no RPC connection"
- return getattr(self.rpc, name)
+ """Dispatches any unrecognised messages to the RPC connection or a CLI instance."""
+ if self.use_cli:
+ return getattr(self.cli, name)
+ else:
+ assert self.rpc_connected and self.rpc is not None, "Error: no RPC connection"
+ return getattr(self.rpc, name)
def start(self, extra_args=None, stderr=None):
"""Start the node."""
@@ -110,10 +118,13 @@ class TestNode():
raise AssertionError("Unable to connect to bitcoind")
def get_wallet_rpc(self, wallet_name):
- assert self.rpc_connected
- assert self.rpc
- wallet_path = "wallet/%s" % wallet_name
- return self.rpc / wallet_path
+ if self.use_cli:
+ return self.cli("-rpcwallet={}".format(wallet_name))
+ else:
+ assert self.rpc_connected
+ assert self.rpc
+ wallet_path = "wallet/%s" % wallet_name
+ return self.rpc / wallet_path
def stop_node(self):
"""Stop the node."""
@@ -187,6 +198,16 @@ class TestNode():
p.peer_disconnect()
del self.p2ps[:]
+class TestNodeCLIAttr:
+ def __init__(self, cli, command):
+ self.cli = cli
+ self.command = command
+
+ def __call__(self, *args, **kwargs):
+ return self.cli.send_cli(self.command, *args, **kwargs)
+
+ def get_request(self, *args, **kwargs):
+ return lambda: self(*args, **kwargs)
class TestNodeCLI():
"""Interface to bitcoin-cli for an individual node"""
@@ -196,17 +217,26 @@ class TestNodeCLI():
self.binary = binary
self.datadir = datadir
self.input = None
+ self.log = logging.getLogger('TestFramework.bitcoincli')
def __call__(self, *args, input=None):
# TestNodeCLI is callable with bitcoin-cli command-line args
- self.args = [str(arg) for arg in args]
- self.input = input
- return self
+ cli = TestNodeCLI(self.binary, self.datadir)
+ cli.args = [str(arg) for arg in args]
+ cli.input = input
+ return cli
def __getattr__(self, command):
- def dispatcher(*args, **kwargs):
- return self.send_cli(command, *args, **kwargs)
- return dispatcher
+ return TestNodeCLIAttr(self, command)
+
+ def batch(self, requests):
+ results = []
+ for request in requests:
+ try:
+ results.append(dict(result=request()))
+ except JSONRPCException as e:
+ results.append(dict(error=e))
+ return results
def send_cli(self, command, *args, **kwargs):
"""Run bitcoin-cli command. Deserializes returned string as python object."""
@@ -218,10 +248,18 @@ class TestNodeCLI():
if named_args:
p_args += ["-named"]
p_args += [command] + pos_args + named_args
+ self.log.debug("Running bitcoin-cli command: %s" % command)
process = subprocess.Popen(p_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
cli_stdout, cli_stderr = process.communicate(input=self.input)
returncode = process.poll()
if returncode:
+ match = re.match(r'error code: ([-0-9]+)\nerror message:\n(.*)', cli_stderr)
+ if match:
+ code, message = match.groups()
+ raise JSONRPCException(dict(code=int(code), message=message))
# Ignore cli_stdout, raise with cli_stderr
raise subprocess.CalledProcessError(returncode, self.binary, output=cli_stderr)
- return json.loads(cli_stdout, parse_float=decimal.Decimal)
+ try:
+ return json.loads(cli_stdout, parse_float=decimal.Decimal)
+ except JSONDecodeError:
+ return cli_stdout.rstrip("\n")
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 428a18be86..72ad300e7e 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -78,6 +78,7 @@ BASE_SCRIPTS= [
'abandonconflict.py',
'bip68-112-113-p2p.py',
'rawtransactions.py',
+ 'address_types.py',
'reindex.py',
# vv Tests less than 30s vv
'keypool-topup.py',
@@ -86,12 +87,14 @@ BASE_SCRIPTS= [
'mempool_resurrect_test.py',
'txn_doublespend.py --mineblock',
'txn_clone.py',
+ 'txn_clone.py --segwit',
'getchaintips.py',
'rest.py',
'mempool_spendcoinbase.py',
'mempool_reorg.py',
'mempool_persist.py',
'multiwallet.py',
+ 'multiwallet.py --usecli',
'httpbasics.py',
'multi_rpc.py',
'proxy_test.py',
@@ -269,6 +272,7 @@ def main():
sys.exit(0)
check_script_list(config["environment"]["SRCDIR"])
+ check_script_prefixes()
if not args.keepcache:
shutil.rmtree("%s/test/cache" % config["environment"]["BUILDDIR"], ignore_errors=True)
@@ -467,6 +471,28 @@ class TestResult():
return self.status != "Failed"
+def check_script_prefixes():
+ """Check that no more than `EXPECTED_VIOLATION_COUNT` of the
+ test scripts don't start with one of the allowed name prefixes."""
+ EXPECTED_VIOLATION_COUNT = 77
+
+ # LEEWAY is provided as a transition measure, so that pull-requests
+ # that introduce new tests that don't conform with the naming
+ # convention don't immediately cause the tests to fail.
+ LEEWAY = 10
+
+ good_prefixes_re = re.compile("(example|feature|interface|mempool|mining|p2p|rpc|wallet)_")
+ bad_script_names = [script for script in ALL_SCRIPTS if good_prefixes_re.match(script) is None]
+
+ if len(bad_script_names) < EXPECTED_VIOLATION_COUNT:
+ print("{}HURRAY!{} Number of functional tests violating naming convention reduced!".format(BOLD[1], BOLD[0]))
+ print("Consider reducing EXPECTED_VIOLATION_COUNT from %d to %d" % (EXPECTED_VIOLATION_COUNT, len(bad_script_names)))
+ elif len(bad_script_names) > EXPECTED_VIOLATION_COUNT:
+ print("INFO: %d tests not meeting naming conventions (expected %d):" % (len(bad_script_names), EXPECTED_VIOLATION_COUNT))
+ print(" %s" % ("\n ".join(sorted(bad_script_names))))
+ assert len(bad_script_names) <= EXPECTED_VIOLATION_COUNT + LEEWAY, "Too many tests not following naming convention! (%d found, expected: <= %d)" % (len(bad_script_names), EXPECTED_VIOLATION_COUNT)
+
+
def check_script_list(src_dir):
"""Check scripts directory.
diff --git a/test/functional/txn_clone.py b/test/functional/txn_clone.py
index c50f86341f..ce26d6e0ee 100755
--- a/test/functional/txn_clone.py
+++ b/test/functional/txn_clone.py
@@ -14,6 +14,8 @@ class TxnMallTest(BitcoinTestFramework):
def add_options(self, parser):
parser.add_option("--mineblock", dest="mine_block", default=False, action="store_true",
help="Test double-spend of 1-confirmed transaction")
+ parser.add_option("--segwit", dest="segwit", default=False, action="store_true",
+ help="Test behaviour with SegWit txn (which should fail")
def setup_network(self):
# Start with split network:
@@ -22,6 +24,11 @@ class TxnMallTest(BitcoinTestFramework):
disconnect_nodes(self.nodes[2], 1)
def run_test(self):
+ if self.options.segwit:
+ output_type="p2sh-segwit"
+ else:
+ output_type="legacy"
+
# All nodes should start with 1,250 BTC:
starting_balance = 1250
for i in range(4):
@@ -31,11 +38,11 @@ class TxnMallTest(BitcoinTestFramework):
# Assign coins to foo and bar accounts:
self.nodes[0].settxfee(.001)
- node0_address_foo = self.nodes[0].getnewaddress("foo")
+ node0_address_foo = self.nodes[0].getnewaddress("foo", output_type)
fund_foo_txid = self.nodes[0].sendfrom("", node0_address_foo, 1219)
fund_foo_tx = self.nodes[0].gettransaction(fund_foo_txid)
- node0_address_bar = self.nodes[0].getnewaddress("bar")
+ node0_address_bar = self.nodes[0].getnewaddress("bar", output_type)
fund_bar_txid = self.nodes[0].sendfrom("", node0_address_bar, 29)
fund_bar_tx = self.nodes[0].gettransaction(fund_bar_txid)
@@ -106,6 +113,10 @@ class TxnMallTest(BitcoinTestFramework):
# Send clone and its parent to miner
self.nodes[2].sendrawtransaction(fund_foo_tx["hex"])
txid1_clone = self.nodes[2].sendrawtransaction(tx1_clone["hex"])
+ if self.options.segwit:
+ assert_equal(txid1, txid1_clone)
+ return
+
# ... mine a block...
self.nodes[2].generate(1)
diff --git a/test/functional/wallet-dump.py b/test/functional/wallet-dump.py
index fa51a2e9d7..8b81c81ba8 100755
--- a/test/functional/wallet-dump.py
+++ b/test/functional/wallet-dump.py
@@ -69,7 +69,7 @@ def read_dump(file_name, addrs, script_addrs, hd_master_addr_old):
class WalletDumpTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
- self.extra_args = [["-keypool=90"]]
+ self.extra_args = [["-keypool=90", "-addresstype=legacy"]]
def setup_network(self, split=False):
# Use 1 minute timeout because the initial getnewaddress RPC can take
diff --git a/test/functional/wallet.py b/test/functional/wallet.py
index 55265d03c6..a90dbc8adf 100755
--- a/test/functional/wallet.py
+++ b/test/functional/wallet.py
@@ -27,15 +27,15 @@ class WalletTest(BitcoinTestFramework):
assert_fee_amount(fee, tx_size, fee_per_byte * 1000)
return curr_balance
+ def get_vsize(self, txn):
+ return self.nodes[0].decoderawtransaction(txn)['vsize']
+
def run_test(self):
# Check that there's no UTXO on none of the nodes
assert_equal(len(self.nodes[0].listunspent()), 0)
assert_equal(len(self.nodes[1].listunspent()), 0)
assert_equal(len(self.nodes[2].listunspent()), 0)
- self.log.info("Check for mempoolminfee in getmempoolinfo")
- assert_equal(self.nodes[0].getmempoolinfo()['mempoolminfee'], Decimal('0.00001000'))
-
self.log.info("Mining blocks...")
self.nodes[0].generate(1)
@@ -165,7 +165,7 @@ class WalletTest(BitcoinTestFramework):
txid = self.nodes[2].sendtoaddress(address, 10, "", "", False)
self.nodes[2].generate(1)
self.sync_all([self.nodes[0:3]])
- node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), Decimal('84'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid)))
+ node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), Decimal('84'), fee_per_byte, self.get_vsize(self.nodes[2].getrawtransaction(txid)))
assert_equal(self.nodes[0].getbalance(), Decimal('10'))
# Send 10 BTC with subtract fee from amount
@@ -174,14 +174,14 @@ class WalletTest(BitcoinTestFramework):
self.sync_all([self.nodes[0:3]])
node_2_bal -= Decimal('10')
assert_equal(self.nodes[2].getbalance(), node_2_bal)
- node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), Decimal('20'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid)))
+ node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), Decimal('20'), fee_per_byte, self.get_vsize(self.nodes[2].getrawtransaction(txid)))
# Sendmany 10 BTC
txid = self.nodes[2].sendmany('from1', {address: 10}, 0, "", [])
self.nodes[2].generate(1)
self.sync_all([self.nodes[0:3]])
node_0_bal += Decimal('10')
- node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), node_2_bal - Decimal('10'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid)))
+ node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), node_2_bal - Decimal('10'), fee_per_byte, self.get_vsize(self.nodes[2].getrawtransaction(txid)))
assert_equal(self.nodes[0].getbalance(), node_0_bal)
# Sendmany 10 BTC with subtract fee from amount
@@ -190,7 +190,7 @@ class WalletTest(BitcoinTestFramework):
self.sync_all([self.nodes[0:3]])
node_2_bal -= Decimal('10')
assert_equal(self.nodes[2].getbalance(), node_2_bal)
- node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), node_0_bal + Decimal('10'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid)))
+ node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), node_0_bal + Decimal('10'), fee_per_byte, self.get_vsize(self.nodes[2].getrawtransaction(txid)))
# Test ResendWalletTransactions:
# Create a couple of transactions, then start up a fourth