Browse Source

Merge #8679: [0.13] Various backports

ab295bb Do not add random inbound peers to addrman. (Gregory Maxwell)
bbf379b Fix some locks (Pieter Wuille)
2215c22 Check for compatibility with download in FindNextBlocksToDownload (Pieter Wuille)
b8c79a0 Precompute sighashes (Pieter Wuille)
a987431 [util] CopyrightHolders: Check for untranslated substitution (MarcoFalke)
32d75a7 doc: Update build-openbsd for 0.13.0+ and OpenBSD 5.9 (Wladimir J. van der Laan)
b09e13c build: Updates for OpenBSD (Wladimir J. van der Laan)
0e6d753 [doc] build: Mention curl (MarcoFalke)
f1c0d78 [Qt] show network/chain errors in the GUI (Jonas Schnelli)
2611ad7 Added feeler connections increasing good addrs in the tried table. (Ethan Heilman)
1db3352 qt: Fix random segfault when closing "Choose data directory" dialog (Wladimir J. van der Laan)
75f2065 build: Remove check for `openssl/ec.h` (Wladimir J. van der Laan)
0.13
Wladimir J. van der Laan 8 years ago
parent
commit
3226944918
No known key found for this signature in database
GPG Key ID: 74810B012346C9A6
  1. 18
      configure.ac
  2. 46
      doc/build-openbsd.md
  3. 5
      doc/build-unix.md
  4. 6
      doc/build-windows.md
  5. 86
      src/main.cpp
  6. 9
      src/main.h
  7. 59
      src/net.cpp
  8. 5
      src/net.h
  9. 3
      src/qt/bitcoin.cpp
  10. 7
      src/qt/intro.cpp
  11. 5
      src/qt/intro.h
  12. 4
      src/script/bitcoinconsensus.cpp
  13. 52
      src/script/interpreter.cpp
  14. 13
      src/script/interpreter.h
  15. 2
      src/script/sigcache.h
  16. 22
      src/test/net_tests.cpp
  17. 6
      src/test/script_P2SH_tests.cpp
  18. 87
      src/test/transaction_tests.cpp
  19. 6
      src/txmempool.cpp
  20. 9
      src/util.cpp

18
configure.ac

@ -67,7 +67,8 @@ AC_PATH_TOOL(STRIP, strip)
AC_PATH_TOOL(GCOV, gcov) AC_PATH_TOOL(GCOV, gcov)
AC_PATH_PROG(LCOV, lcov) AC_PATH_PROG(LCOV, lcov)
AC_PATH_PROG(JAVA, java) AC_PATH_PROG(JAVA, java)
AC_PATH_PROGS([PYTHON], [python3 python2.7 python2 python]) dnl Python 3.x is supported from 3.4 on (see https://github.com/bitcoin/bitcoin/issues/7893)
AC_PATH_PROGS([PYTHON], [python3.6 python3.5 python3.4 python3 python2.7 python2 python])
AC_PATH_PROG(GENHTML, genhtml) AC_PATH_PROG(GENHTML, genhtml)
AC_PATH_PROG([GIT], [git]) AC_PATH_PROG([GIT], [git])
AC_PATH_PROG(CCACHE,ccache) AC_PATH_PROG(CCACHE,ccache)
@ -366,8 +367,15 @@ case $host in
TARGET_OS=linux TARGET_OS=linux
LEVELDB_TARGET_FLAGS="-DOS_LINUX" LEVELDB_TARGET_FLAGS="-DOS_LINUX"
;; ;;
*freebsd*)
LEVELDB_TARGET_FLAGS="-DOS_FREEBSD"
;;
*openbsd*)
LEVELDB_TARGET_FLAGS="-DOS_OPENBSD"
;;
*) *)
OTHER_OS=`echo ${host_os} | awk '{print toupper($0)}'` OTHER_OS=`echo ${host_os} | awk '{print toupper($0)}'`
AC_MSG_WARN([Guessing LevelDB OS as OS_${OTHER_OS}, please check whether this is correct, if not add an entry to configure.ac.])
LEVELDB_TARGET_FLAGS="-DOS_${OTHER_OS}" LEVELDB_TARGET_FLAGS="-DOS_${OTHER_OS}"
;; ;;
esac esac
@ -861,14 +869,6 @@ AM_CONDITIONAL([EMBEDDED_UNIVALUE],[test x$system_univalue = xno])
AC_SUBST(UNIVALUE_CFLAGS) AC_SUBST(UNIVALUE_CFLAGS)
AC_SUBST(UNIVALUE_LIBS) AC_SUBST(UNIVALUE_LIBS)
CXXFLAGS_TEMP="$CXXFLAGS"
LIBS_TEMP="$LIBS"
CXXFLAGS="$CXXFLAGS $SSL_CFLAGS $CRYPTO_CFLAGS"
LIBS="$LIBS $SSL_LIBS $CRYPTO_LIBS"
AC_CHECK_HEADER([openssl/ec.h],, AC_MSG_ERROR(OpenSSL ec header missing),)
CXXFLAGS="$CXXFLAGS_TEMP"
LIBS="$LIBS_TEMP"
BITCOIN_QT_PATH_PROGS([PROTOC], [protoc],$protoc_bin_path) BITCOIN_QT_PATH_PROGS([PROTOC], [protoc],$protoc_bin_path)
AC_MSG_CHECKING([whether to build bitcoind]) AC_MSG_CHECKING([whether to build bitcoind])

46
doc/build-openbsd.md

