diff options
Diffstat (limited to 'src')
108 files changed, 3204 insertions, 1889 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 9d3365fd60..5ce1c8e1b6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,6 +4,9 @@ AM_CPPFLAGS += -I$(top_srcdir)/src/leveldb/helpers/memenv \ -I$(builddir) noinst_LIBRARIES = libbitcoin_server.a libbitcoin_common.a libbitcoin_cli.a +if ENABLE_WALLET +noinst_LIBRARIES += libbitcoin_wallet.a +endif bin_PROGRAMS = bitcoind bitcoin-cli @@ -33,14 +36,38 @@ obj/build.h: FORCE $(abs_top_srcdir) version.o: obj/build.h -libbitcoin_server_a_SOURCES = addrman.cpp alert.cpp \ +libbitcoin_server_a_SOURCES = \ + addrman.cpp \ + alert.cpp \ rpcserver.cpp \ bloom.cpp \ - chainparams.cpp checkpoints.cpp coins.cpp crypter.cpp db.cpp \ - init.cpp keystore.cpp leveldbwrapper.cpp main.cpp miner.cpp \ - net.cpp noui.cpp rpcblockchain.cpp rpcdump.cpp \ - rpcmining.cpp rpcnet.cpp rpcrawtransaction.cpp rpcwallet.cpp \ - txdb.cpp txmempool.cpp wallet.cpp walletdb.cpp $(JSON_H) \ + chainparams.cpp \ + checkpoints.cpp \ + coins.cpp \ + init.cpp \ + keystore.cpp \ + leveldbwrapper.cpp \ + main.cpp \ + miner.cpp \ + net.cpp \ + noui.cpp \ + rpcblockchain.cpp \ + rpcmining.cpp \ + rpcmisc.cpp \ + rpcnet.cpp \ + rpcrawtransaction.cpp \ + txdb.cpp \ + txmempool.cpp \ + $(JSON_H) \ + $(BITCOIN_CORE_H) + +libbitcoin_wallet_a_SOURCES = \ + db.cpp \ + crypter.cpp \ + rpcdump.cpp \ + rpcwallet.cpp \ + wallet.cpp \ + walletdb.cpp \ $(BITCOIN_CORE_H) libbitcoin_common_a_SOURCES = \ @@ -68,6 +95,9 @@ nodist_libbitcoin_common_a_SOURCES = $(top_srcdir)/src/obj/build.h # bitcoind binary # bitcoind_LDADD = libbitcoin_server.a libbitcoin_cli.a libbitcoin_common.a leveldb/libleveldb.a leveldb/libmemenv.a \ $(BOOST_LIBS) +if ENABLE_WALLET +bitcoind_LDADD += libbitcoin_wallet.a +endif bitcoind_SOURCES = bitcoind.cpp # @@ -106,5 +136,4 @@ EXTRA_DIST = leveldb Makefile.include clean-local: -$(MAKE) -C leveldb clean - rm -f leveldb/port/*.gcno leveldb/db/*.gcno leveldb/table/*.gcno leveldb/helpers/*.gcno - rm -f leveldb/util/*.gcno leveldb/helpers/memenv/*.gcno + rm -f leveldb/*/*.gcno leveldb/helpers/memenv/*.gcno diff --git a/src/Makefile.include b/src/Makefile.include index 2e96a6b7dc..13cffd29bc 100644 --- a/src/Makefile.include +++ b/src/Makefile.include @@ -6,6 +6,7 @@ AM_CPPFLAGS = $(INCLUDES) \ AM_LDFLAGS = $(PTHREAD_CFLAGS) LIBBITCOIN_SERVER=$(top_builddir)/src/libbitcoin_server.a +LIBBITCOIN_WALLET=$(top_builddir)/src/libbitcoin_wallet.a LIBBITCOIN_COMMON=$(top_builddir)/src/libbitcoin_common.a LIBBITCOIN_CLI=$(top_builddir)/src/libbitcoin_cli.a LIBLEVELDB=$(top_builddir)/src/leveldb/libleveldb.a diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index e13d688158..de71fde9bc 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -78,7 +78,7 @@ bool AppInit(int argc, char* argv[]) if (mapArgs.count("-?") || mapArgs.count("--help")) { // First part of help message is specific to bitcoind / RPC client - std::string strUsage = _("Bitcoin version") + " " + FormatFullVersion() + "\n\n" + + std::string strUsage = _("Bitcoin Core Daemon") + " " + _("version") + " " + FormatFullVersion() + "\n\n" + _("Usage:") + "\n" + " bitcoind [options] " + _("Start Bitcoin server") + "\n" + _("Usage (deprecated, use bitcoin-cli):") + "\n" + diff --git a/src/crypter.cpp b/src/crypter.cpp index 10a34ae24a..4c43e3a798 100644 --- a/src/crypter.cpp +++ b/src/crypter.cpp @@ -4,9 +4,11 @@ #include "crypter.h" +#include "script.h" + #include <string> #include <vector> - +#include <boost/foreach.hpp> #include <openssl/aes.h> #include <openssl/evp.h> @@ -117,3 +119,156 @@ bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned return false; return cKeyCrypter.Decrypt(vchCiphertext, *((CKeyingMaterial*)&vchPlaintext)); } + +bool CCryptoKeyStore::SetCrypted() +{ + LOCK(cs_KeyStore); + if (fUseCrypto) + return true; + if (!mapKeys.empty()) + return false; + fUseCrypto = true; + return true; +} + +bool CCryptoKeyStore::Lock() +{ + if (!SetCrypted()) + return false; + + { + LOCK(cs_KeyStore); + vMasterKey.clear(); + } + + NotifyStatusChanged(this); + return true; +} + +bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) +{ + { + LOCK(cs_KeyStore); + if (!SetCrypted()) + return false; + + CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin(); + for (; mi != mapCryptedKeys.end(); ++mi) + { + const CPubKey &vchPubKey = (*mi).second.first; + const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second; + CKeyingMaterial vchSecret; + if(!DecryptSecret(vMasterKeyIn, vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) + return false; + if (vchSecret.size() != 32) + return false; + CKey key; + key.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed()); + if (key.GetPubKey() == vchPubKey) + break; + return false; + } + vMasterKey = vMasterKeyIn; + } + NotifyStatusChanged(this); + return true; +} + +bool CCryptoKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey) +{ + { + LOCK(cs_KeyStore); + if (!IsCrypted()) + return CBasicKeyStore::AddKeyPubKey(key, pubkey); + + if (IsLocked()) + return false; + + std::vector<unsigned char> vchCryptedSecret; + CKeyingMaterial vchSecret(key.begin(), key.end()); + if (!EncryptSecret(vMasterKey, vchSecret, pubkey.GetHash(), vchCryptedSecret)) + return false; + + if (!AddCryptedKey(pubkey, vchCryptedSecret)) + return false; + } + return true; +} + + +bool CCryptoKeyStore::AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) +{ + { + LOCK(cs_KeyStore); + if (!SetCrypted()) + return false; + + mapCryptedKeys[vchPubKey.GetID()] = make_pair(vchPubKey, vchCryptedSecret); + } + return true; +} + +bool CCryptoKeyStore::GetKey(const CKeyID &address, CKey& keyOut) const +{ + { + LOCK(cs_KeyStore); + if (!IsCrypted()) + return CBasicKeyStore::GetKey(address, keyOut); + + CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); + if (mi != mapCryptedKeys.end()) + { + const CPubKey &vchPubKey = (*mi).second.first; + const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second; + CKeyingMaterial vchSecret; + if (!DecryptSecret(vMasterKey, vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) + return false; + if (vchSecret.size() != 32) + return false; + keyOut.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed()); + return true; + } + } + return false; +} + +bool CCryptoKeyStore::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const +{ + { + LOCK(cs_KeyStore); + if (!IsCrypted()) + return CKeyStore::GetPubKey(address, vchPubKeyOut); + + CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); + if (mi != mapCryptedKeys.end()) + { + vchPubKeyOut = (*mi).second.first; + return true; + } + } + return false; +} + +bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn) +{ + { + LOCK(cs_KeyStore); + if (!mapCryptedKeys.empty() || IsCrypted()) + return false; + + fUseCrypto = true; + BOOST_FOREACH(KeyMap::value_type& mKey, mapKeys) + { + const CKey &key = mKey.second; + CPubKey vchPubKey = key.GetPubKey(); + CKeyingMaterial vchSecret(key.begin(), key.end()); + std::vector<unsigned char> vchCryptedSecret; + if (!EncryptSecret(vMasterKeyIn, vchSecret, vchPubKey.GetHash(), vchCryptedSecret)) + return false; + if (!AddCryptedKey(vchPubKey, vchCryptedSecret)) + return false; + } + mapKeys.clear(); + } + return true; +} diff --git a/src/crypter.h b/src/crypter.h index 861c4f9441..4791428b48 100644 --- a/src/crypter.h +++ b/src/crypter.h @@ -7,6 +7,7 @@ #include "allocators.h" #include "serialize.h" +#include "keystore.h" class uint256; @@ -106,4 +107,86 @@ public: bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext); bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext); +/** Keystore which keeps the private keys encrypted. + * It derives from the basic key store, which is used if no encryption is active. + */ +class CCryptoKeyStore : public CBasicKeyStore +{ +private: + CryptedKeyMap mapCryptedKeys; + + CKeyingMaterial vMasterKey; + + // if fUseCrypto is true, mapKeys must be empty + // if fUseCrypto is false, vMasterKey must be empty + bool fUseCrypto; + +protected: + bool SetCrypted(); + + // will encrypt previously unencrypted keys + bool EncryptKeys(CKeyingMaterial& vMasterKeyIn); + + bool Unlock(const CKeyingMaterial& vMasterKeyIn); + +public: + CCryptoKeyStore() : fUseCrypto(false) + { + } + + bool IsCrypted() const + { + return fUseCrypto; + } + + bool IsLocked() const + { + if (!IsCrypted()) + return false; + bool result; + { + LOCK(cs_KeyStore); + result = vMasterKey.empty(); + } + return result; + } + + bool Lock(); + + virtual bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret); + bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey); + bool HaveKey(const CKeyID &address) const + { + { + LOCK(cs_KeyStore); + if (!IsCrypted()) + return CBasicKeyStore::HaveKey(address); + return mapCryptedKeys.count(address) > 0; + } + return false; + } + bool GetKey(const CKeyID &address, CKey& keyOut) const; + bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const; + void GetKeys(std::set<CKeyID> &setAddress) const + { + if (!IsCrypted()) + { + CBasicKeyStore::GetKeys(setAddress); + return; + } + setAddress.clear(); + CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin(); + while (mi != mapCryptedKeys.end()) + { + setAddress.insert((*mi).first); + mi++; + } + } + + /* Wallet status (encrypted, locked) changed. + * Note: Called without locks held. + */ + boost::signals2::signal<void (CCryptoKeyStore* wallet)> NotifyStatusChanged; +}; + #endif diff --git a/src/db.cpp b/src/db.cpp index a286d9f726..1f2ee1c554 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -479,113 +479,3 @@ void CDBEnv::Flush(bool fShutdown) } } - - - - - - - - - - -// -// CAddrDB -// - -CAddrDB::CAddrDB() -{ - pathAddr = GetDataDir() / "peers.dat"; -} - -bool CAddrDB::Write(const CAddrMan& addr) -{ - // Generate random temporary filename - unsigned short randv = 0; - RAND_bytes((unsigned char *)&randv, sizeof(randv)); - std::string tmpfn = strprintf("peers.dat.%04x", randv); - - // serialize addresses, checksum data up to that point, then append csum - CDataStream ssPeers(SER_DISK, CLIENT_VERSION); - ssPeers << FLATDATA(Params().MessageStart()); - ssPeers << addr; - uint256 hash = Hash(ssPeers.begin(), ssPeers.end()); - ssPeers << hash; - - // open temp output file, and associate with CAutoFile - boost::filesystem::path pathTmp = GetDataDir() / tmpfn; - FILE *file = fopen(pathTmp.string().c_str(), "wb"); - CAutoFile fileout = CAutoFile(file, SER_DISK, CLIENT_VERSION); - if (!fileout) - return error("CAddrman::Write() : open failed"); - - // Write and commit header, data - try { - fileout << ssPeers; - } - catch (std::exception &e) { - return error("CAddrman::Write() : I/O error"); - } - FileCommit(fileout); - fileout.fclose(); - - // replace existing peers.dat, if any, with new peers.dat.XXXX - if (!RenameOver(pathTmp, pathAddr)) - return error("CAddrman::Write() : Rename-into-place failed"); - - return true; -} - -bool CAddrDB::Read(CAddrMan& addr) -{ - // open input file, and associate with CAutoFile - FILE *file = fopen(pathAddr.string().c_str(), "rb"); - CAutoFile filein = CAutoFile(file, SER_DISK, CLIENT_VERSION); - if (!filein) - return error("CAddrman::Read() : open failed"); - - // use file size to size memory buffer - int fileSize = GetFilesize(filein); - int dataSize = fileSize - sizeof(uint256); - //Don't try to resize to a negative number if file is small - if ( dataSize < 0 ) dataSize = 0; - vector<unsigned char> vchData; - vchData.resize(dataSize); - uint256 hashIn; - - // read data and checksum from file - try { - filein.read((char *)&vchData[0], dataSize); - filein >> hashIn; - } - catch (std::exception &e) { - return error("CAddrman::Read() 2 : I/O error or stream data corrupted"); - } - filein.fclose(); - - CDataStream ssPeers(vchData, SER_DISK, CLIENT_VERSION); - - // verify stored checksum matches input data - uint256 hashTmp = Hash(ssPeers.begin(), ssPeers.end()); - if (hashIn != hashTmp) - return error("CAddrman::Read() : checksum mismatch; data corrupted"); - - unsigned char pchMsgTmp[4]; - try { - // de-serialize file header (network specific magic number) and .. - ssPeers >> FLATDATA(pchMsgTmp); - - // ... verify the network matches ours - if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) - return error("CAddrman::Read() : invalid network magic number"); - - // de-serialize address data into one CAddrMan object - ssPeers >> addr; - } - catch (std::exception &e) { - return error("CAddrman::Read() : I/O error or stream data corrupted"); - } - - return true; -} - @@ -305,22 +305,4 @@ public: bool static Rewrite(const std::string& strFile, const char* pszSkip = NULL); }; - - - - - - - -/** Access to the (IP) address database (peers.dat) */ -class CAddrDB -{ -private: - boost::filesystem::path pathAddr; -public: - CAddrDB(); - bool Write(const CAddrMan& addr); - bool Read(CAddrMan& addr); -}; - #endif // BITCOIN_DB_H diff --git a/src/init.cpp b/src/init.cpp index 54722743e6..28f6e08f9a 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -10,15 +10,19 @@ #include "init.h" #include "addrman.h" -#include "rpcserver.h" #include "checkpoints.h" +#include "main.h" #include "miner.h" #include "net.h" +#include "rpcserver.h" #include "txdb.h" #include "ui_interface.h" #include "util.h" +#ifdef ENABLE_WALLET +#include "db.h" #include "wallet.h" #include "walletdb.h" +#endif #include <inttypes.h> #include <stdint.h> @@ -35,8 +39,10 @@ using namespace std; using namespace boost; +#ifdef ENABLE_WALLET std::string strWalletFile; CWallet* pwalletMain; +#endif #ifdef WIN32 // Win32 LevelDB doesn't use filedescriptors, and the ones used for @@ -109,14 +115,19 @@ void Shutdown() mempool.AddTransactionsUpdated(1); StopRPCThreads(); ShutdownRPCMining(); +#ifdef ENABLE_WALLET if (pwalletMain) bitdb.Flush(false); GenerateBitcoins(false, NULL, 0); +#endif StopNode(); + UnregisterNodeSignals(GetNodeSignals()); { LOCK(cs_main); +#ifdef ENABLE_WALLET if (pwalletMain) pwalletMain->SetBestChain(chainActive.GetLocator()); +#endif if (pblocktree) pblocktree->Flush(); if (pcoinsTip) @@ -125,12 +136,16 @@ void Shutdown() delete pcoinsdbview; pcoinsdbview = NULL; delete pblocktree; pblocktree = NULL; } +#ifdef ENABLE_WALLET if (pwalletMain) bitdb.Flush(true); +#endif boost::filesystem::remove(GetPidFile()); UnregisterAllWallets(); +#ifdef ENABLE_WALLET if (pwalletMain) delete pwalletMain; +#endif LogPrintf("Shutdown : done\n"); } @@ -179,15 +194,13 @@ std::string HelpMessage(HelpMessageMode hmm) strUsage += " -conf=<file> " + _("Specify configuration file (default: bitcoin.conf)") + "\n"; strUsage += " -datadir=<dir> " + _("Specify data directory") + "\n"; strUsage += " -testnet " + _("Use the test network") + "\n"; - 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 += " -proxy=<ip:port> " + _("Connect through SOCKS proxy") + "\n"; + strUsage += " -socks=<n> " + _("Select SOCKS version for -proxy (4 or 5, default: 5)") + "\n"; + strUsage += " -onion=<ip:port> " + _("Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: -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"; @@ -212,7 +225,6 @@ std::string HelpMessage(HelpMessageMode hmm) 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"; 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:"); @@ -229,7 +241,7 @@ std::string HelpMessage(HelpMessageMode hmm) 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"; + "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 @@ -249,27 +261,31 @@ std::string HelpMessage(HelpMessageMode hmm) 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"; 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"; - +#ifdef ENABLE_WALLET + strUsage += "\n" + _("Wallet options:") + "\n"; + strUsage += " -disablewallet " + _("Do not load the wallet and disable wallet RPC calls") + "\n"; + strUsage += " -paytxfee=<amt> " + _("Fee per kB to add to transactions you send") + "\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 += " -upgradewallet " + _("Upgrade wallet to latest format") + "\n"; + strUsage += " -wallet=<file> " + _("Specify wallet file (within data directory)") + "\n"; + strUsage += " -walletnotify=<cmd> " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n"; +#endif 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 += " -blockminsize=<n> " + _("Set minimum block size in bytes (default: 0)") + "\n"; + strUsage += " -blockmaxsize=<n> " + strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE) + "\n"; + strUsage += " -blockprioritysize=<n> " + strprintf(_("Set maximum size of high-priority/low-fee transactions in bytes (default: %d)"), DEFAULT_BLOCK_PRIORITY_SIZE) + "\n"; strUsage += "\n" + _("SSL options: (see the Bitcoin Wiki for SSL setup instructions)") + "\n"; strUsage += " -rpcssl " + _("Use OpenSSL (https) for JSON-RPC connections") + "\n"; @@ -479,7 +495,9 @@ bool AppInit2(boost::thread_group& threadGroup, bool fForceServer) fPrintToConsole = GetBoolArg("-printtoconsole", false); fPrintToDebugger = GetBoolArg("-printtodebugger", false); fLogTimestamps = GetBoolArg("-logtimestamps", true); +#ifdef ENABLE_WALLET bool fDisableWallet = GetBoolArg("-disablewallet", false); +#endif if (mapArgs.count("-timeout")) { @@ -517,6 +535,7 @@ bool AppInit2(boost::thread_group& threadGroup, bool fForceServer) return InitError(strprintf(_("Invalid amount for -minrelaytxfee=<amount>: '%s'"), mapArgs["-minrelaytxfee"].c_str())); } +#ifdef ENABLE_WALLET if (mapArgs.count("-paytxfee")) { if (!ParseMoney(mapArgs["-paytxfee"], nTransactionFee)) @@ -526,15 +545,15 @@ bool AppInit2(boost::thread_group& threadGroup, bool fForceServer) } strWalletFile = GetArg("-wallet", "wallet.dat"); - +#endif // ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log std::string strDataDir = GetDataDir().string(); - +#ifdef ENABLE_WALLET // Wallet file must be a plain filename without a directory if (strWalletFile != boost::filesystem::basename(strWalletFile) + boost::filesystem::extension(strWalletFile)) return InitError(strprintf(_("Wallet %s resides outside data directory %s"), strWalletFile.c_str(), strDataDir.c_str())); - +#endif // Make sure only a single Bitcoin process is using the data directory. boost::filesystem::path pathLockFile = GetDataDir() / ".lock"; FILE* file = fopen(pathLockFile.string().c_str(), "a"); // empty lock file; created if it doesn't exist. @@ -567,7 +586,7 @@ bool AppInit2(boost::thread_group& threadGroup, bool fForceServer) int64_t nStart; // ********************************************************* Step 5: verify wallet database integrity - +#ifdef ENABLE_WALLET if (!fDisableWallet) { uiInterface.InitMessage(_("Verifying wallet...")); @@ -613,7 +632,7 @@ bool AppInit2(boost::thread_group& threadGroup, bool fForceServer) return InitError(_("wallet.dat corrupt, salvage failed")); } } // (!fDisableWallet) - +#endif // ENABLE_WALLET // ********************************************************* Step 6: network initialization RegisterNodeSignals(GetNodeSignals()); @@ -840,8 +859,8 @@ bool AppInit2(boost::thread_group& threadGroup, bool fForceServer) } } - // as LoadBlockIndex can take several minutes, it's possible the user - // requested to kill bitcoin-qt during the last operation. If so, exit. + // As LoadBlockIndex can take several minutes, it's possible the user + // requested to kill the GUI during the last operation. If so, exit. // As the program has not fully started yet, Shutdown() is possibly overkill. if (fRequestShutdown) { @@ -880,7 +899,7 @@ bool AppInit2(boost::thread_group& threadGroup, bool fForceServer) } // ********************************************************* Step 8: load wallet - +#ifdef ENABLE_WALLET if (fDisableWallet) { pwalletMain = NULL; LogPrintf("Wallet disabled!\n"); @@ -972,7 +991,9 @@ bool AppInit2(boost::thread_group& threadGroup, bool fForceServer) nWalletDBUpdated++; } } // (!fDisableWallet) - +#else // ENABLE_WALLET + LogPrintf("No wallet compiled in!\n"); +#endif // !ENABLE_WALLET // ********************************************************* Step 9: import blocks // scan for better chains in the block chain database, that are not yet connected in the active best chain @@ -1016,25 +1037,29 @@ bool AppInit2(boost::thread_group& threadGroup, bool fForceServer) //// debug print LogPrintf("mapBlockIndex.size() = %"PRIszu"\n", mapBlockIndex.size()); LogPrintf("nBestHeight = %d\n", chainActive.Height()); +#ifdef ENABLE_WALLET 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); +#endif StartNode(threadGroup); - // InitRPCMining is needed here so getwork/getblocktemplate in the GUI debug console works properly. InitRPCMining(); if (fServer) StartRPCThreads(); +#ifdef ENABLE_WALLET // Generate coins in the background if (pwalletMain) GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain, GetArg("-genproclimit", -1)); +#endif // ********************************************************* Step 12: finished uiInterface.InitMessage(_("Done loading")); +#ifdef ENABLE_WALLET if (pwalletMain) { // Add wallet transactions that aren't already in a block to mapTransactions pwalletMain->ReacceptWalletTransactions(); @@ -1042,6 +1067,7 @@ bool AppInit2(boost::thread_group& threadGroup, bool fForceServer) // Run a thread to flush wallet periodically threadGroup.create_thread(boost::bind(&ThreadFlushWalletDB, boost::ref(pwalletMain->strWalletFile))); } +#endif return !fRequestShutdown; } diff --git a/src/keystore.cpp b/src/keystore.cpp index 05427291e0..46402ea25b 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -56,155 +56,3 @@ bool CBasicKeyStore::GetCScript(const CScriptID &hash, CScript& redeemScriptOut) return false; } -bool CCryptoKeyStore::SetCrypted() -{ - LOCK(cs_KeyStore); - if (fUseCrypto) - return true; - if (!mapKeys.empty()) - return false; - fUseCrypto = true; - return true; -} - -bool CCryptoKeyStore::Lock() -{ - if (!SetCrypted()) - return false; - - { - LOCK(cs_KeyStore); - vMasterKey.clear(); - } - - NotifyStatusChanged(this); - return true; -} - -bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) -{ - { - LOCK(cs_KeyStore); - if (!SetCrypted()) - return false; - - CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin(); - for (; mi != mapCryptedKeys.end(); ++mi) - { - const CPubKey &vchPubKey = (*mi).second.first; - const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second; - CKeyingMaterial vchSecret; - if(!DecryptSecret(vMasterKeyIn, vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) - return false; - if (vchSecret.size() != 32) - return false; - CKey key; - key.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed()); - if (key.GetPubKey() == vchPubKey) - break; - return false; - } - vMasterKey = vMasterKeyIn; - } - NotifyStatusChanged(this); - return true; -} - -bool CCryptoKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey) -{ - { - LOCK(cs_KeyStore); - if (!IsCrypted()) - return CBasicKeyStore::AddKeyPubKey(key, pubkey); - - if (IsLocked()) - return false; - - std::vector<unsigned char> vchCryptedSecret; - CKeyingMaterial vchSecret(key.begin(), key.end()); - if (!EncryptSecret(vMasterKey, vchSecret, pubkey.GetHash(), vchCryptedSecret)) - return false; - - if (!AddCryptedKey(pubkey, vchCryptedSecret)) - return false; - } - return true; -} - - -bool CCryptoKeyStore::AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) -{ - { - LOCK(cs_KeyStore); - if (!SetCrypted()) - return false; - - mapCryptedKeys[vchPubKey.GetID()] = make_pair(vchPubKey, vchCryptedSecret); - } - return true; -} - -bool CCryptoKeyStore::GetKey(const CKeyID &address, CKey& keyOut) const -{ - { - LOCK(cs_KeyStore); - if (!IsCrypted()) - return CBasicKeyStore::GetKey(address, keyOut); - - CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); - if (mi != mapCryptedKeys.end()) - { - const CPubKey &vchPubKey = (*mi).second.first; - const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second; - CKeyingMaterial vchSecret; - if (!DecryptSecret(vMasterKey, vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) - return false; - if (vchSecret.size() != 32) - return false; - keyOut.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed()); - return true; - } - } - return false; -} - -bool CCryptoKeyStore::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const -{ - { - LOCK(cs_KeyStore); - if (!IsCrypted()) - return CKeyStore::GetPubKey(address, vchPubKeyOut); - - CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); - if (mi != mapCryptedKeys.end()) - { - vchPubKeyOut = (*mi).second.first; - return true; - } - } - return false; -} - -bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn) -{ - { - LOCK(cs_KeyStore); - if (!mapCryptedKeys.empty() || IsCrypted()) - return false; - - fUseCrypto = true; - BOOST_FOREACH(KeyMap::value_type& mKey, mapKeys) - { - const CKey &key = mKey.second; - CPubKey vchPubKey = key.GetPubKey(); - CKeyingMaterial vchSecret(key.begin(), key.end()); - std::vector<unsigned char> vchCryptedSecret; - if (!EncryptSecret(vMasterKeyIn, vchSecret, vchPubKey.GetHash(), vchCryptedSecret)) - return false; - if (!AddCryptedKey(vchPubKey, vchCryptedSecret)) - return false; - } - mapKeys.clear(); - } - return true; -} diff --git a/src/keystore.h b/src/keystore.h index 8d936bcab7..0d55e6c81e 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -93,87 +93,4 @@ public: typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial; typedef std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char> > > CryptedKeyMap; - -/** Keystore which keeps the private keys encrypted. - * It derives from the basic key store, which is used if no encryption is active. - */ -class CCryptoKeyStore : public CBasicKeyStore -{ -private: - CryptedKeyMap mapCryptedKeys; - - CKeyingMaterial vMasterKey; - - // if fUseCrypto is true, mapKeys must be empty - // if fUseCrypto is false, vMasterKey must be empty - bool fUseCrypto; - -protected: - bool SetCrypted(); - - // will encrypt previously unencrypted keys - bool EncryptKeys(CKeyingMaterial& vMasterKeyIn); - - bool Unlock(const CKeyingMaterial& vMasterKeyIn); - -public: - CCryptoKeyStore() : fUseCrypto(false) - { - } - - bool IsCrypted() const - { - return fUseCrypto; - } - - bool IsLocked() const - { - if (!IsCrypted()) - return false; - bool result; - { - LOCK(cs_KeyStore); - result = vMasterKey.empty(); - } - return result; - } - - bool Lock(); - - virtual bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret); - bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey); - bool HaveKey(const CKeyID &address) const - { - { - LOCK(cs_KeyStore); - if (!IsCrypted()) - return CBasicKeyStore::HaveKey(address); - return mapCryptedKeys.count(address) > 0; - } - return false; - } - bool GetKey(const CKeyID &address, CKey& keyOut) const; - bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const; - void GetKeys(std::set<CKeyID> &setAddress) const - { - if (!IsCrypted()) - { - CBasicKeyStore::GetKeys(setAddress); - return; - } - setAddress.clear(); - CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin(); - while (mi != mapCryptedKeys.end()) - { - setAddress.insert((*mi).first); - mi++; - } - } - - /* Wallet status (encrypted, locked) changed. - * Note: Called without locks held. - */ - boost::signals2::signal<void (CCryptoKeyStore* wallet)> NotifyStatusChanged; -}; - #endif diff --git a/src/leveldb/AUTHORS b/src/leveldb/AUTHORS index fc40194ab9..2439d7a452 100644 --- a/src/leveldb/AUTHORS +++ b/src/leveldb/AUTHORS @@ -9,3 +9,4 @@ Sanjay Ghemawat <sanjay@google.com> # Partial list of contributors: Kevin Regan <kevin.d.regan@gmail.com> +Johan Bilien <jobi@litl.com> diff --git a/src/leveldb/Makefile b/src/leveldb/Makefile index 20c9c4f287..344ff2972a 100644 --- a/src/leveldb/Makefile +++ b/src/leveldb/Makefile @@ -44,6 +44,7 @@ TESTS = \ filename_test \ filter_block_test \ issue178_test \ + issue200_test \ log_test \ memenv_test \ skiplist_test \ @@ -71,7 +72,7 @@ SHARED = $(SHARED1) else # Update db.h if you change these. SHARED_MAJOR = 1 -SHARED_MINOR = 13 +SHARED_MINOR = 15 SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT) SHARED2 = $(SHARED1).$(SHARED_MAJOR) SHARED3 = $(SHARED1).$(SHARED_MAJOR).$(SHARED_MINOR) @@ -154,6 +155,9 @@ filter_block_test: table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) issue178_test: issues/issue178_test.o $(LIBOBJECTS) $(TESTHARNESS) $(CXX) $(LDFLAGS) issues/issue178_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) +issue200_test: issues/issue200_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) issues/issue200_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + log_test: db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) $(CXX) $(LDFLAGS) db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) @@ -191,14 +195,14 @@ IOSVERSION=$(shell defaults read $(PLATFORMSROOT)/iPhoneOS.platform/version CFBu mkdir -p ios-x86/$(dir $@) $(CXX) $(CXXFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -c $< -o ios-x86/$@ mkdir -p ios-arm/$(dir $@) - $(DEVICEROOT)/usr/bin/$(CXX) $(CXXFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk -arch armv6 -arch armv7 -c $< -o ios-arm/$@ + xcrun -sdk iphoneos $(CXX) $(CXXFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk -arch armv6 -arch armv7 -c $< -o ios-arm/$@ lipo ios-x86/$@ ios-arm/$@ -create -output $@ .c.o: mkdir -p ios-x86/$(dir $@) $(CC) $(CFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -c $< -o ios-x86/$@ mkdir -p ios-arm/$(dir $@) - $(DEVICEROOT)/usr/bin/$(CC) $(CFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk -arch armv6 -arch armv7 -c $< -o ios-arm/$@ + xcrun -sdk iphoneos $(CC) $(CFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk -arch armv6 -arch armv7 -c $< -o ios-arm/$@ lipo ios-x86/$@ ios-arm/$@ -create -output $@ else diff --git a/src/leveldb/build_detect_platform b/src/leveldb/build_detect_platform index bdfd64172c..85b1ce0224 100755 --- a/src/leveldb/build_detect_platform +++ b/src/leveldb/build_detect_platform @@ -137,6 +137,16 @@ case "$TARGET_OS" in # man ld: +h internal_name PLATFORM_SHARED_LDFLAGS="-shared -Wl,+h -Wl," ;; + IOS) + PLATFORM=IOS + COMMON_FLAGS="$MEMCMP_FLAG -DOS_MACOSX" + [ -z "$INSTALL_PATH" ] && INSTALL_PATH=`pwd` + PORT_FILE=port/port_posix.cc + PLATFORM_SHARED_EXT= + PLATFORM_SHARED_LDFLAGS= + PLATFORM_SHARED_CFLAGS= + PLATFORM_SHARED_VERSIONED= + ;; OS_WINDOWS_CROSSCOMPILE | NATIVE_WINDOWS) PLATFORM=OS_WINDOWS COMMON_FLAGS="-fno-builtin-memcmp -D_REENTRANT -DOS_WINDOWS -DLEVELDB_PLATFORM_WINDOWS -DWINVER=0x0500 -D__USE_MINGW_ANSI_STDIO=1" diff --git a/src/leveldb/db/corruption_test.cc b/src/leveldb/db/corruption_test.cc index b37ffdfe64..96afc68913 100644 --- a/src/leveldb/db/corruption_test.cc +++ b/src/leveldb/db/corruption_test.cc @@ -75,7 +75,13 @@ class CorruptionTest { Slice key = Key(i, &key_space); batch.Clear(); batch.Put(key, Value(i, &value_space)); - ASSERT_OK(db_->Write(WriteOptions(), &batch)); + WriteOptions options; + // Corrupt() doesn't work without this sync on windows; stat reports 0 for + // the file size. + if (i == n - 1) { + options.sync = true; + } + ASSERT_OK(db_->Write(options, &batch)); } } @@ -125,7 +131,7 @@ class CorruptionTest { FileType type; std::string fname; int picked_number = -1; - for (int i = 0; i < filenames.size(); i++) { + for (size_t i = 0; i < filenames.size(); i++) { if (ParseFileName(filenames[i], &number, &type) && type == filetype && int(number) > picked_number) { // Pick latest file @@ -238,6 +244,22 @@ TEST(CorruptionTest, TableFile) { Check(90, 99); } +TEST(CorruptionTest, TableFileRepair) { + options_.block_size = 2 * kValueSize; // Limit scope of corruption + options_.paranoid_checks = true; + Reopen(); + Build(100); + DBImpl* dbi = reinterpret_cast<DBImpl*>(db_); + dbi->TEST_CompactMemTable(); + dbi->TEST_CompactRange(0, NULL, NULL); + dbi->TEST_CompactRange(1, NULL, NULL); + + Corrupt(kTableFile, 100, 1); + RepairDB(); + Reopen(); + Check(95, 99); +} + TEST(CorruptionTest, TableFileIndexData) { Build(10000); // Enough to build multiple Tables DBImpl* dbi = reinterpret_cast<DBImpl*>(db_); diff --git a/src/leveldb/db/db_bench.cc b/src/leveldb/db/db_bench.cc index 7abdf87587..fc46d89693 100644 --- a/src/leveldb/db/db_bench.cc +++ b/src/leveldb/db/db_bench.cc @@ -128,7 +128,7 @@ class RandomGenerator { pos_ = 0; } - Slice Generate(int len) { + Slice Generate(size_t len) { if (pos_ + len > data_.size()) { pos_ = 0; assert(len < data_.size()); @@ -139,11 +139,11 @@ class RandomGenerator { }; static Slice TrimSpace(Slice s) { - int start = 0; + size_t start = 0; while (start < s.size() && isspace(s[start])) { start++; } - int limit = s.size(); + size_t limit = s.size(); while (limit > start && isspace(s[limit-1])) { limit--; } @@ -399,7 +399,7 @@ class Benchmark { heap_counter_(0) { std::vector<std::string> files; Env::Default()->GetChildren(FLAGS_db, &files); - for (int i = 0; i < files.size(); i++) { + for (size_t i = 0; i < files.size(); i++) { if (Slice(files[i]).starts_with("heap-")) { Env::Default()->DeleteFile(std::string(FLAGS_db) + "/" + files[i]); } diff --git a/src/leveldb/db/db_impl.cc b/src/leveldb/db/db_impl.cc index fa1351038b..faf5e7d7ba 100644 --- a/src/leveldb/db/db_impl.cc +++ b/src/leveldb/db/db_impl.cc @@ -133,8 +133,7 @@ DBImpl::DBImpl(const Options& raw_options, const std::string& dbname) seed_(0), tmp_batch_(new WriteBatch), bg_compaction_scheduled_(false), - manual_compaction_(NULL), - consecutive_compaction_errors_(0) { + manual_compaction_(NULL) { mem_->Ref(); has_imm_.Release_Store(NULL); @@ -217,6 +216,12 @@ void DBImpl::MaybeIgnoreError(Status* s) const { } void DBImpl::DeleteObsoleteFiles() { + if (!bg_error_.ok()) { + // After a background error, we don't know whether a new version may + // or may not have been committed, so we cannot safely garbage collect. + return; + } + // Make a set of all of the live files std::set<uint64_t> live = pending_outputs_; versions_->AddLiveFiles(&live); @@ -495,7 +500,7 @@ Status DBImpl::WriteLevel0Table(MemTable* mem, VersionEdit* edit, return s; } -Status DBImpl::CompactMemTable() { +void DBImpl::CompactMemTable() { mutex_.AssertHeld(); assert(imm_ != NULL); @@ -523,9 +528,9 @@ Status DBImpl::CompactMemTable() { imm_ = NULL; has_imm_.Release_Store(NULL); DeleteObsoleteFiles(); + } else { + RecordBackgroundError(s); } - - return s; } void DBImpl::CompactRange(const Slice* begin, const Slice* end) { @@ -568,16 +573,18 @@ void DBImpl::TEST_CompactRange(int level, const Slice* begin,const Slice* end) { } MutexLock l(&mutex_); - while (!manual.done) { - while (manual_compaction_ != NULL) { - bg_cv_.Wait(); - } - manual_compaction_ = &manual; - MaybeScheduleCompaction(); - while (manual_compaction_ == &manual) { + while (!manual.done && !shutting_down_.Acquire_Load() && bg_error_.ok()) { + if (manual_compaction_ == NULL) { // Idle + manual_compaction_ = &manual; + MaybeScheduleCompaction(); + } else { // Running either my compaction or another compaction. bg_cv_.Wait(); } } + if (manual_compaction_ == &manual) { + // Cancel my manual compaction since we aborted early for some reason. + manual_compaction_ = NULL; + } } Status DBImpl::TEST_CompactMemTable() { @@ -596,12 +603,22 @@ Status DBImpl::TEST_CompactMemTable() { return s; } +void DBImpl::RecordBackgroundError(const Status& s) { + mutex_.AssertHeld(); + if (bg_error_.ok()) { + bg_error_ = s; + bg_cv_.SignalAll(); + } +} + void DBImpl::MaybeScheduleCompaction() { mutex_.AssertHeld(); if (bg_compaction_scheduled_) { // Already scheduled } else if (shutting_down_.Acquire_Load()) { // DB is being deleted; no more background compactions + } else if (!bg_error_.ok()) { + // Already got an error; no more changes } else if (imm_ == NULL && manual_compaction_ == NULL && !versions_->NeedsCompaction()) { @@ -619,30 +636,12 @@ void DBImpl::BGWork(void* db) { void DBImpl::BackgroundCall() { MutexLock l(&mutex_); assert(bg_compaction_scheduled_); - if (!shutting_down_.Acquire_Load()) { - Status s = BackgroundCompaction(); - if (s.ok()) { - // Success - consecutive_compaction_errors_ = 0; - } else if (shutting_down_.Acquire_Load()) { - // Error most likely due to shutdown; do not wait - } else { - // Wait a little bit before retrying background compaction in - // case this is an environmental problem and we do not want to - // chew up resources for failed compactions for the duration of - // the problem. - bg_cv_.SignalAll(); // In case a waiter can proceed despite the error - Log(options_.info_log, "Waiting after background compaction error: %s", - s.ToString().c_str()); - mutex_.Unlock(); - ++consecutive_compaction_errors_; - int seconds_to_sleep = 1; - for (int i = 0; i < 3 && i < consecutive_compaction_errors_ - 1; ++i) { - seconds_to_sleep *= 2; - } - env_->SleepForMicroseconds(seconds_to_sleep * 1000000); - mutex_.Lock(); - } + if (shutting_down_.Acquire_Load()) { + // No more background work when shutting down. + } else if (!bg_error_.ok()) { + // No more background work after a background error. + } else { + BackgroundCompaction(); } bg_compaction_scheduled_ = false; @@ -653,11 +652,12 @@ void DBImpl::BackgroundCall() { bg_cv_.SignalAll(); } -Status DBImpl::BackgroundCompaction() { +void DBImpl::BackgroundCompaction() { mutex_.AssertHeld(); if (imm_ != NULL) { - return CompactMemTable(); + CompactMemTable(); + return; } Compaction* c; @@ -691,6 +691,9 @@ Status DBImpl::BackgroundCompaction() { c->edit()->AddFile(c->level() + 1, f->number, f->file_size, f->smallest, f->largest); status = versions_->LogAndApply(c->edit(), &mutex_); + if (!status.ok()) { + RecordBackgroundError(status); + } VersionSet::LevelSummaryStorage tmp; Log(options_.info_log, "Moved #%lld to level-%d %lld bytes %s: %s\n", static_cast<unsigned long long>(f->number), @@ -701,6 +704,9 @@ Status DBImpl::BackgroundCompaction() { } else { CompactionState* compact = new CompactionState(c); status = DoCompactionWork(compact); + if (!status.ok()) { + RecordBackgroundError(status); + } CleanupCompaction(compact); c->ReleaseInputs(); DeleteObsoleteFiles(); @@ -714,9 +720,6 @@ Status DBImpl::BackgroundCompaction() { } else { Log(options_.info_log, "Compaction error: %s", status.ToString().c_str()); - if (options_.paranoid_checks && bg_error_.ok()) { - bg_error_ = status; - } } if (is_manual) { @@ -732,7 +735,6 @@ Status DBImpl::BackgroundCompaction() { } manual_compaction_ = NULL; } - return status; } void DBImpl::CleanupCompaction(CompactionState* compact) { @@ -1002,6 +1004,9 @@ Status DBImpl::DoCompactionWork(CompactionState* compact) { if (status.ok()) { status = InstallCompactionResults(compact); } + if (!status.ok()) { + RecordBackgroundError(status); + } VersionSet::LevelSummaryStorage tmp; Log(options_.info_log, "compacted to: %s", versions_->LevelSummary(&tmp)); @@ -1185,13 +1190,23 @@ Status DBImpl::Write(const WriteOptions& options, WriteBatch* my_batch) { { mutex_.Unlock(); status = log_->AddRecord(WriteBatchInternal::Contents(updates)); + bool sync_error = false; if (status.ok() && options.sync) { status = logfile_->Sync(); + if (!status.ok()) { + sync_error = true; + } } if (status.ok()) { status = WriteBatchInternal::InsertInto(updates, mem_); } mutex_.Lock(); + if (sync_error) { + // The state of the log file is indeterminate: the log record we + // just added may or may not show up when the DB is re-opened. + // So we force the DB into a mode where all future writes fail. + RecordBackgroundError(status); + } } if (updates == tmp_batch_) tmp_batch_->Clear(); diff --git a/src/leveldb/db/db_impl.h b/src/leveldb/db/db_impl.h index 75fd30abe9..cfc998164a 100644 --- a/src/leveldb/db/db_impl.h +++ b/src/leveldb/db/db_impl.h @@ -87,8 +87,8 @@ class DBImpl : public DB { // Compact the in-memory write buffer to disk. Switches to a new // log-file/memtable and writes a new descriptor iff successful. - Status CompactMemTable() - EXCLUSIVE_LOCKS_REQUIRED(mutex_); + // Errors are recorded in bg_error_. + void CompactMemTable() EXCLUSIVE_LOCKS_REQUIRED(mutex_); Status RecoverLogFile(uint64_t log_number, VersionEdit* edit, @@ -102,10 +102,12 @@ class DBImpl : public DB { EXCLUSIVE_LOCKS_REQUIRED(mutex_); WriteBatch* BuildBatchGroup(Writer** last_writer); + void RecordBackgroundError(const Status& s); + void MaybeScheduleCompaction() EXCLUSIVE_LOCKS_REQUIRED(mutex_); static void BGWork(void* db); void BackgroundCall(); - Status BackgroundCompaction() EXCLUSIVE_LOCKS_REQUIRED(mutex_); + void BackgroundCompaction() EXCLUSIVE_LOCKS_REQUIRED(mutex_); void CleanupCompaction(CompactionState* compact) EXCLUSIVE_LOCKS_REQUIRED(mutex_); Status DoCompactionWork(CompactionState* compact) @@ -170,7 +172,6 @@ class DBImpl : public DB { // Have we encountered a background error in paranoid mode? Status bg_error_; - int consecutive_compaction_errors_; // Per level compaction stats. stats_[level] stores the stats for // compactions that produced data for the specified "level". diff --git a/src/leveldb/db/db_iter.cc b/src/leveldb/db/db_iter.cc index 071a54e3f4..3b2035e9e3 100644 --- a/src/leveldb/db/db_iter.cc +++ b/src/leveldb/db/db_iter.cc @@ -161,12 +161,13 @@ void DBIter::Next() { saved_key_.clear(); return; } + // saved_key_ already contains the key to skip past. + } else { + // Store in saved_key_ the current key so we skip it below. + SaveKey(ExtractUserKey(iter_->key()), &saved_key_); } - // Temporarily use saved_key_ as storage for key to skip. - std::string* skip = &saved_key_; - SaveKey(ExtractUserKey(iter_->key()), skip); - FindNextUserEntry(true, skip); + FindNextUserEntry(true, &saved_key_); } void DBIter::FindNextUserEntry(bool skipping, std::string* skip) { diff --git a/src/leveldb/db/db_test.cc b/src/leveldb/db/db_test.cc index 49aae04dbd..280b01c14b 100644 --- a/src/leveldb/db/db_test.cc +++ b/src/leveldb/db/db_test.cc @@ -57,8 +57,11 @@ void DelayMilliseconds(int millis) { // Special Env used to delay background operations class SpecialEnv : public EnvWrapper { public: - // sstable Sync() calls are blocked while this pointer is non-NULL. - port::AtomicPointer delay_sstable_sync_; + // sstable/log Sync() calls are blocked while this pointer is non-NULL. + port::AtomicPointer delay_data_sync_; + + // sstable/log Sync() calls return an error. + port::AtomicPointer data_sync_error_; // Simulate no-space errors while this pointer is non-NULL. port::AtomicPointer no_space_; @@ -75,11 +78,9 @@ class SpecialEnv : public EnvWrapper { bool count_random_reads_; AtomicCounter random_read_counter_; - AtomicCounter sleep_counter_; - AtomicCounter sleep_time_counter_; - explicit SpecialEnv(Env* base) : EnvWrapper(base) { - delay_sstable_sync_.Release_Store(NULL); + delay_data_sync_.Release_Store(NULL); + data_sync_error_.Release_Store(NULL); no_space_.Release_Store(NULL); non_writable_.Release_Store(NULL); count_random_reads_ = false; @@ -88,17 +89,17 @@ class SpecialEnv : public EnvWrapper { } Status NewWritableFile(const std::string& f, WritableFile** r) { - class SSTableFile : public WritableFile { + class DataFile : public WritableFile { private: SpecialEnv* env_; WritableFile* base_; public: - SSTableFile(SpecialEnv* env, WritableFile* base) + DataFile(SpecialEnv* env, WritableFile* base) : env_(env), base_(base) { } - ~SSTableFile() { delete base_; } + ~DataFile() { delete base_; } Status Append(const Slice& data) { if (env_->no_space_.Acquire_Load() != NULL) { // Drop writes on the floor @@ -110,7 +111,10 @@ class SpecialEnv : public EnvWrapper { Status Close() { return base_->Close(); } Status Flush() { return base_->Flush(); } Status Sync() { - while (env_->delay_sstable_sync_.Acquire_Load() != NULL) { + if (env_->data_sync_error_.Acquire_Load() != NULL) { + return Status::IOError("simulated data sync error"); + } + while (env_->delay_data_sync_.Acquire_Load() != NULL) { DelayMilliseconds(100); } return base_->Sync(); @@ -147,8 +151,9 @@ class SpecialEnv : public EnvWrapper { Status s = target()->NewWritableFile(f, r); if (s.ok()) { - if (strstr(f.c_str(), ".sst") != NULL) { - *r = new SSTableFile(this, *r); + if (strstr(f.c_str(), ".ldb") != NULL || + strstr(f.c_str(), ".log") != NULL) { + *r = new DataFile(this, *r); } else if (strstr(f.c_str(), "MANIFEST") != NULL) { *r = new ManifestFile(this, *r); } @@ -179,12 +184,6 @@ class SpecialEnv : public EnvWrapper { } return s; } - - virtual void SleepForMicroseconds(int micros) { - sleep_counter_.Increment(); - sleep_time_counter_.IncrementBy(micros); - } - }; class DBTest { @@ -322,7 +321,7 @@ class DBTest { } // Check reverse iteration results are the reverse of forward results - int matched = 0; + size_t matched = 0; for (iter->SeekToLast(); iter->Valid(); iter->Prev()) { ASSERT_LT(matched, forward.size()); ASSERT_EQ(IterStatus(iter), forward[forward.size() - matched - 1]); @@ -484,6 +483,24 @@ class DBTest { } return false; } + + // Returns number of files renamed. + int RenameLDBToSST() { + std::vector<std::string> filenames; + ASSERT_OK(env_->GetChildren(dbname_, &filenames)); + uint64_t number; + FileType type; + int files_renamed = 0; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type) && type == kTableFile) { + const std::string from = TableFileName(dbname_, number); + const std::string to = SSTTableFileName(dbname_, number); + ASSERT_OK(env_->RenameFile(from, to)); + files_renamed++; + } + } + return files_renamed; + } }; TEST(DBTest, Empty) { @@ -525,11 +542,11 @@ TEST(DBTest, GetFromImmutableLayer) { ASSERT_OK(Put("foo", "v1")); ASSERT_EQ("v1", Get("foo")); - env_->delay_sstable_sync_.Release_Store(env_); // Block sync calls + env_->delay_data_sync_.Release_Store(env_); // Block sync calls Put("k1", std::string(100000, 'x')); // Fill memtable Put("k2", std::string(100000, 'y')); // Trigger compaction ASSERT_EQ("v1", Get("foo")); - env_->delay_sstable_sync_.Release_Store(NULL); // Release sync calls + env_->delay_data_sync_.Release_Store(NULL); // Release sync calls } while (ChangeOptions()); } @@ -1516,41 +1533,13 @@ TEST(DBTest, NoSpace) { Compact("a", "z"); const int num_files = CountFiles(); env_->no_space_.Release_Store(env_); // Force out-of-space errors - env_->sleep_counter_.Reset(); - for (int i = 0; i < 5; i++) { + for (int i = 0; i < 10; i++) { for (int level = 0; level < config::kNumLevels-1; level++) { dbfull()->TEST_CompactRange(level, NULL, NULL); } } env_->no_space_.Release_Store(NULL); ASSERT_LT(CountFiles(), num_files + 3); - - // Check that compaction attempts slept after errors - ASSERT_GE(env_->sleep_counter_.Read(), 5); -} - -TEST(DBTest, ExponentialBackoff) { - Options options = CurrentOptions(); - options.env = env_; - Reopen(&options); - - ASSERT_OK(Put("foo", "v1")); - ASSERT_EQ("v1", Get("foo")); - Compact("a", "z"); - env_->non_writable_.Release_Store(env_); // Force errors for new files - env_->sleep_counter_.Reset(); - env_->sleep_time_counter_.Reset(); - for (int i = 0; i < 5; i++) { - dbfull()->TEST_CompactRange(2, NULL, NULL); - } - env_->non_writable_.Release_Store(NULL); - - // Wait for compaction to finish - DelayMilliseconds(1000); - - ASSERT_GE(env_->sleep_counter_.Read(), 5); - ASSERT_LT(env_->sleep_counter_.Read(), 10); - ASSERT_GE(env_->sleep_time_counter_.Read(), 10e6); } TEST(DBTest, NonWritableFileSystem) { @@ -1573,6 +1562,37 @@ TEST(DBTest, NonWritableFileSystem) { env_->non_writable_.Release_Store(NULL); } +TEST(DBTest, WriteSyncError) { + // Check that log sync errors cause the DB to disallow future writes. + + // (a) Cause log sync calls to fail + Options options = CurrentOptions(); + options.env = env_; + Reopen(&options); + env_->data_sync_error_.Release_Store(env_); + + // (b) Normal write should succeed + WriteOptions w; + ASSERT_OK(db_->Put(w, "k1", "v1")); + ASSERT_EQ("v1", Get("k1")); + + // (c) Do a sync write; should fail + w.sync = true; + ASSERT_TRUE(!db_->Put(w, "k2", "v2").ok()); + ASSERT_EQ("v1", Get("k1")); + ASSERT_EQ("NOT_FOUND", Get("k2")); + + // (d) make sync behave normally + env_->data_sync_error_.Release_Store(NULL); + + // (e) Do a non-sync write; should fail + w.sync = false; + ASSERT_TRUE(!db_->Put(w, "k3", "v3").ok()); + ASSERT_EQ("v1", Get("k1")); + ASSERT_EQ("NOT_FOUND", Get("k2")); + ASSERT_EQ("NOT_FOUND", Get("k3")); +} + TEST(DBTest, ManifestWriteError) { // Test for the following problem: // (a) Compaction produces file F @@ -1632,6 +1652,22 @@ TEST(DBTest, MissingSSTFile) { << s.ToString(); } +TEST(DBTest, StillReadSST) { + ASSERT_OK(Put("foo", "bar")); + ASSERT_EQ("bar", Get("foo")); + + // Dump the memtable to disk. + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("bar", Get("foo")); + Close(); + ASSERT_GT(RenameLDBToSST(), 0); + Options options = CurrentOptions(); + options.paranoid_checks = true; + Status s = TryReopen(&options); + ASSERT_TRUE(s.ok()); + ASSERT_EQ("bar", Get("foo")); +} + TEST(DBTest, FilesDeletedAfterCompaction) { ASSERT_OK(Put("foo", "v2")); Compact("a", "z"); @@ -1663,7 +1699,7 @@ TEST(DBTest, BloomFilter) { dbfull()->TEST_CompactMemTable(); // Prevent auto compactions triggered by seeks - env_->delay_sstable_sync_.Release_Store(env_); + env_->delay_data_sync_.Release_Store(env_); // Lookup present keys. Should rarely read from small sstable. env_->random_read_counter_.Reset(); @@ -1684,7 +1720,7 @@ TEST(DBTest, BloomFilter) { fprintf(stderr, "%d missing => %d reads\n", N, reads); ASSERT_LE(reads, 3*N/100); - env_->delay_sstable_sync_.Release_Store(NULL); + env_->delay_data_sync_.Release_Store(NULL); Close(); delete options.block_cache; delete options.filter_policy; @@ -1744,7 +1780,7 @@ static void MTThreadBody(void* arg) { ASSERT_EQ(k, key); ASSERT_GE(w, 0); ASSERT_LT(w, kNumThreads); - ASSERT_LE(c, reinterpret_cast<uintptr_t>( + ASSERT_LE(static_cast<uintptr_t>(c), reinterpret_cast<uintptr_t>( t->state->counter[w].Acquire_Load())); } } diff --git a/src/leveldb/db/filename.cc b/src/leveldb/db/filename.cc index 3c4d49f64e..da32946d99 100644 --- a/src/leveldb/db/filename.cc +++ b/src/leveldb/db/filename.cc @@ -31,6 +31,11 @@ std::string LogFileName(const std::string& name, uint64_t number) { std::string TableFileName(const std::string& name, uint64_t number) { assert(number > 0); + return MakeFileName(name, number, "ldb"); +} + +std::string SSTTableFileName(const std::string& name, uint64_t number) { + assert(number > 0); return MakeFileName(name, number, "sst"); } @@ -71,7 +76,7 @@ std::string OldInfoLogFileName(const std::string& dbname) { // dbname/LOG // dbname/LOG.old // dbname/MANIFEST-[0-9]+ -// dbname/[0-9]+.(log|sst) +// dbname/[0-9]+.(log|sst|ldb) bool ParseFileName(const std::string& fname, uint64_t* number, FileType* type) { @@ -106,7 +111,7 @@ bool ParseFileName(const std::string& fname, Slice suffix = rest; if (suffix == Slice(".log")) { *type = kLogFile; - } else if (suffix == Slice(".sst")) { + } else if (suffix == Slice(".sst") || suffix == Slice(".ldb")) { *type = kTableFile; } else if (suffix == Slice(".dbtmp")) { *type = kTempFile; diff --git a/src/leveldb/db/filename.h b/src/leveldb/db/filename.h index d5d09b1146..87a752605d 100644 --- a/src/leveldb/db/filename.h +++ b/src/leveldb/db/filename.h @@ -37,6 +37,11 @@ extern std::string LogFileName(const std::string& dbname, uint64_t number); // "dbname". extern std::string TableFileName(const std::string& dbname, uint64_t number); +// Return the legacy file name for an sstable with the specified number +// in the db named by "dbname". The result will be prefixed with +// "dbname". +extern std::string SSTTableFileName(const std::string& dbname, uint64_t number); + // Return the name of the descriptor file for the db named by // "dbname" and the specified incarnation number. The result will be // prefixed with "dbname". diff --git a/src/leveldb/db/filename_test.cc b/src/leveldb/db/filename_test.cc index 5a26da4728..a32556deaf 100644 --- a/src/leveldb/db/filename_test.cc +++ b/src/leveldb/db/filename_test.cc @@ -27,6 +27,7 @@ TEST(FileNameTest, Parse) { { "100.log", 100, kLogFile }, { "0.log", 0, kLogFile }, { "0.sst", 0, kTableFile }, + { "0.ldb", 0, kTableFile }, { "CURRENT", 0, kCurrentFile }, { "LOCK", 0, kDBLockFile }, { "MANIFEST-2", 2, kDescriptorFile }, diff --git a/src/leveldb/db/repair.cc b/src/leveldb/db/repair.cc index 022d52f3de..96c9b37af1 100644 --- a/src/leveldb/db/repair.cc +++ b/src/leveldb/db/repair.cc @@ -244,60 +244,133 @@ class Repairer { void ExtractMetaData() { std::vector<TableInfo> kept; for (size_t i = 0; i < table_numbers_.size(); i++) { - TableInfo t; - t.meta.number = table_numbers_[i]; - Status status = ScanTable(&t); - if (!status.ok()) { - std::string fname = TableFileName(dbname_, table_numbers_[i]); - Log(options_.info_log, "Table #%llu: ignoring %s", - (unsigned long long) table_numbers_[i], - status.ToString().c_str()); - ArchiveFile(fname); - } else { - tables_.push_back(t); - } + ScanTable(table_numbers_[i]); } } - Status ScanTable(TableInfo* t) { - std::string fname = TableFileName(dbname_, t->meta.number); + Iterator* NewTableIterator(const FileMetaData& meta) { + // Same as compaction iterators: if paranoid_checks are on, turn + // on checksum verification. + ReadOptions r; + r.verify_checksums = options_.paranoid_checks; + return table_cache_->NewIterator(r, meta.number, meta.file_size); + } + + void ScanTable(uint64_t number) { + TableInfo t; + t.meta.number = number; + std::string fname = TableFileName(dbname_, number); + Status status = env_->GetFileSize(fname, &t.meta.file_size); + if (!status.ok()) { + // Try alternate file name. + fname = SSTTableFileName(dbname_, number); + Status s2 = env_->GetFileSize(fname, &t.meta.file_size); + if (s2.ok()) { + status = Status::OK(); + } + } + if (!status.ok()) { + ArchiveFile(TableFileName(dbname_, number)); + ArchiveFile(SSTTableFileName(dbname_, number)); + Log(options_.info_log, "Table #%llu: dropped: %s", + (unsigned long long) t.meta.number, + status.ToString().c_str()); + return; + } + + // Extract metadata by scanning through table. int counter = 0; - Status status = env_->GetFileSize(fname, &t->meta.file_size); - if (status.ok()) { - Iterator* iter = table_cache_->NewIterator( - ReadOptions(), t->meta.number, t->meta.file_size); - bool empty = true; - ParsedInternalKey parsed; - t->max_sequence = 0; - for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { - Slice key = iter->key(); - if (!ParseInternalKey(key, &parsed)) { - Log(options_.info_log, "Table #%llu: unparsable key %s", - (unsigned long long) t->meta.number, - EscapeString(key).c_str()); - continue; - } + Iterator* iter = NewTableIterator(t.meta); + bool empty = true; + ParsedInternalKey parsed; + t.max_sequence = 0; + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + Slice key = iter->key(); + if (!ParseInternalKey(key, &parsed)) { + Log(options_.info_log, "Table #%llu: unparsable key %s", + (unsigned long long) t.meta.number, + EscapeString(key).c_str()); + continue; + } - counter++; - if (empty) { - empty = false; - t->meta.smallest.DecodeFrom(key); - } - t->meta.largest.DecodeFrom(key); - if (parsed.sequence > t->max_sequence) { - t->max_sequence = parsed.sequence; - } + counter++; + if (empty) { + empty = false; + t.meta.smallest.DecodeFrom(key); } - if (!iter->status().ok()) { - status = iter->status(); + t.meta.largest.DecodeFrom(key); + if (parsed.sequence > t.max_sequence) { + t.max_sequence = parsed.sequence; } - delete iter; } + if (!iter->status().ok()) { + status = iter->status(); + } + delete iter; Log(options_.info_log, "Table #%llu: %d entries %s", - (unsigned long long) t->meta.number, + (unsigned long long) t.meta.number, counter, status.ToString().c_str()); - return status; + + if (status.ok()) { + tables_.push_back(t); + } else { + RepairTable(fname, t); // RepairTable archives input file. + } + } + + void RepairTable(const std::string& src, TableInfo t) { + // We will copy src contents to a new table and then rename the + // new table over the source. + + // Create builder. + std::string copy = TableFileName(dbname_, next_file_number_++); + WritableFile* file; + Status s = env_->NewWritableFile(copy, &file); + if (!s.ok()) { + return; + } + TableBuilder* builder = new TableBuilder(options_, file); + + // Copy data. + Iterator* iter = NewTableIterator(t.meta); + int counter = 0; + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + builder->Add(iter->key(), iter->value()); + counter++; + } + delete iter; + + ArchiveFile(src); + if (counter == 0) { + builder->Abandon(); // Nothing to save + } else { + s = builder->Finish(); + if (s.ok()) { + t.meta.file_size = builder->FileSize(); + } + } + delete builder; + builder = NULL; + + if (s.ok()) { + s = file->Close(); + } + delete file; + file = NULL; + + if (counter > 0 && s.ok()) { + std::string orig = TableFileName(dbname_, t.meta.number); + s = env_->RenameFile(copy, orig); + if (s.ok()) { + Log(options_.info_log, "Table #%llu: %d entries repaired", + (unsigned long long) t.meta.number, counter); + tables_.push_back(t); + } + } + if (!s.ok()) { + env_->DeleteFile(copy); + } } Status WriteDescriptor() { diff --git a/src/leveldb/db/table_cache.cc b/src/leveldb/db/table_cache.cc index 497db27076..e3d82cd3ea 100644 --- a/src/leveldb/db/table_cache.cc +++ b/src/leveldb/db/table_cache.cc @@ -54,6 +54,12 @@ Status TableCache::FindTable(uint64_t file_number, uint64_t file_size, RandomAccessFile* file = NULL; Table* table = NULL; s = env_->NewRandomAccessFile(fname, &file); + if (!s.ok()) { + std::string old_fname = SSTTableFileName(dbname_, file_number); + if (env_->NewRandomAccessFile(old_fname, &file).ok()) { + s = Status::OK(); + } + } if (s.ok()) { s = Table::Open(*options_, file, file_size, &table); } diff --git a/src/leveldb/db/version_set.cc b/src/leveldb/db/version_set.cc index 66d73be71f..517edd3b18 100644 --- a/src/leveldb/db/version_set.cc +++ b/src/leveldb/db/version_set.cc @@ -876,12 +876,6 @@ Status VersionSet::LogAndApply(VersionEdit* edit, port::Mutex* mu) { } if (!s.ok()) { Log(options_->info_log, "MANIFEST write: %s\n", s.ToString().c_str()); - if (ManifestContains(record)) { - Log(options_->info_log, - "MANIFEST contains log record despite error; advancing to new " - "version to prevent mismatch between in-memory and logged state"); - s = Status::OK(); - } } } @@ -889,8 +883,6 @@ Status VersionSet::LogAndApply(VersionEdit* edit, port::Mutex* mu) { // new CURRENT file that points to it. if (s.ok() && !new_manifest_file.empty()) { s = SetCurrentFile(env_, dbname_, manifest_file_number_); - // No need to double-check MANIFEST in case of error since it - // will be discarded below. } mu->Lock(); @@ -1124,31 +1116,6 @@ const char* VersionSet::LevelSummary(LevelSummaryStorage* scratch) const { return scratch->buffer; } -// Return true iff the manifest contains the specified record. -bool VersionSet::ManifestContains(const std::string& record) const { - std::string fname = DescriptorFileName(dbname_, manifest_file_number_); - Log(options_->info_log, "ManifestContains: checking %s\n", fname.c_str()); - SequentialFile* file = NULL; - Status s = env_->NewSequentialFile(fname, &file); - if (!s.ok()) { - Log(options_->info_log, "ManifestContains: %s\n", s.ToString().c_str()); - return false; - } - log::Reader reader(file, NULL, true/*checksum*/, 0); - Slice r; - std::string scratch; - bool result = false; - while (reader.ReadRecord(&r, &scratch)) { - if (r == Slice(record)) { - result = true; - break; - } - } - delete file; - Log(options_->info_log, "ManifestContains: result = %d\n", result ? 1 : 0); - return result; -} - uint64_t VersionSet::ApproximateOffsetOf(Version* v, const InternalKey& ikey) { uint64_t result = 0; for (int level = 0; level < config::kNumLevels; level++) { diff --git a/src/leveldb/db/version_set.h b/src/leveldb/db/version_set.h index 20de0e2629..8dc14b8e01 100644 --- a/src/leveldb/db/version_set.h +++ b/src/leveldb/db/version_set.h @@ -292,8 +292,6 @@ class VersionSet { void AppendVersion(Version* v); - bool ManifestContains(const std::string& record) const; - Env* const env_; const std::string dbname_; const Options* const options_; diff --git a/src/leveldb/doc/impl.html b/src/leveldb/doc/impl.html index e870795d23..28817fe0da 100644 --- a/src/leveldb/doc/impl.html +++ b/src/leveldb/doc/impl.html @@ -11,7 +11,7 @@ The implementation of leveldb is similar in spirit to the representation of a single -<a href="http://labs.google.com/papers/bigtable.html"> +<a href="http://research.google.com/archive/bigtable.html"> Bigtable tablet (section 5.3)</a>. However the organization of the files that make up the representation is somewhat different and is explained below. diff --git a/src/leveldb/include/leveldb/db.h b/src/leveldb/include/leveldb/db.h index 57c00a5da0..5ffb29d526 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 = 13; +static const int kMinorVersion = 15; struct Options; struct ReadOptions; diff --git a/src/leveldb/include/leveldb/env.h b/src/leveldb/include/leveldb/env.h index fa32289f58..b2072d02c1 100644 --- a/src/leveldb/include/leveldb/env.h +++ b/src/leveldb/include/leveldb/env.h @@ -13,9 +13,9 @@ #ifndef STORAGE_LEVELDB_INCLUDE_ENV_H_ #define STORAGE_LEVELDB_INCLUDE_ENV_H_ -#include <cstdarg> #include <string> #include <vector> +#include <stdarg.h> #include <stdint.h> #include "leveldb/status.h" diff --git a/src/leveldb/issues/issue200_test.cc b/src/leveldb/issues/issue200_test.cc new file mode 100644 index 0000000000..1cec79f443 --- /dev/null +++ b/src/leveldb/issues/issue200_test.cc @@ -0,0 +1,59 @@ +// 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. + +// Test for issue 200: when iterator switches direction from backward +// to forward, the current key can be yielded unexpectedly if a new +// mutation has been added just before the current key. + +#include "leveldb/db.h" +#include "util/testharness.h" + +namespace leveldb { + +class Issue200 { }; + +TEST(Issue200, Test) { + // Get rid of any state from an old run. + std::string dbpath = test::TmpDir() + "/leveldb_issue200_test"; + DestroyDB(dbpath, Options()); + + DB *db; + Options options; + options.create_if_missing = true; + ASSERT_OK(DB::Open(options, dbpath, &db)); + + WriteOptions write_options; + ASSERT_OK(db->Put(write_options, "1", "b")); + ASSERT_OK(db->Put(write_options, "2", "c")); + ASSERT_OK(db->Put(write_options, "3", "d")); + ASSERT_OK(db->Put(write_options, "4", "e")); + ASSERT_OK(db->Put(write_options, "5", "f")); + + ReadOptions read_options; + Iterator *iter = db->NewIterator(read_options); + + // Add an element that should not be reflected in the iterator. + ASSERT_OK(db->Put(write_options, "25", "cd")); + + iter->Seek("5"); + ASSERT_EQ(iter->key().ToString(), "5"); + iter->Prev(); + ASSERT_EQ(iter->key().ToString(), "4"); + iter->Prev(); + ASSERT_EQ(iter->key().ToString(), "3"); + iter->Next(); + ASSERT_EQ(iter->key().ToString(), "4"); + iter->Next(); + ASSERT_EQ(iter->key().ToString(), "5"); + + delete iter; + delete db; + DestroyDB(dbpath, options); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/port/atomic_pointer.h b/src/leveldb/port/atomic_pointer.h index e17bf435ea..a9866b2302 100644 --- a/src/leveldb/port/atomic_pointer.h +++ b/src/leveldb/port/atomic_pointer.h @@ -50,6 +50,13 @@ namespace port { // http://msdn.microsoft.com/en-us/library/ms684208(v=vs.85).aspx #define LEVELDB_HAVE_MEMORY_BARRIER +// Mac OS +#elif defined(OS_MACOSX) +inline void MemoryBarrier() { + OSMemoryBarrier(); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + // Gcc on x86 #elif defined(ARCH_CPU_X86_FAMILY) && defined(__GNUC__) inline void MemoryBarrier() { @@ -68,13 +75,6 @@ inline void MemoryBarrier() { } #define LEVELDB_HAVE_MEMORY_BARRIER -// Mac OS -#elif defined(OS_MACOSX) -inline void MemoryBarrier() { - OSMemoryBarrier(); -} -#define LEVELDB_HAVE_MEMORY_BARRIER - // ARM Linux #elif defined(ARCH_CPU_ARM_FAMILY) && defined(__linux__) typedef void (*LinuxKernelMemoryBarrierFunc)(void); diff --git a/src/leveldb/table/filter_block_test.cc b/src/leveldb/table/filter_block_test.cc index 3a2a07cf53..8c4a4741f2 100644 --- a/src/leveldb/table/filter_block_test.cc +++ b/src/leveldb/table/filter_block_test.cc @@ -29,7 +29,7 @@ class TestHashFilter : public FilterPolicy { virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const { uint32_t h = Hash(key.data(), key.size(), 1); - for (int i = 0; i + 4 <= filter.size(); i += 4) { + for (size_t i = 0; i + 4 <= filter.size(); i += 4) { if (h == DecodeFixed32(filter.data() + i)) { return true; } diff --git a/src/leveldb/util/arena.cc b/src/leveldb/util/arena.cc index 9551d6a3a2..9367f71492 100644 --- a/src/leveldb/util/arena.cc +++ b/src/leveldb/util/arena.cc @@ -40,7 +40,7 @@ char* Arena::AllocateFallback(size_t bytes) { } char* Arena::AllocateAligned(size_t bytes) { - const int align = sizeof(void*); // We'll align to pointer size + const int align = (sizeof(void*) > 8) ? sizeof(void*) : 8; assert((align & (align-1)) == 0); // Pointer size should be a power of 2 size_t current_mod = reinterpret_cast<uintptr_t>(alloc_ptr_) & (align-1); size_t slop = (current_mod == 0 ? 0 : align - current_mod); diff --git a/src/leveldb/util/arena.h b/src/leveldb/util/arena.h index 8f7dde226c..73bbf1cb9b 100644 --- a/src/leveldb/util/arena.h +++ b/src/leveldb/util/arena.h @@ -5,9 +5,9 @@ #ifndef STORAGE_LEVELDB_UTIL_ARENA_H_ #define STORAGE_LEVELDB_UTIL_ARENA_H_ -#include <cstddef> #include <vector> #include <assert.h> +#include <stddef.h> #include <stdint.h> namespace leveldb { diff --git a/src/leveldb/util/arena_test.cc b/src/leveldb/util/arena_test.cc index 63d1778034..58e870ec44 100644 --- a/src/leveldb/util/arena_test.cc +++ b/src/leveldb/util/arena_test.cc @@ -40,7 +40,7 @@ TEST(ArenaTest, Simple) { r = arena.Allocate(s); } - for (int b = 0; b < s; b++) { + for (size_t b = 0; b < s; b++) { // Fill the "i"th allocation with a known bit pattern r[b] = i % 256; } @@ -51,10 +51,10 @@ TEST(ArenaTest, Simple) { ASSERT_LE(arena.MemoryUsage(), bytes * 1.10); } } - for (int i = 0; i < allocated.size(); i++) { + for (size_t i = 0; i < allocated.size(); i++) { size_t num_bytes = allocated[i].first; const char* p = allocated[i].second; - for (int b = 0; b < num_bytes; b++) { + for (size_t b = 0; b < num_bytes; b++) { // Check the "i"th allocation for the known bit pattern ASSERT_EQ(int(p[b]) & 0xff, i % 256); } diff --git a/src/leveldb/util/bloom_test.cc b/src/leveldb/util/bloom_test.cc index 0bf8e8d6eb..77fb1b3159 100644 --- a/src/leveldb/util/bloom_test.cc +++ b/src/leveldb/util/bloom_test.cc @@ -126,7 +126,8 @@ TEST(BloomTest, VaryingLengths) { } Build(); - ASSERT_LE(FilterSize(), (length * 10 / 8) + 40) << length; + ASSERT_LE(FilterSize(), static_cast<size_t>((length * 10 / 8) + 40)) + << length; // All added keys must match for (int i = 0; i < length; i++) { diff --git a/src/leveldb/util/coding_test.cc b/src/leveldb/util/coding_test.cc index fb5726e335..521541ea61 100644 --- a/src/leveldb/util/coding_test.cc +++ b/src/leveldb/util/coding_test.cc @@ -112,13 +112,13 @@ TEST(Coding, Varint64) { } std::string s; - for (int i = 0; i < values.size(); i++) { + for (size_t i = 0; i < values.size(); i++) { PutVarint64(&s, values[i]); } const char* p = s.data(); const char* limit = p + s.size(); - for (int i = 0; i < values.size(); i++) { + for (size_t i = 0; i < values.size(); i++) { ASSERT_TRUE(p < limit); uint64_t actual; const char* start = p; @@ -143,7 +143,7 @@ TEST(Coding, Varint32Truncation) { std::string s; PutVarint32(&s, large_value); uint32_t result; - for (int len = 0; len < s.size() - 1; len++) { + for (size_t len = 0; len < s.size() - 1; len++) { ASSERT_TRUE(GetVarint32Ptr(s.data(), s.data() + len, &result) == NULL); } ASSERT_TRUE(GetVarint32Ptr(s.data(), s.data() + s.size(), &result) != NULL); @@ -162,7 +162,7 @@ TEST(Coding, Varint64Truncation) { std::string s; PutVarint64(&s, large_value); uint64_t result; - for (int len = 0; len < s.size() - 1; len++) { + for (size_t len = 0; len < s.size() - 1; len++) { ASSERT_TRUE(GetVarint64Ptr(s.data(), s.data() + len, &result) == NULL); } ASSERT_TRUE(GetVarint64Ptr(s.data(), s.data() + s.size(), &result) != NULL); diff --git a/src/leveldb/util/env_posix.cc b/src/leveldb/util/env_posix.cc index 0f5dcfac5a..93eadb1a4f 100644 --- a/src/leveldb/util/env_posix.cc +++ b/src/leveldb/util/env_posix.cc @@ -176,147 +176,43 @@ class PosixMmapReadableFile: public RandomAccessFile { } }; -// We preallocate up to an extra megabyte and use memcpy to append new -// data to the file. This is safe since we either properly close the -// file before reading from it, or for log files, the reading code -// knows enough to skip zero suffixes. -class PosixMmapFile : public WritableFile { +class PosixWritableFile : public WritableFile { private: std::string filename_; - int fd_; - size_t page_size_; - size_t map_size_; // How much extra memory to map at a time - char* base_; // The mapped region - char* limit_; // Limit of the mapped region - char* dst_; // Where to write next (in range [base_,limit_]) - char* last_sync_; // Where have we synced up to - uint64_t file_offset_; // Offset of base_ in file - - // Have we done an munmap of unsynced data? - bool pending_sync_; - - // Roundup x to a multiple of y - static size_t Roundup(size_t x, size_t y) { - return ((x + y - 1) / y) * y; - } - - size_t TruncateToPageBoundary(size_t s) { - s -= (s & (page_size_ - 1)); - assert((s % page_size_) == 0); - return s; - } - - bool UnmapCurrentRegion() { - bool result = true; - if (base_ != NULL) { - if (last_sync_ < limit_) { - // Defer syncing this data until next Sync() call, if any - pending_sync_ = true; - } - if (munmap(base_, limit_ - base_) != 0) { - result = false; - } - file_offset_ += limit_ - base_; - base_ = NULL; - limit_ = NULL; - last_sync_ = NULL; - dst_ = NULL; - - // Increase the amount we map the next time, but capped at 1MB - if (map_size_ < (1<<20)) { - map_size_ *= 2; - } - } - return result; - } - - bool MapNewRegion() { - assert(base_ == NULL); - if (ftruncate(fd_, file_offset_ + map_size_) < 0) { - return false; - } - void* ptr = mmap(NULL, map_size_, PROT_READ | PROT_WRITE, MAP_SHARED, - fd_, file_offset_); - if (ptr == MAP_FAILED) { - return false; - } - base_ = reinterpret_cast<char*>(ptr); - limit_ = base_ + map_size_; - dst_ = base_; - last_sync_ = base_; - return true; - } + FILE* file_; public: - PosixMmapFile(const std::string& fname, int fd, size_t page_size) - : filename_(fname), - fd_(fd), - page_size_(page_size), - map_size_(Roundup(65536, page_size)), - base_(NULL), - limit_(NULL), - dst_(NULL), - last_sync_(NULL), - file_offset_(0), - pending_sync_(false) { - assert((page_size & (page_size - 1)) == 0); - } - - - ~PosixMmapFile() { - if (fd_ >= 0) { - PosixMmapFile::Close(); + PosixWritableFile(const std::string& fname, FILE* f) + : filename_(fname), file_(f) { } + + ~PosixWritableFile() { + if (file_ != NULL) { + // Ignoring any potential errors + fclose(file_); } } virtual Status Append(const Slice& data) { - const char* src = data.data(); - size_t left = data.size(); - while (left > 0) { - assert(base_ <= dst_); - assert(dst_ <= limit_); - size_t avail = limit_ - dst_; - if (avail == 0) { - if (!UnmapCurrentRegion() || - !MapNewRegion()) { - return IOError(filename_, errno); - } - } - - size_t n = (left <= avail) ? left : avail; - memcpy(dst_, src, n); - dst_ += n; - src += n; - left -= n; + size_t r = fwrite_unlocked(data.data(), 1, data.size(), file_); + if (r != data.size()) { + return IOError(filename_, errno); } return Status::OK(); } virtual Status Close() { - Status s; - size_t unused = limit_ - dst_; - if (!UnmapCurrentRegion()) { - s = IOError(filename_, errno); - } else if (unused > 0) { - // Trim the extra space at the end of the file - if (ftruncate(fd_, file_offset_ - unused) < 0) { - s = IOError(filename_, errno); - } - } - - if (close(fd_) < 0) { - if (s.ok()) { - s = IOError(filename_, errno); - } + Status result; + if (fclose(file_) != 0) { + result = IOError(filename_, errno); } - - fd_ = -1; - base_ = NULL; - limit_ = NULL; - return s; + file_ = NULL; + return result; } virtual Status Flush() { + if (fflush_unlocked(file_) != 0) { + return IOError(filename_, errno); + } return Status::OK(); } @@ -353,26 +249,10 @@ class PosixMmapFile : public WritableFile { if (!s.ok()) { return s; } - - if (pending_sync_) { - // Some unmapped data was not synced - pending_sync_ = false; - if (fdatasync(fd_) < 0) { - s = IOError(filename_, errno); - } + if (fflush_unlocked(file_) != 0 || + fdatasync(fileno(file_)) != 0) { + s = Status::IOError(filename_, strerror(errno)); } - - if (dst_ > last_sync_) { - // Find the beginnings of the pages that contain the first and last - // bytes to be synced. - size_t p1 = TruncateToPageBoundary(last_sync_ - base_); - size_t p2 = TruncateToPageBoundary(dst_ - base_ - 1); - last_sync_ = dst_; - if (msync(base_ + p1, p2 - p1 + page_size_, MS_SYNC) < 0) { - s = IOError(filename_, errno); - } - } - return s; } }; @@ -463,12 +343,12 @@ class PosixEnv : public Env { virtual Status NewWritableFile(const std::string& fname, WritableFile** result) { Status s; - const int fd = open(fname.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0644); - if (fd < 0) { + FILE* f = fopen(fname.c_str(), "w"); + if (f == NULL) { *result = NULL; s = IOError(fname, errno); } else { - *result = new PosixMmapFile(fname, fd, page_size_); + *result = new PosixWritableFile(fname, f); } return s; } @@ -631,7 +511,6 @@ class PosixEnv : public Env { return NULL; } - size_t page_size_; pthread_mutex_t mu_; pthread_cond_t bgsignal_; pthread_t bgthread_; @@ -646,8 +525,7 @@ class PosixEnv : public Env { MmapLimiter mmap_limit_; }; -PosixEnv::PosixEnv() : page_size_(getpagesize()), - started_bgthread_(false) { +PosixEnv::PosixEnv() : started_bgthread_(false) { PthreadCall("mutex_init", pthread_mutex_init(&mu_, NULL)); PthreadCall("cvar_init", pthread_cond_init(&bgsignal_, NULL)); } diff --git a/src/leveldb/util/testharness.cc b/src/leveldb/util/testharness.cc index eb1bdd554a..402fab34d7 100644 --- a/src/leveldb/util/testharness.cc +++ b/src/leveldb/util/testharness.cc @@ -38,7 +38,7 @@ int RunAllTests() { int num = 0; if (tests != NULL) { - for (int i = 0; i < tests->size(); i++) { + for (size_t i = 0; i < tests->size(); i++) { const Test& t = (*tests)[i]; if (matcher != NULL) { std::string name = t.base; diff --git a/src/leveldb/util/testutil.cc b/src/leveldb/util/testutil.cc index 538d09516d..bee56bf75f 100644 --- a/src/leveldb/util/testutil.cc +++ b/src/leveldb/util/testutil.cc @@ -32,7 +32,7 @@ std::string RandomKey(Random* rnd, int len) { extern Slice CompressibleString(Random* rnd, double compressed_fraction, - int len, std::string* dst) { + size_t len, std::string* dst) { int raw = static_cast<int>(len * compressed_fraction); if (raw < 1) raw = 1; std::string raw_data; diff --git a/src/leveldb/util/testutil.h b/src/leveldb/util/testutil.h index 824e655bd2..adad3fc1ea 100644 --- a/src/leveldb/util/testutil.h +++ b/src/leveldb/util/testutil.h @@ -24,7 +24,7 @@ extern std::string RandomKey(Random* rnd, int len); // "N*compressed_fraction" bytes and return a Slice that references // the generated data. extern Slice CompressibleString(Random* rnd, double compressed_fraction, - int len, std::string* dst); + size_t len, std::string* dst); // A wrapper that allows injection of errors. class ErrorEnv : public EnvWrapper { diff --git a/src/m4/ax_boost_base.m4 b/src/m4/ax_boost_base.m4 index 57d14fe48d..c675f12d84 100644 --- a/src/m4/ax_boost_base.m4 +++ b/src/m4/ax_boost_base.m4 @@ -33,7 +33,7 @@ # and this notice are preserved. This file is offered as-is, without any # warranty. -#serial 21 +#serial 22 AC_DEFUN([AX_BOOST_BASE], [ @@ -97,6 +97,12 @@ if test "x$want_boost" = "xyes"; then ;; esac + dnl allow for real multi-arch paths e.g. /usr/lib/x86_64-linux-gnu. Give + dnl them priority over the other paths since, if libs are found there, they + dnl are almost assuredly the ones desired. + AC_REQUIRE([AC_CANONICAL_HOST]) + libsubdirs="lib/${host_cpu}-${host_os} $libsubdirs" + dnl first we check the system location for boost libraries dnl this location ist chosen if boost libraries are installed with the --layout=system option dnl or if you install boost with RPM diff --git a/src/main.cpp b/src/main.cpp index 25201c7367..e3f9fdd843 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -67,9 +67,6 @@ CScript COINBASE_FLAGS; const string strMessageMagic = "Bitcoin Signed Message:\n"; -// Settings -int64_t nTransactionFee = 0; - // Internal stuff namespace { struct CBlockIndexWorkComparator @@ -153,17 +150,66 @@ void SyncWithWallets(const uint256 &hash, const CTransaction &tx, const CBlock * // Registration of network node signals. // -int static GetHeight() +namespace { +// Maintain validation-specific state about nodes, protected by cs_main, instead +// by CNode's own locks. This simplifies asynchronous operation, where +// processing of incoming data is done after the ProcessMessage call returns, +// and we're no longer holding the node's locks. +struct CNodeState { + int nMisbehavior; + bool fShouldBan; + std::string name; + + CNodeState() { + nMisbehavior = 0; + fShouldBan = false; + } +}; + +map<NodeId, CNodeState> mapNodeState; + +// Requires cs_main. +CNodeState *State(NodeId pnode) { + map<NodeId, CNodeState>::iterator it = mapNodeState.find(pnode); + if (it == mapNodeState.end()) + return NULL; + return &it->second; +} + +int GetHeight() { LOCK(cs_main); return chainActive.Height(); } +void InitializeNode(NodeId nodeid, const CNode *pnode) { + LOCK(cs_main); + CNodeState &state = mapNodeState.insert(std::make_pair(nodeid, CNodeState())).first->second; + state.name = pnode->addrName; +} + +void FinalizeNode(NodeId nodeid) { + LOCK(cs_main); + mapNodeState.erase(nodeid); +} +} + +bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { + LOCK(cs_main); + CNodeState *state = State(nodeid); + if (state == NULL) + return false; + stats.nMisbehavior = state->nMisbehavior; + return true; +} + void RegisterNodeSignals(CNodeSignals& nodeSignals) { nodeSignals.GetHeight.connect(&GetHeight); nodeSignals.ProcessMessages.connect(&ProcessMessages); nodeSignals.SendMessages.connect(&SendMessages); + nodeSignals.InitializeNode.connect(&InitializeNode); + nodeSignals.FinalizeNode.connect(&FinalizeNode); } void UnregisterNodeSignals(CNodeSignals& nodeSignals) @@ -171,6 +217,8 @@ void UnregisterNodeSignals(CNodeSignals& nodeSignals) nodeSignals.GetHeight.disconnect(&GetHeight); nodeSignals.ProcessMessages.disconnect(&ProcessMessages); nodeSignals.SendMessages.disconnect(&SendMessages); + nodeSignals.InitializeNode.disconnect(&InitializeNode); + nodeSignals.FinalizeNode.disconnect(&FinalizeNode); } ////////////////////////////////////////////////////////////////////////////// @@ -2915,6 +2963,23 @@ bool static AlreadyHave(const CInv& inv) } +void Misbehaving(NodeId pnode, int howmuch) +{ + if (howmuch == 0) + return; + + CNodeState *state = State(pnode); + if (state == NULL) + return; + + state->nMisbehavior += howmuch; + if (state->nMisbehavior >= GetArg("-banscore", 100)) + { + LogPrintf("Misbehaving: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", state->name.c_str(), state->nMisbehavior-howmuch, state->nMisbehavior); + state->fShouldBan = true; + } else + LogPrintf("Misbehaving: %s (%d -> %d)\n", state->name.c_str(), state->nMisbehavior-howmuch, state->nMisbehavior); +} void static ProcessGetData(CNode* pfrom) { @@ -3048,7 +3113,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (pfrom->nVersion != 0) { pfrom->PushMessage("reject", strCommand, REJECT_DUPLICATE, string("Duplicate version message")); - pfrom->Misbehaving(1); + Misbehaving(pfrom->GetId(), 1); return false; } @@ -3153,7 +3218,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) else if (pfrom->nVersion == 0) { // Must have a version message before anything else - pfrom->Misbehaving(1); + Misbehaving(pfrom->GetId(), 1); return false; } @@ -3174,7 +3239,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) return true; if (vAddr.size() > 1000) { - pfrom->Misbehaving(20); + Misbehaving(pfrom->GetId(), 20); return error("message addr size() = %"PRIszu"", vAddr.size()); } @@ -3237,7 +3302,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vRecv >> vInv; if (vInv.size() > MAX_INV_SZ) { - pfrom->Misbehaving(20); + Misbehaving(pfrom->GetId(), 20); return error("message inv size() = %"PRIszu"", vInv.size()); } @@ -3288,7 +3353,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vRecv >> vInv; if (vInv.size() > MAX_INV_SZ) { - pfrom->Misbehaving(20); + Misbehaving(pfrom->GetId(), 20); return error("message getdata size() = %"PRIszu"", vInv.size()); } @@ -3461,7 +3526,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), state.GetRejectReason(), inv.hash); if (nDoS > 0) - pfrom->Misbehaving(nDoS); + Misbehaving(pfrom->GetId(), nDoS); } } @@ -3488,7 +3553,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), state.GetRejectReason(), inv.hash); if (nDoS > 0) - pfrom->Misbehaving(nDoS); + Misbehaving(pfrom->GetId(), nDoS); } } @@ -3631,7 +3696,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // This isn't a Misbehaving(100) (immediate ban) because the // peer might be an older or different implementation with // a different signature key, etc. - pfrom->Misbehaving(10); + Misbehaving(pfrom->GetId(), 10); } } } @@ -3644,7 +3709,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (!filter.IsWithinSizeConstraints()) // There is no excuse for sending a too-large filter - pfrom->Misbehaving(100); + Misbehaving(pfrom->GetId(), 100); else { LOCK(pfrom->cs_filter); @@ -3665,13 +3730,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // and thus, the maximum size any matched object can have) in a filteradd message if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) { - pfrom->Misbehaving(100); + Misbehaving(pfrom->GetId(), 100); } else { LOCK(pfrom->cs_filter); if (pfrom->pfilter) pfrom->pfilter->insert(vData); else - pfrom->Misbehaving(100); + Misbehaving(pfrom->GetId(), 100); } } @@ -3936,6 +4001,16 @@ bool SendMessages(CNode* pto, bool fSendTrickle) if (!lockMain) return true; + if (State(pto->GetId())->fShouldBan) { + if (pto->addr.IsLocal()) + LogPrintf("Warning: not banning local node %s!\n", pto->addr.ToString().c_str()); + else { + pto->fDisconnect = true; + CNode::Ban(pto->addr); + } + State(pto->GetId())->fShouldBan = false; + } + // Start block sync if (pto->fStartSync && !fImporting && !fReindex) { pto->fStartSync = false; diff --git a/src/main.h b/src/main.h index c4e1839443..f3f9acb639 100644 --- a/src/main.h +++ b/src/main.h @@ -35,10 +35,12 @@ class CInv; /** The maximum allowed size for a serialized block, in bytes (network rule) */ static const unsigned int MAX_BLOCK_SIZE = 1000000; -/** The maximum size for mined blocks */ -static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2; +/** Default for -blockmaxsize, maximum size for mined blocks **/ +static const unsigned int DEFAULT_BLOCK_MAX_SIZE = 750000; +/** Default for -blockprioritysize, maximum space for zero/low-fee transactions **/ +static const unsigned int DEFAULT_BLOCK_PRIORITY_SIZE = 50000; /** The maximum size for transactions we're willing to relay/mine */ -static const unsigned int MAX_STANDARD_TX_SIZE = MAX_BLOCK_SIZE_GEN/5; +static const unsigned int MAX_STANDARD_TX_SIZE = 100000; /** The maximum allowed number of signature check operations in a block (network rule) */ static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50; /** The maximum number of orphan transactions kept in memory */ @@ -55,8 +57,6 @@ static const int COINBASE_MATURITY = 100; static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC /** Maximum number of script-checking threads allowed */ static const int MAX_SCRIPTCHECK_THREADS = 16; -/** Default amount of block size reserved for high-priority transactions (in bytes) */ -static const int DEFAULT_BLOCK_PRIORITY_SIZE = 27000; #ifdef USE_UPNP static const int fHaveUPnP = true; #else @@ -96,9 +96,6 @@ extern bool fTxIndex; extern unsigned int nCoinCacheSize; extern bool fHaveGUI; -// Settings -extern int64_t nTransactionFee; - // Minimum disk space required - used in CheckDiskSpace() static const uint64_t nMinDiskSpace = 52428800; @@ -110,6 +107,7 @@ class CTxUndo; class CScriptCheck; class CValidationState; class CWalletInterface; +struct CNodeStateStats; struct CBlockTemplate; @@ -182,6 +180,8 @@ CBlockIndex * InsertBlockIndex(uint256 hash); bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); /** Abort with a message */ bool AbortNode(const std::string &msg); +/** Get statistics from node state */ +bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats); /** (try to) add transaction to memory pool **/ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, @@ -194,6 +194,10 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa +struct CNodeStateStats { + int nMisbehavior; +}; + struct CDiskBlockPos { int nFile; diff --git a/src/miner.cpp b/src/miner.cpp index ecc40ac708..21a9fa256b 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -8,11 +8,9 @@ #include "core.h" #include "main.h" #include "net.h" +#ifdef ENABLE_WALLET #include "wallet.h" - -double dHashesPerSec = 0.0; -int64_t nHPSTimerStart = 0; - +#endif ////////////////////////////////////////////////////////////////////////////// // // BitcoinMiner @@ -54,41 +52,6 @@ void SHA256Transform(void* pstate, void* pinput, const void* pinit) ((uint32_t*)pstate)[i] = ctx.h[i]; } -// -// ScanHash scans nonces looking for a hash with at least some zero bits. -// It operates on big endian data. Caller does the byte reversing. -// All input buffers are 16-byte aligned. nNonce is usually preserved -// between calls, but periodically or if nNonce is 0xffff0000 or above, -// the block is rebuilt and nNonce starts over at zero. -// -unsigned int static ScanHash_CryptoPP(char* pmidstate, char* pdata, char* phash1, char* phash, unsigned int& nHashesDone) -{ - unsigned int& nNonce = *(unsigned int*)(pdata + 12); - for (;;) - { - // Crypto++ SHA256 - // Hash pdata using pmidstate as the starting state into - // pre-formatted buffer phash1, then hash phash1 into phash - nNonce++; - SHA256Transform(phash1, pdata, pmidstate); - SHA256Transform(phash, phash1, pSHA256InitState); - - // Return the nonce if the hash has at least some zero bits, - // caller will check if it has enough to reach the target - if (((unsigned short*)phash)[14] == 0) - return nNonce; - - // If nothing found after trying for a while, return -1 - if ((nNonce & 0xffff) == 0) - { - nHashesDone = 0xffff+1; - return (unsigned int) -1; - } - if ((nNonce & 0xfff) == 0) - boost::this_thread::interruption_point(); - } -} - // Some explaining would be appreciated class COrphan { @@ -162,7 +125,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) pblocktemplate->vTxSigOps.push_back(-1); // updated at end // Largest block you're willing to create: - unsigned int nBlockMaxSize = GetArg("-blockmaxsize", MAX_BLOCK_SIZE_GEN/2); + unsigned int nBlockMaxSize = GetArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE); // Limit to betweeen 1K and MAX_BLOCK_SIZE-1K for sanity: nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE-1000), nBlockMaxSize)); @@ -381,16 +344,6 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) return pblocktemplate.release(); } -CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey) -{ - CPubKey pubkey; - if (!reservekey.GetReservedKey(pubkey)) - return NULL; - - CScript scriptPubKey = CScript() << pubkey << OP_CHECKSIG; - return CreateNewBlock(scriptPubKey); -} - void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce) { // Update nExtraNonce @@ -454,6 +407,58 @@ void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash memcpy(phash1, &tmp.hash1, 64); } +#ifdef ENABLE_WALLET +////////////////////////////////////////////////////////////////////////////// +// +// Internal miner +// +double dHashesPerSec = 0.0; +int64_t nHPSTimerStart = 0; + +// +// ScanHash scans nonces looking for a hash with at least some zero bits. +// It operates on big endian data. Caller does the byte reversing. +// All input buffers are 16-byte aligned. nNonce is usually preserved +// between calls, but periodically or if nNonce is 0xffff0000 or above, +// the block is rebuilt and nNonce starts over at zero. +// +unsigned int static ScanHash_CryptoPP(char* pmidstate, char* pdata, char* phash1, char* phash, unsigned int& nHashesDone) +{ + unsigned int& nNonce = *(unsigned int*)(pdata + 12); + for (;;) + { + // Crypto++ SHA256 + // Hash pdata using pmidstate as the starting state into + // pre-formatted buffer phash1, then hash phash1 into phash + nNonce++; + SHA256Transform(phash1, pdata, pmidstate); + SHA256Transform(phash, phash1, pSHA256InitState); + + // Return the nonce if the hash has at least some zero bits, + // caller will check if it has enough to reach the target + if (((unsigned short*)phash)[14] == 0) + return nNonce; + + // If nothing found after trying for a while, return -1 + if ((nNonce & 0xffff) == 0) + { + nHashesDone = 0xffff+1; + return (unsigned int) -1; + } + if ((nNonce & 0xfff) == 0) + boost::this_thread::interruption_point(); + } +} + +CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey) +{ + CPubKey pubkey; + if (!reservekey.GetReservedKey(pubkey)) + return NULL; + + CScript scriptPubKey = CScript() << pubkey << OP_CHECKSIG; + return CreateNewBlock(scriptPubKey); +} bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) { @@ -665,5 +670,5 @@ void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads) minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet)); } - +#endif diff --git a/src/net.cpp b/src/net.cpp index fcef9feea0..ab39be60b5 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -12,7 +12,6 @@ #include "addrman.h" #include "chainparams.h" #include "core.h" -#include "db.h" #include "ui_interface.h" #ifdef WIN32 @@ -80,6 +79,9 @@ CCriticalSection cs_setservAddNodeAddresses; vector<std::string> vAddedNodes; CCriticalSection cs_vAddedNodes; +NodeId nLastNodeId = 0; +CCriticalSection cs_nLastNodeId; + static CSemaphore *semOutbound = NULL; // Signals for message handling @@ -581,35 +583,21 @@ bool CNode::IsBanned(CNetAddr ip) return fResult; } -bool CNode::Misbehaving(int howmuch) -{ - if (addr.IsLocal()) +bool CNode::Ban(const CNetAddr &addr) { + int64_t banTime = GetTime()+GetArg("-bantime", 60*60*24); // Default 24-hour ban { - LogPrintf("Warning: Local node %s misbehaving (delta: %d)!\n", addrName.c_str(), howmuch); - return false; + LOCK(cs_setBanned); + if (setBanned[addr] < banTime) + setBanned[addr] = banTime; } - - nMisbehavior += howmuch; - if (nMisbehavior >= GetArg("-banscore", 100)) - { - int64_t banTime = GetTime()+GetArg("-bantime", 60*60*24); // Default 24-hour ban - LogPrintf("Misbehaving: %s (%d -> %d) DISCONNECTING\n", addr.ToString().c_str(), nMisbehavior-howmuch, nMisbehavior); - { - LOCK(cs_setBanned); - if (setBanned[addr] < banTime) - setBanned[addr] = banTime; - } - CloseSocketDisconnect(); - return true; - } else - LogPrintf("Misbehaving: %s (%d -> %d)\n", addr.ToString().c_str(), nMisbehavior-howmuch, nMisbehavior); - return false; + return true; } #undef X #define X(name) stats.name = name void CNode::copyStats(CNodeStats &stats) { + stats.nodeid = this->GetId(); X(nServices); X(nLastSend); X(nLastRecv); @@ -619,7 +607,6 @@ void CNode::copyStats(CNodeStats &stats) X(cleanSubVer); X(fInbound); X(nStartingHeight); - X(nMisbehavior); X(nSendBytes); X(nRecvBytes); stats.fSyncNode = (this == pnodeSync); @@ -1690,7 +1677,7 @@ bool BindListenPort(const CService &addrBind, string& strError) return true; } -void static Discover() +void static Discover(boost::thread_group& threadGroup) { if (!fDiscover) return; @@ -1743,7 +1730,7 @@ void static Discover() // Don't use external IPv4 discovery, when -onlynet="IPv6" if (!IsLimited(NET_IPV4)) - boost::thread(boost::bind(&TraceThread<void (*)()>, "ext-ip", &ThreadGetMyExternalIP)); + threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "ext-ip", &ThreadGetMyExternalIP)); } void StartNode(boost::thread_group& threadGroup) @@ -1757,7 +1744,7 @@ void StartNode(boost::thread_group& threadGroup) if (pnodeLocalHost == NULL) pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), nLocalServices)); - Discover(); + Discover(threadGroup); // // Start threads @@ -1942,3 +1929,103 @@ void CNode::Fuzz(int nChance) // (more changes exponentially less likely): Fuzz(2); } + +// +// CAddrDB +// + +CAddrDB::CAddrDB() +{ + pathAddr = GetDataDir() / "peers.dat"; +} + +bool CAddrDB::Write(const CAddrMan& addr) +{ + // Generate random temporary filename + unsigned short randv = 0; + RAND_bytes((unsigned char *)&randv, sizeof(randv)); + std::string tmpfn = strprintf("peers.dat.%04x", randv); + + // serialize addresses, checksum data up to that point, then append csum + CDataStream ssPeers(SER_DISK, CLIENT_VERSION); + ssPeers << FLATDATA(Params().MessageStart()); + ssPeers << addr; + uint256 hash = Hash(ssPeers.begin(), ssPeers.end()); + ssPeers << hash; + + // open temp output file, and associate with CAutoFile + boost::filesystem::path pathTmp = GetDataDir() / tmpfn; + FILE *file = fopen(pathTmp.string().c_str(), "wb"); + CAutoFile fileout = CAutoFile(file, SER_DISK, CLIENT_VERSION); + if (!fileout) + return error("CAddrman::Write() : open failed"); + + // Write and commit header, data + try { + fileout << ssPeers; + } + catch (std::exception &e) { + return error("CAddrman::Write() : I/O error"); + } + FileCommit(fileout); + fileout.fclose(); + + // replace existing peers.dat, if any, with new peers.dat.XXXX + if (!RenameOver(pathTmp, pathAddr)) + return error("CAddrman::Write() : Rename-into-place failed"); + + return true; +} + +bool CAddrDB::Read(CAddrMan& addr) +{ + // open input file, and associate with CAutoFile + FILE *file = fopen(pathAddr.string().c_str(), "rb"); + CAutoFile filein = CAutoFile(file, SER_DISK, CLIENT_VERSION); + if (!filein) + return error("CAddrman::Read() : open failed"); + + // use file size to size memory buffer + int fileSize = GetFilesize(filein); + int dataSize = fileSize - sizeof(uint256); + //Don't try to resize to a negative number if file is small + if ( dataSize < 0 ) dataSize = 0; + vector<unsigned char> vchData; + vchData.resize(dataSize); + uint256 hashIn; + + // read data and checksum from file + try { + filein.read((char *)&vchData[0], dataSize); + filein >> hashIn; + } + catch (std::exception &e) { + return error("CAddrman::Read() 2 : I/O error or stream data corrupted"); + } + filein.fclose(); + + CDataStream ssPeers(vchData, SER_DISK, CLIENT_VERSION); + + // verify stored checksum matches input data + uint256 hashTmp = Hash(ssPeers.begin(), ssPeers.end()); + if (hashIn != hashTmp) + return error("CAddrman::Read() : checksum mismatch; data corrupted"); + + unsigned char pchMsgTmp[4]; + try { + // de-serialize file header (network specific magic number) and .. + ssPeers >> FLATDATA(pchMsgTmp); + + // ... verify the network matches ours + if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) + return error("CAddrman::Read() : invalid network magic number"); + + // de-serialize address data into one CAddrMan object + ssPeers >> addr; + } + catch (std::exception &e) { + return error("CAddrman::Read() : I/O error or stream data corrupted"); + } + + return true; +} @@ -57,14 +57,19 @@ void StartNode(boost::thread_group& threadGroup); bool StopNode(); void SocketSendData(CNode *pnode); +typedef int NodeId; + // Signals for message handling struct CNodeSignals { boost::signals2::signal<int ()> GetHeight; boost::signals2::signal<bool (CNode*)> ProcessMessages; boost::signals2::signal<bool (CNode*, bool)> SendMessages; + boost::signals2::signal<void (NodeId, const CNode*)> InitializeNode; + boost::signals2::signal<void (NodeId)> FinalizeNode; }; + CNodeSignals& GetNodeSignals(); @@ -109,12 +114,14 @@ extern limitedmap<CInv, int64_t> mapAlreadyAskedFor; extern std::vector<std::string> vAddedNodes; extern CCriticalSection cs_vAddedNodes; - +extern NodeId nLastNodeId; +extern CCriticalSection cs_nLastNodeId; class CNodeStats { public: + NodeId nodeid; uint64_t nServices; int64_t nLastSend; int64_t nLastRecv; @@ -124,7 +131,6 @@ public: std::string cleanSubVer; bool fInbound; int nStartingHeight; - int nMisbehavior; uint64_t nSendBytes; uint64_t nRecvBytes; bool fSyncNode; @@ -223,13 +229,13 @@ public: CCriticalSection cs_filter; CBloomFilter* pfilter; int nRefCount; + NodeId id; protected: // Denial-of-service detection/prevention // Key is IP address, value is banned-until-time static std::map<CNetAddr, int64_t> setBanned; static CCriticalSection cs_setBanned; - int nMisbehavior; // Basic fuzz-testing void Fuzz(int nChance); // modifies ssSend @@ -289,7 +295,6 @@ public: nStartingHeight = -1; fStartSync = false; fGetAddr = false; - nMisbehavior = 0; fRelayTxes = false; setInventoryKnown.max_size(SendBufferSize() / 1000); pfilter = new CBloomFilter(); @@ -298,9 +303,16 @@ public: nPingUsecTime = 0; fPingQueued = false; + { + LOCK(cs_nLastNodeId); + id = nLastNodeId++; + } + // Be shy and don't send version until we hear if (hSocket != INVALID_SOCKET && !fInbound) PushVersion(); + + GetNodeSignals().InitializeNode(GetId(), this); } ~CNode() @@ -312,6 +324,7 @@ public: } if (pfilter) delete pfilter; + GetNodeSignals().FinalizeNode(GetId()); } private: @@ -326,6 +339,9 @@ private: public: + NodeId GetId() const { + return id; + } int GetRefCount() { @@ -673,7 +689,7 @@ public: // new code. static void ClearBanned(); // needed for unit testing static bool IsBanned(CNetAddr ip); - bool Misbehaving(int howmuch); // 1 == a little, 100 == a lot + static bool Ban(const CNetAddr &ip); void copyStats(CNodeStats &stats); // Network stats @@ -690,4 +706,15 @@ class CTransaction; void RelayTransaction(const CTransaction& tx, const uint256& hash); void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataStream& ss); +/** Access to the (IP) address database (peers.dat) */ +class CAddrDB +{ +private: + boost::filesystem::path pathAddr; +public: + CAddrDB(); + bool Write(const CAddrMan& addr); + bool Read(CAddrMan& addr); +}; + #endif diff --git a/src/noui.cpp b/src/noui.cpp index fd285c5719..86954f3c34 100644 --- a/src/noui.cpp +++ b/src/noui.cpp @@ -34,11 +34,6 @@ static bool noui_ThreadSafeMessageBox(const std::string& message, const std::str return false; } -static bool noui_ThreadSafeAskFee(int64_t /*nFeeRequired*/) -{ - return true; -} - static void noui_InitMessage(const std::string &message) { LogPrintf("init message: %s\n", message.c_str()); @@ -48,6 +43,5 @@ void noui_connect() { // Connect bitcoind signal handlers uiInterface.ThreadSafeMessageBox.connect(noui_ThreadSafeMessageBox); - uiInterface.ThreadSafeAskFee.connect(noui_ThreadSafeAskFee); uiInterface.InitMessage.connect(noui_InitMessage); } diff --git a/src/qt/Makefile.am b/src/qt/Makefile.am index e302adc89f..434373da29 100644 --- a/src/qt/Makefile.am +++ b/src/qt/Makefile.am @@ -96,6 +96,7 @@ QT_MOC_CPP = moc_aboutdialog.cpp moc_addressbookpage.cpp \ moc_optionsmodel.cpp moc_overviewpage.cpp moc_paymentserver.cpp \ moc_receiverequestdialog.cpp moc_qvalidatedlineedit.cpp moc_qvaluecombobox.cpp \ moc_receivecoinsdialog.cpp \ + moc_recentrequeststablemodel.cpp \ moc_rpcconsole.cpp moc_sendcoinsdialog.cpp moc_sendcoinsentry.cpp \ moc_signverifymessagedialog.cpp moc_splashscreen.cpp moc_trafficgraphwidget.cpp moc_transactiondesc.cpp \ moc_transactiondescdialog.cpp moc_transactionfilterproxy.cpp \ @@ -122,6 +123,7 @@ BITCOIN_QT_H = aboutdialog.h addressbookpage.h addresstablemodel.h \ optionsdialog.h \ optionsmodel.h overviewpage.h paymentrequestplus.h paymentserver.h \ receivecoinsdialog.h \ + recentrequeststablemodel.h \ receiverequestdialog.h qvalidatedlineedit.h qvaluecombobox.h rpcconsole.h \ sendcoinsdialog.h sendcoinsentry.h signverifymessagedialog.h splashscreen.h \ trafficgraphwidget.h transactiondescdialog.h transactiondesc.h transactionfilterproxy.h \ @@ -157,6 +159,7 @@ BITCOIN_QT_CPP = aboutdialog.cpp addressbookpage.cpp \ optionsdialog.cpp optionsmodel.cpp overviewpage.cpp paymentrequestplus.cpp \ paymentserver.cpp qvalidatedlineedit.cpp qvaluecombobox.cpp \ receivecoinsdialog.cpp receiverequestdialog.cpp \ + recentrequeststablemodel.cpp \ rpcconsole.cpp sendcoinsdialog.cpp sendcoinsentry.cpp \ signverifymessagedialog.cpp splashscreen.cpp trafficgraphwidget.cpp transactiondesc.cpp \ transactiondescdialog.cpp transactionfilterproxy.cpp transactionrecord.cpp \ @@ -197,7 +200,7 @@ endif bitcoin_qt_CPPFLAGS = $(AM_CPPFLAGS) $(QT_INCLUDES) \ -I$(top_srcdir)/src/qt/forms bitcoin_qt_SOURCES = bitcoin.cpp -bitcoin_qt_LDADD = libbitcoinqt.a $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBLEVELDB) $(LIBMEMENV) \ +bitcoin_qt_LDADD = libbitcoinqt.a $(LIBBITCOIN_SERVER) $(LIBBITCOIN_WALLET) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBLEVELDB) $(LIBMEMENV) \ $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) # forms/foo.h -> forms/ui_foo.h diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index d686cd4fd8..5e7d8e6178 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -297,7 +297,7 @@ QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, { if(orientation == Qt::Horizontal) { - if(role == Qt::DisplayRole) + if(role == Qt::DisplayRole && section < columns.size()) { return columns[section]; } diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 57ce7da361..657b42d16a 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -17,6 +17,7 @@ #include "main.h" #include "ui_interface.h" #include "util.h" +#include "wallet.h" #include <stdint.h> @@ -53,7 +54,6 @@ static SplashScreen *splashref; static bool ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style) { - // Message from network thread if(guiref) { bool modal = (style & CClientUIInterface::MODAL); @@ -75,22 +75,6 @@ static bool ThreadSafeMessageBox(const std::string& message, const std::string& } } -static bool ThreadSafeAskFee(int64_t nFeeRequired) -{ - if(!guiref) - return false; - if(nFeeRequired < CTransaction::nMinTxFee || nFeeRequired <= nTransactionFee || fDaemon) - return true; - - bool payFee = false; - - QMetaObject::invokeMethod(guiref, "askFee", GUIUtil::blockingGUIThreadConnection(), - Q_ARG(qint64, nFeeRequired), - Q_ARG(bool*, &payFee)); - - return payFee; -} - static void InitMessage(const std::string &message) { if(splashref) @@ -160,17 +144,17 @@ static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTrans /* qDebug() message handler --> debug.log */ #if QT_VERSION < 0x050000 -void DebugMessageHandler(QtMsgType type, const char * msg) +void DebugMessageHandler(QtMsgType type, const char *msg) { Q_UNUSED(type); - LogPrint("qt", "Bitcoin-Qt: %s\n", msg); + LogPrint("qt", "GUI: %s\n", msg); } #else void DebugMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &msg) { Q_UNUSED(type); Q_UNUSED(context); - LogPrint("qt", "Bitcoin-Qt: %s\n", qPrintable(msg)); + LogPrint("qt", "GUI: %s\n", qPrintable(msg)); } #endif @@ -201,15 +185,23 @@ int main(int argc, char *argv[]) Q_INIT_RESOURCE(bitcoin); QApplication app(argc, argv); +#if QT_VERSION > 0x050100 + // Generate high-dpi pixmaps + QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); +#endif +#ifdef Q_OS_MAC + QApplication::setAttribute(Qt::AA_DontShowIconsInMenus); +#endif // Register meta types used for QMetaObject::invokeMethod qRegisterMetaType< bool* >(); // Application identification (must be set before OptionsModel is initialized, // as it is used to locate QSettings) + bool isaTestNet = TestNet() || RegTest(); QApplication::setOrganizationName("Bitcoin"); QApplication::setOrganizationDomain("bitcoin.org"); - if (TestNet()) // Separate UI settings for testnet + if (isaTestNet) // Separate UI settings for testnets QApplication::setApplicationName("Bitcoin-Qt-testnet"); else QApplication::setApplicationName("Bitcoin-Qt"); @@ -240,7 +232,7 @@ int main(int argc, char *argv[]) PaymentServer* paymentServer = new PaymentServer(&app); // User language is set up: pick a data directory - Intro::pickDataDirectory(TestNet()); + Intro::pickDataDirectory(isaTestNet); // Install global event filter that makes sure that long tooltips can be word-wrapped app.installEventFilter(new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app)); @@ -256,7 +248,6 @@ int main(int argc, char *argv[]) // Subscribe to global signals from core uiInterface.ThreadSafeMessageBox.connect(ThreadSafeMessageBox); - uiInterface.ThreadSafeAskFee.connect(ThreadSafeAskFee); uiInterface.InitMessage.connect(InitMessage); uiInterface.Translate.connect(Translate); @@ -269,7 +260,7 @@ int main(int argc, char *argv[]) return 1; } - SplashScreen splash(QPixmap(), 0); + SplashScreen splash(QPixmap(), 0, isaTestNet); if (GetBoolArg("-splash", true) && !GetBoolArg("-min", false)) { splash.show(); @@ -291,7 +282,7 @@ int main(int argc, char *argv[]) boost::thread_group threadGroup; - BitcoinGUI window(TestNet(), 0); + BitcoinGUI window(isaTestNet, 0); guiref = &window; QTimer* pollShutdownTimer = new QTimer(guiref); @@ -313,11 +304,12 @@ int main(int argc, char *argv[]) splash.finish(&window); ClientModel clientModel(&optionsModel); + window.setClientModel(&clientModel); + WalletModel *walletModel = 0; if(pwalletMain) walletModel = new WalletModel(pwalletMain, &optionsModel); - window.setClientModel(&clientModel); if(walletModel) { window.addWallet("~Default", walletModel); @@ -357,7 +349,7 @@ int main(int argc, char *argv[]) guiref = 0; delete walletModel; } - // Shutdown the core and its threads, but don't exit Bitcoin-Qt here + // Shutdown the core and its threads, but don't exit the GUI here threadGroup.interrupt_all(); threadGroup.join_all(); Shutdown(); diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index 6b083331d3..1698595188 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -14,8 +14,10 @@ #include <QKeyEvent> #include <qmath.h> // for qPow() -BitcoinAmountField::BitcoinAmountField(QWidget *parent): - QWidget(parent), amount(0), currentUnit(-1) +BitcoinAmountField::BitcoinAmountField(QWidget *parent) : + QWidget(parent), + amount(0), + currentUnit(-1) { amount = new QDoubleSpinBox(this); amount->setLocale(QLocale::c()); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index a44627690f..2d0f51a3fb 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -69,28 +69,32 @@ BitcoinGUI::BitcoinGUI(bool fIsTestnet, QWidget *parent) : { GUIUtil::restoreWindowGeometry("nWindow", QSize(850, 550), this); -#ifndef Q_OS_MAC if (!fIsTestnet) { - setWindowTitle(tr("Bitcoin") + " - " + tr("Wallet")); + setWindowTitle(tr("Bitcoin Core") + " - " + tr("Wallet")); +#ifndef Q_OS_MAC QApplication::setWindowIcon(QIcon(":icons/bitcoin")); setWindowIcon(QIcon(":icons/bitcoin")); +#else + MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin")); +#endif } else { - setWindowTitle(tr("Bitcoin") + " - " + tr("Wallet") + " " + tr("[testnet]")); + setWindowTitle(tr("Bitcoin Core") + " - " + tr("Wallet") + " " + tr("[testnet]")); +#ifndef Q_OS_MAC QApplication::setWindowIcon(QIcon(":icons/bitcoin_testnet")); setWindowIcon(QIcon(":icons/bitcoin_testnet")); - } #else - setUnifiedTitleAndToolBarOnMac(true); - QApplication::setAttribute(Qt::AA_DontShowIconsInMenus); - - if (!fIsTestnet) - MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin")); - else MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin_testnet")); #endif + } + +#if defined(Q_OS_MAC) && QT_VERSION < 0x050000 + // This property is not implemented in Qt 5. Setting it has no effect. + // A replacement API (QtMacUnifiedToolBar) is available in QtMacExtras. + setUnifiedTitleAndToolBarOnMac(true); +#endif // Create wallet frame and make it the central widget walletFrame = new WalletFrame(this); @@ -211,6 +215,8 @@ void BitcoinGUI::createActions(bool fIsTestnet) historyAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_4)); tabGroup->addAction(historyAction); + // These showNormalIfMinimized are needed because Send Coins and Receive Coins + // can be triggered from the tray menu, and need to show the GUI to be useful. connect(overviewAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); connect(overviewAction, SIGNAL(triggered()), this, SLOT(gotoOverviewPage())); connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); @@ -225,9 +231,9 @@ void BitcoinGUI::createActions(bool fIsTestnet) quitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q)); quitAction->setMenuRole(QAction::QuitRole); if (!fIsTestnet) - aboutAction = new QAction(QIcon(":/icons/bitcoin"), tr("&About Bitcoin"), this); + aboutAction = new QAction(QIcon(":/icons/bitcoin"), tr("&About Bitcoin Core"), this); else - aboutAction = new QAction(QIcon(":/icons/bitcoin_testnet"), tr("&About Bitcoin"), this); + aboutAction = new QAction(QIcon(":/icons/bitcoin_testnet"), tr("&About Bitcoin Core"), this); aboutAction->setStatusTip(tr("Show information about Bitcoin")); aboutAction->setMenuRole(QAction::AboutRole); #if QT_VERSION < 0x050000 @@ -327,6 +333,7 @@ void BitcoinGUI::createToolBars() toolbar->addAction(sendCoinsAction); toolbar->addAction(receiveCoinsAction); toolbar->addAction(historyAction); + overviewAction->setChecked(true); } void BitcoinGUI::setClientModel(ClientModel *clientModel) @@ -405,7 +412,7 @@ void BitcoinGUI::createTrayIcon(bool fIsTestnet) trayIcon->show(); #endif - notificator = new Notificator(QApplication::applicationName(), trayIcon); + notificator = new Notificator(QApplication::applicationName(), trayIcon, this); } void BitcoinGUI::createTrayIconMenu() @@ -722,19 +729,6 @@ void BitcoinGUI::closeEvent(QCloseEvent *event) QMainWindow::closeEvent(event); } -void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee) -{ - if (!clientModel || !clientModel->getOptionsModel()) - return; - - 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); - *payFee = (retval == QMessageBox::Yes); -} - void BitcoinGUI::incomingTransaction(const QString& date, int unit, qint64 amount, const QString& type, const QString& address) { // On new transaction, make an info balloon diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index b9a8a03139..75c61d2a8a 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -135,16 +135,6 @@ public slots: */ 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. - https://bugreports.qt-project.org/browse/QTBUG-10440 - - @param[in] nFeeRequired the required fee - @param[out] payFee true to pay the fee, false to not pay the fee - */ - void askFee(qint64 nFeeRequired, bool *payFee); - bool handlePaymentRequest(const SendCoinsRecipient& recipient); /** Show incoming transaction notification for new transactions. */ diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp index 45a9bf6a41..183a98061d 100644 --- a/src/qt/bitcoinstrings.cpp +++ b/src/qt/bitcoinstrings.cpp @@ -61,8 +61,9 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "Number of seconds to keep misbehaving peers from reconnecting (default: " "86400)"), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Set maximum size of high-priority/low-fee transactions in bytes (default: " -"27000)"), +"Output debugging information (default: 0, supplying <category> is optional)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Set maximum size of high-priority/low-fee transactions in bytes (default: %d)"), QT_TRANSLATE_NOOP("bitcoin-core", "" "Set the number of script verification threads (up to 16, 0 = auto, <0 = " "leave that many cores free, default: 0)"), @@ -72,6 +73,9 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" QT_TRANSLATE_NOOP("bitcoin-core", "" "Unable to bind to %s on this computer. Bitcoin is probably already running."), QT_TRANSLATE_NOOP("bitcoin-core", "" +"Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: -" +"proxy)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" "Warning: -paytxfee is set very high! This is the transaction fee you will " "pay if you send a transaction."), QT_TRANSLATE_NOOP("bitcoin-core", "" @@ -95,21 +99,22 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "%s\n" "If the file does not exist, create it with owner-readable-only file " "permissions."), +QT_TRANSLATE_NOOP("bitcoin-core", "<category> can be:"), QT_TRANSLATE_NOOP("bitcoin-core", "Accept command line and JSON-RPC commands"), QT_TRANSLATE_NOOP("bitcoin-core", "Accept connections from outside (default: 1 if no -proxy or -connect)"), QT_TRANSLATE_NOOP("bitcoin-core", "Add a node to connect to and attempt to keep the connection open"), 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 Core Daemon"), 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"), QT_TRANSLATE_NOOP("bitcoin-core", "Cannot resolve -bind address: '%s'"), 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 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"), @@ -138,12 +143,13 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write file info"), QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write to coin database"), QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write transaction index"), QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write undo data"), -QT_TRANSLATE_NOOP("bitcoin-core", "Fee per KB to add to transactions you send"), +QT_TRANSLATE_NOOP("bitcoin-core", "Fee per kB to add to transactions you send"), QT_TRANSLATE_NOOP("bitcoin-core", "Find peers using DNS lookup (default: 1 unless -connect)"), QT_TRANSLATE_NOOP("bitcoin-core", "Generate coins (default: 0)"), QT_TRANSLATE_NOOP("bitcoin-core", "Get help for a command"), QT_TRANSLATE_NOOP("bitcoin-core", "How many blocks to check at startup (default: 288, 0 = all)"), QT_TRANSLATE_NOOP("bitcoin-core", "How thorough the block verification is (0-4, default: 3)"), +QT_TRANSLATE_NOOP("bitcoin-core", "If <category> is not supplied, output all debugging information."), QT_TRANSLATE_NOOP("bitcoin-core", "Imports blocks from external blk000??.dat file"), QT_TRANSLATE_NOOP("bitcoin-core", "Incorrect or no genesis block found. Wrong datadir for network?"), QT_TRANSLATE_NOOP("bitcoin-core", "Information"), @@ -167,16 +173,15 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Not enough file descriptors available."), QT_TRANSLATE_NOOP("bitcoin-core", "Only accept block chain matching built-in checkpoints (default: 1)"), QT_TRANSLATE_NOOP("bitcoin-core", "Only connect to nodes in network <net> (IPv4, IPv6 or Tor)"), QT_TRANSLATE_NOOP("bitcoin-core", "Options:"), -QT_TRANSLATE_NOOP("bitcoin-core", "Output extra debugging information. Implies all other -debug* options"), -QT_TRANSLATE_NOOP("bitcoin-core", "Output extra network debugging information"), QT_TRANSLATE_NOOP("bitcoin-core", "Password for JSON-RPC connections"), -QT_TRANSLATE_NOOP("bitcoin-core", "Prepend debug output with timestamp"), +QT_TRANSLATE_NOOP("bitcoin-core", "Prepend debug output with timestamp (default: 1)"), +QT_TRANSLATE_NOOP("bitcoin-core", "RPC client options:"), QT_TRANSLATE_NOOP("bitcoin-core", "Rebuild block chain index from current blk000??.dat files"), QT_TRANSLATE_NOOP("bitcoin-core", "Rescan the block chain for missing wallet transactions"), 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", "Select SOCKS version for -proxy (4 or 5, default: 5)"), 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"), @@ -185,7 +190,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Server certificate file (default: server.cert QT_TRANSLATE_NOOP("bitcoin-core", "Server private key (default: server.pem)"), QT_TRANSLATE_NOOP("bitcoin-core", "Set database cache size in megabytes (default: 25)"), QT_TRANSLATE_NOOP("bitcoin-core", "Set key pool size to <n> (default: 100)"), -QT_TRANSLATE_NOOP("bitcoin-core", "Set maximum block size in bytes (default: 250000)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Set maximum block size in bytes (default: %d)"), QT_TRANSLATE_NOOP("bitcoin-core", "Set minimum block size in bytes (default: 0)"), QT_TRANSLATE_NOOP("bitcoin-core", "Set the number of threads to service RPC calls (default: 4)"), QT_TRANSLATE_NOOP("bitcoin-core", "Shrink debug.log file on client startup (default: 1 when no -debug)"), @@ -213,15 +218,17 @@ 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)"), QT_TRANSLATE_NOOP("bitcoin-core", "Use UPnP to map the listening port (default: 1 when listening)"), -QT_TRANSLATE_NOOP("bitcoin-core", "Use proxy to reach tor hidden services (default: same as -proxy)"), QT_TRANSLATE_NOOP("bitcoin-core", "Use the test network"), QT_TRANSLATE_NOOP("bitcoin-core", "Username for JSON-RPC connections"), QT_TRANSLATE_NOOP("bitcoin-core", "Verifying blocks..."), QT_TRANSLATE_NOOP("bitcoin-core", "Verifying wallet..."), +QT_TRANSLATE_NOOP("bitcoin-core", "Wait for RPC server to start"), QT_TRANSLATE_NOOP("bitcoin-core", "Wallet %s resides outside data directory %s"), QT_TRANSLATE_NOOP("bitcoin-core", "Wallet needed to be rewritten: restart Bitcoin to complete"), QT_TRANSLATE_NOOP("bitcoin-core", "Warning"), +QT_TRANSLATE_NOOP("bitcoin-core", "Warning: Deprecated argument -debugnet ignored, use -debug=net"), QT_TRANSLATE_NOOP("bitcoin-core", "Warning: This version is obsolete, upgrade required!"), QT_TRANSLATE_NOOP("bitcoin-core", "You need to rebuild the database using -reindex to change -txindex"), +QT_TRANSLATE_NOOP("bitcoin-core", "version"), QT_TRANSLATE_NOOP("bitcoin-core", "wallet.dat corrupt, salvage failed"), }; diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index c64e411bca..f273b9ea46 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -123,9 +123,12 @@ void ClientModel::updateAlert(const QString &hash, int status) emit alertsChanged(getStatusBarWarnings()); } -bool ClientModel::isTestNet() const +QString ClientModel::getNetworkName() const { - return TestNet(); + QString netname(QString::fromStdString(Params().DataDir())); + if(netname.isEmpty()) + netname = "main"; + return netname; } bool ClientModel::inInitialBlockDownload() const diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index dcc528f4fd..ca735f14ce 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -46,8 +46,8 @@ public: double getVerificationProgress() const; QDateTime getLastBlockDate() const; - //! Return true if client connected to testnet - bool isTestNet() const; + //! Return network (main, testnet3, regtest) + QString getNetworkName() const; //! Return true if core is doing initial block download bool inInitialBlockDownload() const; //! Return true if core is importing blocks @@ -84,7 +84,7 @@ signals: void alertsChanged(const QString &warnings); void bytesChanged(quint64 totalBytesIn, quint64 totalBytesOut); - //! Asynchronous message notification + //! Fired when a message should be reported to the user void message(const QString &title, const QString &message, unsigned int style); public slots: diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 14e5b07133..b4e6aeb1dd 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -20,7 +20,6 @@ #include <QCheckBox> #include <QColor> #include <QCursor> -#include <QDateTime> #include <QDialogButtonBox> #include <QFlags> #include <QIcon> @@ -730,7 +729,7 @@ void CoinControlDialog::updateView() itemOutput->setText(COLUMN_AMOUNT_INT64, strPad(QString::number(out.tx->vout[out.i].nValue), 15, " ")); // padding so that sorting works correctly // date - itemOutput->setText(COLUMN_DATE, QDateTime::fromTime_t(out.tx->GetTxTime()).toString("yy-MM-dd hh:mm")); + itemOutput->setText(COLUMN_DATE, GUIUtil::dateTimeStr(out.tx->GetTxTime())); // confirmations itemOutput->setText(COLUMN_CONFIRMATIONS, strPad(QString::number(out.nDepth), 8, " ")); diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp index 618567218f..46982cc339 100644 --- a/src/qt/editaddressdialog.cpp +++ b/src/qt/editaddressdialog.cpp @@ -13,7 +13,10 @@ EditAddressDialog::EditAddressDialog(Mode mode, QWidget *parent) : QDialog(parent), - ui(new Ui::EditAddressDialog), mapper(0), mode(mode), model(0) + ui(new Ui::EditAddressDialog), + mapper(0), + mode(mode), + model(0) { ui->setupUi(this); diff --git a/src/qt/forms/aboutdialog.ui b/src/qt/forms/aboutdialog.ui index 80768f89b0..10a5857cab 100644 --- a/src/qt/forms/aboutdialog.ui +++ b/src/qt/forms/aboutdialog.ui @@ -11,7 +11,7 @@ </rect> </property> <property name="windowTitle"> - <string>About Bitcoin</string> + <string>About Bitcoin Core</string> </property> <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> @@ -50,7 +50,7 @@ <cursorShape>IBeamCursor</cursorShape> </property> <property name="text"> - <string><b>Bitcoin</b> version</string> + <string><b>Bitcoin Core</b> version</string> </property> <property name="textInteractionFlags"> <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> @@ -91,7 +91,7 @@ <cursorShape>IBeamCursor</cursorShape> </property> <property name="text"> - <string notr="true">Copyright &copy; 2009-YYYY The Bitcoin developers</string> + <string notr="true">Copyright &copy; 2009-YYYY The Bitcoin Core developers</string> </property> <property name="textFormat"> <enum>Qt::RichText</enum> diff --git a/src/qt/forms/intro.ui b/src/qt/forms/intro.ui index 0f6ae5a7d0..05ee9466c4 100644 --- a/src/qt/forms/intro.ui +++ b/src/qt/forms/intro.ui @@ -20,7 +20,7 @@ <string notr="true">QLabel { font-style:italic; }</string> </property> <property name="text"> - <string>Welcome to Bitcoin-Qt.</string> + <string>Welcome to Bitcoin Core.</string> </property> <property name="wordWrap"> <bool>true</bool> @@ -46,7 +46,7 @@ <item> <widget class="QLabel" name="label_4"> <property name="text"> - <string>As this is the first time the program is launched, you can choose where Bitcoin-Qt will store its data.</string> + <string>As this is the first time the program is launched, you can choose where Bitcoin Core will store its data.</string> </property> <property name="wordWrap"> <bool>true</bool> @@ -56,7 +56,7 @@ <item> <widget class="QLabel" name="sizeWarningLabel"> <property name="text"> - <string>Bitcoin-Qt will download and store a copy of the Bitcoin block chain. At least %1GB of data will be stored in this directory, and it will grow over time. The wallet will also be stored in this directory.</string> + <string>Bitcoin Core will download and store a copy of the Bitcoin block chain. At least %1GB of data will be stored in this directory, and it will grow over time. The wallet will also be stored in this directory.</string> </property> <property name="wordWrap"> <bool>true</bool> diff --git a/src/qt/forms/receivecoinsdialog.ui b/src/qt/forms/receivecoinsdialog.ui index 6d1a72ecd2..e7138f5371 100644 --- a/src/qt/forms/receivecoinsdialog.ui +++ b/src/qt/forms/receivecoinsdialog.ui @@ -7,35 +7,60 @@ <x>0</x> <y>0</y> <width>776</width> - <height>343</height> + <height>364</height> </rect> </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> <layout class="QGridLayout" name="gridLayout"> - <item row="3" column="0"> - <widget class="QLabel" name="label"> + <item row="7" column="2"> + <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>&Amount:</string> + <string>R&euse an existing receiving address (not recommended)</string> + </property> + </widget> + </item> + <item row="7" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="6" 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>reqAmount</cstring> + <cstring>reqMessage</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> + <item row="4" column="2"> + <widget class="QLineEdit" name="reqLabel"> + <property name="toolTip"> + <string>The label to associate with the new receiving address</string> </property> + </widget> + </item> + <item row="6" column="2"> + <widget class="QLineEdit" name="reqMessage"> <property name="toolTip"> - <string>The amount to request</string> + <string>The message to attach to payment request</string> + </property> + </widget> + </item> + <item row="2" column="2"> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>Use this form to request payments. All fields are optional.</string> </property> </widget> </item> @@ -52,73 +77,35 @@ </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"> + <widget class="QLabel" name="label"> <property name="text"> - <string>&Message:</string> + <string>&Amount:</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> + <cstring>reqAmount</cstring> </property> </widget> </item> - <item row="6" column="0"> - <widget class="QLabel" name="label_4"> - <property name="text"> - <string/> + <item row="5" column="2"> + <widget class="BitcoinAmountField" name="reqAmount"> + <property name="minimumSize"> + <size> + <width>80</width> + <height>0</height> + </size> </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> + <string>The amount to request</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"> @@ -178,6 +165,98 @@ </item> </layout> </item> + <item> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QFrame" name="frame"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QLabel" name="label_6"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>Previously requested payments</string> + </property> + </widget> + </item> + <item> + <widget class="QTableView" name="recentRequestsView"/> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QPushButton" name="showRequestButton"> + <property name="toolTip"> + <string>Show the selected request (does the same as double clicking an entry)</string> + </property> + <property name="text"> + <string>Show</string> + </property> + <property name="icon"> + <iconset resource="../bitcoin.qrc"> + <normaloff>:/icons/edit</normaloff>:/icons/edit</iconset> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="removeRequestButton"> + <property name="toolTip"> + <string>Remove the selected entries from the list</string> + </property> + <property name="text"> + <string>Remove</string> + </property> + <property name="icon"> + <iconset resource="../bitcoin.qrc"> + <normaloff>:/icons/remove</normaloff>:/icons/remove</iconset> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> + </item> </layout> </widget> <customwidgets> @@ -187,6 +266,17 @@ <header>bitcoinamountfield.h</header> </customwidget> </customwidgets> + <tabstops> + <tabstop>reqLabel</tabstop> + <tabstop>reqAmount</tabstop> + <tabstop>reqMessage</tabstop> + <tabstop>reuseAddress</tabstop> + <tabstop>clearButton</tabstop> + <tabstop>receiveButton</tabstop> + <tabstop>recentRequestsView</tabstop> + <tabstop>showRequestButton</tabstop> + <tabstop>removeRequestButton</tabstop> + </tabstops> <resources> <include location="../bitcoin.qrc"/> </resources> diff --git a/src/qt/forms/receiverequestdialog.ui b/src/qt/forms/receiverequestdialog.ui index c9cb3de69f..85928c9be5 100644 --- a/src/qt/forms/receiverequestdialog.ui +++ b/src/qt/forms/receiverequestdialog.ui @@ -84,13 +84,6 @@ </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> diff --git a/src/qt/forms/rpcconsole.ui b/src/qt/forms/rpcconsole.ui index c77c5a384c..69504f3159 100644 --- a/src/qt/forms/rpcconsole.ui +++ b/src/qt/forms/rpcconsole.ui @@ -36,7 +36,7 @@ </font> </property> <property name="text"> - <string>Bitcoin Core</string> + <string>General</string> </property> </widget> </item> @@ -172,14 +172,14 @@ </widget> </item> <item row="7" column="0"> - <widget class="QLabel" name="label_7"> + <widget class="QLabel" name="label_8"> <property name="text"> - <string>Number of connections</string> + <string>Name</string> </property> </widget> </item> <item row="7" column="1"> - <widget class="QLabel" name="numberOfConnections"> + <widget class="QLabel" name="networkName"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> </property> @@ -195,19 +195,25 @@ </widget> </item> <item row="8" column="0"> - <widget class="QLabel" name="label_8"> + <widget class="QLabel" name="label_7"> <property name="text"> - <string>On testnet</string> + <string>Number of connections</string> </property> </widget> </item> <item row="8" column="1"> - <widget class="QCheckBox" name="isTestNet"> - <property name="enabled"> - <bool>false</bool> + <widget class="QLabel" name="numberOfConnections"> + <property name="cursor"> + <cursorShape>IBeamCursor</cursorShape> </property> <property name="text"> - <string/> + <string>N/A</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> </property> </widget> </item> @@ -348,7 +354,7 @@ <item row="17" column="0"> <widget class="QPushButton" name="showCLOptionsButton"> <property name="toolTip"> - <string>Show the Bitcoin-Qt help message to get a list with possible Bitcoin command-line options.</string> + <string>Show the Bitcoin-Core help message to get a list with possible Bitcoin command-line options.</string> </property> <property name="text"> <string>&Show</string> diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 2ce09b479e..b87498402d 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -32,6 +32,7 @@ #include <boost/filesystem.hpp> #include <boost/filesystem/fstream.hpp> + #include <QAbstractItemView> #include <QApplication> #include <QClipboard> @@ -593,7 +594,7 @@ void restoreWindowGeometry(const QString& strSetting, const QSize& defaultSize, HelpMessageBox::HelpMessageBox(QWidget *parent) : QMessageBox(parent) { - header = tr("Bitcoin-Qt") + " " + tr("version") + " " + + header = tr("Bitcoin Core") + " " + tr("version") + " " + QString::fromStdString(FormatFullVersion()) + "\n\n" + tr("Usage:") + "\n" + " bitcoin-qt [" + tr("command-line options") + "] " + "\n"; @@ -606,7 +607,7 @@ HelpMessageBox::HelpMessageBox(QWidget *parent) : " -splash " + tr("Show splash screen on startup (default: 1)") + "\n" + " -choosedatadir " + tr("Choose data directory on startup (default: 0)") + "\n"; - setWindowTitle(tr("Bitcoin-Qt")); + setWindowTitle(tr("Bitcoin Core")); setTextFormat(Qt::PlainText); // setMinimumWidth is ignored for QMessageBox so put in non-breaking spaces to make it wider. setText(header + QString(QChar(0x2003)).repeated(50)); diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts index 0dc56ac5dc..368a4d6003 100644 --- a/src/qt/locale/bitcoin_en.ts +++ b/src/qt/locale/bitcoin_en.ts @@ -5,13 +5,13 @@ <name>AboutDialog</name> <message> <location filename="../forms/aboutdialog.ui" line="+14"/> - <source>About Bitcoin</source> - <translation>About Bitcoin</translation> + <source>About Bitcoin Core</source> + <translation type="unfinished"></translation> </message> <message> <location line="+39"/> - <source><b>Bitcoin</b> version</source> - <translation><b>Bitcoin</b> version</translation> + <source><b>Bitcoin Core</b> version</source> + <translation type="unfinished"></translation> </message> <message> <location line="+57"/> @@ -29,7 +29,7 @@ Distributed under the MIT/X11 software license, see the accompanying file COPYIN This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard.</translation> </message> <message> - <location filename="../aboutdialog.cpp" line="+14"/> + <location filename="../aboutdialog.cpp" line="+19"/> <source>Copyright</source> <translation>Copyright</translation> </message> @@ -72,7 +72,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"></translation> </message> <message> - <location filename="../addressbookpage.cpp" line="+72"/> + <location filename="../addressbookpage.cpp" line="+74"/> <source>&Copy Address</source> <translation>&Copy Address</translation> </message> @@ -142,7 +142,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Edit</translation> </message> <message> - <location line="+197"/> + <location line="+194"/> <source>Export Address List</source> <translation type="unfinished"></translation> </message> @@ -165,7 +165,7 @@ This product includes software developed by the OpenSSL Project for use in the O <context> <name>AddressTableModel</name> <message> - <location filename="../addresstablemodel.cpp" line="+164"/> + <location filename="../addresstablemodel.cpp" line="+168"/> <source>Label</source> <translation>Label</translation> </message> @@ -203,7 +203,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Repeat new passphrase</translation> </message> <message> - <location filename="../askpassphrasedialog.cpp" line="+34"/> + <location filename="../askpassphrasedialog.cpp" line="+40"/> <source>Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>.</source> <translation>Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>.</translation> </message> @@ -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="+250"/> + <location filename="../bitcoingui.cpp" line="+260"/> <source>Sign &message...</source> <translation>Sign &message...</translation> </message> <message> - <location line="+254"/> + <location line="+290"/> <source>Synchronizing with network...</source> <translation>Synchronizing with network...</translation> </message> <message> - <location line="-324"/> + <location line="-360"/> <source>&Overview</source> <translation>&Overview</translation> </message> @@ -400,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="+259"/> + <location line="+295"/> <source>Importing blocks from disk...</source> <translation>Importing blocks from disk...</translation> </message> @@ -410,7 +410,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Reindexing blocks on disk...</translation> </message> <message> - <location line="-322"/> + <location line="-358"/> <source>Send coins to a Bitcoin address</source> <translation>Send coins to a Bitcoin address</translation> </message> @@ -445,20 +445,18 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Verify message...</translation> </message> <message> - <location line="-180"/> - <location line="+6"/> - <location line="+513"/> + <location line="+375"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> <message> - <location line="-519"/> - <location line="+6"/> + <location line="-563"/> + <location line="+10"/> <source>Wallet</source> <translation>Wallet</translation> </message> <message> - <location line="+109"/> + <location line="+113"/> <source>&Send</source> <translation>&Send</translation> </message> @@ -468,13 +466,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Receive</translation> </message> <message> - <location line="+28"/> - <location line="+2"/> - <source>&About Bitcoin</source> - <translation>&About Bitcoin</translation> - </message> - <message> - <location line="+14"/> + <location line="+44"/> <location line="+2"/> <source>&Show / Hide</source> <translation>&Show / Hide</translation> @@ -500,12 +492,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="+35"/> + <location line="+39"/> <source>&File</source> <translation>&File</translation> </message> <message> - <location line="+10"/> + <location line="+11"/> <source>&Settings</source> <translation>&Settings</translation> </message> @@ -520,18 +512,30 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Tabs toolbar</translation> </message> <message> - <location line="-235"/> - <location line="+294"/> + <location line="-244"/> + <location line="+321"/> <source>[testnet]</source> <translation>[testnet]</translation> </message> <message> - <location line="-177"/> + <location line="-331"/> + <location line="+10"/> + <source>Bitcoin Core</source> + <translation type="unfinished">Bitcoin Core</translation> + </message> + <message> + <location line="+121"/> <source>Request payments (generates QR codes and bitcoin: URIs)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+63"/> + <location line="+27"/> + <location line="+2"/> + <source>&About Bitcoin Core</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+34"/> <source>&Used sending addresses...</source> <translation type="unfinished"></translation> </message> @@ -551,13 +555,23 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"></translation> </message> <message> - <location line="+106"/> + <location line="+2"/> + <source>Open URI...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Open a bitcoin: URI or payment request</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+126"/> <location line="+5"/> <source>Bitcoin client</source> <translation>Bitcoin client</translation> </message> <message numerus="yes"> - <location line="+120"/> + <location line="+133"/> <source>%n active connection(s) to Bitcoin network</source> <translation> <numerusform>%n active connection to Bitcoin network</numerusform> @@ -677,17 +691,7 @@ Address: %4 </translation> </message> <message> - <location line="+34"/> - <source>URI handling</source> - <translation>URI handling</translation> - </message> - <message> - <location line="+0"/> - <source>URI can not be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters.</source> - <translation>URI can not be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters.</translation> - </message> - <message> - <location line="+45"/> + <location line="+67"/> <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> @@ -697,7 +701,7 @@ Address: %4 <translation>Wallet is <b>encrypted</b> and currently <b>locked</b></translation> </message> <message> - <location filename="../bitcoin.cpp" line="+110"/> + <location filename="../bitcoin.cpp" line="+116"/> <source>A fatal error occurred. Bitcoin can no longer continue safely and will quit.</source> <translation>A fatal error occurred. Bitcoin can no longer continue safely and will quit.</translation> </message> @@ -705,12 +709,299 @@ Address: %4 <context> <name>ClientModel</name> <message> - <location filename="../clientmodel.cpp" line="+115"/> + <location filename="../clientmodel.cpp" line="+119"/> <source>Network Alert</source> <translation>Network Alert</translation> </message> </context> <context> + <name>CoinControlDialog</name> + <message> + <location filename="../forms/coincontroldialog.ui" line="+14"/> + <source>Coin Control Address Selection</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+34"/> + <source>Quantity:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+29"/> + <source>Bytes:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+45"/> + <source>Amount:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+29"/> + <source>Priority:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+45"/> + <source>Fee:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+32"/> + <source>Low Output:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+48"/> + <source>After Fee:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+32"/> + <source>Change:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+63"/> + <source>(un)select all</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+13"/> + <source>Tree mode</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+16"/> + <source>List mode</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+52"/> + <source>Amount</source> + <translation type="unfinished">Amount</translation> + </message> + <message> + <location line="+10"/> + <source>Address</source> + <translation type="unfinished">Address</translation> + </message> + <message> + <location line="+5"/> + <source>Date</source> + <translation type="unfinished">Date</translation> + </message> + <message> + <location line="+5"/> + <source>Confirmations</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> + <source>Confirmed</source> + <translation type="unfinished">Confirmed</translation> + </message> + <message> + <location line="+5"/> + <source>Priority</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../coincontroldialog.cpp" line="+42"/> + <source>Copy address</source> + <translation type="unfinished">Copy address</translation> + </message> + <message> + <location line="+1"/> + <source>Copy label</source> + <translation type="unfinished">Copy label</translation> + </message> + <message> + <location line="+1"/> + <location line="+26"/> + <source>Copy amount</source> + <translation type="unfinished">Copy amount</translation> + </message> + <message> + <location line="-25"/> + <source>Copy transaction ID</source> + <translation type="unfinished">Copy transaction ID</translation> + </message> + <message> + <location line="+1"/> + <source>Lock unspent</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Unlock unspent</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+22"/> + <source>Copy quantity</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source>Copy fee</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Copy after fee</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Copy bytes</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Copy priority</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Copy low output</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Copy change</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+323"/> + <source>highest</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>higher</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>high</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>medium-high</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>medium</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+4"/> + <source>low-medium</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>low</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>lower</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>lowest</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+11"/> + <source>(%1 locked)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+171"/> + <source>Dust</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+0"/> + <source>yes</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+0"/> + <source>no</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+10"/> + <source>This label turns red, if the transaction size is greater than 1000 bytes.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <location line="+5"/> + <source>This means a fee of at least %1 per kB is required.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="-4"/> + <source>Can vary +/- 1 byte per input.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source>Transactions with higher priority are more likely to get included into a block.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>This label turns red, if the priority is smaller than "medium"</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> + <source>This label turns red, if any recipient receives an amount smaller than %1.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <location line="+4"/> + <source>This means a fee of at least %1 is required.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="-3"/> + <source>Amounts below 0.546 times the minimum relay fee are shown as dust.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source>This label turns red, if the change is smaller than %1.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+43"/> + <location line="+66"/> + <source>(no label)</source> + <translation type="unfinished">(no label)</translation> + </message> + <message> + <location line="-9"/> + <source>change from %1 (%2)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>(change)</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> <name>EditAddressDialog</name> <message> <location filename="../forms/editaddressdialog.ui" line="+14"/> @@ -738,7 +1029,7 @@ Address: %4 <translation>&Address</translation> </message> <message> - <location filename="../editaddressdialog.cpp" line="+21"/> + <location filename="../editaddressdialog.cpp" line="+25"/> <source>New receiving address</source> <translation>New receiving address</translation> </message> @@ -781,7 +1072,7 @@ Address: %4 <context> <name>FreespaceChecker</name> <message> - <location filename="../intro.cpp" line="+61"/> + <location filename="../intro.cpp" line="+65"/> <source>A new data directory will be created.</source> <translation>A new data directory will be created.</translation> </message> @@ -809,18 +1100,18 @@ Address: %4 <context> <name>GUIUtil::HelpMessageBox</name> <message> - <location filename="../guiutil.cpp" line="+558"/> - <location line="+13"/> - <source>Bitcoin-Qt</source> - <translation>Bitcoin-Qt</translation> - </message> - <message> - <location line="-13"/> + <location filename="../guiutil.cpp" line="+597"/> <source>version</source> <translation>version</translation> </message> <message> - <location line="+2"/> + <location line="+0"/> + <location line="+13"/> + <source>Bitcoin Core</source> + <translation type="unfinished">Bitcoin Core</translation> + </message> + <message> + <location line="-11"/> <source>Usage:</source> <translation>Usage:</translation> </message> @@ -864,18 +1155,18 @@ Address: %4 </message> <message> <location line="+9"/> - <source>Welcome to Bitcoin-Qt.</source> - <translation>Welcome to Bitcoin-Qt.</translation> + <source>Welcome to Bitcoin Core.</source> + <translation type="unfinished"></translation> </message> <message> <location line="+26"/> - <source>As this is the first time the program is launched, you can choose where Bitcoin-Qt will store its data.</source> - <translation>As this is the first time the program is launched, you can choose where Bitcoin-Qt will store its data.</translation> + <source>As this is the first time the program is launched, you can choose where Bitcoin Core will store its data.</source> + <translation type="unfinished"></translation> </message> <message> <location line="+10"/> - <source>Bitcoin-Qt will download and store a copy of the Bitcoin block chain. At least %1GB of data will be stored in this directory, and it will grow over time. The wallet will also be stored in this directory.</source> - <translation>Bitcoin-Qt will download and store a copy of the Bitcoin block chain. At least %1GB of data will be stored in this directory, and it will grow over time. The wallet will also be stored in this directory.</translation> + <source>Bitcoin Core will download and store a copy of the Bitcoin block chain. At least %1GB of data will be stored in this directory, and it will grow over time. The wallet will also be stored in this directory.</source> + <translation type="unfinished"></translation> </message> <message> <location line="+10"/> @@ -888,7 +1179,17 @@ Address: %4 <translation>Use a custom data directory:</translation> </message> <message> - <location filename="../intro.cpp" line="+105"/> + <location filename="../intro.cpp" line="+85"/> + <source>Bitcoin</source> + <translation type="unfinished">Bitcoin</translation> + </message> + <message> + <location line="+1"/> + <source>Error: Specified data directory "%1" can not be created.</source> + <translation type="unfinished">Error: Specified data directory "%1" can not be created.</translation> + </message> + <message> + <location line="+19"/> <source>Error</source> <translation>Error</translation> </message> @@ -904,6 +1205,34 @@ Address: %4 </message> </context> <context> + <name>OpenURIDialog</name> + <message> + <location filename="../forms/openuridialog.ui" line="+14"/> + <source>Open URI</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+6"/> + <source>Open payment request from URI or file</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+9"/> + <source>URI:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+11"/> + <source>Select payment request file</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../openuridialog.cpp" line="+47"/> + <source>Select payment request file to open</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> <name>OptionsDialog</name> <message> <location filename="../forms/optionsdialog.ui" line="+14"/> @@ -1061,6 +1390,16 @@ Address: %4 <translation>&Display addresses in transaction list</translation> </message> <message> + <location line="+7"/> + <source>Whether to show coin control features or not.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> + <source>Display coin &control features (experts only)</source> + <translation type="unfinished"></translation> + </message> + <message> <location line="+71"/> <source>&OK</source> <translation>&OK</translation> @@ -1076,12 +1415,12 @@ Address: %4 <translation>&Apply</translation> </message> <message> - <location filename="../optionsdialog.cpp" line="+58"/> + <location filename="../optionsdialog.cpp" line="+63"/> <source>default</source> <translation>default</translation> </message> <message> - <location line="+130"/> + <location line="+131"/> <source>Confirm options reset</source> <translation>Confirm options reset</translation> </message> @@ -1122,17 +1461,17 @@ Address: %4 </message> <message> <location line="+50"/> - <location line="+202"/> + <location line="+214"/> <source>The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</source> <translation>The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</translation> </message> <message> - <location line="-131"/> + <location line="-140"/> <source>Unconfirmed:</source> <translation>Unconfirmed:</translation> </message> <message> - <location line="-78"/> + <location line="-81"/> <source>Wallet</source> <translation>Wallet</translation> </message> @@ -1147,12 +1486,12 @@ Address: %4 <translation>Your current spendable balance</translation> </message> <message> - <location line="+29"/> + <location line="+32"/> <source>Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</source> <translation>Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</translation> </message> <message> - <location line="+13"/> + <location line="+16"/> <source>Immature:</source> <translation>Immature:</translation> </message> @@ -1162,7 +1501,7 @@ Address: %4 <translation>Mined balance that has not yet matured</translation> </message> <message> - <location line="+13"/> + <location line="+16"/> <source>Total:</source> <translation>Total:</translation> </message> @@ -1172,12 +1511,12 @@ Address: %4 <translation>Your current total balance</translation> </message> <message> - <location line="+53"/> + <location line="+56"/> <source><b>Recent transactions</b></source> <translation><b>Recent transactions</b></translation> </message> <message> - <location filename="../overviewpage.cpp" line="+116"/> + <location filename="../overviewpage.cpp" line="+120"/> <location line="+1"/> <source>out of sync</source> <translation>out of sync</translation> @@ -1186,7 +1525,7 @@ Address: %4 <context> <name>PaymentServer</name> <message> - <location filename="../paymentserver.cpp" line="+392"/> + <location filename="../paymentserver.cpp" line="+397"/> <source>URI handling</source> <translation type="unfinished">URI handling</translation> </message> @@ -1196,23 +1535,29 @@ Address: %4 <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"/> + <location line="+91"/> <source>Requested payment amount of %1 is too small (considered dust).</source> <translation type="unfinished"></translation> </message> <message> - <location line="+4"/> - <location line="+37"/> + <location line="-204"/> + <location line="+195"/> + <location line="+13"/> <source>Payment request error</source> <translation type="unfinished"></translation> </message> <message> - <location line="+1"/> - <source>Insecure requests to custom payment scripts unsupported</source> + <location line="-207"/> + <source>Cannot start bitcoin: click-to-pay handler</source> <translation type="unfinished"></translation> </message> <message> - <location line="+38"/> + <location line="+195"/> + <source>Unverified payment requests to custom payment scripts are unsupported.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+59"/> <source>Refund from %1</source> <translation type="unfinished"></translation> </message> @@ -1222,7 +1567,7 @@ Address: %4 <translation type="unfinished"></translation> </message> <message> - <location line="+31"/> + <location line="+28"/> <source>Bad response from server %1</source> <translation type="unfinished"></translation> </message> @@ -1232,8 +1577,8 @@ Address: %4 <translation type="unfinished"></translation> </message> <message> - <location line="-58"/> - <location line="+30"/> + <location line="-55"/> + <location line="+27"/> <location line="+17"/> <source>Network request error</source> <translation type="unfinished"></translation> @@ -1242,9 +1587,8 @@ Address: %4 <context> <name>QObject</name> <message> - <location filename="../bitcoin.cpp" line="+114"/> + <location filename="../bitcoin.cpp" line="+119"/> <location line="+5"/> - <location filename="../intro.cpp" line="-32"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -1258,16 +1602,11 @@ Address: %4 <source>Error: Invalid combination of -regtest and -testnet.</source> <translation type="unfinished"></translation> </message> - <message> - <location filename="../intro.cpp" line="+1"/> - <source>Error: Specified data directory "%1" can not be created.</source> - <translation>Error: Specified data directory "%1" can not be created.</translation> - </message> </context> <context> <name>QRImageWidget</name> <message> - <location filename="../receiverequestdialog.cpp" line="+32"/> + <location filename="../receiverequestdialog.cpp" line="+36"/> <source>&Save Image...</source> <translation type="unfinished"></translation> </message> @@ -1283,8 +1622,8 @@ Address: %4 </message> <message> <location line="+0"/> - <source>PNG Images (*.png)</source> - <translation type="unfinished">PNG Images (*.png)</translation> + <source>PNG Image (*.png)</source> + <translation type="unfinished"></translation> </message> </context> <context> @@ -1304,7 +1643,7 @@ Address: %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+352"/> + <location filename="../rpcconsole.cpp" line="+360"/> <source>N/A</source> <translation>N/A</translation> </message> @@ -1319,7 +1658,12 @@ Address: %4 <translation>&Information</translation> </message> <message> - <location line="+68"/> + <location line="-10"/> + <source>Debug window</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+78"/> <source>Using OpenSSL version</source> <translation>Using OpenSSL version</translation> </message> @@ -1374,12 +1718,7 @@ Address: %4 <translation>Command-line options</translation> </message> <message> - <location line="+7"/> - <source>Show the Bitcoin-Qt help message to get a list with possible Bitcoin command-line options.</source> - <translation>Show the Bitcoin-Qt help message to get a list with possible Bitcoin command-line options.</translation> - </message> - <message> - <location line="+3"/> + <location line="+10"/> <source>&Show</source> <translation>&Show</translation> </message> @@ -1419,12 +1758,7 @@ Address: %4 <translation>Build date</translation> </message> <message> - <location line="-104"/> - <source>Bitcoin - Debug window</source> - <translation>Bitcoin - Debug window</translation> - </message> - <message> - <location line="+25"/> + <location line="-79"/> <source>Bitcoin Core</source> <translation>Bitcoin Core</translation> </message> @@ -1439,7 +1773,12 @@ Address: %4 <translation>Open the Bitcoin debug log file from the current data directory. This can take a few seconds for large log files.</translation> </message> <message> - <location line="+102"/> + <location line="+26"/> + <source>Show the Bitcoin-Core help message to get a list with possible Bitcoin command-line options.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+76"/> <source>Clear console</source> <translation>Clear console</translation> </message> @@ -1497,37 +1836,32 @@ Address: %4 <context> <name>ReceiveCoinsDialog</name> <message> - <location filename="../forms/receivecoinsdialog.ui" line="+22"/> + <location filename="../forms/receivecoinsdialog.ui" line="+83"/> <source>&Amount:</source> <translation type="unfinished"></translation> </message> <message> - <location line="+16"/> + <location line="+19"/> <source>The amount to request</source> <translation type="unfinished"></translation> </message> <message> - <location line="+7"/> + <location line="-32"/> <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"/> + <location line="-34"/> <source>&Message:</source> <translation type="unfinished"></translation> </message> <message> - <location line="+10"/> + <location line="+20"/> <source>The message to attach to payment request</source> <translation type="unfinished"></translation> </message> <message> - <location line="+14"/> + <location line="-37"/> <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> @@ -1537,12 +1871,17 @@ Address: %4 <translation type="unfinished"></translation> </message> <message> - <location line="+7"/> + <location line="+27"/> + <source>The label to associate with the new receiving address</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+14"/> <source>Use this form to request payments. All fields are optional.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+17"/> + <location line="+56"/> <source>Clear all fields of the form.</source> <translation type="unfinished"></translation> </message> @@ -1556,6 +1895,31 @@ Address: %4 <source>&Request payment</source> <translation type="unfinished"></translation> </message> + <message> + <location line="+47"/> + <source>Previously requested payments</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+12"/> + <source>Show the selected request (does the same as double clicking an entry)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> + <source>Show</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+11"/> + <source>Remove the selected entries from the list</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> + <source>Remove</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>ReceiveRequestDialog</name> @@ -1576,16 +1940,11 @@ Address: %4 </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"/> + <location filename="../receiverequestdialog.cpp" line="+56"/> <source>Request payment to %1</source> <translation type="unfinished"></translation> </message> @@ -1631,16 +1990,119 @@ Address: %4 </message> </context> <context> + <name>RecentRequestsTableModel</name> + <message> + <location filename="../recentrequeststablemodel.cpp" line="+14"/> + <source>Date</source> + <translation type="unfinished">Date</translation> + </message> + <message> + <location line="+0"/> + <source>Label</source> + <translation type="unfinished">Label</translation> + </message> + <message> + <location line="+0"/> + <source>Message</source> + <translation type="unfinished">Message</translation> + </message> + <message> + <location line="+0"/> + <source>Amount</source> + <translation type="unfinished">Amount</translation> + </message> + <message> + <location line="+36"/> + <source>(no label)</source> + <translation type="unfinished">(no label)</translation> + </message> + <message> + <location line="+9"/> + <source>(no message)</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> <name>SendCoinsDialog</name> <message> <location filename="../forms/sendcoinsdialog.ui" line="+14"/> - <location filename="../sendcoinsdialog.cpp" line="+140"/> - <location line="+213"/> + <location filename="../sendcoinsdialog.cpp" line="+381"/> + <location line="+81"/> <source>Send Coins</source> <translation>Send Coins</translation> </message> <message> - <location line="+50"/> + <location line="+76"/> + <source>Coin Control Features</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+20"/> + <source>Inputs...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+7"/> + <source>automatically selected</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+19"/> + <source>Insufficient funds!</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+89"/> + <source>Quantity:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+35"/> + <source>Bytes:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+48"/> + <source>Amount:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+32"/> + <source>Priority:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+48"/> + <source>Fee:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+32"/> + <source>Low Output:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+48"/> + <source>After Fee:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+32"/> + <source>Change:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+44"/> + <source>If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> + <source>Custom change address</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+115"/> <source>Send to multiple recipients at once</source> <translation>Send to multiple recipients at once</translation> </message> @@ -1665,12 +2127,7 @@ Address: %4 <translation>Balance:</translation> </message> <message> - <location line="+10"/> - <source>123.456 BTC</source> - <translation>123.456 BTC</translation> - </message> - <message> - <location line="+31"/> + <location line="+41"/> <source>Confirm the send action</source> <translation>Confirm the send action</translation> </message> @@ -1680,69 +2137,135 @@ Address: %4 <translation>S&end</translation> </message> <message> - <location filename="../sendcoinsdialog.cpp" line="-154"/> + <location filename="../sendcoinsdialog.cpp" line="-229"/> <source>Confirm send coins</source> <translation>Confirm send coins</translation> </message> <message> - <location line="-90"/> + <location line="-74"/> <location line="+5"/> <location line="+5"/> + <location line="+4"/> <source>%1 to %2</source> <translation type="unfinished"></translation> </message> <message> - <location line="+26"/> + <location line="-136"/> + <source>Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</source> + <translation type="unfinished">Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</translation> + </message> + <message> + <location line="+15"/> + <source>Copy quantity</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Copy amount</source> + <translation type="unfinished">Copy amount</translation> + </message> + <message> + <location line="+1"/> + <source>Copy fee</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Copy after fee</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Copy bytes</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Copy priority</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Copy low output</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Copy change</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+170"/> + <source>Total Amount %1 (= %2)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source>or</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+202"/> <source>The recipient address is not valid, please recheck.</source> <translation>The recipient address is not valid, please recheck.</translation> </message> <message> - <location line="+4"/> + <location line="+3"/> <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="+4"/> + <location line="+3"/> <source>The amount exceeds your balance.</source> <translation>The amount exceeds your balance.</translation> </message> <message> - <location line="+4"/> + <location line="+3"/> <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="+5"/> + <location line="+3"/> <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="+3"/> + <source>Transaction creation failed!</source> + <translation type="unfinished"></translation> + </message> + <message> <location line="+4"/> - <source>Error: Transaction creation failed!</source> - <translation>Error: Transaction creation failed!</translation> + <source>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.</source> + <translation type="unfinished"></translation> </message> <message> - <location line="+15"/> - <source>Are you sure you want to send?</source> + <location line="+113"/> + <source>Warning: Invalid Bitcoin address</source> <translation type="unfinished"></translation> </message> <message> - <location line="+9"/> - <source>added as transaction fee</source> + <location line="+13"/> + <source>(no label)</source> + <translation type="unfinished">(no label)</translation> + </message> + <message> + <location line="+8"/> + <source>Warning: Unknown change address</source> <translation type="unfinished"></translation> </message> <message> - <location line="+6"/> - <source>Total Amount %1</source> + <location line="-378"/> + <source>Are you sure you want to send?</source> <translation type="unfinished"></translation> </message> <message> - <location line="+20"/> - <source>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.</source> - <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> + <location line="+9"/> + <source>added as transaction fee</source> + <translation type="unfinished"></translation> </message> <message> - <location line="+144"/> + <location line="+170"/> <source>Payment request expired</source> <translation type="unfinished"></translation> </message> @@ -1755,39 +2278,44 @@ Address: %4 <context> <name>SendCoinsEntry</name> <message> - <location filename="../forms/sendcoinsentry.ui" line="+33"/> - <location line="+585"/> + <location filename="../forms/sendcoinsentry.ui" line="+131"/> + <location line="+501"/> + <location line="+536"/> <source>A&mount:</source> <translation>A&mount:</translation> </message> <message> - <location line="-572"/> - <location line="+585"/> + <location line="-1132"/> <source>Pay &To:</source> <translation>Pay &To:</translation> </message> <message> - <location line="-551"/> + <location line="+18"/> <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 filename="../sendcoinsentry.cpp" line="+28"/> + <location filename="../sendcoinsentry.cpp" line="+30"/> <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 filename="../forms/sendcoinsentry.ui" line="-18"/> + <location filename="../forms/sendcoinsentry.ui" line="+57"/> <source>&Label:</source> <translation>&Label:</translation> </message> <message> - <location line="+28"/> + <location line="-50"/> <source>Choose previously used address</source> <translation type="unfinished"></translation> </message> <message> - <location line="+10"/> + <location line="-40"/> + <source>This is a normal payment.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+50"/> <source>Alt+A</source> <translation>Alt+A</translation> </message> @@ -1803,16 +2331,35 @@ Address: %4 </message> <message> <location line="+7"/> - <source>Remove this recipient</source> - <translation>Remove this recipient</translation> + <location line="+504"/> + <location line="+536"/> + <source>Remove this entry</source> + <translation type="unfinished"></translation> </message> <message> - <location line="+16"/> + <location line="-40"/> + <source>This is a verified payment request.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="-971"/> <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="+465"/> + <location line="+439"/> + <source>This is an unverified payment request.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+18"/> + <location line="+532"/> + <source>Pay To:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="-498"/> + <location line="+536"/> <source>Memo:</source> <translation type="unfinished"></translation> </message> @@ -1933,7 +2480,7 @@ Address: %4 <translation>Reset all verify message fields</translation> </message> <message> - <location filename="../signverifymessagedialog.cpp" line="+27"/> + <location filename="../signverifymessagedialog.cpp" line="+29"/> <location line="+3"/> <source>Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</source> <translation>Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</translation> @@ -1949,7 +2496,7 @@ Address: %4 <translation>Enter Bitcoin signature</translation> </message> <message> - <location line="+85"/> + <location line="+84"/> <location line="+81"/> <source>The entered address is invalid.</source> <translation>The entered address is invalid.</translation> @@ -2018,9 +2565,14 @@ Address: %4 <context> <name>SplashScreen</name> <message> - <location filename="../splashscreen.cpp" line="+23"/> - <source>The Bitcoin developers</source> - <translation>The Bitcoin developers</translation> + <location filename="../splashscreen.cpp" line="+26"/> + <source>Bitcoin Core</source> + <translation type="unfinished">Bitcoin Core</translation> + </message> + <message> + <location line="+2"/> + <source>The Bitcoin Core developers</source> + <translation type="unfinished"></translation> </message> <message> <location line="+1"/> @@ -2031,7 +2583,7 @@ Address: %4 <context> <name>TrafficGraphWidget</name> <message> - <location filename="../trafficgraphwidget.cpp" line="+75"/> + <location filename="../trafficgraphwidget.cpp" line="+79"/> <source>KB/s</source> <translation type="unfinished"></translation> </message> @@ -2039,7 +2591,7 @@ Address: %4 <context> <name>TransactionDesc</name> <message> - <location filename="../transactiondesc.cpp" line="+22"/> + <location filename="../transactiondesc.cpp" line="+28"/> <source>Open until %1</source> <translation>Open until %1</translation> </message> @@ -2240,7 +2792,7 @@ Address: %4 <context> <name>TransactionTableModel</name> <message> - <location filename="../transactiontablemodel.cpp" line="+227"/> + <location filename="../transactiontablemodel.cpp" line="+234"/> <source>Date</source> <translation>Date</translation> </message> @@ -2364,7 +2916,7 @@ Address: %4 <context> <name>TransactionView</name> <message> - <location filename="../transactionview.cpp" line="+52"/> + <location filename="../transactionview.cpp" line="+57"/> <location line="+16"/> <source>All</source> <translation>All</translation> @@ -2425,12 +2977,12 @@ Address: %4 <translation>Other</translation> </message> <message> - <location line="+7"/> + <location line="+6"/> <source>Enter address or label to search</source> <translation>Enter address or label to search</translation> </message> <message> - <location line="+7"/> + <location line="+6"/> <source>Min amount</source> <translation>Min amount</translation> </message> @@ -2465,17 +3017,37 @@ Address: %4 <translation>Show transaction details</translation> </message> <message> - <location line="+143"/> - <source>Export Transaction Data</source> - <translation>Export Transaction Data</translation> + <location line="+142"/> + <source>Export Transaction History</source> + <translation type="unfinished"></translation> </message> <message> - <location line="+1"/> + <location line="+19"/> + <source>Exporting Failed</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+0"/> + <source>There was an error trying to save the transaction history to %1.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+4"/> + <source>Exporting Successful</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+0"/> + <source>The transaction history was successfully saved to %1.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="-22"/> <source>Comma separated file (*.csv)</source> <translation>Comma separated file (*.csv)</translation> </message> <message> - <location line="+8"/> + <location line="+9"/> <source>Confirmed</source> <translation>Confirmed</translation> </message> @@ -2510,17 +3082,7 @@ Address: %4 <translation>ID</translation> </message> <message> - <location line="+4"/> - <source>Error exporting</source> - <translation>Error exporting</translation> - </message> - <message> - <location line="+0"/> - <source>Could not write to file %1.</source> - <translation>Could not write to file %1.</translation> - </message> - <message> - <location line="+100"/> + <location line="+107"/> <source>Range:</source> <translation>Range:</translation> </message> @@ -2531,9 +3093,17 @@ Address: %4 </message> </context> <context> + <name>WalletFrame</name> + <message> + <location filename="../walletframe.cpp" line="+26"/> + <source>No wallet has been loaded.</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> <name>WalletModel</name> <message> - <location filename="../walletmodel.cpp" line="+218"/> + <location filename="../walletmodel.cpp" line="+245"/> <source>Send Coins</source> <translation>Send Coins</translation> </message> @@ -2541,7 +3111,7 @@ Address: %4 <context> <name>WalletView</name> <message> - <location filename="../walletview.cpp" line="+46"/> + <location filename="../walletview.cpp" line="+43"/> <source>&Export</source> <translation>&Export</translation> </message> @@ -2551,65 +3121,60 @@ Address: %4 <translation>Export the data in the current tab to a file</translation> </message> <message> - <location line="+183"/> + <location line="+181"/> <source>Backup Wallet</source> <translation>Backup Wallet</translation> </message> <message> - <location line="+0"/> + <location line="+1"/> <source>Wallet Data (*.dat)</source> <translation>Wallet Data (*.dat)</translation> </message> <message> - <location line="+3"/> + <location line="+6"/> <source>Backup Failed</source> <translation>Backup Failed</translation> </message> <message> <location line="+0"/> - <source>There was an error trying to save the wallet data to the new location.</source> - <translation>There was an error trying to save the wallet data to the new location.</translation> + <source>There was an error trying to save the wallet data to %1.</source> + <translation type="unfinished"></translation> </message> <message> <location line="+4"/> - <source>Backup Successful</source> - <translation>Backup Successful</translation> + <source>The wallet data was successfully saved to %1.</source> + <translation type="unfinished"></translation> </message> <message> <location line="+0"/> - <source>The wallet data was successfully saved to the new location.</source> - <translation>The wallet data was successfully saved to the new location.</translation> + <source>Backup Successful</source> + <translation>Backup Successful</translation> </message> </context> <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+102"/> - <source>Bitcoin version</source> - <translation>Bitcoin version</translation> - </message> - <message> - <location line="+107"/> + <location filename="../bitcoinstrings.cpp" line="+217"/> <source>Usage:</source> <translation>Usage:</translation> </message> <message> - <location line="-55"/> + <location line="-54"/> <source>List commands</source> <translation>List commands</translation> </message> <message> - <location line="-13"/> + <location line="-14"/> <source>Get help for a command</source> <translation>Get help for a command</translation> </message> <message> - <location line="+25"/> + <location line="+26"/> <source>Options:</source> <translation>Options:</translation> </message> <message> - <location line="+24"/> + <location line="+23"/> <source>Specify configuration file (default: bitcoin.conf)</source> <translation>Specify configuration file (default: bitcoin.conf)</translation> </message> @@ -2629,7 +3194,7 @@ Address: %4 <translation>Set database cache size in megabytes (default: 25)</translation> </message> <message> - <location line="-28"/> + <location line="-27"/> <source>Listen for connections on <port> (default: 8333 or testnet: 18333)</source> <translation>Listen for connections on <port> (default: 8333 or testnet: 18333)</translation> </message> @@ -2639,7 +3204,7 @@ Address: %4 <translation>Maintain at most <n> connections to peers (default: 125)</translation> </message> <message> - <location line="-49"/> + <location line="-50"/> <source>Connect to a node to retrieve peer addresses, and disconnect</source> <translation>Connect to a node to retrieve peer addresses, and disconnect</translation> </message> @@ -2654,7 +3219,7 @@ Address: %4 <translation>Threshold for disconnecting misbehaving peers (default: 100)</translation> </message> <message> - <location line="-142"/> + <location line="-147"/> <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> @@ -2669,7 +3234,7 @@ Address: %4 <translation>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</translation> </message> <message> - <location line="+40"/> + <location line="+45"/> <source>Accept command line and JSON-RPC commands</source> <translation>Accept command line and JSON-RPC commands</translation> </message> @@ -2679,17 +3244,17 @@ Address: %4 <translation>Run in the background as a daemon and accept commands</translation> </message> <message> - <location line="+40"/> + <location line="+39"/> <source>Use the test network</source> <translation>Use the test network</translation> </message> <message> - <location line="-118"/> + <location line="-117"/> <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> <message> - <location line="-87"/> + <location line="-92"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2754,17 +3319,17 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)</translation> </message> <message> - <location line="+11"/> - <source>Set maximum size of high-priority/low-fee transactions in bytes (default: 27000)</source> - <translation>Set maximum size of high-priority/low-fee transactions in bytes (default: 27000)</translation> - </message> - <message> - <location line="+6"/> + <location line="+18"/> <source>This is a pre-release test build - use at your own risk - do not use for mining or merchant applications</source> <translation>This is a pre-release test build - use at your own risk - do not use for mining or merchant applications</translation> </message> <message> <location line="+5"/> + <source>Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: -proxy)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> <source>Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction.</source> <translation>Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction.</translation> </message> @@ -2794,17 +3359,27 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup.</translation> </message> <message> - <location line="+14"/> + <location line="+9"/> + <source><category> can be:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+6"/> <source>Attempt to recover private keys from a corrupt wallet.dat</source> <translation>Attempt to recover private keys from a corrupt wallet.dat</translation> </message> <message> <location line="+1"/> + <source>Bitcoin Core Daemon</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> <source>Bitcoin RPC client version</source> <translation type="unfinished"></translation> </message> <message> - <location line="+2"/> + <location line="+1"/> <source>Block creation options:</source> <translation>Block creation options:</translation> </message> @@ -2814,7 +3389,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="+2"/> + <location line="+1"/> + <source>Connect through SOCKS proxy</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> <source>Connect to JSON-RPC on <port> (default: 8332 or testnet: 18332)</source> <translation type="unfinished"></translation> </message> @@ -2924,7 +3504,12 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Failed to write undo data</translation> </message> <message> - <location line="+2"/> + <location line="+1"/> + <source>Fee per kB to add to transactions you send</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> <source>Find peers using DNS lookup (default: 1 unless -connect)</source> <translation>Find peers using DNS lookup (default: 1 unless -connect)</translation> </message> @@ -2944,6 +3529,11 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>How thorough the block verification is (0-4, default: 3)</translation> </message> <message> + <location line="+1"/> + <source>If <category> is not supplied, output all debugging information.</source> + <translation type="unfinished"></translation> + </message> + <message> <location line="+2"/> <source>Incorrect or no genesis block found. Wrong datadir for network?</source> <translation>Incorrect or no genesis block found. Wrong datadir for network?</translation> @@ -2959,17 +3549,37 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Not enough file descriptors available.</translation> </message> <message> - <location line="+8"/> + <location line="+5"/> + <source>Prepend debug output with timestamp (default: 1)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>RPC client options:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> <source>Rebuild block chain index from current blk000??.dat files</source> <translation>Rebuild block chain index from current blk000??.dat files</translation> </message> <message> - <location line="+6"/> + <location line="+5"/> + <source>Select SOCKS version for -proxy (4 or 5, default: 5)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> <source>Send command to Bitcoin server</source> <translation type="unfinished"></translation> </message> <message> - <location line="+10"/> + <location line="+8"/> + <source>Set maximum block size in bytes (default: %d)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> <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> @@ -2989,7 +3599,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation type="unfinished"></translation> </message> <message> - <location line="+8"/> + <location line="+7"/> <source>Verifying blocks...</source> <translation>Verifying blocks...</translation> </message> @@ -3000,11 +3610,21 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. </message> <message> <location line="+1"/> + <source>Wait for RPC server to start</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> <source>Wallet %s resides outside data directory %s</source> <translation>Wallet %s resides outside data directory %s</translation> </message> <message> - <location line="+4"/> + <location line="+3"/> + <source>Warning: Deprecated argument -debugnet ignored, use -debug=net</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> <source>You need to rebuild the database using -reindex to change -txindex</source> <translation>You need to rebuild the database using -reindex to change -txindex</translation> </message> @@ -3014,17 +3634,27 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Imports blocks from external blk000??.dat file</translation> </message> <message> - <location line="-98"/> + <location line="-104"/> <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> <message> - <location line="+17"/> + <location line="+14"/> + <source>Output debugging information (default: 0, supplying <category> is optional)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source>Set maximum size of high-priority/low-fee transactions in bytes (default: %d)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> <source>Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0)</source> <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="+83"/> + <location line="+88"/> <source>Information</source> <translation>Information</translation> </message> @@ -3064,32 +3694,12 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Only connect to nodes in network <net> (IPv4, IPv6 or Tor)</translation> </message> <message> - <location line="+2"/> - <source>Output extra debugging information. Implies all other -debug* options</source> - <translation>Output extra debugging information. Implies all other -debug* options</translation> - </message> - <message> - <location line="+1"/> - <source>Output extra network debugging information</source> - <translation>Output extra network debugging information</translation> - </message> - <message> - <location line="+2"/> - <source>Prepend debug output with timestamp</source> - <translation>Prepend debug output with timestamp</translation> - </message> - <message> - <location line="+5"/> + <location line="+9"/> <source>SSL options: (see the Bitcoin Wiki for SSL setup instructions)</source> <translation>SSL options: (see the Bitcoin Wiki for SSL setup instructions)</translation> </message> <message> - <location line="+1"/> - <source>Select the version of socks proxy to use (4-5, default: 5)</source> - <translation>Select the version of socks proxy to use (4-5, default: 5)</translation> - </message> - <message> - <location line="+3"/> + <location line="+4"/> <source>Send trace/debug info to console instead of debug.log file</source> <translation>Send trace/debug info to console instead of debug.log file</translation> </message> @@ -3099,12 +3709,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Send trace/debug info to debugger</translation> </message> <message> - <location line="+5"/> - <source>Set maximum block size in bytes (default: 250000)</source> - <translation>Set maximum block size in bytes (default: 250000)</translation> - </message> - <message> - <location line="+1"/> + <location line="+6"/> <source>Set minimum block size in bytes (default: 0)</source> <translation>Set minimum block size in bytes (default: 0)</translation> </message> @@ -3154,37 +3759,37 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Use UPnP to map the listening port (default: 1 when listening)</translation> </message> <message> - <location line="+1"/> - <source>Use proxy to reach tor hidden services (default: same as -proxy)</source> - <translation>Use proxy to reach tor hidden services (default: same as -proxy)</translation> - </message> - <message> <location line="+2"/> <source>Username for JSON-RPC connections</source> <translation>Username for JSON-RPC connections</translation> </message> <message> - <location line="+5"/> + <location line="+6"/> <source>Warning</source> <translation>Warning</translation> </message> <message> - <location line="+1"/> + <location line="+2"/> <source>Warning: This version is obsolete, upgrade required!</source> <translation>Warning: This version is obsolete, upgrade required!</translation> </message> <message> <location line="+2"/> + <source>version</source> + <translation type="unfinished">version</translation> + </message> + <message> + <location line="+1"/> <source>wallet.dat corrupt, salvage failed</source> <translation>wallet.dat corrupt, salvage failed</translation> </message> <message> - <location line="-54"/> + <location line="-57"/> <source>Password for JSON-RPC connections</source> <translation>Password for JSON-RPC connections</translation> </message> <message> - <location line="-70"/> + <location line="-69"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>Allow JSON-RPC connections from specified IP address</translation> </message> @@ -3194,12 +3799,12 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Send commands to node running on <ip> (default: 127.0.0.1)</translation> </message> <message> - <location line="-126"/> + <location line="-131"/> <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="+155"/> + <location line="+160"/> <source>Upgrade wallet to latest format</source> <translation>Upgrade wallet to latest format</translation> </message> @@ -3239,22 +3844,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="-95"/> - <source>Connect through socks proxy</source> - <translation>Connect through socks proxy</translation> - </message> - <message> - <location line="-11"/> + <location line="-106"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>Allow DNS lookups for -addnode, -seednode and -connect</translation> </message> <message> - <location line="+58"/> + <location line="+59"/> <source>Loading addresses...</source> <translation>Loading addresses...</translation> </message> <message> - <location line="-36"/> + <location line="-37"/> <source>Error loading wallet.dat: Wallet corrupted</source> <translation>Error loading wallet.dat: Wallet corrupted</translation> </message> @@ -3274,12 +3874,12 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Error loading wallet.dat</translation> </message> <message> - <location line="+30"/> + <location line="+31"/> <source>Invalid -proxy address: '%s'</source> <translation>Invalid -proxy address: '%s'</translation> </message> <message> - <location line="+57"/> + <location line="+56"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation>Unknown network specified in -onlynet: '%s'</translation> </message> @@ -3299,7 +3899,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Cannot resolve -externalip address: '%s'</translation> </message> <message> - <location line="+46"/> + <location line="+47"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>Invalid amount for -paytxfee=<amount>: '%s'</translation> </message> @@ -3319,27 +3919,22 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Loading block index...</translation> </message> <message> - <location line="-60"/> + <location line="-61"/> <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> <message> - <location line="-28"/> + <location line="-32"/> <source>Unable to bind to %s on this computer. Bitcoin is probably already running.</source> <translation>Unable to bind to %s on this computer. Bitcoin is probably already running.</translation> </message> <message> - <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> - <message> - <location line="+20"/> + <location line="+94"/> <source>Loading wallet...</source> <translation>Loading wallet...</translation> </message> <message> - <location line="-54"/> + <location line="-55"/> <source>Cannot downgrade wallet</source> <translation>Cannot downgrade wallet</translation> </message> @@ -3369,7 +3964,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Error</translation> </message> <message> - <location line="-33"/> + <location line="-34"/> <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/macdockiconhandler.mm b/src/qt/macdockiconhandler.mm index 86b8c834d4..64291c9188 100644 --- a/src/qt/macdockiconhandler.mm +++ b/src/qt/macdockiconhandler.mm @@ -1,9 +1,13 @@ +// Copyright (c) 2011-2013 The Bitcoin Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #include "macdockiconhandler.h" +#include <QImageWriter> #include <QMenu> -#include <QWidget> #include <QTemporaryFile> -#include <QImageWriter> +#include <QWidget> #undef slots #include <Cocoa/Cocoa.h> diff --git a/src/qt/macnotificationhandler.mm b/src/qt/macnotificationhandler.mm index 8bb9b887a1..8a4c94cc5c 100644 --- a/src/qt/macnotificationhandler.mm +++ b/src/qt/macnotificationhandler.mm @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2013 The Bitcoin Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #include "macnotificationhandler.h" #undef slots diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp index c9f6834ec2..1cef397d12 100644 --- a/src/qt/notificator.cpp +++ b/src/qt/notificator.cpp @@ -4,7 +4,6 @@ #include "notificator.h" - #include <QApplication> #include <QByteArray> #include <QIcon> @@ -32,7 +31,7 @@ // https://wiki.ubuntu.com/NotificationDevelopmentGuidelines recommends at least 128 const int FREEDESKTOP_NOTIFICATION_ICON_SIZE = 128; -Notificator::Notificator(const QString &programName, QSystemTrayIcon *trayicon, QWidget *parent): +Notificator::Notificator(const QString &programName, QSystemTrayIcon *trayicon, QWidget *parent) : QObject(parent), parent(parent), programName(programName), @@ -48,7 +47,7 @@ Notificator::Notificator(const QString &programName, QSystemTrayIcon *trayicon, } #ifdef USE_DBUS interface = new QDBusInterface("org.freedesktop.Notifications", - "/org/freedesktop/Notifications", "org.freedesktop.Notifications"); + "/org/freedesktop/Notifications", "org.freedesktop.Notifications"); if(interface->isValid()) { mode = Freedesktop; diff --git a/src/qt/notificator.h b/src/qt/notificator.h index d6298c3c0d..abab986992 100644 --- a/src/qt/notificator.h +++ b/src/qt/notificator.h @@ -29,7 +29,7 @@ public: /** Create a new notificator. @note Ownership of trayIcon is not transferred to this object. */ - Notificator(const QString &programName=QString(), QSystemTrayIcon *trayIcon=0, QWidget *parent=0); + Notificator(const QString &programName, QSystemTrayIcon *trayIcon, QWidget *parent); ~Notificator(); // Message class diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 15a873d2bd..363f432d62 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -14,6 +14,7 @@ #include "init.h" #include "main.h" #include "net.h" +#include "wallet.h" #include "walletdb.h" #include <QSettings> diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp index 45b2325299..075a16dabf 100644 --- a/src/qt/receivecoinsdialog.cpp +++ b/src/qt/receivecoinsdialog.cpp @@ -12,6 +12,7 @@ #include "guiutil.h" #include "receiverequestdialog.h" #include "addresstablemodel.h" +#include "recentrequeststablemodel.h" #include <QMessageBox> #include <QTextDocument> @@ -27,6 +28,8 @@ ReceiveCoinsDialog::ReceiveCoinsDialog(QWidget *parent) : #ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac ui->clearButton->setIcon(QIcon()); ui->receiveButton->setIcon(QIcon()); + ui->showRequestButton->setIcon(QIcon()); + ui->removeRequestButton->setIcon(QIcon()); #endif connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear())); } @@ -39,6 +42,19 @@ void ReceiveCoinsDialog::setModel(WalletModel *model) { connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); updateDisplayUnit(); + + ui->recentRequestsView->setModel(model->getRecentRequestsTableModel()); + ui->recentRequestsView->setAlternatingRowColors(true); + ui->recentRequestsView->setSelectionBehavior(QAbstractItemView::SelectRows); + ui->recentRequestsView->setSelectionMode(QAbstractItemView::ContiguousSelection); + ui->recentRequestsView->horizontalHeader()->resizeSection(RecentRequestsTableModel::Date, 130); + ui->recentRequestsView->horizontalHeader()->resizeSection(RecentRequestsTableModel::Label, 120); +#if QT_VERSION < 0x050000 + ui->recentRequestsView->horizontalHeader()->setResizeMode(RecentRequestsTableModel::Message, QHeaderView::Stretch); +#else + ui->recentRequestsView->horizontalHeader()->setSectionResizeMode(RecentRequestsTableModel::Message, QHeaderView::Stretch); +#endif + ui->recentRequestsView->horizontalHeader()->resizeSection(RecentRequestsTableModel::Amount, 100); } } @@ -76,7 +92,7 @@ void ReceiveCoinsDialog::updateDisplayUnit() void ReceiveCoinsDialog::on_receiveButton_clicked() { - if(!model || !model->getOptionsModel() || !model->getAddressTableModel()) + if(!model || !model->getOptionsModel() || !model->getAddressTableModel() || !model->getRecentRequestsTableModel()) return; QString address; @@ -101,11 +117,48 @@ void ReceiveCoinsDialog::on_receiveButton_clicked() address = model->getAddressTableModel()->addRow(AddressTableModel::Receive, label, ""); } SendCoinsRecipient info(address, label, - ui->reqAmount->value(), ui->reqMessage->text()); + ui->reqAmount->value(), ui->reqMessage->text()); ReceiveRequestDialog *dialog = new ReceiveRequestDialog(this); + dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->setModel(model->getOptionsModel()); dialog->setInfo(info); - dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); clear(); + + /* Store request for later reference */ + model->getRecentRequestsTableModel()->addNewRequest(info); +} + +void ReceiveCoinsDialog::on_recentRequestsView_doubleClicked(const QModelIndex &index) +{ + const RecentRequestsTableModel *submodel = model->getRecentRequestsTableModel(); + ReceiveRequestDialog *dialog = new ReceiveRequestDialog(this); + dialog->setModel(model->getOptionsModel()); + dialog->setInfo(submodel->entry(index.row()).recipient); + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->show(); +} + +void ReceiveCoinsDialog::on_showRequestButton_clicked() +{ + if(!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel()) + return; + QModelIndexList selection = ui->recentRequestsView->selectionModel()->selectedRows(); + + foreach (QModelIndex index, selection) + { + on_recentRequestsView_doubleClicked(index); + } +} + +void ReceiveCoinsDialog::on_removeRequestButton_clicked() +{ + if(!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel()) + return; + QModelIndexList selection = ui->recentRequestsView->selectionModel()->selectedRows(); + if(selection.empty()) + return; + // correct for selection mode ContiguousSelection + QModelIndex firstIndex = selection.at(0); + model->getRecentRequestsTableModel()->removeRows(firstIndex.row(), selection.length(), firstIndex.parent()); } diff --git a/src/qt/receivecoinsdialog.h b/src/qt/receivecoinsdialog.h index 9980edd1f5..ed4b04d361 100644 --- a/src/qt/receivecoinsdialog.h +++ b/src/qt/receivecoinsdialog.h @@ -14,6 +14,10 @@ namespace Ui { class WalletModel; class OptionsModel; +QT_BEGIN_NAMESPACE +class QModelIndex; +QT_END_NAMESPACE + /** Dialog for requesting payment of bitcoins */ class ReceiveCoinsDialog : public QDialog { @@ -36,6 +40,9 @@ private: private slots: void on_receiveButton_clicked(); + void on_showRequestButton_clicked(); + void on_removeRequestButton_clicked(); + void on_recentRequestsView_doubleClicked(const QModelIndex &index); void updateDisplayUnit(); }; diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp index 7e92715df8..b5e45341d9 100644 --- a/src/qt/receiverequestdialog.cpp +++ b/src/qt/receiverequestdialog.cpp @@ -85,12 +85,10 @@ ReceiveRequestDialog::ReceiveRequestDialog(QWidget *parent) : #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() diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp new file mode 100644 index 0000000000..86c29dd02b --- /dev/null +++ b/src/qt/recentrequeststablemodel.cpp @@ -0,0 +1,129 @@ +// Copyright (c) 2011-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 "recentrequeststablemodel.h" + +#include "bitcoinunits.h" +#include "guiutil.h" +#include "optionsmodel.h" + +RecentRequestsTableModel::RecentRequestsTableModel(CWallet *wallet, WalletModel *parent) : + walletModel(parent) +{ + Q_UNUSED(wallet); + + /* These columns must match the indices in the ColumnIndex enumeration */ + columns << tr("Date") << tr("Label") << tr("Message") << tr("Amount"); +} + +RecentRequestsTableModel::~RecentRequestsTableModel() +{ + /* Intentionally left empty */ +} + +int RecentRequestsTableModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + + return list.length(); +} + +int RecentRequestsTableModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + + return columns.length(); +} + +QVariant RecentRequestsTableModel::data(const QModelIndex &index, int role) const +{ + if(!index.isValid() || index.row() >= list.length()) + return QVariant(); + + const RecentRequestEntry *rec = &list[index.row()]; + + if(role == Qt::DisplayRole || role == Qt::EditRole) + { + switch(index.column()) + { + case Date: + return GUIUtil::dateTimeStr(rec->date); + case Label: + if(rec->recipient.label.isEmpty() && role == Qt::DisplayRole) + { + return tr("(no label)"); + } + else + { + return rec->recipient.label; + } + case Message: + if(rec->recipient.message.isEmpty() && role == Qt::DisplayRole) + { + return tr("(no message)"); + } + else + { + return rec->recipient.message; + } + case Amount: + return BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), rec->recipient.amount); + } + } + return QVariant(); +} + +bool RecentRequestsTableModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + return true; +} + +QVariant RecentRequestsTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if(orientation == Qt::Horizontal) + { + if(role == Qt::DisplayRole && section < columns.size()) + { + return columns[section]; + } + } + return QVariant(); +} + +QModelIndex RecentRequestsTableModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_UNUSED(parent); + + return createIndex(row, column); +} + +bool RecentRequestsTableModel::removeRows(int row, int count, const QModelIndex &parent) +{ + Q_UNUSED(parent); + + if(count > 0 && row >= 0 && (row+count) <= list.size()) + { + beginRemoveRows(parent, row, row + count - 1); + list.erase(list.begin() + row, list.begin() + row + count); + endRemoveRows(); + return true; + } else { + return false; + } +} + +Qt::ItemFlags RecentRequestsTableModel::flags(const QModelIndex &index) const +{ + return Qt::ItemIsSelectable | Qt::ItemIsEnabled; +} + +void RecentRequestsTableModel::addNewRequest(const SendCoinsRecipient &recipient) +{ + RecentRequestEntry newEntry; + newEntry.date = QDateTime::currentDateTime(); + newEntry.recipient = recipient; + beginInsertRows(QModelIndex(), 0, 0); + list.prepend(newEntry); + endInsertRows(); +} diff --git a/src/qt/recentrequeststablemodel.h b/src/qt/recentrequeststablemodel.h new file mode 100644 index 0000000000..3aab7b0a48 --- /dev/null +++ b/src/qt/recentrequeststablemodel.h @@ -0,0 +1,61 @@ +// Copyright (c) 2011-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 RECENTREQUESTSTABLEMODEL_H +#define RECENTREQUESTSTABLEMODEL_H + +#include "walletmodel.h" + +#include <QAbstractTableModel> +#include <QStringList> +#include <QDateTime> + +class CWallet; + +struct RecentRequestEntry +{ + QDateTime date; + SendCoinsRecipient recipient; +}; + +/** Model for list of recently generated payment requests / bitcoin URIs. + * Part of wallet model. + */ +class RecentRequestsTableModel: public QAbstractTableModel +{ + Q_OBJECT + +public: + explicit RecentRequestsTableModel(CWallet *wallet, WalletModel *parent); + ~RecentRequestsTableModel(); + + enum ColumnIndex { + Date = 0, + Label = 1, + Message = 2, + Amount = 3 + }; + + /** @name Methods overridden from QAbstractTableModel + @{*/ + int rowCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex &index, const QVariant &value, int role); + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + QModelIndex index(int row, int column, const QModelIndex &parent) const; + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + Qt::ItemFlags flags(const QModelIndex &index) const; + /*@}*/ + + const RecentRequestEntry &entry(int row) const { return list[row]; } + void addNewRequest(const SendCoinsRecipient &recipient); + +private: + WalletModel *walletModel; + QStringList columns; + QList<RecentRequestEntry> list; +}; + +#endif diff --git a/src/qt/res/bitcoin-qt-res.rc b/src/qt/res/bitcoin-qt-res.rc index 3e3672a835..ee23ae9b78 100644 --- a/src/qt/res/bitcoin-qt-res.rc +++ b/src/qt/res/bitcoin-qt-res.rc @@ -21,13 +21,13 @@ BEGIN BLOCK "040904E4" // U.S. English - multilingual (hex) BEGIN VALUE "CompanyName", "Bitcoin" - VALUE "FileDescription", "Bitcoin-Qt (OSS GUI client for Bitcoin)" + VALUE "FileDescription", "Bitcoin Core (OSS GUI client for Bitcoin)" VALUE "FileVersion", VER_FILEVERSION_STR VALUE "InternalName", "bitcoin-qt" 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-qt.exe" - VALUE "ProductName", "Bitcoin-Qt" + VALUE "ProductName", "Bitcoin Core" VALUE "ProductVersion", VER_PRODUCTVERSION_STR END END diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index d43cdc7e5f..a8470572dd 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -284,7 +284,7 @@ void RPCConsole::setClientModel(ClientModel *model) ui->buildDate->setText(model->formatBuildDate()); ui->startupTime->setText(model->formatClientStartupTime()); - ui->isTestNet->setChecked(model->isTestNet()); + ui->networkName->setText(model->getNetworkName()); } } diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 0a4e80811f..4bf5c3c479 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -306,12 +306,14 @@ void SendCoinsDialog::updateTabsAndLabels() void SendCoinsDialog::removeEntry(SendCoinsEntry* entry) { - entry->deleteLater(); + entry->hide(); - // If the last entry was removed add an empty one - if (!ui->entries->count()) + // If the last entry is about to be removed add an empty one + if (ui->entries->count() == 1) addEntry(); + entry->deleteLater(); + updateTabsAndLabels(); } @@ -450,9 +452,8 @@ void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn 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. + // included to prevent a compiler warning. case WalletModel::OK: - case WalletModel::Aborted: default: return; } @@ -543,7 +544,7 @@ void SendCoinsDialog::coinControlChangeChecked(int state) } // Coin Control: custom change address changed -void SendCoinsDialog::coinControlChangeEdited(const QString & text) +void SendCoinsDialog::coinControlChangeEdited(const QString& text) { if (model) { diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index 12280bb702..6fb834c045 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -4,14 +4,13 @@ #include "splashscreen.h" -#include "chainparams.h" #include "clientversion.h" #include "util.h" #include <QApplication> #include <QPainter> -SplashScreen::SplashScreen(const QPixmap &pixmap, Qt::WindowFlags f) : +SplashScreen::SplashScreen(const QPixmap &pixmap, Qt::WindowFlags f, bool isTestNet) : QSplashScreen(pixmap, f) { // set reference point, paddings @@ -23,16 +22,16 @@ SplashScreen::SplashScreen(const QPixmap &pixmap, Qt::WindowFlags f) : float fontFactor = 1.0; // define text to place - QString titleText = QString(QApplication::applicationName()).replace(QString("-testnet"), QString(""), Qt::CaseSensitive); // cut of testnet, place it as single object further down + QString titleText = tr("Bitcoin Core"); QString versionText = QString("Version %1").arg(QString::fromStdString(FormatFullVersion())); - QString copyrightText = QChar(0xA9)+QString(" 2009-%1 ").arg(COPYRIGHT_YEAR) + QString(tr("The Bitcoin developers")); + QString copyrightText = QChar(0xA9)+QString(" 2009-%1 ").arg(COPYRIGHT_YEAR) + QString(tr("The Bitcoin Core developers")); QString testnetAddText = QString(tr("[testnet]")); // define text to place as single text object QString font = "Arial"; // load the bitmap for writing some text over it QPixmap newPixmap; - if(TestNet()) { + if(isTestNet) { newPixmap = QPixmap(":/images/splash_testnet"); } else { @@ -72,7 +71,7 @@ SplashScreen::SplashScreen(const QPixmap &pixmap, Qt::WindowFlags f) : pixPaint.drawText(newPixmap.width()-titleTextWidth-paddingRight,paddingTop+titleCopyrightVSpace,copyrightText); // draw testnet string if testnet is on - if(TestNet()) { + if(isTestNet) { QFont boldFont = QFont(font, 10*fontFactor); boldFont.setWeight(QFont::Bold); pixPaint.setFont(boldFont); diff --git a/src/qt/splashscreen.h b/src/qt/splashscreen.h index ddf040593d..070e376c95 100644 --- a/src/qt/splashscreen.h +++ b/src/qt/splashscreen.h @@ -14,7 +14,7 @@ class SplashScreen : public QSplashScreen Q_OBJECT public: - explicit SplashScreen(const QPixmap &pixmap = QPixmap(), Qt::WindowFlags f = 0); + explicit SplashScreen(const QPixmap &pixmap, Qt::WindowFlags f, bool isTestNet); }; #endif // SPLASHSCREEN_H diff --git a/src/qt/test/Makefile.am b/src/qt/test/Makefile.am index 29247a79a1..cb6874700e 100644 --- a/src/qt/test/Makefile.am +++ b/src/qt/test/Makefile.am @@ -17,7 +17,7 @@ BUILT_SOURCES = $(TEST_QT_MOC_CPP) test_bitcoin_qt_CPPFLAGS = $(AM_CPPFLAGS) $(QT_INCLUDES) $(QT_TEST_INCLUDES) test_bitcoin_qt_SOURCES = test_main.cpp uritests.cpp paymentservertests.cpp $(TEST_QT_H) nodist_test_bitcoin_qt_SOURCES = $(TEST_QT_MOC_CPP) -test_bitcoin_qt_LDADD = $(LIBBITCOINQT) $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBLEVELDB) \ +test_bitcoin_qt_LDADD = $(LIBBITCOINQT) $(LIBBITCOIN_SERVER) $(LIBBITCOIN_WALLET) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBLEVELDB) \ $(LIBMEMENV) $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QT_TEST_LIBS) \ $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 2470af41a0..f08342b83e 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -6,6 +6,7 @@ #include "addresstablemodel.h" #include "guiconstants.h" +#include "recentrequeststablemodel.h" #include "transactiontablemodel.h" #include "base58.h" @@ -26,6 +27,7 @@ WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *parent) : QObject(parent), wallet(wallet), optionsModel(optionsModel), addressTableModel(0), transactionTableModel(0), + recentRequestsTableModel(0), cachedBalance(0), cachedUnconfirmedBalance(0), cachedImmatureBalance(0), cachedNumTransactions(0), cachedEncryptionStatus(Unencrypted), @@ -33,6 +35,7 @@ WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *p { addressTableModel = new AddressTableModel(wallet, this); transactionTableModel = new TransactionTableModel(wallet, this); + recentRequestsTableModel = new RecentRequestsTableModel(wallet, this); // This timer will be fired repeatedly to update the balance pollTimer = new QTimer(this); @@ -325,6 +328,11 @@ TransactionTableModel *WalletModel::getTransactionTableModel() return transactionTableModel; } +RecentRequestsTableModel *WalletModel::getRecentRequestsTableModel() +{ + return recentRequestsTableModel; +} + WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const { if(!wallet->IsCrypted()) diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 32ddbbc6f6..1a4d25615a 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -17,6 +17,7 @@ class AddressTableModel; class OptionsModel; +class RecentRequestsTableModel; class TransactionTableModel; class WalletModelTransaction; @@ -74,8 +75,7 @@ public: AmountWithFeeExceedsBalance, DuplicateAddress, TransactionCreationFailed, // Error returned when wallet is still locked - TransactionCommitFailed, - Aborted + TransactionCommitFailed }; enum EncryptionStatus @@ -88,6 +88,7 @@ public: OptionsModel *getOptionsModel(); AddressTableModel *getAddressTableModel(); TransactionTableModel *getTransactionTableModel(); + RecentRequestsTableModel *getRecentRequestsTableModel(); qint64 getBalance(const CCoinControl *coinControl = NULL) const; qint64 getUnconfirmedBalance() const; @@ -101,7 +102,7 @@ public: // Return status record for SendCoins, contains error id + information struct SendCoinsReturn { - SendCoinsReturn(StatusCode status = Aborted): + SendCoinsReturn(StatusCode status = OK): status(status) {} StatusCode status; }; @@ -160,6 +161,7 @@ private: AddressTableModel *addressTableModel; TransactionTableModel *transactionTableModel; + RecentRequestsTableModel *recentRequestsTableModel; // Cache some values to be able to detect changes qint64 cachedBalance; diff --git a/src/qt/walletmodeltransaction.h b/src/qt/walletmodeltransaction.h index a948808a75..b7e85bcd11 100644 --- a/src/qt/walletmodeltransaction.h +++ b/src/qt/walletmodeltransaction.h @@ -39,9 +39,6 @@ private: CWalletTx *walletTransaction; CReserveKey *keyChange; qint64 fee; - -public slots: - }; #endif // WALLETMODELTRANSACTION_H diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index ea71d58aa6..1a9c7866db 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -263,8 +263,8 @@ 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->setModel(walletModel->getAddressTableModel()); dlg->show(); } @@ -273,7 +273,7 @@ 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->setModel(walletModel->getAddressTableModel()); dlg->show(); } diff --git a/src/qt/walletview.h b/src/qt/walletview.h index d951691621..ecfa06ac5a 100644 --- a/src/qt/walletview.h +++ b/src/qt/walletview.h @@ -96,6 +96,7 @@ public slots: /** Re-emit encryption status signal */ void updateEncryptionStatus(); + signals: /** Signal that we want to show the main window */ void showNormalIfMinimized(); diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 34ae6e0543..661deffb19 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -127,30 +127,6 @@ Value getdifficulty(const Array& params, bool fHelp) } -Value settxfee(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 1) - throw runtime_error( - "settxfee amount\n" - "\nSet the transaction fee. 'amount' is a real and is rounded to the nearest 0.00000001\n" - "\nArguments:\n" - "1. amount (numeric, required) The transaction fee in btc rounded to the nearest 0.00000001\n" - "\nResult\n" - "true|false (boolean) Returns true if successful\n" - "\nExamples:\n" - + HelpExampleCli("settxfee", "0.00001") - + HelpExampleRpc("settxfee", "0.00001") - ); - - // Amount - int64_t nAmount = 0; - if (params[0].get_real() != 0.0) - nAmount = AmountFromValue(params[0]); // rejects 0.0 amounts - - nTransactionFee = nAmount; - return true; -} - Value getrawmempool(const Array& params, bool fHelp) { if (fHelp || params.size() > 1) diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp index a3168917fc..c404ac274b 100644 --- a/src/rpcclient.cpp +++ b/src/rpcclient.cpp @@ -257,7 +257,7 @@ std::string HelpMessageCli(bool mainProgram) strUsage += " -datadir=<dir> " + _("Specify data directory") + "\n"; strUsage += " -testnet " + _("Use the test network") + "\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"; + "solved instantly. This is intended for regression testing tools and app development.") + "\n"; } else { strUsage += _("RPC client options:") + "\n"; } diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 131a258c84..1a8462fdea 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -5,13 +5,14 @@ #include "rpcserver.h" #include "chainparams.h" -#include "db.h" #include "init.h" #include "net.h" #include "main.h" #include "miner.h" +#ifdef ENABLE_WALLET +#include "db.h" #include "wallet.h" - +#endif #include <stdint.h> #include "json/json_spirit_utils.h" @@ -20,7 +21,8 @@ using namespace json_spirit; using namespace std; -// Key used by getwork/getblocktemplate miners. +#ifdef ENABLE_WALLET +// Key used by getwork miners. // Allocated in InitRPCMining, free'd in ShutdownRPCMining static CReserveKey* pMiningKey = NULL; @@ -40,6 +42,14 @@ void ShutdownRPCMining() delete pMiningKey; pMiningKey = NULL; } +#else +void InitRPCMining() +{ +} +void ShutdownRPCMining() +{ +} +#endif // Return average network hashes per second based on the last 'lookup' blocks, // or from the last difficulty change if 'lookup' is nonpositive. @@ -99,7 +109,7 @@ Value getnetworkhashps(const Array& params, bool fHelp) return GetNetworkHashPS(params.size() > 0 ? params[0].get_int() : 120, params.size() > 1 ? params[1].get_int() : -1); } - +#ifdef ENABLE_WALLET Value getgenerate(const Array& params, bool fHelp) { if (fHelp || params.size() != 0) @@ -197,7 +207,6 @@ Value setgenerate(const Array& params, bool fHelp) return Value::null; } - Value gethashespersec(const Array& params, bool fHelp) { if (fHelp || params.size() != 0) @@ -216,6 +225,7 @@ Value gethashespersec(const Array& params, bool fHelp) return (boost::int64_t)0; return (boost::int64_t)dHashesPerSec; } +#endif Value getmininginfo(const Array& params, bool fHelp) @@ -248,16 +258,19 @@ Value getmininginfo(const Array& params, bool fHelp) obj.push_back(Pair("currentblocktx", (uint64_t)nLastBlockTx)); obj.push_back(Pair("difficulty", (double)GetDifficulty())); obj.push_back(Pair("errors", GetWarnings("statusbar"))); - obj.push_back(Pair("generate", getgenerate(params, false))); obj.push_back(Pair("genproclimit", (int)GetArg("-genproclimit", -1))); - obj.push_back(Pair("hashespersec", gethashespersec(params, false))); obj.push_back(Pair("networkhashps", getnetworkhashps(params, false))); obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); obj.push_back(Pair("testnet", TestNet())); +#ifdef ENABLE_WALLET + obj.push_back(Pair("generate", getgenerate(params, false))); + obj.push_back(Pair("hashespersec", gethashespersec(params, false))); +#endif return obj; } +#ifdef ENABLE_WALLET Value getwork(const Array& params, bool fHelp) { if (fHelp || params.size() > 1) @@ -381,7 +394,7 @@ Value getwork(const Array& params, bool fHelp) return CheckWork(pblock, *pwalletMain, *pMiningKey); } } - +#endif Value getblocktemplate(const Array& params, bool fHelp) { diff --git a/src/rpcmisc.cpp b/src/rpcmisc.cpp new file mode 100644 index 0000000000..c61cc4192b --- /dev/null +++ b/src/rpcmisc.cpp @@ -0,0 +1,326 @@ +// Copyright (c) 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 "base58.h" +#include "rpcserver.h" +#include "init.h" +#include "main.h" +#include "net.h" +#include "netbase.h" +#include "util.h" +#ifdef ENABLE_WALLET +#include "wallet.h" +#include "walletdb.h" +#endif + +#include <stdint.h> + +#include <boost/assign/list_of.hpp> +#include "json/json_spirit_utils.h" +#include "json/json_spirit_value.h" + +using namespace std; +using namespace boost; +using namespace boost::assign; +using namespace json_spirit; + +Value getinfo(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getinfo\n" + "Returns an object containing various state info.\n" + "\nResult:\n" + "{\n" + " \"version\": xxxxx, (numeric) the server version\n" + " \"protocolversion\": xxxxx, (numeric) the protocol version\n" + " \"walletversion\": xxxxx, (numeric) the wallet version\n" + " \"balance\": xxxxxxx, (numeric) the total bitcoin balance of the wallet\n" + " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n" + " \"timeoffset\": xxxxx, (numeric) the time offset\n" + " \"connections\": xxxxx, (numeric) the number of connections\n" + " \"proxy\": \"host:port\", (string, optional) the proxy used by the server\n" + " \"difficulty\": xxxxxx, (numeric) the current difficulty\n" + " \"testnet\": true|false, (boolean) if the server is using testnet or not\n" + " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n" + " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n" + " \"paytxfee\": x.xxxx, (numeric) the transaction fee set in btc\n" + " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" + " \"errors\": \"...\" (string) any error messages\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getinfo", "") + + HelpExampleRpc("getinfo", "") + ); + + proxyType proxy; + GetProxy(NET_IPV4, proxy); + + Object obj; + obj.push_back(Pair("version", (int)CLIENT_VERSION)); + obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION)); +#ifdef ENABLE_WALLET + if (pwalletMain) { + obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); + obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); + } +#endif + obj.push_back(Pair("blocks", (int)chainActive.Height())); + obj.push_back(Pair("timeoffset", (boost::int64_t)GetTimeOffset())); + obj.push_back(Pair("connections", (int)vNodes.size())); + obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string()))); + obj.push_back(Pair("difficulty", (double)GetDifficulty())); + obj.push_back(Pair("testnet", TestNet())); +#ifdef ENABLE_WALLET + if (pwalletMain) { + obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime())); + obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize())); + } + obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee))); + if (pwalletMain && pwalletMain->IsCrypted()) + obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime)); +#endif + obj.push_back(Pair("errors", GetWarnings("statusbar"))); + return obj; +} + +#ifdef ENABLE_WALLET +class DescribeAddressVisitor : public boost::static_visitor<Object> +{ +public: + Object operator()(const CNoDestination &dest) const { return Object(); } + + Object operator()(const CKeyID &keyID) const { + Object obj; + CPubKey vchPubKey; + pwalletMain->GetPubKey(keyID, vchPubKey); + obj.push_back(Pair("isscript", false)); + obj.push_back(Pair("pubkey", HexStr(vchPubKey))); + obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed())); + return obj; + } + + Object operator()(const CScriptID &scriptID) const { + Object obj; + obj.push_back(Pair("isscript", true)); + CScript subscript; + pwalletMain->GetCScript(scriptID, subscript); + std::vector<CTxDestination> addresses; + txnouttype whichType; + int nRequired; + ExtractDestinations(subscript, whichType, addresses, nRequired); + obj.push_back(Pair("script", GetTxnOutputType(whichType))); + obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end()))); + Array a; + BOOST_FOREACH(const CTxDestination& addr, addresses) + a.push_back(CBitcoinAddress(addr).ToString()); + obj.push_back(Pair("addresses", a)); + if (whichType == TX_MULTISIG) + obj.push_back(Pair("sigsrequired", nRequired)); + return obj; + } +}; +#endif + +Value validateaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "validateaddress \"bitcoinaddress\"\n" + "\nReturn information about the given bitcoin address.\n" + "\nArguments:\n" + "1. \"bitcoinaddress\" (string, required) The bitcoin address to validate\n" + "\nResult:\n" + "{\n" + " \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n" + " \"address\" : \"bitcoinaddress\", (string) The bitcoin address validated\n" + " \"ismine\" : true|false, (boolean) If the address is yours or not\n" + " \"isscript\" : true|false, (boolean) If the key is a script\n" + " \"pubkey\" : \"publickeyhex\", (string) The hex value of the raw public key\n" + " \"iscompressed\" : true|false, (boolean) If the address is compressed\n" + " \"account\" : \"account\" (string) The account associated with the address, \"\" is the default account\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") + + HelpExampleRpc("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") + ); + + CBitcoinAddress address(params[0].get_str()); + bool isValid = address.IsValid(); + + Object ret; + ret.push_back(Pair("isvalid", isValid)); + if (isValid) + { + CTxDestination dest = address.Get(); + string currentAddress = address.ToString(); + ret.push_back(Pair("address", currentAddress)); +#ifdef ENABLE_WALLET + bool fMine = pwalletMain ? IsMine(*pwalletMain, dest) : false; + ret.push_back(Pair("ismine", fMine)); + if (fMine) { + Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest); + ret.insert(ret.end(), detail.begin(), detail.end()); + } + if (pwalletMain && pwalletMain->mapAddressBook.count(dest)) + ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest].name)); +#endif + } + return ret; +} + +// +// Used by addmultisigaddress / createmultisig: +// +CScript _createmultisig(const Array& params) +{ + int nRequired = params[0].get_int(); + const Array& keys = params[1].get_array(); + + // Gather public keys + if (nRequired < 1) + throw runtime_error("a multisignature address must require at least one key to redeem"); + if ((int)keys.size() < nRequired) + throw runtime_error( + strprintf("not enough keys supplied " + "(got %"PRIszu" keys, but need at least %d to redeem)", keys.size(), nRequired)); + std::vector<CPubKey> pubkeys; + pubkeys.resize(keys.size()); + for (unsigned int i = 0; i < keys.size(); i++) + { + const std::string& ks = keys[i].get_str(); +#ifdef ENABLE_WALLET + // Case 1: Bitcoin address and we have full public key: + CBitcoinAddress address(ks); + if (pwalletMain && address.IsValid()) + { + CKeyID keyID; + if (!address.GetKeyID(keyID)) + throw runtime_error( + strprintf("%s does not refer to a key",ks.c_str())); + CPubKey vchPubKey; + if (!pwalletMain->GetPubKey(keyID, vchPubKey)) + throw runtime_error( + strprintf("no full public key for address %s",ks.c_str())); + if (!vchPubKey.IsFullyValid()) + throw runtime_error(" Invalid public key: "+ks); + pubkeys[i] = vchPubKey; + } + + // Case 2: hex public key + else +#endif + if (IsHex(ks)) + { + CPubKey vchPubKey(ParseHex(ks)); + if (!vchPubKey.IsFullyValid()) + throw runtime_error(" Invalid public key: "+ks); + pubkeys[i] = vchPubKey; + } + else + { + throw runtime_error(" Invalid public key: "+ks); + } + } + CScript result; + result.SetMultisig(nRequired, pubkeys); + return result; +} + +Value createmultisig(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 2) + { + string msg = "createmultisig nrequired [\"key\",...]\n" + "\nCreates a multi-signature address with n signature of m keys required.\n" + "It returns a json object with the address and redeemScript.\n" + + "\nArguments:\n" + "1. nrequired (numeric, required) The number of required signatures out of the n keys or addresses.\n" + "2. \"keys\" (string, required) A json array of keys which are bitcoin addresses or hex-encoded public keys\n" + " [\n" + " \"key\" (string) bitcoin address or hex-encoded public key\n" + " ,...\n" + " ]\n" + + "\nResult:\n" + "{\n" + " \"address\":\"multisigaddress\", (string) The value of the new multisig address.\n" + " \"redeemScript\":\"script\" (string) The string value of the hex-encoded redemption script.\n" + "}\n" + + "\nExamples:\n" + "\nCreate a multisig address from 2 addresses\n" + + HelpExampleCli("createmultisig", "2 \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") + + "\nAs a json rpc call\n" + + HelpExampleRpc("icreatemultisig", "2, \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") + ; + throw runtime_error(msg); + } + + // Construct using pay-to-script-hash: + CScript inner = _createmultisig(params); + CScriptID innerID = inner.GetID(); + CBitcoinAddress address(innerID); + + Object result; + result.push_back(Pair("address", address.ToString())); + result.push_back(Pair("redeemScript", HexStr(inner.begin(), inner.end()))); + + return result; +} + +Value verifymessage(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 3) + throw runtime_error( + "verifymessage \"bitcoinaddress\" \"signature\" \"message\"\n" + "\nVerify a signed message\n" + "\nArguments:\n" + "1. \"bitcoinaddress\" (string, required) The bitcoin address to use for the signature.\n" + "2. \"signature\" (string, required) The signature provided by the signer in base 64 encoding (see signmessage).\n" + "3. \"message\" (string, required) The message that was signed.\n" + "\nResult:\n" + "true|false (boolean) If the signature is verified or not.\n" + "\nExamples:\n" + "\nUnlock the wallet for 30 seconds\n" + + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") + + "\nCreate the signature\n" + + HelpExampleCli("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"my message\"") + + "\nVerify the signature\n" + + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"signature\" \"my message\"") + + "\nAs json rpc\n" + + HelpExampleRpc("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\", \"signature\", \"my message\"") + ); + + string strAddress = params[0].get_str(); + string strSign = params[1].get_str(); + string strMessage = params[2].get_str(); + + CBitcoinAddress addr(strAddress); + if (!addr.IsValid()) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); + + CKeyID keyID; + if (!addr.GetKeyID(keyID)) + throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); + + bool fInvalid = false; + vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid); + + if (fInvalid) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding"); + + CHashWriter ss(SER_GETHASH, 0); + ss << strMessageMagic; + ss << strMessage; + + CPubKey pubkey; + if (!pubkey.RecoverCompact(ss.GetHash(), vchSig)) + return false; + + return (pubkey.GetID() == keyID); +} + diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index b492b57ded..06ae7070c3 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.cpp @@ -3,11 +3,17 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "rpcserver.h" + +#include "main.h" #include "net.h" #include "netbase.h" #include "protocol.h" #include "sync.h" #include "util.h" +#ifdef ENABLE_WALLET +#include "init.h" // for getinfo +#include "wallet.h" // for getinfo +#endif #include <inttypes.h> @@ -110,7 +116,8 @@ Value getpeerinfo(const Array& params, bool fHelp) BOOST_FOREACH(const CNodeStats& stats, vstats) { Object obj; - + CNodeStateStats statestats; + bool fStateStats = GetNodeStateStats(stats.nodeid, statestats); obj.push_back(Pair("addr", stats.addrName)); if (!(stats.addrLocal.empty())) obj.push_back(Pair("addrlocal", stats.addrLocal)); @@ -130,7 +137,9 @@ Value getpeerinfo(const Array& params, bool fHelp) obj.push_back(Pair("subver", stats.cleanSubVer)); obj.push_back(Pair("inbound", stats.fInbound)); obj.push_back(Pair("startingheight", stats.nStartingHeight)); - obj.push_back(Pair("banscore", stats.nMisbehavior)); + if (fStateStats) { + obj.push_back(Pair("banscore", statestats.nMisbehavior)); + } if (stats.fSyncNode) obj.push_back(Pair("syncnode", true)); diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index a45c2cc1f3..86025918ef 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -8,7 +8,12 @@ #include "init.h" #include "net.h" #include "uint256.h" +#include "core.h" +#include "main.h" +#include "keystore.h" +#ifdef ENABLE_WALLET #include "wallet.h" +#endif #include <stdint.h> @@ -190,6 +195,7 @@ Value getrawtransaction(const Array& params, bool fHelp) return result; } +#ifdef ENABLE_WALLET Value listunspent(const Array& params, bool fHelp) { if (fHelp || params.size() > 3) @@ -303,6 +309,7 @@ Value listunspent(const Array& params, bool fHelp) return results; } +#endif Value createrawtransaction(const Array& params, bool fHelp) { @@ -508,7 +515,9 @@ Value signrawtransaction(const Array& params, bool fHelp) "this transaction depends on but may not yet be in the block chain.\n" "The third optional argument (may be null) is an array of base58-encoded private\n" "keys that, if given, will be the only keys used to sign the transaction.\n" +#ifdef ENABLE_WALLET + HelpRequiringPassphrase() + "\n" +#endif "\nArguments:\n" "1. \"hexstring\" (string, required) The transaction hex string\n" @@ -605,8 +614,10 @@ Value signrawtransaction(const Array& params, bool fHelp) tempKeystore.AddKey(key); } } +#ifdef ENABLE_WALLET else EnsureWalletIsUnlocked(); +#endif // Add previous txouts given in the RPC call: if (params.size() > 1 && params[1].type() != null_type) @@ -662,7 +673,11 @@ Value signrawtransaction(const Array& params, bool fHelp) } } +#ifdef ENABLE_WALLET const CKeyStore& keystore = ((fGivenKeys || !pwalletMain) ? tempKeystore : *pwalletMain); +#else + const CKeyStore& keystore = tempKeystore; +#endif int nHashType = SIGHASH_ALL; if (params.size() > 3 && params[3].type() != null_type) diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index 403567203c..9f2100a8d7 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -9,7 +9,10 @@ #include "init.h" #include "main.h" #include "util.h" +#include "ui_interface.h" +#ifdef ENABLE_WALLET #include "wallet.h" +#endif #include <boost/algorithm/string.hpp> #include <boost/asio.hpp> @@ -149,8 +152,10 @@ string CRPCTable::help(string strCommand) const continue; if (strCommand != "" && strMethod != strCommand) continue; +#ifdef ENABLE_WALLET if (pcmd->reqWallet && !pwalletMain) continue; +#endif try { @@ -228,12 +233,31 @@ static const CRPCCommand vRPCCommands[] = { "getaddednodeinfo", &getaddednodeinfo, true, true, false }, { "getnettotals", &getnettotals, true, true, false }, { "getdifficulty", &getdifficulty, true, false, false }, - { "getnetworkhashps", &getnetworkhashps, true, false, false }, - { "getgenerate", &getgenerate, true, false, false }, - { "setgenerate", &setgenerate, true, true, false }, - { "gethashespersec", &gethashespersec, true, false, false }, { "getinfo", &getinfo, true, false, false }, + { "getrawmempool", &getrawmempool, true, false, false }, + { "getblock", &getblock, false, false, false }, + { "getblockhash", &getblockhash, false, false, false }, + { "getrawtransaction", &getrawtransaction, false, false, false }, + { "createrawtransaction", &createrawtransaction, false, false, false }, + { "decoderawtransaction", &decoderawtransaction, false, false, false }, + { "decodescript", &decodescript, false, false, false }, + { "signrawtransaction", &signrawtransaction, false, false, false }, + { "sendrawtransaction", &sendrawtransaction, false, false, false }, + { "gettxoutsetinfo", &gettxoutsetinfo, true, false, false }, + { "gettxout", &gettxout, true, false, false }, + { "verifychain", &verifychain, true, false, false }, + + /* Mining */ + { "getnetworkhashps", &getnetworkhashps, true, false, false }, { "getmininginfo", &getmininginfo, true, false, false }, + { "getblocktemplate", &getblocktemplate, true, false, false }, + { "submitblock", &submitblock, false, false, false }, + { "validateaddress", &validateaddress, true, false, false }, + { "createmultisig", &createmultisig, true, true , false }, + { "verifymessage", &verifymessage, false, false, false }, + +#ifdef ENABLE_WALLET + /* Wallet */ { "getnewaddress", &getnewaddress, true, false, true }, { "getaccountaddress", &getaccountaddress, true, false, true }, { "getrawchangeaddress", &getrawchangeaddress, true, false, true }, @@ -251,44 +275,33 @@ static const CRPCCommand vRPCCommands[] = { "walletpassphrasechange", &walletpassphrasechange, false, false, true }, { "walletlock", &walletlock, true, false, true }, { "encryptwallet", &encryptwallet, false, false, true }, - { "validateaddress", &validateaddress, true, false, false }, { "getbalance", &getbalance, false, false, true }, { "getunconfirmedbalance", &getunconfirmedbalance, false, false, true }, { "move", &movecmd, false, false, true }, { "sendfrom", &sendfrom, false, false, true }, { "sendmany", &sendmany, false, false, true }, { "addmultisigaddress", &addmultisigaddress, false, false, true }, - { "createmultisig", &createmultisig, true, true , false }, - { "getrawmempool", &getrawmempool, true, false, false }, - { "getblock", &getblock, false, false, false }, - { "getblockhash", &getblockhash, false, false, false }, { "gettransaction", &gettransaction, false, false, true }, { "listtransactions", &listtransactions, false, false, true }, { "listaddressgroupings", &listaddressgroupings, false, false, true }, { "signmessage", &signmessage, false, false, true }, - { "verifymessage", &verifymessage, false, false, false }, - { "getwork", &getwork, true, false, true }, { "listaccounts", &listaccounts, false, false, true }, - { "settxfee", &settxfee, false, false, true }, - { "getblocktemplate", &getblocktemplate, true, false, false }, - { "submitblock", &submitblock, false, false, false }, { "listsinceblock", &listsinceblock, false, false, true }, { "dumpprivkey", &dumpprivkey, true, false, true }, { "dumpwallet", &dumpwallet, true, false, true }, { "importprivkey", &importprivkey, false, false, true }, { "importwallet", &importwallet, false, false, true }, { "listunspent", &listunspent, false, false, true }, - { "getrawtransaction", &getrawtransaction, false, false, false }, - { "createrawtransaction", &createrawtransaction, false, false, false }, - { "decoderawtransaction", &decoderawtransaction, false, false, false }, - { "decodescript", &decodescript, false, false, false }, - { "signrawtransaction", &signrawtransaction, false, false, false }, - { "sendrawtransaction", &sendrawtransaction, false, false, false }, - { "gettxoutsetinfo", &gettxoutsetinfo, true, false, false }, - { "gettxout", &gettxout, true, false, false }, { "lockunspent", &lockunspent, false, false, true }, { "listlockunspent", &listlockunspent, false, false, true }, - { "verifychain", &verifychain, true, false, false }, + { "settxfee", &settxfee, false, false, true }, + + /* Wallet-enabled mining */ + { "getgenerate", &getgenerate, true, false, false }, + { "setgenerate", &setgenerate, true, true, false }, + { "gethashespersec", &gethashespersec, true, false, false }, + { "getwork", &getwork, true, false, true }, +#endif // ENABLE_WALLET }; CRPCTable::CRPCTable() @@ -789,8 +802,10 @@ json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_s const CRPCCommand *pcmd = tableRPC[strMethod]; if (!pcmd) throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found"); +#ifdef ENABLE_WALLET if (pcmd->reqWallet && !pwalletMain) throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (disabled)"); +#endif // Observe safe mode string strWarning = GetWarnings("rpc"); @@ -805,6 +820,7 @@ json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_s { if (pcmd->threadSafe) result = pcmd->actor(params, false); +#ifdef ENABLE_WALLET else if (!pwalletMain) { LOCK(cs_main); result = pcmd->actor(params, false); @@ -812,6 +828,12 @@ json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_s LOCK2(cs_main, pwalletMain->cs_wallet); result = pcmd->actor(params, false); } +#else // ENABLE_WALLET + else { + LOCK(cs_main); + result = pcmd->actor(params, false); + } +#endif // !ENABLE_WALLET } return result; } @@ -821,4 +843,13 @@ json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_s } } +std::string HelpExampleCli(string methodname, string args){ + return "> bitcoin-cli " + methodname + " " + args + "\n"; +} + +std::string HelpExampleRpc(string methodname, string args){ + return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", " + "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n"; +} + const CRPCTable tableRPC; diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index fe42b74dea..8ad5c9c51d 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -33,15 +33,6 @@ std::string HelpRequiringPassphrase() : ""; } -std::string HelpExampleCli(string methodname, string args){ - return "> bitcoin-cli " + methodname + " " + args + "\n"; -} - -std::string HelpExampleRpc(string methodname, string args){ - return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", " - "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n"; -} - void EnsureWalletIsUnlocked() { if (pwalletMain->IsLocked()) @@ -75,64 +66,6 @@ string AccountFromValue(const Value& value) return strAccount; } -Value getinfo(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getinfo\n" - "Returns an object containing various state info.\n" - "\nResult:\n" - "{\n" - " \"version\": xxxxx, (numeric) the server version\n" - " \"protocolversion\": xxxxx, (numeric) the protocol version\n" - " \"walletversion\": xxxxx, (numeric) the wallet version\n" - " \"balance\": xxxxxxx, (numeric) the total bitcoin balance of the wallet\n" - " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n" - " \"timeoffset\": xxxxx, (numeric) the time offset\n" - " \"connections\": xxxxx, (numeric) the number of connections\n" - " \"proxy\": \"host:port\", (string, optional) the proxy used by the server\n" - " \"difficulty\": xxxxxx, (numeric) the current difficulty\n" - " \"testnet\": true|false, (boolean) if the server is using testnet or not\n" - " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n" - " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n" - " \"paytxfee\": x.xxxx, (numeric) the transaction fee set in btc\n" - " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" - " \"errors\": \"...\" (string) any error messages\n" - "}\n" - "\nExamples:\n" - + HelpExampleCli("getinfo", "") - + HelpExampleRpc("getinfo", "") - ); - - proxyType proxy; - GetProxy(NET_IPV4, proxy); - - Object obj; - obj.push_back(Pair("version", (int)CLIENT_VERSION)); - obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION)); - if (pwalletMain) { - obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); - obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); - } - obj.push_back(Pair("blocks", (int)chainActive.Height())); - obj.push_back(Pair("timeoffset", (boost::int64_t)GetTimeOffset())); - obj.push_back(Pair("connections", (int)vNodes.size())); - obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string()))); - obj.push_back(Pair("difficulty", (double)GetDifficulty())); - obj.push_back(Pair("testnet", TestNet())); - if (pwalletMain) { - obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime())); - obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize())); - } - obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee))); - if (pwalletMain && pwalletMain->IsCrypted()) - obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime)); - obj.push_back(Pair("errors", GetWarnings("statusbar"))); - return obj; -} - - - Value getnewaddress(const Array& params, bool fHelp) { if (fHelp || params.size() > 1) @@ -401,8 +334,7 @@ Value sendtoaddress(const Array& params, bool fHelp) if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) wtx.mapValue["to"] = params[3].get_str(); - if (pwalletMain->IsLocked()) - throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); + EnsureWalletIsUnlocked(); string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx); if (strError != "") @@ -509,59 +441,6 @@ Value signmessage(const Array& params, bool fHelp) return EncodeBase64(&vchSig[0], vchSig.size()); } -Value verifymessage(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 3) - throw runtime_error( - "verifymessage \"bitcoinaddress\" \"signature\" \"message\"\n" - "\nVerify a signed message\n" - "\nArguments:\n" - "1. \"bitcoinaddress\" (string, required) The bitcoin address to use for the signature.\n" - "2. \"signature\" (string, required) The signature provided by the signer in base 64 encoding (see signmessage).\n" - "3. \"message\" (string, required) The message that was signed.\n" - "\nResult:\n" - "true|false (boolean) If the signature is verified or not.\n" - "\nExamples:\n" - "\nUnlock the wallet for 30 seconds\n" - + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") + - "\nCreate the signature\n" - + HelpExampleCli("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"my message\"") + - "\nVerify the signature\n" - + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"signature\" \"my message\"") + - "\nAs json rpc\n" - + HelpExampleRpc("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\", \"signature\", \"my message\"") - ); - - string strAddress = params[0].get_str(); - string strSign = params[1].get_str(); - string strMessage = params[2].get_str(); - - CBitcoinAddress addr(strAddress); - if (!addr.IsValid()) - throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); - - CKeyID keyID; - if (!addr.GetKeyID(keyID)) - throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); - - bool fInvalid = false; - vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid); - - if (fInvalid) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding"); - - CHashWriter ss(SER_GETHASH, 0); - ss << strMessageMagic; - ss << strMessage; - - CPubKey pubkey; - if (!pubkey.RecoverCompact(ss.GetHash(), vchSig)) - return false; - - return (pubkey.GetID() == keyID); -} - - Value getreceivedbyaddress(const Array& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 2) @@ -984,61 +863,8 @@ Value sendmany(const Array& params, bool fHelp) return wtx.GetHash().GetHex(); } -// -// Used by addmultisigaddress / createmultisig: -// -static CScript _createmultisig(const Array& params) -{ - int nRequired = params[0].get_int(); - const Array& keys = params[1].get_array(); - - // Gather public keys - if (nRequired < 1) - throw runtime_error("a multisignature address must require at least one key to redeem"); - if ((int)keys.size() < nRequired) - throw runtime_error( - strprintf("not enough keys supplied " - "(got %"PRIszu" keys, but need at least %d to redeem)", keys.size(), nRequired)); - std::vector<CPubKey> pubkeys; - pubkeys.resize(keys.size()); - for (unsigned int i = 0; i < keys.size(); i++) - { - const std::string& ks = keys[i].get_str(); - - // Case 1: Bitcoin address and we have full public key: - CBitcoinAddress address(ks); - if (pwalletMain && address.IsValid()) - { - CKeyID keyID; - if (!address.GetKeyID(keyID)) - throw runtime_error( - strprintf("%s does not refer to a key",ks.c_str())); - CPubKey vchPubKey; - if (!pwalletMain->GetPubKey(keyID, vchPubKey)) - throw runtime_error( - strprintf("no full public key for address %s",ks.c_str())); - if (!vchPubKey.IsFullyValid()) - throw runtime_error(" Invalid public key: "+ks); - pubkeys[i] = vchPubKey; - } - - // Case 2: hex public key - else if (IsHex(ks)) - { - CPubKey vchPubKey(ParseHex(ks)); - if (!vchPubKey.IsFullyValid()) - throw runtime_error(" Invalid public key: "+ks); - pubkeys[i] = vchPubKey; - } - else - { - throw runtime_error(" Invalid public key: "+ks); - } - } - CScript result; - result.SetMultisig(nRequired, pubkeys); - return result; -} +// Defined in rpcmisc.cpp +extern CScript _createmultisig(const Array& params); Value addmultisigaddress(const Array& params, bool fHelp) { @@ -1083,49 +909,6 @@ Value addmultisigaddress(const Array& params, bool fHelp) return CBitcoinAddress(innerID).ToString(); } -Value createmultisig(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 2 || params.size() > 2) - { - string msg = "createmultisig nrequired [\"key\",...]\n" - "\nCreates a multi-signature address with n signature of m keys required.\n" - "It returns a json object with the address and redeemScript.\n" - - "\nArguments:\n" - "1. nrequired (numeric, required) The number of required signatures out of the n keys or addresses.\n" - "2. \"keys\" (string, required) A json array of keys which are bitcoin addresses or hex-encoded public keys\n" - " [\n" - " \"key\" (string) bitcoin address or hex-encoded public key\n" - " ,...\n" - " ]\n" - - "\nResult:\n" - "{\n" - " \"address\":\"multisigaddress\", (string) The value of the new multisig address.\n" - " \"redeemScript\":\"script\" (string) The string value of the hex-encoded redemption script.\n" - "}\n" - - "\nExamples:\n" - "\nCreate a multisig address from 2 addresses\n" - + HelpExampleCli("createmultisig", "2 \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") + - "\nAs a json rpc call\n" - + HelpExampleRpc("icreatemultisig", "2, \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") - ; - throw runtime_error(msg); - } - - // Construct using pay-to-script-hash: - CScript inner = _createmultisig(params); - CScriptID innerID = inner.GetID(); - CBitcoinAddress address(innerID); - - Object result; - result.push_back(Pair("address", address.ToString())); - result.push_back(Pair("redeemScript", HexStr(inner.begin(), inner.end()))); - - return result; -} - struct tallyitem { @@ -1733,15 +1516,15 @@ Value keypoolrefill(const Array& params, bool fHelp) + HelpExampleRpc("keypoolrefill", "") ); - unsigned int kpSize = max(GetArg("-keypool", 100), (int64_t) 0); + // 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool + unsigned int kpSize = 0; if (params.size() > 0) { if (params[0].get_int() < 0) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size"); - kpSize = (unsigned int) params[0].get_int(); + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size."); + kpSize = (unsigned int)params[0].get_int(); } EnsureWalletIsUnlocked(); - pwalletMain->TopUpKeyPool(kpSize); if (pwalletMain->GetKeyPoolSize() < kpSize) @@ -1939,87 +1722,6 @@ Value encryptwallet(const Array& params, bool fHelp) return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup."; } -class DescribeAddressVisitor : public boost::static_visitor<Object> -{ -public: - Object operator()(const CNoDestination &dest) const { return Object(); } - - Object operator()(const CKeyID &keyID) const { - Object obj; - CPubKey vchPubKey; - pwalletMain->GetPubKey(keyID, vchPubKey); - obj.push_back(Pair("isscript", false)); - obj.push_back(Pair("pubkey", HexStr(vchPubKey))); - obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed())); - return obj; - } - - Object operator()(const CScriptID &scriptID) const { - Object obj; - obj.push_back(Pair("isscript", true)); - CScript subscript; - pwalletMain->GetCScript(scriptID, subscript); - std::vector<CTxDestination> addresses; - txnouttype whichType; - int nRequired; - ExtractDestinations(subscript, whichType, addresses, nRequired); - obj.push_back(Pair("script", GetTxnOutputType(whichType))); - obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end()))); - Array a; - BOOST_FOREACH(const CTxDestination& addr, addresses) - a.push_back(CBitcoinAddress(addr).ToString()); - obj.push_back(Pair("addresses", a)); - if (whichType == TX_MULTISIG) - obj.push_back(Pair("sigsrequired", nRequired)); - return obj; - } -}; - -Value validateaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "validateaddress \"bitcoinaddress\"\n" - "\nReturn information about the given bitcoin address.\n" - "\nArguments:\n" - "1. \"bitcoinaddress\" (string, required) The bitcoin address to validate\n" - "\nResult:\n" - "{\n" - " \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n" - " \"address\" : \"bitcoinaddress\", (string) The bitcoin address validated\n" - " \"ismine\" : true|false, (boolean) If the address is yours or not\n" - " \"isscript\" : true|false, (boolean) If the key is a script\n" - " \"pubkey\" : \"publickeyhex\", (string) The hex value of the raw public key\n" - " \"iscompressed\" : true|false, (boolean) If the address is compressed\n" - " \"account\" : \"account\" (string) The account associated with the address, \"\" is the default account\n" - "}\n" - "\nExamples:\n" - + HelpExampleCli("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") - + HelpExampleRpc("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") - ); - - CBitcoinAddress address(params[0].get_str()); - bool isValid = address.IsValid(); - - Object ret; - ret.push_back(Pair("isvalid", isValid)); - if (isValid) - { - CTxDestination dest = address.Get(); - string currentAddress = address.ToString(); - ret.push_back(Pair("address", currentAddress)); - bool fMine = pwalletMain ? IsMine(*pwalletMain, dest) : false; - ret.push_back(Pair("ismine", fMine)); - if (fMine) { - Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest); - ret.insert(ret.end(), detail.begin(), detail.end()); - } - if (pwalletMain && pwalletMain->mapAddressBook.count(dest)) - ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest].name)); - } - return ret; -} - Value lockunspent(const Array& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 2) @@ -2143,3 +1845,28 @@ Value listlockunspent(const Array& params, bool fHelp) return ret; } +Value settxfee(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 1) + throw runtime_error( + "settxfee amount\n" + "\nSet the transaction fee. 'amount' is a real and is rounded to the nearest 0.00000001\n" + "\nArguments:\n" + "1. amount (numeric, required) The transaction fee in btc rounded to the nearest 0.00000001\n" + "\nResult\n" + "true|false (boolean) Returns true if successful\n" + "\nExamples:\n" + + HelpExampleCli("settxfee", "0.00001") + + HelpExampleRpc("settxfee", "0.00001") + ); + + // Amount + int64_t nAmount = 0; + if (params[0].get_real() != 0.0) + nAmount = AmountFromValue(params[0]); // rejects 0.0 amounts + + nTransactionFee = nAmount; + return true; +} + + diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp index f0fb84bc54..fbca09b4dc 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -21,6 +21,7 @@ // Tests this internal-to-main.cpp method: extern bool AddOrphanTx(const CTransaction& tx); extern unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans); +extern void Misbehaving(NodeId nodeid, int howmuch); extern std::map<uint256, CTransaction> mapOrphanTransactions; extern std::map<uint256, std::set<uint256> > mapOrphanTransactionsByPrev; @@ -38,16 +39,21 @@ BOOST_AUTO_TEST_CASE(DoS_banning) CNode::ClearBanned(); CAddress addr1(ip(0xa0b0c001)); CNode dummyNode1(INVALID_SOCKET, addr1, "", true); - dummyNode1.Misbehaving(100); // Should get banned + dummyNode1.nVersion = 1; + Misbehaving(dummyNode1.GetId(), 100); // Should get banned + SendMessages(&dummyNode1, false); BOOST_CHECK(CNode::IsBanned(addr1)); BOOST_CHECK(!CNode::IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different IP, not banned CAddress addr2(ip(0xa0b0c002)); CNode dummyNode2(INVALID_SOCKET, addr2, "", true); - dummyNode2.Misbehaving(50); + dummyNode2.nVersion = 1; + Misbehaving(dummyNode2.GetId(), 50); + SendMessages(&dummyNode2, false); BOOST_CHECK(!CNode::IsBanned(addr2)); // 2 not banned yet... BOOST_CHECK(CNode::IsBanned(addr1)); // ... but 1 still should be - dummyNode2.Misbehaving(50); + Misbehaving(dummyNode2.GetId(), 50); + SendMessages(&dummyNode2, false); BOOST_CHECK(CNode::IsBanned(addr2)); } @@ -57,11 +63,15 @@ BOOST_AUTO_TEST_CASE(DoS_banscore) mapArgs["-banscore"] = "111"; // because 11 is my favorite number CAddress addr1(ip(0xa0b0c001)); CNode dummyNode1(INVALID_SOCKET, addr1, "", true); - dummyNode1.Misbehaving(100); + dummyNode1.nVersion = 1; + Misbehaving(dummyNode1.GetId(), 100); + SendMessages(&dummyNode1, false); BOOST_CHECK(!CNode::IsBanned(addr1)); - dummyNode1.Misbehaving(10); + Misbehaving(dummyNode1.GetId(), 10); + SendMessages(&dummyNode1, false); BOOST_CHECK(!CNode::IsBanned(addr1)); - dummyNode1.Misbehaving(1); + Misbehaving(dummyNode1.GetId(), 1); + SendMessages(&dummyNode1, false); BOOST_CHECK(CNode::IsBanned(addr1)); mapArgs.erase("-banscore"); } @@ -74,8 +84,10 @@ BOOST_AUTO_TEST_CASE(DoS_bantime) CAddress addr(ip(0xa0b0c001)); CNode dummyNode(INVALID_SOCKET, addr, "", true); + dummyNode.nVersion = 1; - dummyNode.Misbehaving(100); + Misbehaving(dummyNode.GetId(), 100); + SendMessages(&dummyNode, false); BOOST_CHECK(CNode::IsBanned(addr)); SetMockTime(nStartTime+60*60); diff --git a/src/test/Makefile.am b/src/test/Makefile.am index 39f2c6a385..ccc8da1690 100644 --- a/src/test/Makefile.am +++ b/src/test/Makefile.am @@ -21,8 +21,13 @@ BUILT_SOURCES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.raw.h) # test_bitcoin binary # test_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(TESTDEFS) test_bitcoin_LDADD = $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBLEVELDB) $(LIBMEMENV) \ - $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(BDB_LIBS) -test_bitcoin_SOURCES = accounting_tests.cpp alert_tests.cpp \ + $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) +if ENABLE_WALLET +test_bitcoin_LDADD += $(LIBBITCOIN_WALLET) +endif +test_bitcoin_LDADD += $(BDB_LIBS) + +test_bitcoin_SOURCES = alert_tests.cpp \ allocator_tests.cpp base32_tests.cpp base58_tests.cpp base64_tests.cpp \ bignum_tests.cpp bloom_tests.cpp canonical_tests.cpp checkblock_tests.cpp \ Checkpoints_tests.cpp compress_tests.cpp DoS_tests.cpp getarg_tests.cpp \ @@ -30,7 +35,11 @@ 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 sighash_tests.cpp $(JSON_TEST_FILES) $(RAW_TEST_FILES) + sighash_tests.cpp $(JSON_TEST_FILES) $(RAW_TEST_FILES) + +if ENABLE_WALLET +test_bitcoin_SOURCES += accounting_tests.cpp wallet_tests.cpp rpc_wallet_tests.cpp +endif nodist_test_bitcoin_SOURCES = $(BUILT_SOURCES) diff --git a/src/test/data/tx_invalid.json b/src/test/data/tx_invalid.json index a26f4a87db..e386f81fe6 100644 --- a/src/test/data/tx_invalid.json +++ b/src/test/data/tx_invalid.json @@ -65,5 +65,10 @@ ["Same as the transactions in valid with one input SIGHASH_ALL and one SIGHASH_ANYONECANPAY, but we set the _ANYONECANPAY sequence number, invalidating the SIGHASH_ALL signature"], [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"], ["0000000000000000000000000000000000000000000000000000000000000200", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"]], - "01000000020001000000000000000000000000000000000000000000000000000000000000000000004948304502203a0f5f0e1f2bdbcd04db3061d18f3af70e07f4f467cbc1b8116f267025f5360b022100c792b6e215afc5afc721a351ec413e714305cb749aae3d7fee76621313418df10101000000000200000000000000000000000000000000000000000000000000000000000000000000484730440220201dc2d030e380e8f9cfb41b442d930fa5a685bb2c8db5906671f865507d0670022018d9e7a8d4c8d86a73c2a724ee38ef983ec249827e0e464841735955c707ece98101000000010100000000000000015100000000", true] + "01000000020001000000000000000000000000000000000000000000000000000000000000000000004948304502203a0f5f0e1f2bdbcd04db3061d18f3af70e07f4f467cbc1b8116f267025f5360b022100c792b6e215afc5afc721a351ec413e714305cb749aae3d7fee76621313418df10101000000000200000000000000000000000000000000000000000000000000000000000000000000484730440220201dc2d030e380e8f9cfb41b442d930fa5a685bb2c8db5906671f865507d0670022018d9e7a8d4c8d86a73c2a724ee38ef983ec249827e0e464841735955c707ece98101000000010100000000000000015100000000", true], + +["Incorrect signature order"], +["Note the input is just required to make the tester happy"], +[[["b3da01dd4aae683c7aee4d5d8b52a540a508e1115f77cd7fa9a291243f501223", 0, "HASH160 0x14 0xb1ce99298d5f07364b57b1e5c9cc00be0b04a954 EQUAL"]], +"01000000012312503f2491a2a97fcd775f11e108a540a5528b5d4dee7a3c68ae4add01dab300000000fdfe000048304502207aacee820e08b0b174e248abd8d7a34ed63b5da3abedb99934df9fddd65c05c4022100dfe87896ab5ee3df476c2655f9fbe5bd089dccbef3e4ea05b5d121169fe7f5f401483045022100f6649b0eddfdfd4ad55426663385090d51ee86c3481bdc6b0c18ea6c0ece2c0b0220561c315b07cffa6f7dd9df96dbae9200c2dee09bf93cc35ca05e6cdf613340aa014c695221031d11db38972b712a9fe1fc023577c7ae3ddb4a3004187d41c45121eecfdbb5b7210207ec36911b6ad2382860d32989c7b8728e9489d7bbc94a6b5509ef0029be128821024ea9fac06f666a4adc3fc1357b7bec1fd0bdece2b9d08579226a8ebde53058e453aeffffffff0180380100000000001976a914c9b99cddf847d10685a4fabaa0baf505f7c3dfab88ac00000000", true] ] diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json index 63e7074a32..c33a7a035a 100644 --- a/src/test/data/tx_valid.json +++ b/src/test/data/tx_valid.json @@ -87,5 +87,10 @@ ["ddc454a1c0c35c188c98976b17670f69e586d9c0f3593ea879928332f0a069e7, which spends an input that pushes using a PUSHDATA1 that is negative when read as signed"], [[["c5510a5dd97a25f43175af1fe649b707b1df8e1a41489bac33a23087027a2f48", 0, "0x4c 0xae 0x606563686f2022553246736447566b58312b5a536e587574356542793066794778625456415675534a6c376a6a334878416945325364667657734f53474f36633338584d7439435c6e543249584967306a486956304f376e775236644546673d3d22203e20743b206f70656e73736c20656e63202d7061737320706173733a5b314a564d7751432d707269766b65792d6865785d202d64202d6165732d3235362d636263202d61202d696e207460 DROP DUP HASH160 0x14 0xbfd7436b6265aa9de506f8a994f881ff08cc2872 EQUALVERIFY CHECKSIG"]], - "0100000001482f7a028730a233ac9b48411a8edfb107b749e61faf7531f4257ad95d0a51c5000000008b483045022100bf0bbae9bde51ad2b222e87fbf67530fbafc25c903519a1e5dcc52a32ff5844e022028c4d9ad49b006dd59974372a54291d5764be541574bb0c4dc208ec51f80b7190141049dd4aad62741dc27d5f267f7b70682eee22e7e9c1923b9c0957bdae0b96374569b460eb8d5b40d972e8c7c0ad441de3d94c4a29864b212d56050acb980b72b2bffffffff0180969800000000001976a914e336d0017a9d28de99d16472f6ca6d5a3a8ebc9988ac00000000", true] + "0100000001482f7a028730a233ac9b48411a8edfb107b749e61faf7531f4257ad95d0a51c5000000008b483045022100bf0bbae9bde51ad2b222e87fbf67530fbafc25c903519a1e5dcc52a32ff5844e022028c4d9ad49b006dd59974372a54291d5764be541574bb0c4dc208ec51f80b7190141049dd4aad62741dc27d5f267f7b70682eee22e7e9c1923b9c0957bdae0b96374569b460eb8d5b40d972e8c7c0ad441de3d94c4a29864b212d56050acb980b72b2bffffffff0180969800000000001976a914e336d0017a9d28de99d16472f6ca6d5a3a8ebc9988ac00000000", true], + +["Correct signature order"], +["Note the input is just required to make the tester happy"], +[[["b3da01dd4aae683c7aee4d5d8b52a540a508e1115f77cd7fa9a291243f501223", 0, "HASH160 0x14 0xb1ce99298d5f07364b57b1e5c9cc00be0b04a954 EQUAL"]], +"01000000012312503f2491a2a97fcd775f11e108a540a5528b5d4dee7a3c68ae4add01dab300000000fdfe0000483045022100f6649b0eddfdfd4ad55426663385090d51ee86c3481bdc6b0c18ea6c0ece2c0b0220561c315b07cffa6f7dd9df96dbae9200c2dee09bf93cc35ca05e6cdf613340aa0148304502207aacee820e08b0b174e248abd8d7a34ed63b5da3abedb99934df9fddd65c05c4022100dfe87896ab5ee3df476c2655f9fbe5bd089dccbef3e4ea05b5d121169fe7f5f4014c695221031d11db38972b712a9fe1fc023577c7ae3ddb4a3004187d41c45121eecfdbb5b7210207ec36911b6ad2382860d32989c7b8728e9489d7bbc94a6b5509ef0029be128821024ea9fac06f666a4adc3fc1357b7bec1fd0bdece2b9d08579226a8ebde53058e453aeffffffff0180380100000000001976a914c9b99cddf847d10685a4fabaa0baf505f7c3dfab88ac00000000", true] ] diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 46c9ae021d..8e3091d555 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -4,11 +4,9 @@ #include "miner.h" #include "uint256.h" #include "util.h" -#include "wallet.h" #include <boost/test/unit_test.hpp> -extern CWallet* pwalletMain; extern void SHA256Transform(void* pstate, void* pinput, const void* pinit); BOOST_AUTO_TEST_SUITE(miner_tests) @@ -51,7 +49,7 @@ struct { // NOTE: These tests rely on CreateNewBlock doing its own self-validation! BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) { - CReserveKey reservekey(pwalletMain); + CScript scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG; CBlockTemplate *pblocktemplate; CTransaction tx; CScript script; @@ -60,7 +58,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) LOCK(cs_main); // Simple block creation, nothing special yet: - BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); // We can't make transactions until we have inputs // Therefore, load 100 blocks :) @@ -86,7 +84,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) delete pblocktemplate; // Just to make sure we can still make simple blocks - BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); delete pblocktemplate; // block sigops > limit: 1000 CHECKMULTISIG + 1 @@ -104,7 +102,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); tx.vin[0].prevout.hash = hash; } - BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); delete pblocktemplate; mempool.clear(); @@ -124,14 +122,14 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); tx.vin[0].prevout.hash = hash; } - BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); delete pblocktemplate; mempool.clear(); // orphan in mempool hash = tx.GetHash(); mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); - BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); delete pblocktemplate; mempool.clear(); @@ -149,7 +147,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue = 5900000000LL; hash = tx.GetHash(); mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); - BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); delete pblocktemplate; mempool.clear(); @@ -160,7 +158,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue = 0; hash = tx.GetHash(); mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); - BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); delete pblocktemplate; mempool.clear(); @@ -178,7 +176,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue -= 1000000; hash = tx.GetHash(); mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); - BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); delete pblocktemplate; mempool.clear(); @@ -192,17 +190,17 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].scriptPubKey = CScript() << OP_2; hash = tx.GetHash(); mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); - BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); delete pblocktemplate; mempool.clear(); // subsidy changing int nHeight = chainActive.Height(); chainActive.Tip()->nHeight = 209999; - BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); delete pblocktemplate; chainActive.Tip()->nHeight = 210000; - BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); delete pblocktemplate; chainActive.Tip()->nHeight = nHeight; diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 76580bae43..29195545da 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -9,9 +9,7 @@ using namespace std; using namespace json_spirit; -BOOST_AUTO_TEST_SUITE(rpc_tests) - -static Array +Array createArgs(int nRequired, const char* address1=NULL, const char* address2=NULL) { Array result; @@ -23,44 +21,7 @@ createArgs(int nRequired, const char* address1=NULL, const char* address2=NULL) return result; } -BOOST_AUTO_TEST_CASE(rpc_addmultisig) -{ - rpcfn_type addmultisig = tableRPC["addmultisigaddress"]->actor; - - // old, 65-byte-long: - const char address1Hex[] = "0434e3e09f49ea168c5bbf53f877ff4206923858aab7c7e1df25bc263978107c95e35065a27ef6f1b27222db0ec97e0e895eaca603d3ee0d4c060ce3d8a00286c8"; - // new, compressed: - const char address2Hex[] = "0388c2037017c62240b6b72ac1a2a5f94da790596ebd06177c8572752922165cb4"; - - Value v; - CBitcoinAddress address; - BOOST_CHECK_NO_THROW(v = addmultisig(createArgs(1, address1Hex), false)); - address.SetString(v.get_str()); - BOOST_CHECK(address.IsValid() && address.IsScript()); - - BOOST_CHECK_NO_THROW(v = addmultisig(createArgs(1, address1Hex, address2Hex), false)); - address.SetString(v.get_str()); - BOOST_CHECK(address.IsValid() && address.IsScript()); - - BOOST_CHECK_NO_THROW(v = addmultisig(createArgs(2, address1Hex, address2Hex), false)); - address.SetString(v.get_str()); - BOOST_CHECK(address.IsValid() && address.IsScript()); - - BOOST_CHECK_THROW(addmultisig(createArgs(0), false), runtime_error); - BOOST_CHECK_THROW(addmultisig(createArgs(1), false), runtime_error); - BOOST_CHECK_THROW(addmultisig(createArgs(2, address1Hex), false), runtime_error); - - BOOST_CHECK_THROW(addmultisig(createArgs(1, ""), false), runtime_error); - BOOST_CHECK_THROW(addmultisig(createArgs(1, "NotAValidPubkey"), false), runtime_error); - - string short1(address1Hex, address1Hex+sizeof(address1Hex)-2); // last byte missing - BOOST_CHECK_THROW(addmultisig(createArgs(2, short1.c_str()), false), runtime_error); - - string short2(address1Hex+1, address1Hex+sizeof(address1Hex)); // first byte missing - BOOST_CHECK_THROW(addmultisig(createArgs(2, short2.c_str()), false), runtime_error); -} - -static Value CallRPC(string args) +Value CallRPC(string args) { vector<string> vArgs; boost::split(vArgs, args, boost::is_any_of(" \t")); @@ -79,34 +40,8 @@ static Value CallRPC(string args) } } -BOOST_AUTO_TEST_CASE(rpc_wallet) -{ - // Test RPC calls for various wallet statistics - Value r; - - BOOST_CHECK_NO_THROW(CallRPC("listunspent")); - BOOST_CHECK_THROW(CallRPC("listunspent string"), runtime_error); - BOOST_CHECK_THROW(CallRPC("listunspent 0 string"), runtime_error); - BOOST_CHECK_THROW(CallRPC("listunspent 0 1 not_array"), runtime_error); - BOOST_CHECK_THROW(CallRPC("listunspent 0 1 [] extra"), runtime_error); - BOOST_CHECK_NO_THROW(r=CallRPC("listunspent 0 1 []")); - BOOST_CHECK(r.get_array().empty()); - - BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaddress")); - BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaddress 0")); - BOOST_CHECK_THROW(CallRPC("listreceivedbyaddress not_int"), runtime_error); - BOOST_CHECK_THROW(CallRPC("listreceivedbyaddress 0 not_bool"), runtime_error); - BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaddress 0 true")); - BOOST_CHECK_THROW(CallRPC("listreceivedbyaddress 0 true extra"), runtime_error); - - BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount")); - BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount 0")); - BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount not_int"), runtime_error); - BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount 0 not_bool"), runtime_error); - BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount 0 true")); - BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount 0 true extra"), runtime_error); -} +BOOST_AUTO_TEST_SUITE(rpc_tests) BOOST_AUTO_TEST_CASE(rpc_rawparams) { diff --git a/src/test/rpc_wallet_tests.cpp b/src/test/rpc_wallet_tests.cpp new file mode 100644 index 0000000000..2cf0fb350b --- /dev/null +++ b/src/test/rpc_wallet_tests.cpp @@ -0,0 +1,82 @@ +#include "rpcserver.h" +#include "rpcclient.h" + +#include "base58.h" + +#include <boost/algorithm/string.hpp> +#include <boost/test/unit_test.hpp> + +using namespace std; +using namespace json_spirit; + +extern Array createArgs(int nRequired, const char* address1=NULL, const char* address2=NULL); +extern Value CallRPC(string args); + +BOOST_AUTO_TEST_SUITE(rpc_wallet_tests) + +BOOST_AUTO_TEST_CASE(rpc_addmultisig) +{ + rpcfn_type addmultisig = tableRPC["addmultisigaddress"]->actor; + + // old, 65-byte-long: + const char address1Hex[] = "0434e3e09f49ea168c5bbf53f877ff4206923858aab7c7e1df25bc263978107c95e35065a27ef6f1b27222db0ec97e0e895eaca603d3ee0d4c060ce3d8a00286c8"; + // new, compressed: + const char address2Hex[] = "0388c2037017c62240b6b72ac1a2a5f94da790596ebd06177c8572752922165cb4"; + + Value v; + CBitcoinAddress address; + BOOST_CHECK_NO_THROW(v = addmultisig(createArgs(1, address1Hex), false)); + address.SetString(v.get_str()); + BOOST_CHECK(address.IsValid() && address.IsScript()); + + BOOST_CHECK_NO_THROW(v = addmultisig(createArgs(1, address1Hex, address2Hex), false)); + address.SetString(v.get_str()); + BOOST_CHECK(address.IsValid() && address.IsScript()); + + BOOST_CHECK_NO_THROW(v = addmultisig(createArgs(2, address1Hex, address2Hex), false)); + address.SetString(v.get_str()); + BOOST_CHECK(address.IsValid() && address.IsScript()); + + BOOST_CHECK_THROW(addmultisig(createArgs(0), false), runtime_error); + BOOST_CHECK_THROW(addmultisig(createArgs(1), false), runtime_error); + BOOST_CHECK_THROW(addmultisig(createArgs(2, address1Hex), false), runtime_error); + + BOOST_CHECK_THROW(addmultisig(createArgs(1, ""), false), runtime_error); + BOOST_CHECK_THROW(addmultisig(createArgs(1, "NotAValidPubkey"), false), runtime_error); + + string short1(address1Hex, address1Hex+sizeof(address1Hex)-2); // last byte missing + BOOST_CHECK_THROW(addmultisig(createArgs(2, short1.c_str()), false), runtime_error); + + string short2(address1Hex+1, address1Hex+sizeof(address1Hex)); // first byte missing + BOOST_CHECK_THROW(addmultisig(createArgs(2, short2.c_str()), false), runtime_error); +} + +BOOST_AUTO_TEST_CASE(rpc_wallet) +{ + // Test RPC calls for various wallet statistics + Value r; + + BOOST_CHECK_NO_THROW(CallRPC("listunspent")); + BOOST_CHECK_THROW(CallRPC("listunspent string"), runtime_error); + BOOST_CHECK_THROW(CallRPC("listunspent 0 string"), runtime_error); + BOOST_CHECK_THROW(CallRPC("listunspent 0 1 not_array"), runtime_error); + BOOST_CHECK_THROW(CallRPC("listunspent 0 1 [] extra"), runtime_error); + BOOST_CHECK_NO_THROW(r=CallRPC("listunspent 0 1 []")); + BOOST_CHECK(r.get_array().empty()); + + BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaddress")); + BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaddress 0")); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaddress not_int"), runtime_error); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaddress 0 not_bool"), runtime_error); + BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaddress 0 true")); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaddress 0 true extra"), runtime_error); + + BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount")); + BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount 0")); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount not_int"), runtime_error); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount 0 not_bool"), runtime_error); + BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount 0 true")); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount 0 true extra"), runtime_error); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp index afcdd118bc..415f957811 100644 --- a/src/test/serialize_tests.cpp +++ b/src/test/serialize_tests.cpp @@ -61,9 +61,16 @@ BOOST_AUTO_TEST_CASE(compactsize) static bool isCanonicalException(const std::ios_base::failure& ex) { - return std::string("non-canonical ReadCompactSize()") == ex.what(); + std::ios_base::failure expectedException("non-canonical ReadCompactSize()"); + + // The string returned by what() can be different for different platforms. + // Instead of directly comparing the ex.what() with an expected string, + // create an instance of exception to see if ex.what() matches + // the expected explanatory string returned by the exception instance. + return strcmp(expectedException.what(), ex.what()) == 0; } + BOOST_AUTO_TEST_CASE(noncanonical) { // Write some non-canonical CompactSize encodings, and diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 443e7735d7..667bdfb62e 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -2,12 +2,14 @@ -#include "db.h" #include "main.h" #include "txdb.h" #include "ui_interface.h" #include "util.h" +#ifdef ENABLE_WALLET +#include "db.h" #include "wallet.h" +#endif #include <boost/filesystem.hpp> #include <boost/test/unit_test.hpp> @@ -26,7 +28,9 @@ struct TestingSetup { TestingSetup() { fPrintToDebugger = true; // don't want to write to debug.log file noui_connect(); +#ifdef ENABLE_WALLET bitdb.MakeMock(); +#endif pathTemp = GetTempPath() / strprintf("test_bitcoin_%lu_%i", (unsigned long)GetTime(), (int)(GetRand(100000))); boost::filesystem::create_directories(pathTemp); mapArgs["-datadir"] = pathTemp.string(); @@ -34,24 +38,32 @@ struct TestingSetup { pcoinsdbview = new CCoinsViewDB(1 << 23, true); pcoinsTip = new CCoinsViewCache(*pcoinsdbview); InitBlockIndex(); +#ifdef ENABLE_WALLET bool fFirstRun; pwalletMain = new CWallet("wallet.dat"); pwalletMain->LoadWallet(fFirstRun); RegisterWallet(pwalletMain); +#endif nScriptCheckThreads = 3; for (int i=0; i < nScriptCheckThreads-1; i++) threadGroup.create_thread(&ThreadScriptCheck); + RegisterNodeSignals(GetNodeSignals()); } ~TestingSetup() { threadGroup.interrupt_all(); threadGroup.join_all(); + UnregisterNodeSignals(GetNodeSignals()); +#ifdef ENABLE_WALLET delete pwalletMain; pwalletMain = NULL; +#endif delete pcoinsTip; delete pcoinsdbview; delete pblocktree; +#ifdef ENABLE_WALLET bitdb.Flush(true); +#endif boost::filesystem::remove_all(pathTemp); } }; diff --git a/src/ui_interface.h b/src/ui_interface.h index 2378d5dfbb..a14ed67814 100644 --- a/src/ui_interface.h +++ b/src/ui_interface.h @@ -72,12 +72,6 @@ public: /** Show message box. */ boost::signals2::signal<bool (const std::string& message, const std::string& caption, unsigned int style), boost::signals2::last_value<bool> > ThreadSafeMessageBox; - /** Ask the user whether they want to pay a fee or not. */ - boost::signals2::signal<bool (int64_t nFeeRequired), boost::signals2::last_value<bool> > ThreadSafeAskFee; - - /** Handle a URL passed at the command line. */ - boost::signals2::signal<void (const std::string& strURI)> ThreadSafeHandleURI; - /** Progress message during initialization. */ boost::signals2::signal<void (const std::string &message)> InitMessage; diff --git a/src/util.cpp b/src/util.cpp index a8c591cc7a..bedf59767b 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -242,17 +242,23 @@ int LogPrint(const char* category, const char* pszFormat, ...) 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) + // Give each thread quick access to -debug settings. + // This helps prevent issues debugging global destructors, + // where mapMultiArgs might be deleted before another + // global destructor calls LogPrint() + static boost::thread_specific_ptr<set<string> > ptrCategory; + if (ptrCategory.get() == NULL) { - // Category was not found (not supplied via -debug=<category>) - if (find(categories.begin(), categories.end(), string(category)) == categories.end()) - return 0; + const vector<string>& categories = mapMultiArgs["-debug"]; + ptrCategory.reset(new set<string>(categories.begin(), categories.end())); + // thread_specific_ptr automatically deletes the set when the thread ends. } + const set<string>& setCategories = *ptrCategory.get(); + + // if not debugging everything and not debugging specific category, LogPrint does nothing. + if (setCategories.count(string("")) == 0 && + setCategories.count(string(category)) == 0) + return 0; } int ret = 0; // Returns total number of characters written @@ -299,27 +305,24 @@ int LogPrint(const char* category, const char* pszFormat, ...) #ifdef WIN32 if (fPrintToDebugger) { - static CCriticalSection cs_OutputDebugStringF; - // accumulate and output a line at a time - { - LOCK(cs_OutputDebugStringF); - static std::string buffer; + static std::string buffer; - va_list arg_ptr; - va_start(arg_ptr, pszFormat); - buffer += vstrprintf(pszFormat, arg_ptr); - va_end(arg_ptr); + boost::mutex::scoped_lock scoped_lock(*mutexDebugLog); - int line_start = 0, line_end; - while((line_end = buffer.find('\n', line_start)) != -1) - { - OutputDebugStringA(buffer.substr(line_start, line_end - line_start).c_str()); - line_start = line_end + 1; - ret += line_end-line_start; - } - buffer.erase(0, line_start); + va_list arg_ptr; + va_start(arg_ptr, pszFormat); + buffer += vstrprintf(pszFormat, arg_ptr); + va_end(arg_ptr); + + int line_start = 0, line_end; + while((line_end = buffer.find('\n', line_start)) != -1) + { + OutputDebugStringA(buffer.substr(line_start, line_end - line_start).c_str()); + line_start = line_end + 1; + ret += line_end-line_start; } + buffer.erase(0, line_start); } #endif return ret; diff --git a/src/wallet.cpp b/src/wallet.cpp index 241e937b1b..76a83082ae 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -16,6 +16,8 @@ using namespace std; +// Settings +int64_t nTransactionFee = 0; ////////////////////////////////////////////////////////////////////////////// // @@ -1434,7 +1436,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) -string CWallet::SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, bool fAskFee) +string CWallet::SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew) { CReserveKey reservekey(this); int64_t nFeeRequired; @@ -1454,9 +1456,6 @@ string CWallet::SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNe return strError; } - if (fAskFee && !uiInterface.ThreadSafeAskFee(nFeeRequired)) - return "ABORTED"; - if (!CommitTransaction(wtxNew, reservekey)) return _("Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); @@ -1465,7 +1464,7 @@ string CWallet::SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNe -string CWallet::SendMoneyToDestination(const CTxDestination& address, int64_t nValue, CWalletTx& wtxNew, bool fAskFee) +string CWallet::SendMoneyToDestination(const CTxDestination& address, int64_t nValue, CWalletTx& wtxNew) { // Check amount if (nValue <= 0) @@ -1477,7 +1476,7 @@ string CWallet::SendMoneyToDestination(const CTxDestination& address, int64_t nV CScript scriptPubKey; scriptPubKey.SetDestination(address); - return SendMoney(scriptPubKey, nValue, wtxNew, fAskFee); + return SendMoney(scriptPubKey, nValue, wtxNew); } diff --git a/src/wallet.h b/src/wallet.h index 90209122fd..99f6293b1e 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -23,6 +23,9 @@ #include <utility> #include <vector> +// Settings +extern int64_t nTransactionFee; + class CAccountingEntry; class CCoinControl; class COutput; @@ -217,8 +220,8 @@ public: bool CreateTransaction(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, std::string& strFailReason, const CCoinControl *coinControl = NULL); bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); - std::string SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, bool fAskFee=false); - std::string SendMoneyToDestination(const CTxDestination &address, int64_t nValue, CWalletTx& wtxNew, bool fAskFee=false); + std::string SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew); + std::string SendMoneyToDestination(const CTxDestination &address, int64_t nValue, CWalletTx& wtxNew); bool NewKeyPool(); bool TopUpKeyPool(unsigned int kpSize = 0); |