diff options
60 files changed, 1439 insertions, 679 deletions
@@ -1,4 +1,4 @@ -Copyright (c) 2009-2012 Bitcoin Developers +Copyright (c) 2009-2013 Bitcoin Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README b/README deleted file mode 120000 index 42061c01a1..0000000000 --- a/README +++ /dev/null @@ -1 +0,0 @@ -README.md
\ No newline at end of file @@ -1,66 +1,82 @@ - Bitcoin integration/staging tree +================================ + +http://www.bitcoin.org + +Copyright (c) 2009-2012 Bitcoin Developers + +What is Bitcoin? +---------------- + +Bitcoin is an experimental new digital currency that enables instant payments to +anyone, anywhere in the world. Bitcoin uses peer-to-peer technology to operate +with no central authority: managing transactions and issuing money are carried +out collectively by the network. Bitcoin is also the name of the open source +software which enables the use of this currency. + +For more information, as well as an immediately useable, binary version of +the Bitcoin client sofware, see http://www.bitcoin.org. + +License +------- + +Bitcoin is released under the terms of the MIT license. See `COPYING` for more +information or see http://opensource.org/licenses/MIT. Development process -=================== +------------------- -Developers work in their own trees, then submit pull requests when -they think their feature or bug fix is ready. +Developers work in their own trees, then submit pull requests when they think +their feature or bug fix is ready. -If it is a simple/trivial/non-controversial change, then one of the -bitcoin development team members simply pulls it. +If it is a simple/trivial/non-controversial change, then one of the Bitcoin +development team members simply pulls it. -If it is a more complicated or potentially controversial -change, then the patch submitter will be asked to start a -discussion (if they haven't already) on the mailing list: -http://sourceforge.net/mailarchive/forum.php?forum_name=bitcoin-development +If it is a *more complicated or potentially controversial* change, then the patch +submitter will be asked to start a discussion (if they haven't already) on the +[mailing list](http://sourceforge.net/mailarchive/forum.php?forum_name=bitcoin-development). -The patch will be accepted if there is broad consensus that it is a -good thing. Developers should expect to rework and resubmit patches -if they don't match the project's coding conventions (see coding.txt) -or are controversial. +The patch will be accepted if there is broad consensus that it is a good thing. +Developers should expect to rework and resubmit patches if the code doesn't +match the project's coding conventions (see `doc/coding.txt`) or are +controversial. -The master branch is regularly built and tested, but is not guaranteed -to be completely stable. Tags are regularly created to indicate new -official, stable release versions of Bitcoin. +The `master` branch is regularly built and tested, but is not guaranteed to be +completely stable. [Tags](https://github.com/bitcoin/bitcoin/tags) are created +regularly to indicate new official, stable release versions of Bitcoin. Testing -======= +------- + +Testing and code review is the bottleneck for development; we get more pull +requests than we can review and test. Please be patient and help out, and +remember this is a security-critical project where any mistake might cost people +lots of money. + +### Automated Testing -Testing and code review is the bottleneck for development; we get more -pull requests than we can review and test. Please be patient and help -out, and remember this is a security-critical project where any -mistake might cost people lots of money. +Developers are strongly encouraged to write unit tests for new code, and to +submit new unit tests for old code. -Automated Testing ------------------ +Unit tests for the core code are in `src/test/`. To compile and run them: -Developers are strongly encouraged to write unit tests for new code, -and to submit new unit tests for old code. + cd src; make -f makefile.linux test -Unit tests for the core code are in src/test/ -To compile and run them: - cd src; make -f makefile.linux test +Unit tests for the GUI code are in `src/qt/test/`. To compile and run them: -Unit tests for the GUI code are in src/qt/test/ -To compile and run them: - qmake BITCOIN_QT_TEST=1 -o Makefile.test bitcoin-qt.pro - make -f Makefile.test - ./Bitcoin-Qt + qmake BITCOIN_QT_TEST=1 -o Makefile.test bitcoin-qt.pro + make -f Makefile.test + ./bitcoin-qt_test -Every pull request is built for both Windows and -Linux on a dedicated server, and unit and sanity -tests are automatically run. The binaries -produced may be used for manual QA testing -(a link to them will appear in a comment on the pull request -from 'BitcoinPullTester'). -See https://github.com/TheBlueMatt/test-scripts for the -build/test scripts. +Every pull request is built for both Windows and Linux on a dedicated server, +and unit and sanity tests are automatically run. The binaries produced may be +used for manual QA testing -- a link to them will appear in a comment on the +pull request posted by 'BitcoinPullTester'. See `https://github.com/TheBlueMatt/test-scripts` +for the build/test scripts. -Manual Quality Assurance (QA) Testing -------------------------------------- +### Manual Quality Assurance (QA) Testing -Large changes should have a test plan, and should be tested -by somebody other than the developer who wrote the code. +Large changes should have a test plan, and should be tested by somebody other +than the developer who wrote the code. -See https://github.com/bitcoin/QA/ for how to create a test plan. +See `https://github.com/bitcoin/QA/` for how to create a test plan. diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 6c1c4a78cc..30e90a3fbc 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -1,6 +1,7 @@ TEMPLATE = app TARGET = bitcoin-qt -VERSION = 0.7.99 +macx:TARGET = "Bitcoin-Qt" +VERSION = 0.8.0 INCLUDEPATH += src src/json src/qt DEFINES += QT_GUI BOOST_THREAD_USE_LIB BOOST_SPIRIT_THREADSAFE CONFIG += no_include_pwd @@ -29,19 +30,19 @@ contains(RELEASE, 1) { !win32:!macx { # Linux: static link - LIBS += -Wl,-Bstatic + LIBS += -Wl,-Bstatic -Wl,-z,relro -Wl,-z,now + # for extra security (see: https://wiki.debian.org/Hardening) + QMAKE_CXXFLAGS *= -D_FORTIFY_SOURCE=2 } } !win32 { -# for extra security against potential buffer overflows: enable GCCs Stack Smashing Protection -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 against potential buffer overflows: enable GCCs Stack Smashing Protection + QMAKE_CXXFLAGS *= -fstack-protector-all + QMAKE_LFLAGS *= -fstack-protector-all + # Exclude on 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 # on Windows: enable GCC large address aware linker flag @@ -99,14 +100,14 @@ contains(BITCOIN_NEED_QT_PLUGINS, 1) { INCLUDEPATH += src/leveldb/include src/leveldb/helpers LIBS += $$PWD/src/leveldb/libleveldb.a $$PWD/src/leveldb/libmemenv.a !win32 { - genleveldb.commands = cd $$PWD/src/leveldb && $(MAKE) libleveldb.a libmemenv.a + genleveldb.commands = cd $$PWD/src/leveldb && CC=$$QMAKE_CC CXX=$$QMAKE_CXX $(MAKE) OPT=\"$$QMAKE_CXXFLAGS\" 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) } LIBS += -lshlwapi - genleveldb.commands = cd $$PWD/src/leveldb && CC=$$QMAKE_CC CXX=$$QMAKE_CXX TARGET_OS=OS_WINDOWS_CROSSCOMPILE $(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 $(MAKE) OPT=\"$$QMAKE_CXXFLAGS\" 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 @@ -300,6 +301,7 @@ DEPENDPATH += src/qt/test QT += testlib TARGET = bitcoin-qt_test DEFINES += BITCOIN_QT_TEST + macx: CONFIG -= app_bundle } CODECFORTR = UTF-8 @@ -372,6 +374,8 @@ win32:!contains(MINGW_THREAD_BUGFIX, 0) { !win32:!macx { DEFINES += LINUX LIBS += -lrt + # _FILE_OFFSET_BITS=64 lets 32-bit fopen transparently support large files. + DEFINES += _FILE_OFFSET_BITS=64 } macx:HEADERS += src/qt/macdockiconhandler.h @@ -379,7 +383,6 @@ macx:OBJECTIVE_SOURCES += src/qt/macdockiconhandler.mm macx:LIBS += -framework Foundation -framework ApplicationServices -framework AppKit macx:DEFINES += MAC_OSX MSG_NOSIGNAL=0 macx:ICON = src/qt/res/icons/bitcoin.icns -macx:TARGET = "Bitcoin-Qt" macx:QMAKE_CFLAGS_THREAD += -pthread macx:QMAKE_LFLAGS_THREAD += -pthread macx:QMAKE_CXXFLAGS_THREAD += -pthread diff --git a/contrib/gitian-descriptors/gitian-win32.yml b/contrib/gitian-descriptors/gitian-win32.yml index 9df42a0bec..fd3b55325a 100644 --- a/contrib/gitian-descriptors/gitian-win32.yml +++ b/contrib/gitian-descriptors/gitian-win32.yml @@ -24,7 +24,7 @@ script: | cd $HOME/qt unzip ../build/qt-win32-4.8.3-gitian-r1.zip cd $HOME/build/ - export PATH=$PATH:$HOME/qt/bin/ + export PATH=$HOME/qt/bin/:$PATH # mkdir boost_1_50_0 cd boost_1_50_0 @@ -51,7 +51,7 @@ script: | export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1 export FAKETIME=$REFERENCE_DATETIME export TZ=UTC - $HOME/qt/src/bin/qmake -spec unsupported/win32-g++-cross MINIUPNPC_LIB_PATH=$HOME/build/miniupnpc MINIUPNPC_INCLUDE_PATH=$HOME/build/ BDB_LIB_PATH=$HOME/build/db-4.8.30.NC/build_unix BDB_INCLUDE_PATH=$HOME/build/db-4.8.30.NC/build_unix BOOST_LIB_PATH=$HOME/build/boost_1_50_0/stage/lib BOOST_INCLUDE_PATH=$HOME/build/boost_1_50_0 BOOST_LIB_SUFFIX=-mt-s BOOST_THREAD_LIB_SUFFIX=_win32-mt-s OPENSSL_LIB_PATH=$HOME/build/openssl-1.0.1c OPENSSL_INCLUDE_PATH=$HOME/build/openssl-1.0.1c/include QRENCODE_LIB_PATH=$HOME/build/qrencode-3.2.0/.libs QRENCODE_INCLUDE_PATH=$HOME/build/qrencode-3.2.0 USE_QRCODE=1 INCLUDEPATH=$HOME/build DEFINES=BOOST_THREAD_USE_LIB BITCOIN_NEED_QT_PLUGINS=1 QMAKE_LRELEASE=lrelease QMAKE_CXXFLAGS=-frandom-seed=bitcoin QMAKE_LFLAGS=-frandom-seed=bitcoin USE_BUILD_INFO=1 + $HOME/qt/src/bin/qmake -spec unsupported/win32-g++-cross MINIUPNPC_LIB_PATH=$HOME/build/miniupnpc MINIUPNPC_INCLUDE_PATH=$HOME/build/ BDB_LIB_PATH=$HOME/build/db-4.8.30.NC/build_unix BDB_INCLUDE_PATH=$HOME/build/db-4.8.30.NC/build_unix BOOST_LIB_PATH=$HOME/build/boost_1_50_0/stage/lib BOOST_INCLUDE_PATH=$HOME/build/boost_1_50_0 BOOST_LIB_SUFFIX=-mt-s BOOST_THREAD_LIB_SUFFIX=_win32-mt-s OPENSSL_LIB_PATH=$HOME/build/openssl-1.0.1c OPENSSL_INCLUDE_PATH=$HOME/build/openssl-1.0.1c/include QRENCODE_LIB_PATH=$HOME/build/qrencode-3.2.0/.libs QRENCODE_INCLUDE_PATH=$HOME/build/qrencode-3.2.0 USE_QRCODE=1 INCLUDEPATH=$HOME/build DEFINES=BOOST_THREAD_USE_LIB BITCOIN_NEED_QT_PLUGINS=1 QMAKE_LRELEASE=lrelease QMAKE_CXXFLAGS=-frandom-seed=bitcoin USE_BUILD_INFO=1 make $MAKEOPTS cp release/bitcoin-qt.exe $OUTDIR/ # diff --git a/contrib/homebrew/makefile.osx.patch b/contrib/homebrew/makefile.osx.patch new file mode 100644 index 0000000000..340de0efdf --- /dev/null +++ b/contrib/homebrew/makefile.osx.patch @@ -0,0 +1,47 @@ +diff --git a/src/makefile.osx b/src/makefile.osx +index 8b7c559..8a0560c 100644 +--- a/src/makefile.osx ++++ b/src/makefile.osx +@@ -7,17 +7,21 @@ + # Originally by Laszlo Hanyecz (solar@heliacal.net) + + CXX=llvm-g++ +-DEPSDIR=/opt/local ++DEPSDIR=/usr/local ++DB4DIR=/usr/local/opt/berkeley-db4 ++OPENSSLDIR=/usr/local/opt/openssl + + INCLUDEPATHS= \ + -I"$(CURDIR)" \ +- -I"$(CURDIR)"/obj \ ++ -I"$(CURDIR)/obj" \ + -I"$(DEPSDIR)/include" \ +- -I"$(DEPSDIR)/include/db48" ++ -I"$(DB4DIR)/include" \ ++ -I"$(OPENSSLDIR)/include" + + LIBPATHS= \ + -L"$(DEPSDIR)/lib" \ +- -L"$(DEPSDIR)/lib/db48" ++ -L"$(DB4DIR)/lib" \ ++ -L"$(OPENSSLDIR)/lib" + + USE_UPNP:=1 + USE_IPV6:=1 +@@ -31,13 +35,13 @@ ifdef STATIC + TESTLIBS += \ + $(DEPSDIR)/lib/libboost_unit_test_framework-mt.a + LIBS += \ +- $(DEPSDIR)/lib/db48/libdb_cxx-4.8.a \ ++ $(DB4DIR)/lib/libdb_cxx-4.8.a \ + $(DEPSDIR)/lib/libboost_system-mt.a \ + $(DEPSDIR)/lib/libboost_filesystem-mt.a \ + $(DEPSDIR)/lib/libboost_program_options-mt.a \ + $(DEPSDIR)/lib/libboost_thread-mt.a \ +- $(DEPSDIR)/lib/libssl.a \ +- $(DEPSDIR)/lib/libcrypto.a \ ++ $(OPENSSLDIR)/lib/libssl.a \ ++ $(OPENSSLDIR)/lib/libcrypto.a \ + -lz + else + TESTLIBS += \ diff --git a/contrib/macdeploy/notes.txt b/contrib/macdeploy/notes.txt index a3f0b5447d..3d74901437 100644 --- a/contrib/macdeploy/notes.txt +++ b/contrib/macdeploy/notes.txt @@ -5,7 +5,7 @@ Python 2.7 and make it your default Python installation. You will need the appscript package for the fancy disk image creation to work. Install it by invoking "sudo easy_install appscript". -Ths script should be invoked in the target directory like this: +This script should be invoked in the target directory like this: $source_dir/contrib/macdeploy/macdeployqtplus Bitcoin-Qt.app -add-qt-tr da,de,es,hu,ru,uk,zh_CN,zh_TW -dmg -fancy $source_dir/contrib/macdeploy/fancy.plist -verbose 2 During the process, the disk image window will pop up briefly where the fancy diff --git a/contrib/seeds/README b/contrib/seeds/README new file mode 100644 index 0000000000..97e3e1ec71 --- /dev/null +++ b/contrib/seeds/README @@ -0,0 +1,9 @@ +Utility to generate the pnSeed[] array that is compiled into the client +(see src/net.cpp). + +The 600 seeds compiled into the 0.8 release were created from sipa's DNS seed data, like this: + +curl -s http://bitcoin.sipa.be/seeds.txt | head -1000 | makeseeds.py + +The input to makeseeds.py is assumed to be approximately sorted from most-reliable to least-reliable, +with IP:port first on each line (lines that don't match IPv4:port are ignored). diff --git a/contrib/seeds/makeseeds.py b/contrib/seeds/makeseeds.py new file mode 100755 index 0000000000..1d01fd7d20 --- /dev/null +++ b/contrib/seeds/makeseeds.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +# +# Generate pnSeed[] from Pieter's DNS seeder +# + +NSEEDS=600 + +import re +import sys +from subprocess import check_output + +def main(): + lines = sys.stdin.readlines() + + ips = [] + pattern = re.compile(r"^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3}):8333") + for line in lines: + m = pattern.match(line) + if m is None: + continue + ip = 0 + for i in range(0,4): + ip = ip + (int(m.group(i+1)) << (8*(i))) + if ip == 0: + continue + ips.append(ip) + + for row in range(0, min(NSEEDS,len(ips)), 8): + print " " + ", ".join([ "0x%08x"%i for i in ips[row:row+8] ]) + "," + +if __name__ == '__main__': + main() diff --git a/contrib/tidy_datadir.sh b/contrib/tidy_datadir.sh new file mode 100755 index 0000000000..5d6d826444 --- /dev/null +++ b/contrib/tidy_datadir.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +if [ -d "$1" ]; then + cd "$1" +else + echo "Usage: $0 <datadir>" >&2 + echo "Removes obsolete Bitcoin database files" >&2 + exit 1 +fi + +LEVEL=0 +if [ -f wallet.dat -a -f addr.dat -a -f blkindex.dat -a -f blk0001.dat ]; then LEVEL=1; fi +if [ -f wallet.dat -a -f peers.dat -a -f blkindex.dat -a -f blk0001.dat ]; then LEVEL=2; fi +if [ -f wallet.dat -a -f peers.dat -a -f coins/CURRENT -a -f blktree/CURRENT -a -f blocks/blk00000.dat ]; then LEVEL=3; fi +if [ -f wallet.dat -a -f peers.dat -a -f chainstate/CURRENT -a -f blocks/index/CURRENT -a -f blocks/blk00000.dat ]; then LEVEL=4; fi + +case $LEVEL in + 0) + echo "Error: no Bitcoin datadir detected." + exit 1 + ;; + 1) + echo "Detected old Bitcoin datadir (before 0.7)." + echo "Nothing to do." + exit 0 + ;; + 2) + echo "Detected Bitcoin 0.7 datadir." + ;; + 3) + echo "Detected Bitcoin pre-0.8 datadir." + ;; + 4) + echo "Detected Bitcoin 0.8 datadir." + ;; +esac + +FILES="" +DIRS="" + +if [ $LEVEL -ge 3 ]; then FILES=$(echo $FILES blk????.dat blkindex.dat); fi +if [ $LEVEL -ge 2 ]; then FILES=$(echo $FILES addr.dat); fi +if [ $LEVEL -ge 4 ]; then DIRS=$(echo $DIRS coins blktree); fi + +for FILE in $FILES; do + if [ -f $FILE ]; then + echo "Deleting: $FILE" + rm -f $FILE + fi +done + +for DIR in $DIRS; do + if [ -d $DIR ]; then + echo "Deleting: $DIR/" + rm -rf $DIR + fi +done + +echo "Done." diff --git a/contrib/verifysfbinaries/verify.sh b/contrib/verifysfbinaries/verify.sh index 336de3ec1f..02814d894b 100755 --- a/contrib/verifysfbinaries/verify.sh +++ b/contrib/verifysfbinaries/verify.sh @@ -18,7 +18,7 @@ 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" +SIGNATUREFILE="http://downloads.sourceforge.net/project/bitcoin/Bitcoin/bitcoin-0.8.0/SHA256SUMS.asc" SIGNATUREFILENAME="SHA256SUMS.asc" RCSUBDIR="test/" @@ -81,7 +81,7 @@ if [ $RET -ne 0 ]; then #and notify the user if it's bad echo "Bad signature." elif [ $RET -eq 2 ]; then - #or if a gpg error has occured + #or if a gpg error has occurred echo "gpg error. Do you have Gavin's code signing key installed?" fi diff --git a/doc/README b/doc/README index 5dbf690a10..dfcb259389 100644 --- a/doc/README +++ b/doc/README @@ -1,6 +1,6 @@ -Bitcoin 0.7.99 BETA +Bitcoin 0.8.0 BETA -Copyright (c) 2009-2012 Bitcoin Developers +Copyright (c) 2009-2013 Bitcoin Developers Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. This product includes software developed by the OpenSSL Project for use in diff --git a/doc/README_windows.txt b/doc/README_windows.txt index 970dca3239..9e7c0836b1 100644 --- a/doc/README_windows.txt +++ b/doc/README_windows.txt @@ -1,6 +1,6 @@ -Bitcoin 0.7.99 BETA
+Bitcoin 0.8.0 BETA
-Copyright (c) 2009-2012 Bitcoin Developers
+Copyright (c) 2009-2013 Bitcoin Developers
Distributed under the MIT/X11 software license, see the accompanying
file COPYING or http://www.opensource.org/licenses/mit-license.php.
This product includes software developed by the OpenSSL Project for use in
diff --git a/doc/build-osx.md b/doc/build-osx.md new file mode 100644 index 0000000000..1ed593d5eb --- /dev/null +++ b/doc/build-osx.md @@ -0,0 +1,173 @@ +Mac OS X bitcoind build instructions +==================================== + +Authors +------- + +* Laszlo Hanyecz <solar@heliacal.net> +* Douglas Huff <dhuff@jrbobdobbs.org> +* Colin Dean <cad@cad.cx> +* Gavin Andresen <gavinandresen@gmail.com> + +License +------- + +Copyright (c) 2009-2012 Bitcoin Developers + +Distributed under the MIT/X11 software license, see the accompanying +file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in +the OpenSSL Toolkit (http://www.openssl.org/). + +This product includes cryptographic software written by +Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + +Notes +----- + +See `doc/readme-qt.rst` for instructions on building Bitcoin-Qt, the +graphical user interface. + +Tested on OS X 10.5 through 10.8 on Intel processors only. PPC is not +supported because it is big-endian. + +All of the commands should be executed in a Terminal application. The +built-in one is located in `/Applications/Utilities`. + +Preparation +----------- + +You need to install XCode with all the options checked so that the compiler +and everything is available in /usr not just /Developer. XCode should be +available on your OS X installation media, but if not, you can get the +current version from https://developer.apple.com/xcode/. If you install +Xcode 4.3 or later, you'll need to install its command line tools. This can +be done in `Xcode > Preferences > Downloads > Components` and generally must +be re-done or updated every time Xcode is updated. + +There's an assumption that you already have `git` installed, as well. If +not, it's the path of least resistance to install Github for Mac +(OS X 10.7+) or +[Git for OS X](https://code.google.com/p/git-osx-installer/). It is also +available via Homebrew or MacPorts. + +You will also need to install [Homebrew](http://mxcl.github.com/homebrew/) +or [MacPorts](http://www.macports.org/) in order to install library +dependencies. It's largely a religious decision which to choose, but, as of +December 2012, MacPorts is a little easier because you can just install the +dependencies immediately - no other work required. If you're unsure, read +the instructions through first in order to assess what you want to do. +Homebrew is a little more popular among those newer to OS X. + +The installation of the actual dependencies is covered in the Instructions +sections below. + +Instructions: MacPorts +---------------------- + +### Install dependencies + +Installing the dependencies using MacPorts is very straightforward. + + sudo port install boost db48@+no_java openssl miniupnpc + +### Building `bitcoind` + +1. Clone the github tree to get the source code and go into the directory. + + git clone git@github.com:bitcoin/bitcoin.git bitcoin + cd bitcoin + +2. Build bitcoind: + + cd src + make -f makefile.osx + +3. It is a good idea to build and run the unit tests, too: + + make -f makefile.osx test + +Instructions: HomeBrew +---------------------- + +#### Install dependencies using Homebrew + + brew install boost miniupnpc openssl berkeley-db4 + +### Building `bitcoind` + +1. Clone the github tree to get the source code and go into the directory. + + git clone git@github.com:bitcoin/bitcoin.git bitcoin + cd bitcoin + +2. Modify source in order to pick up the `openssl` library. + + Edit `makefile.osx` to account for library location differences. There's a + diff in `contrib/homebrew/makefile.osx.patch` that shows what you need to + change, or you can just patch by doing + + patch -p1 < contrib/homebrew/makefile.osx.patch + +3. Build bitcoind: + + cd src + make -f makefile.osx + +4. It is a good idea to build and run the unit tests, too: + + make -f makefile.osx test + +Creating a release build +------------------------ + +A bitcoind binary is not included in the Bitcoin-Qt.app bundle. You can ignore +this section if you are building `bitcoind` for your own use. + +If you are building `bitcoind` for others, your build machine should be set up +as follows for maximum compatibility: + +All dependencies should be compiled with these flags: + + -mmacosx-version-min=10.5 -arch i386 -isysroot /Developer/SDKs/MacOSX10.5.sdk + +For MacPorts, that means editing your macports.conf and setting +`macosx_deployment_target` and `build_arch`: + + macosx_deployment_target=10.5 + build_arch=i386 + +... and then uninstalling and re-installing, or simply rebuilding, all ports. + +As of December 2012, the `boost` port does not obey `macosx_deployment_target`. +Download `http://gavinandresen-bitcoin.s3.amazonaws.com/boost_macports_fix.zip` +for a fix. Some ports also seem to obey either `build_arch` or +`macosx_deployment_target`, but not both at the same time. For example, building +on an OS X 10.6 64-bit machine fails. Official release builds of Bitcoin-Qt are +compiled on an OS X 10.6 32-bit machine to workaround that problem. + +Once dependencies are compiled, creating `Bitcoin-Qt.app` is easy: + + make -f Makefile.osx RELEASE=1 + +Running +------- + +It's now available at `./bitcoind`, provided that you are still in the `src` +directory. We have to first create the RPC configuration file, though. + +Run `./bitcoind` to get the filename where it should be put, or just try these +commands: + + echo -e "rpcuser=bitcoinrpc\nrpcpassword=$(xxd -l 16 -p /dev/urandom)" > "/Users/${USER}/Library/Application Support/Bitcoin/bitcoin.conf" + chmod 600 "/Users/${USER}/Library/Application Support/Bitcoin/bitcoin.conf" + +When next you run it, it will start downloading the blockchain, but it won't +output anything while it's doing this. This process may take several hours. + +Other commands: + + ./bitcoind --help # for a list of command-line options. + ./bitcoind -daemon # to start the bitcoin daemon. + ./bitcoind help # When the daemon is running, to get a list of RPC commands diff --git a/doc/build-osx.txt b/doc/build-osx.txt deleted file mode 100644 index fd878043c0..0000000000 --- a/doc/build-osx.txt +++ /dev/null @@ -1,54 +0,0 @@ -Copyright (c) 2009-2012 Bitcoin Developers -Distributed under the MIT/X11 software license, see the accompanying -file COPYING or http://www.opensource.org/licenses/mit-license.php. -This product includes software developed by the OpenSSL Project for use in -the OpenSSL Toolkit (http://www.openssl.org/). This product includes -cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP -software written by Thomas Bernard. - - -Mac OS X bitcoind build instructions -Laszlo Hanyecz <solar@heliacal.net> -Douglas Huff <dhuff@jrbobdobbs.org> - - -See readme-qt.rst for instructions on building Bitcoin-Qt, the -graphical user interface. - -Tested on 10.5, 10.6 and 10.7 intel. PPC is not supported because it's big-endian. - -All of the commands should be executed in Terminal.app.. it's in -/Applications/Utilities - -You need to install XCode with all the options checked so that the compiler and -everything is available in /usr not just /Developer. XCode should be available on your OS X -install DVD, but if not, you can get the current version from https://developer.apple.com/xcode/ - - -1. Clone the github tree to get the source code: - -git clone git@github.com:bitcoin/bitcoin.git bitcoin - -2. Download and install MacPorts from http://www.macports.org/ - -2a. (for 10.7 Lion) - Edit /opt/local/etc/macports/macports.conf and uncomment "build_arch i386" - -3. Install dependencies from MacPorts - -sudo port install boost db48 openssl miniupnpc - -Optionally install qrencode (and set USE_QRCODE=1): -sudo port install qrencode - -4. Now you should be able to build bitcoind: - -cd bitcoin/src -make -f makefile.osx - -Run: - ./bitcoind --help # for a list of command-line options. -Run - ./bitcoind -daemon # to start the bitcoin daemon. -Run - ./bitcoind help # When the daemon is running, to get a list of RPC commands diff --git a/doc/files.txt b/doc/files.txt new file mode 100644 index 0000000000..5d4cdabf8d --- /dev/null +++ b/doc/files.txt @@ -0,0 +1,19 @@ +Used in 0.8.0: +* wallet.dat: personal wallet (BDB) with keys and transactions +* peers.dat: peer IP address database (custom format); since 0.7.0 +* blocks/blk000??.dat: block data (custom, 128 MiB per file); since 0.8.0 +* blocks/rev000??.dat; block undo data (custom); since 0.8.0 (format changed since pre-0.8) +* blocks/index/*; block index (LevelDB); since 0.8.0 +* chainstate/*; block chain state database (LevelDB); since 0.8.0 +* database/*: BDB database environment; only used for wallet since 0.8.0 + +Only used in pre-0.8.0: +* blktree/*; block chain index (LevelDB); since pre-0.8, replaced by blocks/index/* in 0.8.0 +* coins/*; unspent transaction output database (LevelDB); since pre-0.8, replaced by chainstate/* in 0.8.0 + +Only used before 0.8.0: +* blkindex.dat: block chain index database (BDB); replaced by {chainstate/*,blocks/index/*,blocks/rev000??.dat} in 0.8.0 +* blk000?.dat: block data (custom, 2 GiB per file); replaced by blocks/blk000??.dat in 0.8.0 + +Only used before 0.7.0: +* addr.dat: peer IP address database (BDB); replaced by peers.dat in 0.7.0 diff --git a/doc/readme-qt.rst b/doc/readme-qt.rst index b0fdd4d810..7283dc780b 100644 --- a/doc/readme-qt.rst +++ b/doc/readme-qt.rst @@ -41,25 +41,6 @@ An executable named `bitcoin-qt` will be built. .. _`Qt Creator`: http://qt-project.org/downloads/ -Windows --------- - -Windows build instructions: - -- Download the `Qt Windows SDK`_ and install it. You don't need the Symbian stuff, just the desktop Qt. - -- Download and extract the `dependencies archive`_ [#]_, or compile openssl, boost and dbcxx yourself. - -- Copy the contents of the folder "deps" to "X:\\QtSDK\\mingw", replace X:\\ with the location where you installed the Qt SDK. Make sure that the contents of "deps\\include" end up in the current "include" directory. - -- Open the bitcoin-qt.pro file in Qt Creator and build as normal (ctrl-B) - -.. _`Qt Windows SDK`: http://qt-project.org/downloads/ -.. _`dependencies archive`: https://download.visucore.com/bitcoin/qtgui_deps_1.zip -.. [#] PGP signature: https://download.visucore.com/bitcoin/qtgui_deps_1.zip.sig (signed with RSA key ID `610945D0`_) -.. _`610945D0`: http://pgp.mit.edu:11371/pks/lookup?op=get&search=0x610945D0 - - Mac OS X -------- diff --git a/doc/release-notes.txt b/doc/release-notes.txt index b7f7dfb402..414d5d68c9 100644 --- a/doc/release-notes.txt +++ b/doc/release-notes.txt @@ -3,71 +3,97 @@ release time) Building this from - $ git shortlog --no-merges v0.7.0.. - -How to Upgrade --------------- - -If you are running an older version, shut it down. Wait -until it has completely shut down (which might take a few minutes for older -versions), then run the installer (on Windows) or just copy over -/Applications/Bitcoin-Qt (on Mac) or bitcoind/bitcoin-qt (on Linux). - -If you were running on Linux with a version that might have been compiled -with a different version of Berkeley DB (for example, if you were using an -Ubuntu PPA version), then run the old version again with the -detachdb -argument and shut it down; if you do not, then the new version will not -be able to read the database files and will exit with an error. - -Explanation of -detachdb (and the new "stop true" RPC command): -The Berkeley DB database library stores data in both ".dat" and -"log" files, so the database is always in a consistent state, -even in case of power failure or other sudden shutdown. The -format of the ".dat" files is portable between different -versions of Berkeley DB, but the "log" files are not-- even minor -version differences may have incompatible "log" files. The --detachdb option moves any pending changes from the "log" files -to the "blkindex.dat" file for maximum compatibility, but makes -shutdown much slower. Note that the "wallet.dat" file is always -detached, and versions prior to 0.6.0 detached all databases -at shutdown. - -New features + $ git shortlog --no-merges v0.7.1.. + +Incompatible Changes +-------------------- + +This release no longer maintains a full index of historical transaction ids +by default, so looking up an arbitrary transaction using the getrawtransaction +RPC call will not work. If you need that functionality, you must run once +with -txindex -reindex to rebuild block-chain indices (see below for more +details). + +Improvements ------------ -* Added a boolean argument to the RPC 'stop' command, if true sets - -detachdb to create standalone database .dat files before shutting down. +Mac and Windows binaries are signed with certificates owned by the Bitcoin +Foundation, to be compatible with the new security features in OSX 10.8 and +Windows 8. -* -salvagewallet command-line option, which moves any existing wallet.dat - to wallet.{timestamp}.dat and then attempts to salvage public/private - keys and master encryption keys (if the wallet is encrypted) into - a new wallet.dat. This should only be used if your wallet becomes - corrupted, and is not intended to replace regular wallet backups. +LevelDB, a fast, open-source, non-relational database from Google, is +now used to store transaction and block indices. LevelDB works much better +on machines with slow I/O and is faster in general. Berkeley DB is now only +used for the wallet.dat file (public and private wallet keys and transactions +relevant to you). -* Import $DataDir/bootstrap.dat automatically, if it exists. +Pieter Wuille implemented many optimizations to the way transactions are +verified, so a running, synchronized node uses much less memory and does +much less I/O. He also implemented parallel signature checking, so if you +have a multi-CPU machine all CPUs will be used to verify transactions. -Dependency changes ------------------- +New Features +------------ + +"Bloom filter" support in the network protocol for sending only relevant transactions to +lightweight clients. + +contrib/verifysfbinaries is a shell-script to verify that the binary downloads +at sourceforge have not been tampered with. If you are able, you can help make +everybody's downloads more secure by running this occasionally to check PGP +signatures against download file checksums. + +contrib/spendfrom is a python-language command-line utility that demonstrates +how to use the "raw transactions" JSON-RPC api to send coins received from particular +addresses (also known as "coin control"). + +New/changed settings (command-line or bitcoin.conf file) +-------------------------------------------------------- -* Qt 4.8.2 for Windows builds +dbcache : now controls LevelDB memory usage. Running with (for example) -dbcache=1000 +will use a gigabyte of memory and might make the initial blockchain download faster. -* openssl 1.0.1c +par : controls how many threads to use to validate transactions. Defaults to the number +of CPUs on your machine, use -par=1 to limit to a single CPU. -Bug fixes ---------- +txindex : maintains an extra index of old, spent transaction ids so they will be found +by the getrawtransaction JSON-RPC method. Can only be set when the database is +initialized. -* When running -testnet, use RPC port 18332 by default. +reindex : rebuild block and transaction indices from the downloaded block data. -* Better detection and handling of corrupt wallet.dat and blkindex.dat files. - Previous versions would crash with a DB_RUNRECOVERY exception, this - version detects most problems and tells you how to recover if it - cannot recover itself. +New JSON-RPC API Features +------------------------- -* Fixed an uninitialized variable bug that could cause transactions to - be reported out of order. +lockunspent / listlockunspent allow locking transaction outputs for a period of time so +they will not be spent by other processes that might be accessing the same wallet. -* Fixed a bug that could cause occasional crashes on exit. +addnode / getaddednodeinfo methods, to connect to specific peers without restarting. + +importprivkey now takes an optional boolean parameter (default true) to control whether +or not to rescan the blockchain for transactions after importing a new private key. + +gettxout retrieves a single transaction output from the current set of unspent outputs. +Optionally, the mempool transactions are taken into account. + +gettxoutsetinfo calculates statistics about the current set of unspent outputs. + +Important Bug Fixes +------------------- + +Privacy leak: the position of the "change" output in most transactions was not being +properly randomized, making network analysis of the transaction graph to identify +users' wallets easier. + +Zero-confirmation transaction vulnerability: accepting zero-confirmation transactions +(transactions that have not yet been included in a block) from somebody you do not +trust is still not recommended, because there will always be ways for attackers to +double-spend zero-confirmation transactions. However, this release includes a bug +fix that makes it a little bit more difficult for attackers to double-spend a +certain type ("lockTime in the future") of zero-confirmation transaction. + +Dependency Changes +------------------ -* Warn the user that they need to create fresh wallet backups after they - encrypt their wallet. +Qt 4.8.3 (compiling against older versions of Qt 4 should continue to work) diff --git a/doc/release-process.txt b/doc/release-process.txt index 708e320659..166f2aaf3c 100644 --- a/doc/release-process.txt +++ b/doc/release-process.txt @@ -5,6 +5,7 @@ src/clientversion.h (change CLIENT_VERSION_IS_RELEASE to true) share/setup.nsi doc/README* + contrib/verifysfbinaries/verify.sh * tag version in git diff --git a/share/setup.nsi b/share/setup.nsi index ead20a5c4e..307db6f623 100644 --- a/share/setup.nsi +++ b/share/setup.nsi @@ -5,7 +5,7 @@ SetCompressor /SOLID lzma # General Symbol Definitions
!define REGKEY "SOFTWARE\$(^Name)"
-!define VERSION 0.7.99
+!define VERSION 0.8.0
!define COMPANY "Bitcoin project"
!define URL http://www.bitcoin.org/
@@ -45,13 +45,13 @@ Var StartMenuGroup !insertmacro MUI_LANGUAGE English
# Installer attributes
-OutFile bitcoin-0.7.99-win32-setup.exe
+OutFile bitcoin-0.8.0-win32-setup.exe
InstallDir $PROGRAMFILES\Bitcoin
CRCCheck on
XPStyle on
BrandingText " "
ShowInstDetails show
-VIProductVersion 0.7.99.0
+VIProductVersion 0.8.0.0
VIAddVersionKey ProductName Bitcoin
VIAddVersionKey ProductVersion "${VERSION}"
VIAddVersionKey CompanyName "${COMPANY}"
diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 7751e4c8b6..230a248422 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -246,7 +246,7 @@ static const CRPCCommand vRPCCommands[] = { "getblocktemplate", &getblocktemplate, true, false }, { "submitblock", &submitblock, false, false }, { "listsinceblock", &listsinceblock, false, false }, - { "dumpprivkey", &dumpprivkey, false, false }, + { "dumpprivkey", &dumpprivkey, true, false }, { "importprivkey", &importprivkey, false, false }, { "listunspent", &listunspent, false, false }, { "getrawtransaction", &getrawtransaction, false, false }, diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index 4d2096ef07..9b11f0b351 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -14,13 +14,25 @@ namespace Checkpoints { typedef std::map<int, uint256> MapCheckpoints; - // + // How many times we expect transactions after the last checkpoint to + // be slower. This number is conservative. On multi-core CPUs with + // parallel signature checking enabled, this number is way too high. + // We prefer a progressbar that's faster at the end than the other + // way around, though. + static const double fSigcheckVerificationFactor = 15.0; + + struct CCheckpointData { + const MapCheckpoints *mapCheckpoints; + int64 nTimeLastCheckpoint; + int64 nTransactionsLastCheckpoint; + double fTransactionsPerDay; + }; + // What makes a good checkpoint block? // + Is surrounded by blocks with reasonable timestamps // (no blocks before with a timestamp after, none after with // timestamp before) // + Contains no strange transactions - // static MapCheckpoints mapCheckpoints = boost::assign::map_list_of ( 11111, uint256("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")) @@ -31,31 +43,83 @@ namespace Checkpoints (168000, uint256("0x000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763")) (193000, uint256("0x000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317")) (210000, uint256("0x000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e")) + (216116, uint256("0x00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e")) ; + static const CCheckpointData data = { + &mapCheckpoints, + 1357902690, // * UNIX timestamp of last checkpoint block + 11011160, // * total number of transactions between genesis and last checkpoint + // (the tx=... number in the SetBestChain debug.log lines) + 50000.0 // * estimated number of transactions per day after checkpoint + }; - static MapCheckpoints mapCheckpointsTestnet = + static MapCheckpoints mapCheckpointsTestnet = boost::assign::map_list_of ( 546, uint256("000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70")) ; + static const CCheckpointData dataTestnet = { + &mapCheckpointsTestnet, + 1338180505, + 16341, + 300 + }; + + const CCheckpointData &Checkpoints() { + if (fTestNet) + return dataTestnet; + else + return data; + } bool CheckBlock(int nHeight, const uint256& hash) { if (!GetBoolArg("-checkpoints", true)) return true; - MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints); + const MapCheckpoints& checkpoints = *Checkpoints().mapCheckpoints; MapCheckpoints::const_iterator i = checkpoints.find(nHeight); if (i == checkpoints.end()) return true; return hash == i->second; } + // Guess how far we are in the verification process at the given block index + double GuessVerificationProgress(CBlockIndex *pindex) { + if (pindex==NULL) + return 0.0; + + int64 nNow = time(NULL); + + double fWorkBefore = 0.0; // Amount of work done before pindex + double fWorkAfter = 0.0; // Amount of work left after pindex (estimated) + // Work is defined as: 1.0 per transaction before the last checkoint, and + // fSigcheckVerificationFactor per transaction after. + + const CCheckpointData &data = Checkpoints(); + + if (pindex->nChainTx <= data.nTransactionsLastCheckpoint) { + double nCheapBefore = pindex->nChainTx; + double nCheapAfter = data.nTransactionsLastCheckpoint - pindex->nChainTx; + double nExpensiveAfter = (nNow - data.nTimeLastCheckpoint)/86400.0*data.fTransactionsPerDay; + fWorkBefore = nCheapBefore; + fWorkAfter = nCheapAfter + nExpensiveAfter*fSigcheckVerificationFactor; + } else { + double nCheapBefore = data.nTransactionsLastCheckpoint; + double nExpensiveBefore = pindex->nChainTx - data.nTransactionsLastCheckpoint; + double nExpensiveAfter = (nNow - pindex->nTime)/86400.0*data.fTransactionsPerDay; + fWorkBefore = nCheapBefore + nExpensiveBefore*fSigcheckVerificationFactor; + fWorkAfter = nExpensiveAfter*fSigcheckVerificationFactor; + } + + return fWorkBefore / (fWorkBefore + fWorkAfter); + } + int GetTotalBlocksEstimate() { if (!GetBoolArg("-checkpoints", true)) return 0; - MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints); + const MapCheckpoints& checkpoints = *Checkpoints().mapCheckpoints; return checkpoints.rbegin()->first; } @@ -65,7 +129,7 @@ namespace Checkpoints if (!GetBoolArg("-checkpoints", true)) return NULL; - MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints); + const MapCheckpoints& checkpoints = *Checkpoints().mapCheckpoints; BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, checkpoints) { diff --git a/src/checkpoints.h b/src/checkpoints.h index 70e936564c..dcfb44e10f 100644 --- a/src/checkpoints.h +++ b/src/checkpoints.h @@ -22,6 +22,8 @@ namespace Checkpoints // Returns last CBlockIndex* in mapBlockIndex that is a checkpoint CBlockIndex* GetLastCheckpoint(const std::map<uint256, CBlockIndex*>& mapBlockIndex); + + double GuessVerificationProgress(CBlockIndex *pindex); } #endif diff --git a/src/checkqueue.h b/src/checkqueue.h index 36141dd74b..12dde36fe7 100644 --- a/src/checkqueue.h +++ b/src/checkqueue.h @@ -163,6 +163,10 @@ public: condQuit.wait(lock); } + ~CCheckQueue() { + Quit(); + } + friend class CCheckQueueControl<T>; }; diff --git a/src/clientversion.h b/src/clientversion.h index 24355d1a54..44cda07b81 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -7,12 +7,12 @@ // These need to be macros, as version.cpp's and bitcoin-qt.rc's voodoo requires it #define CLIENT_VERSION_MAJOR 0 -#define CLIENT_VERSION_MINOR 7 -#define CLIENT_VERSION_REVISION 99 +#define CLIENT_VERSION_MINOR 8 +#define CLIENT_VERSION_REVISION 0 #define CLIENT_VERSION_BUILD 0 // Set to true for release, false for prerelease or test build -#define CLIENT_VERSION_IS_RELEASE false +#define CLIENT_VERSION_IS_RELEASE true // Converts the parameter X to a string after macro replacement on X has been performed. // Don't merge these into one macro! diff --git a/src/init.cpp b/src/init.cpp index 9b9415a8ed..64f91a349f 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -82,6 +82,7 @@ void Shutdown(void* parg) if (fFirstThread) { fShutdown = true; + fRequestShutdown = true; nTransactionsUpdated++; bitdb.Flush(false); { @@ -187,9 +188,9 @@ bool AppInit(int argc, char* argv[]) fRet = AppInit2(); } catch (std::exception& e) { - PrintException(&e, "AppInit()"); + PrintExceptionContinue(&e, "AppInit()"); } catch (...) { - PrintException(NULL, "AppInit()"); + PrintExceptionContinue(NULL, "AppInit()"); } if (!fRet) Shutdown(NULL); @@ -299,6 +300,7 @@ std::string HelpMessage() " -rpcallowip=<ip> " + _("Allow JSON-RPC connections from specified IP address") + "\n" + " -rpcconnect=<ip> " + _("Send commands to node running on <ip> (default: 127.0.0.1)") + "\n" + " -blocknotify=<cmd> " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n" + + " -walletnotify=<cmd> " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n" + " -upgradewallet " + _("Upgrade wallet to latest format") + "\n" + " -keypool=<n> " + _("Set key pool size to <n> (default: 100)") + "\n" + " -rescan " + _("Rescan the block chain for missing wallet transactions") + "\n" + @@ -365,6 +367,8 @@ void ThreadImport(void *data) { pblocktree->WriteReindexing(false); fReindex = false; printf("Reindexing finished\n"); + // To avoid ending up in a situation without genesis block, re-try initializing (no-op if reindexing worked): + InitBlockIndex(); } } @@ -789,23 +793,69 @@ bool AppInit2() nTotalCache -= nCoinDBCache; nCoinCacheSize = nTotalCache / 300; // coins in memory require around 300 bytes - uiInterface.InitMessage(_("Loading block index...")); + bool fLoaded = false; + while (!fLoaded) { + bool fReset = fReindex; + std::string strLoadError; - nStart = GetTimeMillis(); - 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 block database")); + uiInterface.InitMessage(_("Loading block index...")); - uiInterface.InitMessage(_("Verifying block database integrity...")); + nStart = GetTimeMillis(); + do { + try { + UnloadBlockIndex(); + delete pcoinsTip; + delete pcoinsdbview; + delete pblocktree; + + pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex); + pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex); + pcoinsTip = new CCoinsViewCache(*pcoinsdbview); + + if (fReindex) + pblocktree->WriteReindexing(true); + + if (!LoadBlockIndex()) { + strLoadError = _("Error loading block database"); + break; + } + + // Initialize the block index (no-op if non-empty database was already loaded) + if (!InitBlockIndex()) { + strLoadError = _("Error initializing block database"); + break; + } + + uiInterface.InitMessage(_("Verifying database...")); + if (!VerifyDB()) { + strLoadError = _("Corrupted block database detected"); + break; + } + } catch(std::exception &e) { + strLoadError = _("Error opening block database"); + break; + } - if (!VerifyDB()) - return InitError(_("Corrupted block database detected. Please restart the client with -reindex.")); + fLoaded = true; + } while(false); + + if (!fLoaded) { + // first suggest a reindex + if (!fReset) { + bool fRet = uiInterface.ThreadSafeMessageBox( + strLoadError + ".\n" + _("Do you want to rebuild the block database now?"), + "", CClientUIInterface::MSG_ERROR | CClientUIInterface::BTN_ABORT); + if (fRet) { + fReindex = true; + fRequestShutdown = false; + } else { + return false; + } + } else { + return InitError(strLoadError); + } + } + } if (mapArgs.count("-txindex") && fTxIndex != GetBoolArg("-txindex", false)) return InitError(_("You need to rebuild the databases using -reindex to change -txindex")); @@ -936,7 +986,8 @@ bool AppInit2() // scan for better chains in the block chain database, that are not yet connected in the active best chain uiInterface.InitMessage(_("Importing blocks from block database...")); - if (!ConnectBestBlock()) + CValidationState state; + if (!ConnectBestBlock(state)) strErrors << "Failed to connect best block"; CImportData *pimport = new CImportData(); diff --git a/src/leveldb.cpp b/src/leveldb.cpp index 9e2f32a171..b41764f51f 100644 --- a/src/leveldb.cpp +++ b/src/leveldb.cpp @@ -12,6 +12,18 @@ #include <boost/filesystem.hpp> +void HandleError(const leveldb::Status &status) throw(leveldb_error) { + if (status.ok()) + return; + if (status.IsCorruption()) + throw leveldb_error("Database corrupted"); + if (status.IsIOError()) + throw leveldb_error("Database I/O error"); + if (status.IsNotFound()) + throw leveldb_error("Database entry missing"); + throw leveldb_error("Unknown database error"); +} + static leveldb::Options GetOptions(size_t nCacheSize) { leveldb::Options options; options.block_cache = leveldb::NewLRUCache(nCacheSize / 2); @@ -57,12 +69,12 @@ CLevelDB::~CLevelDB() { options.env = NULL; } -bool CLevelDB::WriteBatch(CLevelDBBatch &batch, bool fSync) { +bool CLevelDB::WriteBatch(CLevelDBBatch &batch, bool fSync) throw(leveldb_error) { leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch); if (!status.ok()) { printf("LevelDB write failure: %s\n", status.ToString().c_str()); + HandleError(status); return false; } return true; } - diff --git a/src/leveldb.h b/src/leveldb.h index 0b83432072..79262edbb5 100644 --- a/src/leveldb.h +++ b/src/leveldb.h @@ -11,6 +11,14 @@ #include <boost/filesystem/path.hpp> +class leveldb_error : public std::runtime_error +{ +public: + leveldb_error(const std::string &msg) : std::runtime_error(msg) {} +}; + +void HandleError(const leveldb::Status &status) throw(leveldb_error); + // Batch of changes queued to be written to a CLevelDB class CLevelDBBatch { @@ -72,7 +80,7 @@ public: 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) { + template<typename K, typename V> bool Read(const K& key, V& value) throw(leveldb_error) { CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(ssKey.GetSerializeSize(key)); ssKey << key; @@ -84,6 +92,7 @@ public: if (status.IsNotFound()) return false; printf("LevelDB read failure: %s\n", status.ToString().c_str()); + HandleError(status); } try { CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION); @@ -94,13 +103,13 @@ public: return true; } - template<typename K, typename V> bool Write(const K& key, const V& value, bool fSync = false) { + template<typename K, typename V> bool Write(const K& key, const V& value, bool fSync = false) throw(leveldb_error) { CLevelDBBatch batch; batch.Write(key, value); return WriteBatch(batch, fSync); } - template<typename K> bool Exists(const K& key) { + template<typename K> bool Exists(const K& key) throw(leveldb_error) { CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(ssKey.GetSerializeSize(key)); ssKey << key; @@ -112,24 +121,25 @@ public: if (status.IsNotFound()) return false; printf("LevelDB read failure: %s\n", status.ToString().c_str()); + HandleError(status); } return true; } - template<typename K> bool Erase(const K& key, bool fSync = false) { + template<typename K> bool Erase(const K& key, bool fSync = false) throw(leveldb_error) { CLevelDBBatch batch; batch.Erase(key); return WriteBatch(batch, fSync); } - bool WriteBatch(CLevelDBBatch &batch, bool fSync = false); + bool WriteBatch(CLevelDBBatch &batch, bool fSync = false) throw(leveldb_error); // not available for LevelDB; provide for compatibility with BDB bool Flush() { return true; } - bool Sync() { + bool Sync() throw(leveldb_error) { CLevelDBBatch batch; return WriteBatch(batch, true); } @@ -141,4 +151,3 @@ public: }; #endif // BITCOIN_LEVELDB_H -
\ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index a94fdc3090..9edd9a2990 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -93,15 +93,6 @@ void UnregisterWallet(CWallet* pwalletIn) } } -// check whether the passed transaction is from us -bool static IsFromMe(CTransaction& tx) -{ - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) - if (pwallet->IsFromMe(tx)) - return true; - return false; -} - // get the wallet transaction with the given hash (if it exists) bool static GetTransaction(const uint256& hashTx, CWalletTx& wtx) { @@ -371,6 +362,14 @@ bool CTransaction::IsStandard() const if (!IsFinal()) return false; + // Extremely large transactions with lots of inputs can cost the network + // almost as much to process as they cost the sender in fees, because + // computing signature hashes is O(ninputs*txsize). Limiting transactions + // to MAX_STANDARD_TX_SIZE mitigates CPU exhaustion attacks. + unsigned int sz = this->GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); + if (sz >= MAX_STANDARD_TX_SIZE) + return false; + BOOST_FOREACH(const CTxIn& txin, vin) { // Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG @@ -524,28 +523,28 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) -bool CTransaction::CheckTransaction() const +bool CTransaction::CheckTransaction(CValidationState &state) const { // Basic checks that don't depend on any context if (vin.empty()) - return DoS(10, error("CTransaction::CheckTransaction() : vin empty")); + return state.DoS(10, error("CTransaction::CheckTransaction() : vin empty")); if (vout.empty()) - return DoS(10, error("CTransaction::CheckTransaction() : vout empty")); + return state.DoS(10, error("CTransaction::CheckTransaction() : vout empty")); // Size limits if (::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) - return DoS(100, error("CTransaction::CheckTransaction() : size limits failed")); + return state.DoS(100, error("CTransaction::CheckTransaction() : size limits failed")); // Check for negative or overflow output values int64 nValueOut = 0; BOOST_FOREACH(const CTxOut& txout, vout) { if (txout.nValue < 0) - return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue negative")); + return state.DoS(100, error("CTransaction::CheckTransaction() : txout.nValue negative")); if (txout.nValue > MAX_MONEY) - return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue too high")); + return state.DoS(100, error("CTransaction::CheckTransaction() : txout.nValue too high")); nValueOut += txout.nValue; if (!MoneyRange(nValueOut)) - return DoS(100, error("CTransaction::CheckTransaction() : txout total out of range")); + return state.DoS(100, error("CTransaction::CheckTransaction() : txout total out of range")); } // Check for duplicate inputs @@ -553,20 +552,20 @@ bool CTransaction::CheckTransaction() const BOOST_FOREACH(const CTxIn& txin, vin) { if (vInOutPoints.count(txin.prevout)) - return false; + return state.DoS(100, error("CTransaction::CheckTransaction() : duplicate inputs")); vInOutPoints.insert(txin.prevout); } if (IsCoinBase()) { if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100) - return DoS(100, error("CTransaction::CheckTransaction() : coinbase script size")); + return state.DoS(100, error("CTransaction::CheckTransaction() : coinbase script size")); } else { BOOST_FOREACH(const CTxIn& txin, vin) if (txin.prevout.IsNull()) - return DoS(10, error("CTransaction::CheckTransaction() : prevout is null")); + return state.DoS(10, error("CTransaction::CheckTransaction() : prevout is null")); } return true; @@ -633,18 +632,18 @@ void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins) } } -bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, bool fLimitFree, +bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs) { if (pfMissingInputs) *pfMissingInputs = false; - if (!tx.CheckTransaction()) + if (!tx.CheckTransaction(state)) return error("CTxMemPool::accept() : CheckTransaction failed"); // Coinbase is only valid in a block, not as a loose transaction if (tx.IsCoinBase()) - return tx.DoS(100, error("CTxMemPool::accept() : coinbase as individual tx")); + return state.DoS(100, error("CTxMemPool::accept() : coinbase as individual tx")); // To help v0.1.5 clients who would see it as a negative number if ((int64)tx.nLockTime > std::numeric_limits<int>::max()) @@ -717,7 +716,7 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, bool fLimitFree, // are the actual inputs available? if (!tx.HaveInputs(view)) - return error("CTxMemPool::accept() : inputs already spent"); + return state.Invalid(error("CTxMemPool::accept() : inputs already spent")); // Bring the best block into scope view.GetBestBlock(); @@ -769,7 +768,7 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, bool fLimitFree, // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. - if (!tx.CheckInputs(view, true, SCRIPT_VERIFY_P2SH)) + if (!tx.CheckInputs(state, view, true, SCRIPT_VERIFY_P2SH)) { return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str()); } @@ -798,9 +797,13 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, bool fLimitFree, return true; } -bool CTransaction::AcceptToMemoryPool(bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs) +bool CTransaction::AcceptToMemoryPool(CValidationState &state, bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs) { - return mempool.accept(*this, fCheckInputs, fLimitFree, pfMissingInputs); + try { + return mempool.accept(state, *this, fCheckInputs, fLimitFree, pfMissingInputs); + } catch(std::runtime_error &e) { + return state.Abort(_("System error: ") + e.what()); + } } bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx) @@ -913,7 +916,8 @@ int CMerkleTx::GetBlocksToMaturity() const bool CMerkleTx::AcceptToMemoryPool(bool fCheckInputs, bool fLimitFree) { - return CTransaction::AcceptToMemoryPool(fCheckInputs, fLimitFree); + CValidationState state; + return CTransaction::AcceptToMemoryPool(state, fCheckInputs, fLimitFree); } @@ -1209,11 +1213,13 @@ void static InvalidBlockFound(CBlockIndex *pindex) { pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex)); setBlockIndexValid.erase(pindex); InvalidChainFound(pindex); - if (pindex->pnext) - ConnectBestBlock(); // reorganise away from the failed block + if (pindex->pnext) { + CValidationState stateDummy; + ConnectBestBlock(stateDummy); // reorganise away from the failed block + } } -bool ConnectBestBlock() { +bool ConnectBestBlock(CValidationState &state) { do { CBlockIndex *pindexNewBest; @@ -1252,8 +1258,12 @@ bool ConnectBestBlock() { BOOST_FOREACH(CBlockIndex *pindexSwitch, vAttach) { if (fRequestShutdown) break; - if (!SetBestChain(pindexSwitch)) - return false; + try { + if (!SetBestChain(state, pindexSwitch)) + return false; + } catch(std::runtime_error &e) { + return state.Abort(_("System error: ") + e.what()); + } } return true; } @@ -1315,22 +1325,20 @@ unsigned int CTransaction::GetP2SHSigOpCount(CCoinsViewCache& inputs) const return nSigOps; } -bool CTransaction::UpdateCoins(CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, const uint256 &txhash) const +bool CTransaction::UpdateCoins(CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, const uint256 &txhash) const { // mark inputs spent if (!IsCoinBase()) { BOOST_FOREACH(const CTxIn &txin, vin) { CCoins &coins = inputs.GetCoins(txin.prevout.hash); CTxInUndo undo; - if (!coins.Spend(txin.prevout, undo)) - return error("UpdateCoins() : cannot spend input"); + assert(coins.Spend(txin.prevout, undo)); txundo.vprevout.push_back(undo); } } // add outputs - if (!inputs.SetCoins(txhash, CCoins(*this, nHeight))) - return error("UpdateCoins() : cannot update output"); + assert(inputs.SetCoins(txhash, CCoins(*this, nHeight))); return true; } @@ -1368,7 +1376,7 @@ bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned in return CScriptCheck(txFrom, txTo, nIn, flags, nHashType)(); } -bool CTransaction::CheckInputs(CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, std::vector<CScriptCheck> *pvChecks) const +bool CTransaction::CheckInputs(CValidationState &state, CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, std::vector<CScriptCheck> *pvChecks) const { if (!IsCoinBase()) { @@ -1378,7 +1386,7 @@ bool CTransaction::CheckInputs(CCoinsViewCache &inputs, bool fScriptChecks, unsi // This doesn't trigger the DoS code on purpose; if it did, it would make it easier // for an attacker to attempt to split the network. if (!HaveInputs(inputs)) - return error("CheckInputs() : %s inputs unavailable", GetHash().ToString().substr(0,10).c_str()); + return state.Invalid(error("CheckInputs() : %s inputs unavailable", GetHash().ToString().substr(0,10).c_str())); // While checking, GetBestBlock() refers to the parent block. // This is also true for mempool checks. @@ -1393,26 +1401,26 @@ bool CTransaction::CheckInputs(CCoinsViewCache &inputs, bool fScriptChecks, unsi // If prev is coinbase, check that it's matured if (coins.IsCoinBase()) { if (nSpendHeight - coins.nHeight < COINBASE_MATURITY) - return error("CheckInputs() : tried to spend coinbase at depth %d", nSpendHeight - coins.nHeight); + return state.Invalid(error("CheckInputs() : tried to spend coinbase at depth %d", nSpendHeight - coins.nHeight)); } // Check for negative or overflow input values nValueIn += coins.vout[prevout.n].nValue; if (!MoneyRange(coins.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) - return DoS(100, error("CheckInputs() : txin values out of range")); + return state.DoS(100, error("CheckInputs() : txin values out of range")); } if (nValueIn < GetValueOut()) - return DoS(100, error("ChecktInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str())); + return state.DoS(100, error("CheckInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str())); // Tally transaction fees int64 nTxFee = nValueIn - GetValueOut(); if (nTxFee < 0) - return DoS(100, error("CheckInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str())); + return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str())); nFees += nTxFee; if (!MoneyRange(nFees)) - return DoS(100, error("CheckInputs() : nFees out of range")); + return state.DoS(100, error("CheckInputs() : nFees out of range")); // The first loop above does all the inexpensive checks. // Only if ALL inputs pass do we perform expensive ECDSA signature checks. @@ -1432,7 +1440,7 @@ bool CTransaction::CheckInputs(CCoinsViewCache &inputs, bool fScriptChecks, unsi pvChecks->push_back(CScriptCheck()); check.swap(pvChecks->back()); } else if (!check()) - return DoS(100,false); + return state.DoS(100,false); } } } @@ -1441,56 +1449,9 @@ bool CTransaction::CheckInputs(CCoinsViewCache &inputs, bool fScriptChecks, unsi } -bool CTransaction::ClientCheckInputs() const -{ - if (IsCoinBase()) - return false; - - // Take over previous transactions' spent pointers - { - LOCK(mempool.cs); - int64 nValueIn = 0; - for (unsigned int i = 0; i < vin.size(); i++) - { - // Get prev tx from single transactions in memory - COutPoint prevout = vin[i].prevout; - if (!mempool.exists(prevout.hash)) - return false; - CTransaction& txPrev = mempool.lookup(prevout.hash); - - if (prevout.n >= txPrev.vout.size()) - return false; - - // Verify signature - if (!VerifySignature(CCoins(txPrev, -1), *this, i, SCRIPT_VERIFY_P2SH, 0)) - return error("ConnectInputs() : VerifySignature failed"); - - ///// this is redundant with the mempool.mapNextTx stuff, - ///// not sure which I want to get rid of - ///// this has to go away now that posNext is gone - // // Check for conflicts - // if (!txPrev.vout[prevout.n].posNext.IsNull()) - // return error("ConnectInputs() : prev tx already used"); - // - // // Flag outpoints as used - // txPrev.vout[prevout.n].posNext = posThisTx; - - nValueIn += txPrev.vout[prevout.n].nValue; - - if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) - return error("ClientConnectInputs() : txin values out of range"); - } - if (GetValueOut() > nValueIn) - return false; - } - - return true; -} - - -bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &view, bool *pfClean) +bool CBlock::DisconnectBlock(CValidationState &state, CBlockIndex *pindex, CCoinsViewCache &view, bool *pfClean) { assert(pindex == view.GetBestBlock()); @@ -1595,7 +1556,7 @@ void static FlushBlockFile(bool fFinalize = false) } } -bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize); +bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize); static CCheckQueue<CScriptCheck> scriptcheckqueue(128); @@ -1610,10 +1571,10 @@ void ThreadScriptCheckQuit() { scriptcheckqueue.Quit(); } -bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJustCheck) +bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsViewCache &view, bool fJustCheck) { // Check it again in case a previous version let a bad block in - if (!CheckBlock(!fJustCheck, !fJustCheck)) + if (!CheckBlock(state, !fJustCheck, !fJustCheck)) return false; // verify that the view's current state corresponds to the previous block @@ -1648,7 +1609,7 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust for (unsigned int i=0; i<vtx.size(); i++) { uint256 hash = GetTxHash(i); if (view.HaveCoins(hash) && !view.GetCoins(hash).IsPruned()) - return error("ConnectBlock() : tried to overwrite transaction"); + return state.DoS(100, error("ConnectBlock() : tried to overwrite transaction")); } } @@ -1678,12 +1639,12 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust nInputs += tx.vin.size(); nSigOps += tx.GetLegacySigOpCount(); if (nSigOps > MAX_BLOCK_SIGOPS) - return DoS(100, error("ConnectBlock() : too many sigops")); + return state.DoS(100, error("ConnectBlock() : too many sigops")); if (!tx.IsCoinBase()) { if (!tx.HaveInputs(view)) - return DoS(100, error("ConnectBlock() : inputs missing/spent")); + return state.DoS(100, error("ConnectBlock() : inputs missing/spent")); if (fStrictPayToScriptHash) { @@ -1692,19 +1653,19 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust // an incredibly-expensive-to-validate block. nSigOps += tx.GetP2SHSigOpCount(view); if (nSigOps > MAX_BLOCK_SIGOPS) - return DoS(100, error("ConnectBlock() : too many sigops")); + return state.DoS(100, error("ConnectBlock() : too many sigops")); } nFees += tx.GetValueIn(view)-tx.GetValueOut(); std::vector<CScriptCheck> vChecks; - if (!tx.CheckInputs(view, fScriptChecks, flags, nScriptCheckThreads ? &vChecks : NULL)) + if (!tx.CheckInputs(state, view, fScriptChecks, flags, nScriptCheckThreads ? &vChecks : NULL)) return false; control.Add(vChecks); } CTxUndo txundo; - if (!tx.UpdateCoins(view, txundo, pindex->nHeight, GetTxHash(i))) + if (!tx.UpdateCoins(state, view, txundo, pindex->nHeight, GetTxHash(i))) return error("ConnectBlock() : UpdateInputs failed"); if (!tx.IsCoinBase()) blockundo.vtxundo.push_back(txundo); @@ -1717,10 +1678,10 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust printf("- Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin)\n", (unsigned)vtx.size(), 0.001 * nTime, 0.001 * nTime / vtx.size(), nInputs <= 1 ? 0 : 0.001 * nTime / (nInputs-1)); if (vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees)) - return error("ConnectBlock() : coinbase pays too much (actual=%"PRI64d" vs limit=%"PRI64d")", vtx[0].GetValueOut(), GetBlockValue(pindex->nHeight, nFees)); + return state.DoS(100, error("ConnectBlock() : coinbase pays too much (actual=%"PRI64d" vs limit=%"PRI64d")", vtx[0].GetValueOut(), GetBlockValue(pindex->nHeight, nFees))); if (!control.Wait()) - return DoS(100, false); + return state.DoS(100, false); int64 nTime2 = GetTimeMicros() - nStart; if (fBenchmark) printf("- Verify %u txins: %.2fms (%.3fms/txin)\n", nInputs - 1, 0.001 * nTime2, nInputs <= 1 ? 0 : 0.001 * nTime2 / (nInputs-1)); @@ -1733,10 +1694,10 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust { if (pindex->GetUndoPos().IsNull()) { CDiskBlockPos pos; - if (!FindUndoPos(pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40)) + if (!FindUndoPos(state, pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40)) return error("ConnectBlock() : FindUndoPos failed"); if (!blockundo.WriteToDisk(pos, pindex->pprev->GetBlockHash())) - return error("ConnectBlock() : CBlockUndo::WriteToDisk failed"); + return state.Abort(_("Failed to write undo data")); // update nUndoPos in block index pindex->nUndoPos = pos.nPos; @@ -1747,15 +1708,15 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust CDiskBlockIndex blockindex(pindex); if (!pblocktree->WriteBlockIndex(blockindex)) - return error("ConnectBlock() : WriteBlockIndex failed"); + return state.Abort(_("Failed to write block index")); } if (fTxIndex) - pblocktree->WriteTxIndex(vPos); + if (!pblocktree->WriteTxIndex(vPos)) + return state.Abort(_("Failed to write transaction index")); // add this block to the view's block chain - if (!view.SetBestBlock(pindex)) - return false; + assert(view.SetBestBlock(pindex)); // Watch for transactions paying to me for (unsigned int i=0; i<vtx.size(); i++) @@ -1764,7 +1725,7 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust return true; } -bool SetBestChain(CBlockIndex* pindexNew) +bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) { // All modifications to the coin state will be done in this cache. // Only when all have succeeded, we push it to pcoinsTip. @@ -1775,13 +1736,14 @@ bool SetBestChain(CBlockIndex* pindexNew) CBlockIndex* plonger = pindexNew; while (pfork && pfork != plonger) { - while (plonger->nHeight > pfork->nHeight) - if (!(plonger = plonger->pprev)) - return error("SetBestChain() : plonger->pprev is null"); + while (plonger->nHeight > pfork->nHeight) { + plonger = plonger->pprev; + assert(plonger != NULL); + } if (pfork == plonger) break; - if (!(pfork = pfork->pprev)) - return error("SetBestChain() : pfork->pprev is null"); + pfork = pfork->pprev; + assert(pfork != NULL); } // List of what to disconnect (typically nothing) @@ -1805,9 +1767,9 @@ bool SetBestChain(CBlockIndex* pindexNew) BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) { CBlock block; if (!block.ReadFromDisk(pindex)) - return error("SetBestBlock() : ReadFromDisk for disconnect failed"); + return state.Abort(_("Failed to read block")); int64 nStart = GetTimeMicros(); - if (!block.DisconnectBlock(pindex, view)) + if (!block.DisconnectBlock(state, pindex, view)) return error("SetBestBlock() : DisconnectBlock %s failed", BlockHashStr(pindex->GetBlockHash()).c_str()); if (fBenchmark) printf("- Disconnect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); @@ -1825,11 +1787,13 @@ bool SetBestChain(CBlockIndex* pindexNew) BOOST_FOREACH(CBlockIndex *pindex, vConnect) { CBlock block; if (!block.ReadFromDisk(pindex)) - return error("SetBestBlock() : ReadFromDisk for connect failed"); + return state.Abort(_("Failed to read block")); int64 nStart = GetTimeMicros(); - if (!block.ConnectBlock(pindex, view)) { - InvalidChainFound(pindexNew); - InvalidBlockFound(pindex); + if (!block.ConnectBlock(state, pindex, view)) { + if (state.IsInvalid()) { + InvalidChainFound(pindexNew); + InvalidBlockFound(pindex); + } return error("SetBestBlock() : ConnectBlock %s failed", BlockHashStr(pindex->GetBlockHash()).c_str()); } if (fBenchmark) @@ -1843,8 +1807,7 @@ bool SetBestChain(CBlockIndex* pindexNew) // Flush changes to global coin state int64 nStart = GetTimeMicros(); int nModified = view.GetCacheSize(); - if (!view.Flush()) - return error("SetBestBlock() : unable to modify coin state"); + assert(view.Flush()); int64 nTime = GetTimeMicros() - nStart; if (fBenchmark) printf("- Flush %i transactions: %.2fms (%.4fms/tx)\n", nModified, 0.001 * nTime, 0.001 * nTime / nModified); @@ -1852,10 +1815,17 @@ bool SetBestChain(CBlockIndex* pindexNew) // Make sure it's successfully written to disk before changing memory structure bool fIsInitialDownload = IsInitialBlockDownload(); if (!fIsInitialDownload || pcoinsTip->GetCacheSize() > nCoinCacheSize) { + // Typical CCoins structures on disk are around 100 bytes in size. + // Pushing a new one to the database can cause it to be written + // twice (once in the log, and once in the tables). This is already + // an overestimation, as most will delete an existing entry or + // overwrite one. Still, use a conservative safety factor of 2. + if (!CheckDiskSpace(100 * 2 * 2 * pcoinsTip->GetCacheSize())) + return state.Error(); FlushBlockFile(); pblocktree->Sync(); if (!pcoinsTip->Flush()) - return false; + return state.Abort(_("Failed to write to coin database")); } // At this point, all changes have been done to the database. @@ -1872,8 +1842,11 @@ bool SetBestChain(CBlockIndex* pindexNew) pindex->pprev->pnext = pindex; // Resurrect memory transactions that were in the disconnected branch - BOOST_FOREACH(CTransaction& tx, vResurrect) - tx.AcceptToMemoryPool(true, false); + BOOST_FOREACH(CTransaction& tx, vResurrect) { + // ignore validation errors in resurrected transactions + CValidationState stateDummy; + tx.AcceptToMemoryPool(stateDummy, true, false); + } // Delete redundant memory transactions that are in the connected branch BOOST_FOREACH(CTransaction& tx, vDelete) { @@ -1930,17 +1903,16 @@ bool SetBestChain(CBlockIndex* pindexNew) } -bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos) +bool CBlock::AddToBlockIndex(CValidationState &state, const CDiskBlockPos &pos) { // Check for duplicate uint256 hash = GetHash(); if (mapBlockIndex.count(hash)) - return error("AddToBlockIndex() : %s already exists", BlockHashStr(hash).c_str()); + return state.Invalid(error("AddToBlockIndex() : %s already exists", BlockHashStr(hash).c_str())); // Construct new block index object CBlockIndex* pindexNew = new CBlockIndex(*this); - if (!pindexNew) - return error("AddToBlockIndex() : new CBlockIndex failed"); + assert(pindexNew); map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; pindexNew->phashBlock = &((*mi).first); map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(hashPrevBlock); @@ -1958,10 +1930,11 @@ bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos) pindexNew->nStatus = BLOCK_VALID_TRANSACTIONS | BLOCK_HAVE_DATA; setBlockIndexValid.insert(pindexNew); - pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew)); + if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew))) + return state.Abort(_("Failed to write block index")); // New best? - if (!ConnectBestBlock()) + if (!ConnectBestBlock(state)) return false; if (pindexNew == pindexBest) @@ -1972,14 +1945,15 @@ bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos) hashPrevBestCoinBase = GetTxHash(0); } - pblocktree->Flush(); + if (!pblocktree->Flush()) + return state.Abort(_("Failed to sync block index")); uiInterface.NotifyBlocksChanged(); return true; } -bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64 nTime, bool fKnown = false) +bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64 nTime, bool fKnown = false) { bool fUpdatedLast = false; @@ -2021,19 +1995,19 @@ bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeigh } } else - return error("FindBlockPos() : out of disk space"); + return state.Error(); } } if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile)) - return error("FindBlockPos() : cannot write updated block info"); + return state.Abort(_("Failed to write file info")); if (fUpdatedLast) pblocktree->WriteLastBlockFile(nLastBlockFile); return true; } -bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize) +bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize) { pos.nFile = nFile; @@ -2044,15 +2018,15 @@ bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize) pos.nPos = infoLastBlockFile.nUndoSize; nNewSize = (infoLastBlockFile.nUndoSize += nAddSize); if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile)) - return error("FindUndoPos() : cannot write updated block info"); + return state.Abort(_("Failed to write block info")); } else { CBlockFileInfo info; if (!pblocktree->ReadBlockFileInfo(nFile, info)) - return error("FindUndoPos() : cannot read block info"); + return state.Abort(_("Failed to read block info")); pos.nPos = info.nUndoSize; nNewSize = (info.nUndoSize += nAddSize); if (!pblocktree->WriteBlockFileInfo(nFile, info)) - return error("FindUndoPos() : cannot write updated block info"); + return state.Abort(_("Failed to write block info")); } unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; @@ -2067,41 +2041,41 @@ bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize) } } else - return error("FindUndoPos() : out of disk space"); + return state.Error(); } return true; } -bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const +bool CBlock::CheckBlock(CValidationState &state, bool fCheckPOW, bool fCheckMerkleRoot) const { // These are checks that are independent of context // that can be verified before saving an orphan block. // Size limits if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) - return DoS(100, error("CheckBlock() : size limits failed")); + return state.DoS(100, error("CheckBlock() : size limits failed")); // Check proof of work matches claimed amount if (fCheckPOW && !CheckProofOfWork(GetHash(), nBits)) - return DoS(50, error("CheckBlock() : proof of work failed")); + return state.DoS(50, error("CheckBlock() : proof of work failed")); // Check timestamp if (GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) - return error("CheckBlock() : block timestamp too far in the future"); + return state.Invalid(error("CheckBlock() : block timestamp too far in the future")); // First transaction must be coinbase, the rest must not be if (vtx.empty() || !vtx[0].IsCoinBase()) - return DoS(100, error("CheckBlock() : first tx is not coinbase")); + return state.DoS(100, error("CheckBlock() : first tx is not coinbase")); for (unsigned int i = 1; i < vtx.size(); i++) if (vtx[i].IsCoinBase()) - return DoS(100, error("CheckBlock() : more than one coinbase")); + return state.DoS(100, error("CheckBlock() : more than one coinbase")); // Check transactions BOOST_FOREACH(const CTransaction& tx, vtx) - if (!tx.CheckTransaction()) - return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed")); + if (!tx.CheckTransaction(state)) + return 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 @@ -2115,7 +2089,7 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const uniqueTx.insert(GetTxHash(i)); } if (uniqueTx.size() != vtx.size()) - return DoS(100, error("CheckBlock() : duplicate transaction")); + return state.DoS(100, error("CheckBlock() : duplicate transaction")); unsigned int nSigOps = 0; BOOST_FOREACH(const CTransaction& tx, vtx) @@ -2123,21 +2097,21 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const nSigOps += tx.GetLegacySigOpCount(); } if (nSigOps > MAX_BLOCK_SIGOPS) - return DoS(100, error("CheckBlock() : out-of-bounds SigOpCount")); + return state.DoS(100, error("CheckBlock() : out-of-bounds SigOpCount")); // Check merkle root if (fCheckMerkleRoot && hashMerkleRoot != BuildMerkleTree()) - return DoS(100, error("CheckBlock() : hashMerkleRoot mismatch")); + return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch")); return true; } -bool CBlock::AcceptBlock(CDiskBlockPos *dbp) +bool CBlock::AcceptBlock(CValidationState &state, CDiskBlockPos *dbp) { // Check for duplicate uint256 hash = GetHash(); if (mapBlockIndex.count(hash)) - return error("AcceptBlock() : block already in mapBlockIndex"); + return state.Invalid(error("AcceptBlock() : block already in mapBlockIndex")); // Get prev block index CBlockIndex* pindexPrev = NULL; @@ -2145,26 +2119,26 @@ bool CBlock::AcceptBlock(CDiskBlockPos *dbp) if (hash != hashGenesisBlock) { map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashPrevBlock); if (mi == mapBlockIndex.end()) - return DoS(10, error("AcceptBlock() : prev block not found")); + return state.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")); + return state.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"); + return state.Invalid(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")); + return state.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)); + return state.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) @@ -2172,7 +2146,7 @@ bool CBlock::AcceptBlock(CDiskBlockPos *dbp) if ((!fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 950, 1000)) || (fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 75, 100))) { - return error("AcceptBlock() : rejected nVersion=1 block"); + return state.Invalid(error("AcceptBlock() : rejected nVersion=1 block")); } } // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height @@ -2184,23 +2158,27 @@ bool CBlock::AcceptBlock(CDiskBlockPos *dbp) { 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")); + return state.DoS(100, error("AcceptBlock() : block height mismatch in coinbase")); } } } // Write block to history file - unsigned int nBlockSize = ::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION); - CDiskBlockPos blockPos; - if (dbp != NULL) - blockPos = *dbp; - if (!FindBlockPos(blockPos, nBlockSize+8, nHeight, nTime, dbp != NULL)) - return error("AcceptBlock() : FindBlockPos failed"); - if (dbp == NULL) - if (!WriteToDisk(blockPos)) - return error("AcceptBlock() : WriteToDisk failed"); - if (!AddToBlockIndex(blockPos)) - return error("AcceptBlock() : AddToBlockIndex failed"); + try { + unsigned int nBlockSize = ::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION); + CDiskBlockPos blockPos; + if (dbp != NULL) + blockPos = *dbp; + if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, nTime, dbp != NULL)) + return error("AcceptBlock() : FindBlockPos failed"); + if (dbp == NULL) + if (!WriteToDisk(blockPos)) + return state.Abort(_("Failed to write block")); + if (!AddToBlockIndex(state, blockPos)) + return error("AcceptBlock() : AddToBlockIndex failed"); + } catch(std::runtime_error &e) { + return state.Abort(_("System error: ") + e.what()); + } // Relay inventory, but don't relay old inventory during initial block download int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(); @@ -2227,17 +2205,17 @@ bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, uns return (nFound >= nRequired); } -bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) +bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) { // Check for duplicate uint256 hash = pblock->GetHash(); if (mapBlockIndex.count(hash)) - return error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, BlockHashStr(hash).c_str()); + return state.Invalid(error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, BlockHashStr(hash).c_str())); if (mapOrphanBlocks.count(hash)) - return error("ProcessBlock() : already have block (orphan) %s", BlockHashStr(hash).c_str()); + return state.Invalid(error("ProcessBlock() : already have block (orphan) %s", BlockHashStr(hash).c_str())); // Preliminary checks - if (!pblock->CheckBlock()) + if (!pblock->CheckBlock(state)) return error("ProcessBlock() : CheckBlock FAILED"); CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); @@ -2247,9 +2225,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) int64 deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime; if (deltaTime < 0) { - if (pfrom) - pfrom->Misbehaving(100); - return error("ProcessBlock() : block with timestamp before last checkpoint"); + return state.DoS(100, error("ProcessBlock() : block with timestamp before last checkpoint")); } CBigNum bnNewBlock; bnNewBlock.SetCompact(pblock->nBits); @@ -2257,9 +2233,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime)); if (bnNewBlock > bnRequired) { - if (pfrom) - pfrom->Misbehaving(100); - return error("ProcessBlock() : block with too little proof-of-work"); + return state.DoS(100, error("ProcessBlock() : block with too little proof-of-work")); } } @@ -2282,7 +2256,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) } // Store to disk - if (!pblock->AcceptBlock(dbp)) + if (!pblock->AcceptBlock(state, dbp)) return error("ProcessBlock() : AcceptBlock FAILED"); // Recursively process any orphan blocks that depended on this one @@ -2296,7 +2270,9 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) ++mi) { CBlock* pblockOrphan = (*mi).second; - if (pblockOrphan->AcceptBlock()) + // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan resolution (that is, feeding people an invalid block based on LegitBlockX in order to get anyone relaying LegitBlockX banned) + CValidationState stateDummy; + if (pblockOrphan->AcceptBlock(stateDummy)) vWorkQueue.push_back(pblockOrphan->GetHash()); mapOrphanBlocks.erase(pblockOrphan->GetHash()); delete pblockOrphan; @@ -2468,6 +2444,14 @@ uint256 CPartialMerkleTree::ExtractMatches(std::vector<uint256> &vMatch) { +bool AbortNode(const std::string &strMessage) { + fRequestShutdown = true; + strMiscWarning = strMessage; + printf("*** %s\n", strMessage.c_str()); + uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_ERROR | CClientUIInterface::MODAL); + StartShutdown(); + return false; +} bool CheckDiskSpace(uint64 nAdditionalBytes) { @@ -2475,15 +2459,8 @@ bool CheckDiskSpace(uint64 nAdditionalBytes) // Check for nMinDiskSpace bytes (currently 50MB) if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes) - { - fShutdown = true; - string strMessage = _("Error: Disk space is low!"); - strMiscWarning = strMessage; - printf("*** %s\n", strMessage.c_str()); - uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_ERROR); - StartShutdown(); - return false; - } + return AbortNode(_("Error: Disk space is low!")); + return true; } @@ -2625,6 +2602,7 @@ bool VerifyDB() { CBlockIndex* pindexState = pindexBest; CBlockIndex* pindexFailure = NULL; int nGoodTransactions = 0; + CValidationState state; for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev) { if (fRequestShutdown || pindex->nHeight < nBestHeight-nCheckDepth) @@ -2634,7 +2612,7 @@ bool VerifyDB() { if (!block.ReadFromDisk(pindex)) return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); // check level 1: verify block validity - if (nCheckLevel >= 1 && !block.CheckBlock()) + if (nCheckLevel >= 1 && !block.CheckBlock(state)) return error("VerifyDB() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); // check level 2: verify undo validity if (nCheckLevel >= 2 && pindex) { @@ -2648,7 +2626,7 @@ bool VerifyDB() { // check level 3: check for inconsistencies during memory-only disconnect of tip blocks if (nCheckLevel >= 3 && pindex == pindexState && (coins.GetCacheSize() + pcoinsTip->GetCacheSize()) <= 2*nCoinCacheSize + 32000) { bool fClean = true; - if (!block.DisconnectBlock(pindex, coins, &fClean)) + if (!block.DisconnectBlock(state, pindex, coins, &fClean)) return error("VerifyDB() : *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); pindexState = pindex->pprev; if (!fClean) { @@ -2669,7 +2647,7 @@ bool VerifyDB() { CBlock block; if (!block.ReadFromDisk(pindex)) return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); - if (!block.ConnectBlock(pindex, coins)) + if (!block.ConnectBlock(state, pindex, coins)) return error("VerifyDB() : *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); } } @@ -2679,6 +2657,18 @@ bool VerifyDB() { return true; } +void UnloadBlockIndex() +{ + mapBlockIndex.clear(); + setBlockIndexValid.clear(); + pindexGenesisBlock = NULL; + nBestHeight = 0; + bnBestChainWork = 0; + bnBestInvalidWork = 0; + hashBestChain = 0; + pindexBest = NULL; +} + bool LoadBlockIndex() { if (fTestNet) @@ -2696,18 +2686,22 @@ bool LoadBlockIndex() if (!fReindex && !LoadBlockIndexDB()) return false; - // - // Init with genesis block - // - if (mapBlockIndex.empty()) - { - fTxIndex = GetBoolArg("-txindex", false); - pblocktree->WriteFlag("txindex", fTxIndex); - printf("Initializing databases...\n"); + return true; +} - if (fReindex) - return true; +bool InitBlockIndex() { + // Check whether we're already initialized + if (pindexGenesisBlock != NULL) + return true; + + // Use the provided setting for -txindex in the new database + fTxIndex = GetBoolArg("-txindex", false); + pblocktree->WriteFlag("txindex", fTxIndex); + printf("Initializing databases...\n"); + + // Only add the genesis block if not reindexing (in which case we reuse the one already on disk) + if (!fReindex) { // 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) @@ -2748,14 +2742,19 @@ bool LoadBlockIndex() assert(hash == hashGenesisBlock); // Start new block file - unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); - CDiskBlockPos blockPos; - if (!FindBlockPos(blockPos, nBlockSize+8, 0, block.nTime)) - return error("AcceptBlock() : FindBlockPos failed"); - if (!block.WriteToDisk(blockPos)) - return error("LoadBlockIndex() : writing genesis block to disk failed"); - if (!block.AddToBlockIndex(blockPos)) - return error("LoadBlockIndex() : genesis block not accepted"); + try { + unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); + CDiskBlockPos blockPos; + CValidationState state; + if (!FindBlockPos(state, blockPos, nBlockSize+8, 0, block.nTime)) + return error("AcceptBlock() : FindBlockPos failed"); + if (!block.WriteToDisk(blockPos)) + return error("LoadBlockIndex() : writing genesis block to disk failed"); + if (!block.AddToBlockIndex(state, blockPos)) + return error("LoadBlockIndex() : genesis block not accepted"); + } catch(std::runtime_error &e) { + return error("LoadBlockIndex() : failed to initialize block database: %s", e.what()); + } } return true; @@ -2838,7 +2837,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) int64 nStart = GetTimeMillis(); int nLoaded = 0; - { + try { CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SIZE, MAX_BLOCK_SIZE+8, SER_DISK, CLIENT_VERSION); uint64 nStartByte = 0; if (dbp) { @@ -2884,14 +2883,19 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) LOCK(cs_main); if (dbp) dbp->nPos = nBlockPos; - if (ProcessBlock(NULL, &block, dbp)) + CValidationState state; + if (ProcessBlock(state, NULL, &block, dbp)) nLoaded++; + if (state.IsError()) + break; } } catch (std::exception &e) { printf("%s() : Deserialize or I/O error caught during load\n", __PRETTY_FUNCTION__); } } fclose(fileIn); + } catch(std::runtime_error &e) { + AbortNode(_("Error: system error: ") + e.what()); } if (nLoaded > 0) printf("Loaded %i blocks from external file in %"PRI64d"ms\n", nLoaded, GetTimeMillis() - nStart); @@ -3461,7 +3465,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->AddInventoryKnown(inv); bool fMissingInputs = false; - if (tx.AcceptToMemoryPool(true, true, &fMissingInputs)) + CValidationState state; + if (tx.AcceptToMemoryPool(state, true, true, &fMissingInputs)) { RelayTransaction(tx, inv.hash, vMsg); mapAlreadyAskedFor.erase(inv); @@ -3481,8 +3486,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CDataStream(vMsg) >> tx; CInv inv(MSG_TX, tx.GetHash()); bool fMissingInputs2 = false; + // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get anyone relaying LegitTxX banned) + CValidationState stateDummy; - if (tx.AcceptToMemoryPool(true, true, &fMissingInputs2)) + if (tx.AcceptToMemoryPool(stateDummy, true, true, &fMissingInputs2)) { printf(" accepted orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str()); RelayTransaction(tx, inv.hash, vMsg); @@ -3511,7 +3518,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (nEvicted > 0) printf("mapOrphan overflow, removed %u tx\n", nEvicted); } - if (tx.nDoS) pfrom->Misbehaving(tx.nDoS); + int nDoS; + if (state.IsInvalid(nDoS)) + pfrom->Misbehaving(nDoS); } @@ -3526,9 +3535,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CInv inv(MSG_BLOCK, block.GetHash()); pfrom->AddInventoryKnown(inv); - if (ProcessBlock(pfrom, &block)) + CValidationState state; + if (ProcessBlock(state, pfrom, &block)) mapAlreadyAskedFor.erase(inv); - if (block.nDoS) pfrom->Misbehaving(block.nDoS); + int nDoS; + if (state.IsInvalid(nDoS)) + pfrom->Misbehaving(nDoS); } @@ -3816,7 +3828,12 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } // Resend wallet transactions that haven't gotten in a block yet - ResendWalletTransactions(); + // Except during reindex, importing and IBD, when old wallet + // transactions become unconfirmed and spams other nodes. + if (!fReindex && !fImporting && !IsInitialBlockDownload()) + { + ResendWalletTransactions(); + } // Address refresh broadcast static int64 nLastRebroadcast; @@ -4278,12 +4295,13 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey) if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) continue; - if (!tx.CheckInputs(viewTemp, true, SCRIPT_VERIFY_P2SH)) + CValidationState state; + if (!tx.CheckInputs(state, viewTemp, true, SCRIPT_VERIFY_P2SH)) continue; CTxUndo txundo; uint256 hash = tx.GetHash(); - if (!tx.UpdateCoins(viewTemp, txundo, pindexPrev->nHeight+1, hash)) + if (!tx.UpdateCoins(state, viewTemp, txundo, pindexPrev->nHeight+1, hash)) continue; // push changes from the second layer cache to the first one @@ -4341,7 +4359,8 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey) indexDummy.pprev = pindexPrev; indexDummy.nHeight = pindexPrev->nHeight + 1; CCoinsViewCache viewNew(*pcoinsTip, true); - if (!pblock->ConnectBlock(&indexDummy, viewNew, true)) + CValidationState state; + if (!pblock->ConnectBlock(state, &indexDummy, viewNew, true)) throw std::runtime_error("CreateNewBlock() : ConnectBlock failed"); } @@ -4443,7 +4462,8 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) } // Process this block the same as if we had received it from another node - if (!ProcessBlock(NULL, pblock)) + CValidationState state; + if (!ProcessBlock(state, NULL, pblock)) return error("BitcoinMiner : ProcessBlock, block not accepted"); } diff --git a/src/main.h b/src/main.h index 9dbcac0b5a..4a217d1746 100644 --- a/src/main.h +++ b/src/main.h @@ -28,6 +28,8 @@ struct CBlockIndexWorkComparator; 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 size for transactions we're willing to relay/mine **/ +static const unsigned int MAX_STANDARD_TX_SIZE = MAX_BLOCK_SIZE_GEN/5; /** 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 */ @@ -106,15 +108,17 @@ static const uint64 nMinDiskSpace = 52428800; class CReserveKey; class CCoinsDB; class CBlockTreeDB; -class CDiskBlockPos; +struct CDiskBlockPos; class CCoins; class CTxUndo; class CCoinsView; class CCoinsViewCache; class CScriptCheck; +class CValidationState; struct CBlockTemplate; + /** Register a wallet to receive updates from core */ void RegisterWallet(CWallet* pwalletIn); /** Unregister a wallet from core */ @@ -122,7 +126,7 @@ 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); /** Process an incoming block */ -bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp = NULL); +bool ProcessBlock(CValidationState &state, 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) */ @@ -131,8 +135,12 @@ FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly = false); FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false); /** Import blocks from an external file */ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp = NULL); +/** Initialize a new block tree database + block data on disk */ +bool InitBlockIndex(); /** Load the block tree and coins database from disk */ bool LoadBlockIndex(); +/** Unload database information */ +void UnloadBlockIndex(); /** Verify consistency of the block and coin databases */ bool VerifyDB(); /** Print the loaded block tree */ @@ -172,13 +180,15 @@ 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); +bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew); /** Find the best known block, and make it the tip of the block chain */ -bool ConnectBestBlock(); +bool ConnectBestBlock(CValidationState &state); /** Create a new block index entry for a given block hash */ CBlockIndex * InsertBlockIndex(uint256 hash); /** Verify a signature */ bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); +/** Abort with a message */ +bool AbortNode(const std::string &msg); @@ -454,7 +464,6 @@ public: - enum GetMinFee_mode { GMF_BLOCK, @@ -474,10 +483,6 @@ public: std::vector<CTxOut> vout; unsigned int nLockTime; - // Denial-of-service detection: - mutable int nDoS; - bool DoS(int nDoSIn, bool fIn) const { nDoS += nDoSIn; return fIn; } - CTransaction() { SetNull(); @@ -498,7 +503,6 @@ public: vin.clear(); vout.clear(); nLockTime = 0; - nDoS = 0; // Denial-of-service prevention } bool IsNull() const @@ -654,27 +658,24 @@ public: } - // Do all possible client-mode checks - bool ClientCheckInputs() const; - // Check whether all prevouts of this transaction are present in the UTXO set represented by view bool HaveInputs(CCoinsViewCache &view) const; // Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts) // This does not modify the UTXO set. If pvChecks is not NULL, script checks are pushed onto it // instead of being performed inline. - bool CheckInputs(CCoinsViewCache &view, bool fScriptChecks = true, + bool CheckInputs(CValidationState &state, CCoinsViewCache &view, bool fScriptChecks = true, unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, std::vector<CScriptCheck> *pvChecks = NULL) 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; + bool UpdateCoins(CValidationState &state, CCoinsViewCache &view, CTxUndo &txundo, int nHeight, const uint256 &txhash) const; // Context-independent validity checks - bool CheckTransaction() const; + bool CheckTransaction(CValidationState &state) const; // Try to accept this transaction into the memory pool - bool AcceptToMemoryPool(bool fCheckInputs=true, bool fLimitFree = true, bool* pfMissingInputs=NULL); + bool AcceptToMemoryPool(CValidationState &state, bool fCheckInputs=true, bool fLimitFree = true, bool* pfMissingInputs=NULL); protected: static const CTxOut &GetOutputFor(const CTxIn& input, CCoinsViewCache& mapInputs); @@ -1320,10 +1321,6 @@ public: // memory only mutable std::vector<uint256> vMerkleTree; - // Denial-of-service detection: - mutable int nDoS; - bool DoS(int nDoSIn, bool fIn) const { nDoS += nDoSIn; return fIn; } - CBlock() { SetNull(); @@ -1346,7 +1343,6 @@ public: CBlockHeader::SetNull(); vtx.clear(); vMerkleTree.clear(); - nDoS = 0; } CBlockHeader GetBlockHeader() const @@ -1494,23 +1490,23 @@ public: * In case pfClean is provided, operation will try to be tolerant about errors, and *pfClean * will be true if no problems were found. Otherwise, the return value will be false in case * of problems. Note that in any case, coins may be modified. */ - bool DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &coins, bool *pfClean = NULL); + bool DisconnectBlock(CValidationState &state, CBlockIndex *pindex, CCoinsViewCache &coins, bool *pfClean = NULL); // Apply the effects of this block (with given index) on the UTXO set represented by coins - bool ConnectBlock(CBlockIndex *pindex, CCoinsViewCache &coins, bool fJustCheck=false); + bool ConnectBlock(CValidationState &state, CBlockIndex *pindex, CCoinsViewCache &coins, bool fJustCheck=false); // Read a block from disk bool ReadFromDisk(const CBlockIndex* pindex); // Add this block to the block index, and if necessary, switch the active block chain to this - bool AddToBlockIndex(const CDiskBlockPos &pos); + bool AddToBlockIndex(CValidationState &state, const CDiskBlockPos &pos); // Context-independent validity checks - bool CheckBlock(bool fCheckPOW=true, bool fCheckMerkleRoot=true) const; + bool CheckBlock(CValidationState &state, bool fCheckPOW=true, bool fCheckMerkleRoot=true) const; // Store block on disk // if dbp is provided, the file is known to already reside on disk - bool AcceptBlock(CDiskBlockPos *dbp = NULL); + bool AcceptBlock(CValidationState &state, CDiskBlockPos *dbp = NULL); }; @@ -1877,6 +1873,52 @@ public: } }; +/** Capture information about block/transaction validation */ +class CValidationState { +private: + enum mode_state { + MODE_VALID, // everything ok + MODE_INVALID, // network rule violation (DoS value may be set) + MODE_ERROR, // run-time error + } mode; + int nDoS; +public: + CValidationState() : mode(MODE_VALID), nDoS(0) {} + bool DoS(int level, bool ret = false) { + if (mode == MODE_ERROR) + return ret; + nDoS += level; + mode = MODE_INVALID; + return ret; + } + bool Invalid(bool ret = false) { + return DoS(0, ret); + } + bool Error() { + mode = MODE_ERROR; + return false; + } + bool Abort(const std::string &msg) { + AbortNode(msg); + return Error(); + } + bool IsValid() { + return mode == MODE_VALID; + } + bool IsInvalid() { + return mode == MODE_INVALID; + } + bool IsError() { + return mode == MODE_ERROR; + } + bool IsInvalid(int &nDoSOut) { + if (IsInvalid()) { + nDoSOut = nDoS; + return true; + } + return false; + } +}; @@ -2025,7 +2067,7 @@ public: std::map<uint256, CTransaction> mapTx; std::map<COutPoint, CInPoint> mapNextTx; - bool accept(CTransaction &tx, bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs); + bool accept(CValidationState &state, CTransaction &tx, bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs); bool addUnchecked(const uint256& hash, CTransaction &tx); bool remove(const CTransaction &tx, bool fRecursive = false); bool removeConflicts(const CTransaction &tx); diff --git a/src/makefile.linux-mingw b/src/makefile.linux-mingw index c3cbe90bcd..7509e2798e 100644 --- a/src/makefile.linux-mingw +++ b/src/makefile.linux-mingw @@ -4,6 +4,9 @@ DEPSDIR:=/usr/i586-mingw32msvc +CC := i586-mingw32msvc-gcc +CXX := i586-mingw32msvc-g++ + USE_UPNP:=0 USE_IPV6:=1 @@ -58,6 +61,7 @@ LIBS += -l mingwthrd -l kernel32 -l user32 -l gdi32 -l comdlg32 -l winspool -l w HEADERS = $(wildcard *.h) OBJS= \ + leveldb/libleveldb.a \ obj/alert.o \ obj/version.o \ obj/checkpoints.o \ @@ -95,8 +99,7 @@ all: bitcoind.exe DEFS += -I"$(CURDIR)/leveldb/include" DEFS += -I"$(CURDIR)/leveldb/helpers" leveldb/libleveldb.a: - @echo "Building LevelDB ..." && cd leveldb && CC=i586-mingw32msvc-gcc CXX=i586-mingw32msvc-g++ TARGET_OS=OS_WINDOWS_CROSSCOMPILE CXXFLAGS="$(INCLUDEPATHS)" LDFLAGS="$(LIBPATHS)" $(MAKE) libleveldb.a libmemenv.a && i586-mingw32msvc-ranlib libleveldb.a && i586-mingw32msvc-ranlib libmemenv.a && cd .. -obj/leveldb.o: leveldb/libleveldb.a + @echo "Building LevelDB ..." && cd leveldb && TARGET_OS=OS_WINDOWS_CROSSCOMPILE $(MAKE) CC=$(CC) CXX=$(CXX) OPT="$(CFLAGS)" libleveldb.a libmemenv.a && i586-mingw32msvc-ranlib libleveldb.a && i586-mingw32msvc-ranlib libmemenv.a && cd .. obj/build.h: FORCE /bin/sh ../share/genbuild.sh obj/build.h @@ -104,18 +107,18 @@ version.cpp: obj/build.h DEFS += -DHAVE_BUILD_INFO obj/%.o: %.cpp $(HEADERS) - i586-mingw32msvc-g++ -c $(CFLAGS) -o $@ $< + $(CXX) -c $(CFLAGS) -o $@ $< bitcoind.exe: $(OBJS:obj/%=obj/%) - i586-mingw32msvc-g++ $(CFLAGS) $(LDFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) + $(CXX) $(CFLAGS) $(LDFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) TESTOBJS := $(patsubst test/%.cpp,obj-test/%.o,$(wildcard test/*.cpp)) obj-test/%.o: test/%.cpp $(HEADERS) - i586-mingw32msvc-g++ -c $(TESTDEFS) $(CFLAGS) -o $@ $< + $(CXX) -c $(TESTDEFS) $(CFLAGS) -o $@ $< test_bitcoin.exe: $(TESTOBJS) $(filter-out obj/init.o,$(OBJS:obj/%=obj/%)) - i586-mingw32msvc-g++ $(CFLAGS) $(LDFLAGS) -o $@ $(LIBPATHS) $^ -lboost_unit_test_framework-mt-s $(LIBS) + $(CXX) $(CFLAGS) $(LDFLAGS) -o $@ $(LIBPATHS) $^ -lboost_unit_test_framework-mt-s $(LIBS) clean: diff --git a/src/makefile.mingw b/src/makefile.mingw index 366d32bd86..2e092ff686 100644 --- a/src/makefile.mingw +++ b/src/makefile.mingw @@ -15,6 +15,8 @@ # 'make clean' assumes it is running inside a MSYS shell, and uses 'rm' # to remove files. +CXX ?= g++ + USE_UPNP:=- USE_IPV6:=1 @@ -67,6 +69,7 @@ LIBS += -l kernel32 -l user32 -l gdi32 -l comdlg32 -l winspool -l winmm -l shell HEADERS = $(wildcard *.h) OBJS= \ + leveldb/libleveldb.a \ obj/alert.o \ obj/version.o \ obj/checkpoints.o \ @@ -112,23 +115,21 @@ DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers) leveldb/libleveldb.a: - cd leveldb && $(MAKE) OPT="$(DEBUGFLAGS)" TARGET_OS=NATIVE_WINDOWS libleveldb.a libmemenv.a && cd .. - -obj/leveldb.o: leveldb/libleveldb.a + cd leveldb && $(MAKE) CC=$(CC) CXX=$(CXX) OPT="$(CFLAGS)" TARGET_OS=NATIVE_WINDOWS libleveldb.a libmemenv.a && cd .. obj/%.o: %.cpp $(HEADERS) - g++ -c $(CFLAGS) -o $@ $< + $(CXX) -c $(CFLAGS) -o $@ $< bitcoind.exe: $(OBJS:obj/%=obj/%) - g++ $(CFLAGS) $(LDFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) + $(CXX) $(CFLAGS) $(LDFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) TESTOBJS := $(patsubst test/%.cpp,obj-test/%.o,$(wildcard test/*.cpp)) obj-test/%.o: test/%.cpp $(HEADERS) - g++ -c $(TESTDEFS) $(CFLAGS) -o $@ $< + $(CXX) -c $(TESTDEFS) $(CFLAGS) -o $@ $< test_bitcoin.exe: $(TESTOBJS) $(filter-out obj/init.o,$(OBJS:obj/%=obj/%)) - g++ $(CFLAGS) $(LDFLAGS) -o $@ $(LIBPATHS) $^ -lboost_unit_test_framework$(BOOST_SUFFIX) $(LIBS) + $(CXX) $(CFLAGS) $(LDFLAGS) -o $@ $(LIBPATHS) $^ -lboost_unit_test_framework$(BOOST_SUFFIX) $(LIBS) clean: rm -f bitcoind.exe test_bitcoin.exe diff --git a/src/makefile.osx b/src/makefile.osx index 8b7c559fa1..cdee781257 100644 --- a/src/makefile.osx +++ b/src/makefile.osx @@ -62,7 +62,7 @@ ifdef RELEASE # the same way. CFLAGS = -mmacosx-version-min=10.5 -arch i386 -O3 else -CFLAGS = -g +DEBUGFLAGS = -g endif # ppc doesn't work because we don't support big-endian @@ -70,6 +70,7 @@ CFLAGS += -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter \ $(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS) OBJS= \ + leveldb/libleveldb.a \ obj/alert.o \ obj/version.o \ obj/checkpoints.o \ @@ -130,8 +131,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 .. -obj/leveldb.o: leveldb/libleveldb.a + @echo "Building LevelDB ..." && cd leveldb && $(MAKE) CC=$(CC) CXX=$(CXX) OPT="$(CFLAGS)" libleveldb.a libmemenv.a && cd .. # auto-generated dependencies: -include obj/*.P @@ -171,5 +171,6 @@ clean: -rm -f obj/*.P -rm -f obj-test/*.P -rm -f obj/build.h + -cd leveldb && $(MAKE) clean || true FORCE: diff --git a/src/makefile.unix b/src/makefile.unix index 14cf1b8fa5..ece2f59cf5 100644 --- a/src/makefile.unix +++ b/src/makefile.unix @@ -7,7 +7,7 @@ USE_IPV6:=1 LINK:=$(CXX) -DEFS=-DBOOST_SPIRIT_THREADSAFE +DEFS=-DBOOST_SPIRIT_THREADSAFE -D_FILE_OFFSET_BITS=64 DEFS += $(addprefix -I,$(CURDIR) $(CURDIR)/obj $(BOOST_INCLUDE_PATH) $(BDB_INCLUDE_PATH) $(OPENSSL_INCLUDE_PATH)) LIBS = $(addprefix -L,$(BOOST_LIB_PATH) $(BDB_LIB_PATH) $(OPENSSL_LIB_PATH)) @@ -101,6 +101,7 @@ xCXXFLAGS=-O2 -pthread -Wall -Wextra -Wformat -Wformat-security -Wno-unused-para xLDFLAGS=$(LDHARDENING) $(LDFLAGS) OBJS= \ + leveldb/libleveldb.a \ obj/alert.o \ obj/version.o \ obj/checkpoints.o \ @@ -142,12 +143,12 @@ test check: test_bitcoin FORCE # # LevelDB support # +MAKEOVERRIDES = 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 .. -obj/leveldb.o: leveldb/libleveldb.a + @echo "Building LevelDB ..." && cd leveldb && $(MAKE) CC=$(CC) CXX=$(CXX) OPT="$(xCXXFLAGS)" libleveldb.a libmemenv.a && cd .. # auto-generated dependencies: -include obj/*.P @@ -187,5 +188,6 @@ clean: -rm -f obj/*.P -rm -f obj-test/*.P -rm -f obj/build.h + -cd leveldb && $(MAKE) clean || true FORCE: diff --git a/src/net.cpp b/src/net.cpp index a6017b7356..3406a28b0e 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1145,11 +1145,17 @@ void MapPort() // Each pair gives a source name and a seed name. // The first name is used as information source for addrman. // The second name should resolve to a list of seed addresses. -static const char *strDNSSeed[][2] = { +static const char *strMainNetDNSSeed[][2] = { {"bitcoin.sipa.be", "seed.bitcoin.sipa.be"}, {"bluematt.me", "dnsseed.bluematt.me"}, {"dashjr.org", "dnsseed.bitcoin.dashjr.org"}, {"xf2.org", "bitseed.xf2.org"}, + {NULL, NULL} +}; + +static const char *strTestNetDNSSeed[][2] = { + {"bitcoin.petertodd.org", "testnet-seed.bitcoin.petertodd.org"}, + {NULL, NULL} }; void ThreadDNSAddressSeed(void* parg) @@ -1175,32 +1181,31 @@ void ThreadDNSAddressSeed(void* parg) void ThreadDNSAddressSeed2(void* parg) { + static const char *(*strDNSSeed)[2] = fTestNet ? strTestNetDNSSeed : strMainNetDNSSeed; + printf("ThreadDNSAddressSeed started\n"); int found = 0; - if (!fTestNet) - { - printf("Loading addresses from DNS seeds (could take a while)\n"); - - for (unsigned int seed_idx = 0; seed_idx < ARRAYLEN(strDNSSeed); seed_idx++) { - if (HaveNameProxy()) { - AddOneShot(strDNSSeed[seed_idx][1]); - } else { - vector<CNetAddr> vaddr; - vector<CAddress> vAdd; - if (LookupHost(strDNSSeed[seed_idx][1], vaddr)) + printf("Loading addresses from DNS seeds (could take a while)\n"); + + for (unsigned int seed_idx = 0; strDNSSeed[seed_idx][0] != NULL; seed_idx++) { + if (HaveNameProxy()) { + AddOneShot(strDNSSeed[seed_idx][1]); + } else { + vector<CNetAddr> vaddr; + vector<CAddress> vAdd; + if (LookupHost(strDNSSeed[seed_idx][1], vaddr)) + { + BOOST_FOREACH(CNetAddr& ip, vaddr) { - BOOST_FOREACH(CNetAddr& ip, vaddr) - { - int nOneDay = 24*3600; - CAddress addr = CAddress(CService(ip, GetDefaultPort())); - addr.nTime = GetTime() - 3*nOneDay - GetRand(4*nOneDay); // use a random age between 3 and 7 days old - vAdd.push_back(addr); - found++; - } + int nOneDay = 24*3600; + CAddress addr = CAddress(CService(ip, GetDefaultPort())); + addr.nTime = GetTime() - 3*nOneDay - GetRand(4*nOneDay); // use a random age between 3 and 7 days old + vAdd.push_back(addr); + found++; } - addrman.Add(vAdd, CNetAddr(strDNSSeed[seed_idx][0], true)); } + addrman.Add(vAdd, CNetAddr(strDNSSeed[seed_idx][0], true)); } } diff --git a/src/noui.cpp b/src/noui.cpp index 302d059291..c0e00c4715 100644 --- a/src/noui.cpp +++ b/src/noui.cpp @@ -9,7 +9,7 @@ #include <string> -static int noui_ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style) +static bool noui_ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style) { std::string strCaption; // Check for usage of predefined caption @@ -29,7 +29,7 @@ static int noui_ThreadSafeMessageBox(const std::string& message, const std::stri printf("%s: %s\n", strCaption.c_str(), message.c_str()); fprintf(stderr, "%s: %s\n", strCaption.c_str(), message.c_str()); - return 4; + return false; } static bool noui_ThreadSafeAskFee(int64 /*nFeeRequired*/) diff --git a/src/qt/aboutdialog.cpp b/src/qt/aboutdialog.cpp index 0b98befe8d..755413b2bb 100644 --- a/src/qt/aboutdialog.cpp +++ b/src/qt/aboutdialog.cpp @@ -1,14 +1,20 @@ #include "aboutdialog.h" #include "ui_aboutdialog.h" + #include "clientmodel.h" -#include "version.h" +// Copyright year (2009-this) +// Todo: update this when changing our copyright comments in the source +const int ABOUTDIALOG_COPYRIGHT_YEAR = 2013; AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AboutDialog) { ui->setupUi(this); + + // Set current copyright year + ui->copyrightLabel->setText(tr("Copyright") + QString(" © ") + tr("2009-%1 The Bitcoin developers").arg(ABOUTDIALOG_COPYRIGHT_YEAR)); } void AboutDialog::setModel(ClientModel *model) diff --git a/src/qt/aboutdialog.h b/src/qt/aboutdialog.h index 2ed9e9e7c4..33b1437674 100644 --- a/src/qt/aboutdialog.h +++ b/src/qt/aboutdialog.h @@ -18,6 +18,7 @@ public: ~AboutDialog(); void setModel(ClientModel *model); + private: Ui::AboutDialog *ui; diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index e5526a6c09..afd8d71a0e 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -34,23 +34,27 @@ Q_IMPORT_PLUGIN(qtaccessiblewidgets) static BitcoinGUI *guiref; static QSplashScreen *splashref; -static void ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style) +static bool ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style) { // Message from network thread if(guiref) { bool modal = (style & CClientUIInterface::MODAL); + bool ret = false; // In case of modal message, use blocking connection to wait for user to click a button QMetaObject::invokeMethod(guiref, "message", modal ? GUIUtil::blockingGUIThreadConnection() : Qt::QueuedConnection, Q_ARG(QString, QString::fromStdString(caption)), Q_ARG(QString, QString::fromStdString(message)), - Q_ARG(unsigned int, style)); + Q_ARG(unsigned int, style), + Q_ARG(bool*, &ret)); + return ret; } else { printf("%s: %s\n", caption.c_str(), message.c_str()); fprintf(stderr, "%s: %s\n", caption.c_str(), message.c_str()); + return false; } } diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 9cd22ed297..f1bf5f5880 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -67,7 +67,8 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): aboutQtAction(0), trayIcon(0), notificator(0), - rpcConsole(0) + rpcConsole(0), + prevBlocks(0) { resize(850, 550); setWindowTitle(tr("Bitcoin") + " - " + tr("Wallet")); @@ -527,52 +528,17 @@ void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks) importText = tr("Reindexing blocks on disk..."); } - if(count < nTotalBlocks) - { - int nRemainingBlocks = nTotalBlocks - count; - float nPercentageDone = count / (nTotalBlocks * 0.01f); - - 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("Processed %1 of %2 blocks of transaction history (%3% done).").arg(count).arg(nTotalBlocks).arg(nPercentageDone, 0, 'f', 2); - } - else - { - progressBarLabel->setVisible(false); - - progressBar->setVisible(false); - tooltip = tr("Processed %1 blocks of transaction history.").arg(count); - } - QDateTime lastBlockDate = clientModel->getLastBlockDate(); - int secs = lastBlockDate.secsTo(QDateTime::currentDateTime()); - QString text; + QDateTime currentDate = QDateTime::currentDateTime(); + int secs = lastBlockDate.secsTo(currentDate); - // Represent time from last generated block in human readable text - if(secs <= 0) - { - // Fully up to date. Leave text empty. - } - else if(secs < 60) - { - text = tr("%n second(s) ago","",secs); - } - else if(secs < 60*60) - { - text = tr("%n minute(s) ago","",secs/60); - } - else if(secs < 24*60*60) + if(count < nTotalBlocks) { - text = tr("%n hour(s) ago","",secs/(60*60)); + tooltip = tr("Processed %1 of %2 (estimated) blocks of transaction history.").arg(count).arg(nTotalBlocks); } else { - text = tr("%n day(s) ago","",secs/(60*60*24)); + tooltip = tr("Processed %1 blocks of transaction history.").arg(count); } // Set icon state: spinning if catching up, tick otherwise @@ -582,20 +548,46 @@ void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks) labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); overviewPage->showOutOfSyncWarning(false); + + progressBarLabel->setVisible(false); + progressBar->setVisible(false); } else { + // Represent time from last generated block in human readable text + QString timeBehindText; + if(secs < 48*60*60) + { + timeBehindText = tr("%n hour(s)","",secs/(60*60)); + } + else if(secs < 14*24*60*60) + { + timeBehindText = tr("%n day(s)","",secs/(24*60*60)); + } + else + { + timeBehindText = tr("%n week(s)","",secs/(7*24*60*60)); + } + + progressBarLabel->setText(importText); + progressBarLabel->setVisible(true); + progressBar->setFormat(tr("%1 behind").arg(timeBehindText)); + progressBar->setMaximum(1000000000); + progressBar->setValue(clientModel->getVerificationProgress() * 1000000000.0 + 0.5); + progressBar->setVisible(true); + tooltip = tr("Catching up...") + QString("<br>") + tooltip; labelBlocksIcon->setMovie(syncIconMovie); - syncIconMovie->start(); + if(count != prevBlocks) + syncIconMovie->jumpToNextFrame(); + prevBlocks = count; overviewPage->showOutOfSyncWarning(true); - } - if(!text.isEmpty()) - { tooltip += QString("<br>"); - tooltip += tr("Last received block was generated %1.").arg(text); + tooltip += tr("Last received block was generated %1 ago.").arg(timeBehindText); + tooltip += QString("<br>"); + tooltip += tr("Transactions after this will not yet be visible."); } // Don't word-wrap this (fixed-width) tooltip @@ -606,7 +598,7 @@ void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks) progressBar->setToolTip(tooltip); } -void BitcoinGUI::message(const QString &title, const QString &message, unsigned int style) +void BitcoinGUI::message(const QString &title, const QString &message, unsigned int style, bool *ret) { QString strTitle = tr("Bitcoin") + " - "; // Default to information icon @@ -646,7 +638,9 @@ void BitcoinGUI::message(const QString &title, const QString &message, unsigned buttons = QMessageBox::Ok; QMessageBox mBox((QMessageBox::Icon)nMBoxIcon, strTitle, message, buttons); - mBox.exec(); + int r = mBox.exec(); + if (ret != NULL) + *ret = r == QMessageBox::Ok; } else notificator->notify((Notificator::Class)nNotifyIcon, strTitle, message); diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index b7afdb1c8c..8ce0335bcd 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -98,6 +98,8 @@ private: RPCConsole *rpcConsole; QMovie *syncIconMovie; + /** Keep track of previous number of blocks, to detect progress */ + int prevBlocks; /** Create the main UI actions. */ void createActions(); @@ -126,8 +128,9 @@ public slots: @param[in] message the displayed text @param[in] style modality and style definitions (icon and used buttons - buttons only for message boxes) @see CClientUIInterface::MessageBoxFlags + @param[in] ret pointer to a bool that will be modified to whether Ok was clicked (modal only) */ - void message(const QString &title, const QString &message, unsigned int style); + void message(const QString &title, const QString &message, unsigned int style, bool *ret = NULL); /** Asks the user whether to pay the transaction fee or to cancel the transaction. It is currently not possible to pass a return value to another thread through BlockingQueuedConnection, so an indirected pointer is used. diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp index 92a511adb9..2c3d859c82 100644 --- a/src/qt/bitcoinstrings.cpp +++ b/src/qt/bitcoinstrings.cpp @@ -100,6 +100,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Connect to a node to retrieve peer addresses, QT_TRANSLATE_NOOP("bitcoin-core", "Discover own IP address (default: 1 when listening and no -externalip)"), QT_TRANSLATE_NOOP("bitcoin-core", "Don't generate coins"), QT_TRANSLATE_NOOP("bitcoin-core", "Done loading"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error initializing block database"), QT_TRANSLATE_NOOP("bitcoin-core", "Error loading block database"), QT_TRANSLATE_NOOP("bitcoin-core", "Error loading wallet.dat"), QT_TRANSLATE_NOOP("bitcoin-core", "Error loading wallet.dat: Wallet corrupted"), @@ -109,13 +110,24 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Error: Disk space is low!"), QT_TRANSLATE_NOOP("bitcoin-core", "Error: Transaction creation failed!"), QT_TRANSLATE_NOOP("bitcoin-core", "Error: Wallet locked, unable to create transaction!"), QT_TRANSLATE_NOOP("bitcoin-core", "Error: could not start node"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error: system error: "), QT_TRANSLATE_NOOP("bitcoin-core", "Failed to listen on any port. Use -listen=0 if you want this."), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to read block info"), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to read block"), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to sync block index"), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write block index"), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write block info"), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write block"), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write file info"), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write to coin database"), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write transaction index"), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write undo data"), QT_TRANSLATE_NOOP("bitcoin-core", "Fee per KB to add to transactions you send"), QT_TRANSLATE_NOOP("bitcoin-core", "Find peers using DNS lookup (default: 1 unless -connect)"), QT_TRANSLATE_NOOP("bitcoin-core", "Find peers using internet relay chat (default: 0)"), QT_TRANSLATE_NOOP("bitcoin-core", "Generate coins"), QT_TRANSLATE_NOOP("bitcoin-core", "Get help for a command"), -QT_TRANSLATE_NOOP("bitcoin-core", "How many blocks to check at startup (default: 2500, 0 = all)"), +QT_TRANSLATE_NOOP("bitcoin-core", "How many blocks to check at startup (default: 288, 0 = all)"), QT_TRANSLATE_NOOP("bitcoin-core", "How thorough the block verification is (0-4, default: 3)"), QT_TRANSLATE_NOOP("bitcoin-core", "Importing blocks from block database..."), QT_TRANSLATE_NOOP("bitcoin-core", "Imports blocks from external blk000??.dat file"), @@ -163,6 +175,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Specify connection timeout in milliseconds (d QT_TRANSLATE_NOOP("bitcoin-core", "Specify data directory"), QT_TRANSLATE_NOOP("bitcoin-core", "Specify pid file (default: bitcoind.pid)"), QT_TRANSLATE_NOOP("bitcoin-core", "Specify your own public address"), +QT_TRANSLATE_NOOP("bitcoin-core", "System error: "), QT_TRANSLATE_NOOP("bitcoin-core", "This help message"), QT_TRANSLATE_NOOP("bitcoin-core", "Threshold for disconnecting misbehaving peers (default: 100)"), QT_TRANSLATE_NOOP("bitcoin-core", "To use the %s option"), diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 12bd989338..858fbe241f 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -6,6 +6,7 @@ #include "alert.h" #include "main.h" +#include "checkpoints.h" #include "ui_interface.h" #include <QDateTime> @@ -48,7 +49,15 @@ int ClientModel::getNumBlocksAtStartup() QDateTime ClientModel::getLastBlockDate() const { - return QDateTime::fromTime_t(pindexBest->GetBlockTime()); + if (pindexBest) + return QDateTime::fromTime_t(pindexBest->GetBlockTime()); + else + return QDateTime::fromTime_t(1231006505); // Genesis block's time +} + +double ClientModel::getVerificationProgress() const +{ + return Checkpoints::GuessVerificationProgress(pindexBest); } void ClientModel::updateTimer() diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 1afccb7859..a3fe92048c 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -34,6 +34,7 @@ public: int getNumBlocks() const; int getNumBlocksAtStartup(); + double getVerificationProgress() const; QDateTime getLastBlockDate() const; //! Return true if client connected to testnet diff --git a/src/qt/forms/aboutdialog.ui b/src/qt/forms/aboutdialog.ui index b59c2445de..80768f89b0 100644 --- a/src/qt/forms/aboutdialog.ui +++ b/src/qt/forms/aboutdialog.ui @@ -91,7 +91,10 @@ <cursorShape>IBeamCursor</cursorShape> </property> <property name="text"> - <string>Copyright © 2009-2012 The Bitcoin developers</string> + <string notr="true">Copyright &copy; 2009-YYYY The Bitcoin developers</string> + </property> + <property name="textFormat"> + <enum>Qt::RichText</enum> </property> <property name="textInteractionFlags"> <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index 405ba396b7..92417834ec 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -2,7 +2,7 @@ #define GUICONSTANTS_H /* Milliseconds between model updates */ -static const int MODEL_UPDATE_DELAY = 500; +static const int MODEL_UPDATE_DELAY = 250; /* AskPassphraseDialog -- Maximum passphrase length */ static const int MAX_PASSPHRASE_SIZE = 1024; diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts index 4895e9ba4e..39062d0a2c 100644 --- a/src/qt/locale/bitcoin_en.ts +++ b/src/qt/locale/bitcoin_en.ts @@ -15,12 +15,7 @@ <translation><b>Bitcoin</b> version</translation> </message> <message> - <location line="+41"/> - <source>Copyright © 2009-2012 The Bitcoin developers</source> - <translation>Copyright © 2009-2012 The Bitcoin developers</translation> - </message> - <message> - <location line="+13"/> + <location line="+57"/> <source> This is experimental software. @@ -34,6 +29,16 @@ Distributed under the MIT/X11 software license, see the accompanying file COPYIN This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard.</translation> </message> + <message> + <location filename="../aboutdialog.cpp" line="+17"/> + <source>Copyright</source> + <translation>Copyright</translation> + </message> + <message> + <location line="+0"/> + <source>2009-%1 The Bitcoin developers</source> + <translation>2009-%1 The Bitcoin developers</translation> + </message> </context> <context> <name>AddressBookPage</name> @@ -713,7 +718,7 @@ Address: %4 <context> <name>ClientModel</name> <message> - <location filename="../clientmodel.cpp" line="+86"/> + <location filename="../clientmodel.cpp" line="+89"/> <source>Network Alert</source> <translation>Network Alert</translation> </message> @@ -2174,12 +2179,12 @@ Address: %4 <translation>Bitcoin version</translation> </message> <message> - <location line="+83"/> + <location line="+96"/> <source>Usage:</source> <translation>Usage:</translation> </message> <message> - <location line="-23"/> + <location line="-24"/> <source>Send command to -server or bitcoind</source> <translation>Send command to -server or bitcoind</translation> </message> @@ -2214,12 +2219,12 @@ Address: %4 <translation>Generate coins</translation> </message> <message> - <location line="-15"/> + <location line="-27"/> <source>Don't generate coins</source> <translation>Don't generate coins</translation> </message> <message> - <location line="+62"/> + <location line="+74"/> <source>Specify data directory</source> <translation>Specify data directory</translation> </message> @@ -2239,22 +2244,22 @@ Address: %4 <translation>Maintain at most <n> connections to peers (default: 125)</translation> </message> <message> - <location line="-35"/> + <location line="-47"/> <source>Connect to a node to retrieve peer addresses, and disconnect</source> <translation>Connect to a node to retrieve peer addresses, and disconnect</translation> </message> <message> - <location line="+66"/> + <location line="+78"/> <source>Specify your own public address</source> <translation>Specify your own public address</translation> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Threshold for disconnecting misbehaving peers (default: 100)</source> <translation>Threshold for disconnecting misbehaving peers (default: 100)</translation> </message> <message> - <location line="-117"/> + <location line="-130"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</translation> </message> @@ -2274,17 +2279,17 @@ Address: %4 <translation>Accept command line and JSON-RPC commands</translation> </message> <message> - <location line="+63"/> + <location line="+75"/> <source>Run in the background as a daemon and accept commands</source> <translation>Run in the background as a daemon and accept commands</translation> </message> <message> - <location line="+31"/> + <location line="+32"/> <source>Use the test network</source> <translation>Use the test network</translation> </message> <message> - <location line="-93"/> + <location line="-106"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation>Accept connections from outside (default: 1 if no -proxy or -connect)</translation> </message> @@ -2405,6 +2410,11 @@ If the file does not exist, create it with owner-readable-only file permissions. </message> <message> <location line="+3"/> + <source>Error initializing block database</source> + <translation>Error initializing block database</translation> + </message> + <message> + <location line="+1"/> <source>Error loading block database</source> <translation>Error loading block database</translation> </message> @@ -2425,16 +2435,76 @@ If the file does not exist, create it with owner-readable-only file permissions. </message> <message> <location line="+2"/> + <source>Error: system error: </source> + <translation>Error: system error: </translation> + </message> + <message> + <location line="+1"/> <source>Failed to listen on any port. Use -listen=0 if you want this.</source> <translation>Failed to listen on any port. Use -listen=0 if you want this.</translation> </message> <message> + <location line="+1"/> + <source>Failed to read block info</source> + <translation>Failed to read block info</translation> + </message> + <message> + <location line="+1"/> + <source>Failed to read block</source> + <translation>Failed to read block</translation> + </message> + <message> + <location line="+1"/> + <source>Failed to sync block index</source> + <translation>Failed to sync block index</translation> + </message> + <message> + <location line="+1"/> + <source>Failed to write block index</source> + <translation>Failed to write block index</translation> + </message> + <message> + <location line="+1"/> + <source>Failed to write block info</source> + <translation>Failed to write block info</translation> + </message> + <message> + <location line="+1"/> + <source>Failed to write block</source> + <translation>Failed to write block</translation> + </message> + <message> + <location line="+1"/> + <source>Failed to write file info</source> + <translation>Failed to write file info</translation> + </message> + <message> + <location line="+1"/> + <source>Failed to write to coin database</source> + <translation>Failed to write to coin database</translation> + </message> + <message> + <location line="+1"/> + <source>Failed to write transaction index</source> + <translation>Failed to write transaction index</translation> + </message> + <message> + <location line="+1"/> + <source>Failed to write undo data</source> + <translation>Failed to write undo data</translation> + </message> + <message> <location line="+2"/> <source>Find peers using DNS lookup (default: 1 unless -connect)</source> <translation>Find peers using DNS lookup (default: 1 unless -connect)</translation> </message> <message> - <location line="+5"/> + <location line="+4"/> + <source>How many blocks to check at startup (default: 288, 0 = all)</source> + <translation>How many blocks to check at startup (default: 288, 0 = all)</translation> + </message> + <message> + <location line="+1"/> <source>How thorough the block verification is (0-4, default: 3)</source> <translation>How thorough the block verification is (0-4, default: 3)</translation> </message> @@ -2544,7 +2614,12 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation>Specify connection timeout in milliseconds (default: 5000)</translation> </message> <message> - <location line="+13"/> + <location line="+4"/> + <source>System error: </source> + <translation>System error: </translation> + </message> + <message> + <location line="+10"/> <source>Use UPnP to map the listening port (default: 0)</source> <translation>Use UPnP to map the listening port (default: 0)</translation> </message> @@ -2584,32 +2659,32 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation>wallet.dat corrupt, salvage failed</translation> </message> <message> - <location line="-44"/> + <location line="-45"/> <source>Password for JSON-RPC connections</source> <translation>Password for JSON-RPC connections</translation> </message> <message> - <location line="-54"/> + <location line="-66"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>Allow JSON-RPC connections from specified IP address</translation> </message> <message> - <location line="+63"/> + <location line="+75"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>Send commands to node running on <ip> (default: 127.0.0.1)</translation> </message> <message> - <location line="-106"/> + <location line="-118"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation>Execute command when the best block changes (%s in cmd is replaced by block hash)</translation> </message> <message> - <location line="+127"/> + <location line="+140"/> <source>Upgrade wallet to latest format</source> <translation>Upgrade wallet to latest format</translation> </message> <message> - <location line="-15"/> + <location line="-16"/> <source>Set key pool size to <n> (default: 100)</source> <translation>Set key pool size to <n> (default: 100)</translation> </message> @@ -2619,17 +2694,12 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation>Rescan the block chain for missing wallet transactions</translation> </message> <message> - <location line="-27"/> - <source>How many blocks to check at startup (default: 2500, 0 = all)</source> - <translation>How many blocks to check at startup (default: 2500, 0 = all)</translation> - </message> - <message> - <location line="+56"/> + <location line="+30"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>Use OpenSSL (https) for JSON-RPC connections</translation> </message> <message> - <location line="-20"/> + <location line="-21"/> <source>Server certificate file (default: server.cert)</source> <translation>Server certificate file (default: server.cert)</translation> </message> @@ -2639,12 +2709,12 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation>Server private key (default: server.pem)</translation> </message> <message> - <location line="-136"/> + <location line="-148"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> </message> <message> - <location line="+147"/> + <location line="+160"/> <source>This help message</source> <translation>This help message</translation> </message> @@ -2654,7 +2724,7 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation>Unable to bind to %s on this computer (bind returned error %d, %s)</translation> </message> <message> - <location line="-71"/> + <location line="-84"/> <source>Connect through socks proxy</source> <translation>Connect through socks proxy</translation> </message> @@ -2664,12 +2734,12 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation>Allow DNS lookups for -addnode, -seednode and -connect</translation> </message> <message> - <location line="+43"/> + <location line="+55"/> <source>Loading addresses...</source> <translation>Loading addresses...</translation> </message> <message> - <location line="-25"/> + <location line="-36"/> <source>Error loading wallet.dat: Wallet corrupted</source> <translation>Error loading wallet.dat: Wallet corrupted</translation> </message> @@ -2679,7 +2749,7 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation>Error loading wallet.dat: Wallet requires newer version of Bitcoin</translation> </message> <message> - <location line="+74"/> + <location line="+86"/> <source>Verifying block database integrity...</source> <translation>Verifying block database integrity...</translation> </message> @@ -2694,17 +2764,17 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation>Wallet needed to be rewritten: restart Bitcoin to complete</translation> </message> <message> - <location line="-78"/> + <location line="-90"/> <source>Error loading wallet.dat</source> <translation>Error loading wallet.dat</translation> </message> <message> - <location line="+20"/> + <location line="+31"/> <source>Invalid -proxy address: '%s'</source> <translation>Invalid -proxy address: '%s'</translation> </message> <message> - <location line="+47"/> + <location line="+48"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation>Unknown network specified in -onlynet: '%s'</translation> </message> @@ -2714,7 +2784,7 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation>Unknown -socks proxy version requested: %i</translation> </message> <message> - <location line="-76"/> + <location line="-89"/> <source>Cannot resolve -bind address: '%s'</source> <translation>Cannot resolve -bind address: '%s'</translation> </message> @@ -2724,17 +2794,17 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation>Cannot resolve -externalip address: '%s'</translation> </message> <message> - <location line="+31"/> + <location line="+43"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>Invalid amount for -paytxfee=<amount>: '%s'</translation> </message> <message> - <location line="-15"/> + <location line="-26"/> <source>Error: could not start node</source> <translation>Error: could not start node</translation> </message> <message> - <location line="+16"/> + <location line="+27"/> <source>Invalid amount</source> <translation>Invalid amount</translation> </message> @@ -2749,7 +2819,7 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation>Loading block index...</translation> </message> <message> - <location line="-45"/> + <location line="-57"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>Add a node to connect to and attempt to keep the connection open</translation> </message> @@ -2759,7 +2829,7 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation>Unable to bind to %s on this computer. Bitcoin is probably already running.</translation> </message> <message> - <location line="+54"/> + <location line="+66"/> <source>Find peers using internet relay chat (default: 0)</source> <translation>Find peers using internet relay chat (default: 0)</translation> </message> @@ -2774,7 +2844,7 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation>Loading wallet...</translation> </message> <message> - <location line="-40"/> + <location line="-52"/> <source>Cannot downgrade wallet</source> <translation>Cannot downgrade wallet</translation> </message> @@ -2789,27 +2859,27 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation>Cannot write default address</translation> </message> <message> - <location line="+50"/> + <location line="+62"/> <source>Rescanning...</source> <translation>Rescanning...</translation> </message> <message> - <location line="-44"/> + <location line="-56"/> <source>Done loading</source> <translation>Done loading</translation> </message> <message> - <location line="+66"/> + <location line="+79"/> <source>To use the %s option</source> <translation>To use the %s option</translation> </message> <message> - <location line="-61"/> + <location line="-73"/> <source>Error</source> <translation>Error</translation> </message> <message> - <location line="-28"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp index 8028190b82..628dca1591 100644 --- a/src/qt/notificator.cpp +++ b/src/qt/notificator.cpp @@ -113,7 +113,7 @@ FreedesktopImage::FreedesktopImage(const QImage &img): { // Convert 00xAARRGGBB to RGBA bytewise (endian-independent) format QImage tmp = img.convertToFormat(QImage::Format_ARGB32); - const uint32_t *data = reinterpret_cast<const uint32_t*>(tmp.constBits()); + const uint32_t *data = reinterpret_cast<const uint32_t*>(tmp.bits()); unsigned int num_pixels = width * height; image.resize(num_pixels * BYTES_PER_PIXEL); diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 778e0acbd2..fddda8b5f4 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -307,7 +307,7 @@ Value getblocktemplate(const Array& params, bool fHelp) } entry.push_back(Pair("depends", deps)); - int index_in_template = &tx - pblock->vtx.data(); + int index_in_template = i - 1; entry.push_back(Pair("fee", pblocktemplate->vTxFees[index_in_template])); entry.push_back(Pair("sigops", pblocktemplate->vTxSigOps[index_in_template])); @@ -365,9 +365,10 @@ Value submitblock(const Array& params, bool fHelp) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed"); } - bool fAccepted = ProcessBlock(NULL, &pblock); + CValidationState state; + bool fAccepted = ProcessBlock(state, NULL, &pblock); if (!fAccepted) - return "rejected"; + return "rejected"; // TODO: report validation state return Value::null; } diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index 8d89c2f302..bbaf40c735 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -421,7 +421,7 @@ 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)("redeemScript",str_type)); + RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type)); uint256 txid = ParseHashO(prevOut, "txid"); @@ -450,12 +450,16 @@ Value signrawtransaction(const Array& params, bool fHelp) // 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)) + if (fGivenKeys && scriptPubKey.IsPayToScriptHash()) { - vector<unsigned char> rsData(ParseHexV(v, "redeemScript")); - CScript redeemScript(rsData.begin(), rsData.end()); - tempKeystore.AddCScript(redeemScript); + RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type)("redeemScript",str_type)); + Value v = find_value(prevOut, "redeemScript"); + if (!(v == Value::null)) + { + vector<unsigned char> rsData(ParseHexV(v, "redeemScript")); + CScript redeemScript(rsData.begin(), rsData.end()); + tempKeystore.AddCScript(redeemScript); + } } } } @@ -546,8 +550,9 @@ Value sendrawtransaction(const Array& params, bool fHelp) fHave = view.GetCoins(hashTx, existingCoins); if (!fHave) { // push to local node - if (!tx.AcceptToMemoryPool(true, false)) - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected"); + CValidationState state; + if (!tx.AcceptToMemoryPool(state, true, false)) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected"); // TODO: report validation state } } if (fHave) { diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 90a68f560a..21eb2fd1aa 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -75,6 +75,7 @@ Value getinfo(const Array& params, bool fHelp) obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); obj.push_back(Pair("blocks", (int)nBestHeight)); + obj.push_back(Pair("timeoffset", (boost::int64_t)GetTimeOffset())); obj.push_back(Pair("connections", (int)vNodes.size())); obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string()))); obj.push_back(Pair("difficulty", (double)GetDifficulty())); diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index bc2a05a6b3..af284653dd 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -73,7 +73,9 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) txFirst.push_back(new CTransaction(pblock->vtx[0])); pblock->hashMerkleRoot = pblock->BuildMerkleTree(); pblock->nNonce = blockinfo[i].nonce; - assert(ProcessBlock(NULL, pblock)); + CValidationState state; + BOOST_CHECK(ProcessBlock(state, NULL, pblock)); + BOOST_CHECK(state.IsValid()); pblock->hashPrevBlock = pblock->GetHash(); } delete pblocktemplate; diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index f75b762f1f..1116507a34 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -28,7 +28,7 @@ struct TestingSetup { pblocktree = new CBlockTreeDB(1 << 20, true); pcoinsdbview = new CCoinsViewDB(1 << 23, true); pcoinsTip = new CCoinsViewCache(*pcoinsdbview); - LoadBlockIndex(); + InitBlockIndex(); 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 23837c6c15..f44d46fdb8 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -66,7 +66,9 @@ BOOST_AUTO_TEST_CASE(tx_valid) CTransaction tx; stream >> tx; - BOOST_CHECK_MESSAGE(tx.CheckTransaction(), strTest); + CValidationState state; + BOOST_CHECK_MESSAGE(tx.CheckTransaction(state), strTest); + BOOST_CHECK(state.IsValid()); for (unsigned int i = 0; i < tx.vin.size(); i++) { @@ -133,7 +135,8 @@ BOOST_AUTO_TEST_CASE(tx_invalid) CTransaction tx; stream >> tx; - fValid = tx.CheckTransaction(); + CValidationState state; + fValid = tx.CheckTransaction(state) && state.IsValid(); for (unsigned int i = 0; i < tx.vin.size() && fValid; i++) { @@ -159,11 +162,12 @@ BOOST_AUTO_TEST_CASE(basic_transaction_tests) CDataStream stream(vch, SER_DISK, CLIENT_VERSION); CTransaction tx; stream >> tx; - BOOST_CHECK_MESSAGE(tx.CheckTransaction(), "Simple deserialized transaction should be valid."); + CValidationState state; + BOOST_CHECK_MESSAGE(tx.CheckTransaction(state) && state.IsValid(), "Simple deserialized transaction should be valid."); // Check that duplicate txins fail tx.vin.push_back(tx.vin[0]); - BOOST_CHECK_MESSAGE(!tx.CheckTransaction(), "Transaction with duplicate txins should be invalid."); + BOOST_CHECK_MESSAGE(!tx.CheckTransaction(state) || !state.IsValid(), "Transaction with duplicate txins should be invalid."); } // diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index f56969cba6..1b0ccad511 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -261,4 +261,66 @@ BOOST_AUTO_TEST_CASE(util_IsHex) BOOST_CHECK(!IsHex("0x0000")); } +BOOST_AUTO_TEST_CASE(util_seed_insecure_rand) +{ + // Expected results for the determinstic seed. + const uint32_t exp_vals[11] = { 91632771U,1889679809U,3842137544U,3256031132U, + 1761911779U, 489223532U,2692793790U,2737472863U, + 2796262275U,1309899767U,840571781U}; + // Expected 0s in rand()%(idx+2) for the determinstic seed. + const int exp_count[9] = {5013,3346,2415,1972,1644,1386,1176,1096,1009}; + int i; + int count=0; + + seed_insecure_rand(); + + //Does the non-determistic rand give us results that look too like the determinstic one? + for (i=0;i<10;i++) + { + int match = 0; + uint32_t rval = insecure_rand(); + for (int j=0;j<11;j++)match |= rval==exp_vals[j]; + count += match; + } + // sum(binomial(10,i)*(11/(2^32))^i*(1-(11/(2^32)))^(10-i),i,0,4) ~= 1-1/2^134.73 + // So _very_ unlikely to throw a false failure here. + BOOST_CHECK(count<=4); + + for (int mod=2;mod<11;mod++) + { + int mask = 1; + // Really rough binomal confidence approximation. + int err = 30*10000./mod*sqrt((1./mod*(1-1./mod))/10000.); + //mask is 2^ceil(log2(mod))-1 + while(mask<mod-1)mask=(mask<<1)+1; + + count = 0; + //How often does it get a zero from the uniform range [0,mod)? + for (i=0;i<10000;i++) + { + uint32_t rval; + do{ + rval=insecure_rand()&mask; + }while(rval>=(uint32_t)mod); + count += rval==0; + } + BOOST_CHECK(count<=10000/mod+err); + BOOST_CHECK(count>=10000/mod-err); + } + + seed_insecure_rand(true); + + for (i=0;i<11;i++) + { + BOOST_CHECK_EQUAL(insecure_rand(),exp_vals[i]); + } + + for (int mod=2;mod<11;mod++) + { + count = 0; + for (i=0;i<10000;i++) count += insecure_rand()%mod==0; + BOOST_CHECK_EQUAL(count,exp_count[mod-2]); + } +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/ui_interface.h b/src/ui_interface.h index 703e15f095..f7dbe20894 100644 --- a/src/ui_interface.h +++ b/src/ui_interface.h @@ -68,7 +68,7 @@ public: }; /** Show message box. */ - boost::signals2::signal<void (const std::string& message, const std::string& caption, unsigned int style)> ThreadSafeMessageBox; + boost::signals2::signal<bool (const std::string& message, const std::string& caption, unsigned int style), boost::signals2::last_value<bool> > ThreadSafeMessageBox; /** Ask the user whether they want to pay a fee or not. */ boost::signals2::signal<bool (int64 nFeeRequired), boost::signals2::last_value<bool> > ThreadSafeAskFee; diff --git a/src/util.cpp b/src/util.cpp index 4ac66d4660..fc3e846a6b 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -1265,9 +1265,14 @@ void SetMockTime(int64 nMockTimeIn) static int64 nTimeOffset = 0; +int64 GetTimeOffset() +{ + return nTimeOffset; +} + int64 GetAdjustedTime() { - return GetTime() + nTimeOffset; + return GetTime() + GetTimeOffset(); } void AddTimeData(const CNetAddr& ip, int64 nTime) @@ -1323,12 +1328,26 @@ void AddTimeData(const CNetAddr& ip, int64 nTime) } } - - - - - - +uint32_t insecure_rand_Rz = 11; +uint32_t insecure_rand_Rw = 11; +void seed_insecure_rand(bool fDeterministic) +{ + //The seed values have some unlikely fixed points which we avoid. + if(fDeterministic) + { + insecure_rand_Rz = insecure_rand_Rw = 11; + } else { + uint32_t tmp; + do{ + RAND_bytes((unsigned char*)&tmp,4); + }while(tmp==0 || tmp==0x9068ffffU); + insecure_rand_Rz=tmp; + do{ + RAND_bytes((unsigned char*)&tmp,4); + }while(tmp==0 || tmp==0x464fffffU); + insecure_rand_Rw=tmp; + } +} string FormatVersion(int nVersion) { diff --git a/src/util.h b/src/util.h index 1d29867076..d129d13692 100644 --- a/src/util.h +++ b/src/util.h @@ -213,6 +213,7 @@ uint256 GetRandHash(); int64 GetTime(); void SetMockTime(int64 nMockTimeIn); int64 GetAdjustedTime(); +int64 GetTimeOffset(); std::string FormatFullVersion(); std::string FormatSubVersion(const std::string& name, int nClientVersion, const std::vector<std::string>& comments); void AddTimeData(const CNetAddr& ip, int64 nTime); @@ -404,13 +405,27 @@ bool SoftSetArg(const std::string& strArg, const std::string& strValue); */ bool SoftSetBoolArg(const std::string& strArg, bool fValue); +/** + * MWC RNG of George Marsaglia + * This is intended to be fast. It has a period of 2^59.3, though the + * least significant 16 bits only have a period of about 2^30.1. + * + * @return random value + */ +extern uint32_t insecure_rand_Rz; +extern uint32_t insecure_rand_Rw; +static inline uint32_t insecure_rand(void) +{ + insecure_rand_Rz=36969*(insecure_rand_Rz&65535)+(insecure_rand_Rz>>16); + insecure_rand_Rw=18000*(insecure_rand_Rw&65535)+(insecure_rand_Rw>>16); + return (insecure_rand_Rw<<16)+insecure_rand_Rz; +} - - - - - - +/** + * Seed insecure_rand using the random pool. + * @param Deterministic Use a determinstic seed + */ +void seed_insecure_rand(bool fDeterministic=false); /** Median filter over a stream of values. * Returns the median of the last N numbers diff --git a/src/wallet.cpp b/src/wallet.cpp index 557784e5c2..eecb7d2d22 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -8,6 +8,7 @@ #include "crypter.h" #include "ui_interface.h" #include "base58.h" +#include <boost/algorithm/string/replace.hpp> using namespace std; @@ -476,6 +477,16 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn) // Notify UI of new or updated transaction NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED); + + // notify an external script when a wallet transaction comes in or is updated + std::string strCmd = GetArg("-walletnotify", ""); + + if ( !strCmd.empty()) + { + boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); + boost::thread t(runCommand, strCmd); // thread runs free + } + } return true; } @@ -973,6 +984,8 @@ static void ApproximateBestSubset(vector<pair<int64, pair<const CWalletTx*,unsig vfBest.assign(vValue.size(), true); nBest = nTotalLower; + seed_insecure_rand(); + for (int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++) { vfIncluded.assign(vValue.size(), false); @@ -982,7 +995,13 @@ static void ApproximateBestSubset(vector<pair<int64, pair<const CWalletTx*,unsig { for (unsigned int i = 0; i < vValue.size(); i++) { - if (nPass == 0 ? rand() % 2 : !vfIncluded[i]) + //The solver here uses a randomized algorithm, + //the randomness serves no real security purpose but is just + //needed to prevent degenerate behavior and it is important + //that the rng fast. We do not use a constant random sequence, + //because there may be some privacy improvement by making + //the selection random. + if (nPass == 0 ? insecure_rand()&1 : !vfIncluded[i]) { nTotal += vValue[i].first; vfIncluded[i] = true; @@ -1190,7 +1209,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CW scriptChange.SetDestination(vchPubKey.GetID()); // Insert change txn at random position: - vector<CTxOut>::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()); + vector<CTxOut>::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()+1); wtxNew.vout.insert(position, CTxOut(nChange, scriptChange)); } else @@ -1208,7 +1227,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CW // Limit size unsigned int nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK, PROTOCOL_VERSION); - if (nBytes >= MAX_BLOCK_SIZE_GEN/5) + if (nBytes >= MAX_STANDARD_TX_SIZE) return false; dPriority /= nBytes; diff --git a/src/walletdb.cpp b/src/walletdb.cpp index 2282bed18a..fe9bce21e8 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -203,7 +203,8 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, ssKey >> hash; CWalletTx& wtx = pwallet->mapWallet[hash]; ssValue >> wtx; - if (wtx.CheckTransaction() && (wtx.GetHash() == hash)) + CValidationState state; + if (wtx.CheckTransaction(state) && (wtx.GetHash() == hash) && state.IsValid()) wtx.BindWallet(pwallet); else { |