@ -1,6 +1,6 @@
OpenBSD build guide OpenBSD build guide
====================== ======================
(updated for OpenBSD 5.7) (updated for OpenBSD 5.9)
This guide describes how to build bitcoind and command-line utilities on OpenBSD. This guide describes how to build bitcoind and command-line utilities on OpenBSD.
@ -15,11 +15,10 @@ Run the following as root to install the base dependencies for building:
pkg_add gmake libtool libevent pkg_add gmake libtool libevent
pkg_add autoconf # (select highest version, e.g. 2.69) pkg_add autoconf # (select highest version, e.g. 2.69)
pkg_add automake # (select highest version, e.g. 1.15) pkg_add automake # (select highest version, e.g. 1.15)
pkg_add python # (select version 2.7.x, not 3.x) pkg_add python # (select highest version, e.g. 3.5)
ln -sf /usr/local/bin/python2.7 /usr/local/bin/python2
``` ```
The default C++ compiler that comes with OpenBSD 5.7 is g++ 4.2. This version is old (from 2007), and is not able to compile the current version of Bitcoin Core. It is possible to patch it up to compile, but with the planned transition to C++11 this is a losing battle. So here we will be installing a newer compiler. The default C++ compiler that comes with OpenBSD 5.9 is g++ 4.2. This version is old (from 2007), and is not able to compile the current version of Bitcoin Core, primarily as it has no C++11 support, but even before there were issues. So here we will be installing a newer compiler.
GCC GCC
------- -------
@ -27,7 +26,7 @@ GCC
You can install a newer version of gcc with: You can install a newer version of gcc with:
```bash ```bash
pkg_add g++ # (select newest 4.x version, e.g. 4.9.2) pkg_add g++ # (select newest 4.x version, e.g. 4.9.3)
``` ```
This compiler will not overwrite the system compiler, it will be installed as `egcc` and `eg++` in `/usr/local/bin`. This compiler will not overwrite the system compiler, it will be installed as `egcc` and `eg++` in `/usr/local/bin`.
@ -49,18 +48,15 @@ BOOST_PREFIX="${BITCOIN_ROOT}/boost"
mkdir -p $BOOST_PREFIX mkdir -p $BOOST_PREFIX
# Fetch the source and verify that it is not tampered with # Fetch the source and verify that it is not tampered with
wget http://heanet.dl.sourceforge.net/project/boost/boost/1.59.0/boost_1_59_0.tar.bz2 curl -o boost_1_61_0.tar.bz2 http://heanet.dl.sourceforge.net/project/boost/boost/1.61.0/boost_1_61_0.tar.bz2
echo '727a932322d94287b62abb1bd2d41723eec4356a7728909e38adb65ca25241ca boost_1_59_0.tar.bz2' | sha256 -c echo 'a547bd06c2fd9a71ba1d169d9cf0339da7ebf4753849a8f7d6fdb8feee99b640 boost_1_61_0.tar.bz2' | sha256 -c
# MUST output: (SHA256) boost_1_59_0.tar.bz2: OK # MUST output: (SHA256) boost_1_61_0.tar.bz2: OK
tar -xjf boost_1_59_0.tar.bz2 tar -xjf boost_1_61_0.tar.bz2
# Boost 1.59 needs two small patches for OpenBSD # Boost 1.61 needs one small patch for OpenBSD
cd boost_1_59_0 cd boost_1_61_0
# Also here: https://gist.githubusercontent.com/laanwj/bf359281dc319b8ff2e1/raw/92250de8404b97bb99d72ab898f4a8cb35ae1ea3/patch-boost_test_impl_execution_monitor_ipp.patch # Also here: https://gist.githubusercontent.com/laanwj/bf359281dc319b8ff2e1/raw/92250de8404b97bb99d72ab898f4a8cb35ae1ea3/patch-boost_test_impl_execution_monitor_ipp.patch
patch -p0 < /usr/ports/devel/boost/patches/patch-boost_test_impl_execution_monitor_ipp patch -p0 < /usr/ports/devel/boost/patches/patch-boost_test_impl_execution_monitor_ipp
# https://github.com/boostorg/filesystem/commit/90517e459681790a091566dce27ca3acabf9a70c
sed 's/__OPEN_BSD__/__OpenBSD__/g' < libs/filesystem/src/path.cpp > libs/filesystem/src/path.cpp.tmp
mv libs/filesystem/src/path.cpp.tmp libs/filesystem/src/path.cpp
# Build w/ minimum configuration necessary for bitcoin # Build w/ minimum configuration necessary for bitcoin
echo 'using gcc : : eg++ : <cxxflags>"-fvisibility=hidden -fPIC" <linkflags>"" <archiver>"ar" <striper>"strip" <ranlib>"ranlib" <rc>"" : ;' > user-config.jam echo 'using gcc : : eg++ : <cxxflags>"-fvisibility=hidden -fPIC" <linkflags>"" <archiver>"ar" <striper>"strip" <ranlib>"ranlib" <rc>"" : ;' > user-config.jam
@ -84,7 +80,7 @@ BDB_PREFIX="${BITCOIN_ROOT}/db4"
mkdir -p $BDB_PREFIX mkdir -p $BDB_PREFIX
# Fetch the source and verify that it is not tampered with # Fetch the source and verify that it is not tampered with
wget 'http://download.oracle.com/berkeley-db/db-4.8.30.NC.tar.gz' curl -o db-4.8.30.NC.tar.gz 'http://download.oracle.com/berkeley-db/db-4.8.30.NC.tar.gz'
echo '12edc0df75bf9abd7f82f821795bcee50f42cb2e5f76a6a281b85732798364ef db-4.8.30.NC.tar.gz' | sha256 -c echo '12edc0df75bf9abd7f82f821795bcee50f42cb2e5f76a6a281b85732798364ef db-4.8.30.NC.tar.gz' | sha256 -c
# MUST output: (SHA256) db-4.8.30.NC.tar.gz: OK # MUST output: (SHA256) db-4.8.30.NC.tar.gz: OK
tar -xzf db-4.8.30.NC.tar.gz tar -xzf db-4.8.30.NC.tar.gz
@ -93,9 +89,25 @@ tar -xzf db-4.8.30.NC.tar.gz
cd db-4.8.30.NC/build_unix/ cd db-4.8.30.NC/build_unix/
# Note: Do a static build so that it can be embedded into the executable, instead of having to find a .so at runtime # Note: Do a static build so that it can be embedded into the executable, instead of having to find a .so at runtime
../dist/configure --enable-cxx --disable-shared --with-pic --prefix=$BDB_PREFIX CC=egcc CXX=eg++ CPP=ecpp ../dist/configure --enable-cxx --disable-shared --with-pic --prefix=$BDB_PREFIX CC=egcc CXX=eg++ CPP=ecpp
make install make install # do NOT use -jX, this is broken
``` ```
### Resource limits
The standard ulimit restrictions in OpenBSD are very strict:
data(kbytes) 1572864
This is, unfortunately, no longer enough to compile some `.cpp` files in the project,
at least with gcc 4.9.3 (see issue https://github.com/bitcoin/bitcoin/issues/6658).
If your user is in the `staff` group the limit can be raised with:
ulimit -d 3000000
The change will only affect the current shell and processes spawned by it. To
make the change system-wide, change `datasize-cur` and `datasize-max` in
`/etc/login.conf`, and reboot.
### Building Bitcoin Core ### Building Bitcoin Core
**Important**: use `gmake`, not `make`. The non-GNU `make` will exit with a horrible error. **Important**: use `gmake`, not `make`. The non-GNU `make` will exit with a horrible error.
@ -123,7 +135,7 @@ To configure without wallet:
Build and run the tests: Build and run the tests:
```bash ```bash
gmake gmake # can use -jX here for parallelism
gmake check gmake check
``` ```

5
doc/build-unix.md

@ -293,9 +293,10 @@ These steps can be performed on, for example, an Ubuntu VM. The depends system
will also work on other Linux distributions, however the commands for will also work on other Linux distributions, however the commands for
installing the toolchain will be different. installing the toolchain will be different.
First install the toolchain: Make sure you install the build requirements mentioned above.
Then, install the toolchain and curl:
sudo apt-get install g++-arm-linux-gnueabihf sudo apt-get install g++-arm-linux-gnueabihf curl
To build executables for ARM: To build executables for ARM:

6
doc/build-windows.md

@ -16,9 +16,11 @@ These steps can be performed on, for example, an Ubuntu VM. The depends system
will also work on other Linux distributions, however the commands for will also work on other Linux distributions, however the commands for
installing the toolchain will be different. installing the toolchain will be different.
First install the toolchains: Make sure you install the build requirements mentioned in
[build-unix.md](/doc/build-unix.md).
Then, install the toolchains and curl:
sudo apt-get install g++-mingw-w64-i686 mingw-w64-i686-dev g++-mingw-w64-x86-64 mingw-w64-x86-64-dev sudo apt-get install g++-mingw-w64-i686 mingw-w64-i686-dev g++-mingw-w64-x86-64 mingw-w64-x86-64-dev curl
To build executables for Windows 32-bit: To build executables for Windows 32-bit:

86
src/main.cpp

@ -537,7 +537,7 @@ CBlockIndex* LastCommonAncestor(CBlockIndex* pa, CBlockIndex* pb) {
/** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has /** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has
* at most count entries. */ * at most count entries. */
void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<CBlockIndex*>& vBlocks, NodeId& nodeStaller) { void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<CBlockIndex*>& vBlocks, NodeId& nodeStaller, const Consensus::Params& consensusParams) {
if (count == 0) if (count == 0)
return; return;
@ -594,6 +594,10 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<CBl
// We consider the chain that this peer is on invalid. // We consider the chain that this peer is on invalid.
return; return;
} }
if (!State(nodeid)->fHaveWitness && IsWitnessEnabled(pindex->pprev, consensusParams)) {
// We wouldn't download this block or its descendants from this peer.
return;
}
if (pindex->nStatus & BLOCK_HAVE_DATA || chainActive.Contains(pindex)) { if (pindex->nStatus & BLOCK_HAVE_DATA || chainActive.Contains(pindex)) {
if (pindex->nChainTx) if (pindex->nChainTx)
state->pindexLastCommonBlock = pindex; state->pindexLastCommonBlock = pindex;
@ -1497,12 +1501,13 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
// Check against previous transactions // Check against previous transactions
// This is done last to help prevent CPU exhaustion denial-of-service attacks. // This is done last to help prevent CPU exhaustion denial-of-service attacks.
if (!CheckInputs(tx, state, view, true, scriptVerifyFlags, true)) { PrecomputedTransactionData txdata(tx);
if (!CheckInputs(tx, state, view, true, scriptVerifyFlags, true, txdata)) {
// SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we // SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we
// need to turn both off, and compare against just turning off CLEANSTACK // need to turn both off, and compare against just turning off CLEANSTACK
// to see if the failure is specifically due to witness validation. // to see if the failure is specifically due to witness validation.
if (CheckInputs(tx, state, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true) && if (CheckInputs(tx, state, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, txdata) &&
!CheckInputs(tx, state, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true)) { !CheckInputs(tx, state, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, txdata)) {
// Only the witness is wrong, so the transaction itself may be fine. // Only the witness is wrong, so the transaction itself may be fine.
state.SetCorruptionPossible(); state.SetCorruptionPossible();
} }
@ -1518,7 +1523,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
// There is a similar check in CreateNewBlock() to prevent creating // There is a similar check in CreateNewBlock() to prevent creating
// invalid blocks, however allowing such transactions into the mempool // invalid blocks, however allowing such transactions into the mempool
// can be exploited as a DoS attack. // can be exploited as a DoS attack.
if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true)) if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata))
{ {
return error("%s: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s, %s", return error("%s: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s, %s",
__func__, hash.ToString(), FormatStateMessage(state)); __func__, hash.ToString(), FormatStateMessage(state));
@ -1915,7 +1920,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight)
bool CScriptCheck::operator()() { bool CScriptCheck::operator()() {
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
const CScriptWitness *witness = (nIn < ptxTo->wit.vtxinwit.size()) ? &ptxTo->wit.vtxinwit[nIn].scriptWitness : NULL; const CScriptWitness *witness = (nIn < ptxTo->wit.vtxinwit.size()) ? &ptxTo->wit.vtxinwit[nIn].scriptWitness : NULL;
if (!VerifyScript(scriptSig, scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore), &error)) { if (!VerifyScript(scriptSig, scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore, *txdata), &error)) {
return false; return false;
} }
return true; return true;
@ -1974,7 +1979,7 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins
} }
}// namespace Consensus }// namespace Consensus
bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, std::vector<CScriptCheck> *pvChecks) 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()) if (!tx.IsCoinBase())
{ {
@ -2001,7 +2006,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
assert(coins); assert(coins);
// Verify signature // Verify signature
CScriptCheck check(*coins, tx, i, flags, cacheStore); CScriptCheck check(*coins, tx, i, flags, cacheStore, &txdata);
if (pvChecks) { if (pvChecks) {
pvChecks->push_back(CScriptCheck()); pvChecks->push_back(CScriptCheck());
check.swap(pvChecks->back()); check.swap(pvChecks->back());
@ -2014,7 +2019,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
// avoid splitting the network between upgraded and // avoid splitting the network between upgraded and
// non-upgraded nodes. // non-upgraded nodes.
CScriptCheck check2(*coins, tx, i, CScriptCheck check2(*coins, tx, i,
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore); flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore, &txdata);
if (check2()) if (check2())
return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError()))); return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError())));
} }
@ -2412,6 +2417,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
std::vector<std::pair<uint256, CDiskTxPos> > vPos; std::vector<std::pair<uint256, CDiskTxPos> > vPos;
vPos.reserve(block.vtx.size()); vPos.reserve(block.vtx.size());
blockundo.vtxundo.reserve(block.vtx.size() - 1); 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++) for (unsigned int i = 0; i < block.vtx.size(); i++)
{ {
const CTransaction &tx = block.vtx[i]; const CTransaction &tx = block.vtx[i];
@ -2458,13 +2465,14 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
return state.DoS(100, error("ConnectBlock(): too many sigops"), return state.DoS(100, error("ConnectBlock(): too many sigops"),
REJECT_INVALID, "bad-blk-sigops"); REJECT_INVALID, "bad-blk-sigops");
txdata.emplace_back(tx);
if (!tx.IsCoinBase()) if (!tx.IsCoinBase())
{ {
nFees += view.GetValueIn(tx)-tx.GetValueOut(); nFees += view.GetValueIn(tx)-tx.GetValueOut();
std::vector<CScriptCheck> vChecks; std::vector<CScriptCheck> vChecks;
bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */ bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, nScriptCheckThreads ? &vChecks : NULL)) if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, txdata[i], nScriptCheckThreads ? &vChecks : NULL))
return error("ConnectBlock(): CheckInputs on %s failed with %s", return error("ConnectBlock(): CheckInputs on %s failed with %s",
tx.GetHash().ToString(), FormatStateMessage(state)); tx.GetHash().ToString(), FormatStateMessage(state));
control.Add(vChecks); control.Add(vChecks);
@ -4647,6 +4655,7 @@ std::string GetWarnings(const std::string& strFor)
string strStatusBar; string strStatusBar;
string strRPC; string strRPC;
string strGUI; string strGUI;
const string uiAlertSeperator = "<hr />";
if (!CLIENT_VERSION_IS_RELEASE) { if (!CLIENT_VERSION_IS_RELEASE) {
strStatusBar = "This is a pre-release test build - use at your own risk - do not use for mining or merchant applications"; strStatusBar = "This is a pre-release test build - use at your own risk - do not use for mining or merchant applications";
@ -4659,18 +4668,19 @@ std::string GetWarnings(const std::string& strFor)
// Misc warnings like out of disk space and clock is wrong // Misc warnings like out of disk space and clock is wrong
if (strMiscWarning != "") if (strMiscWarning != "")
{ {
strStatusBar = strGUI = strMiscWarning; strStatusBar = strMiscWarning;
strGUI += (strGUI.empty() ? "" : uiAlertSeperator) + strMiscWarning;
} }
if (fLargeWorkForkFound) if (fLargeWorkForkFound)
{ {
strStatusBar = strRPC = "Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues."; strStatusBar = strRPC = "Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.";
strGUI = _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues."); strGUI += strGUI.empty() ? "" : uiAlertSeperator + _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.");
} }
else if (fLargeWorkInvalidChainFound) else if (fLargeWorkInvalidChainFound)
{ {
strStatusBar = strRPC = "Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade."; strStatusBar = strRPC = "Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.";
strGUI = _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade."); strGUI += strGUI.empty() ? "" : uiAlertSeperator + _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.");
} }
if (strFor == "gui") if (strFor == "gui")
@ -4793,10 +4803,16 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
pfrom->PushMessage(NetMsgType::BLOCK, block); pfrom->PushMessage(NetMsgType::BLOCK, block);
else if (inv.type == MSG_FILTERED_BLOCK) else if (inv.type == MSG_FILTERED_BLOCK)
{ {
LOCK(pfrom->cs_filter); bool send = false;
if (pfrom->pfilter) CMerkleBlock merkleBlock;
{ {
CMerkleBlock merkleBlock(block, *pfrom->pfilter); LOCK(pfrom->cs_filter);
if (pfrom->pfilter) {
send = true;
merkleBlock = CMerkleBlock(block, *pfrom->pfilter);
}
}
if (send) {
pfrom->PushMessage(NetMsgType::MERKLEBLOCK, merkleBlock); pfrom->PushMessage(NetMsgType::MERKLEBLOCK, merkleBlock);
// CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see // CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see
// This avoids hurting performance by pointlessly requiring a round-trip // This avoids hurting performance by pointlessly requiring a round-trip
@ -4917,6 +4933,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
if (strCommand == NetMsgType::VERSION) if (strCommand == NetMsgType::VERSION)
{ {
// Feeler connections exist only to verify if address is online.
if (pfrom->fFeeler) {
assert(pfrom->fInbound == false);
pfrom->fDisconnect = true;
}
// Each connection can only send one version message // Each connection can only send one version message
if (pfrom->nVersion != 0) if (pfrom->nVersion != 0)
{ {
@ -5035,12 +5057,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
pfrom->fGetAddr = true; pfrom->fGetAddr = true;
} }
addrman.Good(pfrom->addr); addrman.Good(pfrom->addr);
} else {
if (((CNetAddr)pfrom->addr) == (CNetAddr)addrFrom)
{
addrman.Add(addrFrom, addrFrom);
addrman.Good(addrFrom);
}
} }
pfrom->fSuccessfullyConnected = true; pfrom->fSuccessfullyConnected = true;
@ -6058,8 +6074,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
CBloomFilter filter; CBloomFilter filter;
vRecv >> filter; vRecv >> filter;
LOCK(pfrom->cs_filter);
if (!filter.IsWithinSizeConstraints()) if (!filter.IsWithinSizeConstraints())
{ {
// There is no excuse for sending a too-large filter // There is no excuse for sending a too-large filter
@ -6068,12 +6082,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
} }
else else
{ {
LOCK(pfrom->cs_filter);
delete pfrom->pfilter; delete pfrom->pfilter;
pfrom->pfilter = new CBloomFilter(filter); pfrom->pfilter = new CBloomFilter(filter);
pfrom->pfilter->UpdateEmptyFull(); pfrom->pfilter->UpdateEmptyFull();
}
pfrom->fRelayTxes = true; pfrom->fRelayTxes = true;
} }
}
else if (strCommand == NetMsgType::FILTERADD) else if (strCommand == NetMsgType::FILTERADD)
@ -6083,21 +6098,22 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// Nodes must NEVER send a data item > 520 bytes (the max size for a script data object, // Nodes must NEVER send a data item > 520 bytes (the max size for a script data object,
// and thus, the maximum size any matched object can have) in a filteradd message // and thus, the maximum size any matched object can have) in a filteradd message
if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) bool bad = false;
{ if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) {
LOCK(cs_main); bad = true;
Misbehaving(pfrom->GetId(), 100);
} else { } else {
LOCK(pfrom->cs_filter); LOCK(pfrom->cs_filter);
if (pfrom->pfilter) if (pfrom->pfilter) {
pfrom->pfilter->insert(vData); pfrom->pfilter->insert(vData);
else } else {
{ bad = true;
}
}
if (bad) {
LOCK(cs_main); LOCK(cs_main);
Misbehaving(pfrom->GetId(), 100); Misbehaving(pfrom->GetId(), 100);
} }
} }
}
else if (strCommand == NetMsgType::FILTERCLEAR) else if (strCommand == NetMsgType::FILTERCLEAR)
@ -6714,16 +6730,14 @@ bool SendMessages(CNode* pto)
if (!pto->fDisconnect && !pto->fClient && (fFetch || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { if (!pto->fDisconnect && !pto->fClient && (fFetch || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
vector<CBlockIndex*> vToDownload; vector<CBlockIndex*> vToDownload;
NodeId staller = -1; NodeId staller = -1;
FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller); FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller, consensusParams);
BOOST_FOREACH(CBlockIndex *pindex, vToDownload) { BOOST_FOREACH(CBlockIndex *pindex, vToDownload) {
if (State(pto->GetId())->fHaveWitness || !IsWitnessEnabled(pindex->pprev, consensusParams)) {
uint32_t nFetchFlags = GetFetchFlags(pto, pindex->pprev, consensusParams); uint32_t nFetchFlags = GetFetchFlags(pto, pindex->pprev, consensusParams);
vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash())); vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), consensusParams, pindex); MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), consensusParams, pindex);
LogPrint("net", "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(), LogPrint("net", "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(),
pindex->nHeight, pto->id); pindex->nHeight, pto->id);
} }
}
if (state.nBlocksInFlight == 0 && staller != -1) { if (state.nBlocksInFlight == 0 && staller != -1) {
if (State(staller)->nStallingSince == 0) { if (State(staller)->nStallingSince == 0) {
State(staller)->nStallingSince = nNow; State(staller)->nStallingSince = nNow;

9
src/main.h

@ -39,6 +39,7 @@ class CTxMemPool;
class CValidationInterface; class CValidationInterface;
class CValidationState; class CValidationState;
struct PrecomputedTransactionData;
struct CNodeStateStats; struct CNodeStateStats;
struct LockPoints; struct LockPoints;
@ -347,7 +348,7 @@ int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& i
* instead of being performed inline. * instead of being performed inline.
*/ */
bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &view, bool fScriptChecks, bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &view, bool fScriptChecks,
unsigned int flags, bool cacheStore, std::vector<CScriptCheck> *pvChecks = NULL); 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 */ /** Apply the effects of this transaction on the UTXO set represented by view */
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight); void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight);
@ -408,12 +409,13 @@ private:
unsigned int nFlags; unsigned int nFlags;
bool cacheStore; bool cacheStore;
ScriptError error; ScriptError error;
PrecomputedTransactionData *txdata;
public: public:
CScriptCheck(): amount(0), ptxTo(0), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {} CScriptCheck(): amount(0), ptxTo(0), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {}
CScriptCheck(const CCoins& txFromIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn) : CScriptCheck(const CCoins& txFromIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn, PrecomputedTransactionData* txdataIn) :
scriptPubKey(txFromIn.vout[txToIn.vin[nInIn].prevout.n].scriptPubKey), amount(txFromIn.vout[txToIn.vin[nInIn].prevout.n].nValue), scriptPubKey(txFromIn.vout[txToIn.vin[nInIn].prevout.n].scriptPubKey), amount(txFromIn.vout[txToIn.vin[nInIn].prevout.n].nValue),
ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR) { } ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR), txdata(txdataIn) { }
bool operator()(); bool operator()();
@ -425,6 +427,7 @@ public:
std::swap(nFlags, check.nFlags); std::swap(nFlags, check.nFlags);
std::swap(cacheStore, check.cacheStore); std::swap(cacheStore, check.cacheStore);
std::swap(error, check.error); std::swap(error, check.error);
std::swap(txdata, check.txdata);
} }
ScriptError GetScriptError() const { return error; } ScriptError GetScriptError() const { return error; }

