aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xci/test/06_script_a.sh7
-rw-r--r--contrib/gitian-descriptors/gitian-win-signer.yml1
-rw-r--r--src/interfaces/chain.cpp2
-rw-r--r--[-rwxr-xr-x]src/qt/res/icons/bitcoin.icobin57964 -> 57964 bytes
-rw-r--r--src/script/interpreter.cpp17
-rw-r--r--src/script/interpreter.h7
-rw-r--r--src/test/coins_tests.cpp25
-rw-r--r--src/test/interfaces_tests.cpp6
-rw-r--r--src/validation.cpp17
-rwxr-xr-xtest/functional/interface_bitcoin_cli.py40
10 files changed, 89 insertions, 33 deletions
diff --git a/ci/test/06_script_a.sh b/ci/test/06_script_a.sh
index 8d77b5408a..2e18ad1d52 100755
--- a/ci/test/06_script_a.sh
+++ b/ci/test/06_script_a.sh
@@ -7,10 +7,7 @@
export LC_ALL=C.UTF-8
BITCOIN_CONFIG_ALL="--disable-dependency-tracking --prefix=$DEPENDS_DIR/$HOST --bindir=$BASE_OUTDIR/bin --libdir=$BASE_OUTDIR/lib"
-DOCKER_EXEC "command -v ccache > /dev/null && ccache --zero-stats"
-if [ -z "$NO_DEPENDS" ]; then
- DOCKER_EXEC ccache --max-size=$CCACHE_SIZE
-fi
+DOCKER_EXEC "ccache --zero-stats --max-size=$CCACHE_SIZE"
BEGIN_FOLD autogen
if [ -n "$CONFIG_SHELL" ]; then
@@ -47,5 +44,5 @@ DOCKER_EXEC make $MAKEJOBS $GOAL || ( echo "Build failure. Verbose build follows
END_FOLD
BEGIN_FOLD ccache_stats
-DOCKER_EXEC "command -v ccache > /dev/null && ccache --version | head -n 1 && ccache --show-stats"
+DOCKER_EXEC "ccache --version | head -n 1 && ccache --show-stats"
END_FOLD
diff --git a/contrib/gitian-descriptors/gitian-win-signer.yml b/contrib/gitian-descriptors/gitian-win-signer.yml
index 9d96465742..6bcd126662 100644
--- a/contrib/gitian-descriptors/gitian-win-signer.yml
+++ b/contrib/gitian-descriptors/gitian-win-signer.yml
@@ -8,6 +8,7 @@ architectures:
packages:
- "libssl-dev"
- "autoconf"
+- "automake"
- "libtool"
- "pkg-config"
remotes:
diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp
index c8311b2986..0e7641ae32 100644
--- a/src/interfaces/chain.cpp
+++ b/src/interfaces/chain.cpp
@@ -275,6 +275,8 @@ public:
const CBlockIndex* block1 = LookupBlockIndex(block_hash1);
const CBlockIndex* block2 = LookupBlockIndex(block_hash2);
const CBlockIndex* ancestor = block1 && block2 ? LastCommonAncestor(block1, block2) : nullptr;
+ // Using & instead of && below to avoid short circuiting and leaving
+ // output uninitialized.
return FillBlock(ancestor, ancestor_out, lock) & FillBlock(block1, block1_out, lock) & FillBlock(block2, block2_out, lock);
}
void findCoins(std::map<COutPoint, Coin>& coins) override { return FindCoins(m_node, coins); }
diff --git a/src/qt/res/icons/bitcoin.ico b/src/qt/res/icons/bitcoin.ico
index 8f5045015d..8f5045015d 100755..100644
--- a/src/qt/res/icons/bitcoin.ico
+++ b/src/qt/res/icons/bitcoin.ico
Binary files differ
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index 50f7806ba5..d70960a8bd 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -1291,18 +1291,29 @@ uint256 GetOutputsHash(const T& txTo)
} // namespace
template <class T>
-PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo)
+void PrecomputedTransactionData::Init(const T& txTo)
{
+ assert(!m_ready);
+
// Cache is calculated only for transactions with witness
if (txTo.HasWitness()) {
hashPrevouts = GetPrevoutHash(txTo);
hashSequence = GetSequenceHash(txTo);
hashOutputs = GetOutputsHash(txTo);
- ready = true;
}
+
+ m_ready = true;
+}
+
+template <class T>
+PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo)
+{
+ Init(txTo);
}
// explicit instantiation
+template void PrecomputedTransactionData::Init(const CTransaction& txTo);
+template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo);
template PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo);
template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo);
@@ -1315,7 +1326,7 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
uint256 hashPrevouts;
uint256 hashSequence;
uint256 hashOutputs;
- const bool cacheready = cache && cache->ready;
+ const bool cacheready = cache && cache->m_ready;
if (!(nHashType & SIGHASH_ANYONECANPAY)) {
hashPrevouts = cacheready ? cache->hashPrevouts : GetPrevoutHash(txTo);
diff --git a/src/script/interpreter.h b/src/script/interpreter.h
index 2b104a608c..71f2436369 100644
--- a/src/script/interpreter.h
+++ b/src/script/interpreter.h
@@ -121,7 +121,12 @@ bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned i
struct PrecomputedTransactionData
{
uint256 hashPrevouts, hashSequence, hashOutputs;
- bool ready = false;
+ bool m_ready = false;
+
+ PrecomputedTransactionData() = default;
+
+ template <class T>
+ void Init(const T& tx);
template <class T>
explicit PrecomputedTransactionData(const T& tx);
diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
index 436c1bffa0..c91621e227 100644
--- a/src/test/coins_tests.cpp
+++ b/src/test/coins_tests.cpp
@@ -8,6 +8,7 @@
#include <script/standard.h>
#include <streams.h>
#include <test/util/setup_common.h>
+#include <txdb.h>
#include <uint256.h>
#include <undo.h>
#include <util/strencodings.h>
@@ -109,7 +110,12 @@ static const unsigned int NUM_SIMULATION_ITERATIONS = 40000;
//
// During the process, booleans are kept to make sure that the randomized
// operation hits all branches.
-BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
+//
+// If fake_best_block is true, assign a random uint256 to mock the recording
+// of best block on flush. This is necessary when using CCoinsViewDB as the base,
+// otherwise we'll hit an assertion in BatchWrite.
+//
+void SimulationTest(CCoinsView* base, bool fake_best_block)
{
// Various coverage trackers.
bool removed_all_caches = false;
@@ -126,9 +132,8 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
std::map<COutPoint, Coin> result;
// The cache stack.
- CCoinsViewTest base; // A CCoinsViewTest at the bottom.
std::vector<CCoinsViewCacheTest*> stack; // A stack of CCoinsViewCaches on top.
- stack.push_back(new CCoinsViewCacheTest(&base)); // Start with one cache.
+ stack.push_back(new CCoinsViewCacheTest(base)); // Start with one cache.
// Use a limited set of random transaction ids, so we do test overwriting entries.
std::vector<uint256> txids;
@@ -211,6 +216,7 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
// Every 100 iterations, flush an intermediate cache
if (stack.size() > 1 && InsecureRandBool() == 0) {
unsigned int flushIndex = InsecureRandRange(stack.size() - 1);
+ if (fake_best_block) stack[flushIndex]->SetBestBlock(InsecureRand256());
BOOST_CHECK(stack[flushIndex]->Flush());
}
}
@@ -218,13 +224,14 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
// Every 100 iterations, change the cache stack.
if (stack.size() > 0 && InsecureRandBool() == 0) {
//Remove the top cache
+ if (fake_best_block) stack.back()->SetBestBlock(InsecureRand256());
BOOST_CHECK(stack.back()->Flush());
delete stack.back();
stack.pop_back();
}
if (stack.size() == 0 || (stack.size() < 4 && InsecureRandBool())) {
//Add a new cache
- CCoinsView* tip = &base;
+ CCoinsView* tip = base;
if (stack.size() > 0) {
tip = stack.back();
} else {
@@ -256,6 +263,16 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
BOOST_CHECK(uncached_an_entry);
}
+// Run the above simulation for multiple base types.
+BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
+{
+ CCoinsViewTest base;
+ SimulationTest(&base, false);
+
+ CCoinsViewDB db_base{"test", /*nCacheSize*/ 1 << 23, /*fMemory*/ true, /*fWipe*/ false};
+ SimulationTest(&db_base, true);
+}
+
// Store of all necessary tx and undo data for next test
typedef std::map<COutPoint, std::tuple<CTransaction,CTxUndo,Coin>> UtxoData;
UtxoData utxoData;
diff --git a/src/test/interfaces_tests.cpp b/src/test/interfaces_tests.cpp
index fab3571756..b0d4de89f3 100644
--- a/src/test/interfaces_tests.cpp
+++ b/src/test/interfaces_tests.cpp
@@ -116,6 +116,12 @@ BOOST_AUTO_TEST_CASE(findCommonAncestor)
BOOST_CHECK_EQUAL(orig_height, orig_tip->nHeight);
BOOST_CHECK_EQUAL(fork_height, orig_tip->nHeight - 10);
BOOST_CHECK_EQUAL(fork_hash, active[fork_height]->GetBlockHash());
+
+ uint256 active_hash, orig_hash;
+ BOOST_CHECK(!chain->findCommonAncestor(active.Tip()->GetBlockHash(), {}, {}, FoundBlock().hash(active_hash), {}));
+ BOOST_CHECK(!chain->findCommonAncestor({}, orig_tip->GetBlockHash(), {}, {}, FoundBlock().hash(orig_hash)));
+ BOOST_CHECK_EQUAL(active_hash, active.Tip()->GetBlockHash());
+ BOOST_CHECK_EQUAL(orig_hash, orig_tip->GetBlockHash());
}
BOOST_AUTO_TEST_CASE(hasBlocks)
diff --git a/src/validation.cpp b/src/validation.cpp
index 9f5c59e52b..60d028336f 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -1016,7 +1016,7 @@ bool MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs
// scripts (ie, other policy checks pass). We perform the inexpensive
// checks first and avoid hashing and signature verification unless those
// checks pass, to mitigate CPU exhaustion denial-of-service attacks.
- PrecomputedTransactionData txdata(*ptx);
+ PrecomputedTransactionData txdata;
if (!PolicyScriptChecks(args, workspace, txdata)) return false;
@@ -1516,6 +1516,10 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const C
return true;
}
+ if (!txdata.m_ready) {
+ txdata.Init(tx);
+ }
+
for (unsigned int i = 0; i < tx.vin.size(); i++) {
const COutPoint &prevout = tx.vin[i].prevout;
const Coin& coin = inputs.AccessCoin(prevout);
@@ -2079,15 +2083,19 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
CBlockUndo blockundo;
+ // Precomputed transaction data pointers must not be invalidated
+ // until after `control` has run the script checks (potentially
+ // in multiple threads). Preallocate the vector size so a new allocation
+ // doesn't invalidate pointers into the vector, and keep txsdata in scope
+ // for as long as `control`.
CCheckQueueControl<CScriptCheck> control(fScriptChecks && g_parallel_script_checks ? &scriptcheckqueue : nullptr);
+ std::vector<PrecomputedTransactionData> txsdata(block.vtx.size());
std::vector<int> prevheights;
CAmount nFees = 0;
int nInputs = 0;
int64_t nSigOpsCost = 0;
blockundo.vtxundo.reserve(block.vtx.size() - 1);
- std::vector<PrecomputedTransactionData> txdata;
- txdata.reserve(block.vtx.size()); // Required so that pointers to individual PrecomputedTransactionData don't get invalidated
for (unsigned int i = 0; i < block.vtx.size(); i++)
{
const CTransaction &tx = *(block.vtx[i]);
@@ -2134,13 +2142,12 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-blk-sigops");
}
- txdata.emplace_back(tx);
if (!tx.IsCoinBase())
{
std::vector<CScriptCheck> vChecks;
bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
TxValidationState tx_state;
- if (fScriptChecks && !CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txdata[i], g_parallel_script_checks ? &vChecks : nullptr)) {
+ if (fScriptChecks && !CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], g_parallel_script_checks ? &vChecks : nullptr)) {
// Any transaction validation failure in ConnectBlock is a block consensus failure
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
tx_state.GetRejectReason(), tx_state.GetDebugMessage());
diff --git a/test/functional/interface_bitcoin_cli.py b/test/functional/interface_bitcoin_cli.py
index 2e1e84b707..07edc4e0ba 100755
--- a/test/functional/interface_bitcoin_cli.py
+++ b/test/functional/interface_bitcoin_cli.py
@@ -25,15 +25,6 @@ class TestBitcoinCli(BitcoinTestFramework):
"""Main test logic"""
self.nodes[0].generate(BLOCKS)
- cli_response = self.nodes[0].cli("-version").send_cli()
- assert "{} RPC client version".format(self.config['environment']['PACKAGE_NAME']) in cli_response
-
- self.log.info("Compare responses from getwalletinfo RPC and `bitcoin-cli getwalletinfo`")
- if self.is_wallet_compiled():
- cli_response = self.nodes[0].cli.getwalletinfo()
- rpc_response = self.nodes[0].getwalletinfo()
- assert_equal(cli_response, rpc_response)
-
self.log.info("Compare responses from getblockchaininfo RPC and `bitcoin-cli getblockchaininfo`")
cli_response = self.nodes[0].cli.getblockchaininfo()
rpc_response = self.nodes[0].getblockchaininfo()
@@ -55,31 +46,50 @@ class TestBitcoinCli(BitcoinTestFramework):
self.log.info("Test connecting with non-existing RPC cookie file")
assert_raises_process_error(1, "Could not locate RPC credentials", self.nodes[0].cli('-rpccookiefile=does-not-exist', '-rpcpassword=').echo)
- self.log.info("Make sure that -getinfo with arguments fails")
+ self.log.info("Test -getinfo with arguments fails")
assert_raises_process_error(1, "-getinfo takes no arguments", self.nodes[0].cli('-getinfo').help)
- self.log.info("Test that -getinfo returns the expected network and blockchain info")
+ self.log.info("Test -getinfo returns expected network and blockchain info")
+ if self.is_wallet_compiled():
+ self.nodes[0].encryptwallet(password)
cli_get_info = self.nodes[0].cli('-getinfo').send_cli()
network_info = self.nodes[0].getnetworkinfo()
blockchain_info = self.nodes[0].getblockchaininfo()
-
assert_equal(cli_get_info['version'], network_info['version'])
assert_equal(cli_get_info['blocks'], blockchain_info['blocks'])
+ assert_equal(cli_get_info['headers'], blockchain_info['headers'])
assert_equal(cli_get_info['timeoffset'], network_info['timeoffset'])
assert_equal(cli_get_info['connections'], network_info['connections'])
assert_equal(cli_get_info['proxy'], network_info['networks'][0]['proxy'])
assert_equal(cli_get_info['difficulty'], blockchain_info['difficulty'])
assert_equal(cli_get_info['chain'], blockchain_info['chain'])
+
if self.is_wallet_compiled():
- self.log.info("Test that -getinfo returns the expected wallet info")
+ self.log.info("Test -getinfo and bitcoin-cli getwalletinfo return expected wallet info")
assert_equal(cli_get_info['balance'], BALANCE)
wallet_info = self.nodes[0].getwalletinfo()
assert_equal(cli_get_info['keypoolsize'], wallet_info['keypoolsize'])
+ assert_equal(cli_get_info['unlocked_until'], wallet_info['unlocked_until'])
assert_equal(cli_get_info['paytxfee'], wallet_info['paytxfee'])
assert_equal(cli_get_info['relayfee'], network_info['relayfee'])
- # unlocked_until is not tested because the wallet is not encrypted
+ assert_equal(self.nodes[0].cli.getwalletinfo(), wallet_info)
else:
- self.log.info("*** Wallet not compiled; -getinfo wallet tests skipped")
+ self.log.info("*** Wallet not compiled; cli getwalletinfo and -getinfo wallet tests skipped")
+
+ self.stop_node(0)
+
+ self.log.info("Test -version with node stopped")
+ cli_response = self.nodes[0].cli("-version").send_cli()
+ assert "{} RPC client version".format(self.config['environment']['PACKAGE_NAME']) in cli_response
+
+ self.log.info("Test -rpcwait option waits for RPC connection instead of failing")
+ # Start node without RPC connection.
+ self.nodes[0].start()
+ # Verify failure without -rpcwait.
+ assert_raises_process_error(1, "Could not connect to the server", self.nodes[0].cli('getblockcount').echo)
+ # Verify success using -rpcwait.
+ assert_equal(BLOCKS, self.nodes[0].cli('-rpcwait', 'getblockcount').send_cli())
+ self.nodes[0].wait_for_rpc_connection()
if __name__ == '__main__':