diff options
106 files changed, 2316 insertions, 1019 deletions
@@ -20,7 +20,7 @@ the Bitcoin client software, see http://www.bitcoin.org. License ------- -Bitcoin is released under the terms of the MIT license. See `COPYING` for more +Bitcoin is released under the terms of the MIT license. See [COPYING](COPYING) for more information or see http://opensource.org/licenses/MIT. Development process @@ -38,7 +38,7 @@ submitter will be asked to start a discussion (if they haven't already) on the 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.md`) or are +match the project's coding conventions (see [doc/coding.md](doc/coding.md)) or are controversial. The `master` branch is regularly built and tested, but is not guaranteed to be @@ -56,10 +56,7 @@ lots of money. ### Automated Testing Developers are strongly encouraged to write unit tests for new code, and to -submit new unit tests for old code. - -Unit tests can be compiled and run (assuming they weren't disabled in configure) with: - make check +submit new unit tests for old code. Unit tests can be compiled and run (assuming they weren't disabled in configure) with: `make check` 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 @@ -71,5 +68,4 @@ for the build/test scripts. 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. diff --git a/configure.ac b/configure.ac index 11357669dd..905acd573c 100644 --- a/configure.ac +++ b/configure.ac @@ -125,7 +125,6 @@ AC_PROG_MKDIR_P AC_PROG_SED AC_PATH_TOOL(AR, ar) AC_PATH_TOOL(RANLIB, ranlib) -AC_PATH_TOOL(WINDRES, windres) AC_PATH_TOOL(STRIP, strip) AC_PATH_TOOL(GCOV, gcov) AC_PATH_PROG(LCOV, lcov) @@ -155,7 +154,7 @@ use_pkgconfig=yes case $host in *mingw*) - #pkgconfig does more harm than good with mingw + #pkgconfig does more harm than good with MinGW use_pkgconfig=no TARGET_OS=windows @@ -177,12 +176,21 @@ case $host in AC_CHECK_LIB([mswsock], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([shlwapi], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([iphlpapi], [main],, AC_MSG_ERROR(lib missing)) + AC_CHECK_LIB([crypt32], [main],, AC_MSG_ERROR(lib missing)) + + AX_CHECK_LINK_FLAG([[-static-libgcc]],[LDFLAGS="$LDFLAGS -static-libgcc"]) + AX_CHECK_LINK_FLAG([[-static-libstdc++]],[LDFLAGS="$LDFLAGS -static-libstdc++"]) AC_PATH_PROG([MAKENSIS], [makensis], none) if test x$MAKENSIS = xnone; then AC_MSG_WARN("makensis not found. Cannot create installer.") fi + AC_PATH_TOOL(WINDRES, windres, none) + if test x$WINDRES = xnone; then + AC_MSG_ERROR("windres not found") + fi + CPPFLAGS="$CPPFLAGS -D_MT -DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB" LEVELDB_TARGET_FLAGS="TARGET_OS=OS_WINDOWS_CROSSCOMPILE" CXXFLAGS="$CXXFLAGS -w" @@ -290,7 +298,7 @@ if test x$use_hardening != xno; then AX_CHECK_COMPILE_FLAG([-fno-stack-protector],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fno-stack-protector"]) AX_CHECK_COMPILE_FLAG([-fstack-protector-all],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fstack-protector-all"]) - # -pie will link successfully with mingw, but it's unsupported and leads to undeterministic binaries + # -pie will link successfully with MinGW, but it's unsupported and leads to undeterministic binaries AX_CHECK_LINK_FLAG([[-pie]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -pie"]) fi diff --git a/contrib/README.md b/contrib/README.md new file mode 100644 index 0000000000..7128fd5eb0 --- /dev/null +++ b/contrib/README.md @@ -0,0 +1,50 @@ +Contrib Index +--------------------- + +### [BitRPC](/contrib/bitrpc) ### +Added bitrpc.py which allows for sending of all standard Bitcoin commands via RPC rather than as command line args. + +### [Debian](/contrib/debian) ### +Contains files used to package bitcoind/bitcoin-qt +for Debian-based Linux systems. If you compile bitcoind/bitcoin-qt yourself, there are some useful files here. + +### [Gitian-descriptors](/contrib/gitian-descriptors) ### +Gavin's notes on getting gitian builds up and running using KVM. + +### [Gitian-downloader](/contrib/gitian-downloader) +Various PGP files of core developers. + +### [Linearize](/contrib/linearize) ### +Construct a linear, no-fork, best version of the blockchain. + +### [MacDeploy](/contrib/macdeploy) ### +Scripts and notes for Mac builds. + +### [PyMiner](/contrib/pyminer) ### + +This is a 'getwork' CPU mining client for Bitcoin. It is pure-python, and therefore very, very slow. The purpose is to provide a reference implementation of a miner, for study. + +### [Qos](/contrib/qos) ### + +A Linux bash script that will set up tc to limit the outgoing bandwidth for connections to the Bitcoin network. This means one can have an always-on bitcoind instance running, and another local bitcoind/bitcoin-qt instance which connects to this node and receives blocks from it. + +### [Seeds](/contrib/seeds) ### +Utility to generate the pnSeed[] array that is compiled into the client. + +### [SpendFrom](/contrib/spendfrom) ### + +Use the raw transactions API to send coins received on a particular +address (or addresses). + +### [TestGen](/contrib/testgen) ### +Utilities to generate test vectors for the data-driven Bitcoin tests. + +### [Test Patches](/contrib/test-patches) ### +These patches are applied when the automated pull-tester +tests each pull and when master is tested using jenkins. + +### [Verify SF Binaries](/contrib/verifysfbinaries) ### +This script attempts to download and verify the signature file SHA256SUMS.asc from SourceForge. + +### [Wallet Tools](/contrib/wallettools) ### +Contains a wallet change password and unlock script. diff --git a/contrib/bitrpc/README.md b/contrib/bitrpc/README.md new file mode 100644 index 0000000000..2dde60a08e --- /dev/null +++ b/contrib/bitrpc/README.md @@ -0,0 +1,2 @@ +### BitRPC ### +Allows for sending of all standard Bitcoin commands via RPC rather than as command line args.
\ No newline at end of file diff --git a/contrib/debian/README b/contrib/debian/README deleted file mode 100644 index 1cb9b75fbd..0000000000 --- a/contrib/debian/README +++ /dev/null @@ -1,20 +0,0 @@ -This directory contains files used to package bitcoind/bitcoin-qt -for Debian-based Linux systems. - -If you compile bitcoind/bitcoin-qt yourself, there are some -useful files here: - -bitcoin: URI support --------------------- - -bitcoin-qt.desktop (Gnome / Open Desktop) -To install: - sudo desktop-file-install bitcoin-qt.desktop - sudo update-desktop-database - -If you build yourself, you will either need to modify the paths in -the .desktop file or copy or symlink your bitcoin-qt binary to /usr/bin -and the ../../share/pixmaps/bitcoin128.png to /usr/share/pixmaps - -bitcoin-qt.protocol (KDE) - diff --git a/contrib/debian/README.md b/contrib/debian/README.md new file mode 100644 index 0000000000..fab9cc2381 --- /dev/null +++ b/contrib/debian/README.md @@ -0,0 +1,21 @@ + +Debian +==================== +This directory contains files used to package bitcoind/bitcoin-qt +for Debian-based Linux systems. If you compile bitcoind/bitcoin-qt yourself, there are some useful files here. + +## bitcoin: URI support ## + + +bitcoin-qt.desktop (Gnome / Open Desktop) +To install: + + sudo desktop-file-install bitcoin-qt.desktop + sudo update-desktop-database + +If you build yourself, you will either need to modify the paths in +the .desktop file or copy or symlink your bitcoin-qt binary to `/usr/bin` +and the `../../share/pixmaps/bitcoin128.png` to `/usr/share/pixmaps` + +bitcoin-qt.protocol (KDE) + diff --git a/contrib/debian/bitcoin-qt.desktop b/contrib/debian/bitcoin-qt.desktop index b2a2cef622..82bc80a734 100644 --- a/contrib/debian/bitcoin-qt.desktop +++ b/contrib/debian/bitcoin-qt.desktop @@ -9,4 +9,4 @@ Terminal=false Type=Application Icon=/usr/share/pixmaps/bitcoin128.png MimeType=x-scheme-handler/bitcoin; -Categories=Office; +Categories=Office;Finance; diff --git a/contrib/debian/changelog b/contrib/debian/changelog index e600e46705..bd6b42dc51 100644 --- a/contrib/debian/changelog +++ b/contrib/debian/changelog @@ -1,3 +1,27 @@ +bitcoin (0.8.5-precise1) precise; urgency=medium + + * New upstream release. + + -- Matt Corallo <matt@bluematt.me> Sun, 15 Sep 2013 14:02:00 -0400 + +bitcoin (0.8.4-precise1) precise; urgency=medium + + * New upstream release. + + -- Matt Corallo <matt@bluematt.me> Wed, 4 Sep 2013 10:25:00 -0400 + +bitcoin (0.8.3-natty1) natty; urgency=low + + * New upstream release. + + -- Matt Corallo <matt@bluematt.me> Wed, 26 Jun 2013 00:18:00 +0100 + +bitcoin (0.8.2-natty1) natty; urgency=low + + * New upstream release. + + -- Matt Corallo <matt@bluematt.me> Wed, 29 Mar 2013 23:23:00 +0100 + bitcoin (0.8.1-natty3) natty; urgency=low * New pixmaps diff --git a/contrib/gitian-descriptors/README b/contrib/gitian-descriptors/README.md index e9c7d4efc2..40bdbd8e32 100644 --- a/contrib/gitian-descriptors/README +++ b/contrib/gitian-descriptors/README.md @@ -1,7 +1,7 @@ -Gavin's notes on getting gitian builds up and running using KVM: +### Gavin's notes on getting gitian builds up and running using KVM:### These instructions distilled from: - https://help.ubuntu.com/community/KVM/Installation +[ https://help.ubuntu.com/community/KVM/Installation]( https://help.ubuntu.com/community/KVM/Installation) ... see there for complete details. You need the right hardware: you need a 64-bit-capable CPU with hardware virtualization support (Intel VT-x or AMD-V). Not all modern CPUs support hardware virtualization. @@ -9,11 +9,13 @@ You need the right hardware: you need a 64-bit-capable CPU with hardware virtual You probably need to enable hardware virtualization in your machine's BIOS. You need to be running a recent version of 64-bit-Ubuntu, and you need to install several prerequisites: - sudo apt-get install ruby apache2 git apt-cacher-ng python-vm-builder qemu-kvm + + sudo apt-get install ruby apache2 git apt-cacher-ng python-vm-builder qemu-kvm Sanity checks: - sudo service apt-cacher-ng status # Should return apt-cacher-ng is running - ls -l /dev/kvm # Should show a /dev/kvm device + + sudo service apt-cacher-ng status # Should return apt-cacher-ng is running + ls -l /dev/kvm # Should show a /dev/kvm device Once you've got the right hardware and software: @@ -22,10 +24,12 @@ Once you've got the right hardware and software: git clone git://github.com/devrandom/gitian-builder.git mkdir gitian-builder/inputs cd gitian-builder/inputs + # Inputs for Linux and Win32: wget -O miniupnpc-1.6.tar.gz 'http://miniupnp.tuxfamily.org/files/download.php?file=miniupnpc-1.6.tar.gz' wget 'http://fukuchi.org/works/qrencode/qrencode-3.2.0.tar.bz2' - # Inputs for Win32: (Linux has packages for these) + + # Inputs for Win32: (Linux has packages for these) wget 'https://downloads.sourceforge.net/project/boost/boost/1.50.0/boost_1_50_0.tar.bz2' wget 'http://www.openssl.org/source/openssl-1.0.1c.tar.gz' wget 'http://download.oracle.com/berkeley-db/db-4.8.30.NC.tar.gz' @@ -58,31 +62,29 @@ Once you've got the right hardware and software: --------------------- -gitian-builder now also supports building using LXC. See - https://help.ubuntu.com/12.04/serverguide/lxc.html +`gitian-builder` now also supports building using LXC. See +[ https://help.ubuntu.com/12.04/serverguide/lxc.html]( https://help.ubuntu.com/12.04/serverguide/lxc.html) ... for how to get LXC up and running under Ubuntu. If your main machine is a 64-bit Mac or PC with a few gigabytes of memory -and at least 10 gigabytes of free disk space, you can gitian-build using +and at least 10 gigabytes of free disk space, you can `gitian-build` using LXC running inside a virtual machine. Here's a description of Gavin's setup on OSX 10.6: -1. Download and install VirtualBox from https://www.virtualbox.org/ +1. Download and install VirtualBox from [https://www.virtualbox.org/](https://www.virtualbox.org/) 2. Download the 64-bit Ubuntu Desktop 12.04 LTS .iso CD image from - http://www.ubuntu.com/ + [http://www.ubuntu.com/](http://www.ubuntu.com/) -3. Run VirtualBox and create a new virtual machine, using the - Ubuntu .iso (see the VirtualBox documentation for details). - Create it with at least 2 gigabytes of memory and a disk - that is at least 20 gigabytes big. +3. Run VirtualBox and create a new virtual machine, using the Ubuntu .iso (see the [VirtualBox documentation](https://www.virtualbox.org/wiki/Documentation) for details). Create it with at least 2 gigabytes of memory and a disk that is at least 20 gigabytes big. 4. Inside the running Ubuntu desktop, install: - sudo apt-get install debootstrap lxc ruby apache2 git apt-cacher-ng python-vm-builder -5. Still inside Ubuntu, tell gitian-builder to use LXC, then follow the "Once you've got the right - hardware and software" instructions above: - export USE_LXC=1 - git clone git://github.com/bitcoin/bitcoin.git - ... etc + sudo apt-get install debootstrap lxc ruby apache2 git apt-cacher-ng python-vm-builder + +5. Still inside Ubuntu, tell gitian-builder to use LXC, then follow the "Once you've got the right hardware and software" instructions above: + + export USE_LXC=1 + git clone git://github.com/bitcoin/bitcoin.git + ... etc
\ No newline at end of file diff --git a/contrib/gitian-descriptors/boost-win32.yml b/contrib/gitian-descriptors/boost-win32.yml index 9eb4cf5968..b421cbe8c5 100644 --- a/contrib/gitian-descriptors/boost-win32.yml +++ b/contrib/gitian-descriptors/boost-win32.yml @@ -1,33 +1,66 @@ --- name: "boost" suites: -- "lucid" +- "precise" architectures: -- "i386" +- "amd64" packages: -- "mingw32" +- "mingw-w64" +- "g++-mingw-w64" - "faketime" - "zip" reference_datetime: "2011-01-30 00:00:00" remotes: [] files: -- "boost_1_50_0.tar.bz2" +- "boost_1_54_0.tar.bz2" +- "boost-mingw-gas-cross-compile-2013-03-03.patch" script: | + # Defines INSTALLPREFIX="$OUTDIR/staging/boost" + HOST=i686-w64-mingw32 + # Input Integrity Check + echo "047e927de336af106a24bceba30069980c191529fd76b8dff8eb9a328b48ae1d boost_1_54_0.tar.bz2" | shasum -c + echo "d2b7f6a1d7051faef3c9cf41a92fa3671d905ef1e1da920d07651a43299f6268 boost-mingw-gas-cross-compile-2013-03-03.patch" | shasum -c + mkdir -p "$INSTALLPREFIX" - tar xjf boost_1_50_0.tar.bz2 - cd boost_1_50_0 - echo "using gcc : 4.4 : i586-mingw32msvc-g++ + tar xjf boost_1_54_0.tar.bz2 + cd boost_1_54_0 + GCCVERSION=$($HOST-g++ -E -dM $(mktemp --suffix=.h) | grep __VERSION__ | cut -d ' ' -f 3 | cut -d '"' -f 2) + echo "using gcc : $GCCVERSION : $HOST-g++ : - <rc>i586-mingw32msvc-windres - <archiver>i586-mingw32msvc-ar + <rc>$HOST-windres + <archiver>$HOST-ar <cxxflags>-frandom-seed=boost1 + <ranlib>$HOST-ranlib ;" > user-config.jam ./bootstrap.sh --without-icu - ./bjam toolset=gcc target-os=windows threadapi=win32 threading=multi variant=release link=static --user-config=user-config.jam --without-mpi --without-python -sNO_BZIP2=1 -sNO_ZLIB=1 --layout=tagged --build-type=complete --prefix="$INSTALLPREFIX" $MAKEOPTS install + + # Workaround: Upstream boost dev refuses to include patch that would allow Free Software cross-compile toolchain to work + # This patch was authored by the Fedora package developer and ships in Fedora's mingw32-boost. + # Please obtain the exact patch that matches the above sha256sum from one of the following mirrors. + # + # Read History: https://svn.boost.org/trac/boost/ticket/7262 + # History Mirror: http://rose.makesad.us/~paulproteus/mirrors/7262%20Boost.Context%20fails%20to%20build%20using%20MinGW.html + # + # Patch: https://svn.boost.org/trac/boost/raw-attachment/ticket/7262/boost-mingw.patch + # Patch Mirror: http://wtogami.fedorapeople.org/boost-mingw-gas-cross-compile-2013-03-03.patch + # Patch Mirror: http://mindstalk.net/host/boost-mingw-gas-cross-compile-2013-03-03.patch + # Patch Mirror: http://rose.makesad.us/~paulproteus/mirrors/boost-mingw-gas-cross-compile-2013-03-03.patch + patch -p0 < ../boost-mingw-gas-cross-compile-2013-03-03.patch + + # Bug Workaround: boost-1.54.0 broke the ability to disable zlib + # https://svn.boost.org/trac/boost/ticket/9156 + sed -i 's^\[ ac.check-library /zlib//zlib : <library>/zlib//zlib^^' libs/iostreams/build/Jamfile.v2 + sed -i 's^<source>zlib.cpp <source>gzip.cpp \]^^' libs/iostreams/build/Jamfile.v2 + + # http://statmt.org/~s0565741/software/boost_1_52_0/libs/context/doc/html/context/requirements.html + # Note: Might need these options in the future for 64bit builds. + # "Please note that address-model=64 must be given to bjam command line on 64bit Windows for 64bit build; otherwise 32bit code will be generated." + # "For cross-compiling the lib you must specify certain additional properties at bjam command line: target-os, abi, binary-format, architecture and address-model." + ./bjam toolset=gcc binary-format=pe target-os=windows threadapi=win32 threading=multi variant=release link=static --user-config=user-config.jam --without-mpi --without-python -sNO_BZIP2=1 -sNO_ZLIB=1 --layout=tagged --build-type=complete --prefix="$INSTALLPREFIX" $MAKEOPTS install cd "$INSTALLPREFIX" export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1 export FAKETIME=$REFERENCE_DATETIME - zip -r boost-win32-1.50.0-gitian3.zip * - cp boost-win32-1.50.0-gitian3.zip $OUTDIR + zip -r boost-win32-1.54.0-gitian-r6.zip * + cp boost-win32-1.54.0-gitian-r6.zip $OUTDIR diff --git a/contrib/gitian-descriptors/deps-win32.yml b/contrib/gitian-descriptors/deps-win32.yml index b00269dcc3..7ad00fcb01 100644 --- a/contrib/gitian-descriptors/deps-win32.yml +++ b/contrib/gitian-descriptors/deps-win32.yml @@ -1,15 +1,15 @@ --- name: "bitcoin-deps" suites: -- "lucid" +- "precise" architectures: -- "i386" +- "amd64" packages: -- "mingw32" +- "mingw-w64" +- "g++-mingw-w64" - "git-core" - "zip" - "faketime" -- "wine" - "psmisc" reference_datetime: "2011-01-30 00:00:00" remotes: [] @@ -26,7 +26,7 @@ script: | export FAKETIME=$REFERENCE_DATETIME export TZ=UTC export INSTALLPREFIX=$OUTDIR/staging/deps - export HOST=i586-mingw32msvc + export HOST=i686-w64-mingw32 # mkdir -p $INSTALLPREFIX @@ -46,10 +46,23 @@ script: | # tar xzf miniupnpc-1.6.tar.gz cd miniupnpc-1.6 - sed 's/dllwrap -k --driver-name gcc/$(DLLWRAP) -k --driver-name $(CC)/' -i Makefile.mingw - sed 's|wingenminiupnpcstrings $< $@|./wingenminiupnpcstrings $< $@|' -i Makefile.mingw + echo " + --- miniupnpc-1.6/Makefile.mingw.orig 2013-09-29 18:52:51.014087958 -1000 + +++ miniupnpc-1.6/Makefile.mingw 2013-09-29 19:09:29.663318691 -1000 + @@ -67,8 +67,8 @@ + + wingenminiupnpcstrings.o: wingenminiupnpcstrings.c + + -miniupnpcstrings.h: miniupnpcstrings.h.in wingenminiupnpcstrings + - wingenminiupnpcstrings \$< \$@ + +miniupnpcstrings.h: miniupnpcstrings.h.in + + sed -e 's|OS/version|MSWindows/5.1.2600|' -e 's|MINIUPNPC_VERSION_STRING \"version\"|MINIUPNPC_VERSION_STRING \"VERSIONHERE\"|' \$< > \$@ + + minixml.o: minixml.c minixml.h miniupnpcstrings.h + + " | sed "s/VERSIONHERE/$(cat VERSION)/" | patch -p1 mkdir -p dll - make -f Makefile.mingw DLLWRAP=$HOST-dllwrap CC=$HOST-gcc AR=$HOST-ar libminiupnpc.a + make -f Makefile.mingw CC=$HOST-gcc AR=$HOST-ar libminiupnpc.a install -d $INSTALLPREFIX/include/miniupnpc install *.h $INSTALLPREFIX/include/miniupnpc install libminiupnpc.a $INSTALLPREFIX/lib @@ -71,12 +84,10 @@ script: | # tar xjf qrencode-3.2.0.tar.bz2 cd qrencode-3.2.0 - png_CFLAGS="-I$INSTALLPREFIX/include" png_LIBS="-L$INSTALLPREFIX/lib -lpng" ./configure --prefix=$INSTALLPREFIX --host=i586-mingw32msvc + png_CFLAGS="-I$INSTALLPREFIX/include" png_LIBS="-L$INSTALLPREFIX/lib -lpng" ./configure --prefix=$INSTALLPREFIX --host=$HOST make make install cd .. # cd $INSTALLPREFIX - zip -r $OUTDIR/bitcoin-deps-0.0.7.zip include lib - # Kill wine processes as gitian won't figure out we are done otherwise - killall wineserver services.exe explorer.exe winedevice.exe + zip -r $OUTDIR/bitcoin-deps-win32-gitian-r9.zip include lib diff --git a/contrib/gitian-descriptors/gitian-win32.yml b/contrib/gitian-descriptors/gitian-win32.yml index f8c7d818ab..a8a823cac0 100644 --- a/contrib/gitian-descriptors/gitian-win32.yml +++ b/contrib/gitian-descriptors/gitian-win32.yml @@ -1,11 +1,12 @@ --- name: "bitcoin" suites: -- "lucid" +- "precise" architectures: -- "i386" +- "amd64" packages: -- "mingw32" +- "mingw-w64" +- "g++-mingw-w64" - "git-core" - "unzip" - "nsis" @@ -21,31 +22,33 @@ remotes: - "url": "https://github.com/bitcoin/bitcoin.git" "dir": "bitcoin" files: -- "qt-win32-4.8.3-gitian-r3.zip" -- "boost-win32-1.50.0-gitian3.zip" -- "bitcoin-deps-0.0.7.zip" -- "protobuf-win32-2.5.0-gitian-r2.zip" +- "qt-win32-4.8.3-gitian-r4.zip" +- "boost-win32-1.54.0-gitian-r6.zip" +- "bitcoin-deps-win32-gitian-r9.zip" +- "protobuf-win32-2.5.0-gitian-r3.zip" script: | # STAGING=$HOME/staging + HOST=i686-w64-mingw32 + # mkdir -p $STAGING cd $STAGING - unzip ../build/qt-win32-4.8.3-gitian-r3.zip - unzip ../build/boost-win32-1.50.0-gitian3.zip - unzip ../build/bitcoin-deps-0.0.7.zip - unzip ../build/protobuf-win32-2.5.0-gitian-r2.zip + unzip ../build/qt-win32-4.8.3-gitian-r4.zip + unzip ../build/boost-win32-1.54.0-gitian-r6.zip + unzip ../build/bitcoin-deps-win32-gitian-r9.zip + unzip ../build/protobuf-win32-2.5.0-gitian-r3.zip cd $HOME/build/ # cd bitcoin export PATH=$STAGING/host/bin:$PATH export TAR_OPTIONS=--mtime=`echo $REFERENCE_DATETIME | awk '{ print $1 }'` ./autogen.sh - ./configure --bindir=$OUTDIR --prefix=$STAGING --host=i586-mingw32msvc --with-qt-plugindir=$STAGING/plugins --with-qt-incdir=$STAGING/include --with-qt-bindir=$STAGING/host/bin --with-boost=$STAGING --disable-maintainer-mode --with-protoc-bindir=$STAGING/host/bin --disable-dependency-tracking CPPFLAGS="-I$STAGING/include" LDFLAGS="-L$STAGING/lib" CXXFLAGS="-frandom-seed=bitcoin" + ./configure --bindir=$OUTDIR --prefix=$STAGING --host=$HOST --with-qt-plugindir=$STAGING/plugins --with-qt-incdir=$STAGING/include --with-qt-bindir=$STAGING/host/bin --with-boost=$STAGING --disable-maintainer-mode --with-protoc-bindir=$STAGING/host/bin --disable-dependency-tracking CPPFLAGS="-I$STAGING/include" LDFLAGS="-L$STAGING/lib" CXXFLAGS="-frandom-seed=bitcoin" make dist mkdir -p distsrc cd distsrc tar --strip-components=1 -xf ../bitcoin-*.tar.* - ./configure --bindir=$OUTDIR --prefix=$STAGING --host=i586-mingw32msvc --with-qt-plugindir=$STAGING/plugins --with-qt-incdir=$STAGING/include --with-qt-bindir=$STAGING/host/bin --with-boost=$STAGING --disable-maintainer-mode --with-protoc-bindir=$STAGING/host/bin --disable-dependency-tracking CPPFLAGS="-I$STAGING/include" LDFLAGS="-L$STAGING/lib" CXXFLAGS="-frandom-seed=bitcoin" + ./configure --bindir=$OUTDIR --prefix=$STAGING --host=i686-w64-mingw32 --with-qt-plugindir=$STAGING/plugins --with-qt-incdir=$STAGING/include --with-qt-bindir=$STAGING/host/bin --with-boost=$STAGING --disable-maintainer-mode --with-protoc-bindir=$STAGING/host/bin --disable-dependency-tracking CPPFLAGS="-I$STAGING/include" LDFLAGS="-L$STAGING/lib" CXXFLAGS="-frandom-seed=bitcoin" export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1 export FAKETIME=$REFERENCE_DATETIME export TZ=UTC diff --git a/contrib/gitian-descriptors/protobuf-win32.yml b/contrib/gitian-descriptors/protobuf-win32.yml index 5385247732..35f261e1a2 100644 --- a/contrib/gitian-descriptors/protobuf-win32.yml +++ b/contrib/gitian-descriptors/protobuf-win32.yml @@ -1,11 +1,12 @@ --- name: "protobuf-win32" suites: -- "lucid" +- "precise" architectures: -- "i386" +- "amd64" packages: -- "mingw32" +- "mingw-w64" +- "g++-mingw-w64" - "zip" - "faketime" reference_datetime: "2013-04-15 00:00:00" @@ -16,7 +17,7 @@ script: | # export TZ=UTC export INSTALLPREFIX=$OUTDIR/staging/deps - export HOST=i586-mingw32msvc + export HOST=i686-w64-mingw32 # # mkdir -p $INSTALLPREFIX @@ -36,6 +37,6 @@ script: | cd $INSTALLPREFIX export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1 export FAKETIME=$REFERENCE_DATETIME - zip -r $OUTDIR/protobuf-win32-2.5.0-gitian-r2.zip include lib host + zip -r $OUTDIR/protobuf-win32-2.5.0-gitian-r3.zip include lib host unset LD_PRELOAD unset FAKETIME diff --git a/contrib/gitian-descriptors/qt-win32.yml b/contrib/gitian-descriptors/qt-win32.yml index 34495c20a6..1fc6f861e0 100644 --- a/contrib/gitian-descriptors/qt-win32.yml +++ b/contrib/gitian-descriptors/qt-win32.yml @@ -1,11 +1,12 @@ --- name: "qt" suites: -- "lucid" +- "precise" architectures: -- "i386" +- "amd64" packages: -- "mingw32" +- "mingw-w64" +- "g++-mingw-w64" - "zip" - "unzip" - "faketime" @@ -14,34 +15,37 @@ reference_datetime: "2011-01-30 00:00:00" remotes: [] files: - "qt-everywhere-opensource-src-4.8.3.tar.gz" -- "bitcoin-deps-0.0.7.zip" +- "bitcoin-deps-win32-gitian-r9.zip" script: | + # + HOST=i686-w64-mingw32 INSTDIR="$HOME/qt/" + # mkdir $INSTDIR mkdir -p $INSTDIR/host/bin # # Need mingw-compiled openssl from bitcoin-deps: - unzip bitcoin-deps-0.0.7.zip + unzip bitcoin-deps-win32-gitian-r9.zip DEPSDIR=`pwd` # tar xzf qt-everywhere-opensource-src-4.8.3.tar.gz cd qt-everywhere-opensource-src-4.8.3 sed 's/$TODAY/2011-01-30/' -i configure - sed 's/i686-pc-mingw32-/i586-mingw32msvc-/' -i mkspecs/unsupported/win32-g++-cross/qmake.conf - sed --posix 's|QMAKE_CFLAGS\t\t= -pipe|QMAKE_CFLAGS\t\t= -pipe -isystem /usr/i586-mingw32msvc/include/ -frandom-seed=qtbuild|' -i mkspecs/unsupported/win32-g++-cross/qmake.conf + sed "s/i686-pc-mingw32-/$HOST-/" -i mkspecs/unsupported/win32-g++-cross/qmake.conf + sed --posix "s|QMAKE_CFLAGS\t\t= -pipe|QMAKE_CFLAGS\t\t= -pipe -isystem /usr/$HOST/include/ -frandom-seed=qtbuild|" -i mkspecs/unsupported/win32-g++-cross/qmake.conf sed 's/QMAKE_CXXFLAGS_EXCEPTIONS_ON = -fexceptions -mthreads/QMAKE_CXXFLAGS_EXCEPTIONS_ON = -fexceptions/' -i mkspecs/unsupported/win32-g++-cross/qmake.conf sed 's/QMAKE_LFLAGS_EXCEPTIONS_ON = -mthreads/QMAKE_LFLAGS_EXCEPTIONS_ON = -lmingwthrd/' -i mkspecs/unsupported/win32-g++-cross/qmake.conf - sed --posix 's/QMAKE_MOC\t\t= i586-mingw32msvc-moc/QMAKE_MOC\t\t= moc/' -i mkspecs/unsupported/win32-g++-cross/qmake.conf - sed --posix 's/QMAKE_RCC\t\t= i586-mingw32msvc-rcc/QMAKE_RCC\t\t= rcc/' -i mkspecs/unsupported/win32-g++-cross/qmake.conf - sed --posix 's/QMAKE_UIC\t\t= i586-mingw32msvc-uic/QMAKE_UIC\t\t= uic/' -i mkspecs/unsupported/win32-g++-cross/qmake.conf + sed --posix "s/QMAKE_MOC\t\t= $HOST-moc/QMAKE_MOC\t\t= moc/" -i mkspecs/unsupported/win32-g++-cross/qmake.conf + sed --posix "s/QMAKE_RCC\t\t= $HOST-rcc/QMAKE_RCC\t\t= rcc/" -i mkspecs/unsupported/win32-g++-cross/qmake.conf + sed --posix "s/QMAKE_UIC\t\t= $HOST-uic/QMAKE_UIC\t\t= uic/" -i mkspecs/unsupported/win32-g++-cross/qmake.conf # ar adds timestamps to every object file included in the static library # providing -D as ar argument is supposed to solve it, but doesn't work as qmake strips off the arguments and adds -M to pass a script... # which somehow cannot be combined with other flags. # use faketime only for ar, as it confuses make/qmake into hanging sometimes - sed --posix "s|QMAKE_LIB\t\t= i586-mingw32msvc-ar -ru|QMAKE_LIB\t\t= $HOME/ar -Dr|" -i mkspecs/unsupported/win32-g++-cross/qmake.conf + sed --posix "s|QMAKE_LIB\t\t= $HOST-ar -ru|QMAKE_LIB\t\t= $HOME/ar -Dr|" -i mkspecs/unsupported/win32-g++-cross/qmake.conf echo '#!/bin/bash' > $HOME/ar echo 'export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1' >> $HOME/ar - echo 'i586-mingw32msvc-ar "$@"' >> $HOME/ar + echo "$HOST-ar \"\$@\"" >> $HOME/ar chmod +x $HOME/ar #export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1 export FAKETIME=$REFERENCE_DATETIME @@ -56,4 +60,4 @@ script: | # as zip stores file timestamps, use faketime to intercept stat calls to set dates for all files to reference date export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1 - zip -r $OUTDIR/qt-win32-4.8.3-gitian-r3.zip * + zip -r $OUTDIR/qt-win32-4.8.3-gitian-r4.zip * diff --git a/contrib/gitian-downloader/michagogo-key.pgp b/contrib/gitian-downloader/michagogo-key.pgp index bc20629fa0..47bc404554 100644 --- a/contrib/gitian-downloader/michagogo-key.pgp +++ b/contrib/gitian-downloader/michagogo-key.pgp @@ -1,27 +1,59 @@ -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: SKS 1.1.0 +Version: GnuPG v1.4.12 (GNU/Linux) -mQENBFGeqJ4BCADb7SI3/+q93gIvN0AGRg9Mtz73OLIOzCHeeoyn+tp7JcYNzxkQ9lfeXiEf -n72Sh8gHkLtLIqr7HlIMo8DxSS8JPRVjlJGkNyAW4SeEwN2wNa5OV8k0N4jBa9a1csFyCyrE -kPKvkUpBkQDvNXjNxyEhHwyZqPanKxy6NXIHOJji8ObOMQXIT9HwJrpjRth3u4uKG968JBTE -yAXAmkt0Zidl1Ykgzcedk4mJSE9uZCW8DjSv2wMLXcQz8+dYsoskT3KRdkowLHxAfj1BNyNc -1+rKLghliM5vSQWi+Lbhi1Bxh4sY1UwAlKnAGqrnAGyIvCtkwTq5QI6ufF2ZY44bvVgpABEB -AAG0IU1pY2hhZ29nbyA8bWljaGFnb2dvQHNlcnZlci5mYWtlPokBOAQTAQIAIgUCUZ6ongIb -AwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQgsXACWKOzwzMUAgAuqUmK10xE5C3lUym -2f72z0t6a2NM5Wfjr9//Y1/okC36C5XAMEtN2UwckPzzJ5p5D5y5yzwfZq5Jd8Py29VQIMsV -7FbC1a0H3D+bCyX+JJ6FAmUbnWOQ/+mydYc74RvD8iwjePNT6kziZNv6dMGctJTl0alwjtQY -gyGkeYKnIxbcyjHX/IawLUrunb/6mSKun87T8+NM/omfFCTc3l8TakpM0wyNYRiUkIfUBvB8 -sDUU3A80qKN/hqRKvlFu3+/kMiAc9ZYQrbmsB+sYWdmM+4zw8NBw3yuYzWyPuoa4PR5ZmS9F -11WLMR5vTRCdLudAqYsWu3LtV6vAIvlOUa2LMLkBDQRRnqieAQgAxZoEe6BBpBRQeal57jWB -atZ1BgwIdHSzbQA9yehDMVGRF3Zqfg0BycOv3g0UGhvRG4zWYXsnG1VKCPCtmP2duvB0Lnhr -iBCCb+nGWF6F3Vgmww+H5xzDVF9sDZaQ6lsW+zzVBLX1PYSN4Jhxpa+TohId4ldOromYOJ9q -21j24+OZuSyc78KQq1APqS3JfV48NnGo2kk43Vx5PYL10wqOrNmUt/90OJfsbs2adTBjcz0B -wEjCrMHnEiSpGIOlvCOVrFZT3fWvKEAnPzX8+wT8N9TEVUf0heRQDKml8ebi8IIVXRwdonC1 -EbDrSZQnz/FtOoAe8kvjB+OSav6MQUYiQQARAQABiQEfBBgBAgAJBQJRnqieAhsMAAoJEILF -wAlijs8ME+MIAMuiAnJwWfZdo0rvpXcuexeo3+ZKSmYAfTVfFdpjADXmNcDEOFU1aVOLd7OB -Ni2LRjSTZD0FR27dT3+HRj2rLzD2hzGpoMIMqoGIsarNMQ+T3Ss1a3548E06/2QBXXHEj5r8 -uf4W2NIHkP/ps3Wy9O/sWk+fCSKBuZ8kgyoZglXjr4Q2bVzFQIRFgQzxIIfx/bBLgYDIGa83 -xNY96vNNqol3kLOlrWnVpUNUTfSKtWcyIurvfwYnxCESNKTv1sCjmXVy03phVKGpKHu7k9wO -+DferdBvS2VYoWGxKDvj3OSGhzwVeVS3kd0wdS554x1X/xcIvHJxsudJDVo3GzMhA2o= -=YdfZ ------END PGP PUBLIC KEY BLOCK-----
\ No newline at end of file +mQENBFGeqJ4BCADb7SI3/+q93gIvN0AGRg9Mtz73OLIOzCHeeoyn+tp7JcYNzxkQ +9lfeXiEfn72Sh8gHkLtLIqr7HlIMo8DxSS8JPRVjlJGkNyAW4SeEwN2wNa5OV8k0 +N4jBa9a1csFyCyrEkPKvkUpBkQDvNXjNxyEhHwyZqPanKxy6NXIHOJji8ObOMQXI +T9HwJrpjRth3u4uKG968JBTEyAXAmkt0Zidl1Ykgzcedk4mJSE9uZCW8DjSv2wML +XcQz8+dYsoskT3KRdkowLHxAfj1BNyNc1+rKLghliM5vSQWi+Lbhi1Bxh4sY1UwA +lKnAGqrnAGyIvCtkwTq5QI6ufF2ZY44bvVgpABEBAAG0IU1pY2hhZ29nbyA8bWlj +aGFnb2dvQHNlcnZlci5mYWtlPokBOAQTAQIAIgUCUZ6ongIbAwYLCQgHAwIGFQgC +CQoLBBYCAwECHgECF4AACgkQgsXACWKOzwzMUAgAuqUmK10xE5C3lUym2f72z0t6 +a2NM5Wfjr9//Y1/okC36C5XAMEtN2UwckPzzJ5p5D5y5yzwfZq5Jd8Py29VQIMsV +7FbC1a0H3D+bCyX+JJ6FAmUbnWOQ/+mydYc74RvD8iwjePNT6kziZNv6dMGctJTl +0alwjtQYgyGkeYKnIxbcyjHX/IawLUrunb/6mSKun87T8+NM/omfFCTc3l8TakpM +0wyNYRiUkIfUBvB8sDUU3A80qKN/hqRKvlFu3+/kMiAc9ZYQrbmsB+sYWdmM+4zw +8NBw3yuYzWyPuoa4PR5ZmS9F11WLMR5vTRCdLudAqYsWu3LtV6vAIvlOUa2LMLRg +TWljaGFnb2dvIChSZWdpc3RlcmVkIG5pY2sgbWljaGFnb2dvIG9uIGZyZWVub2Rl +IGFzIG9mIE9jdG9iZXIgMTIsIDIwMTMpIDxtaWNoYWdvZ29Ac2VydmVyLmZha2U+ +iQE4BBMBAgAiBQJSWarzAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRCC +xcAJYo7PDA7nB/91wAiaMlU5nHLUu0anhNQbGvUdFgKK1zO90S5KzUdJcY438jcS +UJW1az8l9U9JBRIfPRYVhz/Z1TAJ+dCzD7D8BXHFeGEr0zNOh87ly9aB5du7dpN2 +oSBD6wLcJpqxt4h+XjSS2CX98/2ZIJxXENE2KySaTXP39Xl3eNwvJTUBA4XlcMey +J8KMp/IERli4H0O7vRyLgu3yYpUArTqAonzG1g2lfB35PQJfeInrRSniQ336otnZ +A8qwJ63kfUtWVDRz0g1fnvtiLGPivDJaI5hyIaUeJPaXU1+sg7YNroDu60o2NGZh +F+0IjHlvRfzzA+F9Vw38rpSqR3BmCdjf6Sv3iQEcBBABAgAGBQJSWa9/AAoJEH+r +EUJn5PoE/hgH/1T2dAthVucA/hzY0nl4SMjbg+dzNlYBq00Qwx8DRKVjk5et8+kY +oPI3DGILcr+ELnxNekeMv9WQBBtJanUh1K5ohZ6ohoR7lG18LXf5HCdspflB5Me6 +LMA6iMryEP6gIs9GFuoGe2YQavm58YrkqhcPu34dGN7kdurfEXLvDfVlh5ZbKCsP +Gyd7Pbz04SpqykgK1udiTsLVjc70Xhv+jAMqeaCugDX6TLEwjVmZH/xsyKk2Uh3V +Oib5FXADAtKH+vSqqhFpXrw7R/NaBzvCbas8l61DFHiUg1/bo8vsV8MtGcyZmzXJ +C5Gm0njtGOil/g7JF9siUrpxs9Yyt/h+T2W0W01pY2hhZ29nbyAoVXNlciBhY2Nv +dW50IG1pY2hhZ29nbyBvbiBHaXRodWIgYXMgb2YgT2N0b2JlciAxMiwgMjAxMykg +PG1pY2hhZ29nb0BzZXJ2ZXIuZmFrZT6JATcEEwECACIFAlJZqxkCGwMGCwkIBwMC +BhUIAgkKCwQWAgMBAh4BAheAAAoJEILFwAlijs8M+1AH+IU78ARblqTnJeSl0iWH +mEsg4IBK30Q6/exDAcqOEm1Yc171uw2WnGmIvPYOQqxrRTvj3LoQ816dU6jrj6vY +s+XX0R2hxy7ILh17D/3UKnHcddu7rmc7pNEqZeBXaMughqQaPOWkAIe52+qK5tsl +sWllzTYE4jo29uZ3dAtDcKEJjBo/pIXnu1GOslE1+V4X1H9WDlwrS/JXHzyDQAjt +maPR+3gNesDanhrRmrnT3ZXW2ZVd3vGBibhia8PWUhU1uwOH23ySWXncgsHH0Zad +UMjd4w3YliZP/mLn2ghAxHB70IO7lgAgN3HYZeFoufP3pcK440A+CezfQiRcjHl/ +oIkBHAQQAQIABgUCUlmvfwAKCRB/qxFCZ+T6BOq9CACItsrUZPKGeWSTkMHknMrV +K5vxIXJVCBb+Tppc0Q/J5p4EkW/RFhTwIP2zw8NLDKMh5oO9md4LXhvfIZkqQJFo +6ZtLa3Vf+Kj7uyxezBo4QHA+G7tDsRGaMKVrEMiyLCwS1+hg9VaNzsf7zmQW7mYE +vTLMHp3cVaSU7Mh2Dl8rnAaM/DpTUZQwZ+32Qrb/Z4HSa4f278iqoFpjEbBE2KCr +vT5yEVvpCZ4lwSgA2a+uTlRTvVV6NA/kpsxU64tmhuEOjy+ToDqJ8wv4mqvWZxMv +C6OhfVaXBy3U9gG8aQV0ffXGs+TbCtv8ApHd6E1/AVk0oyZGJaBVrEl688bBIWd/ +uQENBFGeqJ4BCADFmgR7oEGkFFB5qXnuNYFq1nUGDAh0dLNtAD3J6EMxUZEXdmp+ +DQHJw6/eDRQaG9EbjNZheycbVUoI8K2Y/Z268HQueGuIEIJv6cZYXoXdWCbDD4fn +HMNUX2wNlpDqWxb7PNUEtfU9hI3gmHGlr5OiEh3iV06uiZg4n2rbWPbj45m5LJzv +wpCrUA+pLcl9Xjw2cajaSTjdXHk9gvXTCo6s2ZS3/3Q4l+xuzZp1MGNzPQHASMKs +wecSJKkYg6W8I5WsVlPd9a8oQCc/Nfz7BPw31MRVR/SF5FAMqaXx5uLwghVdHB2i +cLURsOtJlCfP8W06gB7yS+MH45Jq/oxBRiJBABEBAAGJAR8EGAECAAkFAlGeqJ4C +GwwACgkQgsXACWKOzwwT4wgAy6ICcnBZ9l2jSu+ldy57F6jf5kpKZgB9NV8V2mMA +NeY1wMQ4VTVpU4t3s4E2LYtGNJNkPQVHbt1Pf4dGPasvMPaHMamgwgyqgYixqs0x +D5PdKzVrfnjwTTr/ZAFdccSPmvy5/hbY0geQ/+mzdbL07+xaT58JIoG5nySDKhmC +VeOvhDZtXMVAhEWBDPEgh/H9sEuBgMgZrzfE1j3q802qiXeQs6WtadWlQ1RN9Iq1 +ZzIi6u9/BifEIRI0pO/WwKOZdXLTemFUoakoe7uT3A74N96t0G9LZVihYbEoO+Pc +5IaHPBV5VLeR3TB1LnnjHVf/Fwi8cnGy50kNWjcbMyEDag== +=jyQ4 +-----END PGP PUBLIC KEY BLOCK----- diff --git a/contrib/linearize/README.md b/contrib/linearize/README.md new file mode 100644 index 0000000000..70b9f034cd --- /dev/null +++ b/contrib/linearize/README.md @@ -0,0 +1,2 @@ +### Linearize ### +Construct a linear, no-fork, best version of the blockchain.
\ No newline at end of file diff --git a/contrib/misc/example-linearize.cfg b/contrib/linearize/example-linearize.cfg index 9e5aa404c2..9e5aa404c2 100644 --- a/contrib/misc/example-linearize.cfg +++ b/contrib/linearize/example-linearize.cfg diff --git a/contrib/misc/linearize.py b/contrib/linearize/linearize.py index 2d8509f83c..2d8509f83c 100755..100644 --- a/contrib/misc/linearize.py +++ b/contrib/linearize/linearize.py diff --git a/contrib/macdeploy/README.md b/contrib/macdeploy/README.md new file mode 100644 index 0000000000..5f0611f20c --- /dev/null +++ b/contrib/macdeploy/README.md @@ -0,0 +1,19 @@ +### MacDeploy ### + +You will need the appscript package for the fancy disk image creation to work: + + sudo easy_install appscript + +For Snow Leopard (which uses [Python 2.6](http://www.python.org/download/releases/2.6/)), you will need the param_parser package: + + sudo easy_install argparse + +This script should not be run manually, instead, after building as usual: + + make deploy + +During the process, the disk image window will pop up briefly where the fancy +settings are applied. This is normal, please do not interfere. + +When finished, it will produce `Bitcoin-Qt.dmg`. + diff --git a/contrib/macdeploy/notes.txt b/contrib/macdeploy/notes.txt deleted file mode 100644 index e7f62c4a39..0000000000 --- a/contrib/macdeploy/notes.txt +++ /dev/null @@ -1,14 +0,0 @@ -You will need the appscript package for the fancy disk image creation to work. -Install it by invoking "sudo easy_install appscript". - -For Snow Leopard (which uses Python 2.6), you will need the param_parser package. -Install it by invoking "sudo easy_install argparse" - -This script should not be run manually, instead, after building as usual: -"make deploy" - -During the process, the disk image window will pop up briefly where the fancy -settings are applied. This is normal, please do not interfere. - -When finished, it will produce Bitcoin-Qt.dmg. - diff --git a/contrib/pyminer/README b/contrib/pyminer/README.md index d1596575dd..119d51bdc7 100644 --- a/contrib/pyminer/README +++ b/contrib/pyminer/README.md @@ -1,5 +1,6 @@ +### PyMiner ### -This is a 'getwork' CPU mining client for bitcoin. +This is a 'getwork' CPU mining client for Bitcoin. It is pure-python, and therefore very, very slow. The purpose is to provide a reference implementation of a miner, for study. diff --git a/contrib/qos/README b/contrib/qos/README.md index f419685744..5e0a975fc6 100644 --- a/contrib/qos/README +++ b/contrib/qos/README.md @@ -1,3 +1,5 @@ +### Qos ### + This is a Linux bash script that will set up tc to limit the outgoing bandwidth for connections to the Bitcoin network. It limits outbound TCP traffic with a source or destination port of 8333, but not if the destination IP is within a LAN (defined as 192.168.x.x). This means one can have an always-on bitcoind instance running, and another local bitcoind/bitcoin-qt instance which connects to this node and receives blocks from it. diff --git a/contrib/seeds/README b/contrib/seeds/README.md index 97e3e1ec71..f9a0c277e2 100644 --- a/contrib/seeds/README +++ b/contrib/seeds/README.md @@ -1,9 +1,11 @@ +### Seeds ### + Utility to generate the pnSeed[] array that is compiled into the client -(see src/net.cpp). +(see [src/net.cpp](/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 + 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/spendfrom/README b/contrib/spendfrom/README.md index 8a087a0c1e..bc4def5f74 100644 --- a/contrib/spendfrom/README +++ b/contrib/spendfrom/README.md @@ -1,16 +1,18 @@ +### SpendFrom ### + Use the raw transactions API to send coins received on a particular address (or addresses). -Depends on jsonrpc +Depends on `jsonrpc` Usage: -spendfrom.py --from=FROMADDRESS1[,FROMADDRESS2] --to=TOADDRESS --amount=amount \ - --fee=fee --datadir=/path/to/.bitcoin --testnet --dry_run + spendfrom.py --from=FROMADDRESS1[,FROMADDRESS2] --to=TOADDRESS --amount=amount \ + --fee=fee --datadir=/path/to/.bitcoin --testnet --dry_run With no arguments, outputs a list of amounts associated with addresses. -With arguments, sends coins received by the FROMADDRESS addresses to the TOADDRESS. +With arguments, sends coins received by the `FROMADDRESS` addresses to the `TOADDRESS`. You may explictly specify how much fee to pay (a fee more than 1% of the amount will fail, though, to prevent bitcoin-losing accidents). Spendfrom may fail if @@ -18,11 +20,11 @@ it thinks the transaction would never be confirmed (if the amount being sent is too small, or if the transaction is too many bytes for the fee). If a change output needs to be created, the change will be sent to the last -FROMADDRESS (if you specify just one FROMADDRESS, change will go back to it). +`FROMADDRESS` (if you specify just one `FROMADDRESS`, change will go back to it). -If --datadir is not specified, the default datadir is used. +If `--datadir` is not specified, the default datadir is used. -The --dry_run option will just create and sign the the transaction and print +The `--dry_run` option will just create and sign the the transaction and print the transaction data (as hexadecimal), instead of broadcasting it. If the transaction is created and broadcast successfully, a transaction id diff --git a/contrib/test-patches/README b/contrib/test-patches/README.md index ed754cea7a..def40b0d6c 100644 --- a/contrib/test-patches/README +++ b/contrib/test-patches/README.md @@ -1,4 +1,7 @@ +### Test Patches ### + These patches are applied when the automated pull-tester tests each pull and when master is tested using jenkins. You can find more information about the tests run at -http://jenkins.bluematt.me/pull-tester/files/ +[http://jenkins.bluematt.me/pull-tester/files/ +](http://jenkins.bluematt.me/pull-tester/files/)
\ No newline at end of file diff --git a/contrib/testgen/README b/contrib/testgen/README deleted file mode 100644 index 02d6c4cdc2..0000000000 --- a/contrib/testgen/README +++ /dev/null @@ -1 +0,0 @@ -Utilities to generate test vectors for the data-driven Bitcoin tests diff --git a/contrib/testgen/README.md b/contrib/testgen/README.md new file mode 100644 index 0000000000..83624f443a --- /dev/null +++ b/contrib/testgen/README.md @@ -0,0 +1,8 @@ +### TestGen ### + +Utilities to generate test vectors for the data-driven Bitcoin tests. + +Usage: + + gen_base58_test_vectors.py valid 50 > ../../src/test/data/base58_keys_valid.json + gen_base58_test_vectors.py invalid 50 > ../../src/test/data/base58_keys_invalid.json
\ No newline at end of file diff --git a/contrib/verifysfbinaries/README.md b/contrib/verifysfbinaries/README.md new file mode 100644 index 0000000000..caa4073605 --- /dev/null +++ b/contrib/verifysfbinaries/README.md @@ -0,0 +1,7 @@ +### Verify SF Binaries ### +This script attempts to download the signature file `SHA256SUMS.asc` from SourceForge. + +It first checks if the signature passes, and then downloads the files specified in +the file, and checks if the hashes of these files match those that are specified +in the signature file. The script returns 0 if everything passes the checks. It returns 1 if either the +signature check or the hash check doesn't pass. If an error occurs the return value is 2
\ No newline at end of file diff --git a/contrib/wallettools/README.md b/contrib/wallettools/README.md new file mode 100644 index 0000000000..358f52569e --- /dev/null +++ b/contrib/wallettools/README.md @@ -0,0 +1,2 @@ +### Wallet Tools ### +Contains a wallet change password and unlock script. diff --git a/doc/README.md b/doc/README.md index 1238033fb7..068ed988d1 100644 --- a/doc/README.md +++ b/doc/README.md @@ -3,22 +3,15 @@ Bitcoin 0.8.2 BETA Copyright (c) 2009-2013 Bitcoin Developers +License +--------------------- Distributed under the [MIT/X11 software license](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](mailto:eay@cryptsoft.com)), and UPnP software written by Thomas Bernard. - -Intro ---------------------- -Bitcoin is a free open source peer-to-peer electronic cash system that is -completely decentralized, without the need for a central server or trusted -parties. Users hold the crypto keys to their own money and transact directly -with each other, with the help of a P2P network to check for double-spending. - - Setup --------------------- -[Bitcoin-Qt](http://bitcoin.org/en/download) is the original Bitcoin client and it builds the backbone of the network. However, it downloads and stores the entire history of Bitcoin transactions; depending on the speed of your computer and network connection, the synchronization process can take anywhere from a few hours to a day or more. +[Bitcoin-Qt](http://bitcoin.org/en/download) is the original Bitcoin client and it builds the backbone of the network. However, it downloads and stores the entire history of Bitcoin transactions (which is currently several GBs); depending on the speed of your computer and network connection, the synchronization process can take anywhere from a few hours to a day or more. ### Unix @@ -44,17 +37,20 @@ Unpack the files into a directory and run bitcoin-qt.exe. * See the documentation at the [Bitcoin Wiki](https://en.bitcoin.it/wiki/Main_Page) for help and more information. * Ask for help on [#bitcoin](http://webchat.freenode.net?channels=bitcoin) on Freenode. If you don't have an IRC client use [webchat here](http://webchat.freenode.net?channels=bitcoin). -* Ask for help on the [BitcoinTalk](https://bitcointalk.org/) forums. +* Ask for help on the [BitcoinTalk](https://bitcointalk.org/) forums, in the [technical support board](https://bitcointalk.org/index.php?board=4.0). Building --------------------- -- [Bitcoin-Qt Readme](readme-qt.md) +The following are developer notes on how to build Bitcoin on your native platform. They are not complete guide, but include notes on the necessary libraries, compile flags, etc. + - [OSX Build Notes](build-osx.md) - [Unix Build Notes](build-unix.md) - [Windows Build Notes](build-msw.md) Development --------------------- +The Bitcoin repo's [root README](https://github.com/bitcoin/bitcoin/blob/master/README.md) contains relevant information on the development process and automated testing. + - [Coding Guidelines](coding.md) - [Multiwallet Qt Development](multiwallet-qt.md) - [Release Notes](release-notes.md) @@ -67,4 +63,4 @@ Other Pages --------------------- - [Assets Attribution](assets-attribution.md) - [Files](files.md) -- [Tor Support](tor.md)
\ No newline at end of file +- [Tor Support](tor.md) diff --git a/doc/build-unix.md b/doc/build-unix.md index 579341d2d9..422b46cc3a 100644 --- a/doc/build-unix.md +++ b/doc/build-unix.md @@ -21,6 +21,9 @@ Dependencies libdb4.8 Berkeley DB Blockchain & wallet storage libboost Boost C++ Library miniupnpc UPnP Support Optional firewall-jumping support + qt GUI GUI toolkit + protobuf Payments in GUI Data interchange format used for payment protocol + libqrencode QR codes in GUI Optional for generating QR codes [miniupnpc](http://miniupnp.free.fr/) may be used for UPnP port mapping. It can be downloaded from [here]( http://miniupnp.tuxfamily.org/files/). UPnP support is compiled in and @@ -46,21 +49,25 @@ Licenses of statically linked libraries: - Berkeley DB 4.8.30.NC - Boost 1.37 - miniupnpc 1.6 +- qt 4.8.3 +- protobuf 2.5.0 +- libqrencode 3.2.0 Dependency Build Instructions: Ubuntu & Debian ---------------------------------------------- Build requirements: sudo apt-get install build-essential + sudo apt-get install libtool autotools-dev sudo apt-get install libssl-dev -for Ubuntu 12.04: +for Ubuntu 12.04 and later: sudo apt-get install libboost-all-dev db4.8 packages are available [here](https://launchpad.net/~bitcoin/+archive/bitcoin). - Ubuntu precise has packages for libdb5.1-dev and libdb5.1++-dev, + Ubuntu 12.04 and later have packages for libdb5.1-dev and libdb5.1++-dev, but using these will break binary wallet compatibility, and is not recommended. for other Ubuntu & Debian: @@ -74,6 +81,22 @@ Optional: sudo apt-get install libminiupnpc-dev (see --with-miniupnpc and --enable-upnp-default) +Dependencies for the GUI: Ubuntu & Debian +----------------------------------------- + +If you want to build Bitcoin-Qt, make sure that the required packages for Qt development +are installed. Qt 4 is currently necessary to build the GUI. + +To build with Qt 4 you need the following: + + apt-get install libqt4-dev libprotobuf-dev + +libqrencode (optional) can be installed with: + + apt-get install libqrencode-dev + +Once these are installed, they will be found by configure and a bitcoin-qt executable will be +built by default. Notes ----- diff --git a/doc/readme-qt.md b/doc/readme-qt.md deleted file mode 100644 index 5aa849af10..0000000000 --- a/doc/readme-qt.md +++ /dev/null @@ -1,101 +0,0 @@ -Bitcoin-Qt Readme -=============================== -Contains build and configuration instructions for Bitcoin-Qt (Qt4 GUI for Bitcoin). - -Build Instructions ---------------------- - -### Debian - - -First, make sure that the required packages for Qt4 development of your -distribution are installed, these are - - - -for Debian and Ubuntu <= 11.10 : - - - apt-get install qt4-qmake libqt4-dev build-essential libboost-dev libboost-system-dev \ - libboost-filesystem-dev libboost-program-options-dev libboost-thread-dev \ - libssl-dev libdb4.8++-dev libprotobuf-dev protobuf-compiler - -for Ubuntu >= 12.04 (please read the 'Berkely DB version warning' below): - - apt-get install qt4-qmake libqt4-dev build-essential libboost-dev libboost-system-dev \ - libboost-filesystem-dev libboost-program-options-dev libboost-thread-dev \ - libssl-dev libdb++-dev libminiupnpc-dev libprotobuf-dev protobuf-compiler - -For Qt 5 you need the following, otherwise you get an error with lrelease when running qmake: - - - apt-get install qt5-qmake libqt5gui5 libqt5core5 libqt5dbus5 qttools5-dev-tools - -Once these are installed, they will be found by configure and bitcoin-qt will be -built by default. - - -### Mac OS X - -* Download and install the [Qt Mac OS X SDK](https://qt-project.org/downloads). It is recommended to also install Apple's Xcode with UNIX tools. -* Download and install either [MacPorts](https://www.macports.org/) or [HomeBrew](http://mxcl.github.io/homebrew/). -* Execute the following commands in a terminal to get the dependencies using MacPorts - - sudo port selfupdate - sudo port install boost db48 miniupnpc protobuf-cpp - -* Execute the following commands in a terminal to get the dependencies using HomeBrew: - - brew update - brew install boost miniupnpc openssl berkeley-db4 protobuf - -Build Configuration Options ---------------------- - -### UPnP port forwarding - -UPnP support is compiled in when possible and turned off by default. See the -configure options for upnp behavior desired: - - --with-miniupnpc No UPnP support miniupnp not required - --disable-upnp-default (the default) UPnP support turned off by default at runtime - --enable-upnp-default UPnP support turned on by default at runtime - -### Notification support for recent (k)ubuntu versions - -DBUS support is enabled by default if dependencies are met. - -See the --with-qtdbus configure option. - -### Generation of QR codes - -[libqrencode](http://fukuchi.org/works/qrencode/) may be used to generate QRCode images for payment requests. - -QR code support is enabled by default if dependencies are met. - -See the --with-qrencode configure option. - -Warnings ---------------------- - -### Berkely DB Version Warning - - -A warning for people using the *static binary* version of Bitcoin on a Linux/UNIX-ish system (tl;dr: **Berkely DB databases are not forward compatible**). - -The static binary version of Bitcoin is linked against libdb4.8 (see also [this Debian issue](http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=621425)). - -Now the nasty thing is that databases from 5.X are not compatible with 4.X. - -If the globally installed development package of Berkely DB installed on your system is 5.X, any source you build yourself will be linked against that. The first time you run with a 5.X version the database will be upgraded, and 4.X cannot open the new format. This means that you cannot go back to the old statically linked version without significant hassle! - -### Ubuntu 11.10 Warning - - -Ubuntu 11.10 has a package called 'qt-at-spi' installed by default. At the time of writing, having that package installed causes bitcoin-qt to crash intermittently. The issue has been reported as [launchpad bug 857790](https://bugs.launchpad.net/ubuntu/+source/qt-at-spi/+bug/857790), but -isn't yet fixed. - -Until the bug is fixed, you can remove the qt-at-spi package to work around the problem, though this will presumably disable screen reader functionality for Qt apps: - - sudo apt-get remove qt-at-spi - diff --git a/doc/release-process.md b/doc/release-process.md index 57a3c9d91f..9e0b860a8c 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -31,29 +31,34 @@ Release Process export SIGNER=(your gitian key, ie bluematt, sipa, etc) export VERSION=(new version, e.g. 0.8.0) + pushd ./bitcoin + git checkout v${VERSION} + popd pushd ./gitian-builder Fetch and build inputs: (first time, or when dependency versions change) mkdir -p inputs; cd inputs/ wget 'http://miniupnp.free.fr/files/download.php?file=miniupnpc-1.6.tar.gz' -O miniupnpc-1.6.tar.gz - wget 'http://www.openssl.org/source/openssl-1.0.1c.tar.gz' + wget 'https://www.openssl.org/source/openssl-1.0.1c.tar.gz' wget 'http://download.oracle.com/berkeley-db/db-4.8.30.NC.tar.gz' - wget 'http://zlib.net/zlib-1.2.6.tar.gz' - wget 'ftp://ftp.simplesystems.org/pub/libpng/png/src/libpng-1.5.9.tar.gz' - wget 'http://fukuchi.org/works/qrencode/qrencode-3.2.0.tar.bz2' - wget 'http://downloads.sourceforge.net/project/boost/boost/1.50.0/boost_1_50_0.tar.bz2' - wget 'http://releases.qt-project.org/qt4/source/qt-everywhere-opensource-src-4.8.3.tar.gz' - wget 'http://protobuf.googlecode.com/files/protobuf-2.5.0.tar.bz2' + wget 'ftp://ftp.simplesystems.org/pub/libpng/png/src/history/zlib/zlib-1.2.6.tar.gz' + wget 'ftp://ftp.simplesystems.org/pub/libpng/png/src/history/libpng15/libpng-1.5.9.tar.gz' + wget 'https://fukuchi.org/works/qrencode/qrencode-3.2.0.tar.bz2' + wget 'https://downloads.sourceforge.net/project/boost/boost/1.54.0/boost_1_54_0.tar.bz2' + wget 'https://svn.boost.org/trac/boost/raw-attachment/ticket/7262/boost-mingw.patch' -O \ + boost-mingw-gas-cross-compile-2013-03-03.patch + wget 'https://download.qt-project.org/archive/qt/4.8/4.8.3/qt-everywhere-opensource-src-4.8.3.tar.gz' + wget 'https://protobuf.googlecode.com/files/protobuf-2.5.0.tar.bz2' cd .. ./bin/gbuild ../bitcoin/contrib/gitian-descriptors/boost-win32.yml - mv build/out/boost-win32-1.50.0-gitian2.zip inputs/ - ./bin/gbuild ../bitcoin/contrib/gitian-descriptors/qt-win32.yml - mv build/out/qt-win32-4.8.3-gitian-r2.zip inputs/ + mv build/out/boost-win32-*.zip inputs/ ./bin/gbuild ../bitcoin/contrib/gitian-descriptors/deps-win32.yml - mv build/out/bitcoin-deps-0.0.5.zip inputs/ + mv build/out/bitcoin-deps-*.zip inputs/ + ./bin/gbuild ../bitcoin/contrib/gitian-descriptors/qt-win32.yml + mv build/out/qt-win32-*.zip inputs/ ./bin/gbuild ../bitcoin/contrib/gitian-descriptors/protobuf-win32.yml - mv build/out/protobuf-win32-2.5.0-gitian-r1.zip inputs/ + mv build/out/protobuf-win32-*.zip inputs/ Build bitcoind and bitcoin-qt on Linux32, Linux64, and Win32: diff --git a/doc/tor.md b/doc/tor.md index 86d56cffd5..6057801d34 100644 --- a/doc/tor.md +++ b/doc/tor.md @@ -48,6 +48,7 @@ config file): HiddenServiceDir /var/lib/tor/bitcoin-service/ HiddenServicePort 8333 127.0.0.1:8333 + HiddenServicePort 18333 127.0.0.1:18333 The directory can be different of course, but (both) port numbers should be equal to your bitcoind's P2P listen port (8333 by default). diff --git a/qa/pull-tester/build-tests.sh.in b/qa/pull-tester/build-tests.sh.in index b353c57909..461e7be048 100755 --- a/qa/pull-tester/build-tests.sh.in +++ b/qa/pull-tester/build-tests.sh.in @@ -3,43 +3,72 @@ # Param2: Path to java comparison tool # Param3: Number of make jobs. Defaults to 1. +# Exit immediately if anything fails: set -e set -o xtrace MINGWPREFIX=$1 JAVA_COMPARISON_TOOL=$2 JOBS=${3-1} +OUT_DIR=${4-} if [ $# -lt 2 ]; then - echo "Usage: $0 [mingw-prefix] [java-comparison-tool] <make jobs>" + echo "Usage: $0 [mingw-prefix] [java-comparison-tool] <make jobs> <save output dir>" exit 1 fi DISTDIR=@PACKAGE@-@VERSION@ +# Cross-compile for windows first (breaking the mingw/windows build is most common) cd @abs_top_srcdir@ make distdir -mv $DISTDIR linux-build -cd linux-build -./configure --with-comparison-tool="$JAVA_COMPARISON_TOOL" +mv $DISTDIR win32-build +cd win32-build +./configure --disable-silent-rules --disable-ccache --prefix=$MINGWPREFIX --host=i586-mingw32msvc --with-qt-bindir=$MINGWPREFIX/host/bin --with-qt-plugindir=$MINGWPREFIX/plugins --with-qt-incdir=$MINGWPREFIX/include --with-boost=$MINGWPREFIX --with-protoc-bindir=$MINGWPREFIX/host/bin CPPFLAGS=-I$MINGWPREFIX/include LDFLAGS=-L$MINGWPREFIX/lib make -j$JOBS -make check -#Test code coverage +# And compile for Linux: cd @abs_top_srcdir@ make distdir -mv $DISTDIR linux-coverage-build -cd linux-coverage-build -./configure --enable-lcov --with-comparison-tool="$JAVA_COMPARISON_TOOL" +mv $DISTDIR linux-build +cd linux-build +# TODO: re-enable blockchain tester tool, as of 11 Oct 2013 is it not working properly +# on the pull-tester machine. +#./configure --disable-silent-rules --disable-ccache --with-comparison-tool="$JAVA_COMPARISON_TOOL" +./configure --disable-silent-rules --disable-ccache make -j$JOBS -make cov - -# win32 build disabled until pull-tester has updated dependencies -##Test win32 build -#cd @abs_top_srcdir@ -#make distdir -#mv $DISTDIR win32-build -#cd win32-build -#./configure --prefix=$MINGWPREFIX --host=i586-mingw32msvc --with-qt-bindir=$MINGWPREFIX/host/bin --with-qt-plugindir=$MINGWPREFIX/plugins --with-qt-incdir=$MINGWPREFIX/include --with-boost=$MINGWPREFIX --with-protoc-bindir=$MINGWPREFIX/host/bin --with-comparison-tool="$JAVA_COMPARISON_TOOL" CPPFLAGS=-I$MINGWPREFIX/include LDFLAGS=-L$MINGWPREFIX/lib -#make -j$JOBS -#make check + +# link interesting binaries to parent out/ directory, if it exists. Do this before +# running unit tests (we want bad binaries to be easy to find) +if [ -d "$OUT_DIR" -a -w "$OUT_DIR" ]; then + set +e + # Windows: + cp @abs_top_srcdir@/win32-build/src/bitcoind.exe $OUT_DIR/bitcoind.exe + cp @abs_top_srcdir@/win32-build/src/test/test_bitcoin.exe $OUT_DIR/test_bitcoin.exe + cp @abs_top_srcdir@/win32-build/src/qt/bitcoind-qt.exe $OUT_DIR/bitcoin-qt.exe + # Linux: + cp @abs_top_srcdir@/linux-build/src/bitcoind $OUT_DIR/bitcoind + cp @abs_top_srcdir@/linux-build/src/test/test_bitcoin $OUT_DIR/test_bitcoin + cp @abs_top_srcdir@/linux-build/src/qt/bitcoind-qt $OUT_DIR/bitcoin-qt + set -e +fi + +# Run unit tests and blockchain-tester on Linux: +cd @abs_top_srcdir@/linux-build +make check + +# Clean up builds (pull-tester machine doesn't have infinite disk space) +cd @abs_top_srcdir@/linux-build +make clean +cd @abs_top_srcdir@/win32-build +make clean + +# TODO: Fix code coverage builds on pull-tester machine +# # Test code coverage +# cd @abs_top_srcdir@ +# make distdir +# mv $DISTDIR linux-coverage-build +# cd linux-coverage-build +# ./configure --enable-lcov --disable-silent-rules --disable-ccache --with-comparison-tool="$JAVA_COMPARISON_TOOL" +# make -j$JOBS +# make cov diff --git a/qa/pull-tester/pull-tester.py b/qa/pull-tester/pull-tester.py index fe50177a24..34dd74c7e0 100755 --- a/qa/pull-tester/pull-tester.py +++ b/qa/pull-tester/pull-tester.py @@ -38,7 +38,7 @@ def checkout_pull(clone_url, commit, out): # Init build_dir=os.environ["BUILD_DIR"] run("umount ${CHROOT_COPY}/proc", fail_hard=False) - run("rsync --delete -apv ${CHROOT_MASTER} ${CHROOT_COPY}") + run("rsync --delete -apv ${CHROOT_MASTER}/ ${CHROOT_COPY}") run("rm -rf ${CHROOT_COPY}${SCRIPTS_DIR}") run("cp -a ${SCRIPTS_DIR} ${CHROOT_COPY}${SCRIPTS_DIR}") # Merge onto upstream/master @@ -108,17 +108,18 @@ def testpull(number, comment_url, clone_url, commit): open(os.environ["TESTED_DB"], "a").write(commit + "\n") return - # New: pull-tester.sh script(s) are in the tree: + run("rm -rf ${CHROOT_COPY}/${OUT_DIR}", fail_hard=False); + run("mkdir -p ${CHROOT_COPY}/${OUT_DIR}", fail_hard=False); + run("chown -R ${BUILD_USER}:${BUILD_GROUP} ${CHROOT_COPY}/${OUT_DIR}", fail_hard=False) + script = os.environ["BUILD_PATH"]+"/qa/pull-tester/pull-tester.sh" - script += " ${BUILD_PATH} ${MINGW_DEPS_DIR} ${SCRIPTS_DIR}/BitcoindComparisonTool.jar 1" + script += " ${BUILD_PATH} ${MINGW_DEPS_DIR} ${SCRIPTS_DIR}/BitcoindComparisonTool.jar 6 ${OUT_DIR}" returncode = run("chroot ${CHROOT_COPY} sudo -u ${BUILD_USER} -H timeout ${TEST_TIMEOUT} "+script, fail_hard=False, stdout=out, stderr=out) + run("mv ${CHROOT_COPY}/${OUT_DIR} " + dir) run("mv ${BUILD_DIR} " + dir) - # TODO: FIXME - # Idea: have run-script save interesting output... - # run("cp /mnt/chroot-tmp/home/ubuntu/.bitcoin/regtest/debug.log " + dir) - # os.system("chmod +r " + dir + "/debug.log") + if returncode == 42: print("Successfully tested pull (needs tests) - sending comment to: " + comment_url) commentOn(comment_url, True, False, True, resultsurl) @@ -147,6 +148,7 @@ environ_default("MINGW_DEPS_DIR", "/mnt/w32deps") environ_default("SCRIPTS_DIR", "/mnt/test-scripts") environ_default("CHROOT_COPY", "/mnt/chroot-tmp") environ_default("CHROOT_MASTER", "/mnt/chroot") +environ_default("OUT_DIR", "/mnt/out") environ_default("BUILD_PATH", "/mnt/bitcoin") os.environ["BUILD_DIR"] = os.environ["CHROOT_COPY"] + os.environ["BUILD_PATH"] environ_default("RESULTS_DIR", "/mnt/www/pull-tester") diff --git a/src/Makefile.am b/src/Makefile.am index 3b0f096744..49249fedc7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,7 @@ +include Makefile.include AM_CPPFLAGS = $(INCLUDES) -I$(top_builddir)/src/obj \ -I$(top_srcdir)/src/leveldb/include -I$(top_srcdir)/src/leveldb/helpers \ - -I$(builddir) $(BOOST_INCLUDES) + -I$(builddir) $(BOOST_INCLUDES) $(BDB_CPPFLAGS) AM_LDFLAGS = $(PTHREAD_CFLAGS) noinst_LIBRARIES = libbitcoin.a @@ -30,7 +31,7 @@ obj/build.h: FORCE $(abs_top_srcdir) version.o: obj/build.h -libbitcoin_a_SOURCES = addrman.cpp alert.cpp bitcoinrpc.cpp bloom.cpp \ +libbitcoin_a_SOURCES = addrman.cpp alert.cpp allocators.cpp bitcoinrpc.cpp bloom.cpp \ chainparams.cpp checkpoints.cpp core.cpp crypter.cpp db.cpp hash.cpp \ init.cpp key.cpp keystore.cpp leveldb.cpp main.cpp miner.cpp \ netbase.cpp net.cpp noui.cpp protocol.cpp rpcblockchain.cpp rpcdump.cpp \ @@ -47,6 +48,10 @@ bitcoind_LDADD = libbitcoin.a leveldb/libleveldb.a leveldb/libmemenv.a \ bitcoind_SOURCES = bitcoind.cpp # +if TARGET_WINDOWS +bitcoind_SOURCES += bitcoind-res.rc +endif + AM_CPPFLAGS += $(BDB_CPPFLAGS) bitcoind_LDADD += $(BDB_LIBS) diff --git a/src/Makefile.include b/src/Makefile.include index d002e8ed6f..352471061d 100644 --- a/src/Makefile.include +++ b/src/Makefile.include @@ -1,13 +1,11 @@ -.PHONY: FORCE +# Helper for rules and subdir Makefiles to find parent targets. +# Flags and other non-target variables should not be set here. LIBBITCOIN=$(top_builddir)/src/libbitcoin.a LIBLEVELDB=$(top_builddir)/src/leveldb/libleveldb.a LIBMEMENV=$(top_builddir)/src/leveldb/libmemenv.a LIBBITCOINQT=$(top_builddir)/src/qt/libbitcoinqt.a -INCLUDES += $(BDB_CPPFLAGS) -LIBBITCOIN += $(BDB_LIBS) - $(LIBBITCOIN): $(MAKE) -C $(top_builddir)/src $(@F) diff --git a/src/allocators.cpp b/src/allocators.cpp new file mode 100644 index 0000000000..b239b623d8 --- /dev/null +++ b/src/allocators.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2009-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "allocators.h" + +#ifdef WIN32 +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif +#define _WIN32_WINNT 0x0501 +#define WIN32_LEAN_AND_MEAN 1 +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include <windows.h> +// This is used to attempt to keep keying material out of swap +// Note that VirtualLock does not provide this as a guarantee on Windows, +// but, in practice, memory that has been VirtualLock'd almost never gets written to +// the pagefile except in rare circumstances where memory is extremely low. +#else +#include <sys/mman.h> +#include <limits.h> // for PAGESIZE +#include <unistd.h> // for sysconf +#endif + +/** Determine system page size in bytes */ +static inline size_t GetSystemPageSize() +{ + size_t page_size; +#if defined(WIN32) + SYSTEM_INFO sSysInfo; + GetSystemInfo(&sSysInfo); + page_size = sSysInfo.dwPageSize; +#elif defined(PAGESIZE) // defined in limits.h + page_size = PAGESIZE; +#else // assume some POSIX OS + page_size = sysconf(_SC_PAGESIZE); +#endif + return page_size; +} + +bool MemoryPageLocker::Lock(const void *addr, size_t len) +{ +#ifdef WIN32 + return VirtualLock(const_cast<void*>(addr), len); +#else + return mlock(addr, len) == 0; +#endif +} + +bool MemoryPageLocker::Unlock(const void *addr, size_t len) +{ +#ifdef WIN32 + return VirtualUnlock(const_cast<void*>(addr), len); +#else + return munlock(addr, len) == 0; +#endif +} + +LockedPageManager::LockedPageManager() : LockedPageManagerBase<MemoryPageLocker>(GetSystemPageSize()) +{ +} + diff --git a/src/allocators.h b/src/allocators.h index 85af8fe376..fd6f51b27e 100644 --- a/src/allocators.h +++ b/src/allocators.h @@ -11,25 +11,6 @@ #include <map> #include <openssl/crypto.h> // for OPENSSL_cleanse() -#ifdef WIN32 -#ifdef _WIN32_WINNT -#undef _WIN32_WINNT -#endif -#define _WIN32_WINNT 0x0501 -#define WIN32_LEAN_AND_MEAN 1 -#ifndef NOMINMAX -#define NOMINMAX -#endif -#include <windows.h> -// This is used to attempt to keep keying material out of swap -// Note that VirtualLock does not provide this as a guarantee on Windows, -// but, in practice, memory that has been VirtualLock'd almost never gets written to -// the pagefile except in rare circumstances where memory is extremely low. -#else -#include <sys/mman.h> -#include <limits.h> // for PAGESIZE -#include <unistd.h> // for sysconf -#endif /** * Thread-safe class to keep track of locked (ie, non-swappable) memory pages. @@ -115,21 +96,6 @@ private: Histogram histogram; }; -/** Determine system page size in bytes */ -static inline size_t GetSystemPageSize() -{ - size_t page_size; -#if defined(WIN32) - SYSTEM_INFO sSysInfo; - GetSystemInfo(&sSysInfo); - page_size = sSysInfo.dwPageSize; -#elif defined(PAGESIZE) // defined in limits.h - page_size = PAGESIZE; -#else // assume some POSIX OS - page_size = sysconf(_SC_PAGESIZE); -#endif - return page_size; -} /** * OS-dependent memory page locking/unlocking. @@ -141,25 +107,11 @@ public: /** Lock memory pages. * addr and len must be a multiple of the system page size */ - bool Lock(const void *addr, size_t len) - { -#ifdef WIN32 - return VirtualLock(const_cast<void*>(addr), len); -#else - return mlock(addr, len) == 0; -#endif - } + bool Lock(const void *addr, size_t len); /** Unlock memory pages. * addr and len must be a multiple of the system page size */ - bool Unlock(const void *addr, size_t len) - { -#ifdef WIN32 - return VirtualUnlock(const_cast<void*>(addr), len); -#else - return munlock(addr, len) == 0; -#endif - } + bool Unlock(const void *addr, size_t len); }; /** @@ -171,9 +123,7 @@ class LockedPageManager: public LockedPageManagerBase<MemoryPageLocker> public: static LockedPageManager instance; // instantiated in util.cpp private: - LockedPageManager(): - LockedPageManagerBase<MemoryPageLocker>(GetSystemPageSize()) - {} + LockedPageManager(); }; // diff --git a/src/bitcoind-res.rc b/src/bitcoind-res.rc new file mode 100644 index 0000000000..202b7ab352 --- /dev/null +++ b/src/bitcoind-res.rc @@ -0,0 +1,36 @@ +#include <windows.h> // needed for VERSIONINFO +#include "clientversion.h" // holds the needed client version information + +#define VER_PRODUCTVERSION CLIENT_VERSION_MAJOR,CLIENT_VERSION_MINOR,CLIENT_VERSION_REVISION,CLIENT_VERSION_BUILD +#define VER_PRODUCTVERSION_STR STRINGIZE(CLIENT_VERSION_MAJOR) "." STRINGIZE(CLIENT_VERSION_MINOR) "." STRINGIZE(CLIENT_VERSION_REVISION) "." STRINGIZE(CLIENT_VERSION_BUILD) +#define VER_FILEVERSION VER_PRODUCTVERSION +#define VER_FILEVERSION_STR VER_PRODUCTVERSION_STR +#define COPYRIGHT_STR "2009-" STRINGIZE(COPYRIGHT_YEAR) " The Bitcoin developers" + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VER_FILEVERSION +PRODUCTVERSION VER_PRODUCTVERSION +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_APP +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" // U.S. English - multilingual (hex) + BEGIN + VALUE "CompanyName", "Bitcoin" + VALUE "FileDescription", "Bitcoind (OSS daemon/client for Bitcoin)" + VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "InternalName", "bitcoind" + VALUE "LegalCopyright", COPYRIGHT_STR + VALUE "LegalTrademarks1", "Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php." + VALUE "OriginalFilename", "bitcoind.exe" + VALUE "ProductName", "Bitcoind" + VALUE "ProductVersion", VER_PRODUCTVERSION_STR + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1252 // language neutral - multilingual (decimal) + END +END diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 02e4e7d6e7..add3b4e1f0 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -9,15 +9,18 @@ void DetectShutdownThread(boost::thread_group* threadGroup) { - bool shutdown = ShutdownRequested(); + bool fShutdown = ShutdownRequested(); // Tell the main threads to shutdown. - while (!shutdown) + while (!fShutdown) { MilliSleep(200); - shutdown = ShutdownRequested(); + fShutdown = ShutdownRequested(); } if (threadGroup) + { threadGroup->interrupt_all(); + threadGroup->join_all(); + } } ////////////////////////////////////////////////////////////////////////////// @@ -66,6 +69,7 @@ bool AppInit(int argc, char* argv[]) } // Command-line RPC + bool fCommandLine = false; for (int i = 1; i < argc; i++) if (!IsSwitchChar(argv[i][0]) && !boost::algorithm::istarts_with(argv[i], "bitcoin:")) fCommandLine = true; @@ -107,10 +111,16 @@ bool AppInit(int argc, char* argv[]) } catch (...) { PrintExceptionContinue(NULL, "AppInit()"); } - if (!fRet) { + + if (!fRet) + { if (detectShutdownThread) detectShutdownThread->interrupt(); + threadGroup.interrupt_all(); + // threadGroup.join_all(); was left out intentionally here, because we didn't re-test all of + // the startup-failure cases to make sure they don't result in a hang due to some + // thread-blocking-waiting-for-another-thread-during-startup case } if (detectShutdownThread) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index f9e0c476f6..798660dff3 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -160,6 +160,9 @@ string CRPCTable::help(string strCommand) const continue; if (strCommand != "" && strMethod != strCommand) continue; + if (pcmd->reqWallet && !pwalletMain) + continue; + try { Array params; @@ -218,76 +221,79 @@ Value stop(const Array& params, bool fHelp) static const CRPCCommand vRPCCommands[] = -{ // name actor (function) okSafeMode threadSafe - // ------------------------ ----------------------- ---------- ---------- - { "help", &help, true, true }, - { "stop", &stop, true, true }, - { "getblockcount", &getblockcount, true, false }, - { "getbestblockhash", &getbestblockhash, true, false }, - { "getconnectioncount", &getconnectioncount, true, false }, - { "getpeerinfo", &getpeerinfo, true, false }, - { "addnode", &addnode, true, true }, - { "getaddednodeinfo", &getaddednodeinfo, true, true }, - { "getdifficulty", &getdifficulty, true, false }, - { "getgenerate", &getgenerate, true, false }, - { "setgenerate", &setgenerate, true, false }, - { "gethashespersec", &gethashespersec, true, false }, - { "getinfo", &getinfo, true, false }, - { "getmininginfo", &getmininginfo, true, false }, - { "getnewaddress", &getnewaddress, true, false }, - { "getaccountaddress", &getaccountaddress, true, false }, - { "getrawchangeaddress", &getrawchangeaddress, true, false }, - { "setaccount", &setaccount, true, false }, - { "getaccount", &getaccount, false, false }, - { "getaddressesbyaccount", &getaddressesbyaccount, true, false }, - { "sendtoaddress", &sendtoaddress, false, false }, - { "getreceivedbyaddress", &getreceivedbyaddress, false, false }, - { "getreceivedbyaccount", &getreceivedbyaccount, false, false }, - { "listreceivedbyaddress", &listreceivedbyaddress, false, false }, - { "listreceivedbyaccount", &listreceivedbyaccount, false, false }, - { "backupwallet", &backupwallet, true, false }, - { "keypoolrefill", &keypoolrefill, true, false }, - { "walletpassphrase", &walletpassphrase, true, false }, - { "walletpassphrasechange", &walletpassphrasechange, false, false }, - { "walletlock", &walletlock, true, false }, - { "encryptwallet", &encryptwallet, false, false }, - { "validateaddress", &validateaddress, true, false }, - { "getbalance", &getbalance, false, false }, - { "move", &movecmd, false, false }, - { "sendfrom", &sendfrom, false, false }, - { "sendmany", &sendmany, false, false }, - { "addmultisigaddress", &addmultisigaddress, false, false }, - { "createmultisig", &createmultisig, true, true }, - { "getrawmempool", &getrawmempool, true, false }, - { "getblock", &getblock, false, false }, - { "getblockhash", &getblockhash, false, false }, - { "gettransaction", &gettransaction, false, false }, - { "listtransactions", &listtransactions, false, false }, - { "listaddressgroupings", &listaddressgroupings, false, false }, - { "signmessage", &signmessage, false, false }, - { "verifymessage", &verifymessage, false, false }, - { "getwork", &getwork, true, false }, - { "listaccounts", &listaccounts, false, false }, - { "settxfee", &settxfee, false, false }, - { "getblocktemplate", &getblocktemplate, true, false }, - { "submitblock", &submitblock, false, false }, - { "listsinceblock", &listsinceblock, false, false }, - { "dumpprivkey", &dumpprivkey, true, false }, - { "dumpwallet", &dumpwallet, true, false }, - { "importprivkey", &importprivkey, false, false }, - { "importwallet", &importwallet, false, false }, - { "listunspent", &listunspent, false, false }, - { "getrawtransaction", &getrawtransaction, false, false }, - { "createrawtransaction", &createrawtransaction, false, false }, - { "decoderawtransaction", &decoderawtransaction, false, false }, - { "decodescript", &decodescript, false, false }, - { "signrawtransaction", &signrawtransaction, false, false }, - { "sendrawtransaction", &sendrawtransaction, false, false }, - { "gettxoutsetinfo", &gettxoutsetinfo, true, false }, - { "gettxout", &gettxout, true, false }, - { "lockunspent", &lockunspent, false, false }, - { "listlockunspent", &listlockunspent, false, false }, - { "verifychain", &verifychain, true, false }, +{ // name actor (function) okSafeMode threadSafe reqWallet + // ------------------------ ----------------------- ---------- ---------- --------- + { "help", &help, true, true, false }, + { "stop", &stop, true, true, false }, + { "getblockcount", &getblockcount, true, false, false }, + { "getbestblockhash", &getbestblockhash, true, false, false }, + { "getconnectioncount", &getconnectioncount, true, false, false }, + { "getpeerinfo", &getpeerinfo, true, false, false }, + { "ping", &ping, true, false, false }, + { "addnode", &addnode, true, true, false }, + { "getaddednodeinfo", &getaddednodeinfo, true, true, false }, + { "getnettotals", &getnettotals, true, true, false }, + { "getdifficulty", &getdifficulty, true, false, false }, + { "getnetworkhashps", &getnetworkhashps, true, false, false }, + { "getgenerate", &getgenerate, true, false, false }, + { "setgenerate", &setgenerate, true, false, true }, + { "gethashespersec", &gethashespersec, true, false, false }, + { "getinfo", &getinfo, true, false, false }, + { "getmininginfo", &getmininginfo, true, false, false }, + { "getnewaddress", &getnewaddress, true, false, true }, + { "getaccountaddress", &getaccountaddress, true, false, true }, + { "getrawchangeaddress", &getrawchangeaddress, true, false, true }, + { "setaccount", &setaccount, true, false, true }, + { "getaccount", &getaccount, false, false, true }, + { "getaddressesbyaccount", &getaddressesbyaccount, true, false, true }, + { "sendtoaddress", &sendtoaddress, false, false, true }, + { "getreceivedbyaddress", &getreceivedbyaddress, false, false, true }, + { "getreceivedbyaccount", &getreceivedbyaccount, false, false, true }, + { "listreceivedbyaddress", &listreceivedbyaddress, false, false, true }, + { "listreceivedbyaccount", &listreceivedbyaccount, false, false, true }, + { "backupwallet", &backupwallet, true, false, true }, + { "keypoolrefill", &keypoolrefill, true, false, true }, + { "walletpassphrase", &walletpassphrase, true, false, true }, + { "walletpassphrasechange", &walletpassphrasechange, false, false, true }, + { "walletlock", &walletlock, true, false, true }, + { "encryptwallet", &encryptwallet, false, false, true }, + { "validateaddress", &validateaddress, true, false, false }, + { "getbalance", &getbalance, false, false, true }, + { "move", &movecmd, false, false, true }, + { "sendfrom", &sendfrom, false, false, true }, + { "sendmany", &sendmany, false, false, true }, + { "addmultisigaddress", &addmultisigaddress, false, false, true }, + { "createmultisig", &createmultisig, true, true , false }, + { "getrawmempool", &getrawmempool, true, false, false }, + { "getblock", &getblock, false, false, false }, + { "getblockhash", &getblockhash, false, false, false }, + { "gettransaction", &gettransaction, false, false, true }, + { "listtransactions", &listtransactions, false, false, true }, + { "listaddressgroupings", &listaddressgroupings, false, false, true }, + { "signmessage", &signmessage, false, false, true }, + { "verifymessage", &verifymessage, false, false, false }, + { "getwork", &getwork, true, false, true }, + { "listaccounts", &listaccounts, false, false, true }, + { "settxfee", &settxfee, false, false, true }, + { "getblocktemplate", &getblocktemplate, true, false, false }, + { "submitblock", &submitblock, false, false, false }, + { "listsinceblock", &listsinceblock, false, false, true }, + { "dumpprivkey", &dumpprivkey, true, false, true }, + { "dumpwallet", &dumpwallet, true, false, true }, + { "importprivkey", &importprivkey, false, false, true }, + { "importwallet", &importwallet, false, false, true }, + { "listunspent", &listunspent, false, false, true }, + { "getrawtransaction", &getrawtransaction, false, false, false }, + { "createrawtransaction", &createrawtransaction, false, false, false }, + { "decoderawtransaction", &decoderawtransaction, false, false, false }, + { "decodescript", &decodescript, false, false, false }, + { "signrawtransaction", &signrawtransaction, false, false, false }, + { "sendrawtransaction", &sendrawtransaction, false, false, false }, + { "gettxoutsetinfo", &gettxoutsetinfo, true, false, false }, + { "gettxout", &gettxout, true, false, false }, + { "lockunspent", &lockunspent, false, false, true }, + { "listlockunspent", &listlockunspent, false, false, true }, + { "verifychain", &verifychain, true, false, false }, }; CRPCTable::CRPCTable() @@ -876,7 +882,8 @@ void StopRPCThreads() deadlineTimers.clear(); rpc_io_service->stop(); - rpc_worker_group->join_all(); + if (rpc_worker_group != NULL) + rpc_worker_group->join_all(); delete rpc_worker_group; rpc_worker_group = NULL; delete rpc_ssl_context; rpc_ssl_context = NULL; delete rpc_io_service; rpc_io_service = NULL; @@ -1007,8 +1014,8 @@ void ServiceConnection(AcceptedConnection *conn) { LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", conn->peer_address_to_string().c_str()); /* Deter brute-forcing short passwords. - If this results in a DOS the user really - shouldn't have their RPC port exposed.*/ + If this results in a DoS the user really + shouldn't have their RPC port exposed. */ if (mapArgs["-rpcpassword"].size() < 20) MilliSleep(250); @@ -1064,6 +1071,8 @@ json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_s const CRPCCommand *pcmd = tableRPC[strMethod]; if (!pcmd) throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found"); + if (pcmd->reqWallet && !pwalletMain) + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (disabled)"); // Observe safe mode string strWarning = GetWarnings("rpc"); @@ -1078,7 +1087,10 @@ json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_s { if (pcmd->threadSafe) result = pcmd->actor(params, false); - else { + else if (!pwalletMain) { + LOCK(cs_main); + result = pcmd->actor(params, false); + } else { LOCK2(cs_main, pwalletMain->cs_wallet); result = pcmd->actor(params, false); } @@ -1188,6 +1200,8 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri if (strMethod == "getaddednodeinfo" && n > 0) ConvertTo<bool>(params[0]); if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]); if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]); + if (strMethod == "getnetworkhashps" && n > 0) ConvertTo<boost::int64_t>(params[0]); + if (strMethod == "getnetworkhashps" && n > 1) ConvertTo<boost::int64_t>(params[1]); if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]); if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]); if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]); diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h index ab0a0bc968..1cad12f14a 100644 --- a/src/bitcoinrpc.h +++ b/src/bitcoinrpc.h @@ -104,6 +104,7 @@ public: rpcfn_type actor; bool okSafeMode; bool threadSafe; + bool reqWallet; }; /** @@ -152,8 +153,10 @@ extern void EnsureWalletIsUnlocked(); extern json_spirit::Value getconnectioncount(const json_spirit::Array& params, bool fHelp); // in rpcnet.cpp extern json_spirit::Value getpeerinfo(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value ping(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value addnode(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getaddednodeinfo(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getnettotals(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value dumpprivkey(const json_spirit::Array& params, bool fHelp); // in rpcdump.cpp extern json_spirit::Value importprivkey(const json_spirit::Array& params, bool fHelp); @@ -162,6 +165,7 @@ extern json_spirit::Value importwallet(const json_spirit::Array& params, bool fH extern json_spirit::Value getgenerate(const json_spirit::Array& params, bool fHelp); // in rpcmining.cpp extern json_spirit::Value setgenerate(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getnetworkhashps(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value gethashespersec(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getmininginfo(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getwork(const json_spirit::Array& params, bool fHelp); diff --git a/src/chainparams.h b/src/chainparams.h index ce3c14306d..3f99b7eb06 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -68,7 +68,7 @@ public: virtual const vector<CAddress>& FixedSeeds() const = 0; int RPCPort() const { return nRPCPort; } protected: - CChainParams() {}; + CChainParams() {} uint256 hashGenesisBlock; MessageStartChars pchMessageStart; diff --git a/src/compat.h b/src/compat.h index 9caf5e4810..b126df901a 100644 --- a/src/compat.h +++ b/src/compat.h @@ -6,11 +6,17 @@ #define _BITCOIN_COMPAT_H #ifdef WIN32 +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif #define _WIN32_WINNT 0x0501 #define WIN32_LEAN_AND_MEAN 1 #ifndef NOMINMAX #define NOMINMAX #endif +#ifdef FD_SETSIZE +#undef FD_SETSIZE // prevent redefinition compiler warning +#endif #define FD_SETSIZE 1024 // max number of fds in fd_set #include <winsock2.h> #include <ws2tcpip.h> diff --git a/src/core.h b/src/core.h index ce21acd59e..9ee8b2edce 100644 --- a/src/core.h +++ b/src/core.h @@ -661,4 +661,38 @@ public: void print() const; }; + +/** Describes a place in the block chain to another node such that if the + * other node doesn't have the same branch, it can find a recent common trunk. + * The further back it is, the further before the fork it may be. + */ +struct CBlockLocator +{ + std::vector<uint256> vHave; + + CBlockLocator() {} + + CBlockLocator(const std::vector<uint256>& vHaveIn) + { + vHave = vHaveIn; + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(vHave); + ) + + void SetNull() + { + vHave.clear(); + } + + bool IsNull() + { + return vHave.empty(); + } +}; + #endif @@ -16,7 +16,7 @@ #include <db_cxx.h> class CAddrMan; -class CBlockLocator; +struct CBlockLocator; class CDiskBlockIndex; class CMasterKey; class COutPoint; diff --git a/src/init.cpp b/src/init.cpp index fb03e7b663..fce5992255 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -100,6 +100,7 @@ static CCoinsViewDB *pcoinsdbview; void Shutdown() { + LogPrintf("Shutdown : In progress...\n"); static CCriticalSection cs_Shutdown; TRY_LOCK(cs_Shutdown, lockShutdown); if (!lockShutdown) return; @@ -108,13 +109,14 @@ void Shutdown() nTransactionsUpdated++; StopRPCThreads(); ShutdownRPCMining(); - bitdb.Flush(false); + if (pwalletMain) + bitdb.Flush(false); GenerateBitcoins(false, NULL); StopNode(); { LOCK(cs_main); if (pwalletMain) - pwalletMain->SetBestChain(CBlockLocator(pindexBest)); + pwalletMain->SetBestChain(chainActive.GetLocator()); if (pblocktree) pblocktree->Flush(); if (pcoinsTip) @@ -123,10 +125,13 @@ void Shutdown() delete pcoinsdbview; pcoinsdbview = NULL; delete pblocktree; pblocktree = NULL; } - bitdb.Flush(true); + if (pwalletMain) + bitdb.Flush(true); boost::filesystem::remove(GetPidFile()); UnregisterAllWallets(); - delete pwalletMain; + if (pwalletMain) + delete pwalletMain; + LogPrintf("Shutdown : done\n"); } // @@ -339,8 +344,8 @@ bool AppInit2(boost::thread_group& threadGroup) // Minimum supported OS versions: WinXP SP3, WinVista >= SP1, Win Server 2008 // A failure is non-critical and needs no further attention! #ifndef PROCESS_DEP_ENABLE -// We define this here, because GCCs winbase.h limits this to _WIN32_WINNT >= 0x0601 (Windows 7), -// which is not correct. Can be removed, when GCCs winbase.h is fixed! + // We define this here, because GCCs winbase.h limits this to _WIN32_WINNT >= 0x0601 (Windows 7), + // which is not correct. Can be removed, when GCCs winbase.h is fixed! #define PROCESS_DEP_ENABLE 0x00000001 #endif typedef BOOL (WINAPI *PSETPROCDEPPOL)(DWORD); @@ -761,7 +766,7 @@ bool AppInit2(boost::thread_group& threadGroup) // If the loaded chain has a wrong genesis, bail out immediately // (we're likely using a testnet datadir, or the other way around). - if (!mapBlockIndex.empty() && pindexGenesisBlock == NULL) + if (!mapBlockIndex.empty() && chainActive.Genesis() == NULL) return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?")); // Initialize the block index (no-op if non-empty database was already loaded) @@ -907,7 +912,7 @@ bool AppInit2(boost::thread_group& threadGroup) strErrors << _("Cannot write default address") << "\n"; } - pwalletMain->SetBestChain(CBlockLocator(pindexBest)); + pwalletMain->SetBestChain(chainActive.GetLocator()); } LogPrintf("%s", strErrors.str().c_str()); @@ -915,26 +920,26 @@ bool AppInit2(boost::thread_group& threadGroup) RegisterWallet(pwalletMain); - CBlockIndex *pindexRescan = pindexBest; + CBlockIndex *pindexRescan = chainActive.Tip(); if (GetBoolArg("-rescan", false)) - pindexRescan = pindexGenesisBlock; + pindexRescan = chainActive.Genesis(); else { CWalletDB walletdb(strWalletFile); CBlockLocator locator; if (walletdb.ReadBestBlock(locator)) - pindexRescan = locator.GetBlockIndex(); + pindexRescan = chainActive.FindFork(locator); else - pindexRescan = pindexGenesisBlock; + pindexRescan = chainActive.Genesis(); } - if (pindexBest && pindexBest != pindexRescan) + if (chainActive.Tip() && chainActive.Tip() != pindexRescan) { uiInterface.InitMessage(_("Rescanning...")); - LogPrintf("Rescanning last %i blocks (from block %i)...\n", pindexBest->nHeight - pindexRescan->nHeight, pindexRescan->nHeight); + LogPrintf("Rescanning last %i blocks (from block %i)...\n", chainActive.Height() - pindexRescan->nHeight, pindexRescan->nHeight); nStart = GetTimeMillis(); pwalletMain->ScanForWalletTransactions(pindexRescan, true); LogPrintf(" rescan %15"PRI64d"ms\n", GetTimeMillis() - nStart); - pwalletMain->SetBestChain(CBlockLocator(pindexBest)); + pwalletMain->SetBestChain(chainActive.GetLocator()); nWalletDBUpdated++; } @@ -980,10 +985,10 @@ bool AppInit2(boost::thread_group& threadGroup) //// debug print LogPrintf("mapBlockIndex.size() = %"PRIszu"\n", mapBlockIndex.size()); - LogPrintf("nBestHeight = %d\n", nBestHeight); - LogPrintf("setKeyPool.size() = %"PRIszu"\n", pwalletMain->setKeyPool.size()); - LogPrintf("mapWallet.size() = %"PRIszu"\n", pwalletMain->mapWallet.size()); - LogPrintf("mapAddressBook.size() = %"PRIszu"\n", pwalletMain->mapAddressBook.size()); + LogPrintf("nBestHeight = %d\n", chainActive.Height()); + LogPrintf("setKeyPool.size() = %"PRIszu"\n", pwalletMain ? pwalletMain->setKeyPool.size() : 0); + LogPrintf("mapWallet.size() = %"PRIszu"\n", pwalletMain ? pwalletMain->mapWallet.size() : 0); + LogPrintf("mapAddressBook.size() = %"PRIszu"\n", pwalletMain ? pwalletMain->mapAddressBook.size() : 0); StartNode(threadGroup); @@ -993,17 +998,20 @@ bool AppInit2(boost::thread_group& threadGroup) StartRPCThreads(); // Generate coins in the background - GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain); + if (pwalletMain) + GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain); // ********************************************************* Step 12: finished uiInterface.InitMessage(_("Done loading")); - // Add wallet transactions that aren't already in a block to mapTransactions - pwalletMain->ReacceptWalletTransactions(); + if (pwalletMain) { + // Add wallet transactions that aren't already in a block to mapTransactions + pwalletMain->ReacceptWalletTransactions(); - // Run a thread to flush wallet periodically - threadGroup.create_thread(boost::bind(&ThreadFlushWalletDB, boost::ref(pwalletMain->strWalletFile))); + // Run a thread to flush wallet periodically + threadGroup.create_thread(boost::bind(&ThreadFlushWalletDB, boost::ref(pwalletMain->strWalletFile))); + } return !fRequestShutdown; } diff --git a/src/leveldb/Makefile b/src/leveldb/Makefile index 96af7765be..20c9c4f287 100644 --- a/src/leveldb/Makefile +++ b/src/leveldb/Makefile @@ -31,6 +31,7 @@ TESTHARNESS = ./util/testharness.o $(TESTUTIL) TESTS = \ arena_test \ + autocompact_test \ bloom_test \ c_test \ cache_test \ @@ -70,7 +71,7 @@ SHARED = $(SHARED1) else # Update db.h if you change these. SHARED_MAJOR = 1 -SHARED_MINOR = 12 +SHARED_MINOR = 13 SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT) SHARED2 = $(SHARED1).$(SHARED_MAJOR) SHARED3 = $(SHARED1).$(SHARED_MAJOR).$(SHARED_MINOR) @@ -114,6 +115,9 @@ leveldbutil: db/leveldb_main.o $(LIBOBJECTS) arena_test: util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) $(CXX) $(LDFLAGS) util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) +autocompact_test: db/autocompact_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/autocompact_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + bloom_test: util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) $(CXX) $(LDFLAGS) util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) diff --git a/src/leveldb/db/autocompact_test.cc b/src/leveldb/db/autocompact_test.cc new file mode 100644 index 0000000000..d20a2362c3 --- /dev/null +++ b/src/leveldb/db/autocompact_test.cc @@ -0,0 +1,118 @@ +// Copyright (c) 2013 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/db.h" +#include "db/db_impl.h" +#include "leveldb/cache.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +class AutoCompactTest { + public: + std::string dbname_; + Cache* tiny_cache_; + Options options_; + DB* db_; + + AutoCompactTest() { + dbname_ = test::TmpDir() + "/autocompact_test"; + tiny_cache_ = NewLRUCache(100); + options_.block_cache = tiny_cache_; + DestroyDB(dbname_, options_); + options_.create_if_missing = true; + options_.compression = kNoCompression; + ASSERT_OK(DB::Open(options_, dbname_, &db_)); + } + + ~AutoCompactTest() { + delete db_; + DestroyDB(dbname_, Options()); + delete tiny_cache_; + } + + std::string Key(int i) { + char buf[100]; + snprintf(buf, sizeof(buf), "key%06d", i); + return std::string(buf); + } + + uint64_t Size(const Slice& start, const Slice& limit) { + Range r(start, limit); + uint64_t size; + db_->GetApproximateSizes(&r, 1, &size); + return size; + } + + void DoReads(int n); +}; + +static const int kValueSize = 200 * 1024; +static const int kTotalSize = 100 * 1024 * 1024; +static const int kCount = kTotalSize / kValueSize; + +// Read through the first n keys repeatedly and check that they get +// compacted (verified by checking the size of the key space). +void AutoCompactTest::DoReads(int n) { + std::string value(kValueSize, 'x'); + DBImpl* dbi = reinterpret_cast<DBImpl*>(db_); + + // Fill database + for (int i = 0; i < kCount; i++) { + ASSERT_OK(db_->Put(WriteOptions(), Key(i), value)); + } + ASSERT_OK(dbi->TEST_CompactMemTable()); + + // Delete everything + for (int i = 0; i < kCount; i++) { + ASSERT_OK(db_->Delete(WriteOptions(), Key(i))); + } + ASSERT_OK(dbi->TEST_CompactMemTable()); + + // Get initial measurement of the space we will be reading. + const int64_t initial_size = Size(Key(0), Key(n)); + const int64_t initial_other_size = Size(Key(n), Key(kCount)); + + // Read until size drops significantly. + std::string limit_key = Key(n); + for (int read = 0; true; read++) { + ASSERT_LT(read, 100) << "Taking too long to compact"; + Iterator* iter = db_->NewIterator(ReadOptions()); + for (iter->SeekToFirst(); + iter->Valid() && iter->key().ToString() < limit_key; + iter->Next()) { + // Drop data + } + delete iter; + // Wait a little bit to allow any triggered compactions to complete. + Env::Default()->SleepForMicroseconds(1000000); + uint64_t size = Size(Key(0), Key(n)); + fprintf(stderr, "iter %3d => %7.3f MB [other %7.3f MB]\n", + read+1, size/1048576.0, Size(Key(n), Key(kCount))/1048576.0); + if (size <= initial_size/10) { + break; + } + } + + // Verify that the size of the key space not touched by the reads + // is pretty much unchanged. + const int64_t final_other_size = Size(Key(n), Key(kCount)); + ASSERT_LE(final_other_size, initial_other_size + 1048576); + ASSERT_GE(final_other_size, initial_other_size/5 - 1048576); +} + +TEST(AutoCompactTest, ReadAll) { + DoReads(kCount); +} + +TEST(AutoCompactTest, ReadHalf) { + DoReads(kCount/2); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/corruption_test.cc b/src/leveldb/db/corruption_test.cc index 31b2d5f416..b37ffdfe64 100644 --- a/src/leveldb/db/corruption_test.cc +++ b/src/leveldb/db/corruption_test.cc @@ -35,6 +35,7 @@ class CorruptionTest { CorruptionTest() { tiny_cache_ = NewLRUCache(100); options_.env = &env_; + options_.block_cache = tiny_cache_; dbname_ = test::TmpDir() + "/db_test"; DestroyDB(dbname_, options_); @@ -50,17 +51,14 @@ class CorruptionTest { delete tiny_cache_; } - Status TryReopen(Options* options = NULL) { + Status TryReopen() { delete db_; db_ = NULL; - Options opt = (options ? *options : options_); - opt.env = &env_; - opt.block_cache = tiny_cache_; - return DB::Open(opt, dbname_, &db_); + return DB::Open(options_, dbname_, &db_); } - void Reopen(Options* options = NULL) { - ASSERT_OK(TryReopen(options)); + void Reopen() { + ASSERT_OK(TryReopen()); } void RepairDB() { @@ -92,6 +90,10 @@ class CorruptionTest { for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { uint64_t key; Slice in(iter->key()); + if (in == "" || in == "~") { + // Ignore boundary keys. + continue; + } if (!ConsumeDecimalNumber(&in, &key) || !in.empty() || key < next_expected) { @@ -233,7 +235,7 @@ TEST(CorruptionTest, TableFile) { dbi->TEST_CompactRange(1, NULL, NULL); Corrupt(kTableFile, 100, 1); - Check(99, 99); + Check(90, 99); } TEST(CorruptionTest, TableFileIndexData) { @@ -299,7 +301,7 @@ TEST(CorruptionTest, CompactionInputError) { ASSERT_EQ(1, Property("leveldb.num-files-at-level" + NumberToString(last))); Corrupt(kTableFile, 100, 1); - Check(9, 9); + Check(5, 9); // Force compactions by writing lots of values Build(10000); @@ -307,32 +309,23 @@ TEST(CorruptionTest, CompactionInputError) { } TEST(CorruptionTest, CompactionInputErrorParanoid) { - Options options; - options.paranoid_checks = true; - options.write_buffer_size = 1048576; - Reopen(&options); + options_.paranoid_checks = true; + options_.write_buffer_size = 512 << 10; + Reopen(); DBImpl* dbi = reinterpret_cast<DBImpl*>(db_); - // Fill levels >= 1 so memtable compaction outputs to level 1 - for (int level = 1; level < config::kNumLevels; level++) { - dbi->Put(WriteOptions(), "", "begin"); - dbi->Put(WriteOptions(), "~", "end"); + // Make multiple inputs so we need to compact. + for (int i = 0; i < 2; i++) { + Build(10); dbi->TEST_CompactMemTable(); + Corrupt(kTableFile, 100, 1); + env_.SleepForMicroseconds(100000); } + dbi->CompactRange(NULL, NULL); - Build(10); - dbi->TEST_CompactMemTable(); - ASSERT_EQ(1, Property("leveldb.num-files-at-level0")); - - Corrupt(kTableFile, 100, 1); - Check(9, 9); - - // Write must eventually fail because of corrupted table - Status s; + // Write must fail because of corrupted table std::string tmp1, tmp2; - for (int i = 0; i < 10000 && s.ok(); i++) { - s = db_->Put(WriteOptions(), Key(i, &tmp1), Value(i, &tmp2)); - } + Status s = db_->Put(WriteOptions(), Key(5, &tmp1), Value(5, &tmp2)); ASSERT_TRUE(!s.ok()) << "write did not fail in corrupted paranoid db"; } diff --git a/src/leveldb/db/db_impl.cc b/src/leveldb/db/db_impl.cc index 395d3172ad..fa1351038b 100644 --- a/src/leveldb/db/db_impl.cc +++ b/src/leveldb/db/db_impl.cc @@ -113,14 +113,14 @@ Options SanitizeOptions(const std::string& dbname, return result; } -DBImpl::DBImpl(const Options& options, const std::string& dbname) - : env_(options.env), - internal_comparator_(options.comparator), - internal_filter_policy_(options.filter_policy), - options_(SanitizeOptions( - dbname, &internal_comparator_, &internal_filter_policy_, options)), - owns_info_log_(options_.info_log != options.info_log), - owns_cache_(options_.block_cache != options.block_cache), +DBImpl::DBImpl(const Options& raw_options, const std::string& dbname) + : env_(raw_options.env), + internal_comparator_(raw_options.comparator), + internal_filter_policy_(raw_options.filter_policy), + options_(SanitizeOptions(dbname, &internal_comparator_, + &internal_filter_policy_, raw_options)), + owns_info_log_(options_.info_log != raw_options.info_log), + owns_cache_(options_.block_cache != raw_options.block_cache), dbname_(dbname), db_lock_(NULL), shutting_down_(NULL), @@ -130,6 +130,7 @@ DBImpl::DBImpl(const Options& options, const std::string& dbname) logfile_(NULL), logfile_number_(0), log_(NULL), + seed_(0), tmp_batch_(new WriteBatch), bg_compaction_scheduled_(false), manual_compaction_(NULL), @@ -138,7 +139,7 @@ DBImpl::DBImpl(const Options& options, const std::string& dbname) has_imm_.Release_Store(NULL); // Reserve ten files or so for other uses and give the rest to TableCache. - const int table_cache_size = options.max_open_files - kNumNonTableCacheFiles; + const int table_cache_size = options_.max_open_files - kNumNonTableCacheFiles; table_cache_ = new TableCache(dbname_, &options_, table_cache_size); versions_ = new VersionSet(dbname_, &options_, table_cache_, @@ -1027,7 +1028,8 @@ static void CleanupIteratorState(void* arg1, void* arg2) { } // namespace Iterator* DBImpl::NewInternalIterator(const ReadOptions& options, - SequenceNumber* latest_snapshot) { + SequenceNumber* latest_snapshot, + uint32_t* seed) { IterState* cleanup = new IterState; mutex_.Lock(); *latest_snapshot = versions_->LastSequence(); @@ -1051,13 +1053,15 @@ Iterator* DBImpl::NewInternalIterator(const ReadOptions& options, cleanup->version = versions_->current(); internal_iter->RegisterCleanup(CleanupIteratorState, cleanup, NULL); + *seed = ++seed_; mutex_.Unlock(); return internal_iter; } Iterator* DBImpl::TEST_NewInternalIterator() { SequenceNumber ignored; - return NewInternalIterator(ReadOptions(), &ignored); + uint32_t ignored_seed; + return NewInternalIterator(ReadOptions(), &ignored, &ignored_seed); } int64_t DBImpl::TEST_MaxNextLevelOverlappingBytes() { @@ -1114,12 +1118,21 @@ Status DBImpl::Get(const ReadOptions& options, Iterator* DBImpl::NewIterator(const ReadOptions& options) { SequenceNumber latest_snapshot; - Iterator* internal_iter = NewInternalIterator(options, &latest_snapshot); + uint32_t seed; + Iterator* iter = NewInternalIterator(options, &latest_snapshot, &seed); return NewDBIterator( - &dbname_, env_, user_comparator(), internal_iter, + this, user_comparator(), iter, (options.snapshot != NULL ? reinterpret_cast<const SnapshotImpl*>(options.snapshot)->number_ - : latest_snapshot)); + : latest_snapshot), + seed); +} + +void DBImpl::RecordReadSample(Slice key) { + MutexLock l(&mutex_); + if (versions_->current()->RecordReadSample(key)) { + MaybeScheduleCompaction(); + } } const Snapshot* DBImpl::GetSnapshot() { diff --git a/src/leveldb/db/db_impl.h b/src/leveldb/db/db_impl.h index 3c8d711ae0..75fd30abe9 100644 --- a/src/leveldb/db/db_impl.h +++ b/src/leveldb/db/db_impl.h @@ -59,13 +59,19 @@ class DBImpl : public DB { // file at a level >= 1. int64_t TEST_MaxNextLevelOverlappingBytes(); + // Record a sample of bytes read at the specified internal key. + // Samples are taken approximately once every config::kReadBytesPeriod + // bytes. + void RecordReadSample(Slice key); + private: friend class DB; struct CompactionState; struct Writer; Iterator* NewInternalIterator(const ReadOptions&, - SequenceNumber* latest_snapshot); + SequenceNumber* latest_snapshot, + uint32_t* seed); Status NewDB(); @@ -135,6 +141,7 @@ class DBImpl : public DB { WritableFile* logfile_; uint64_t logfile_number_; log::Writer* log_; + uint32_t seed_; // For sampling. // Queue of writers. std::deque<Writer*> writers_; diff --git a/src/leveldb/db/db_iter.cc b/src/leveldb/db/db_iter.cc index 87dca2ded4..071a54e3f4 100644 --- a/src/leveldb/db/db_iter.cc +++ b/src/leveldb/db/db_iter.cc @@ -5,12 +5,14 @@ #include "db/db_iter.h" #include "db/filename.h" +#include "db/db_impl.h" #include "db/dbformat.h" #include "leveldb/env.h" #include "leveldb/iterator.h" #include "port/port.h" #include "util/logging.h" #include "util/mutexlock.h" +#include "util/random.h" namespace leveldb { @@ -46,15 +48,16 @@ class DBIter: public Iterator { kReverse }; - DBIter(const std::string* dbname, Env* env, - const Comparator* cmp, Iterator* iter, SequenceNumber s) - : dbname_(dbname), - env_(env), + DBIter(DBImpl* db, const Comparator* cmp, Iterator* iter, SequenceNumber s, + uint32_t seed) + : db_(db), user_comparator_(cmp), iter_(iter), sequence_(s), direction_(kForward), - valid_(false) { + valid_(false), + rnd_(seed), + bytes_counter_(RandomPeriod()) { } virtual ~DBIter() { delete iter_; @@ -100,8 +103,12 @@ class DBIter: public Iterator { } } - const std::string* const dbname_; - Env* const env_; + // Pick next gap with average value of config::kReadBytesPeriod. + ssize_t RandomPeriod() { + return rnd_.Uniform(2*config::kReadBytesPeriod); + } + + DBImpl* db_; const Comparator* const user_comparator_; Iterator* const iter_; SequenceNumber const sequence_; @@ -112,13 +119,23 @@ class DBIter: public Iterator { Direction direction_; bool valid_; + Random rnd_; + ssize_t bytes_counter_; + // No copying allowed DBIter(const DBIter&); void operator=(const DBIter&); }; inline bool DBIter::ParseKey(ParsedInternalKey* ikey) { - if (!ParseInternalKey(iter_->key(), ikey)) { + Slice k = iter_->key(); + ssize_t n = k.size() + iter_->value().size(); + bytes_counter_ -= n; + while (bytes_counter_ < 0) { + bytes_counter_ += RandomPeriod(); + db_->RecordReadSample(k); + } + if (!ParseInternalKey(k, ikey)) { status_ = Status::Corruption("corrupted internal key in DBIter"); return false; } else { @@ -288,12 +305,12 @@ void DBIter::SeekToLast() { } // anonymous namespace Iterator* NewDBIterator( - const std::string* dbname, - Env* env, + DBImpl* db, const Comparator* user_key_comparator, Iterator* internal_iter, - const SequenceNumber& sequence) { - return new DBIter(dbname, env, user_key_comparator, internal_iter, sequence); + SequenceNumber sequence, + uint32_t seed) { + return new DBIter(db, user_key_comparator, internal_iter, sequence, seed); } } // namespace leveldb diff --git a/src/leveldb/db/db_iter.h b/src/leveldb/db/db_iter.h index d9e1b174ab..04927e937b 100644 --- a/src/leveldb/db/db_iter.h +++ b/src/leveldb/db/db_iter.h @@ -11,15 +11,17 @@ namespace leveldb { +class DBImpl; + // Return a new iterator that converts internal keys (yielded by // "*internal_iter") that were live at the specified "sequence" number // into appropriate user keys. extern Iterator* NewDBIterator( - const std::string* dbname, - Env* env, + DBImpl* db, const Comparator* user_key_comparator, Iterator* internal_iter, - const SequenceNumber& sequence); + SequenceNumber sequence, + uint32_t seed); } // namespace leveldb diff --git a/src/leveldb/db/dbformat.h b/src/leveldb/db/dbformat.h index f7f64dafb6..5d8a032bd3 100644 --- a/src/leveldb/db/dbformat.h +++ b/src/leveldb/db/dbformat.h @@ -38,6 +38,9 @@ static const int kL0_StopWritesTrigger = 12; // space if the same key space is being repeatedly overwritten. static const int kMaxMemCompactLevel = 2; +// Approximate gap in bytes between samples of data read during iteration. +static const int kReadBytesPeriod = 1048576; + } // namespace config class InternalKey; diff --git a/src/leveldb/db/version_set.cc b/src/leveldb/db/version_set.cc index 4fd1ddef21..66d73be71f 100644 --- a/src/leveldb/db/version_set.cc +++ b/src/leveldb/db/version_set.cc @@ -289,6 +289,51 @@ static bool NewestFirst(FileMetaData* a, FileMetaData* b) { return a->number > b->number; } +void Version::ForEachOverlapping(Slice user_key, Slice internal_key, + void* arg, + bool (*func)(void*, int, FileMetaData*)) { + // TODO(sanjay): Change Version::Get() to use this function. + const Comparator* ucmp = vset_->icmp_.user_comparator(); + + // Search level-0 in order from newest to oldest. + std::vector<FileMetaData*> tmp; + tmp.reserve(files_[0].size()); + for (uint32_t i = 0; i < files_[0].size(); i++) { + FileMetaData* f = files_[0][i]; + if (ucmp->Compare(user_key, f->smallest.user_key()) >= 0 && + ucmp->Compare(user_key, f->largest.user_key()) <= 0) { + tmp.push_back(f); + } + } + if (!tmp.empty()) { + std::sort(tmp.begin(), tmp.end(), NewestFirst); + for (uint32_t i = 0; i < tmp.size(); i++) { + if (!(*func)(arg, 0, tmp[i])) { + return; + } + } + } + + // Search other levels. + for (int level = 1; level < config::kNumLevels; level++) { + size_t num_files = files_[level].size(); + if (num_files == 0) continue; + + // Binary search to find earliest index whose largest key >= internal_key. + uint32_t index = FindFile(vset_->icmp_, files_[level], internal_key); + if (index < num_files) { + FileMetaData* f = files_[level][index]; + if (ucmp->Compare(user_key, f->smallest.user_key()) < 0) { + // All of "f" is past any data for user_key + } else { + if (!(*func)(arg, level, f)) { + return; + } + } + } + } +} + Status Version::Get(const ReadOptions& options, const LookupKey& k, std::string* value, @@ -401,6 +446,44 @@ bool Version::UpdateStats(const GetStats& stats) { return false; } +bool Version::RecordReadSample(Slice internal_key) { + ParsedInternalKey ikey; + if (!ParseInternalKey(internal_key, &ikey)) { + return false; + } + + struct State { + GetStats stats; // Holds first matching file + int matches; + + static bool Match(void* arg, int level, FileMetaData* f) { + State* state = reinterpret_cast<State*>(arg); + state->matches++; + if (state->matches == 1) { + // Remember first match. + state->stats.seek_file = f; + state->stats.seek_file_level = level; + } + // We can stop iterating once we have a second match. + return state->matches < 2; + } + }; + + State state; + state.matches = 0; + ForEachOverlapping(ikey.user_key, internal_key, &state, &State::Match); + + // Must have at least two matches since we want to merge across + // files. But what if we have a single file that contains many + // overwrites and deletions? Should we have another mechanism for + // finding such files? + if (state.matches >= 2) { + // 1MB cost is about 1 seek (see comment in Builder::Apply). + return UpdateStats(state.stats); + } + return false; +} + void Version::Ref() { ++refs_; } @@ -435,10 +518,13 @@ int Version::PickLevelForMemTableOutput( if (OverlapInLevel(level + 1, &smallest_user_key, &largest_user_key)) { break; } - GetOverlappingInputs(level + 2, &start, &limit, &overlaps); - const int64_t sum = TotalFileSize(overlaps); - if (sum > kMaxGrandParentOverlapBytes) { - break; + if (level + 2 < config::kNumLevels) { + // Check that file does not overlap too many grandparent bytes. + GetOverlappingInputs(level + 2, &start, &limit, &overlaps); + const int64_t sum = TotalFileSize(overlaps); + if (sum > kMaxGrandParentOverlapBytes) { + break; + } } level++; } @@ -452,6 +538,8 @@ void Version::GetOverlappingInputs( const InternalKey* begin, const InternalKey* end, std::vector<FileMetaData*>* inputs) { + assert(level >= 0); + assert(level < config::kNumLevels); inputs->clear(); Slice user_begin, user_end; if (begin != NULL) { diff --git a/src/leveldb/db/version_set.h b/src/leveldb/db/version_set.h index 9d084fdb7d..20de0e2629 100644 --- a/src/leveldb/db/version_set.h +++ b/src/leveldb/db/version_set.h @@ -78,6 +78,12 @@ class Version { // REQUIRES: lock is held bool UpdateStats(const GetStats& stats); + // Record a sample of bytes read at the specified internal key. + // Samples are taken approximately once every config::kReadBytesPeriod + // bytes. Returns true if a new compaction may need to be triggered. + // REQUIRES: lock is held + bool RecordReadSample(Slice key); + // Reference count management (so Versions do not disappear out from // under live iterators) void Ref(); @@ -114,6 +120,15 @@ class Version { class LevelFileNumIterator; Iterator* NewConcatenatingIterator(const ReadOptions&, int level) const; + // Call func(arg, level, f) for every file that overlaps user_key in + // order from newest to oldest. If an invocation of func returns + // false, makes no more calls. + // + // REQUIRES: user portion of internal_key == user_key. + void ForEachOverlapping(Slice user_key, Slice internal_key, + void* arg, + bool (*func)(void*, int, FileMetaData*)); + VersionSet* vset_; // VersionSet to which this Version belongs Version* next_; // Next version in linked list Version* prev_; // Previous version in linked list diff --git a/src/leveldb/include/leveldb/db.h b/src/leveldb/include/leveldb/db.h index da8b11a8c0..57c00a5da0 100644 --- a/src/leveldb/include/leveldb/db.h +++ b/src/leveldb/include/leveldb/db.h @@ -14,7 +14,7 @@ namespace leveldb { // Update Makefile if you change these static const int kMajorVersion = 1; -static const int kMinorVersion = 12; +static const int kMinorVersion = 13; struct Options; struct ReadOptions; diff --git a/src/leveldb/util/env_posix.cc b/src/leveldb/util/env_posix.cc index 6badfdc230..0f5dcfac5a 100644 --- a/src/leveldb/util/env_posix.cc +++ b/src/leveldb/util/env_posix.cc @@ -320,8 +320,39 @@ class PosixMmapFile : public WritableFile { return Status::OK(); } - virtual Status Sync() { + Status SyncDirIfManifest() { + const char* f = filename_.c_str(); + const char* sep = strrchr(f, '/'); + Slice basename; + std::string dir; + if (sep == NULL) { + dir = "."; + basename = f; + } else { + dir = std::string(f, sep - f); + basename = sep + 1; + } Status s; + if (basename.starts_with("MANIFEST")) { + int fd = open(dir.c_str(), O_RDONLY); + if (fd < 0) { + s = IOError(dir, errno); + } else { + if (fsync(fd) < 0) { + s = IOError(dir, errno); + } + close(fd); + } + } + return s; + } + + virtual Status Sync() { + // Ensure new files referred to by the manifest are in the filesystem. + Status s = SyncDirIfManifest(); + if (!s.ok()) { + return s; + } if (pending_sync_) { // Some unmapped data was not synced diff --git a/src/leveldb/util/random.h b/src/leveldb/util/random.h index 07538242ea..ddd51b1c7b 100644 --- a/src/leveldb/util/random.h +++ b/src/leveldb/util/random.h @@ -16,7 +16,12 @@ class Random { private: uint32_t seed_; public: - explicit Random(uint32_t s) : seed_(s & 0x7fffffffu) { } + explicit Random(uint32_t s) : seed_(s & 0x7fffffffu) { + // Avoid bad seeds. + if (seed_ == 0 || seed_ == 2147483647L) { + seed_ = 1; + } + } uint32_t Next() { static const uint32_t M = 2147483647L; // 2^31-1 static const uint64_t A = 16807; // bits 14, 8, 7, 5, 2, 1, 0 diff --git a/src/m4/bitcoin_subdir_to_include.m4 b/src/m4/bitcoin_subdir_to_include.m4 index 9b37a75ef0..66f106c7d4 100644 --- a/src/m4/bitcoin_subdir_to_include.m4 +++ b/src/m4/bitcoin_subdir_to_include.m4 @@ -5,8 +5,10 @@ AC_DEFUN([BITCOIN_SUBDIR_TO_INCLUDE],[ AC_MSG_RESULT([default]) else echo "#include <$2$3.h>" >conftest.cpp - newinclpath=`${CXXCPP} -M conftest.cpp 2>/dev/null | [ tr -d '\\n\\r\\\\' | sed -e 's/^.*[[:space:]:]\(\/[^[:space:]]*\)]$3[\.h[[:space:]].*$/\1/' -e t -e d`] + newinclpath=`${CXXCPP} ${CPPFLAGS} -M conftest.cpp 2>/dev/null | [ tr -d '\\n\\r\\\\' | sed -e 's/^.*[[:space:]:]\(\/[^[:space:]]*\)]$3[\.h[[:space:]].*$/\1/' -e t -e d`] AC_MSG_RESULT([${newinclpath}]) - eval "$1=\"\$$1\"' -I${newinclpath}'" + if test "x${newinclpath}" != "x"; then + eval "$1=\"\$$1\"' -I${newinclpath}'" + fi fi ]) diff --git a/src/main.cpp b/src/main.cpp index dc690111e6..215a7ba620 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -32,13 +32,8 @@ CTxMemPool mempool; unsigned int nTransactionsUpdated = 0; map<uint256, CBlockIndex*> mapBlockIndex; -std::vector<CBlockIndex*> vBlockIndexByHeight; -CBlockIndex* pindexGenesisBlock = NULL; -int nBestHeight = -1; -uint256 nBestChainWork = 0; +CChain chainActive; uint256 nBestInvalidWork = 0; -uint256 hashBestChain = 0; -CBlockIndex* pindexBest = NULL; set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexValid; // may contain all CBlockIndex*'s that have validness >=BLOCK_VALID_TRANSACTIONS, and must contain those who aren't failed int64 nTimeBestReceived = 0; int nScriptCheckThreads = 0; @@ -173,106 +168,83 @@ void static ResendWalletTransactions() // Registration of network node signals. // +int static GetHeight() +{ + LOCK(cs_main); + return chainActive.Height(); +} + void RegisterNodeSignals(CNodeSignals& nodeSignals) { + nodeSignals.GetHeight.connect(&GetHeight); nodeSignals.ProcessMessages.connect(&ProcessMessages); nodeSignals.SendMessages.connect(&SendMessages); } void UnregisterNodeSignals(CNodeSignals& nodeSignals) { + nodeSignals.GetHeight.disconnect(&GetHeight); nodeSignals.ProcessMessages.disconnect(&ProcessMessages); nodeSignals.SendMessages.disconnect(&SendMessages); } ////////////////////////////////////////////////////////////////////////////// // -// CBlockLocator implementation +// CChain implementation // -CBlockLocator::CBlockLocator(uint256 hashBlock) -{ - std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock); - if (mi != mapBlockIndex.end()) - Set((*mi).second); +CBlockIndex *CChain::SetTip(CBlockIndex *pindex) { + if (pindex == NULL) { + vChain.clear(); + return NULL; + } + vChain.resize(pindex->nHeight + 1); + while (pindex && vChain[pindex->nHeight] != pindex) { + vChain[pindex->nHeight] = pindex; + pindex = pindex->pprev; + } + return pindex; } -void CBlockLocator::Set(const CBlockIndex* pindex) -{ - vHave.clear(); +CBlockLocator CChain::GetLocator(const CBlockIndex *pindex) const { int nStep = 1; - while (pindex) - { - vHave.push_back(pindex->GetBlockHash()); + std::vector<uint256> vHave; + vHave.reserve(32); - // Exponentially larger steps back - for (int i = 0; pindex && i < nStep; i++) + if (!pindex) + pindex = Tip(); + while (pindex) { + vHave.push_back(pindex->GetBlockHash()); + // Stop when we have added the genesis block. + if (pindex->nHeight == 0) + break; + // Exponentially larger steps back, plus the genesis block. + int nHeight = std::max(pindex->nHeight - nStep, 0); + // In case pindex is not in this chain, iterate pindex->pprev to find blocks. + while (pindex->nHeight > nHeight && !Contains(pindex)) pindex = pindex->pprev; + // If pindex is in this chain, use direct height-based access. + if (pindex->nHeight > nHeight) + pindex = (*this)[nHeight]; if (vHave.size() > 10) nStep *= 2; } - vHave.push_back(Params().HashGenesisBlock()); -} -int CBlockLocator::GetDistanceBack() -{ - // Retrace how far back it was in the sender's branch - int nDistance = 0; - int nStep = 1; - BOOST_FOREACH(const uint256& hash, vHave) - { - std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash); - if (mi != mapBlockIndex.end()) - { - CBlockIndex* pindex = (*mi).second; - if (pindex->IsInMainChain()) - return nDistance; - } - nDistance += nStep; - if (nDistance > 10) - nStep *= 2; - } - return nDistance; + return CBlockLocator(vHave); } -CBlockIndex *CBlockLocator::GetBlockIndex() -{ +CBlockIndex *CChain::FindFork(const CBlockLocator &locator) const { // Find the first block the caller has in the main chain - BOOST_FOREACH(const uint256& hash, vHave) - { + BOOST_FOREACH(const uint256& hash, locator.vHave) { std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash); if (mi != mapBlockIndex.end()) { CBlockIndex* pindex = (*mi).second; - if (pindex->IsInMainChain()) + if (Contains(pindex)) return pindex; } } - return pindexGenesisBlock; -} - -uint256 CBlockLocator::GetBlockHash() -{ - // Find the first block the caller has in the main chain - BOOST_FOREACH(const uint256& hash, vHave) - { - std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash); - if (mi != mapBlockIndex.end()) - { - CBlockIndex* pindex = (*mi).second; - if (pindex->IsInMainChain()) - return hash; - } - } - return Params().HashGenesisBlock(); -} - -int CBlockLocator::GetHeight() -{ - CBlockIndex* pindex = GetBlockIndex(); - if (!pindex) - return 0; - return pindex->nHeight; + return Genesis(); } ////////////////////////////////////////////////////////////////////////////// @@ -517,7 +489,7 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64 nBlockTime) if (tx.nLockTime == 0) return true; if (nBlockHeight == 0) - nBlockHeight = nBestHeight; + nBlockHeight = chainActive.Height(); if (nBlockTime == 0) nBlockTime = GetAdjustedTime(); if ((int64)tx.nLockTime < ((int64)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64)nBlockHeight : nBlockTime)) @@ -644,7 +616,7 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) if (pblock == NULL) { CCoins coins; if (pcoinsTip->GetCoins(GetHash(), coins)) { - CBlockIndex *pindex = FindBlockByHeight(coins.nHeight); + CBlockIndex *pindex = chainActive[coins.nHeight]; if (pindex) { if (!ReadBlockFromDisk(blockTmp, pindex)) return 0; @@ -678,10 +650,10 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) if (mi == mapBlockIndex.end()) return 0; CBlockIndex* pindex = (*mi).second; - if (!pindex || !pindex->IsInMainChain()) + if (!pindex || !chainActive.Contains(pindex)) return 0; - return pindexBest->nHeight - pindex->nHeight + 1; + return chainActive.Height() - pindex->nHeight + 1; } @@ -1078,7 +1050,7 @@ int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const if (mi == mapBlockIndex.end()) return 0; CBlockIndex* pindex = (*mi).second; - if (!pindex || !pindex->IsInMainChain()) + if (!pindex || !chainActive.Contains(pindex)) return 0; // Make sure the merkle branch connects to this block @@ -1090,7 +1062,7 @@ int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const } pindexRet = pindex; - return pindexBest->nHeight - pindex->nHeight + 1; + return chainActive.Height() - pindex->nHeight + 1; } @@ -1098,7 +1070,7 @@ int CMerkleTx::GetBlocksToMaturity() const { if (!IsCoinBase()) return 0; - return max(0, (COINBASE_MATURITY+20) - GetDepthInMainChain()); + return max(0, (COINBASE_MATURITY+1) - GetDepthInMainChain()); } @@ -1173,7 +1145,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock nHeight = coins.nHeight; } if (nHeight > 0) - pindexSlow = FindBlockByHeight(nHeight); + pindexSlow = chainActive[nHeight]; } } @@ -1203,14 +1175,6 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock // CBlock and CBlockIndex // -static CBlockIndex* pblockindexFBBHLast; -CBlockIndex* FindBlockByHeight(int nHeight) -{ - if (nHeight >= (int)vBlockIndexByHeight.size()) - return NULL; - return vBlockIndexByHeight[nHeight]; -} - bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos) { // Open history file to append @@ -1404,17 +1368,17 @@ int GetNumBlocksOfPeers() bool IsInitialBlockDownload() { - if (pindexBest == NULL || fImporting || fReindex || nBestHeight < Checkpoints::GetTotalBlocksEstimate()) + if (fImporting || fReindex || chainActive.Height() < Checkpoints::GetTotalBlocksEstimate()) return true; static int64 nLastUpdate; static CBlockIndex* pindexLastBest; - if (pindexBest != pindexLastBest) + if (chainActive.Tip() != pindexLastBest) { - pindexLastBest = pindexBest; + pindexLastBest = chainActive.Tip(); nLastUpdate = GetTime(); } return (GetTime() - nLastUpdate < 10 && - pindexBest->GetBlockTime() < GetTime() - 24 * 60 * 60); + chainActive.Tip()->GetBlockTime() < GetTime() - 24 * 60 * 60); } bool fLargeWorkForkFound = false; @@ -1430,10 +1394,10 @@ void CheckForkWarningConditions() // If our best fork is no longer within 72 blocks (+/- 12 hours if no one mines it) // of our head, drop it - if (pindexBestForkTip && nBestHeight - pindexBestForkTip->nHeight >= 72) + if (pindexBestForkTip && chainActive.Height() - pindexBestForkTip->nHeight >= 72) pindexBestForkTip = NULL; - if (pindexBestForkTip || nBestInvalidWork > nBestChainWork + (pindexBest->GetBlockWork() * 6).getuint256()) + if (pindexBestForkTip || nBestInvalidWork > chainActive.Tip()->nChainWork + (chainActive.Tip()->GetBlockWork() * 6).getuint256()) { if (!fLargeWorkForkFound) { @@ -1470,7 +1434,7 @@ void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) { // If we are on a fork that is sufficiently large, set a warning flag CBlockIndex* pfork = pindexNewForkTip; - CBlockIndex* plonger = pindexBest; + CBlockIndex* plonger = chainActive.Tip(); while (pfork && pfork != plonger) { while (plonger && plonger->nHeight > pfork->nHeight) @@ -1489,7 +1453,7 @@ void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) // the 7-block condition and from this always have the most-likely-to-cause-warning fork if (pfork && (!pindexBestForkTip || (pindexBestForkTip && pindexNewForkTip->nHeight > pindexBestForkTip->nHeight)) && pindexNewForkTip->nChainWork - pfork->nChainWork > (pfork->GetBlockWork() * 7).getuint256() && - nBestHeight - pindexNewForkTip->nHeight < 72) + chainActive.Height() - pindexNewForkTip->nHeight < 72) { pindexBestForkTip = pindexNewForkTip; pindexBestForkBase = pfork; @@ -1511,8 +1475,8 @@ void static InvalidChainFound(CBlockIndex* pindexNew) log(pindexNew->nChainWork.getdouble())/log(2.0), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexNew->GetBlockTime()).c_str()); LogPrintf("InvalidChainFound: current best=%s height=%d log2_work=%.8g date=%s\n", - hashBestChain.ToString().c_str(), nBestHeight, log(nBestChainWork.getdouble())/log(2.0), - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexBest->GetBlockTime()).c_str()); + chainActive.Tip()->GetBlockHash().ToString().c_str(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()).c_str()); CheckForkWarningConditions(); } @@ -1521,7 +1485,7 @@ void static InvalidBlockFound(CBlockIndex *pindex) { pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex)); setBlockIndexValid.erase(pindex); InvalidChainFound(pindex); - if (pindex->GetNextInMainChain()) { + if (chainActive.Next(pindex)) { CValidationState stateDummy; ConnectBestBlock(stateDummy); // reorganise away from the failed block } @@ -1538,7 +1502,7 @@ bool ConnectBestBlock(CValidationState &state) { pindexNewBest = *it; } - if (pindexNewBest == pindexBest || (pindexBest && pindexNewBest->nChainWork == pindexBest->nChainWork)) + if (pindexNewBest == chainActive.Tip() || (chainActive.Tip() && pindexNewBest->nChainWork == chainActive.Tip()->nChainWork)) return true; // nothing to do // check ancestry @@ -1558,10 +1522,10 @@ bool ConnectBestBlock(CValidationState &state) { break; } - if (pindexBest == NULL || pindexTest->nChainWork > pindexBest->nChainWork) + if (chainActive.Tip() == NULL || pindexTest->nChainWork > chainActive.Tip()->nChainWork) vAttach.push_back(pindexTest); - if (pindexTest->pprev == NULL || pindexTest->GetNextInMainChain()) { + if (pindexTest->pprev == NULL || chainActive.Next(pindexTest)) { reverse(vAttach.begin(), vAttach.end()); BOOST_FOREACH(CBlockIndex *pindexSwitch, vAttach) { boost::this_thread::interruption_point(); @@ -1881,7 +1845,6 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C // (its coinbase is unspendable) if (block.GetHash() == Params().HashGenesisBlock()) { view.SetBestBlock(pindex); - pindexGenesisBlock = pindex; return true; } @@ -2129,9 +2092,7 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) // Proceed by updating the memory structures. // Register new best chain - vBlockIndexByHeight.resize(pindexNew->nHeight + 1); - BOOST_FOREACH(CBlockIndex* pindex, vConnect) - vBlockIndexByHeight[pindex->nHeight] = pindex; + chainActive.SetTip(pindexNew); // Resurrect memory transactions that were in the disconnected branch BOOST_FOREACH(CTransaction& tx, vResurrect) { @@ -2151,29 +2112,21 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) // Update best block in wallet (so we can detect restored wallets) if ((pindexNew->nHeight % 20160) == 0 || (!fIsInitialDownload && (pindexNew->nHeight % 144) == 0)) - { - const CBlockLocator locator(pindexNew); - ::SetBestChain(locator); - } + ::SetBestChain(chainActive.GetLocator(pindexNew)); // New best block - hashBestChain = pindexNew->GetBlockHash(); - pindexBest = pindexNew; - pblockindexFBBHLast = NULL; - nBestHeight = pindexBest->nHeight; - nBestChainWork = pindexNew->nChainWork; nTimeBestReceived = GetTime(); nTransactionsUpdated++; LogPrintf("SetBestChain: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f\n", - hashBestChain.ToString().c_str(), nBestHeight, log(nBestChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx, - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexBest->GetBlockTime()).c_str(), - Checkpoints::GuessVerificationProgress(pindexBest)); + chainActive.Tip()->GetBlockHash().ToString().c_str(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx, + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()).c_str(), + Checkpoints::GuessVerificationProgress(chainActive.Tip())); // Check the version of the last 100 blocks to see if we need to upgrade: if (!fIsInitialDownload) { int nUpgraded = 0; - const CBlockIndex* pindex = pindexBest; + const CBlockIndex* pindex = chainActive.Tip(); for (int i = 0; i < 100 && pindex != NULL; i++) { if (pindex->nVersion > CBlock::CURRENT_VERSION) @@ -2191,7 +2144,7 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) if (!fIsInitialDownload && !strCmd.empty()) { - boost::replace_all(strCmd, "%s", hashBestChain.GetHex()); + boost::replace_all(strCmd, "%s", chainActive.Tip()->GetBlockHash().GetHex()); boost::thread t(runCommand, strCmd); // thread runs free } @@ -2233,7 +2186,7 @@ bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos if (!ConnectBestBlock(state)) return false; - if (pindexNew == pindexBest) + if (pindexNew == chainActive.Tip()) { // Clear fork warning if its no longer applicable CheckForkWarningConditions(); @@ -2482,11 +2435,11 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp) // Relay inventory, but don't relay old inventory during initial block download int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(); - if (hashBestChain == hash) + if (chainActive.Tip()->GetBlockHash() == hash) { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) - if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) + if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) pnode->PushInventory(CInv(MSG_BLOCK, hash)); } @@ -2505,6 +2458,18 @@ bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, uns return (nFound >= nRequired); } +int64 CBlockIndex::GetMedianTime() const +{ + const CBlockIndex* pindex = this; + for (int i = 0; i < nMedianTimeSpan/2; i++) + { + if (!chainActive.Next(pindex)) + return GetBlockTime(); + pindex = chainActive.Next(pindex); + } + return pindex->GetMedianTimePast(); +} + void PushGetBlocks(CNode* pnode, CBlockIndex* pindexBegin, uint256 hashEnd) { // Filter out duplicate requests @@ -2513,7 +2478,7 @@ void PushGetBlocks(CNode* pnode, CBlockIndex* pindexBegin, uint256 hashEnd) pnode->pindexLastGetBlocksBegin = pindexBegin; pnode->hashLastGetBlocksEnd = hashEnd; - pnode->PushMessage("getblocks", CBlockLocator(pindexBegin), hashEnd); + pnode->PushMessage("getblocks", chainActive.GetLocator(pindexBegin), hashEnd); } bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) @@ -2530,7 +2495,7 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl return error("ProcessBlock() : CheckBlock FAILED"); CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); - if (pcheckpoint && pblock->hashPrevBlock != hashBestChain) + if (pcheckpoint && pblock->hashPrevBlock != (chainActive.Tip() ? chainActive.Tip()->GetBlockHash() : uint256(0))) { // Extra checks to prevent "fill up memory by spamming with bogus blocks" int64 deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime; @@ -2561,7 +2526,7 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl mapOrphanBlocksByPrev.insert(make_pair(pblock2->hashPrevBlock, pblock2)); // Ask this guy to fill in what we're missing - PushGetBlocks(pfrom, pindexBest, GetOrphanRoot(pblock2)); + PushGetBlocks(pfrom, chainActive.Tip(), GetOrphanRoot(pblock2)); } return true; } @@ -2875,48 +2840,39 @@ bool static LoadBlockIndexDB() LogPrintf("LoadBlockIndexDB(): transaction index %s\n", fTxIndex ? "enabled" : "disabled"); // Load hashBestChain pointer to end of best chain - pindexBest = pcoinsTip->GetBestBlock(); - if (pindexBest == NULL) + chainActive.SetTip(pcoinsTip->GetBestBlock()); + if (chainActive.Tip() == NULL) return true; - hashBestChain = pindexBest->GetBlockHash(); - nBestHeight = pindexBest->nHeight; - nBestChainWork = pindexBest->nChainWork; // register best chain - CBlockIndex *pindex = pindexBest; - vBlockIndexByHeight.resize(pindexBest->nHeight + 1); - while(pindex != NULL) { - vBlockIndexByHeight[pindex->nHeight] = pindex; - pindex = pindex->pprev; - } LogPrintf("LoadBlockIndexDB(): hashBestChain=%s height=%d date=%s\n", - hashBestChain.ToString().c_str(), nBestHeight, - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexBest->GetBlockTime()).c_str()); + chainActive.Tip()->GetBlockHash().ToString().c_str(), chainActive.Height(), + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()).c_str()); return true; } bool VerifyDB(int nCheckLevel, int nCheckDepth) { - if (pindexBest == NULL || pindexBest->pprev == NULL) + if (chainActive.Tip() == NULL || chainActive.Tip()->pprev == NULL) return true; // Verify blocks in the best chain if (nCheckDepth <= 0) nCheckDepth = 1000000000; // suffices until the year 19000 - if (nCheckDepth > nBestHeight) - nCheckDepth = nBestHeight; + if (nCheckDepth > chainActive.Height()) + nCheckDepth = chainActive.Height(); nCheckLevel = std::max(0, std::min(4, nCheckLevel)); LogPrintf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); CCoinsViewCache coins(*pcoinsTip, true); - CBlockIndex* pindexState = pindexBest; + CBlockIndex* pindexState = chainActive.Tip(); CBlockIndex* pindexFailure = NULL; int nGoodTransactions = 0; CValidationState state; - for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev) + for (CBlockIndex* pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) { boost::this_thread::interruption_point(); - if (pindex->nHeight < nBestHeight-nCheckDepth) + if (pindex->nHeight < chainActive.Height()-nCheckDepth) break; CBlock block; // check level 0: read from disk @@ -2948,14 +2904,14 @@ bool VerifyDB(int nCheckLevel, int nCheckDepth) } } if (pindexFailure) - return error("VerifyDB() : *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", pindexBest->nHeight - pindexFailure->nHeight + 1, nGoodTransactions); + return error("VerifyDB() : *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", chainActive.Height() - pindexFailure->nHeight + 1, nGoodTransactions); // check level 4: try reconnecting blocks if (nCheckLevel >= 4) { CBlockIndex *pindex = pindexState; - while (pindex != pindexBest) { + while (pindex != chainActive.Tip()) { boost::this_thread::interruption_point(); - pindex = pindex->GetNextInMainChain(); + pindex = chainActive.Next(pindex); CBlock block; if (!ReadBlockFromDisk(block, pindex)) return error("VerifyDB() : *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); @@ -2964,7 +2920,7 @@ bool VerifyDB(int nCheckLevel, int nCheckDepth) } } - LogPrintf("No coin database inconsistencies in last %i blocks (%i transactions)\n", pindexBest->nHeight - pindexState->nHeight, nGoodTransactions); + LogPrintf("No coin database inconsistencies in last %i blocks (%i transactions)\n", chainActive.Height() - pindexState->nHeight, nGoodTransactions); return true; } @@ -2973,12 +2929,8 @@ void UnloadBlockIndex() { mapBlockIndex.clear(); setBlockIndexValid.clear(); - pindexGenesisBlock = NULL; - nBestHeight = 0; - nBestChainWork = 0; + chainActive.SetTip(NULL); nBestInvalidWork = 0; - hashBestChain = 0; - pindexBest = NULL; } bool LoadBlockIndex() @@ -2992,7 +2944,7 @@ bool LoadBlockIndex() bool InitBlockIndex() { // Check whether we're already initialized - if (pindexGenesisBlock != NULL) + if (chainActive.Genesis() != NULL) return true; // Use the provided setting for -txindex in the new database @@ -3038,7 +2990,7 @@ void PrintBlockTree() } vector<pair<int, CBlockIndex*> > vStack; - vStack.push_back(make_pair(0, pindexGenesisBlock)); + vStack.push_back(make_pair(0, chainActive.Genesis())); int nPrevCol = 0; while (!vStack.empty()) @@ -3081,7 +3033,7 @@ void PrintBlockTree() vector<CBlockIndex*>& vNext = mapNext[pindex]; for (unsigned int i = 0; i < vNext.size(); i++) { - if (vNext[i]->GetNextInMainChain()) + if (chainActive.Next(vNext[i])) { swap(vNext[0], vNext[i]); break; @@ -3328,7 +3280,7 @@ void static ProcessGetData(CNode* pfrom) // and we want it right after the last block so they don't // wait for other stuff first. vector<CInv> vInv; - vInv.push_back(CInv(MSG_BLOCK, hashBestChain)); + vInv.push_back(CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash())); pfrom->PushMessage("inv", vInv); pfrom->hashContinue = 0; } @@ -3610,7 +3562,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (!fImporting && !fReindex) pfrom->AskFor(inv); } else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) { - PushGetBlocks(pfrom, pindexBest, GetOrphanRoot(mapOrphanBlocks[inv.hash])); + PushGetBlocks(pfrom, chainActive.Tip(), GetOrphanRoot(mapOrphanBlocks[inv.hash])); } else if (nInv == nLastBlock) { // In case we are on a very long side-chain, it is possible that we already have // the last block in an inv bundle sent in response to getblocks. Try to detect @@ -3654,14 +3606,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vRecv >> locator >> hashStop; // Find the last block the caller has in the main chain - CBlockIndex* pindex = locator.GetBlockIndex(); + CBlockIndex* pindex = chainActive.FindFork(locator); // Send the rest of the chain if (pindex) - pindex = pindex->GetNextInMainChain(); + pindex = chainActive.Next(pindex); int nLimit = 500; LogPrint("net", "getblocks %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().c_str(), nLimit); - for (; pindex; pindex = pindex->GetNextInMainChain()) + for (; pindex; pindex = chainActive.Next(pindex)) { if (pindex->GetBlockHash() == hashStop) { @@ -3699,16 +3651,16 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) else { // Find the last block the caller has in the main chain - pindex = locator.GetBlockIndex(); + pindex = chainActive.FindFork(locator); if (pindex) - pindex = pindex->GetNextInMainChain(); + pindex = chainActive.Next(pindex); } // we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end vector<CBlock> vHeaders; int nLimit = 2000; LogPrint("net", "getheaders %d to %s\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().c_str()); - for (; pindex; pindex = pindex->GetNextInMainChain()) + for (; pindex; pindex = chainActive.Next(pindex)) { vHeaders.push_back(pindex->GetBlockHeader()); if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop) @@ -3785,9 +3737,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (nEvicted > 0) LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted); } - int nDoS; + int nDoS = 0; if (state.IsInvalid(nDoS)) - pfrom->Misbehaving(nDoS); + if (nDoS > 0) + pfrom->Misbehaving(nDoS); } @@ -3805,9 +3758,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CValidationState state; if (ProcessBlock(state, pfrom, &block)) mapAlreadyAskedFor.erase(inv); - int nDoS; + int nDoS = 0; if (state.IsInvalid(nDoS)) - pfrom->Misbehaving(nDoS); + if (nDoS > 0) + pfrom->Misbehaving(nDoS); } @@ -3861,6 +3815,63 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } + else if (strCommand == "pong") + { + int64 pingUsecEnd = GetTimeMicros(); + uint64 nonce = 0; + size_t nAvail = vRecv.in_avail(); + bool bPingFinished = false; + std::string sProblem; + + if (nAvail >= sizeof(nonce)) { + vRecv >> nonce; + + // Only process pong message if there is an outstanding ping (old ping without nonce should never pong) + if (pfrom->nPingNonceSent != 0) { + if (nonce == pfrom->nPingNonceSent) { + // Matching pong received, this ping is no longer outstanding + bPingFinished = true; + int64 pingUsecTime = pingUsecEnd - pfrom->nPingUsecStart; + if (pingUsecTime > 0) { + // Successful ping time measurement, replace previous + pfrom->nPingUsecTime = pingUsecTime; + } else { + // This should never happen + sProblem = "Timing mishap"; + } + } else { + // Nonce mismatches are normal when pings are overlapping + sProblem = "Nonce mismatch"; + if (nonce == 0) { + // This is most likely a bug in another implementation somewhere, cancel this ping + bPingFinished = true; + sProblem = "Nonce zero"; + } + } + } else { + sProblem = "Unsolicited pong without ping"; + } + } else { + // This is most likely a bug in another implementation somewhere, cancel this ping + bPingFinished = true; + sProblem = "Short payload"; + } + + if (!(sProblem.empty())) { + LogPrint("net", "pong %s %s: %s, %"PRI64x" expected, %"PRI64x" received, %"PRIszu" bytes\n", + pfrom->addr.ToString().c_str(), + pfrom->strSubVer.c_str(), + sProblem.c_str(), + pfrom->nPingNonceSent, + nonce, + nAvail); + } + if (bPingFinished) { + pfrom->nPingNonceSent = 0; + } + } + + else if (strCommand == "alert") { CAlert alert; @@ -3959,7 +3970,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) bool ProcessMessages(CNode* pfrom) { //if (fDebug) - // LogPrintf("ProcessMessages(%zu messages)\n", pfrom->vRecvMsg.size()); + // LogPrintf("ProcessMessages(%"PRIszu" messages)\n", pfrom->vRecvMsg.size()); // // Message format @@ -3984,7 +3995,7 @@ bool ProcessMessages(CNode* pfrom) CNetMessage& msg = *it; //if (fDebug) - // LogPrintf("ProcessMessages(message %u msgsz, %zu bytes, complete:%s)\n", + // LogPrintf("ProcessMessages(message %u msgsz, %"PRIszu" bytes, complete:%s)\n", // msg.hdr.nMessageSize, msg.vRecv.size(), // msg.complete() ? "Y" : "N"); @@ -4082,20 +4093,40 @@ bool SendMessages(CNode* pto, bool fSendTrickle) if (pto->nVersion == 0) return true; - // Keep-alive ping. We send a nonce of zero because we don't use it anywhere - // right now. + // + // Message: ping + // + bool pingSend = false; + if (pto->fPingQueued) { + // RPC ping request by user + pingSend = true; + } if (pto->nLastSend && GetTime() - pto->nLastSend > 30 * 60 && pto->vSendMsg.empty()) { + // Ping automatically sent as a keepalive + pingSend = true; + } + if (pingSend) { uint64 nonce = 0; - if (pto->nVersion > BIP0031_VERSION) + while (nonce == 0) { + RAND_bytes((unsigned char*)&nonce, sizeof(nonce)); + } + pto->nPingNonceSent = nonce; + pto->fPingQueued = false; + if (pto->nVersion > BIP0031_VERSION) { + // Take timestamp as close as possible before transmitting ping + pto->nPingUsecStart = GetTimeMicros(); pto->PushMessage("ping", nonce); - else + } else { + // Peer is too old to support ping command with nonce, pong will never arrive, disable timing + pto->nPingUsecStart = 0; pto->PushMessage("ping"); + } } // Start block sync if (pto->fStartSync && !fImporting && !fReindex) { pto->fStartSync = false; - PushGetBlocks(pto, pindexBest, uint256(0)); + PushGetBlocks(pto, chainActive.Tip(), uint256(0)); } // Resend wallet transactions that haven't gotten in a block yet diff --git a/src/main.h b/src/main.h index 83b0d07f63..fc60ccc0b5 100644 --- a/src/main.h +++ b/src/main.h @@ -74,14 +74,8 @@ extern CScript COINBASE_FLAGS; extern CCriticalSection cs_main; extern std::map<uint256, CBlockIndex*> mapBlockIndex; -extern std::vector<CBlockIndex*> vBlockIndexByHeight; extern std::set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexValid; -extern CBlockIndex* pindexGenesisBlock; -extern int nBestHeight; -extern uint256 nBestChainWork; extern uint256 nBestInvalidWork; -extern uint256 hashBestChain; -extern CBlockIndex* pindexBest; extern unsigned int nTransactionsUpdated; extern uint64 nLastBlockTx; extern uint64 nLastBlockSize; @@ -153,8 +147,6 @@ void UnloadBlockIndex(); bool VerifyDB(int nCheckLevel, int nCheckDepth); /** Print the loaded block tree */ void PrintBlockTree(); -/** Find a block by height in the currently-connected chain */ -CBlockIndex* FindBlockByHeight(int nHeight); /** Process protocol messages received from a given node */ bool ProcessMessages(CNode* pfrom); /** Send queued protocol messages to be sent to a give node */ @@ -819,15 +811,6 @@ public: return (CBigNum(1)<<256) / (bnTarget+1); } - bool IsInMainChain() const - { - return nHeight < (int)vBlockIndexByHeight.size() && vBlockIndexByHeight[nHeight] == this; - } - - CBlockIndex *GetNextInMainChain() const { - return nHeight+1 >= (int)vBlockIndexByHeight.size() ? NULL : vBlockIndexByHeight[nHeight+1]; - } - bool CheckIndex() const { return CheckProofOfWork(GetBlockHash(), nBits); @@ -849,17 +832,7 @@ public: return pbegin[(pend - pbegin)/2]; } - int64 GetMedianTime() const - { - const CBlockIndex* pindex = this; - for (int i = 0; i < nMedianTimeSpan/2; i++) - { - if (!pindex->GetNextInMainChain()) - return GetBlockTime(); - pindex = pindex->GetNextInMainChain(); - } - return pindex->GetMedianTimePast(); - } + int64 GetMedianTime() const; /** * Returns true if there are nRequired or more blocks of minVersion or above @@ -870,8 +843,8 @@ public: std::string ToString() const { - return strprintf("CBlockIndex(pprev=%p, pnext=%p, nHeight=%d, merkle=%s, hashBlock=%s)", - pprev, GetNextInMainChain(), nHeight, + return strprintf("CBlockIndex(pprev=%p, nHeight=%d, merkle=%s, hashBlock=%s)", + pprev, nHeight, hashMerkleRoot.ToString().c_str(), GetBlockHash().ToString().c_str()); } @@ -1011,64 +984,67 @@ public: } }; +/** An in-memory indexed chain of blocks. */ +class CChain { +private: + std::vector<CBlockIndex*> vChain; - - - - - -/** Describes a place in the block chain to another node such that if the - * other node doesn't have the same branch, it can find a recent common trunk. - * The further back it is, the further before the fork it may be. - */ -class CBlockLocator -{ -protected: - std::vector<uint256> vHave; public: - CBlockLocator() {} + /** Returns the index entry for the genesis block of this chain, or NULL if none. */ + CBlockIndex *Genesis() const { + return vChain.size() > 0 ? vChain[0] : NULL; + } - explicit CBlockLocator(const CBlockIndex* pindex) - { - Set(pindex); + /** Returns the index entry for the tip of this chain, or NULL if none. */ + CBlockIndex *Tip() const { + return vChain.size() > 0 ? vChain[vChain.size() - 1] : NULL; } - explicit CBlockLocator(uint256 hashBlock); + /** Returns the index entry at a particular height in this chain, or NULL if no such height exists. */ + CBlockIndex *operator[](int nHeight) const { + if (nHeight < 0 || nHeight >= (int)vChain.size()) + return NULL; + return vChain[nHeight]; + } - CBlockLocator(const std::vector<uint256>& vHaveIn) - { - vHave = vHaveIn; + /** Compare two chains efficiently. */ + friend bool operator==(const CChain &a, const CChain &b) { + return a.vChain.size() == b.vChain.size() && + a.vChain[a.vChain.size() - 1] == b.vChain[b.vChain.size() - 1]; } - IMPLEMENT_SERIALIZE - ( - if (!(nType & SER_GETHASH)) - READWRITE(nVersion); - READWRITE(vHave); - ) + /** Efficiently check whether a block is present in this chain. */ + bool Contains(const CBlockIndex *pindex) const { + return (*this)[pindex->nHeight] == pindex; + } - void SetNull() - { - vHave.clear(); + /** Find the successor of a block in this chain, or NULL if the given index is not found or is the tip. */ + CBlockIndex *Next(const CBlockIndex *pindex) const { + if (Contains(pindex)) + return (*this)[pindex->nHeight + 1]; + else + return NULL; } - bool IsNull() - { - return vHave.empty(); + /** Return the maximal height in the chain. Is equal to chain.Tip() ? chain.Tip()->nHeight : -1. */ + int Height() const { + return vChain.size() - 1; } - /** Given a block initialises the locator to that point in the chain. */ - void Set(const CBlockIndex* pindex); - /** Returns the distance in blocks this locator is from our chain head. */ - int GetDistanceBack(); - /** Returns the first best-chain block the locator contains. */ - CBlockIndex* GetBlockIndex(); - /** Returns the hash of the first best chain block the locator contains. */ - uint256 GetBlockHash(); - /** Returns the height of the first best chain block the locator has. */ - int GetHeight(); + /** Set/initialize a chain with a given tip. Returns the forking point. */ + CBlockIndex *SetTip(CBlockIndex *pindex); + + /** Return a CBlockLocator that refers to a block in this chain (by default the tip). */ + CBlockLocator GetLocator(const CBlockIndex *pindex = NULL) const; + + /** Find the last common block between this chain and a locator. */ + CBlockIndex *FindFork(const CBlockLocator &locator) const; }; +/** The currently-connected chain of blocks. */ +extern CChain chainActive; + + diff --git a/src/miner.cpp b/src/miner.cpp index 30c600071f..e9c1d9aff9 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -176,7 +176,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) int64 nFees = 0; { LOCK2(cs_main, mempool.cs); - CBlockIndex* pindexPrev = pindexBest; + CBlockIndex* pindexPrev = chainActive.Tip(); CCoinsViewCache view(*pcoinsTip, true); // Priority order to process transactions @@ -467,7 +467,7 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) // Found a solution { LOCK(cs_main); - if (pblock->hashPrevBlock != hashBestChain) + if (pblock->hashPrevBlock != chainActive.Tip()->GetBlockHash()) return error("BitcoinMiner : generated block is stale"); // Remove key from key pool @@ -510,7 +510,7 @@ void static BitcoinMiner(CWallet *pwallet) // Create new block // unsigned int nTransactionsUpdatedLast = nTransactionsUpdated; - CBlockIndex* pindexPrev = pindexBest; + CBlockIndex* pindexPrev = chainActive.Tip(); auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey(reservekey)); if (!pblocktemplate.get()) @@ -613,7 +613,7 @@ void static BitcoinMiner(CWallet *pwallet) break; if (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60) break; - if (pindexPrev != pindexBest) + if (pindexPrev != chainActive.Tip()) break; // Update nTime every few seconds diff --git a/src/net.cpp b/src/net.cpp index 781dbfadca..99457be0f5 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -426,8 +426,10 @@ void AddressCurrentlyConnected(const CService& addr) - - +uint64 CNode::nTotalBytesRecv = 0; +uint64 CNode::nTotalBytesSent = 0; +CCriticalSection CNode::cs_totalBytesRecv; +CCriticalSection CNode::cs_totalBytesSent; CNode* FindNode(const CNetAddr& ip) { @@ -540,6 +542,8 @@ void CNode::Cleanup() void CNode::PushVersion() { + int nBestHeight = g_signals.GetHeight().get_value_or(0); + /// when NTP implemented, change to just nTime = GetAdjustedTime() int64 nTime = (fInbound ? GetAdjustedTime() : GetTime()); CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0",0))); @@ -620,6 +624,21 @@ void CNode::copyStats(CNodeStats &stats) X(nSendBytes); X(nRecvBytes); stats.fSyncNode = (this == pnodeSync); + + // It is common for nodes with good ping times to suddenly become lagged, + // due to a new block arriving or other large transfer. + // Merely reporting pingtime might fool the caller into thinking the node was still responsive, + // since pingtime does not update until the ping is complete, which might take a while. + // So, if a ping is taking an unusually long time in flight, + // the caller can immediately detect that this is happening. + int64 nPingUsecWait = 0; + if ((0 != nPingNonceSent) && (0 != nPingUsecStart)) { + nPingUsecWait = GetTimeMicros() - nPingUsecStart; + } + + // Raw ping time is in microseconds, but show it to user as whole seconds (Bitcoin users should be well used to small numbers with many decimal places by now :) + stats.dPingTime = (((double)nPingUsecTime) / 1e6); + stats.dPingWait = (((double)nPingUsecWait) / 1e6); } #undef X @@ -716,6 +735,7 @@ void SocketSendData(CNode *pnode) pnode->nLastSend = GetTime(); pnode->nSendBytes += nBytes; pnode->nSendOffset += nBytes; + pnode->RecordBytesSent(nBytes); if (pnode->nSendOffset == data.size()) { pnode->nSendOffset = 0; pnode->nSendSize -= data.size(); @@ -811,10 +831,9 @@ void ThreadSocketHandler() } } } - if (vNodes.size() != nPrevNodeCount) - { + if(vNodes.size() != nPrevNodeCount) { nPrevNodeCount = vNodes.size(); - uiInterface.NotifyNumConnectionsChanged(vNodes.size()); + uiInterface.NotifyNumConnectionsChanged(nPrevNodeCount); } @@ -993,6 +1012,7 @@ void ThreadSocketHandler() pnode->CloseSocketDisconnect(); pnode->nLastRecv = GetTime(); pnode->nRecvBytes += nBytes; + pnode->RecordBytesRecv(nBytes); } else if (nBytes == 0) { @@ -1467,6 +1487,8 @@ void static StartSync(const vector<CNode*> &vNodes) { CNode *pnodeNewSync = NULL; double dBestScore = 0; + int nBestHeight = g_signals.GetHeight().get_value_or(0); + // Iterate over all nodes BOOST_FOREACH(CNode* pnode, vNodes) { // check preconditions for allowing a sync @@ -1844,3 +1866,27 @@ void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataSt pnode->PushInventory(inv); } } + +void CNode::RecordBytesRecv(uint64 bytes) +{ + LOCK(cs_totalBytesRecv); + nTotalBytesRecv += bytes; +} + +void CNode::RecordBytesSent(uint64 bytes) +{ + LOCK(cs_totalBytesSent); + nTotalBytesSent += bytes; +} + +uint64 CNode::GetTotalBytesRecv() +{ + LOCK(cs_totalBytesRecv); + return nTotalBytesRecv; +} + +uint64 CNode::GetTotalBytesSent() +{ + LOCK(cs_totalBytesSent); + return nTotalBytesSent; +} @@ -28,7 +28,6 @@ static const unsigned int MAX_INV_SZ = 50000; class CNode; class CBlockIndex; -extern int nBestHeight; @@ -52,6 +51,7 @@ void SocketSendData(CNode *pnode); // Signals for message handling struct CNodeSignals { + boost::signals2::signal<int ()> GetHeight; boost::signals2::signal<bool (CNode*)> ProcessMessages; boost::signals2::signal<bool (CNode*, bool)> SendMessages; }; @@ -119,6 +119,8 @@ public: uint64 nSendBytes; uint64 nRecvBytes; bool fSyncNode; + double dPingTime; + double dPingWait; }; @@ -234,6 +236,12 @@ public: CCriticalSection cs_inventory; std::multimap<int64, CInv> mapAskFor; + // Ping time measurement + uint64 nPingNonceSent; + int64 nPingUsecStart; + int64 nPingUsecTime; + bool fPingQueued; + CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn = "", bool fInboundIn=false) : ssSend(SER_NETWORK, MIN_PROTO_VERSION) { nServices = 0; @@ -268,6 +276,10 @@ public: fRelayTxes = false; setInventoryKnown.max_size(SendBufferSize() / 1000); pfilter = new CBloomFilter(); + nPingNonceSent = 0; + nPingUsecStart = 0; + nPingUsecTime = 0; + fPingQueued = false; // Be shy and don't send version until we hear if (hSocket != INVALID_SOCKET && !fInbound) @@ -286,8 +298,15 @@ public: } private: + // Network usage totals + static CCriticalSection cs_totalBytesRecv; + static CCriticalSection cs_totalBytesSent; + static uint64 nTotalBytesRecv; + static uint64 nTotalBytesSent; + CNode(const CNode&); void operator=(const CNode&); + public: @@ -301,7 +320,7 @@ public: unsigned int GetTotalRecvSize() { unsigned int total = 0; - BOOST_FOREACH(const CNetMessage &msg, vRecvMsg) + BOOST_FOREACH(const CNetMessage &msg, vRecvMsg) total += msg.vRecv.size() + 24; return total; } @@ -634,6 +653,13 @@ public: static bool IsBanned(CNetAddr ip); bool Misbehaving(int howmuch); // 1 == a little, 100 == a lot void copyStats(CNodeStats &stats); + + // Network stats + static void RecordBytesRecv(uint64 bytes); + static void RecordBytesSent(uint64 bytes); + + static uint64 GetTotalBytesRecv(); + static uint64 GetTotalBytesSent(); }; diff --git a/src/netbase.cpp b/src/netbase.cpp index acc2ae32aa..360ecdd959 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -864,7 +864,7 @@ std::vector<unsigned char> CNetAddr::GetGroup() const nBits = 4; } // for he.net, use /36 groups - else if (GetByte(15) == 0x20 && GetByte(14) == 0x11 && GetByte(13) == 0x04 && GetByte(12) == 0x70) + else if (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x04 && GetByte(12) == 0x70) nBits = 36; // for the rest of the IPv6 network, use /32 groups else diff --git a/src/netbase.h b/src/netbase.h index 8d5135e970..e1f80b4ee8 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -54,7 +54,7 @@ class CNetAddr bool IsRFC3849() const; // IPv6 documentation address (2001:0DB8::/32) bool IsRFC3927() const; // IPv4 autoconfig (169.254.0.0/16) bool IsRFC3964() const; // IPv6 6to4 tunnelling (2002::/16) - bool IsRFC4193() const; // IPv6 unique local (FC00::/15) + bool IsRFC4193() const; // IPv6 unique local (FC00::/7) bool IsRFC4380() const; // IPv6 Teredo tunnelling (2001::/32) bool IsRFC4843() const; // IPv6 ORCHID (2001:10::/28) bool IsRFC4862() const; // IPv6 autoconfig (FE80::/64) diff --git a/src/qt/Makefile.am b/src/qt/Makefile.am index 33c5825eb3..e7ae0a905f 100644 --- a/src/qt/Makefile.am +++ b/src/qt/Makefile.am @@ -4,7 +4,7 @@ AM_CPPFLAGS = $(INCLUDES) -I$(top_builddir)/src/obj \ -I$(top_srcdir)/src/leveldb/include -I$(top_srcdir)/src \ -I$(top_srcdir)/src/leveldb/helpers -I$(top_builddir)/src/qt \ -I$(top_builddir)/src/qt/forms $(BOOST_INCLUDES) $(PROTOBUF_CFLAGS) \ - $(QR_CFLAGS) + $(QR_CFLAGS) $(BDB_CPPFLAGS) AM_LDFLAGS = $(PTHREAD_CFLAGS) bin_PROGRAMS = bitcoin-qt noinst_LIBRARIES = libbitcoinqt.a @@ -48,7 +48,7 @@ QT_MOC_CPP = moc_aboutdialog.cpp moc_addressbookpage.cpp \ moc_optionsmodel.cpp moc_overviewpage.cpp moc_paymentserver.cpp \ moc_qrcodedialog.cpp moc_qvalidatedlineedit.cpp moc_qvaluecombobox.cpp \ moc_rpcconsole.cpp moc_sendcoinsdialog.cpp moc_sendcoinsentry.cpp \ - moc_signverifymessagedialog.cpp moc_splashscreen.cpp moc_transactiondesc.cpp \ + moc_signverifymessagedialog.cpp moc_splashscreen.cpp moc_trafficgraphwidget.cpp moc_transactiondesc.cpp \ moc_transactiondescdialog.cpp moc_transactionfilterproxy.cpp \ moc_transactiontablemodel.cpp moc_transactionview.cpp moc_walletframe.cpp \ moc_walletmodel.cpp moc_walletstack.cpp moc_walletview.cpp @@ -73,7 +73,7 @@ BITCOIN_QT_H = aboutdialog.h addressbookpage.h addresstablemodel.h \ optionsmodel.h overviewpage.h paymentrequestplus.h paymentserver.h \ qrcodedialog.h qvalidatedlineedit.h qvaluecombobox.h rpcconsole.h \ sendcoinsdialog.h sendcoinsentry.h signverifymessagedialog.h splashscreen.h \ - transactiondescdialog.h transactiondesc.h transactionfilterproxy.h \ + trafficgraphwidget.h transactiondescdialog.h transactiondesc.h transactionfilterproxy.h \ transactionrecord.h transactiontablemodel.h transactionview.h walletframe.h \ walletmodel.h walletmodeltransaction.h walletstack.h walletview.h @@ -102,7 +102,7 @@ BITCOIN_QT_CPP = aboutdialog.cpp addressbookpage.cpp \ optionsdialog.cpp optionsmodel.cpp overviewpage.cpp paymentrequestplus.cpp \ paymentserver.cpp qvalidatedlineedit.cpp qvaluecombobox.cpp \ rpcconsole.cpp sendcoinsdialog.cpp sendcoinsentry.cpp \ - signverifymessagedialog.cpp splashscreen.cpp transactiondesc.cpp \ + signverifymessagedialog.cpp splashscreen.cpp trafficgraphwidget.cpp transactiondesc.cpp \ transactiondescdialog.cpp transactionfilterproxy.cpp transactionrecord.cpp \ transactiontablemodel.cpp transactionview.cpp walletframe.cpp \ walletmodel.cpp walletmodeltransaction.cpp walletstack.cpp walletview.cpp @@ -145,7 +145,7 @@ bitcoin_qt_CPPFLAGS = $(AM_CPPFLAGS) $(QT_INCLUDES) \ -I$(top_srcdir)/src/qt/forms bitcoin_qt_SOURCES = bitcoin.cpp bitcoin_qt_LDADD = libbitcoinqt.a $(LIBBITCOIN) $(LIBLEVELDB) $(LIBMEMENV) \ - $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(PROTOBUF_LIBS) + $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) # forms/foo.h -> forms/ui_foo.h QT_FORMS_H=$(join $(dir $(QT_FORMS_UI)),$(addprefix ui_, $(notdir $(QT_FORMS_UI:.ui=.h)))) diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index f184fb9ef0..78693971da 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -155,11 +155,14 @@ static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTrans #if QT_VERSION < 0x050000 void DebugMessageHandler(QtMsgType type, const char * msg) { + Q_UNUSED(type); LogPrint("qt", "Bitcoin-Qt: %s\n", msg); } #else void DebugMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &msg) { + Q_UNUSED(type); + Q_UNUSED(context); LogPrint("qt", "Bitcoin-Qt: %s\n", qPrintable(msg)); } #endif diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index d9d4e3b23d..37b8743eff 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -130,9 +130,10 @@ void BitcoinAmountField::setValue(qint64 value) setText(BitcoinUnits::format(currentUnit, value)); } -void BitcoinAmountField::setReadOnly(bool fReadeOnly) +void BitcoinAmountField::setReadOnly(bool fReadOnly) { - // TODO ... + amount->setReadOnly(fReadOnly); + unit->setEnabled(!fReadOnly); } void BitcoinAmountField::unitChanged(int idx) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 49eb35dfec..23a221120f 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -157,6 +157,8 @@ BitcoinGUI::BitcoinGUI(bool fIsTestnet, QWidget *parent) : rpcConsole = new RPCConsole(this); connect(openRPCConsoleAction, SIGNAL(triggered()), rpcConsole, SLOT(show())); + // prevents an oben debug window from becoming stuck/unusable on client shutdown + connect(quitAction, SIGNAL(triggered()), rpcConsole, SLOT(hide())); // Install event filter to be able to catch status tip events (QEvent::StatusTip) this->installEventFilter(this); @@ -233,7 +235,11 @@ void BitcoinGUI::createActions(bool fIsTestnet) aboutAction = new QAction(QIcon(":/icons/bitcoin_testnet"), tr("&About Bitcoin"), this); aboutAction->setStatusTip(tr("Show information about Bitcoin")); aboutAction->setMenuRole(QAction::AboutRole); +#if QT_VERSION < 0x050000 aboutQtAction = new QAction(QIcon(":/trolltech/qmessagebox/images/qtlogo-64.png"), tr("About &Qt"), this); +#else + aboutQtAction = new QAction(QIcon(":/qt-project.org/qmessagebox/images/qtlogo-64.png"), tr("About &Qt"), this); +#endif aboutQtAction->setStatusTip(tr("Show information about Qt")); aboutQtAction->setMenuRole(QAction::AboutQtRole); optionsAction = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); @@ -751,13 +757,9 @@ void BitcoinGUI::handlePaymentRequest(const SendCoinsRecipient& recipient) walletFrame->handlePaymentRequest(recipient); } -void BitcoinGUI::showPaymentACK(QString msg) +void BitcoinGUI::showPaymentACK(const QString& msg) { -#if QT_VERSION < 0x050000 - message(tr("Payment acknowledged"), Qt::escape(msg), CClientUIInterface::MODAL); -#else - message(tr("Payment acknowledged"), msg.toHtmlEscaped(), CClientUIInterface::MODAL); -#endif + message(tr("Payment acknowledged"), GUIUtil::HtmlEscape(msg), CClientUIInterface::MODAL); } void BitcoinGUI::setEncryptionStatus(int status) diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index fc25e867fc..e2dd5dc6bc 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -154,7 +154,7 @@ public slots: void askFee(qint64 nFeeRequired, bool *payFee); void handlePaymentRequest(const SendCoinsRecipient& recipient); - void showPaymentACK(QString msg); + void showPaymentACK(const QString& msg); /** Show incoming transaction notification for new transactions. */ void incomingTransaction(const QString& date, int unit, qint64 amount, const QString& type, const QString& address); diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index f7dd8adb6b..2bab488135 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -24,9 +24,8 @@ ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) : numBlocksAtStartup(-1), pollTimer(0) { pollTimer = new QTimer(this); - pollTimer->setInterval(MODEL_UPDATE_DELAY); - pollTimer->start(); connect(pollTimer, SIGNAL(timeout()), this, SLOT(updateTimer())); + pollTimer->start(MODEL_UPDATE_DELAY); subscribeToCoreSignals(); } @@ -43,7 +42,7 @@ int ClientModel::getNumConnections() const int ClientModel::getNumBlocks() const { - return nBestHeight; + return chainActive.Height(); } int ClientModel::getNumBlocksAtStartup() @@ -52,10 +51,20 @@ int ClientModel::getNumBlocksAtStartup() return numBlocksAtStartup; } +quint64 ClientModel::getTotalBytesRecv() const +{ + return CNode::GetTotalBytesRecv(); +} + +quint64 ClientModel::getTotalBytesSent() const +{ + return CNode::GetTotalBytesSent(); +} + QDateTime ClientModel::getLastBlockDate() const { - if (pindexBest) - return QDateTime::fromTime_t(pindexBest->GetBlockTime()); + if (chainActive.Tip()) + return QDateTime::fromTime_t(chainActive.Tip()->GetBlockTime()); else if(!isTestNet()) return QDateTime::fromTime_t(1231006505); // Genesis block's time else @@ -64,7 +73,7 @@ QDateTime ClientModel::getLastBlockDate() const double ClientModel::getVerificationProgress() const { - return Checkpoints::GuessVerificationProgress(pindexBest); + return Checkpoints::GuessVerificationProgress(chainActive.Tip()); } void ClientModel::updateTimer() @@ -86,6 +95,8 @@ void ClientModel::updateTimer() // ensure we return the maximum of newNumBlocksOfPeers and newNumBlocks to not create weird displays in the GUI emit numBlocksChanged(newNumBlocks, std::max(newNumBlocksOfPeers, newNumBlocks)); } + + emit bytesChanged(getTotalBytesRecv(), getTotalBytesSent()); } void ClientModel::updateNumConnections(int numConnections) diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 15074300b4..925f20acd9 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -35,6 +35,9 @@ public: int getNumBlocks() const; int getNumBlocksAtStartup(); + quint64 getTotalBytesRecv() const; + quint64 getTotalBytesSent() const; + double getVerificationProgress() const; QDateTime getLastBlockDate() const; @@ -74,6 +77,7 @@ signals: void numConnectionsChanged(int count); void numBlocksChanged(int count, int countOfPeers); void alertsChanged(const QString &warnings); + void bytesChanged(quint64 totalBytesIn, quint64 totalBytesOut); //! Asynchronous message notification void message(const QString &title, const QString &message, unsigned int style); diff --git a/src/qt/forms/rpcconsole.ui b/src/qt/forms/rpcconsole.ui index d1d8ab42a0..54c41ffb67 100644 --- a/src/qt/forms/rpcconsole.ui +++ b/src/qt/forms/rpcconsole.ui @@ -445,10 +445,271 @@ </item> </layout> </widget> + <widget class="QWidget" name="tab"> + <attribute name="title"> + <string>&Network Traffic</string> + </attribute> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <item> + <widget class="TrafficGraphWidget" name="trafficGraph" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QSlider" name="sldGraphRange"> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>288</number> + </property> + <property name="pageStep"> + <number>12</number> + </property> + <property name="value"> + <number>6</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="lblGraphRange"> + <property name="minimumSize"> + <size> + <width>100</width> + <height>0</height> + </size> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="btnClearTrafficGraph"> + <property name="text"> + <string>&Clear</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Totals</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <widget class="Line" name="line"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>10</width> + <height>0</height> + </size> + </property> + <property name="palette"> + <palette> + <active> + <colorrole role="Light"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>0</red> + <green>255</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + </active> + <inactive> + <colorrole role="Light"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>0</red> + <green>255</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + </inactive> + <disabled> + <colorrole role="Light"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>0</red> + <green>255</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + </disabled> + </palette> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_16"> + <property name="text"> + <string>In:</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="lblBytesIn"> + <property name="minimumSize"> + <size> + <width>50</width> + <height>0</height> + </size> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_5"> + <item> + <widget class="Line" name="line_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>10</width> + <height>0</height> + </size> + </property> + <property name="palette"> + <palette> + <active> + <colorrole role="Light"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>0</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + </active> + <inactive> + <colorrole role="Light"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>0</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + </inactive> + <disabled> + <colorrole role="Light"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>0</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + </disabled> + </palette> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_17"> + <property name="text"> + <string>Out:</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="lblBytesOut"> + <property name="minimumSize"> + <size> + <width>50</width> + <height>0</height> + </size> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer_4"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>407</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + </layout> + </item> + </layout> + </widget> </widget> </item> </layout> </widget> + <customwidgets> + <customwidget> + <class>TrafficGraphWidget</class> + <extends>QWidget</extends> + <header>trafficgraphwidget.h</header> + <container>1</container> + <slots> + <slot>clear()</slot> + </slots> + </customwidget> + </customwidgets> <resources> <include location="../bitcoin.qrc"/> </resources> diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui index a2ef9a0a38..5c6afd6c71 100644 --- a/src/qt/forms/sendcoinsentry.ui +++ b/src/qt/forms/sendcoinsentry.ui @@ -10,19 +10,13 @@ <height>150</height> </rect> </property> - <property name="windowTitle"> - <string>StackedWidget</string> - </property> <property name="autoFillBackground"> <bool>false</bool> </property> <property name="currentIndex"> - <number>1</number> + <number>0</number> </property> <widget class="QFrame" name="SendCoinsInsecure"> - <property name="windowTitle"> - <string>Form</string> - </property> <property name="frameShape"> <enum>QFrame::StyledPanel</enum> </property> @@ -34,7 +28,7 @@ <number>12</number> </property> <item row="5" column="0"> - <widget class="QLabel" name="label"> + <widget class="QLabel" name="amountLabel"> <property name="text"> <string>A&mount:</string> </property> @@ -47,7 +41,7 @@ </widget> </item> <item row="3" column="0"> - <widget class="QLabel" name="label_2"> + <widget class="QLabel" name="payToLabel"> <property name="text"> <string>Pay &To:</string> </property> @@ -63,7 +57,7 @@ <widget class="BitcoinAmountField" name="payAmount"/> </item> <item row="4" column="0"> - <widget class="QLabel" name="label_4"> + <widget class="QLabel" name="labellLabel"> <property name="text"> <string>&Label:</string> </property> @@ -592,9 +586,6 @@ </disabled> </palette> </property> - <property name="windowTitle"> - <string>SecureSend</string> - </property> <property name="autoFillBackground"> <bool>true</bool> </property> @@ -609,7 +600,7 @@ <number>12</number> </property> <item row="4" column="0"> - <widget class="QLabel" name="label_s4"> + <widget class="QLabel" name="memoLabel_s"> <property name="text"> <string>Memo:</string> </property> @@ -622,7 +613,7 @@ </widget> </item> <item row="5" column="0"> - <widget class="QLabel" name="label_s1"> + <widget class="QLabel" name="amountLabel_s"> <property name="text"> <string>A&mount:</string> </property> @@ -630,12 +621,12 @@ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> <property name="buddy"> - <cstring>payAmount</cstring> + <cstring>payAmount_s</cstring> </property> </widget> </item> <item row="3" column="0"> - <widget class="QLabel" name="label_s2"> + <widget class="QLabel" name="payToLabel_s"> <property name="text"> <string>Pay &To:</string> </property> @@ -649,15 +640,9 @@ </item> <item row="5" column="2"> <widget class="BitcoinAmountField" name="payAmount_s"> - <property name="enabled"> - <bool>false</bool> - </property> <property name="acceptDrops"> <bool>false</bool> </property> - <property name="readOnly"> - <bool>true</bool> - </property> </widget> </item> <item row="3" column="2"> @@ -667,14 +652,17 @@ </property> <item> <widget class="QLabel" name="payTo_s"> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> </widget> </item> </layout> </item> <item row="4" column="2"> - <widget class="QLabel" name="memo_s"> - <property name="text"> - <string>message from merchant</string> + <widget class="QLabel" name="memoTextLabel_s"> + <property name="textFormat"> + <enum>Qt::PlainText</enum> </property> </widget> </item> diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index 72f75b42e6..96ba997943 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -48,6 +48,7 @@ const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // milliseconds const QString BITCOIN_IPC_PREFIX("bitcoin:"); const char* BITCOIN_REQUEST_MIMETYPE = "application/bitcoin-paymentrequest"; const char* BITCOIN_PAYMENTACK_MIMETYPE = "application/bitcoin-paymentack"; +const char* BITCOIN_PAYMENTACK_CONTENTTYPE = "application/bitcoin-payment"; X509_STORE* PaymentServer::certStore = NULL; void PaymentServer::freeCertStore() @@ -471,11 +472,7 @@ bool PaymentServer::processPaymentRequest(PaymentRequestPlus& request, QList<Sen recipients.append(SendCoinsRecipient()); recipients[i].amount = sendingTo.second; QString memo = QString::fromStdString(request.getDetails().memo()); -#if QT_VERSION < 0x050000 - recipients[i].label = Qt::escape(memo); -#else - recipients[i].label = memo.toHtmlEscaped(); -#endif + recipients[i].label = GUIUtil::HtmlEscape(memo); CTxDestination dest; if (ExtractDestination(sendingTo.first, dest)) { if (i == 0) // Tie request to first pay-to, we don't want multiple ACKs @@ -518,7 +515,7 @@ void PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipien QNetworkRequest netRequest; netRequest.setAttribute(QNetworkRequest::User, "PaymentACK"); netRequest.setUrl(QString::fromStdString(details.payment_url())); - netRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/bitcoin-payment"); + netRequest.setHeader(QNetworkRequest::ContentTypeHeader, BITCOIN_PAYMENTACK_CONTENTTYPE); netRequest.setRawHeader("User-Agent", CLIENT_NAME.c_str()); netRequest.setRawHeader("Accept", BITCOIN_PAYMENTACK_MIMETYPE); diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 8953c36579..e7dcdf62a1 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -22,6 +22,8 @@ const int CONSOLE_HISTORY = 50; const QSize ICON_SIZE(24, 24); +const int INITIAL_TRAFFIC_GRAPH_MINS = 30; + const struct { const char *url; const char *source; @@ -204,6 +206,7 @@ RPCConsole::RPCConsole(QWidget *parent) : ui->openSSLVersion->setText(SSLeay_version(SSLEAY_VERSION)); startExecutor(); + setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS); clear(); } @@ -253,7 +256,8 @@ bool RPCConsole::eventFilter(QObject* obj, QEvent *event) void RPCConsole::setClientModel(ClientModel *model) { - this->clientModel = model; + clientModel = model; + ui->trafficGraph->setClientModel(model); if(model) { // Keep up to date with client @@ -263,6 +267,9 @@ void RPCConsole::setClientModel(ClientModel *model) setNumBlocks(model->getNumBlocks(), model->getNumBlocksOfPeers()); connect(model, SIGNAL(numBlocksChanged(int,int)), this, SLOT(setNumBlocks(int,int))); + updateTrafficStats(model->getTotalBytesRecv(), model->getTotalBytesSent()); + connect(model, SIGNAL(bytesChanged(quint64,quint64)), this, SLOT(updateTrafficStats(quint64, quint64))); + // Provide initial values ui->clientVersion->setText(model->formatFullVersion()); ui->clientName->setText(model->clientName()); @@ -431,3 +438,49 @@ void RPCConsole::on_showCLOptionsButton_clicked() GUIUtil::HelpMessageBox help; help.exec(); } + +void RPCConsole::on_sldGraphRange_valueChanged(int value) +{ + const int multiplier = 5; // each position on the slider represents 5 min + int mins = value * multiplier; + setTrafficGraphRange(mins); +} + +QString RPCConsole::FormatBytes(quint64 bytes) +{ + if(bytes < 1024) + return QString(tr("%1 B")).arg(bytes); + if(bytes < 1024 * 1024) + return QString(tr("%1 KB")).arg(bytes / 1024); + if(bytes < 1024 * 1024 * 1024) + return QString(tr("%1 MB")).arg(bytes / 1024 / 1024); + + return QString(tr("%1 GB")).arg(bytes / 1024 / 1024 / 1024); +} + +void RPCConsole::setTrafficGraphRange(int mins) +{ + ui->trafficGraph->setGraphRangeMins(mins); + if(mins < 60) { + ui->lblGraphRange->setText(QString(tr("%1 m")).arg(mins)); + } else { + int hours = mins / 60; + int minsLeft = mins % 60; + if(minsLeft == 0) { + ui->lblGraphRange->setText(QString(tr("%1 h")).arg(hours)); + } else { + ui->lblGraphRange->setText(QString(tr("%1 h %2 m")).arg(hours).arg(minsLeft)); + } + } +} + +void RPCConsole::updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut) +{ + ui->lblBytesIn->setText(FormatBytes(totalBytesIn)); + ui->lblBytesOut->setText(FormatBytes(totalBytesOut)); +} + +void RPCConsole::on_btnClearTrafficGraph_clicked() +{ + ui->trafficGraph->clear(); +} diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 3c38b4b8de..af92b55770 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -37,6 +37,12 @@ private slots: void on_openDebugLogfileButton_clicked(); /** display messagebox with program parameters (same as bitcoin-qt --help) */ void on_showCLOptionsButton_clicked(); + /** change the time range of the network traffic graph */ + void on_sldGraphRange_valueChanged(int value); + /** update traffic statistics */ + void updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut); + /** clear traffic graph */ + void on_btnClearTrafficGraph_clicked(); public slots: void clear(); @@ -55,6 +61,9 @@ signals: void cmdRequest(const QString &command); private: + static QString FormatBytes(quint64 bytes); + void setTrafficGraphRange(int mins); + Ui::RPCConsole *ui; ClientModel *clientModel; QStringList history; diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 809eff9c27..3fd4a26e76 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -94,27 +94,33 @@ void SendCoinsDialog::on_sendButton_clicked() QStringList formatted; foreach(const SendCoinsRecipient &rcp, recipients) { - QString amount = BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount); + // generate bold amount string + QString amount = "<b>" + BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount); + amount.append("</b>"); + // generate monospace address string + QString address = "<span style='font-family: monospace;'>" + rcp.address; + address.append("</span>"); + + QString recipientElement; + if (rcp.authenticatedMerchant.isEmpty()) { - QString recipientElement = QString("<b>%1</b> ").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount)); - recipientElement.append(tr("to")); - - if(rcp.label.length() > 0) + if(rcp.label.length() > 0) // label with address { - recipientElement.append(QString(" %1 <span style='font-size:8px;'>%2</span><br />").arg(GUIUtil::HtmlEscape(rcp.label), rcp.address)); // add address with label + recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.label)); + recipientElement.append(QString(" (%1)").arg(address)); } - else + else // just address { - recipientElement.append(QString(" %1<br />").arg(rcp.address)); // add address WITHOUT label + recipientElement = tr("%1 to %2").arg(amount, address); } - formatted.append(recipientElement); } - else + else // just merchant { - QString merchant = GUIUtil::HtmlEscape(rcp.authenticatedMerchant); - formatted.append(tr("<b>%1</b> to %2").arg(amount, merchant)); + recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.authenticatedMerchant)); } + + formatted.append(recipientElement); } fNewRecipientAllowed = false; @@ -132,42 +138,38 @@ void SendCoinsDialog::on_sendButton_clicked() WalletModelTransaction currentTransaction(recipients); WalletModel::SendCoinsReturn prepareStatus = model->prepareTransaction(currentTransaction); + QString strSendCoins = tr("Send Coins"); switch(prepareStatus.status) { case WalletModel::InvalidAddress: - QMessageBox::warning(this, tr("Send Coins"), - tr("The recipient address is not valid, please recheck."), - QMessageBox::Ok, QMessageBox::Ok); + QMessageBox::warning(this, strSendCoins, + tr("The recipient address is not valid, please recheck.")); break; case WalletModel::InvalidAmount: - QMessageBox::warning(this, tr("Send Coins"), - tr("The amount to pay must be larger than 0."), - QMessageBox::Ok, QMessageBox::Ok); + QMessageBox::warning(this, strSendCoins, + tr("The amount to pay must be larger than 0.")); break; case WalletModel::AmountExceedsBalance: - QMessageBox::warning(this, tr("Send Coins"), - tr("The amount exceeds your balance."), - QMessageBox::Ok, QMessageBox::Ok); + QMessageBox::warning(this, strSendCoins, + tr("The amount exceeds your balance.")); break; case WalletModel::AmountWithFeeExceedsBalance: - QMessageBox::warning(this, tr("Send Coins"), + QMessageBox::warning(this, strSendCoins, tr("The total exceeds your balance when the %1 transaction fee is included."). - arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), currentTransaction.getTransactionFee())), - QMessageBox::Ok, QMessageBox::Ok); + arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), currentTransaction.getTransactionFee()))); break; case WalletModel::DuplicateAddress: - QMessageBox::warning(this, tr("Send Coins"), - tr("Duplicate address found, can only send to each address once per send operation."), - QMessageBox::Ok, QMessageBox::Ok); + QMessageBox::warning(this, strSendCoins, + tr("Duplicate address found, can only send to each address once per send operation.")); break; case WalletModel::TransactionCreationFailed: - QMessageBox::warning(this, tr("Send Coins"), - tr("Error: Transaction creation failed!"), - QMessageBox::Ok, QMessageBox::Ok); + QMessageBox::warning(this, strSendCoins, + tr("Error: Transaction creation failed!")); break; - case WalletModel::Aborted: // User aborted, nothing to do - case WalletModel::OK: case WalletModel::TransactionCommitFailed: + case WalletModel::OK: + case WalletModel::Aborted: // User aborted, nothing to do + default: break; } @@ -197,7 +199,7 @@ void SendCoinsDialog::on_sendButton_clicked() QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"), questionString.arg(formatted.join("<br />")), - QMessageBox::Yes|QMessageBox::Cancel, + QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); if(retval != QMessageBox::Yes) @@ -211,15 +213,13 @@ void SendCoinsDialog::on_sendButton_clicked() switch(sendstatus.status) { case WalletModel::TransactionCommitFailed: - QMessageBox::warning(this, tr("Send Coins"), - tr("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."), - QMessageBox::Ok, QMessageBox::Ok); - break; - case WalletModel::Aborted: // User aborted, nothing to do + QMessageBox::warning(this, strSendCoins, + tr("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.")); break; case WalletModel::OK: accept(); break; + case WalletModel::Aborted: // User aborted, nothing to do default: break; } @@ -351,22 +351,22 @@ void SendCoinsDialog::pasteEntry(const SendCoinsRecipient &rv) bool SendCoinsDialog::handlePaymentRequest(const SendCoinsRecipient &rv) { + QString strSendCoins = tr("Send Coins"); if (!rv.authenticatedMerchant.isEmpty()) { // Expired payment request? const payments::PaymentDetails& details = rv.paymentRequest.getDetails(); if (details.has_expires() && (int64)details.expires() < GetTime()) { - QMessageBox::warning(this, tr("Send Coins"), - tr("Payment request expired")); + QMessageBox::warning(this, strSendCoins, + tr("Payment request expired")); return false; } } else { CBitcoinAddress address(rv.address.toStdString()); if (!address.IsValid()) { - QString strAddress(address.ToString().c_str()); - QMessageBox::warning(this, tr("Send Coins"), - tr("Invalid payment address %1").arg(strAddress)); + QMessageBox::warning(this, strSendCoins, + tr("Invalid payment address %1").arg(rv.address)); return false; } } diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index 75610f199e..aa671e0540 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -85,10 +85,17 @@ void SendCoinsEntry::setRemoveEnabled(bool enabled) void SendCoinsEntry::clear() { + // clear UI elements for insecure payments ui->payTo->clear(); ui->addAsLabel->clear(); ui->payAmount->clear(); + // and the ones for secure payments just to be sure + ui->payTo_s->clear(); + ui->memoTextLabel_s->clear(); + ui->payAmount_s->clear(); + ui->payTo->setFocus(); + // update the display unit, to not use the default ("BTC") updateDisplayUnit(); } @@ -106,8 +113,8 @@ bool SendCoinsEntry::validate() if (!recipient.authenticatedMerchant.isEmpty()) return retval; - if(!ui->payTo->hasAcceptableInput() || - (model && !model->validateAddress(ui->payTo->text()))) + if (!ui->payTo->hasAcceptableInput() || + (model && !model->validateAddress(ui->payTo->text()))) { ui->payTo->setValid(false); retval = false; @@ -154,18 +161,20 @@ void SendCoinsEntry::setValue(const SendCoinsRecipient &value) { recipient = value; - ui->payTo->setText(value.address); - ui->addAsLabel->setText(value.label); - ui->payAmount->setValue(value.amount); - - if (!recipient.authenticatedMerchant.isEmpty()) + if (recipient.authenticatedMerchant.isEmpty()) + { + ui->payTo->setText(recipient.address); + ui->addAsLabel->setText(recipient.label); + ui->payAmount->setValue(recipient.amount); + } + else { - const payments::PaymentDetails& details = value.paymentRequest.getDetails(); + const payments::PaymentDetails& details = recipient.paymentRequest.getDetails(); - ui->payTo_s->setText(value.authenticatedMerchant); - ui->memo_s->setTextFormat(Qt::PlainText); - ui->memo_s->setText(QString::fromStdString(details.memo())); - ui->payAmount_s->setValue(value.amount); + ui->payTo_s->setText(recipient.authenticatedMerchant); + ui->memoTextLabel_s->setText(QString::fromStdString(details.memo())); + ui->payAmount_s->setValue(recipient.amount); + ui->payAmount_s->setReadOnly(true); setCurrentWidget(ui->SendCoinsSecure); } } diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h index 9c7bfe9521..49e622daf1 100644 --- a/src/qt/sendcoinsentry.h +++ b/src/qt/sendcoinsentry.h @@ -33,7 +33,8 @@ public: void setValue(const SendCoinsRecipient &value); void setAddress(const QString &address); - /** Set up the tab chain manually, as Qt messes up the tab chain by default in some cases (issue https://bugreports.qt-project.org/browse/QTBUG-10907). + /** Set up the tab chain manually, as Qt messes up the tab chain by default in some cases + * (issue https://bugreports.qt-project.org/browse/QTBUG-10907). */ QWidget *setupTabChain(QWidget *prev); diff --git a/src/qt/test/Makefile.am b/src/qt/test/Makefile.am index f51ac9bd6d..f8fe97462b 100644 --- a/src/qt/test/Makefile.am +++ b/src/qt/test/Makefile.am @@ -4,7 +4,7 @@ AM_CPPFLAGS = $(INCLUDES) -I$(top_builddir)/src/obj \ -I$(top_srcdir)/src/leveldb/include -I$(top_srcdir)/src \ -I$(top_srcdir)/src/leveldb/helpers -I$(top_srcdir)/src/qt \ -I$(top_builddir)/src/qt $(BOOST_INCLUDES) $(PROTOBUF_CFLAGS) \ - $(QR_CFLAGS) + $(QR_CFLAGS) $(BDB_CPPFLAGS) AM_LDFLAGS = $(PTHREAD_CFLAGS) bin_PROGRAMS = test_bitcoin-qt TESTS = test_bitcoin-qt @@ -20,7 +20,7 @@ test_bitcoin_qt_SOURCES = test_main.cpp uritests.cpp paymentservertests.cpp $(TE nodist_test_bitcoin_qt_SOURCES = $(TEST_QT_MOC_CPP) test_bitcoin_qt_LDADD = $(LIBBITCOINQT) $(LIBBITCOIN) $(LIBLEVELDB) \ $(LIBMEMENV) $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QT_TEST_LIBS) \ - $(QR_LIBS) $(PROTOBUF_LIBS) + $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) CLEANFILES = $(BUILT_SOURCES) *.gcda *.gcno diff --git a/src/qt/trafficgraphwidget.cpp b/src/qt/trafficgraphwidget.cpp new file mode 100644 index 0000000000..d49bc31f3e --- /dev/null +++ b/src/qt/trafficgraphwidget.cpp @@ -0,0 +1,169 @@ +#include "trafficgraphwidget.h" +#include "clientmodel.h" + +#include <QPainter> +#include <QColor> +#include <QTimer> + +#include <cmath> + +#define DESIRED_SAMPLES 800 + +#define XMARGIN 10 +#define YMARGIN 10 + +TrafficGraphWidget::TrafficGraphWidget(QWidget *parent) : + QWidget(parent), + timer(0), + fMax(0.0f), + nMins(0), + vSamplesIn(), + vSamplesOut(), + nLastBytesIn(0), + nLastBytesOut(0), + clientModel(0) +{ + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), SLOT(updateRates())); +} + +void TrafficGraphWidget::setClientModel(ClientModel *model) +{ + clientModel = model; + if(model) { + nLastBytesIn = model->getTotalBytesRecv(); + nLastBytesOut = model->getTotalBytesSent(); + } +} + +int TrafficGraphWidget::getGraphRangeMins() const +{ + return nMins; +} + +void TrafficGraphWidget::paintPath(QPainterPath &path, QQueue<float> &samples) +{ + int h = height() - YMARGIN * 2, w = width() - XMARGIN * 2; + int sampleCount = samples.size(), x = XMARGIN + w, y; + if(sampleCount > 0) { + path.moveTo(x, YMARGIN + h); + for(int i = 0; i < sampleCount; ++i) { + x = XMARGIN + w - w * i / DESIRED_SAMPLES; + y = YMARGIN + h - (int)(h * samples.at(i) / fMax); + path.lineTo(x, y); + } + path.lineTo(x, YMARGIN + h); + } +} + +void TrafficGraphWidget::paintEvent(QPaintEvent *) +{ + QPainter painter(this); + painter.fillRect(rect(), Qt::black); + + if(fMax <= 0.0f) return; + + QColor axisCol(Qt::gray); + int h = height() - YMARGIN * 2; + painter.setPen(axisCol); + painter.drawLine(XMARGIN, YMARGIN + h, width() - XMARGIN, YMARGIN + h); + + // decide what order of magnitude we are + int base = floor(log10(fMax)); + float val = pow(10.0f, base); + + const QString units = tr("KB/s"); + // draw lines + painter.setPen(axisCol); + painter.drawText(XMARGIN, YMARGIN + h - h * val / fMax, QString("%1 %2").arg(val).arg(units)); + for(float y = val; y < fMax; y += val) { + int yy = YMARGIN + h - h * y / fMax; + painter.drawLine(XMARGIN, yy, width() - XMARGIN, yy); + } + // if we drew 3 or fewer lines, break them up at the next lower order of magnitude + if(fMax / val <= 3.0f) { + axisCol = axisCol.darker(); + val = pow(10.0f, base - 1); + painter.setPen(axisCol); + painter.drawText(XMARGIN, YMARGIN + h - h * val / fMax, QString("%1 %2").arg(val).arg(units)); + int count = 1; + for(float y = val; y < fMax; y += val, count++) { + // don't overwrite lines drawn above + if(count % 10 == 0) + continue; + int yy = YMARGIN + h - h * y / fMax; + painter.drawLine(XMARGIN, yy, width() - XMARGIN, yy); + } + } + + if(!vSamplesIn.empty()) { + QPainterPath p; + paintPath(p, vSamplesIn); + painter.fillPath(p, QColor(0, 255, 0, 128)); + painter.setPen(Qt::green); + painter.drawPath(p); + } + if(!vSamplesOut.empty()) { + QPainterPath p; + paintPath(p, vSamplesOut); + painter.fillPath(p, QColor(255, 0, 0, 128)); + painter.setPen(Qt::red); + painter.drawPath(p); + } +} + +void TrafficGraphWidget::updateRates() +{ + if(!clientModel) return; + + quint64 bytesIn = clientModel->getTotalBytesRecv(), + bytesOut = clientModel->getTotalBytesSent(); + float inRate = (bytesIn - nLastBytesIn) / 1024.0f * 1000 / timer->interval(); + float outRate = (bytesOut - nLastBytesOut) / 1024.0f * 1000 / timer->interval(); + vSamplesIn.push_front(inRate); + vSamplesOut.push_front(outRate); + nLastBytesIn = bytesIn; + nLastBytesOut = bytesOut; + + while(vSamplesIn.size() > DESIRED_SAMPLES) { + vSamplesIn.pop_back(); + } + while(vSamplesOut.size() > DESIRED_SAMPLES) { + vSamplesOut.pop_back(); + } + + float tmax = 0.0f; + foreach(float f, vSamplesIn) { + if(f > tmax) tmax = f; + } + foreach(float f, vSamplesOut) { + if(f > tmax) tmax = f; + } + fMax = tmax; + update(); +} + +void TrafficGraphWidget::setGraphRangeMins(int mins) +{ + nMins = mins; + int msecsPerSample = nMins * 60 * 1000 / DESIRED_SAMPLES; + timer->stop(); + timer->setInterval(msecsPerSample); + + clear(); +} + +void TrafficGraphWidget::clear() +{ + timer->stop(); + + vSamplesOut.clear(); + vSamplesIn.clear(); + fMax = 0.0f; + + if(clientModel) { + nLastBytesIn = clientModel->getTotalBytesRecv(); + nLastBytesOut = clientModel->getTotalBytesSent(); + } + timer->start(); +} diff --git a/src/qt/trafficgraphwidget.h b/src/qt/trafficgraphwidget.h new file mode 100644 index 0000000000..b31d1d5b0a --- /dev/null +++ b/src/qt/trafficgraphwidget.h @@ -0,0 +1,44 @@ +#ifndef TRAFFICGRAPHWIDGET_H +#define TRAFFICGRAPHWIDGET_H + +#include <QWidget> +#include <QQueue> + +class ClientModel; + +QT_BEGIN_NAMESPACE +class QPaintEvent; +class QTimer; +QT_END_NAMESPACE + +class TrafficGraphWidget : public QWidget +{ + Q_OBJECT + +public: + explicit TrafficGraphWidget(QWidget *parent = 0); + void setClientModel(ClientModel *model); + int getGraphRangeMins() const; + +protected: + void paintEvent(QPaintEvent *); + +public slots: + void updateRates(); + void setGraphRangeMins(int mins); + void clear(); + +private: + void paintPath(QPainterPath &path, QQueue<float> &samples); + + QTimer *timer; + float fMax; + int nMins; + QQueue<float> vSamplesIn; + QQueue<float> vSamplesOut; + quint64 nLastBytesIn; + quint64 nLastBytesOut; + ClientModel *clientModel; +}; + +#endif // TRAFFICGRAPHWIDGET_H diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index e27aa93a4a..93fc8cab22 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -17,7 +17,7 @@ QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx) if (!IsFinalTx(wtx)) { if (wtx.nLockTime < LOCKTIME_THRESHOLD) - return tr("Open for %n more block(s)", "", wtx.nLockTime - nBestHeight + 1); + return tr("Open for %n more block(s)", "", wtx.nLockTime - chainActive.Height() + 1); else return tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx.nLockTime)); } diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index ea2c1f0a5c..162908a9a4 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -160,14 +160,14 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) idx); status.confirmed = wtx.IsConfirmed(); status.depth = wtx.GetDepthInMainChain(); - status.cur_num_blocks = nBestHeight; + status.cur_num_blocks = chainActive.Height(); if (!IsFinalTx(wtx)) { if (wtx.nLockTime < LOCKTIME_THRESHOLD) { status.status = TransactionStatus::OpenUntilBlock; - status.open_for = wtx.nLockTime - nBestHeight + 1; + status.open_for = wtx.nLockTime - chainActive.Height() + 1; } else { @@ -221,7 +221,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) bool TransactionRecord::statusUpdateNeeded() { - return status.cur_num_blocks != nBestHeight; + return status.cur_num_blocks != chainActive.Height(); } QString TransactionRecord::getTxID() const diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 07f6a62150..6f7a5933ab 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -250,9 +250,9 @@ void TransactionTableModel::updateTransaction(const QString &hash, int status) void TransactionTableModel::updateConfirmations() { - if(nBestHeight != cachedNumBlocks) + if(chainActive.Height() != cachedNumBlocks) { - cachedNumBlocks = nBestHeight; + cachedNumBlocks = chainActive.Height(); // Blocks came in since last poll. // Invalidate status (number of confirmations) and (possibly) description // for all rows. Qt is smart enough to only actually request the data for the diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp index 99a6647a65..8d6a1b387e 100644 --- a/src/qt/walletframe.cpp +++ b/src/qt/walletframe.cpp @@ -56,12 +56,9 @@ bool WalletFrame::handlePaymentRequest(const SendCoinsRecipient &recipient) void WalletFrame::showOutOfSyncWarning(bool fShow) { - if (!walletStack) { - QMessageBox box; - box.setText("walletStack is null"); - box.exec(); + if (!walletStack) return; - } + walletStack->showOutOfSyncWarning(fShow); } diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 1dcecbe60b..417bac9928 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -73,10 +73,10 @@ void WalletModel::updateStatus() void WalletModel::pollBalanceChanged() { - if(nBestHeight != cachedNumBlocks) + if(chainActive.Height() != cachedNumBlocks) { // Balance and number of transactions might have changed - cachedNumBlocks = nBestHeight; + cachedNumBlocks = chainActive.Height(); checkBalanceChanged(); } } @@ -143,7 +143,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact foreach(const SendCoinsRecipient &rcp, recipients) { if (rcp.paymentRequest.IsInitialized()) - { // PaymentRequest... + { // PaymentRequest... int64 subtotal = 0; const payments::PaymentDetails& details = rcp.paymentRequest.getDetails(); for (int i = 0; i < details.outputs_size(); i++) @@ -258,22 +258,26 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran // and emit coinsSent signal for each recipient foreach(const SendCoinsRecipient &rcp, transaction.getRecipients()) { - std::string strAddress = rcp.address.toStdString(); - CTxDestination dest = CBitcoinAddress(strAddress).Get(); - std::string strLabel = rcp.label.toStdString(); + // Don't touch the address book when we have a secure payment-request + if (rcp.authenticatedMerchant.isEmpty()) { - LOCK(wallet->cs_wallet); - - std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(dest); - - // Check if we have a new address or an updated label - if (mi == wallet->mapAddressBook.end()) - { - wallet->SetAddressBook(dest, strLabel, "send"); - } - else if (mi->second.name != strLabel) + std::string strAddress = rcp.address.toStdString(); + CTxDestination dest = CBitcoinAddress(strAddress).Get(); + std::string strLabel = rcp.label.toStdString(); { - wallet->SetAddressBook(dest, strLabel, ""); // "" means don't change purpose + LOCK(wallet->cs_wallet); + + std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(dest); + + // Check if we have a new address or an updated label + if (mi == wallet->mapAddressBook.end()) + { + wallet->SetAddressBook(dest, strLabel, "send"); + } + else if (mi->second.name != strLabel) + { + wallet->SetAddressBook(dest, strLabel, ""); // "" means don't change purpose + } } } emit coinsSent(wallet, rcp, transaction_array); diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp index 96fc3edbb2..706ed60b77 100644 --- a/src/qt/walletmodeltransaction.cpp +++ b/src/qt/walletmodeltransaction.cpp @@ -32,7 +32,7 @@ qint64 WalletModelTransaction::getTransactionFee() void WalletModelTransaction::setTransactionFee(qint64 newFee) { - fee=newFee; + fee = newFee; } qint64 WalletModelTransaction::getTotalTransactionAmount() @@ -40,7 +40,7 @@ qint64 WalletModelTransaction::getTotalTransactionAmount() qint64 totalTransactionAmount = 0; foreach(const SendCoinsRecipient &rcp, recipients) { - totalTransactionAmount+=rcp.amount; + totalTransactionAmount += rcp.amount; } return totalTransactionAmount; } diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 398f33605a..957241d6a0 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -17,10 +17,10 @@ double GetDifficulty(const CBlockIndex* blockindex) // minimum difficulty = 1.0. if (blockindex == NULL) { - if (pindexBest == NULL) + if (chainActive.Tip() == NULL) return 1.0; else - blockindex = pindexBest; + blockindex = chainActive.Tip(); } int nShift = (blockindex->nBits >> 24) & 0xff; @@ -66,7 +66,7 @@ Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex) if (blockindex->pprev) result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); - CBlockIndex *pnext = blockindex->GetNextInMainChain(); + CBlockIndex *pnext = chainActive.Next(blockindex); if (pnext) result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex())); return result; @@ -80,7 +80,7 @@ Value getblockcount(const Array& params, bool fHelp) "getblockcount\n" "Returns the number of blocks in the longest block chain."); - return nBestHeight; + return chainActive.Height(); } Value getbestblockhash(const Array& params, bool fHelp) @@ -90,7 +90,7 @@ Value getbestblockhash(const Array& params, bool fHelp) "getbestblockhash\n" "Returns the hash of the best (tip) block in the longest block chain."); - return hashBestChain.GetHex(); + return chainActive.Tip()->GetBlockHash().GetHex(); } Value getdifficulty(const Array& params, bool fHelp) @@ -145,11 +145,11 @@ Value getblockhash(const Array& params, bool fHelp) "Returns hash of block in best-block-chain at <index>."); int nHeight = params[0].get_int(); - if (nHeight < 0 || nHeight > nBestHeight) + if (nHeight < 0 || nHeight > chainActive.Height()) throw runtime_error("Block number out of range."); - CBlockIndex* pblockindex = FindBlockByHeight(nHeight); - return pblockindex->phashBlock->GetHex(); + CBlockIndex* pblockindex = chainActive[nHeight]; + return pblockindex->GetBlockHash().GetHex(); } Value getblock(const Array& params, bool fHelp) diff --git a/src/rpcdump.cpp b/src/rpcdump.cpp index 842910c7e0..61cd07d507 100644 --- a/src/rpcdump.cpp +++ b/src/rpcdump.cpp @@ -102,7 +102,7 @@ Value importprivkey(const Array& params, bool fHelp) throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet"); if (fRescan) { - pwalletMain->ScanForWalletTransactions(pindexGenesisBlock, true); + pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true); pwalletMain->ReacceptWalletTransactions(); } } @@ -124,7 +124,7 @@ Value importwallet(const Array& params, bool fHelp) if (!file.is_open()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); - int64 nTimeBegin = pindexBest->nTime; + int64 nTimeBegin = chainActive.Tip()->nTime; bool fGood = true; @@ -175,11 +175,11 @@ Value importwallet(const Array& params, bool fHelp) } file.close(); - CBlockIndex *pindex = pindexBest; + CBlockIndex *pindex = chainActive.Tip(); while (pindex && pindex->pprev && pindex->nTime > nTimeBegin - 7200) pindex = pindex->pprev; - LogPrintf("Rescanning last %i blocks\n", pindexBest->nHeight - pindex->nHeight + 1); + LogPrintf("Rescanning last %i blocks\n", chainActive.Height() - pindex->nHeight + 1); pwalletMain->ScanForWalletTransactions(pindex); pwalletMain->ReacceptWalletTransactions(); pwalletMain->MarkDirty(); @@ -243,8 +243,8 @@ Value dumpwallet(const Array& params, bool fHelp) // produce output file << strprintf("# Wallet dump created by Bitcoin %s (%s)\n", CLIENT_BUILD.c_str(), CLIENT_DATE.c_str()); file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime()).c_str()); - file << strprintf("# * Best block at time of backup was %i (%s),\n", nBestHeight, hashBestChain.ToString().c_str()); - file << strprintf("# mined on %s\n", EncodeDumpTime(pindexBest->nTime).c_str()); + file << strprintf("# * Best block at time of backup was %i (%s),\n", chainActive.Height(), chainActive.Tip()->GetBlockHash().ToString().c_str()); + file << strprintf("# mined on %s\n", EncodeDumpTime(chainActive.Tip()->nTime).c_str()); file << "\n"; for (std::vector<std::pair<int64, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) { const CKeyID &keyid = it->second; diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index c7f516caa7..f123c3a9e0 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -18,15 +18,71 @@ static CReserveKey* pMiningKey = NULL; void InitRPCMining() { + if (!pwalletMain) + return; + // getwork/getblocktemplate mining rewards paid here: pMiningKey = new CReserveKey(pwalletMain); } void ShutdownRPCMining() { + if (!pMiningKey) + return; + delete pMiningKey; pMiningKey = NULL; } +// Return average network hashes per second based on the last 'lookup' blocks, +// or from the last difficulty change if 'lookup' is nonpositive. +// If 'height' is nonnegative, compute the estimate at the time when a given block was found. +Value GetNetworkHashPS(int lookup, int height) { + CBlockIndex *pb = chainActive[height]; + + if (pb == NULL || !pb->nHeight) + return 0; + + // If lookup is -1, then use blocks since last difficulty change. + if (lookup <= 0) + lookup = pb->nHeight % 2016 + 1; + + // If lookup is larger than chain, then set it to chain length. + if (lookup > pb->nHeight) + lookup = pb->nHeight; + + CBlockIndex *pb0 = pb; + int64 minTime = pb0->GetBlockTime(); + int64 maxTime = minTime; + for (int i = 0; i < lookup; i++) { + pb0 = pb0->pprev; + int64 time = pb0->GetBlockTime(); + minTime = std::min(time, minTime); + maxTime = std::max(time, maxTime); + } + + // In case there's a situation where minTime == maxTime, we don't want a divide by zero exception. + if (minTime == maxTime) + return 0; + + uint256 workDiff = pb->nChainWork - pb0->nChainWork; + int64 timeDiff = maxTime - minTime; + + return (boost::int64_t)(workDiff.getdouble() / timeDiff); +} + +Value getnetworkhashps(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "getnetworkhashps [blocks] [height]\n" + "Returns the estimated network hashes per second based on the last 120 blocks.\n" + "Pass in [blocks] to override # of blocks, -1 specifies since last difficulty change.\n" + "Pass in [height] to estimate the network speed at the time when a certain block was found."); + + return GetNetworkHashPS(params.size() > 0 ? params[0].get_int() : 120, params.size() > 1 ? params[1].get_int() : -1); +} + + Value getgenerate(const Array& params, bool fHelp) { if (fHelp || params.size() != 0) @@ -34,6 +90,9 @@ Value getgenerate(const Array& params, bool fHelp) "getgenerate\n" "Returns true or false."); + if (!pMiningKey) + return false; + return GetBoolArg("-gen", false); } @@ -59,6 +118,7 @@ Value setgenerate(const Array& params, bool fHelp) } mapArgs["-gen"] = (fGenerate ? "1" : "0"); + assert(pwalletMain != NULL); GenerateBitcoins(fGenerate, pwalletMain); return Value::null; } @@ -85,14 +145,15 @@ Value getmininginfo(const Array& params, bool fHelp) "Returns an object containing mining-related information."); Object obj; - obj.push_back(Pair("blocks", (int)nBestHeight)); + obj.push_back(Pair("blocks", (int)chainActive.Height())); obj.push_back(Pair("currentblocksize", (uint64_t)nLastBlockSize)); obj.push_back(Pair("currentblocktx", (uint64_t)nLastBlockTx)); obj.push_back(Pair("difficulty", (double)GetDifficulty())); obj.push_back(Pair("errors", GetWarnings("statusbar"))); - obj.push_back(Pair("generate", GetBoolArg("-gen", false))); + obj.push_back(Pair("generate", getgenerate(params, false))); obj.push_back(Pair("genproclimit", (int)GetArg("-genproclimit", -1))); obj.push_back(Pair("hashespersec", gethashespersec(params, false))); + obj.push_back(Pair("networkhashps", getnetworkhashps(params, false))); obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); obj.push_back(Pair("testnet", TestNet())); return obj; @@ -128,10 +189,10 @@ Value getwork(const Array& params, bool fHelp) static CBlockIndex* pindexPrev; static int64 nStart; static CBlockTemplate* pblocktemplate; - if (pindexPrev != pindexBest || + if (pindexPrev != chainActive.Tip() || (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)) { - if (pindexPrev != pindexBest) + if (pindexPrev != chainActive.Tip()) { // Deallocate old blocks since they're obsolete now mapNewBlock.clear(); @@ -145,7 +206,7 @@ Value getwork(const Array& params, bool fHelp) // Store the pindexBest used before CreateNewBlock, to avoid races nTransactionsUpdatedLast = nTransactionsUpdated; - CBlockIndex* pindexPrevNew = pindexBest; + CBlockIndex* pindexPrevNew = chainActive.Tip(); nStart = GetTime(); // Create new block @@ -207,6 +268,7 @@ Value getwork(const Array& params, bool fHelp) pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second; pblock->hashMerkleRoot = pblock->BuildMerkleTree(); + assert(pwalletMain != NULL); return CheckWork(pblock, *pwalletMain, *pMiningKey); } } @@ -263,7 +325,7 @@ Value getblocktemplate(const Array& params, bool fHelp) static CBlockIndex* pindexPrev; static int64 nStart; static CBlockTemplate* pblocktemplate; - if (pindexPrev != pindexBest || + if (pindexPrev != chainActive.Tip() || (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5)) { // Clear pindexPrev so future calls make a new block, despite any failures from here on @@ -271,7 +333,7 @@ Value getblocktemplate(const Array& params, bool fHelp) // Store the pindexBest used before CreateNewBlock, to avoid races nTransactionsUpdatedLast = nTransactionsUpdated; - CBlockIndex* pindexPrevNew = pindexBest; + CBlockIndex* pindexPrevNew = chainActive.Tip(); nStart = GetTime(); // Create new block diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index bd7bc0ba10..f78f034e32 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.cpp @@ -19,6 +19,24 @@ Value getconnectioncount(const Array& params, bool fHelp) return (int)vNodes.size(); } +Value ping(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "ping\n" + "Requests that a ping be sent to all other nodes, to measure ping time.\n" + "Results provided in getpeerinfo, pingtime and pingwait fields are decimal seconds.\n" + "Ping command is handled in queue with all other commands, so it measures processing backlog, not just network ping."); + + // Request that each node send a ping during next message processing pass + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pNode, vNodes) { + pNode->fPingQueued = true; + } + + return Value::null; +} + static void CopyNodeStats(std::vector<CNodeStats>& vstats) { vstats.clear(); @@ -54,6 +72,9 @@ Value getpeerinfo(const Array& params, bool fHelp) obj.push_back(Pair("bytessent", (boost::int64_t)stats.nSendBytes)); obj.push_back(Pair("bytesrecv", (boost::int64_t)stats.nRecvBytes)); obj.push_back(Pair("conntime", (boost::int64_t)stats.nTimeConnected)); + obj.push_back(Pair("pingtime", stats.dPingTime)); + if (stats.dPingWait > 0.0) + obj.push_back(Pair("pingwait", stats.dPingWait)); obj.push_back(Pair("version", stats.nVersion)); obj.push_back(Pair("subver", stats.strSubVer)); obj.push_back(Pair("inbound", stats.fInbound)); @@ -202,3 +223,17 @@ Value getaddednodeinfo(const Array& params, bool fHelp) return ret; } +Value getnettotals(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 0) + throw runtime_error( + "getnettotals\n" + "Returns information about network traffic, including bytes in, bytes out,\n" + "and current time."); + + Object obj; + obj.push_back(Pair("totalbytesrecv", static_cast< boost::uint64_t>(CNode::GetTotalBytesRecv()))); + obj.push_back(Pair("totalbytessent", static_cast<boost::uint64_t>(CNode::GetTotalBytesSent()))); + obj.push_back(Pair("timemillis", static_cast<boost::int64_t>(GetTimeMillis()))); + return obj; +} diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index ce9d60e667..5384b65906 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -87,9 +87,9 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry) if (mi != mapBlockIndex.end() && (*mi).second) { CBlockIndex* pindex = (*mi).second; - if (pindex->IsInMainChain()) + if (chainActive.Contains(pindex)) { - entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight)); + entry.push_back(Pair("confirmations", 1 + chainActive.Height() - pindex->nHeight)); entry.push_back(Pair("time", (boost::int64_t)pindex->nTime)); entry.push_back(Pair("blocktime", (boost::int64_t)pindex->nTime)); } @@ -171,6 +171,7 @@ Value listunspent(const Array& params, bool fHelp) Array results; vector<COutput> vecOutputs; + assert(pwalletMain != NULL); pwalletMain->AvailableCoins(vecOutputs, false); BOOST_FOREACH(const COutput& out, vecOutputs) { @@ -458,7 +459,7 @@ Value signrawtransaction(const Array& params, bool fHelp) } } - const CKeyStore& keystore = (fGivenKeys ? tempKeystore : *pwalletMain); + const CKeyStore& keystore = ((fGivenKeys || !pwalletMain) ? tempKeystore : *pwalletMain); int nHashType = SIGHASH_ALL; if (params.size() > 3 && params[3].type() != null_type) diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 46338bdf26..f7341f7b69 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -21,7 +21,7 @@ static CCriticalSection cs_nWalletUnlockTime; std::string HelpRequiringPassphrase() { - return pwalletMain->IsCrypted() + return pwalletMain && pwalletMain->IsCrypted() ? "\nrequires wallet passphrase to be set with walletpassphrase first" : ""; } @@ -72,18 +72,22 @@ Value getinfo(const Array& params, bool fHelp) Object obj; obj.push_back(Pair("version", (int)CLIENT_VERSION)); obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION)); - obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); - obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); - obj.push_back(Pair("blocks", (int)nBestHeight)); + if (pwalletMain) { + obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); + obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); + } + obj.push_back(Pair("blocks", (int)chainActive.Height())); 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())); obj.push_back(Pair("testnet", TestNet())); - obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime())); - obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize())); + if (pwalletMain) { + obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime())); + obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize())); + } obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee))); - if (pwalletMain->IsCrypted()) + if (pwalletMain && pwalletMain->IsCrypted()) obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime)); obj.push_back(Pair("errors", GetWarnings("statusbar"))); return obj; @@ -738,7 +742,7 @@ static CScript _createmultisig(const Array& params) // Case 1: Bitcoin address and we have full public key: CBitcoinAddress address(ks); - if (address.IsValid()) + if (pwalletMain && address.IsValid()) { CKeyID keyID; if (!address.GetKeyID(keyID)) @@ -1165,7 +1169,9 @@ Value listsinceblock(const Array& params, bool fHelp) uint256 blockId = 0; blockId.SetHex(params[0].get_str()); - pindex = CBlockLocator(blockId).GetBlockIndex(); + std::map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(blockId); + if (it != mapBlockIndex.end()) + pindex = it->second; } if (params.size() > 1) @@ -1176,7 +1182,7 @@ Value listsinceblock(const Array& params, bool fHelp) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); } - int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1; + int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1; Array transactions; @@ -1188,23 +1194,8 @@ Value listsinceblock(const Array& params, bool fHelp) ListTransactions(tx, "*", 0, true, transactions); } - uint256 lastblock; - - if (target_confirms == 1) - { - lastblock = hashBestChain; - } - else - { - int target_height = pindexBest->nHeight + 1 - target_confirms; - - CBlockIndex *block; - for (block = pindexBest; - block && block->nHeight > target_height; - block = block->pprev) { } - - lastblock = block ? block->GetBlockHash() : 0; - } + CBlockIndex *pblockLast = chainActive[chainActive.Height() + 1 - target_confirms]; + uint256 lastblock = pblockLast ? pblockLast->GetBlockHash() : 0; Object ret; ret.push_back(Pair("transactions", transactions)); @@ -1475,13 +1466,13 @@ Value validateaddress(const Array& params, bool fHelp) CTxDestination dest = address.Get(); string currentAddress = address.ToString(); ret.push_back(Pair("address", currentAddress)); - bool fMine = IsMine(*pwalletMain, dest); + bool fMine = pwalletMain ? IsMine(*pwalletMain, dest) : false; ret.push_back(Pair("ismine", fMine)); if (fMine) { Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest); ret.insert(ret.end(), detail.begin(), detail.end()); } - if (pwalletMain->mapAddressBook.count(dest)) + if (pwalletMain && pwalletMain->mapAddressBook.count(dest)) ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest].name)); } return ret; diff --git a/src/test/Makefile.am b/src/test/Makefile.am index bedc5d0c29..c3495095d9 100644 --- a/src/test/Makefile.am +++ b/src/test/Makefile.am @@ -2,7 +2,7 @@ include $(top_srcdir)/src/Makefile.include AM_CPPFLAGS = $(INCLUDES) -I$(top_builddir)/src/obj \ -I$(top_srcdir)/src/leveldb/include -I$(top_srcdir)/src/leveldb/helpers \ - -I$(top_srcdir)/src $(BOOST_INCLUDES) + -I$(top_srcdir)/src $(BOOST_INCLUDES) $(BDB_CPPFLAGS) AM_LDFLAGS = $(PTHREAD_CFLAGS) @@ -25,7 +25,7 @@ BUILT_SOURCES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.raw.h) # test_bitcoin binary # test_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(TESTDEFS) test_bitcoin_LDADD = $(LIBBITCOIN) $(LIBLEVELDB) $(LIBMEMENV) \ - $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) + $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(BDB_LIBS) test_bitcoin_SOURCES = accounting_tests.cpp alert_tests.cpp \ allocator_tests.cpp base32_tests.cpp base58_tests.cpp base64_tests.cpp \ bignum_tests.cpp bloom_tests.cpp canonical_tests.cpp checkblock_tests.cpp \ diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index eeeacb0ad4..67165760b2 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -65,10 +65,10 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) { CBlock *pblock = &pblocktemplate->block; // pointer for convenience pblock->nVersion = 1; - pblock->nTime = pindexBest->GetMedianTimePast()+1; + pblock->nTime = chainActive.Tip()->GetMedianTimePast()+1; pblock->vtx[0].vin[0].scriptSig = CScript(); pblock->vtx[0].vin[0].scriptSig.push_back(blockinfo[i].extranonce); - pblock->vtx[0].vin[0].scriptSig.push_back(pindexBest->nHeight); + pblock->vtx[0].vin[0].scriptSig.push_back(chainActive.Height()); pblock->vtx[0].vout[0].scriptPubKey = CScript(); if (txFirst.size() < 2) txFirst.push_back(new CTransaction(pblock->vtx[0])); @@ -193,14 +193,14 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) mempool.clear(); // subsidy changing - int nHeight = pindexBest->nHeight; - pindexBest->nHeight = 209999; + int nHeight = chainActive.Height(); + chainActive.Tip()->nHeight = 209999; BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); delete pblocktemplate; - pindexBest->nHeight = 210000; + chainActive.Tip()->nHeight = 210000; BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); delete pblocktemplate; - pindexBest->nHeight = nHeight; + chainActive.Tip()->nHeight = nHeight; BOOST_FOREACH(CTransaction *tx, txFirst) delete tx; diff --git a/src/txdb.cpp b/src/txdb.cpp index 0d2fdc2887..24ee8ec3e8 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -223,10 +223,6 @@ bool CBlockTreeDB::LoadBlockIndexGuts() pindexNew->nStatus = diskindex.nStatus; pindexNew->nTx = diskindex.nTx; - // Watch for genesis block - if (pindexGenesisBlock == NULL && diskindex.GetBlockHash() == Params().HashGenesisBlock()) - pindexGenesisBlock = pindexNew; - if (!pindexNew->CheckIndex()) return error("LoadBlockIndex() : CheckIndex failed: %s", pindexNew->ToString().c_str()); diff --git a/src/util.cpp b/src/util.cpp index 73428b4c22..cfaf5bdf8c 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -78,7 +78,6 @@ bool fPrintToConsole = false; bool fPrintToDebugger = false; bool fDaemon = false; bool fServer = false; -bool fCommandLine = false; string strMiscWarning; bool fNoListen = false; bool fLogTimestamps = false; diff --git a/src/util.h b/src/util.h index a216aacdc9..c3fb01dbf0 100644 --- a/src/util.h +++ b/src/util.h @@ -145,7 +145,6 @@ extern bool fPrintToConsole; extern bool fPrintToDebugger; extern bool fDaemon; extern bool fServer; -extern bool fCommandLine; extern std::string strMiscWarning; extern bool fNoListen; extern bool fLogTimestamps; diff --git a/src/wallet.cpp b/src/wallet.cpp index 26ffc71e19..0f0ce7e631 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -799,7 +799,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) // no need to read and scan block, if block was created before // our wallet birthday (as adjusted for block time variability) if (nTimeFirstKey && (pindex->nTime < (nTimeFirstKey - 7200))) { - pindex = pindex->GetNextInMainChain(); + pindex = chainActive.Next(pindex); continue; } @@ -810,7 +810,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) if (AddToWalletIfInvolvingMe(tx.GetHash(), tx, &block, fUpdate)) ret++; } - pindex = pindex->GetNextInMainChain(); + pindex = chainActive.Next(pindex); } } return ret; @@ -864,7 +864,7 @@ void CWallet::ReacceptWalletTransactions() if (fMissing) { // TODO: optimize this to scan just part of the block chain? - if (ScanForWalletTransactions(pindexGenesisBlock)) + if (ScanForWalletTransactions(chainActive.Genesis())) fRepeat = true; // Found missing transactions: re-do re-accept. } } @@ -1933,7 +1933,7 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64> &mapKeyBirth) const { mapKeyBirth[it->first] = it->second.nCreateTime; // map in which we'll infer heights of other keys - CBlockIndex *pindexMax = FindBlockByHeight(std::max(0, nBestHeight - 144)); // the tip can be reorganised; use a 144-block safety margin + CBlockIndex *pindexMax = chainActive[std::max(0, chainActive.Height() - 144)]; // the tip can be reorganised; use a 144-block safety margin std::map<CKeyID, CBlockIndex*> mapKeyFirstBlock; std::set<CKeyID> setKeys; GetKeys(setKeys); @@ -1953,7 +1953,7 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64> &mapKeyBirth) const { // iterate over all wallet transactions... const CWalletTx &wtx = (*it).second; std::map<uint256, CBlockIndex*>::const_iterator blit = mapBlockIndex.find(wtx.hashBlock); - if (blit != mapBlockIndex.end() && blit->second->IsInMainChain()) { + if (blit != mapBlockIndex.end() && chainActive.Contains(blit->second)) { // ... which are already in a block int nHeight = blit->second->nHeight; BOOST_FOREACH(const CTxOut &txout, wtx.vout) { |