59
src/net.cpp

@ -42,6 +42,9 @@
// Dump addresses to peers.dat and banlist.dat every 15 minutes (900s) // Dump addresses to peers.dat and banlist.dat every 15 minutes (900s)
#define DUMP_ADDRESSES_INTERVAL 900 #define DUMP_ADDRESSES_INTERVAL 900
// We add a random period time (0 to 1 seconds) to feeler connections to prevent synchronization.
#define FEELER_SLEEP_WINDOW 1
#if !defined(HAVE_MSG_NOSIGNAL) && !defined(MSG_NOSIGNAL) #if !defined(HAVE_MSG_NOSIGNAL) && !defined(MSG_NOSIGNAL)
#define MSG_NOSIGNAL 0 #define MSG_NOSIGNAL 0
#endif #endif
@ -60,6 +63,7 @@
namespace { namespace {
const int MAX_OUTBOUND_CONNECTIONS = 8; const int MAX_OUTBOUND_CONNECTIONS = 8;
const int MAX_FEELER_CONNECTIONS = 1;
struct ListenSocket { struct ListenSocket {
SOCKET socket; SOCKET socket;
@ -1016,7 +1020,8 @@ static void AcceptConnection(const ListenSocket& hListenSocket) {
SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len); SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len);
CAddress addr; CAddress addr;
int nInbound = 0; int nInbound = 0;
int nMaxInbound = nMaxConnections - MAX_OUTBOUND_CONNECTIONS; int nMaxInbound = nMaxConnections - (MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS);
assert(nMaxInbound > 0);
if (hSocket != INVALID_SOCKET) if (hSocket != INVALID_SOCKET)
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr))
@ -1609,6 +1614,9 @@ void ThreadOpenConnections()
// Initiate network connections // Initiate network connections
int64_t nStart = GetTime(); int64_t nStart = GetTime();
// Minimum time before next feeler connection (in microseconds).
int64_t nNextFeeler = PoissonNextSend(nStart*1000*1000, FEELER_INTERVAL);
while (true) while (true)
{ {
ProcessOneShot(); ProcessOneShot();
@ -1646,13 +1654,36 @@ void ThreadOpenConnections()
} }
} }
} }
assert(nOutbound <= (MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS));
int64_t nANow = GetAdjustedTime(); // Feeler Connections
//
// Design goals:
// * Increase the number of connectable addresses in the tried table.
//
// Method:
// * Choose a random address from new and attempt to connect to it if we can connect
// successfully it is added to tried.
// * Start attempting feeler connections only after node finishes making outbound
// connections.
// * Only make a feeler connection once every few minutes.
//
bool fFeeler = false;
if (nOutbound >= MAX_OUTBOUND_CONNECTIONS) {
int64_t nTime = GetTimeMicros(); // The current time right now (in microseconds).
if (nTime > nNextFeeler) {
nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL);
fFeeler = true;
} else {
continue;
}
}
int64_t nANow = GetAdjustedTime();
int nTries = 0; int nTries = 0;
while (true) while (true)
{ {
CAddrInfo addr = addrman.Select(); CAddrInfo addr = addrman.Select(fFeeler);
// if we selected an invalid address, restart // if we selected an invalid address, restart
if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr)) if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr))
@ -1688,8 +1719,17 @@ void ThreadOpenConnections()
break; break;
} }
if (addrConnect.IsValid()) if (addrConnect.IsValid()) {
OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant);
if (fFeeler) {
// Add small amount of random noise before connection to avoid synchronization.
int randsleep = GetRandInt(FEELER_SLEEP_WINDOW * 1000);
MilliSleep(randsleep);
LogPrint("net", "Making feeler connection to %s\n", addrConnect.ToString());
}
OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant, NULL, false, fFeeler);
}
} }
} }
@ -1771,7 +1811,7 @@ void ThreadOpenAddedConnections()
} }
// if successful, this moves the passed grant to the constructed node // if successful, this moves the passed grant to the constructed node
bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot) bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler)
{ {
// //
// Initiate outbound network connection // Initiate outbound network connection
@ -1795,6 +1835,8 @@ bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSem
pnode->fNetworkNode = true; pnode->fNetworkNode = true;
if (fOneShot) if (fOneShot)
pnode->fOneShot = true; pnode->fOneShot = true;
if (fFeeler)
pnode->fFeeler = true;
return true; return true;
} }
@ -2054,7 +2096,7 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler)
if (semOutbound == NULL) { if (semOutbound == NULL) {
// initialize semaphore // initialize semaphore
int nMaxOutbound = std::min(MAX_OUTBOUND_CONNECTIONS, nMaxConnections); int nMaxOutbound = std::min((MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS), nMaxConnections);
semOutbound = new CSemaphore(nMaxOutbound); semOutbound = new CSemaphore(nMaxOutbound);
} }
@ -2096,7 +2138,7 @@ bool StopNode()
LogPrintf("StopNode()\n"); LogPrintf("StopNode()\n");
MapPort(false); MapPort(false);
if (semOutbound) if (semOutbound)
for (int i=0; i<MAX_OUTBOUND_CONNECTIONS; i++) for (int i=0; i<(MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS); i++)
semOutbound->post(); semOutbound->post();
if (fAddressesInitialized) if (fAddressesInitialized)
@ -2437,6 +2479,7 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa
fWhitelisted = false; fWhitelisted = false;
fOneShot = false; fOneShot = false;
fClient = false; // set by version message fClient = false; // set by version message
fFeeler = false;
fInbound = fInboundIn; fInbound = fInboundIn;
fNetworkNode = false; fNetworkNode = false;
fSuccessfullyConnected = false; fSuccessfullyConnected = false;

