diff options
68 files changed, 1605 insertions, 1018 deletions
diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index f277afc5ef..e78318e28e 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -33,11 +33,13 @@ contains(RELEASE, 1) { !win32 { # for extra security against potential buffer overflows: enable GCCs Stack Smashing Protection -QMAKE_CXXFLAGS *= -fstack-protector-all --param ssp-buffer-size=1 -QMAKE_LFLAGS *= -fstack-protector-all --param ssp-buffer-size=1 +QMAKE_CXXFLAGS *= -fstack-protector-all +QMAKE_LFLAGS *= -fstack-protector-all # We need to exclude this for Windows cross compile with MinGW 4.2.x, as it will result in a non-working executable! # This can be enabled for Windows, when we switch to MinGW >= 4.4.x. } +# for extra security (see: https://wiki.debian.org/Hardening) +QMAKE_CXXFLAGS *= -D_FORTIFY_SOURCE=2 -Wl,-z,relro -Wl,-z,now # for extra security on Windows: enable ASLR and DEP via GCC linker flags win32:QMAKE_LFLAGS *= -Wl,--dynamicbase -Wl,--nxcompat @@ -93,13 +95,13 @@ contains(BITCOIN_NEED_QT_PLUGINS, 1) { INCLUDEPATH += src/leveldb/include src/leveldb/helpers LIBS += $$PWD/src/leveldb/libleveldb.a $$PWD/src/leveldb/libmemenv.a !windows { - genleveldb.commands = cd $$PWD/src/leveldb ; $(MAKE) libleveldb.a libmemenv.a + genleveldb.commands = cd $$PWD/src/leveldb && $(MAKE) libleveldb.a libmemenv.a } else { # make an educated guess about what the ranlib command is called isEmpty(QMAKE_RANLIB) { QMAKE_RANLIB = $$replace(QMAKE_STRIP, strip, ranlib) } - genleveldb.commands = cd $$PWD/src/leveldb ; CC=$$QMAKE_CC CXX=$$QMAKE_CXX TARGET_OS=OS_WINDOWS_CROSSCOMPILE CXXFLAGS="-I$$BOOST_INCLUDE_PATH" LDFLAGS="-L$$BOOST_LIB_PATH" $(MAKE) libleveldb.a libmemenv.a ; $$QMAKE_RANLIB $$PWD/src/leveldb/libleveldb.a ; $$QMAKE_RANLIB $$PWD/src/leveldb/libmemenv.a + genleveldb.commands = cd $$PWD/src/leveldb && CC=$$QMAKE_CC CXX=$$QMAKE_CXX TARGET_OS=OS_WINDOWS_CROSSCOMPILE CXXFLAGS="-I$$BOOST_INCLUDE_PATH" LDFLAGS="-L$$BOOST_LIB_PATH" $(MAKE) libleveldb.a libmemenv.a && $$QMAKE_RANLIB $$PWD/src/leveldb/libleveldb.a && $$QMAKE_RANLIB $$PWD/src/leveldb/libmemenv.a } genleveldb.target = $$PWD/src/leveldb/libleveldb.a genleveldb.depends = FORCE @@ -146,7 +148,6 @@ HEADERS += src/qt/bitcoingui.h \ src/net.h \ src/key.h \ src/db.h \ - src/txdb.h \ src/walletdb.h \ src/script.h \ src/init.h \ @@ -343,7 +344,6 @@ isEmpty(BOOST_INCLUDE_PATH) { macx:BOOST_INCLUDE_PATH = /opt/local/include } -windows:LIBS += -lws2_32 -lshlwapi -lmswsock windows:DEFINES += WIN32 windows:RC_FILE = src/qt/res/bitcoin-qt.rc @@ -378,7 +378,7 @@ INCLUDEPATH += $$BOOST_INCLUDE_PATH $$BDB_INCLUDE_PATH $$OPENSSL_INCLUDE_PATH $$ LIBS += $$join(BOOST_LIB_PATH,,-L,) $$join(BDB_LIB_PATH,,-L,) $$join(OPENSSL_LIB_PATH,,-L,) $$join(QRENCODE_LIB_PATH,,-L,) LIBS += -lssl -lcrypto -ldb_cxx$$BDB_LIB_SUFFIX # -lgdi32 has to happen after -lcrypto (see #681) -windows:LIBS += -lole32 -luuid -lgdi32 +windows:LIBS += -lws2_32 -lshlwapi -lmswsock -lole32 -loleaut32 -luuid -lgdi32 LIBS += -lboost_system$$BOOST_LIB_SUFFIX -lboost_filesystem$$BOOST_LIB_SUFFIX -lboost_program_options$$BOOST_LIB_SUFFIX -lboost_thread$$BOOST_THREAD_LIB_SUFFIX windows:LIBS += -lboost_chrono$$BOOST_LIB_SUFFIX diff --git a/contrib/gitian-descriptors/README b/contrib/gitian-descriptors/README index 1d3910b994..46c7668ab9 100644 --- a/contrib/gitian-descriptors/README +++ b/contrib/gitian-descriptors/README @@ -31,7 +31,7 @@ Once you've got the right hardware and software: wget 'http://download.oracle.com/berkeley-db/db-4.8.30.NC.tar.gz' wget 'https://downloads.sourceforge.net/project/libpng/zlib/1.2.6/zlib-1.2.6.tar.gz' wget 'https://downloads.sourceforge.net/project/libpng/libpng15/older-releases/1.5.9/libpng-1.5.9.tar.gz' - wget 'http://releases.qt-project.org/qt4/source/qt-everywhere-opensource-src-4.8.2.tar.gz' + wget 'http://releases.qt-project.org/qt4/source/qt-everywhere-opensource-src-4.8.3.tar.gz' cd ../.. cd gitian-builder diff --git a/contrib/gitian-descriptors/gitian-win32.yml b/contrib/gitian-descriptors/gitian-win32.yml index c5979614d3..9df42a0bec 100644 --- a/contrib/gitian-descriptors/gitian-win32.yml +++ b/contrib/gitian-descriptors/gitian-win32.yml @@ -15,14 +15,14 @@ remotes: - "url": "https://github.com/bitcoin/bitcoin.git" "dir": "bitcoin" files: -- "qt-win32-4.8.2-gitian-r1.zip" +- "qt-win32-4.8.3-gitian-r1.zip" - "boost-win32-1.50.0-gitian2.zip" - "bitcoin-deps-0.0.5.zip" script: | # mkdir $HOME/qt cd $HOME/qt - unzip ../build/qt-win32-4.8.2-gitian-r1.zip + unzip ../build/qt-win32-4.8.3-gitian-r1.zip cd $HOME/build/ export PATH=$PATH:$HOME/qt/bin/ # diff --git a/contrib/gitian-descriptors/qt-win32.yml b/contrib/gitian-descriptors/qt-win32.yml index 87887dec87..0b711790e6 100644 --- a/contrib/gitian-descriptors/qt-win32.yml +++ b/contrib/gitian-descriptors/qt-win32.yml @@ -11,15 +11,15 @@ packages: reference_datetime: "2011-01-30 00:00:00" remotes: [] files: -- "qt-everywhere-opensource-src-4.8.2.tar.gz" +- "qt-everywhere-opensource-src-4.8.3.tar.gz" script: | INSTDIR="$HOME/qt/" mkdir $INSTDIR SRCDIR="$INSTDIR/src/" mkdir $SRCDIR # - tar xzf qt-everywhere-opensource-src-4.8.2.tar.gz - cd qt-everywhere-opensource-src-4.8.2 + tar xzf qt-everywhere-opensource-src-4.8.3.tar.gz + cd qt-everywhere-opensource-src-4.8.3 sed 's/$TODAY/2011-01-30/' -i configure sed 's/i686-pc-mingw32-/i586-mingw32msvc-/' -i mkspecs/unsupported/win32-g++-cross/qmake.conf sed --posix 's|QMAKE_CFLAGS\t\t= -pipe|QMAKE_CFLAGS\t\t= -pipe -isystem /usr/i586-mingw32msvc/include/ -frandom-seed=qtbuild|' -i mkspecs/unsupported/win32-g++-cross/qmake.conf @@ -51,4 +51,4 @@ script: | # as zip stores file timestamps, use faketime to intercept stat calls to set dates for all files to reference date export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1 - zip -r $OUTDIR/qt-win32-4.8.2-gitian-r1.zip * + zip -r $OUTDIR/qt-win32-4.8.3-gitian-r1.zip * diff --git a/contrib/verifysfbinaries/verify.sh b/contrib/verifysfbinaries/verify.sh new file mode 100755 index 0000000000..336de3ec1f --- /dev/null +++ b/contrib/verifysfbinaries/verify.sh @@ -0,0 +1,119 @@ +#!/bin/bash + +### This script attempts to download the signature file SHA256SUMS.asc from SourceForge +### It first checks if the signature passes, and then downloads the files specified in +### the file, and checks if the hashes of these files match those that are specified +### in the signature file. +### The script returns 0 if everything passes the checks. It returns 1 if either the +### signature check or the hash check doesn't pass. If an error occurs the return value is 2 + +function clean_up { + for file in $* + do + rm "$file" 2> /dev/null + done +} + +WORKINGDIR="/tmp/bitcoin" +TMPFILE="hashes.tmp" + +#this URL is used if a version number is not specified as an argument to the script +SIGNATUREFILE="http://downloads.sourceforge.net/project/bitcoin/Bitcoin/bitcoin-0.7.1/test/SHA256SUMS.asc" + +SIGNATUREFILENAME="SHA256SUMS.asc" +RCSUBDIR="test/" +BASEDIR="http://downloads.sourceforge.net/project/bitcoin/Bitcoin/" +VERSIONPREFIX="bitcoin-" +RCVERSIONSTRING="rc" + +if [ ! -d "$WORKINGDIR" ]; then + mkdir "$WORKINGDIR" +fi + +cd "$WORKINGDIR" + +#test if a version number has been passed as an argument +if [ -n "$1" ]; then + #let's also check if the version number includes the prefix 'bitcoin-', + # and add this prefix if it doesn't + if [[ $1 == "$VERSIONPREFIX"* ]]; then + VERSION="$1" + else + VERSION="$VERSIONPREFIX$1" + fi + + #now let's see if the version string contains "rc", and strip it off if it does + # and simultaneously add RCSUBDIR to BASEDIR, where we will look for SIGNATUREFILENAME + if [[ $VERSION == *"$RCVERSIONSTRING"* ]]; then + BASEDIR="$BASEDIR${VERSION/%-$RCVERSIONSTRING*}/" + BASEDIR="$BASEDIR$RCSUBDIR" + else + BASEDIR="$BASEDIR$VERSION/" + fi + + SIGNATUREFILE="$BASEDIR$SIGNATUREFILENAME" +else + BASEDIR="${SIGNATUREFILE%/*}/" +fi + +#first we fetch the file containing the signature +WGETOUT=$(wget -N "$BASEDIR$SIGNATUREFILENAME" 2>&1) + +#and then see if wget completed successfully +if [ $? -ne 0 ]; then + echo "Error: couldn't fetch signature file. Have you specified the version number in the following format?" + echo "[bitcoin-]<version>-[rc[0-9]] (example: bitcoin-0.7.1-rc1)" + echo "wget output:" + echo "$WGETOUT"|sed 's/^/\t/g' + exit 2 +fi + +#then we check it +GPGOUT=$(gpg --yes --decrypt --output "$TMPFILE" "$SIGNATUREFILENAME" 2>&1) + +#return value 0: good signature +#return value 1: bad signature +#return value 2: gpg error + +RET="$?" +if [ $RET -ne 0 ]; then + if [ $RET -eq 1 ]; then + #and notify the user if it's bad + echo "Bad signature." + elif [ $RET -eq 2 ]; then + #or if a gpg error has occured + echo "gpg error. Do you have Gavin's code signing key installed?" + fi + + echo "gpg output:" + echo "$GPGOUT"|sed 's/^/\t/g' + clean_up $SIGNATUREFILENAME $TMPFILE + exit "$RET" +fi + +#here we extract the filenames from the signature file +FILES=$(awk '{print $2}' "$TMPFILE") + +#and download these one by one +for file in in $FILES +do + wget --quiet -N "$BASEDIR$file" +done + +#check hashes +DIFF=$(diff <(sha256sum $FILES) "$TMPFILE") + +if [ $? -eq 1 ]; then + echo "Hashes don't match." + echo "Offending files:" + echo "$DIFF"|grep "^<"|awk '{print "\t"$3}' + exit 1 +elif [ $? -gt 1 ]; then + echo "Error executing 'diff'" + exit 2 +fi + +#everything matches! clean up the mess +clean_up $FILES $SIGNATUREFILENAME $TMPFILE + +exit 0 diff --git a/doc/release-process.txt b/doc/release-process.txt index 075fb01db7..02f0c47f47 100644 --- a/doc/release-process.txt +++ b/doc/release-process.txt @@ -2,7 +2,7 @@ * update (commit) version in sources bitcoin-qt.pro - src/clientversion.h + src/clientversion.h (change CLIENT_VERSION_IS_RELEASE to true) share/setup.nsi doc/README* @@ -30,12 +30,12 @@ wget 'ftp://ftp.simplesystems.org/pub/libpng/png/src/libpng-1.5.9.tar.gz' wget 'http://fukuchi.org/works/qrencode/qrencode-3.2.0.tar.bz2' wget 'http://downloads.sourceforge.net/project/boost/boost/1.50.0/boost_1_50_0.tar.bz2' - wget 'http://releases.qt-project.org/qt4/source/qt-everywhere-opensource-src-4.8.2.tar.gz' + wget 'http://releases.qt-project.org/qt4/source/qt-everywhere-opensource-src-4.8.3.tar.gz' cd .. ./bin/gbuild ../bitcoin/contrib/gitian-descriptors/boost-win32.yml mv build/out/boost-win32-1.50.0-gitian2.zip inputs/ ./bin/gbuild ../bitcoin/contrib/gitian-descriptors/qt-win32.yml - mv build/out/qt-win32-4.8.2-gitian-r1.zip inputs/ + mv build/out/qt-win32-4.8.3-gitian-r1.zip inputs/ ./bin/gbuild ../bitcoin/contrib/gitian-descriptors/deps-win32.yml mv build/out/bitcoin-deps-0.0.5.zip inputs/ diff --git a/src/allocators.h b/src/allocators.h index 99afa10c25..eb2aed6721 100644 --- a/src/allocators.h +++ b/src/allocators.h @@ -9,6 +9,7 @@ #include <string> #include <boost/thread/mutex.hpp> #include <map> +#include <openssl/crypto.h> // for OPENSSL_cleanse() #ifdef WIN32 #ifdef _WIN32_WINNT @@ -212,7 +213,7 @@ struct secure_allocator : public std::allocator<T> { if (p != NULL) { - memset(p, 0, sizeof(T) * n); + OPENSSL_cleanse(p, sizeof(T) * n); LockedPageManager::instance.UnlockRange(p, sizeof(T) * n); } std::allocator<T>::deallocate(p, n); @@ -246,7 +247,7 @@ struct zero_after_free_allocator : public std::allocator<T> void deallocate(T* p, std::size_t n) { if (p != NULL) - memset(p, 0, sizeof(T) * n); + OPENSSL_cleanse(p, sizeof(T) * n); std::allocator<T>::deallocate(p, n); } }; diff --git a/src/base58.h b/src/base58.h index 9dfea86ff5..be8a541f67 100644 --- a/src/base58.h +++ b/src/base58.h @@ -17,9 +17,11 @@ #include <string> #include <vector> + #include "bignum.h" #include "key.h" #include "script.h" +#include "allocators.h" static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; @@ -178,7 +180,8 @@ protected: unsigned char nVersion; // the actually encoded data - std::vector<unsigned char> vchData; + typedef std::vector<unsigned char, zero_after_free_allocator<unsigned char> > vector_uchar; + vector_uchar vchData; CBase58Data() { @@ -186,13 +189,6 @@ protected: vchData.clear(); } - ~CBase58Data() - { - // zero the memory, as it may contain sensitive data - if (!vchData.empty()) - memset(&vchData[0], 0, vchData.size()); - } - void SetData(int nVersionIn, const void* pdata, size_t nSize) { nVersion = nVersionIn; @@ -221,7 +217,7 @@ public: vchData.resize(vchTemp.size() - 1); if (!vchData.empty()) memcpy(&vchData[0], &vchTemp[1], vchData.size()); - memset(&vchTemp[0], 0, vchTemp.size()); + OPENSSL_cleanse(&vchTemp[0], vchData.size()); return true; } @@ -457,4 +453,4 @@ public: } }; -#endif +#endif // BITCOIN_BASE58_H diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 725037addc..07b616438e 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -11,7 +11,6 @@ #include "bitcoinrpc.h" #include "db.h" -#undef printf #include <boost/asio.hpp> #include <boost/asio/ip/v6_only.hpp> #include <boost/bind.hpp> @@ -26,8 +25,6 @@ #include <boost/shared_ptr.hpp> #include <list> -#define printf OutputDebugStringF - using namespace std; using namespace boost; using namespace boost::asio; @@ -179,14 +176,12 @@ Value help(const Array& params, bool fHelp) Value stop(const Array& params, bool fHelp) { + // Accept the deprecated and ignored 'detach´ boolean argument if (fHelp || params.size() > 1) throw runtime_error( - "stop <detach>\n" - "<detach> is true or false to detach the database or not for this stop only\n" - "Stop Bitcoin server (and possibly override the detachdb config value)."); + "stop\n" + "Stop Bitcoin server."); // Shutdown will take long enough that the response should get back - if (params.size() > 0) - bitdb.SetDetach(params[0].get_bool()); StartShutdown(); return "Bitcoin server stopping"; } @@ -234,6 +229,7 @@ static const CRPCCommand vRPCCommands[] = { "sendfrom", &sendfrom, false, false }, { "sendmany", &sendmany, false, false }, { "addmultisigaddress", &addmultisigaddress, false, false }, + { "createmultisig", &createmultisig, true, true }, { "getrawmempool", &getrawmempool, true, false }, { "getblock", &getblock, false, false }, { "getblockhash", &getblockhash, false, false }, @@ -361,6 +357,41 @@ static string HTTPReply(int nStatus, const string& strMsg, bool keepalive) strMsg.c_str()); } +bool ReadHTTPRequestLine(std::basic_istream<char>& stream, int &proto, + string& http_method, string& http_uri) +{ + string str; + getline(stream, str); + + // HTTP request line is space-delimited + vector<string> vWords; + boost::split(vWords, str, boost::is_any_of(" ")); + if (vWords.size() < 2) + return false; + + // HTTP methods permitted: GET, POST + http_method = vWords[0]; + if (http_method != "GET" && http_method != "POST") + return false; + + // HTTP URI must be an absolute path, relative to current host + http_uri = vWords[1]; + if (http_uri.size() == 0 || http_uri[0] != '/') + return false; + + // parse proto, if present + string strProto = ""; + if (vWords.size() > 2) + strProto = vWords[2]; + + proto = 0; + const char *ver = strstr(strProto.c_str(), "HTTP/1."); + if (ver != NULL) + proto = atoi(ver+7); + + return true; +} + int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto) { string str; @@ -376,7 +407,7 @@ int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto) return atoi(vWords[1].c_str()); } -int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet) +int ReadHTTPHeaders(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet) { int nLen = 0; loop @@ -401,17 +432,15 @@ int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHea return nLen; } -int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet, string& strMessageRet) +int ReadHTTPMessage(std::basic_istream<char>& stream, map<string, + string>& mapHeadersRet, string& strMessageRet, + int nProto) { mapHeadersRet.clear(); strMessageRet = ""; - // Read status - int nProto = 0; - int nStatus = ReadHTTPStatus(stream, nProto); - // Read header - int nLen = ReadHTTPHeader(stream, mapHeadersRet); + int nLen = ReadHTTPHeaders(stream, mapHeadersRet); if (nLen < 0 || nLen > (int)MAX_SIZE) return HTTP_INTERNAL_SERVER_ERROR; @@ -433,7 +462,7 @@ int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRe mapHeadersRet["connection"] = "close"; } - return nStatus; + return HTTP_OK; } bool HTTPAuthorized(map<string, string>& mapHeaders) @@ -719,7 +748,8 @@ void ThreadRPCServer2(void* parg) printf("ThreadRPCServer started\n"); strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]; - if (mapArgs["-rpcpassword"] == "") + if ((mapArgs["-rpcpassword"] == "") || + (mapArgs["-rpcuser"] == mapArgs["-rpcpassword"])) { unsigned char rand_pwd[32]; RAND_bytes(rand_pwd, 32); @@ -734,6 +764,7 @@ void ThreadRPCServer2(void* parg) "rpcuser=bitcoinrpc\n" "rpcpassword=%s\n" "(you do not need to remember this password)\n" + "The username and password MUST NOT be the same.\n" "If the file does not exist, create it with owner-readable-only file permissions.\n"), strWhatAmI.c_str(), GetConfigFile().string().c_str(), @@ -940,10 +971,17 @@ void ThreadRPCServer3(void* parg) } return; } + + int nProto = 0; map<string, string> mapHeaders; - string strRequest; + string strRequest, strMethod, strURI; + + // Read HTTP request line + if (!ReadHTTPRequestLine(conn->stream(), nProto, strMethod, strURI)) + break; - ReadHTTP(conn->stream(), mapHeaders, strRequest); + // Read HTTP message headers and body + ReadHTTPMessage(conn->stream(), mapHeaders, strRequest, nProto); // Check authorization if (mapHeaders.count("authorization") == 0) @@ -1075,10 +1113,15 @@ Object CallRPC(const string& strMethod, const Array& params) string strPost = HTTPPost(strRequest, mapRequestHeaders); stream << strPost << std::flush; - // Receive reply + // Receive HTTP reply status + int nProto = 0; + int nStatus = ReadHTTPStatus(stream, nProto); + + // Receive HTTP reply message headers and body map<string, string> mapHeaders; string strReply; - int nStatus = ReadHTTP(stream, mapHeaders, strReply); + ReadHTTPMessage(stream, mapHeaders, strReply, nProto); + if (nStatus == HTTP_UNAUTHORIZED) throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); else if (nStatus >= 400 && nStatus != HTTP_BAD_REQUEST && nStatus != HTTP_NOT_FOUND && nStatus != HTTP_INTERNAL_SERVER_ERROR) @@ -1160,6 +1203,8 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]); if (strMethod == "addmultisigaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]); if (strMethod == "addmultisigaddress" && n > 1) ConvertTo<Array>(params[1]); + if (strMethod == "createmultisig" && n > 0) ConvertTo<boost::int64_t>(params[0]); + if (strMethod == "createmultisig" && n > 1) ConvertTo<Array>(params[1]); if (strMethod == "listunspent" && n > 0) ConvertTo<boost::int64_t>(params[0]); if (strMethod == "listunspent" && n > 1) ConvertTo<boost::int64_t>(params[1]); if (strMethod == "listunspent" && n > 2) ConvertTo<Array>(params[2]); diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h index 9290697664..dc4dc303a8 100644 --- a/src/bitcoinrpc.h +++ b/src/bitcoinrpc.h @@ -158,6 +158,7 @@ extern json_spirit::Value movecmd(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value sendfrom(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value sendmany(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value addmultisigaddress(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value createmultisig(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value listreceivedbyaddress(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value listreceivedbyaccount(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value listtransactions(const json_spirit::Array& params, bool fHelp); diff --git a/src/clientversion.h b/src/clientversion.h index e79c306c09..24355d1a54 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -11,6 +11,9 @@ #define CLIENT_VERSION_REVISION 99 #define CLIENT_VERSION_BUILD 0 +// Set to true for release, false for prerelease or test build +#define CLIENT_VERSION_IS_RELEASE false + // Converts the parameter X to a string after macro replacement on X has been performed. // Don't merge these into one macro! #define STRINGIZE(X) DO_STRINGIZE(X) diff --git a/src/crypter.cpp b/src/crypter.cpp index 181b8fa00a..a2b62a87c8 100644 --- a/src/crypter.cpp +++ b/src/crypter.cpp @@ -24,8 +24,8 @@ bool CCrypter::SetKeyFromPassphrase(const SecureString& strKeyData, const std::v if (i != (int)WALLET_CRYPTO_KEY_SIZE) { - memset(&chKey, 0, sizeof chKey); - memset(&chIV, 0, sizeof chIV); + OPENSSL_cleanse(chKey, sizeof(chKey)); + OPENSSL_cleanse(chIV, sizeof(chIV)); return false; } diff --git a/src/crypter.h b/src/crypter.h index 04538a3fa5..6f75170bac 100644 --- a/src/crypter.h +++ b/src/crypter.h @@ -76,8 +76,8 @@ public: void CleanKey() { - memset(&chKey, 0, sizeof chKey); - memset(&chIV, 0, sizeof chIV); + OPENSSL_cleanse(chKey, sizeof(chKey)); + OPENSSL_cleanse(chIV, sizeof(chIV)); fKeySet = false; } diff --git a/src/db.cpp b/src/db.cpp index cf96fe6e38..60e1f6f280 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -55,7 +55,7 @@ void CDBEnv::Close() EnvShutdown(); } -bool CDBEnv::Open(boost::filesystem::path pathEnv_) +bool CDBEnv::Open(const boost::filesystem::path& path) { if (fDbEnvInit) return true; @@ -63,18 +63,16 @@ bool CDBEnv::Open(boost::filesystem::path pathEnv_) if (fShutdown) return false; - pathEnv = pathEnv_; - filesystem::path pathDataDir = pathEnv; - filesystem::path pathLogDir = pathDataDir / "database"; + filesystem::path pathLogDir = path / "database"; filesystem::create_directory(pathLogDir); - filesystem::path pathErrorFile = pathDataDir / "db.log"; + filesystem::path pathErrorFile = path / "db.log"; printf("dbenv.open LogDir=%s ErrorFile=%s\n", pathLogDir.string().c_str(), pathErrorFile.string().c_str()); unsigned int nEnvFlags = 0; if (GetBoolArg("-privdb", true)) nEnvFlags |= DB_PRIVATE; - int nDbCache = GetArg("-dbcache", 25); + unsigned int nDbCache = GetArg("-dbcache", 25); dbenv.set_lg_dir(pathLogDir.string().c_str()); dbenv.set_cachesize(nDbCache / 1024, (nDbCache % 1024)*1048576, 1); dbenv.set_lg_bsize(1048576); @@ -85,7 +83,7 @@ bool CDBEnv::Open(boost::filesystem::path pathEnv_) dbenv.set_flags(DB_AUTO_COMMIT, 1); dbenv.set_flags(DB_TXN_WRITE_NOSYNC, 1); dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1); - int ret = dbenv.open(pathDataDir.string().c_str(), + int ret = dbenv.open(path.string().c_str(), DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | @@ -271,14 +269,6 @@ CDB::CDB(const char *pszFile, const char* pszMode) : } } -static bool IsChainFile(std::string strFile) -{ - if (strFile == "coins.dat" || strFile == "blktree.dat") - return true; - - return false; -} - void CDB::Flush() { if (activeTxn) @@ -288,10 +278,6 @@ void CDB::Flush() unsigned int nMinutes = 0; if (fReadOnly) nMinutes = 1; - if (IsChainFile(strFile)) - nMinutes = 2; - if (IsChainFile(strFile) && IsInitialBlockDownload()) - nMinutes = 5; bitdb.dbenv.txn_checkpoint(nMinutes ? GetArg("-dblogsize", 100)*1024 : 0, nMinutes, 0); } @@ -453,11 +439,9 @@ void CDBEnv::Flush(bool fShutdown) CloseDb(strFile); printf("%s checkpoint\n", strFile.c_str()); dbenv.txn_checkpoint(0, 0, 0); - if (!IsChainFile(strFile) || fDetachDB) { - printf("%s detach\n", strFile.c_str()); - if (!fMockDb) - dbenv.lsn_reset(strFile.c_str(), 0); - } + printf("%s detach\n", strFile.c_str()); + if (!fMockDb) + dbenv.lsn_reset(strFile.c_str(), 0); printf("%s closed\n", strFile.c_str()); mapFileUseCount.erase(mi++); } @@ -31,10 +31,8 @@ bool BackupWallet(const CWallet& wallet, const std::string& strDest); class CDBEnv { private: - bool fDetachDB; bool fDbEnvInit; bool fMockDb; - boost::filesystem::path pathEnv; void EnvShutdown(); @@ -47,7 +45,7 @@ public: CDBEnv(); ~CDBEnv(); void MakeMock(); - bool IsMock() { return fMockDb; }; + bool IsMock() { return fMockDb; } /* * Verify that database file strFile is OK. If it is not, @@ -67,12 +65,10 @@ public: typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair; bool Salvage(std::string strFile, bool fAggressive, std::vector<KeyValPair>& vResult); - bool Open(boost::filesystem::path pathEnv_); + bool Open(const boost::filesystem::path &path); void Close(); void Flush(bool fShutdown); void CheckpointLSN(std::string strFile); - void SetDetach(bool fDetachDB_) { fDetachDB = fDetachDB_; } - bool GetDetach() { return fDetachDB; } void CloseDb(const std::string& strFile); bool RemoveDb(const std::string& strFile); diff --git a/src/init.cpp b/src/init.cpp index 45658e49d0..06f7359f80 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -26,6 +26,13 @@ using namespace boost; CWallet* pwalletMain; CClientUIInterface uiInterface; +// Used to pass flags to the Bind() function +enum BindFlags { + BF_NONE = 0, + BF_EXPLICIT = (1U << 0), + BF_REPORT_ERROR = (1U << 1) +}; + ////////////////////////////////////////////////////////////////////////////// // // Shutdown @@ -78,8 +85,10 @@ void Shutdown(void* parg) StopNode(); { LOCK(cs_main); - pcoinsTip->Flush(); - pblocktree->Flush(); + if (pblocktree) + pblocktree->Flush(); + if (pcoinsTip) + pcoinsTip->Flush(); delete pcoinsTip; delete pcoinsdbview; delete pblocktree; @@ -211,12 +220,12 @@ bool static InitWarning(const std::string &str) } -bool static Bind(const CService &addr, bool fError = true) { - if (IsLimited(addr)) +bool static Bind(const CService &addr, unsigned int flags) { + if (!(flags & BF_EXPLICIT) && IsLimited(addr)) return false; std::string strError; if (!BindListenPort(addr, strError)) { - if (fError) + if (flags & BF_REPORT_ERROR) return InitError(strError); return false; } @@ -234,7 +243,6 @@ std::string HelpMessage() " -gen=0 " + _("Don't generate coins") + "\n" + " -datadir=<dir> " + _("Specify data directory") + "\n" + " -dbcache=<n> " + _("Set database cache size in megabytes (default: 25)") + "\n" + - " -dblogsize=<n> " + _("Set database disk log size in megabytes (default: 100)") + "\n" + " -timeout=<n> " + _("Specify connection timeout in milliseconds (default: 5000)") + "\n" + " -proxy=<ip:port> " + _("Connect through socks proxy") + "\n" + " -socks=<n> " + _("Select the version of socks proxy to use (4-5, default: 5)") + "\n" + @@ -250,7 +258,7 @@ std::string HelpMessage() " -discover " + _("Discover own IP address (default: 1 when listening and no -externalip)") + "\n" + " -irc " + _("Find peers using internet relay chat (default: 0)") + "\n" + " -listen " + _("Accept connections from outside (default: 1 if no -proxy or -connect)") + "\n" + - " -bind=<addr> " + _("Bind to given address. Use [host]:port notation for IPv6") + "\n" + + " -bind=<addr> " + _("Bind to given address and always listen on it. Use [host]:port notation for IPv6") + "\n" + " -dnsseed " + _("Find peers using DNS lookup (default: 1 unless -connect)") + "\n" + " -banscore=<n> " + _("Threshold for disconnecting misbehaving peers (default: 100)") + "\n" + " -bantime=<n> " + _("Number of seconds to keep misbehaving peers from reconnecting (default: 86400)") + "\n" + @@ -263,7 +271,6 @@ std::string HelpMessage() " -upnp " + _("Use UPnP to map the listening port (default: 0)") + "\n" + #endif #endif - " -detachdb " + _("Detach block and address databases. Increases shutdown time (default: 0)") + "\n" + " -paytxfee=<amt> " + _("Fee per KB to add to transactions you send") + "\n" + #ifdef QT_GUI " -server " + _("Accept command line and JSON-RPC commands") + "\n" + @@ -293,6 +300,7 @@ std::string HelpMessage() " -checkblocks=<n> " + _("How many blocks to check at startup (default: 2500, 0 = all)") + "\n" + " -checklevel=<n> " + _("How thorough the block verification is (0-6, default: 1)") + "\n" + " -loadblock=<file> " + _("Imports blocks from external blk000?.dat file") + "\n" + + " -reindex " + _("Rebuild blockchain index from current blk000??.dat files") + "\n" + "\n" + _("Block creation options:") + "\n" + " -blockminsize=<n> " + _("Set minimum block size in bytes (default: 0)") + "\n" + @@ -308,6 +316,81 @@ std::string HelpMessage() return strUsage; } +struct CImportingNow +{ + CImportingNow() { + assert(fImporting == false); + fImporting = true; + } + + ~CImportingNow() { + assert(fImporting == true); + fImporting = false; + } +}; + +struct CImportData { + std::vector<boost::filesystem::path> vFiles; +}; + +void ThreadImport(void *data) { + CImportData *import = reinterpret_cast<CImportData*>(data); + + RenameThread("bitcoin-loadblk"); + + vnThreadsRunning[THREAD_IMPORT]++; + + // -reindex + if (fReindex) { + CImportingNow imp; + int nFile = 0; + while (!fShutdown) { + CDiskBlockPos pos; + pos.nFile = nFile; + pos.nPos = 0; + FILE *file = OpenBlockFile(pos, true); + if (!file) + break; + printf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile); + LoadExternalBlockFile(file, &pos); + nFile++; + } + if (!fShutdown) { + pblocktree->WriteReindexing(false); + fReindex = false; + printf("Reindexing finished\n"); + } + } + + // hardcoded $DATADIR/bootstrap.dat + filesystem::path pathBootstrap = GetDataDir() / "bootstrap.dat"; + if (filesystem::exists(pathBootstrap) && !fShutdown) { + FILE *file = fopen(pathBootstrap.string().c_str(), "rb"); + if (file) { + CImportingNow imp; + filesystem::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old"; + printf("Importing bootstrap.dat...\n"); + LoadExternalBlockFile(file); + RenameOver(pathBootstrap, pathBootstrapOld); + } + } + + // -loadblock= + BOOST_FOREACH(boost::filesystem::path &path, import->vFiles) { + if (fShutdown) + break; + FILE *file = fopen(path.string().c_str(), "rb"); + if (file) { + CImportingNow imp; + printf("Importing %s...\n", path.string().c_str()); + LoadExternalBlockFile(file); + } + } + + delete import; + + vnThreadsRunning[THREAD_IMPORT]--; +} /** Initialize bitcoin. * @pre Parameters should be parsed and config file should be read. @@ -406,8 +489,6 @@ bool AppInit2() else fDebugNet = GetBoolArg("-debugnet"); - bitdb.SetDetach(GetBoolArg("-detachdb", false)); - #if !defined(WIN32) && !defined(QT_GUI) fDaemon = GetBoolArg("-daemon"); #else @@ -459,7 +540,7 @@ bool AppInit2() if (file) fclose(file); static boost::interprocess::file_lock lock(pathLockFile.string().c_str()); if (!lock.try_lock()) - return InitError(strprintf(_("Cannot obtain a lock on data directory %s. Bitcoin is probably already running."), strDataDir.c_str())); + return InitError(strprintf(_("Cannot obtain a lock on data directory %s. Bitcoin is probably already running."), strDataDir.c_str())); #if !defined(WIN32) && !defined(QT_GUI) if (fDaemon) @@ -602,32 +683,28 @@ bool AppInit2() #endif bool fBound = false; - if (!fNoListen) - { - std::string strError; + if (!fNoListen) { if (mapArgs.count("-bind")) { BOOST_FOREACH(std::string strBind, mapMultiArgs["-bind"]) { CService addrBind; if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false)) return InitError(strprintf(_("Cannot resolve -bind address: '%s'"), strBind.c_str())); - fBound |= Bind(addrBind); + fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR)); } - } else { + } + else { struct in_addr inaddr_any; inaddr_any.s_addr = INADDR_ANY; #ifdef USE_IPV6 - if (!IsLimited(NET_IPV6)) - fBound |= Bind(CService(in6addr_any, GetListenPort()), false); + fBound |= Bind(CService(in6addr_any, GetListenPort()), BF_NONE); #endif - if (!IsLimited(NET_IPV4)) - fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound); + fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE); } if (!fBound) return InitError(_("Failed to listen on any port. Use -listen=0 if you want this.")); } - if (mapArgs.count("-externalip")) - { + if (mapArgs.count("-externalip")) { BOOST_FOREACH(string strAddr, mapMultiArgs["-externalip"]) { CService addrLocal(strAddr, GetListenPort(), fNameLookup); if (!addrLocal.IsValid()) @@ -641,6 +718,8 @@ bool AppInit2() // ********************************************************* Step 7: load block chain + fReindex = GetBoolArg("-reindex"); + if (!bitdb.Open(GetDataDir())) { string msg = strprintf(_("Error initializing database environment %s!" @@ -649,13 +728,28 @@ bool AppInit2() return InitError(msg); } + // cache size calculations + size_t nTotalCache = GetArg("-dbcache", 25) << 20; + if (nTotalCache < (1 << 22)) + nTotalCache = (1 << 22); // total cache cannot be less than 4 MiB + size_t nBlockTreeDBCache = nTotalCache / 8; + if (nBlockTreeDBCache > (1 << 21)) + nBlockTreeDBCache = (1 << 21); // block tree db cache shouldn't be larger than 2 MiB + nTotalCache -= nBlockTreeDBCache; + size_t nCoinDBCache = nTotalCache / 2; // use half of the remaining cache for coindb cache + nTotalCache -= nCoinDBCache; + nCoinCacheSize = nTotalCache / 300; // coins in memory require around 300 bytes + uiInterface.InitMessage(_("Loading block index...")); printf("Loading block index...\n"); nStart = GetTimeMillis(); - pblocktree = new CBlockTreeDB(); - pcoinsdbview = new CCoinsViewDB(); + pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex); + pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex); pcoinsTip = new CCoinsViewCache(*pcoinsdbview); + if (fReindex) + pblocktree->WriteReindexing(true); + if (!LoadBlockIndex()) return InitError(_("Error loading blkindex.dat")); @@ -788,13 +882,13 @@ bool AppInit2() if (!ConnectBestBlock()) strErrors << "Failed to connect best block"; - std::vector<boost::filesystem::path> *vPath = new std::vector<boost::filesystem::path>(); + CImportData *pimport = new CImportData(); if (mapArgs.count("-loadblock")) { BOOST_FOREACH(string strFile, mapMultiArgs["-loadblock"]) - vPath->push_back(strFile); + pimport->vFiles.push_back(strFile); } - NewThread(ThreadImport, vPath); + NewThread(ThreadImport, pimport); // ********************************************************* Step 10: load peers diff --git a/src/irc.cpp b/src/irc.cpp index 17d5ff1a5a..e8471a6630 100644 --- a/src/irc.cpp +++ b/src/irc.cpp @@ -192,6 +192,8 @@ void ThreadIRCSeed(void* parg) // Make this thread recognisable as the IRC seeding thread RenameThread("bitcoin-ircseed"); + printf("ThreadIRCSeed started\n"); + try { ThreadIRCSeed2(parg); @@ -218,7 +220,8 @@ void ThreadIRCSeed2(void* parg) if (!GetBoolArg("-irc", false)) return; - printf("ThreadIRCSeed started\n"); + printf("ThreadIRCSeed trying to connect...\n"); + int nErrorWait = 10; int nRetryWait = 10; int nNameRetry = 0; diff --git a/src/leveldb.cpp b/src/leveldb.cpp index e8a0fbe874..9e2f32a171 100644 --- a/src/leveldb.cpp +++ b/src/leveldb.cpp @@ -12,27 +12,31 @@ #include <boost/filesystem.hpp> -static leveldb::Options GetOptions() { +static leveldb::Options GetOptions(size_t nCacheSize) { leveldb::Options options; - int nCacheSizeMB = GetArg("-dbcache", 25); - options.block_cache = leveldb::NewLRUCache(nCacheSizeMB * 1048576); + options.block_cache = leveldb::NewLRUCache(nCacheSize / 2); + options.write_buffer_size = nCacheSize / 4; // up to two write buffers may be held in memory simultaneously options.filter_policy = leveldb::NewBloomFilterPolicy(10); options.compression = leveldb::kNoCompression; return options; } -CLevelDB::CLevelDB(const boost::filesystem::path &path, bool fMemory) { +CLevelDB::CLevelDB(const boost::filesystem::path &path, size_t nCacheSize, bool fMemory, bool fWipe) { penv = NULL; readoptions.verify_checksums = true; iteroptions.verify_checksums = true; iteroptions.fill_cache = false; syncoptions.sync = true; - options = GetOptions(); + options = GetOptions(nCacheSize); options.create_if_missing = true; if (fMemory) { penv = leveldb::NewMemEnv(leveldb::Env::Default()); options.env = penv; } else { + if (fWipe) { + printf("Wiping LevelDB in %s\n", path.string().c_str()); + leveldb::DestroyDB(path.string(), options); + } boost::filesystem::create_directory(path); printf("Opening LevelDB in %s\n", path.string().c_str()); } diff --git a/src/leveldb.h b/src/leveldb.h index ee9079c3c3..0b83432072 100644 --- a/src/leveldb.h +++ b/src/leveldb.h @@ -69,7 +69,7 @@ private: leveldb::DB *pdb; public: - CLevelDB(const boost::filesystem::path &path, bool fMemory = false); + CLevelDB(const boost::filesystem::path &path, size_t nCacheSize, bool fMemory = false, bool fWipe = false); ~CLevelDB(); template<typename K, typename V> bool Read(const K& key, V& value) { diff --git a/src/leveldb/Makefile b/src/leveldb/Makefile index 7f658cfdf9..14e494f3ce 100755 --- a/src/leveldb/Makefile +++ b/src/leveldb/Makefile @@ -19,10 +19,10 @@ $(shell ./build_detect_platform build_config.mk) # this file is generated by the previous line to set build flags and sources include build_config.mk -CFLAGS += -I. -I./include $(PLATFORM_CCFLAGS) $(OPT) -CXXFLAGS += -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT) +xCFLAGS = -I. -I./include $(PLATFORM_CCFLAGS) $(OPT) $(CFLAGS) +xCXXFLAGS = -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT) $(CXXFLAGS) -LDFLAGS += $(PLATFORM_LDFLAGS) +xLDFLAGS = $(PLATFORM_LDFLAGS) $(LDFLAGS) LIBOBJECTS = $(SOURCES:.cc=.o) MEMENVOBJECTS = $(MEMENV_SOURCES:.cc=.o) @@ -82,7 +82,7 @@ $(SHARED2): $(SHARED3) endif $(SHARED3): - $(CXX) $(LDFLAGS) $(PLATFORM_SHARED_LDFLAGS)$(SHARED2) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SOURCES) $(PLATFORM_EXTRALIBS) -o $(SHARED3) + $(CXX) $(xLDFLAGS) $(PLATFORM_SHARED_LDFLAGS)$(SHARED2) $(xCXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SOURCES) $(PLATFORM_EXTRALIBS) -o $(SHARED3) endif # PLATFORM_SHARED_EXT @@ -100,74 +100,74 @@ $(LIBRARY): $(LIBOBJECTS) $(AR) -rs $@ $(LIBOBJECTS) db_bench: db/db_bench.o $(LIBOBJECTS) $(TESTUTIL) - $(CXX) db/db_bench.o $(LIBOBJECTS) $(TESTUTIL) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) db/db_bench.o $(LIBOBJECTS) $(TESTUTIL) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) db_bench_sqlite3: doc/bench/db_bench_sqlite3.o $(LIBOBJECTS) $(TESTUTIL) - $(CXX) doc/bench/db_bench_sqlite3.o $(LIBOBJECTS) $(TESTUTIL) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) -lsqlite3 + $(CXX) doc/bench/db_bench_sqlite3.o $(LIBOBJECTS) $(TESTUTIL) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) -lsqlite3 db_bench_tree_db: doc/bench/db_bench_tree_db.o $(LIBOBJECTS) $(TESTUTIL) - $(CXX) doc/bench/db_bench_tree_db.o $(LIBOBJECTS) $(TESTUTIL) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) -lkyotocabinet + $(CXX) doc/bench/db_bench_tree_db.o $(LIBOBJECTS) $(TESTUTIL) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) -lkyotocabinet arena_test: util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) bloom_test: util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) c_test: db/c_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) db/c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) cache_test: util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) coding_test: util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) corruption_test: db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) crc32c_test: util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) db_test: db/db_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/db_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) db/db_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) dbformat_test: db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) env_test: util/env_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) util/env_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) util/env_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) filename_test: db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) filter_block_test: table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) log_test: db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) table_test: table/table_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) table/table_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) table/table_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) skiplist_test: db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) version_edit_test: db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) version_set_test: db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) write_batch_test: db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) $(MEMENVLIBRARY) : $(MEMENVOBJECTS) rm -f $@ $(AR) -rs $@ $(MEMENVOBJECTS) memenv_test : helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS) - $(CXX) helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) ifeq ($(PLATFORM), IOS) # For iOS, create universal object files to be used on both the simulator and @@ -179,22 +179,22 @@ IOSVERSION=$(shell defaults read $(PLATFORMSROOT)/iPhoneOS.platform/version CFBu .cc.o: mkdir -p ios-x86/$(dir $@) - $(SIMULATORROOT)/usr/bin/$(CXX) $(CXXFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -c $< -o ios-x86/$@ + $(SIMULATORROOT)/usr/bin/$(CXX) $(xCXXFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -c $< -o ios-x86/$@ mkdir -p ios-arm/$(dir $@) - $(DEVICEROOT)/usr/bin/$(CXX) $(CXXFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk -arch armv6 -arch armv7 -c $< -o ios-arm/$@ + $(DEVICEROOT)/usr/bin/$(CXX) $(xCXXFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk -arch armv6 -arch armv7 -c $< -o ios-arm/$@ lipo ios-x86/$@ ios-arm/$@ -create -output $@ .c.o: mkdir -p ios-x86/$(dir $@) - $(SIMULATORROOT)/usr/bin/$(CC) $(CFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -c $< -o ios-x86/$@ + $(SIMULATORROOT)/usr/bin/$(CC) $(xCFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -c $< -o ios-x86/$@ mkdir -p ios-arm/$(dir $@) - $(DEVICEROOT)/usr/bin/$(CC) $(CFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk -arch armv6 -arch armv7 -c $< -o ios-arm/$@ + $(DEVICEROOT)/usr/bin/$(CC) $(xCFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk -arch armv6 -arch armv7 -c $< -o ios-arm/$@ lipo ios-x86/$@ ios-arm/$@ -create -output $@ else .cc.o: - $(CXX) $(CXXFLAGS) -c $< -o $@ + $(CXX) $(xCXXFLAGS) -c $< -o $@ .c.o: - $(CC) $(CFLAGS) -c $< -o $@ + $(CC) $(xCFLAGS) -c $< -o $@ endif diff --git a/src/main.cpp b/src/main.cpp index ad5a7b0f19..346ed73323 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -41,6 +41,8 @@ CBlockIndex* pindexBest = NULL; set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexValid; // may contain all CBlockIndex*'s that have validness >=BLOCK_VALID_TRANSACTIONS, and must contain those who aren't failed int64 nTimeBestReceived = 0; bool fImporting = false; +bool fReindex = false; +unsigned int nCoinCacheSize = 5000; CMedianFilter<int> cPeerBlockCounts(5, 0); // Amount of blocks that other nodes claim to have @@ -691,13 +693,21 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, if (fCheckInputs) { - CCoinsViewCache &view = *pcoinsTip; + CCoinsView dummy; + CCoinsViewCache view(dummy); + + { + LOCK(cs); + CCoinsViewMemPool viewMemPool(*pcoinsTip, *this); + view.SetBackend(viewMemPool); // do we already have it? if (view.HaveCoins(hash)) return false; // do all inputs exist? + // Note that this does not check for the presence of actual outputs (see the next check for that), + // only helps filling in pfMissingInputs (to determine missing vs spent). BOOST_FOREACH(const CTxIn txin, tx.vin) { if (!view.HaveCoins(txin.prevout.hash)) { if (pfMissingInputs) @@ -706,9 +716,17 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, } } + // are the actual inputs available? if (!tx.HaveInputs(view)) return error("CTxMemPool::accept() : inputs already spent"); + // Bring the best block into scope + view.GetBestBlock(); + + // we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool + view.SetBackend(dummy); + } + // Check for non-standard pay-to-script-hash in inputs if (!tx.AreInputsStandard(view) && !fTestNet) return error("CTxMemPool::accept() : nonstandard transaction input"); @@ -738,7 +756,6 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, int64 nNow = GetTime(); { - LOCK(cs); // Use an exponentially decaying ~10-minute window: dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime)); nLastTime = nNow; @@ -754,7 +771,7 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. - if (!tx.CheckInputs(view, CS_ALWAYS, true, false)) + if (!tx.CheckInputs(view, CS_ALWAYS, SCRIPT_VERIFY_P2SH)) { return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str()); } @@ -1129,7 +1146,7 @@ int GetNumBlocksOfPeers() bool IsInitialBlockDownload() { - if (pindexBest == NULL || nBestHeight < Checkpoints::GetTotalBlocksEstimate()) + if (pindexBest == NULL || nBestHeight < Checkpoints::GetTotalBlocksEstimate() || fReindex || fImporting) return true; static int64 nLastUpdate; static CBlockIndex* pindexLastBest; @@ -1310,7 +1327,7 @@ bool CTransaction::HaveInputs(CCoinsViewCache &inputs) const return true; } -bool CTransaction::CheckInputs(CCoinsViewCache &inputs, enum CheckSig_mode csmode, bool fStrictPayToScriptHash, bool fStrictEncodings) const +bool CTransaction::CheckInputs(CCoinsViewCache &inputs, enum CheckSig_mode csmode, unsigned int flags) const { if (!IsCoinBase()) { @@ -1319,7 +1336,9 @@ bool CTransaction::CheckInputs(CCoinsViewCache &inputs, enum CheckSig_mode csmod if (!HaveInputs(inputs)) return error("CheckInputs() : %s inputs unavailable", GetHash().ToString().substr(0,10).c_str()); - CBlockIndex *pindexBlock = inputs.GetBestBlock(); + // While checking, GetBestBlock() refers to the parent block. + // This is also true for mempool checks. + int nSpendHeight = inputs.GetBestBlock()->nHeight + 1; int64 nValueIn = 0; int64 nFees = 0; for (unsigned int i = 0; i < vin.size(); i++) @@ -1329,8 +1348,8 @@ bool CTransaction::CheckInputs(CCoinsViewCache &inputs, enum CheckSig_mode csmod // If prev is coinbase, check that it's matured if (coins.IsCoinBase()) { - if (pindexBlock->nHeight - coins.nHeight < COINBASE_MATURITY) - return error("CheckInputs() : tried to spend coinbase at depth %d", pindexBlock->nHeight - coins.nHeight); + if (nSpendHeight - coins.nHeight < COINBASE_MATURITY) + return error("CheckInputs() : tried to spend coinbase at depth %d", nSpendHeight - coins.nHeight); } // Check for negative or overflow input values @@ -1365,14 +1384,8 @@ bool CTransaction::CheckInputs(CCoinsViewCache &inputs, enum CheckSig_mode csmod const CCoins &coins = inputs.GetCoins(prevout.hash); // Verify signature - if (!VerifySignature(coins, *this, i, fStrictPayToScriptHash, fStrictEncodings, 0)) { - // only during transition phase for P2SH: do not invoke anti-DoS code for - // potentially old clients relaying bad P2SH transactions - if (fStrictPayToScriptHash && VerifySignature(coins, *this, i, false, fStrictEncodings, 0)) - return error("CheckInputs() : %s P2SH VerifySignature failed", GetHash().ToString().substr(0,10).c_str()); - + if (!VerifySignature(coins, *this, i, flags, 0)) return DoS(100,error("CheckInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str())); - } } } } @@ -1402,7 +1415,7 @@ bool CTransaction::ClientCheckInputs() const return false; // Verify signature - if (!VerifySignature(CCoins(txPrev, -1), *this, i, true, false, 0)) + if (!VerifySignature(CCoins(txPrev, -1), *this, i, SCRIPT_VERIFY_P2SH, 0)) return error("ConnectInputs() : VerifySignature failed"); ///// this is redundant with the mempool.mapNextTx stuff, @@ -1541,7 +1554,8 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust // Now that the whole chain is irreversibly beyond that time it is applied to all blocks except the // two in the chain that violate it. This prevents exploiting the issue against nodes in their // initial block download. - bool fEnforceBIP30 = !((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) || + bool fEnforceBIP30 = (!pindex->phashBlock) || // Enforce on CreateNewBlock invocations which don't have a hash. + !((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) || (pindex->nHeight==91880 && pindex->GetBlockHash() == uint256("0x00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721"))); if (fEnforceBIP30) { for (unsigned int i=0; i<vtx.size(); i++) { @@ -1584,7 +1598,7 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust nFees += tx.GetValueIn(view)-tx.GetValueOut(); - if (!tx.CheckInputs(view, CS_AFTER_CHECKPOINT, fStrictPayToScriptHash, false)) + if (!tx.CheckInputs(view, CS_AFTER_CHECKPOINT, fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE)) return false; } @@ -1595,6 +1609,9 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust blockundo.vtxundo.push_back(txundo); } + if (vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees)) + return error("ConnectBlock() : coinbase pays too much (actual=%lld vs limit=%lld)", (long long)vtx[0].GetValueOut(), (long long)GetBlockValue(pindex->nHeight, nFees)); + if (fJustCheck) return true; @@ -1720,7 +1737,7 @@ bool SetBestChain(CBlockIndex* pindexNew) // Make sure it's successfully written to disk before changing memory structure bool fIsInitialDownload = IsInitialBlockDownload(); - if (!fIsInitialDownload || view.GetCacheSize()>5000) { + if (!fIsInitialDownload || view.GetCacheSize() > nCoinCacheSize) { FlushBlockFile(); pblocktree->Sync(); if (!view.Flush()) @@ -1846,35 +1863,45 @@ bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos) } -bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64 nTime) +bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64 nTime, bool fKnown = false) { bool fUpdatedLast = false; LOCK(cs_LastBlockFile); - while (infoLastBlockFile.nSize + nAddSize >= MAX_BLOCKFILE_SIZE) { - printf("Leaving block file %i: %s\n", nLastBlockFile, infoLastBlockFile.ToString().c_str()); - FlushBlockFile(); - nLastBlockFile++; - infoLastBlockFile.SetNull(); - pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile); // check whether data for the new file somehow already exist; can fail just fine - fUpdatedLast = true; + if (fKnown) { + if (nLastBlockFile != pos.nFile) { + nLastBlockFile = pos.nFile; + infoLastBlockFile.SetNull(); + pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile); + } + } else { + while (infoLastBlockFile.nSize + nAddSize >= MAX_BLOCKFILE_SIZE) { + printf("Leaving block file %i: %s\n", nLastBlockFile, infoLastBlockFile.ToString().c_str()); + FlushBlockFile(); + nLastBlockFile++; + infoLastBlockFile.SetNull(); + pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile); // check whether data for the new file somehow already exist; can fail just fine + fUpdatedLast = true; + } + pos.nFile = nLastBlockFile; + pos.nPos = infoLastBlockFile.nSize; } - pos.nFile = nLastBlockFile; - pos.nPos = infoLastBlockFile.nSize; infoLastBlockFile.nSize += nAddSize; infoLastBlockFile.AddBlock(nHeight, nTime); - unsigned int nOldChunks = (pos.nPos + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; - unsigned int nNewChunks = (infoLastBlockFile.nSize + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; - if (nNewChunks > nOldChunks) { - FILE *file = OpenBlockFile(pos); - if (file) { - printf("Pre-allocating up to position 0x%x in blk%05u.dat\n", nNewChunks * BLOCKFILE_CHUNK_SIZE, pos.nFile); - AllocateFileRange(file, pos.nPos, nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos); + if (!fKnown) { + unsigned int nOldChunks = (pos.nPos + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; + unsigned int nNewChunks = (infoLastBlockFile.nSize + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; + if (nNewChunks > nOldChunks) { + FILE *file = OpenBlockFile(pos); + if (file) { + printf("Pre-allocating up to position 0x%x in blk%05u.dat\n", nNewChunks * BLOCKFILE_CHUNK_SIZE, pos.nFile); + AllocateFileRange(file, pos.nPos, nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos); + fclose(file); + } } - fclose(file); } if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile)) @@ -1914,8 +1941,8 @@ bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize) if (file) { printf("Pre-allocating up to position 0x%x in rev%05u.dat\n", nNewChunks * UNDOFILE_CHUNK_SIZE, pos.nFile); AllocateFileRange(file, pos.nPos, nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos); + fclose(file); } - fclose(file); } return true; @@ -1951,9 +1978,13 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const if (!tx.CheckTransaction()) return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed")); + // Build the merkle tree already. We need it anyway later, and it makes the + // block cache the transaction hashes, which means they don't need to be + // recalculated many times during this block's validation. + BuildMerkleTree(); + // Check for duplicate txids. This is caught by ConnectInputs(), // but catching it earlier avoids a potential DoS attack: - BuildMerkleTree(); set<uint256> uniqueTx; for (unsigned int i=0; i<vtx.size(); i++) { uniqueTx.insert(GetTxHash(i)); @@ -1976,7 +2007,7 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const return true; } -bool CBlock::AcceptBlock() +bool CBlock::AcceptBlock(CDiskBlockPos *dbp) { // Check for duplicate uint256 hash = GetHash(); @@ -1984,60 +2015,69 @@ bool CBlock::AcceptBlock() return error("AcceptBlock() : block already in mapBlockIndex"); // Get prev block index - map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashPrevBlock); - if (mi == mapBlockIndex.end()) - return DoS(10, error("AcceptBlock() : prev block not found")); - CBlockIndex* pindexPrev = (*mi).second; - int nHeight = pindexPrev->nHeight+1; - - // Check proof of work - if (nBits != GetNextWorkRequired(pindexPrev, this)) - return DoS(100, error("AcceptBlock() : incorrect proof of work")); - - // Check timestamp against prev - if (GetBlockTime() <= pindexPrev->GetMedianTimePast()) - return error("AcceptBlock() : block's timestamp is too early"); - - // Check that all transactions are finalized - BOOST_FOREACH(const CTransaction& tx, vtx) - if (!tx.IsFinal(nHeight, GetBlockTime())) - return DoS(10, error("AcceptBlock() : contains a non-final transaction")); - - // Check that the block chain matches the known block chain up to a checkpoint - if (!Checkpoints::CheckBlock(nHeight, hash)) - return DoS(100, error("AcceptBlock() : rejected by checkpoint lock-in at %d", nHeight)); - - // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: - if (nVersion < 2) - { - if ((!fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 950, 1000)) || - (fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 75, 100))) + CBlockIndex* pindexPrev = NULL; + int nHeight = 0; + if (hash != hashGenesisBlock) { + map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashPrevBlock); + if (mi == mapBlockIndex.end()) + return DoS(10, error("AcceptBlock() : prev block not found")); + pindexPrev = (*mi).second; + nHeight = pindexPrev->nHeight+1; + + // Check proof of work + if (nBits != GetNextWorkRequired(pindexPrev, this)) + return DoS(100, error("AcceptBlock() : incorrect proof of work")); + + // Check timestamp against prev + if (GetBlockTime() <= pindexPrev->GetMedianTimePast()) + return error("AcceptBlock() : block's timestamp is too early"); + + // Check that all transactions are finalized + BOOST_FOREACH(const CTransaction& tx, vtx) + if (!tx.IsFinal(nHeight, GetBlockTime())) + return DoS(10, error("AcceptBlock() : contains a non-final transaction")); + + // Check that the block chain matches the known block chain up to a checkpoint + if (!Checkpoints::CheckBlock(nHeight, hash)) + return DoS(100, error("AcceptBlock() : rejected by checkpoint lock-in at %d", nHeight)); + + // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: + if (nVersion < 2) { - return error("AcceptBlock() : rejected nVersion=1 block"); + if ((!fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 950, 1000)) || + (fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 75, 100))) + { + return error("AcceptBlock() : rejected nVersion=1 block"); + } } - } - // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height - if (nVersion >= 2) - { - // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet): - if ((!fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 750, 1000)) || - (fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 51, 100))) + // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height + if (nVersion >= 2) { - CScript expect = CScript() << nHeight; - if (!std::equal(expect.begin(), expect.end(), vtx[0].vin[0].scriptSig.begin())) - return DoS(100, error("AcceptBlock() : block height mismatch in coinbase")); + // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet): + if ((!fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 750, 1000)) || + (fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 51, 100))) + { + CScript expect = CScript() << nHeight; + if (!std::equal(expect.begin(), expect.end(), vtx[0].vin[0].scriptSig.begin())) + return DoS(100, error("AcceptBlock() : block height mismatch in coinbase")); + } } } // Write block to history file unsigned int nBlockSize = ::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION); - if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION))) - return error("AcceptBlock() : out of disk space"); CDiskBlockPos blockPos; - if (!FindBlockPos(blockPos, nBlockSize+8, nHeight, nTime)) + if (dbp == NULL) { + if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION))) + return error("AcceptBlock() : out of disk space"); + } else { + blockPos = *dbp; + } + if (!FindBlockPos(blockPos, nBlockSize+8, nHeight, nTime, dbp != NULL)) return error("AcceptBlock() : FindBlockPos failed"); - if (!WriteToDisk(blockPos)) - return error("AcceptBlock() : WriteToDisk failed"); + if (dbp == NULL) + if (!WriteToDisk(blockPos)) + return error("AcceptBlock() : WriteToDisk failed"); if (!AddToBlockIndex(blockPos)) return error("AcceptBlock() : AddToBlockIndex failed"); @@ -2066,7 +2106,7 @@ bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, uns return (nFound >= nRequired); } -bool ProcessBlock(CNode* pfrom, CBlock* pblock) +bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) { // Check for duplicate uint256 hash = pblock->GetHash(); @@ -2104,7 +2144,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) // If we don't already have its previous block, shunt it off to holding area until we get it - if (!mapBlockIndex.count(pblock->hashPrevBlock)) + if (pblock->hashPrevBlock != 0 && !mapBlockIndex.count(pblock->hashPrevBlock)) { printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().substr(0,20).c_str()); @@ -2121,7 +2161,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) } // Store to disk - if (!pblock->AcceptBlock()) + if (!pblock->AcceptBlock(dbp)) return error("ProcessBlock() : AcceptBlock FAILED"); // Recursively process any orphan blocks that depended on this one @@ -2284,6 +2324,11 @@ bool static LoadBlockIndexDB() // Load bnBestInvalidWork, OK if it doesn't exist pblocktree->ReadBestInvalidWork(bnBestInvalidWork); + // Check whether we need to continue reindexing + bool fReindexing = false; + pblocktree->ReadReindexing(fReindexing); + fReindex |= fReindexing; + // Verify blocks in the best chain int nCheckLevel = GetArg("-checklevel", 1); int nCheckDepth = GetArg( "-checkblocks", 2500); @@ -2317,7 +2362,7 @@ bool static LoadBlockIndexDB() return true; } -bool LoadBlockIndex(bool fAllowNew) +bool LoadBlockIndex() { if (fTestNet) { @@ -2328,6 +2373,9 @@ bool LoadBlockIndex(bool fAllowNew) hashGenesisBlock = uint256("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"); } + if (fReindex) + return true; + // // Load block index from databases // @@ -2339,9 +2387,6 @@ bool LoadBlockIndex(bool fAllowNew) // if (mapBlockIndex.empty()) { - if (!fAllowNew) - return false; - // Genesis Block: // CBlock(hash=000000000019d6, ver=1, hashPrevBlock=00000000000000, hashMerkleRoot=4a5e1e, nTime=1231006505, nBits=1d00ffff, nNonce=2083236893, vtx=1) // CTransaction(hash=4a5e1e, ver=1, vin.size=1, vout.size=1, nLockTime=0) @@ -2467,110 +2512,71 @@ void PrintBlockTree() } } -bool LoadExternalBlockFile(FILE* fileIn) +bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) { int64 nStart = GetTimeMillis(); int nLoaded = 0; { - try { - CAutoFile blkdat(fileIn, SER_DISK, CLIENT_VERSION); - unsigned int nPos = 0; - while (nPos != (unsigned int)-1 && blkdat.good() && !fRequestShutdown) - { - unsigned char pchData[65536]; - do { - fseek(blkdat, nPos, SEEK_SET); - int nRead = fread(pchData, 1, sizeof(pchData), blkdat); - if (nRead <= 8) - { - nPos = (unsigned int)-1; - break; - } - void* nFind = memchr(pchData, pchMessageStart[0], nRead+1-sizeof(pchMessageStart)); - if (nFind) - { - if (memcmp(nFind, pchMessageStart, sizeof(pchMessageStart))==0) - { - nPos += ((unsigned char*)nFind - pchData) + sizeof(pchMessageStart); - break; - } - nPos += ((unsigned char*)nFind - pchData) + 1; - } - else - nPos += sizeof(pchData) - sizeof(pchMessageStart) + 1; - } while(!fRequestShutdown); - if (nPos == (unsigned int)-1) - break; - fseek(blkdat, nPos, SEEK_SET); - unsigned int nSize; + CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SIZE, MAX_BLOCK_SIZE+8, SER_DISK, CLIENT_VERSION); + uint64 nStartByte = 0; + if (dbp) { + // (try to) skip already indexed part + CBlockFileInfo info; + if (pblocktree->ReadBlockFileInfo(dbp->nFile, info)) { + nStartByte = info.nSize; + blkdat.Seek(info.nSize); + } + } + uint64 nRewind = blkdat.GetPos(); + while (blkdat.good() && !blkdat.eof() && !fShutdown) { + blkdat.SetPos(nRewind); + nRewind++; // start one byte further next time, in case of failure + blkdat.SetLimit(); // remove former limit + unsigned int nSize = 0; + try { + // locate a header + unsigned char buf[4]; + blkdat.FindByte(pchMessageStart[0]); + nRewind = blkdat.GetPos()+1; + blkdat >> FLATDATA(buf); + if (memcmp(buf, pchMessageStart, 4)) + continue; + // read size blkdat >> nSize; - if (nSize > 0 && nSize <= MAX_BLOCK_SIZE) - { - CBlock block; - blkdat >> block; + if (nSize < 80 || nSize > MAX_BLOCK_SIZE) + continue; + } catch (std::exception &e) { + // no valid block header found; don't complain + break; + } + try { + // read block + uint64 nBlockPos = blkdat.GetPos(); + blkdat.SetLimit(nBlockPos + nSize); + CBlock block; + blkdat >> block; + nRewind = blkdat.GetPos(); + + // process block + if (nBlockPos >= nStartByte) { LOCK(cs_main); - if (ProcessBlock(NULL,&block)) - { + if (dbp) + dbp->nPos = nBlockPos; + if (ProcessBlock(NULL, &block, dbp)) nLoaded++; - nPos += 4 + nSize; - } } + } catch (std::exception &e) { + printf("%s() : Deserialize or I/O error caught during load\n", __PRETTY_FUNCTION__); } } - catch (std::exception &e) { - printf("%s() : Deserialize or I/O error caught during load\n", - __PRETTY_FUNCTION__); - } + fclose(fileIn); } - printf("Loaded %i blocks from external file in %"PRI64d"ms\n", nLoaded, GetTimeMillis() - nStart); + if (nLoaded > 0) + printf("Loaded %i blocks from external file in %"PRI64d"ms\n", nLoaded, GetTimeMillis() - nStart); return nLoaded > 0; } -struct CImportingNow -{ - CImportingNow() { - assert(fImporting == false); - fImporting = true; - } - - ~CImportingNow() { - assert(fImporting == true); - fImporting = false; - } -}; - -void ThreadImport(void *data) { - std::vector<boost::filesystem::path> *vFiles = reinterpret_cast<std::vector<boost::filesystem::path>*>(data); - - RenameThread("bitcoin-loadblk"); - - CImportingNow imp; - vnThreadsRunning[THREAD_IMPORT]++; - - // -loadblock= - BOOST_FOREACH(boost::filesystem::path &path, *vFiles) { - FILE *file = fopen(path.string().c_str(), "rb"); - if (file) - LoadExternalBlockFile(file); - } - - // hardcoded $DATADIR/bootstrap.dat - filesystem::path pathBootstrap = GetDataDir() / "bootstrap.dat"; - if (filesystem::exists(pathBootstrap)) { - FILE *file = fopen(pathBootstrap.string().c_str(), "rb"); - if (file) { - filesystem::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old"; - LoadExternalBlockFile(file); - RenameOver(pathBootstrap, pathBootstrapOld); - } - } - - delete vFiles; - - vnThreadsRunning[THREAD_IMPORT]--; -} - @@ -2593,9 +2599,13 @@ string GetWarnings(string strFor) int nPriority = 0; string strStatusBar; string strRPC; + if (GetBoolArg("-testsafemode")) strRPC = "test"; + 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"); + // Misc warnings like out of disk space and clock is wrong if (strMiscWarning != "") { @@ -2678,7 +2688,6 @@ unsigned char pchMessageStart[4] = { 0xf9, 0xbe, 0xb4, 0xd9 }; bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { - static map<CService, CPubKey> mapReuseKey; RandAddSeedPerfmon(); if (fDebug) printf("received: %s (%"PRIszu" bytes)\n", strCommand.c_str(), vRecv.size()); @@ -2777,7 +2786,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // Ask the first connected node for block updates static int nAskedForBlocks = 0; - if (!pfrom->fClient && !pfrom->fOneShot && !fImporting && + if (!pfrom->fClient && !pfrom->fOneShot && !fImporting && !fReindex && (pfrom->nStartingHeight > (nBestHeight - 144)) && (pfrom->nVersion < NOBLKS_VERSION_START || pfrom->nVersion >= NOBLKS_VERSION_END) && @@ -2914,7 +2923,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) printf(" got inventory: %s %s\n", inv.ToString().c_str(), fAlreadyHave ? "have" : "new"); if (!fAlreadyHave) { - if (!fImporting) + if (!fImporting && !fReindex) pfrom->AskFor(inv); } else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) { pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(mapOrphanBlocks[inv.hash])); @@ -3144,7 +3153,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } - else if (strCommand == "block") + else if (strCommand == "block" && !fImporting && !fReindex) // Ignore blocks received while importing { CBlock block; vRecv >> block; @@ -3186,53 +3195,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } - else if (strCommand == "checkorder") - { - uint256 hashReply; - vRecv >> hashReply; - - if (!GetBoolArg("-allowreceivebyip")) - { - pfrom->PushMessage("reply", hashReply, (int)2, string("")); - return true; - } - - CWalletTx order; - vRecv >> order; - - /// we have a chance to check the order here - - // Keep giving the same key to the same ip until they use it - if (!mapReuseKey.count(pfrom->addr)) - pwalletMain->GetKeyFromPool(mapReuseKey[pfrom->addr], true); - - // Send back approval of order and pubkey to use - CScript scriptPubKey; - scriptPubKey << mapReuseKey[pfrom->addr] << OP_CHECKSIG; - pfrom->PushMessage("reply", hashReply, (int)0, scriptPubKey); - } - - - else if (strCommand == "reply") - { - uint256 hashReply; - vRecv >> hashReply; - - CRequestTracker tracker; - { - LOCK(pfrom->cs_mapRequests); - map<uint256, CRequestTracker>::iterator mi = pfrom->mapRequests.find(hashReply); - if (mi != pfrom->mapRequests.end()) - { - tracker = (*mi).second; - pfrom->mapRequests.erase(mi); - } - } - if (!tracker.IsNull()) - tracker.fn(tracker.param1, vRecv); - } - - else if (strCommand == "ping") { if (pfrom->nVersion > BIP0031_VERSION) @@ -3721,12 +3683,8 @@ public: } }; -const char* pszDummy = "\0\0"; -CScript scriptDummy(std::vector<unsigned char>(pszDummy, pszDummy + sizeof(pszDummy))); - CBlock* CreateNewBlock(CReserveKey& reservekey) { - CBlockIndex* pindexPrev = pindexBest; // Create new block auto_ptr<CBlock> pblock(new CBlock()); @@ -3771,6 +3729,7 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) int64 nFees = 0; { LOCK2(cs_main, mempool.cs); + CBlockIndex* pindexPrev = pindexBest; CCoinsViewCache view(*pcoinsTip, true); // Priority order to process transactions @@ -3825,7 +3784,7 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) int64 nValueIn = coins.vout[txin.prevout.n].nValue; nTotalIn += nValueIn; - int nConf = pindexPrev->nHeight - coins.nHeight; + int nConf = pindexPrev->nHeight - coins.nHeight + 1; dPriority += (double)nValueIn * nConf; } @@ -3904,7 +3863,7 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) continue; - if (!tx.CheckInputs(viewTemp, CS_ALWAYS, true, false)) + if (!tx.CheckInputs(viewTemp, CS_ALWAYS, SCRIPT_VERIFY_P2SH)) continue; CTxUndo txundo; @@ -3957,7 +3916,7 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) pblock->UpdateTime(pindexPrev); pblock->nBits = GetNextWorkRequired(pindexPrev, pblock.get()); pblock->nNonce = 0; - pblock->vtx[0].vin[0].scriptSig = scriptDummy; + pblock->vtx[0].vin[0].scriptSig = CScript() << OP_0 << OP_0; CBlockIndex indexDummy(*pblock); indexDummy.pprev = pindexPrev; diff --git a/src/main.h b/src/main.h index 75950109da..ab341af6fe 100644 --- a/src/main.h +++ b/src/main.h @@ -20,26 +20,38 @@ class CReserveKey; class CAddress; class CInv; -class CRequestTracker; class CNode; -class CBlockIndexWorkComparator; +struct CBlockIndexWorkComparator; +/** The maximum allowed size for a serialized block, in bytes (network rule) */ static const unsigned int MAX_BLOCK_SIZE = 1000000; +/** The maximum size for mined blocks */ static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2; +/** The maximum allowed number of signature check operations in a block (network rule) */ static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50; +/** The maximum number of orphan transactions kept in memory */ static const unsigned int MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100; +/** The maximum number of entries in an 'inv' protocol message */ static const unsigned int MAX_INV_SZ = 50000; +/** The maximum size of a blk?????.dat file (since 0.8) */ static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB +/** The pre-allocation chunk size for blk?????.dat files (since 0.8) */ static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB +/** The pre-allocation chunk size for rev?????.dat files (since 0.8) */ static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB +/** Fake height value used in CCoins to signify they are only in the memory pool (since 0.8) */ static const unsigned int MEMPOOL_HEIGHT = 0x7FFFFFFF; +/** Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) */ static const int64 MIN_TX_FEE = 50000; +/** Fees smaller than this (in satoshi) are considered zero fee (for relaying) */ static const int64 MIN_RELAY_TX_FEE = 10000; +/** No amount larger than this (in satoshi) is valid */ static const int64 MAX_MONEY = 21000000 * COIN; inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } +/** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */ static const int COINBASE_MATURITY = 100; -// Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp. +/** Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp. */ static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC #ifdef USE_UPNP static const int fHaveUPnP = true; @@ -76,6 +88,8 @@ extern CCriticalSection cs_setpwalletRegistered; extern std::set<CWallet*> setpwalletRegistered; extern unsigned char pchMessageStart[4]; extern bool fImporting; +extern bool fReindex; +extern unsigned int nCoinCacheSize; // Settings extern int64 nTransactionFee; @@ -93,32 +107,61 @@ class CTxUndo; class CCoinsView; class CCoinsViewCache; +/** Register a wallet to receive updates from core */ void RegisterWallet(CWallet* pwalletIn); +/** Unregister a wallet from core */ void UnregisterWallet(CWallet* pwalletIn); +/** Push an updated transaction to all registered wallets */ void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false); -bool ProcessBlock(CNode* pfrom, CBlock* pblock); +/** Process an incoming block */ +bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp = NULL); +/** Check whether enough disk space is available for an incoming block */ bool CheckDiskSpace(uint64 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); -bool LoadBlockIndex(bool fAllowNew=true); +/** Import blocks from an external file */ +bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp = NULL); +/** Load the block tree and coins database from disk */ +bool LoadBlockIndex(); +/** Print the loaded block tree */ void PrintBlockTree(); +/** Find a block by height in the currently-connected chain */ CBlockIndex* FindBlockByHeight(int nHeight); +/** Process protocol messages received from a given node */ bool ProcessMessages(CNode* pfrom); +/** Send queued protocol messages to be sent to a give node */ bool SendMessages(CNode* pto, bool fSendTrickle); +/** Run the importer thread, which deals with reindexing, loading bootstrap.dat, and whatever is passed to -loadblock */ void ThreadImport(void *parg); +/** Run the miner threads */ void GenerateBitcoins(bool fGenerate, CWallet* pwallet); +/** Generate a new block, without valid proof-of-work */ CBlock* CreateNewBlock(CReserveKey& reservekey); +/** Modify the extranonce in a block */ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce); +/** Do mining precalculation */ void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1); +/** Check mined block */ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey); +/** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */ bool CheckProofOfWork(uint256 hash, unsigned int nBits); +/** Calculate the minimum amount of work a received block needs, without knowing its direct parent */ unsigned int ComputeMinWork(unsigned int nBase, int64 nTime); +/** Get the number of active peers */ int GetNumBlocksOfPeers(); +/** Check whether we are doin an inital block download (synchronizing from disk or network) */ bool IsInitialBlockDownload(); +/** Format a string that describes several potential problems detected by the core */ std::string GetWarnings(std::string strFor); +/** Retrieve a transaction (from memory pool, or from disk, if possible) */ bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, bool fAllowSlow = false); +/** Connect/disconnect blocks until pindexNew is the new tip of the active block chain */ bool SetBestChain(CBlockIndex* pindexNew); +/** Find the best known block, and make it the tip of the block chain */ bool ConnectBestBlock(); +/** Create a new block index entry for a given block hash */ CBlockIndex * InsertBlockIndex(uint256 hash); @@ -582,7 +625,7 @@ public: // Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts) // This does not modify the UTXO set - bool CheckInputs(CCoinsViewCache &view, enum CheckSig_mode csmode, bool fStrictPayToScriptHash=true, bool fStrictEncodings=true) const; + bool CheckInputs(CCoinsViewCache &view, enum CheckSig_mode csmode, unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC) const; // Apply the effects of this transaction on the UTXO set represented by view bool UpdateCoins(CCoinsViewCache &view, CTxUndo &txundo, int nHeight, const uint256 &txhash) const; @@ -690,7 +733,6 @@ public: bool WriteToDisk(CDiskBlockPos &pos) { - // Open history file to append CAutoFile fileout = CAutoFile(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); if (!fileout) @@ -703,7 +745,7 @@ public: // Write undo data long fileOutPos = ftell(fileout); if (fileOutPos < 0) - return error("CBlock::WriteToDisk() : ftell failed"); + return error("CBlockUndo::WriteToDisk() : ftell failed"); pos.nPos = (unsigned int)fileOutPos; fileout << *this; @@ -714,7 +756,6 @@ public: return true; } - }; /** pruned version of CTransaction: only retains metadata and unspent transaction outputs @@ -1248,7 +1289,8 @@ public: bool CheckBlock(bool fCheckPOW=true, bool fCheckMerkleRoot=true) const; // Store block on disk - bool AcceptBlock(); + // if dbp is provided, the file is known to already reside on disk + bool AcceptBlock(CDiskBlockPos *dbp = NULL); }; @@ -1820,8 +1862,15 @@ public: // Modify the currently active block index virtual bool SetBestBlock(CBlockIndex *pindex); + + // Do a bulk modification (multiple SetCoins + one SetBestBlock) virtual bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex); + + // Calculate statistics about the unspent transaction output set virtual bool GetStats(CCoinsStats &stats); + + // As we use CCoinsViews polymorphically, have a virtual destructor + virtual ~CCoinsView() {} }; /** CCoinsView backed by another CCoinsView */ @@ -1851,14 +1900,25 @@ protected: public: CCoinsViewCache(CCoinsView &baseIn, bool fDummy = false); + + // Standard CCoinsView methods bool GetCoins(uint256 txid, CCoins &coins); bool SetCoins(uint256 txid, const CCoins &coins); bool HaveCoins(uint256 txid); - CCoins &GetCoins(uint256 txid); CBlockIndex *GetBestBlock(); bool SetBestBlock(CBlockIndex *pindex); bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex); + + // Return a modifiable reference to a CCoins. Check HaveCoins first. + // Many methods explicitly require a CCoinsViewCache because of this method, to reduce + // copying. + CCoins &GetCoins(uint256 txid); + + // Push the modifications applied to this cache to its base. + // Failure to call this method before destruction will cause the changes to be forgotten. bool Flush(); + + // Calculate the size of the cache (in number of transactions) unsigned int GetCacheSize(); private: diff --git a/src/makefile.linux-mingw b/src/makefile.linux-mingw index f4adbb2bff..64268dd0aa 100644 --- a/src/makefile.linux-mingw +++ b/src/makefile.linux-mingw @@ -92,7 +92,7 @@ LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a DEFS += -I"$(CURDIR)/leveldb/include" DEFS += -I"$(CURDIR)/leveldb/helpers" leveldb/libleveldb.a: - @echo "Building LevelDB ..."; cd leveldb; TARGET_OS=OS_WINDOWS_CROSSCOMPILE CXXFLAGS="-I$(INCLUDEPATHS)" LDFLAGS="-L$(LIBPATHS)" make libleveldb.a libmemenv.a; cd .. + @echo "Building LevelDB ..." && cd leveldb && CC=i586-mingw32msvc-gcc CXX=i586-mingw32msvc-g++ TARGET_OS=OS_WINDOWS_CROSSCOMPILE CXXFLAGS="-I$(INCLUDEPATHS)" LDFLAGS="-L$(LIBPATHS)" make libleveldb.a libmemenv.a && i586-mingw32msvc-ranlib libleveldb.a && i586-mingw32msvc-ranlib libmemenv.a && cd .. obj/leveldb.o: leveldb/libleveldb.a obj/build.h: FORCE diff --git a/src/makefile.mingw b/src/makefile.mingw index 945ec77099..85d63e9ceb 100644 --- a/src/makefile.mingw +++ b/src/makefile.mingw @@ -94,8 +94,9 @@ test check: test_bitcoin.exe FORCE LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers) +# TODO: If this fails, try adding a ranlib libleveldb.a && ranlib libmemenv.a leveldb/libleveldb.a: - cd leveldb; make libleveldb.a libmemenv.a; cd .. + cd leveldb && make libleveldb.a libmemenv.a && cd .. obj/leveldb.o: leveldb/libleveldb.lib obj/%.o: %.cpp $(HEADERS) diff --git a/src/makefile.osx b/src/makefile.osx index f3e17d0d13..9629545c0a 100644 --- a/src/makefile.osx +++ b/src/makefile.osx @@ -128,7 +128,7 @@ LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers) leveldb/libleveldb.a: - @echo "Building LevelDB ..."; cd leveldb; make libleveldb.a libmemenv.a; cd .. + @echo "Building LevelDB ..." && cd leveldb && make libleveldb.a libmemenv.a && cd .. obj/leveldb.o: leveldb/libleveldb.a # auto-generated dependencies: diff --git a/src/makefile.unix b/src/makefile.unix index df05f7990a..653f512e82 100644 --- a/src/makefile.unix +++ b/src/makefile.unix @@ -144,7 +144,7 @@ LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers) leveldb/libleveldb.a: - @echo "Building LevelDB ..."; cd leveldb; make libleveldb.a libmemenv.a; cd ..; + @echo "Building LevelDB ..." && cd leveldb && make libleveldb.a libmemenv.a && cd .. obj/leveldb.o: leveldb/libleveldb.a # auto-generated dependencies: diff --git a/src/net.cpp b/src/net.cpp index 2598f0214e..b54f8c15f7 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -710,13 +710,9 @@ void ThreadSocketHandler2(void* parg) TRY_LOCK(pnode->cs_vRecv, lockRecv); if (lockRecv) { - TRY_LOCK(pnode->cs_mapRequests, lockReq); - if (lockReq) - { - TRY_LOCK(pnode->cs_inventory, lockInv); - if (lockInv) - fDelete = true; - } + TRY_LOCK(pnode->cs_inventory, lockInv); + if (lockInv) + fDelete = true; } } } @@ -1313,6 +1309,8 @@ void DumpAddresses() void ThreadDumpAddress2(void* parg) { + printf("ThreadDumpAddress started\n"); + vnThreadsRunning[THREAD_DUMPADDRESS]++; while (!fShutdown) { @@ -19,7 +19,6 @@ #include "protocol.h" #include "addrman.h" -class CRequestTracker; class CNode; class CBlockIndex; extern int nBestHeight; @@ -74,25 +73,6 @@ enum MSG_BLOCK, }; -class CRequestTracker -{ -public: - void (*fn)(void*, CDataStream&); - void* param1; - - explicit CRequestTracker(void (*fnIn)(void*, CDataStream&)=NULL, void* param1In=NULL) - { - fn = fnIn; - param1 = param1In; - } - - bool IsNull() - { - return fn == NULL; - } -}; - - /** Thread types */ enum threadId { @@ -189,8 +169,6 @@ protected: public: int64 nReleaseTime; - std::map<uint256, CRequestTracker> mapRequests; - CCriticalSection cs_mapRequests; uint256 hashContinue; CBlockIndex* pindexLastGetBlocksBegin; uint256 hashLastGetBlocksEnd; @@ -564,53 +542,6 @@ public: } } - - void PushRequest(const char* pszCommand, - void (*fn)(void*, CDataStream&), void* param1) - { - uint256 hashReply; - RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply)); - - { - LOCK(cs_mapRequests); - mapRequests[hashReply] = CRequestTracker(fn, param1); - } - - PushMessage(pszCommand, hashReply); - } - - template<typename T1> - void PushRequest(const char* pszCommand, const T1& a1, - void (*fn)(void*, CDataStream&), void* param1) - { - uint256 hashReply; - RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply)); - - { - LOCK(cs_mapRequests); - mapRequests[hashReply] = CRequestTracker(fn, param1); - } - - PushMessage(pszCommand, hashReply, a1); - } - - template<typename T1, typename T2> - void PushRequest(const char* pszCommand, const T1& a1, const T2& a2, - void (*fn)(void*, CDataStream&), void* param1) - { - uint256 hashReply; - RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply)); - - { - LOCK(cs_mapRequests); - mapRequests[hashReply] = CRequestTracker(fn, param1); - } - - PushMessage(pszCommand, hashReply, a1, a2); - } - - - void PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd); bool IsSubscribed(unsigned int nChannel); void Subscribe(unsigned int nChannel, unsigned int nHops=0); diff --git a/src/netbase.cpp b/src/netbase.cpp index 7b28e7f1bc..9e7307204a 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -545,7 +545,7 @@ bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest void CNetAddr::Init() { - memset(ip, 0, 16); + memset(ip, 0, sizeof(ip)); } void CNetAddr::SetIP(const CNetAddr& ipIn) diff --git a/src/qt/addressbookpage.h b/src/qt/addressbookpage.h index df87486949..f7d177c513 100644 --- a/src/qt/addressbookpage.h +++ b/src/qt/addressbookpage.h @@ -82,4 +82,4 @@ signals: void verifyMessage(QString addr); }; -#endif // ADDRESSBOOKDIALOG_H +#endif // ADDRESSBOOKPAGE_H diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp index 03aa7af0be..cf35ee2457 100644 --- a/src/qt/askpassphrasedialog.cpp +++ b/src/qt/askpassphrasedialog.cpp @@ -19,7 +19,7 @@ AskPassphraseDialog::AskPassphraseDialog(Mode mode, QWidget *parent) : ui->passEdit1->setMaxLength(MAX_PASSPHRASE_SIZE); ui->passEdit2->setMaxLength(MAX_PASSPHRASE_SIZE); ui->passEdit3->setMaxLength(MAX_PASSPHRASE_SIZE); - + // Setup Caps Lock detection. ui->passEdit1->installEventFilter(this); ui->passEdit2->installEventFilter(this); @@ -108,15 +108,15 @@ void AskPassphraseDialog::accept() if(model->setWalletEncrypted(true, newpass1)) { QMessageBox::warning(this, tr("Wallet encrypted"), - "<qt>" + + "<qt>" + tr("Bitcoin will close now to finish the encryption process. " "Remember that encrypting your wallet cannot fully protect " - "your bitcoins from being stolen by malware infecting your computer.") + - "<br><br><b>" + + "your bitcoins from being stolen by malware infecting your computer.") + + "<br><br><b>" + tr("IMPORTANT: Any previous backups you have made of your wallet file " "should be replaced with the newly generated, encrypted wallet file. " "For security reasons, previous backups of the unencrypted wallet file " - "will become useless as soon as you start using the new, encrypted wallet.") + + "will become useless as soon as you start using the new, encrypted wallet.") + "</b></qt>"); QApplication::quit(); } @@ -221,7 +221,7 @@ bool AskPassphraseDialog::event(QEvent *event) return QWidget::event(event); } -bool AskPassphraseDialog::eventFilter(QObject *, QEvent *event) +bool AskPassphraseDialog::eventFilter(QObject *object, QEvent *event) { /* Detect Caps Lock. * There is no good OS-independent way to check a key state in Qt, but we @@ -244,5 +244,5 @@ bool AskPassphraseDialog::eventFilter(QObject *, QEvent *event) } } } - return false; + return QDialog::eventFilter(object, event); } diff --git a/src/qt/askpassphrasedialog.h b/src/qt/askpassphrasedialog.h index b500ff49bf..338853ce22 100644 --- a/src/qt/askpassphrasedialog.h +++ b/src/qt/askpassphrasedialog.h @@ -39,7 +39,7 @@ private: private slots: void textChanged(); bool event(QEvent *event); - bool eventFilter(QObject *, QEvent *event); + bool eventFilter(QObject *object, QEvent *event); }; #endif // ASKPASSPHRASEDIALOG_H diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h index 66792e00a9..4797c4c882 100644 --- a/src/qt/bitcoinamountfield.h +++ b/src/qt/bitcoinamountfield.h @@ -1,5 +1,5 @@ -#ifndef BITCOINFIELD_H -#define BITCOINFIELD_H +#ifndef BITCOINAMOUNTFIELD_H +#define BITCOINAMOUNTFIELD_H #include <QWidget> @@ -57,4 +57,4 @@ private slots: }; -#endif // BITCOINFIELD_H +#endif // BITCOINAMOUNTFIELD_H diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 0d269ea210..0198a92c05 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -176,6 +176,9 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): // Clicking on "Sign Message" in the receive coins page sends you to the sign message tab connect(receiveCoinsPage, SIGNAL(signMessage(QString)), this, SLOT(gotoSignMessageTab(QString))); + // Install event filter to be able to catch status tip events (QEvent::StatusTip) + this->installEventFilter(this); + gotoOverviewPage(); } @@ -193,31 +196,36 @@ void BitcoinGUI::createActions() QActionGroup *tabGroup = new QActionGroup(this); overviewAction = new QAction(QIcon(":/icons/overview"), tr("&Overview"), this); - overviewAction->setToolTip(tr("Show general overview of wallet")); + overviewAction->setStatusTip(tr("Show general overview of wallet")); + overviewAction->setToolTip(overviewAction->statusTip()); overviewAction->setCheckable(true); overviewAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_1)); tabGroup->addAction(overviewAction); sendCoinsAction = new QAction(QIcon(":/icons/send"), tr("&Send coins"), this); - sendCoinsAction->setToolTip(tr("Send coins to a Bitcoin address")); + sendCoinsAction->setStatusTip(tr("Send coins to a Bitcoin address")); + sendCoinsAction->setToolTip(sendCoinsAction->statusTip()); sendCoinsAction->setCheckable(true); sendCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_2)); tabGroup->addAction(sendCoinsAction); receiveCoinsAction = new QAction(QIcon(":/icons/receiving_addresses"), tr("&Receive coins"), this); - receiveCoinsAction->setToolTip(tr("Show the list of addresses for receiving payments")); + receiveCoinsAction->setStatusTip(tr("Show the list of addresses for receiving payments")); + receiveCoinsAction->setToolTip(receiveCoinsAction->statusTip()); receiveCoinsAction->setCheckable(true); receiveCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_3)); tabGroup->addAction(receiveCoinsAction); historyAction = new QAction(QIcon(":/icons/history"), tr("&Transactions"), this); - historyAction->setToolTip(tr("Browse transaction history")); + historyAction->setStatusTip(tr("Browse transaction history")); + historyAction->setToolTip(historyAction->statusTip()); historyAction->setCheckable(true); historyAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_4)); tabGroup->addAction(historyAction); addressBookAction = new QAction(QIcon(":/icons/address-book"), tr("&Address Book"), this); - addressBookAction->setToolTip(tr("Edit the list of stored addresses and labels")); + addressBookAction->setStatusTip(tr("Edit the list of stored addresses and labels")); + addressBookAction->setToolTip(addressBookAction->statusTip()); addressBookAction->setCheckable(true); addressBookAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_5)); tabGroup->addAction(addressBookAction); @@ -234,33 +242,37 @@ void BitcoinGUI::createActions() connect(addressBookAction, SIGNAL(triggered()), this, SLOT(gotoAddressBookPage())); quitAction = new QAction(QIcon(":/icons/quit"), tr("E&xit"), this); - quitAction->setToolTip(tr("Quit application")); + quitAction->setStatusTip(tr("Quit application")); quitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q)); quitAction->setMenuRole(QAction::QuitRole); aboutAction = new QAction(QIcon(":/icons/bitcoin"), tr("&About Bitcoin"), this); - aboutAction->setToolTip(tr("Show information about Bitcoin")); + aboutAction->setStatusTip(tr("Show information about Bitcoin")); aboutAction->setMenuRole(QAction::AboutRole); aboutQtAction = new QAction(QIcon(":/trolltech/qmessagebox/images/qtlogo-64.png"), tr("About &Qt"), this); - aboutQtAction->setToolTip(tr("Show information about Qt")); + aboutQtAction->setStatusTip(tr("Show information about Qt")); aboutQtAction->setMenuRole(QAction::AboutQtRole); optionsAction = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); - optionsAction->setToolTip(tr("Modify configuration options for Bitcoin")); + optionsAction->setStatusTip(tr("Modify configuration options for Bitcoin")); optionsAction->setMenuRole(QAction::PreferencesRole); toggleHideAction = new QAction(QIcon(":/icons/bitcoin"), tr("&Show / Hide"), this); + toggleHideAction->setStatusTip(tr("Show or hide the main Window")); encryptWalletAction = new QAction(QIcon(":/icons/lock_closed"), tr("&Encrypt Wallet..."), this); - encryptWalletAction->setToolTip(tr("Encrypt or decrypt wallet")); + encryptWalletAction->setStatusTip(tr("Encrypt the private keys that belong to your wallet")); encryptWalletAction->setCheckable(true); backupWalletAction = new QAction(QIcon(":/icons/filesave"), tr("&Backup Wallet..."), this); - backupWalletAction->setToolTip(tr("Backup wallet to another location")); + backupWalletAction->setStatusTip(tr("Backup wallet to another location")); changePassphraseAction = new QAction(QIcon(":/icons/key"), tr("&Change Passphrase..."), this); - changePassphraseAction->setToolTip(tr("Change the passphrase used for wallet encryption")); + changePassphraseAction->setStatusTip(tr("Change the passphrase used for wallet encryption")); signMessageAction = new QAction(QIcon(":/icons/edit"), tr("Sign &message..."), this); + signMessageAction->setStatusTip(tr("Sign messages with your Bitcoin addresses to prove you own them")); verifyMessageAction = new QAction(QIcon(":/icons/transaction_0"), tr("&Verify message..."), this); + verifyMessageAction->setStatusTip(tr("Verify messages to ensure they were signed with specified Bitcoin addresses")); exportAction = new QAction(QIcon(":/icons/export"), tr("&Export..."), this); - exportAction->setToolTip(tr("Export the data in the current tab to a file")); + exportAction->setStatusTip(tr("Export the data in the current tab to a file")); + exportAction->setToolTip(exportAction->statusTip()); openRPCConsoleAction = new QAction(QIcon(":/icons/debugwindow"), tr("&Debug window"), this); - openRPCConsoleAction->setToolTip(tr("Open debugging and diagnostic console")); + openRPCConsoleAction->setStatusTip(tr("Open debugging and diagnostic console")); connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); connect(aboutAction, SIGNAL(triggered()), this, SLOT(aboutClicked())); @@ -338,7 +350,8 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel) #endif if(trayIcon) { - trayIcon->setToolTip(tr("Bitcoin client") + QString(" ") + tr("[testnet]")); + // Just attach " [testnet]" to the existing tooltip + trayIcon->setToolTip(trayIcon->toolTip() + QString(" ") + tr("[testnet]")); trayIcon->setIcon(QIcon(":/icons/toolbar_testnet")); toggleHideAction->setIcon(QIcon(":/icons/toolbar_testnet")); } @@ -356,6 +369,7 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel) // Report errors from network/worker thread connect(clientModel, SIGNAL(error(QString,QString,bool)), this, SLOT(error(QString,QString,bool))); + overviewPage->setClientModel(clientModel); rpcConsole->setClientModel(clientModel); addressBookPage->setOptionsModel(clientModel->getOptionsModel()); receiveCoinsPage->setOptionsModel(clientModel->getOptionsModel()); @@ -372,8 +386,7 @@ void BitcoinGUI::setWalletModel(WalletModel *walletModel) // Put transaction list in tabs transactionView->setModel(walletModel); - - overviewPage->setModel(walletModel); + overviewPage->setWalletModel(walletModel); addressBookPage->setModel(walletModel->getAddressTableModel()); receiveCoinsPage->setModel(walletModel->getAddressTableModel()); sendCoinsPage->setModel(walletModel); @@ -472,8 +485,12 @@ void BitcoinGUI::setNumConnections(int count) void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks) { + // Prevent orphan statusbar messages (e.g. hover Quit in main menu, wait until chain-sync starts -> garbelled text) + statusBar()->clearMessage(); + // don't show / hide progress bar and its label if we have no connection to the network - if (!clientModel || (clientModel->getNumConnections() == 0 && !clientModel->isImporting())) + enum BlockSource blockSource = clientModel ? clientModel->getBlockSource() : BLOCK_SOURCE_NONE; + if (blockSource == BLOCK_SOURCE_NONE || (blockSource == BLOCK_SOURCE_NETWORK && clientModel->getNumConnections() == 0)) { progressBarLabel->setVisible(false); progressBar->setVisible(false); @@ -481,41 +498,41 @@ void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks) return; } - QString strStatusBarWarnings = clientModel->getStatusBarWarnings(); QString tooltip; + QString importText; + switch (blockSource) { + case BLOCK_SOURCE_NONE: + case BLOCK_SOURCE_NETWORK: + importText = tr("Synchronizing with network..."); + break; + case BLOCK_SOURCE_DISK: + importText = tr("Importing blocks from disk..."); + break; + case BLOCK_SOURCE_REINDEX: + importText = tr("Reindexing blocks on disk..."); + } + if(count < nTotalBlocks) { int nRemainingBlocks = nTotalBlocks - count; float nPercentageDone = count / (nTotalBlocks * 0.01f); - if (strStatusBarWarnings.isEmpty()) - { - progressBarLabel->setText(tr(clientModel->isImporting() ? "Importing blocks..." : "Synchronizing with network...")); - progressBarLabel->setVisible(true); - progressBar->setFormat(tr("~%n block(s) remaining", "", nRemainingBlocks)); - progressBar->setMaximum(nTotalBlocks); - progressBar->setValue(count); - progressBar->setVisible(true); - } + progressBarLabel->setText(importText); + progressBarLabel->setVisible(true); + progressBar->setFormat(tr("~%n block(s) remaining", "", nRemainingBlocks)); + progressBar->setMaximum(nTotalBlocks); + progressBar->setValue(count); + progressBar->setVisible(true); - tooltip = tr("Downloaded %1 of %2 blocks of transaction history (%3% done).").arg(count).arg(nTotalBlocks).arg(nPercentageDone, 0, 'f', 2); + tooltip = tr("Processed %1 of %2 blocks of transaction history (%3% done).").arg(count).arg(nTotalBlocks).arg(nPercentageDone, 0, 'f', 2); } else { - if (strStatusBarWarnings.isEmpty()) - progressBarLabel->setVisible(false); - - progressBar->setVisible(false); - tooltip = tr("Downloaded %1 blocks of transaction history.").arg(count); - } + progressBarLabel->setVisible(false); - // Override progressBarLabel text and hide progress bar, when we have warnings to display - if (!strStatusBarWarnings.isEmpty()) - { - progressBarLabel->setText(strStatusBarWarnings); - progressBarLabel->setVisible(true); progressBar->setVisible(false); + tooltip = tr("Processed %1 blocks of transaction history.").arg(count); } QDateTime lastBlockDate = clientModel->getLastBlockDate(); @@ -622,11 +639,9 @@ void BitcoinGUI::closeEvent(QCloseEvent *event) void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee) { - QString strMessage = - tr("This transaction is over the size limit. You can still send it for a fee of %1, " - "which goes to the nodes that process your transaction and helps to support the network. " - "Do you want to pay the fee?").arg( - BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nFeeRequired)); + QString strMessage = tr("This transaction is over the size limit. You can still send it for a fee of %1, " + "which goes to the nodes that process your transaction and helps to support the network. " + "Do you want to pay the fee?").arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nFeeRequired)); QMessageBox::StandardButton retval = QMessageBox::question( this, tr("Confirm transaction fee"), strMessage, QMessageBox::Yes|QMessageBox::Cancel, QMessageBox::Yes); @@ -763,6 +778,18 @@ void BitcoinGUI::dropEvent(QDropEvent *event) event->acceptProposedAction(); } +bool BitcoinGUI::eventFilter(QObject *object, QEvent *event) +{ + // Catch status tip events + if (event->type() == QEvent::StatusTip) + { + // Prevent adding text from setStatusTip(), if we currently use the status bar for displaying other stuff + if (progressBarLabel->isVisible() && progressBar->isVisible()) + return true; + } + return QMainWindow::eventFilter(object, event); +} + void BitcoinGUI::handleURI(QString strURI) { // URI has to be valid diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index c67e887c0f..8b4607d3ed 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -52,6 +52,7 @@ protected: void closeEvent(QCloseEvent *event); void dragEnterEvent(QDragEnterEvent *event); void dropEvent(QDropEvent *event); + bool eventFilter(QObject *object, QEvent *event); private: ClientModel *clientModel; @@ -172,8 +173,8 @@ private slots: /** Show window if hidden, unminimize when minimized, rise when obscured or show if hidden and fToggleHidden is true */ void showNormalIfMinimized(bool fToggleHidden = false); - /** simply calls showNormalIfMinimized(true) for use in SLOT() macro */ + /** Simply calls showNormalIfMinimized(true) for use in SLOT() macro */ void toggleHidden(); }; -#endif +#endif // BITCOINGUI_H diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp index b92a26c4a4..7e8e102ee7 100644 --- a/src/qt/bitcoinstrings.cpp +++ b/src/qt/bitcoinstrings.cpp @@ -27,8 +27,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "Cannot obtain a lock on data directory %s. Bitcoin is probably already " "running."), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Detach block and address databases. Increases shutdown time (default: 0)"), -QT_TRANSLATE_NOOP("bitcoin-core", "" "Error: The transaction was rejected. This might happen if some of the coins " "in your wallet were already spent, such as if you used a copy of wallet.dat " "and coins were spent in the copy but not marked as spent here."), diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 990b364a94..9b7362d757 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -88,9 +88,7 @@ void ClientModel::updateAlert(const QString &hash, int status) } } - // Emit a numBlocksChanged when the status message changes, - // so that the view recomputes and updates the status bar. - emit numBlocksChanged(getNumBlocks(), getNumBlocksOfPeers()); + emit alertsChanged(getStatusBarWarnings()); } bool ClientModel::isTestNet() const @@ -103,9 +101,13 @@ bool ClientModel::inInitialBlockDownload() const return IsInitialBlockDownload(); } -bool ClientModel::isImporting() const +enum BlockSource ClientModel::getBlockSource() const { - return fImporting; + if (fReindex) + return BLOCK_SOURCE_REINDEX; + if (fImporting) + return BLOCK_SOURCE_DISK; + return BLOCK_SOURCE_NETWORK; } int ClientModel::getNumBlocksOfPeers() const @@ -133,6 +135,11 @@ QString ClientModel::formatBuildDate() const return QString::fromStdString(CLIENT_DATE); } +bool ClientModel::isReleaseVersion() const +{ + return CLIENT_VERSION_IS_RELEASE; +} + QString ClientModel::clientName() const { return QString::fromStdString(CLIENT_NAME); diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 926390a07a..7d6401ab25 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -13,6 +13,13 @@ class QDateTime; class QTimer; QT_END_NAMESPACE +enum BlockSource { + BLOCK_SOURCE_NONE, + BLOCK_SOURCE_NETWORK, + BLOCK_SOURCE_DISK, + BLOCK_SOURCE_REINDEX +}; + /** Model for Bitcoin network client. */ class ClientModel : public QObject { @@ -34,7 +41,7 @@ public: //! Return true if core is doing initial block download bool inInitialBlockDownload() const; //! Return true if core is importing blocks - bool isImporting() const; + enum BlockSource getBlockSource() const; //! Return conservative estimate of total number of blocks, or 0 if unknown int getNumBlocksOfPeers() const; //! Return warnings to be displayed in status bar @@ -42,6 +49,7 @@ public: QString formatFullVersion() const; QString formatBuildDate() const; + bool isReleaseVersion() const; QString clientName() const; QString formatClientStartupTime() const; @@ -60,6 +68,7 @@ private: signals: void numConnectionsChanged(int count); void numBlocksChanged(int count, int countOfPeers); + void alertsChanged(const QString &warnings); //! Asynchronous error notification void error(const QString &title, const QString &message, bool modal); diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 1b81b0cdc8..6a13361974 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -87,16 +87,6 @@ </widget> </item> <item> - <widget class="QCheckBox" name="detachDatabases"> - <property name="toolTip"> - <string>Detach block and address databases at shutdown. This means they can be moved to another data directory, but it slows down shutdown. The wallet is always detached.</string> - </property> - <property name="text"> - <string>&Detach databases at shutdown</string> - </property> - </widget> - </item> - <item> <spacer name="verticalSpacer_Main"> <property name="orientation"> <enum>Qt::Vertical</enum> diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui index 98cb63e9ff..4c4dec6c9c 100644 --- a/src/qt/forms/overviewpage.ui +++ b/src/qt/forms/overviewpage.ui @@ -13,282 +13,303 @@ <property name="windowTitle"> <string>Form</string> </property> - <layout class="QHBoxLayout" name="horizontalLayout" stretch="1,1"> + <layout class="QVBoxLayout" name="topLayout"> <item> - <layout class="QVBoxLayout" name="verticalLayout_2"> + <widget class="QLabel" name="labelAlerts"> + <property name="visible"> + <bool>false</bool> + </property> + <property name="styleSheet"> + <string notr="true">background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop:0 #F0D0A0, stop:1 #F8D488); color:#000000 +</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + <property name="margin"> + <number>3</number> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout" stretch="1,1"> <item> - <widget class="QFrame" name="frame"> - <property name="frameShape"> - <enum>QFrame::StyledPanel</enum> - </property> - <property name="frameShadow"> - <enum>QFrame::Raised</enum> - </property> - <layout class="QVBoxLayout" name="verticalLayout_4"> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_4"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QFrame" name="frame"> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <layout class="QVBoxLayout" name="verticalLayout_4"> <item> - <widget class="QLabel" name="label_5"> - <property name="font"> - <font> - <pointsize>11</pointsize> - <weight>75</weight> - <bold>true</bold> - </font> - </property> - <property name="text"> - <string>Wallet</string> - </property> - </widget> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <widget class="QLabel" name="label_5"> + <property name="font"> + <font> + <pointsize>11</pointsize> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>Wallet</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="labelWalletStatus"> + <property name="toolTip"> + <string>The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</string> + </property> + <property name="styleSheet"> + <string notr="true">QLabel { color: red; }</string> + </property> + <property name="text"> + <string notr="true">(out of sync)</string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> </item> <item> - <widget class="QLabel" name="labelWalletStatus"> - <property name="toolTip"> - <string>The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</string> - </property> - <property name="styleSheet"> - <string notr="true">QLabel { color: red; }</string> - </property> - <property name="text"> - <string notr="true">(out of sync)</string> - </property> - <property name="alignment"> - <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - <item> - <layout class="QFormLayout" name="formLayout_2"> - <property name="fieldGrowthPolicy"> - <enum>QFormLayout::AllNonFixedFieldsGrow</enum> - </property> - <property name="horizontalSpacing"> - <number>12</number> - </property> - <property name="verticalSpacing"> - <number>12</number> - </property> - <item row="0" column="0"> - <widget class="QLabel" name="label"> - <property name="text"> - <string>Balance:</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QLabel" name="labelBalance"> - <property name="font"> - <font> - <weight>75</weight> - <bold>true</bold> - </font> - </property> - <property name="cursor"> - <cursorShape>IBeamCursor</cursorShape> - </property> - <property name="toolTip"> - <string>Your current balance</string> - </property> - <property name="text"> - <string notr="true">0 BTC</string> - </property> - <property name="textInteractionFlags"> - <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> - </property> - </widget> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="label_3"> - <property name="text"> - <string>Unconfirmed:</string> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QLabel" name="labelUnconfirmed"> - <property name="font"> - <font> - <weight>75</weight> - <bold>true</bold> - </font> - </property> - <property name="cursor"> - <cursorShape>IBeamCursor</cursorShape> - </property> - <property name="toolTip"> - <string>Total of transactions that have yet to be confirmed, and do not yet count toward the current balance</string> - </property> - <property name="text"> - <string notr="true">0 BTC</string> - </property> - <property name="textInteractionFlags"> - <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> - </property> - </widget> - </item> - <item row="3" column="0"> - <widget class="QLabel" name="label_2"> - <property name="text"> - <string>Number of transactions:</string> - </property> - </widget> - </item> - <item row="3" column="1"> - <widget class="QLabel" name="labelNumTransactions"> - <property name="toolTip"> - <string>Total number of transactions in wallet</string> - </property> - <property name="text"> - <string notr="true">0</string> - </property> - </widget> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="labelImmatureText"> - <property name="text"> - <string>Immature:</string> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="QLabel" name="labelImmature"> - <property name="font"> - <font> - <weight>75</weight> - <bold>true</bold> - </font> - </property> - <property name="toolTip"> - <string>Mined balance that has not yet matured</string> - </property> - <property name="text"> - <string notr="true">0 BTC</string> - </property> - <property name="textInteractionFlags"> - <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> - </property> - </widget> + <layout class="QFormLayout" name="formLayout_2"> + <property name="fieldGrowthPolicy"> + <enum>QFormLayout::AllNonFixedFieldsGrow</enum> + </property> + <property name="horizontalSpacing"> + <number>12</number> + </property> + <property name="verticalSpacing"> + <number>12</number> + </property> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Balance:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLabel" name="labelBalance"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="cursor"> + <cursorShape>IBeamCursor</cursorShape> + </property> + <property name="toolTip"> + <string>Your current balance</string> + </property> + <property name="text"> + <string notr="true">0 BTC</string> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Unconfirmed:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="labelUnconfirmed"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="cursor"> + <cursorShape>IBeamCursor</cursorShape> + </property> + <property name="toolTip"> + <string>Total of transactions that have yet to be confirmed, and do not yet count toward the current balance</string> + </property> + <property name="text"> + <string notr="true">0 BTC</string> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Number of transactions:</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QLabel" name="labelNumTransactions"> + <property name="toolTip"> + <string>Total number of transactions in wallet</string> + </property> + <property name="text"> + <string notr="true">0</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="labelImmatureText"> + <property name="text"> + <string>Immature:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLabel" name="labelImmature"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="toolTip"> + <string>Mined balance that has not yet matured</string> + </property> + <property name="text"> + <string notr="true">0 BTC</string> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + </layout> </item> </layout> - </item> - </layout> - </widget> - </item> - <item> - <spacer name="verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>40</height> - </size> - </property> - </spacer> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> </item> - </layout> - </item> - <item> - <layout class="QVBoxLayout" name="verticalLayout_3"> <item> - <widget class="QFrame" name="frame_2"> - <property name="frameShape"> - <enum>QFrame::StyledPanel</enum> - </property> - <property name="frameShadow"> - <enum>QFrame::Raised</enum> - </property> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_2"> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QFrame" name="frame_2"> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> <item> - <widget class="QLabel" name="label_4"> - <property name="text"> - <string><b>Recent transactions</b></string> - </property> - </widget> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string><b>Recent transactions</b></string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="labelTransactionsStatus"> + <property name="toolTip"> + <string>The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</string> + </property> + <property name="styleSheet"> + <string notr="true">QLabel { color: red; }</string> + </property> + <property name="text"> + <string notr="true">(out of sync)</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> </item> <item> - <widget class="QLabel" name="labelTransactionsStatus"> - <property name="toolTip"> - <string>The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</string> - </property> + <widget class="QListView" name="listTransactions"> <property name="styleSheet"> - <string notr="true">QLabel { color: red; }</string> + <string notr="true">QListView { background: transparent; }</string> </property> - <property name="text"> - <string notr="true">(out of sync)</string> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + <property name="verticalScrollBarPolicy"> + <enum>Qt::ScrollBarAlwaysOff</enum> </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> + <property name="horizontalScrollBarPolicy"> + <enum>Qt::ScrollBarAlwaysOff</enum> </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> + <property name="selectionMode"> + <enum>QAbstractItemView::NoSelection</enum> </property> - </spacer> + </widget> </item> </layout> - </item> - <item> - <widget class="QListView" name="listTransactions"> - <property name="styleSheet"> - <string notr="true">QListView { background: transparent; }</string> - </property> - <property name="frameShape"> - <enum>QFrame::NoFrame</enum> - </property> - <property name="verticalScrollBarPolicy"> - <enum>Qt::ScrollBarAlwaysOff</enum> - </property> - <property name="horizontalScrollBarPolicy"> - <enum>Qt::ScrollBarAlwaysOff</enum> - </property> - <property name="selectionMode"> - <enum>QAbstractItemView::NoSelection</enum> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <spacer name="verticalSpacer_2"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>40</height> - </size> - </property> - </spacer> + </widget> + </item> + <item> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> </item> </layout> </item> diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui index 22a3f8fdc6..28553c5508 100644 --- a/src/qt/forms/sendcoinsentry.ui +++ b/src/qt/forms/sendcoinsentry.ui @@ -52,23 +52,6 @@ <item row="5" column="1"> <widget class="BitcoinAmountField" name="payAmount"/> </item> - <item row="4" column="1"> - <layout class="QHBoxLayout" name="horizontalLayout_2"> - <property name="spacing"> - <number>0</number> - </property> - <item> - <widget class="QValidatedLineEdit" name="addAsLabel"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="toolTip"> - <string>Enter a label for this address to add it to your address book</string> - </property> - </widget> - </item> - </layout> - </item> <item row="4" column="0"> <widget class="QLabel" name="label_4"> <property name="text"> @@ -90,7 +73,7 @@ <item> <widget class="QValidatedLineEdit" name="payTo"> <property name="toolTip"> - <string>The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</string> + <string>The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</string> </property> <property name="maxLength"> <number>34</number> @@ -147,6 +130,13 @@ </item> </layout> </item> + <item row="4" column="1"> + <widget class="QValidatedLineEdit" name="addAsLabel"> + <property name="toolTip"> + <string>Enter a label for this address to add it to your address book</string> + </property> + </widget> + </item> </layout> </widget> <customwidgets> diff --git a/src/qt/macdockiconhandler.h b/src/qt/macdockiconhandler.h index 2092fb26b3..dd85e0c33d 100644 --- a/src/qt/macdockiconhandler.h +++ b/src/qt/macdockiconhandler.h @@ -1,7 +1,7 @@ #ifndef MACDOCKICONHANDLER_H #define MACDOCKICONHANDLER_H -#include <QtCore/QObject> +#include <QObject> class QMenu; class QIcon; diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 3fb30a9518..03dcb0b538 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -128,7 +128,6 @@ void OptionsDialog::setMapper() /* Main */ mapper->addMapping(ui->transactionFee, OptionsModel::Fee); mapper->addMapping(ui->bitcoinAtStartup, OptionsModel::StartAtStartup); - mapper->addMapping(ui->detachDatabases, OptionsModel::DetachDatabases); /* Network */ mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP); diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 1b2f18eab3..e3c9413f1b 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -56,8 +56,6 @@ void OptionsModel::Init() SoftSetArg("-proxy", settings.value("addrProxy").toString().toStdString()); if (settings.contains("nSocksVersion") && settings.value("fUseProxy").toBool()) SoftSetArg("-socks", settings.value("nSocksVersion").toString().toStdString()); - if (settings.contains("detachDB")) - SoftSetBoolArg("-detachdb", settings.value("detachDB").toBool()); if (!language.isEmpty()) SoftSetArg("-lang", language.toStdString()); } @@ -142,8 +140,10 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const return settings.value("fUseUPnP", GetBoolArg("-upnp", true)); case MinimizeOnClose: return QVariant(fMinimizeOnClose); - case ProxyUse: - return settings.value("fUseProxy", false); + case ProxyUse: { + proxyType proxy; + return QVariant(GetProxy(NET_IPV4, proxy)); + } case ProxyIP: { proxyType proxy; if (GetProxy(NET_IPV4, proxy)) @@ -158,16 +158,19 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const else return QVariant(9050); } - case ProxySocksVersion: - return settings.value("nSocksVersion", 5); + case ProxySocksVersion: { + proxyType proxy; + if (GetProxy(NET_IPV4, proxy)) + return QVariant(proxy.second); + else + return QVariant(5); + } case Fee: return QVariant(nTransactionFee); case DisplayUnit: return QVariant(nDisplayUnit); case DisplayAddresses: return QVariant(bDisplayAddresses); - case DetachDatabases: - return QVariant(bitdb.GetDetach()); case Language: return settings.value("language", ""); default: @@ -203,7 +206,7 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in break; case ProxyUse: settings.setValue("fUseProxy", value.toBool()); - ApplyProxySettings(); + successful = ApplyProxySettings(); break; case ProxyIP: { proxyType proxy; @@ -249,12 +252,6 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in bDisplayAddresses = value.toBool(); settings.setValue("bDisplayAddresses", bDisplayAddresses); break; - case DetachDatabases: { - bool fDetachDB = value.toBool(); - bitdb.SetDetach(fDetachDB); - settings.setValue("detachDB", fDetachDB); - } - break; case Language: settings.setValue("language", value); break; diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 2d86a7a9ca..4f893bb44e 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -28,7 +28,6 @@ public: Fee, // qint64 DisplayUnit, // BitcoinUnits::Unit DisplayAddresses, // bool - DetachDatabases, // bool Language, // QString OptionIDRowCount, }; diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 07be9c520a..8f1ff5325e 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -1,6 +1,7 @@ #include "overviewpage.h" #include "ui_overviewpage.h" +#include "clientmodel.h" #include "walletmodel.h" #include "bitcoinunits.h" #include "optionsmodel.h" @@ -92,6 +93,8 @@ public: OverviewPage::OverviewPage(QWidget *parent) : QWidget(parent), ui(new Ui::OverviewPage), + clientModel(0), + walletModel(0), currentBalance(-1), currentUnconfirmedBalance(-1), currentImmatureBalance(-1), @@ -129,7 +132,7 @@ OverviewPage::~OverviewPage() void OverviewPage::setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance) { - int unit = model->getOptionsModel()->getDisplayUnit(); + int unit = walletModel->getOptionsModel()->getDisplayUnit(); currentBalance = balance; currentUnconfirmedBalance = unconfirmedBalance; currentImmatureBalance = immatureBalance; @@ -149,9 +152,20 @@ void OverviewPage::setNumTransactions(int count) ui->labelNumTransactions->setText(QLocale::system().toString(count)); } -void OverviewPage::setModel(WalletModel *model) +void OverviewPage::setClientModel(ClientModel *model) { - this->model = model; + this->clientModel = model; + if(model) + { + // Show warning if this is a prerelease version + connect(model, SIGNAL(alertsChanged(QString)), this, SLOT(updateAlerts(QString))); + updateAlerts(model->getStatusBarWarnings()); + } +} + +void OverviewPage::setWalletModel(WalletModel *model) +{ + this->walletModel = model; if(model && model->getOptionsModel()) { // Set up transaction list @@ -181,18 +195,24 @@ void OverviewPage::setModel(WalletModel *model) void OverviewPage::updateDisplayUnit() { - if(model && model->getOptionsModel()) + if(walletModel && walletModel->getOptionsModel()) { if(currentBalance != -1) setBalance(currentBalance, currentUnconfirmedBalance, currentImmatureBalance); // Update txdelegate->unit with the current unit - txdelegate->unit = model->getOptionsModel()->getDisplayUnit(); + txdelegate->unit = walletModel->getOptionsModel()->getDisplayUnit(); ui->listTransactions->update(); } } +void OverviewPage::updateAlerts(const QString &warnings) +{ + this->ui->labelAlerts->setVisible(!warnings.isEmpty()); + this->ui->labelAlerts->setText(warnings); +} + void OverviewPage::showOutOfSyncWarning(bool fShow) { ui->labelWalletStatus->setVisible(fShow); diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index 00048cc8f8..bb32a0c33f 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -10,6 +10,7 @@ QT_END_NAMESPACE namespace Ui { class OverviewPage; } +class ClientModel; class WalletModel; class TxViewDelegate; class TransactionFilterProxy; @@ -23,7 +24,8 @@ public: explicit OverviewPage(QWidget *parent = 0); ~OverviewPage(); - void setModel(WalletModel *model); + void setClientModel(ClientModel *clientModel); + void setWalletModel(WalletModel *walletModel); void showOutOfSyncWarning(bool fShow); public slots: @@ -35,7 +37,8 @@ signals: private: Ui::OverviewPage *ui; - WalletModel *model; + ClientModel *clientModel; + WalletModel *walletModel; qint64 currentBalance; qint64 currentUnconfirmedBalance; qint64 currentImmatureBalance; @@ -46,6 +49,7 @@ private: private slots: void updateDisplayUnit(); void handleTransactionClicked(const QModelIndex &index); + void updateAlerts(const QString &warnings); }; #endif // OVERVIEWPAGE_H diff --git a/src/qt/qtipcserver.cpp b/src/qt/qtipcserver.cpp index 74f44fac57..b26c37581c 100644 --- a/src/qt/qtipcserver.cpp +++ b/src/qt/qtipcserver.cpp @@ -26,7 +26,7 @@ using namespace boost; using namespace boost::interprocess; using namespace boost::posix_time; -#ifdef MAC_OSX +#if defined MAC_OSX || defined __FreeBSD__ // URI handling not implemented on OSX yet void ipcScanRelay(int argc, char *argv[]) { } diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index ca2c615333..0c1547bd8e 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -148,7 +148,7 @@ void SendCoinsDialog::on_sendButton_clicked() break; case WalletModel::TransactionCreationFailed: QMessageBox::warning(this, tr("Send Coins"), - tr("Error: Transaction creation failed."), + tr("Error: Transaction creation failed!"), QMessageBox::Ok, QMessageBox::Ok); break; case WalletModel::TransactionCommitFailed: diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index fd321ce280..b0687d5399 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -81,5 +81,4 @@ public slots: friend class TransactionTablePriv; }; -#endif - +#endif // TRANSACTIONTABLEMODEL_H diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 0270b5f102..0591f35392 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -198,7 +198,7 @@ Value getwork(const Array& params, bool fHelp) Value getblocktemplate(const Array& params, bool fHelp) { - if (fHelp || params.size() != 1) + if (fHelp || params.size() > 1) throw runtime_error( "getblocktemplate [params]\n" "Returns data needed to construct a block to work on:\n" diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index 4714bfdd20..9531b12678 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -18,6 +18,39 @@ using namespace boost; using namespace boost::assign; using namespace json_spirit; +// +// Utilities: convert hex-encoded Values +// (throws error if not hex). +// +uint256 ParseHashV(const Value& v, string strName) +{ + string strHex; + if (v.type() == str_type) + strHex = v.get_str(); + if (!IsHex(strHex)) // Note: IsHex("") is false + throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); + uint256 result; + result.SetHex(strHex); + return result; +} +uint256 ParseHashO(const Object& o, string strKey) +{ + return ParseHashV(find_value(o, strKey), strKey); +} +vector<unsigned char> ParseHexV(const Value& v, string strName) +{ + string strHex; + if (v.type() == str_type) + strHex = v.get_str(); + if (!IsHex(strHex)) + throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); + return ParseHex(strHex); +} +vector<unsigned char> ParseHexO(const Object& o, string strKey) +{ + return ParseHexV(find_value(o, strKey), strKey); +} + void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out) { txnouttype type; @@ -109,8 +142,7 @@ Value getrawtransaction(const Array& params, bool fHelp) "If verbose is non-zero, returns an Object\n" "with information about <txid>."); - uint256 hash; - hash.SetHex(params[0].get_str()); + uint256 hash = ParseHashV(params[0], "parameter 1"); bool fVerbose = false; if (params.size() > 1) @@ -178,10 +210,10 @@ Value listunspent(const Array& params, bool fHelp) if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) continue; - if(setAddress.size()) + if (setAddress.size()) { CTxDestination address; - if(!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) + if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) continue; if (!setAddress.count(address)) @@ -194,6 +226,17 @@ Value listunspent(const Array& params, bool fHelp) entry.push_back(Pair("txid", out.tx->GetHash().GetHex())); entry.push_back(Pair("vout", out.i)); entry.push_back(Pair("scriptPubKey", HexStr(pk.begin(), pk.end()))); + if (pk.IsPayToScriptHash()) + { + CTxDestination address; + if (ExtractDestination(pk, address)) + { + const CScriptID& hash = boost::get<const CScriptID&>(address); + CScript redeemScript; + if (pwalletMain->GetCScript(hash, redeemScript)) + entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()))); + } + } entry.push_back(Pair("amount",ValueFromAmount(nValue))); entry.push_back(Pair("confirmations",out.nDepth)); results.push_back(entry); @@ -221,16 +264,11 @@ Value createrawtransaction(const Array& params, bool fHelp) CTransaction rawTx; - BOOST_FOREACH(Value& input, inputs) + BOOST_FOREACH(const Value& input, inputs) { const Object& o = input.get_obj(); - const Value& txid_v = find_value(o, "txid"); - if (txid_v.type() != str_type) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing txid key"); - string txid = txid_v.get_str(); - if (!IsHex(txid)) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid"); + uint256 txid = ParseHashO(o, "txid"); const Value& vout_v = find_value(o, "vout"); if (vout_v.type() != int_type) @@ -239,7 +277,7 @@ Value createrawtransaction(const Array& params, bool fHelp) if (nOutput < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); - CTxIn in(COutPoint(uint256(txid), nOutput)); + CTxIn in(COutPoint(txid, nOutput)); rawTx.vin.push_back(in); } @@ -274,9 +312,7 @@ Value decoderawtransaction(const Array& params, bool fHelp) "decoderawtransaction <hex string>\n" "Return a JSON object representing the serialized, hex-encoded transaction."); - RPCTypeCheck(params, list_of(str_type)); - - vector<unsigned char> txData(ParseHex(params[0].get_str())); + vector<unsigned char> txData(ParseHexV(params[0], "argument")); CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); CTransaction tx; try { @@ -296,7 +332,7 @@ Value signrawtransaction(const Array& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 4) throw runtime_error( - "signrawtransaction <hex string> [{\"txid\":txid,\"vout\":n,\"scriptPubKey\":hex},...] [<privatekey1>,...] [sighashtype=\"ALL\"]\n" + "signrawtransaction <hex string> [{\"txid\":txid,\"vout\":n,\"scriptPubKey\":hex,\"redeemScript\":hex},...] [<privatekey1>,...] [sighashtype=\"ALL\"]\n" "Sign inputs for raw transaction (serialized, hex-encoded).\n" "Second optional argument (may be null) is an array of previous transaction outputs that\n" "this transaction depends on but may not yet be in the block chain.\n" @@ -311,7 +347,7 @@ Value signrawtransaction(const Array& params, bool fHelp) RPCTypeCheck(params, list_of(str_type)(array_type)(array_type)(str_type), true); - vector<unsigned char> txData(ParseHex(params[0].get_str())); + vector<unsigned char> txData(ParseHexV(params[0], "argument 1")); CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); vector<CTransaction> txVariants; while (!ssData.empty()) @@ -352,6 +388,28 @@ Value signrawtransaction(const Array& params, bool fHelp) view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long } + bool fGivenKeys = false; + CBasicKeyStore tempKeystore; + if (params.size() > 2 && params[2].type() != null_type) + { + fGivenKeys = true; + Array keys = params[2].get_array(); + BOOST_FOREACH(Value k, keys) + { + CBitcoinSecret vchSecret; + bool fGood = vchSecret.SetString(k.get_str()); + if (!fGood) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); + CKey key; + bool fCompressed; + CSecret secret = vchSecret.GetSecret(fCompressed); + key.SetSecret(secret, fCompressed); + tempKeystore.AddKey(key); + } + } + else + EnsureWalletIsUnlocked(); + // Add previous txouts given in the RPC call: if (params.size() > 1 && params[1].type() != null_type) { @@ -363,22 +421,15 @@ Value signrawtransaction(const Array& params, bool fHelp) Object prevOut = p.get_obj(); - RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type)); + RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type)("redeemScript",str_type)); - string txidHex = find_value(prevOut, "txid").get_str(); - if (!IsHex(txidHex)) - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "txid must be hexadecimal"); - uint256 txid; - txid.SetHex(txidHex); + uint256 txid = ParseHashO(prevOut, "txid"); int nOut = find_value(prevOut, "vout").get_int(); if (nOut < 0) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive"); - string pkHex = find_value(prevOut, "scriptPubKey").get_str(); - if (!IsHex(pkHex)) - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "scriptPubKey must be hexadecimal"); - vector<unsigned char> pkData(ParseHex(pkHex)); + vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey")); CScript scriptPubKey(pkData.begin(), pkData.end()); CCoins coins; @@ -391,33 +442,23 @@ Value signrawtransaction(const Array& params, bool fHelp) } // what todo if txid is known, but the actual output isn't? } + if ((unsigned int)nOut >= coins.vout.size()) + coins.vout.resize(nOut+1); coins.vout[nOut].scriptPubKey = scriptPubKey; coins.vout[nOut].nValue = 0; // we don't know the actual output value view.SetCoins(txid, coins); - } - } - bool fGivenKeys = false; - CBasicKeyStore tempKeystore; - if (params.size() > 2 && params[2].type() != null_type) - { - fGivenKeys = true; - Array keys = params[2].get_array(); - BOOST_FOREACH(Value k, keys) - { - CBitcoinSecret vchSecret; - bool fGood = vchSecret.SetString(k.get_str()); - if (!fGood) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,"Invalid private key"); - CKey key; - bool fCompressed; - CSecret secret = vchSecret.GetSecret(fCompressed); - key.SetSecret(secret, fCompressed); - tempKeystore.AddKey(key); + // if redeemScript given and not using the local wallet (private keys + // given), add redeemScript to the tempKeystore so it can be signed: + Value v = find_value(prevOut, "redeemScript"); + if (fGivenKeys && scriptPubKey.IsPayToScriptHash() && !(v == Value::null)) + { + vector<unsigned char> rsData(ParseHexV(v, "redeemScript")); + CScript redeemScript(rsData.begin(), rsData.end()); + tempKeystore.AddCScript(redeemScript); + } } } - else - EnsureWalletIsUnlocked(); const CKeyStore& keystore = (fGivenKeys ? tempKeystore : *pwalletMain); @@ -464,7 +505,7 @@ Value signrawtransaction(const Array& params, bool fHelp) { txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig); } - if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, true, true, 0)) + if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, 0)) fComplete = false; } @@ -484,10 +525,8 @@ Value sendrawtransaction(const Array& params, bool fHelp) "sendrawtransaction <hex string>\n" "Submits raw transaction (serialized, hex-encoded) to local node and network."); - RPCTypeCheck(params, list_of(str_type)); - // parse hex string from parameter - vector<unsigned char> txData(ParseHex(params[0].get_str())); + vector<unsigned char> txData(ParseHexV(params[0], "parameter")); CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); CTransaction tx; diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 6f8728e979..29b3298b99 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -702,22 +702,13 @@ Value sendmany(const Array& params, bool fHelp) return wtx.GetHash().GetHex(); } -Value addmultisigaddress(const Array& params, bool fHelp) +// +// Used by addmultisigaddress / createmultisig: +// +static CScript _createmultisig(const Array& params) { - if (fHelp || params.size() < 2 || params.size() > 3) - { - string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n" - "Add a nrequired-to-sign multisignature address to the wallet\"\n" - "each key is a Bitcoin address or hex-encoded public key\n" - "If [account] is specified, assign address to [account]."; - throw runtime_error(msg); - } - int nRequired = params[0].get_int(); const Array& keys = params[1].get_array(); - string strAccount; - if (params.size() > 2) - strAccount = AccountFromValue(params[2]); // Gather public keys if (nRequired < 1) @@ -760,10 +751,28 @@ Value addmultisigaddress(const Array& params, bool fHelp) throw runtime_error(" Invalid public key: "+ks); } } + CScript result; + result.SetMultisig(nRequired, pubkeys); + return result; +} + +Value addmultisigaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 3) + { + string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n" + "Add a nrequired-to-sign multisignature address to the wallet\"\n" + "each key is a Bitcoin address or hex-encoded public key\n" + "If [account] is specified, assign address to [account]."; + throw runtime_error(msg); + } + + string strAccount; + if (params.size() > 2) + strAccount = AccountFromValue(params[2]); // Construct using pay-to-script-hash: - CScript inner; - inner.SetMultisig(nRequired, pubkeys); + CScript inner = _createmultisig(params); CScriptID innerID = inner.GetID(); pwalletMain->AddCScript(inner); @@ -771,6 +780,30 @@ Value addmultisigaddress(const Array& params, bool fHelp) return CBitcoinAddress(innerID).ToString(); } +Value createmultisig(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 2) + { + string msg = "createmultisig <nrequired> <'[\"key\",\"key\"]'>\n" + "Creates a multi-signature address and returns a json object\n" + "with keys:\n" + "address : bitcoin address\n" + "redeemScript : hex-encoded redemption script"; + throw runtime_error(msg); + } + + // Construct using pay-to-script-hash: + CScript inner = _createmultisig(params); + CScriptID innerID = inner.GetID(); + CBitcoinAddress address(innerID); + + Object result; + result.push_back(Pair("address", address.ToString())); + result.push_back(Pair("redeemScript", HexStr(inner.begin(), inner.end()))); + + return result; +} + struct tallyitem { @@ -1396,7 +1429,7 @@ Value encryptwallet(const Array& params, bool fHelp) // slack space in .dat files; that is bad if the old data is // unencrypted private keys. So: StartShutdown(); - return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup."; + return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup."; } class DescribeAddressVisitor : public boost::static_visitor<Object> diff --git a/src/script.cpp b/src/script.cpp index 36d208c247..a40ae5b1db 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -314,7 +314,7 @@ bool IsCanonicalSignature(const valtype &vchSig) { return true; } -bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, bool fStrictEncodings, int nHashType) +bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType) { CAutoBN_CTX pctx; CScript::const_iterator pc = script.begin(); @@ -327,7 +327,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co if (script.size() > 10000) return false; int nOpCount = 0; - + bool fStrictEncodings = flags & SCRIPT_VERIFY_STRICTENC; try { @@ -1637,14 +1637,14 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vecto } bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, - bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType) + unsigned int flags, int nHashType) { vector<vector<unsigned char> > stack, stackCopy; - if (!EvalScript(stack, scriptSig, txTo, nIn, fStrictEncodings, nHashType)) + if (!EvalScript(stack, scriptSig, txTo, nIn, flags, nHashType)) return false; - if (fValidatePayToScriptHash) + if (flags & SCRIPT_VERIFY_P2SH) stackCopy = stack; - if (!EvalScript(stack, scriptPubKey, txTo, nIn, fStrictEncodings, nHashType)) + if (!EvalScript(stack, scriptPubKey, txTo, nIn, flags, nHashType)) return false; if (stack.empty()) return false; @@ -1653,7 +1653,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C return false; // Additional validation for spend-to-script-hash transactions: - if (fValidatePayToScriptHash && scriptPubKey.IsPayToScriptHash()) + if ((flags & SCRIPT_VERIFY_P2SH) && scriptPubKey.IsPayToScriptHash()) { if (!scriptSig.IsPushOnly()) // scriptSig must be literals-only return false; // or validation fails @@ -1662,7 +1662,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end()); popstack(stackCopy); - if (!EvalScript(stackCopy, pubKey2, txTo, nIn, fStrictEncodings, nHashType)) + if (!EvalScript(stackCopy, pubKey2, txTo, nIn, flags, nHashType)) return false; if (stackCopy.empty()) return false; @@ -1705,7 +1705,7 @@ bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CTransa } // Test solution - return VerifyScript(txin.scriptSig, fromPubKey, txTo, nIn, true, true, 0); + return VerifyScript(txin.scriptSig, fromPubKey, txTo, nIn, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, 0); } bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType) @@ -1718,7 +1718,7 @@ bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTrans return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, nHashType); } -bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType) +bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType) { assert(nIn < txTo.vin.size()); const CTxIn& txin = txTo.vin[nIn]; @@ -1726,7 +1726,7 @@ bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned in return false; const CTxOut& txout = txFrom.vout[txin.prevout.n]; - return VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, fValidatePayToScriptHash, fStrictEncodings, nHashType); + return VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, flags, nHashType); } static CScript PushAll(const vector<valtype>& values) @@ -1844,9 +1844,9 @@ CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsign Solver(scriptPubKey, txType, vSolutions); vector<valtype> stack1; - EvalScript(stack1, scriptSig1, CTransaction(), 0, true, 0); + EvalScript(stack1, scriptSig1, CTransaction(), 0, SCRIPT_VERIFY_STRICTENC, 0); vector<valtype> stack2; - EvalScript(stack2, scriptSig2, CTransaction(), 0, true, 0); + EvalScript(stack2, scriptSig2, CTransaction(), 0, SCRIPT_VERIFY_STRICTENC, 0); return CombineSignatures(scriptPubKey, txTo, nIn, txType, vSolutions, stack1, stack2); } diff --git a/src/script.h b/src/script.h index f9df587ca3..7b0643f70a 100644 --- a/src/script.h +++ b/src/script.h @@ -26,6 +26,13 @@ enum SIGHASH_ANYONECANPAY = 0x80, }; +/** Script verification flags */ +enum +{ + SCRIPT_VERIFY_NONE = 0, + SCRIPT_VERIFY_P2SH = (1U << 0), + SCRIPT_VERIFY_STRICTENC = (1U << 1), +}; enum txnouttype { @@ -656,7 +663,7 @@ public: bool IsCanonicalPubKey(const std::vector<unsigned char> &vchPubKey); bool IsCanonicalSignature(const std::vector<unsigned char> &vchSig); -bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, bool fStrictEncodings, int nHashType); +bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet); int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned char> >& vSolutions); bool IsStandard(const CScript& scriptPubKey); @@ -667,8 +674,8 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std:: bool SignSignature(const CKeyStore& keystore, const CScript& fromPubKey, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, - bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType); -bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType); + unsigned int flags, int nHashType); +bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); // Given two sets of signatures for scriptPubKey, possibly with OP_0 placeholders, // combine them intelligently and return the result. diff --git a/src/serialize.h b/src/serialize.h index 549e46cbc3..9e14666fac 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -1225,4 +1225,148 @@ public: } }; +/** Wrapper around a FILE* that implements a ring buffer to + * deserialize from. It guarantees the ability to rewind + * a given number of bytes. */ +class CBufferedFile +{ +private: + FILE *src; // source file + uint64 nSrcPos; // how many bytes have been read from source + uint64 nReadPos; // how many bytes have been read from this + uint64 nReadLimit; // up to which position we're allowed to read + uint64 nRewind; // how many bytes we guarantee to rewind + std::vector<char> vchBuf; // the buffer + + short state; + short exceptmask; + +protected: + void setstate(short bits, const char *psz) { + state |= bits; + if (state & exceptmask) + throw std::ios_base::failure(psz); + } + + // read data from the source to fill the buffer + bool Fill() { + unsigned int pos = nSrcPos % vchBuf.size(); + unsigned int readNow = vchBuf.size() - pos; + unsigned int nAvail = vchBuf.size() - (nSrcPos - nReadPos) - nRewind; + if (nAvail < readNow) + readNow = nAvail; + if (readNow == 0) + return false; + size_t read = fread((void*)&vchBuf[pos], 1, readNow, src); + if (read == 0) { + setstate(std::ios_base::failbit, feof(src) ? "CBufferedFile::Fill : end of file" : "CBufferedFile::Fill : fread failed"); + return false; + } else { + nSrcPos += read; + return true; + } + } + +public: + int nType; + int nVersion; + + CBufferedFile(FILE *fileIn, uint64 nBufSize, uint64 nRewindIn, int nTypeIn, int nVersionIn) : + src(fileIn), nSrcPos(0), nReadPos(0), nReadLimit((uint64)(-1)), nRewind(nRewindIn), vchBuf(nBufSize, 0), + state(0), exceptmask(std::ios_base::badbit | std::ios_base::failbit), nType(nTypeIn), nVersion(nVersionIn) { + } + + // check whether no error occurred + bool good() const { + return state == 0; + } + + // check whether we're at the end of the source file + bool eof() const { + return nReadPos == nSrcPos && feof(src); + } + + // read a number of bytes + CBufferedFile& read(char *pch, size_t nSize) { + if (nSize + nReadPos > nReadLimit) + throw std::ios_base::failure("Read attempted past buffer limit"); + if (nSize + nRewind > vchBuf.size()) + throw std::ios_base::failure("Read larger than buffer size"); + while (nSize > 0) { + if (nReadPos == nSrcPos) + Fill(); + unsigned int pos = nReadPos % vchBuf.size(); + size_t nNow = nSize; + if (nNow + pos > vchBuf.size()) + nNow = vchBuf.size() - pos; + if (nNow + nReadPos > nSrcPos) + nNow = nSrcPos - nReadPos; + memcpy(pch, &vchBuf[pos], nNow); + nReadPos += nNow; + pch += nNow; + nSize -= nNow; + } + return (*this); + } + + // return the current reading position + uint64 GetPos() { + return nReadPos; + } + + // rewind to a given reading position + bool SetPos(uint64 nPos) { + nReadPos = nPos; + if (nReadPos + nRewind < nSrcPos) { + nReadPos = nSrcPos - nRewind; + return false; + } else if (nReadPos > nSrcPos) { + nReadPos = nSrcPos; + return false; + } else { + return true; + } + } + + bool Seek(uint64 nPos) { + long nLongPos = nPos; + if (nPos != (uint64)nLongPos) + return false; + if (fseek(src, nLongPos, SEEK_SET)) + return false; + nLongPos = ftell(src); + nSrcPos = nLongPos; + nReadPos = nLongPos; + state = 0; + return true; + } + + // prevent reading beyond a certain position + // no argument removes the limit + bool SetLimit(uint64 nPos = (uint64)(-1)) { + if (nPos < nReadPos) + return false; + nReadLimit = nPos; + return true; + } + + template<typename T> + CBufferedFile& operator>>(T& obj) { + // Unserialize from this stream + ::Unserialize(*this, obj, nType, nVersion); + return (*this); + } + + // search for a given byte in the stream, and remain positioned on it + void FindByte(char ch) { + while (true) { + if (nReadPos == nSrcPos) + Fill(); + if (vchBuf[nReadPos % vchBuf.size()] == ch) + break; + nReadPos++; + } + } +}; + #endif diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp index 0db33dcd05..b1e98f65ed 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -230,6 +230,7 @@ BOOST_AUTO_TEST_CASE(DoS_checkSig) key.MakeNewKey(true); CBasicKeyStore keystore; keystore.AddKey(key); + unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC; // 100 orphan transactions: static const int NPREV=100; @@ -277,7 +278,7 @@ BOOST_AUTO_TEST_CASE(DoS_checkSig) mst1 = boost::posix_time::microsec_clock::local_time(); for (unsigned int i = 0; i < 5; i++) for (unsigned int j = 0; j < tx.vin.size(); j++) - BOOST_CHECK(VerifySignature(CCoins(orphans[j], MEMPOOL_HEIGHT), tx, j, true, true, SIGHASH_ALL)); + BOOST_CHECK(VerifySignature(CCoins(orphans[j], MEMPOOL_HEIGHT), tx, j, flags, SIGHASH_ALL)); mst2 = boost::posix_time::microsec_clock::local_time(); msdiff = mst2 - mst1; long nManyValidate = msdiff.total_milliseconds(); @@ -288,13 +289,13 @@ BOOST_AUTO_TEST_CASE(DoS_checkSig) // Empty a signature, validation should fail: CScript save = tx.vin[0].scriptSig; tx.vin[0].scriptSig = CScript(); - BOOST_CHECK(!VerifySignature(CCoins(orphans[0], MEMPOOL_HEIGHT), tx, 0, true, true, SIGHASH_ALL)); + BOOST_CHECK(!VerifySignature(CCoins(orphans[0], MEMPOOL_HEIGHT), tx, 0, flags, SIGHASH_ALL)); tx.vin[0].scriptSig = save; // Swap signatures, validation should fail: std::swap(tx.vin[0].scriptSig, tx.vin[1].scriptSig); - BOOST_CHECK(!VerifySignature(CCoins(orphans[0], MEMPOOL_HEIGHT), tx, 0, true, true, SIGHASH_ALL)); - BOOST_CHECK(!VerifySignature(CCoins(orphans[1], MEMPOOL_HEIGHT), tx, 1, true, true, SIGHASH_ALL)); + BOOST_CHECK(!VerifySignature(CCoins(orphans[0], MEMPOOL_HEIGHT), tx, 0, flags, SIGHASH_ALL)); + BOOST_CHECK(!VerifySignature(CCoins(orphans[1], MEMPOOL_HEIGHT), tx, 1, flags, SIGHASH_ALL)); std::swap(tx.vin[0].scriptSig, tx.vin[1].scriptSig); // Exercise -maxsigcachesize code: @@ -304,7 +305,7 @@ BOOST_AUTO_TEST_CASE(DoS_checkSig) BOOST_CHECK(SignSignature(keystore, orphans[0], tx, 0)); BOOST_CHECK(tx.vin[0].scriptSig != oldSig); for (unsigned int j = 0; j < tx.vin.size(); j++) - BOOST_CHECK(VerifySignature(CCoins(orphans[j], MEMPOOL_HEIGHT), tx, j, true, true, SIGHASH_ALL)); + BOOST_CHECK(VerifySignature(CCoins(orphans[j], MEMPOOL_HEIGHT), tx, j, flags, SIGHASH_ALL)); mapArgs.erase("-maxsigcachesize"); LimitOrphanTxSize(0); diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index aa6feb7201..7297bb9a75 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -20,8 +20,6 @@ using namespace boost::assign; typedef vector<unsigned char> valtype; extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); -extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, - bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType); BOOST_AUTO_TEST_SUITE(multisig_tests) @@ -44,6 +42,8 @@ sign_multisig(CScript scriptPubKey, vector<CKey> keys, CTransaction transaction, BOOST_AUTO_TEST_CASE(multisig_verify) { + unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC; + CKey key[4]; for (int i = 0; i < 4; i++) key[i].MakeNewKey(true); @@ -80,19 +80,19 @@ BOOST_AUTO_TEST_CASE(multisig_verify) keys.clear(); keys += key[0],key[1]; // magic operator+= from boost.assign s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK(VerifyScript(s, a_and_b, txTo[0], 0, true, true, 0)); + BOOST_CHECK(VerifyScript(s, a_and_b, txTo[0], 0, flags, 0)); for (int i = 0; i < 4; i++) { keys.clear(); keys += key[i]; s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, true, true, 0), strprintf("a&b 1: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, flags, 0), strprintf("a&b 1: %d", i)); keys.clear(); keys += key[1],key[i]; s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, true, true, 0), strprintf("a&b 2: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, flags, 0), strprintf("a&b 2: %d", i)); } // Test a OR b: @@ -102,16 +102,16 @@ BOOST_AUTO_TEST_CASE(multisig_verify) keys += key[i]; s = sign_multisig(a_or_b, keys, txTo[1], 0); if (i == 0 || i == 1) - BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, txTo[1], 0, true, true, 0), strprintf("a|b: %d", i)); + BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, txTo[1], 0, flags, 0), strprintf("a|b: %d", i)); else - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, txTo[1], 0, true, true, 0), strprintf("a|b: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, txTo[1], 0, flags, 0), strprintf("a|b: %d", i)); } s.clear(); s << OP_0 << OP_0; - BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, true, true, 0)); + BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, flags, 0)); s.clear(); s << OP_0 << OP_1; - BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, true, true, 0)); + BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, flags, 0)); for (int i = 0; i < 4; i++) @@ -121,9 +121,9 @@ BOOST_AUTO_TEST_CASE(multisig_verify) keys += key[i],key[j]; s = sign_multisig(escrow, keys, txTo[2], 0); if (i < j && i < 3 && j < 3) - BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, txTo[2], 0, true, true, 0), strprintf("escrow 1: %d %d", i, j)); + BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, txTo[2], 0, flags, 0), strprintf("escrow 1: %d %d", i, j)); else - BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, txTo[2], 0, true, true, 0), strprintf("escrow 2: %d %d", i, j)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, txTo[2], 0, flags, 0), strprintf("escrow 2: %d %d", i, j)); } } diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index eb820ade6d..f8fe443b87 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -1,5 +1,6 @@ -#include <boost/test/unit_test.hpp> +#include <boost/algorithm/string.hpp> #include <boost/foreach.hpp> +#include <boost/test/unit_test.hpp> #include "base58.h" #include "util.h" @@ -22,14 +23,7 @@ createArgs(int nRequired, const char* address1=NULL, const char* address2=NULL) return result; } -// This can be removed this when addmultisigaddress is enabled on main net: -struct TestNetFixture -{ - TestNetFixture() { fTestNet = true; } - ~TestNetFixture() { fTestNet = false; } -}; - -BOOST_FIXTURE_TEST_CASE(rpc_addmultisig, TestNetFixture) +BOOST_AUTO_TEST_CASE(rpc_addmultisig) { rpcfn_type addmultisig = tableRPC["addmultisigaddress"]->actor; @@ -66,4 +60,91 @@ BOOST_FIXTURE_TEST_CASE(rpc_addmultisig, TestNetFixture) BOOST_CHECK_THROW(addmultisig(createArgs(2, short2.c_str()), false), runtime_error); } +static Value CallRPC(string args) +{ + vector<string> vArgs; + boost::split(vArgs, args, boost::is_any_of(" \t")); + string strMethod = vArgs[0]; + vArgs.erase(vArgs.begin()); + Array params = RPCConvertValues(strMethod, vArgs); + + rpcfn_type method = tableRPC[strMethod]->actor; + try { + Value result = (*method)(params, false); + return result; + } + catch (Object& objError) + { + throw runtime_error(find_value(objError, "message").get_str()); + } +} + +BOOST_AUTO_TEST_CASE(rpc_rawparams) +{ + // Test raw transaction API argument handling + Value r; + + BOOST_CHECK_THROW(CallRPC("getrawtransaction"), runtime_error); + BOOST_CHECK_THROW(CallRPC("getrawtransaction not_hex"), runtime_error); + BOOST_CHECK_THROW(CallRPC("getrawtransaction a3b807410df0b60fcb9736768df5823938b2f838694939ba45f3c0a1bff150ed not_int"), runtime_error); + + BOOST_CHECK_NO_THROW(CallRPC("listunspent")); + BOOST_CHECK_THROW(CallRPC("listunspent string"), runtime_error); + BOOST_CHECK_THROW(CallRPC("listunspent 0 string"), runtime_error); + BOOST_CHECK_THROW(CallRPC("listunspent 0 1 not_array"), runtime_error); + BOOST_CHECK_NO_THROW(r=CallRPC("listunspent 0 1 []")); + BOOST_CHECK_THROW(r=CallRPC("listunspent 0 1 [] extra"), runtime_error); + BOOST_CHECK(r.get_array().empty()); + + BOOST_CHECK_THROW(CallRPC("createrawtransaction"), runtime_error); + BOOST_CHECK_THROW(CallRPC("createrawtransaction null null"), runtime_error); + BOOST_CHECK_THROW(CallRPC("createrawtransaction not_array"), runtime_error); + BOOST_CHECK_THROW(CallRPC("createrawtransaction [] []"), runtime_error); + BOOST_CHECK_THROW(CallRPC("createrawtransaction {} {}"), runtime_error); + BOOST_CHECK_NO_THROW(CallRPC("createrawtransaction [] {}")); + BOOST_CHECK_THROW(CallRPC("createrawtransaction [] {} extra"), runtime_error); + + BOOST_CHECK_THROW(CallRPC("decoderawtransaction"), runtime_error); + BOOST_CHECK_THROW(CallRPC("decoderawtransaction null"), runtime_error); + BOOST_CHECK_THROW(CallRPC("decoderawtransaction DEADBEEF"), runtime_error); + string rawtx = "0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000"; + BOOST_CHECK_NO_THROW(r = CallRPC(string("decoderawtransaction ")+rawtx)); + BOOST_CHECK_EQUAL(find_value(r.get_obj(), "version").get_int(), 1); + BOOST_CHECK_EQUAL(find_value(r.get_obj(), "locktime").get_int(), 0); + BOOST_CHECK_THROW(r = CallRPC(string("decoderawtransaction ")+rawtx+" extra"), runtime_error); + + BOOST_CHECK_THROW(CallRPC("signrawtransaction"), runtime_error); + BOOST_CHECK_THROW(CallRPC("signrawtransaction null"), runtime_error); + BOOST_CHECK_THROW(CallRPC("signrawtransaction ff00"), runtime_error); + BOOST_CHECK_NO_THROW(CallRPC(string("signrawtransaction ")+rawtx)); + BOOST_CHECK_NO_THROW(CallRPC(string("signrawtransaction ")+rawtx+" null null NONE|ANYONECANPAY")); + BOOST_CHECK_NO_THROW(CallRPC(string("signrawtransaction ")+rawtx+" [] [] NONE|ANYONECANPAY")); + BOOST_CHECK_THROW(CallRPC(string("signrawtransaction ")+rawtx+" null null badenum"), runtime_error); + + // Only check failure cases for sendrawtransaction, there's no network to send to... + BOOST_CHECK_THROW(CallRPC("sendrawtransaction"), runtime_error); + BOOST_CHECK_THROW(CallRPC("sendrawtransaction null"), runtime_error); + BOOST_CHECK_THROW(CallRPC("sendrawtransaction DEADBEEF"), runtime_error); + BOOST_CHECK_THROW(CallRPC(string("sendrawtransaction ")+rawtx+" extra"), runtime_error); +} + +BOOST_AUTO_TEST_CASE(rpc_rawsign) +{ + Value r; + // input is a 1-of-2 multisig (so is output): + string prevout = + "[{\"txid\":\"b4cc287e58f87cdae59417329f710f3ecd75a4ee1d2872b7248f50977c8493f3\"," + "\"vout\":1,\"scriptPubKey\":\"a914b10c9df5f7edf436c697f02f1efdba4cf399615187\"," + "\"redeemScript\":\"512103debedc17b3df2badbcdd86d5feb4562b86fe182e5998abd8bcd4f122c6155b1b21027e940bb73ab8732bfdf7f9216ecefca5b94d6df834e77e108f68e66f126044c052ae\"}]"; + r = CallRPC(string("createrawtransaction ")+prevout+" "+ + "{\"3HqAe9LtNBjnsfM4CyYaWTnvCaUYT7v4oZ\":11}"); + string notsigned = r.get_str(); + string privkey1 = "\"KzsXybp9jX64P5ekX1KUxRQ79Jht9uzW7LorgwE65i5rWACL6LQe\""; + string privkey2 = "\"Kyhdf5LuKTRx4ge69ybABsiUAWjVRK4XGxAKk2FQLp2HjGMy87Z4\""; + r = CallRPC(string("signrawtransaction ")+notsigned+" "+prevout+" "+"[]"); + BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == false); + r = CallRPC(string("signrawtransaction ")+notsigned+" "+prevout+" "+"["+privkey1+","+privkey2+"]"); + BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == true); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index 35069a3fdd..b5107193fd 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -13,8 +13,6 @@ using namespace std; // Test routines internal to script.cpp: extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); -extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, - bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType); // Helpers: static std::vector<unsigned char> @@ -40,7 +38,7 @@ Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict) txTo.vin[0].scriptSig = scriptSig; txTo.vout[0].nValue = 1; - return VerifyScript(scriptSig, scriptPubKey, txTo, 0, fStrict, true, 0); + return VerifyScript(scriptSig, scriptPubKey, txTo, 0, fStrict ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, 0); } @@ -105,7 +103,7 @@ BOOST_AUTO_TEST_CASE(sign) { CScript sigSave = txTo[i].vin[0].scriptSig; txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig; - bool sigOK = VerifySignature(CCoins(txFrom, 0), txTo[i], 0, true, true, 0); + bool sigOK = VerifySignature(CCoins(txFrom, 0), txTo[i], 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, 0); if (i == j) BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j)); else diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 3f280ba947..5d5a1525f7 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -20,8 +20,8 @@ using namespace json_spirit; using namespace boost::algorithm; extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); -extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, - bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType); + +static const unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC; CScript ParseScript(string s) @@ -143,7 +143,7 @@ BOOST_AUTO_TEST_CASE(script_valid) CScript scriptPubKey = ParseScript(scriptPubKeyString); CTransaction tx; - BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, tx, 0, true, true, SIGHASH_NONE), strTest); + BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, tx, 0, flags, SIGHASH_NONE), strTest); } } @@ -167,7 +167,7 @@ BOOST_AUTO_TEST_CASE(script_invalid) CScript scriptPubKey = ParseScript(scriptPubKeyString); CTransaction tx; - BOOST_CHECK_MESSAGE(!VerifyScript(scriptSig, scriptPubKey, tx, 0, true, true, SIGHASH_NONE), strTest); + BOOST_CHECK_MESSAGE(!VerifyScript(scriptSig, scriptPubKey, tx, 0, flags, SIGHASH_NONE), strTest); } } @@ -250,15 +250,15 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12) txTo12.vout[0].nValue = 1; CScript goodsig1 = sign_multisig(scriptPubKey12, key1, txTo12); - BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, true, true, 0)); + BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, flags, 0)); txTo12.vout[0].nValue = 2; - BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, true, true, 0)); + BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, flags, 0)); CScript goodsig2 = sign_multisig(scriptPubKey12, key2, txTo12); - BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, txTo12, 0, true, true, 0)); + BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, txTo12, 0, flags, 0)); CScript badsig1 = sign_multisig(scriptPubKey12, key3, txTo12); - BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, txTo12, 0, true, true, 0)); + BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, txTo12, 0, flags, 0)); } BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23) @@ -286,46 +286,46 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23) std::vector<CKey> keys; keys.push_back(key1); keys.push_back(key2); CScript goodsig1 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, txTo23, 0, true, true, 0)); + BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, txTo23, 0, flags, 0)); keys.clear(); keys.push_back(key1); keys.push_back(key3); CScript goodsig2 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, txTo23, 0, true, true, 0)); + BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, txTo23, 0, flags, 0)); keys.clear(); keys.push_back(key2); keys.push_back(key3); CScript goodsig3 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, txTo23, 0, true, true, 0)); + BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, txTo23, 0, flags, 0)); keys.clear(); keys.push_back(key2); keys.push_back(key2); // Can't re-use sig CScript badsig1 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, txTo23, 0, true, true, 0)); + BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, txTo23, 0, flags, 0)); keys.clear(); keys.push_back(key2); keys.push_back(key1); // sigs must be in correct order CScript badsig2 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, txTo23, 0, true, true, 0)); + BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, txTo23, 0, flags, 0)); keys.clear(); keys.push_back(key3); keys.push_back(key2); // sigs must be in correct order CScript badsig3 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, txTo23, 0, true, true, 0)); + BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, txTo23, 0, flags, 0)); keys.clear(); keys.push_back(key4); keys.push_back(key2); // sigs must match pubkeys CScript badsig4 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, txTo23, 0, true, true, 0)); + BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, txTo23, 0, flags, 0)); keys.clear(); keys.push_back(key1); keys.push_back(key4); // sigs must match pubkeys CScript badsig5 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, txTo23, 0, true, true, 0)); + BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, txTo23, 0, flags, 0)); keys.clear(); // Must have signatures CScript badsig6 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, txTo23, 0, true, true, 0)); + BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, txTo23, 0, flags, 0)); } BOOST_AUTO_TEST_CASE(script_combineSigs) diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index c1f47f786b..e4ba0259ba 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -19,10 +19,10 @@ struct TestingSetup { fPrintToDebugger = true; // don't want to write to debug.log file noui_connect(); bitdb.MakeMock(); - pblocktree = new CBlockTreeDB(true); - pcoinsdbview = new CCoinsViewDB(true); + pblocktree = new CBlockTreeDB(1 << 20, true); + pcoinsdbview = new CCoinsViewDB(1 << 23, true); pcoinsTip = new CCoinsViewCache(*pcoinsdbview); - LoadBlockIndex(true); + LoadBlockIndex(); bool fFirstRun; pwalletMain = new CWallet("wallet.dat"); pwalletMain->LoadWallet(fFirstRun); diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 4385b9ba37..23837c6c15 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -76,7 +76,7 @@ BOOST_AUTO_TEST_CASE(tx_valid) break; } - BOOST_CHECK_MESSAGE(VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], tx, i, test[2].get_bool(), false, 0), strTest); + BOOST_CHECK_MESSAGE(VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], tx, i, test[2].get_bool() ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, 0), strTest); } } } @@ -143,7 +143,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid) break; } - fValid = VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], tx, i, test[2].get_bool(), true, 0); + fValid = VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], tx, i, test[2].get_bool() ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, 0); } BOOST_CHECK_MESSAGE(!fValid, strTest); diff --git a/src/txdb.cpp b/src/txdb.cpp index 67d15cb58f..93c5f23d8b 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -19,7 +19,7 @@ void static BatchWriteHashBestChain(CLevelDBBatch &batch, const uint256 &hash) { batch.Write('B', hash); } -CCoinsViewDB::CCoinsViewDB(bool fMemory) : db(GetDataDir() / "coins", fMemory) { +CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "coins", nCacheSize, fMemory, fWipe) { } bool CCoinsViewDB::GetCoins(uint256 txid, CCoins &coins) { @@ -58,12 +58,13 @@ bool CCoinsViewDB::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockI CLevelDBBatch batch; for (std::map<uint256, CCoins>::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++) BatchWriteCoins(batch, it->first, it->second); - BatchWriteHashBestChain(batch, pindex->GetBlockHash()); + if (pindex) + BatchWriteHashBestChain(batch, pindex->GetBlockHash()); return db.WriteBatch(batch); } -CBlockTreeDB::CBlockTreeDB(bool fMemory) : CLevelDB(GetDataDir() / "blktree", fMemory) { +CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CLevelDB(GetDataDir() / "blktree", nCacheSize, fMemory, fWipe) { } bool CBlockTreeDB::WriteBlockIndex(const CDiskBlockIndex& blockindex) @@ -93,6 +94,18 @@ bool CBlockTreeDB::WriteLastBlockFile(int nFile) { return Write('l', nFile); } +bool CBlockTreeDB::WriteReindexing(bool fReindexing) { + if (fReindexing) + return Write('R', '1'); + else + return Erase('R'); +} + +bool CBlockTreeDB::ReadReindexing(bool &fReindexing) { + fReindexing = Exists('R'); + return true; +} + bool CBlockTreeDB::ReadLastBlockFile(int &nFile) { return Read('l', nFile); } diff --git a/src/txdb.h b/src/txdb.h index 123ec00d23..d7d327069f 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -14,7 +14,7 @@ class CCoinsViewDB : public CCoinsView protected: CLevelDB db; public: - CCoinsViewDB(bool fMemory = false); + CCoinsViewDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); bool GetCoins(uint256 txid, CCoins &coins); bool SetCoins(uint256 txid, const CCoins &coins); @@ -29,7 +29,7 @@ public: class CBlockTreeDB : public CLevelDB { public: - CBlockTreeDB(bool fMemory = false); + CBlockTreeDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); private: CBlockTreeDB(const CBlockTreeDB&); void operator=(const CBlockTreeDB&); @@ -41,6 +41,8 @@ public: bool WriteBlockFileInfo(int nFile, const CBlockFileInfo &fileinfo); bool ReadLastBlockFile(int &nFile); bool WriteLastBlockFile(int nFile); + bool WriteReindexing(bool fReindex); + bool ReadReindexing(bool &fReindex); bool LoadBlockIndexGuts(); }; diff --git a/src/util.cpp b/src/util.cpp index 9162886450..03014a5da0 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -156,7 +156,7 @@ void RandAddSeedPerfmon() if (ret == ERROR_SUCCESS) { RAND_add(pdata, nSize, nSize/100.0); - memset(pdata, 0, nSize); + OPENSSL_cleanse(pdata, nSize); printf("RandAddSeed() %lu bytes\n", nSize); } #endif diff --git a/src/wallet.cpp b/src/wallet.cpp index dc6169c4b8..0115e56b8e 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -820,21 +820,17 @@ void CWallet::ReacceptWalletTransactions() void CWalletTx::RelayWalletTransaction() { - CCoinsViewCache& coins = *pcoinsTip; BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) { - if (!tx.IsCoinBase()) - { - uint256 hash = tx.GetHash(); - if (!coins.HaveCoins(hash)) - RelayMessage(CInv(MSG_TX, hash), (CTransaction)tx); + if (!tx.IsCoinBase()) { + if (tx.GetDepthInMainChain() == 0) + RelayMessage(CInv(MSG_TX, tx.GetHash()), (CTransaction)tx); } } if (!IsCoinBase()) { - uint256 hash = GetHash(); - if (!coins.HaveCoins(hash)) - { + if (GetDepthInMainChain() == 0) { + uint256 hash = GetHash(); printf("Relaying wtx %s\n", hash.ToString().substr(0,10).c_str()); RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this); } @@ -930,9 +926,8 @@ int64 CWallet::GetImmatureBalance() const LOCK(cs_wallet); for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { - const CWalletTx& pcoin = (*it).second; - if (pcoin.IsCoinBase() && pcoin.GetBlocksToMaturity() > 0 && pcoin.IsInMainChain()) - nTotal += GetCredit(pcoin); + const CWalletTx* pcoin = &(*it).second; + nTotal += pcoin->GetImmatureCredit(); } } return nTotal; @@ -1296,7 +1291,7 @@ string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, if (IsLocked()) { - string strError = _("Error: Wallet locked, unable to create transaction "); + string strError = _("Error: Wallet locked, unable to create transaction!"); printf("SendMoney() : %s", strError.c_str()); return strError; } @@ -1304,9 +1299,9 @@ string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, { string strError; if (nValue + nFeeRequired > GetBalance()) - strError = strprintf(_("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds "), FormatMoney(nFeeRequired).c_str()); + strError = strprintf(_("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds!"), FormatMoney(nFeeRequired).c_str()); else - strError = _("Error: Transaction creation failed "); + strError = _("Error: Transaction creation failed!"); printf("SendMoney() : %s", strError.c_str()); return strError; } @@ -1315,7 +1310,7 @@ string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, return "ABORTED"; if (!CommitTransaction(wtxNew, reservekey)) - return _("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); + return _("Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); return ""; } diff --git a/src/wallet.h b/src/wallet.h index 43b695c597..5e2f8e0ba1 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -375,10 +375,12 @@ public: // memory only mutable bool fDebitCached; mutable bool fCreditCached; + mutable bool fImmatureCreditCached; mutable bool fAvailableCreditCached; mutable bool fChangeCached; mutable int64 nDebitCached; mutable int64 nCreditCached; + mutable int64 nImmatureCreditCached; mutable int64 nAvailableCreditCached; mutable int64 nChangeCached; @@ -416,10 +418,12 @@ public: vfSpent.clear(); fDebitCached = false; fCreditCached = false; + fImmatureCreditCached = false; fAvailableCreditCached = false; fChangeCached = false; nDebitCached = 0; nCreditCached = 0; + nImmatureCreditCached = 0; nAvailableCreditCached = 0; nChangeCached = 0; nOrderPos = -1; @@ -563,6 +567,20 @@ public: return nCreditCached; } + int64 GetImmatureCredit(bool fUseCache=true) const + { + if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) + { + if (fUseCache && fImmatureCreditCached) + return nImmatureCreditCached; + nImmatureCreditCached = pwallet->GetCredit(*this); + fImmatureCreditCached = true; + return nImmatureCreditCached; + } + + return 0; + } + int64 GetAvailableCredit(bool fUseCache=true) const { // Must wait until coinbase is safely deep enough in the chain before valuing it |