aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--Makefile.am4
-rw-r--r--configure.ac8
-rw-r--r--doc/release-notes.md6
-rw-r--r--src/Makefile.am2
-rw-r--r--src/Makefile.test.include2
-rw-r--r--src/bitcoin-tx.cpp25
-rw-r--r--src/chainparams.cpp7
-rw-r--r--src/chainparams.h6
-rw-r--r--src/coins.cpp2
-rw-r--r--src/init.cpp16
-rw-r--r--src/net_processing.cpp39
-rw-r--r--src/net_processing.h4
-rw-r--r--src/policy/rbf.h2
-rw-r--r--src/primitives/block.h5
-rw-r--r--src/protocol.cpp6
-rw-r--r--src/rpc/blockchain.cpp9
-rw-r--r--src/rpc/client.cpp4
-rw-r--r--src/rpc/mining.cpp27
-rw-r--r--src/rpc/rawtransaction.cpp26
-rw-r--r--src/rpc/server.h2
-rw-r--r--src/test/skiplist_tests.cpp35
-rw-r--r--src/validation.cpp111
-rw-r--r--src/validation.h40
-rw-r--r--src/versionbits.cpp2
-rw-r--r--src/versionbits.h4
-rw-r--r--src/wallet/db.cpp1
-rw-r--r--src/wallet/rpcwallet.cpp30
-rw-r--r--src/wallet/wallet.cpp10
-rw-r--r--src/wallet/wallet.h7
-rw-r--r--test/config.ini.in (renamed from test/functional/config.ini.in)2
-rwxr-xr-xtest/functional/bip68-sequence.py4
-rwxr-xr-xtest/functional/blockchain.py3
-rwxr-xr-xtest/functional/fundrawtransaction.py2
-rwxr-xr-xtest/functional/mempool_packages.py8
-rwxr-xr-xtest/functional/p2p-compactblocks.py2
-rwxr-xr-xtest/functional/p2p-segwit.py2
-rwxr-xr-xtest/functional/prioritise_transaction.py8
-rwxr-xr-xtest/functional/replace-by-fee.py27
-rwxr-xr-xtest/functional/test_runner.py2
-rw-r--r--test/util/bctest.py139
-rwxr-xr-xtest/util/bitcoin-util-test.py167
-rw-r--r--test/util/buildenv.py.in4
43 files changed, 472 insertions, 344 deletions
diff --git a/.gitignore b/.gitignore
index f1e9ca20c1..60c26dae8b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -80,7 +80,6 @@ Bitcoin-Qt.app
# Unit-tests
Makefile.test
bitcoin-qt_test
-src/test/buildenv.py
# Resources cpp
qrc_*.cpp
@@ -101,8 +100,7 @@ coverage_percent.txt
linux-coverage-build
linux-build
win32-build
-test/functional/config.ini
-test/util/buildenv.py
+test/config.ini
test/cache/*
!src/leveldb*/Makefile
diff --git a/Makefile.am b/Makefile.am
index 3a56eea0c0..40114a551f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -223,7 +223,6 @@ dist_noinst_SCRIPTS = autogen.sh
EXTRA_DIST = $(top_srcdir)/share/genbuild.sh test/functional/test_runner.py test/functional $(DIST_CONTRIB) $(DIST_DOCS) $(WINDOWS_PACKAGING) $(OSX_PACKAGING) $(BIN_CHECKS)
EXTRA_DIST += \
- test/util/bctest.py \
test/util/bitcoin-util-test.py \
test/util/data/bitcoin-util-test.json \
test/util/data/blanktxv1.hex \
@@ -277,9 +276,6 @@ EXTRA_DIST += \
CLEANFILES = $(OSX_DMG) $(BITCOIN_WIN_INSTALLER)
-# This file is problematic for out-of-tree builds if it exists.
-DISTCLEANFILES = test/util/buildenv.pyc
-
.INTERMEDIATE: $(COVERAGE_INFO)
DISTCHECK_CONFIGURE_FLAGS = --enable-man
diff --git a/configure.ac b/configure.ac
index ca66216554..160be397ba 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1161,13 +1161,11 @@ AC_SUBST(EVENT_PTHREADS_LIBS)
AC_SUBST(ZMQ_LIBS)
AC_SUBST(PROTOBUF_LIBS)
AC_SUBST(QR_LIBS)
-AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile share/setup.nsi share/qt/Info.plist test/functional/config.ini])
-AC_CONFIG_FILES([test/util/buildenv.py],[chmod +x test/util/buildenv.py])
+AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile share/setup.nsi share/qt/Info.plist test/config.ini])
AC_CONFIG_FILES([contrib/devtools/split-debug.sh],[chmod +x contrib/devtools/split-debug.sh])
AC_CONFIG_FILES([doc/Doxyfile])
AC_CONFIG_LINKS([test/functional/test_runner.py:test/functional/test_runner.py])
AC_CONFIG_LINKS([test/util/bitcoin-util-test.py:test/util/bitcoin-util-test.py])
-AC_CONFIG_LINKS([test/util/bctest.py:test/util/bctest.py])
dnl boost's m4 checks do something really nasty: they export these vars. As a
dnl result, they leak into secp256k1's configure and crazy things happen.
@@ -1215,8 +1213,8 @@ esac
dnl Replace the BUILDDIR path with the correct Windows path if compiling on Native Windows
case ${OS} in
*Windows*)
- sed 's/BUILDDIR="\/\([[a-z]]\)/BUILDDIR="\1:/' test/functional/config.ini > test/functional/config-2.ini
- mv test/functional/config-2.ini test/functional/config.ini
+ sed 's/BUILDDIR="\/\([[a-z]]\)/BUILDDIR="\1:/' test/config.ini > test/config-2.ini
+ mv test/config-2.ini test/config.ini
;;
esac
diff --git a/doc/release-notes.md b/doc/release-notes.md
index 075c7c3911..a13ede2dd5 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -42,9 +42,15 @@ Low-level RPC changes
- The `gettxoutsetinfo` RPC reports `hash_serialized_2` instead of `hash_serialized`,
which does not commit to the transaction versions of unspent outputs, but does
commit to the height and coinbase information.
+ - The `gettxoutsetinfo` response now contains `disk_size` and `bogosize` instead of
+ `bytes_serialized`. The first is a more accurate estimate of actual disk usage, but
+ is not deterministic. The second is unrelated to disk usage, but is a
+ database-independent metric of UTXO set size: it counts every UTXO entry as 50 + the
+ length of its scriptPubKey.
- The `getutxos` REST path no longer reports the `txvers` field in JSON format,
and always reports 0 for transaction versions in the binary format
+
- Error codes have been updated to be more accurate for the following error cases:
- `getblock` now returns RPC_MISC_ERROR if the block can't be found on disk (for
example if the block has been pruned). Previously returned RPC_INTERNAL_ERROR.
diff --git a/src/Makefile.am b/src/Makefile.am
index ae2eb29c94..199d60b725 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -198,6 +198,7 @@ libbitcoin_server_a_SOURCES = \
noui.cpp \
policy/fees.cpp \
policy/policy.cpp \
+ policy/rbf.cpp \
pow.cpp \
rest.cpp \
rpc/blockchain.cpp \
@@ -240,7 +241,6 @@ libbitcoin_wallet_a_SOURCES = \
wallet/rpcwallet.cpp \
wallet/wallet.cpp \
wallet/walletdb.cpp \
- policy/rbf.cpp \
$(BITCOIN_CORE_H)
# crypto primitives library
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 44b5f83edb..13e0a03c76 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -147,7 +147,7 @@ bitcoin_test_clean : FORCE
check-local:
@echo "Running test/util/bitcoin-util-test.py..."
- $(PYTHON) $(top_builddir)/test/util/bitcoin-util-test.py
+ $(top_builddir)/test/util/bitcoin-util-test.py
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C secp256k1 check
if EMBEDDED_UNIVALUE
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C univalue check
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index cf280f485c..367801dd78 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -13,6 +13,7 @@
#include "core_io.h"
#include "keystore.h"
#include "policy/policy.h"
+#include "policy/rbf.h"
#include "primitives/transaction.h"
#include "script/script.h"
#include "script/sign.h"
@@ -77,6 +78,7 @@ static int AppInitRawTx(int argc, char* argv[])
strUsage += HelpMessageOpt("in=TXID:VOUT(:SEQUENCE_NUMBER)", _("Add input to TX"));
strUsage += HelpMessageOpt("locktime=N", _("Set TX lock time to N"));
strUsage += HelpMessageOpt("nversion=N", _("Set TX version to N"));
+ strUsage += HelpMessageOpt("rbfoptin(=N)", _("Set RBF opt-in sequence number for input N (if not provided, opt-in all available inputs)"));
strUsage += HelpMessageOpt("outaddr=VALUE:ADDRESS", _("Add address-based output to TX"));
strUsage += HelpMessageOpt("outpubkey=VALUE:PUBKEY[:FLAGS]", _("Add pay-to-pubkey output to TX") + ". " +
_("Optionally add the \"W\" flag to produce a pay-to-witness-pubkey-hash output") + ". " +
@@ -202,6 +204,26 @@ static void MutateTxLocktime(CMutableTransaction& tx, const std::string& cmdVal)
tx.nLockTime = (unsigned int) newLocktime;
}
+static void MutateTxRBFOptIn(CMutableTransaction& tx, const std::string& strInIdx)
+{
+ // parse requested index
+ int inIdx = atoi(strInIdx);
+ if (inIdx < 0 || inIdx >= (int)tx.vin.size()) {
+ throw std::runtime_error("Invalid TX input index '" + strInIdx + "'");
+ }
+
+ // set the nSequence to MAX_INT - 2 (= RBF opt in flag)
+ int cnt = 0;
+ for (CTxIn& txin : tx.vin) {
+ if (strInIdx == "" || cnt == inIdx) {
+ if (txin.nSequence > MAX_BIP125_RBF_SEQUENCE) {
+ txin.nSequence = MAX_BIP125_RBF_SEQUENCE;
+ }
+ }
+ ++cnt;
+ }
+}
+
static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInput)
{
std::vector<std::string> vStrInputParts;
@@ -649,6 +671,9 @@ static void MutateTx(CMutableTransaction& tx, const std::string& command,
MutateTxVersion(tx, commandVal);
else if (command == "locktime")
MutateTxLocktime(tx, commandVal);
+ else if (command == "rbfoptin") {
+ MutateTxRBFOptIn(tx, commandVal);
+ }
else if (command == "delin")
MutateTxDelInput(tx, commandVal);
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 5055fb3e0a..04a2f680fa 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -55,7 +55,7 @@ static CBlock CreateGenesisBlock(uint32_t nTime, uint32_t nNonce, uint32_t nBits
return CreateGenesisBlock(pszTimestamp, genesisOutputScript, nTime, nNonce, nBits, nVersion, genesisReward);
}
-void CChainParams::UpdateBIP9Parameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout)
+void CChainParams::UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout)
{
consensus.vDeployments[d].nStartTime = nStartTime;
consensus.vDeployments[d].nTimeout = nTimeout;
@@ -356,8 +356,7 @@ void SelectParams(const std::string& network)
globalChainParams = CreateChainParams(network);
}
-void UpdateBIP9Parameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout)
+void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout)
{
- globalChainParams->UpdateBIP9Parameters(d, nStartTime, nTimeout);
+ globalChainParams->UpdateVersionBitsParameters(d, nStartTime, nTimeout);
}
-
diff --git a/src/chainparams.h b/src/chainparams.h
index e5312d1080..a2f136171b 100644
--- a/src/chainparams.h
+++ b/src/chainparams.h
@@ -76,7 +76,7 @@ public:
const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; }
const CCheckpointData& Checkpoints() const { return checkpointData; }
const ChainTxData& TxData() const { return chainTxData; }
- void UpdateBIP9Parameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout);
+ void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout);
protected:
CChainParams() {}
@@ -116,8 +116,8 @@ const CChainParams &Params();
void SelectParams(const std::string& chain);
/**
- * Allows modifying the BIP9 regtest parameters.
+ * Allows modifying the Version Bits regtest parameters.
*/
-void UpdateBIP9Parameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout);
+void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout);
#endif // BITCOIN_CHAINPARAMS_H
diff --git a/src/coins.cpp b/src/coins.cpp
index 5b7c562678..b45fc76338 100644
--- a/src/coins.cpp
+++ b/src/coins.cpp
@@ -86,7 +86,7 @@ void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight) {
const uint256& txid = tx.GetHash();
for (size_t i = 0; i < tx.vout.size(); ++i) {
// Pass fCoinbase as the possible_overwrite flag to AddCoin, in order to correctly
- // deal with the pre-BIP30 occurrances of duplicate coinbase transactions.
+ // deal with the pre-BIP30 occurrences of duplicate coinbase transactions.
cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase), fCoinbase);
}
}
diff --git a/src/init.cpp b/src/init.cpp
index 33023a1800..04d1280c92 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -449,7 +449,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-limitancestorsize=<n>", strprintf("Do not accept transactions whose size with all in-mempool ancestors exceeds <n> kilobytes (default: %u)", DEFAULT_ANCESTOR_SIZE_LIMIT));
strUsage += HelpMessageOpt("-limitdescendantcount=<n>", strprintf("Do not accept transactions if any ancestor would have <n> or more in-mempool descendants (default: %u)", DEFAULT_DESCENDANT_LIMIT));
strUsage += HelpMessageOpt("-limitdescendantsize=<n>", strprintf("Do not accept transactions if any ancestor would have more than <n> kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT));
- strUsage += HelpMessageOpt("-bip9params=deployment:start:end", "Use given start/end times for specified BIP9 deployment (regtest-only)");
+ strUsage += HelpMessageOpt("-vbparams=deployment:start:end", "Use given start/end times for specified version bits deployment (regtest-only)");
}
strUsage += HelpMessageOpt("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " +
_("If <category> is not supplied or if <category> = 1, output all debugging information.") + " " + _("<category> can be:") + " " + ListLogCategories() + ".");
@@ -1104,16 +1104,16 @@ bool AppInitParameterInteraction()
fEnableReplacement = (std::find(vstrReplacementModes.begin(), vstrReplacementModes.end(), "fee") != vstrReplacementModes.end());
}
- if (gArgs.IsArgSet("-bip9params")) {
- // Allow overriding BIP9 parameters for testing
+ if (gArgs.IsArgSet("-vbparams")) {
+ // Allow overriding version bits parameters for testing
if (!chainparams.MineBlocksOnDemand()) {
- return InitError("BIP9 parameters may only be overridden on regtest.");
+ return InitError("Version bits parameters may only be overridden on regtest.");
}
- for (const std::string& strDeployment : gArgs.GetArgs("-bip9params")) {
+ for (const std::string& strDeployment : gArgs.GetArgs("-vbparams")) {
std::vector<std::string> vDeploymentParams;
boost::split(vDeploymentParams, strDeployment, boost::is_any_of(":"));
if (vDeploymentParams.size() != 3) {
- return InitError("BIP9 parameters malformed, expecting deployment:start:end");
+ return InitError("Version bits parameters malformed, expecting deployment:start:end");
}
int64_t nStartTime, nTimeout;
if (!ParseInt64(vDeploymentParams[1], &nStartTime)) {
@@ -1126,9 +1126,9 @@ bool AppInitParameterInteraction()
for (int j=0; j<(int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j)
{
if (vDeploymentParams[0].compare(VersionBitsDeploymentInfo[j].name) == 0) {
- UpdateBIP9Parameters(Consensus::DeploymentPos(j), nStartTime, nTimeout);
+ UpdateVersionBitsParameters(Consensus::DeploymentPos(j), nStartTime, nTimeout);
found = true;
- LogPrintf("Setting BIP9 activation parameters for %s to start=%ld, timeout=%ld\n", vDeploymentParams[0], nStartTime, nTimeout);
+ LogPrintf("Setting version bits activation parameters for %s to start=%ld, timeout=%ld\n", vDeploymentParams[0], nStartTime, nTimeout);
break;
}
}
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 971db3427c..4ca02c281d 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -168,6 +168,8 @@ struct CNodeState {
int nUnconnectingHeaders;
//! Whether we've started headers synchronization with this peer.
bool fSyncStarted;
+ //! When to potentially disconnect peer for stalling headers download
+ int64_t nHeadersSyncTimeout;
//! Since when we're stalling block download progress (in microseconds), or 0.
int64_t nStallingSince;
std::list<QueuedBlock> vBlocksInFlight;
@@ -207,6 +209,7 @@ struct CNodeState {
pindexBestHeaderSent = NULL;
nUnconnectingHeaders = 0;
fSyncStarted = false;
+ nHeadersSyncTimeout = 0;
nStallingSince = 0;
nDownloadingSince = 0;
nBlocksInFlight = 0;
@@ -481,7 +484,7 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<con
// Make sure pindexBestKnownBlock is up to date, we'll need it.
ProcessBlockAvailability(nodeid);
- if (state->pindexBestKnownBlock == NULL || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork) {
+ if (state->pindexBestKnownBlock == NULL || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork || state->pindexBestKnownBlock->nChainWork < UintToArith256(consensusParams.nMinimumChainWork)) {
// This peer has nothing interesting.
return;
}
@@ -2881,6 +2884,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
// Only actively request headers from a single peer, unless we're close to today.
if ((nSyncStarted == 0 && fFetch) || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) {
state.fSyncStarted = true;
+ state.nHeadersSyncTimeout = GetTimeMicros() + HEADERS_DOWNLOAD_TIMEOUT_BASE + HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER * (GetAdjustedTime() - pindexBestHeader->GetBlockTime())/(consensusParams.nPowTargetSpacing);
nSyncStarted++;
const CBlockIndex *pindexStart = pindexBestHeader;
/* If possible, start at the block preceding the currently
@@ -3204,6 +3208,39 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
return true;
}
}
+ // Check for headers sync timeouts
+ if (state.fSyncStarted && state.nHeadersSyncTimeout < std::numeric_limits<int64_t>::max()) {
+ // Detect whether this is a stalling initial-headers-sync peer
+ if (pindexBestHeader->GetBlockTime() <= GetAdjustedTime() - 24*60*60) {
+ if (nNow > state.nHeadersSyncTimeout && nSyncStarted == 1 && (nPreferredDownload - state.fPreferredDownload >= 1)) {
+ // Disconnect a (non-whitelisted) peer if it is our only sync peer,
+ // and we have others we could be using instead.
+ // Note: If all our peers are inbound, then we won't
+ // disconnect our sync peer for stalling; we have bigger
+ // problems if we can't get any outbound peers.
+ if (!pto->fWhitelisted) {
+ LogPrintf("Timeout downloading headers from peer=%d, disconnecting\n", pto->GetId());
+ pto->fDisconnect = true;
+ return true;
+ } else {
+ LogPrintf("Timeout downloading headers from whitelisted peer=%d, not disconnecting\n", pto->GetId());
+ // Reset the headers sync state so that we have a
+ // chance to try downloading from a different peer.
+ // Note: this will also result in at least one more
+ // getheaders message to be sent to
+ // this peer (eventually).
+ state.fSyncStarted = false;
+ nSyncStarted--;
+ state.nHeadersSyncTimeout = 0;
+ }
+ }
+ } else {
+ // After we've caught up once, reset the timeout so we can't trigger
+ // disconnect later.
+ state.nHeadersSyncTimeout = std::numeric_limits<int64_t>::max();
+ }
+ }
+
//
// Message: getdata (blocks)
diff --git a/src/net_processing.h b/src/net_processing.h
index f460595bc1..db6d81e6b6 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -17,6 +17,10 @@ static const int64_t ORPHAN_TX_EXPIRE_TIME = 20 * 60;
static const int64_t ORPHAN_TX_EXPIRE_INTERVAL = 5 * 60;
/** Default number of orphan+recently-replaced txn to keep around for block reconstruction */
static const unsigned int DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN = 100;
+/** Headers download timeout expressed in microseconds
+ * Timeout = base + per_header * (expected number of headers) */
+static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_BASE = 15 * 60 * 1000000; // 15 minutes
+static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER = 1000; // 1ms/header
/** Register with a network node to receive its signals */
void RegisterNodeSignals(CNodeSignals& nodeSignals);
diff --git a/src/policy/rbf.h b/src/policy/rbf.h
index 139aec5760..22c73f3319 100644
--- a/src/policy/rbf.h
+++ b/src/policy/rbf.h
@@ -7,6 +7,8 @@
#include "txmempool.h"
+static const uint32_t MAX_BIP125_RBF_SEQUENCE = 0xfffffffd;
+
enum RBFTransactionState {
RBF_TRANSACTIONSTATE_UNKNOWN,
RBF_TRANSACTIONSTATE_REPLACEABLE_BIP125,
diff --git a/src/primitives/block.h b/src/primitives/block.h
index 4c6eb20ad5..f03cf48504 100644
--- a/src/primitives/block.h
+++ b/src/primitives/block.h
@@ -129,10 +129,7 @@ struct CBlockLocator
CBlockLocator() {}
- CBlockLocator(const std::vector<uint256>& vHaveIn)
- {
- vHave = vHaveIn;
- }
+ CBlockLocator(const std::vector<uint256>& vHaveIn) : vHave(vHaveIn) {}
ADD_SERIALIZE_METHODS;
diff --git a/src/protocol.cpp b/src/protocol.cpp
index 28d1d0eeb4..6cd246ed53 100644
--- a/src/protocol.cpp
+++ b/src/protocol.cpp
@@ -151,11 +151,7 @@ CInv::CInv()
hash.SetNull();
}
-CInv::CInv(int typeIn, const uint256& hashIn)
-{
- type = typeIn;
- hash = hashIn;
-}
+CInv::CInv(int typeIn, const uint256& hashIn) : type(typeIn), hash(hashIn) {}
bool operator<(const CInv& a, const CInv& b)
{
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 96871ce1dc..b66c1c2b64 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -781,11 +781,12 @@ struct CCoinsStats
uint256 hashBlock;
uint64_t nTransactions;
uint64_t nTransactionOutputs;
+ uint64_t nBogoSize;
uint256 hashSerialized;
uint64_t nDiskSize;
CAmount nTotalAmount;
- CCoinsStats() : nHeight(0), nTransactions(0), nTransactionOutputs(0), nTotalAmount(0) {}
+ CCoinsStats() : nHeight(0), nTransactions(0), nTransactionOutputs(0), nBogoSize(0), nDiskSize(0), nTotalAmount(0) {}
};
static void ApplyStats(CCoinsStats &stats, CHashWriter& ss, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
@@ -800,6 +801,8 @@ static void ApplyStats(CCoinsStats &stats, CHashWriter& ss, const uint256& hash,
ss << VARINT(output.second.out.nValue);
stats.nTransactionOutputs++;
stats.nTotalAmount += output.second.out.nValue;
+ stats.nBogoSize += 32 /* txid */ + 4 /* vout index */ + 4 /* height + coinbase */ + 8 /* amount */ +
+ 2 /* scriptPubKey len */ + output.second.out.scriptPubKey.size() /* scriptPubKey */;
}
ss << VARINT(0);
}
@@ -904,7 +907,8 @@ UniValue gettxoutsetinfo(const JSONRPCRequest& request)
" \"bestblock\": \"hex\", (string) the best block hash hex\n"
" \"transactions\": n, (numeric) The number of transactions\n"
" \"txouts\": n, (numeric) The number of output transactions\n"
- " \"hash_serialized\": \"hash\", (string) The serialized hash\n"
+ " \"bogosize\": n, (numeric) A meaningless metric for UTXO set size\n"
+ " \"hash_serialized_2\": \"hash\", (string) The serialized hash\n"
" \"disk_size\": n, (numeric) The estimated size of the chainstate on disk\n"
" \"total_amount\": x.xxx (numeric) The total amount\n"
"}\n"
@@ -922,6 +926,7 @@ UniValue gettxoutsetinfo(const JSONRPCRequest& request)
ret.push_back(Pair("bestblock", stats.hashBlock.GetHex()));
ret.push_back(Pair("transactions", (int64_t)stats.nTransactions));
ret.push_back(Pair("txouts", (int64_t)stats.nTransactionOutputs));
+ ret.push_back(Pair("bogosize", (int64_t)stats.nBogoSize));
ret.push_back(Pair("hash_serialized_2", stats.hashSerialized.GetHex()));
ret.push_back(Pair("disk_size", stats.nDiskSize));
ret.push_back(Pair("total_amount", ValueFromAmount(stats.nTotalAmount)));
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index a3a692c14d..8dd84e20c9 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -86,6 +86,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "createrawtransaction", 0, "inputs" },
{ "createrawtransaction", 1, "outputs" },
{ "createrawtransaction", 2, "locktime" },
+ { "createrawtransaction", 3, "optintorbf" },
{ "signrawtransaction", 1, "prevtxs" },
{ "signrawtransaction", 2, "privkeys" },
{ "sendrawtransaction", 1, "allowhighfees" },
@@ -112,7 +113,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "estimaterawfee", 0, "nblocks" },
{ "estimaterawfee", 1, "threshold" },
{ "estimaterawfee", 2, "horizon" },
- { "prioritisetransaction", 1, "fee_delta" },
+ { "prioritisetransaction", 1, "priority_delta" },
+ { "prioritisetransaction", 2, "fee_delta" },
{ "setban", 2, "bantime" },
{ "setban", 3, "absolute" },
{ "setnetworkactive", 0, "state" },
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index d744269df1..cfe42ec7d8 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -257,26 +257,31 @@ UniValue getmininginfo(const JSONRPCRequest& request)
// NOTE: Unlike wallet RPC (which use BTC values), mining RPCs follow GBT (BIP 22) in using satoshi amounts
UniValue prioritisetransaction(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 2)
+ if (request.fHelp || request.params.size() != 3)
throw std::runtime_error(
- "prioritisetransaction <txid> <fee delta>\n"
+ "prioritisetransaction <txid> <priority delta> <fee delta>\n"
"Accepts the transaction into mined blocks at a higher (or lower) priority\n"
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id.\n"
- "2. fee_delta (numeric, required) The fee value (in satoshis) to add (or subtract, if negative).\n"
+ "2. priority_delta (numeric, optional) Fee-independent priority adjustment. Not supported, so must be zero or null.\n"
+ "3. fee_delta (numeric, required) The fee value (in satoshis) to add (or subtract, if negative).\n"
" The fee is not actually paid, only the algorithm for selecting transactions into a block\n"
" considers the transaction as it would have paid a higher (or lower) fee.\n"
"\nResult:\n"
"true (boolean) Returns true\n"
"\nExamples:\n"
- + HelpExampleCli("prioritisetransaction", "\"txid\" 10000")
- + HelpExampleRpc("prioritisetransaction", "\"txid\", 10000")
+ + HelpExampleCli("prioritisetransaction", "\"txid\" 0.0 10000")
+ + HelpExampleRpc("prioritisetransaction", "\"txid\", 0.0, 10000")
);
LOCK(cs_main);
uint256 hash = ParseHashStr(request.params[0].get_str(), "txid");
- CAmount nAmount = request.params[1].get_int64();
+ CAmount nAmount = request.params[2].get_int64();
+
+ if (!(request.params[1].isNull() || request.params[1].get_real() == 0)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Priority is not supported, and adjustment thereof must be zero.");
+ }
mempool.PrioritiseTransaction(hash, nAmount);
return true;
@@ -303,7 +308,7 @@ static UniValue BIP22ValidationResult(const CValidationState& state)
}
std::string gbt_vb_name(const Consensus::DeploymentPos pos) {
- const struct BIP9DeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos];
+ const struct VBDeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos];
std::string s = vbinfo.name;
if (!vbinfo.gbt_force) {
s.insert(s.begin(), '!');
@@ -515,7 +520,7 @@ UniValue getblocktemplate(const JSONRPCRequest& request)
// TODO: Maybe recheck connections/IBD and (if something wrong) send an expires-immediately template to stop miners?
}
- const struct BIP9DeploymentInfo& segwit_info = VersionBitsDeploymentInfo[Consensus::DEPLOYMENT_SEGWIT];
+ const struct VBDeploymentInfo& segwit_info = VersionBitsDeploymentInfo[Consensus::DEPLOYMENT_SEGWIT];
// If the caller is indicating segwit support, then allow CreateNewBlock()
// to select witness transactions, after segwit activates (otherwise
// don't).
@@ -629,7 +634,7 @@ UniValue getblocktemplate(const JSONRPCRequest& request)
// FALL THROUGH to get vbavailable set...
case THRESHOLD_STARTED:
{
- const struct BIP9DeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos];
+ const struct VBDeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos];
vbavailable.push_back(Pair(gbt_vb_name(pos), consensusParams.vDeployments[pos].bit));
if (setClientRules.find(vbinfo.name) == setClientRules.end()) {
if (!vbinfo.gbt_force) {
@@ -642,7 +647,7 @@ UniValue getblocktemplate(const JSONRPCRequest& request)
case THRESHOLD_ACTIVE:
{
// Add to rules only
- const struct BIP9DeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos];
+ const struct VBDeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos];
aRules.push_back(gbt_vb_name(pos));
if (setClientRules.find(vbinfo.name) == setClientRules.end()) {
// Not supported by the client; make sure it's safe to proceed
@@ -959,7 +964,7 @@ static const CRPCCommand commands[] =
// --------------------- ------------------------ ----------------------- ----------
{ "mining", "getnetworkhashps", &getnetworkhashps, true, {"nblocks","height"} },
{ "mining", "getmininginfo", &getmininginfo, true, {} },
- { "mining", "prioritisetransaction", &prioritisetransaction, true, {"txid","fee_delta"} },
+ { "mining", "prioritisetransaction", &prioritisetransaction, true, {"txid","priority_delta","fee_delta"} },
{ "mining", "getblocktemplate", &getblocktemplate, true, {"template_request"} },
{ "mining", "submitblock", &submitblock, true, {"hexdata","parameters"} },
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index e27c2a77c7..00ddd9d16f 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -14,6 +14,7 @@
#include "merkleblock.h"
#include "net.h"
#include "policy/policy.h"
+#include "policy/rbf.h"
#include "primitives/transaction.h"
#include "rpc/server.h"
#include "script/script.h"
@@ -289,9 +290,9 @@ UniValue verifytxoutproof(const JSONRPCRequest& request)
UniValue createrawtransaction(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
+ if (request.fHelp || request.params.size() < 2 || request.params.size() > 4)
throw std::runtime_error(
- "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,\"data\":\"hex\",...} ( locktime )\n"
+ "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,\"data\":\"hex\",...} ( locktime ) ( optintorbf )\n"
"\nCreate a transaction spending the given inputs and creating new outputs.\n"
"Outputs can be addresses or data.\n"
"Returns hex-encoded raw transaction.\n"
@@ -315,6 +316,7 @@ UniValue createrawtransaction(const JSONRPCRequest& request)
" ,...\n"
" }\n"
"3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n"
+ "4. optintorbf (boolean, optional, default=false) Allow this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n"
"\nResult:\n"
"\"transaction\" (string) hex string of the transaction\n"
@@ -341,6 +343,8 @@ UniValue createrawtransaction(const JSONRPCRequest& request)
rawTx.nLockTime = nLockTime;
}
+ bool rbfOptIn = request.params.size() > 3 ? request.params[3].isTrue() : false;
+
for (unsigned int idx = 0; idx < inputs.size(); idx++) {
const UniValue& input = inputs[idx];
const UniValue& o = input.get_obj();
@@ -354,16 +358,24 @@ UniValue createrawtransaction(const JSONRPCRequest& request)
if (nOutput < 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
- uint32_t nSequence = (rawTx.nLockTime ? std::numeric_limits<uint32_t>::max() - 1 : std::numeric_limits<uint32_t>::max());
+ uint32_t nSequence;
+ if (rbfOptIn) {
+ nSequence = MAX_BIP125_RBF_SEQUENCE;
+ } else if (rawTx.nLockTime) {
+ nSequence = std::numeric_limits<uint32_t>::max() - 1;
+ } else {
+ nSequence = std::numeric_limits<uint32_t>::max();
+ }
// set the sequence number if passed in the parameters object
const UniValue& sequenceObj = find_value(o, "sequence");
if (sequenceObj.isNum()) {
int64_t seqNr64 = sequenceObj.get_int64();
- if (seqNr64 < 0 || seqNr64 > std::numeric_limits<uint32_t>::max())
+ if (seqNr64 < 0 || seqNr64 > std::numeric_limits<uint32_t>::max()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range");
- else
+ } else {
nSequence = (uint32_t)seqNr64;
+ }
}
CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence);
@@ -397,6 +409,10 @@ UniValue createrawtransaction(const JSONRPCRequest& request)
}
}
+ if (request.params.size() > 3 && rbfOptIn != SignalsOptInRBF(rawTx)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict optintorbf option");
+ }
+
return EncodeHexTx(rawTx);
}
diff --git a/src/rpc/server.h b/src/rpc/server.h
index 1e984cbc0d..a893f49033 100644
--- a/src/rpc/server.h
+++ b/src/rpc/server.h
@@ -50,7 +50,7 @@ public:
std::string URI;
std::string authUser;
- JSONRPCRequest() { id = NullUniValue; params = NullUniValue; fHelp = false; }
+ JSONRPCRequest() : id(NullUniValue), params(NullUniValue), fHelp(false) {}
void parse(const UniValue& valRequest);
};
diff --git a/src/test/skiplist_tests.cpp b/src/test/skiplist_tests.cpp
index d70ccafd85..77c321cdf6 100644
--- a/src/test/skiplist_tests.cpp
+++ b/src/test/skiplist_tests.cpp
@@ -142,4 +142,39 @@ BOOST_AUTO_TEST_CASE(findearliestatleast_test)
BOOST_CHECK(vBlocksMain[r].GetAncestor(ret->nHeight) == ret);
}
}
+
+BOOST_AUTO_TEST_CASE(findearliestatleast_edge_test)
+{
+ std::list<CBlockIndex> blocks;
+ for (unsigned int timeMax : {100, 100, 100, 200, 200, 200, 300, 300, 300}) {
+ CBlockIndex* prev = blocks.empty() ? nullptr : &blocks.back();
+ blocks.emplace_back();
+ blocks.back().nHeight = prev ? prev->nHeight + 1 : 0;
+ blocks.back().pprev = prev;
+ blocks.back().BuildSkip();
+ blocks.back().nTimeMax = timeMax;
+ }
+
+ CChain chain;
+ chain.SetTip(&blocks.back());
+
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(50)->nHeight, 0);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(100)->nHeight, 0);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(150)->nHeight, 3);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(200)->nHeight, 3);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(250)->nHeight, 6);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(300)->nHeight, 6);
+ BOOST_CHECK(!chain.FindEarliestAtLeast(350));
+
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0)->nHeight, 0);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(-1)->nHeight, 0);
+
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(std::numeric_limits<int64_t>::min())->nHeight, 0);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(std::numeric_limits<unsigned int>::min())->nHeight, 0);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(-int64_t(std::numeric_limits<unsigned int>::max()) - 1)->nHeight, 0);
+ BOOST_CHECK(!chain.FindEarliestAtLeast(std::numeric_limits<int64_t>::max()));
+ BOOST_CHECK(!chain.FindEarliestAtLeast(std::numeric_limits<unsigned int>::max()));
+ BOOST_CHECK(!chain.FindEarliestAtLeast(int64_t(std::numeric_limits<unsigned int>::max()) + 1));
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/validation.cpp b/src/validation.cpp
index de65839eef..eaefa95411 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -186,8 +186,11 @@ enum FlushStateMode {
};
// See definition for documentation
-bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode, int nManualPruneHeight=0);
-void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight);
+static bool FlushStateToDisk(const CChainParams& chainParams, CValidationState &state, FlushStateMode mode, int nManualPruneHeight=0);
+static void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight);
+static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight);
+static bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks = NULL);
+static FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false);
bool CheckFinalTx(const CTransaction &tx, int flags)
{
@@ -309,7 +312,7 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool
return EvaluateSequenceLocks(index, lockPair);
}
-void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) {
+static void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) {
int expired = pool.Expire(GetTime() - age);
if (expired != 0) {
LogPrint(BCLog::MEMPOOL, "Expired %i transactions from the memory pool\n", expired);
@@ -392,7 +395,7 @@ void UpdateMempoolForReorg(DisconnectedBlockTransactions &disconnectpool, bool f
LimitMempoolSize(mempool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
}
-bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx, bool fLimitFree,
+static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx, bool fLimitFree,
bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
bool fOverrideMempoolLimit, const CAmount& nAbsurdFee, std::vector<COutPoint>& coins_to_uncache)
{
@@ -410,7 +413,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
return state.DoS(100, false, REJECT_INVALID, "coinbase");
// Reject transactions with witness before segregated witness activates (override with -prematurewitness)
- bool witnessEnabled = IsWitnessEnabled(chainActive.Tip(), Params().GetConsensus());
+ bool witnessEnabled = IsWitnessEnabled(chainActive.Tip(), chainparams.GetConsensus());
if (!GetBoolArg("-prematurewitness",false) && tx.HasWitness() && !witnessEnabled) {
return state.DoS(0, false, REJECT_NONSTANDARD, "no-witness-yet", true);
}
@@ -739,7 +742,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
}
unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS;
- if (!Params().RequireStandard()) {
+ if (!chainparams.RequireStandard()) {
scriptVerifyFlags = GetArg("-promiscuousmempoolflags", scriptVerifyFlags);
}
@@ -809,19 +812,20 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
return true;
}
-bool AcceptToMemoryPoolWithTime(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree,
+/** (try to) add transaction to memory pool with a specified acceptance time **/
+static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree,
bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
bool fOverrideMempoolLimit, const CAmount nAbsurdFee)
{
std::vector<COutPoint> coins_to_uncache;
- bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, nAcceptTime, plTxnReplaced, fOverrideMempoolLimit, nAbsurdFee, coins_to_uncache);
+ bool res = AcceptToMemoryPoolWorker(chainparams, pool, state, tx, fLimitFree, pfMissingInputs, nAcceptTime, plTxnReplaced, fOverrideMempoolLimit, nAbsurdFee, coins_to_uncache);
if (!res) {
BOOST_FOREACH(const COutPoint& hashTx, coins_to_uncache)
pcoinsTip->Uncache(hashTx);
}
// After we've (potentially) uncached entries, ensure our coins cache is still within its size limits
CValidationState stateDummy;
- FlushStateToDisk(stateDummy, FLUSH_STATE_PERIODIC);
+ FlushStateToDisk(chainparams, stateDummy, FLUSH_STATE_PERIODIC);
return res;
}
@@ -829,7 +833,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
bool* pfMissingInputs, std::list<CTransactionRef>* plTxnReplaced,
bool fOverrideMempoolLimit, const CAmount nAbsurdFee)
{
- return AcceptToMemoryPoolWithTime(pool, state, tx, fLimitFree, pfMissingInputs, GetTime(), plTxnReplaced, fOverrideMempoolLimit, nAbsurdFee);
+ const CChainParams& chainparams = Params();
+ return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, fLimitFree, pfMissingInputs, GetTime(), plTxnReplaced, fOverrideMempoolLimit, nAbsurdFee);
}
/** Return transaction in txOut, and if it was found inside a block, its hash is placed in hashBlock */
@@ -898,7 +903,7 @@ bool GetTransaction(const uint256 &hash, CTransactionRef &txOut, const Consensus
// CBlock and CBlockIndex
//
-bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart)
+static bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart)
{
// Open history file to append
CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION);
@@ -1011,7 +1016,7 @@ static void AlertNotify(const std::string& strMessage)
boost::thread t(runCommand, strCmd); // thread runs free
}
-void CheckForkWarningConditions()
+static void CheckForkWarningConditions()
{
AssertLockHeld(cs_main);
// Before we get past initial download, we cannot reliably alert about forks
@@ -1052,7 +1057,7 @@ void CheckForkWarningConditions()
}
}
-void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip)
+static void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip)
{
AssertLockHeld(cs_main);
// If we are on a fork that is sufficiently large, set a warning flag
@@ -1144,7 +1149,12 @@ int GetSpendHeight(const CCoinsViewCache& inputs)
return pindexPrev->nHeight + 1;
}
-bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks)
+/**
+ * Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts)
+ * This does not modify the UTXO set. If pvChecks is not NULL, script checks are pushed onto it
+ * instead of being performed inline.
+ */
+static bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks)
{
if (!tx.IsCoinBase())
{
@@ -1411,7 +1421,7 @@ void static FlushBlockFile(bool fFinalize = false)
}
}
-bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize);
+static bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize);
static CCheckQueue<CScriptCheck> scriptcheckqueue(128);
@@ -1730,9 +1740,8 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
* if they're too large, if it's been a while since the last write,
* or always and in all cases if we're in prune mode and are deleting files.
*/
-bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode, int nManualPruneHeight) {
+bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &state, FlushStateMode mode, int nManualPruneHeight) {
int64_t nMempoolUsage = mempool.DynamicMemoryUsage();
- const CChainParams& chainparams = Params();
LOCK2(cs_main, cs_LastBlockFile);
static int64_t nLastWrite = 0;
static int64_t nLastFlush = 0;
@@ -1836,13 +1845,15 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode, int n
void FlushStateToDisk() {
CValidationState state;
- FlushStateToDisk(state, FLUSH_STATE_ALWAYS);
+ const CChainParams& chainparams = Params();
+ FlushStateToDisk(chainparams, state, FLUSH_STATE_ALWAYS);
}
void PruneAndFlush() {
CValidationState state;
fCheckForPruning = true;
- FlushStateToDisk(state, FLUSH_STATE_NONE);
+ const CChainParams& chainparams = Params();
+ FlushStateToDisk(chainparams, state, FLUSH_STATE_NONE);
}
static void DoWarning(const std::string& strWarning)
@@ -1939,7 +1950,7 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
}
LogPrint(BCLog::BENCH, "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * 0.001);
// Write the chain state to disk, if necessary.
- if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED))
+ if (!FlushStateToDisk(chainparams, state, FLUSH_STATE_IF_NEEDED))
return false;
if (disconnectpool) {
@@ -2076,7 +2087,7 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
int64_t nTime4 = GetTimeMicros(); nTimeFlush += nTime4 - nTime3;
LogPrint(BCLog::BENCH, " - Flush: %.2fms [%.2fs]\n", (nTime4 - nTime3) * 0.001, nTimeFlush * 0.000001);
// Write the chain state to disk, if necessary.
- if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED))
+ if (!FlushStateToDisk(chainparams, state, FLUSH_STATE_IF_NEEDED))
return false;
int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4;
LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001);
@@ -2336,7 +2347,7 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
CheckBlockIndex(chainparams.GetConsensus());
// Write changes periodically to disk, after relay.
- if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC)) {
+ if (!FlushStateToDisk(chainparams, state, FLUSH_STATE_PERIODIC)) {
return false;
}
@@ -2453,7 +2464,7 @@ bool ResetBlockFailureFlags(CBlockIndex *pindex) {
return true;
}
-CBlockIndex* AddToBlockIndex(const CBlockHeader& block)
+static CBlockIndex* AddToBlockIndex(const CBlockHeader& block)
{
// Check for duplicate
uint256 hash = block.GetHash();
@@ -2537,7 +2548,7 @@ static bool ReceivedBlockTransactions(const CBlock &block, CValidationState& sta
return true;
}
-bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false)
+static bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false)
{
LOCK(cs_LastBlockFile);
@@ -2594,7 +2605,7 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd
return true;
}
-bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize)
+static bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize)
{
pos.nFile = nFile;
@@ -2625,7 +2636,7 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne
return true;
}
-bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW)
+static bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW = true)
{
// Check proof of work matches claimed amount
if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits, consensusParams))
@@ -2775,7 +2786,10 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
return commitment;
}
-bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev, int64_t nAdjustedTime)
+/** Context-dependent validity checks.
+ * By "context", we mean only the previous block headers, but not the UTXO
+ * set; UTXO-related validity checks are done in ConnectBlock(). */
+static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev, int64_t nAdjustedTime)
{
assert(pindexPrev != NULL);
const int nHeight = pindexPrev->nHeight + 1;
@@ -2802,7 +2816,7 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta
return true;
}
-bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev)
+static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev)
{
const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1;
@@ -3026,7 +3040,7 @@ static bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidation
}
if (fCheckForPruning)
- FlushStateToDisk(state, FLUSH_STATE_NONE); // we just allocated more disk space for block files
+ FlushStateToDisk(chainparams, state, FLUSH_STATE_NONE); // we just allocated more disk space for block files
return true;
}
@@ -3094,7 +3108,7 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
*/
/* Calculate the amount of disk space the block & undo files currently use */
-uint64_t CalculateCurrentUsage()
+static uint64_t CalculateCurrentUsage()
{
uint64_t retval = 0;
BOOST_FOREACH(const CBlockFileInfo &file, vinfoBlockFile) {
@@ -3147,7 +3161,7 @@ void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune)
}
/* Calculate the block/rev files to delete based on height specified by user with RPC command pruneblockchain */
-void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight)
+static void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight)
{
assert(fPruneMode && nManualPruneHeight > 0);
@@ -3172,11 +3186,26 @@ void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeig
void PruneBlockFilesManual(int nManualPruneHeight)
{
CValidationState state;
- FlushStateToDisk(state, FLUSH_STATE_NONE, nManualPruneHeight);
+ const CChainParams& chainparams = Params();
+ FlushStateToDisk(chainparams, state, FLUSH_STATE_NONE, nManualPruneHeight);
}
-/* Calculate the block/rev files that should be deleted to remain under target*/
-void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight)
+/**
+ * Prune block and undo files (blk???.dat and undo???.dat) so that the disk space used is less than a user-defined target.
+ * The user sets the target (in MB) on the command line or in config file. This will be run on startup and whenever new
+ * space is allocated in a block or undo file, staying below the target. Changing back to unpruned requires a reindex
+ * (which in this case means the blockchain must be re-downloaded.)
+ *
+ * Pruning functions are called from FlushStateToDisk when the global fCheckForPruning flag has been set.
+ * Block and undo files are deleted in lock-step (when blk00003.dat is deleted, so is rev00003.dat.)
+ * Pruning cannot take place until the longest chain is at least a certain length (100000 on mainnet, 1000 on testnet, 1000 on regtest).
+ * Pruning will never delete a block within a defined distance (currently 288) from the active chain's tip.
+ * The block index is updated by unsetting HAVE_DATA and HAVE_UNDO for any blocks that were stored in the deleted files.
+ * A db flag records the fact that at least some block files have been pruned.
+ *
+ * @param[out] setFilesToPrune The set of file indices that can be unlinked will be returned
+ */
+static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight)
{
LOCK2(cs_main, cs_LastBlockFile);
if (chainActive.Tip() == NULL || nPruneTarget == 0) {
@@ -3234,7 +3263,7 @@ bool CheckDiskSpace(uint64_t nAdditionalBytes)
return true;
}
-FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly)
+static FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly)
{
if (pos.IsNull())
return NULL;
@@ -3261,7 +3290,8 @@ FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly) {
return OpenDiskFile(pos, "blk", fReadOnly);
}
-FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly) {
+/** Open an undo file (rev?????.dat) */
+static FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly) {
return OpenDiskFile(pos, "rev", fReadOnly);
}
@@ -3533,7 +3563,7 @@ bool RewindBlockIndex(const CChainParams& params)
return error("RewindBlockIndex: unable to disconnect block at height %i", pindex->nHeight);
}
// Occasionally flush state to disk.
- if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC))
+ if (!FlushStateToDisk(params, state, FLUSH_STATE_PERIODIC))
return false;
}
@@ -3582,7 +3612,7 @@ bool RewindBlockIndex(const CChainParams& params)
CheckBlockIndex(params.GetConsensus());
- if (!FlushStateToDisk(state, FLUSH_STATE_ALWAYS)) {
+ if (!FlushStateToDisk(params, state, FLUSH_STATE_ALWAYS)) {
return false;
}
@@ -3655,7 +3685,7 @@ bool InitBlockIndex(const CChainParams& chainparams)
if (!ReceivedBlockTransactions(block, state, pindex, blockPos, chainparams.GetConsensus()))
return error("LoadBlockIndex(): genesis block not accepted");
// Force a chainstate write so that when we VerifyDB in a moment, it doesn't check stale data
- return FlushStateToDisk(state, FLUSH_STATE_ALWAYS);
+ return FlushStateToDisk(chainparams, state, FLUSH_STATE_ALWAYS);
} catch (const std::runtime_error& e) {
return error("LoadBlockIndex(): failed to initialize block database: %s", e.what());
}
@@ -3997,6 +4027,7 @@ static const uint64_t MEMPOOL_DUMP_VERSION = 1;
bool LoadMempool(void)
{
+ const CChainParams& chainparams = Params();
int64_t nExpiryTimeout = GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60;
FILE* filestr = fsbridge::fopen(GetDataDir() / "mempool.dat", "rb");
CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
@@ -4033,7 +4064,7 @@ bool LoadMempool(void)
CValidationState state;
if (nTime + nExpiryTimeout > nNow) {
LOCK(cs_main);
- AcceptToMemoryPoolWithTime(mempool, state, tx, true, NULL, nTime);
+ AcceptToMemoryPoolWithTime(chainparams, mempool, state, tx, true, NULL, nTime, NULL, false, 0);
if (state.IsValid()) {
++count;
} else {
diff --git a/src/validation.h b/src/validation.h
index 096fd0a9ee..3a7f7cf1bc 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -255,8 +255,6 @@ bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, CValidationS
bool CheckDiskSpace(uint64_t nAdditionalBytes = 0);
/** Open a block file (blk?????.dat) */
FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly = false);
-/** Open an undo file (rev?????.dat) */
-FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false);
/** Translation to a filesystem path */
fs::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix);
/** Import blocks from an external file */
@@ -289,23 +287,6 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams);
double GuessVerificationProgress(const ChainTxData& data, CBlockIndex* pindex);
/**
- * Prune block and undo files (blk???.dat and undo???.dat) so that the disk space used is less than a user-defined target.
- * The user sets the target (in MB) on the command line or in config file. This will be run on startup and whenever new
- * space is allocated in a block or undo file, staying below the target. Changing back to unpruned requires a reindex
- * (which in this case means the blockchain must be re-downloaded.)
- *
- * Pruning functions are called from FlushStateToDisk when the global fCheckForPruning flag has been set.
- * Block and undo files are deleted in lock-step (when blk00003.dat is deleted, so is rev00003.dat.)
- * Pruning cannot take place until the longest chain is at least a certain length (100000 on mainnet, 1000 on testnet, 1000 on regtest).
- * Pruning will never delete a block within a defined distance (currently 288) from the active chain's tip.
- * The block index is updated by unsetting HAVE_DATA and HAVE_UNDO for any blocks that were stored in the deleted files.
- * A db flag records the fact that at least some block files have been pruned.
- *
- * @param[out] setFilesToPrune The set of file indices that can be unlinked will be returned
- */
-void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight);
-
-/**
* Mark one block file as pruned.
*/
void PruneOneBlockFile(const int fileNumber);
@@ -330,11 +311,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
bool* pfMissingInputs, std::list<CTransactionRef>* plTxnReplaced = NULL,
bool fOverrideMempoolLimit=false, const CAmount nAbsurdFee=0);
-/** (try to) add transaction to memory pool with a specified acceptance time **/
-bool AcceptToMemoryPoolWithTime(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree,
- bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced = NULL,
- bool fOverrideMempoolLimit=false, const CAmount nAbsurdFee=0);
-
/** Convert CValidationState to a human-readable message for logging */
std::string FormatStateMessage(const CValidationState &state);
@@ -348,14 +324,6 @@ BIP9Stats VersionBitsTipStatistics(const Consensus::Params& params, Consensus::D
int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::DeploymentPos pos);
-/**
- * Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts)
- * This does not modify the UTXO set. If pvChecks is not NULL, script checks are pushed onto it
- * instead of being performed inline.
- */
-bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &view, bool fScriptChecks,
- unsigned int flags, bool cacheStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks = NULL);
-
/** Apply the effects of this transaction on the UTXO set represented by view */
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight);
@@ -428,22 +396,14 @@ public:
/** Functions for disk access for blocks */
-bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart);
bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams);
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams);
/** Functions for validating blocks and updating the block tree */
/** Context-independent validity checks */
-bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW = true);
bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW = true, bool fCheckMerkleRoot = true);
-/** Context-dependent validity checks.
- * By "context", we mean only the previous block headers, but not the UTXO
- * set; UTXO-related validity checks are done in ConnectBlock(). */
-bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev, int64_t nAdjustedTime);
-bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev);
-
/** Check a block is completely valid from start to finish (only works on top of our current best block, with cs_main held) */
bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true);
diff --git a/src/versionbits.cpp b/src/versionbits.cpp
index 80786233f5..4bb352f23c 100644
--- a/src/versionbits.cpp
+++ b/src/versionbits.cpp
@@ -5,7 +5,7 @@
#include "versionbits.h"
#include "consensus/params.h"
-const struct BIP9DeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_BITS_DEPLOYMENTS] = {
+const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_BITS_DEPLOYMENTS] = {
{
/*.name =*/ "testdummy",
/*.gbt_force =*/ true,
diff --git a/src/versionbits.h b/src/versionbits.h
index f1d31ea0af..f4dfb71515 100644
--- a/src/versionbits.h
+++ b/src/versionbits.h
@@ -30,7 +30,7 @@ enum ThresholdState {
// will either be NULL or a block with (height + 1) % Period() == 0.
typedef std::map<const CBlockIndex*, ThresholdState> ThresholdConditionCache;
-struct BIP9DeploymentInfo {
+struct VBDeploymentInfo {
/** Deployment name */
const char *name;
/** Whether GBT clients can safely ignore this rule in simplified usage */
@@ -45,7 +45,7 @@ struct BIP9Stats {
bool possible;
};
-extern const struct BIP9DeploymentInfo VersionBitsDeploymentInfo[];
+extern const struct VBDeploymentInfo VersionBitsDeploymentInfo[];
/**
* Abstract class that implements BIP9-style threshold logic, and caches results.
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index 25f6bdd9d9..89f204bc31 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -211,7 +211,6 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco
{
CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION);
CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION);
- std::string strType, strErr;
if (!(*recoverKVcallback)(callbackDataIn, ssKey, ssValue))
continue;
}
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 3b6dfd42ca..0841c23b86 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -21,6 +21,7 @@
#include "timedata.h"
#include "util.h"
#include "utilmoneystr.h"
+#include "wallet/coincontrol.h"
#include "wallet/feebumper.h"
#include "wallet/wallet.h"
#include "wallet/walletdb.h"
@@ -2628,7 +2629,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
throw std::runtime_error(
"fundrawtransaction \"hexstring\" ( options )\n"
"\nAdd inputs to a transaction until it has enough in value to meet its out value.\n"
@@ -2657,6 +2658,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
" Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
" If no outputs are specified here, the sender pays the fee.\n"
" [vout_index,...]\n"
+ " \"optIntoRbf\" (boolean, optional) Allow this transaction to be replaced by a transaction with higher fees\n"
" }\n"
" for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}\n"
"\nResult:\n"
@@ -2678,20 +2680,21 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR));
- CTxDestination changeAddress = CNoDestination();
+ CCoinControl coinControl;
+ coinControl.destChange = CNoDestination();
int changePosition = -1;
- bool includeWatching = false;
+ coinControl.fAllowWatchOnly = false; // include watching
bool lockUnspents = false;
bool reserveChangeKey = true;
- CFeeRate feeRate = CFeeRate(0);
- bool overrideEstimatedFeerate = false;
+ coinControl.nFeeRate = CFeeRate(0);
+ coinControl.fOverrideFeeRate = false;
UniValue subtractFeeFromOutputs;
std::set<int> setSubtractFeeFromOutputs;
if (request.params.size() > 1) {
if (request.params[1].type() == UniValue::VBOOL) {
// backward compatibility bool only fallback
- includeWatching = request.params[1].get_bool();
+ coinControl.fAllowWatchOnly = request.params[1].get_bool();
}
else {
RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VOBJ));
@@ -2707,6 +2710,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
{"reserveChangeKey", UniValueType(UniValue::VBOOL)},
{"feeRate", UniValueType()}, // will be checked below
{"subtractFeeFromOutputs", UniValueType(UniValue::VARR)},
+ {"optIntoRbf", UniValueType(UniValue::VBOOL)},
},
true, true);
@@ -2716,14 +2720,14 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
if (!address.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "changeAddress must be a valid bitcoin address");
- changeAddress = address.Get();
+ coinControl.destChange = address.Get();
}
if (options.exists("changePosition"))
changePosition = options["changePosition"].get_int();
if (options.exists("includeWatching"))
- includeWatching = options["includeWatching"].get_bool();
+ coinControl.fAllowWatchOnly = options["includeWatching"].get_bool();
if (options.exists("lockUnspents"))
lockUnspents = options["lockUnspents"].get_bool();
@@ -2733,12 +2737,16 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
if (options.exists("feeRate"))
{
- feeRate = CFeeRate(AmountFromValue(options["feeRate"]));
- overrideEstimatedFeerate = true;
+ coinControl.nFeeRate = CFeeRate(AmountFromValue(options["feeRate"]));
+ coinControl.fOverrideFeeRate = true;
}
if (options.exists("subtractFeeFromOutputs"))
subtractFeeFromOutputs = options["subtractFeeFromOutputs"].get_array();
+
+ if (options.exists("optIntoRbf")) {
+ coinControl.signalRbf = options["optIntoRbf"].get_bool();
+ }
}
}
@@ -2767,7 +2775,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
CAmount nFeeOut;
std::string strFailReason;
- if (!pwallet->FundTransaction(tx, nFeeOut, overrideEstimatedFeerate, feeRate, changePosition, strFailReason, includeWatching, lockUnspents, setSubtractFeeFromOutputs, reserveChangeKey, changeAddress)) {
+ if (!pwallet->FundTransaction(tx, nFeeOut, changePosition, strFailReason, lockUnspents, setSubtractFeeFromOutputs, coinControl, reserveChangeKey)) {
throw JSONRPCError(RPC_WALLET_ERROR, strFailReason);
}
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 3e000d2a9d..b2706d09f6 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -2414,7 +2414,7 @@ bool CWallet::SignTransaction(CMutableTransaction &tx)
return true;
}
-bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, bool keepReserveKey, const CTxDestination& destChange)
+bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl coinControl, bool keepReserveKey)
{
std::vector<CRecipient> vecSend;
@@ -2426,12 +2426,7 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool ov
vecSend.push_back(recipient);
}
- CCoinControl coinControl;
- coinControl.destChange = destChange;
coinControl.fAllowOtherInputs = true;
- coinControl.fAllowWatchOnly = includeWatching;
- coinControl.fOverrideFeeRate = overrideEstimatedFeeRate;
- coinControl.nFeeRate = specificFeeRate;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
coinControl.Select(txin.prevout);
@@ -2690,9 +2685,10 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
// and in the spirit of "smallest possible change from prior
// behavior."
bool rbf = coinControl ? coinControl->signalRbf : fWalletRbf;
+ const uint32_t nSequence = rbf ? MAX_BIP125_RBF_SEQUENCE : (std::numeric_limits<unsigned int>::max() - 1);
for (const auto& coin : setCoins)
txNew.vin.push_back(CTxIn(coin.outpoint,CScript(),
- std::numeric_limits<unsigned int>::max() - (rbf ? 2 : 1)));
+ nSequence));
// Fill in dummy signatures for fee calculation.
if (!DummySignTx(txNew, setCoins)) {
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index a3974bf00b..a9c50aee4d 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -136,10 +136,7 @@ public:
std::string name;
std::string purpose;
- CAddressBookData()
- {
- purpose = "unknown";
- }
+ CAddressBookData() : purpose("unknown") {}
typedef std::map<std::string, std::string> StringMap;
StringMap destdata;
@@ -935,7 +932,7 @@ public:
* Insert additional inputs into the transaction by
* calling CreateTransaction();
*/
- bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, bool keepReserveKey = true, const CTxDestination& destChange = CNoDestination());
+ bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl, bool keepReserveKey = true);
bool SignTransaction(CMutableTransaction& tx);
/**
diff --git a/test/functional/config.ini.in b/test/config.ini.in
index 29586c555d..35ee092be4 100644
--- a/test/functional/config.ini.in
+++ b/test/config.ini.in
@@ -3,7 +3,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
# These environment variables are set by the build process and read by
-# test/functional/test_runner.py
+# test/functional/test_runner.py and test/util/bitcoin-util-test.py
[environment]
SRCDIR=@abs_top_srcdir@
diff --git a/test/functional/bip68-sequence.py b/test/functional/bip68-sequence.py
index db66b7719c..87a50692f6 100755
--- a/test/functional/bip68-sequence.py
+++ b/test/functional/bip68-sequence.py
@@ -241,7 +241,7 @@ class BIP68Test(BitcoinTestFramework):
# Now mine some blocks, but make sure tx2 doesn't get mined.
# Use prioritisetransaction to lower the effective feerate to 0
- self.nodes[0].prioritisetransaction(tx2.hash, int(-self.relayfee*COIN))
+ self.nodes[0].prioritisetransaction(txid=tx2.hash, fee_delta=int(-self.relayfee*COIN))
cur_time = int(time.time())
for i in range(10):
self.nodes[0].setmocktime(cur_time + 600)
@@ -254,7 +254,7 @@ class BIP68Test(BitcoinTestFramework):
test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=False)
# Mine tx2, and then try again
- self.nodes[0].prioritisetransaction(tx2.hash, int(self.relayfee*COIN))
+ self.nodes[0].prioritisetransaction(txid=tx2.hash, fee_delta=int(self.relayfee*COIN))
# Advance the time on the node so that we can test timelocks
self.nodes[0].setmocktime(cur_time+600)
diff --git a/test/functional/blockchain.py b/test/functional/blockchain.py
index 5ada3e629e..6aef6d4489 100755
--- a/test/functional/blockchain.py
+++ b/test/functional/blockchain.py
@@ -59,6 +59,7 @@ class BlockchainTest(BitcoinTestFramework):
assert_equal(res['transactions'], 200)
assert_equal(res['height'], 200)
assert_equal(res['txouts'], 200)
+ assert_equal(res['bogosize'], 17000),
assert_equal(res['bestblock'], node.getblockhash(200))
size = res['disk_size']
assert size > 6400
@@ -75,6 +76,7 @@ class BlockchainTest(BitcoinTestFramework):
assert_equal(res2['total_amount'], Decimal('0'))
assert_equal(res2['height'], 0)
assert_equal(res2['txouts'], 0)
+ assert_equal(res2['bogosize'], 0),
assert_equal(res2['bestblock'], node.getblockhash(0))
assert_equal(len(res2['hash_serialized_2']), 64)
@@ -86,6 +88,7 @@ class BlockchainTest(BitcoinTestFramework):
assert_equal(res['transactions'], res3['transactions'])
assert_equal(res['height'], res3['height'])
assert_equal(res['txouts'], res3['txouts'])
+ assert_equal(res['bogosize'], res3['bogosize'])
assert_equal(res['bestblock'], res3['bestblock'])
assert_equal(res['hash_serialized_2'], res3['hash_serialized_2'])
diff --git a/test/functional/fundrawtransaction.py b/test/functional/fundrawtransaction.py
index c41afe2b93..0a3166b89b 100755
--- a/test/functional/fundrawtransaction.py
+++ b/test/functional/fundrawtransaction.py
@@ -452,7 +452,7 @@ class RawTransactionsTest(BitcoinTestFramework):
self.stop_node(2)
self.stop_node(3)
self.nodes[1].encryptwallet("test")
- self.nodes.pop(1)
+ bitcoind_processes[1].wait(timeout=BITCOIND_PROC_WAIT_TIMEOUT)
self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir)
# This test is not meant to test fee estimation and we'd like
diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py
index 72f04095f4..e225493816 100755
--- a/test/functional/mempool_packages.py
+++ b/test/functional/mempool_packages.py
@@ -96,7 +96,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
# Check that ancestor modified fees includes fee deltas from
# prioritisetransaction
- self.nodes[0].prioritisetransaction(chain[0], 1000)
+ self.nodes[0].prioritisetransaction(txid=chain[0], fee_delta=1000)
mempool = self.nodes[0].getrawmempool(True)
ancestor_fees = 0
for x in chain:
@@ -104,11 +104,11 @@ class MempoolPackagesTest(BitcoinTestFramework):
assert_equal(mempool[x]['ancestorfees'], ancestor_fees * COIN + 1000)
# Undo the prioritisetransaction for later tests
- self.nodes[0].prioritisetransaction(chain[0], -1000)
+ self.nodes[0].prioritisetransaction(txid=chain[0], fee_delta=-1000)
# Check that descendant modified fees includes fee deltas from
# prioritisetransaction
- self.nodes[0].prioritisetransaction(chain[-1], 1000)
+ self.nodes[0].prioritisetransaction(txid=chain[-1], fee_delta=1000)
mempool = self.nodes[0].getrawmempool(True)
descendant_fees = 0
@@ -126,7 +126,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
assert_equal(len(self.nodes[0].getrawmempool()), 0)
# Prioritise a transaction that has been mined, then add it back to the
# mempool by using invalidateblock.
- self.nodes[0].prioritisetransaction(chain[-1], 2000)
+ self.nodes[0].prioritisetransaction(txid=chain[-1], fee_delta=2000)
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
# Keep node1's tip synced with node0
self.nodes[1].invalidateblock(self.nodes[1].getbestblockhash())
diff --git a/test/functional/p2p-compactblocks.py b/test/functional/p2p-compactblocks.py
index 9b302120ac..ff76e49fba 100755
--- a/test/functional/p2p-compactblocks.py
+++ b/test/functional/p2p-compactblocks.py
@@ -98,7 +98,7 @@ class CompactBlocksTest(BitcoinTestFramework):
self.setup_clean_chain = True
# Node0 = pre-segwit, node1 = segwit-aware
self.num_nodes = 2
- self.extra_args = [["-bip9params=segwit:0:0"], ["-txindex"]]
+ self.extra_args = [["-vbparams=segwit:0:0"], ["-txindex"]]
self.utxos = []
def build_block_on_tip(self, node, segwit=False):
diff --git a/test/functional/p2p-segwit.py b/test/functional/p2p-segwit.py
index ab97596ba3..dbc61d21fc 100755
--- a/test/functional/p2p-segwit.py
+++ b/test/functional/p2p-segwit.py
@@ -114,7 +114,7 @@ class SegWitTest(BitcoinTestFramework):
super().__init__()
self.setup_clean_chain = True
self.num_nodes = 3
- self.extra_args = [["-whitelist=127.0.0.1"], ["-whitelist=127.0.0.1", "-acceptnonstdtxn=0"], ["-whitelist=127.0.0.1", "-bip9params=segwit:0:0"]]
+ self.extra_args = [["-whitelist=127.0.0.1"], ["-whitelist=127.0.0.1", "-acceptnonstdtxn=0"], ["-whitelist=127.0.0.1", "-vbparams=segwit:0:0"]]
def setup_network(self):
self.setup_nodes()
diff --git a/test/functional/prioritise_transaction.py b/test/functional/prioritise_transaction.py
index 9c3b3fd5d9..4fc03d2547 100755
--- a/test/functional/prioritise_transaction.py
+++ b/test/functional/prioritise_transaction.py
@@ -46,7 +46,7 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
# add a fee delta to something in the cheapest bucket and make sure it gets mined
# also check that a different entry in the cheapest bucket is NOT mined
- self.nodes[0].prioritisetransaction(txids[0][0], int(3*base_fee*COIN))
+ self.nodes[0].prioritisetransaction(txid=txids[0][0], fee_delta=int(3*base_fee*COIN))
self.nodes[0].generate(1)
@@ -65,7 +65,7 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
# Add a prioritisation before a tx is in the mempool (de-prioritising a
# high-fee transaction so that it's now low fee).
- self.nodes[0].prioritisetransaction(high_fee_tx, -int(2*base_fee*COIN))
+ self.nodes[0].prioritisetransaction(txid=high_fee_tx, fee_delta=-int(2*base_fee*COIN))
# Add everything back to mempool
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
@@ -109,7 +109,7 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
# This is a less than 1000-byte transaction, so just set the fee
# to be the minimum for a 1000 byte transaction and check that it is
# accepted.
- self.nodes[0].prioritisetransaction(tx_id, int(self.relayfee*COIN))
+ self.nodes[0].prioritisetransaction(txid=tx_id, fee_delta=int(self.relayfee*COIN))
self.log.info("Assert that prioritised free transaction is accepted to mempool")
assert_equal(self.nodes[0].sendrawtransaction(tx_hex), tx_id)
@@ -120,7 +120,7 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
mock_time = int(time.time())
self.nodes[0].setmocktime(mock_time)
template = self.nodes[0].getblocktemplate()
- self.nodes[0].prioritisetransaction(tx_id, -int(self.relayfee*COIN))
+ self.nodes[0].prioritisetransaction(txid=tx_id, fee_delta=-int(self.relayfee*COIN))
self.nodes[0].setmocktime(mock_time+10)
new_template = self.nodes[0].getblocktemplate()
diff --git a/test/functional/replace-by-fee.py b/test/functional/replace-by-fee.py
index e940ce535c..ca7b623ca3 100755
--- a/test/functional/replace-by-fee.py
+++ b/test/functional/replace-by-fee.py
@@ -99,6 +99,9 @@ class ReplaceByFeeTest(BitcoinTestFramework):
self.log.info("Running test opt-in...")
self.test_opt_in()
+ self.log.info("Running test RPC...")
+ self.test_rpc()
+
self.log.info("Running test prioritised transactions...")
self.test_prioritised_transactions()
@@ -482,7 +485,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
assert_raises_jsonrpc(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True)
# Use prioritisetransaction to set tx1a's fee to 0.
- self.nodes[0].prioritisetransaction(tx1a_txid, int(-0.1*COIN))
+ self.nodes[0].prioritisetransaction(txid=tx1a_txid, fee_delta=int(-0.1*COIN))
# Now tx1b should be able to replace tx1a
tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True)
@@ -509,12 +512,32 @@ class ReplaceByFeeTest(BitcoinTestFramework):
assert_raises_jsonrpc(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx2b_hex, True)
# Now prioritise tx2b to have a higher modified fee
- self.nodes[0].prioritisetransaction(tx2b.hash, int(0.1*COIN))
+ self.nodes[0].prioritisetransaction(txid=tx2b.hash, fee_delta=int(0.1*COIN))
# tx2b should now be accepted
tx2b_txid = self.nodes[0].sendrawtransaction(tx2b_hex, True)
assert(tx2b_txid in self.nodes[0].getrawmempool())
+ def test_rpc(self):
+ us0 = self.nodes[0].listunspent()[0]
+ ins = [us0];
+ outs = {self.nodes[0].getnewaddress() : Decimal(1.0000000)}
+ rawtx0 = self.nodes[0].createrawtransaction(ins, outs, 0, True)
+ rawtx1 = self.nodes[0].createrawtransaction(ins, outs, 0, False)
+ json0 = self.nodes[0].decoderawtransaction(rawtx0)
+ json1 = self.nodes[0].decoderawtransaction(rawtx1)
+ assert_equal(json0["vin"][0]["sequence"], 4294967293)
+ assert_equal(json1["vin"][0]["sequence"], 4294967295)
+
+ rawtx2 = self.nodes[0].createrawtransaction([], outs)
+ frawtx2a = self.nodes[0].fundrawtransaction(rawtx2, {"optIntoRbf": True})
+ frawtx2b = self.nodes[0].fundrawtransaction(rawtx2, {"optIntoRbf": False})
+
+ json0 = self.nodes[0].decoderawtransaction(frawtx2a['hex'])
+ json1 = self.nodes[0].decoderawtransaction(frawtx2b['hex'])
+ assert_equal(json0["vin"][0]["sequence"], 4294967293)
+ assert_equal(json1["vin"][0]["sequence"], 4294967294)
+
if __name__ == '__main__':
ReplaceByFeeTest().main()
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index b2aee7c739..4702f2d773 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -180,7 +180,7 @@ def main():
# Read config generated by configure.
config = configparser.ConfigParser()
- configfile = os.path.abspath(os.path.dirname(__file__)) + "/config.ini"
+ configfile = os.path.abspath(os.path.dirname(__file__)) + "/../config.ini"
config.read_file(open(configfile))
passon_args.append("--configfile=%s" % configfile)
diff --git a/test/util/bctest.py b/test/util/bctest.py
deleted file mode 100644
index b17cf77ae3..0000000000
--- a/test/util/bctest.py
+++ /dev/null
@@ -1,139 +0,0 @@
-# Copyright 2014 BitPay Inc.
-# Copyright 2016 The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-from __future__ import division,print_function,unicode_literals
-import subprocess
-import os
-import json
-import sys
-import binascii
-import difflib
-import logging
-import pprint
-
-def parse_output(a, fmt):
- """Parse the output according to specified format.
-
- Raise an error if the output can't be parsed."""
- if fmt == 'json': # json: compare parsed data
- return json.loads(a)
- elif fmt == 'hex': # hex: parse and compare binary data
- return binascii.a2b_hex(a.strip())
- else:
- raise NotImplementedError("Don't know how to compare %s" % fmt)
-
-def bctest(testDir, testObj, buildenv):
- """Runs a single test, comparing output and RC to expected output and RC.
-
- Raises an error if input can't be read, executable fails, or output/RC
- are not as expected. Error is caught by bctester() and reported.
- """
- # Get the exec names and arguments
- execprog = buildenv.BUILDDIR + "/src/" + testObj['exec'] + buildenv.exeext
- execargs = testObj['args']
- execrun = [execprog] + execargs
-
- # Read the input data (if there is any)
- stdinCfg = None
- inputData = None
- if "input" in testObj:
- filename = testDir + "/" + testObj['input']
- inputData = open(filename).read()
- stdinCfg = subprocess.PIPE
-
- # Read the expected output data (if there is any)
- outputFn = None
- outputData = None
- if "output_cmp" in testObj:
- outputFn = testObj['output_cmp']
- outputType = os.path.splitext(outputFn)[1][1:] # output type from file extension (determines how to compare)
- try:
- outputData = open(testDir + "/" + outputFn).read()
- except:
- logging.error("Output file " + outputFn + " can not be opened")
- raise
- if not outputData:
- logging.error("Output data missing for " + outputFn)
- raise Exception
-
- # Run the test
- proc = subprocess.Popen(execrun, stdin=stdinCfg, stdout=subprocess.PIPE, stderr=subprocess.PIPE,universal_newlines=True)
- try:
- outs = proc.communicate(input=inputData)
- except OSError:
- logging.error("OSError, Failed to execute " + execprog)
- raise
-
- if outputData:
- data_mismatch, formatting_mismatch = False, False
- # Parse command output and expected output
- try:
- a_parsed = parse_output(outs[0], outputType)
- except Exception as e:
- logging.error('Error parsing command output as %s: %s' % (outputType,e))
- raise
- try:
- b_parsed = parse_output(outputData, outputType)
- except Exception as e:
- logging.error('Error parsing expected output %s as %s: %s' % (outputFn,outputType,e))
- raise
- # Compare data
- if a_parsed != b_parsed:
- logging.error("Output data mismatch for " + outputFn + " (format " + outputType + ")")
- data_mismatch = True
- # Compare formatting
- if outs[0] != outputData:
- error_message = "Output formatting mismatch for " + outputFn + ":\n"
- error_message += "".join(difflib.context_diff(outputData.splitlines(True),
- outs[0].splitlines(True),
- fromfile=outputFn,
- tofile="returned"))
- logging.error(error_message)
- formatting_mismatch = True
-
- assert not data_mismatch and not formatting_mismatch
-
- # Compare the return code to the expected return code
- wantRC = 0
- if "return_code" in testObj:
- wantRC = testObj['return_code']
- if proc.returncode != wantRC:
- logging.error("Return code mismatch for " + outputFn)
- raise Exception
-
- if "error_txt" in testObj:
- want_error = testObj["error_txt"]
- # Compare error text
- # TODO: ideally, we'd compare the strings exactly and also assert
- # That stderr is empty if no errors are expected. However, bitcoin-tx
- # emits DISPLAY errors when running as a windows application on
- # linux through wine. Just assert that the expected error text appears
- # somewhere in stderr.
- if want_error not in outs[1]:
- logging.error("Error mismatch:\n" + "Expected: " + want_error + "\nReceived: " + outs[1].rstrip())
- raise Exception
-
-def bctester(testDir, input_basename, buildenv):
- """ Loads and parses the input file, runs all tests and reports results"""
- input_filename = testDir + "/" + input_basename
- raw_data = open(input_filename).read()
- input_data = json.loads(raw_data)
-
- failed_testcases = []
-
- for testObj in input_data:
- try:
- bctest(testDir, testObj, buildenv)
- logging.info("PASSED: " + testObj["description"])
- except:
- logging.info("FAILED: " + testObj["description"])
- failed_testcases.append(testObj["description"])
-
- if failed_testcases:
- error_message = "FAILED_TESTCASES:\n"
- error_message += pprint.pformat(failed_testcases, width=400)
- logging.error(error_message)
- sys.exit(1)
- else:
- sys.exit(0)
diff --git a/test/util/bitcoin-util-test.py b/test/util/bitcoin-util-test.py
index e09a25159d..d15d6a6011 100755
--- a/test/util/bitcoin-util-test.py
+++ b/test/util/bitcoin-util-test.py
@@ -1,26 +1,30 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright 2014 BitPay Inc.
-# Copyright 2016 The Bitcoin Core developers
+# Copyright 2016-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.
-from __future__ import division,print_function,unicode_literals
-import os
-import sys
-import argparse
-import logging
+"""Test framework for bitcoin utils.
-help_text="""Test framework for bitcoin utils.
-
-Runs automatically during `make check`.
+Runs automatically during `make check`.
Can also be run manually."""
-if __name__ == '__main__':
- sys.path.append(os.path.dirname(os.path.abspath(__file__)))
- import buildenv
- import bctest
+import argparse
+import binascii
+import configparser
+import difflib
+import json
+import logging
+import os
+import pprint
+import subprocess
+import sys
- parser = argparse.ArgumentParser(description=help_text)
+def main():
+ config = configparser.ConfigParser()
+ config.read_file(open(os.path.dirname(__file__) + "/../config.ini"))
+
+ parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('-v', '--verbose', action='store_true')
args = parser.parse_args()
verbose = args.verbose
@@ -31,6 +35,135 @@ if __name__ == '__main__':
level = logging.ERROR
formatter = '%(asctime)s - %(levelname)s - %(message)s'
# Add the format/level to the logger
- logging.basicConfig(format = formatter, level=level)
+ logging.basicConfig(format=formatter, level=level)
+
+ bctester(config["environment"]["SRCDIR"] + "/test/util/data", "bitcoin-util-test.json", config["environment"])
+
+def bctester(testDir, input_basename, buildenv):
+ """ Loads and parses the input file, runs all tests and reports results"""
+ input_filename = testDir + "/" + input_basename
+ raw_data = open(input_filename).read()
+ input_data = json.loads(raw_data)
+
+ failed_testcases = []
+
+ for testObj in input_data:
+ try:
+ bctest(testDir, testObj, buildenv)
+ logging.info("PASSED: " + testObj["description"])
+ except:
+ logging.info("FAILED: " + testObj["description"])
+ failed_testcases.append(testObj["description"])
- bctest.bctester(buildenv.SRCDIR + "/test/util/data", "bitcoin-util-test.json", buildenv)
+ if failed_testcases:
+ error_message = "FAILED_TESTCASES:\n"
+ error_message += pprint.pformat(failed_testcases, width=400)
+ logging.error(error_message)
+ sys.exit(1)
+ else:
+ sys.exit(0)
+
+def bctest(testDir, testObj, buildenv):
+ """Runs a single test, comparing output and RC to expected output and RC.
+
+ Raises an error if input can't be read, executable fails, or output/RC
+ are not as expected. Error is caught by bctester() and reported.
+ """
+ # Get the exec names and arguments
+ execprog = buildenv["BUILDDIR"] + "/src/" + testObj['exec'] + buildenv["EXEEXT"]
+ execargs = testObj['args']
+ execrun = [execprog] + execargs
+
+ # Read the input data (if there is any)
+ stdinCfg = None
+ inputData = None
+ if "input" in testObj:
+ filename = testDir + "/" + testObj['input']
+ inputData = open(filename).read()
+ stdinCfg = subprocess.PIPE
+
+ # Read the expected output data (if there is any)
+ outputFn = None
+ outputData = None
+ if "output_cmp" in testObj:
+ outputFn = testObj['output_cmp']
+ outputType = os.path.splitext(outputFn)[1][1:] # output type from file extension (determines how to compare)
+ try:
+ outputData = open(testDir + "/" + outputFn).read()
+ except:
+ logging.error("Output file " + outputFn + " can not be opened")
+ raise
+ if not outputData:
+ logging.error("Output data missing for " + outputFn)
+ raise Exception
+
+ # Run the test
+ proc = subprocess.Popen(execrun, stdin=stdinCfg, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
+ try:
+ outs = proc.communicate(input=inputData)
+ except OSError:
+ logging.error("OSError, Failed to execute " + execprog)
+ raise
+
+ if outputData:
+ data_mismatch, formatting_mismatch = False, False
+ # Parse command output and expected output
+ try:
+ a_parsed = parse_output(outs[0], outputType)
+ except Exception as e:
+ logging.error('Error parsing command output as %s: %s' % (outputType, e))
+ raise
+ try:
+ b_parsed = parse_output(outputData, outputType)
+ except Exception as e:
+ logging.error('Error parsing expected output %s as %s: %s' % (outputFn, outputType, e))
+ raise
+ # Compare data
+ if a_parsed != b_parsed:
+ logging.error("Output data mismatch for " + outputFn + " (format " + outputType + ")")
+ data_mismatch = True
+ # Compare formatting
+ if outs[0] != outputData:
+ error_message = "Output formatting mismatch for " + outputFn + ":\n"
+ error_message += "".join(difflib.context_diff(outputData.splitlines(True),
+ outs[0].splitlines(True),
+ fromfile=outputFn,
+ tofile="returned"))
+ logging.error(error_message)
+ formatting_mismatch = True
+
+ assert not data_mismatch and not formatting_mismatch
+
+ # Compare the return code to the expected return code
+ wantRC = 0
+ if "return_code" in testObj:
+ wantRC = testObj['return_code']
+ if proc.returncode != wantRC:
+ logging.error("Return code mismatch for " + outputFn)
+ raise Exception
+
+ if "error_txt" in testObj:
+ want_error = testObj["error_txt"]
+ # Compare error text
+ # TODO: ideally, we'd compare the strings exactly and also assert
+ # That stderr is empty if no errors are expected. However, bitcoin-tx
+ # emits DISPLAY errors when running as a windows application on
+ # linux through wine. Just assert that the expected error text appears
+ # somewhere in stderr.
+ if want_error not in outs[1]:
+ logging.error("Error mismatch:\n" + "Expected: " + want_error + "\nReceived: " + outs[1].rstrip())
+ raise Exception
+
+def parse_output(a, fmt):
+ """Parse the output according to specified format.
+
+ Raise an error if the output can't be parsed."""
+ if fmt == 'json': # json: compare parsed data
+ return json.loads(a)
+ elif fmt == 'hex': # hex: parse and compare binary data
+ return binascii.a2b_hex(a.strip())
+ else:
+ raise NotImplementedError("Don't know how to compare %s" % fmt)
+
+if __name__ == '__main__':
+ main()
diff --git a/test/util/buildenv.py.in b/test/util/buildenv.py.in
deleted file mode 100644
index 33030b0348..0000000000
--- a/test/util/buildenv.py.in
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/usr/bin/env python
-exeext="@EXEEXT@"
-SRCDIR="@abs_top_srcdir@"
-BUILDDIR="@abs_top_builddir@"