5
src/net.h

@ -41,6 +41,8 @@ namespace boost {
static const int PING_INTERVAL = 2 * 60; static const int PING_INTERVAL = 2 * 60;
/** Time after which to disconnect, after waiting for a ping response (or inactivity). */ /** Time after which to disconnect, after waiting for a ping response (or inactivity). */
static const int TIMEOUT_INTERVAL = 20 * 60; static const int TIMEOUT_INTERVAL = 20 * 60;
/** Run the feeler connection loop once every 2 minutes or 120 seconds. **/
static const int FEELER_INTERVAL = 120;
/** The maximum number of entries in an 'inv' protocol message */ /** The maximum number of entries in an 'inv' protocol message */
static const unsigned int MAX_INV_SZ = 50000; static const unsigned int MAX_INV_SZ = 50000;
/** The maximum number of new addresses to accumulate before announcing. */ /** The maximum number of new addresses to accumulate before announcing. */
@ -89,7 +91,7 @@ CNode* FindNode(const CSubNet& subNet);
CNode* FindNode(const std::string& addrName); CNode* FindNode(const std::string& addrName);
CNode* FindNode(const CService& ip); CNode* FindNode(const CService& ip);
CNode* FindNode(const NodeId id); //TODO: Remove this CNode* FindNode(const NodeId id); //TODO: Remove this
bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false); bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false, bool fFeeler = false);
void MapPort(bool fUseUPnP); void MapPort(bool fUseUPnP);
unsigned short GetListenPort(); unsigned short GetListenPort();
bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false); bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false);
@ -350,6 +352,7 @@ public:
// the network or wire types and the cleaned string used when displayed or logged. // the network or wire types and the cleaned string used when displayed or logged.
std::string strSubVer, cleanSubVer; std::string strSubVer, cleanSubVer;
bool fWhitelisted; // This peer can bypass DoS banning. bool fWhitelisted; // This peer can bypass DoS banning.
bool fFeeler; // If true this node is being used as a short lived feeler.
bool fOneShot; bool fOneShot;
bool fClient; bool fClient;
bool fInbound; bool fInbound;

