diff options
Diffstat (limited to 'src')
142 files changed, 4832 insertions, 2822 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index c0687ae2e6..508063d5e2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -6,7 +6,7 @@ AM_LDFLAGS = $(PTHREAD_CFLAGS) noinst_LIBRARIES = libbitcoin.a -bin_PROGRAMS = bitcoind +bin_PROGRAMS = bitcoind bitcoin-cli SUBDIRS = . $(BUILD_QT) $(BUILD_TEST) DIST_SUBDIRS = . qt test @@ -17,7 +17,7 @@ BITCOIN_CORE_H = addrman.h alert.h allocators.h base58.h bignum.h \ clientversion.h compat.h core.h crypter.h db.h hash.h init.h \ key.h keystore.h leveldb.h limitedmap.h main.h miner.h mruset.h \ netbase.h net.h protocol.h script.h serialize.h sync.h threadsafety.h \ - txdb.h ui_interface.h uint256.h util.h version.h walletdb.h wallet.h + txdb.h txmempool.h ui_interface.h uint256.h util.h version.h walletdb.h wallet.h JSON_H = json/json_spirit.h json/json_spirit_error_position.h \ json/json_spirit_reader.h json/json_spirit_reader_template.h \ @@ -31,12 +31,12 @@ 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 \ rpcmining.cpp rpcnet.cpp rpcrawtransaction.cpp rpcwallet.cpp script.cpp \ - sync.cpp txdb.cpp util.cpp version.cpp wallet.cpp walletdb.cpp $(JSON_H) \ + sync.cpp txdb.cpp txmempool.cpp util.cpp version.cpp wallet.cpp walletdb.cpp $(JSON_H) \ $(BITCOIN_CORE_H) nodist_libbitcoin_a_SOURCES = $(top_srcdir)/src/obj/build.h @@ -55,6 +55,17 @@ endif AM_CPPFLAGS += $(BDB_CPPFLAGS) bitcoind_LDADD += $(BDB_LIBS) +# bitcoin-cli binary # +bitcoin_cli_LDADD = libbitcoin.a leveldb/libleveldb.a leveldb/libmemenv.a \ + $(BOOST_LIBS) +bitcoin_cli_SOURCES = bitcoin-cli.cpp +# + +if TARGET_WINDOWS +bitcoin_cli_SOURCES += bitcoin-cli-res.rc +endif +bitcoin_cli_LDADD += $(BDB_LIBS) + leveldb/libleveldb.a: leveldb/libmemenv.a leveldb/%.a: diff --git a/src/addrman.h b/src/addrman.h index 081543ace4..d703e1091e 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -419,7 +419,7 @@ public: Check(); } if (fRet) - LogPrint("addr", "Added %s from %s: %i tried, %i new\n", addr.ToStringIPPort().c_str(), source.ToString().c_str(), nTried, nNew); + LogPrint("addrman", "Added %s from %s: %i tried, %i new\n", addr.ToStringIPPort().c_str(), source.ToString().c_str(), nTried, nNew); return fRet; } @@ -435,7 +435,7 @@ public: Check(); } if (nAdd) - LogPrint("addr", "Added %i addresses from %s: %i tried, %i new\n", nAdd, source.ToString().c_str(), nTried, nNew); + LogPrint("addrman", "Added %i addresses from %s: %i tried, %i new\n", nAdd, source.ToString().c_str(), nTried, nNew); return nAdd > 0; } diff --git a/src/alert.h b/src/alert.h index 25e140f573..e4841f9c3f 100644 --- a/src/alert.h +++ b/src/alert.h @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. diff --git a/src/allocators.cpp b/src/allocators.cpp new file mode 100644 index 0000000000..15f34aa2c8 --- /dev/null +++ b/src/allocators.cpp @@ -0,0 +1,67 @@ +// 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 + +LockedPageManager* LockedPageManager::_instance = NULL; +boost::once_flag LockedPageManager::init_flag = BOOST_ONCE_INIT; + +/** 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..a806780ad3 100644 --- a/src/allocators.h +++ b/src/allocators.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. #ifndef BITCOIN_ALLOCATORS_H @@ -8,28 +8,10 @@ #include <string.h> #include <string> #include <boost/thread/mutex.hpp> +#include <boost/thread/once.hpp> #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. @@ -53,6 +35,12 @@ public: page_mask = ~(page_size - 1); } + ~LockedPageManagerBase() + { + assert(this->GetLockedPageCount() == 0); + } + + // For all pages in affected range, increase lock count void LockRange(void *p, size_t size) { @@ -115,21 +103,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,39 +114,49 @@ 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); }; /** * Singleton class to keep track of locked (ie, non-swappable) memory pages, for use in * std::allocator templates. + * + * Some implementations of the STL allocate memory in some constructors (i.e., see + * MSVC's vector<T> implementation where it allocates 1 byte of memory in the allocator.) + * Due to the unpredictable order of static initializers, we have to make sure the + * LockedPageManager instance exists before any other STL-based objects that use + * secure_allocator are created. So instead of having LockedPageManager also be + * static-intialized, it is created on demand. */ class LockedPageManager: public LockedPageManagerBase<MemoryPageLocker> { public: - static LockedPageManager instance; // instantiated in util.cpp + static LockedPageManager& Instance() + { + boost::call_once(LockedPageManager::CreateInstance, LockedPageManager::init_flag); + return *LockedPageManager::_instance; + } + private: - LockedPageManager(): - LockedPageManagerBase<MemoryPageLocker>(GetSystemPageSize()) - {} + LockedPageManager(); + + static void CreateInstance() + { + // Using a local static instance guarantees that the object is initialized + // when it's first needed and also deinitialized after all objects that use + // it are done with it. I can think of one unlikely scenario where we may + // have a static deinitialization order/problem, but the check in + // LockedPageManagerBase's destructor helps us detect if that ever happens. + static LockedPageManager instance; + LockedPageManager::_instance = &instance; + } + + static LockedPageManager* _instance; + static boost::once_flag init_flag; }; // @@ -181,12 +164,12 @@ private: // Intended for non-dynamically allocated structures. // template<typename T> void LockObject(const T &t) { - LockedPageManager::instance.LockRange((void*)(&t), sizeof(T)); + LockedPageManager::Instance().LockRange((void*)(&t), sizeof(T)); } template<typename T> void UnlockObject(const T &t) { OPENSSL_cleanse((void*)(&t), sizeof(T)); - LockedPageManager::instance.UnlockRange((void*)(&t), sizeof(T)); + LockedPageManager::Instance().UnlockRange((void*)(&t), sizeof(T)); } // @@ -218,7 +201,7 @@ struct secure_allocator : public std::allocator<T> T *p; p = std::allocator<T>::allocate(n, hint); if (p != NULL) - LockedPageManager::instance.LockRange(p, sizeof(T) * n); + LockedPageManager::Instance().LockRange(p, sizeof(T) * n); return p; } @@ -227,7 +210,7 @@ struct secure_allocator : public std::allocator<T> if (p != NULL) { OPENSSL_cleanse(p, sizeof(T) * n); - LockedPageManager::instance.UnlockRange(p, sizeof(T) * n); + LockedPageManager::Instance().UnlockRange(p, sizeof(T) * n); } std::allocator<T>::deallocate(p, n); } diff --git a/src/base58.h b/src/base58.h index aabae8de88..44bf5281e1 100644 --- a/src/base58.h +++ b/src/base58.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin Developers +// 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. diff --git a/src/bignum.h b/src/bignum.h index 98655cc0e9..e9d9007d3d 100644 --- a/src/bignum.h +++ b/src/bignum.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. #ifndef BITCOIN_BIGNUM_H diff --git a/src/bitcoin-cli-res.rc b/src/bitcoin-cli-res.rc new file mode 100644 index 0000000000..337897753e --- /dev/null +++ b/src/bitcoin-cli-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", "Bitcoin-cli (OSS RPC client for Bitcoin)" + VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "InternalName", "bitcoin-cli" + 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", "bitcoin-cli.exe" + VALUE "ProductName", "Bitcoin-cli" + VALUE "ProductVersion", VER_PRODUCTVERSION_STR + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1252 // language neutral - multilingual (decimal) + END +END diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp new file mode 100644 index 0000000000..bb30c748bb --- /dev/null +++ b/src/bitcoin-cli.cpp @@ -0,0 +1,69 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// 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 "util.h" +#include "init.h" +#include "bitcoinrpc.h" +#include "ui_interface.h" /* for _(...) */ + +////////////////////////////////////////////////////////////////////////////// +// +// Start +// +static bool AppInitRPC(int argc, char* argv[]) +{ + // + // Parameters + // + ParseParameters(argc, argv); + if (!boost::filesystem::is_directory(GetDataDir(false))) + { + fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str()); + return false; + } + ReadConfigFile(mapArgs, mapMultiArgs); + + if (argc<2 || mapArgs.count("-?") || mapArgs.count("--help")) + { + // First part of help message is specific to RPC client + std::string strUsage = _("Bitcoin RPC client version") + " " + FormatFullVersion() + "\n\n" + + _("Usage:") + "\n" + + " bitcoin-cli [options] <command> [params] " + _("Send command to Bitcoin server") + "\n" + + " bitcoin-cli [options] help " + _("List commands") + "\n" + + " bitcoin-cli [options] help <command> " + _("Get help for a command") + "\n"; + + strUsage += "\n" + HelpMessage(HMM_BITCOIN_CLI); + + fprintf(stdout, "%s", strUsage.c_str()); + return false; + } + return true; +} + +int main(int argc, char* argv[]) +{ + try + { + if(!AppInitRPC(argc, argv)) + return 1; + } + catch (std::exception& e) { + PrintExceptionContinue(&e, "AppInitRPC()"); + } catch (...) { + PrintExceptionContinue(NULL, "AppInitRPC()"); + } + + try + { + if(!CommandLineRPC(argc, argv)) + return 1; + } + catch (std::exception& e) { + PrintExceptionContinue(&e, "CommandLineRPC()"); + } catch (...) { + PrintExceptionContinue(NULL, "CommandLineRPC()"); + } + return 0; +} diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 02e4e7d6e7..4fd3296069 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -1,23 +1,29 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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 "ui_interface.h" #include "init.h" +#include "util.h" +#include "main.h" #include "bitcoinrpc.h" #include <boost/algorithm/string/predicate.hpp> 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(); + } } ////////////////////////////////////////////////////////////////////////////// @@ -54,18 +60,20 @@ bool AppInit(int argc, char* argv[]) // First part of help message is specific to bitcoind / RPC client std::string strUsage = _("Bitcoin version") + " " + FormatFullVersion() + "\n\n" + _("Usage:") + "\n" + - " bitcoind [options] " + "\n" + - " bitcoind [options] <command> [params] " + _("Send command to -server or bitcoind") + "\n" + + " bitcoind [options] " + _("Start Bitcoin server") + "\n" + + _("Usage (deprecated, use bitcoin-cli):") + "\n" + + " bitcoind [options] <command> [params] " + _("Send command to Bitcoin server") + "\n" + " bitcoind [options] help " + _("List commands") + "\n" + " bitcoind [options] help <command> " + _("Get help for a command") + "\n"; - strUsage += "\n" + HelpMessage(); + strUsage += "\n" + HelpMessage(HMM_BITCOIND); fprintf(stdout, "%s", strUsage.c_str()); return false; } // 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 +115,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 ef50ccd07f..f3ea0adb40 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -1,9 +1,11 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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 "chainparams.h" +#include "main.h" +#include "wallet.h" #include "init.h" #include "util.h" #include "sync.h" @@ -229,8 +231,10 @@ static const CRPCCommand vRPCCommands[] = { "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 }, @@ -807,7 +811,7 @@ void StartRPCThreads() if (filesystem::exists(pathPKFile)) rpc_ssl_context->use_private_key_file(pathPKFile.string(), ssl::context::pem); else LogPrintf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile.string().c_str()); - string strCiphers = GetArg("-rpcsslciphers", "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH"); + string strCiphers = GetArg("-rpcsslciphers", "TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH"); SSL_CTX_set_cipher_list(rpc_ssl_context->impl(), strCiphers.c_str()); } @@ -880,7 +884,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; diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h index ce1c0e68b7..0c1c722b50 100644 --- a/src/bitcoinrpc.h +++ b/src/bitcoinrpc.h @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. @@ -49,10 +49,13 @@ enum RPCErrorCode RPC_INVALID_PARAMETER = -8, // Invalid, missing or duplicate parameter RPC_DATABASE_ERROR = -20, // Database error RPC_DESERIALIZATION_ERROR = -22, // Error parsing or validating structure in raw format + RPC_SERVER_NOT_STARTED = -18, // RPC server was not started (StartRPCThreads() not called) // P2P client errors RPC_CLIENT_NOT_CONNECTED = -9, // Bitcoin is not connected RPC_CLIENT_IN_INITIAL_DOWNLOAD = -10, // Still downloading initial blocks + RPC_CLIENT_NODE_ALREADY_ADDED = -23, // Node is already added + RPC_CLIENT_NODE_NOT_ADDED = -24, // Node has not been added before // Wallet errors RPC_WALLET_ERROR = -4, // Unspecified problem with wallet (key not found etc.) @@ -153,8 +156,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); diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 0795f09765..82f41bcc50 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. 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/checkpoints.cpp b/src/checkpoints.cpp index bb551501f1..76ae911a5c 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. diff --git a/src/checkpoints.h b/src/checkpoints.h index a49a908a38..4afd29326b 100644 --- a/src/checkpoints.h +++ b/src/checkpoints.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. #ifndef BITCOIN_CHECKPOINT_H diff --git a/src/checkqueue.h b/src/checkqueue.h index eba424fbaa..6e2f609fcf 100644 --- a/src/checkqueue.h +++ b/src/checkqueue.h @@ -4,6 +4,7 @@ #ifndef CHECKQUEUE_H #define CHECKQUEUE_H +#include <boost/foreach.hpp> #include <boost/thread/mutex.hpp> #include <boost/thread/locks.hpp> #include <boost/thread/condition_variable.hpp> diff --git a/src/compat.h b/src/compat.h index 9caf5e4810..c910f01cb4 100644 --- a/src/compat.h +++ b/src/compat.h @@ -1,16 +1,22 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. #ifndef _BITCOIN_COMPAT_H #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.cpp b/src/core.cpp index 99b5c6641a..5512f81b61 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -63,8 +63,6 @@ uint256 CTxOut::GetHash() const std::string CTxOut::ToString() const { - if (scriptPubKey.size() < 6) - return "CTxOut(error)"; return strprintf("CTxOut(nValue=%"PRI64d".%08"PRI64d", scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,30).c_str()); } diff --git a/src/core.h b/src/core.h index ce21acd59e..70e62716f3 100644 --- a/src/core.h +++ b/src/core.h @@ -421,6 +421,9 @@ public: // equality test friend bool operator==(const CCoins &a, const CCoins &b) { + // Empty CCoins objects are always equal. + if (a.IsPruned() && b.IsPruned()) + return true; return a.fCoinBase == b.fCoinBase && a.nHeight == b.nHeight && a.nVersion == b.nVersion && @@ -661,4 +664,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 diff --git a/src/crypter.cpp b/src/crypter.cpp index 754de536a9..f3b9396dae 100644 --- a/src/crypter.cpp +++ b/src/crypter.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2012 The Bitcoin Developers +// 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. diff --git a/src/crypter.h b/src/crypter.h index 4134c1b49b..22187791e9 100644 --- a/src/crypter.h +++ b/src/crypter.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2012 The Bitcoin Developers +// 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. #ifndef __CRYPTER_H__ @@ -88,16 +88,16 @@ public: // Try to keep the key data out of swap (and be a bit over-careful to keep the IV that we don't even use out of swap) // Note that this does nothing about suspend-to-disk (which will put all our key data on disk) // Note as well that at no point in this program is any attempt made to prevent stealing of keys by reading the memory of the running process. - LockedPageManager::instance.LockRange(&chKey[0], sizeof chKey); - LockedPageManager::instance.LockRange(&chIV[0], sizeof chIV); + LockedPageManager::Instance().LockRange(&chKey[0], sizeof chKey); + LockedPageManager::Instance().LockRange(&chIV[0], sizeof chIV); } ~CCrypter() { CleanKey(); - LockedPageManager::instance.UnlockRange(&chKey[0], sizeof chKey); - LockedPageManager::instance.UnlockRange(&chIV[0], sizeof chIV); + LockedPageManager::Instance().UnlockRange(&chKey[0], sizeof chKey); + LockedPageManager::Instance().UnlockRange(&chIV[0], sizeof chIV); } }; diff --git a/src/db.cpp b/src/db.cpp index f722c52e93..7e58ff45fb 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. #ifndef BITCOIN_DB_H @@ -16,7 +16,7 @@ #include <db_cxx.h> class CAddrMan; -class CBlockLocator; +struct CBlockLocator; class CDiskBlockIndex; class CMasterKey; class COutPoint; diff --git a/src/hash.h b/src/hash.h index 880468a2d2..ff7d57399a 100644 --- a/src/hash.h +++ b/src/hash.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. #ifndef BITCOIN_HASH_H diff --git a/src/init.cpp b/src/init.cpp index e75e981a57..a803598f32 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. @@ -106,7 +106,7 @@ void Shutdown() if (!lockShutdown) return; RenameThread("bitcoin-shutoff"); - nTransactionsUpdated++; + mempool.AddTransactionsUpdated(1); StopRPCThreads(); ShutdownRPCMining(); if (pwalletMain) @@ -116,7 +116,7 @@ void Shutdown() { LOCK(cs_main); if (pwalletMain) - pwalletMain->SetBestChain(CBlockLocator(pindexBest)); + pwalletMain->SetBestChain(chainActive.GetLocator()); if (pblocktree) pblocktree->Flush(); if (pcoinsTip) @@ -171,94 +171,130 @@ bool static Bind(const CService &addr, unsigned int flags) { return true; } -// Core-specific options shared between UI and daemon -std::string HelpMessage() +// Core-specific options shared between UI, daemon and RPC client +std::string HelpMessage(HelpMessageMode hmm) { string strUsage = _("Options:") + "\n"; strUsage += " -? " + _("This help message") + "\n"; strUsage += " -conf=<file> " + _("Specify configuration file (default: bitcoin.conf)") + "\n"; - strUsage += " -pid=<file> " + _("Specify pid file (default: bitcoind.pid)") + "\n"; - strUsage += " -gen " + _("Generate coins (default: 0)") + "\n"; strUsage += " -datadir=<dir> " + _("Specify data directory") + "\n"; - strUsage += " -wallet=<file> " + _("Specify wallet file (within data directory)") + "\n"; - strUsage += " -dbcache=<n> " + _("Set database cache size in megabytes (default: 25)") + "\n"; - strUsage += " -timeout=<n> " + _("Specify connection timeout in milliseconds (default: 5000)") + "\n"; - strUsage += " -proxy=<ip:port> " + _("Connect through socks proxy") + "\n"; - strUsage += " -socks=<n> " + _("Select the version of socks proxy to use (4-5, default: 5)") + "\n"; - strUsage += " -tor=<ip:port> " + _("Use proxy to reach tor hidden services (default: same as -proxy)") + "\n"; - strUsage += " -dns " + _("Allow DNS lookups for -addnode, -seednode and -connect") + "\n"; - strUsage += " -port=<port> " + _("Listen for connections on <port> (default: 8333 or testnet: 18333)") + "\n"; - strUsage += " -maxconnections=<n> " + _("Maintain at most <n> connections to peers (default: 125)") + "\n"; - strUsage += " -addnode=<ip> " + _("Add a node to connect to and attempt to keep the connection open") + "\n"; - strUsage += " -connect=<ip> " + _("Connect only to the specified node(s)") + "\n"; - strUsage += " -seednode=<ip> " + _("Connect to a node to retrieve peer addresses, and disconnect") + "\n"; - strUsage += " -externalip=<ip> " + _("Specify your own public address") + "\n"; - strUsage += " -onlynet=<net> " + _("Only connect to nodes in network <net> (IPv4, IPv6 or Tor)") + "\n"; - strUsage += " -discover " + _("Discover own IP address (default: 1 when listening and no -externalip)") + "\n"; - strUsage += " -checkpoints " + _("Only accept block chain matching built-in checkpoints (default: 1)") + "\n"; - strUsage += " -listen " + _("Accept connections from outside (default: 1 if no -proxy or -connect)") + "\n"; - strUsage += " -bind=<addr> " + _("Bind to given address and always listen on it. Use [host]:port notation for IPv6") + "\n"; - strUsage += " -dnsseed " + _("Find peers using DNS lookup (default: 1 unless -connect)") + "\n"; - strUsage += " -banscore=<n> " + _("Threshold for disconnecting misbehaving peers (default: 100)") + "\n"; - strUsage += " -bantime=<n> " + _("Number of seconds to keep misbehaving peers from reconnecting (default: 86400)") + "\n"; - strUsage += " -maxreceivebuffer=<n> " + _("Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)") + "\n"; - strUsage += " -maxsendbuffer=<n> " + _("Maximum per-connection send buffer, <n>*1000 bytes (default: 1000)") + "\n"; + strUsage += " -testnet " + _("Use the test network") + "\n"; + + if(hmm == HMM_BITCOIND || hmm == HMM_BITCOIN_QT) + { + strUsage += " -pid=<file> " + _("Specify pid file (default: bitcoind.pid)") + "\n"; + strUsage += " -gen " + _("Generate coins (default: 0)") + "\n"; + strUsage += " -wallet=<file> " + _("Specify wallet file (within data directory)") + "\n"; + strUsage += " -dbcache=<n> " + _("Set database cache size in megabytes (default: 25)") + "\n"; + strUsage += " -timeout=<n> " + _("Specify connection timeout in milliseconds (default: 5000)") + "\n"; + strUsage += " -proxy=<ip:port> " + _("Connect through socks proxy") + "\n"; + strUsage += " -socks=<n> " + _("Select the version of socks proxy to use (4-5, default: 5)") + "\n"; + strUsage += " -onion=<ip:port> " + _("Use proxy to reach tor hidden services (default: same as -proxy)") + "\n"; + strUsage += " -dns " + _("Allow DNS lookups for -addnode, -seednode and -connect") + "\n"; + strUsage += " -port=<port> " + _("Listen for connections on <port> (default: 8333 or testnet: 18333)") + "\n"; + strUsage += " -maxconnections=<n> " + _("Maintain at most <n> connections to peers (default: 125)") + "\n"; + strUsage += " -addnode=<ip> " + _("Add a node to connect to and attempt to keep the connection open") + "\n"; + strUsage += " -connect=<ip> " + _("Connect only to the specified node(s)") + "\n"; + strUsage += " -seednode=<ip> " + _("Connect to a node to retrieve peer addresses, and disconnect") + "\n"; + strUsage += " -externalip=<ip> " + _("Specify your own public address") + "\n"; + strUsage += " -onlynet=<net> " + _("Only connect to nodes in network <net> (IPv4, IPv6 or Tor)") + "\n"; + strUsage += " -discover " + _("Discover own IP address (default: 1 when listening and no -externalip)") + "\n"; + strUsage += " -checkpoints " + _("Only accept block chain matching built-in checkpoints (default: 1)") + "\n"; + strUsage += " -listen " + _("Accept connections from outside (default: 1 if no -proxy or -connect)") + "\n"; + strUsage += " -bind=<addr> " + _("Bind to given address and always listen on it. Use [host]:port notation for IPv6") + "\n"; + strUsage += " -dnsseed " + _("Find peers using DNS lookup (default: 1 unless -connect)") + "\n"; + strUsage += " -banscore=<n> " + _("Threshold for disconnecting misbehaving peers (default: 100)") + "\n"; + strUsage += " -bantime=<n> " + _("Number of seconds to keep misbehaving peers from reconnecting (default: 86400)") + "\n"; + strUsage += " -maxreceivebuffer=<n> " + _("Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)") + "\n"; + strUsage += " -maxsendbuffer=<n> " + _("Maximum per-connection send buffer, <n>*1000 bytes (default: 1000)") + "\n"; #ifdef USE_UPNP #if USE_UPNP - strUsage += " -upnp " + _("Use UPnP to map the listening port (default: 1 when listening)") + "\n"; + strUsage += " -upnp " + _("Use UPnP to map the listening port (default: 1 when listening)") + "\n"; #else - strUsage += " -upnp " + _("Use UPnP to map the listening port (default: 0)") + "\n"; + strUsage += " -upnp " + _("Use UPnP to map the listening port (default: 0)") + "\n"; #endif #endif - strUsage += " -paytxfee=<amt> " + _("Fee per KB to add to transactions you send") + "\n"; - if (fHaveGUI) + strUsage += " -paytxfee=<amt> " + _("Fee per KB to add to transactions you send") + "\n"; + strUsage += " -debug=<category> " + _("Output debugging information (default: 0, supplying <category> is optional)") + "\n"; + strUsage += _("If <category> is not supplied, output all debugging information.") + "\n"; + strUsage += _("<category> can be:"); + strUsage += " addrman, alert, coindb, db, lock, rand, rpc, selectcoins, mempool, net"; // Don't translate these and qt below + if (hmm == HMM_BITCOIN_QT) + { + strUsage += ", qt.\n"; + } + else + { + strUsage += ".\n"; + } + strUsage += " -logtimestamps " + _("Prepend debug output with timestamp") + "\n"; + strUsage += " -shrinkdebugfile " + _("Shrink debug.log file on client startup (default: 1 when no -debug)") + "\n"; + strUsage += " -printtoconsole " + _("Send trace/debug info to console instead of debug.log file") + "\n"; + strUsage += " -regtest " + _("Enter regression test mode, which uses a special chain in which blocks can be " + "solved instantly. This is intended for regression testing tools and app development.") + "\n"; +#ifdef WIN32 + strUsage += " -printtodebugger " + _("Send trace/debug info to debugger") + "\n"; +#endif + } + + if (hmm == HMM_BITCOIN_QT) + { strUsage += " -server " + _("Accept command line and JSON-RPC commands") + "\n"; + } + + if (hmm == HMM_BITCOIND) + { #if !defined(WIN32) - if (fHaveGUI) strUsage += " -daemon " + _("Run in the background as a daemon and accept commands") + "\n"; #endif - strUsage += " -testnet " + _("Use the test network") + "\n"; - strUsage += " -debug " + _("Output extra debugging information. Implies all other -debug* options") + "\n"; - strUsage += " -debugnet " + _("Output extra network debugging information") + "\n"; - strUsage += " -logtimestamps " + _("Prepend debug output with timestamp") + "\n"; - strUsage += " -shrinkdebugfile " + _("Shrink debug.log file on client startup (default: 1 when no -debug)") + "\n"; - strUsage += " -printtoconsole " + _("Send trace/debug info to console instead of debug.log file") + "\n"; - strUsage += " -regtest " + _("Enter regression test mode, which uses a special chain in which blocks can be " - "solved instantly. This is intended for regression testing tools and app development.") + "\n"; -#ifdef WIN32 - strUsage += " -printtodebugger " + _("Send trace/debug info to debugger") + "\n"; -#endif + } + + if (hmm == HMM_BITCOIND || hmm == HMM_BITCOIN_CLI) + { + strUsage += " -rpcconnect=<ip> " + _("Send commands to node running on <ip> (default: 127.0.0.1)") + "\n"; + } + strUsage += " -rpcuser=<user> " + _("Username for JSON-RPC connections") + "\n"; strUsage += " -rpcpassword=<pw> " + _("Password for JSON-RPC connections") + "\n"; - strUsage += " -rpcport=<port> " + _("Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)") + "\n"; - strUsage += " -rpcallowip=<ip> " + _("Allow JSON-RPC connections from specified IP address") + "\n"; - if (!fHaveGUI) - strUsage += " -rpcconnect=<ip> " + _("Send commands to node running on <ip> (default: 127.0.0.1)") + "\n"; - strUsage += " -rpcthreads=<n> " + _("Set the number of threads to service RPC calls (default: 4)") + "\n"; - strUsage += " -blocknotify=<cmd> " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n"; - strUsage += " -walletnotify=<cmd> " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n"; - strUsage += " -alertnotify=<cmd> " + _("Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)") + "\n"; - strUsage += " -upgradewallet " + _("Upgrade wallet to latest format") + "\n"; - strUsage += " -keypool=<n> " + _("Set key pool size to <n> (default: 100)") + "\n"; - strUsage += " -rescan " + _("Rescan the block chain for missing wallet transactions") + "\n"; - strUsage += " -salvagewallet " + _("Attempt to recover private keys from a corrupt wallet.dat") + "\n"; - strUsage += " -checkblocks=<n> " + _("How many blocks to check at startup (default: 288, 0 = all)") + "\n"; - strUsage += " -checklevel=<n> " + _("How thorough the block verification is (0-4, default: 3)") + "\n"; - strUsage += " -txindex " + _("Maintain a full transaction index (default: 0)") + "\n"; - strUsage += " -loadblock=<file> " + _("Imports blocks from external blk000??.dat file") + "\n"; - strUsage += " -reindex " + _("Rebuild block chain index from current blk000??.dat files") + "\n"; - strUsage += " -par=<n> " + _("Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0)") + "\n"; - - strUsage += "\n" + _("Block creation options:") + "\n"; - strUsage += " -blockminsize=<n> " + _("Set minimum block size in bytes (default: 0)") + "\n"; - strUsage += " -blockmaxsize=<n> " + _("Set maximum block size in bytes (default: 250000)") + "\n"; - strUsage += " -blockprioritysize=<n> " + _("Set maximum size of high-priority/low-fee transactions in bytes (default: 27000)") + "\n"; + if (hmm == HMM_BITCOIND || hmm == HMM_BITCOIN_QT) + { + strUsage += " -rpcport=<port> " + _("Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)") + "\n"; + } else { + strUsage += " -rpcport=<port> " + _("Connect to JSON-RPC on <port> (default: 8332 or testnet: 18332)") + "\n"; + } + + if(hmm == HMM_BITCOIND || hmm == HMM_BITCOIN_QT) + { + strUsage += " -rpcallowip=<ip> " + _("Allow JSON-RPC connections from specified IP address") + "\n"; + strUsage += " -rpcthreads=<n> " + _("Set the number of threads to service RPC calls (default: 4)") + "\n"; + strUsage += " -blocknotify=<cmd> " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n"; + strUsage += " -walletnotify=<cmd> " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n"; + strUsage += " -alertnotify=<cmd> " + _("Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)") + "\n"; + strUsage += " -upgradewallet " + _("Upgrade wallet to latest format") + "\n"; + strUsage += " -keypool=<n> " + _("Set key pool size to <n> (default: 100)") + "\n"; + strUsage += " -rescan " + _("Rescan the block chain for missing wallet transactions") + "\n"; + strUsage += " -salvagewallet " + _("Attempt to recover private keys from a corrupt wallet.dat") + "\n"; + strUsage += " -checkblocks=<n> " + _("How many blocks to check at startup (default: 288, 0 = all)") + "\n"; + strUsage += " -checklevel=<n> " + _("How thorough the block verification is (0-4, default: 3)") + "\n"; + strUsage += " -txindex " + _("Maintain a full transaction index (default: 0)") + "\n"; + strUsage += " -loadblock=<file> " + _("Imports blocks from external blk000??.dat file") + "\n"; + strUsage += " -reindex " + _("Rebuild block chain index from current blk000??.dat files") + "\n"; + strUsage += " -par=<n> " + _("Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0)") + "\n"; + + strUsage += "\n" + _("Block creation options:") + "\n"; + strUsage += " -blockminsize=<n> " + _("Set minimum block size in bytes (default: 0)") + "\n"; + strUsage += " -blockmaxsize=<n> " + _("Set maximum block size in bytes (default: 250000)") + "\n"; + strUsage += " -blockprioritysize=<n> " + _("Set maximum size of high-priority/low-fee transactions in bytes (default: 27000)") + "\n"; + } strUsage += "\n" + _("SSL options: (see the Bitcoin Wiki for SSL setup instructions)") + "\n"; strUsage += " -rpcssl " + _("Use OpenSSL (https) for JSON-RPC connections") + "\n"; - strUsage += " -rpcsslcertificatechainfile=<file.cert> " + _("Server certificate file (default: server.cert)") + "\n"; - strUsage += " -rpcsslprivatekeyfile=<file.pem> " + _("Server private key (default: server.pem)") + "\n"; - strUsage += " -rpcsslciphers=<ciphers> " + _("Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)") + "\n"; + if (hmm == HMM_BITCOIND || hmm == HMM_BITCOIN_QT) + { + strUsage += " -rpcsslcertificatechainfile=<file.cert> " + _("Server certificate file (default: server.cert)") + "\n"; + strUsage += " -rpcsslprivatekeyfile=<file.pem> " + _("Server private key (default: server.pem)") + "\n"; + strUsage += " -rpcsslciphers=<ciphers> " + _("Acceptable ciphers (default: TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH)") + "\n"; + } return strUsage; } @@ -431,9 +467,18 @@ bool AppInit2(boost::thread_group& threadGroup) // ********************************************************* Step 3: parameter-to-internal-flags - if (mapMultiArgs.count("-debug")) fDebug = true; + fDebug = !mapMultiArgs["-debug"].empty(); + // Special-case: if -debug=0/-nodebug is set, turn off debugging messages + const vector<string>& categories = mapMultiArgs["-debug"]; + if (GetBoolArg("-nodebug", false) || find(categories.begin(), categories.end(), string("0")) != categories.end()) + fDebug = false; + + // Check for -debugnet (deprecated) + if (GetBoolArg("-debugnet", false)) + InitWarning(_("Warning: Deprecated argument -debugnet ignored, use -debug=net")); + fBenchmark = GetBoolArg("-benchmark", false); - mempool.fChecks = GetBoolArg("-checkmempool", RegTest()); + mempool.setSanityCheck(GetBoolArg("-checkmempool", RegTest())); Checkpoints::fEnabled = GetBoolArg("-checkpoints", true); // -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency @@ -445,12 +490,6 @@ bool AppInit2(boost::thread_group& threadGroup) else if (nScriptCheckThreads > MAX_SCRIPTCHECK_THREADS) nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS; - // -debug implies fDebug* - if (fDebug) - fDebugNet = true; - else - fDebugNet = GetBoolArg("-debugnet", false); - if (fDaemon) fServer = true; else @@ -642,15 +681,20 @@ bool AppInit2(boost::thread_group& threadGroup) fProxy = true; } - // -tor can override normal proxy, -notor disables tor entirely - if (!(mapArgs.count("-tor") && mapArgs["-tor"] == "0") && (fProxy || mapArgs.count("-tor"))) { + // -onion can override normal proxy, -noonion disables tor entirely + // -tor here is a temporary backwards compatibility measure + if (mapArgs.count("-tor")) + printf("Notice: option -tor has been replaced with -onion and will be removed in a later version.\n"); + if (!(mapArgs.count("-onion") && mapArgs["-onion"] == "0") && + !(mapArgs.count("-tor") && mapArgs["-tor"] == "0") && + (fProxy || mapArgs.count("-onion") || mapArgs.count("-tor"))) { CService addrOnion; - if (!mapArgs.count("-tor")) + if (!mapArgs.count("-onion") && !mapArgs.count("-tor")) addrOnion = addrProxy; else - addrOnion = CService(mapArgs["-tor"], 9050); + addrOnion = mapArgs.count("-onion")?CService(mapArgs["-onion"], 9050):CService(mapArgs["-tor"], 9050); if (!addrOnion.IsValid()) - return InitError(strprintf(_("Invalid -tor address: '%s'"), mapArgs["-tor"].c_str())); + return InitError(strprintf(_("Invalid -onion address: '%s'"), mapArgs.count("-onion")?mapArgs["-onion"].c_str():mapArgs["-tor"].c_str())); SetProxy(NET_TOR, addrOnion, 5); SetReachable(NET_TOR); } @@ -766,7 +810,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) @@ -912,7 +956,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()); @@ -920,26 +964,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++; } @@ -985,7 +1029,7 @@ bool AppInit2(boost::thread_group& threadGroup) //// debug print LogPrintf("mapBlockIndex.size() = %"PRIszu"\n", mapBlockIndex.size()); - LogPrintf("nBestHeight = %d\n", nBestHeight); + 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); diff --git a/src/init.h b/src/init.h index a4d5a67252..8cb1bf52fc 100644 --- a/src/init.h +++ b/src/init.h @@ -1,11 +1,14 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. #ifndef BITCOIN_INIT_H #define BITCOIN_INIT_H -#include "wallet.h" +#include <string> +#include <boost/thread.hpp> + +class CWallet; extern std::string strWalletFile; extern CWallet* pwalletMain; @@ -14,6 +17,15 @@ void StartShutdown(); bool ShutdownRequested(); void Shutdown(); bool AppInit2(boost::thread_group& threadGroup); -std::string HelpMessage(); + +/* The help message mode determines what help message to show */ +enum HelpMessageMode +{ + HMM_BITCOIND, + HMM_BITCOIN_QT, + HMM_BITCOIN_CLI +}; + +std::string HelpMessage(HelpMessageMode mode); #endif diff --git a/src/key.cpp b/src/key.cpp index 8ef1c414c4..996539dca5 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. @@ -166,9 +166,12 @@ public: assert(nSize == nSize2); } - bool SetPrivKey(const CPrivKey &privkey) { + bool SetPrivKey(const CPrivKey &privkey, bool fSkipCheck=false) { const unsigned char* pbegin = &privkey[0]; if (d2i_ECPrivateKey(&pkey, &pbegin, privkey.size())) { + if(fSkipCheck) + return true; + // d2i_ECPrivateKey returns true if parsing succeeds. // This doesn't necessarily mean the key is valid. if (EC_KEY_check_key(pkey)) @@ -411,6 +414,24 @@ bool CKey::SignCompact(const uint256 &hash, std::vector<unsigned char>& vchSig) return true; } +bool CKey::Load(CPrivKey &privkey, CPubKey &vchPubKey, bool fSkipCheck=false) { + CECKey key; + if (!key.SetPrivKey(privkey, fSkipCheck)) + return false; + + key.GetSecretBytes(vch); + fCompressed = vchPubKey.IsCompressed(); + fValid = true; + + if (fSkipCheck) + return true; + + if (GetPubKey() != vchPubKey) + return false; + + return true; +} + bool CPubKey::Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig) const { if (!IsValid()) return false; @@ -205,7 +205,8 @@ public: } friend bool operator==(const CKey &a, const CKey &b) { - return a.fCompressed == b.fCompressed && memcmp(&a.vch[0], &b.vch[0], 32); + return a.fCompressed == b.fCompressed && a.size() == b.size() && + memcmp(&a.vch[0], &b.vch[0], a.size()) == 0; } // Initialize using begin and end iterators to byte data. @@ -261,6 +262,9 @@ public: // Derive BIP32 child key. bool Derive(CKey& keyChild, unsigned char ccChild[32], unsigned int nChild, const unsigned char cc[32]) const; + + // Load private key and check that public key matches. + bool Load(CPrivKey &privkey, CPubKey &vchPubKey, bool fSkipCheck); }; struct CExtPubKey { diff --git a/src/keystore.cpp b/src/keystore.cpp index 808f8c24ef..301180737f 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. diff --git a/src/keystore.h b/src/keystore.h index 49a7bf569d..4f28fba91b 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. #ifndef BITCOIN_KEYSTORE_H 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/limitedmap.h b/src/limitedmap.h index 7049d68e5a..01d1b07df4 100644 --- a/src/limitedmap.h +++ b/src/limitedmap.h @@ -4,6 +4,7 @@ #ifndef BITCOIN_LIMITEDMAP_H #define BITCOIN_LIMITEDMAP_H +#include <assert.h> // TODO: remove #include <map> #include <deque> diff --git a/src/main.cpp b/src/main.cpp index dcf0ee01ae..4dc2e5c6ff 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,20 +1,22 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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 <boost/algorithm/string/replace.hpp> +#include <boost/filesystem.hpp> +#include <boost/filesystem/fstream.hpp> + #include "alert.h" +#include "chainparams.h" #include "checkpoints.h" +#include "checkqueue.h" #include "db.h" -#include "txdb.h" -#include "net.h" #include "init.h" +#include "net.h" +#include "txdb.h" +#include "txmempool.h" #include "ui_interface.h" -#include "checkqueue.h" -#include "chainparams.h" -#include <boost/algorithm/string/replace.hpp> -#include <boost/filesystem.hpp> -#include <boost/filesystem/fstream.hpp> using namespace std; using namespace boost; @@ -29,17 +31,9 @@ set<CWallet*> setpwalletRegistered; CCriticalSection cs_main; CTxMemPool mempool; -unsigned int nTransactionsUpdated = 0; map<uint256, CBlockIndex*> mapBlockIndex; -std::vector<CBlockIndex*> vBlockIndexByHeight; -CBlockIndex* pindexGenesisBlock = NULL; -int nBestHeight = -1; -uint256 nBestChainWork = 0; -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 +CChain chainActive; int64 nTimeBestReceived = 0; int nScriptCheckThreads = 0; bool fImporting = false; @@ -54,7 +48,7 @@ int64 CTransaction::nMinTxFee = 10000; // Override with -mintxfee /** Fees smaller than this (in satoshi) are considered zero fee (for relaying) */ int64 CTransaction::nMinRelayTxFee = 10000; -CMedianFilter<int> cPeerBlockCounts(8, 0); // Amount of blocks that other nodes claim to have +static CMedianFilter<int> cPeerBlockCounts(8, 0); // Amount of blocks that other nodes claim to have map<uint256, CBlock*> mapOrphanBlocks; multimap<uint256, CBlock*> mapOrphanBlocksByPrev; @@ -70,102 +64,82 @@ const string strMessageMagic = "Bitcoin Signed Message:\n"; // Settings int64 nTransactionFee = 0; - - -////////////////////////////////////////////////////////////////////////////// -// -// dispatching functions -// - -// These functions dispatch to one or all registered wallets - - -void RegisterWallet(CWallet* pwalletIn) +// Internal stuff +namespace { +struct CBlockIndexWorkComparator { - { - LOCK(cs_setpwalletRegistered); - setpwalletRegistered.insert(pwalletIn); - } -} + bool operator()(CBlockIndex *pa, CBlockIndex *pb) { + if (pa->nChainWork > pb->nChainWork) return false; + if (pa->nChainWork < pb->nChainWork) return true; -void UnregisterWallet(CWallet* pwalletIn) -{ - { - LOCK(cs_setpwalletRegistered); - setpwalletRegistered.erase(pwalletIn); + if (pa->GetBlockHash() < pb->GetBlockHash()) return false; + if (pa->GetBlockHash() > pb->GetBlockHash()) return true; + + return false; // identical blocks } -} +}; -void UnregisterAllWallets() -{ - LOCK(cs_setpwalletRegistered); - setpwalletRegistered.clear(); -} +CBlockIndex *pindexBestInvalid; +set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexValid; // may contain all CBlockIndex*'s that have validness >=BLOCK_VALID_TRANSACTIONS, and must contain those who aren't failed -// get the wallet transaction with the given hash (if it exists) -bool static GetTransaction(const uint256& hashTx, CWalletTx& wtx) -{ - LOCK(cs_setpwalletRegistered); - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) - if (pwallet->GetTransaction(hashTx,wtx)) - return true; - return false; +CCriticalSection cs_LastBlockFile; +CBlockFileInfo infoLastBlockFile; +int nLastBlockFile = 0; } -// erases transaction with the given hash from all wallets -void static EraseFromWallets(uint256 hash) -{ - LOCK(cs_setpwalletRegistered); - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) - pwallet->EraseFromWallet(hash); -} +////////////////////////////////////////////////////////////////////////////// +// +// dispatching functions +// -// make sure all wallets know about the given transaction, in the given block -void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate) -{ - LOCK(cs_setpwalletRegistered); - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) - pwallet->AddToWalletIfInvolvingMe(hash, tx, pblock, fUpdate); -} +// These functions dispatch to one or all registered wallets -// notify wallets about a new best chain -void static SetBestChain(const CBlockLocator& loc) -{ - LOCK(cs_setpwalletRegistered); - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) - pwallet->SetBestChain(loc); +namespace { +struct CMainSignals { + // Notifies listeners of updated transaction data (passing hash, transaction, and optionally the block it is found in. + boost::signals2::signal<void (const uint256 &, const CTransaction &, const CBlock *)> SyncTransaction; + // Notifies listeners of an erased transaction (currently disabled, requires transaction replacement). + boost::signals2::signal<void (const uint256 &)> EraseTransaction; + // Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible). + boost::signals2::signal<void (const uint256 &)> UpdatedTransaction; + // Notifies listeners of a new active block chain. + boost::signals2::signal<void (const CBlockLocator &)> SetBestChain; + // Notifies listeners about an inventory item being seen on the network. + boost::signals2::signal<void (const uint256 &)> Inventory; + // Tells listeners to broadcast their data. + boost::signals2::signal<void ()> Broadcast; +} g_signals; } -// notify wallets about an updated transaction -void static UpdatedTransaction(const uint256& hashTx) -{ - LOCK(cs_setpwalletRegistered); - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) - pwallet->UpdatedTransaction(hashTx); +void RegisterWallet(CWalletInterface* pwalletIn) { + g_signals.SyncTransaction.connect(boost::bind(&CWalletInterface::SyncTransaction, pwalletIn, _1, _2, _3)); + g_signals.EraseTransaction.connect(boost::bind(&CWalletInterface::EraseFromWallet, pwalletIn, _1)); + g_signals.UpdatedTransaction.connect(boost::bind(&CWalletInterface::UpdatedTransaction, pwalletIn, _1)); + g_signals.SetBestChain.connect(boost::bind(&CWalletInterface::SetBestChain, pwalletIn, _1)); + g_signals.Inventory.connect(boost::bind(&CWalletInterface::Inventory, pwalletIn, _1)); + g_signals.Broadcast.connect(boost::bind(&CWalletInterface::ResendWalletTransactions, pwalletIn)); } -// dump all wallets -void static PrintWallets(const CBlock& block) -{ - LOCK(cs_setpwalletRegistered); - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) - pwallet->PrintWallet(block); +void UnregisterWallet(CWalletInterface* pwalletIn) { + g_signals.Broadcast.disconnect(boost::bind(&CWalletInterface::ResendWalletTransactions, pwalletIn)); + g_signals.Inventory.disconnect(boost::bind(&CWalletInterface::Inventory, pwalletIn, _1)); + g_signals.SetBestChain.disconnect(boost::bind(&CWalletInterface::SetBestChain, pwalletIn, _1)); + g_signals.UpdatedTransaction.disconnect(boost::bind(&CWalletInterface::UpdatedTransaction, pwalletIn, _1)); + g_signals.EraseTransaction.disconnect(boost::bind(&CWalletInterface::EraseFromWallet, pwalletIn, _1)); + g_signals.SyncTransaction.disconnect(boost::bind(&CWalletInterface::SyncTransaction, pwalletIn, _1, _2, _3)); } -// notify wallets about an incoming inventory (for request counts) -void static Inventory(const uint256& hash) -{ - LOCK(cs_setpwalletRegistered); - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) - pwallet->Inventory(hash); +void UnregisterAllWallets() { + g_signals.Broadcast.disconnect_all_slots(); + g_signals.Inventory.disconnect_all_slots(); + g_signals.SetBestChain.disconnect_all_slots(); + g_signals.UpdatedTransaction.disconnect_all_slots(); + g_signals.EraseTransaction.disconnect_all_slots(); + g_signals.SyncTransaction.disconnect_all_slots(); } -// ask wallets to resend their transactions -void static ResendWalletTransactions() -{ - LOCK(cs_setpwalletRegistered); - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) - pwallet->ResendWalletTransactions(); +void SyncWithWallets(const uint256 &hash, const CTransaction &tx, const CBlock *pblock) { + g_signals.SyncTransaction(hash, tx, pblock); } ////////////////////////////////////////////////////////////////////////////// @@ -173,106 +147,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(); } ////////////////////////////////////////////////////////////////////////////// @@ -369,6 +320,15 @@ unsigned int CCoinsViewCache::GetCacheSize() { return cacheCoins.size(); } +/** Helper; lookup from tip (used calling mempool.check() + NOTE: code calling this MUST hold the cs_main lock so + another thread doesn't modify pcoinsTip. When we switch + to C++11 this should be replaced by lambda expressions... + **/ +static CCoins &LookupFromTip(const uint256& hash) { + return pcoinsTip->GetCoins(hash); +} + /** CCoinsView that brings transactions from a memorypool into view. It does not check for spendings by memory pool transactions. */ CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { } @@ -376,8 +336,8 @@ CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn) bool CCoinsViewMemPool::GetCoins(const uint256 &txid, CCoins &coins) { if (base->GetCoins(txid, coins)) return true; - if (mempool.exists(txid)) { - const CTransaction &tx = mempool.lookup(txid); + CTransaction tx; + if (mempool.lookup(txid, tx)) { coins = CCoins(tx, MEMPOOL_HEIGHT); return true; } @@ -497,17 +457,28 @@ bool IsStandardTx(const CTransaction& tx, string& reason) return false; } } + + unsigned int nDataOut = 0; + txnouttype whichType; BOOST_FOREACH(const CTxOut& txout, tx.vout) { - if (!::IsStandard(txout.scriptPubKey)) { + if (!::IsStandard(txout.scriptPubKey, whichType)) { reason = "scriptpubkey"; return false; } - if (txout.IsDust(CTransaction::nMinRelayTxFee)) { + if (whichType == TX_NULL_DATA) + nDataOut++; + else if (txout.IsDust(CTransaction::nMinRelayTxFee)) { reason = "dust"; return false; } } + // only one OP_RETURN txout is permitted + if (nDataOut > 1) { + reason = "mucho-data"; + return false; + } + return true; } @@ -517,7 +488,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 +615,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 +649,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; } @@ -750,15 +721,18 @@ int64 GetMinFee(const CTransaction& tx, bool fAllowFree, enum GetMinFee_mode mod { // There is a free transaction area in blocks created by most miners, // * If we are relaying we allow transactions up to DEFAULT_BLOCK_PRIORITY_SIZE - 1000 - // to be considered to fall into this category - // * If we are creating a transaction we allow transactions up to DEFAULT_BLOCK_PRIORITY_SIZE - 17000 - // (= 10000) to be considered safe and assume they can likely make it into this section - if (nBytes < (mode == GMF_SEND ? (DEFAULT_BLOCK_PRIORITY_SIZE - 17000) : (DEFAULT_BLOCK_PRIORITY_SIZE - 1000))) + // to be considered to fall into this category. We don't want to encourage sending + // multiple transactions instead of one big transaction to avoid fees. + // * If we are creating a transaction we allow transactions up to 1,000 bytes + // to be considered safe and assume they can likely make it into this section. + if (nBytes < (mode == GMF_SEND ? 1000 : (DEFAULT_BLOCK_PRIORITY_SIZE - 1000))) nMinFee = 0; } - // To limit dust spam, require base fee if any output is less than 0.01 - if (nMinFee < nBaseFee) + // This code can be removed after enough miners have upgraded to version 0.9. + // Until then, be safe when sending and require a fee if any output + // is less than CENT: + if (nMinFee < nBaseFee && mode == GMF_SEND) { BOOST_FOREACH(const CTxOut& txout, tx.vout) if (txout.nValue < CENT) @@ -770,56 +744,39 @@ int64 GetMinFee(const CTransaction& tx, bool fAllowFree, enum GetMinFee_mode mod return nMinFee; } -void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins) -{ - LOCK(cs); - std::map<COutPoint, CInPoint>::iterator it = mapNextTx.lower_bound(COutPoint(hashTx, 0)); - - // iterate over all COutPoints in mapNextTx whose hash equals the provided hashTx - while (it != mapNextTx.end() && it->first.hash == hashTx) { - coins.Spend(it->first.n); // and remove those outputs from coins - it++; - } -} - -bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fLimitFree, +bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, bool* pfMissingInputs, bool fRejectInsaneFee) { if (pfMissingInputs) *pfMissingInputs = false; if (!CheckTransaction(tx, state)) - return error("CTxMemPool::accept() : CheckTransaction failed"); + return error("AcceptToMemoryPool: : CheckTransaction failed"); // Coinbase is only valid in a block, not as a loose transaction if (tx.IsCoinBase()) - return state.DoS(100, error("CTxMemPool::accept() : coinbase as individual tx")); - - // To help v0.1.5 clients who would see it as a negative number - if ((int64)tx.nLockTime > std::numeric_limits<int>::max()) - return error("CTxMemPool::accept() : not accepting nLockTime beyond 2038 yet"); + return state.DoS(100, error("AcceptToMemoryPool: : coinbase as individual tx")); // Rather not work on nonstandard transactions (unless -testnet/-regtest) string reason; if (Params().NetworkID() == CChainParams::MAIN && !IsStandardTx(tx, reason)) - return error("CTxMemPool::accept() : nonstandard transaction: %s", + return error("AcceptToMemoryPool: : nonstandard transaction: %s", reason.c_str()); // is it already in the memory pool? uint256 hash = tx.GetHash(); - { - LOCK(cs); - if (mapTx.count(hash)) - return false; - } + if (pool.exists(hash)) + return false; // Check for conflicts with in-memory transactions CTransaction* ptxOld = NULL; + { + LOCK(pool.cs); // protect pool.mapNextTx for (unsigned int i = 0; i < tx.vin.size(); i++) { COutPoint outpoint = tx.vin[i].prevout; - if (mapNextTx.count(outpoint)) + if (pool.mapNextTx.count(outpoint)) { // Disable replacement feature for now return false; @@ -827,7 +784,7 @@ bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fL // Allow replacing with a newer version of the same transaction if (i != 0) return false; - ptxOld = mapNextTx[outpoint].ptx; + ptxOld = pool.mapNextTx[outpoint].ptx; if (IsFinalTx(*ptxOld)) return false; if (!tx.IsNewerThan(*ptxOld)) @@ -835,20 +792,21 @@ bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fL for (unsigned int i = 0; i < tx.vin.size(); i++) { COutPoint outpoint = tx.vin[i].prevout; - if (!mapNextTx.count(outpoint) || mapNextTx[outpoint].ptx != ptxOld) + if (!pool.mapNextTx.count(outpoint) || pool.mapNextTx[outpoint].ptx != ptxOld) return false; } break; } } + } { CCoinsView dummy; CCoinsViewCache view(dummy); { - LOCK(cs); - CCoinsViewMemPool viewMemPool(*pcoinsTip, *this); + LOCK(pool.cs); + CCoinsViewMemPool viewMemPool(*pcoinsTip, pool); view.SetBackend(viewMemPool); // do we already have it? @@ -868,7 +826,7 @@ bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fL // are the actual inputs available? if (!view.HaveInputs(tx)) - return state.Invalid(error("CTxMemPool::accept() : inputs already spent")); + return state.Invalid(error("AcceptToMemoryPool: : inputs already spent")); // Bring the best block into scope view.GetBestBlock(); @@ -879,7 +837,7 @@ bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fL // Check for non-standard pay-to-script-hash in inputs if (Params().NetworkID() == CChainParams::MAIN && !AreInputsStandard(tx, view)) - return error("CTxMemPool::accept() : nonstandard transaction input"); + return error("AcceptToMemoryPool: : nonstandard transaction input"); // Note: if you modify this code to accept non-standard transactions, then // you should add code here to check that the transaction does a @@ -891,7 +849,7 @@ bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fL // Don't accept it if it can't get into a block int64 txMinFee = GetMinFee(tx, true, GMF_RELAY); if (fLimitFree && nFees < txMinFee) - return error("CTxMemPool::accept() : not enough fees %s, %"PRI64d" < %"PRI64d, + return error("AcceptToMemoryPool: : not enough fees %s, %"PRI64d" < %"PRI64d, hash.ToString().c_str(), nFees, txMinFee); @@ -900,11 +858,12 @@ bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fL // be annoying or make others' transactions take longer to confirm. if (fLimitFree && nFees < CTransaction::nMinRelayTxFee) { + static CCriticalSection csFreeLimiter; static double dFreeCount; static int64 nLastTime; int64 nNow = GetTime(); - LOCK(cs); + LOCK(csFreeLimiter); // Use an exponentially decaying ~10-minute window: dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime)); @@ -912,14 +871,13 @@ bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fL // -limitfreerelay unit is thousand-bytes-per-minute // At default rate it would take over a month to fill 1GB if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000) - return error("CTxMemPool::accept() : free transaction rejected by rate limiter"); - if (fDebug) - LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); + return error("AcceptToMemoryPool: : free transaction rejected by rate limiter"); + LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); dFreeCount += nSize; } if (fRejectInsaneFee && nFees > CTransaction::nMinRelayTxFee * 10000) - return error("CTxMemPool::accept() : insane fees %s, %"PRI64d" > %"PRI64d, + return error("AcceptToMemoryPool: : insane fees %s, %"PRI64d" > %"PRI64d, hash.ToString().c_str(), nFees, CTransaction::nMinRelayTxFee * 10000); @@ -927,146 +885,32 @@ bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fL // This is done last to help prevent CPU exhaustion denial-of-service attacks. if (!CheckInputs(tx, state, view, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC)) { - return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().c_str()); + return error("AcceptToMemoryPool: : ConnectInputs failed %s", hash.ToString().c_str()); } } // Store transaction in memory { - LOCK(cs); if (ptxOld) { - LogPrint("mempool", "CTxMemPool::accept() : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str()); - remove(*ptxOld); + LogPrint("mempool", "AcceptToMemoryPool: : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str()); + pool.remove(*ptxOld); } - addUnchecked(hash, tx); + pool.addUnchecked(hash, tx); } ///// are we sure this is ok when loading transactions or restoring block txes // If updated, erase old tx from wallet if (ptxOld) - EraseFromWallets(ptxOld->GetHash()); - SyncWithWallets(hash, tx, NULL, true); - - LogPrint("mempool", "CTxMemPool::accept() : accepted %s (poolsz %"PRIszu")\n", - hash.ToString().c_str(), - mapTx.size()); - return true; -} - - -bool CTxMemPool::addUnchecked(const uint256& hash, const CTransaction &tx) -{ - // Add to memory pool without checking anything. Don't call this directly, - // call CTxMemPool::accept to properly check the transaction first. - { - mapTx[hash] = tx; - for (unsigned int i = 0; i < tx.vin.size(); i++) - mapNextTx[tx.vin[i].prevout] = CInPoint(&mapTx[hash], i); - nTransactionsUpdated++; - } - return true; -} - - -bool CTxMemPool::remove(const CTransaction &tx, bool fRecursive) -{ - // Remove transaction from memory pool - { - LOCK(cs); - uint256 hash = tx.GetHash(); - if (fRecursive) { - for (unsigned int i = 0; i < tx.vout.size(); i++) { - std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(COutPoint(hash, i)); - if (it != mapNextTx.end()) - remove(*it->second.ptx, true); - } - } - if (mapTx.count(hash)) - { - BOOST_FOREACH(const CTxIn& txin, tx.vin) - mapNextTx.erase(txin.prevout); - mapTx.erase(hash); - nTransactionsUpdated++; - } - } - return true; -} + g_signals.EraseTransaction(ptxOld->GetHash()); + g_signals.SyncTransaction(hash, tx, NULL); -bool CTxMemPool::removeConflicts(const CTransaction &tx) -{ - // Remove transactions which depend on inputs of tx, recursively - LOCK(cs); - BOOST_FOREACH(const CTxIn &txin, tx.vin) { - std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(txin.prevout); - if (it != mapNextTx.end()) { - const CTransaction &txConflict = *it->second.ptx; - if (txConflict != tx) - remove(txConflict, true); - } - } + LogPrint("mempool", "AcceptToMemoryPool: : accepted %s (poolsz %"PRIszu")\n", + hash.ToString().c_str(), + pool.mapTx.size()); return true; } -void CTxMemPool::clear() -{ - LOCK(cs); - mapTx.clear(); - mapNextTx.clear(); - ++nTransactionsUpdated; -} - -bool CTxMemPool::fChecks = false; - -void CTxMemPool::check(CCoinsViewCache *pcoins) const -{ - if (!fChecks) - return; - - LogPrintf("Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size()); - - LOCK(cs); - for (std::map<uint256, CTransaction>::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { - unsigned int i = 0; - BOOST_FOREACH(const CTxIn &txin, it->second.vin) { - // Check that every mempool transaction's inputs refer to available coins, or other mempool tx's. - std::map<uint256, CTransaction>::const_iterator it2 = mapTx.find(txin.prevout.hash); - if (it2 != mapTx.end()) { - assert(it2->second.vout.size() > txin.prevout.n && !it2->second.vout[txin.prevout.n].IsNull()); - } else { - CCoins &coins = pcoins->GetCoins(txin.prevout.hash); - assert(coins.IsAvailable(txin.prevout.n)); - } - // Check whether its inputs are marked in mapNextTx. - std::map<COutPoint, CInPoint>::const_iterator it3 = mapNextTx.find(txin.prevout); - assert(it3 != mapNextTx.end()); - assert(it3->second.ptx == &it->second); - assert(it3->second.n == i); - i++; - } - } - for (std::map<COutPoint, CInPoint>::const_iterator it = mapNextTx.begin(); it != mapNextTx.end(); it++) { - uint256 hash = it->second.ptx->GetHash(); - std::map<uint256, CTransaction>::const_iterator it2 = mapTx.find(hash); - assert(it2 != mapTx.end()); - assert(&it2->second == it->second.ptx); - assert(it2->second.vin.size() > it->second.n); - assert(it->first == it->second.ptx->vin[it->second.n].prevout); - } -} - -void CTxMemPool::queryHashes(std::vector<uint256>& vtxid) -{ - vtxid.clear(); - - LOCK(cs); - vtxid.reserve(mapTx.size()); - for (map<uint256, CTransaction>::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi) - vtxid.push_back((*mi).first); -} - - - int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const { @@ -1078,7 +922,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 +934,7 @@ int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const } pindexRet = pindex; - return pindexBest->nHeight - pindex->nHeight + 1; + return chainActive.Height() - pindex->nHeight + 1; } @@ -1105,28 +949,7 @@ int CMerkleTx::GetBlocksToMaturity() const bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree) { CValidationState state; - return mempool.accept(state, *this, fLimitFree, NULL); -} - - - -bool CWalletTx::AcceptWalletTransaction() -{ - { - LOCK(mempool.cs); - // Add previous supporting transactions first - BOOST_FOREACH(CMerkleTx& tx, vtxPrev) - { - if (!tx.IsCoinBase()) - { - uint256 hash = tx.GetHash(); - if (!mempool.exists(hash) && pcoinsTip->HaveCoins(hash)) - tx.AcceptToMemoryPool(false); - } - } - return AcceptToMemoryPool(false); - } - return false; + return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL); } @@ -1137,10 +960,8 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock { LOCK(cs_main); { - LOCK(mempool.cs); - if (mempool.exists(hash)) + if (mempool.lookup(hash, txOut)) { - txOut = mempool.lookup(hash); return true; } } @@ -1173,7 +994,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock nHeight = coins.nHeight; } if (nHeight > 0) - pindexSlow = FindBlockByHeight(nHeight); + pindexSlow = chainActive[nHeight]; } } @@ -1203,14 +1024,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 +1217,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 +1243,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 || (pindexBestInvalid && pindexBestInvalid->nChainWork > chainActive.Tip()->nChainWork + (chainActive.Tip()->GetBlockWork() * 6).getuint256())) { if (!fLargeWorkForkFound) { @@ -1470,7 +1283,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 +1302,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; @@ -1500,10 +1313,13 @@ void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) void static InvalidChainFound(CBlockIndex* pindexNew) { - if (pindexNew->nChainWork > nBestInvalidWork) + if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork) { - nBestInvalidWork = pindexNew->nChainWork; - pblocktree->WriteBestInvalidWork(CBigNum(nBestInvalidWork)); + pindexBestInvalid = pindexNew; + // The current code doesn't actually read the BestInvalidWork entry in + // the block database anymore, as it is derived from the flags in block + // index entry. We only write it for backward compatibility. + pblocktree->WriteBestInvalidWork(CBigNum(pindexBestInvalid->nChainWork)); uiInterface.NotifyBlocksChanged(); } LogPrintf("InvalidChainFound: invalid block=%s height=%d log2_work=%.8g date=%s\n", @@ -1511,8 +1327,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 +1337,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 +1354,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 +1374,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(); @@ -1772,12 +1588,12 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex const CTransaction &tx = block.vtx[i]; uint256 hash = tx.GetHash(); - // check that all outputs are available - if (!view.HaveCoins(hash)) { - fClean = fClean && error("DisconnectBlock() : outputs still spent? database corrupted"); - view.SetCoins(hash, CCoins()); - } - CCoins &outs = view.GetCoins(hash); + // Check that all outputs are available and match the outputs in the block itself + // exactly. Note that transactions with only provably unspendable outputs won't + // have outputs available even in the block itself, so we handle that case + // specially with outsEmpty. + CCoins outsEmpty; + CCoins &outs = view.HaveCoins(hash) ? view.GetCoins(hash) : outsEmpty; outs.ClearUnspendable(); CCoins outsBlock = CCoins(tx, pindex->nHeight); @@ -1881,7 +1697,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; } @@ -2015,14 +1830,14 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C // Watch for transactions paying to me for (unsigned int i = 0; i < block.vtx.size(); i++) - SyncWithWallets(block.GetTxHash(i), block.vtx[i], &block, true); + g_signals.SyncTransaction(block.GetTxHash(i), block.vtx[i], &block); return true; } bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) { - mempool.check(pcoinsTip); + mempool.check(&LookupFromTip); // All modifications to the coin state will be done in this cache. // Only when all have succeeded, we push it to pcoinsTip. @@ -2129,15 +1944,13 @@ 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) { // ignore validation errors in resurrected transactions CValidationState stateDummy; - if (!mempool.accept(stateDummy, tx, false, NULL)) + if (!AcceptToMemoryPool(mempool,stateDummy, tx, false, NULL)) mempool.remove(tx, true); } @@ -2147,33 +1960,25 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) mempool.removeConflicts(tx); } - mempool.check(pcoinsTip); + mempool.check(&LookupFromTip); // 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); - } + g_signals.SetBestChain(chainActive.GetLocator(pindexNew)); // New best block - hashBestChain = pindexNew->GetBlockHash(); - pindexBest = pindexNew; - pblockindexFBBHLast = NULL; - nBestHeight = pindexBest->nHeight; - nBestChainWork = pindexNew->nChainWork; nTimeBestReceived = GetTime(); - nTransactionsUpdated++; + mempool.AddTransactionsUpdated(1); 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 +1996,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,13 +2038,13 @@ 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(); // Notify UI to display prev block's coinbase if it was ours static uint256 hashPrevBestCoinBase; - UpdatedTransaction(hashPrevBestCoinBase); + g_signals.UpdatedTransaction(hashPrevBestCoinBase); hashPrevBestCoinBase = block.GetTxHash(0); } else CheckForkWarningConditionsOnNewFork(pindexNew); @@ -2388,7 +2193,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo uniqueTx.insert(block.GetTxHash(i)); } if (uniqueTx.size() != block.vtx.size()) - return state.DoS(100, error("CheckBlock() : duplicate transaction")); + return state.DoS(100, error("CheckBlock() : duplicate transaction"), true); unsigned int nSigOps = 0; BOOST_FOREACH(const CTransaction& tx, block.vtx) @@ -2482,11 +2287,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 +2310,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 +2330,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 +2347,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 +2378,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; } @@ -2774,10 +2591,6 @@ bool CheckDiskSpace(uint64 nAdditionalBytes) return true; } -CCriticalSection cs_LastBlockFile; -CBlockFileInfo infoLastBlockFile; -int nLastBlockFile = 0; - FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly) { if (pos.IsNull()) @@ -2852,6 +2665,8 @@ bool static LoadBlockIndexDB() pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx; if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS && !(pindex->nStatus & BLOCK_FAILED_MASK)) setBlockIndexValid.insert(pindex); + if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork)) + pindexBestInvalid = pindex; } // Load block file info @@ -2860,11 +2675,6 @@ bool static LoadBlockIndexDB() if (pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile)) LogPrintf("LoadBlockIndexDB(): last block file info: %s\n", infoLastBlockFile.ToString().c_str()); - // Load nBestInvalidWork, OK if it doesn't exist - CBigNum bnBestInvalidWork; - pblocktree->ReadBestInvalidWork(bnBestInvalidWork); - nBestInvalidWork = bnBestInvalidWork.getuint256(); - // Check whether we need to continue reindexing bool fReindexing = false; pblocktree->ReadReindexing(fReindexing); @@ -2874,49 +2684,38 @@ bool static LoadBlockIndexDB() pblocktree->ReadFlag("txindex", fTxIndex); LogPrintf("LoadBlockIndexDB(): transaction index %s\n", fTxIndex ? "enabled" : "disabled"); - // Load hashBestChain pointer to end of best chain - pindexBest = pcoinsTip->GetBestBlock(); - if (pindexBest == NULL) + // Load pointer to end of best chain + 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 +2747,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 +2763,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 +2772,8 @@ void UnloadBlockIndex() { mapBlockIndex.clear(); setBlockIndexValid.clear(); - pindexGenesisBlock = NULL; - nBestHeight = 0; - nBestChainWork = 0; - nBestInvalidWork = 0; - hashBestChain = 0; - pindexBest = NULL; + chainActive.SetTip(NULL); + pindexBestInvalid = NULL; } bool LoadBlockIndex() @@ -2992,7 +2787,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 +2833,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()) @@ -3075,13 +2870,11 @@ void PrintBlockTree() DateTimeStrFormat("%Y-%m-%d %H:%M:%S", block.GetBlockTime()).c_str(), block.vtx.size()); - PrintWallets(block); - // put the main time-chain first 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; @@ -3255,10 +3048,7 @@ bool static AlreadyHave(const CInv& inv) case MSG_TX: { bool txInMap = false; - { - LOCK(mempool.cs); - txInMap = mempool.exists(inv.hash); - } + txInMap = mempool.exists(inv.hash); return txInMap || mapOrphanTransactions.count(inv.hash) || pcoinsTip->HaveCoins(inv.hash); } @@ -3279,6 +3069,8 @@ void static ProcessGetData(CNode* pfrom) vector<CInv> vNotFound; + LOCK(cs_main); + while (it != pfrom->vRecvGetData.end()) { // Don't bother if send buffer is too full to respond anyway if (pfrom->nSendSize >= SendBufferSize()) @@ -3328,7 +3120,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; } @@ -3347,9 +3139,8 @@ void static ProcessGetData(CNode* pfrom) } } if (!pushed && inv.type == MSG_TX) { - LOCK(mempool.cs); - if (mempool.exists(inv.hash)) { - CTransaction tx = mempool.lookup(inv.hash); + CTransaction tx; + if (mempool.lookup(inv.hash, tx)) { CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss.reserve(1000); ss << tx; @@ -3363,7 +3154,10 @@ void static ProcessGetData(CNode* pfrom) } // Track requests for our stuff. - Inventory(inv.hash); + g_signals.Inventory(inv.hash); + + if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) + break; } } @@ -3451,7 +3245,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->fClient = !(pfrom->nServices & NODE_NETWORK); - AddTimeData(pfrom->addr, nTime); // Change version pfrom->PushMessage("verack"); @@ -3493,6 +3286,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) LogPrintf("receive version message: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", pfrom->nVersion, pfrom->nStartingHeight, addrMe.ToString().c_str(), addrFrom.ToString().c_str(), pfrom->addr.ToString().c_str()); + AddTimeData(pfrom->addr, nTime); + + LOCK(cs_main); cPeerBlockCounts.input(pfrom->nStartingHeight); } @@ -3596,6 +3392,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) break; } } + + LOCK(cs_main); + for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) { const CInv &inv = vInv[nInv]; @@ -3610,7 +3409,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 @@ -3621,7 +3420,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } // Track requests for our stuff - Inventory(inv.hash); + g_signals.Inventory(inv.hash); } } @@ -3636,10 +3435,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) return error("message getdata size() = %"PRIszu"", vInv.size()); } - if (fDebugNet || (vInv.size() != 1)) + if (fDebug || (vInv.size() != 1)) LogPrint("net", "received getdata (%"PRIszu" invsz)\n", vInv.size()); - if ((fDebugNet && vInv.size() > 0) || (vInv.size() == 1)) + if ((fDebug && vInv.size() > 0) || (vInv.size() == 1)) LogPrint("net", "received getdata for: %s\n", vInv[0].ToString().c_str()); pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end()); @@ -3653,15 +3452,17 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) uint256 hashStop; vRecv >> locator >> hashStop; + LOCK(cs_main); + // 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) { @@ -3687,6 +3488,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) uint256 hashStop; vRecv >> locator >> hashStop; + LOCK(cs_main); + CBlockIndex* pindex = NULL; if (locator.IsNull()) { @@ -3699,16 +3502,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) @@ -3722,18 +3525,19 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { vector<uint256> vWorkQueue; vector<uint256> vEraseQueue; - CDataStream vMsg(vRecv); CTransaction tx; vRecv >> tx; CInv inv(MSG_TX, tx.GetHash()); pfrom->AddInventoryKnown(inv); + LOCK(cs_main); + bool fMissingInputs = false; CValidationState state; - if (mempool.accept(state, tx, true, &fMissingInputs)) + if (AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs)) { - mempool.check(pcoinsTip); + mempool.check(&LookupFromTip); RelayTransaction(tx, inv.hash); mapAlreadyAskedFor.erase(inv); vWorkQueue.push_back(inv.hash); @@ -3755,7 +3559,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // anyone relaying LegitTxX banned) CValidationState stateDummy; - if (mempool.accept(stateDummy, orphanTx, true, &fMissingInputs2)) + if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2)) { LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString().c_str()); RelayTransaction(orphanTx, orphanHash); @@ -3769,7 +3573,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vEraseQueue.push_back(orphanHash); LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString().c_str()); } - mempool.check(pcoinsTip); + mempool.check(&LookupFromTip); } } @@ -3786,8 +3590,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted); } int nDoS = 0; - if (state.IsInvalid(nDoS) && nDoS > 0) - pfrom->Misbehaving(nDoS); + if (state.IsInvalid(nDoS)) + if (nDoS > 0) + pfrom->Misbehaving(nDoS); } @@ -3802,12 +3607,15 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CInv inv(MSG_BLOCK, block.GetHash()); pfrom->AddInventoryKnown(inv); + LOCK(cs_main); + CValidationState state; - if (ProcessBlock(state, pfrom, &block)) + if (ProcessBlock(state, pfrom, &block) || state.CorruptionPossible()) mapAlreadyAskedFor.erase(inv); int nDoS = 0; - if (state.IsInvalid(nDoS) && nDoS > 0) - pfrom->Misbehaving(nDoS); + if (state.IsInvalid(nDoS)) + if (nDoS > 0) + pfrom->Misbehaving(nDoS); } @@ -3822,17 +3630,23 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) else if (strCommand == "mempool") { + LOCK2(cs_main, pfrom->cs_filter); + std::vector<uint256> vtxid; - LOCK2(mempool.cs, pfrom->cs_filter); mempool.queryHashes(vtxid); vector<CInv> vInv; BOOST_FOREACH(uint256& hash, vtxid) { CInv inv(MSG_TX, hash); - if ((pfrom->pfilter && pfrom->pfilter->IsRelevantAndUpdate(mempool.lookup(hash), hash)) || + CTransaction tx; + bool fInMemPool = mempool.lookup(hash, tx); + if (!fInMemPool) continue; // another thread removed since queryHashes, maybe... + if ((pfrom->pfilter && pfrom->pfilter->IsRelevantAndUpdate(tx, hash)) || (!pfrom->pfilter)) vInv.push_back(inv); - if (vInv.size() == MAX_INV_SZ) - break; + if (vInv.size() == MAX_INV_SZ) { + pfrom->PushMessage("inv", vInv); + vInv.clear(); + } } if (vInv.size() > 0) pfrom->PushMessage("inv", vInv); @@ -3861,6 +3675,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 +3830,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 @@ -3973,7 +3844,10 @@ bool ProcessMessages(CNode* pfrom) if (!pfrom->vRecvGetData.empty()) ProcessGetData(pfrom); - + + // this maintains the order of responses + if (!pfrom->vRecvGetData.empty()) return fOk; + std::deque<CNetMessage>::iterator it = pfrom->vRecvMsg.begin(); while (!pfrom->fDisconnect && it != pfrom->vRecvMsg.end()) { // Don't bother if send buffer is too full to respond anyway @@ -3984,7 +3858,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"); @@ -4030,10 +3904,7 @@ bool ProcessMessages(CNode* pfrom) bool fRet = false; try { - { - LOCK(cs_main); - fRet = ProcessMessage(pfrom, strCommand, vRecv); - } + fRet = ProcessMessage(pfrom, strCommand, vRecv); boost::this_thread::interruption_point(); } catch (std::ios_base::failure& e) @@ -4064,6 +3935,8 @@ bool ProcessMessages(CNode* pfrom) if (!fRet) LogPrintf("ProcessMessage(%s, %u bytes) FAILED\n", strCommand.c_str(), nMessageSize); + + break; } // In case the connection got shut down, its receive buffer was wiped @@ -4076,34 +3949,39 @@ bool ProcessMessages(CNode* pfrom) bool SendMessages(CNode* pto, bool fSendTrickle) { - TRY_LOCK(cs_main, lockMain); - if (lockMain) { + { // Don't send anything until we get their version message 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)); - } - - // Resend wallet transactions that haven't gotten in a block yet - // Except during reindex, importing and IBD, when old wallet - // transactions become unconfirmed and spams other nodes. - if (!fReindex && !fImporting && !IsInitialBlockDownload()) - { - ResendWalletTransactions(); + } } // Address refresh broadcast @@ -4156,6 +4034,23 @@ bool SendMessages(CNode* pto, bool fSendTrickle) pto->PushMessage("addr", vAddr); } + TRY_LOCK(cs_main, lockMain); + if (!lockMain) + return true; + + // Start block sync + if (pto->fStartSync && !fImporting && !fReindex) { + pto->fStartSync = false; + PushGetBlocks(pto, chainActive.Tip(), uint256(0)); + } + + // Resend wallet transactions that haven't gotten in a block yet + // Except during reindex, importing and IBD, when old wallet + // transactions become unconfirmed and spams other nodes. + if (!fReindex && !fImporting && !IsInitialBlockDownload()) + { + g_signals.Broadcast(); + } // // Message: inventory @@ -4182,15 +4077,6 @@ bool SendMessages(CNode* pto, bool fSendTrickle) hashRand = Hash(BEGIN(hashRand), END(hashRand)); bool fTrickleWait = ((hashRand & 3) != 0); - // always trickle our own transactions - if (!fTrickleWait) - { - CWalletTx wtx; - if (GetTransaction(inv.hash, wtx)) - if (wtx.fFromMe) - fTrickleWait = true; - } - if (fTrickleWait) { vInvWait.push_back(inv); @@ -4225,7 +4111,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) const CInv& inv = (*pto->mapAskFor.begin()).second; if (!AlreadyHave(inv)) { - if (fDebugNet) + if (fDebug) LogPrint("net", "sending getdata: %s\n", inv.ToString().c_str()); vGetData.push_back(inv); if (vGetData.size() >= 1000) diff --git a/src/main.h b/src/main.h index 83b0d07f63..80972330f3 100644 --- a/src/main.h +++ b/src/main.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. #ifndef BITCOIN_MAIN_H @@ -12,22 +12,19 @@ #include "core.h" #include "bignum.h" #include "sync.h" +#include "txmempool.h" #include "net.h" #include "script.h" #include <list> -class CWallet; class CBlock; class CBlockIndex; -class CKeyItem; -class CReserveKey; - -class CAddress; class CInv; +class CKeyItem; class CNode; - -struct CBlockIndexWorkComparator; +class CReserveKey; +class CWallet; /** The maximum allowed size for a serialized block, in bytes (network rule) */ static const unsigned int MAX_BLOCK_SIZE = 1000000; @@ -73,22 +70,12 @@ extern CScript COINBASE_FLAGS; extern CCriticalSection cs_main; +extern CTxMemPool mempool; 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; extern const std::string strMessageMagic; extern int64 nTimeBestReceived; -extern CCriticalSection cs_setpwalletRegistered; -extern std::set<CWallet*> setpwalletRegistered; extern bool fImporting; extern bool fReindex; extern bool fBenchmark; @@ -114,17 +101,18 @@ class CCoinsView; class CCoinsViewCache; class CScriptCheck; class CValidationState; +class CWalletInterface; struct CBlockTemplate; /** Register a wallet to receive updates from core */ -void RegisterWallet(CWallet* pwalletIn); +void RegisterWallet(CWalletInterface* pwalletIn); /** Unregister a wallet from core */ -void UnregisterWallet(CWallet* pwalletIn); +void UnregisterWallet(CWalletInterface* pwalletIn); /** Unregister all wallets from core */ void UnregisterAllWallets(); /** Push an updated transaction to all registered wallets */ -void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false); +void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock = NULL); /** Register with a network node to receive its signals */ void RegisterNodeSignals(CNodeSignals& nodeSignals); @@ -153,8 +141,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 */ @@ -189,6 +175,9 @@ bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned in /** Abort with a message */ bool AbortNode(const std::string &msg); +/** (try to) add transaction to memory pool **/ +bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, + bool* pfMissingInputs, bool fRejectInsaneFee=false); @@ -197,10 +186,6 @@ bool AbortNode(const std::string &msg); - - -bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut); - struct CDiskBlockPos { int nFile; @@ -660,10 +645,6 @@ public: } }; -extern CCriticalSection cs_LastBlockFile; -extern CBlockFileInfo infoLastBlockFile; -extern int nLastBlockFile; - enum BlockStatus { BLOCK_VALID_UNKNOWN = 0, BLOCK_VALID_HEADER = 1, // parsed, version ok, hash satisfies claimed PoW, 1 <= vtx count <= max, timestamp not in future @@ -819,15 +800,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 +821,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 +832,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()); } @@ -882,19 +844,6 @@ public: } }; -struct CBlockIndexWorkComparator -{ - bool operator()(CBlockIndex *pa, CBlockIndex *pb) { - if (pa->nChainWork > pb->nChainWork) return false; - if (pa->nChainWork < pb->nChainWork) return true; - - if (pa->GetBlockHash() < pb->GetBlockHash()) return false; - if (pa->GetBlockHash() > pb->GetBlockHash()) return true; - - return false; // identical blocks - } -}; - /** Used to marshal pointers into hashes for db storage. */ @@ -973,13 +922,15 @@ private: MODE_ERROR, // run-time error } mode; int nDoS; + bool corruptionPossible; public: CValidationState() : mode(MODE_VALID), nDoS(0) {} - bool DoS(int level, bool ret = false) { + bool DoS(int level, bool ret = false, bool corruptionIn = false) { if (mode == MODE_ERROR) return ret; nDoS += level; mode = MODE_INVALID; + corruptionPossible = corruptionIn; return ret; } bool Invalid(bool ret = false) { @@ -1009,108 +960,76 @@ public: } return false; } + bool CorruptionPossible() { + return corruptionPossible; + } }; +/** 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() {} - - explicit CBlockLocator(const CBlockIndex* pindex) - { - Set(pindex); + /** 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(uint256 hashBlock); - - CBlockLocator(const std::vector<uint256>& vHaveIn) - { - vHave = vHaveIn; + /** 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; } - IMPLEMENT_SERIALIZE - ( - if (!(nType & SER_GETHASH)) - READWRITE(nVersion); - READWRITE(vHave); - ) + /** 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]; + } - void SetNull() - { - vHave.clear(); + /** 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]; } - bool IsNull() - { - return vHave.empty(); + /** Efficiently check whether a block is present in this chain. */ + bool Contains(const CBlockIndex *pindex) const { + return (*this)[pindex->nHeight] == pindex; } - /** 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(); -}; + /** 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; + } + /** Return the maximal height in the chain. Is equal to chain.Tip() ? chain.Tip()->nHeight : -1. */ + int Height() const { + return vChain.size() - 1; + } + /** 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; -class CTxMemPool -{ -public: - static bool fChecks; - mutable CCriticalSection cs; - std::map<uint256, CTransaction> mapTx; - std::map<COutPoint, CInPoint> mapNextTx; - - bool accept(CValidationState &state, const CTransaction &tx, bool fLimitFree, bool* pfMissingInputs, bool fRejectInsaneFee = false); - bool addUnchecked(const uint256& hash, const CTransaction &tx); - bool remove(const CTransaction &tx, bool fRecursive = false); - bool removeConflicts(const CTransaction &tx); - void clear(); - void queryHashes(std::vector<uint256>& vtxid); - void pruneSpent(const uint256& hash, CCoins &coins); - void check(CCoinsViewCache *pcoins) const; - - unsigned long size() - { - LOCK(cs); - return mapTx.size(); - } - bool exists(uint256 hash) - { - return (mapTx.count(hash) != 0); - } - CTransaction& lookup(uint256 hash) - { - return mapTx[hash]; - } -}; -extern CTxMemPool mempool; struct CCoinsStats { @@ -1280,4 +1199,18 @@ public: ) }; + +class CWalletInterface { +protected: + virtual void SyncTransaction(const uint256 &hash, const CTransaction &tx, const CBlock *pblock) =0; + virtual void EraseFromWallet(const uint256 &hash) =0; + virtual void SetBestChain(const CBlockLocator &locator) =0; + virtual void UpdatedTransaction(const uint256 &hash) =0; + virtual void Inventory(const uint256 &hash) =0; + virtual void ResendWalletTransactions() =0; + friend void ::RegisterWallet(CWalletInterface*); + friend void ::UnregisterWallet(CWalletInterface*); + friend void ::UnregisterAllWallets(); +}; + #endif diff --git a/src/miner.cpp b/src/miner.cpp index 30c600071f..b03f915dc9 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. @@ -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 @@ -238,9 +238,21 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) } if (fMissingInputs) continue; - // Priority is sum(valuein * age) / txsize + // Priority is sum(valuein * age) / modified_txsize unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); - dPriority /= nTxSize; + unsigned int nTxSizeMod = nTxSize; + // In order to avoid disincentivizing cleaning up the UTXO set we don't count + // the constant overhead for each txin and up to 110 bytes of scriptSig (which + // is enough to cover a compressed pubkey p2sh redemption) for priority. + // Providing any more cleanup incentive than making additional inputs free would + // risk encouraging people to create junk outputs to redeem later. + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + unsigned int offset = 41U + min(110U, (unsigned int)txin.scriptSig.size()); + if (nTxSizeMod > offset) + nTxSizeMod -= offset; + } + dPriority /= nTxSizeMod; // This is a more accurate fee-per-kilobyte than is used by the client code, because the // client code rounds up the size to the nearest 1K. That's good, because it gives an @@ -467,7 +479,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 @@ -509,8 +521,8 @@ void static BitcoinMiner(CWallet *pwallet) // // Create new block // - unsigned int nTransactionsUpdatedLast = nTransactionsUpdated; - CBlockIndex* pindexPrev = pindexBest; + unsigned int nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); + CBlockIndex* pindexPrev = chainActive.Tip(); auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey(reservekey)); if (!pblocktemplate.get()) @@ -611,9 +623,9 @@ void static BitcoinMiner(CWallet *pwallet) break; if (nBlockNonce >= 0xffff0000) break; - if (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60) + if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60) break; - if (pindexPrev != pindexBest) + if (pindexPrev != chainActive.Tip()) break; // Update nTime every few seconds diff --git a/src/miner.h b/src/miner.h index 7e60b9e53b..a2800341d8 100644 --- a/src/miner.h +++ b/src/miner.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. #ifndef BITCOIN_MINER_H diff --git a/src/net.cpp b/src/net.cpp index 781dbfadca..8c0ada8f92 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. @@ -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,24 @@ 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); + + // Leave string empty if addrLocal invalid (not filled in yet) + stats.addrLocal = addrLocal.IsValid() ? addrLocal.ToString() : ""; } #undef X @@ -716,6 +738,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(); @@ -781,7 +804,8 @@ void ThreadSocketHandler() vNodesDisconnected.push_back(pnode); } } - + } + { // Delete disconnected nodes list<CNode*> vNodesDisconnectedCopy = vNodesDisconnected; BOOST_FOREACH(CNode* pnode, vNodesDisconnectedCopy) @@ -811,10 +835,9 @@ void ThreadSocketHandler() } } } - if (vNodes.size() != nPrevNodeCount) - { + if(vNodes.size() != nPrevNodeCount) { nPrevNodeCount = vNodes.size(); - uiInterface.NotifyNumConnectionsChanged(vNodes.size()); + uiInterface.NotifyNumConnectionsChanged(nPrevNodeCount); } @@ -993,6 +1016,7 @@ void ThreadSocketHandler() pnode->CloseSocketDisconnect(); pnode->nLastRecv = GetTime(); pnode->nRecvBytes += nBytes; + pnode->RecordBytesRecv(nBytes); } else if (nBytes == 0) { @@ -1467,6 +1491,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 @@ -1514,6 +1540,9 @@ void ThreadMessageHandler() CNode* pnodeTrickle = NULL; if (!vNodesCopy.empty()) pnodeTrickle = vNodesCopy[GetRand(vNodesCopy.size())]; + + bool fSleep = true; + BOOST_FOREACH(CNode* pnode, vNodesCopy) { if (pnode->fDisconnect) @@ -1523,8 +1552,18 @@ void ThreadMessageHandler() { TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); if (lockRecv) + { if (!g_signals.ProcessMessages(pnode)) pnode->CloseSocketDisconnect(); + + if (pnode->nSendSize < SendBufferSize()) + { + if (!pnode->vRecvGetData.empty() || (!pnode->vRecvMsg.empty() && pnode->vRecvMsg[0].complete())) + { + fSleep = false; + } + } + } } boost::this_thread::interruption_point(); @@ -1542,8 +1581,9 @@ void ThreadMessageHandler() BOOST_FOREACH(CNode* pnode, vNodesCopy) pnode->Release(); } - - MilliSleep(100); + + if (fSleep) + MilliSleep(100); } } @@ -1844,3 +1884,62 @@ 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; +} + +void CNode::Fuzz(int nChance) +{ + if (!fSuccessfullyConnected) return; // Don't fuzz initial handshake + if (GetRand(nChance) != 0) return; // Fuzz 1 of every nChance messages + + switch (GetRand(3)) + { + case 0: + // xor a random byte with a random value: + if (!ssSend.empty()) { + CDataStream::size_type pos = GetRand(ssSend.size()); + ssSend[pos] ^= (unsigned char)(GetRand(256)); + } + break; + case 1: + // delete a random byte: + if (!ssSend.empty()) { + CDataStream::size_type pos = GetRand(ssSend.size()); + ssSend.erase(ssSend.begin()+pos); + } + break; + case 2: + // insert a random byte at a random position + { + CDataStream::size_type pos = GetRand(ssSend.size()); + char ch = (char)GetRand(256); + ssSend.insert(ssSend.begin()+pos, ch); + } + break; + } + // Chance of more than one change half the time: + // (more changes exponentially less likely): + Fuzz(2); +} @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. #ifndef BITCOIN_NET_H @@ -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,9 @@ public: uint64 nSendBytes; uint64 nRecvBytes; bool fSyncNode; + double dPingTime; + double dPingWait; + std::string addrLocal; }; @@ -215,6 +218,9 @@ protected: static CCriticalSection cs_setBanned; int nMisbehavior; + // Basic fuzz-testing + void Fuzz(int nChance); // modifies ssSend + public: uint256 hashContinue; CBlockIndex* pindexLastGetBlocksBegin; @@ -234,6 +240,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 +280,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 +302,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 +324,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; } @@ -414,12 +437,17 @@ public: // TODO: Document the precondition of this function. Is cs_vSend locked? void EndMessage() UNLOCK_FUNCTION(cs_vSend) { - if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) + // The -*messagestest options are intentionally not documented in the help message, + // since they are only used during development to debug the networking code and are + // not intended for end-users. + if (mapArgs.count("-dropmessagestest") && GetRand(GetArg("-dropmessagestest", 2)) == 0) { LogPrint("net", "dropmessages DROPPING SEND MESSAGE\n"); AbortMessage(); return; } + if (mapArgs.count("-fuzzmessagestest")) + Fuzz(GetArg("-fuzzmessagestest", 10)); if (ssSend.size() == 0) return; @@ -634,6 +662,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..36b90e0d4c 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. @@ -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 e1f80b4ee8..c14fa5075d 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. #ifndef BITCOIN_NETBASE_H diff --git a/src/noui.cpp b/src/noui.cpp index 67eac944c6..06c507d0e5 100644 --- a/src/noui.cpp +++ b/src/noui.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. diff --git a/src/protocol.cpp b/src/protocol.cpp index ba254be3e1..a841bbc14e 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. diff --git a/src/protocol.h b/src/protocol.h index ae541dfdba..3d8eae55f9 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. diff --git a/src/qt/Makefile.am b/src/qt/Makefile.am index 248dcba0ec..4ecd5629ea 100644 --- a/src/qt/Makefile.am +++ b/src/qt/Makefile.am @@ -34,7 +34,8 @@ QT_TS = locale/bitcoin_ach.ts locale/bitcoin_af_ZA.ts locale/bitcoin_ar.ts \ QT_FORMS_UI = forms/aboutdialog.ui forms/addressbookpage.ui \ forms/askpassphrasedialog.ui forms/editaddressdialog.ui forms/intro.ui \ - forms/optionsdialog.ui forms/overviewpage.ui forms/qrcodedialog.ui \ + forms/optionsdialog.ui forms/overviewpage.ui forms/receiverequestdialog.ui \ + forms/receivecoinsdialog.ui \ forms/rpcconsole.ui forms/sendcoinsdialog.ui forms/sendcoinsentry.ui \ forms/signverifymessagedialog.ui forms/transactiondescdialog.ui @@ -46,15 +47,15 @@ QT_MOC_CPP = moc_aboutdialog.cpp moc_addressbookpage.cpp \ moc_intro.cpp moc_macdockiconhandler.cpp moc_macnotificationhandler.cpp \ moc_monitoreddatamapper.cpp moc_notificator.cpp moc_optionsdialog.cpp \ moc_optionsmodel.cpp moc_overviewpage.cpp moc_paymentserver.cpp \ - moc_qrcodedialog.cpp moc_qvalidatedlineedit.cpp moc_qvaluecombobox.cpp \ + moc_receiverequestdialog.cpp moc_qvalidatedlineedit.cpp moc_qvaluecombobox.cpp \ + moc_receivecoinsdialog.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 + moc_walletmodel.cpp moc_walletview.cpp BITCOIN_MM = macdockiconhandler.mm macnotificationhandler.mm -QR_CPP = qrcodedialog.cpp QT_MOC = intro.moc overviewpage.moc rpcconsole.moc @@ -71,11 +72,12 @@ BITCOIN_QT_H = aboutdialog.h addressbookpage.h addresstablemodel.h \ editaddressdialog.h guiconstants.h guiutil.h intro.h macdockiconhandler.h \ macnotificationhandler.h monitoreddatamapper.h notificator.h optionsdialog.h \ optionsmodel.h overviewpage.h paymentrequestplus.h paymentserver.h \ - qrcodedialog.h qvalidatedlineedit.h qvaluecombobox.h rpcconsole.h \ + receivecoinsdialog.h \ + receiverequestdialog.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 + walletmodel.h walletmodeltransaction.h walletview.h RES_ICONS = res/icons/bitcoin.png res/icons/address-book.png \ res/icons/quit.png res/icons/send.png res/icons/toolbar.png \ @@ -101,11 +103,12 @@ BITCOIN_QT_CPP = aboutdialog.cpp addressbookpage.cpp \ guiutil.cpp intro.cpp monitoreddatamapper.cpp notificator.cpp \ optionsdialog.cpp optionsmodel.cpp overviewpage.cpp paymentrequestplus.cpp \ paymentserver.cpp qvalidatedlineedit.cpp qvaluecombobox.cpp \ + receivecoinsdialog.cpp receiverequestdialog.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 + walletmodel.cpp walletmodeltransaction.cpp walletview.cpp RES_IMAGES = res/images/about.png res/images/splash.png \ res/images/splash_testnet.png @@ -135,9 +138,6 @@ endif if TARGET_WINDOWS libbitcoinqt_a_SOURCES += $(BITCOIN_RC) endif -if USE_QRCODE - libbitcoinqt_a_SOURCES += $(QR_CPP) -endif # # bitcoin-qt binary # @@ -159,7 +159,7 @@ QT_QM=$(QT_TS:.ts=.qm) bitcoinstrings.cpp: FORCE $(MAKE) -C $(top_srcdir)/src qt/bitcoinstrings.cpp -translate: bitcoinstrings.cpp $(QT_FORMS_UI) $(QT_FORMS_UI) $(BITCOIN_QT_CPP) $(BITCOIN_QT_H) $(BITCOIN_MM) $(QR_CPP) +translate: bitcoinstrings.cpp $(QT_FORMS_UI) $(QT_FORMS_UI) $(BITCOIN_QT_CPP) $(BITCOIN_QT_H) $(BITCOIN_MM) @test -n $(LUPDATE) || echo "lupdate is required for updating translations" @$(LUPDATE) $^ -locations relative -no-obsolete -ts locale/bitcoin_en.ts diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index 5b8d44481e..ba5de4660a 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -12,10 +12,6 @@ #include "csvmodelwriter.h" #include "guiutil.h" -#ifdef USE_QRCODE -#include "qrcodedialog.h" -#endif - #include <QSortFilterProxyModel> #include <QClipboard> #include <QMessageBox> @@ -35,25 +31,29 @@ AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget *parent) : ui->newAddress->setIcon(QIcon()); ui->copyAddress->setIcon(QIcon()); ui->deleteAddress->setIcon(QIcon()); - ui->verifyMessage->setIcon(QIcon()); - ui->signMessage->setIcon(QIcon()); ui->exportButton->setIcon(QIcon()); #endif -#ifndef USE_QRCODE - ui->showQRCode->setVisible(false); -#endif - switch(mode) { - case ForSending: + case ForSelection: + switch(tab) + { + case SendingTab: setWindowTitle(tr("Choose the address to send coins to")); break; + case ReceivingTab: setWindowTitle(tr("Choose the address to receive coins with")); break; + } connect(ui->tableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(accept())); ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers); ui->tableView->setFocus(); + ui->closeButton->setText(tr("C&hoose")); ui->exportButton->hide(); break; case ForEditing: - ui->buttonBox->setVisible(false); + switch(tab) + { + case SendingTab: setWindowTitle(tr("Sending addresses")); break; + case ReceivingTab: setWindowTitle(tr("Receiving addresses")); break; + } break; } switch(tab) @@ -61,23 +61,17 @@ AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget *parent) : case SendingTab: ui->labelExplanation->setText(tr("These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.")); ui->deleteAddress->setVisible(true); - ui->signMessage->setVisible(false); break; case ReceivingTab: - ui->labelExplanation->setText(tr("These are your Bitcoin addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you.")); + ui->labelExplanation->setText(tr("These are your Bitcoin addresses for receiving payments. It is recommended to use a new receiving address for each transaction.")); ui->deleteAddress->setVisible(false); - ui->signMessage->setVisible(true); break; } // Context menu actions - QAction *copyAddressAction = new QAction(ui->copyAddress->text(), this); + QAction *copyAddressAction = new QAction(tr("&Copy Address"), this); QAction *copyLabelAction = new QAction(tr("Copy &Label"), this); QAction *editAction = new QAction(tr("&Edit"), this); - QAction *sendCoinsAction = new QAction(tr("Send &Coins"), this); - QAction *showQRCodeAction = new QAction(ui->showQRCode->text(), this); - QAction *signMessageAction = new QAction(ui->signMessage->text(), this); - QAction *verifyMessageAction = new QAction(ui->verifyMessage->text(), this); deleteAction = new QAction(ui->deleteAddress->text(), this); // Build context menu @@ -88,30 +82,16 @@ AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget *parent) : if(tab == SendingTab) contextMenu->addAction(deleteAction); contextMenu->addSeparator(); - if(tab == SendingTab) - contextMenu->addAction(sendCoinsAction); -#ifdef USE_QRCODE - contextMenu->addAction(showQRCodeAction); -#endif - if(tab == ReceivingTab) - contextMenu->addAction(signMessageAction); - else if(tab == SendingTab) - contextMenu->addAction(verifyMessageAction); // Connect signals for context menu actions connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(on_copyAddress_clicked())); connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(onCopyLabelAction())); connect(editAction, SIGNAL(triggered()), this, SLOT(onEditAction())); connect(deleteAction, SIGNAL(triggered()), this, SLOT(on_deleteAddress_clicked())); - connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(onSendCoinsAction())); - connect(showQRCodeAction, SIGNAL(triggered()), this, SLOT(on_showQRCode_clicked())); - connect(signMessageAction, SIGNAL(triggered()), this, SLOT(on_signMessage_clicked())); - connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(on_verifyMessage_clicked())); connect(ui->tableView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint))); - // Pass through accept action from button box - connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(ui->closeButton, SIGNAL(clicked()), this, SLOT(accept())); } AddressBookPage::~AddressBookPage() @@ -197,42 +177,6 @@ void AddressBookPage::onEditAction() dlg.exec(); } -void AddressBookPage::on_signMessage_clicked() -{ - QTableView *table = ui->tableView; - QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); - - foreach (QModelIndex index, indexes) - { - QString address = index.data().toString(); - emit signMessage(address); - } -} - -void AddressBookPage::on_verifyMessage_clicked() -{ - QTableView *table = ui->tableView; - QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); - - foreach (QModelIndex index, indexes) - { - QString address = index.data().toString(); - emit verifyMessage(address); - } -} - -void AddressBookPage::onSendCoinsAction() -{ - QTableView *table = ui->tableView; - QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); - - foreach (QModelIndex index, indexes) - { - QString address = index.data().toString(); - emit sendCoins(address); - } -} - void AddressBookPage::on_newAddress_clicked() { if(!model) @@ -278,32 +222,20 @@ void AddressBookPage::selectionChanged() ui->deleteAddress->setEnabled(true); ui->deleteAddress->setVisible(true); deleteAction->setEnabled(true); - ui->signMessage->setEnabled(false); - ui->signMessage->setVisible(false); - ui->verifyMessage->setEnabled(true); - ui->verifyMessage->setVisible(true); break; case ReceivingTab: // Deleting receiving addresses, however, is not allowed ui->deleteAddress->setEnabled(false); ui->deleteAddress->setVisible(false); deleteAction->setEnabled(false); - ui->signMessage->setEnabled(true); - ui->signMessage->setVisible(true); - ui->verifyMessage->setEnabled(false); - ui->verifyMessage->setVisible(false); break; } ui->copyAddress->setEnabled(true); - ui->showQRCode->setEnabled(true); } else { ui->deleteAddress->setEnabled(false); - ui->showQRCode->setEnabled(false); ui->copyAddress->setEnabled(false); - ui->signMessage->setEnabled(false); - ui->verifyMessage->setEnabled(false); } } @@ -312,9 +244,6 @@ void AddressBookPage::done(int retval) QTableView *table = ui->tableView; if(!table->selectionModel() || !table->model()) return; - // When this is a tab/widget and not a model dialog, ignore "done" - if(mode == ForEditing) - return; // Figure out which address was selected, and return it QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); @@ -339,7 +268,7 @@ void AddressBookPage::on_exportButton_clicked() // CSV is currently the only supported format QString filename = GUIUtil::getSaveFileName( this, - tr("Export Address Book Data"), QString(), + tr("Export Address List"), QString(), tr("Comma separated file (*.csv)")); if (filename.isNull()) return; @@ -358,25 +287,6 @@ void AddressBookPage::on_exportButton_clicked() } } -void AddressBookPage::on_showQRCode_clicked() -{ -#ifdef USE_QRCODE - QTableView *table = ui->tableView; - QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); - - foreach (QModelIndex index, indexes) - { - QString address = index.data().toString(); - QString label = index.sibling(index.row(), 0).data(Qt::EditRole).toString(); - - QRCodeDialog *dialog = new QRCodeDialog(address, label, tab == ReceivingTab, this); - dialog->setModel(optionsModel); - dialog->setAttribute(Qt::WA_DeleteOnClose); - dialog->show(); - } -#endif -} - void AddressBookPage::contextualMenu(const QPoint &point) { QModelIndex index = ui->tableView->indexAt(point); diff --git a/src/qt/addressbookpage.h b/src/qt/addressbookpage.h index 34465aa65f..9255e58144 100644 --- a/src/qt/addressbookpage.h +++ b/src/qt/addressbookpage.h @@ -30,7 +30,7 @@ public: }; enum Mode { - ForSending, /**< Open address book to pick address for sending */ + ForSelection, /**< Open address book to pick address */ ForEditing /**< Open address book for editing */ }; @@ -63,14 +63,6 @@ private slots: void on_newAddress_clicked(); /** Copy address of currently selected address entry to clipboard */ void on_copyAddress_clicked(); - /** Open the sign message tab in the Sign/Verify Message dialog with currently selected address */ - void on_signMessage_clicked(); - /** Open the verify message tab in the Sign/Verify Message dialog with currently selected address */ - void on_verifyMessage_clicked(); - /** Open send coins dialog for currently selected address (no button) */ - void onSendCoinsAction(); - /** Generate a QR Code from the currently selected address */ - void on_showQRCode_clicked(); /** Copy label of currently selected address entry to clipboard (no button) */ void onCopyLabelAction(); /** Edit currently selected address entry (no button) */ @@ -86,8 +78,6 @@ private slots: void selectNewAddress(const QModelIndex &parent, int begin, int /*end*/); signals: - void signMessage(QString addr); - void verifyMessage(QString addr); void sendCoins(QString addr); }; diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 78693971da..09f2a53680 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -331,10 +331,8 @@ int main(int argc, char *argv[]) &window, SLOT(handlePaymentRequest(SendCoinsRecipient))); QObject::connect(&walletModel, SIGNAL(coinsSent(CWallet*,SendCoinsRecipient,QByteArray)), paymentServer, SLOT(fetchPaymentACK(CWallet*,const SendCoinsRecipient&,QByteArray))); - QObject::connect(paymentServer, SIGNAL(receivedPaymentACK(QString)), - &window, SLOT(showPaymentACK(QString))); - QObject::connect(paymentServer, SIGNAL(reportError(QString, QString, unsigned int)), - guiref, SLOT(message(QString, QString, unsigned int))); + QObject::connect(paymentServer, SIGNAL(message(QString,QString,unsigned int)), + guiref, SLOT(message(QString,QString,unsigned int))); QTimer::singleShot(100, paymentServer, SLOT(uiReady())); app.exec(); 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 5afd93957b..2279d4fb4b 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -7,14 +7,12 @@ #include "bitcoingui.h" -#include "transactiontablemodel.h" #include "optionsdialog.h" #include "aboutdialog.h" #include "clientmodel.h" #include "walletmodel.h" #include "walletframe.h" #include "optionsmodel.h" -#include "transactiondescdialog.h" #include "bitcoinunits.h" #include "guiconstants.h" #include "notificator.h" @@ -157,6 +155,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); @@ -192,7 +192,7 @@ void BitcoinGUI::createActions(bool fIsTestnet) tabGroup->addAction(sendCoinsAction); receiveCoinsAction = new QAction(QIcon(":/icons/receiving_addresses"), tr("&Receive"), this); - receiveCoinsAction->setStatusTip(tr("Show the list of addresses for receiving payments")); + receiveCoinsAction->setStatusTip(tr("Request payments (generates QR codes and bitcoin: URIs)")); receiveCoinsAction->setToolTip(receiveCoinsAction->statusTip()); receiveCoinsAction->setCheckable(true); receiveCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_3)); @@ -205,13 +205,6 @@ void BitcoinGUI::createActions(bool fIsTestnet) historyAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_4)); tabGroup->addAction(historyAction); - addressBookAction = new QAction(QIcon(":/icons/address-book"), tr("&Addresses"), this); - addressBookAction->setStatusTip(tr("Edit the list of stored addresses and labels")); - addressBookAction->setToolTip(addressBookAction->statusTip()); - addressBookAction->setCheckable(true); - addressBookAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_5)); - tabGroup->addAction(addressBookAction); - connect(overviewAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); connect(overviewAction, SIGNAL(triggered()), this, SLOT(gotoOverviewPage())); connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); @@ -220,8 +213,6 @@ void BitcoinGUI::createActions(bool fIsTestnet) connect(receiveCoinsAction, SIGNAL(triggered()), this, SLOT(gotoReceiveCoinsPage())); connect(historyAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); connect(historyAction, SIGNAL(triggered()), this, SLOT(gotoHistoryPage())); - connect(addressBookAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); - connect(addressBookAction, SIGNAL(triggered()), this, SLOT(gotoAddressBookPage())); quitAction = new QAction(QIcon(":/icons/quit"), tr("E&xit"), this); quitAction->setStatusTip(tr("Quit application")); @@ -233,7 +224,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); @@ -260,6 +255,11 @@ void BitcoinGUI::createActions(bool fIsTestnet) openRPCConsoleAction = new QAction(QIcon(":/icons/debugwindow"), tr("&Debug window"), this); openRPCConsoleAction->setStatusTip(tr("Open debugging and diagnostic console")); + usedSendingAddressesAction = new QAction(QIcon(":/icons/address-book"), tr("&Used sending addresses..."), this); + usedSendingAddressesAction->setStatusTip(tr("Show the list of used sending addresses and labels")); + usedReceivingAddressesAction = new QAction(QIcon(":/icons/address-book"), tr("Used &receiving addresses..."), this); + usedReceivingAddressesAction->setStatusTip(tr("Show the list of used receiving addresses and labels")); + connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); connect(aboutAction, SIGNAL(triggered()), this, SLOT(aboutClicked())); connect(aboutQtAction, SIGNAL(triggered()), qApp, SLOT(aboutQt())); @@ -270,6 +270,8 @@ void BitcoinGUI::createActions(bool fIsTestnet) connect(changePassphraseAction, SIGNAL(triggered()), walletFrame, SLOT(changePassphrase())); connect(signMessageAction, SIGNAL(triggered()), this, SLOT(gotoSignMessageTab())); connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(gotoVerifyMessageTab())); + connect(usedSendingAddressesAction, SIGNAL(triggered()), walletFrame, SLOT(usedSendingAddresses())); + connect(usedReceivingAddressesAction, SIGNAL(triggered()), walletFrame, SLOT(usedReceivingAddresses())); } void BitcoinGUI::createMenuBar() @@ -288,6 +290,9 @@ void BitcoinGUI::createMenuBar() file->addAction(signMessageAction); file->addAction(verifyMessageAction); file->addSeparator(); + file->addAction(usedSendingAddressesAction); + file->addAction(usedReceivingAddressesAction); + file->addSeparator(); file->addAction(quitAction); QMenu *settings = appMenuBar->addMenu(tr("&Settings")); @@ -311,7 +316,6 @@ void BitcoinGUI::createToolBars() toolbar->addAction(sendCoinsAction); toolbar->addAction(receiveCoinsAction); toolbar->addAction(historyAction); - toolbar->addAction(addressBookAction); } void BitcoinGUI::setClientModel(ClientModel *clientModel) @@ -441,26 +445,25 @@ void BitcoinGUI::aboutClicked() void BitcoinGUI::gotoOverviewPage() { + overviewAction->setChecked(true); if (walletFrame) walletFrame->gotoOverviewPage(); } void BitcoinGUI::gotoHistoryPage() { + historyAction->setChecked(true); if (walletFrame) walletFrame->gotoHistoryPage(); } -void BitcoinGUI::gotoAddressBookPage() -{ - if (walletFrame) walletFrame->gotoAddressBookPage(); -} - void BitcoinGUI::gotoReceiveCoinsPage() { + receiveCoinsAction->setChecked(true); if (walletFrame) walletFrame->gotoReceiveCoinsPage(); } void BitcoinGUI::gotoSendCoinsPage(QString addr) { + sendCoinsAction->setChecked(true); if (walletFrame) walletFrame->gotoSendCoinsPage(addr); } @@ -632,6 +635,8 @@ void BitcoinGUI::message(const QString &title, const QString &message, unsigned if (!(buttons = (QMessageBox::StandardButton)(style & CClientUIInterface::BTN_MASK))) buttons = QMessageBox::Ok; + // Ensure we get users attention + showNormalIfMinimized(); QMessageBox mBox((QMessageBox::Icon)nMBoxIcon, strTitle, message, buttons, this); int r = mBox.exec(); if (ret != NULL) @@ -683,9 +688,8 @@ void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee) QString strMessage = tr("This transaction is over the size limit. You can still send it for a fee of %1, " "which goes to the nodes that process your transaction and helps to support the network. " "Do you want to pay the fee?").arg(BitcoinUnits::formatWithUnit(clientModel->getOptionsModel()->getDisplayUnit(), nFeeRequired)); - QMessageBox::StandardButton retval = QMessageBox::question( - this, tr("Confirm transaction fee"), strMessage, - QMessageBox::Yes|QMessageBox::Cancel, QMessageBox::Yes); + QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm transaction fee"), strMessage, + QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); *payFee = (retval == QMessageBox::Yes); } @@ -728,7 +732,7 @@ void BitcoinGUI::dropEvent(QDropEvent *event) walletFrame->gotoSendCoinsPage(); else message(tr("URI handling"), tr("URI can not be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters."), - CClientUIInterface::ICON_WARNING); + CClientUIInterface::ICON_WARNING); } event->acceptProposedAction(); @@ -746,14 +750,17 @@ bool BitcoinGUI::eventFilter(QObject *object, QEvent *event) return QMainWindow::eventFilter(object, event); } -void BitcoinGUI::handlePaymentRequest(const SendCoinsRecipient& recipient) -{ - walletFrame->handlePaymentRequest(recipient); -} - -void BitcoinGUI::showPaymentACK(const QString& msg) +bool BitcoinGUI::handlePaymentRequest(const SendCoinsRecipient& recipient) { - message(tr("Payment acknowledged"), GUIUtil::HtmlEscape(msg), CClientUIInterface::MODAL); + // URI has to be valid + if (walletFrame->handlePaymentRequest(recipient)) + { + showNormalIfMinimized(); + gotoSendCoinsPage(); + return true; + } + else + return false; } void BitcoinGUI::setEncryptionStatus(int status) diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index e2dd5dc6bc..215bb6fa05 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -5,15 +5,12 @@ #include <QSystemTrayIcon> #include <QMap> -class TransactionTableModel; class WalletFrame; class WalletView; class ClientModel; class WalletModel; class WalletStack; -class TransactionView; class OverviewPage; -class AddressBookPage; class SendCoinsDialog; class SendCoinsRecipient; class SignVerifyMessageDialog; @@ -51,24 +48,16 @@ public: The client model represents the part of the core that communicates with the P2P network, and is wallet-agnostic. */ void setClientModel(ClientModel *clientModel); + /** Set the wallet model. The wallet model represents a bitcoin wallet, and offers access to the list of transactions, address book and sending functionality. */ - bool addWallet(const QString& name, WalletModel *walletModel); bool setCurrentWallet(const QString& name); void removeAllWallets(); - /** Used by WalletView to allow access to needed QActions */ - // Todo: Use Qt signals for these - QAction * getOverviewAction() { return overviewAction; } - QAction * getHistoryAction() { return historyAction; } - QAction * getAddressBookAction() { return addressBookAction; } - QAction * getReceiveCoinsAction() { return receiveCoinsAction; } - QAction * getSendCoinsAction() { return sendCoinsAction; } - protected: void changeEvent(QEvent *e); void closeEvent(QCloseEvent *event); @@ -91,7 +80,8 @@ private: QAction *historyAction; QAction *quitAction; QAction *sendCoinsAction; - QAction *addressBookAction; + QAction *usedSendingAddressesAction; + QAction *usedReceivingAddressesAction; QAction *signMessageAction; QAction *verifyMessageAction; QAction *aboutAction; @@ -106,7 +96,6 @@ private: QSystemTrayIcon *trayIcon; Notificator *notificator; - TransactionView *transactionView; RPCConsole *rpcConsole; QMovie *syncIconMovie; @@ -143,6 +132,7 @@ public slots: @param[in] ret pointer to a bool that will be modified to whether Ok was clicked (modal only) */ void message(const QString &title, const QString &message, unsigned int style, bool *ret = NULL); + /** Asks the user whether to pay the transaction fee or to cancel the transaction. It is currently not possible to pass a return value to another thread through BlockingQueuedConnection, so an indirected pointer is used. @@ -153,8 +143,7 @@ public slots: */ void askFee(qint64 nFeeRequired, bool *payFee); - void handlePaymentRequest(const SendCoinsRecipient& recipient); - void showPaymentACK(const QString& msg); + bool handlePaymentRequest(const SendCoinsRecipient& recipient); /** Show incoming transaction notification for new transactions. */ void incomingTransaction(const QString& date, int unit, qint64 amount, const QString& type, const QString& address); @@ -164,8 +153,6 @@ private slots: void gotoOverviewPage(); /** Switch to history (transactions) page */ void gotoHistoryPage(); - /** Switch to address book page */ - void gotoAddressBookPage(); /** Switch to receive coins page */ void gotoReceiveCoinsPage(); /** Switch to send coins page */ diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp index bfe9494b17..457090b1f5 100644 --- a/src/qt/bitcoinstrings.cpp +++ b/src/qt/bitcoinstrings.cpp @@ -19,8 +19,8 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "It is also recommended to set alertnotify so you are notified of problems;\n" "for example: alertnotify=echo %%s | mail -s \"Bitcoin Alert\" admin@foo.com\n"), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:" -"@STRENGTH)"), +"Acceptable ciphers (default: TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!" +"3DES:@STRENGTH)"), QT_TRANSLATE_NOOP("bitcoin-core", "" "An error occurred while setting up the RPC port %u for listening on IPv4: %s"), QT_TRANSLATE_NOOP("bitcoin-core", "" @@ -98,6 +98,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Add a node to connect to and attempt to keep QT_TRANSLATE_NOOP("bitcoin-core", "Allow DNS lookups for -addnode, -seednode and -connect"), QT_TRANSLATE_NOOP("bitcoin-core", "Allow JSON-RPC connections from specified IP address"), QT_TRANSLATE_NOOP("bitcoin-core", "Attempt to recover private keys from a corrupt wallet.dat"), +QT_TRANSLATE_NOOP("bitcoin-core", "Bitcoin RPC client version"), QT_TRANSLATE_NOOP("bitcoin-core", "Bitcoin version"), QT_TRANSLATE_NOOP("bitcoin-core", "Block creation options:"), QT_TRANSLATE_NOOP("bitcoin-core", "Cannot downgrade wallet"), @@ -106,6 +107,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Cannot resolve -externalip address: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Cannot write default address"), QT_TRANSLATE_NOOP("bitcoin-core", "Connect only to the specified node(s)"), QT_TRANSLATE_NOOP("bitcoin-core", "Connect through socks proxy"), +QT_TRANSLATE_NOOP("bitcoin-core", "Connect to JSON-RPC on <port> (default: 8332 or testnet: 18332)"), QT_TRANSLATE_NOOP("bitcoin-core", "Connect to a node to retrieve peer addresses, and disconnect"), QT_TRANSLATE_NOOP("bitcoin-core", "Corrupted block database detected"), QT_TRANSLATE_NOOP("bitcoin-core", "Discover own IP address (default: 1 when listening and no -externalip)"), @@ -143,8 +145,8 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Imports blocks from external blk000??.dat fil QT_TRANSLATE_NOOP("bitcoin-core", "Incorrect or no genesis block found. Wrong datadir for network?"), QT_TRANSLATE_NOOP("bitcoin-core", "Information"), QT_TRANSLATE_NOOP("bitcoin-core", "Insufficient funds"), +QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -onion address: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -proxy address: '%s'"), -QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -tor address: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -minrelaytxfee=<amount>: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -mintxfee=<amount>: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -paytxfee=<amount>: '%s'"), @@ -172,7 +174,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Rescanning..."), QT_TRANSLATE_NOOP("bitcoin-core", "Run in the background as a daemon and accept commands"), QT_TRANSLATE_NOOP("bitcoin-core", "SSL options: (see the Bitcoin Wiki for SSL setup instructions)"), QT_TRANSLATE_NOOP("bitcoin-core", "Select the version of socks proxy to use (4-5, default: 5)"), -QT_TRANSLATE_NOOP("bitcoin-core", "Send command to -server or bitcoind"), +QT_TRANSLATE_NOOP("bitcoin-core", "Send command to Bitcoin server"), QT_TRANSLATE_NOOP("bitcoin-core", "Send commands to node running on <ip> (default: 127.0.0.1)"), QT_TRANSLATE_NOOP("bitcoin-core", "Send trace/debug info to console instead of debug.log file"), QT_TRANSLATE_NOOP("bitcoin-core", "Send trace/debug info to debugger"), @@ -191,6 +193,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Specify data directory"), QT_TRANSLATE_NOOP("bitcoin-core", "Specify pid file (default: bitcoind.pid)"), QT_TRANSLATE_NOOP("bitcoin-core", "Specify wallet file (within data directory)"), QT_TRANSLATE_NOOP("bitcoin-core", "Specify your own public address"), +QT_TRANSLATE_NOOP("bitcoin-core", "Start Bitcoin server"), QT_TRANSLATE_NOOP("bitcoin-core", "System error: "), QT_TRANSLATE_NOOP("bitcoin-core", "This help message"), QT_TRANSLATE_NOOP("bitcoin-core", "Threshold for disconnecting misbehaving peers (default: 100)"), @@ -202,6 +205,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Unable to bind to %s on this computer (bind r QT_TRANSLATE_NOOP("bitcoin-core", "Unknown -socks proxy version requested: %i"), QT_TRANSLATE_NOOP("bitcoin-core", "Unknown network specified in -onlynet: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Upgrade wallet to latest format"), +QT_TRANSLATE_NOOP("bitcoin-core", "Usage (deprecated, use bitcoin-cli):"), QT_TRANSLATE_NOOP("bitcoin-core", "Usage:"), QT_TRANSLATE_NOOP("bitcoin-core", "Use OpenSSL (https) for JSON-RPC connections"), QT_TRANSLATE_NOOP("bitcoin-core", "Use UPnP to map the listening port (default: 0)"), diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index b33f534f7c..212fa6974a 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -42,7 +42,7 @@ int ClientModel::getNumConnections() const int ClientModel::getNumBlocks() const { - return nBestHeight; + return chainActive.Height(); } int ClientModel::getNumBlocksAtStartup() @@ -51,19 +51,27 @@ 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()); - else if(!isTestNet()) - return QDateTime::fromTime_t(1231006505); // Genesis block's time + if (chainActive.Tip()) + return QDateTime::fromTime_t(chainActive.Tip()->GetBlockTime()); else - return QDateTime::fromTime_t(1296688602); // Genesis block's time (testnet) + return QDateTime::fromTime_t(Params().GenesisBlock().nTime); // Genesis block's time of current network } double ClientModel::getVerificationProgress() const { - return Checkpoints::GuessVerificationProgress(pindexBest); + return Checkpoints::GuessVerificationProgress(chainActive.Tip()); } void ClientModel::updateTimer() @@ -85,6 +93,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/addressbookpage.ui b/src/qt/forms/addressbookpage.ui index a2a7da34dd..49221f41c1 100644 --- a/src/qt/forms/addressbookpage.ui +++ b/src/qt/forms/addressbookpage.ui @@ -10,9 +10,6 @@ <height>380</height> </rect> </property> - <property name="windowTitle"> - <string>Address Book</string> - </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> <widget class="QLabel" name="labelExplanation"> @@ -60,7 +57,7 @@ <string>Create a new address</string> </property> <property name="text"> - <string>&New Address</string> + <string>&New</string> </property> <property name="icon"> <iconset resource="../bitcoin.qrc"> @@ -74,7 +71,7 @@ <string>Copy the currently selected address to the system clipboard</string> </property> <property name="text"> - <string>&Copy Address</string> + <string>&Copy</string> </property> <property name="icon"> <iconset resource="../bitcoin.qrc"> @@ -83,45 +80,6 @@ </widget> </item> <item> - <widget class="QPushButton" name="showQRCode"> - <property name="text"> - <string>Show &QR Code</string> - </property> - <property name="icon"> - <iconset resource="../bitcoin.qrc"> - <normaloff>:/icons/qrcode</normaloff>:/icons/qrcode</iconset> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="signMessage"> - <property name="toolTip"> - <string>Sign a message to prove you own a Bitcoin address</string> - </property> - <property name="text"> - <string>Sign &Message</string> - </property> - <property name="icon"> - <iconset resource="../bitcoin.qrc"> - <normaloff>:/icons/edit</normaloff>:/icons/edit</iconset> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="verifyMessage"> - <property name="toolTip"> - <string>Verify a message to ensure it was signed with a specified Bitcoin address</string> - </property> - <property name="text"> - <string>&Verify Message</string> - </property> - <property name="icon"> - <iconset resource="../bitcoin.qrc"> - <normaloff>:/icons/transaction_0</normaloff>:/icons/transaction_0</iconset> - </property> - </widget> - </item> - <item> <widget class="QPushButton" name="deleteAddress"> <property name="toolTip"> <string>Delete the currently selected address from the list</string> @@ -163,15 +121,9 @@ </widget> </item> <item> - <widget class="QDialogButtonBox" name="buttonBox"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="standardButtons"> - <set>QDialogButtonBox::Ok</set> + <widget class="QPushButton" name="closeButton"> + <property name="text"> + <string>C&lose</string> </property> </widget> </item> diff --git a/src/qt/forms/editaddressdialog.ui b/src/qt/forms/editaddressdialog.ui index b4a4c1b1e9..8ff3805226 100644 --- a/src/qt/forms/editaddressdialog.ui +++ b/src/qt/forms/editaddressdialog.ui @@ -32,7 +32,7 @@ <item row="0" column="1"> <widget class="QLineEdit" name="labelEdit"> <property name="toolTip"> - <string>The label associated with this address book entry</string> + <string>The label associated with this address list entry</string> </property> </widget> </item> @@ -49,7 +49,7 @@ <item row="1" column="1"> <widget class="QLineEdit" name="addressEdit"> <property name="toolTip"> - <string>The address associated with this address book entry. This can only be modified for sending addresses.</string> + <string>The address associated with this address list entry. This can only be modified for sending addresses.</string> </property> </widget> </item> diff --git a/src/qt/forms/qrcodedialog.ui b/src/qt/forms/qrcodedialog.ui deleted file mode 100644 index 1cec9066f8..0000000000 --- a/src/qt/forms/qrcodedialog.ui +++ /dev/null @@ -1,212 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>QRCodeDialog</class> - <widget class="QDialog" name="QRCodeDialog"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>340</width> - <height>530</height> - </rect> - </property> - <property name="windowTitle"> - <string>QR Code Dialog</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_3"> - <item> - <widget class="QLabel" name="lblQRCode"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>300</width> - <height>300</height> - </size> - </property> - <property name="textFormat"> - <enum>Qt::PlainText</enum> - </property> - <property name="alignment"> - <set>Qt::AlignCenter</set> - </property> - <property name="wordWrap"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QPlainTextEdit" name="outUri"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Minimum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>50</height> - </size> - </property> - <property name="tabChangesFocus"> - <bool>true</bool> - </property> - <property name="textInteractionFlags"> - <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> - </property> - </widget> - </item> - <item> - <widget class="QWidget" name="widget" native="true"> - <layout class="QVBoxLayout" name="verticalLayout_2"> - <item> - <widget class="QCheckBox" name="chkReqPayment"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="text"> - <string>Request Payment</string> - </property> - </widget> - </item> - <item> - <layout class="QFormLayout" name="formLayout"> - <property name="fieldGrowthPolicy"> - <enum>QFormLayout::AllNonFixedFieldsGrow</enum> - </property> - <item row="1" column="0"> - <widget class="QLabel" name="lblLabel"> - <property name="text"> - <string>Label:</string> - </property> - <property name="textFormat"> - <enum>Qt::PlainText</enum> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - <property name="buddy"> - <cstring>lnLabel</cstring> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QLineEdit" name="lnLabel"/> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="lblMessage"> - <property name="text"> - <string>Message:</string> - </property> - <property name="textFormat"> - <enum>Qt::PlainText</enum> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - <property name="buddy"> - <cstring>lnMessage</cstring> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="QLineEdit" name="lnMessage"/> - </item> - <item row="0" column="0"> - <widget class="QLabel" name="lblAmount"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Amount:</string> - </property> - <property name="textFormat"> - <enum>Qt::PlainText</enum> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - <property name="buddy"> - <cstring>lnReqAmount</cstring> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="BitcoinAmountField" name="lnReqAmount"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="minimumSize"> - <size> - <width>80</width> - <height>0</height> - </size> - </property> - </widget> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout"> - <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QPushButton" name="btnSaveAs"> - <property name="text"> - <string>&Save As...</string> - </property> - </widget> - </item> - </layout> - </item> - </layout> - </widget> - </item> - </layout> - </widget> - <customwidgets> - <customwidget> - <class>BitcoinAmountField</class> - <extends>QLineEdit</extends> - <header>bitcoinamountfield.h</header> - </customwidget> - </customwidgets> - <resources/> - <connections> - <connection> - <sender>chkReqPayment</sender> - <signal>clicked(bool)</signal> - <receiver>lnReqAmount</receiver> - <slot>setEnabled(bool)</slot> - <hints> - <hint type="sourcelabel"> - <x>92</x> - <y>285</y> - </hint> - <hint type="destinationlabel"> - <x>98</x> - <y>311</y> - </hint> - </hints> - </connection> - </connections> -</ui> diff --git a/src/qt/forms/receivecoinsdialog.ui b/src/qt/forms/receivecoinsdialog.ui new file mode 100644 index 0000000000..6d1a72ecd2 --- /dev/null +++ b/src/qt/forms/receivecoinsdialog.ui @@ -0,0 +1,194 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ReceiveCoinsDialog</class> + <widget class="QWidget" name="ReceiveCoinsDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>776</width> + <height>343</height> + </rect> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QGridLayout" name="gridLayout"> + <item row="3" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>&Amount:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="buddy"> + <cstring>reqAmount</cstring> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="BitcoinAmountField" name="reqAmount"> + <property name="minimumSize"> + <size> + <width>80</width> + <height>0</height> + </size> + </property> + <property name="toolTip"> + <string>The amount to request</string> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>&Label:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="buddy"> + <cstring>reqLabel</cstring> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QLineEdit" name="reqLabel"> + <property name="toolTip"> + <string>The label to associate with the receiving address</string> + </property> + </widget> + </item> + <item row="5" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>&Message:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="buddy"> + <cstring>reqMessage</cstring> + </property> + </widget> + </item> + <item row="5" column="1"> + <widget class="QLineEdit" name="reqMessage"> + <property name="toolTip"> + <string>The message to attach to payment request</string> + </property> + </widget> + </item> + <item row="6" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="6" column="1"> + <widget class="QCheckBox" name="reuseAddress"> + <property name="toolTip"> + <string>Reuse one of the previously used receiving addresses. Reusing addresses has security and privacy issues. Do not use this unless re-generating a payment request made before.</string> + </property> + <property name="text"> + <string>R&euse an existing receiving address (not recommended)</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>Use this form to request payments. All fields are optional.</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QPushButton" name="clearButton"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Clear all fields of the form.</string> + </property> + <property name="text"> + <string>Clear</string> + </property> + <property name="icon"> + <iconset resource="../bitcoin.qrc"> + <normaloff>:/icons/remove</normaloff>:/icons/remove</iconset> + </property> + <property name="autoRepeatDelay"> + <number>300</number> + </property> + <property name="autoDefault"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="receiveButton"> + <property name="minimumSize"> + <size> + <width>150</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>&Request payment</string> + </property> + <property name="icon"> + <iconset resource="../bitcoin.qrc"> + <normaloff>:/icons/receiving_addresses</normaloff>:/icons/receiving_addresses</iconset> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>BitcoinAmountField</class> + <extends>QLineEdit</extends> + <header>bitcoinamountfield.h</header> + </customwidget> + </customwidgets> + <resources> + <include location="../bitcoin.qrc"/> + </resources> + <connections/> +</ui> diff --git a/src/qt/forms/receiverequestdialog.ui b/src/qt/forms/receiverequestdialog.ui new file mode 100644 index 0000000000..c9cb3de69f --- /dev/null +++ b/src/qt/forms/receiverequestdialog.ui @@ -0,0 +1,166 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ReceiveRequestDialog</class> + <widget class="QDialog" name="ReceiveRequestDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>487</width> + <height>597</height> + </rect> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QRImageWidget" name="lblQRCode"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>300</width> + <height>300</height> + </size> + </property> + <property name="toolTip"> + <string>QR Code</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QTextEdit" name="outUri"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>50</height> + </size> + </property> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Plain</enum> + </property> + <property name="tabChangesFocus"> + <bool>true</bool> + </property> + <property name="textInteractionFlags"> + <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QPushButton" name="btnCopyURI"> + <property name="text"> + <string>Copy &URI</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="btnCopyAddress"> + <property name="text"> + <string>Copy &Address</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="btnCopyImage"> + <property name="text"> + <string>&Copy Image</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="btnSaveAs"> + <property name="text"> + <string>&Save Image...</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="standardButtons"> + <set>QDialogButtonBox::Close</set> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>QRImageWidget</class> + <extends>QLabel</extends> + <header>receiverequestdialog.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>ReceiveRequestDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>452</x> + <y>573</y> + </hint> + <hint type="destinationlabel"> + <x>243</x> + <y>298</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>ReceiveRequestDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>452</x> + <y>573</y> + </hint> + <hint type="destinationlabel"> + <x>243</x> + <y>298</y> + </hint> + </hints> + </connection> + </connections> +</ui> 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/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 2a00fc5455..67ea45d2fd 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -84,7 +84,7 @@ </sizepolicy> </property> <property name="toolTip"> - <string>Remove all transaction fields</string> + <string>Clear all fields of the form.</string> </property> <property name="text"> <string>Clear &All</string> diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui index 2c1cec600c..db742d633d 100644 --- a/src/qt/forms/sendcoinsentry.ui +++ b/src/qt/forms/sendcoinsentry.ui @@ -87,7 +87,7 @@ <item> <widget class="QToolButton" name="addressBookButton"> <property name="toolTip"> - <string>Choose address from address book</string> + <string>Choose previously used address</string> </property> <property name="text"> <string/> @@ -137,7 +137,7 @@ <item row="4" column="1"> <widget class="QValidatedLineEdit" name="addAsLabel"> <property name="toolTip"> - <string>Enter a label for this address to add it to your address book</string> + <string>Enter a label for this address to add it to the list of used addresses</string> </property> </widget> </item> @@ -621,7 +621,7 @@ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> <property name="buddy"> - <cstring>payAmount</cstring> + <cstring>payAmount_s</cstring> </property> </widget> </item> @@ -640,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"> @@ -680,7 +674,6 @@ <class>BitcoinAmountField</class> <extends>QLineEdit</extends> <header>bitcoinamountfield.h</header> - <container>1</container> </customwidget> <customwidget> <class>QValidatedLineEdit</class> diff --git a/src/qt/forms/signverifymessagedialog.ui b/src/qt/forms/signverifymessagedialog.ui index 279b2a5052..04d614a1cd 100644 --- a/src/qt/forms/signverifymessagedialog.ui +++ b/src/qt/forms/signverifymessagedialog.ui @@ -58,7 +58,7 @@ <item> <widget class="QPushButton" name="addressBookButton_SM"> <property name="toolTip"> - <string>Choose an address from the address book</string> + <string>Choose previously used address</string> </property> <property name="text"> <string/> @@ -271,7 +271,7 @@ <item> <widget class="QPushButton" name="addressBookButton_VM"> <property name="toolTip"> - <string>Choose an address from the address book</string> + <string>Choose previously used address</string> </property> <property name="text"> <string/> diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index c951b21b80..5951cef99a 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -112,6 +112,11 @@ bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out) rv.label = i->second; fShouldReturnFalse = false; } + if (i->first == "message") + { + rv.message = i->second; + fShouldReturnFalse = false; + } else if (i->first == "amount") { if(!i->second.isEmpty()) @@ -148,6 +153,34 @@ bool parseBitcoinURI(QString uri, SendCoinsRecipient *out) return parseBitcoinURI(uriInstance, out); } +QString formatBitcoinURI(const SendCoinsRecipient &info) +{ + QString ret = QString("bitcoin:%1").arg(info.address); + int paramCount = 0; + + if (info.amount) + { + ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnits::BTC, info.amount)); + paramCount++; + } + + if (!info.label.isEmpty()) + { + QString lbl(QUrl::toPercentEncoding(info.label)); + ret += QString("%1label=%2").arg(paramCount == 0 ? "?" : "&").arg(lbl); + paramCount++; + } + + if (!info.message.isEmpty()) + { + QString msg(QUrl::toPercentEncoding(info.message));; + ret += QString("%1message=%2").arg(paramCount == 0 ? "?" : "&").arg(msg); + paramCount++; + } + + return ret; +} + bool isDust(const QString& address, qint64 amount) { CTxDestination dest = CBitcoinAddress(address.toStdString()).Get(); @@ -190,10 +223,9 @@ void copyEntryData(QAbstractItemView *view, int column, int role) } } -QString getSaveFileName(QWidget *parent, const QString &caption, - const QString &dir, - const QString &filter, - QString *selectedSuffixOut) +QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, + const QString &filter, + QString *selectedSuffixOut) { QString selectedFilter; QString myDir; @@ -209,7 +241,8 @@ QString getSaveFileName(QWidget *parent, const QString &caption, { myDir = dir; } - QString result = QFileDialog::getSaveFileName(parent, caption, myDir, filter, &selectedFilter); + /* Directly convert path to native OS path separators */ + QString result = QDir::toNativeSeparators(QFileDialog::getSaveFileName(parent, caption, myDir, filter, &selectedFilter)); /* Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...) */ QRegExp filter_re(".* \\(\\*\\.(.*)[ \\)]"); @@ -527,7 +560,7 @@ HelpMessageBox::HelpMessageBox(QWidget *parent) : tr("Usage:") + "\n" + " bitcoin-qt [" + tr("command-line options") + "] " + "\n"; - coreOptions = QString::fromStdString(HelpMessage()); + coreOptions = QString::fromStdString(HelpMessage(HMM_BITCOIN_QT)); uiOptions = tr("UI options") + ":\n" + " -lang=<lang> " + tr("Set language, for example \"de_DE\" (default: system locale)") + "\n" + diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 8472700f48..956f550989 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -35,6 +35,7 @@ namespace GUIUtil // See Bitcoin URI definition discussion here: https://bitcointalk.org/index.php?topic=33490.0 bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out); bool parseBitcoinURI(QString uri, SendCoinsRecipient *out); + QString formatBitcoinURI(const SendCoinsRecipient &info); // Returns true if given address+amount meets "dust" definition bool isDust(const QString& address, qint64 amount); diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts index 4d1a8574d0..0dc56ac5dc 100644 --- a/src/qt/locale/bitcoin_en.ts +++ b/src/qt/locale/bitcoin_en.ts @@ -42,12 +42,7 @@ This product includes software developed by the OpenSSL Project for use in the O <context> <name>AddressBookPage</name> <message> - <location filename="../forms/addressbookpage.ui" line="+14"/> - <source>Address Book</source> - <translation>Address Book</translation> - </message> - <message> - <location line="+19"/> + <location filename="../forms/addressbookpage.ui" line="+30"/> <source>Double-click to edit address or label</source> <translation>Double-click to edit address or label</translation> </message> @@ -57,42 +52,32 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Create a new address</translation> </message> <message> - <location line="+14"/> + <location line="+3"/> + <source>&New</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+11"/> <source>Copy the currently selected address to the system clipboard</source> <translation>Copy the currently selected address to the system clipboard</translation> </message> <message> - <location line="-11"/> - <source>&New Address</source> - <translation>&New Address</translation> + <location line="+3"/> + <source>&Copy</source> + <translation type="unfinished"></translation> </message> <message> - <location filename="../addressbookpage.cpp" line="+67"/> - <source>These are your Bitcoin addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you.</source> - <translation>These are your Bitcoin addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you.</translation> + <location line="+52"/> + <source>C&lose</source> + <translation type="unfinished"></translation> </message> <message> - <location filename="../forms/addressbookpage.ui" line="+14"/> + <location filename="../addressbookpage.cpp" line="+72"/> <source>&Copy Address</source> <translation>&Copy Address</translation> </message> <message> - <location line="+11"/> - <source>Show &QR Code</source> - <translation>Show &QR Code</translation> - </message> - <message> - <location line="+11"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation>Sign a message to prove you own a Bitcoin address</translation> - </message> - <message> - <location line="+3"/> - <source>Sign &Message</source> - <translation>Sign &Message</translation> - </message> - <message> - <location line="+25"/> + <location filename="../forms/addressbookpage.ui" line="-41"/> <source>Delete the currently selected address from the list</source> <translation>Delete the currently selected address from the list</translation> </message> @@ -107,27 +92,47 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Export</translation> </message> <message> - <location line="-44"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation>Verify a message to ensure it was signed with a specified Bitcoin address</translation> + <location line="-27"/> + <source>&Delete</source> + <translation>&Delete</translation> </message> <message> - <location line="+3"/> - <source>&Verify Message</source> - <translation>&Verify Message</translation> + <location filename="../addressbookpage.cpp" line="-30"/> + <source>Choose the address to send coins to</source> + <translation type="unfinished"></translation> </message> <message> - <location line="+14"/> - <source>&Delete</source> - <translation>&Delete</translation> + <location line="+1"/> + <source>Choose the address to receive coins with</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+5"/> + <source>C&hoose</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+6"/> + <source>Sending addresses</source> + <translation type="unfinished"></translation> </message> <message> - <location filename="../addressbookpage.cpp" line="-5"/> + <location line="+1"/> + <source>Receiving addresses</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+7"/> <source>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source> <translation>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</translation> </message> <message> - <location line="+13"/> + <location line="+4"/> + <source>These are your Bitcoin addresses for receiving payments. It is recommended to use a new receiving address for each transaction.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+7"/> <source>Copy &Label</source> <translation>Copy &Label</translation> </message> @@ -137,14 +142,9 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Edit</translation> </message> <message> - <location line="+1"/> - <source>Send &Coins</source> - <translation>Send &Coins</translation> - </message> - <message> - <location line="+265"/> - <source>Export Address Book Data</source> - <translation>Export Address Book Data</translation> + <location line="+197"/> + <source>Export Address List</source> + <translation type="unfinished"></translation> </message> <message> <location line="+1"/> @@ -324,17 +324,17 @@ This product includes software developed by the OpenSSL Project for use in the O <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+255"/> + <location filename="../bitcoingui.cpp" line="+250"/> <source>Sign &message...</source> <translation>Sign &message...</translation> </message> <message> - <location line="+246"/> + <location line="+254"/> <source>Synchronizing with network...</source> <translation>Synchronizing with network...</translation> </message> <message> - <location line="-321"/> + <location line="-324"/> <source>&Overview</source> <translation>&Overview</translation> </message> @@ -354,17 +354,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Browse transaction history</translation> </message> <message> - <location line="+7"/> - <source>Edit the list of stored addresses and labels</source> - <translation>Edit the list of stored addresses and labels</translation> - </message> - <message> - <location line="-14"/> - <source>Show the list of addresses for receiving payments</source> - <translation>Show the list of addresses for receiving payments</translation> - </message> - <message> - <location line="+31"/> + <location line="+15"/> <source>E&xit</source> <translation>E&xit</translation> </message> @@ -379,12 +369,13 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Show information about Bitcoin</translation> </message> <message> + <location line="+3"/> <location line="+2"/> <source>About &Qt</source> <translation>About &Qt</translation> </message> <message> - <location line="+1"/> + <location line="+2"/> <source>Show information about Qt</source> <translation>Show information about Qt</translation> </message> @@ -409,7 +400,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Change Passphrase...</translation> </message> <message> - <location line="+251"/> + <location line="+259"/> <source>Importing blocks from disk...</source> <translation>Importing blocks from disk...</translation> </message> @@ -419,12 +410,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Reindexing blocks on disk...</translation> </message> <message> - <location line="-319"/> + <location line="-322"/> <source>Send coins to a Bitcoin address</source> <translation>Send coins to a Bitcoin address</translation> </message> <message> - <location line="+52"/> + <location line="+47"/> <source>Modify configuration options for Bitcoin</source> <translation>Modify configuration options for Bitcoin</translation> </message> @@ -454,20 +445,20 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Verify message...</translation> </message> <message> - <location line="-183"/> + <location line="-180"/> <location line="+6"/> - <location line="+508"/> + <location line="+513"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> <message> - <location line="-514"/> + <location line="-519"/> <location line="+6"/> <source>Wallet</source> <translation>Wallet</translation> </message> <message> - <location line="+107"/> + <location line="+109"/> <source>&Send</source> <translation>&Send</translation> </message> @@ -477,18 +468,13 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Receive</translation> </message> <message> - <location line="+14"/> - <source>&Addresses</source> - <translation>&Addresses</translation> - </message> - <message> - <location line="+23"/> + <location line="+28"/> <location line="+2"/> <source>&About Bitcoin</source> <translation>&About Bitcoin</translation> </message> <message> - <location line="+10"/> + <location line="+14"/> <location line="+2"/> <source>&Show / Hide</source> <translation>&Show / Hide</translation> @@ -514,12 +500,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Verify messages to ensure they were signed with specified Bitcoin addresses</translation> </message> <message> - <location line="+28"/> + <location line="+35"/> <source>&File</source> <translation>&File</translation> </message> <message> - <location line="+7"/> + <location line="+10"/> <source>&Settings</source> <translation>&Settings</translation> </message> @@ -534,19 +520,44 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Tabs toolbar</translation> </message> <message> - <location line="-228"/> - <location line="+288"/> + <location line="-235"/> + <location line="+294"/> <source>[testnet]</source> <translation>[testnet]</translation> </message> <message> - <location line="-5"/> + <location line="-177"/> + <source>Request payments (generates QR codes and bitcoin: URIs)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+63"/> + <source>&Used sending addresses...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Show the list of used sending addresses and labels</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Used &receiving addresses...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Show the list of used receiving addresses and labels</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+106"/> <location line="+5"/> <source>Bitcoin client</source> <translation>Bitcoin client</translation> </message> <message numerus="yes"> - <location line="+121"/> + <location line="+120"/> <source>%n active connection(s) to Bitcoin network</source> <translation> <numerusform>%n active connection to Bitcoin network</numerusform> @@ -608,7 +619,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Transactions after this will not yet be visible.</translation> </message> <message> - <location line="+22"/> + <location line="+27"/> <source>Error</source> <translation>Error</translation> </message> @@ -623,12 +634,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Information</translation> </message> <message> - <location line="+73"/> + <location line="+77"/> <source>This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee?</source> <translation>This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee?</translation> </message> <message> - <location line="-143"/> + <location line="-152"/> <source>Up to date</source> <translation>Up to date</translation> </message> @@ -638,7 +649,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Catching up...</translation> </message> <message> - <location line="+116"/> + <location line="+124"/> <source>Confirm transaction fee</source> <translation>Confirm transaction fee</translation> </message> @@ -676,13 +687,7 @@ Address: %4 <translation>URI can not be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters.</translation> </message> <message> - <location line="+27"/> - <location line="+2"/> - <source>Payment acknowledged</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+17"/> + <location line="+45"/> <source>Wallet is <b>encrypted</b> and currently <b>unlocked</b></source> <translation>Wallet is <b>encrypted</b> and currently <b>unlocked</b></translation> </message> @@ -700,7 +705,7 @@ Address: %4 <context> <name>ClientModel</name> <message> - <location filename="../clientmodel.cpp" line="+106"/> + <location filename="../clientmodel.cpp" line="+115"/> <source>Network Alert</source> <translation>Network Alert</translation> </message> @@ -719,18 +724,18 @@ Address: %4 </message> <message> <location line="+10"/> - <source>The label associated with this address book entry</source> - <translation>The label associated with this address book entry</translation> + <source>The label associated with this address list entry</source> + <translation type="unfinished"></translation> </message> <message> - <location line="+7"/> - <source>&Address</source> - <translation>&Address</translation> + <location line="+17"/> + <source>The address associated with this address list entry. This can only be modified for sending addresses.</source> + <translation type="unfinished"></translation> </message> <message> - <location line="+10"/> - <source>The address associated with this address book entry. This can only be modified for sending addresses.</source> - <translation>The address associated with this address book entry. This can only be modified for sending addresses.</translation> + <location line="-10"/> + <source>&Address</source> + <translation>&Address</translation> </message> <message> <location filename="../editaddressdialog.cpp" line="+21"/> @@ -804,7 +809,7 @@ Address: %4 <context> <name>GUIUtil::HelpMessageBox</name> <message> - <location filename="../guiutil.cpp" line="+525"/> + <location filename="../guiutil.cpp" line="+558"/> <location line="+13"/> <source>Bitcoin-Qt</source> <translation>Bitcoin-Qt</translation> @@ -1181,8 +1186,23 @@ Address: %4 <context> <name>PaymentServer</name> <message> - <location filename="../paymentserver.cpp" line="+450"/> - <location line="+41"/> + <location filename="../paymentserver.cpp" line="+392"/> + <source>URI handling</source> + <translation type="unfinished">URI handling</translation> + </message> + <message> + <location line="+1"/> + <source>URI can not be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters.</source> + <translation type="unfinished">URI can not be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters.</translation> + </message> + <message> + <location line="+69"/> + <source>Requested payment amount of %1 is too small (considered dust).</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+4"/> + <location line="+37"/> <source>Payment request error</source> <translation type="unfinished"></translation> </message> @@ -1193,12 +1213,27 @@ Address: %4 </message> <message> <location line="+38"/> - <source>Refund from</source> + <source>Refund from %1</source> <translation type="unfinished"></translation> </message> <message> - <location line="+46"/> - <location line="+28"/> + <location line="+42"/> + <source>Error communicating with %1: %2</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+31"/> + <source>Bad response from server %1</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+32"/> + <source>Payment acknowledged</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="-58"/> + <location line="+30"/> <location line="+17"/> <source>Network request error</source> <translation type="unfinished"></translation> @@ -1207,7 +1242,7 @@ Address: %4 <context> <name>QObject</name> <message> - <location filename="../bitcoin.cpp" line="+111"/> + <location filename="../bitcoin.cpp" line="+114"/> <location line="+5"/> <location filename="../intro.cpp" line="-32"/> <source>Bitcoin</source> @@ -1228,78 +1263,28 @@ Address: %4 <source>Error: Specified data directory "%1" can not be created.</source> <translation>Error: Specified data directory "%1" can not be created.</translation> </message> - <message> - <location filename="../paymentserver.cpp" line="-175"/> - <source>Requested payment amount (%1) too small</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+126"/> - <source>Error communicating with %1: %2</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+29"/> - <source>Bad response from server %1</source> - <translation type="unfinished"></translation> - </message> </context> <context> - <name>QRCodeDialog</name> - <message> - <location filename="../forms/qrcodedialog.ui" line="+14"/> - <source>QR Code Dialog</source> - <translation>QR Code Dialog</translation> - </message> - <message> - <location line="+59"/> - <source>Request Payment</source> - <translation>Request Payment</translation> - </message> - <message> - <location line="+56"/> - <source>Amount:</source> - <translation>Amount:</translation> - </message> - <message> - <location line="-44"/> - <source>Label:</source> - <translation>Label:</translation> - </message> + <name>QRImageWidget</name> <message> - <location line="+19"/> - <source>Message:</source> - <translation>Message:</translation> - </message> - <message> - <location line="+71"/> - <source>&Save As...</source> - <translation>&Save As...</translation> - </message> - <message> - <location filename="../qrcodedialog.cpp" line="+64"/> - <source>Error encoding URI into QR Code.</source> - <translation>Error encoding URI into QR Code.</translation> - </message> - <message> - <location line="+40"/> - <source>The entered amount is invalid, please check.</source> - <translation>The entered amount is invalid, please check.</translation> + <location filename="../receiverequestdialog.cpp" line="+32"/> + <source>&Save Image...</source> + <translation type="unfinished"></translation> </message> <message> - <location line="+23"/> - <source>Resulting URI too long, try to reduce the text for label / message.</source> - <translation>Resulting URI too long, try to reduce the text for label / message.</translation> + <location line="+3"/> + <source>&Copy Image</source> + <translation type="unfinished"></translation> </message> <message> - <location line="+25"/> + <location line="+28"/> <source>Save QR Code</source> - <translation>Save QR Code</translation> + <translation type="unfinished">Save QR Code</translation> </message> <message> <location line="+0"/> <source>PNG Images (*.png)</source> - <translation>PNG Images (*.png)</translation> + <translation type="unfinished">PNG Images (*.png)</translation> </message> </context> <context> @@ -1319,7 +1304,7 @@ Address: %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+345"/> + <location filename="../rpcconsole.cpp" line="+352"/> <source>N/A</source> <translation>N/A</translation> </message> @@ -1404,7 +1389,32 @@ Address: %4 <translation>&Console</translation> </message> <message> - <location line="-260"/> + <location line="+72"/> + <source>&Network Traffic</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+52"/> + <source>&Clear</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+13"/> + <source>Totals</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+64"/> + <source>In:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+80"/> + <source>Out:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="-541"/> <source>Build date</source> <translation>Build date</translation> </message> @@ -1448,20 +1458,184 @@ Address: %4 <source>Type <b>help</b> for an overview of available commands.</source> <translation>Type <b>help</b> for an overview of available commands.</translation> </message> + <message> + <location line="+128"/> + <source>%1 B</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source>%1 KB</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source>%1 MB</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source>%1 GB</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+7"/> + <source>%1 m</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+5"/> + <source>%1 h</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source>%1 h %2 m</source> + <translation type="unfinished"></translation> + </message> </context> <context> - <name>SendCoinsDialog</name> + <name>ReceiveCoinsDialog</name> + <message> + <location filename="../forms/receivecoinsdialog.ui" line="+22"/> + <source>&Amount:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+16"/> + <source>The amount to request</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+7"/> + <source>&Label:</source> + <translation type="unfinished">&Label:</translation> + </message> + <message> + <location line="+10"/> + <source>The label to associate with the receiving address</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+7"/> + <source>&Message:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+10"/> + <source>The message to attach to payment request</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+14"/> + <source>Reuse one of the previously used receiving addresses. Reusing addresses has security and privacy issues. Do not use this unless re-generating a payment request made before.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> + <source>R&euse an existing receiving address (not recommended)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+7"/> + <source>Use this form to request payments. All fields are optional.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+17"/> + <source>Clear all fields of the form.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> + <source>Clear</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+36"/> + <source>&Request payment</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>ReceiveRequestDialog</name> + <message> + <location filename="../forms/receiverequestdialog.ui" line="+29"/> + <source>QR Code</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+46"/> + <source>Copy &URI</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+7"/> + <source>Copy &Address</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+7"/> + <source>&Copy Image</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+7"/> + <source>&Save Image...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../receiverequestdialog.cpp" line="+58"/> + <source>Request payment to %1</source> + <translation type="unfinished"></translation> + </message> <message> - <location filename="../forms/sendcoinsdialog.ui" line="+14"/> - <location filename="../sendcoinsdialog.cpp" line="+138"/> - <location line="+5"/> - <location line="+5"/> - <location line="+5"/> <location line="+6"/> + <source>Payment information</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>URI</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source>Address</source> + <translation type="unfinished">Address</translation> + </message> + <message> + <location line="+2"/> + <source>Amount</source> + <translation type="unfinished">Amount</translation> + </message> + <message> + <location line="+2"/> + <source>Label</source> + <translation type="unfinished">Label</translation> + </message> + <message> + <location line="+2"/> + <source>Message</source> + <translation type="unfinished">Message</translation> + </message> + <message> + <location line="+10"/> + <source>Resulting URI too long, try to reduce the text for label / message.</source> + <translation type="unfinished">Resulting URI too long, try to reduce the text for label / message.</translation> + </message> + <message> <location line="+5"/> - <location line="+50"/> - <location line="+145"/> - <location line="+9"/> + <source>Error encoding URI into QR Code.</source> + <translation type="unfinished">Error encoding URI into QR Code.</translation> + </message> +</context> +<context> + <name>SendCoinsDialog</name> + <message> + <location filename="../forms/sendcoinsdialog.ui" line="+14"/> + <location filename="../sendcoinsdialog.cpp" line="+140"/> + <location line="+213"/> <source>Send Coins</source> <translation>Send Coins</translation> </message> @@ -1477,8 +1651,8 @@ Address: %4 </message> <message> <location line="+20"/> - <source>Remove all transaction fields</source> - <translation>Remove all transaction fields</translation> + <source>Clear all fields of the form.</source> + <translation type="unfinished"></translation> </message> <message> <location line="+3"/> @@ -1506,47 +1680,44 @@ Address: %4 <translation>S&end</translation> </message> <message> - <location filename="../sendcoinsdialog.cpp" line="-170"/> + <location filename="../sendcoinsdialog.cpp" line="-154"/> <source>Confirm send coins</source> <translation>Confirm send coins</translation> </message> <message> - <location line="-97"/> - <source>to</source> - <translation type="unfinished">to</translation> - </message> - <message> - <location line="+15"/> - <source><b>%1</b> to %2</source> + <location line="-90"/> + <location line="+5"/> + <location line="+5"/> + <source>%1 to %2</source> <translation type="unfinished"></translation> </message> <message> - <location line="+23"/> + <location line="+26"/> <source>The recipient address is not valid, please recheck.</source> <translation>The recipient address is not valid, please recheck.</translation> </message> <message> - <location line="+5"/> + <location line="+4"/> <source>The amount to pay must be larger than 0.</source> <translation>The amount to pay must be larger than 0.</translation> </message> <message> - <location line="+5"/> + <location line="+4"/> <source>The amount exceeds your balance.</source> <translation>The amount exceeds your balance.</translation> </message> <message> - <location line="+5"/> + <location line="+4"/> <source>The total exceeds your balance when the %1 transaction fee is included.</source> <translation>The total exceeds your balance when the %1 transaction fee is included.</translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <source>Duplicate address found, can only send to each address once per send operation.</source> <translation>Duplicate address found, can only send to each address once per send operation.</translation> </message> <message> - <location line="+5"/> + <location line="+4"/> <source>Error: Transaction creation failed!</source> <translation>Error: Transaction creation failed!</translation> </message> @@ -1571,12 +1742,12 @@ Address: %4 <translation>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.</translation> </message> <message> - <location line="+145"/> + <location line="+144"/> <source>Payment request expired</source> <translation type="unfinished"></translation> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Invalid payment address %1</source> <translation type="unfinished"></translation> </message> @@ -1584,49 +1755,38 @@ Address: %4 <context> <name>SendCoinsEntry</name> <message> - <location filename="../forms/sendcoinsentry.ui" line="+24"/> - <source>Form</source> - <translation>Form</translation> - </message> - <message> - <location line="+15"/> - <location line="+588"/> + <location filename="../forms/sendcoinsentry.ui" line="+33"/> + <location line="+585"/> <source>A&mount:</source> <translation>A&mount:</translation> </message> <message> - <location line="-575"/> - <location line="+588"/> + <location line="-572"/> + <location line="+585"/> <source>Pay &To:</source> <translation>Pay &To:</translation> </message> <message> - <location line="-554"/> + <location line="-551"/> <source>The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</source> <translation>The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</translation> </message> <message> - <location line="+60"/> <location filename="../sendcoinsentry.cpp" line="+28"/> <source>Enter a label for this address to add it to your address book</source> <translation>Enter a label for this address to add it to your address book</translation> </message> <message> - <location line="-78"/> + <location filename="../forms/sendcoinsentry.ui" line="-18"/> <source>&Label:</source> <translation>&Label:</translation> </message> <message> - <location line="-54"/> - <source>StackedWidget</source> + <location line="+28"/> + <source>Choose previously used address</source> <translation type="unfinished"></translation> </message> <message> - <location line="+82"/> - <source>Choose address from address book</source> - <translation>Choose address from address book</translation> - </message> - <message> <location line="+10"/> <source>Alt+A</source> <translation>Alt+A</translation> @@ -1647,21 +1807,16 @@ Address: %4 <translation>Remove this recipient</translation> </message> <message> - <location line="+466"/> - <source>SecureSend</source> + <location line="+16"/> + <source>Enter a label for this address to add it to the list of used addresses</source> <translation type="unfinished"></translation> </message> <message> - <location line="+18"/> + <location line="+465"/> <source>Memo:</source> <translation type="unfinished"></translation> </message> <message> - <location line="+63"/> - <source>message from merchant</source> - <translation type="unfinished"></translation> - </message> - <message> <location filename="../sendcoinsentry.cpp" line="+1"/> <source>Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</source> <translation>Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</translation> @@ -1692,8 +1847,8 @@ Address: %4 <message> <location line="+10"/> <location line="+213"/> - <source>Choose an address from the address book</source> - <translation>Choose an address from the address book</translation> + <source>Choose previously used address</source> + <translation type="unfinished"></translation> </message> <message> <location line="-203"/> @@ -1874,6 +2029,14 @@ Address: %4 </message> </context> <context> + <name>TrafficGraphWidget</name> + <message> + <location filename="../trafficgraphwidget.cpp" line="+75"/> + <source>KB/s</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> <name>TransactionDesc</name> <message> <location filename="../transactiondesc.cpp" line="+22"/> @@ -1952,12 +2115,12 @@ Address: %4 <location line="+12"/> <location line="+45"/> <location line="+17"/> - <location line="+45"/> + <location line="+48"/> <source>Credit</source> <translation>Credit</translation> </message> <message numerus="yes"> - <location line="-117"/> + <location line="-120"/> <source>matures in %n more block(s)</source> <translation> <numerusform>matures in %n more block</numerusform> @@ -1973,12 +2136,12 @@ Address: %4 <location line="+44"/> <location line="+8"/> <location line="+15"/> - <location line="+45"/> + <location line="+48"/> <source>Debit</source> <translation>Debit</translation> </message> <message> - <location line="-54"/> + <location line="-57"/> <source>Transaction fee</source> <translation>Transaction fee</translation> </message> @@ -2008,12 +2171,12 @@ Address: %4 <translation type="unfinished"></translation> </message> <message> - <location line="+5"/> - <source>Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.</source> - <translation>Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.</translation> + <location line="+7"/> + <source>Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.</source> + <translation type="unfinished"></translation> </message> <message> - <location line="+7"/> + <location line="+8"/> <source>Debug information</source> <translation>Debug information</translation> </message> @@ -2043,7 +2206,7 @@ Address: %4 <translation>false</translation> </message> <message> - <location line="-224"/> + <location line="-227"/> <source>, has not been successfully broadcast yet</source> <translation>, has not been successfully broadcast yet</translation> </message> @@ -2388,7 +2551,7 @@ Address: %4 <translation>Export the data in the current tab to a file</translation> </message> <message> - <location line="+198"/> + <location line="+183"/> <source>Backup Wallet</source> <translation>Backup Wallet</translation> </message> @@ -2421,22 +2584,17 @@ Address: %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+101"/> + <location filename="../bitcoinstrings.cpp" line="+102"/> <source>Bitcoin version</source> <translation>Bitcoin version</translation> </message> <message> - <location line="+104"/> + <location line="+107"/> <source>Usage:</source> <translation>Usage:</translation> </message> <message> - <location line="-30"/> - <source>Send command to -server or bitcoind</source> - <translation>Send command to -server or bitcoind</translation> - </message> - <message> - <location line="-23"/> + <location line="-55"/> <source>List commands</source> <translation>List commands</translation> </message> @@ -2491,12 +2649,12 @@ Address: %4 <translation>Specify your own public address</translation> </message> <message> - <location line="+3"/> + <location line="+4"/> <source>Threshold for disconnecting misbehaving peers (default: 100)</source> <translation>Threshold for disconnecting misbehaving peers (default: 100)</translation> </message> <message> - <location line="-139"/> + <location line="-142"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</translation> </message> @@ -2516,17 +2674,17 @@ Address: %4 <translation>Accept command line and JSON-RPC commands</translation> </message> <message> - <location line="+77"/> + <location line="+79"/> <source>Run in the background as a daemon and accept commands</source> <translation>Run in the background as a daemon and accept commands</translation> </message> <message> - <location line="+38"/> + <location line="+40"/> <source>Use the test network</source> <translation>Use the test network</translation> </message> <message> - <location line="-114"/> + <location line="-118"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation>Accept connections from outside (default: 1 if no -proxy or -connect)</translation> </message> @@ -2556,7 +2714,12 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. </translation> </message> <message> - <location line="+17"/> + <location line="+12"/> + <source>Acceptable ciphers (default: TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+5"/> <source>An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s</source> <translation>An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s</translation> </message> @@ -2636,6 +2799,11 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Attempt to recover private keys from a corrupt wallet.dat</translation> </message> <message> + <location line="+1"/> + <source>Bitcoin RPC client version</source> + <translation type="unfinished"></translation> + </message> + <message> <location line="+2"/> <source>Block creation options:</source> <translation>Block creation options:</translation> @@ -2646,7 +2814,12 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Connect only to the specified node(s)</translation> </message> <message> - <location line="+3"/> + <location line="+2"/> + <source>Connect to JSON-RPC on <port> (default: 8332 or testnet: 18332)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> <source>Corrupted block database detected</source> <translation>Corrupted block database detected</translation> </message> @@ -2776,7 +2949,12 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Incorrect or no genesis block found. Wrong datadir for network?</translation> </message> <message> - <location line="+18"/> + <location line="+3"/> + <source>Invalid -onion address: '%s'</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+15"/> <source>Not enough file descriptors available.</source> <translation>Not enough file descriptors available.</translation> </message> @@ -2786,7 +2964,12 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Rebuild block chain index from current blk000??.dat files</translation> </message> <message> - <location line="+16"/> + <location line="+6"/> + <source>Send command to Bitcoin server</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+10"/> <source>Set the number of threads to service RPC calls (default: 4)</source> <translation>Set the number of threads to service RPC calls (default: 4)</translation> </message> @@ -2796,7 +2979,17 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Specify wallet file (within data directory)</translation> </message> <message> - <location line="+20"/> + <location line="+2"/> + <source>Start Bitcoin server</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+12"/> + <source>Usage (deprecated, use bitcoin-cli):</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+8"/> <source>Verifying blocks...</source> <translation>Verifying blocks...</translation> </message> @@ -2816,12 +3009,12 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>You need to rebuild the database using -reindex to change -txindex</translation> </message> <message> - <location line="-76"/> + <location line="-78"/> <source>Imports blocks from external blk000??.dat file</source> <translation>Imports blocks from external blk000??.dat file</translation> </message> <message> - <location line="-96"/> + <location line="-98"/> <source>Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)</source> <translation>Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)</translation> </message> @@ -2831,17 +3024,12 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0)</translation> </message> <message> - <location line="+81"/> + <location line="+83"/> <source>Information</source> <translation>Information</translation> </message> <message> - <location line="+3"/> - <source>Invalid -tor address: '%s'</source> - <translation>Invalid -tor address: '%s'</translation> - </message> - <message> - <location line="+1"/> + <location line="+4"/> <source>Invalid amount for -minrelaytxfee=<amount>: '%s'</source> <translation>Invalid amount for -minrelaytxfee=<amount>: '%s'</translation> </message> @@ -2936,7 +3124,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Specify connection timeout in milliseconds (default: 5000)</translation> </message> <message> - <location line="+5"/> + <location line="+6"/> <source>System error: </source> <translation>System error: </translation> </message> @@ -2956,7 +3144,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Transaction too large</translation> </message> <message> - <location line="+7"/> + <location line="+8"/> <source>Use UPnP to map the listening port (default: 0)</source> <translation>Use UPnP to map the listening port (default: 0)</translation> </message> @@ -2991,32 +3179,32 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>wallet.dat corrupt, salvage failed</translation> </message> <message> - <location line="-52"/> + <location line="-54"/> <source>Password for JSON-RPC connections</source> <translation>Password for JSON-RPC connections</translation> </message> <message> - <location line="-68"/> + <location line="-70"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>Allow JSON-RPC connections from specified IP address</translation> </message> <message> - <location line="+77"/> + <location line="+79"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>Send commands to node running on <ip> (default: 127.0.0.1)</translation> </message> <message> - <location line="-124"/> + <location line="-126"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation>Execute command when the best block changes (%s in cmd is replaced by block hash)</translation> </message> <message> - <location line="+152"/> + <location line="+155"/> <source>Upgrade wallet to latest format</source> <translation>Upgrade wallet to latest format</translation> </message> <message> - <location line="-22"/> + <location line="-23"/> <source>Set key pool size to <n> (default: 100)</source> <translation>Set key pool size to <n> (default: 100)</translation> </message> @@ -3026,12 +3214,12 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Rescan the block chain for missing wallet transactions</translation> </message> <message> - <location line="+36"/> + <location line="+38"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>Use OpenSSL (https) for JSON-RPC connections</translation> </message> <message> - <location line="-27"/> + <location line="-29"/> <source>Server certificate file (default: server.cert)</source> <translation>Server certificate file (default: server.cert)</translation> </message> @@ -3041,12 +3229,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Server private key (default: server.pem)</translation> </message> <message> - <location line="-159"/> - <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> - <translation>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> - </message> - <message> - <location line="+174"/> + <location line="+16"/> <source>This help message</source> <translation>This help message</translation> </message> @@ -3056,17 +3239,17 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Unable to bind to %s on this computer (bind returned error %d, %s)</translation> </message> <message> - <location line="-93"/> + <location line="-95"/> <source>Connect through socks proxy</source> <translation>Connect through socks proxy</translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>Allow DNS lookups for -addnode, -seednode and -connect</translation> </message> <message> - <location line="+56"/> + <location line="+58"/> <source>Loading addresses...</source> <translation>Loading addresses...</translation> </message> @@ -3081,17 +3264,17 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Error loading wallet.dat: Wallet requires newer version of Bitcoin</translation> </message> <message> - <location line="+96"/> + <location line="+98"/> <source>Wallet needed to be rewritten: restart Bitcoin to complete</source> <translation>Wallet needed to be rewritten: restart Bitcoin to complete</translation> </message> <message> - <location line="-98"/> + <location line="-100"/> <source>Error loading wallet.dat</source> <translation>Error loading wallet.dat</translation> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid -proxy address: '%s'</source> <translation>Invalid -proxy address: '%s'</translation> </message> @@ -3106,7 +3289,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Unknown -socks proxy version requested: %i</translation> </message> <message> - <location line="-98"/> + <location line="-100"/> <source>Cannot resolve -bind address: '%s'</source> <translation>Cannot resolve -bind address: '%s'</translation> </message> @@ -3116,7 +3299,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Cannot resolve -externalip address: '%s'</translation> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>Invalid amount for -paytxfee=<amount>: '%s'</translation> </message> @@ -3136,7 +3319,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Loading block index...</translation> </message> <message> - <location line="-58"/> + <location line="-60"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>Add a node to connect to and attempt to keep the connection open</translation> </message> @@ -3146,7 +3329,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Unable to bind to %s on this computer. Bitcoin is probably already running.</translation> </message> <message> - <location line="+67"/> + <location line="+69"/> <source>Fee per KB to add to transactions you send</source> <translation>Fee per KB to add to transactions you send</translation> </message> @@ -3156,7 +3339,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Loading wallet...</translation> </message> <message> - <location line="-53"/> + <location line="-54"/> <source>Cannot downgrade wallet</source> <translation>Cannot downgrade wallet</translation> </message> @@ -3166,7 +3349,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Cannot write default address</translation> </message> <message> - <location line="+65"/> + <location line="+66"/> <source>Rescanning...</source> <translation>Rescanning...</translation> </message> @@ -3176,17 +3359,17 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Done loading</translation> </message> <message> - <location line="+84"/> + <location line="+85"/> <source>To use the %s option</source> <translation>To use the %s option</translation> </message> <message> - <location line="-76"/> + <location line="-77"/> <source>Error</source> <translation>Error</translation> </message> <message> - <location line="-31"/> + <location line="-33"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 95efc58320..4db048dcce 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -6,6 +6,9 @@ #include "bitcoinunits.h" #include "init.h" +#include "core.h" +#include "wallet.h" +#include "netbase.h" #include "walletdb.h" #include "guiutil.h" diff --git a/src/qt/paymentrequestplus.cpp b/src/qt/paymentrequestplus.cpp index f6a898ff7c..c8afd837d2 100644 --- a/src/qt/paymentrequestplus.cpp +++ b/src/qt/paymentrequestplus.cpp @@ -75,8 +75,7 @@ bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) c digestAlgorithm = EVP_sha1(); } else if (paymentRequest.pki_type() == "none") { - if (fDebug) - qDebug() << "PaymentRequestPlus::getMerchant : Payment request: pki_type == none"; + qDebug() << "PaymentRequestPlus::getMerchant : Payment request: pki_type == none"; return false; } else { diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index 72f75b42e6..0f386680ac 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. @@ -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() @@ -86,9 +87,7 @@ static QList<QString> savedPaymentRequests; static void ReportInvalidCertificate(const QSslCertificate& cert) { - if (fDebug) { - qDebug() << "ReportInvalidCertificate : Payment server found an invalid certificate: " << cert.subjectInfo(QSslCertificate::CommonName); - } + qDebug() << "ReportInvalidCertificate : Payment server found an invalid certificate: " << cert.subjectInfo(QSslCertificate::CommonName); } // @@ -159,8 +158,7 @@ void PaymentServer::LoadRootCAs(X509_STORE* _store) continue; } } - if (fDebug) - qDebug() << "PaymentServer::LoadRootCAs : Loaded " << nRootCerts << " root certificates"; + qDebug() << "PaymentServer::LoadRootCAs : Loaded " << nRootCerts << " root certificates"; // Project for another day: // Fetch certificate revocation lists, and add them to certStore. @@ -188,7 +186,7 @@ bool PaymentServer::ipcSendCommandLine(int argc, char* argv[]) if (arg.startsWith("-")) continue; - if (arg.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: + if (arg.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: URI { savedPaymentRequests.append(arg); @@ -219,9 +217,9 @@ bool PaymentServer::ipcSendCommandLine(int argc, char* argv[]) } else { - qDebug() << "PaymentServer::ipcSendCommandLine : Payment request file does not exist: " << argv[i]; // Printing to debug.log is about the best we can do here, the // GUI hasn't started yet so we can't pop up a message box. + qDebug() << "PaymentServer::ipcSendCommandLine : Payment request file does not exist: " << arg; } } @@ -245,16 +243,24 @@ bool PaymentServer::ipcSendCommandLine(int argc, char* argv[]) delete socket; fResult = true; } + return fResult; } -PaymentServer::PaymentServer(QObject* parent, bool startLocalServer) : QObject(parent), saveURIs(true) +PaymentServer::PaymentServer(QObject* parent, bool startLocalServer) : + QObject(parent), + saveURIs(true), + uriServer(0), + netManager(0), + optionsModel(0) { // Verify that the version of the library that we linked against is // compatible with the version of the headers we compiled against. GOOGLE_PROTOBUF_VERIFY_VERSION; - // Install global event filter to catch QFileOpenEvents on the mac (sent when you click bitcoin: links) + // Install global event filter to catch QFileOpenEvents + // on Mac: sent when you click bitcoin: links + // other OSes: helpful when dealing with payment request files (in the future) if (parent) parent->installEventFilter(this); @@ -269,8 +275,10 @@ PaymentServer::PaymentServer(QObject* parent, bool startLocalServer) : QObject(p if (!uriServer->listen(name)) qDebug() << "PaymentServer::PaymentServer : Cannot start bitcoin: click-to-pay handler"; - else + else { connect(uriServer, SIGNAL(newConnection()), this, SLOT(handleURIConnection())); + connect(this, SIGNAL(receivedPaymentACK(QString)), this, SLOT(handlePaymentACK(QString))); + } } // netManager is null until uiReady() is called @@ -309,10 +317,10 @@ void PaymentServer::initNetManager() if (netManager != NULL) delete netManager; - // netManager is used to fetch paymentrequests given in bitcoin: URI's + // netManager is used to fetch paymentrequests given in bitcoin: URIs netManager = new QNetworkAccessManager(this); - // Use proxy settings from optionsModel: + // Use proxy settings from optionsModel QString proxyIP; quint16 proxyPort; if (optionsModel->getProxySettings(proxyIP, proxyPort)) @@ -359,23 +367,27 @@ void PaymentServer::handleURIOrFile(const QString& s) #endif if (uri.hasQueryItem("request")) { - QByteArray temp; temp.append(uri.queryItemValue("request")); + QByteArray temp; + temp.append(uri.queryItemValue("request")); QString decoded = QUrl::fromPercentEncoding(temp); QUrl fetchUrl(decoded, QUrl::StrictMode); - if (fDebug) - qDebug() << "PaymentServer::handleURIOrFile : fetchRequest(" << fetchUrl << ")"; + qDebug() << "PaymentServer::handleURIOrFile : fetchRequest(" << fetchUrl << ")"; if (fetchUrl.isValid()) fetchRequest(fetchUrl); else - qDebug() << "PaymentServer::handleURIOrFile : Invalid url: " << fetchUrl; + qDebug() << "PaymentServer::handleURIOrFile : Invalid URL: " << fetchUrl; return; } SendCoinsRecipient recipient; if (GUIUtil::parseBitcoinURI(s, &recipient)) emit receivedPaymentRequest(recipient); + else + emit message(tr("URI handling"), + tr("URI can not be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters."), + CClientUIInterface::ICON_WARNING); return; } @@ -407,10 +419,10 @@ void PaymentServer::handleURIConnection() if (clientConnection->bytesAvailable() < (int)sizeof(quint16)) { return; } - QString message; - in >> message; + QString msg; + in >> msg; - handleURIOrFile(message); + handleURIOrFile(msg); } bool PaymentServer::readPaymentRequest(const QString& filename, PaymentRequestPlus& request) @@ -443,11 +455,11 @@ bool PaymentServer::processPaymentRequest(PaymentRequestPlus& request, QList<Sen foreach(const PAIRTYPE(CScript, qint64)& sendingTo, sendingTos) { CTxOut txOut(sendingTo.second, sendingTo.first); if (txOut.IsDust(CTransaction::nMinRelayTxFee)) { - QString message = QObject::tr("Requested payment amount (%1) too small") + QString msg = tr("Requested payment amount of %1 is too small (considered dust).") .arg(BitcoinUnits::formatWithUnit(optionsModel->getDisplayUnit(), sendingTo.second)); - qDebug() << "PaymentServer::processPaymentRequest : " << message; - emit reportError(tr("Payment request error"), message, CClientUIInterface::MODAL); + qDebug() << "PaymentServer::processPaymentRequest : " << msg; + emit message(tr("Payment request error"), msg, CClientUIInterface::MSG_ERROR); return false; } @@ -459,8 +471,7 @@ bool PaymentServer::processPaymentRequest(PaymentRequestPlus& request, QList<Sen if (request.getMerchant(PaymentServer::certStore, recipients[0].authenticatedMerchant)) { recipients[0].paymentRequest = request; recipients[0].amount = totalAmount; - if (fDebug) - qDebug() << "PaymentServer::processPaymentRequest : Payment request from " << recipients[0].authenticatedMerchant; + qDebug() << "PaymentServer::processPaymentRequest : Payment request from " << recipients[0].authenticatedMerchant; } else { recipients.clear(); @@ -471,26 +482,21 @@ 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 recipients[i].paymentRequest = request; recipients[i].address = QString::fromStdString(CBitcoinAddress(dest).ToString()); - if (fDebug) - qDebug() << "PaymentServer::processPaymentRequest : Payment request, insecure " << recipients[i].address; + qDebug() << "PaymentServer::processPaymentRequest : Payment request, insecure " << recipients[i].address; } else { // Insecure payments to custom bitcoin addresses are not supported // (there is no good way to tell the user where they are paying in a way // they'd have a chance of understanding). - emit reportError(tr("Payment request error"), - tr("Insecure requests to custom payment scripts unsupported"), - CClientUIInterface::MODAL); + emit message(tr("Payment request error"), + tr("Insecure requests to custom payment scripts unsupported"), + CClientUIInterface::MSG_ERROR); return false; } } @@ -518,7 +524,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); @@ -527,7 +533,7 @@ void PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipien payment.add_transactions(transaction.data(), transaction.size()); // Create a new refund address, or re-use: - QString account = tr("Refund from") + QString(" ") + recipient.authenticatedMerchant; + QString account = tr("Refund from %1").arg(recipient.authenticatedMerchant); std::string strAccount = account.toStdString(); set<CTxDestination> refundAddresses = wallet->GetAccountAddresses(strAccount); if (!refundAddresses.empty()) { @@ -569,11 +575,12 @@ void PaymentServer::netRequestFinished(QNetworkReply* reply) reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { - QString message = QObject::tr("Error communicating with %1: %2") + QString msg = tr("Error communicating with %1: %2") .arg(reply->request().url().toString()) .arg(reply->errorString()); - qDebug() << "PaymentServer::netRequestFinished : " << message; - emit reportError(tr("Network request error"), message, CClientUIInterface::MODAL); + + qDebug() << "PaymentServer::netRequestFinished : " << msg; + emit message(tr("Network request error"), msg, CClientUIInterface::MSG_ERROR); return; } @@ -585,12 +592,13 @@ void PaymentServer::netRequestFinished(QNetworkReply* reply) PaymentRequestPlus request; QList<SendCoinsRecipient> recipients; if (request.parse(data) && processPaymentRequest(request, recipients)) { - foreach (const SendCoinsRecipient& recipient, recipients){ + foreach (const SendCoinsRecipient& recipient, recipients) { emit receivedPaymentRequest(recipient); } } else qDebug() << "PaymentServer::netRequestFinished : Error processing payment request"; + return; } else if (requestType == "PaymentACK") @@ -598,13 +606,14 @@ void PaymentServer::netRequestFinished(QNetworkReply* reply) payments::PaymentACK paymentACK; if (!paymentACK.ParseFromArray(data.data(), data.size())) { - QString message = QObject::tr("Bad response from server %1") + QString msg = tr("Bad response from server %1") .arg(reply->request().url().toString()); - qDebug() << "PaymentServer::netRequestFinished : " << message; - emit reportError(tr("Network request error"), message, CClientUIInterface::MODAL); + + qDebug() << "PaymentServer::netRequestFinished : " << msg; + emit message(tr("Network request error"), msg, CClientUIInterface::MSG_ERROR); } else { - emit receivedPaymentACK(QString::fromStdString(paymentACK.memo())); + emit receivedPaymentACK(GUIUtil::HtmlEscape(paymentACK.memo())); } } } @@ -618,10 +627,16 @@ void PaymentServer::reportSslErrors(QNetworkReply* reply, const QList<QSslError> qDebug() << "PaymentServer::reportSslErrors : " << err; errString += err.errorString() + "\n"; } - emit reportError(tr("Network request error"), errString, CClientUIInterface::MODAL); + emit message(tr("Network request error"), errString, CClientUIInterface::MSG_ERROR); } void PaymentServer::setOptionsModel(OptionsModel *optionsModel) { this->optionsModel = optionsModel; } + +void PaymentServer::handlePaymentACK(const QString& paymentACKMsg) +{ + // currently we don't futher process or store the paymentACK message + emit message(tr("Payment acknowledged"), paymentACKMsg, CClientUIInterface::ICON_INFORMATION | CClientUIInterface::MODAL); +} diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h index f9d827204b..9f43571edc 100644 --- a/src/qt/paymentserver.h +++ b/src/qt/paymentserver.h @@ -88,10 +88,10 @@ signals: void receivedPaymentRequest(SendCoinsRecipient); // Fired when a valid PaymentACK is received - void receivedPaymentACK(QString); + void receivedPaymentACK(const QString &paymentACKMsg); - // Fired when an error should be reported to the user - void reportError(QString, QString, unsigned int); + // Fired when a message should be reported to the user + void message(const QString &title, const QString &message, unsigned int style); public slots: // Signal this when the main window's UI is ready @@ -105,6 +105,7 @@ private slots: void handleURIConnection(); void netRequestFinished(QNetworkReply*); void reportSslErrors(QNetworkReply*, const QList<QSslError> &); + void handlePaymentACK(const QString& paymentACKMsg); private: static bool readPaymentRequest(const QString& filename, PaymentRequestPlus& request); diff --git a/src/qt/qrcodedialog.cpp b/src/qt/qrcodedialog.cpp deleted file mode 100644 index 6ddcaaf5d9..0000000000 --- a/src/qt/qrcodedialog.cpp +++ /dev/null @@ -1,173 +0,0 @@ -#include "qrcodedialog.h" -#include "ui_qrcodedialog.h" - -#include "bitcoinunits.h" -#include "guiconstants.h" -#include "guiutil.h" -#include "optionsmodel.h" - -#include <QPixmap> -#if QT_VERSION < 0x050000 -#include <QUrl> -#endif - -#include <qrencode.h> - -QRCodeDialog::QRCodeDialog(const QString &addr, const QString &label, bool enableReq, QWidget *parent) : - QDialog(parent), - ui(new Ui::QRCodeDialog), - model(0), - address(addr) -{ - ui->setupUi(this); - - setWindowTitle(QString("%1").arg(address)); - - ui->chkReqPayment->setVisible(enableReq); - ui->lblAmount->setVisible(enableReq); - ui->lnReqAmount->setVisible(enableReq); - - ui->lnLabel->setText(label); - - ui->btnSaveAs->setEnabled(false); - - genCode(); -} - -QRCodeDialog::~QRCodeDialog() -{ - delete ui; -} - -void QRCodeDialog::setModel(OptionsModel *model) -{ - this->model = model; - - if (model) - connect(model, SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); - - // update the display unit, to not use the default ("BTC") - updateDisplayUnit(); -} - -void QRCodeDialog::genCode() -{ - QString uri = getURI(); - - if (uri != "") - { - ui->lblQRCode->setText(""); - - QRcode *code = QRcode_encodeString(uri.toUtf8().constData(), 0, QR_ECLEVEL_L, QR_MODE_8, 1); - if (!code) - { - ui->lblQRCode->setText(tr("Error encoding URI into QR Code.")); - return; - } - myImage = QImage(code->width + 8, code->width + 8, QImage::Format_RGB32); - myImage.fill(0xffffff); - unsigned char *p = code->data; - for (int y = 0; y < code->width; y++) - { - for (int x = 0; x < code->width; x++) - { - myImage.setPixel(x + 4, y + 4, ((*p & 1) ? 0x0 : 0xffffff)); - p++; - } - } - QRcode_free(code); - - ui->lblQRCode->setPixmap(QPixmap::fromImage(myImage).scaled(300, 300)); - - ui->outUri->setPlainText(uri); - } -} - -QString QRCodeDialog::getURI() -{ - QString ret = QString("bitcoin:%1").arg(address); - int paramCount = 0; - - ui->outUri->clear(); - - if (ui->chkReqPayment->isChecked()) - { - if (ui->lnReqAmount->validate()) - { - // even if we allow a non BTC unit input in lnReqAmount, we generate the URI with BTC as unit (as defined in BIP21) - ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnits::BTC, ui->lnReqAmount->value())); - paramCount++; - } - else - { - ui->btnSaveAs->setEnabled(false); - ui->lblQRCode->setText(tr("The entered amount is invalid, please check.")); - return QString(""); - } - } - - if (!ui->lnLabel->text().isEmpty()) - { - QString lbl(QUrl::toPercentEncoding(ui->lnLabel->text())); - ret += QString("%1label=%2").arg(paramCount == 0 ? "?" : "&").arg(lbl); - paramCount++; - } - - if (!ui->lnMessage->text().isEmpty()) - { - QString msg(QUrl::toPercentEncoding(ui->lnMessage->text())); - ret += QString("%1message=%2").arg(paramCount == 0 ? "?" : "&").arg(msg); - paramCount++; - } - - // limit URI length to prevent a DoS against the QR-Code dialog - if (ret.length() > MAX_URI_LENGTH) - { - ui->btnSaveAs->setEnabled(false); - ui->lblQRCode->setText(tr("Resulting URI too long, try to reduce the text for label / message.")); - return QString(""); - } - - ui->btnSaveAs->setEnabled(true); - return ret; -} - -void QRCodeDialog::on_lnReqAmount_textChanged() -{ - genCode(); -} - -void QRCodeDialog::on_lnLabel_textChanged() -{ - genCode(); -} - -void QRCodeDialog::on_lnMessage_textChanged() -{ - genCode(); -} - -void QRCodeDialog::on_btnSaveAs_clicked() -{ - QString fn = GUIUtil::getSaveFileName(this, tr("Save QR Code"), QString(), tr("PNG Images (*.png)")); - if (!fn.isEmpty()) - myImage.scaled(EXPORT_IMAGE_SIZE, EXPORT_IMAGE_SIZE).save(fn); -} - -void QRCodeDialog::on_chkReqPayment_toggled(bool fChecked) -{ - if (!fChecked) - // if chkReqPayment is not active, don't display lnReqAmount as invalid - ui->lnReqAmount->setValid(true); - - genCode(); -} - -void QRCodeDialog::updateDisplayUnit() -{ - if (model) - { - // Update lnReqAmount with the current unit - ui->lnReqAmount->setDisplayUnit(model->getDisplayUnit()); - } -} diff --git a/src/qt/qrcodedialog.h b/src/qt/qrcodedialog.h deleted file mode 100644 index c55c34bce6..0000000000 --- a/src/qt/qrcodedialog.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef QRCODEDIALOG_H -#define QRCODEDIALOG_H - -#include <QDialog> -#include <QImage> - -namespace Ui { - class QRCodeDialog; -} -class OptionsModel; - -class QRCodeDialog : public QDialog -{ - Q_OBJECT - -public: - explicit QRCodeDialog(const QString &addr, const QString &label, bool enableReq, QWidget *parent = 0); - ~QRCodeDialog(); - - void setModel(OptionsModel *model); - -private slots: - void on_lnReqAmount_textChanged(); - void on_lnLabel_textChanged(); - void on_lnMessage_textChanged(); - void on_btnSaveAs_clicked(); - void on_chkReqPayment_toggled(bool fChecked); - - void updateDisplayUnit(); - -private: - Ui::QRCodeDialog *ui; - OptionsModel *model; - QString address; - QImage myImage; - - void genCode(); - QString getURI(); -}; - -#endif // QRCODEDIALOG_H diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp new file mode 100644 index 0000000000..f3ab343fea --- /dev/null +++ b/src/qt/receivecoinsdialog.cpp @@ -0,0 +1,107 @@ +#include "receivecoinsdialog.h" +#include "ui_receivecoinsdialog.h" + +#include "walletmodel.h" +#include "bitcoinunits.h" +#include "addressbookpage.h" +#include "optionsmodel.h" +#include "guiutil.h" +#include "receiverequestdialog.h" +#include "addresstablemodel.h" + +#include <QMessageBox> +#include <QTextDocument> +#include <QScrollBar> + +ReceiveCoinsDialog::ReceiveCoinsDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::ReceiveCoinsDialog), + model(0) +{ + ui->setupUi(this); + +#ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac + ui->clearButton->setIcon(QIcon()); + ui->receiveButton->setIcon(QIcon()); +#endif + connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear())); +} + +void ReceiveCoinsDialog::setModel(WalletModel *model) +{ + this->model = model; + + if(model && model->getOptionsModel()) + { + connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); + updateDisplayUnit(); + } +} + +ReceiveCoinsDialog::~ReceiveCoinsDialog() +{ + delete ui; +} + +void ReceiveCoinsDialog::clear() +{ + ui->reqAmount->clear(); + ui->reqLabel->setText(""); + ui->reqMessage->setText(""); + ui->reuseAddress->setChecked(false); + updateDisplayUnit(); +} + +void ReceiveCoinsDialog::reject() +{ + clear(); +} + +void ReceiveCoinsDialog::accept() +{ + clear(); +} + +void ReceiveCoinsDialog::updateDisplayUnit() +{ + if(model && model->getOptionsModel()) + { + ui->reqAmount->setDisplayUnit(model->getOptionsModel()->getDisplayUnit()); + } +} + +void ReceiveCoinsDialog::on_receiveButton_clicked() +{ + if(!model || !model->getOptionsModel() || !model->getAddressTableModel()) + return; + + QString address; + QString label = ui->reqLabel->text(); + if(ui->reuseAddress->isChecked()) + { + /* Choose existing receiving address */ + AddressBookPage dlg(AddressBookPage::ForSelection, AddressBookPage::ReceivingTab, this); + dlg.setModel(model->getAddressTableModel()); + if(dlg.exec()) + { + address = dlg.getReturnValue(); + if(label.isEmpty()) /* If no label provided, use the previously used label */ + { + label = model->getAddressTableModel()->labelForAddress(address); + } + } else { + return; + } + } else { + /* Generate new receiving address */ + address = model->getAddressTableModel()->addRow(AddressTableModel::Receive, label, ""); + } + SendCoinsRecipient info(address, label, + ui->reqAmount->value(), ui->reqMessage->text()); + ReceiveRequestDialog *dialog = new ReceiveRequestDialog(this); + dialog->setModel(model->getOptionsModel()); + dialog->setInfo(info); + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->show(); + clear(); +} diff --git a/src/qt/receivecoinsdialog.h b/src/qt/receivecoinsdialog.h new file mode 100644 index 0000000000..8d12e55afe --- /dev/null +++ b/src/qt/receivecoinsdialog.h @@ -0,0 +1,38 @@ +#ifndef RECEIVECOINSDIALOG_H +#define RECEIVECOINSDIALOG_H + +#include <QDialog> +#include <QVariant> + +namespace Ui { + class ReceiveCoinsDialog; +} +class WalletModel; +class OptionsModel; + +/** Dialog for requesting payment of bitcoins */ +class ReceiveCoinsDialog : public QDialog +{ + Q_OBJECT + +public: + explicit ReceiveCoinsDialog(QWidget *parent = 0); + ~ReceiveCoinsDialog(); + + void setModel(WalletModel *model); + +public slots: + void clear(); + void reject(); + void accept(); + +private: + Ui::ReceiveCoinsDialog *ui; + WalletModel *model; + +private slots: + void on_receiveButton_clicked(); + void updateDisplayUnit(); +}; + +#endif // RECEIVECOINSDIALOG_H diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp new file mode 100644 index 0000000000..896acfd233 --- /dev/null +++ b/src/qt/receiverequestdialog.cpp @@ -0,0 +1,185 @@ +#include "receiverequestdialog.h" +#include "ui_receiverequestdialog.h" + +#include "bitcoinunits.h" +#include "guiconstants.h" +#include "guiutil.h" +#include "optionsmodel.h" +#include "walletmodel.h" + +#include <QPixmap> +#include <QClipboard> +#include <QMouseEvent> +#include <QDrag> +#include <QMimeData> +#if QT_VERSION < 0x050000 +#include <QUrl> +#endif + +#if defined(HAVE_CONFIG_H) +#include "bitcoin-config.h" /* for USE_QRCODE */ +#endif + +#ifdef USE_QRCODE +#include <qrencode.h> +#endif + +QRImageWidget::QRImageWidget(QWidget *parent): + QLabel(parent) +{ + setContextMenuPolicy(Qt::ActionsContextMenu); + + QAction *saveImageAction = new QAction(tr("&Save Image..."), this); + connect(saveImageAction, SIGNAL(triggered()), this, SLOT(saveImage())); + addAction(saveImageAction); + QAction *copyImageAction = new QAction(tr("&Copy Image"), this); + connect(copyImageAction, SIGNAL(triggered()), this, SLOT(copyImage())); + addAction(copyImageAction); +} + +QImage QRImageWidget::exportImage() +{ + return pixmap()->toImage().scaled(EXPORT_IMAGE_SIZE, EXPORT_IMAGE_SIZE); +} + +void QRImageWidget::mousePressEvent(QMouseEvent *event) +{ + if(event->button() == Qt::LeftButton) + { + event->accept(); + QMimeData *mimeData = new QMimeData; + mimeData->setImageData(exportImage()); + + QDrag *drag = new QDrag(this); + drag->setMimeData(mimeData); + drag->exec(); + } else { + QLabel::mousePressEvent(event); + } +} + +void QRImageWidget::saveImage() +{ + QString fn = GUIUtil::getSaveFileName(this, tr("Save QR Code"), QString(), tr("PNG Images (*.png)")); + if (!fn.isEmpty()) + { + exportImage().save(fn); + } +} + +void QRImageWidget::copyImage() +{ + QApplication::clipboard()->setImage(exportImage()); +} + +ReceiveRequestDialog::ReceiveRequestDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::ReceiveRequestDialog), + model(0) +{ + ui->setupUi(this); + +#ifndef USE_QRCODE + ui->btnSaveAs->setVisible(false); + ui->btnCopyImage->setVisible(false); + ui->lblQRCode->setVisible(false); +#endif + + connect(ui->btnSaveAs, SIGNAL(clicked()), ui->lblQRCode, SLOT(saveImage())); + connect(ui->btnCopyImage, SIGNAL(clicked()), ui->lblQRCode, SLOT(copyImage())); +} + +ReceiveRequestDialog::~ReceiveRequestDialog() +{ + delete ui; +} + +void ReceiveRequestDialog::setModel(OptionsModel *model) +{ + this->model = model; + + if (model) + connect(model, SIGNAL(displayUnitChanged(int)), this, SLOT(update())); + + // update the display unit if necessary + update(); +} + +void ReceiveRequestDialog::setInfo(const SendCoinsRecipient &info) +{ + this->info = info; + update(); +} + +void ReceiveRequestDialog::update() +{ + if(!model) + return; + QString target = info.label; + if(target.isEmpty()) + target = info.address; + setWindowTitle(tr("Request payment to %1").arg(target)); + + QString uri = GUIUtil::formatBitcoinURI(info); + ui->btnSaveAs->setEnabled(false); + QString html; + html += "<html><font face='verdana, arial, helvetica, sans-serif'>"; + html += "<b>"+tr("Payment information")+"</b><br>"; + html += "<b>"+tr("URI")+"</b>: "; + html += "<a href=\""+uri+"\">" + GUIUtil::HtmlEscape(uri) + "</a><br>"; + html += "<b>"+tr("Address")+"</b>: " + GUIUtil::HtmlEscape(info.address) + "<br>"; + if(info.amount) + html += "<b>"+tr("Amount")+"</b>: " + BitcoinUnits::formatWithUnit(model->getDisplayUnit(), info.amount) + "<br>"; + if(!info.label.isEmpty()) + html += "<b>"+tr("Label")+"</b>: " + GUIUtil::HtmlEscape(info.label) + "<br>"; + if(!info.message.isEmpty()) + html += "<b>"+tr("Message")+"</b>: " + GUIUtil::HtmlEscape(info.message) + "<br>"; + ui->outUri->setText(html); + +#ifdef USE_QRCODE + ui->lblQRCode->setText(""); + if(!uri.isEmpty()) + { + // limit URI length + if (uri.length() > MAX_URI_LENGTH) + { + ui->lblQRCode->setText(tr("Resulting URI too long, try to reduce the text for label / message.")); + } else { + QRcode *code = QRcode_encodeString(uri.toUtf8().constData(), 0, QR_ECLEVEL_L, QR_MODE_8, 1); + if (!code) + { + ui->lblQRCode->setText(tr("Error encoding URI into QR Code.")); + return; + } + QImage myImage = QImage(code->width + 8, code->width + 8, QImage::Format_RGB32); + myImage.fill(0xffffff); + unsigned char *p = code->data; + for (int y = 0; y < code->width; y++) + { + for (int x = 0; x < code->width; x++) + { + myImage.setPixel(x + 4, y + 4, ((*p & 1) ? 0x0 : 0xffffff)); + p++; + } + } + QRcode_free(code); + + ui->lblQRCode->setPixmap(QPixmap::fromImage(myImage).scaled(300, 300)); + ui->btnSaveAs->setEnabled(true); + } + } +#endif +} + +void ReceiveRequestDialog::on_btnCopyURI_clicked() +{ + QString uri = GUIUtil::formatBitcoinURI(info); + QApplication::clipboard()->setText(uri, QClipboard::Clipboard); + QApplication::clipboard()->setText(uri, QClipboard::Selection); +} + +void ReceiveRequestDialog::on_btnCopyAddress_clicked() +{ + QApplication::clipboard()->setText(info.address, QClipboard::Clipboard); + QApplication::clipboard()->setText(info.address, QClipboard::Selection); +} diff --git a/src/qt/receiverequestdialog.h b/src/qt/receiverequestdialog.h new file mode 100644 index 0000000000..1beb873dfd --- /dev/null +++ b/src/qt/receiverequestdialog.h @@ -0,0 +1,57 @@ +#ifndef QRCODEDIALOG_H +#define QRCODEDIALOG_H + +#include "walletmodel.h" + +#include <QDialog> +#include <QImage> +#include <QLabel> + +namespace Ui { + class ReceiveRequestDialog; +} +class OptionsModel; + +/* Label widget for QR code. This image can be dragged, dropped, copied and saved + * to disk. + */ +class QRImageWidget : public QLabel +{ + Q_OBJECT + +public: + explicit QRImageWidget(QWidget *parent = 0); + QImage exportImage(); + +public slots: + void saveImage(); + void copyImage(); + +protected: + virtual void mousePressEvent(QMouseEvent *event); +}; + +class ReceiveRequestDialog : public QDialog +{ + Q_OBJECT + +public: + explicit ReceiveRequestDialog(QWidget *parent = 0); + ~ReceiveRequestDialog(); + + void setModel(OptionsModel *model); + void setInfo(const SendCoinsRecipient &info); + +private slots: + void on_btnCopyURI_clicked(); + void on_btnCopyAddress_clicked(); + + void update(); + +private: + Ui::ReceiveRequestDialog *ui; + OptionsModel *model; + SendCoinsRecipient info; +}; + +#endif // QRCODEDIALOG_H 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 00cea463ef..51304bc05c 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -1,14 +1,13 @@ #include "sendcoinsdialog.h" #include "ui_sendcoinsdialog.h" -#include "walletmodel.h" #include "bitcoinunits.h" -#include "addressbookpage.h" #include "optionsmodel.h" #include "sendcoinsentry.h" #include "guiutil.h" #include "askpassphrasedialog.h" #include "base58.h" +#include "ui_interface.h" #include <QMessageBox> #include <QTextDocument> @@ -137,41 +136,9 @@ void SendCoinsDialog::on_sendButton_clicked() // prepare transaction for getting txFee earlier WalletModelTransaction currentTransaction(recipients); WalletModel::SendCoinsReturn prepareStatus = model->prepareTransaction(currentTransaction); - - QString strSendCoins = tr("Send Coins"); - switch(prepareStatus.status) - { - case WalletModel::InvalidAddress: - QMessageBox::warning(this, strSendCoins, - tr("The recipient address is not valid, please recheck.")); - break; - case WalletModel::InvalidAmount: - QMessageBox::warning(this, strSendCoins, - tr("The amount to pay must be larger than 0.")); - break; - case WalletModel::AmountExceedsBalance: - QMessageBox::warning(this, strSendCoins, - tr("The amount exceeds your balance.")); - break; - case WalletModel::AmountWithFeeExceedsBalance: - 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()))); - break; - case WalletModel::DuplicateAddress: - 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, strSendCoins, - tr("Error: Transaction creation failed!")); - break; - case WalletModel::TransactionCommitFailed: - case WalletModel::OK: - case WalletModel::Aborted: // User aborted, nothing to do - default: - break; - } + // process prepareStatus and on error generate message shown to user + processSendCoinsReturn(prepareStatus, + BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), currentTransaction.getTransactionFee())); if(prepareStatus.status != WalletModel::OK) { fNewRecipientAllowed = true; @@ -209,19 +176,13 @@ void SendCoinsDialog::on_sendButton_clicked() } // now send the prepared transaction - WalletModel::SendCoinsReturn sendstatus = model->sendCoins(currentTransaction); - switch(sendstatus.status) + WalletModel::SendCoinsReturn sendStatus = model->sendCoins(currentTransaction); + // process sendStatus and on error generate message shown to user + processSendCoinsReturn(sendStatus); + + if (sendStatus.status == WalletModel::OK) { - case WalletModel::TransactionCommitFailed: - 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; } fNewRecipientAllowed = true; } @@ -357,17 +318,16 @@ bool SendCoinsDialog::handlePaymentRequest(const SendCoinsRecipient &rv) const payments::PaymentDetails& details = rv.paymentRequest.getDetails(); if (details.has_expires() && (int64)details.expires() < GetTime()) { - QMessageBox::warning(this, strSendCoins, - tr("Payment request expired")); + emit message(strSendCoins, tr("Payment request expired"), + CClientUIInterface::MSG_WARNING); return false; } } else { CBitcoinAddress address(rv.address.toStdString()); if (!address.IsValid()) { - QString strAddress(address.ToString().c_str()); - QMessageBox::warning(this, strSendCoins, - tr("Invalid payment address %1").arg(strAddress)); + emit message(strSendCoins, tr("Invalid payment address %1").arg(rv.address), + CClientUIInterface::MSG_WARNING); return false; } } @@ -391,3 +351,47 @@ void SendCoinsDialog::updateDisplayUnit() { setBalance(model->getBalance(), 0, 0); } + +void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg) +{ + QPair<QString, CClientUIInterface::MessageBoxFlags> msgParams; + // Default to a warning message, override if error message is needed + msgParams.second = CClientUIInterface::MSG_WARNING; + + // This comment is specific to SendCoinsDialog usage of WalletModel::SendCoinsReturn. + // WalletModel::TransactionCommitFailed is used only in WalletModel::sendCoins() + // all others are used only in WalletModel::prepareTransaction() + switch(sendCoinsReturn.status) + { + case WalletModel::InvalidAddress: + msgParams.first = tr("The recipient address is not valid, please recheck."); + break; + case WalletModel::InvalidAmount: + msgParams.first = tr("The amount to pay must be larger than 0."); + break; + case WalletModel::AmountExceedsBalance: + msgParams.first = tr("The amount exceeds your balance."); + break; + case WalletModel::AmountWithFeeExceedsBalance: + msgParams.first = tr("The total exceeds your balance when the %1 transaction fee is included.").arg(msgArg); + break; + case WalletModel::DuplicateAddress: + msgParams.first = tr("Duplicate address found, can only send to each address once per send operation."); + break; + case WalletModel::TransactionCreationFailed: + msgParams.first = tr("Transaction creation failed!"); + msgParams.second = CClientUIInterface::MSG_ERROR; + break; + case WalletModel::TransactionCommitFailed: + msgParams.first = tr("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."); + msgParams.second = CClientUIInterface::MSG_ERROR; + break; + // OK and Aborted are included to prevent a compiler warning. + case WalletModel::OK: + case WalletModel::Aborted: + default: + return; + } + + emit message(tr("Send Coins"), msgParams.first, msgParams.second); +} diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index f4bffedc9b..4e68e26731 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -1,16 +1,17 @@ #ifndef SENDCOINSDIALOG_H #define SENDCOINSDIALOG_H +#include "walletmodel.h" + #include <QDialog> #include <QVariant> +#include <QPair> namespace Ui { class SendCoinsDialog; } -class WalletModel; class SendCoinsEntry; class SendCoinsRecipient; -class OptionsModel; QT_BEGIN_NAMESPACE class QUrl; @@ -48,10 +49,19 @@ private: WalletModel *model; bool fNewRecipientAllowed; + // Process WalletModel::SendCoinsReturn and generate a pair consisting + // of a message and message flags for use in emit message(). + // Additional parameter msgArg can be used via .arg(msgArg). + void processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg = QString()); + private slots: void on_sendButton_clicked(); void removeEntry(SendCoinsEntry* entry); void updateDisplayUnit(); + +signals: + // Fired when a message should be reported to the user + void message(const QString &title, const QString &message, unsigned int style); }; #endif // SENDCOINSDIALOG_H diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index ee84f7bc11..2d42ecb568 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -49,7 +49,7 @@ void SendCoinsEntry::on_addressBookButton_clicked() { if(!model) return; - AddressBookPage dlg(AddressBookPage::ForSending, AddressBookPage::SendingTab, this); + AddressBookPage dlg(AddressBookPage::ForSelection, AddressBookPage::SendingTab, this); dlg.setModel(model->getAddressTableModel()); if(dlg.exec()) { @@ -60,12 +60,7 @@ void SendCoinsEntry::on_addressBookButton_clicked() void SendCoinsEntry::on_payTo_textChanged(const QString &address) { - if(!model) - return; - // Fill in label from address book, if address has an associated label - QString associatedLabel = model->getAddressTableModel()->labelForAddress(address); - if(!associatedLabel.isEmpty()) - ui->addAsLabel->setText(associatedLabel); + updateLabel(address); } void SendCoinsEntry::setModel(WalletModel *model) @@ -85,10 +80,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(); } @@ -154,17 +156,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->payTo_s->setText(recipient.authenticatedMerchant); ui->memoTextLabel_s->setText(QString::fromStdString(details.memo())); - ui->payAmount_s->setValue(value.amount); + ui->payAmount_s->setValue(recipient.amount); + ui->payAmount_s->setReadOnly(true); setCurrentWidget(ui->SendCoinsSecure); } } @@ -194,3 +199,19 @@ void SendCoinsEntry::updateDisplayUnit() ui->payAmount_s->setDisplayUnit(model->getOptionsModel()->getDisplayUnit()); } } + +bool SendCoinsEntry::updateLabel(const QString &address) +{ + if(!model) + return false; + + // Fill in label from address book, if address has an associated label + QString associatedLabel = model->getAddressTableModel()->labelForAddress(address); + if(!associatedLabel.isEmpty()) + { + ui->addAsLabel->setText(associatedLabel); + return true; + } + + return false; +} diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h index 49e622daf1..66d9752909 100644 --- a/src/qt/sendcoinsentry.h +++ b/src/qt/sendcoinsentry.h @@ -58,6 +58,8 @@ private: SendCoinsRecipient recipient; Ui::SendCoinsEntry *ui; WalletModel *model; + + bool updateLabel(const QString &address); }; #endif // SENDCOINSENTRY_H diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp index 1e9c84fe1a..ff3d00b326 100644 --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -86,7 +86,7 @@ void SignVerifyMessageDialog::on_addressBookButton_SM_clicked() { if (model && model->getAddressTableModel()) { - AddressBookPage dlg(AddressBookPage::ForSending, AddressBookPage::ReceivingTab, this); + AddressBookPage dlg(AddressBookPage::ForSelection, AddressBookPage::ReceivingTab, this); dlg.setModel(model->getAddressTableModel()); if (dlg.exec()) { @@ -178,7 +178,7 @@ void SignVerifyMessageDialog::on_addressBookButton_VM_clicked() { if (model && model->getAddressTableModel()) { - AddressBookPage dlg(AddressBookPage::ForSending, AddressBookPage::SendingTab, this); + AddressBookPage dlg(AddressBookPage::ForSelection, AddressBookPage::SendingTab, this); dlg.setModel(model->getAddressTableModel()); if (dlg.exec()) { diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index 7c9bc46c46..07b71a3c9a 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -66,9 +66,8 @@ SplashScreen::SplashScreen(const QPixmap &pixmap, Qt::WindowFlags f) : pixPaint.setFont(QFont(font, 10*fontFactor)); pixPaint.drawText(newPixmap.width()-titleTextWidth-paddingRight,paddingTop+titleCopyrightVSpace,copyrightText); - // draw testnet string if -testnet is on - if(QApplication::applicationName().contains(QString("-testnet"))) { - // draw copyright stuff + // draw testnet string if testnet is on + if(TestNet()) { QFont boldFont = QFont(font, 10*fontFactor); boldFont.setWeight(QFont::Bold); pixPaint.setFont(boldFont); diff --git a/src/qt/test/uritests.cpp b/src/qt/test/uritests.cpp index 802d74719e..df4df3154b 100644 --- a/src/qt/test/uritests.cpp +++ b/src/qt/test/uritests.cpp @@ -50,9 +50,8 @@ void URITests::uriTests() QVERIFY(rv.address == QString("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W")); QVERIFY(rv.label == QString()); - // We currently don't implement the message parameter (ok, yea, we break spec...) uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?req-message=Wikipedia Example Address")); - QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=1,000&label=Wikipedia Example")); QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv)); 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..63a72c4553 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)); } @@ -234,7 +234,10 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, int vout, int u } if (wtx.IsCoinBase()) - strHTML += "<br>" + tr("Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to \"not accepted\" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.") + "<br>"; + { + quint32 numBlocksToMaturity = COINBASE_MATURITY + 1; + strHTML += "<br>" + tr("Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to \"not accepted\" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.").arg(QString::number(numBlocksToMaturity)) + "<br>"; + } // // Debug view 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/transactionview.cpp b/src/qt/transactionview.cpp index a43e29c476..89ecf99656 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -11,6 +11,7 @@ #include "editaddressdialog.h" #include "optionsmodel.h" #include "guiutil.h" +#include "ui_interface.h" #include <QScrollBar> #include <QComboBox> @@ -20,7 +21,6 @@ #include <QLineEdit> #include <QTableView> #include <QHeaderView> -#include <QMessageBox> #include <QPoint> #include <QMenu> #include <QLabel> @@ -266,12 +266,12 @@ void TransactionView::changedAmount(const QString &amount) void TransactionView::exportClicked() { // CSV is currently the only supported format - QString filename = GUIUtil::getSaveFileName( - this, - tr("Export Transaction Data"), QString(), - tr("Comma separated file (*.csv)")); + QString filename = GUIUtil::getSaveFileName(this, + tr("Export Transaction History"), QString(), + tr("Comma separated file (*.csv)")); - if (filename.isNull()) return; + if (filename.isNull()) + return; CSVModelWriter writer(filename); @@ -285,10 +285,13 @@ void TransactionView::exportClicked() writer.addColumn(tr("Amount"), 0, TransactionTableModel::FormattedAmountRole); writer.addColumn(tr("ID"), 0, TransactionTableModel::TxIDRole); - if(!writer.write()) - { - QMessageBox::critical(this, tr("Error exporting"), tr("Could not write to file %1.").arg(filename), - QMessageBox::Abort, QMessageBox::Abort); + if(!writer.write()) { + emit message(tr("Exporting Failed"), tr("There was an error trying to save the transaction history to %1.").arg(filename), + CClientUIInterface::MSG_ERROR); + } + else { + emit message(tr("Exporting Successful"), tr("The transaction history was successfully saved to %1.").arg(filename), + CClientUIInterface::MSG_INFORMATION); } } diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index bb41a83e32..464ba3e8ce 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -71,6 +71,9 @@ private slots: signals: void doubleClicked(const QModelIndex&); + /** Fired when a message should be reported to the user */ + void message(const QString &title, const QString &message, unsigned int style); + public slots: void chooseDate(int idx); void chooseType(int idx); diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp index 8d6a1b387e..d2807f465a 100644 --- a/src/qt/walletframe.cpp +++ b/src/qt/walletframe.cpp @@ -5,20 +5,21 @@ * The Bitcoin Developers 2011-2013 */ #include "walletframe.h" +#include "walletview.h" #include "bitcoingui.h" -#include "walletstack.h" #include <QHBoxLayout> #include <QMessageBox> +#include <QStackedWidget> WalletFrame::WalletFrame(BitcoinGUI *_gui) : - QFrame(_gui) + QFrame(_gui), + gui(_gui) { // Leave HBox hook for adding a list view later QHBoxLayout *walletFrameLayout = new QHBoxLayout(this); setContentsMargins(0,0,0,0); - walletStack = new WalletStack(this); - walletStack->setBitcoinGUI(_gui); + walletStack = new QStackedWidget(this); walletFrameLayout->setContentsMargins(0,0,0,0); walletFrameLayout->addWidget(walletStack); } @@ -29,95 +30,157 @@ WalletFrame::~WalletFrame() void WalletFrame::setClientModel(ClientModel *clientModel) { - if (clientModel) - walletStack->setClientModel(clientModel); + this->clientModel = clientModel; } bool WalletFrame::addWallet(const QString& name, WalletModel *walletModel) { - return walletStack->addWallet(name, walletModel); + if (!gui || !clientModel || !walletModel || mapWalletViews.count(name) > 0) + return false; + + WalletView *walletView = new WalletView(this); + walletView->setBitcoinGUI(gui); + walletView->setClientModel(clientModel); + walletView->setWalletModel(walletModel); + walletView->showOutOfSyncWarning(bOutOfSync); + + /* TODO we should goto the currently selected page once dynamically adding wallets is supported */ + walletView->gotoOverviewPage(); + walletStack->addWidget(walletView); + mapWalletViews[name] = walletView; + + // Ensure a walletView is able to show the main window + connect(walletView, SIGNAL(showNormalIfMinimized()), gui, SLOT(showNormalIfMinimized())); + + return true; } bool WalletFrame::setCurrentWallet(const QString& name) { - // TODO: Check if valid name - return walletStack->setCurrentWallet(name); + if (mapWalletViews.count(name) == 0) + return false; + + WalletView *walletView = mapWalletViews.value(name); + walletStack->setCurrentWidget(walletView); + walletView->updateEncryptionStatus(); + return true; +} + +bool WalletFrame::removeWallet(const QString &name) +{ + if (mapWalletViews.count(name) == 0) + return false; + + WalletView *walletView = mapWalletViews.take(name); + walletStack->removeWidget(walletView); + return true; } void WalletFrame::removeAllWallets() { - walletStack->removeAllWallets(); + QMap<QString, WalletView*>::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + walletStack->removeWidget(i.value()); + mapWalletViews.clear(); } bool WalletFrame::handlePaymentRequest(const SendCoinsRecipient &recipient) { - return walletStack->handlePaymentRequest(recipient); + WalletView *walletView = (WalletView*)walletStack->currentWidget(); + if (!walletView) + return false; + + return walletView->handlePaymentRequest(recipient); } void WalletFrame::showOutOfSyncWarning(bool fShow) { - if (!walletStack) - return; - - walletStack->showOutOfSyncWarning(fShow); + bOutOfSync = fShow; + QMap<QString, WalletView*>::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + i.value()->showOutOfSyncWarning(fShow); } void WalletFrame::gotoOverviewPage() { - walletStack->gotoOverviewPage(); + QMap<QString, WalletView*>::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + i.value()->gotoOverviewPage(); } void WalletFrame::gotoHistoryPage() { - walletStack->gotoHistoryPage(); -} - -void WalletFrame::gotoAddressBookPage() -{ - walletStack->gotoAddressBookPage(); + QMap<QString, WalletView*>::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + i.value()->gotoHistoryPage(); } void WalletFrame::gotoReceiveCoinsPage() { - walletStack->gotoReceiveCoinsPage(); + QMap<QString, WalletView*>::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + i.value()->gotoReceiveCoinsPage(); } void WalletFrame::gotoSendCoinsPage(QString addr) { - walletStack->gotoSendCoinsPage(addr); + QMap<QString, WalletView*>::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + i.value()->gotoSendCoinsPage(addr); } void WalletFrame::gotoSignMessageTab(QString addr) { - walletStack->gotoSignMessageTab(addr); + WalletView *walletView = (WalletView*)walletStack->currentWidget(); + if (walletView) + walletView->gotoSignMessageTab(addr); } void WalletFrame::gotoVerifyMessageTab(QString addr) { - walletStack->gotoSignMessageTab(addr); + WalletView *walletView = (WalletView*)walletStack->currentWidget(); + if (walletView) + walletView->gotoVerifyMessageTab(addr); } void WalletFrame::encryptWallet(bool status) { - walletStack->encryptWallet(status); + WalletView *walletView = (WalletView*)walletStack->currentWidget(); + if (walletView) + walletView->encryptWallet(status); } void WalletFrame::backupWallet() { - walletStack->backupWallet(); + WalletView *walletView = (WalletView*)walletStack->currentWidget(); + if (walletView) + walletView->backupWallet(); } void WalletFrame::changePassphrase() { - walletStack->changePassphrase(); + WalletView *walletView = (WalletView*)walletStack->currentWidget(); + if (walletView) + walletView->changePassphrase(); } void WalletFrame::unlockWallet() { - walletStack->unlockWallet(); + WalletView *walletView = (WalletView*)walletStack->currentWidget(); + if (walletView) + walletView->unlockWallet(); +} + +void WalletFrame::usedSendingAddresses() +{ + WalletView *walletView = (WalletView*)walletStack->currentWidget(); + if (walletView) + walletView->usedSendingAddresses(); } -void WalletFrame::setEncryptionStatus() +void WalletFrame::usedReceivingAddresses() { - walletStack->setEncryptionStatus(); + WalletView *walletView = (WalletView*)walletStack->currentWidget(); + if (walletView) + walletView->usedReceivingAddresses(); } diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h index eaae053ccd..edf2390dbc 100644 --- a/src/qt/walletframe.h +++ b/src/qt/walletframe.h @@ -8,12 +8,17 @@ #define WALLETFRAME_H #include <QFrame> +#include <QMap> class BitcoinGUI; class ClientModel; class SendCoinsRecipient; class WalletModel; -class WalletStack; +class WalletView; + +QT_BEGIN_NAMESPACE +class QStackedWidget; +QT_END_NAMESPACE class WalletFrame : public QFrame { @@ -27,7 +32,7 @@ public: bool addWallet(const QString& name, WalletModel *walletModel); bool setCurrentWallet(const QString& name); - + bool removeWallet(const QString &name); void removeAllWallets(); bool handlePaymentRequest(const SendCoinsRecipient& recipient); @@ -35,15 +40,18 @@ public: void showOutOfSyncWarning(bool fShow); private: - WalletStack *walletStack; + QStackedWidget *walletStack; + BitcoinGUI *gui; + ClientModel *clientModel; + QMap<QString, WalletView*> mapWalletViews; + + bool bOutOfSync; public slots: /** Switch to overview (home) page */ void gotoOverviewPage(); /** Switch to history (transactions) page */ void gotoHistoryPage(); - /** Switch to address book page */ - void gotoAddressBookPage(); /** Switch to receive coins page */ void gotoReceiveCoinsPage(); /** Switch to send coins page */ @@ -63,11 +71,10 @@ public slots: /** Ask for passphrase to unlock wallet temporarily */ void unlockWallet(); - /** Set the encryption status as shown in the UI. - @param[in] status current encryption status - @see WalletModel::EncryptionStatus - */ - void setEncryptionStatus(); + /** Show used sending addresses */ + void usedSendingAddresses(); + /** Show used receiving addresses */ + void usedReceivingAddresses(); }; #endif // WALLETFRAME_H diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index bda39b675f..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(); } } @@ -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/walletmodel.h b/src/qt/walletmodel.h index 6abcdaf8cb..2e99eaddcb 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -21,11 +21,14 @@ QT_END_NAMESPACE class SendCoinsRecipient { public: - SendCoinsRecipient() : amount(0) { } + explicit SendCoinsRecipient() : amount(0) { } + explicit SendCoinsRecipient(const QString &addr, const QString &label, quint64 amount, const QString &message): + address(addr), label(label), amount(amount), message(message) {} QString address; QString label; qint64 amount; + QString message; // If from a payment request, paymentRequest.IsInitialized() will be true PaymentRequestPlus paymentRequest; diff --git a/src/qt/walletstack.cpp b/src/qt/walletstack.cpp deleted file mode 100644 index 4ef87aed52..0000000000 --- a/src/qt/walletstack.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Qt4 bitcoin GUI. - * - * W.J. van der Laan 2011-2012 - * The Bitcoin Developers 2011-2013 - */ -#include "walletstack.h" -#include "walletview.h" -#include "bitcoingui.h" - -#include <QMap> -#include <QMessageBox> - -WalletStack::WalletStack(QWidget *parent) : - QStackedWidget(parent), - gui(0), - clientModel(0), - bOutOfSync(true) -{ - setContentsMargins(0,0,0,0); -} - -WalletStack::~WalletStack() -{ -} - -bool WalletStack::addWallet(const QString& name, WalletModel *walletModel) -{ - if (!gui || !clientModel || !walletModel || mapWalletViews.count(name) > 0) - return false; - - WalletView *walletView = new WalletView(this, gui); - walletView->setBitcoinGUI(gui); - walletView->setClientModel(clientModel); - walletView->setWalletModel(walletModel); - walletView->showOutOfSyncWarning(bOutOfSync); - addWidget(walletView); - mapWalletViews[name] = walletView; - - // Ensure a walletView is able to show the main window - connect(walletView, SIGNAL(showNormalIfMinimized()), gui, SLOT(showNormalIfMinimized())); - - return true; -} - -bool WalletStack::removeWallet(const QString& name) -{ - if (mapWalletViews.count(name) == 0) - return false; - - WalletView *walletView = mapWalletViews.take(name); - removeWidget(walletView); - return true; -} - -void WalletStack::removeAllWallets() -{ - QMap<QString, WalletView*>::const_iterator i; - for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) - removeWidget(i.value()); - mapWalletViews.clear(); -} - -bool WalletStack::handlePaymentRequest(const SendCoinsRecipient &recipient) -{ - WalletView *walletView = (WalletView*)currentWidget(); - if (!walletView) - return false; - - return walletView->handlePaymentRequest(recipient); -} - -void WalletStack::showOutOfSyncWarning(bool fShow) -{ - bOutOfSync = fShow; - QMap<QString, WalletView*>::const_iterator i; - for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) - i.value()->showOutOfSyncWarning(fShow); -} - -void WalletStack::gotoOverviewPage() -{ - QMap<QString, WalletView*>::const_iterator i; - for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) - i.value()->gotoOverviewPage(); -} - -void WalletStack::gotoHistoryPage() -{ - QMap<QString, WalletView*>::const_iterator i; - for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) - i.value()->gotoHistoryPage(); -} - -void WalletStack::gotoAddressBookPage() -{ - QMap<QString, WalletView*>::const_iterator i; - for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) - i.value()->gotoAddressBookPage(); -} - -void WalletStack::gotoReceiveCoinsPage() -{ - QMap<QString, WalletView*>::const_iterator i; - for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) - i.value()->gotoReceiveCoinsPage(); -} - -void WalletStack::gotoSendCoinsPage(QString addr) -{ - QMap<QString, WalletView*>::const_iterator i; - for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) - i.value()->gotoSendCoinsPage(addr); -} - -void WalletStack::gotoSignMessageTab(QString addr) -{ - WalletView *walletView = (WalletView*)currentWidget(); - if (walletView) - walletView->gotoSignMessageTab(addr); -} - -void WalletStack::gotoVerifyMessageTab(QString addr) -{ - WalletView *walletView = (WalletView*)currentWidget(); - if (walletView) - walletView->gotoVerifyMessageTab(addr); -} - -void WalletStack::encryptWallet(bool status) -{ - WalletView *walletView = (WalletView*)currentWidget(); - if (walletView) - walletView->encryptWallet(status); -} - -void WalletStack::backupWallet() -{ - WalletView *walletView = (WalletView*)currentWidget(); - if (walletView) - walletView->backupWallet(); -} - -void WalletStack::changePassphrase() -{ - WalletView *walletView = (WalletView*)currentWidget(); - if (walletView) - walletView->changePassphrase(); -} - -void WalletStack::unlockWallet() -{ - WalletView *walletView = (WalletView*)currentWidget(); - if (walletView) - walletView->unlockWallet(); -} - -void WalletStack::setEncryptionStatus() -{ - WalletView *walletView = (WalletView*)currentWidget(); - if (walletView) - walletView->setEncryptionStatus(); -} - -bool WalletStack::setCurrentWallet(const QString& name) -{ - if (mapWalletViews.count(name) == 0) - return false; - - WalletView *walletView = mapWalletViews.value(name); - setCurrentWidget(walletView); - walletView->setEncryptionStatus(); - return true; -} diff --git a/src/qt/walletstack.h b/src/qt/walletstack.h deleted file mode 100644 index 74b9f09081..0000000000 --- a/src/qt/walletstack.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Qt4 bitcoin GUI. - * - * W.J. van der Laan 2011-2012 - * The Bitcoin Developers 2011-2013 - */ -#ifndef WALLETSTACK_H -#define WALLETSTACK_H - -#include <QStackedWidget> -#include <QMap> -#include <boost/shared_ptr.hpp> - -class BitcoinGUI; -class TransactionTableModel; -class ClientModel; -class WalletModel; -class WalletView; -class TransactionView; -class OverviewPage; -class AddressBookPage; -class SendCoinsDialog; -class SendCoinsRecipient; -class SignVerifyMessageDialog; -class Notificator; -class RPCConsole; - -class CWalletManager; - -QT_BEGIN_NAMESPACE -class QLabel; -class QModelIndex; -QT_END_NAMESPACE - -/* - WalletStack class. This class is a container for WalletView instances. It takes the place of centralWidget. - It was added to support multiple wallet functionality. It communicates with both the client and the - wallet models to give the user an up-to-date view of the current core state. It manages all the wallet views - it contains and updates them accordingly. - */ -class WalletStack : public QStackedWidget -{ - Q_OBJECT - -public: - explicit WalletStack(QWidget *parent = 0); - ~WalletStack(); - - void setBitcoinGUI(BitcoinGUI *gui) { this->gui = gui; } - - void setClientModel(ClientModel *clientModel) { this->clientModel = clientModel; } - - bool addWallet(const QString& name, WalletModel *walletModel); - bool removeWallet(const QString& name); - - void removeAllWallets(); - - bool handlePaymentRequest(const SendCoinsRecipient &recipient); - - void showOutOfSyncWarning(bool fShow); - -private: - BitcoinGUI *gui; - ClientModel *clientModel; - QMap<QString, WalletView*> mapWalletViews; - - bool bOutOfSync; - -public slots: - bool setCurrentWallet(const QString& name); - - /** Switch to overview (home) page */ - void gotoOverviewPage(); - /** Switch to history (transactions) page */ - void gotoHistoryPage(); - /** Switch to address book page */ - void gotoAddressBookPage(); - /** Switch to receive coins page */ - void gotoReceiveCoinsPage(); - /** Switch to send coins page */ - void gotoSendCoinsPage(QString addr = ""); - - /** Show Sign/Verify Message dialog and switch to sign message tab */ - void gotoSignMessageTab(QString addr = ""); - /** Show Sign/Verify Message dialog and switch to verify message tab */ - void gotoVerifyMessageTab(QString addr = ""); - - /** Encrypt the wallet */ - void encryptWallet(bool status); - /** Backup the wallet */ - void backupWallet(); - /** Change encrypted wallet passphrase */ - void changePassphrase(); - /** Ask for passphrase to unlock wallet temporarily */ - void unlockWallet(); - - /** Set the encryption status as shown in the UI. - @param[in] status current encryption status - @see WalletModel::EncryptionStatus - */ - void setEncryptionStatus(); -}; - -#endif // WALLETSTACK_H diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index efb74efaa0..d1e5e47bd5 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -9,6 +9,7 @@ #include "transactiontablemodel.h" #include "addressbookpage.h" #include "sendcoinsdialog.h" +#include "receivecoinsdialog.h" #include "signverifymessagedialog.h" #include "clientmodel.h" #include "walletmodel.h" @@ -17,21 +18,15 @@ #include "overviewpage.h" #include "askpassphrasedialog.h" #include "ui_interface.h" +#include "guiutil.h" #include <QHBoxLayout> #include <QVBoxLayout> #include <QAction> -#if QT_VERSION < 0x050000 -#include <QDesktopServices> -#else -#include <QStandardPaths> -#endif -#include <QFileDialog> #include <QPushButton> -WalletView::WalletView(QWidget *parent, BitcoinGUI *_gui): +WalletView::WalletView(QWidget *parent): QStackedWidget(parent), - gui(_gui), clientModel(0), walletModel(0) { @@ -53,37 +48,27 @@ WalletView::WalletView(QWidget *parent, BitcoinGUI *_gui): vbox->addLayout(hbox_buttons); transactionsPage->setLayout(vbox); - addressBookPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::SendingTab); - - receiveCoinsPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::ReceivingTab); - - sendCoinsPage = new SendCoinsDialog(gui); - - signVerifyMessageDialog = new SignVerifyMessageDialog(gui); + receiveCoinsPage = new ReceiveCoinsDialog(); + sendCoinsPage = new SendCoinsDialog(); addWidget(overviewPage); addWidget(transactionsPage); - addWidget(addressBookPage); addWidget(receiveCoinsPage); addWidget(sendCoinsPage); - // Clicking on a transaction on the overview page simply sends you to transaction history page - connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), this, SLOT(gotoHistoryPage())); + // Clicking on a transaction on the overview pre-selects the transaction on the transaction history page connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), transactionView, SLOT(focusTransaction(QModelIndex))); // Double-clicking on a transaction on the transaction history page shows details connect(transactionView, SIGNAL(doubleClicked(QModelIndex)), transactionView, SLOT(showDetails())); - // Clicking on "Send Coins" in the address book sends you to the send coins tab - connect(addressBookPage, SIGNAL(sendCoins(QString)), this, SLOT(gotoSendCoinsPage(QString))); - // Clicking on "Verify Message" in the address book opens the verify message tab in the Sign/Verify Message dialog - connect(addressBookPage, SIGNAL(verifyMessage(QString)), this, SLOT(gotoVerifyMessageTab(QString))); - // Clicking on "Sign Message" in the receive coins page opens the sign message tab in the Sign/Verify Message dialog - connect(receiveCoinsPage, SIGNAL(signMessage(QString)), this, SLOT(gotoSignMessageTab(QString))); // Clicking on "Export" allows to export the transaction list connect(exportButton, SIGNAL(clicked()), transactionView, SLOT(exportClicked())); - gotoOverviewPage(); + // Pass through messages from sendCoinsPage + connect(sendCoinsPage, SIGNAL(message(QString,QString,unsigned int)), this, SIGNAL(message(QString,QString,unsigned int))); + // Pass through messages from transactionView + connect(transactionView, SIGNAL(message(QString,QString,unsigned int)), this, SIGNAL(message(QString,QString,unsigned int))); } WalletView::~WalletView() @@ -92,49 +77,58 @@ WalletView::~WalletView() void WalletView::setBitcoinGUI(BitcoinGUI *gui) { - this->gui = gui; + if (gui) + { + // Clicking on a transaction on the overview page simply sends you to transaction history page + connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), gui, SLOT(gotoHistoryPage())); + + // Receive and report messages + connect(this, SIGNAL(message(QString,QString,unsigned int)), gui, SLOT(message(QString,QString,unsigned int))); + + // Pass through encryption status changed signals + connect(this, SIGNAL(encryptionStatusChanged(int)), gui, SLOT(setEncryptionStatus(int))); + + // Pass through transaction notifications + connect(this, SIGNAL(incomingTransaction(QString,int,qint64,QString,QString)), gui, SLOT(incomingTransaction(QString,int,qint64,QString,QString))); + } } void WalletView::setClientModel(ClientModel *clientModel) { this->clientModel = clientModel; - if (clientModel) - { - overviewPage->setClientModel(clientModel); - addressBookPage->setOptionsModel(clientModel->getOptionsModel()); - receiveCoinsPage->setOptionsModel(clientModel->getOptionsModel()); - } + + overviewPage->setClientModel(clientModel); } void WalletView::setWalletModel(WalletModel *walletModel) { this->walletModel = walletModel; + + // Put transaction list in tabs + transactionView->setModel(walletModel); + overviewPage->setWalletModel(walletModel); + receiveCoinsPage->setModel(walletModel); + sendCoinsPage->setModel(walletModel); + if (walletModel) { - // Receive and report messages from wallet thread - connect(walletModel, SIGNAL(message(QString,QString,unsigned int)), gui, SLOT(message(QString,QString,unsigned int))); - - // Put transaction list in tabs - transactionView->setModel(walletModel); - overviewPage->setWalletModel(walletModel); - addressBookPage->setModel(walletModel->getAddressTableModel()); - receiveCoinsPage->setModel(walletModel->getAddressTableModel()); - sendCoinsPage->setModel(walletModel); - signVerifyMessageDialog->setModel(walletModel); + // Receive and pass through messages from wallet model + connect(walletModel, SIGNAL(message(QString,QString,unsigned int)), this, SIGNAL(message(QString,QString,unsigned int))); - setEncryptionStatus(); - connect(walletModel, SIGNAL(encryptionStatusChanged(int)), gui, SLOT(setEncryptionStatus(int))); + // Handle changes in encryption status + connect(walletModel, SIGNAL(encryptionStatusChanged(int)), this, SIGNAL(encryptionStatusChanged(int))); + updateEncryptionStatus(); // Balloon pop-up for new transaction connect(walletModel->getTransactionTableModel(), SIGNAL(rowsInserted(QModelIndex,int,int)), - this, SLOT(incomingTransaction(QModelIndex,int,int))); + this, SLOT(processNewTransaction(QModelIndex,int,int))); // Ask for passphrase if needed connect(walletModel, SIGNAL(requireUnlock()), this, SLOT(unlockWallet())); } } -void WalletView::incomingTransaction(const QModelIndex& parent, int start, int /*end*/) +void WalletView::processNewTransaction(const QModelIndex& parent, int start, int /*end*/) { // Prevent balloon-spam when initial block download is in progress if (!walletModel || !clientModel || clientModel->inInitialBlockDownload()) @@ -147,36 +141,26 @@ void WalletView::incomingTransaction(const QModelIndex& parent, int start, int / QString type = ttm->index(start, TransactionTableModel::Type, parent).data().toString(); QString address = ttm->index(start, TransactionTableModel::ToAddress, parent).data().toString(); - gui->incomingTransaction(date, walletModel->getOptionsModel()->getDisplayUnit(), amount, type, address); + emit incomingTransaction(date, walletModel->getOptionsModel()->getDisplayUnit(), amount, type, address); } void WalletView::gotoOverviewPage() { - gui->getOverviewAction()->setChecked(true); setCurrentWidget(overviewPage); } void WalletView::gotoHistoryPage() { - gui->getHistoryAction()->setChecked(true); setCurrentWidget(transactionsPage); } -void WalletView::gotoAddressBookPage() -{ - gui->getAddressBookAction()->setChecked(true); - setCurrentWidget(addressBookPage); -} - void WalletView::gotoReceiveCoinsPage() { - gui->getReceiveCoinsAction()->setChecked(true); setCurrentWidget(receiveCoinsPage); } void WalletView::gotoSendCoinsPage(QString addr) { - gui->getSendCoinsAction()->setChecked(true); setCurrentWidget(sendCoinsPage); if (!addr.isEmpty()) @@ -185,7 +169,10 @@ void WalletView::gotoSendCoinsPage(QString addr) void WalletView::gotoSignMessageTab(QString addr) { - // call show() in showTab_SM() + // calls show() in showTab_SM() + SignVerifyMessageDialog *signVerifyMessageDialog = new SignVerifyMessageDialog(this); + signVerifyMessageDialog->setAttribute(Qt::WA_DeleteOnClose); + signVerifyMessageDialog->setModel(walletModel); signVerifyMessageDialog->showTab_SM(true); if (!addr.isEmpty()) @@ -194,7 +181,10 @@ void WalletView::gotoSignMessageTab(QString addr) void WalletView::gotoVerifyMessageTab(QString addr) { - // call show() in showTab_VM() + // calls show() in showTab_VM() + SignVerifyMessageDialog *signVerifyMessageDialog = new SignVerifyMessageDialog(this); + signVerifyMessageDialog->setAttribute(Qt::WA_DeleteOnClose); + signVerifyMessageDialog->setModel(walletModel); signVerifyMessageDialog->showTab_VM(true); if (!addr.isEmpty()) @@ -203,15 +193,7 @@ void WalletView::gotoVerifyMessageTab(QString addr) bool WalletView::handlePaymentRequest(const SendCoinsRecipient& recipient) { - // URI has to be valid - if (sendCoinsPage->handlePaymentRequest(recipient)) - { - gotoSendCoinsPage(); - emit showNormalIfMinimized(); - return true; - } - else - return false; + return sendCoinsPage->handlePaymentRequest(recipient); } void WalletView::showOutOfSyncWarning(bool fShow) @@ -219,9 +201,9 @@ void WalletView::showOutOfSyncWarning(bool fShow) overviewPage->showOutOfSyncWarning(fShow); } -void WalletView::setEncryptionStatus() +void WalletView::updateEncryptionStatus() { - gui->setEncryptionStatus(walletModel->getEncryptionStatus()); + emit encryptionStatusChanged(walletModel->getEncryptionStatus()); } void WalletView::encryptWallet(bool status) @@ -232,25 +214,25 @@ void WalletView::encryptWallet(bool status) dlg.setModel(walletModel); dlg.exec(); - setEncryptionStatus(); + updateEncryptionStatus(); } void WalletView::backupWallet() { -#if QT_VERSION < 0x050000 - QString saveDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); -#else - QString saveDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); -#endif - QString filename = QFileDialog::getSaveFileName(this, tr("Backup Wallet"), saveDir, tr("Wallet Data (*.dat)")); - if (!filename.isEmpty()) { - if (!walletModel->backupWallet(filename)) { - gui->message(tr("Backup Failed"), tr("There was an error trying to save the wallet data to the new location."), - CClientUIInterface::MSG_ERROR); + QString filename = GUIUtil::getSaveFileName(this, + tr("Backup Wallet"), QString(), + tr("Wallet Data (*.dat)")); + + if (filename.isEmpty()) + return; + + if (!walletModel->backupWallet(filename)) { + emit message(tr("Backup Failed"), tr("There was an error trying to save the wallet data to %1.").arg(filename), + CClientUIInterface::MSG_ERROR); } - else - gui->message(tr("Backup Successful"), tr("The wallet data was successfully saved to the new location."), - CClientUIInterface::MSG_INFORMATION); + else { + emit message(tr("Backup Successful"), tr("The wallet data was successfully saved to %1.").arg(filename), + CClientUIInterface::MSG_INFORMATION); } } @@ -273,3 +255,23 @@ void WalletView::unlockWallet() dlg.exec(); } } + +void WalletView::usedSendingAddresses() +{ + if(!walletModel) + return; + AddressBookPage *dlg = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::SendingTab, this); + dlg->setModel(walletModel->getAddressTableModel()); + dlg->setAttribute(Qt::WA_DeleteOnClose); + dlg->show(); +} + +void WalletView::usedReceivingAddresses() +{ + if(!walletModel) + return; + AddressBookPage *dlg = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::ReceivingTab, this); + dlg->setModel(walletModel->getAddressTableModel()); + dlg->setAttribute(Qt::WA_DeleteOnClose); + dlg->show(); +} diff --git a/src/qt/walletview.h b/src/qt/walletview.h index ce4e051098..61515047ca 100644 --- a/src/qt/walletview.h +++ b/src/qt/walletview.h @@ -14,8 +14,8 @@ class ClientModel; class WalletModel; class TransactionView; class OverviewPage; -class AddressBookPage; class SendCoinsDialog; +class ReceiveCoinsDialog; class SendCoinsRecipient; class SignVerifyMessageDialog; class RPCConsole; @@ -36,7 +36,7 @@ class WalletView : public QStackedWidget Q_OBJECT public: - explicit WalletView(QWidget *parent, BitcoinGUI *_gui); + explicit WalletView(QWidget *parent); ~WalletView(); void setBitcoinGUI(BitcoinGUI *gui); @@ -55,16 +55,13 @@ public: void showOutOfSyncWarning(bool fShow); private: - BitcoinGUI *gui; ClientModel *clientModel; WalletModel *walletModel; OverviewPage *overviewPage; QWidget *transactionsPage; - AddressBookPage *addressBookPage; - AddressBookPage *receiveCoinsPage; + ReceiveCoinsDialog *receiveCoinsPage; SendCoinsDialog *sendCoinsPage; - SignVerifyMessageDialog *signVerifyMessageDialog; TransactionView *transactionView; @@ -73,8 +70,6 @@ public slots: void gotoOverviewPage(); /** Switch to history (transactions) page */ void gotoHistoryPage(); - /** Switch to address book page */ - void gotoAddressBookPage(); /** Switch to receive coins page */ void gotoReceiveCoinsPage(); /** Switch to send coins page */ @@ -89,7 +84,7 @@ public slots: The new items are those between start and end inclusive, under the given parent item. */ - void incomingTransaction(const QModelIndex& parent, int start, int /*end*/); + void processNewTransaction(const QModelIndex& parent, int start, int /*end*/); /** Encrypt the wallet */ void encryptWallet(bool status); /** Backup the wallet */ @@ -99,11 +94,22 @@ public slots: /** Ask for passphrase to unlock wallet temporarily */ void unlockWallet(); - void setEncryptionStatus(); + /** Show used sending addresses */ + void usedSendingAddresses(); + /** Show used receiving addresses */ + void usedReceivingAddresses(); + /** Re-emit encryption status signal */ + void updateEncryptionStatus(); signals: /** Signal that we want to show the main window */ void showNormalIfMinimized(); + /** Fired when a message should be reported to the user */ + void message(const QString &title, const QString &message, unsigned int style); + /** Encryption status of wallet changed */ + void encryptionStatusChanged(int status); + /** Notify that a new transaction appeared */ + void incomingTransaction(const QString& date, int unit, qint64 amount, const QString& type, const QString& address); }; #endif // WALLETVIEW_H diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 398f33605a..6ea805a7f1 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. @@ -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..a5f7a542de 100644 --- a/src/rpcdump.cpp +++ b/src/rpcdump.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2012 Bitcoin Developers +// 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. @@ -6,6 +6,7 @@ #include <fstream> #include "init.h" // for pwalletMain +#include "wallet.h" #include "bitcoinrpc.h" #include "ui_interface.h" #include "base58.h" @@ -102,7 +103,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 +125,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 +176,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 +244,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 b013b4b200..d91f26e20a 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. @@ -37,10 +37,7 @@ void ShutdownRPCMining() // 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 = pindexBest; - - if (height >= 0 && height < nBestHeight) - pb = FindBlockByHeight(height); + CBlockIndex *pb = chainActive[height]; if (pb == NULL || !pb->nHeight) return 0; @@ -148,7 +145,7 @@ 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())); @@ -192,10 +189,10 @@ Value getwork(const Array& params, bool fHelp) static CBlockIndex* pindexPrev; static int64 nStart; static CBlockTemplate* pblocktemplate; - if (pindexPrev != pindexBest || - (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)) + if (pindexPrev != chainActive.Tip() || + (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60)) { - if (pindexPrev != pindexBest) + if (pindexPrev != chainActive.Tip()) { // Deallocate old blocks since they're obsolete now mapNewBlock.clear(); @@ -208,8 +205,8 @@ Value getwork(const Array& params, bool fHelp) pindexPrev = NULL; // Store the pindexBest used before CreateNewBlock, to avoid races - nTransactionsUpdatedLast = nTransactionsUpdated; - CBlockIndex* pindexPrevNew = pindexBest; + nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); + CBlockIndex* pindexPrevNew = chainActive.Tip(); nStart = GetTime(); // Create new block @@ -328,15 +325,15 @@ Value getblocktemplate(const Array& params, bool fHelp) static CBlockIndex* pindexPrev; static int64 nStart; static CBlockTemplate* pblocktemplate; - if (pindexPrev != pindexBest || - (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5)) + if (pindexPrev != chainActive.Tip() || + (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5)) { // Clear pindexPrev so future calls make a new block, despite any failures from here on pindexPrev = NULL; // Store the pindexBest used before CreateNewBlock, to avoid races - nTransactionsUpdatedLast = nTransactionsUpdated; - CBlockIndex* pindexPrevNew = pindexBest; + nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); + CBlockIndex* pindexPrevNew = chainActive.Tip(); nStart = GetTime(); // Create new block diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index bd7bc0ba10..5d87a2449f 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2012 Bitcoin Developers +// 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. @@ -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(); @@ -48,12 +66,17 @@ Value getpeerinfo(const Array& params, bool fHelp) Object obj; obj.push_back(Pair("addr", stats.addrName)); + if (!(stats.addrLocal.empty())) + obj.push_back(Pair("addrlocal", stats.addrLocal)); obj.push_back(Pair("services", strprintf("%08"PRI64x, stats.nServices))); obj.push_back(Pair("lastsend", (boost::int64_t)stats.nLastSend)); obj.push_back(Pair("lastrecv", (boost::int64_t)stats.nLastRecv)); 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)); @@ -97,13 +120,13 @@ Value addnode(const Array& params, bool fHelp) if (strCommand == "add") { if (it != vAddedNodes.end()) - throw JSONRPCError(-23, "Error: Node already added"); + throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Node already added"); vAddedNodes.push_back(strNode); } else if(strCommand == "remove") { if (it == vAddedNodes.end()) - throw JSONRPCError(-24, "Error: Node has not been added."); + throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node has not been added."); vAddedNodes.erase(it); } @@ -140,7 +163,7 @@ Value getaddednodeinfo(const Array& params, bool fHelp) break; } if (laddedNodes.size() == 0) - throw JSONRPCError(-24, "Error: Node has not been added."); + throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node has not been added."); } if (!fDns) @@ -202,3 +225,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 fcc5359dd6..4771094e1f 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. @@ -29,7 +29,7 @@ void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out, bool fIncludeH if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) { - out.push_back(Pair("type", GetTxnOutputType(TX_NONSTANDARD))); + out.push_back(Pair("type", GetTxnOutputType(type))); return; } @@ -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)); } @@ -550,7 +550,7 @@ Value sendrawtransaction(const Array& params, bool fHelp) if (!fHave) { // push to local node CValidationState state; - if (!mempool.accept(state, tx, false, NULL, !fOverrideFees)) + if (!AcceptToMemoryPool(mempool, state, tx, false, NULL, !fOverrideFees)) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected"); // TODO: report validation state } } @@ -560,7 +560,7 @@ Value sendrawtransaction(const Array& params, bool fHelp) // Not in block, but already in the memory pool; will drop // through to re-relay it. } else { - SyncWithWallets(hashTx, tx, NULL, true); + SyncWithWallets(hashTx, tx, NULL); } RelayTransaction(tx, hashTx); diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index cafb6db9b1..fe22dfe54c 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. @@ -76,7 +76,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("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()))); @@ -968,6 +968,13 @@ Value listreceivedbyaccount(const Array& params, bool fHelp) return ListReceived(params, true); } +static void MaybePushAddress(Object & entry, const CTxDestination &dest) +{ + CBitcoinAddress addr; + if (addr.Set(dest)) + entry.push_back(Pair("address", addr.ToString())); +} + void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret) { int64 nFee; @@ -986,7 +993,7 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe { Object entry; entry.push_back(Pair("account", strSentAccount)); - entry.push_back(Pair("address", CBitcoinAddress(s.first).ToString())); + MaybePushAddress(entry, s.first); entry.push_back(Pair("category", "send")); entry.push_back(Pair("amount", ValueFromAmount(-s.second))); entry.push_back(Pair("fee", ValueFromAmount(-nFee))); @@ -1008,7 +1015,7 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe { Object entry; entry.push_back(Pair("account", account)); - entry.push_back(Pair("address", CBitcoinAddress(r.first).ToString())); + MaybePushAddress(entry, r.first); if (wtx.IsCoinBase()) { if (wtx.GetDepthInMainChain() < 1) @@ -1169,7 +1176,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) @@ -1180,7 +1189,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; @@ -1192,23 +1201,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)); @@ -1277,7 +1271,7 @@ Value keypoolrefill(const Array& params, bool fHelp) unsigned int kpSize = max(GetArg("-keypool", 100), 0LL); if (params.size() > 0) { if (params[0].get_int() < 0) - throw JSONRPCError(-8, "Invalid parameter, expected valid size"); + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size"); kpSize = (unsigned int) params[0].get_int(); } @@ -1307,6 +1301,8 @@ Value walletpassphrase(const Array& params, bool fHelp) "Stores the wallet decryption key in memory for <timeout> seconds."); if (fHelp) return true; + if (!fServer) + throw JSONRPCError(RPC_SERVER_NOT_STARTED, "Error: RPC server was not started, use server=1 to change this."); if (!pwalletMain->IsCrypted()) throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called."); diff --git a/src/script.cpp b/src/script.cpp index 0fe2953548..ec9e9d61de 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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 "script.h" @@ -79,6 +79,7 @@ const char* GetTxnOutputType(txnouttype t) case TX_PUBKEYHASH: return "pubkeyhash"; case TX_SCRIPTHASH: return "scripthash"; case TX_MULTISIG: return "multisig"; + case TX_NULL_DATA: return "nulldata"; } return NULL; } @@ -220,6 +221,7 @@ const char* GetOpName(opcodetype opcode) // template matching params case OP_PUBKEYHASH : return "OP_PUBKEYHASH"; case OP_PUBKEY : return "OP_PUBKEY"; + case OP_SMALLDATA : return "OP_SMALLDATA"; case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE"; default: @@ -971,62 +973,118 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co +namespace { +/** Wrapper that serializes like CTransaction, but with the modifications + * required for the signature hash done in-place + */ +class CTransactionSignatureSerializer { +private: + const CTransaction &txTo; // reference to the spending transaction (the one being serialized) + const CScript &scriptCode; // output script being consumed + const unsigned int nIn; // input index of txTo being signed + const bool fAnyoneCanPay; // whether the hashtype has the SIGHASH_ANYONECANPAY flag set + const bool fHashSingle; // whether the hashtype is SIGHASH_SINGLE + const bool fHashNone; // whether the hashtype is SIGHASH_NONE - -uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType) -{ - if (nIn >= txTo.vin.size()) - { - LogPrintf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn); - return 1; +public: + CTransactionSignatureSerializer(const CTransaction &txToIn, const CScript &scriptCodeIn, unsigned int nInIn, int nHashTypeIn) : + txTo(txToIn), scriptCode(scriptCodeIn), nIn(nInIn), + fAnyoneCanPay(!!(nHashTypeIn & SIGHASH_ANYONECANPAY)), + fHashSingle((nHashTypeIn & 0x1f) == SIGHASH_SINGLE), + fHashNone((nHashTypeIn & 0x1f) == SIGHASH_NONE) {} + + /** Serialize the passed scriptCode, skipping OP_CODESEPARATORs */ + template<typename S> + void SerializeScriptCode(S &s, int nType, int nVersion) const { + CScript::const_iterator it = scriptCode.begin(); + CScript::const_iterator itBegin = it; + opcodetype opcode; + unsigned int nCodeSeparators = 0; + while (scriptCode.GetOp(it, opcode)) { + if (opcode == OP_CODESEPARATOR) + nCodeSeparators++; + } + ::WriteCompactSize(s, scriptCode.size() - nCodeSeparators); + it = itBegin; + while (scriptCode.GetOp(it, opcode)) { + if (opcode == OP_CODESEPARATOR) { + s.write((char*)&itBegin[0], it-itBegin-1); + itBegin = it; + } + } + s.write((char*)&itBegin[0], it-itBegin); } - CTransaction txTmp(txTo); - // In case concatenating two scripts ends up with two codeseparators, - // or an extra one at the end, this prevents all those possible incompatibilities. - scriptCode.FindAndDelete(CScript(OP_CODESEPARATOR)); + /** Serialize an input of txTo */ + template<typename S> + void SerializeInput(S &s, unsigned int nInput, int nType, int nVersion) const { + // In case of SIGHASH_ANYONECANPAY, only the input being signed is serialized + if (fAnyoneCanPay) + nInput = nIn; + // Serialize the prevout + ::Serialize(s, txTo.vin[nInput].prevout, nType, nVersion); + // Serialize the script + if (nInput != nIn) + // Blank out other inputs' signatures + ::Serialize(s, CScript(), nType, nVersion); + else + SerializeScriptCode(s, nType, nVersion); + // Serialize the nSequence + if (nInput != nIn && (fHashSingle || fHashNone)) + // let the others update at will + ::Serialize(s, (int)0, nType, nVersion); + else + ::Serialize(s, txTo.vin[nInput].nSequence, nType, nVersion); + } - // Blank out other inputs' signatures - for (unsigned int i = 0; i < txTmp.vin.size(); i++) - txTmp.vin[i].scriptSig = CScript(); - txTmp.vin[nIn].scriptSig = scriptCode; + /** Serialize an output of txTo */ + template<typename S> + void SerializeOutput(S &s, unsigned int nOutput, int nType, int nVersion) const { + if (fHashSingle && nOutput != nIn) + // Do not lock-in the txout payee at other indices as txin + ::Serialize(s, CTxOut(), nType, nVersion); + else + ::Serialize(s, txTo.vout[nOutput], nType, nVersion); + } - // Blank out some of the outputs - if ((nHashType & 0x1f) == SIGHASH_NONE) - { - // Wildcard payee - txTmp.vout.clear(); + /** Serialize txTo */ + template<typename S> + void Serialize(S &s, int nType, int nVersion) const { + // Serialize nVersion + ::Serialize(s, txTo.nVersion, nType, nVersion); + // Serialize vin + unsigned int nInputs = fAnyoneCanPay ? 1 : txTo.vin.size(); + ::WriteCompactSize(s, nInputs); + for (unsigned int nInput = 0; nInput < nInputs; nInput++) + SerializeInput(s, nInput, nType, nVersion); + // Serialize vout + unsigned int nOutputs = fHashNone ? 0 : (fHashSingle ? nIn+1 : txTo.vout.size()); + ::WriteCompactSize(s, nOutputs); + for (unsigned int nOutput = 0; nOutput < nOutputs; nOutput++) + SerializeOutput(s, nOutput, nType, nVersion); + // Serialie nLockTime + ::Serialize(s, txTo.nLockTime, nType, nVersion); + } +}; +} - // Let the others update at will - for (unsigned int i = 0; i < txTmp.vin.size(); i++) - if (i != nIn) - txTmp.vin[i].nSequence = 0; +uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType) +{ + if (nIn >= txTo.vin.size()) { + LogPrintf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn); + return 1; } - else if ((nHashType & 0x1f) == SIGHASH_SINGLE) - { - // Only lock-in the txout payee at same index as txin - unsigned int nOut = nIn; - if (nOut >= txTmp.vout.size()) - { - LogPrintf("ERROR: SignatureHash() : nOut=%d out of range\n", nOut); + + // Check for invalid use of SIGHASH_SINGLE + if ((nHashType & 0x1f) == SIGHASH_SINGLE) { + if (nIn >= txTo.vout.size()) { + LogPrintf("ERROR: SignatureHash() : nOut=%d out of range\n", nIn); return 1; } - txTmp.vout.resize(nOut+1); - for (unsigned int i = 0; i < nOut; i++) - txTmp.vout[i].SetNull(); - - // Let the others update at will - for (unsigned int i = 0; i < txTmp.vin.size(); i++) - if (i != nIn) - txTmp.vin[i].nSequence = 0; } - // Blank out other inputs completely, not recommended for open transactions - if (nHashType & SIGHASH_ANYONECANPAY) - { - txTmp.vin[0] = txTmp.vin[nIn]; - txTmp.vin.resize(1); - } + // Wrapper to serialize only the necessary parts of the transaction being signed + CTransactionSignatureSerializer txTmp(txTo, scriptCode, nIn, nHashType); // Serialize and hash CHashWriter ss(SER_GETHASH, 0); @@ -1137,7 +1195,7 @@ bool CheckSig(vector<unsigned char> vchSig, const vector<unsigned char> &vchPubK bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsigned char> >& vSolutionsRet) { // Templates - static map<txnouttype, CScript> mTemplates; + static multimap<txnouttype, CScript> mTemplates; if (mTemplates.empty()) { // Standard tx, sender provides pubkey, receiver adds signature @@ -1148,6 +1206,10 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi // Sender provides N pubkeys, receivers provides M signatures mTemplates.insert(make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG)); + + // Empty, provably prunable, data-carrying output + mTemplates.insert(make_pair(TX_NULL_DATA, CScript() << OP_RETURN << OP_SMALLDATA)); + mTemplates.insert(make_pair(TX_NULL_DATA, CScript() << OP_RETURN)); } // Shortcut for pay-to-script-hash, which are more constrained than the other types: @@ -1232,6 +1294,12 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi else break; } + else if (opcode2 == OP_SMALLDATA) + { + // small pushdata, <= 80 bytes + if (vch1.size() > 80) + break; + } else if (opcode1 != opcode2 || vch1 != vch2) { // Others must match exactly @@ -1294,6 +1362,7 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash switch (whichTypeRet) { case TX_NONSTANDARD: + case TX_NULL_DATA: return false; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); @@ -1324,6 +1393,7 @@ int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned c switch (t) { case TX_NONSTANDARD: + case TX_NULL_DATA: return -1; case TX_PUBKEY: return 1; @@ -1339,10 +1409,9 @@ int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned c return -1; } -bool IsStandard(const CScript& scriptPubKey) +bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType) { vector<valtype> vSolutions; - txnouttype whichType; if (!Solver(scriptPubKey, whichType, vSolutions)) return false; @@ -1401,6 +1470,7 @@ bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) switch (whichType) { case TX_NONSTANDARD: + case TX_NULL_DATA: return false; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); @@ -1462,6 +1532,10 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vecto vector<valtype> vSolutions; if (!Solver(scriptPubKey, typeRet, vSolutions)) return false; + if (typeRet == TX_NULL_DATA){ + // This is data, not addresses + return false; + } if (typeRet == TX_MULTISIG) { @@ -1677,6 +1751,7 @@ static CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, switch (txType) { case TX_NONSTANDARD: + case TX_NULL_DATA: // Don't know anything about this, assume bigger one is correct: if (sigs1.size() >= sigs2.size()) return PushAll(sigs1); diff --git a/src/script.h b/src/script.h index 842b8512eb..931307007f 100644 --- a/src/script.h +++ b/src/script.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. #ifndef H_BITCOIN_SCRIPT @@ -46,6 +46,7 @@ enum txnouttype TX_PUBKEYHASH, TX_SCRIPTHASH, TX_MULTISIG, + TX_NULL_DATA, }; class CNoDestination { @@ -202,6 +203,7 @@ enum opcodetype // template matching params + OP_SMALLDATA = 0xf9, OP_SMALLINTEGER = 0xfa, OP_PUBKEYS = 0xfb, OP_PUBKEYHASH = 0xfd, @@ -683,7 +685,7 @@ bool IsCanonicalSignature(const std::vector<unsigned char> &vchSig, unsigned int bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet); int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned char> >& vSolutions); -bool IsStandard(const CScript& scriptPubKey); +bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType); bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey); bool IsMine(const CKeyStore& keystore, const CTxDestination &dest); void ExtractAffectedKeys(const CKeyStore &keystore, const CScript& scriptPubKey, std::vector<CKeyID> &vKeys); diff --git a/src/serialize.h b/src/serialize.h index e3d9939bcc..115ea1d447 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. #ifndef BITCOIN_SERIALIZE_H @@ -216,18 +216,24 @@ uint64 ReadCompactSize(Stream& is) unsigned short xSize; READDATA(is, xSize); nSizeRet = xSize; + if (nSizeRet < 253) + throw std::ios_base::failure("non-canonical ReadCompactSize()"); } else if (chSize == 254) { unsigned int xSize; READDATA(is, xSize); nSizeRet = xSize; + if (nSizeRet < 0x10000u) + throw std::ios_base::failure("non-canonical ReadCompactSize()"); } else { uint64 xSize; READDATA(is, xSize); nSizeRet = xSize; + if (nSizeRet < 0x100000000LLu) + throw std::ios_base::failure("non-canonical ReadCompactSize()"); } if (nSizeRet > (uint64)MAX_SIZE) throw std::ios_base::failure("ReadCompactSize() : size too large"); @@ -895,19 +901,6 @@ public: iterator insert(iterator it, const char& x=char()) { return vch.insert(it, x); } void insert(iterator it, size_type n, const char& x) { vch.insert(it, n, x); } - void insert(iterator it, const_iterator first, const_iterator last) - { - assert(last - first >= 0); - if (it == vch.begin() + nReadPos && (unsigned int)(last - first) <= nReadPos) - { - // special case for inserting at the front when there's room - nReadPos -= (last - first); - memcpy(&vch[nReadPos], &first[0], last - first); - } - else - vch.insert(it, first, last); - } - void insert(iterator it, std::vector<char>::const_iterator first, std::vector<char>::const_iterator last) { assert(last - first >= 0); @@ -1098,8 +1091,8 @@ public: } void GetAndClear(CSerializeData &data) { - vch.swap(data); - CSerializeData().swap(vch); + data.insert(data.end(), begin(), end()); + clear(); } }; diff --git a/src/sync.cpp b/src/sync.cpp index 29a455f9b2..d6444141dc 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -78,7 +78,7 @@ static void push_lock(void* c, const CLockLocation& locklocation, bool fTry) if (lockstack.get() == NULL) lockstack.reset(new LockStack); - if (fDebug) LogPrintf("Locking: %s\n", locklocation.ToString().c_str()); + LogPrint("lock", "Locking: %s\n", locklocation.ToString().c_str()); dd_mutex.lock(); (*lockstack).push_back(std::make_pair(c, locklocation)); @@ -108,7 +108,7 @@ static void pop_lock() if (fDebug) { const CLockLocation& locklocation = (*lockstack).rbegin()->second; - LogPrintf("Unlocked: %s\n", locklocation.ToString().c_str()); + LogPrint("lock", "Unlocked: %s\n", locklocation.ToString().c_str()); } dd_mutex.lock(); (*lockstack).pop_back(); diff --git a/src/sync.h b/src/sync.h index 64de7cc57c..10e0470a20 100644 --- a/src/sync.h +++ b/src/sync.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. #ifndef BITCOIN_SYNC_H diff --git a/src/test/Makefile.am b/src/test/Makefile.am index a859eb1de8..c3495095d9 100644 --- a/src/test/Makefile.am +++ b/src/test/Makefile.am @@ -34,7 +34,7 @@ test_bitcoin_SOURCES = accounting_tests.cpp alert_tests.cpp \ netbase_tests.cpp pmt_tests.cpp rpc_tests.cpp script_P2SH_tests.cpp \ script_tests.cpp serialize_tests.cpp sigopcount_tests.cpp test_bitcoin.cpp \ transaction_tests.cpp uint160_tests.cpp uint256_tests.cpp util_tests.cpp \ - wallet_tests.cpp $(JSON_TEST_FILES) $(RAW_TEST_FILES) + wallet_tests.cpp sighash_tests.cpp $(JSON_TEST_FILES) $(RAW_TEST_FILES) nodist_test_bitcoin_SOURCES = $(BUILT_SOURCES) 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/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index 9ef932b5b4..05309f1a4d 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -19,7 +19,7 @@ using namespace boost::assign; typedef vector<unsigned char> valtype; -extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); +extern uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); BOOST_AUTO_TEST_SUITE(multisig_tests) @@ -133,21 +133,23 @@ BOOST_AUTO_TEST_CASE(multisig_IsStandard) for (int i = 0; i < 4; i++) key[i].MakeNewKey(true); + txnouttype whichType; + CScript a_and_b; a_and_b << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; - BOOST_CHECK(::IsStandard(a_and_b)); + BOOST_CHECK(::IsStandard(a_and_b, whichType)); CScript a_or_b; a_or_b << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; - BOOST_CHECK(::IsStandard(a_or_b)); + BOOST_CHECK(::IsStandard(a_or_b, whichType)); CScript escrow; escrow << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG; - BOOST_CHECK(::IsStandard(escrow)); + BOOST_CHECK(::IsStandard(escrow, whichType)); CScript one_of_four; one_of_four << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << key[3].GetPubKey() << OP_4 << OP_CHECKMULTISIG; - BOOST_CHECK(!::IsStandard(one_of_four)); + BOOST_CHECK(!::IsStandard(one_of_four, whichType)); CScript malformed[6]; malformed[0] << OP_3 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; @@ -158,7 +160,7 @@ BOOST_AUTO_TEST_CASE(multisig_IsStandard) malformed[5] << OP_1 << key[0].GetPubKey() << key[1].GetPubKey(); for (int i = 0; i < 6; i++) - BOOST_CHECK(!::IsStandard(malformed[i])); + BOOST_CHECK(!::IsStandard(malformed[i], whichType)); } BOOST_AUTO_TEST_CASE(multisig_Solver1) diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 35eabed0e2..25849054eb 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -168,4 +168,35 @@ BOOST_AUTO_TEST_CASE(rpc_rawsign) BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == true); } +BOOST_AUTO_TEST_CASE(rpc_format_monetary_values) +{ + BOOST_CHECK(write_string(ValueFromAmount(0LL), false) == "0.00000000"); + BOOST_CHECK(write_string(ValueFromAmount(1LL), false) == "0.00000001"); + BOOST_CHECK(write_string(ValueFromAmount(17622195LL), false) == "0.17622195"); + BOOST_CHECK(write_string(ValueFromAmount(50000000LL), false) == "0.50000000"); + BOOST_CHECK(write_string(ValueFromAmount(89898989LL), false) == "0.89898989"); + BOOST_CHECK(write_string(ValueFromAmount(100000000LL), false) == "1.00000000"); + BOOST_CHECK(write_string(ValueFromAmount(2099999999999990LL), false) == "20999999.99999990"); + BOOST_CHECK(write_string(ValueFromAmount(2099999999999999LL), false) == "20999999.99999999"); +} + +static Value ValueFromString(const std::string &str) +{ + Value value; + BOOST_CHECK(read_string(str, value)); + return value; +} + +BOOST_AUTO_TEST_CASE(rpc_parse_monetary_values) +{ + BOOST_CHECK(AmountFromValue(ValueFromString("0.00000001")) == 1LL); + BOOST_CHECK(AmountFromValue(ValueFromString("0.17622195")) == 17622195LL); + BOOST_CHECK(AmountFromValue(ValueFromString("0.5")) == 50000000LL); + BOOST_CHECK(AmountFromValue(ValueFromString("0.50000000")) == 50000000LL); + BOOST_CHECK(AmountFromValue(ValueFromString("0.89898989")) == 89898989LL); + BOOST_CHECK(AmountFromValue(ValueFromString("1.00000000")) == 100000000LL); + BOOST_CHECK(AmountFromValue(ValueFromString("20999999.9999999")) == 2099999999999990LL); + BOOST_CHECK(AmountFromValue(ValueFromString("20999999.99999999")) == 2099999999999999LL); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index dfa5529b87..32be914414 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -21,7 +21,7 @@ using namespace std; using namespace json_spirit; using namespace boost::algorithm; -extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); +extern uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); static const unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC; diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp index 19ffdcab66..7e193c88ce 100644 --- a/src/test/serialize_tests.cpp +++ b/src/test/serialize_tests.cpp @@ -39,7 +39,115 @@ BOOST_AUTO_TEST_CASE(varints) ss >> VARINT(j); BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i); } +} + +BOOST_AUTO_TEST_CASE(compactsize) +{ + CDataStream ss(SER_DISK, 0); + vector<char>::size_type i, j; + + for (i = 1; i <= MAX_SIZE; i *= 2) + { + WriteCompactSize(ss, i-1); + WriteCompactSize(ss, i); + } + for (i = 1; i <= MAX_SIZE; i *= 2) + { + j = ReadCompactSize(ss); + BOOST_CHECK_MESSAGE((i-1) == j, "decoded:" << j << " expected:" << (i-1)); + j = ReadCompactSize(ss); + BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i); + } +} + +static bool isCanonicalException(const std::ios_base::failure& ex) +{ + return std::string("non-canonical ReadCompactSize()") == ex.what(); +} + +BOOST_AUTO_TEST_CASE(noncanonical) +{ + // Write some non-canonical CompactSize encodings, and + // make sure an exception is thrown when read back. + CDataStream ss(SER_DISK, 0); + vector<char>::size_type n; + + // zero encoded with three bytes: + ss.write("\xfd\x00\x00", 3); + BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); + + // 0xfc encoded with three bytes: + ss.write("\xfd\xfc\x00", 3); + BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); + + // 0xfd encoded with three bytes is OK: + ss.write("\xfd\xfd\x00", 3); + n = ReadCompactSize(ss); + BOOST_CHECK(n == 0xfd); + + // zero encoded with five bytes: + ss.write("\xfe\x00\x00\x00\x00", 5); + BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); + + // 0xffff encoded with five bytes: + ss.write("\xfe\xff\xff\x00\x00", 5); + BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); + + // zero encoded with nine bytes: + ss.write("\xff\x00\x00\x00\x00\x00\x00\x00\x00", 9); + BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); + + // 0x01ffffff encoded with nine bytes: + ss.write("\xff\xff\xff\xff\x01\x00\x00\x00\x00", 9); + BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); +} + +BOOST_AUTO_TEST_CASE(insert_delete) +{ + // Test inserting/deleting bytes. + CDataStream ss(SER_DISK, 0); + BOOST_CHECK_EQUAL(ss.size(), 0); + + ss.write("\x00\x01\x02\xff", 4); + BOOST_CHECK_EQUAL(ss.size(), 4); + + char c = (char)11; + + // Inserting at beginning/end/middle: + ss.insert(ss.begin(), c); + BOOST_CHECK_EQUAL(ss.size(), 5); + BOOST_CHECK_EQUAL(ss[0], c); + BOOST_CHECK_EQUAL(ss[1], 0); + + ss.insert(ss.end(), c); + BOOST_CHECK_EQUAL(ss.size(), 6); + BOOST_CHECK_EQUAL(ss[4], (char)0xff); + BOOST_CHECK_EQUAL(ss[5], c); + + ss.insert(ss.begin()+2, c); + BOOST_CHECK_EQUAL(ss.size(), 7); + BOOST_CHECK_EQUAL(ss[2], c); + + // Delete at beginning/end/middle + ss.erase(ss.begin()); + BOOST_CHECK_EQUAL(ss.size(), 6); + BOOST_CHECK_EQUAL(ss[0], 0); + + ss.erase(ss.begin()+ss.size()-1); + BOOST_CHECK_EQUAL(ss.size(), 5); + BOOST_CHECK_EQUAL(ss[4], (char)0xff); + + ss.erase(ss.begin()+1); + BOOST_CHECK_EQUAL(ss.size(), 4); + BOOST_CHECK_EQUAL(ss[0], 0); + BOOST_CHECK_EQUAL(ss[1], 1); + BOOST_CHECK_EQUAL(ss[2], 2); + BOOST_CHECK_EQUAL(ss[3], (char)0xff); + // Make sure GetAndClear does the right thing: + CSerializeData d; + ss.GetAndClear(d); + BOOST_CHECK_EQUAL(ss.size(), 0); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp new file mode 100644 index 0000000000..f098d46186 --- /dev/null +++ b/src/test/sighash_tests.cpp @@ -0,0 +1,120 @@ +#include <boost/test/unit_test.hpp> + +#include "main.h" +#include "util.h" + +extern uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); + +// Old script.cpp SignatureHash function +uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType) +{ + if (nIn >= txTo.vin.size()) + { + printf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn); + return 1; + } + CTransaction txTmp(txTo); + + // In case concatenating two scripts ends up with two codeseparators, + // or an extra one at the end, this prevents all those possible incompatibilities. + scriptCode.FindAndDelete(CScript(OP_CODESEPARATOR)); + + // Blank out other inputs' signatures + for (unsigned int i = 0; i < txTmp.vin.size(); i++) + txTmp.vin[i].scriptSig = CScript(); + txTmp.vin[nIn].scriptSig = scriptCode; + + // Blank out some of the outputs + if ((nHashType & 0x1f) == SIGHASH_NONE) + { + // Wildcard payee + txTmp.vout.clear(); + + // Let the others update at will + for (unsigned int i = 0; i < txTmp.vin.size(); i++) + if (i != nIn) + txTmp.vin[i].nSequence = 0; + } + else if ((nHashType & 0x1f) == SIGHASH_SINGLE) + { + // Only lock-in the txout payee at same index as txin + unsigned int nOut = nIn; + if (nOut >= txTmp.vout.size()) + { + printf("ERROR: SignatureHash() : nOut=%d out of range\n", nOut); + return 1; + } + txTmp.vout.resize(nOut+1); + for (unsigned int i = 0; i < nOut; i++) + txTmp.vout[i].SetNull(); + + // Let the others update at will + for (unsigned int i = 0; i < txTmp.vin.size(); i++) + if (i != nIn) + txTmp.vin[i].nSequence = 0; + } + + // Blank out other inputs completely, not recommended for open transactions + if (nHashType & SIGHASH_ANYONECANPAY) + { + txTmp.vin[0] = txTmp.vin[nIn]; + txTmp.vin.resize(1); + } + + // Serialize and hash + CHashWriter ss(SER_GETHASH, 0); + ss << txTmp << nHashType; + return ss.GetHash(); +} + +void static RandomScript(CScript &script) { + static const opcodetype oplist[] = {OP_FALSE, OP_1, OP_2, OP_3, OP_CHECKSIG, OP_IF, OP_VERIF, OP_RETURN, OP_CODESEPARATOR}; + script = CScript(); + int ops = (insecure_rand() % 10); + for (int i=0; i<ops; i++) + script << oplist[insecure_rand() % (sizeof(oplist)/sizeof(oplist[0]))]; +} + +void static RandomTransaction(CTransaction &tx, bool fSingle) { + tx.nVersion = insecure_rand(); + tx.vin.clear(); + tx.vout.clear(); + tx.nLockTime = (insecure_rand() % 2) ? insecure_rand() : 0; + int ins = (insecure_rand() % 4) + 1; + int outs = fSingle ? ins : (insecure_rand() % 4) + 1; + for (int in = 0; in < ins; in++) { + tx.vin.push_back(CTxIn()); + CTxIn &txin = tx.vin.back(); + txin.prevout.hash = GetRandHash(); + txin.prevout.n = insecure_rand() % 4; + RandomScript(txin.scriptSig); + txin.nSequence = (insecure_rand() % 2) ? insecure_rand() : (unsigned int)-1; + } + for (int out = 0; out < outs; out++) { + tx.vout.push_back(CTxOut()); + CTxOut &txout = tx.vout.back(); + txout.nValue = insecure_rand() % 100000000; + RandomScript(txout.scriptPubKey); + } +} + +BOOST_AUTO_TEST_SUITE(sighash_tests) + +BOOST_AUTO_TEST_CASE(sighash_test) +{ + seed_insecure_rand(false); + + for (int i=0; i<50000; i++) { + int nHashType = insecure_rand(); + CTransaction txTo; + RandomTransaction(txTo, (nHashType & 0x1f) == SIGHASH_SINGLE); + CScript scriptCode; + RandomScript(scriptCode); + int nIn = insecure_rand() % txTo.vin.size(); + BOOST_CHECK(SignatureHash(scriptCode, txTo, nIn, nHashType) == + SignatureHashOld(scriptCode, txTo, nIn, nHashType)); + } +} + +BOOST_AUTO_TEST_SUITE_END() + diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 416b93ab33..bd999caa14 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -273,6 +273,33 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) t.vout[0].scriptPubKey = CScript() << OP_1; BOOST_CHECK(!IsStandardTx(t, reason)); + + // 80-byte TX_NULL_DATA (standard) + t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"); + BOOST_CHECK(IsStandardTx(t, reason)); + + // 81-byte TX_NULL_DATA (non-standard) + t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3800"); + BOOST_CHECK(!IsStandardTx(t, reason)); + + // TX_NULL_DATA w/o PUSHDATA + t.vout.resize(1); + t.vout[0].scriptPubKey = CScript() << OP_RETURN; + BOOST_CHECK(IsStandardTx(t, reason)); + + // Only one TX_NULL_DATA permitted in all cases + t.vout.resize(2); + t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"); + t.vout[1].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"); + BOOST_CHECK(!IsStandardTx(t, reason)); + + t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"); + t.vout[1].scriptPubKey = CScript() << OP_RETURN; + BOOST_CHECK(!IsStandardTx(t, reason)); + + t.vout[0].scriptPubKey = CScript() << OP_RETURN; + t.vout[1].scriptPubKey = CScript() << OP_RETURN; + BOOST_CHECK(!IsStandardTx(t, reason)); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/txdb.cpp b/src/txdb.cpp index 0d2fdc2887..27d6caf4d9 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. @@ -74,13 +74,9 @@ bool CBlockTreeDB::WriteBlockIndex(const CDiskBlockIndex& blockindex) return Write(make_pair('b', blockindex.GetBlockHash()), blockindex); } -bool CBlockTreeDB::ReadBestInvalidWork(CBigNum& bnBestInvalidWork) -{ - return Read('I', bnBestInvalidWork); -} - bool CBlockTreeDB::WriteBestInvalidWork(const CBigNum& bnBestInvalidWork) { + // Obsolete; only written for backward compatibility. return Write('I', bnBestInvalidWork); } @@ -223,10 +219,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/txdb.h b/src/txdb.h index f59fc5da86..b555be3de7 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. #ifndef BITCOIN_TXDB_LEVELDB_H @@ -35,7 +35,6 @@ private: void operator=(const CBlockTreeDB&); public: bool WriteBlockIndex(const CDiskBlockIndex& blockindex); - bool ReadBestInvalidWork(CBigNum& bnBestInvalidWork); bool WriteBestInvalidWork(const CBigNum& bnBestInvalidWork); bool ReadBlockFileInfo(int nFile, CBlockFileInfo &fileinfo); bool WriteBlockFileInfo(int nFile, const CBlockFileInfo &fileinfo); diff --git a/src/txmempool.cpp b/src/txmempool.cpp new file mode 100644 index 0000000000..975955a458 --- /dev/null +++ b/src/txmempool.cpp @@ -0,0 +1,162 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// 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 "core.h" +#include "txmempool.h" + +using namespace std; + +CTxMemPool::CTxMemPool() +{ + // Sanity checks off by default for performance, because otherwise + // accepting transactions becomes O(N^2) where N is the number + // of transactions in the pool + fSanityCheck = false; +} + +void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins) +{ + LOCK(cs); + + std::map<COutPoint, CInPoint>::iterator it = mapNextTx.lower_bound(COutPoint(hashTx, 0)); + + // iterate over all COutPoints in mapNextTx whose hash equals the provided hashTx + while (it != mapNextTx.end() && it->first.hash == hashTx) { + coins.Spend(it->first.n); // and remove those outputs from coins + it++; + } +} + +unsigned int CTxMemPool::GetTransactionsUpdated() const +{ + LOCK(cs); + return nTransactionsUpdated; +} + +void CTxMemPool::AddTransactionsUpdated(unsigned int n) +{ + LOCK(cs); + nTransactionsUpdated += n; +} + + +bool CTxMemPool::addUnchecked(const uint256& hash, const CTransaction &tx) +{ + // Add to memory pool without checking anything. + // Used by main.cpp AcceptToMemoryPool(), which DOES do + // all the appropriate checks. + LOCK(cs); + { + mapTx[hash] = tx; + for (unsigned int i = 0; i < tx.vin.size(); i++) + mapNextTx[tx.vin[i].prevout] = CInPoint(&mapTx[hash], i); + nTransactionsUpdated++; + } + return true; +} + + +bool CTxMemPool::remove(const CTransaction &tx, bool fRecursive) +{ + // Remove transaction from memory pool + { + LOCK(cs); + uint256 hash = tx.GetHash(); + if (fRecursive) { + for (unsigned int i = 0; i < tx.vout.size(); i++) { + std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(COutPoint(hash, i)); + if (it != mapNextTx.end()) + remove(*it->second.ptx, true); + } + } + if (mapTx.count(hash)) + { + BOOST_FOREACH(const CTxIn& txin, tx.vin) + mapNextTx.erase(txin.prevout); + mapTx.erase(hash); + nTransactionsUpdated++; + } + } + return true; +} + +bool CTxMemPool::removeConflicts(const CTransaction &tx) +{ + // Remove transactions which depend on inputs of tx, recursively + LOCK(cs); + BOOST_FOREACH(const CTxIn &txin, tx.vin) { + std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(txin.prevout); + if (it != mapNextTx.end()) { + const CTransaction &txConflict = *it->second.ptx; + if (txConflict != tx) + remove(txConflict, true); + } + } + return true; +} + +void CTxMemPool::clear() +{ + LOCK(cs); + mapTx.clear(); + mapNextTx.clear(); + ++nTransactionsUpdated; +} + +void CTxMemPool::check(CTxMemPool::CoinLookupFunc fnLookup) const +{ + if (!fSanityCheck) + return; + + LogPrint("mempool", "Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size()); + + LOCK(cs); + for (std::map<uint256, CTransaction>::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { + unsigned int i = 0; + BOOST_FOREACH(const CTxIn &txin, it->second.vin) { + // Check that every mempool transaction's inputs refer to available coins, or other mempool tx's. + std::map<uint256, CTransaction>::const_iterator it2 = mapTx.find(txin.prevout.hash); + if (it2 != mapTx.end()) { + assert(it2->second.vout.size() > txin.prevout.n && !it2->second.vout[txin.prevout.n].IsNull()); + } else { + CCoins &coins = (*fnLookup)(txin.prevout.hash); + assert(coins.IsAvailable(txin.prevout.n)); + } + // Check whether its inputs are marked in mapNextTx. + std::map<COutPoint, CInPoint>::const_iterator it3 = mapNextTx.find(txin.prevout); + assert(it3 != mapNextTx.end()); + assert(it3->second.ptx == &it->second); + assert(it3->second.n == i); + i++; + } + } + for (std::map<COutPoint, CInPoint>::const_iterator it = mapNextTx.begin(); it != mapNextTx.end(); it++) { + uint256 hash = it->second.ptx->GetHash(); + std::map<uint256, CTransaction>::const_iterator it2 = mapTx.find(hash); + assert(it2 != mapTx.end()); + assert(&it2->second == it->second.ptx); + assert(it2->second.vin.size() > it->second.n); + assert(it->first == it->second.ptx->vin[it->second.n].prevout); + } +} + +void CTxMemPool::queryHashes(std::vector<uint256>& vtxid) +{ + vtxid.clear(); + + LOCK(cs); + vtxid.reserve(mapTx.size()); + for (map<uint256, CTransaction>::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi) + vtxid.push_back((*mi).first); +} + +bool CTxMemPool::lookup(uint256 hash, CTransaction& result) const +{ + LOCK(cs); + std::map<uint256, CTransaction>::const_iterator i = mapTx.find(hash); + if (i == mapTx.end()) return false; + result = i->second; + return true; +} diff --git a/src/txmempool.h b/src/txmempool.h new file mode 100644 index 0000000000..1b555d99f4 --- /dev/null +++ b/src/txmempool.h @@ -0,0 +1,67 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// 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. +#ifndef BITCOIN_TXMEMPOOL_H +#define BITCOIN_TXMEMPOOL_H + +#include "core.h" + +/* + * CTxMemPool stores valid-according-to-the-current-best-chain + * transactions that may be included in the next block. + * + * Transactions are added when they are seen on the network + * (or created by the local node), but not all transactions seen + * are added to the pool: if a new transaction double-spends + * an input of a transaction in the pool, it is dropped, + * as are non-standard transactions. + */ +class CTxMemPool +{ +private: + bool fSanityCheck; // Normally false, true if -checkmempool or -regtest + unsigned int nTransactionsUpdated; + +public: + mutable CCriticalSection cs; + std::map<uint256, CTransaction> mapTx; + std::map<COutPoint, CInPoint> mapNextTx; + + CTxMemPool(); + + /* + * If sanity-checking is turned on, check makes sure the pool is + * consistent (does not contain two transactions that spend the same inputs, + * all inputs are in the mapNextTx array). If sanity-checking is turned off, + * check does nothing. + */ + typedef CCoins& (*CoinLookupFunc)(const uint256&); + void check(CoinLookupFunc fnLookup) const; + void setSanityCheck(bool _fSanityCheck) { fSanityCheck = _fSanityCheck; } + + bool addUnchecked(const uint256& hash, const CTransaction &tx); + bool remove(const CTransaction &tx, bool fRecursive = false); + bool removeConflicts(const CTransaction &tx); + void clear(); + void queryHashes(std::vector<uint256>& vtxid); + void pruneSpent(const uint256& hash, CCoins &coins); + unsigned int GetTransactionsUpdated() const; + void AddTransactionsUpdated(unsigned int n); + + unsigned long size() + { + LOCK(cs); + return mapTx.size(); + } + + bool exists(uint256 hash) + { + LOCK(cs); + return (mapTx.count(hash) != 0); + } + + bool lookup(uint256 hash, CTransaction& result) const; +}; + +#endif /* BITCOIN_TXMEMPOOL_H */ diff --git a/src/uint256.h b/src/uint256.h index 562426a0d8..b5a4950eaa 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. #ifndef BITCOIN_UINT256_H diff --git a/src/util.cpp b/src/util.cpp index e0b738dcc6..539e759ddd 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. @@ -73,16 +73,13 @@ using namespace std; map<string, string> mapArgs; map<string, vector<string> > mapMultiArgs; bool fDebug = false; -bool fDebugNet = false; bool fPrintToConsole = false; bool fPrintToDebugger = false; bool fDaemon = false; bool fServer = false; -bool fCommandLine = false; string strMiscWarning; bool fNoListen = false; bool fLogTimestamps = false; -CMedianFilter<int64> vTimeOffsets(200,0); volatile bool fReopenDebugLog = false; // Init OpenSSL library multithreading support @@ -96,8 +93,6 @@ void locking_callback(int mode, int i, const char* file, int line) } } -LockedPageManager LockedPageManager::instance; - // Init class CInit { @@ -229,10 +224,20 @@ int LogPrint(const char* category, const char* pszFormat, ...) { if (category != NULL) { - if (!fDebug) return 0; - const vector<string>& categories = mapMultiArgs["-debug"]; - if (find(categories.begin(), categories.end(), string(category)) == categories.end()) + if (!fDebug) return 0; + + const vector<string>& categories = mapMultiArgs["-debug"]; + bool allCategories = count(categories.begin(), categories.end(), string("")); + + // Only look for categories, if not -debug/-debug=1 was passed, + // as that implies every category should be logged. + if (!allCategories) + { + // Category was not found (not supplied via -debug=<category>) + if (find(categories.begin(), categories.end(), string(category)) == categories.end()) + return 0; + } } int ret = 0; // Returns total number of characters written @@ -1299,10 +1304,12 @@ void SetMockTime(int64 nMockTimeIn) nMockTime = nMockTimeIn; } +static CCriticalSection cs_nTimeOffset; static int64 nTimeOffset = 0; int64 GetTimeOffset() { + LOCK(cs_nTimeOffset); return nTimeOffset; } @@ -1315,12 +1322,14 @@ void AddTimeData(const CNetAddr& ip, int64 nTime) { int64 nOffsetSample = nTime - GetTime(); + LOCK(cs_nTimeOffset); // Ignore duplicates static set<CNetAddr> setKnown; if (!setKnown.insert(ip).second) return; // Add data + static CMedianFilter<int64> vTimeOffsets(200,0); vTimeOffsets.input(nOffsetSample); LogPrintf("Added time data, samples %d, offset %+"PRI64d" (%+"PRI64d" minutes)\n", vTimeOffsets.size(), nOffsetSample, nOffsetSample/60); if (vTimeOffsets.size() >= 5 && vTimeOffsets.size() % 2 == 1) diff --git a/src/util.h b/src/util.h index a216aacdc9..2573694046 100644 --- a/src/util.h +++ b/src/util.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. #ifndef BITCOIN_UTIL_H @@ -140,12 +140,10 @@ inline void MilliSleep(int64 n) extern std::map<std::string, std::string> mapArgs; extern std::map<std::string, std::vector<std::string> > mapMultiArgs; extern bool fDebug; -extern bool fDebugNet; 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..ea1e01e6e9 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. @@ -483,25 +483,6 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn) if (!wtx.WriteToDisk()) return false; - if (!fHaveGUI) { - // If default receiving address gets used, replace it with a new one - if (vchDefaultKey.IsValid()) { - CScript scriptDefaultKey; - scriptDefaultKey.SetDestination(vchDefaultKey.GetID()); - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - { - if (txout.scriptPubKey == scriptDefaultKey) - { - CPubKey newDefaultKey; - if (GetKeyFromPool(newDefaultKey)) - { - SetDefaultKey(newDefaultKey); - SetAddressBook(vchDefaultKey.GetID(), "", "receive"); - } - } - } - } - } // since AddToWallet is called directly for self-originating transactions, check for consumption of own coins WalletUpdateSpent(wtx); @@ -524,7 +505,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn) // Add a transaction to the wallet, or update it. // pblock is optional, but should be provided if the transaction is known to be in a block. // If fUpdate is true, existing transactions will be updated. -bool CWallet::AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fFindBlock) +bool CWallet::AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate) { { LOCK(cs_wallet); @@ -544,16 +525,20 @@ bool CWallet::AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& return false; } -bool CWallet::EraseFromWallet(uint256 hash) +void CWallet::SyncTransaction(const uint256 &hash, const CTransaction& tx, const CBlock* pblock) { + AddToWalletIfInvolvingMe(hash, tx, pblock, true); +} + +void CWallet::EraseFromWallet(const uint256 &hash) { if (!fFileBacked) - return false; + return; { LOCK(cs_wallet); if (mapWallet.erase(hash)) CWalletDB(strWalletFile).EraseTx(hash); } - return true; + return; } @@ -673,22 +658,35 @@ void CWalletTx::GetAmounts(list<pair<CTxDestination, int64> >& listReceived, // Sent/received. BOOST_FOREACH(const CTxOut& txout, vout) { + bool fIsMine; + // Only need to handle txouts if AT LEAST one of these is true: + // 1) they debit from us (sent) + // 2) the output is to us (received) + if (nDebit > 0) + { + // Don't report 'change' txouts + if (pwallet->IsChange(txout)) + continue; + fIsMine = pwallet->IsMine(txout); + } + else if (!(fIsMine = pwallet->IsMine(txout))) + continue; + + // In either case, we need to get the destination address CTxDestination address; - vector<unsigned char> vchPubKey; if (!ExtractDestination(txout.scriptPubKey, address)) { LogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", this->GetHash().ToString().c_str()); + address = CNoDestination(); } - // Don't report 'change' txouts - if (nDebit > 0 && pwallet->IsChange(txout)) - continue; - + // If we are debited by the transaction, add the output as a "sent" entry if (nDebit > 0) listSent.push_back(make_pair(address, txout.nValue)); - if (pwallet->IsMine(txout)) + // If we are receiving the output, add it as a "received" entry + if (fIsMine) listReceived.push_back(make_pair(address, txout.nValue)); } @@ -779,6 +777,25 @@ void CWalletTx::AddSupportingTransactions() reverse(vtxPrev.begin(), vtxPrev.end()); } +bool CWalletTx::AcceptWalletTransaction() +{ + { + LOCK(mempool.cs); + // Add previous supporting transactions first + BOOST_FOREACH(CMerkleTx& tx, vtxPrev) + { + if (!tx.IsCoinBase()) + { + uint256 hash = tx.GetHash(); + if (!mempool.exists(hash) && pcoinsTip->HaveCoins(hash)) + tx.AcceptToMemoryPool(false); + } + } + return AcceptToMemoryPool(false); + } + return false; +} + bool CWalletTx::WriteToDisk() { return CWalletDB(pwallet->strWalletFile).WriteTx(GetHash(), *this); @@ -799,7 +816,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 +827,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 +881,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. } } @@ -1231,9 +1248,10 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, } int64 nChange = nValueIn - nValue - nFeeRet; - // if sub-cent change is required, the fee must be raised to at least nMinTxFee - // or until nChange becomes zero - // NOTE: this depends on the exact behaviour of GetMinFee + // The following if statement should be removed once enough miners + // have upgraded to the 0.9 GetMinFee() rules. Until then, this avoids + // creating free transactions that have change outputs less than + // CENT bitcoins. if (nFeeRet < CTransaction::nMinTxFee && nChange > 0 && nChange < CENT) { int64 nMoveToFee = min(nChange, CTransaction::nMinTxFee - nFeeRet); @@ -1299,7 +1317,15 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, strFailReason = _("Transaction too large"); return false; } - dPriority /= nBytes; + unsigned int nTxSizeMod = nBytes; + // See miner.c's dPriority logic for the matching network-node side code. + BOOST_FOREACH(const CTxIn& txin, (*(CTransaction*)&wtxNew).vin) + { + unsigned int offset = 41U + min(110U, (unsigned int)txin.scriptSig.size()); + if (nTxSizeMod > offset) + nTxSizeMod -= offset; + } + dPriority /= nTxSizeMod; // Check that enough fee is included int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000); @@ -1482,33 +1508,6 @@ bool CWallet::DelAddressBook(const CTxDestination& address) return CWalletDB(strWalletFile).EraseName(CBitcoinAddress(address).ToString()); } -void CWallet::PrintWallet(const CBlock& block) -{ - { - LOCK(cs_wallet); - if (mapWallet.count(block.vtx[0].GetHash())) - { - CWalletTx& wtx = mapWallet[block.vtx[0].GetHash()]; - LogPrintf(" mine: %d %d %"PRI64d"", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit()); - } - } - LogPrintf("\n"); -} - -bool CWallet::GetTransaction(const uint256 &hashTx, CWalletTx& wtx) -{ - { - LOCK(cs_wallet); - map<uint256, CWalletTx>::iterator mi = mapWallet.find(hashTx); - if (mi != mapWallet.end()) - { - wtx = (*mi).second; - return true; - } - } - return false; -} - bool CWallet::SetDefaultKey(const CPubKey &vchPubKey) { if (fFileBacked) @@ -1520,14 +1519,6 @@ bool CWallet::SetDefaultKey(const CPubKey &vchPubKey) return true; } -bool GetWalletFile(CWallet* pwallet, string &strWalletFileOut) -{ - if (!pwallet->fFileBacked) - return false; - strWalletFileOut = pwallet->strWalletFile; - return true; -} - // // Mark old keypool keys as used, // and generate all new keys @@ -1933,7 +1924,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 +1944,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) { diff --git a/src/wallet.h b/src/wallet.h index 51bc9f67c4..21510f8c39 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. #ifndef BITCOIN_WALLET_H @@ -80,7 +80,7 @@ public: /** A CWallet is an extension of a keystore, which also maintains a set of transactions and balances, * and provides the ability to create new transactions. */ -class CWallet : public CCryptoKeyStore +class CWallet : public CCryptoKeyStore, public CWalletInterface { private: bool SelectCoins(int64 nTargetValue, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const; @@ -197,8 +197,9 @@ public: void MarkDirty(); bool AddToWallet(const CWalletTx& wtxIn); - bool AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate = false, bool fFindBlock = false); - bool EraseFromWallet(uint256 hash); + void SyncTransaction(const uint256 &hash, const CTransaction& tx, const CBlock* pblock); + bool AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate); + void EraseFromWallet(const uint256 &hash); void WalletUpdateSpent(const CTransaction& prevout); int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); void ReacceptWalletTransactions(); @@ -302,8 +303,6 @@ public: void UpdatedTransaction(const uint256 &hashTx); - void PrintWallet(const CBlock& block); - void Inventory(const uint256 &hash) { { @@ -319,8 +318,6 @@ public: return setKeyPool.size(); } - bool GetTransaction(const uint256 &hashTx, CWalletTx& wtx); - bool SetDefaultKey(const CPubKey &vchPubKey); // signify that a particular wallet feature is now used. this may change nWalletVersion and nWalletMaxVersion if those are lower @@ -891,6 +888,4 @@ private: std::vector<char> _ssExtra; }; -bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut); - #endif diff --git a/src/walletdb.cpp b/src/walletdb.cpp index 635fda1b42..831ef8b00d 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. @@ -306,6 +306,8 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, } CKey key; CPrivKey pkey; + uint256 hash = 0; + if (strType == "key") { wss.nKeys++; @@ -315,14 +317,40 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, ssValue >> wkey; pkey = wkey.vchPrivKey; } - if (!key.SetPrivKey(pkey, vchPubKey.IsCompressed())) + + // Old wallets store keys as "key" [pubkey] => [privkey] + // ... which was slow for wallets with lots of keys, because the public key is re-derived from the private key + // using EC operations as a checksum. + // Newer wallets store keys as "key"[pubkey] => [privkey][hash(pubkey,privkey)], which is much faster while + // remaining backwards-compatible. + try { - strErr = "Error reading wallet database: CPrivKey corrupt"; - return false; + ssValue >> hash; + } + catch(...){} + + bool fSkipCheck = false; + + if (hash != 0) + { + // hash pubkey/privkey to accelerate wallet load + std::vector<unsigned char> vchKey; + vchKey.reserve(vchPubKey.size() + pkey.size()); + vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end()); + vchKey.insert(vchKey.end(), pkey.begin(), pkey.end()); + + if (Hash(vchKey.begin(), vchKey.end()) != hash) + { + strErr = "Error reading wallet database: CPubKey/CPrivKey corrupt"; + return false; + } + + fSkipCheck = true; } - if (key.GetPubKey() != vchPubKey) + + if (!key.Load(pkey, vchPubKey, fSkipCheck)) { - strErr = "Error reading wallet database: CPrivKey pubkey inconsistency"; + strErr = "Error reading wallet database: CPrivKey corrupt"; return false; } if (!pwallet->LoadKey(key, vchPubKey)) diff --git a/src/walletdb.h b/src/walletdb.h index 09ebebe5ac..b6d0d45449 100644 --- a/src/walletdb.h +++ b/src/walletdb.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. #ifndef BITCOIN_WALLETDB_H @@ -93,8 +93,14 @@ public: if (!Write(std::make_pair(std::string("keymeta"), vchPubKey), keyMeta)) return false; - - return Write(std::make_pair(std::string("key"), vchPubKey), vchPrivKey, false); + + // hash pubkey/privkey to accelerate wallet load + std::vector<unsigned char> vchKey; + vchKey.reserve(vchPubKey.size() + vchPrivKey.size()); + vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end()); + vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end()); + + return Write(std::make_pair(std::string("key"), vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false); } bool WriteCryptedKey(const CPubKey& vchPubKey, |