diff options
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | bitcoin-qt.pro | 9 | ||||
-rw-r--r-- | contrib/homebrew/makefile.osx.patch | 47 | ||||
-rw-r--r-- | doc/build-osx.md | 173 | ||||
-rw-r--r-- | doc/build-osx.txt | 54 | ||||
-rw-r--r-- | share/qt/Info.plist | 31 | ||||
-rw-r--r-- | src/bitcoinrpc.cpp | 2 | ||||
-rw-r--r-- | src/checkpoints.cpp | 75 | ||||
-rw-r--r-- | src/checkpoints.h | 2 | ||||
-rw-r--r-- | src/init.cpp | 3 | ||||
-rw-r--r-- | src/main.cpp | 45 | ||||
-rw-r--r-- | src/main.h | 35 | ||||
-rw-r--r-- | src/qt/bitcoin.cpp | 33 | ||||
-rw-r--r-- | src/qt/bitcoingui.cpp | 84 | ||||
-rw-r--r-- | src/qt/bitcoingui.h | 2 | ||||
-rw-r--r-- | src/qt/clientmodel.cpp | 6 | ||||
-rw-r--r-- | src/qt/clientmodel.h | 1 | ||||
-rw-r--r-- | src/qt/guiconstants.h | 2 | ||||
-rw-r--r-- | src/qt/paymentserver.cpp | 159 | ||||
-rw-r--r-- | src/qt/paymentserver.h | 66 | ||||
-rw-r--r-- | src/qt/qtipcserver.cpp | 165 | ||||
-rw-r--r-- | src/qt/qtipcserver.h | 16 | ||||
-rw-r--r-- | src/rpcwallet.cpp | 1 | ||||
-rw-r--r-- | src/test/util_tests.cpp | 62 | ||||
-rw-r--r-- | src/txdb.cpp | 6 | ||||
-rw-r--r-- | src/txdb.h | 6 | ||||
-rw-r--r-- | src/util.cpp | 80 | ||||
-rw-r--r-- | src/util.h | 28 | ||||
-rw-r--r-- | src/wallet.cpp | 21 |
29 files changed, 848 insertions, 368 deletions
@@ -66,7 +66,7 @@ Unit tests for the GUI code are in `src/qt/test/`. To compile and run them: qmake BITCOIN_QT_TEST=1 -o Makefile.test bitcoin-qt.pro make -f Makefile.test - ./Bitcoin-Qt + ./bitcoin-qt_test Every pull request is built for both Windows and Linux on a dedicated server, and unit and sanity tests are automatically run. The binaries produced may be diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index c527b76dad..5042adfbdf 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -1,7 +1,9 @@ TEMPLATE = app TARGET = bitcoin-qt +macx:TARGET = "Bitcoin-Qt" VERSION = 0.8.0 INCLUDEPATH += src src/json src/qt +QT += network DEFINES += QT_GUI BOOST_THREAD_USE_LIB BOOST_SPIRIT_THREADSAFE CONFIG += no_include_pwd CONFIG += thread @@ -195,7 +197,7 @@ HEADERS += src/qt/bitcoingui.h \ src/qt/askpassphrasedialog.h \ src/protocol.h \ src/qt/notificator.h \ - src/qt/qtipcserver.h \ + src/qt/paymentserver.h \ src/allocators.h \ src/ui_interface.h \ src/qt/rpcconsole.h \ @@ -265,7 +267,7 @@ SOURCES += src/qt/bitcoin.cpp \ src/qt/askpassphrasedialog.cpp \ src/protocol.cpp \ src/qt/notificator.cpp \ - src/qt/qtipcserver.cpp \ + src/qt/paymentserver.cpp \ src/qt/rpcconsole.cpp \ src/noui.cpp \ src/leveldb.cpp \ @@ -299,6 +301,7 @@ DEPENDPATH += src/qt/test QT += testlib TARGET = bitcoin-qt_test DEFINES += BITCOIN_QT_TEST + macx: CONFIG -= app_bundle } CODECFORTR = UTF-8 @@ -380,10 +383,10 @@ macx:OBJECTIVE_SOURCES += src/qt/macdockiconhandler.mm macx:LIBS += -framework Foundation -framework ApplicationServices -framework AppKit macx:DEFINES += MAC_OSX MSG_NOSIGNAL=0 macx:ICON = src/qt/res/icons/bitcoin.icns -macx:TARGET = "Bitcoin-Qt" macx:QMAKE_CFLAGS_THREAD += -pthread macx:QMAKE_LFLAGS_THREAD += -pthread macx:QMAKE_CXXFLAGS_THREAD += -pthread +macx:QMAKE_INFO_PLIST = share/qt/Info.plist # Set libraries and includes at end, to use platform-defined defaults if not overridden INCLUDEPATH += $$BOOST_INCLUDE_PATH $$BDB_INCLUDE_PATH $$OPENSSL_INCLUDE_PATH $$QRENCODE_INCLUDE_PATH diff --git a/contrib/homebrew/makefile.osx.patch b/contrib/homebrew/makefile.osx.patch new file mode 100644 index 0000000000..340de0efdf --- /dev/null +++ b/contrib/homebrew/makefile.osx.patch @@ -0,0 +1,47 @@ +diff --git a/src/makefile.osx b/src/makefile.osx +index 8b7c559..8a0560c 100644 +--- a/src/makefile.osx ++++ b/src/makefile.osx +@@ -7,17 +7,21 @@ + # Originally by Laszlo Hanyecz (solar@heliacal.net) + + CXX=llvm-g++ +-DEPSDIR=/opt/local ++DEPSDIR=/usr/local ++DB4DIR=/usr/local/opt/berkeley-db4 ++OPENSSLDIR=/usr/local/opt/openssl + + INCLUDEPATHS= \ + -I"$(CURDIR)" \ +- -I"$(CURDIR)"/obj \ ++ -I"$(CURDIR)/obj" \ + -I"$(DEPSDIR)/include" \ +- -I"$(DEPSDIR)/include/db48" ++ -I"$(DB4DIR)/include" \ ++ -I"$(OPENSSLDIR)/include" + + LIBPATHS= \ + -L"$(DEPSDIR)/lib" \ +- -L"$(DEPSDIR)/lib/db48" ++ -L"$(DB4DIR)/lib" \ ++ -L"$(OPENSSLDIR)/lib" + + USE_UPNP:=1 + USE_IPV6:=1 +@@ -31,13 +35,13 @@ ifdef STATIC + TESTLIBS += \ + $(DEPSDIR)/lib/libboost_unit_test_framework-mt.a + LIBS += \ +- $(DEPSDIR)/lib/db48/libdb_cxx-4.8.a \ ++ $(DB4DIR)/lib/libdb_cxx-4.8.a \ + $(DEPSDIR)/lib/libboost_system-mt.a \ + $(DEPSDIR)/lib/libboost_filesystem-mt.a \ + $(DEPSDIR)/lib/libboost_program_options-mt.a \ + $(DEPSDIR)/lib/libboost_thread-mt.a \ +- $(DEPSDIR)/lib/libssl.a \ +- $(DEPSDIR)/lib/libcrypto.a \ ++ $(OPENSSLDIR)/lib/libssl.a \ ++ $(OPENSSLDIR)/lib/libcrypto.a \ + -lz + else + TESTLIBS += \ diff --git a/doc/build-osx.md b/doc/build-osx.md new file mode 100644 index 0000000000..1ed593d5eb --- /dev/null +++ b/doc/build-osx.md @@ -0,0 +1,173 @@ +Mac OS X bitcoind build instructions +==================================== + +Authors +------- + +* Laszlo Hanyecz <solar@heliacal.net> +* Douglas Huff <dhuff@jrbobdobbs.org> +* Colin Dean <cad@cad.cx> +* Gavin Andresen <gavinandresen@gmail.com> + +License +------- + +Copyright (c) 2009-2012 Bitcoin Developers + +Distributed under the MIT/X11 software license, see the accompanying +file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in +the OpenSSL Toolkit (http://www.openssl.org/). + +This product includes cryptographic software written by +Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + +Notes +----- + +See `doc/readme-qt.rst` for instructions on building Bitcoin-Qt, the +graphical user interface. + +Tested on OS X 10.5 through 10.8 on Intel processors only. PPC is not +supported because it is big-endian. + +All of the commands should be executed in a Terminal application. The +built-in one is located in `/Applications/Utilities`. + +Preparation +----------- + +You need to install XCode with all the options checked so that the compiler +and everything is available in /usr not just /Developer. XCode should be +available on your OS X installation media, but if not, you can get the +current version from https://developer.apple.com/xcode/. If you install +Xcode 4.3 or later, you'll need to install its command line tools. This can +be done in `Xcode > Preferences > Downloads > Components` and generally must +be re-done or updated every time Xcode is updated. + +There's an assumption that you already have `git` installed, as well. If +not, it's the path of least resistance to install Github for Mac +(OS X 10.7+) or +[Git for OS X](https://code.google.com/p/git-osx-installer/). It is also +available via Homebrew or MacPorts. + +You will also need to install [Homebrew](http://mxcl.github.com/homebrew/) +or [MacPorts](http://www.macports.org/) in order to install library +dependencies. It's largely a religious decision which to choose, but, as of +December 2012, MacPorts is a little easier because you can just install the +dependencies immediately - no other work required. If you're unsure, read +the instructions through first in order to assess what you want to do. +Homebrew is a little more popular among those newer to OS X. + +The installation of the actual dependencies is covered in the Instructions +sections below. + +Instructions: MacPorts +---------------------- + +### Install dependencies + +Installing the dependencies using MacPorts is very straightforward. + + sudo port install boost db48@+no_java openssl miniupnpc + +### Building `bitcoind` + +1. Clone the github tree to get the source code and go into the directory. + + git clone git@github.com:bitcoin/bitcoin.git bitcoin + cd bitcoin + +2. Build bitcoind: + + cd src + make -f makefile.osx + +3. It is a good idea to build and run the unit tests, too: + + make -f makefile.osx test + +Instructions: HomeBrew +---------------------- + +#### Install dependencies using Homebrew + + brew install boost miniupnpc openssl berkeley-db4 + +### Building `bitcoind` + +1. Clone the github tree to get the source code and go into the directory. + + git clone git@github.com:bitcoin/bitcoin.git bitcoin + cd bitcoin + +2. Modify source in order to pick up the `openssl` library. + + Edit `makefile.osx` to account for library location differences. There's a + diff in `contrib/homebrew/makefile.osx.patch` that shows what you need to + change, or you can just patch by doing + + patch -p1 < contrib/homebrew/makefile.osx.patch + +3. Build bitcoind: + + cd src + make -f makefile.osx + +4. It is a good idea to build and run the unit tests, too: + + make -f makefile.osx test + +Creating a release build +------------------------ + +A bitcoind binary is not included in the Bitcoin-Qt.app bundle. You can ignore +this section if you are building `bitcoind` for your own use. + +If you are building `bitcoind` for others, your build machine should be set up +as follows for maximum compatibility: + +All dependencies should be compiled with these flags: + + -mmacosx-version-min=10.5 -arch i386 -isysroot /Developer/SDKs/MacOSX10.5.sdk + +For MacPorts, that means editing your macports.conf and setting +`macosx_deployment_target` and `build_arch`: + + macosx_deployment_target=10.5 + build_arch=i386 + +... and then uninstalling and re-installing, or simply rebuilding, all ports. + +As of December 2012, the `boost` port does not obey `macosx_deployment_target`. +Download `http://gavinandresen-bitcoin.s3.amazonaws.com/boost_macports_fix.zip` +for a fix. Some ports also seem to obey either `build_arch` or +`macosx_deployment_target`, but not both at the same time. For example, building +on an OS X 10.6 64-bit machine fails. Official release builds of Bitcoin-Qt are +compiled on an OS X 10.6 32-bit machine to workaround that problem. + +Once dependencies are compiled, creating `Bitcoin-Qt.app` is easy: + + make -f Makefile.osx RELEASE=1 + +Running +------- + +It's now available at `./bitcoind`, provided that you are still in the `src` +directory. We have to first create the RPC configuration file, though. + +Run `./bitcoind` to get the filename where it should be put, or just try these +commands: + + echo -e "rpcuser=bitcoinrpc\nrpcpassword=$(xxd -l 16 -p /dev/urandom)" > "/Users/${USER}/Library/Application Support/Bitcoin/bitcoin.conf" + chmod 600 "/Users/${USER}/Library/Application Support/Bitcoin/bitcoin.conf" + +When next you run it, it will start downloading the blockchain, but it won't +output anything while it's doing this. This process may take several hours. + +Other commands: + + ./bitcoind --help # for a list of command-line options. + ./bitcoind -daemon # to start the bitcoin daemon. + ./bitcoind help # When the daemon is running, to get a list of RPC commands diff --git a/doc/build-osx.txt b/doc/build-osx.txt deleted file mode 100644 index fd878043c0..0000000000 --- a/doc/build-osx.txt +++ /dev/null @@ -1,54 +0,0 @@ -Copyright (c) 2009-2012 Bitcoin Developers -Distributed under the MIT/X11 software license, see the accompanying -file COPYING or http://www.opensource.org/licenses/mit-license.php. -This product includes software developed by the OpenSSL Project for use in -the OpenSSL Toolkit (http://www.openssl.org/). This product includes -cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP -software written by Thomas Bernard. - - -Mac OS X bitcoind build instructions -Laszlo Hanyecz <solar@heliacal.net> -Douglas Huff <dhuff@jrbobdobbs.org> - - -See readme-qt.rst for instructions on building Bitcoin-Qt, the -graphical user interface. - -Tested on 10.5, 10.6 and 10.7 intel. PPC is not supported because it's big-endian. - -All of the commands should be executed in Terminal.app.. it's in -/Applications/Utilities - -You need to install XCode with all the options checked so that the compiler and -everything is available in /usr not just /Developer. XCode should be available on your OS X -install DVD, but if not, you can get the current version from https://developer.apple.com/xcode/ - - -1. Clone the github tree to get the source code: - -git clone git@github.com:bitcoin/bitcoin.git bitcoin - -2. Download and install MacPorts from http://www.macports.org/ - -2a. (for 10.7 Lion) - Edit /opt/local/etc/macports/macports.conf and uncomment "build_arch i386" - -3. Install dependencies from MacPorts - -sudo port install boost db48 openssl miniupnpc - -Optionally install qrencode (and set USE_QRCODE=1): -sudo port install qrencode - -4. Now you should be able to build bitcoind: - -cd bitcoin/src -make -f makefile.osx - -Run: - ./bitcoind --help # for a list of command-line options. -Run - ./bitcoind -daemon # to start the bitcoin daemon. -Run - ./bitcoind help # When the daemon is running, to get a list of RPC commands diff --git a/share/qt/Info.plist b/share/qt/Info.plist new file mode 100644 index 0000000000..58b2152e9f --- /dev/null +++ b/share/qt/Info.plist @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd"> +<plist version="0.9"> +<dict> + <key>CFBundleIconFile</key> + <string>bitcoin.icns</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleGetInfoString</key> + <string>Bitcoin-Qt</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleExecutable</key> + <string>Bitcoin-Qt</string> + <key>CFBundleIdentifier</key> + <string>org.bitcoinfoundation.Bitcoin-Qt</string> + <key>CFBundleURLTypes</key> + <array> + <dict> + <key>CFBundleTypeRole</key> + <string>Editor</string> + <key>CFBundleURLName</key> + <string>org.bitcoinfoundation.BitcoinPayment</string> + <key>CFBundleURLSchemes</key> + <array> + <string>bitcoin</string> + </array> + </dict> + </array> +</dict> +</plist> diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 7751e4c8b6..230a248422 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -246,7 +246,7 @@ static const CRPCCommand vRPCCommands[] = { "getblocktemplate", &getblocktemplate, true, false }, { "submitblock", &submitblock, false, false }, { "listsinceblock", &listsinceblock, false, false }, - { "dumpprivkey", &dumpprivkey, false, false }, + { "dumpprivkey", &dumpprivkey, true, false }, { "importprivkey", &importprivkey, false, false }, { "listunspent", &listunspent, false, false }, { "getrawtransaction", &getrawtransaction, false, false }, diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index e2c420edd7..9b11f0b351 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -14,13 +14,25 @@ namespace Checkpoints { typedef std::map<int, uint256> MapCheckpoints; - // + // How many times we expect transactions after the last checkpoint to + // be slower. This number is conservative. On multi-core CPUs with + // parallel signature checking enabled, this number is way too high. + // We prefer a progressbar that's faster at the end than the other + // way around, though. + static const double fSigcheckVerificationFactor = 15.0; + + struct CCheckpointData { + const MapCheckpoints *mapCheckpoints; + int64 nTimeLastCheckpoint; + int64 nTransactionsLastCheckpoint; + double fTransactionsPerDay; + }; + // What makes a good checkpoint block? // + Is surrounded by blocks with reasonable timestamps // (no blocks before with a timestamp after, none after with // timestamp before) // + Contains no strange transactions - // static MapCheckpoints mapCheckpoints = boost::assign::map_list_of ( 11111, uint256("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")) @@ -33,30 +45,81 @@ namespace Checkpoints (210000, uint256("0x000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e")) (216116, uint256("0x00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e")) ; + static const CCheckpointData data = { + &mapCheckpoints, + 1357902690, // * UNIX timestamp of last checkpoint block + 11011160, // * total number of transactions between genesis and last checkpoint + // (the tx=... number in the SetBestChain debug.log lines) + 50000.0 // * estimated number of transactions per day after checkpoint + }; - static MapCheckpoints mapCheckpointsTestnet = + static MapCheckpoints mapCheckpointsTestnet = boost::assign::map_list_of ( 546, uint256("000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70")) ; + static const CCheckpointData dataTestnet = { + &mapCheckpointsTestnet, + 1338180505, + 16341, + 300 + }; + + const CCheckpointData &Checkpoints() { + if (fTestNet) + return dataTestnet; + else + return data; + } bool CheckBlock(int nHeight, const uint256& hash) { if (!GetBoolArg("-checkpoints", true)) return true; - MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints); + const MapCheckpoints& checkpoints = *Checkpoints().mapCheckpoints; MapCheckpoints::const_iterator i = checkpoints.find(nHeight); if (i == checkpoints.end()) return true; return hash == i->second; } + // Guess how far we are in the verification process at the given block index + double GuessVerificationProgress(CBlockIndex *pindex) { + if (pindex==NULL) + return 0.0; + + int64 nNow = time(NULL); + + double fWorkBefore = 0.0; // Amount of work done before pindex + double fWorkAfter = 0.0; // Amount of work left after pindex (estimated) + // Work is defined as: 1.0 per transaction before the last checkoint, and + // fSigcheckVerificationFactor per transaction after. + + const CCheckpointData &data = Checkpoints(); + + if (pindex->nChainTx <= data.nTransactionsLastCheckpoint) { + double nCheapBefore = pindex->nChainTx; + double nCheapAfter = data.nTransactionsLastCheckpoint - pindex->nChainTx; + double nExpensiveAfter = (nNow - data.nTimeLastCheckpoint)/86400.0*data.fTransactionsPerDay; + fWorkBefore = nCheapBefore; + fWorkAfter = nCheapAfter + nExpensiveAfter*fSigcheckVerificationFactor; + } else { + double nCheapBefore = data.nTransactionsLastCheckpoint; + double nExpensiveBefore = pindex->nChainTx - data.nTransactionsLastCheckpoint; + double nExpensiveAfter = (nNow - pindex->nTime)/86400.0*data.fTransactionsPerDay; + fWorkBefore = nCheapBefore + nExpensiveBefore*fSigcheckVerificationFactor; + fWorkAfter = nExpensiveAfter*fSigcheckVerificationFactor; + } + + return fWorkBefore / (fWorkBefore + fWorkAfter); + } + int GetTotalBlocksEstimate() { if (!GetBoolArg("-checkpoints", true)) return 0; - MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints); + const MapCheckpoints& checkpoints = *Checkpoints().mapCheckpoints; return checkpoints.rbegin()->first; } @@ -66,7 +129,7 @@ namespace Checkpoints if (!GetBoolArg("-checkpoints", true)) return NULL; - MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints); + const MapCheckpoints& checkpoints = *Checkpoints().mapCheckpoints; BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, checkpoints) { diff --git a/src/checkpoints.h b/src/checkpoints.h index 240bd12fde..3d56885556 100644 --- a/src/checkpoints.h +++ b/src/checkpoints.h @@ -22,6 +22,8 @@ namespace Checkpoints // Returns last CBlockIndex* in mapBlockIndex that is a checkpoint CBlockIndex* GetLastCheckpoint(const std::map<uint256, CBlockIndex*>& mapBlockIndex); + + double GuessVerificationProgress(CBlockIndex *pindex); } #endif diff --git a/src/init.cpp b/src/init.cpp index cf49831b6b..5b8436651a 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -300,6 +300,7 @@ std::string HelpMessage() " -rpcallowip=<ip> " + _("Allow JSON-RPC connections from specified IP address") + "\n" + " -rpcconnect=<ip> " + _("Send commands to node running on <ip> (default: 127.0.0.1)") + "\n" + " -blocknotify=<cmd> " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n" + + " -walletnotify=<cmd> " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n" + " -upgradewallet " + _("Upgrade wallet to latest format") + "\n" + " -keypool=<n> " + _("Set key pool size to <n> (default: 100)") + "\n" + " -rescan " + _("Rescan the block chain for missing wallet transactions") + "\n" + @@ -825,7 +826,7 @@ bool AppInit2() break; } - uiInterface.InitMessage(_("Verifying block database integrity...")); + uiInterface.InitMessage(_("Verifying database...")); if (!VerifyDB()) { strLoadError = _("Corrupted block database detected"); break; diff --git a/src/main.cpp b/src/main.cpp index 9fde08b2c8..9a06dbf13e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -162,9 +162,9 @@ void static ResendWalletTransactions() // CCoinsView implementations // -bool CCoinsView::GetCoins(uint256 txid, CCoins &coins) { return false; } -bool CCoinsView::SetCoins(uint256 txid, const CCoins &coins) { return false; } -bool CCoinsView::HaveCoins(uint256 txid) { return false; } +bool CCoinsView::GetCoins(const uint256 &txid, CCoins &coins) { return false; } +bool CCoinsView::SetCoins(const uint256 &txid, const CCoins &coins) { return false; } +bool CCoinsView::HaveCoins(const uint256 &txid) { return false; } CBlockIndex *CCoinsView::GetBestBlock() { return NULL; } bool CCoinsView::SetBestBlock(CBlockIndex *pindex) { return false; } bool CCoinsView::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) { return false; } @@ -172,9 +172,9 @@ bool CCoinsView::GetStats(CCoinsStats &stats) { return false; } CCoinsViewBacked::CCoinsViewBacked(CCoinsView &viewIn) : base(&viewIn) { } -bool CCoinsViewBacked::GetCoins(uint256 txid, CCoins &coins) { return base->GetCoins(txid, coins); } -bool CCoinsViewBacked::SetCoins(uint256 txid, const CCoins &coins) { return base->SetCoins(txid, coins); } -bool CCoinsViewBacked::HaveCoins(uint256 txid) { return base->HaveCoins(txid); } +bool CCoinsViewBacked::GetCoins(const uint256 &txid, CCoins &coins) { return base->GetCoins(txid, coins); } +bool CCoinsViewBacked::SetCoins(const uint256 &txid, const CCoins &coins) { return base->SetCoins(txid, coins); } +bool CCoinsViewBacked::HaveCoins(const uint256 &txid) { return base->HaveCoins(txid); } CBlockIndex *CCoinsViewBacked::GetBestBlock() { return base->GetBestBlock(); } bool CCoinsViewBacked::SetBestBlock(CBlockIndex *pindex) { return base->SetBestBlock(pindex); } void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; } @@ -183,7 +183,7 @@ bool CCoinsViewBacked::GetStats(CCoinsStats &stats) { return base->GetStats(stat CCoinsViewCache::CCoinsViewCache(CCoinsView &baseIn, bool fDummy) : CCoinsViewBacked(baseIn), pindexTip(NULL) { } -bool CCoinsViewCache::GetCoins(uint256 txid, CCoins &coins) { +bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) { if (cacheCoins.count(txid)) { coins = cacheCoins[txid]; return true; @@ -195,29 +195,30 @@ bool CCoinsViewCache::GetCoins(uint256 txid, CCoins &coins) { return false; } -std::map<uint256,CCoins>::iterator CCoinsViewCache::FetchCoins(uint256 txid) { - std::map<uint256,CCoins>::iterator it = cacheCoins.find(txid); - if (it != cacheCoins.end()) +std::map<uint256,CCoins>::iterator CCoinsViewCache::FetchCoins(const uint256 &txid) { + std::map<uint256,CCoins>::iterator it = cacheCoins.lower_bound(txid); + if (it != cacheCoins.end() && it->first == txid) return it; CCoins tmp; if (!base->GetCoins(txid,tmp)) - return it; - std::pair<std::map<uint256,CCoins>::iterator,bool> ret = cacheCoins.insert(std::make_pair(txid, tmp)); - return ret.first; + return cacheCoins.end(); + std::map<uint256,CCoins>::iterator ret = cacheCoins.insert(it, std::make_pair(txid, CCoins())); + tmp.swap(ret->second); + return ret; } -CCoins &CCoinsViewCache::GetCoins(uint256 txid) { +CCoins &CCoinsViewCache::GetCoins(const uint256 &txid) { std::map<uint256,CCoins>::iterator it = FetchCoins(txid); assert(it != cacheCoins.end()); return it->second; } -bool CCoinsViewCache::SetCoins(uint256 txid, const CCoins &coins) { +bool CCoinsViewCache::SetCoins(const uint256 &txid, const CCoins &coins) { cacheCoins[txid] = coins; return true; } -bool CCoinsViewCache::HaveCoins(uint256 txid) { +bool CCoinsViewCache::HaveCoins(const uint256 &txid) { return FetchCoins(txid) != cacheCoins.end(); } @@ -254,7 +255,7 @@ unsigned int CCoinsViewCache::GetCacheSize() { It does not check for spendings by memory pool transactions. */ CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { } -bool CCoinsViewMemPool::GetCoins(uint256 txid, CCoins &coins) { +bool CCoinsViewMemPool::GetCoins(const uint256 &txid, CCoins &coins) { if (base->GetCoins(txid, coins)) return true; if (mempool.exists(txid)) { @@ -265,7 +266,7 @@ bool CCoinsViewMemPool::GetCoins(uint256 txid, CCoins &coins) { return false; } -bool CCoinsViewMemPool::HaveCoins(uint256 txid) { +bool CCoinsViewMemPool::HaveCoins(const uint256 &txid) { return mempool.exists(txid) || base->HaveCoins(txid); } @@ -1532,7 +1533,7 @@ bool CBlock::DisconnectBlock(CValidationState &state, CBlockIndex *pindex, CCoin } } -void static FlushBlockFile() +void static FlushBlockFile(bool fFinalize = false) { LOCK(cs_LastBlockFile); @@ -1540,12 +1541,16 @@ void static FlushBlockFile() FILE *fileOld = OpenBlockFile(posOld); if (fileOld) { + if (fFinalize) + TruncateFile(fileOld, infoLastBlockFile.nSize); FileCommit(fileOld); fclose(fileOld); } fileOld = OpenUndoFile(posOld); if (fileOld) { + if (fFinalize) + TruncateFile(fileOld, infoLastBlockFile.nUndoSize); FileCommit(fileOld); fclose(fileOld); } @@ -1963,7 +1968,7 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd } else { while (infoLastBlockFile.nSize + nAddSize >= MAX_BLOCKFILE_SIZE) { printf("Leaving block file %i: %s\n", nLastBlockFile, infoLastBlockFile.ToString().c_str()); - FlushBlockFile(); + FlushBlockFile(true); nLastBlockFile++; infoLastBlockFile.SetNull(); pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile); // check whether data for the new file somehow already exist; can fail just fine diff --git a/src/main.h b/src/main.h index e9601c3a44..43c3051e4c 100644 --- a/src/main.h +++ b/src/main.h @@ -908,6 +908,15 @@ public: void Cleanup() { while (vout.size() > 0 && vout.back().IsNull()) vout.pop_back(); + if (vout.empty()) + std::vector<CTxOut>().swap(vout); + } + + void swap(CCoins &to) { + std::swap(to.fCoinBase, fCoinBase); + to.vout.swap(vout); + std::swap(to.nHeight, nHeight); + std::swap(to.nVersion, nVersion); } // equality test @@ -2108,14 +2117,14 @@ class CCoinsView { public: // Retrieve the CCoins (unspent transaction outputs) for a given txid - virtual bool GetCoins(uint256 txid, CCoins &coins); + virtual bool GetCoins(const uint256 &txid, CCoins &coins); // Modify the CCoins for a given txid - virtual bool SetCoins(uint256 txid, const CCoins &coins); + virtual bool SetCoins(const uint256 &txid, const CCoins &coins); // Just check whether we have data for a given txid. // This may (but cannot always) return true for fully spent transactions - virtual bool HaveCoins(uint256 txid); + virtual bool HaveCoins(const uint256 &txid); // Retrieve the block index whose state this CCoinsView currently represents virtual CBlockIndex *GetBestBlock(); @@ -2141,9 +2150,9 @@ protected: public: CCoinsViewBacked(CCoinsView &viewIn); - bool GetCoins(uint256 txid, CCoins &coins); - bool SetCoins(uint256 txid, const CCoins &coins); - bool HaveCoins(uint256 txid); + bool GetCoins(const uint256 &txid, CCoins &coins); + bool SetCoins(const uint256 &txid, const CCoins &coins); + bool HaveCoins(const uint256 &txid); CBlockIndex *GetBestBlock(); bool SetBestBlock(CBlockIndex *pindex); void SetBackend(CCoinsView &viewIn); @@ -2162,9 +2171,9 @@ public: CCoinsViewCache(CCoinsView &baseIn, bool fDummy = false); // Standard CCoinsView methods - bool GetCoins(uint256 txid, CCoins &coins); - bool SetCoins(uint256 txid, const CCoins &coins); - bool HaveCoins(uint256 txid); + bool GetCoins(const uint256 &txid, CCoins &coins); + bool SetCoins(const uint256 &txid, const CCoins &coins); + bool HaveCoins(const uint256 &txid); CBlockIndex *GetBestBlock(); bool SetBestBlock(CBlockIndex *pindex); bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex); @@ -2172,7 +2181,7 @@ public: // Return a modifiable reference to a CCoins. Check HaveCoins first. // Many methods explicitly require a CCoinsViewCache because of this method, to reduce // copying. - CCoins &GetCoins(uint256 txid); + CCoins &GetCoins(const uint256 &txid); // Push the modifications applied to this cache to its base. // Failure to call this method before destruction will cause the changes to be forgotten. @@ -2182,7 +2191,7 @@ public: unsigned int GetCacheSize(); private: - std::map<uint256,CCoins>::iterator FetchCoins(uint256 txid); + std::map<uint256,CCoins>::iterator FetchCoins(const uint256 &txid); }; /** CCoinsView that brings transactions from a memorypool into view. @@ -2194,8 +2203,8 @@ protected: public: CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn); - bool GetCoins(uint256 txid, CCoins &coins); - bool HaveCoins(uint256 txid); + bool GetCoins(const uint256 &txid, CCoins &coins); + bool HaveCoins(const uint256 &txid); }; /** Global variable that points to the active CCoinsView (protected by cs_main) */ diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index afd8d71a0e..75e9b965b1 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -9,12 +9,13 @@ #include "guiconstants.h" #include "init.h" #include "ui_interface.h" -#include "qtipcserver.h" +#include "paymentserver.h" #include <QApplication> #include <QMessageBox> #include <QTextCodec> #include <QLocale> +#include <QTimer> #include <QTranslator> #include <QSplashScreen> #include <QLibraryInfo> @@ -74,15 +75,6 @@ static bool ThreadSafeAskFee(int64 nFeeRequired) return payFee; } -static void ThreadSafeHandleURI(const std::string& strURI) -{ - if(!guiref) - return; - - QMetaObject::invokeMethod(guiref, "handleURI", GUIUtil::blockingGUIThreadConnection(), - Q_ARG(QString, QString::fromStdString(strURI))); -} - static void InitMessage(const std::string &message) { if(splashref) @@ -121,14 +113,6 @@ int main(int argc, char *argv[]) // Command-line options take precedence: ParseParameters(argc, argv); - if(GetBoolArg("-testnet")) // Separate message queue name for testnet - strBitcoinURIQueueName = BITCOINURI_QUEUE_NAME_TESTNET; - else - strBitcoinURIQueueName = BITCOINURI_QUEUE_NAME_MAINNET; - - // Do this early as we don't want to bother initializing if we are just calling IPC - ipcScanRelay(argc, argv); - // Internal string conversion is all UTF-8 QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8")); QTextCodec::setCodecForCStrings(QTextCodec::codecForTr()); @@ -136,6 +120,12 @@ int main(int argc, char *argv[]) Q_INIT_RESOURCE(bitcoin); QApplication app(argc, argv); + // Do this early as we don't want to bother initializing if we are just calling IPC + // ... but do it after creating app, so QCoreApplication::arguments is initialized: + if (PaymentServer::ipcSendCommandLine()) + exit(0); + PaymentServer* paymentServer = new PaymentServer(&app); + // Install global event filter that makes sure that long tooltips can be word-wrapped app.installEventFilter(new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app)); @@ -192,7 +182,6 @@ int main(int argc, char *argv[]) // Subscribe to global signals from core uiInterface.ThreadSafeMessageBox.connect(ThreadSafeMessageBox); uiInterface.ThreadSafeAskFee.connect(ThreadSafeAskFee); - uiInterface.ThreadSafeHandleURI.connect(ThreadSafeHandleURI); uiInterface.InitMessage.connect(InitMessage); uiInterface.QueueShutdown.connect(QueueShutdown); uiInterface.Translate.connect(Translate); @@ -253,8 +242,10 @@ int main(int argc, char *argv[]) window.show(); } - // Place this here as guiref has to be defined if we don't want to lose URIs - ipcInit(argc, argv); + // Now that initialization/startup is done, process any command-line + // bitcoin: URIs + QObject::connect(paymentServer, SIGNAL(receivedURI(QString)), &window, SLOT(handleURI(QString))); + QTimer::singleShot(100, paymentServer, SLOT(uiReady())); app.exec(); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index d884701883..f1bf5f5880 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -67,7 +67,8 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): aboutQtAction(0), trayIcon(0), notificator(0), - rpcConsole(0) + rpcConsole(0), + prevBlocks(0) { resize(850, 550); setWindowTitle(tr("Bitcoin") + " - " + tr("Wallet")); @@ -527,52 +528,17 @@ void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks) importText = tr("Reindexing blocks on disk..."); } - if(count < nTotalBlocks) - { - int nRemainingBlocks = nTotalBlocks - count; - float nPercentageDone = count / (nTotalBlocks * 0.01f); - - progressBarLabel->setText(importText); - progressBarLabel->setVisible(true); - progressBar->setFormat(tr("~%n block(s) remaining", "", nRemainingBlocks)); - progressBar->setMaximum(nTotalBlocks); - progressBar->setValue(count); - progressBar->setVisible(true); - - tooltip = tr("Processed %1 of %2 blocks of transaction history (%3% done).").arg(count).arg(nTotalBlocks).arg(nPercentageDone, 0, 'f', 2); - } - else - { - progressBarLabel->setVisible(false); - - progressBar->setVisible(false); - tooltip = tr("Processed %1 blocks of transaction history.").arg(count); - } - QDateTime lastBlockDate = clientModel->getLastBlockDate(); - int secs = lastBlockDate.secsTo(QDateTime::currentDateTime()); - QString text; + QDateTime currentDate = QDateTime::currentDateTime(); + int secs = lastBlockDate.secsTo(currentDate); - // Represent time from last generated block in human readable text - if(secs <= 0) - { - // Fully up to date. Leave text empty. - } - else if(secs < 60) - { - text = tr("%n second(s) ago","",secs); - } - else if(secs < 60*60) - { - text = tr("%n minute(s) ago","",secs/60); - } - else if(secs < 24*60*60) + if(count < nTotalBlocks) { - text = tr("%n hour(s) ago","",secs/(60*60)); + tooltip = tr("Processed %1 of %2 (estimated) blocks of transaction history.").arg(count).arg(nTotalBlocks); } else { - text = tr("%n day(s) ago","",secs/(60*60*24)); + tooltip = tr("Processed %1 blocks of transaction history.").arg(count); } // Set icon state: spinning if catching up, tick otherwise @@ -582,20 +548,46 @@ void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks) labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); overviewPage->showOutOfSyncWarning(false); + + progressBarLabel->setVisible(false); + progressBar->setVisible(false); } else { + // Represent time from last generated block in human readable text + QString timeBehindText; + if(secs < 48*60*60) + { + timeBehindText = tr("%n hour(s)","",secs/(60*60)); + } + else if(secs < 14*24*60*60) + { + timeBehindText = tr("%n day(s)","",secs/(24*60*60)); + } + else + { + timeBehindText = tr("%n week(s)","",secs/(7*24*60*60)); + } + + progressBarLabel->setText(importText); + progressBarLabel->setVisible(true); + progressBar->setFormat(tr("%1 behind").arg(timeBehindText)); + progressBar->setMaximum(1000000000); + progressBar->setValue(clientModel->getVerificationProgress() * 1000000000.0 + 0.5); + progressBar->setVisible(true); + tooltip = tr("Catching up...") + QString("<br>") + tooltip; labelBlocksIcon->setMovie(syncIconMovie); - syncIconMovie->start(); + if(count != prevBlocks) + syncIconMovie->jumpToNextFrame(); + prevBlocks = count; overviewPage->showOutOfSyncWarning(true); - } - if(!text.isEmpty()) - { tooltip += QString("<br>"); - tooltip += tr("Last received block was generated %1.").arg(text); + tooltip += tr("Last received block was generated %1 ago.").arg(timeBehindText); + tooltip += QString("<br>"); + tooltip += tr("Transactions after this will not yet be visible."); } // Don't word-wrap this (fixed-width) tooltip diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index c684fcf249..8ce0335bcd 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -98,6 +98,8 @@ private: RPCConsole *rpcConsole; QMovie *syncIconMovie; + /** Keep track of previous number of blocks, to detect progress */ + int prevBlocks; /** Create the main UI actions. */ void createActions(); diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 084ad12a56..858fbe241f 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -6,6 +6,7 @@ #include "alert.h" #include "main.h" +#include "checkpoints.h" #include "ui_interface.h" #include <QDateTime> @@ -54,6 +55,11 @@ QDateTime ClientModel::getLastBlockDate() const return QDateTime::fromTime_t(1231006505); // Genesis block's time } +double ClientModel::getVerificationProgress() const +{ + return Checkpoints::GuessVerificationProgress(pindexBest); +} + void ClientModel::updateTimer() { // Some quantities (such as number of blocks) change so fast that we don't want to be notified for each change. diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 1afccb7859..a3fe92048c 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -34,6 +34,7 @@ public: int getNumBlocks() const; int getNumBlocksAtStartup(); + double getVerificationProgress() const; QDateTime getLastBlockDate() const; //! Return true if client connected to testnet diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index 405ba396b7..92417834ec 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -2,7 +2,7 @@ #define GUICONSTANTS_H /* Milliseconds between model updates */ -static const int MODEL_UPDATE_DELAY = 500; +static const int MODEL_UPDATE_DELAY = 250; /* AskPassphraseDialog -- Maximum passphrase length */ static const int MAX_PASSPHRASE_SIZE = 1024; diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp new file mode 100644 index 0000000000..05f2ac10e4 --- /dev/null +++ b/src/qt/paymentserver.cpp @@ -0,0 +1,159 @@ +// Copyright (c) 2009-2012 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 "paymentserver.h" +#include "guiconstants.h" +#include "ui_interface.h" +#include "util.h" + +#include <QApplication> +#include <QByteArray> +#include <QCoreApplication> +#include <QDataStream> +#include <QDebug> +#include <QFileOpenEvent> +#include <QHash> +#include <QLocalServer> +#include <QLocalSocket> +#include <QStringList> +#include <QUrl> + +using namespace boost; + +const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // milliseconds +const QString BITCOIN_IPC_PREFIX("bitcoin:"); + +// +// Create a name that is unique for: +// testnet / non-testnet +// data directory +// +static QString ipcServerName() +{ + QString name("BitcoinQt"); + + // Append a simple hash of the datadir + // Note that GetDataDir(true) returns a different path + // for -testnet versus main net + QString ddir(GetDataDir(true).string().c_str()); + name.append(QString::number(qHash(ddir))); + + return name; +} + +// +// This stores payment requests received before +// the main GUI window is up and ready to ask the user +// to send payment. +// +static QStringList savedPaymentRequests; + +// +// Sending to the server is done synchronously, at startup. +// If the server isn't already running, startup continues, +// and the items in savedPaymentRequest will be handled +// when uiReady() is called. +// +bool PaymentServer::ipcSendCommandLine() +{ + bool fResult = false; + + const QStringList& args = QCoreApplication::arguments(); + for (int i = 1; i < args.size(); i++) + { + if (!args[i].startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) + continue; + savedPaymentRequests.append(args[i]); + } + + foreach (const QString& arg, savedPaymentRequests) + { + QLocalSocket* socket = new QLocalSocket(); + socket->connectToServer(ipcServerName(), QIODevice::WriteOnly); + if (!socket->waitForConnected(BITCOIN_IPC_CONNECT_TIMEOUT)) + return false; + + QByteArray block; + QDataStream out(&block, QIODevice::WriteOnly); + out.setVersion(QDataStream::Qt_4_0); + out << arg; + out.device()->seek(0); + socket->write(block); + socket->flush(); + + socket->waitForBytesWritten(BITCOIN_IPC_CONNECT_TIMEOUT); + socket->disconnectFromServer(); + delete socket; + fResult = true; + } + return fResult; +} + +PaymentServer::PaymentServer(QApplication* parent) : QObject(parent), saveURIs(true) +{ + // Install global event filter to catch QFileOpenEvents on the mac (sent when you click bitcoin: links) + parent->installEventFilter(this); + + QString name = ipcServerName(); + + // Clean up old socket leftover from a crash: + QLocalServer::removeServer(name); + + uriServer = new QLocalServer(this); + + if (!uriServer->listen(name)) + qDebug() << tr("Cannot start bitcoin: click-to-pay handler"); + else + connect(uriServer, SIGNAL(newConnection()), this, SLOT(handleURIConnection())); +} + +bool PaymentServer::eventFilter(QObject *object, QEvent *event) +{ + // clicking on bitcoin: URLs creates FileOpen events on the Mac: + if (event->type() == QEvent::FileOpen) + { + QFileOpenEvent* fileEvent = static_cast<QFileOpenEvent*>(event); + if (!fileEvent->url().isEmpty()) + { + if (saveURIs) // Before main window is ready: + savedPaymentRequests.append(fileEvent->url().toString()); + else + emit receivedURI(fileEvent->url().toString()); + return true; + } + } + return false; +} + +void PaymentServer::uiReady() +{ + saveURIs = false; + foreach (const QString& s, savedPaymentRequests) + emit receivedURI(s); + savedPaymentRequests.clear(); +} + +void PaymentServer::handleURIConnection() +{ + QLocalSocket *clientConnection = uriServer->nextPendingConnection(); + + while (clientConnection->bytesAvailable() < (int)sizeof(quint32)) + clientConnection->waitForReadyRead(); + + connect(clientConnection, SIGNAL(disconnected()), + clientConnection, SLOT(deleteLater())); + + QDataStream in(clientConnection); + in.setVersion(QDataStream::Qt_4_0); + if (clientConnection->bytesAvailable() < (int)sizeof(quint16)) { + return; + } + QString message; + in >> message; + + if (saveURIs) + savedPaymentRequests.append(message); + else + emit receivedURI(message); +} diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h new file mode 100644 index 0000000000..cfc48afb38 --- /dev/null +++ b/src/qt/paymentserver.h @@ -0,0 +1,66 @@ +#ifndef PAYMENTSERVER_H +#define PAYMENTSERVER_H + +// +// This class handles payment requests from clicking on +// bitcoin: URIs +// +// This is somewhat tricky, because we have to deal with +// the situation where the user clicks on a link during +// startup/initialization, when the splash-screen is up +// but the main window (and the Send Coins tab) is not. +// +// So, the strategy is: +// +// Create the server, and register the event handler, +// when the application is created. Save any URIs +// received at or during startup in a list. +// +// When startup is finished and the main window is +// show, a signal is sent to slot uiReady(), which +// emits a receivedURL() signal for any payment +// requests that happened during startup. +// +// After startup, receivedURL() happens as usual. +// +// This class has one more feature: a static +// method that finds URIs passed in the command line +// and, if a server is running in another process, +// sends them to the server. +// +#include <QObject> +#include <QString> + +class QApplication; +class QLocalServer; + +class PaymentServer : public QObject +{ + Q_OBJECT +private: + bool saveURIs; + QLocalServer* uriServer; + +public: + // Returns true if there were URIs on the command line + // which were successfully sent to an already-running + // process. + static bool ipcSendCommandLine(); + + PaymentServer(QApplication* parent); + + bool eventFilter(QObject *object, QEvent *event); + +signals: + void receivedURI(QString); + +public slots: + // Signal this when the main window's UI is ready + // to display payment requests to the user + void uiReady(); + +private slots: + void handleURIConnection(); +}; + +#endif // PAYMENTSERVER_H diff --git a/src/qt/qtipcserver.cpp b/src/qt/qtipcserver.cpp deleted file mode 100644 index 2777fab852..0000000000 --- a/src/qt/qtipcserver.cpp +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright (c) 2009-2012 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 <boost/version.hpp> -#if defined(WIN32) && BOOST_VERSION == 104900 -#define BOOST_INTERPROCESS_HAS_WINDOWS_KERNEL_BOOTTIME -#define BOOST_INTERPROCESS_HAS_KERNEL_BOOTTIME -#endif - -#include "qtipcserver.h" -#include "guiconstants.h" -#include "ui_interface.h" -#include "util.h" - -#include <boost/algorithm/string/predicate.hpp> -#include <boost/date_time/posix_time/posix_time.hpp> -#include <boost/interprocess/ipc/message_queue.hpp> -#include <boost/version.hpp> - -#if defined(WIN32) && (!defined(BOOST_INTERPROCESS_HAS_WINDOWS_KERNEL_BOOTTIME) || !defined(BOOST_INTERPROCESS_HAS_KERNEL_BOOTTIME) || BOOST_VERSION < 104900) -#warning Compiling without BOOST_INTERPROCESS_HAS_WINDOWS_KERNEL_BOOTTIME and BOOST_INTERPROCESS_HAS_KERNEL_BOOTTIME uncommented in boost/interprocess/detail/tmp_dir_helpers.hpp or using a boost version before 1.49 may have unintended results see svn.boost.org/trac/boost/ticket/5392 -#endif - -using namespace boost; -using namespace boost::interprocess; -using namespace boost::posix_time; - -// holds Bitcoin-Qt message queue name (initialized in bitcoin.cpp) -std::string strBitcoinURIQueueName; - -#if defined MAC_OSX || defined __FreeBSD__ -// URI handling not implemented on OSX yet - -void ipcScanRelay(int argc, char *argv[]) { } -void ipcInit(int argc, char *argv[]) { } - -#else - -static void ipcThread2(void* pArg); - -static bool ipcScanCmd(int argc, char *argv[], bool fRelay) -{ - // Check for URI in argv - bool fSent = false; - for (int i = 1; i < argc; i++) - { - if (boost::algorithm::istarts_with(argv[i], "bitcoin:")) - { - const char *strURI = argv[i]; - try { - boost::interprocess::message_queue mq(boost::interprocess::open_only, strBitcoinURIQueueName.c_str()); - if (mq.try_send(strURI, strlen(strURI), 0)) - fSent = true; - else if (fRelay) - break; - } - catch (boost::interprocess::interprocess_exception &ex) { - // don't log the "file not found" exception, because that's normal for - // the first start of the first instance - if (ex.get_error_code() != boost::interprocess::not_found_error || !fRelay) - { - printf("main() - boost interprocess exception #%d: %s\n", ex.get_error_code(), ex.what()); - break; - } - } - } - } - return fSent; -} - -void ipcScanRelay(int argc, char *argv[]) -{ - if (ipcScanCmd(argc, argv, true)) - exit(0); -} - -static void ipcThread(void* pArg) -{ - // Make this thread recognisable as the GUI-IPC thread - RenameThread("bitcoin-gui-ipc"); - - try - { - ipcThread2(pArg); - } - catch (std::exception& e) { - PrintExceptionContinue(&e, "ipcThread()"); - } catch (...) { - PrintExceptionContinue(NULL, "ipcThread()"); - } - printf("ipcThread exited\n"); -} - -static void ipcThread2(void* pArg) -{ - printf("ipcThread started\n"); - - message_queue* mq = (message_queue*)pArg; - char buffer[MAX_URI_LENGTH + 1] = ""; - size_t nSize = 0; - unsigned int nPriority = 0; - - loop - { - ptime d = boost::posix_time::microsec_clock::universal_time() + millisec(100); - if (mq->timed_receive(&buffer, sizeof(buffer), nSize, nPriority, d)) - { - uiInterface.ThreadSafeHandleURI(std::string(buffer, nSize)); - Sleep(1000); - } - - if (fShutdown) - break; - } - - // Remove message queue - message_queue::remove(strBitcoinURIQueueName.c_str()); - // Cleanup allocated memory - delete mq; -} - -void ipcInit(int argc, char *argv[]) -{ - message_queue* mq = NULL; - char buffer[MAX_URI_LENGTH + 1] = ""; - size_t nSize = 0; - unsigned int nPriority = 0; - - try { - mq = new message_queue(open_or_create, strBitcoinURIQueueName.c_str(), 2, MAX_URI_LENGTH); - - // Make sure we don't lose any bitcoin: URIs - for (int i = 0; i < 2; i++) - { - ptime d = boost::posix_time::microsec_clock::universal_time() + millisec(1); - if (mq->timed_receive(&buffer, sizeof(buffer), nSize, nPriority, d)) - { - uiInterface.ThreadSafeHandleURI(std::string(buffer, nSize)); - } - else - break; - } - - // Make sure only one bitcoin instance is listening - message_queue::remove(strBitcoinURIQueueName.c_str()); - delete mq; - - mq = new message_queue(open_or_create, strBitcoinURIQueueName.c_str(), 2, MAX_URI_LENGTH); - } - catch (interprocess_exception &ex) { - printf("ipcInit() - boost interprocess exception #%d: %s\n", ex.get_error_code(), ex.what()); - return; - } - - if (!NewThread(ipcThread, mq)) - { - delete mq; - return; - } - - ipcScanCmd(argc, argv, false); -} - -#endif diff --git a/src/qt/qtipcserver.h b/src/qt/qtipcserver.h deleted file mode 100644 index f775f272c2..0000000000 --- a/src/qt/qtipcserver.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef QTIPCSERVER_H -#define QTIPCSERVER_H - -#include <string> - -// Define Bitcoin-Qt message queue name for mainnet -#define BITCOINURI_QUEUE_NAME_MAINNET "BitcoinURI" -// Define Bitcoin-Qt message queue name for testnet -#define BITCOINURI_QUEUE_NAME_TESTNET "BitcoinURI-testnet" - -extern std::string strBitcoinURIQueueName; - -void ipcScanRelay(int argc, char *argv[]); -void ipcInit(int argc, char *argv[]); - -#endif // QTIPCSERVER_H diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 90a68f560a..21eb2fd1aa 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -75,6 +75,7 @@ Value getinfo(const Array& params, bool fHelp) obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); obj.push_back(Pair("blocks", (int)nBestHeight)); + obj.push_back(Pair("timeoffset", (boost::int64_t)GetTimeOffset())); obj.push_back(Pair("connections", (int)vNodes.size())); obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string()))); obj.push_back(Pair("difficulty", (double)GetDifficulty())); diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index f56969cba6..1b0ccad511 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -261,4 +261,66 @@ BOOST_AUTO_TEST_CASE(util_IsHex) BOOST_CHECK(!IsHex("0x0000")); } +BOOST_AUTO_TEST_CASE(util_seed_insecure_rand) +{ + // Expected results for the determinstic seed. + const uint32_t exp_vals[11] = { 91632771U,1889679809U,3842137544U,3256031132U, + 1761911779U, 489223532U,2692793790U,2737472863U, + 2796262275U,1309899767U,840571781U}; + // Expected 0s in rand()%(idx+2) for the determinstic seed. + const int exp_count[9] = {5013,3346,2415,1972,1644,1386,1176,1096,1009}; + int i; + int count=0; + + seed_insecure_rand(); + + //Does the non-determistic rand give us results that look too like the determinstic one? + for (i=0;i<10;i++) + { + int match = 0; + uint32_t rval = insecure_rand(); + for (int j=0;j<11;j++)match |= rval==exp_vals[j]; + count += match; + } + // sum(binomial(10,i)*(11/(2^32))^i*(1-(11/(2^32)))^(10-i),i,0,4) ~= 1-1/2^134.73 + // So _very_ unlikely to throw a false failure here. + BOOST_CHECK(count<=4); + + for (int mod=2;mod<11;mod++) + { + int mask = 1; + // Really rough binomal confidence approximation. + int err = 30*10000./mod*sqrt((1./mod*(1-1./mod))/10000.); + //mask is 2^ceil(log2(mod))-1 + while(mask<mod-1)mask=(mask<<1)+1; + + count = 0; + //How often does it get a zero from the uniform range [0,mod)? + for (i=0;i<10000;i++) + { + uint32_t rval; + do{ + rval=insecure_rand()&mask; + }while(rval>=(uint32_t)mod); + count += rval==0; + } + BOOST_CHECK(count<=10000/mod+err); + BOOST_CHECK(count>=10000/mod-err); + } + + seed_insecure_rand(true); + + for (i=0;i<11;i++) + { + BOOST_CHECK_EQUAL(insecure_rand(),exp_vals[i]); + } + + for (int mod=2;mod<11;mod++) + { + count = 0; + for (i=0;i<10000;i++) count += insecure_rand()%mod==0; + BOOST_CHECK_EQUAL(count,exp_count[mod-2]); + } +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/txdb.cpp b/src/txdb.cpp index 8c01158254..ae7aceb7f1 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -22,17 +22,17 @@ void static BatchWriteHashBestChain(CLevelDBBatch &batch, const uint256 &hash) { CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe) { } -bool CCoinsViewDB::GetCoins(uint256 txid, CCoins &coins) { +bool CCoinsViewDB::GetCoins(const uint256 &txid, CCoins &coins) { return db.Read(make_pair('c', txid), coins); } -bool CCoinsViewDB::SetCoins(uint256 txid, const CCoins &coins) { +bool CCoinsViewDB::SetCoins(const uint256 &txid, const CCoins &coins) { CLevelDBBatch batch; BatchWriteCoins(batch, txid, coins); return db.WriteBatch(batch); } -bool CCoinsViewDB::HaveCoins(uint256 txid) { +bool CCoinsViewDB::HaveCoins(const uint256 &txid) { return db.Exists(make_pair('c', txid)); } diff --git a/src/txdb.h b/src/txdb.h index eb8f574e46..f59fc5da86 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -16,9 +16,9 @@ protected: public: CCoinsViewDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); - bool GetCoins(uint256 txid, CCoins &coins); - bool SetCoins(uint256 txid, const CCoins &coins); - bool HaveCoins(uint256 txid); + bool GetCoins(const uint256 &txid, CCoins &coins); + bool SetCoins(const uint256 &txid, const CCoins &coins); + bool HaveCoins(const uint256 &txid); CBlockIndex *GetBestBlock(); bool SetBestBlock(CBlockIndex *pindex); bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex); diff --git a/src/util.cpp b/src/util.cpp index d8f05cb9fd..fc3e846a6b 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -3,6 +3,15 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef WIN32 +// for posix_fallocate +#ifdef __linux__ +#define _POSIX_C_SOURCE 200112L +#endif +#include <fcntl.h> +#include <sys/stat.h> +#endif + #include "util.h" #include "sync.h" #include "version.h" @@ -1152,9 +1161,46 @@ int GetFilesize(FILE* file) return nFilesize; } +bool TruncateFile(FILE *file, unsigned int length) { +#if defined(WIN32) + return _chsize(_fileno(file), length) == 0; +#else + return ftruncate(fileno(file), length) == 0; +#endif +} + // this function tries to make a particular range of a file allocated (corresponding to disk space) // it is advisory, and the range specified in the arguments will never contain live data void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) { +#if defined(WIN32) + // Windows-specific version + HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file)); + LARGE_INTEGER nFileSize; + int64 nEndPos = (int64)offset + length; + nFileSize.u.LowPart = nEndPos & 0xFFFFFFFF; + nFileSize.u.HighPart = nEndPos >> 32; + SetFilePointerEx(hFile, nFileSize, 0, FILE_BEGIN); + SetEndOfFile(hFile); +#elif defined(MAC_OSX) + // OSX specific version + fstore_t fst; + fst.fst_flags = F_ALLOCATECONTIG; + fst.fst_posmode = F_PEOFPOSMODE; + fst.fst_offset = 0; + fst.fst_length = (off_t)offset + length; + fst.fst_bytesalloc = 0; + if (fcntl(fileno(file), F_PREALLOCATE, &fst) == -1) { + fst.fst_flags = F_ALLOCATEALL; + fcntl(fileno(file), F_PREALLOCATE, &fst); + } + ftruncate(fileno(file), fst.fst_length); +#elif defined(__linux__) + // Version using posix_fallocate + off_t nEndPos = (off_t)offset + length; + posix_fallocate(fileno(file), 0, nEndPos); +#else + // Fallback version + // TODO: just write one byte per block static const char buf[65536] = {}; fseek(file, offset, SEEK_SET); while (length > 0) { @@ -1164,6 +1210,7 @@ void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) { fwrite(buf, 1, now, file); // allowed to fail; this function is advisory anyway length -= now; } +#endif } void ShrinkDebugFile() @@ -1218,9 +1265,14 @@ void SetMockTime(int64 nMockTimeIn) static int64 nTimeOffset = 0; +int64 GetTimeOffset() +{ + return nTimeOffset; +} + int64 GetAdjustedTime() { - return GetTime() + nTimeOffset; + return GetTime() + GetTimeOffset(); } void AddTimeData(const CNetAddr& ip, int64 nTime) @@ -1276,12 +1328,26 @@ void AddTimeData(const CNetAddr& ip, int64 nTime) } } - - - - - - +uint32_t insecure_rand_Rz = 11; +uint32_t insecure_rand_Rw = 11; +void seed_insecure_rand(bool fDeterministic) +{ + //The seed values have some unlikely fixed points which we avoid. + if(fDeterministic) + { + insecure_rand_Rz = insecure_rand_Rw = 11; + } else { + uint32_t tmp; + do{ + RAND_bytes((unsigned char*)&tmp,4); + }while(tmp==0 || tmp==0x9068ffffU); + insecure_rand_Rz=tmp; + do{ + RAND_bytes((unsigned char*)&tmp,4); + }while(tmp==0 || tmp==0x464fffffU); + insecure_rand_Rw=tmp; + } +} string FormatVersion(int nVersion) { diff --git a/src/util.h b/src/util.h index 97911d7493..d129d13692 100644 --- a/src/util.h +++ b/src/util.h @@ -193,6 +193,7 @@ bool WildcardMatch(const char* psz, const char* mask); bool WildcardMatch(const std::string& str, const std::string& mask); void FileCommit(FILE *fileout); int GetFilesize(FILE* file); +bool TruncateFile(FILE *file, unsigned int length); void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length); bool RenameOver(boost::filesystem::path src, boost::filesystem::path dest); boost::filesystem::path GetDefaultDataDir(); @@ -212,6 +213,7 @@ uint256 GetRandHash(); int64 GetTime(); void SetMockTime(int64 nMockTimeIn); int64 GetAdjustedTime(); +int64 GetTimeOffset(); std::string FormatFullVersion(); std::string FormatSubVersion(const std::string& name, int nClientVersion, const std::vector<std::string>& comments); void AddTimeData(const CNetAddr& ip, int64 nTime); @@ -403,13 +405,27 @@ bool SoftSetArg(const std::string& strArg, const std::string& strValue); */ bool SoftSetBoolArg(const std::string& strArg, bool fValue); +/** + * MWC RNG of George Marsaglia + * This is intended to be fast. It has a period of 2^59.3, though the + * least significant 16 bits only have a period of about 2^30.1. + * + * @return random value + */ +extern uint32_t insecure_rand_Rz; +extern uint32_t insecure_rand_Rw; +static inline uint32_t insecure_rand(void) +{ + insecure_rand_Rz=36969*(insecure_rand_Rz&65535)+(insecure_rand_Rz>>16); + insecure_rand_Rw=18000*(insecure_rand_Rw&65535)+(insecure_rand_Rw>>16); + return (insecure_rand_Rw<<16)+insecure_rand_Rz; +} - - - - - - +/** + * Seed insecure_rand using the random pool. + * @param Deterministic Use a determinstic seed + */ +void seed_insecure_rand(bool fDeterministic=false); /** Median filter over a stream of values. * Returns the median of the last N numbers diff --git a/src/wallet.cpp b/src/wallet.cpp index 2317ac31ac..eecb7d2d22 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -8,6 +8,7 @@ #include "crypter.h" #include "ui_interface.h" #include "base58.h" +#include <boost/algorithm/string/replace.hpp> using namespace std; @@ -476,6 +477,16 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn) // Notify UI of new or updated transaction NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED); + + // notify an external script when a wallet transaction comes in or is updated + std::string strCmd = GetArg("-walletnotify", ""); + + if ( !strCmd.empty()) + { + boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); + boost::thread t(runCommand, strCmd); // thread runs free + } + } return true; } @@ -973,6 +984,8 @@ static void ApproximateBestSubset(vector<pair<int64, pair<const CWalletTx*,unsig vfBest.assign(vValue.size(), true); nBest = nTotalLower; + seed_insecure_rand(); + for (int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++) { vfIncluded.assign(vValue.size(), false); @@ -982,7 +995,13 @@ static void ApproximateBestSubset(vector<pair<int64, pair<const CWalletTx*,unsig { for (unsigned int i = 0; i < vValue.size(); i++) { - if (nPass == 0 ? rand() % 2 : !vfIncluded[i]) + //The solver here uses a randomized algorithm, + //the randomness serves no real security purpose but is just + //needed to prevent degenerate behavior and it is important + //that the rng fast. We do not use a constant random sequence, + //because there may be some privacy improvement by making + //the selection random. + if (nPass == 0 ? insecure_rand()&1 : !vfIncluded[i]) { nTotal += vValue[i].first; vfIncluded[i] = true; |