3
src/qt/bitcoin.cpp

@ -578,7 +578,8 @@ int main(int argc, char *argv[])
/// 5. Now that settings and translations are available, ask user for data directory /// 5. Now that settings and translations are available, ask user for data directory
// User language is set up: pick a data directory // User language is set up: pick a data directory
Intro::pickDataDirectory(); if (!Intro::pickDataDirectory())
return 0;
/// 6. Determine availability of data directory and parse bitcoin.conf /// 6. Determine availability of data directory and parse bitcoin.conf
/// - Do not call GetDataDir(true) before this step finishes /// - Do not call GetDataDir(true) before this step finishes

7
src/qt/intro.cpp

@ -165,14 +165,14 @@ QString Intro::getDefaultDataDirectory()
return GUIUtil::boostPathToQString(GetDefaultDataDir()); return GUIUtil::boostPathToQString(GetDefaultDataDir());
} }
void Intro::pickDataDirectory() bool Intro::pickDataDirectory()
{ {
namespace fs = boost::filesystem; namespace fs = boost::filesystem;
QSettings settings; QSettings settings;
/* If data directory provided on command line, no need to look at settings /* If data directory provided on command line, no need to look at settings
or show a picking dialog */ or show a picking dialog */
if(!GetArg("-datadir", "").empty()) if(!GetArg("-datadir", "").empty())
return; return true;
/* 1) Default data directory for operating system */ /* 1) Default data directory for operating system */
QString dataDir = getDefaultDataDirectory(); QString dataDir = getDefaultDataDirectory();
/* 2) Allow QSettings to override default dir */ /* 2) Allow QSettings to override default dir */
@ -190,7 +190,7 @@ void Intro::pickDataDirectory()
if(!intro.exec()) if(!intro.exec())
{ {
/* Cancel clicked */ /* Cancel clicked */
exit(0); return false;
} }
dataDir = intro.getDataDirectory(); dataDir = intro.getDataDirectory();
try { try {
@ -211,6 +211,7 @@ void Intro::pickDataDirectory()
*/ */
if(dataDir != getDefaultDataDirectory()) if(dataDir != getDefaultDataDirectory())
SoftSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting SoftSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting
return true;
} }
void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable) void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable)

5
src/qt/intro.h

@ -35,10 +35,13 @@ public:
/** /**
* Determine data directory. Let the user choose if the current one doesn't exist. * Determine data directory. Let the user choose if the current one doesn't exist.
* *
* @returns true if a data directory was selected, false if the user cancelled the selection
* dialog.
*
* @note do NOT call global GetDataDir() before calling this function, this * @note do NOT call global GetDataDir() before calling this function, this
* will cause the wrong path to be cached. * will cause the wrong path to be cached.
*/ */
static void pickDataDirectory(); static bool pickDataDirectory();
/** /**
* Determine default data directory for operating system. * Determine default data directory for operating system.

4
src/script/bitcoinconsensus.cpp

@ -84,8 +84,8 @@ static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptP
// Regardless of the verification result, the tx did not error. // Regardless of the verification result, the tx did not error.
set_error(err, bitcoinconsensus_ERR_OK); set_error(err, bitcoinconsensus_ERR_OK);
PrecomputedTransactionData txdata(tx);
return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), nIn < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[nIn].scriptWitness : NULL, flags, TransactionSignatureChecker(&tx, nIn, amount), NULL); return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), nIn < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[nIn].scriptWitness : NULL, flags, TransactionSignatureChecker(&tx, nIn, amount, txdata), NULL);
} catch (const std::exception&) { } catch (const std::exception&) {
return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing
} }

52
src/script/interpreter.cpp

@ -1108,37 +1108,57 @@ public:
} }
}; };
} // anon namespace uint256 GetPrevoutHash(const CTransaction& txTo) {
uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion)
{
if (sigversion == SIGVERSION_WITNESS_V0) {
uint256 hashPrevouts;
uint256 hashSequence;
uint256 hashOutputs;
if (!(nHashType & SIGHASH_ANYONECANPAY)) {
CHashWriter ss(SER_GETHASH, 0); CHashWriter ss(SER_GETHASH, 0);
for (unsigned int n = 0; n < txTo.vin.size(); n++) { for (unsigned int n = 0; n < txTo.vin.size(); n++) {
ss << txTo.vin[n].prevout; ss << txTo.vin[n].prevout;
} }
hashPrevouts = ss.GetHash(); // TODO: cache this value for all signatures in a transaction return ss.GetHash();
} }
if (!(nHashType & SIGHASH_ANYONECANPAY) && (nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) { uint256 GetSequenceHash(const CTransaction& txTo) {
CHashWriter ss(SER_GETHASH, 0); CHashWriter ss(SER_GETHASH, 0);
for (unsigned int n = 0; n < txTo.vin.size(); n++) { for (unsigned int n = 0; n < txTo.vin.size(); n++) {
ss << txTo.vin[n].nSequence; ss << txTo.vin[n].nSequence;
} }
hashSequence = ss.GetHash(); // TODO: cache this value for all signatures in a transaction return ss.GetHash();
} }
if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) { uint256 GetOutputsHash(const CTransaction& txTo) {
CHashWriter ss(SER_GETHASH, 0); CHashWriter ss(SER_GETHASH, 0);
for (unsigned int n = 0; n < txTo.vout.size(); n++) { for (unsigned int n = 0; n < txTo.vout.size(); n++) {
ss << txTo.vout[n]; ss << txTo.vout[n];
} }
hashOutputs = ss.GetHash(); // TODO: cache this value for all signatures in a transaction return ss.GetHash();
}
} // anon namespace
PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo)
{
hashPrevouts = GetPrevoutHash(txTo);
hashSequence = GetSequenceHash(txTo);
hashOutputs = GetOutputsHash(txTo);
}
uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache)
{
if (sigversion == SIGVERSION_WITNESS_V0) {
uint256 hashPrevouts;
uint256 hashSequence;
uint256 hashOutputs;
if (!(nHashType & SIGHASH_ANYONECANPAY)) {
hashPrevouts = cache ? cache->hashPrevouts : GetPrevoutHash(txTo);
}
if (!(nHashType & SIGHASH_ANYONECANPAY) && (nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) {
hashSequence = cache ? cache->hashSequence : GetSequenceHash(txTo);
}
if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) {
hashOutputs = cache ? cache->hashOutputs : GetOutputsHash(txTo);
} else if ((nHashType & 0x1f) == SIGHASH_SINGLE && nIn < txTo.vout.size()) { } else if ((nHashType & 0x1f) == SIGHASH_SINGLE && nIn < txTo.vout.size()) {
CHashWriter ss(SER_GETHASH, 0); CHashWriter ss(SER_GETHASH, 0);
ss << txTo.vout[nIn]; ss << txTo.vout[nIn];
@ -1209,7 +1229,7 @@ bool TransactionSignatureChecker::CheckSig(const vector<unsigned char>& vchSigIn
int nHashType = vchSig.back(); int nHashType = vchSig.back();
vchSig.pop_back(); vchSig.pop_back();
uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion); uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion, this->txdata);
if (!VerifySignature(vchSig, pubkey, sighash)) if (!VerifySignature(vchSig, pubkey, sighash))
return false; return false;

13
src/script/interpreter.h

@ -98,13 +98,20 @@ enum
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror); bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror);
struct PrecomputedTransactionData
{
uint256 hashPrevouts, hashSequence, hashOutputs;
PrecomputedTransactionData(const CTransaction& tx);
};
enum SigVersion enum SigVersion
{ {
SIGVERSION_BASE = 0, SIGVERSION_BASE = 0,
SIGVERSION_WITNESS_V0 = 1, SIGVERSION_WITNESS_V0 = 1,
}; };
uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion); uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = NULL);
class BaseSignatureChecker class BaseSignatureChecker
{ {
@ -133,12 +140,14 @@ private:
const CTransaction* txTo; const CTransaction* txTo;
unsigned int nIn; unsigned int nIn;
const CAmount amount; const CAmount amount;
const PrecomputedTransactionData* txdata;
protected: protected:
virtual bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; virtual bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
public: public:
TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn) {} TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(NULL) {}
TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {}
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const; bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const;
bool CheckLockTime(const CScriptNum& nLockTime) const; bool CheckLockTime(const CScriptNum& nLockTime) const;
bool CheckSequence(const CScriptNum& nSequence) const; bool CheckSequence(const CScriptNum& nSequence) const;

2
src/script/sigcache.h

@ -22,7 +22,7 @@ private:
bool store; bool store;
public: public:
CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amount, bool storeIn) : TransactionSignatureChecker(txToIn, nInIn, amount), store(storeIn) {} CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amount, bool storeIn, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amount, txdataIn), store(storeIn) {}
bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
}; };

22
src/test/net_tests.cpp

@ -142,4 +142,26 @@ BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted)
BOOST_CHECK(addrman2.size() == 0); BOOST_CHECK(addrman2.size() == 0);
} }
BOOST_AUTO_TEST_CASE(cnode_simple_test)
{
SOCKET hSocket = INVALID_SOCKET;
in_addr ipv4Addr;
ipv4Addr.s_addr = 0xa0b0c001;
CAddress addr = CAddress(CService(ipv4Addr, 7777), NODE_NETWORK);
std::string pszDest = "";
bool fInboundIn = false;
// Test that fFeeler is false by default.
CNode* pnode1 = new CNode(hSocket, addr, pszDest, fInboundIn);
BOOST_CHECK(pnode1->fInbound == false);
BOOST_CHECK(pnode1->fFeeler == false);
fInboundIn = true;
CNode* pnode2 = new CNode(hSocket, addr, pszDest, fInboundIn);
BOOST_CHECK(pnode2->fInbound == true);
BOOST_CHECK(pnode2->fFeeler == false);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

6
src/test/script_P2SH_tests.cpp

@ -107,12 +107,13 @@ BOOST_AUTO_TEST_CASE(sign)
} }
// All of the above should be OK, and the txTos have valid signatures // All of the above should be OK, and the txTos have valid signatures
// Check to make sure signature verification fails if we use the wrong ScriptSig: // Check to make sure signature verification fails if we use the wrong ScriptSig:
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++) {
PrecomputedTransactionData txdata(txTo[i]);
for (int j = 0; j < 8; j++) for (int j = 0; j < 8; j++)
{ {
CScript sigSave = txTo[i].vin[0].scriptSig; CScript sigSave = txTo[i].vin[0].scriptSig;
txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig; txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig;
bool sigOK = CScriptCheck(CCoins(txFrom, 0), txTo[i], 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false)(); bool sigOK = CScriptCheck(CCoins(txFrom, 0), txTo[i], 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false, &txdata)();
if (i == j) if (i == j)
BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j)); BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j));
else else
@ -120,6 +121,7 @@ BOOST_AUTO_TEST_CASE(sign)
txTo[i].vin[0].scriptSig = sigSave; txTo[i].vin[0].scriptSig = sigSave;
} }
} }
}
BOOST_AUTO_TEST_CASE(norecurse) BOOST_AUTO_TEST_CASE(norecurse)
{ {

87
src/test/transaction_tests.cpp

@ -7,6 +7,7 @@
#include "test/test_bitcoin.h" #include "test/test_bitcoin.h"
#include "clientversion.h" #include "clientversion.h"
#include "checkqueue.h"
#include "consensus/validation.h" #include "consensus/validation.h"
#include "core_io.h" #include "core_io.h"
#include "key.h" #include "key.h"
@ -153,6 +154,7 @@ BOOST_AUTO_TEST_CASE(tx_valid)
BOOST_CHECK_MESSAGE(CheckTransaction(tx, state), strTest); BOOST_CHECK_MESSAGE(CheckTransaction(tx, state), strTest);
BOOST_CHECK(state.IsValid()); BOOST_CHECK(state.IsValid());
PrecomputedTransactionData txdata(tx);
for (unsigned int i = 0; i < tx.vin.size(); i++) for (unsigned int i = 0; i < tx.vin.size(); i++)
{ {
if (!mapprevOutScriptPubKeys.count(tx.vin[i].prevout)) if (!mapprevOutScriptPubKeys.count(tx.vin[i].prevout))
@ -168,7 +170,7 @@ BOOST_AUTO_TEST_CASE(tx_valid)
unsigned int verify_flags = ParseScriptFlags(test[2].get_str()); unsigned int verify_flags = ParseScriptFlags(test[2].get_str());
const CScriptWitness *witness = (i < tx.wit.vtxinwit.size()) ? &tx.wit.vtxinwit[i].scriptWitness : NULL; const CScriptWitness *witness = (i < tx.wit.vtxinwit.size()) ? &tx.wit.vtxinwit[i].scriptWitness : NULL;
BOOST_CHECK_MESSAGE(VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], BOOST_CHECK_MESSAGE(VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout],
witness, verify_flags, TransactionSignatureChecker(&tx, i, amount), &err), witness, verify_flags, TransactionSignatureChecker(&tx, i, amount, txdata), &err),
strTest); strTest);
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
} }
@ -237,6 +239,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
CValidationState state; CValidationState state;
fValid = CheckTransaction(tx, state) && state.IsValid(); fValid = CheckTransaction(tx, state) && state.IsValid();
PrecomputedTransactionData txdata(tx);
for (unsigned int i = 0; i < tx.vin.size() && fValid; i++) for (unsigned int i = 0; i < tx.vin.size() && fValid; i++)
{ {
if (!mapprevOutScriptPubKeys.count(tx.vin[i].prevout)) if (!mapprevOutScriptPubKeys.count(tx.vin[i].prevout))
@ -252,7 +255,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
} }
const CScriptWitness *witness = (i < tx.wit.vtxinwit.size()) ? &tx.wit.vtxinwit[i].scriptWitness : NULL; const CScriptWitness *witness = (i < tx.wit.vtxinwit.size()) ? &tx.wit.vtxinwit[i].scriptWitness : NULL;
fValid = VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], fValid = VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout],
witness, verify_flags, TransactionSignatureChecker(&tx, i, amount), &err); witness, verify_flags, TransactionSignatureChecker(&tx, i, amount, txdata), &err);
} }
BOOST_CHECK_MESSAGE(!fValid, strTest); BOOST_CHECK_MESSAGE(!fValid, strTest);
BOOST_CHECK_MESSAGE(err != SCRIPT_ERR_OK, ScriptErrorString(err)); BOOST_CHECK_MESSAGE(err != SCRIPT_ERR_OK, ScriptErrorString(err));
@ -419,6 +422,86 @@ void ReplaceRedeemScript(CScript& script, const CScript& redeemScript)
script = PushAll(stack); script = PushAll(stack);
} }
BOOST_AUTO_TEST_CASE(test_big_witness_transaction) {
CMutableTransaction mtx;
mtx.nVersion = 1;
CKey key;
key.MakeNewKey(false);
CBasicKeyStore keystore;
keystore.AddKeyPubKey(key, key.GetPubKey());
CKeyID hash = key.GetPubKey().GetID();
CScript scriptPubKey = CScript() << OP_0 << std::vector<unsigned char>(hash.begin(), hash.end());
vector<int> sigHashes;
sigHashes.push_back(SIGHASH_NONE | SIGHASH_ANYONECANPAY);
sigHashes.push_back(SIGHASH_SINGLE | SIGHASH_ANYONECANPAY);
sigHashes.push_back(SIGHASH_ALL | SIGHASH_ANYONECANPAY);
sigHashes.push_back(SIGHASH_NONE);
sigHashes.push_back(SIGHASH_SINGLE);
sigHashes.push_back(SIGHASH_ALL);
// create a big transaction of 4500 inputs signed by the same key
for(uint32_t ij = 0; ij < 4500; ij++) {
uint32_t i = mtx.vin.size();
uint256 prevId;
prevId.SetHex("0000000000000000000000000000000000000000000000000000000000000100");
COutPoint outpoint(prevId, i);
mtx.vin.resize(mtx.vin.size() + 1);
mtx.vin[i].prevout = outpoint;
mtx.vin[i].scriptSig = CScript();
mtx.vout.resize(mtx.vout.size() + 1);
mtx.vout[i].nValue = 1000;
mtx.vout[i].scriptPubKey = CScript() << OP_1;
}
// sign all inputs
for(uint32_t i = 0; i < mtx.vin.size(); i++) {
bool hashSigned = SignSignature(keystore, scriptPubKey, mtx, i, 1000, sigHashes.at(i % sigHashes.size()));
assert(hashSigned);
}
CTransaction tx;
CDataStream ssout(SER_NETWORK, PROTOCOL_VERSION);
WithOrVersion(&ssout, 0) << mtx;
WithOrVersion(&ssout, 0) >> tx;
// check all inputs concurrently, with the cache
PrecomputedTransactionData txdata(tx);
boost::thread_group threadGroup;
CCheckQueue<CScriptCheck> scriptcheckqueue(128);
CCheckQueueControl<CScriptCheck> control(&scriptcheckqueue);
for (int i=0; i<20; i++)
threadGroup.create_thread(boost::bind(&CCheckQueue<CScriptCheck>::Thread, boost::ref(scriptcheckqueue)));
CCoins coins;
coins.nVersion = 1;
coins.fCoinBase = false;
for(uint32_t i = 0; i < mtx.vin.size(); i++) {
CTxOut txout;
txout.nValue = 1000;
txout.scriptPubKey = scriptPubKey;
coins.vout.push_back(txout);
}
for(uint32_t i = 0; i < mtx.vin.size(); i++) {
std::vector<CScriptCheck> vChecks;
CScriptCheck check(coins, tx, i, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false, &txdata);
vChecks.push_back(CScriptCheck());
check.swap(vChecks.back());
control.Add(vChecks);
}
bool controlCheck = control.Wait();
assert(controlCheck);
threadGroup.interrupt_all();
threadGroup.join_all();
}
BOOST_AUTO_TEST_CASE(test_witness) BOOST_AUTO_TEST_CASE(test_witness)
{ {
CBasicKeyStore keystore, keystore2; CBasicKeyStore keystore, keystore2;

6
src/txmempool.cpp

@ -737,7 +737,8 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
waitingOnDependants.push_back(&(*it)); waitingOnDependants.push_back(&(*it));
else { else {
CValidationState state; CValidationState state;
assert(CheckInputs(tx, state, mempoolDuplicate, false, 0, false, NULL)); PrecomputedTransactionData txdata(tx);
assert(CheckInputs(tx, state, mempoolDuplicate, false, 0, false, txdata, NULL));
UpdateCoins(tx, mempoolDuplicate, 1000000); UpdateCoins(tx, mempoolDuplicate, 1000000);
} }
} }
@ -751,7 +752,8 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
stepsSinceLastRemove++; stepsSinceLastRemove++;
assert(stepsSinceLastRemove < waitingOnDependants.size()); assert(stepsSinceLastRemove < waitingOnDependants.size());
} else { } else {
assert(CheckInputs(entry->GetTx(), state, mempoolDuplicate, false, 0, false, NULL)); PrecomputedTransactionData txdata(entry->GetTx());
assert(CheckInputs(entry->GetTx(), state, mempoolDuplicate, false, 0, false, txdata, NULL));
UpdateCoins(entry->GetTx(), mempoolDuplicate, 1000000); UpdateCoins(entry->GetTx(), mempoolDuplicate, 1000000);
stepsSinceLastRemove = 0; stepsSinceLastRemove = 0;
} }

9
src/util.cpp

@ -801,11 +801,10 @@ int GetNumCores()
std::string CopyrightHolders(const std::string& strPrefix) std::string CopyrightHolders(const std::string& strPrefix)
{ {
std::string strCopyrightHolders = strPrefix + _(COPYRIGHT_HOLDERS); std::string strCopyrightHolders = strPrefix + strprintf(_(COPYRIGHT_HOLDERS), _(COPYRIGHT_HOLDERS_SUBSTITUTION));
if (strCopyrightHolders.find("%s") != strCopyrightHolders.npos) {
strCopyrightHolders = strprintf(strCopyrightHolders, _(COPYRIGHT_HOLDERS_SUBSTITUTION)); // Check for untranslated substitution to make sure Bitcoin Core copyright is not removed by accident
} if (strprintf(COPYRIGHT_HOLDERS, COPYRIGHT_HOLDERS_SUBSTITUTION).find("Bitcoin Core") == std::string::npos) {
if (strCopyrightHolders.find("Bitcoin Core developers") == strCopyrightHolders.npos) {
strCopyrightHolders += "\n" + strPrefix + "The Bitcoin Core developers"; strCopyrightHolders += "\n" + strPrefix + "The Bitcoin Core developers";
} }
return strCopyrightHolders; return strCopyrightHolders;

Loading…
Cancel
Save