diff options
Diffstat (limited to 'src')
77 files changed, 2325 insertions, 1470 deletions
diff --git a/src/allocators.h b/src/allocators.h index 99afa10c25..eb2aed6721 100644 --- a/src/allocators.h +++ b/src/allocators.h @@ -9,6 +9,7 @@ #include <string> #include <boost/thread/mutex.hpp> #include <map> +#include <openssl/crypto.h> // for OPENSSL_cleanse() #ifdef WIN32 #ifdef _WIN32_WINNT @@ -212,7 +213,7 @@ struct secure_allocator : public std::allocator<T> { if (p != NULL) { - memset(p, 0, sizeof(T) * n); + OPENSSL_cleanse(p, sizeof(T) * n); LockedPageManager::instance.UnlockRange(p, sizeof(T) * n); } std::allocator<T>::deallocate(p, n); @@ -246,7 +247,7 @@ struct zero_after_free_allocator : public std::allocator<T> void deallocate(T* p, std::size_t n) { if (p != NULL) - memset(p, 0, sizeof(T) * n); + OPENSSL_cleanse(p, sizeof(T) * n); std::allocator<T>::deallocate(p, n); } }; diff --git a/src/base58.h b/src/base58.h index 9dfea86ff5..be8a541f67 100644 --- a/src/base58.h +++ b/src/base58.h @@ -17,9 +17,11 @@ #include <string> #include <vector> + #include "bignum.h" #include "key.h" #include "script.h" +#include "allocators.h" static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; @@ -178,7 +180,8 @@ protected: unsigned char nVersion; // the actually encoded data - std::vector<unsigned char> vchData; + typedef std::vector<unsigned char, zero_after_free_allocator<unsigned char> > vector_uchar; + vector_uchar vchData; CBase58Data() { @@ -186,13 +189,6 @@ protected: vchData.clear(); } - ~CBase58Data() - { - // zero the memory, as it may contain sensitive data - if (!vchData.empty()) - memset(&vchData[0], 0, vchData.size()); - } - void SetData(int nVersionIn, const void* pdata, size_t nSize) { nVersion = nVersionIn; @@ -221,7 +217,7 @@ public: vchData.resize(vchTemp.size() - 1); if (!vchData.empty()) memcpy(&vchData[0], &vchTemp[1], vchData.size()); - memset(&vchTemp[0], 0, vchTemp.size()); + OPENSSL_cleanse(&vchTemp[0], vchData.size()); return true; } @@ -457,4 +453,4 @@ public: } }; -#endif +#endif // BITCOIN_BASE58_H diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 725037addc..bfb696da3d 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -11,7 +11,6 @@ #include "bitcoinrpc.h" #include "db.h" -#undef printf #include <boost/asio.hpp> #include <boost/asio/ip/v6_only.hpp> #include <boost/bind.hpp> @@ -26,8 +25,6 @@ #include <boost/shared_ptr.hpp> #include <list> -#define printf OutputDebugStringF - using namespace std; using namespace boost; using namespace boost::asio; @@ -179,14 +176,12 @@ Value help(const Array& params, bool fHelp) Value stop(const Array& params, bool fHelp) { + // Accept the deprecated and ignored 'detach´ boolean argument if (fHelp || params.size() > 1) throw runtime_error( - "stop <detach>\n" - "<detach> is true or false to detach the database or not for this stop only\n" - "Stop Bitcoin server (and possibly override the detachdb config value)."); + "stop\n" + "Stop Bitcoin server."); // Shutdown will take long enough that the response should get back - if (params.size() > 0) - bitdb.SetDetach(params[0].get_bool()); StartShutdown(); return "Bitcoin server stopping"; } @@ -234,6 +229,7 @@ static const CRPCCommand vRPCCommands[] = { "sendfrom", &sendfrom, false, false }, { "sendmany", &sendmany, false, false }, { "addmultisigaddress", &addmultisigaddress, false, false }, + { "createmultisig", &createmultisig, true, true }, { "getrawmempool", &getrawmempool, true, false }, { "getblock", &getblock, false, false }, { "getblockhash", &getblockhash, false, false }, @@ -258,6 +254,8 @@ static const CRPCCommand vRPCCommands[] = { "sendrawtransaction", &sendrawtransaction, false, false }, { "gettxoutsetinfo", &gettxoutsetinfo, true, false }, { "gettxout", &gettxout, true, false }, + { "lockunspent", &lockunspent, false, false }, + { "listlockunspent", &listlockunspent, false, false }, }; CRPCTable::CRPCTable() @@ -361,6 +359,41 @@ static string HTTPReply(int nStatus, const string& strMsg, bool keepalive) strMsg.c_str()); } +bool ReadHTTPRequestLine(std::basic_istream<char>& stream, int &proto, + string& http_method, string& http_uri) +{ + string str; + getline(stream, str); + + // HTTP request line is space-delimited + vector<string> vWords; + boost::split(vWords, str, boost::is_any_of(" ")); + if (vWords.size() < 2) + return false; + + // HTTP methods permitted: GET, POST + http_method = vWords[0]; + if (http_method != "GET" && http_method != "POST") + return false; + + // HTTP URI must be an absolute path, relative to current host + http_uri = vWords[1]; + if (http_uri.size() == 0 || http_uri[0] != '/') + return false; + + // parse proto, if present + string strProto = ""; + if (vWords.size() > 2) + strProto = vWords[2]; + + proto = 0; + const char *ver = strstr(strProto.c_str(), "HTTP/1."); + if (ver != NULL) + proto = atoi(ver+7); + + return true; +} + int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto) { string str; @@ -376,7 +409,7 @@ int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto) return atoi(vWords[1].c_str()); } -int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet) +int ReadHTTPHeaders(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet) { int nLen = 0; loop @@ -401,17 +434,15 @@ int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHea return nLen; } -int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet, string& strMessageRet) +int ReadHTTPMessage(std::basic_istream<char>& stream, map<string, + string>& mapHeadersRet, string& strMessageRet, + int nProto) { mapHeadersRet.clear(); strMessageRet = ""; - // Read status - int nProto = 0; - int nStatus = ReadHTTPStatus(stream, nProto); - // Read header - int nLen = ReadHTTPHeader(stream, mapHeadersRet); + int nLen = ReadHTTPHeaders(stream, mapHeadersRet); if (nLen < 0 || nLen > (int)MAX_SIZE) return HTTP_INTERNAL_SERVER_ERROR; @@ -433,7 +464,7 @@ int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRe mapHeadersRet["connection"] = "close"; } - return nStatus; + return HTTP_OK; } bool HTTPAuthorized(map<string, string>& mapHeaders) @@ -719,7 +750,8 @@ void ThreadRPCServer2(void* parg) printf("ThreadRPCServer started\n"); strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]; - if (mapArgs["-rpcpassword"] == "") + if ((mapArgs["-rpcpassword"] == "") || + (mapArgs["-rpcuser"] == mapArgs["-rpcpassword"])) { unsigned char rand_pwd[32]; RAND_bytes(rand_pwd, 32); @@ -734,11 +766,12 @@ void ThreadRPCServer2(void* parg) "rpcuser=bitcoinrpc\n" "rpcpassword=%s\n" "(you do not need to remember this password)\n" + "The username and password MUST NOT be the same.\n" "If the file does not exist, create it with owner-readable-only file permissions.\n"), strWhatAmI.c_str(), GetConfigFile().string().c_str(), EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str()), - _("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL); + "", CClientUIInterface::MSG_ERROR); StartShutdown(); return; } @@ -829,7 +862,7 @@ void ThreadRPCServer2(void* parg) } if (!fListening) { - uiInterface.ThreadSafeMessageBox(strerr, _("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL); + uiInterface.ThreadSafeMessageBox(strerr, "", CClientUIInterface::MSG_ERROR); StartShutdown(); return; } @@ -940,10 +973,17 @@ void ThreadRPCServer3(void* parg) } return; } + + int nProto = 0; map<string, string> mapHeaders; - string strRequest; + string strRequest, strMethod, strURI; + + // Read HTTP request line + if (!ReadHTTPRequestLine(conn->stream(), nProto, strMethod, strURI)) + break; - ReadHTTP(conn->stream(), mapHeaders, strRequest); + // Read HTTP message headers and body + ReadHTTPMessage(conn->stream(), mapHeaders, strRequest, nProto); // Check authorization if (mapHeaders.count("authorization") == 0) @@ -1075,10 +1115,15 @@ Object CallRPC(const string& strMethod, const Array& params) string strPost = HTTPPost(strRequest, mapRequestHeaders); stream << strPost << std::flush; - // Receive reply + // Receive HTTP reply status + int nProto = 0; + int nStatus = ReadHTTPStatus(stream, nProto); + + // Receive HTTP reply message headers and body map<string, string> mapHeaders; string strReply; - int nStatus = ReadHTTP(stream, mapHeaders, strReply); + ReadHTTPMessage(stream, mapHeaders, strReply, nProto); + if (nStatus == HTTP_UNAUTHORIZED) throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); else if (nStatus >= 400 && nStatus != HTTP_BAD_REQUEST && nStatus != HTTP_NOT_FOUND && nStatus != HTTP_INTERNAL_SERVER_ERROR) @@ -1160,6 +1205,8 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]); if (strMethod == "addmultisigaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]); if (strMethod == "addmultisigaddress" && n > 1) ConvertTo<Array>(params[1]); + if (strMethod == "createmultisig" && n > 0) ConvertTo<boost::int64_t>(params[0]); + if (strMethod == "createmultisig" && n > 1) ConvertTo<Array>(params[1]); if (strMethod == "listunspent" && n > 0) ConvertTo<boost::int64_t>(params[0]); if (strMethod == "listunspent" && n > 1) ConvertTo<boost::int64_t>(params[1]); if (strMethod == "listunspent" && n > 2) ConvertTo<Array>(params[2]); @@ -1170,6 +1217,8 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri if (strMethod == "signrawtransaction" && n > 2) ConvertTo<Array>(params[2], true); if (strMethod == "gettxout" && n > 1) ConvertTo<boost::int64_t>(params[1]); if (strMethod == "gettxout" && n > 2) ConvertTo<bool>(params[2]); + if (strMethod == "lockunspent" && n > 0) ConvertTo<bool>(params[0]); + if (strMethod == "lockunspent" && n > 1) ConvertTo<Array>(params[1]); return params; } diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h index 9290697664..44050ae1bb 100644 --- a/src/bitcoinrpc.h +++ b/src/bitcoinrpc.h @@ -158,6 +158,7 @@ extern json_spirit::Value movecmd(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value sendfrom(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value sendmany(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value addmultisigaddress(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value createmultisig(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value listreceivedbyaddress(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value listreceivedbyaccount(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value listtransactions(const json_spirit::Array& params, bool fHelp); @@ -176,6 +177,8 @@ extern json_spirit::Value getinfo(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getrawtransaction(const json_spirit::Array& params, bool fHelp); // in rcprawtransaction.cpp extern json_spirit::Value listunspent(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value lockunspent(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value listlockunspent(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value createrawtransaction(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value decoderawtransaction(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value signrawtransaction(const json_spirit::Array& params, bool fHelp); diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index 8208854962..279003072e 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -39,6 +39,9 @@ namespace Checkpoints bool CheckBlock(int nHeight, const uint256& hash) { + if (!GetBoolArg("-checkpoints", true)) + return true; + MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints); MapCheckpoints::const_iterator i = checkpoints.find(nHeight); @@ -48,6 +51,9 @@ namespace Checkpoints int GetTotalBlocksEstimate() { + if (!GetBoolArg("-checkpoints", true)) + return 0; + MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints); return checkpoints.rbegin()->first; @@ -55,6 +61,9 @@ namespace Checkpoints CBlockIndex* GetLastCheckpoint(const std::map<uint256, CBlockIndex*>& mapBlockIndex) { + if (!GetBoolArg("-checkpoints", true)) + return NULL; + MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints); BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, checkpoints) diff --git a/src/clientversion.h b/src/clientversion.h index e79c306c09..24355d1a54 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -11,6 +11,9 @@ #define CLIENT_VERSION_REVISION 99 #define CLIENT_VERSION_BUILD 0 +// Set to true for release, false for prerelease or test build +#define CLIENT_VERSION_IS_RELEASE false + // Converts the parameter X to a string after macro replacement on X has been performed. // Don't merge these into one macro! #define STRINGIZE(X) DO_STRINGIZE(X) diff --git a/src/crypter.cpp b/src/crypter.cpp index 181b8fa00a..a2b62a87c8 100644 --- a/src/crypter.cpp +++ b/src/crypter.cpp @@ -24,8 +24,8 @@ bool CCrypter::SetKeyFromPassphrase(const SecureString& strKeyData, const std::v if (i != (int)WALLET_CRYPTO_KEY_SIZE) { - memset(&chKey, 0, sizeof chKey); - memset(&chIV, 0, sizeof chIV); + OPENSSL_cleanse(chKey, sizeof(chKey)); + OPENSSL_cleanse(chIV, sizeof(chIV)); return false; } diff --git a/src/crypter.h b/src/crypter.h index 04538a3fa5..6f75170bac 100644 --- a/src/crypter.h +++ b/src/crypter.h @@ -76,8 +76,8 @@ public: void CleanKey() { - memset(&chKey, 0, sizeof chKey); - memset(&chIV, 0, sizeof chIV); + OPENSSL_cleanse(chKey, sizeof(chKey)); + OPENSSL_cleanse(chIV, sizeof(chIV)); fKeySet = false; } diff --git a/src/db.cpp b/src/db.cpp index cf96fe6e38..94629f3cad 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -38,11 +38,13 @@ void CDBEnv::EnvShutdown() if (ret != 0) printf("EnvShutdown exception: %s (%d)\n", DbEnv::strerror(ret), ret); if (!fMockDb) - DbEnv(0).remove(GetDataDir().string().c_str(), 0); + DbEnv(0).remove(strPath.c_str(), 0); } CDBEnv::CDBEnv() : dbenv(DB_CXX_NO_EXCEPTIONS) { + fDbEnvInit = false; + fMockDb = false; } CDBEnv::~CDBEnv() @@ -55,7 +57,7 @@ void CDBEnv::Close() EnvShutdown(); } -bool CDBEnv::Open(boost::filesystem::path pathEnv_) +bool CDBEnv::Open(const boost::filesystem::path& path) { if (fDbEnvInit) return true; @@ -63,18 +65,17 @@ bool CDBEnv::Open(boost::filesystem::path pathEnv_) if (fShutdown) return false; - pathEnv = pathEnv_; - filesystem::path pathDataDir = pathEnv; - filesystem::path pathLogDir = pathDataDir / "database"; + strPath = path.string(); + filesystem::path pathLogDir = path / "database"; filesystem::create_directory(pathLogDir); - filesystem::path pathErrorFile = pathDataDir / "db.log"; + filesystem::path pathErrorFile = path / "db.log"; printf("dbenv.open LogDir=%s ErrorFile=%s\n", pathLogDir.string().c_str(), pathErrorFile.string().c_str()); unsigned int nEnvFlags = 0; if (GetBoolArg("-privdb", true)) nEnvFlags |= DB_PRIVATE; - int nDbCache = GetArg("-dbcache", 25); + unsigned int nDbCache = GetArg("-dbcache", 25); dbenv.set_lg_dir(pathLogDir.string().c_str()); dbenv.set_cachesize(nDbCache / 1024, (nDbCache % 1024)*1048576, 1); dbenv.set_lg_bsize(1048576); @@ -85,7 +86,7 @@ bool CDBEnv::Open(boost::filesystem::path pathEnv_) dbenv.set_flags(DB_AUTO_COMMIT, 1); dbenv.set_flags(DB_TXN_WRITE_NOSYNC, 1); dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1); - int ret = dbenv.open(pathDataDir.string().c_str(), + int ret = dbenv.open(strPath.c_str(), DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | @@ -271,14 +272,6 @@ CDB::CDB(const char *pszFile, const char* pszMode) : } } -static bool IsChainFile(std::string strFile) -{ - if (strFile == "coins.dat" || strFile == "blktree.dat") - return true; - - return false; -} - void CDB::Flush() { if (activeTxn) @@ -288,10 +281,6 @@ void CDB::Flush() unsigned int nMinutes = 0; if (fReadOnly) nMinutes = 1; - if (IsChainFile(strFile)) - nMinutes = 2; - if (IsChainFile(strFile) && IsInitialBlockDownload()) - nMinutes = 5; bitdb.dbenv.txn_checkpoint(nMinutes ? GetArg("-dblogsize", 100)*1024 : 0, nMinutes, 0); } @@ -453,11 +442,9 @@ void CDBEnv::Flush(bool fShutdown) CloseDb(strFile); printf("%s checkpoint\n", strFile.c_str()); dbenv.txn_checkpoint(0, 0, 0); - if (!IsChainFile(strFile) || fDetachDB) { - printf("%s detach\n", strFile.c_str()); - if (!fMockDb) - dbenv.lsn_reset(strFile.c_str(), 0); - } + printf("%s detach\n", strFile.c_str()); + if (!fMockDb) + dbenv.lsn_reset(strFile.c_str(), 0); printf("%s closed\n", strFile.c_str()); mapFileUseCount.erase(mi++); } @@ -31,10 +31,9 @@ bool BackupWallet(const CWallet& wallet, const std::string& strDest); class CDBEnv { private: - bool fDetachDB; bool fDbEnvInit; bool fMockDb; - boost::filesystem::path pathEnv; + std::string strPath; void EnvShutdown(); @@ -47,7 +46,7 @@ public: CDBEnv(); ~CDBEnv(); void MakeMock(); - bool IsMock() { return fMockDb; }; + bool IsMock() { return fMockDb; } /* * Verify that database file strFile is OK. If it is not, @@ -67,12 +66,10 @@ public: typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair; bool Salvage(std::string strFile, bool fAggressive, std::vector<KeyValPair>& vResult); - bool Open(boost::filesystem::path pathEnv_); + bool Open(const boost::filesystem::path &path); void Close(); void Flush(bool fShutdown); void CheckpointLSN(std::string strFile); - void SetDetach(bool fDetachDB_) { fDetachDB = fDetachDB_; } - bool GetDetach() { return fDetachDB; } void CloseDb(const std::string& strFile); bool RemoveDb(const std::string& strFile); diff --git a/src/init.cpp b/src/init.cpp index 4d5720306d..7f311fa881 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2,6 +2,7 @@ // Copyright (c) 2009-2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #include "txdb.h" #include "walletdb.h" #include "bitcoinrpc.h" @@ -9,6 +10,7 @@ #include "init.h" #include "util.h" #include "ui_interface.h" + #include <boost/filesystem.hpp> #include <boost/filesystem/fstream.hpp> #include <boost/filesystem/convenience.hpp> @@ -26,6 +28,13 @@ using namespace boost; CWallet* pwalletMain; CClientUIInterface uiInterface; +// Used to pass flags to the Bind() function +enum BindFlags { + BF_NONE = 0, + BF_EXPLICIT = (1U << 0), + BF_REPORT_ERROR = (1U << 1) +}; + ////////////////////////////////////////////////////////////////////////////// // // Shutdown @@ -78,8 +87,10 @@ void Shutdown(void* parg) StopNode(); { LOCK(cs_main); - pcoinsTip->Flush(); - pblocktree->Flush(); + if (pblocktree) + pblocktree->Flush(); + if (pcoinsTip) + pcoinsTip->Flush(); delete pcoinsTip; delete pcoinsdbview; delete pblocktree; @@ -200,23 +211,22 @@ int main(int argc, char* argv[]) bool static InitError(const std::string &str) { - uiInterface.ThreadSafeMessageBox(str, _("Bitcoin"), CClientUIInterface::OK | CClientUIInterface::MODAL); + uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_ERROR); return false; } bool static InitWarning(const std::string &str) { - uiInterface.ThreadSafeMessageBox(str, _("Bitcoin"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION | CClientUIInterface::MODAL); + uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_WARNING); return true; } - -bool static Bind(const CService &addr, bool fError = true) { - if (IsLimited(addr)) +bool static Bind(const CService &addr, unsigned int flags) { + if (!(flags & BF_EXPLICIT) && IsLimited(addr)) return false; std::string strError; if (!BindListenPort(addr, strError)) { - if (fError) + if (flags & BF_REPORT_ERROR) return InitError(strError); return false; } @@ -234,7 +244,6 @@ std::string HelpMessage() " -gen=0 " + _("Don't generate coins") + "\n" + " -datadir=<dir> " + _("Specify data directory") + "\n" + " -dbcache=<n> " + _("Set database cache size in megabytes (default: 25)") + "\n" + - " -dblogsize=<n> " + _("Set database disk log size in megabytes (default: 100)") + "\n" + " -timeout=<n> " + _("Specify connection timeout in milliseconds (default: 5000)") + "\n" + " -proxy=<ip:port> " + _("Connect through socks proxy") + "\n" + " -socks=<n> " + _("Select the version of socks proxy to use (4-5, default: 5)") + "\n" + @@ -249,8 +258,9 @@ std::string HelpMessage() " -onlynet=<net> " + _("Only connect to nodes in network <net> (IPv4, IPv6 or Tor)") + "\n" + " -discover " + _("Discover own IP address (default: 1 when listening and no -externalip)") + "\n" + " -irc " + _("Find peers using internet relay chat (default: 0)") + "\n" + + " -checkpoints " + _("Lock in block chain with compiled-in checkpoints (default: 1)") + "\n" + " -listen " + _("Accept connections from outside (default: 1 if no -proxy or -connect)") + "\n" + - " -bind=<addr> " + _("Bind to given address. Use [host]:port notation for IPv6") + "\n" + + " -bind=<addr> " + _("Bind to given address and always listen on it. Use [host]:port notation for IPv6") + "\n" + " -dnsseed " + _("Find peers using DNS lookup (default: 1 unless -connect)") + "\n" + " -banscore=<n> " + _("Threshold for disconnecting misbehaving peers (default: 100)") + "\n" + " -bantime=<n> " + _("Number of seconds to keep misbehaving peers from reconnecting (default: 86400)") + "\n" + @@ -263,7 +273,6 @@ std::string HelpMessage() " -upnp " + _("Use UPnP to map the listening port (default: 0)") + "\n" + #endif #endif - " -detachdb " + _("Detach block and address databases. Increases shutdown time (default: 0)") + "\n" + " -paytxfee=<amt> " + _("Fee per KB to add to transactions you send") + "\n" + #ifdef QT_GUI " -server " + _("Accept command line and JSON-RPC commands") + "\n" + @@ -293,6 +302,7 @@ std::string HelpMessage() " -checkblocks=<n> " + _("How many blocks to check at startup (default: 2500, 0 = all)") + "\n" + " -checklevel=<n> " + _("How thorough the block verification is (0-6, default: 1)") + "\n" + " -loadblock=<file> " + _("Imports blocks from external blk000?.dat file") + "\n" + + " -reindex " + _("Rebuild blockchain index from current blk000??.dat files") + "\n" + "\n" + _("Block creation options:") + "\n" + " -blockminsize=<n> " + _("Set minimum block size in bytes (default: 0)") + "\n" + @@ -308,6 +318,79 @@ std::string HelpMessage() return strUsage; } +struct CImportingNow +{ + CImportingNow() { + assert(fImporting == false); + fImporting = true; + } + + ~CImportingNow() { + assert(fImporting == true); + fImporting = false; + } +}; + +struct CImportData { + std::vector<boost::filesystem::path> vFiles; +}; + +void ThreadImport(void *data) { + CImportData *import = reinterpret_cast<CImportData*>(data); + + RenameThread("bitcoin-loadblk"); + + vnThreadsRunning[THREAD_IMPORT]++; + + // -reindex + if (fReindex) { + CImportingNow imp; + int nFile = 0; + while (!fRequestShutdown) { + CDiskBlockPos pos(nFile, 0); + FILE *file = OpenBlockFile(pos, true); + if (!file) + break; + printf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile); + LoadExternalBlockFile(file, &pos); + nFile++; + } + if (!fRequestShutdown) { + pblocktree->WriteReindexing(false); + fReindex = false; + printf("Reindexing finished\n"); + } + } + + // hardcoded $DATADIR/bootstrap.dat + filesystem::path pathBootstrap = GetDataDir() / "bootstrap.dat"; + if (filesystem::exists(pathBootstrap) && !fRequestShutdown) { + FILE *file = fopen(pathBootstrap.string().c_str(), "rb"); + if (file) { + CImportingNow imp; + filesystem::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old"; + printf("Importing bootstrap.dat...\n"); + LoadExternalBlockFile(file); + RenameOver(pathBootstrap, pathBootstrapOld); + } + } + + // -loadblock= + BOOST_FOREACH(boost::filesystem::path &path, import->vFiles) { + if (fRequestShutdown) + break; + FILE *file = fopen(path.string().c_str(), "rb"); + if (file) { + CImportingNow imp; + printf("Importing %s...\n", path.string().c_str()); + LoadExternalBlockFile(file); + } + } + + delete import; + + vnThreadsRunning[THREAD_IMPORT]--; +} /** Initialize bitcoin. * @pre Parameters should be parsed and config file should be read. @@ -399,6 +482,7 @@ bool AppInit2() // ********************************************************* Step 3: parameter-to-internal-flags fDebug = GetBoolArg("-debug"); + fBenchmark = GetBoolArg("-benchmark"); // -debug implies fDebug* if (fDebug) @@ -406,8 +490,6 @@ bool AppInit2() else fDebugNet = GetBoolArg("-debugnet"); - bitdb.SetDetach(GetBoolArg("-detachdb", false)); - #if !defined(WIN32) && !defined(QT_GUI) fDaemon = GetBoolArg("-daemon"); #else @@ -459,7 +541,7 @@ bool AppInit2() if (file) fclose(file); static boost::interprocess::file_lock lock(pathLockFile.string().c_str()); if (!lock.try_lock()) - return InitError(strprintf(_("Cannot obtain a lock on data directory %s. Bitcoin is probably already running."), strDataDir.c_str())); + return InitError(strprintf(_("Cannot obtain a lock on data directory %s. Bitcoin is probably already running."), strDataDir.c_str())); #if !defined(WIN32) && !defined(QT_GUI) if (fDaemon) @@ -527,7 +609,7 @@ bool AppInit2() " Original wallet.dat saved as wallet.{timestamp}.bak in %s; if" " your balance or transactions are incorrect you should" " restore from a backup."), strDataDir.c_str()); - uiInterface.ThreadSafeMessageBox(msg, _("Bitcoin"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION | CClientUIInterface::MODAL); + InitWarning(msg); } if (r == CDBEnv::RECOVER_FAIL) return InitError(_("wallet.dat corrupt, salvage failed")); @@ -602,32 +684,28 @@ bool AppInit2() #endif bool fBound = false; - if (!fNoListen) - { - std::string strError; + if (!fNoListen) { if (mapArgs.count("-bind")) { BOOST_FOREACH(std::string strBind, mapMultiArgs["-bind"]) { CService addrBind; if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false)) return InitError(strprintf(_("Cannot resolve -bind address: '%s'"), strBind.c_str())); - fBound |= Bind(addrBind); + fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR)); } - } else { + } + else { struct in_addr inaddr_any; inaddr_any.s_addr = INADDR_ANY; #ifdef USE_IPV6 - if (!IsLimited(NET_IPV6)) - fBound |= Bind(CService(in6addr_any, GetListenPort()), false); + fBound |= Bind(CService(in6addr_any, GetListenPort()), BF_NONE); #endif - if (!IsLimited(NET_IPV4)) - fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound); + fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE); } if (!fBound) return InitError(_("Failed to listen on any port. Use -listen=0 if you want this.")); } - if (mapArgs.count("-externalip")) - { + if (mapArgs.count("-externalip")) { BOOST_FOREACH(string strAddr, mapMultiArgs["-externalip"]) { CService addrLocal(strAddr, GetListenPort(), fNameLookup); if (!addrLocal.IsValid()) @@ -639,7 +717,9 @@ bool AppInit2() BOOST_FOREACH(string strDest, mapMultiArgs["-seednode"]) AddOneShot(strDest); - // ********************************************************* Step 7: load blockchain + // ********************************************************* Step 7: load block chain + + fReindex = GetBoolArg("-reindex"); if (!bitdb.Open(GetDataDir())) { @@ -649,13 +729,28 @@ bool AppInit2() return InitError(msg); } + // cache size calculations + size_t nTotalCache = GetArg("-dbcache", 25) << 20; + if (nTotalCache < (1 << 22)) + nTotalCache = (1 << 22); // total cache cannot be less than 4 MiB + size_t nBlockTreeDBCache = nTotalCache / 8; + if (nBlockTreeDBCache > (1 << 21)) + nBlockTreeDBCache = (1 << 21); // block tree db cache shouldn't be larger than 2 MiB + nTotalCache -= nBlockTreeDBCache; + size_t nCoinDBCache = nTotalCache / 2; // use half of the remaining cache for coindb cache + nTotalCache -= nCoinDBCache; + nCoinCacheSize = nTotalCache / 300; // coins in memory require around 300 bytes + uiInterface.InitMessage(_("Loading block index...")); printf("Loading block index...\n"); nStart = GetTimeMillis(); - pblocktree = new CBlockTreeDB(); - pcoinsdbview = new CCoinsViewDB(); + pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex); + pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex); pcoinsTip = new CCoinsViewCache(*pcoinsdbview); + if (fReindex) + pblocktree->WriteReindexing(true); + if (!LoadBlockIndex()) return InitError(_("Error loading blkindex.dat")); @@ -714,7 +809,7 @@ bool AppInit2() { string msg(_("Warning: error reading wallet.dat! All keys read correctly, but transaction data" " or address book entries might be missing or incorrect.")); - uiInterface.ThreadSafeMessageBox(msg, _("Bitcoin"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION | CClientUIInterface::MODAL); + InitWarning(msg); } else if (nLoadWalletRet == DB_TOO_NEW) strErrors << _("Error loading wallet.dat: Wallet requires newer version of Bitcoin") << "\n"; @@ -772,7 +867,7 @@ bool AppInit2() if (walletdb.ReadBestBlock(locator)) pindexRescan = locator.GetBlockIndex(); } - if (pindexBest != pindexRescan) + if (pindexBest && pindexBest != pindexRescan) { uiInterface.InitMessage(_("Rescanning...")); printf("Rescanning last %i blocks (from block %i)...\n", pindexBest->nHeight - pindexRescan->nHeight, pindexRescan->nHeight); @@ -788,13 +883,13 @@ bool AppInit2() if (!ConnectBestBlock()) strErrors << "Failed to connect best block"; - std::vector<boost::filesystem::path> *vPath = new std::vector<boost::filesystem::path>(); + CImportData *pimport = new CImportData(); if (mapArgs.count("-loadblock")) { BOOST_FOREACH(string strFile, mapMultiArgs["-loadblock"]) - vPath->push_back(strFile); + pimport->vFiles.push_back(strFile); } - NewThread(ThreadImport, vPath); + NewThread(ThreadImport, pimport); // ********************************************************* Step 10: load peers @@ -820,7 +915,7 @@ bool AppInit2() //// debug print printf("mapBlockIndex.size() = %"PRIszu"\n", mapBlockIndex.size()); - printf("nBestHeight = %d\n", nBestHeight); + printf("nBestHeight = %d\n", nBestHeight); printf("setKeyPool.size() = %"PRIszu"\n", pwalletMain->setKeyPool.size()); printf("mapWallet.size() = %"PRIszu"\n", pwalletMain->mapWallet.size()); printf("mapAddressBook.size() = %"PRIszu"\n", pwalletMain->mapAddressBook.size()); diff --git a/src/irc.cpp b/src/irc.cpp index 17d5ff1a5a..e8471a6630 100644 --- a/src/irc.cpp +++ b/src/irc.cpp @@ -192,6 +192,8 @@ void ThreadIRCSeed(void* parg) // Make this thread recognisable as the IRC seeding thread RenameThread("bitcoin-ircseed"); + printf("ThreadIRCSeed started\n"); + try { ThreadIRCSeed2(parg); @@ -218,7 +220,8 @@ void ThreadIRCSeed2(void* parg) if (!GetBoolArg("-irc", false)) return; - printf("ThreadIRCSeed started\n"); + printf("ThreadIRCSeed trying to connect...\n"); + int nErrorWait = 10; int nRetryWait = 10; int nNameRetry = 0; diff --git a/src/leveldb.cpp b/src/leveldb.cpp index e8a0fbe874..9e2f32a171 100644 --- a/src/leveldb.cpp +++ b/src/leveldb.cpp @@ -12,27 +12,31 @@ #include <boost/filesystem.hpp> -static leveldb::Options GetOptions() { +static leveldb::Options GetOptions(size_t nCacheSize) { leveldb::Options options; - int nCacheSizeMB = GetArg("-dbcache", 25); - options.block_cache = leveldb::NewLRUCache(nCacheSizeMB * 1048576); + options.block_cache = leveldb::NewLRUCache(nCacheSize / 2); + options.write_buffer_size = nCacheSize / 4; // up to two write buffers may be held in memory simultaneously options.filter_policy = leveldb::NewBloomFilterPolicy(10); options.compression = leveldb::kNoCompression; return options; } -CLevelDB::CLevelDB(const boost::filesystem::path &path, bool fMemory) { +CLevelDB::CLevelDB(const boost::filesystem::path &path, size_t nCacheSize, bool fMemory, bool fWipe) { penv = NULL; readoptions.verify_checksums = true; iteroptions.verify_checksums = true; iteroptions.fill_cache = false; syncoptions.sync = true; - options = GetOptions(); + options = GetOptions(nCacheSize); options.create_if_missing = true; if (fMemory) { penv = leveldb::NewMemEnv(leveldb::Env::Default()); options.env = penv; } else { + if (fWipe) { + printf("Wiping LevelDB in %s\n", path.string().c_str()); + leveldb::DestroyDB(path.string(), options); + } boost::filesystem::create_directory(path); printf("Opening LevelDB in %s\n", path.string().c_str()); } diff --git a/src/leveldb.h b/src/leveldb.h index ee9079c3c3..0b83432072 100644 --- a/src/leveldb.h +++ b/src/leveldb.h @@ -69,7 +69,7 @@ private: leveldb::DB *pdb; public: - CLevelDB(const boost::filesystem::path &path, bool fMemory = false); + CLevelDB(const boost::filesystem::path &path, size_t nCacheSize, bool fMemory = false, bool fWipe = false); ~CLevelDB(); template<typename K, typename V> bool Read(const K& key, V& value) { diff --git a/src/leveldb/Makefile b/src/leveldb/Makefile index 7f658cfdf9..14e494f3ce 100755 --- a/src/leveldb/Makefile +++ b/src/leveldb/Makefile @@ -19,10 +19,10 @@ $(shell ./build_detect_platform build_config.mk) # this file is generated by the previous line to set build flags and sources include build_config.mk -CFLAGS += -I. -I./include $(PLATFORM_CCFLAGS) $(OPT) -CXXFLAGS += -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT) +xCFLAGS = -I. -I./include $(PLATFORM_CCFLAGS) $(OPT) $(CFLAGS) +xCXXFLAGS = -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT) $(CXXFLAGS) -LDFLAGS += $(PLATFORM_LDFLAGS) +xLDFLAGS = $(PLATFORM_LDFLAGS) $(LDFLAGS) LIBOBJECTS = $(SOURCES:.cc=.o) MEMENVOBJECTS = $(MEMENV_SOURCES:.cc=.o) @@ -82,7 +82,7 @@ $(SHARED2): $(SHARED3) endif $(SHARED3): - $(CXX) $(LDFLAGS) $(PLATFORM_SHARED_LDFLAGS)$(SHARED2) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SOURCES) $(PLATFORM_EXTRALIBS) -o $(SHARED3) + $(CXX) $(xLDFLAGS) $(PLATFORM_SHARED_LDFLAGS)$(SHARED2) $(xCXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SOURCES) $(PLATFORM_EXTRALIBS) -o $(SHARED3) endif # PLATFORM_SHARED_EXT @@ -100,74 +100,74 @@ $(LIBRARY): $(LIBOBJECTS) $(AR) -rs $@ $(LIBOBJECTS) db_bench: db/db_bench.o $(LIBOBJECTS) $(TESTUTIL) - $(CXX) db/db_bench.o $(LIBOBJECTS) $(TESTUTIL) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) db/db_bench.o $(LIBOBJECTS) $(TESTUTIL) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) db_bench_sqlite3: doc/bench/db_bench_sqlite3.o $(LIBOBJECTS) $(TESTUTIL) - $(CXX) doc/bench/db_bench_sqlite3.o $(LIBOBJECTS) $(TESTUTIL) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) -lsqlite3 + $(CXX) doc/bench/db_bench_sqlite3.o $(LIBOBJECTS) $(TESTUTIL) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) -lsqlite3 db_bench_tree_db: doc/bench/db_bench_tree_db.o $(LIBOBJECTS) $(TESTUTIL) - $(CXX) doc/bench/db_bench_tree_db.o $(LIBOBJECTS) $(TESTUTIL) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) -lkyotocabinet + $(CXX) doc/bench/db_bench_tree_db.o $(LIBOBJECTS) $(TESTUTIL) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) -lkyotocabinet arena_test: util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) bloom_test: util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) c_test: db/c_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) db/c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) cache_test: util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) coding_test: util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) corruption_test: db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) crc32c_test: util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) db_test: db/db_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/db_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) db/db_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) dbformat_test: db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) env_test: util/env_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) util/env_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) util/env_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) filename_test: db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) filter_block_test: table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) log_test: db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) table_test: table/table_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) table/table_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) table/table_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) skiplist_test: db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) version_edit_test: db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) version_set_test: db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) write_batch_test: db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) $(MEMENVLIBRARY) : $(MEMENVOBJECTS) rm -f $@ $(AR) -rs $@ $(MEMENVOBJECTS) memenv_test : helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS) - $(CXX) helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + $(CXX) helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS) -o $@ $(xLDFLAGS) $(PLATFORM_EXTRALIBS) ifeq ($(PLATFORM), IOS) # For iOS, create universal object files to be used on both the simulator and @@ -179,22 +179,22 @@ IOSVERSION=$(shell defaults read $(PLATFORMSROOT)/iPhoneOS.platform/version CFBu .cc.o: mkdir -p ios-x86/$(dir $@) - $(SIMULATORROOT)/usr/bin/$(CXX) $(CXXFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -c $< -o ios-x86/$@ + $(SIMULATORROOT)/usr/bin/$(CXX) $(xCXXFLAGS) -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/$@ + $(DEVICEROOT)/usr/bin/$(CXX) $(xCXXFLAGS) -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 $@) - $(SIMULATORROOT)/usr/bin/$(CC) $(CFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -c $< -o ios-x86/$@ + $(SIMULATORROOT)/usr/bin/$(CC) $(xCFLAGS) -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/$@ + $(DEVICEROOT)/usr/bin/$(CC) $(xCFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk -arch armv6 -arch armv7 -c $< -o ios-arm/$@ lipo ios-x86/$@ ios-arm/$@ -create -output $@ else .cc.o: - $(CXX) $(CXXFLAGS) -c $< -o $@ + $(CXX) $(xCXXFLAGS) -c $< -o $@ .c.o: - $(CC) $(CFLAGS) -c $< -o $@ + $(CC) $(xCFLAGS) -c $< -o $@ endif diff --git a/src/leveldb/port/port_posix.h b/src/leveldb/port/port_posix.h index 654a4b9d3d..6ca352e2ca 100644 --- a/src/leveldb/port/port_posix.h +++ b/src/leveldb/port/port_posix.h @@ -21,13 +21,20 @@ #else #define PLATFORM_IS_LITTLE_ENDIAN false #endif -#elif defined(OS_FREEBSD) || defined(OS_OPENBSD) || defined(OS_NETBSD) ||\ +#elif defined(OS_FREEBSD) + #include <sys/types.h> + #include <sys/endian.h> + #define PLATFORM_IS_LITTLE_ENDIAN (_BYTE_ORDER == _LITTLE_ENDIAN) +#elif defined(OS_OPENBSD) || defined(OS_NETBSD) ||\ defined(OS_DRAGONFLYBSD) || defined(OS_ANDROID) #include <sys/types.h> #include <sys/endian.h> +#elif defined(OS_HPUX) + #define PLATFORM_IS_LITTLE_ENDIAN false #else #include <endian.h> #endif + #include <pthread.h> #ifdef SNAPPY #include <snappy.h> @@ -42,7 +49,7 @@ #if defined(OS_MACOSX) || defined(OS_SOLARIS) || defined(OS_FREEBSD) ||\ defined(OS_NETBSD) || defined(OS_OPENBSD) || defined(OS_DRAGONFLYBSD) ||\ - defined(OS_ANDROID) + defined(OS_ANDROID) || defined(OS_HPUX) // Use fread/fwrite/fflush on platforms without _unlocked variants #define fread_unlocked fread #define fwrite_unlocked fwrite diff --git a/src/main.cpp b/src/main.cpp index 064ac7e10d..61bd6b7b87 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -41,6 +41,9 @@ CBlockIndex* pindexBest = NULL; set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexValid; // may contain all CBlockIndex*'s that have validness >=BLOCK_VALID_TRANSACTIONS, and must contain those who aren't failed int64 nTimeBestReceived = 0; bool fImporting = false; +bool fReindex = false; +bool fBenchmark = false; +unsigned int nCoinCacheSize = 5000; CMedianFilter<int> cPeerBlockCounts(5, 0); // Amount of blocks that other nodes claim to have @@ -691,13 +694,21 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, if (fCheckInputs) { - CCoinsViewCache &view = *pcoinsTip; + CCoinsView dummy; + CCoinsViewCache view(dummy); + + { + LOCK(cs); + CCoinsViewMemPool viewMemPool(*pcoinsTip, *this); + view.SetBackend(viewMemPool); // do we already have it? if (view.HaveCoins(hash)) return false; // do all inputs exist? + // Note that this does not check for the presence of actual outputs (see the next check for that), + // only helps filling in pfMissingInputs (to determine missing vs spent). BOOST_FOREACH(const CTxIn txin, tx.vin) { if (!view.HaveCoins(txin.prevout.hash)) { if (pfMissingInputs) @@ -706,9 +717,17 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, } } + // are the actual inputs available? if (!tx.HaveInputs(view)) return error("CTxMemPool::accept() : inputs already spent"); + // Bring the best block into scope + view.GetBestBlock(); + + // we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool + view.SetBackend(dummy); + } + // Check for non-standard pay-to-script-hash in inputs if (!tx.AreInputsStandard(view) && !fTestNet) return error("CTxMemPool::accept() : nonstandard transaction input"); @@ -738,7 +757,6 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, int64 nNow = GetTime(); { - LOCK(cs); // Use an exponentially decaying ~10-minute window: dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime)); nLastTime = nNow; @@ -754,7 +772,7 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. - if (!tx.CheckInputs(view, CS_ALWAYS, true, false)) + if (!tx.CheckInputs(view, CS_ALWAYS, SCRIPT_VERIFY_P2SH)) { return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str()); } @@ -801,7 +819,7 @@ bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx) } -bool CTxMemPool::remove(CTransaction &tx) +bool CTxMemPool::remove(const CTransaction &tx, bool fRecursive) { // Remove transaction from memory pool { @@ -809,6 +827,13 @@ bool CTxMemPool::remove(CTransaction &tx) uint256 hash = tx.GetHash(); if (mapTx.count(hash)) { + if (fRecursive) { + for (unsigned int i = 0; i < tx.vout.size(); i++) { + std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(COutPoint(hash, i)); + if (it != mapNextTx.end()) + remove(*it->second.ptx, true); + } + } BOOST_FOREACH(const CTxIn& txin, tx.vin) mapNextTx.erase(txin.prevout); mapTx.erase(hash); @@ -818,6 +843,21 @@ bool CTxMemPool::remove(CTransaction &tx) return true; } +bool CTxMemPool::removeConflicts(const CTransaction &tx) +{ + // Remove transactions which depend on inputs of tx, recursively + LOCK(cs); + BOOST_FOREACH(const CTxIn &txin, tx.vin) { + std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(txin.prevout); + if (it != mapNextTx.end()) { + const CTransaction &txConflict = *it->second.ptx; + if (txConflict != tx) + remove(txConflict, true); + } + } + return true; +} + void CTxMemPool::clear() { LOCK(cs); @@ -981,21 +1021,16 @@ CBlockIndex* FindBlockByHeight(int nHeight) return pblockindex; } -bool CBlock::ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions) +bool CBlock::ReadFromDisk(const CBlockIndex* pindex) { - if (!fReadTransactions) - { - *this = pindex->GetBlockHeader(); - return true; - } - if (!ReadFromDisk(pindex->GetBlockPos(), fReadTransactions)) + if (!ReadFromDisk(pindex->GetBlockPos())) return false; if (GetHash() != pindex->GetBlockHash()) return error("CBlock::ReadFromDisk() : GetHash() doesn't match index"); return true; } -uint256 static GetOrphanRoot(const CBlock* pblock) +uint256 static GetOrphanRoot(const CBlockHeader* pblock) { // Work back to the first block in the orphan chain while (mapOrphanBlocks.count(pblock->hashPrevBlock)) @@ -1042,7 +1077,7 @@ unsigned int ComputeMinWork(unsigned int nBase, int64 nTime) return bnResult.GetCompact(); } -unsigned int static GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlock *pblock) +unsigned int static GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock) { unsigned int nProofOfWorkLimit = bnProofOfWorkLimit.GetCompact(); @@ -1129,7 +1164,7 @@ int GetNumBlocksOfPeers() bool IsInitialBlockDownload() { - if (pindexBest == NULL || nBestHeight < Checkpoints::GetTotalBlocksEstimate()) + if (pindexBest == NULL || nBestHeight < Checkpoints::GetTotalBlocksEstimate() || fReindex || fImporting) return true; static int64 nLastUpdate; static CBlockIndex* pindexLastBest; @@ -1151,11 +1186,11 @@ void static InvalidChainFound(CBlockIndex* pindexNew) uiInterface.NotifyBlocksChanged(); } printf("InvalidChainFound: invalid block=%s height=%d work=%s date=%s\n", - pindexNew->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->nHeight, + BlockHashStr(pindexNew->GetBlockHash()).c_str(), pindexNew->nHeight, pindexNew->bnChainWork.ToString().c_str(), DateTimeStrFormat("%x %H:%M:%S", pindexNew->GetBlockTime()).c_str()); printf("InvalidChainFound: current best=%s height=%d work=%s date=%s\n", - hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, bnBestChainWork.ToString().c_str(), + BlockHashStr(hashBestChain).c_str(), nBestHeight, bnBestChainWork.ToString().c_str(), DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str()); if (pindexBest && bnBestInvalidWork > bnBestChainWork + pindexBest->GetBlockWork() * 6) printf("InvalidChainFound: Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.\n"); @@ -1181,7 +1216,7 @@ bool ConnectBestBlock() { pindexNewBest = *it; } - if (pindexNewBest == pindexBest) + if (pindexNewBest == pindexBest || (pindexBest && pindexNewBest->bnChainWork == pindexBest->bnChainWork)) return true; // nothing to do // check ancestry @@ -1206,9 +1241,12 @@ bool ConnectBestBlock() { if (pindexTest->pprev == NULL || pindexTest->pnext != NULL) { reverse(vAttach.begin(), vAttach.end()); - BOOST_FOREACH(CBlockIndex *pindexSwitch, vAttach) + BOOST_FOREACH(CBlockIndex *pindexSwitch, vAttach) { + if (fRequestShutdown) + break; if (!SetBestChain(pindexSwitch)) return false; + } return true; } pindexTest = pindexTest->pprev; @@ -1216,7 +1254,7 @@ bool ConnectBestBlock() { } while(true); } -void CBlock::UpdateTime(const CBlockIndex* pindexPrev) +void CBlockHeader::UpdateTime(const CBlockIndex* pindexPrev) { nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); @@ -1291,7 +1329,7 @@ bool CTransaction::UpdateCoins(CCoinsViewCache &inputs, CTxUndo &txundo, int nHe bool CTransaction::HaveInputs(CCoinsViewCache &inputs) const { - if (!IsCoinBase()) { + if (!IsCoinBase()) { // first check whether information about the prevout hash is available for (unsigned int i = 0; i < vin.size(); i++) { const COutPoint &prevout = vin[i].prevout; @@ -1310,7 +1348,7 @@ bool CTransaction::HaveInputs(CCoinsViewCache &inputs) const return true; } -bool CTransaction::CheckInputs(CCoinsViewCache &inputs, enum CheckSig_mode csmode, bool fStrictPayToScriptHash, bool fStrictEncodings) const +bool CTransaction::CheckInputs(CCoinsViewCache &inputs, enum CheckSig_mode csmode, unsigned int flags) const { if (!IsCoinBase()) { @@ -1319,7 +1357,9 @@ bool CTransaction::CheckInputs(CCoinsViewCache &inputs, enum CheckSig_mode csmod if (!HaveInputs(inputs)) return error("CheckInputs() : %s inputs unavailable", GetHash().ToString().substr(0,10).c_str()); - CBlockIndex *pindexBlock = inputs.GetBestBlock(); + // While checking, GetBestBlock() refers to the parent block. + // This is also true for mempool checks. + int nSpendHeight = inputs.GetBestBlock()->nHeight + 1; int64 nValueIn = 0; int64 nFees = 0; for (unsigned int i = 0; i < vin.size(); i++) @@ -1329,8 +1369,8 @@ bool CTransaction::CheckInputs(CCoinsViewCache &inputs, enum CheckSig_mode csmod // If prev is coinbase, check that it's matured if (coins.IsCoinBase()) { - if (pindexBlock->nHeight - coins.nHeight < COINBASE_MATURITY) - return error("CheckInputs() : tried to spend coinbase at depth %d", pindexBlock->nHeight - coins.nHeight); + if (nSpendHeight - coins.nHeight < COINBASE_MATURITY) + return error("CheckInputs() : tried to spend coinbase at depth %d", nSpendHeight - coins.nHeight); } // Check for negative or overflow input values @@ -1356,23 +1396,17 @@ bool CTransaction::CheckInputs(CCoinsViewCache &inputs, enum CheckSig_mode csmod // Helps prevent CPU exhaustion attacks. // Skip ECDSA signature verification when connecting blocks - // before the last blockchain checkpoint. This is safe because block merkle hashes are + // before the last block chain checkpoint. This is safe because block merkle hashes are // still computed and checked, and any change will be caught at the next checkpoint. - if (csmode == CS_ALWAYS || + if (csmode == CS_ALWAYS || (csmode == CS_AFTER_CHECKPOINT && inputs.GetBestBlock()->nHeight >= Checkpoints::GetTotalBlocksEstimate())) { for (unsigned int i = 0; i < vin.size(); i++) { const COutPoint &prevout = vin[i].prevout; const CCoins &coins = inputs.GetCoins(prevout.hash); // Verify signature - if (!VerifySignature(coins, *this, i, fStrictPayToScriptHash, fStrictEncodings, 0)) { - // only during transition phase for P2SH: do not invoke anti-DoS code for - // potentially old clients relaying bad P2SH transactions - if (fStrictPayToScriptHash && VerifySignature(coins, *this, i, false, fStrictEncodings, 0)) - return error("CheckInputs() : %s P2SH VerifySignature failed", GetHash().ToString().substr(0,10).c_str()); - + if (!VerifySignature(coins, *this, i, flags, 0)) return DoS(100,error("CheckInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str())); - } } } } @@ -1402,7 +1436,7 @@ bool CTransaction::ClientCheckInputs() const return false; // Verify signature - if (!VerifySignature(CCoins(txPrev, -1), *this, i, true, false, 0)) + if (!VerifySignature(CCoins(txPrev, -1), *this, i, SCRIPT_VERIFY_P2SH, 0)) return error("ConnectInputs() : VerifySignature failed"); ///// this is redundant with the mempool.mapNextTx stuff, @@ -1505,17 +1539,19 @@ void static FlushBlockFile() { LOCK(cs_LastBlockFile); - CDiskBlockPos posOld; - posOld.nFile = nLastBlockFile; - posOld.nPos = 0; + CDiskBlockPos posOld(nLastBlockFile, 0); FILE *fileOld = OpenBlockFile(posOld); - FileCommit(fileOld); - fclose(fileOld); + if (fileOld) { + FileCommit(fileOld); + fclose(fileOld); + } fileOld = OpenUndoFile(posOld); - FileCommit(fileOld); - fclose(fileOld); + if (fileOld) { + FileCommit(fileOld); + fclose(fileOld); + } } bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize); @@ -1541,7 +1577,8 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust // Now that the whole chain is irreversibly beyond that time it is applied to all blocks except the // two in the chain that violate it. This prevents exploiting the issue against nodes in their // initial block download. - bool fEnforceBIP30 = !((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) || + bool fEnforceBIP30 = (!pindex->phashBlock) || // Enforce on CreateNewBlock invocations which don't have a hash. + !((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) || (pindex->nHeight==91880 && pindex->GetBlockHash() == uint256("0x00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721"))); if (fEnforceBIP30) { for (unsigned int i=0; i<vtx.size(); i++) { @@ -1557,12 +1594,16 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust CBlockUndo blockundo; + int64 nStart = GetTimeMicros(); int64 nFees = 0; + int nInputs = 0; unsigned int nSigOps = 0; for (unsigned int i=0; i<vtx.size(); i++) { + const CTransaction &tx = vtx[i]; + nInputs += tx.vin.size(); nSigOps += tx.GetLegacySigOpCount(); if (nSigOps > MAX_BLOCK_SIGOPS) return DoS(100, error("ConnectBlock() : too many sigops")); @@ -1584,7 +1625,7 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust nFees += tx.GetValueIn(view)-tx.GetValueOut(); - if (!tx.CheckInputs(view, CS_AFTER_CHECKPOINT, fStrictPayToScriptHash, false)) + if (!tx.CheckInputs(view, CS_AFTER_CHECKPOINT, fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE)) return false; } @@ -1593,7 +1634,14 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust return error("ConnectBlock() : UpdateInputs failed"); if (!tx.IsCoinBase()) blockundo.vtxundo.push_back(txundo); + } + int64 nTime = GetTimeMicros() - nStart; + if (fBenchmark) + printf("- Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin)\n", (unsigned)vtx.size(), 0.001 * nTime, 0.001 * nTime / vtx.size(), nInputs <= 1 ? 0 : 0.001 * nTime / (nInputs-1)); + + if (vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees)) + return error("ConnectBlock() : coinbase pays too much (actual=%"PRI64d" vs limit=%"PRI64d")", vtx[0].GetValueOut(), GetBlockValue(pindex->nHeight, nFees)); if (fJustCheck) return true; @@ -1620,7 +1668,7 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust return error("ConnectBlock() : WriteBlockIndex failed"); } - // add this block to the view's blockchain + // add this block to the view's block chain if (!view.SetBestBlock(pindex)) return false; @@ -1633,7 +1681,9 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust bool SetBestChain(CBlockIndex* pindexNew) { - CCoinsViewCache &view = *pcoinsTip; + // All modifications to the coin state will be done in this cache. + // Only when all have succeeded, we push it to pcoinsTip. + CCoinsViewCache view(*pcoinsTip, true); // special case for attaching the genesis block // note that no ConnectBlock is called, so its coinbase output is non-spendable @@ -1653,7 +1703,7 @@ bool SetBestChain(CBlockIndex* pindexNew) // Find the fork (typically, there is none) CBlockIndex* pfork = view.GetBestBlock(); CBlockIndex* plonger = pindexNew; - while (pfork != plonger) + while (pfork && pfork != plonger) { while (plonger->nHeight > pfork->nHeight) if (!(plonger = plonger->pprev)) @@ -1676,8 +1726,8 @@ bool SetBestChain(CBlockIndex* pindexNew) reverse(vConnect.begin(), vConnect.end()); if (vDisconnect.size() > 0) { - printf("REORGANIZE: Disconnect %"PRIszu" blocks; %s..%s\n", vDisconnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexBest->GetBlockHash().ToString().substr(0,20).c_str()); - printf("REORGANIZE: Connect %"PRIszu" blocks; %s..%s\n", vConnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->GetBlockHash().ToString().substr(0,20).c_str()); + printf("REORGANIZE: Disconnect %"PRIszu" blocks; %s..%s\n", vDisconnect.size(), BlockHashStr(pfork->GetBlockHash()).c_str(), BlockHashStr(pindexBest->GetBlockHash()).c_str()); + printf("REORGANIZE: Connect %"PRIszu" blocks; %s..%s\n", vConnect.size(), BlockHashStr(pfork->GetBlockHash()).c_str(), BlockHashStr(pindexNew->GetBlockHash()).c_str()); } // Disconnect shorter branch @@ -1686,15 +1736,17 @@ bool SetBestChain(CBlockIndex* pindexNew) CBlock block; if (!block.ReadFromDisk(pindex)) return error("SetBestBlock() : ReadFromDisk for disconnect failed"); - CCoinsViewCache viewTemp(view, true); - if (!block.DisconnectBlock(pindex, viewTemp)) - return error("SetBestBlock() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str()); - if (!viewTemp.Flush()) - return error("SetBestBlock() : Cache flush failed after disconnect"); - - // Queue memory transactions to resurrect + int64 nStart = GetTimeMicros(); + if (!block.DisconnectBlock(pindex, view)) + return error("SetBestBlock() : DisconnectBlock %s failed", BlockHashStr(pindex->GetBlockHash()).c_str()); + if (fBenchmark) + printf("- Disconnect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); + + // Queue memory transactions to resurrect. + // We only do this for blocks after the last checkpoint (reorganisation before that + // point should only happen with -reindex/-loadblock, or a misbehaving peer. BOOST_FOREACH(const CTransaction& tx, block.vtx) - if (!tx.IsCoinBase()) + if (!tx.IsCoinBase() && pindex->nHeight > Checkpoints::GetTotalBlocksEstimate()) vResurrect.push_back(tx); } @@ -1704,26 +1756,35 @@ bool SetBestChain(CBlockIndex* pindexNew) CBlock block; if (!block.ReadFromDisk(pindex)) return error("SetBestBlock() : ReadFromDisk for connect failed"); - CCoinsViewCache viewTemp(view, true); - if (!block.ConnectBlock(pindex, viewTemp)) { + int64 nStart = GetTimeMicros(); + if (!block.ConnectBlock(pindex, view)) { InvalidChainFound(pindexNew); InvalidBlockFound(pindex); - return error("SetBestBlock() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str()); + return error("SetBestBlock() : ConnectBlock %s failed", BlockHashStr(pindex->GetBlockHash()).c_str()); } - if (!viewTemp.Flush()) - return error("SetBestBlock() : Cache flush failed after connect"); + if (fBenchmark) + printf("- Connect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); // Queue memory transactions to delete BOOST_FOREACH(const CTransaction& tx, block.vtx) vDelete.push_back(tx); } + // Flush changes to global coin state + int64 nStart = GetTimeMicros(); + int nModified = view.GetCacheSize(); + if (!view.Flush()) + return error("SetBestBlock() : unable to modify coin state"); + int64 nTime = GetTimeMicros() - nStart; + if (fBenchmark) + printf("- Flush %i transactions: %.2fms (%.4fms/tx)\n", nModified, 0.001 * nTime, 0.001 * nTime / nModified); + // Make sure it's successfully written to disk before changing memory structure bool fIsInitialDownload = IsInitialBlockDownload(); - if (!fIsInitialDownload || view.GetCacheSize()>5000) { + if (!fIsInitialDownload || pcoinsTip->GetCacheSize() > nCoinCacheSize) { FlushBlockFile(); pblocktree->Sync(); - if (!view.Flush()) + if (!pcoinsTip->Flush()) return false; } @@ -1742,11 +1803,13 @@ bool SetBestChain(CBlockIndex* pindexNew) // Resurrect memory transactions that were in the disconnected branch BOOST_FOREACH(CTransaction& tx, vResurrect) - tx.AcceptToMemoryPool(false); + tx.AcceptToMemoryPool(); // Delete redundant memory transactions that are in the connected branch - BOOST_FOREACH(CTransaction& tx, vDelete) + BOOST_FOREACH(CTransaction& tx, vDelete) { mempool.remove(tx); + mempool.removeConflicts(tx); + } // Update best block in wallet (so we can detect restored wallets) if (!fIsInitialDownload) @@ -1764,7 +1827,7 @@ bool SetBestChain(CBlockIndex* pindexNew) nTimeBestReceived = GetTime(); nTransactionsUpdated++; printf("SetBestChain: new best=%s height=%d work=%s tx=%lu date=%s\n", - hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, bnBestChainWork.ToString().c_str(), (unsigned long)pindexNew->nChainTx, + BlockHashStr(hashBestChain).c_str(), nBestHeight, bnBestChainWork.ToString().c_str(), (unsigned long)pindexNew->nChainTx, DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str()); // Check the version of the last 100 blocks to see if we need to upgrade: @@ -1802,7 +1865,7 @@ bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos) // Check for duplicate uint256 hash = GetHash(); if (mapBlockIndex.count(hash)) - return error("AddToBlockIndex() : %s already exists", hash.ToString().substr(0,20).c_str()); + return error("AddToBlockIndex() : %s already exists", BlockHashStr(hash).c_str()); // Construct new block index object CBlockIndex* pindexNew = new CBlockIndex(*this); @@ -1846,35 +1909,50 @@ bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos) } -bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64 nTime) +bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64 nTime, bool fKnown = false) { bool fUpdatedLast = false; LOCK(cs_LastBlockFile); - while (infoLastBlockFile.nSize + nAddSize >= MAX_BLOCKFILE_SIZE) { - printf("Leaving block file %i: %s\n", nLastBlockFile, infoLastBlockFile.ToString().c_str()); - FlushBlockFile(); - nLastBlockFile++; - infoLastBlockFile.SetNull(); - pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile); // check whether data for the new file somehow already exist; can fail just fine - fUpdatedLast = true; + if (fKnown) { + if (nLastBlockFile != pos.nFile) { + nLastBlockFile = pos.nFile; + infoLastBlockFile.SetNull(); + pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile); + fUpdatedLast = true; + } + } else { + while (infoLastBlockFile.nSize + nAddSize >= MAX_BLOCKFILE_SIZE) { + printf("Leaving block file %i: %s\n", nLastBlockFile, infoLastBlockFile.ToString().c_str()); + FlushBlockFile(); + nLastBlockFile++; + infoLastBlockFile.SetNull(); + pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile); // check whether data for the new file somehow already exist; can fail just fine + fUpdatedLast = true; + } + pos.nFile = nLastBlockFile; + pos.nPos = infoLastBlockFile.nSize; } - pos.nFile = nLastBlockFile; - pos.nPos = infoLastBlockFile.nSize; infoLastBlockFile.nSize += nAddSize; infoLastBlockFile.AddBlock(nHeight, nTime); - unsigned int nOldChunks = (pos.nPos + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; - unsigned int nNewChunks = (infoLastBlockFile.nSize + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; - if (nNewChunks > nOldChunks) { - FILE *file = OpenBlockFile(pos); - if (file) { - printf("Pre-allocating up to position 0x%x in blk%05u.dat\n", nNewChunks * BLOCKFILE_CHUNK_SIZE, pos.nFile); - AllocateFileRange(file, pos.nPos, nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos); + if (!fKnown) { + unsigned int nOldChunks = (pos.nPos + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; + unsigned int nNewChunks = (infoLastBlockFile.nSize + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; + if (nNewChunks > nOldChunks) { + if (CheckDiskSpace(nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos)) { + FILE *file = OpenBlockFile(pos); + if (file) { + printf("Pre-allocating up to position 0x%x in blk%05u.dat\n", nNewChunks * BLOCKFILE_CHUNK_SIZE, pos.nFile); + AllocateFileRange(file, pos.nPos, nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos); + fclose(file); + } + } + else + return error("FindBlockPos() : out of disk space"); } - fclose(file); } if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile)) @@ -1910,12 +1988,16 @@ bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize) unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; unsigned int nNewChunks = (nNewSize + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; if (nNewChunks > nOldChunks) { - FILE *file = OpenUndoFile(pos); - if (file) { - printf("Pre-allocating up to position 0x%x in rev%05u.dat\n", nNewChunks * UNDOFILE_CHUNK_SIZE, pos.nFile); - AllocateFileRange(file, pos.nPos, nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos); + if (CheckDiskSpace(nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos)) { + FILE *file = OpenUndoFile(pos); + if (file) { + printf("Pre-allocating up to position 0x%x in rev%05u.dat\n", nNewChunks * UNDOFILE_CHUNK_SIZE, pos.nFile); + AllocateFileRange(file, pos.nPos, nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos); + fclose(file); + } } - fclose(file); + else + return error("FindUndoPos() : out of disk space"); } return true; @@ -1951,9 +2033,13 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const if (!tx.CheckTransaction()) return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed")); + // Build the merkle tree already. We need it anyway later, and it makes the + // block cache the transaction hashes, which means they don't need to be + // recalculated many times during this block's validation. + BuildMerkleTree(); + // Check for duplicate txids. This is caught by ConnectInputs(), // but catching it earlier avoids a potential DoS attack: - BuildMerkleTree(); set<uint256> uniqueTx; for (unsigned int i=0; i<vtx.size(); i++) { uniqueTx.insert(GetTxHash(i)); @@ -1976,7 +2062,7 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const return true; } -bool CBlock::AcceptBlock() +bool CBlock::AcceptBlock(CDiskBlockPos *dbp) { // Check for duplicate uint256 hash = GetHash(); @@ -1984,60 +2070,65 @@ bool CBlock::AcceptBlock() return error("AcceptBlock() : block already in mapBlockIndex"); // Get prev block index - map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashPrevBlock); - if (mi == mapBlockIndex.end()) - return DoS(10, error("AcceptBlock() : prev block not found")); - CBlockIndex* pindexPrev = (*mi).second; - int nHeight = pindexPrev->nHeight+1; - - // Check proof of work - if (nBits != GetNextWorkRequired(pindexPrev, this)) - return DoS(100, error("AcceptBlock() : incorrect proof of work")); - - // Check timestamp against prev - if (GetBlockTime() <= pindexPrev->GetMedianTimePast()) - return error("AcceptBlock() : block's timestamp is too early"); - - // Check that all transactions are finalized - BOOST_FOREACH(const CTransaction& tx, vtx) - if (!tx.IsFinal(nHeight, GetBlockTime())) - return DoS(10, error("AcceptBlock() : contains a non-final transaction")); - - // Check that the block chain matches the known block chain up to a checkpoint - if (!Checkpoints::CheckBlock(nHeight, hash)) - return DoS(100, error("AcceptBlock() : rejected by checkpoint lock-in at %d", nHeight)); - - // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: - if (nVersion < 2) - { - if ((!fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 950, 1000)) || - (fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 75, 100))) + CBlockIndex* pindexPrev = NULL; + int nHeight = 0; + if (hash != hashGenesisBlock) { + map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashPrevBlock); + if (mi == mapBlockIndex.end()) + return DoS(10, error("AcceptBlock() : prev block not found")); + pindexPrev = (*mi).second; + nHeight = pindexPrev->nHeight+1; + + // Check proof of work + if (nBits != GetNextWorkRequired(pindexPrev, this)) + return DoS(100, error("AcceptBlock() : incorrect proof of work")); + + // Check timestamp against prev + if (GetBlockTime() <= pindexPrev->GetMedianTimePast()) + return error("AcceptBlock() : block's timestamp is too early"); + + // Check that all transactions are finalized + BOOST_FOREACH(const CTransaction& tx, vtx) + if (!tx.IsFinal(nHeight, GetBlockTime())) + return DoS(10, error("AcceptBlock() : contains a non-final transaction")); + + // Check that the block chain matches the known block chain up to a checkpoint + if (!Checkpoints::CheckBlock(nHeight, hash)) + return DoS(100, error("AcceptBlock() : rejected by checkpoint lock-in at %d", nHeight)); + + // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: + if (nVersion < 2) { - return error("AcceptBlock() : rejected nVersion=1 block"); + if ((!fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 950, 1000)) || + (fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 75, 100))) + { + return error("AcceptBlock() : rejected nVersion=1 block"); + } } - } - // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height - if (nVersion >= 2) - { - // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet): - if ((!fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 750, 1000)) || - (fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 51, 100))) + // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height + if (nVersion >= 2) { - CScript expect = CScript() << nHeight; - if (!std::equal(expect.begin(), expect.end(), vtx[0].vin[0].scriptSig.begin())) - return DoS(100, error("AcceptBlock() : block height mismatch in coinbase")); + // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet): + if ((!fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 750, 1000)) || + (fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 51, 100))) + { + CScript expect = CScript() << nHeight; + if (!std::equal(expect.begin(), expect.end(), vtx[0].vin[0].scriptSig.begin())) + return DoS(100, error("AcceptBlock() : block height mismatch in coinbase")); + } } } // Write block to history file unsigned int nBlockSize = ::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION); - if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION))) - return error("AcceptBlock() : out of disk space"); CDiskBlockPos blockPos; - if (!FindBlockPos(blockPos, nBlockSize+8, nHeight, nTime)) + if (dbp != NULL) + blockPos = *dbp; + if (!FindBlockPos(blockPos, nBlockSize+8, nHeight, nTime, dbp != NULL)) return error("AcceptBlock() : FindBlockPos failed"); - if (!WriteToDisk(blockPos)) - return error("AcceptBlock() : WriteToDisk failed"); + if (dbp == NULL) + if (!WriteToDisk(blockPos)) + return error("AcceptBlock() : WriteToDisk failed"); if (!AddToBlockIndex(blockPos)) return error("AcceptBlock() : AddToBlockIndex failed"); @@ -2066,14 +2157,14 @@ bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, uns return (nFound >= nRequired); } -bool ProcessBlock(CNode* pfrom, CBlock* pblock) +bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) { // Check for duplicate uint256 hash = pblock->GetHash(); if (mapBlockIndex.count(hash)) - return error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, hash.ToString().substr(0,20).c_str()); + return error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, BlockHashStr(hash).c_str()); if (mapOrphanBlocks.count(hash)) - return error("ProcessBlock() : already have block (orphan) %s", hash.ToString().substr(0,20).c_str()); + return error("ProcessBlock() : already have block (orphan) %s", BlockHashStr(hash).c_str()); // Preliminary checks if (!pblock->CheckBlock()) @@ -2104,9 +2195,9 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) // If we don't already have its previous block, shunt it off to holding area until we get it - if (!mapBlockIndex.count(pblock->hashPrevBlock)) + if (pblock->hashPrevBlock != 0 && !mapBlockIndex.count(pblock->hashPrevBlock)) { - printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().substr(0,20).c_str()); + printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", BlockHashStr(pblock->hashPrevBlock).c_str()); // Accept orphans as long as there is a node to request its parents from if (pfrom) { @@ -2121,7 +2212,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) } // Store to disk - if (!pblock->AcceptBlock()) + if (!pblock->AcceptBlock(dbp)) return error("ProcessBlock() : AcceptBlock FAILED"); // Recursively process any orphan blocks that depended on this one @@ -2162,10 +2253,10 @@ bool CheckDiskSpace(uint64 nAdditionalBytes) if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes) { fShutdown = true; - string strMessage = _("Warning: Disk space is low!"); + string strMessage = _("Error: Disk space is low!"); strMiscWarning = strMessage; printf("*** %s\n", strMessage.c_str()); - uiInterface.ThreadSafeMessageBox(strMessage, "Bitcoin", CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION | CClientUIInterface::MODAL); + uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_ERROR); StartShutdown(); return false; } @@ -2258,14 +2349,19 @@ bool static LoadBlockIndexDB() printf("LoadBlockIndex(): last block file = %i\n", nLastBlockFile); if (pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile)) printf("LoadBlockIndex(): last block file: %s\n", infoLastBlockFile.ToString().c_str()); - + + // Load bnBestInvalidWork, OK if it doesn't exist + pblocktree->ReadBestInvalidWork(bnBestInvalidWork); + + // Check whether we need to continue reindexing + bool fReindexing = false; + pblocktree->ReadReindexing(fReindexing); + fReindex |= fReindexing; + // Load hashBestChain pointer to end of best chain pindexBest = pcoinsTip->GetBestBlock(); if (pindexBest == NULL) - { - if (pindexGenesisBlock == NULL) - return true; - } + return true; hashBestChain = pindexBest->GetBlockHash(); nBestHeight = pindexBest->nHeight; bnBestChainWork = pindexBest->bnChainWork; @@ -2278,12 +2374,9 @@ bool static LoadBlockIndexDB() pindex = pindexPrev; } printf("LoadBlockIndex(): hashBestChain=%s height=%d date=%s\n", - hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, + BlockHashStr(hashBestChain).c_str(), nBestHeight, DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str()); - // Load bnBestInvalidWork, OK if it doesn't exist - pblocktree->ReadBestInvalidWork(bnBestInvalidWork); - // Verify blocks in the best chain int nCheckLevel = GetArg("-checklevel", 1); int nCheckDepth = GetArg( "-checkblocks", 2500); @@ -2317,7 +2410,7 @@ bool static LoadBlockIndexDB() return true; } -bool LoadBlockIndex(bool fAllowNew) +bool LoadBlockIndex() { if (fTestNet) { @@ -2328,6 +2421,9 @@ bool LoadBlockIndex(bool fAllowNew) hashGenesisBlock = uint256("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"); } + if (fReindex) + return true; + // // Load block index from databases // @@ -2339,9 +2435,6 @@ bool LoadBlockIndex(bool fAllowNew) // if (mapBlockIndex.empty()) { - if (!fAllowNew) - return false; - // Genesis Block: // CBlock(hash=000000000019d6, ver=1, hashPrevBlock=00000000000000, hashMerkleRoot=4a5e1e, nTime=1231006505, nBits=1d00ffff, nNonce=2083236893, vtx=1) // CTransaction(hash=4a5e1e, ver=1, vin.size=1, vout.size=1, nLockTime=0) @@ -2467,113 +2560,71 @@ void PrintBlockTree() } } -bool LoadExternalBlockFile(FILE* fileIn) +bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) { int64 nStart = GetTimeMillis(); int nLoaded = 0; { - try { - CAutoFile blkdat(fileIn, SER_DISK, CLIENT_VERSION); - unsigned int nPos = 0; - while (nPos != (unsigned int)-1 && blkdat.good() && !fRequestShutdown) - { - unsigned char pchData[65536]; - do { - fseek(blkdat, nPos, SEEK_SET); - int nRead = fread(pchData, 1, sizeof(pchData), blkdat); - if (nRead <= 8) - { - nPos = (unsigned int)-1; - break; - } - void* nFind = memchr(pchData, pchMessageStart[0], nRead+1-sizeof(pchMessageStart)); - if (nFind) - { - if (memcmp(nFind, pchMessageStart, sizeof(pchMessageStart))==0) - { - nPos += ((unsigned char*)nFind - pchData) + sizeof(pchMessageStart); - break; - } - nPos += ((unsigned char*)nFind - pchData) + 1; - } - else - nPos += sizeof(pchData) - sizeof(pchMessageStart) + 1; - } while(!fRequestShutdown); - if (nPos == (unsigned int)-1) - break; - fseek(blkdat, nPos, SEEK_SET); - unsigned int nSize; + CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SIZE, MAX_BLOCK_SIZE+8, SER_DISK, CLIENT_VERSION); + uint64 nStartByte = 0; + if (dbp) { + // (try to) skip already indexed part + CBlockFileInfo info; + if (pblocktree->ReadBlockFileInfo(dbp->nFile, info)) { + nStartByte = info.nSize; + blkdat.Seek(info.nSize); + } + } + uint64 nRewind = blkdat.GetPos(); + while (blkdat.good() && !blkdat.eof() && !fRequestShutdown) { + blkdat.SetPos(nRewind); + nRewind++; // start one byte further next time, in case of failure + blkdat.SetLimit(); // remove former limit + unsigned int nSize = 0; + try { + // locate a header + unsigned char buf[4]; + blkdat.FindByte(pchMessageStart[0]); + nRewind = blkdat.GetPos()+1; + blkdat >> FLATDATA(buf); + if (memcmp(buf, pchMessageStart, 4)) + continue; + // read size blkdat >> nSize; - if (nSize > 0 && nSize <= MAX_BLOCK_SIZE) - { - CBlock block; - blkdat >> block; + if (nSize < 80 || nSize > MAX_BLOCK_SIZE) + continue; + } catch (std::exception &e) { + // no valid block header found; don't complain + break; + } + try { + // read block + uint64 nBlockPos = blkdat.GetPos(); + blkdat.SetLimit(nBlockPos + nSize); + CBlock block; + blkdat >> block; + nRewind = blkdat.GetPos(); + + // process block + if (nBlockPos >= nStartByte) { LOCK(cs_main); - if (ProcessBlock(NULL,&block)) - { + if (dbp) + dbp->nPos = nBlockPos; + if (ProcessBlock(NULL, &block, dbp)) nLoaded++; - nPos += 4 + nSize; - } } + } catch (std::exception &e) { + printf("%s() : Deserialize or I/O error caught during load\n", __PRETTY_FUNCTION__); } } - catch (std::exception &e) { - printf("%s() : Deserialize or I/O error caught during load\n", - __PRETTY_FUNCTION__); - } + fclose(fileIn); } - printf("Loaded %i blocks from external file in %"PRI64d"ms\n", nLoaded, GetTimeMillis() - nStart); + if (nLoaded > 0) + printf("Loaded %i blocks from external file in %"PRI64d"ms\n", nLoaded, GetTimeMillis() - nStart); return nLoaded > 0; } -struct CImportingNow -{ - CImportingNow() { - assert(fImporting == false); - fImporting = true; - } - - ~CImportingNow() { - assert(fImporting == true); - fImporting = false; - } -}; - -void ThreadImport(void *data) { - std::vector<boost::filesystem::path> *vFiles = reinterpret_cast<std::vector<boost::filesystem::path>*>(data); - - RenameThread("bitcoin-loadblk"); - - CImportingNow imp; - vnThreadsRunning[THREAD_IMPORT]++; - - // -loadblock= - uiInterface.InitMessage(_("Starting block import...")); - BOOST_FOREACH(boost::filesystem::path &path, *vFiles) { - FILE *file = fopen(path.string().c_str(), "rb"); - if (file) - LoadExternalBlockFile(file); - } - - // hardcoded $DATADIR/bootstrap.dat - filesystem::path pathBootstrap = GetDataDir() / "bootstrap.dat"; - if (filesystem::exists(pathBootstrap)) { - uiInterface.InitMessage(_("Importing bootstrap blockchain data file.")); - - FILE *file = fopen(pathBootstrap.string().c_str(), "rb"); - if (file) { - filesystem::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old"; - LoadExternalBlockFile(file); - RenameOver(pathBootstrap, pathBootstrapOld); - } - } - - delete vFiles; - - vnThreadsRunning[THREAD_IMPORT]--; -} - @@ -2596,9 +2647,13 @@ string GetWarnings(string strFor) int nPriority = 0; string strStatusBar; string strRPC; + if (GetBoolArg("-testsafemode")) strRPC = "test"; + if (!CLIENT_VERSION_IS_RELEASE) + strStatusBar = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications"); + // Misc warnings like out of disk space and clock is wrong if (strMiscWarning != "") { @@ -2681,7 +2736,6 @@ unsigned char pchMessageStart[4] = { 0xf9, 0xbe, 0xb4, 0xd9 }; bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { - static map<CService, CPubKey> mapReuseKey; RandAddSeedPerfmon(); if (fDebug) printf("received: %s (%"PRIszu" bytes)\n", strCommand.c_str(), vRecv.size()); @@ -2780,7 +2834,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // Ask the first connected node for block updates static int nAskedForBlocks = 0; - if (!pfrom->fClient && !pfrom->fOneShot && !fImporting && + if (!pfrom->fClient && !pfrom->fOneShot && !fImporting && !fReindex && (pfrom->nStartingHeight > (nBestHeight - 144)) && (pfrom->nVersion < NOBLKS_VERSION_START || pfrom->nVersion >= NOBLKS_VERSION_END) && @@ -2917,7 +2971,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) printf(" got inventory: %s %s\n", inv.ToString().c_str(), fAlreadyHave ? "have" : "new"); if (!fAlreadyHave) { - if (!fImporting) + if (!fImporting && !fReindex) pfrom->AskFor(inv); } else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) { pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(mapOrphanBlocks[inv.hash])); @@ -3022,12 +3076,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (pindex) pindex = pindex->pnext; int nLimit = 500; - printf("getblocks %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,20).c_str(), nLimit); + printf("getblocks %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), BlockHashStr(hashStop).c_str(), nLimit); for (; pindex; pindex = pindex->pnext) { if (pindex->GetBlockHash() == hashStop) { - printf(" getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str()); + printf(" getblocks stopping at %d %s\n", pindex->nHeight, BlockHashStr(pindex->GetBlockHash()).c_str()); break; } pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash())); @@ -3035,7 +3089,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { // When this block is requested, we'll send an inv that'll make them // getblocks the next batch of inventory. - printf(" getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str()); + printf(" getblocks stopping at limit %d %s\n", pindex->nHeight, BlockHashStr(pindex->GetBlockHash()).c_str()); pfrom->hashContinue = pindex->GetBlockHash(); break; } @@ -3066,9 +3120,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pindex = pindex->pnext; } + // we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end vector<CBlock> vHeaders; int nLimit = 2000; - printf("getheaders %d to %s\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,20).c_str()); + printf("getheaders %d to %s\n", (pindex ? pindex->nHeight : -1), BlockHashStr(hashStop).c_str()); for (; pindex; pindex = pindex->pnext) { vHeaders.push_back(pindex->GetBlockHeader()); @@ -3147,12 +3202,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } - else if (strCommand == "block") + else if (strCommand == "block" && !fImporting && !fReindex) // Ignore blocks received while importing { CBlock block; vRecv >> block; - printf("received block %s\n", block.GetHash().ToString().substr(0,20).c_str()); + printf("received block %s\n", BlockHashStr(block.GetHash()).c_str()); // block.print(); CInv inv(MSG_BLOCK, block.GetHash()); @@ -3189,53 +3244,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } - else if (strCommand == "checkorder") - { - uint256 hashReply; - vRecv >> hashReply; - - if (!GetBoolArg("-allowreceivebyip")) - { - pfrom->PushMessage("reply", hashReply, (int)2, string("")); - return true; - } - - CWalletTx order; - vRecv >> order; - - /// we have a chance to check the order here - - // Keep giving the same key to the same ip until they use it - if (!mapReuseKey.count(pfrom->addr)) - pwalletMain->GetKeyFromPool(mapReuseKey[pfrom->addr], true); - - // Send back approval of order and pubkey to use - CScript scriptPubKey; - scriptPubKey << mapReuseKey[pfrom->addr] << OP_CHECKSIG; - pfrom->PushMessage("reply", hashReply, (int)0, scriptPubKey); - } - - - else if (strCommand == "reply") - { - uint256 hashReply; - vRecv >> hashReply; - - CRequestTracker tracker; - { - LOCK(pfrom->cs_mapRequests); - map<uint256, CRequestTracker>::iterator mi = pfrom->mapRequests.find(hashReply); - if (mi != pfrom->mapRequests.end()) - { - tracker = (*mi).second; - pfrom->mapRequests.erase(mi); - } - } - if (!tracker.IsNull()) - tracker.fn(tracker.param1, vRecv); - } - - else if (strCommand == "ping") { if (pfrom->nVersion > BIP0031_VERSION) @@ -3724,13 +3732,8 @@ public: } }; -const char* pszDummy = "\0\0"; -CScript scriptDummy(std::vector<unsigned char>(pszDummy, pszDummy + sizeof(pszDummy))); - CBlock* CreateNewBlock(CReserveKey& reservekey) { - CBlockIndex* pindexPrev = pindexBest; - // Create new block auto_ptr<CBlock> pblock(new CBlock()); if (!pblock.get()) @@ -3774,11 +3777,13 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) int64 nFees = 0; { LOCK2(cs_main, mempool.cs); + CBlockIndex* pindexPrev = pindexBest; CCoinsViewCache view(*pcoinsTip, true); // Priority order to process transactions list<COrphan> vOrphan; // list memory doesn't move map<uint256, vector<COrphan*> > mapDependers; + bool fPrintPriority = GetBoolArg("-printpriority"); // This vector will be sorted into a priority queue: vector<TxPriority> vecPriority; @@ -3828,7 +3833,7 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) int64 nValueIn = coins.vout[txin.prevout.n].nValue; nTotalIn += nValueIn; - int nConf = pindexPrev->nHeight - coins.nHeight; + int nConf = pindexPrev->nHeight - coins.nHeight + 1; dPriority += (double)nValueIn * nConf; } @@ -3907,7 +3912,7 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) continue; - if (!tx.CheckInputs(viewTemp, CS_ALWAYS, true, false)) + if (!tx.CheckInputs(viewTemp, CS_ALWAYS, SCRIPT_VERIFY_P2SH)) continue; CTxUndo txundo; @@ -3925,7 +3930,7 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) nBlockSigOps += nTxSigOps; nFees += nTxFees; - if (fDebug && GetBoolArg("-printpriority")) + if (fPrintPriority) { printf("priority %.1f feeperkb %.1f txid %s\n", dPriority, dFeePerKb, tx.GetHash().ToString().c_str()); @@ -3960,7 +3965,7 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) pblock->UpdateTime(pindexPrev); pblock->nBits = GetNextWorkRequired(pindexPrev, pblock.get()); pblock->nNonce = 0; - pblock->vtx[0].vin[0].scriptSig = scriptDummy; + pblock->vtx[0].vin[0].scriptSig = CScript() << OP_0 << OP_0; CBlockIndex indexDummy(*pblock); indexDummy.pprev = pindexPrev; diff --git a/src/main.h b/src/main.h index 75ee7a9be5..fdaec3469e 100644 --- a/src/main.h +++ b/src/main.h @@ -20,26 +20,38 @@ class CReserveKey; class CAddress; class CInv; -class CRequestTracker; class CNode; -class CBlockIndexWorkComparator; +struct CBlockIndexWorkComparator; +/** 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; +/** 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 */ static const unsigned int MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100; +/** The maximum number of entries in an 'inv' protocol message */ static const unsigned int MAX_INV_SZ = 50000; +/** The maximum size of a blk?????.dat file (since 0.8) */ static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB +/** The pre-allocation chunk size for blk?????.dat files (since 0.8) */ static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB +/** The pre-allocation chunk size for rev?????.dat files (since 0.8) */ static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB +/** Fake height value used in CCoins to signify they are only in the memory pool (since 0.8) */ static const unsigned int MEMPOOL_HEIGHT = 0x7FFFFFFF; +/** Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) */ static const int64 MIN_TX_FEE = 50000; +/** Fees smaller than this (in satoshi) are considered zero fee (for relaying) */ static const int64 MIN_RELAY_TX_FEE = 10000; +/** No amount larger than this (in satoshi) is valid */ static const int64 MAX_MONEY = 21000000 * COIN; inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } +/** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */ static const int COINBASE_MATURITY = 100; -// Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp. +/** Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp. */ static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC #ifdef USE_UPNP static const int fHaveUPnP = true; @@ -76,6 +88,9 @@ extern CCriticalSection cs_setpwalletRegistered; extern std::set<CWallet*> setpwalletRegistered; extern unsigned char pchMessageStart[4]; extern bool fImporting; +extern bool fReindex; +extern bool fBenchmark; +extern unsigned int nCoinCacheSize; // Settings extern int64 nTransactionFee; @@ -93,32 +108,61 @@ class CTxUndo; class CCoinsView; class CCoinsViewCache; +/** Register a wallet to receive updates from core */ void RegisterWallet(CWallet* pwalletIn); +/** Unregister a wallet from core */ void UnregisterWallet(CWallet* pwalletIn); +/** Push an updated transaction to all registered wallets */ void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false); -bool ProcessBlock(CNode* pfrom, CBlock* pblock); +/** Process an incoming block */ +bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp = NULL); +/** Check whether enough disk space is available for an incoming block */ bool CheckDiskSpace(uint64 nAdditionalBytes=0); +/** Open a block file (blk?????.dat) */ FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly = false); +/** Open an undo file (rev?????.dat) */ FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false); -bool LoadBlockIndex(bool fAllowNew=true); +/** Import blocks from an external file */ +bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp = NULL); +/** Load the block tree and coins database from disk */ +bool LoadBlockIndex(); +/** Print the loaded block tree */ void PrintBlockTree(); +/** Find a block by height in the currently-connected chain */ CBlockIndex* FindBlockByHeight(int nHeight); +/** Process protocol messages received from a given node */ bool ProcessMessages(CNode* pfrom); +/** Send queued protocol messages to be sent to a give node */ bool SendMessages(CNode* pto, bool fSendTrickle); +/** Run the importer thread, which deals with reindexing, loading bootstrap.dat, and whatever is passed to -loadblock */ void ThreadImport(void *parg); +/** Run the miner threads */ void GenerateBitcoins(bool fGenerate, CWallet* pwallet); +/** Generate a new block, without valid proof-of-work */ CBlock* CreateNewBlock(CReserveKey& reservekey); +/** Modify the extranonce in a block */ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce); +/** Do mining precalculation */ void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1); +/** Check mined block */ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey); +/** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */ bool CheckProofOfWork(uint256 hash, unsigned int nBits); +/** Calculate the minimum amount of work a received block needs, without knowing its direct parent */ unsigned int ComputeMinWork(unsigned int nBase, int64 nTime); +/** Get the number of active peers */ int GetNumBlocksOfPeers(); +/** Check whether we are doin an inital block download (synchronizing from disk or network) */ bool IsInitialBlockDownload(); +/** Format a string that describes several potential problems detected by the core */ std::string GetWarnings(std::string strFor); +/** Retrieve a transaction (from memory pool, or from disk, if possible) */ bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, bool fAllowSlow = false); +/** Connect/disconnect blocks until pindexNew is the new tip of the active block chain */ bool SetBestChain(CBlockIndex* pindexNew); +/** Find the best known block, and make it the tip of the block chain */ bool ConnectBestBlock(); +/** Create a new block index entry for a given block hash */ CBlockIndex * InsertBlockIndex(uint256 hash); @@ -131,6 +175,11 @@ CBlockIndex * InsertBlockIndex(uint256 hash); +static inline std::string BlockHashStr(const uint256& hash) +{ + return hash.ToString(); +} + bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut); class CDiskBlockPos @@ -144,6 +193,15 @@ public: READWRITE(VARINT(nPos)); ) + CDiskBlockPos() { + SetNull(); + } + + CDiskBlockPos(int nFileIn, unsigned int nPosIn) { + nFile = nFileIn; + nPos = nPosIn; + } + friend bool operator==(const CDiskBlockPos &a, const CDiskBlockPos &b) { return (a.nFile == b.nFile && a.nPos == b.nPos); } @@ -582,7 +640,7 @@ public: // Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts) // This does not modify the UTXO set - bool CheckInputs(CCoinsViewCache &view, enum CheckSig_mode csmode, bool fStrictPayToScriptHash=true, bool fStrictEncodings=true) const; + bool CheckInputs(CCoinsViewCache &view, enum CheckSig_mode csmode, unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC) const; // Apply the effects of this transaction on the UTXO set represented by view bool UpdateCoins(CCoinsViewCache &view, CTxUndo &txundo, int nHeight, const uint256 &txhash) const; @@ -690,7 +748,6 @@ public: bool WriteToDisk(CDiskBlockPos &pos) { - // Open history file to append CAutoFile fileout = CAutoFile(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); if (!fileout) @@ -703,7 +760,7 @@ public: // Write undo data long fileOutPos = ftell(fileout); if (fileOutPos < 0) - return error("CBlock::WriteToDisk() : ftell failed"); + return error("CBlockUndo::WriteToDisk() : ftell failed"); pos.nPos = (unsigned int)fileOutPos; fileout << *this; @@ -714,7 +771,6 @@ public: return true; } - }; /** pruned version of CTransaction: only retains metadata and unspent transaction outputs @@ -777,7 +833,7 @@ public: // unspent transaction outputs; spent outputs are .IsNull(); spent outputs at the end of the array are dropped std::vector<CTxOut> vout; - // at which height this transaction was included in the active blockchain + // at which height this transaction was included in the active block chain int nHeight; // version of the CTransaction; accesses to this value should probably check for nHeight as well, @@ -798,7 +854,7 @@ public: // equality test friend bool operator==(const CCoins &a, const CCoins &b) { - return a.fCoinBase == b.fCoinBase && + return a.fCoinBase == b.fCoinBase && a.nHeight == b.nHeight && a.nVersion == b.nVersion && a.vout == b.vout; @@ -1025,7 +1081,7 @@ public: * in the block is a special one that creates a new coin owned by the creator * of the block. */ -class CBlock +class CBlockHeader { public: // header @@ -1037,17 +1093,7 @@ public: unsigned int nBits; unsigned int nNonce; - // network and disk - std::vector<CTransaction> vtx; - - // memory only - mutable std::vector<uint256> vMerkleTree; - - // Denial-of-service detection: - mutable int nDoS; - bool DoS(int nDoSIn, bool fIn) const { nDoS += nDoSIn; return fIn; } - - CBlock() + CBlockHeader() { SetNull(); } @@ -1061,25 +1107,16 @@ public: READWRITE(nTime); READWRITE(nBits); READWRITE(nNonce); - - // ConnectBlock depends on vtx being last so it can calculate offset - if (!(nType & (SER_GETHASH|SER_BLOCKHEADERONLY))) - READWRITE(vtx); - else if (fRead) - const_cast<CBlock*>(this)->vtx.clear(); ) void SetNull() { - nVersion = CBlock::CURRENT_VERSION; + nVersion = CBlockHeader::CURRENT_VERSION; hashPrevBlock = 0; hashMerkleRoot = 0; nTime = 0; nBits = 0; nNonce = 0; - vtx.clear(); - vMerkleTree.clear(); - nDoS = 0; } bool IsNull() const @@ -1098,7 +1135,45 @@ public: } void UpdateTime(const CBlockIndex* pindexPrev); +}; + +class CBlock : public CBlockHeader +{ +public: + // network and disk + std::vector<CTransaction> vtx; + + // memory only + mutable std::vector<uint256> vMerkleTree; + // Denial-of-service detection: + mutable int nDoS; + bool DoS(int nDoSIn, bool fIn) const { nDoS += nDoSIn; return fIn; } + + CBlock() + { + SetNull(); + } + + CBlock(const CBlockHeader &header) + { + SetNull(); + *((CBlockHeader*)this) = header; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(*(CBlockHeader*)this); + READWRITE(vtx); + ) + + void SetNull() + { + CBlockHeader::SetNull(); + vtx.clear(); + vMerkleTree.clear(); + nDoS = 0; + } uint256 BuildMerkleTree() const { @@ -1183,7 +1258,7 @@ public: return true; } - bool ReadFromDisk(const CDiskBlockPos &pos, bool fReadTransactions = true) + bool ReadFromDisk(const CDiskBlockPos &pos) { SetNull(); @@ -1191,8 +1266,6 @@ public: CAutoFile filein = CAutoFile(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION); if (!filein) return error("CBlock::ReadFromDisk() : OpenBlockFile failed"); - if (!fReadTransactions) - filein.nType |= SER_BLOCKHEADERONLY; // Read block try { @@ -1214,9 +1287,9 @@ public: void print() const { printf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%"PRIszu")\n", - GetHash().ToString().substr(0,20).c_str(), + BlockHashStr(GetHash()).c_str(), nVersion, - hashPrevBlock.ToString().substr(0,20).c_str(), + BlockHashStr(hashPrevBlock).c_str(), hashMerkleRoot.ToString().substr(0,10).c_str(), nTime, nBits, nNonce, vtx.size()); @@ -1239,7 +1312,7 @@ public: bool ConnectBlock(CBlockIndex *pindex, CCoinsViewCache &coins, bool fJustCheck=false); // Read a block from disk - bool ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions=true); + bool ReadFromDisk(const CBlockIndex* pindex); // Add this block to the block index, and if necessary, switch the active block chain to this bool AddToBlockIndex(const CDiskBlockPos &pos); @@ -1248,7 +1321,8 @@ public: bool CheckBlock(bool fCheckPOW=true, bool fCheckMerkleRoot=true) const; // Store block on disk - bool AcceptBlock(); + // if dbp is provided, the file is known to already reside on disk + bool AcceptBlock(CDiskBlockPos *dbp = NULL); }; @@ -1403,7 +1477,7 @@ public: nNonce = 0; } - CBlockIndex(CBlock& block) + CBlockIndex(CBlockHeader& block) { phashBlock = NULL; pprev = NULL; @@ -1429,8 +1503,7 @@ public: if (nStatus & BLOCK_HAVE_DATA) { ret.nFile = nFile; ret.nPos = nDataPos; - } else - ret.SetNull(); + } return ret; } @@ -1439,14 +1512,13 @@ public: if (nStatus & BLOCK_HAVE_UNDO) { ret.nFile = nFile; ret.nPos = nUndoPos; - } else - ret.SetNull(); + } return ret; } - CBlock GetBlockHeader() const + CBlockHeader GetBlockHeader() const { - CBlock block; + CBlockHeader block; block.nVersion = nVersion; if (pprev) block.hashPrevBlock = pprev->GetBlockHash(); @@ -1526,7 +1598,7 @@ public: return strprintf("CBlockIndex(pprev=%p, pnext=%p, nHeight=%d, merkle=%s, hashBlock=%s)", pprev, pnext, nHeight, hashMerkleRoot.ToString().substr(0,10).c_str(), - GetBlockHash().ToString().substr(0,20).c_str()); + BlockHashStr(GetBlockHash()).c_str()); } void print() const @@ -1590,7 +1662,7 @@ public: uint256 GetBlockHash() const { - CBlock block; + CBlockHeader block; block.nVersion = nVersion; block.hashPrevBlock = hashPrev; block.hashMerkleRoot = hashMerkleRoot; @@ -1607,7 +1679,7 @@ public: str += CBlockIndex::ToString(); str += strprintf("\n hashBlock=%s, hashPrev=%s)", GetBlockHash().ToString().c_str(), - hashPrev.ToString().substr(0,20).c_str()); + BlockHashStr(hashPrev).c_str()); return str; } @@ -1767,7 +1839,8 @@ public: bool accept(CTransaction &tx, bool fCheckInputs, bool* pfMissingInputs); bool addUnchecked(const uint256& hash, CTransaction &tx); - bool remove(CTransaction &tx); + bool remove(const CTransaction &tx, bool fRecursive = false); + bool removeConflicts(const CTransaction &tx); void clear(); void queryHashes(std::vector<uint256>& vtxid); void pruneSpent(const uint256& hash, CCoins &coins); @@ -1820,8 +1893,15 @@ public: // Modify the currently active block index virtual bool SetBestBlock(CBlockIndex *pindex); + + // Do a bulk modification (multiple SetCoins + one SetBestBlock) virtual bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex); + + // Calculate statistics about the unspent transaction output set virtual bool GetStats(CCoinsStats &stats); + + // As we use CCoinsViews polymorphically, have a virtual destructor + virtual ~CCoinsView() {} }; /** CCoinsView backed by another CCoinsView */ @@ -1851,14 +1931,25 @@ protected: public: CCoinsViewCache(CCoinsView &baseIn, bool fDummy = false); + + // Standard CCoinsView methods bool GetCoins(uint256 txid, CCoins &coins); bool SetCoins(uint256 txid, const CCoins &coins); bool HaveCoins(uint256 txid); - CCoins &GetCoins(uint256 txid); CBlockIndex *GetBestBlock(); bool SetBestBlock(CBlockIndex *pindex); bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex); + + // Return a modifiable reference to a CCoins. Check HaveCoins first. + // Many methods explicitly require a CCoinsViewCache because of this method, to reduce + // copying. + CCoins &GetCoins(uint256 txid); + + // Push the modifications applied to this cache to its base. + // Failure to call this method before destruction will cause the changes to be forgotten. bool Flush(); + + // Calculate the size of the cache (in number of transactions) unsigned int GetCacheSize(); private: diff --git a/src/makefile.linux-mingw b/src/makefile.linux-mingw index f4adbb2bff..47dc7c5c40 100644 --- a/src/makefile.linux-mingw +++ b/src/makefile.linux-mingw @@ -92,7 +92,7 @@ LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a DEFS += -I"$(CURDIR)/leveldb/include" DEFS += -I"$(CURDIR)/leveldb/helpers" leveldb/libleveldb.a: - @echo "Building LevelDB ..."; cd leveldb; TARGET_OS=OS_WINDOWS_CROSSCOMPILE CXXFLAGS="-I$(INCLUDEPATHS)" LDFLAGS="-L$(LIBPATHS)" make libleveldb.a libmemenv.a; cd .. + @echo "Building LevelDB ..." && cd leveldb && CC=i586-mingw32msvc-gcc CXX=i586-mingw32msvc-g++ TARGET_OS=OS_WINDOWS_CROSSCOMPILE CXXFLAGS="-I$(INCLUDEPATHS)" LDFLAGS="-L$(LIBPATHS)" $(MAKE) libleveldb.a libmemenv.a && i586-mingw32msvc-ranlib libleveldb.a && i586-mingw32msvc-ranlib libmemenv.a && cd .. obj/leveldb.o: leveldb/libleveldb.a obj/build.h: FORCE diff --git a/src/makefile.mingw b/src/makefile.mingw index 945ec77099..22d65d6703 100644 --- a/src/makefile.mingw +++ b/src/makefile.mingw @@ -94,9 +94,10 @@ test check: test_bitcoin.exe FORCE LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers) +# TODO: If this fails, try adding a ranlib libleveldb.a && ranlib libmemenv.a leveldb/libleveldb.a: - cd leveldb; make libleveldb.a libmemenv.a; cd .. -obj/leveldb.o: leveldb/libleveldb.lib + cd leveldb && $(MAKE) libleveldb.a libmemenv.a && cd .. +obj/leveldb.o: leveldb/libleveldb.a obj/%.o: %.cpp $(HEADERS) g++ -c $(CFLAGS) -o $@ $< diff --git a/src/makefile.osx b/src/makefile.osx index f3e17d0d13..25164c8679 100644 --- a/src/makefile.osx +++ b/src/makefile.osx @@ -128,7 +128,7 @@ LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers) leveldb/libleveldb.a: - @echo "Building LevelDB ..."; cd leveldb; make libleveldb.a libmemenv.a; cd .. + @echo "Building LevelDB ..." && cd leveldb && $(MAKE) libleveldb.a libmemenv.a && cd .. obj/leveldb.o: leveldb/libleveldb.a # auto-generated dependencies: diff --git a/src/makefile.unix b/src/makefile.unix index df05f7990a..9e17e8ace2 100644 --- a/src/makefile.unix +++ b/src/makefile.unix @@ -144,7 +144,7 @@ LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers) leveldb/libleveldb.a: - @echo "Building LevelDB ..."; cd leveldb; make libleveldb.a libmemenv.a; cd ..; + @echo "Building LevelDB ..." && cd leveldb && $(MAKE) libleveldb.a libmemenv.a && cd .. obj/leveldb.o: leveldb/libleveldb.a # auto-generated dependencies: diff --git a/src/net.cpp b/src/net.cpp index 2598f0214e..b54f8c15f7 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -710,13 +710,9 @@ void ThreadSocketHandler2(void* parg) TRY_LOCK(pnode->cs_vRecv, lockRecv); if (lockRecv) { - TRY_LOCK(pnode->cs_mapRequests, lockReq); - if (lockReq) - { - TRY_LOCK(pnode->cs_inventory, lockInv); - if (lockInv) - fDelete = true; - } + TRY_LOCK(pnode->cs_inventory, lockInv); + if (lockInv) + fDelete = true; } } } @@ -1313,6 +1309,8 @@ void DumpAddresses() void ThreadDumpAddress2(void* parg) { + printf("ThreadDumpAddress started\n"); + vnThreadsRunning[THREAD_DUMPADDRESS]++; while (!fShutdown) { @@ -19,7 +19,6 @@ #include "protocol.h" #include "addrman.h" -class CRequestTracker; class CNode; class CBlockIndex; extern int nBestHeight; @@ -68,31 +67,6 @@ void SetReachable(enum Network net, bool fFlag = true); CAddress GetLocalAddress(const CNetAddr *paddrPeer = NULL); -enum -{ - MSG_TX = 1, - MSG_BLOCK, -}; - -class CRequestTracker -{ -public: - void (*fn)(void*, CDataStream&); - void* param1; - - explicit CRequestTracker(void (*fnIn)(void*, CDataStream&)=NULL, void* param1In=NULL) - { - fn = fnIn; - param1 = param1In; - } - - bool IsNull() - { - return fn == NULL; - } -}; - - /** Thread types */ enum threadId { @@ -189,8 +163,6 @@ protected: public: int64 nReleaseTime; - std::map<uint256, CRequestTracker> mapRequests; - CCriticalSection cs_mapRequests; uint256 hashContinue; CBlockIndex* pindexLastGetBlocksBegin; uint256 hashLastGetBlocksEnd; @@ -564,53 +536,6 @@ public: } } - - void PushRequest(const char* pszCommand, - void (*fn)(void*, CDataStream&), void* param1) - { - uint256 hashReply; - RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply)); - - { - LOCK(cs_mapRequests); - mapRequests[hashReply] = CRequestTracker(fn, param1); - } - - PushMessage(pszCommand, hashReply); - } - - template<typename T1> - void PushRequest(const char* pszCommand, const T1& a1, - void (*fn)(void*, CDataStream&), void* param1) - { - uint256 hashReply; - RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply)); - - { - LOCK(cs_mapRequests); - mapRequests[hashReply] = CRequestTracker(fn, param1); - } - - PushMessage(pszCommand, hashReply, a1); - } - - template<typename T1, typename T2> - void PushRequest(const char* pszCommand, const T1& a1, const T2& a2, - void (*fn)(void*, CDataStream&), void* param1) - { - uint256 hashReply; - RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply)); - - { - LOCK(cs_mapRequests); - mapRequests[hashReply] = CRequestTracker(fn, param1); - } - - PushMessage(pszCommand, hashReply, a1, a2); - } - - - void PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd); bool IsSubscribed(unsigned int nChannel); void Subscribe(unsigned int nChannel, unsigned int nHops=0); diff --git a/src/netbase.cpp b/src/netbase.cpp index 7b28e7f1bc..9e7307204a 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -545,7 +545,7 @@ bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest void CNetAddr::Init() { - memset(ip, 0, 16); + memset(ip, 0, sizeof(ip)); } void CNetAddr::SetIP(const CNetAddr& ipIn) diff --git a/src/noui.cpp b/src/noui.cpp index db25f2d285..204e76aba7 100644 --- a/src/noui.cpp +++ b/src/noui.cpp @@ -2,20 +2,37 @@ // Copyright (c) 2009-2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #include "ui_interface.h" #include "init.h" #include "bitcoinrpc.h" #include <string> -static int noui_ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style) +static int noui_ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style) { + std::string strCaption; + // Check for usage of predefined caption + switch (style) { + case CClientUIInterface::MSG_ERROR: + strCaption += _("Error"); + break; + case CClientUIInterface::MSG_WARNING: + strCaption += _("Warning"); + break; + case CClientUIInterface::MSG_INFORMATION: + strCaption += _("Information"); + break; + default: + strCaption += caption; // Use supplied caption + } + printf("%s: %s\n", caption.c_str(), message.c_str()); - fprintf(stderr, "%s: %s\n", caption.c_str(), message.c_str()); + fprintf(stderr, "%s: %s\n", strCaption.c_str(), message.c_str()); return 4; } -static bool noui_ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption) +static bool noui_ThreadSafeAskFee(int64 /*nFeeRequired*/) { return true; } diff --git a/src/protocol.cpp b/src/protocol.cpp index d6e340e366..23969e5b97 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -6,6 +6,7 @@ #include "protocol.h" #include "util.h" #include "netbase.h" +#include "main.h" #ifndef WIN32 # include <arpa/inet.h> @@ -140,6 +141,11 @@ const char* CInv::GetCommand() const std::string CInv::ToString() const { + if (type == MSG_BLOCK) + return strprintf("%s %s", GetCommand(), BlockHashStr(hash).c_str()); + if (type == MSG_TX) + return strprintf("%s %s", GetCommand(), hash.ToString().substr(0,10).c_str()); + return strprintf("%s %s", GetCommand(), hash.ToString().substr(0,20).c_str()); } diff --git a/src/protocol.h b/src/protocol.h index 36f8b144cd..96fd197ecd 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -134,4 +134,10 @@ class CInv uint256 hash; }; +enum +{ + MSG_TX = 1, + MSG_BLOCK, +}; + #endif // __INCLUDED_PROTOCOL_H__ diff --git a/src/qt/addressbookpage.h b/src/qt/addressbookpage.h index df87486949..f7d177c513 100644 --- a/src/qt/addressbookpage.h +++ b/src/qt/addressbookpage.h @@ -82,4 +82,4 @@ signals: void verifyMessage(QString addr); }; -#endif // ADDRESSBOOKDIALOG_H +#endif // ADDRESSBOOKPAGE_H diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp index 03aa7af0be..cf35ee2457 100644 --- a/src/qt/askpassphrasedialog.cpp +++ b/src/qt/askpassphrasedialog.cpp @@ -19,7 +19,7 @@ AskPassphraseDialog::AskPassphraseDialog(Mode mode, QWidget *parent) : ui->passEdit1->setMaxLength(MAX_PASSPHRASE_SIZE); ui->passEdit2->setMaxLength(MAX_PASSPHRASE_SIZE); ui->passEdit3->setMaxLength(MAX_PASSPHRASE_SIZE); - + // Setup Caps Lock detection. ui->passEdit1->installEventFilter(this); ui->passEdit2->installEventFilter(this); @@ -108,15 +108,15 @@ void AskPassphraseDialog::accept() if(model->setWalletEncrypted(true, newpass1)) { QMessageBox::warning(this, tr("Wallet encrypted"), - "<qt>" + + "<qt>" + tr("Bitcoin will close now to finish the encryption process. " "Remember that encrypting your wallet cannot fully protect " - "your bitcoins from being stolen by malware infecting your computer.") + - "<br><br><b>" + + "your bitcoins from being stolen by malware infecting your computer.") + + "<br><br><b>" + tr("IMPORTANT: Any previous backups you have made of your wallet file " "should be replaced with the newly generated, encrypted wallet file. " "For security reasons, previous backups of the unencrypted wallet file " - "will become useless as soon as you start using the new, encrypted wallet.") + + "will become useless as soon as you start using the new, encrypted wallet.") + "</b></qt>"); QApplication::quit(); } @@ -221,7 +221,7 @@ bool AskPassphraseDialog::event(QEvent *event) return QWidget::event(event); } -bool AskPassphraseDialog::eventFilter(QObject *, QEvent *event) +bool AskPassphraseDialog::eventFilter(QObject *object, QEvent *event) { /* Detect Caps Lock. * There is no good OS-independent way to check a key state in Qt, but we @@ -244,5 +244,5 @@ bool AskPassphraseDialog::eventFilter(QObject *, QEvent *event) } } } - return false; + return QDialog::eventFilter(object, event); } diff --git a/src/qt/askpassphrasedialog.h b/src/qt/askpassphrasedialog.h index b500ff49bf..338853ce22 100644 --- a/src/qt/askpassphrasedialog.h +++ b/src/qt/askpassphrasedialog.h @@ -39,7 +39,7 @@ private: private slots: void textChanged(); bool event(QEvent *event); - bool eventFilter(QObject *, QEvent *event); + bool eventFilter(QObject *object, QEvent *event); }; #endif // ASKPASSPHRASEDIALOG_H diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index cd1764a53f..dbdfade0b1 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -7,7 +7,6 @@ #include "optionsmodel.h" #include "guiutil.h" #include "guiconstants.h" - #include "init.h" #include "ui_interface.h" #include "qtipcserver.h" @@ -35,18 +34,18 @@ Q_IMPORT_PLUGIN(qtaccessiblewidgets) static BitcoinGUI *guiref; static QSplashScreen *splashref; -static void ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style) +static void ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style) { // Message from network thread if(guiref) { bool modal = (style & CClientUIInterface::MODAL); - // in case of modal message, use blocking connection to wait for user to click OK - QMetaObject::invokeMethod(guiref, "error", + // In case of modal message, use blocking connection to wait for user to click a button + QMetaObject::invokeMethod(guiref, "message", modal ? GUIUtil::blockingGUIThreadConnection() : Qt::QueuedConnection, Q_ARG(QString, QString::fromStdString(caption)), Q_ARG(QString, QString::fromStdString(message)), - Q_ARG(bool, modal)); + Q_ARG(unsigned int, style)); } else { @@ -55,12 +54,13 @@ static void ThreadSafeMessageBox(const std::string& message, const std::string& } } -static bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption) +static bool ThreadSafeAskFee(int64 nFeeRequired) { if(!guiref) return false; if(nFeeRequired < MIN_TX_FEE || nFeeRequired <= nTransactionFee || fDaemon) return true; + bool payFee = false; QMetaObject::invokeMethod(guiref, "askFee", GUIUtil::blockingGUIThreadConnection(), diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h index 66792e00a9..4797c4c882 100644 --- a/src/qt/bitcoinamountfield.h +++ b/src/qt/bitcoinamountfield.h @@ -1,5 +1,5 @@ -#ifndef BITCOINFIELD_H -#define BITCOINFIELD_H +#ifndef BITCOINAMOUNTFIELD_H +#define BITCOINAMOUNTFIELD_H #include <QWidget> @@ -57,4 +57,4 @@ private slots: }; -#endif // BITCOINFIELD_H +#endif // BITCOINAMOUNTFIELD_H diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 0d269ea210..3fe86501f6 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -25,6 +25,7 @@ #include "notificator.h" #include "guiutil.h" #include "rpcconsole.h" +#include "ui_interface.h" #ifdef Q_OS_MAC #include "macdockiconhandler.h" @@ -89,7 +90,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): // Create the toolbars createToolBars(); - // Create the tray icon (or setup the dock icon) + // Create system tray icon and notification createTrayIcon(); // Create tabs @@ -176,6 +177,9 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): // Clicking on "Sign Message" in the receive coins page sends you to the sign message tab connect(receiveCoinsPage, SIGNAL(signMessage(QString)), this, SLOT(gotoSignMessageTab(QString))); + // Install event filter to be able to catch status tip events (QEvent::StatusTip) + this->installEventFilter(this); + gotoOverviewPage(); } @@ -193,31 +197,36 @@ void BitcoinGUI::createActions() QActionGroup *tabGroup = new QActionGroup(this); overviewAction = new QAction(QIcon(":/icons/overview"), tr("&Overview"), this); - overviewAction->setToolTip(tr("Show general overview of wallet")); + overviewAction->setStatusTip(tr("Show general overview of wallet")); + overviewAction->setToolTip(overviewAction->statusTip()); overviewAction->setCheckable(true); overviewAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_1)); tabGroup->addAction(overviewAction); sendCoinsAction = new QAction(QIcon(":/icons/send"), tr("&Send coins"), this); - sendCoinsAction->setToolTip(tr("Send coins to a Bitcoin address")); + sendCoinsAction->setStatusTip(tr("Send coins to a Bitcoin address")); + sendCoinsAction->setToolTip(sendCoinsAction->statusTip()); sendCoinsAction->setCheckable(true); sendCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_2)); tabGroup->addAction(sendCoinsAction); receiveCoinsAction = new QAction(QIcon(":/icons/receiving_addresses"), tr("&Receive coins"), this); - receiveCoinsAction->setToolTip(tr("Show the list of addresses for receiving payments")); + receiveCoinsAction->setStatusTip(tr("Show the list of addresses for receiving payments")); + receiveCoinsAction->setToolTip(receiveCoinsAction->statusTip()); receiveCoinsAction->setCheckable(true); receiveCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_3)); tabGroup->addAction(receiveCoinsAction); historyAction = new QAction(QIcon(":/icons/history"), tr("&Transactions"), this); - historyAction->setToolTip(tr("Browse transaction history")); + historyAction->setStatusTip(tr("Browse transaction history")); + historyAction->setToolTip(historyAction->statusTip()); historyAction->setCheckable(true); historyAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_4)); tabGroup->addAction(historyAction); addressBookAction = new QAction(QIcon(":/icons/address-book"), tr("&Address Book"), this); - addressBookAction->setToolTip(tr("Edit the list of stored addresses and labels")); + addressBookAction->setStatusTip(tr("Edit the list of stored addresses and labels")); + addressBookAction->setToolTip(addressBookAction->statusTip()); addressBookAction->setCheckable(true); addressBookAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_5)); tabGroup->addAction(addressBookAction); @@ -234,33 +243,37 @@ void BitcoinGUI::createActions() connect(addressBookAction, SIGNAL(triggered()), this, SLOT(gotoAddressBookPage())); quitAction = new QAction(QIcon(":/icons/quit"), tr("E&xit"), this); - quitAction->setToolTip(tr("Quit application")); + quitAction->setStatusTip(tr("Quit application")); quitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q)); quitAction->setMenuRole(QAction::QuitRole); aboutAction = new QAction(QIcon(":/icons/bitcoin"), tr("&About Bitcoin"), this); - aboutAction->setToolTip(tr("Show information about Bitcoin")); + aboutAction->setStatusTip(tr("Show information about Bitcoin")); aboutAction->setMenuRole(QAction::AboutRole); aboutQtAction = new QAction(QIcon(":/trolltech/qmessagebox/images/qtlogo-64.png"), tr("About &Qt"), this); - aboutQtAction->setToolTip(tr("Show information about Qt")); + aboutQtAction->setStatusTip(tr("Show information about Qt")); aboutQtAction->setMenuRole(QAction::AboutQtRole); optionsAction = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); - optionsAction->setToolTip(tr("Modify configuration options for Bitcoin")); + optionsAction->setStatusTip(tr("Modify configuration options for Bitcoin")); optionsAction->setMenuRole(QAction::PreferencesRole); toggleHideAction = new QAction(QIcon(":/icons/bitcoin"), tr("&Show / Hide"), this); + toggleHideAction->setStatusTip(tr("Show or hide the main Window")); encryptWalletAction = new QAction(QIcon(":/icons/lock_closed"), tr("&Encrypt Wallet..."), this); - encryptWalletAction->setToolTip(tr("Encrypt or decrypt wallet")); + encryptWalletAction->setStatusTip(tr("Encrypt the private keys that belong to your wallet")); encryptWalletAction->setCheckable(true); backupWalletAction = new QAction(QIcon(":/icons/filesave"), tr("&Backup Wallet..."), this); - backupWalletAction->setToolTip(tr("Backup wallet to another location")); + backupWalletAction->setStatusTip(tr("Backup wallet to another location")); changePassphraseAction = new QAction(QIcon(":/icons/key"), tr("&Change Passphrase..."), this); - changePassphraseAction->setToolTip(tr("Change the passphrase used for wallet encryption")); + changePassphraseAction->setStatusTip(tr("Change the passphrase used for wallet encryption")); signMessageAction = new QAction(QIcon(":/icons/edit"), tr("Sign &message..."), this); + signMessageAction->setStatusTip(tr("Sign messages with your Bitcoin addresses to prove you own them")); verifyMessageAction = new QAction(QIcon(":/icons/transaction_0"), tr("&Verify message..."), this); + verifyMessageAction->setStatusTip(tr("Verify messages to ensure they were signed with specified Bitcoin addresses")); exportAction = new QAction(QIcon(":/icons/export"), tr("&Export..."), this); - exportAction->setToolTip(tr("Export the data in the current tab to a file")); + exportAction->setStatusTip(tr("Export the data in the current tab to a file")); + exportAction->setToolTip(exportAction->statusTip()); openRPCConsoleAction = new QAction(QIcon(":/icons/debugwindow"), tr("&Debug window"), this); - openRPCConsoleAction->setToolTip(tr("Open debugging and diagnostic console")); + openRPCConsoleAction->setStatusTip(tr("Open debugging and diagnostic console")); connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); connect(aboutAction, SIGNAL(triggered()), this, SLOT(aboutClicked())); @@ -338,14 +351,20 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel) #endif if(trayIcon) { - trayIcon->setToolTip(tr("Bitcoin client") + QString(" ") + tr("[testnet]")); + // Just attach " [testnet]" to the existing tooltip + trayIcon->setToolTip(trayIcon->toolTip() + QString(" ") + tr("[testnet]")); trayIcon->setIcon(QIcon(":/icons/toolbar_testnet")); - toggleHideAction->setIcon(QIcon(":/icons/toolbar_testnet")); } + toggleHideAction->setIcon(QIcon(":/icons/toolbar_testnet")); aboutAction->setIcon(QIcon(":/icons/toolbar_testnet")); } + // Create system tray menu (or setup the dock menu) that late to prevent users from calling actions, + // while the client has not yet fully loaded + if(trayIcon) + createTrayIconMenu(); + // Keep up to date with client setNumConnections(clientModel->getNumConnections()); connect(clientModel, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int))); @@ -353,9 +372,10 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel) setNumBlocks(clientModel->getNumBlocks(), clientModel->getNumBlocksOfPeers()); connect(clientModel, SIGNAL(numBlocksChanged(int,int)), this, SLOT(setNumBlocks(int,int))); - // Report errors from network/worker thread - connect(clientModel, SIGNAL(error(QString,QString,bool)), this, SLOT(error(QString,QString,bool))); + // Receive and report messages from network/worker thread + connect(clientModel, SIGNAL(message(QString,QString,unsigned int)), this, SLOT(message(QString,QString,unsigned int))); + overviewPage->setClientModel(clientModel); rpcConsole->setClientModel(clientModel); addressBookPage->setOptionsModel(clientModel->getOptionsModel()); receiveCoinsPage->setOptionsModel(clientModel->getOptionsModel()); @@ -367,13 +387,12 @@ void BitcoinGUI::setWalletModel(WalletModel *walletModel) this->walletModel = walletModel; if(walletModel) { - // Report errors from wallet thread - connect(walletModel, SIGNAL(error(QString,QString,bool)), this, SLOT(error(QString,QString,bool))); + // Receive and report messages from wallet thread + connect(walletModel, SIGNAL(message(QString,QString,unsigned int)), this, SLOT(message(QString,QString,unsigned int))); // Put transaction list in tabs transactionView->setModel(walletModel); - - overviewPage->setModel(walletModel); + overviewPage->setWalletModel(walletModel); addressBookPage->setModel(walletModel->getAddressTableModel()); receiveCoinsPage->setModel(walletModel->getAddressTableModel()); sendCoinsPage->setModel(walletModel); @@ -393,16 +412,26 @@ void BitcoinGUI::setWalletModel(WalletModel *walletModel) void BitcoinGUI::createTrayIcon() { - QMenu *trayIconMenu; #ifndef Q_OS_MAC trayIcon = new QSystemTrayIcon(this); - trayIconMenu = new QMenu(this); - trayIcon->setContextMenu(trayIconMenu); + trayIcon->setToolTip(tr("Bitcoin client")); trayIcon->setIcon(QIcon(":/icons/toolbar")); + trayIcon->show(); +#endif + + notificator = new Notificator(qApp->applicationName(), trayIcon); +} + +void BitcoinGUI::createTrayIconMenu() +{ + QMenu *trayIconMenu; +#ifndef Q_OS_MAC + trayIconMenu = new QMenu(this); + trayIcon->setContextMenu(trayIconMenu); + connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason))); - trayIcon->show(); #else // Note: On Mac, the dock icon is used to provide the tray's functionality. MacDockIconHandler *dockIconHandler = MacDockIconHandler::instance(); @@ -424,8 +453,6 @@ void BitcoinGUI::createTrayIcon() trayIconMenu->addSeparator(); trayIconMenu->addAction(quitAction); #endif - - notificator = new Notificator(qApp->applicationName(), trayIcon); } #ifndef Q_OS_MAC @@ -472,8 +499,12 @@ void BitcoinGUI::setNumConnections(int count) void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks) { + // Prevent orphan statusbar messages (e.g. hover Quit in main menu, wait until chain-sync starts -> garbelled text) + statusBar()->clearMessage(); + // don't show / hide progress bar and its label if we have no connection to the network - if (!clientModel || (clientModel->getNumConnections() == 0 && !clientModel->isImporting())) + enum BlockSource blockSource = clientModel ? clientModel->getBlockSource() : BLOCK_SOURCE_NONE; + if (blockSource == BLOCK_SOURCE_NONE || (blockSource == BLOCK_SOURCE_NETWORK && clientModel->getNumConnections() == 0)) { progressBarLabel->setVisible(false); progressBar->setVisible(false); @@ -481,41 +512,41 @@ void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks) return; } - QString strStatusBarWarnings = clientModel->getStatusBarWarnings(); QString tooltip; + QString importText; + switch (blockSource) { + case BLOCK_SOURCE_NONE: + case BLOCK_SOURCE_NETWORK: + importText = tr("Synchronizing with network..."); + break; + case BLOCK_SOURCE_DISK: + importText = tr("Importing blocks from disk..."); + break; + case BLOCK_SOURCE_REINDEX: + importText = tr("Reindexing blocks on disk..."); + } + if(count < nTotalBlocks) { int nRemainingBlocks = nTotalBlocks - count; float nPercentageDone = count / (nTotalBlocks * 0.01f); - if (strStatusBarWarnings.isEmpty()) - { - progressBarLabel->setText(tr(clientModel->isImporting() ? "Importing blocks..." : "Synchronizing with network...")); - progressBarLabel->setVisible(true); - progressBar->setFormat(tr("~%n block(s) remaining", "", nRemainingBlocks)); - progressBar->setMaximum(nTotalBlocks); - progressBar->setValue(count); - progressBar->setVisible(true); - } + progressBarLabel->setText(importText); + progressBarLabel->setVisible(true); + progressBar->setFormat(tr("~%n block(s) remaining", "", nRemainingBlocks)); + progressBar->setMaximum(nTotalBlocks); + progressBar->setValue(count); + progressBar->setVisible(true); - tooltip = tr("Downloaded %1 of %2 blocks of transaction history (%3% done).").arg(count).arg(nTotalBlocks).arg(nPercentageDone, 0, 'f', 2); + tooltip = tr("Processed %1 of %2 blocks of transaction history (%3% done).").arg(count).arg(nTotalBlocks).arg(nPercentageDone, 0, 'f', 2); } else { - if (strStatusBarWarnings.isEmpty()) - progressBarLabel->setVisible(false); + progressBarLabel->setVisible(false); progressBar->setVisible(false); - tooltip = tr("Downloaded %1 blocks of transaction history.").arg(count); - } - - // Override progressBarLabel text and hide progress bar, when we have warnings to display - if (!strStatusBarWarnings.isEmpty()) - { - progressBarLabel->setText(strStatusBarWarnings); - progressBarLabel->setVisible(true); - progressBar->setVisible(false); + tooltip = tr("Processed %1 blocks of transaction history.").arg(count); } QDateTime lastBlockDate = clientModel->getLastBlockDate(); @@ -575,15 +606,50 @@ void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks) progressBar->setToolTip(tooltip); } -void BitcoinGUI::error(const QString &title, const QString &message, bool modal) +void BitcoinGUI::message(const QString &title, const QString &message, unsigned int style) { - // Report errors from network/worker thread - if(modal) - { - QMessageBox::critical(this, title, message, QMessageBox::Ok, QMessageBox::Ok); - } else { - notificator->notify(Notificator::Critical, title, message); + QString strTitle = tr("Bitcoin") + " - "; + // Default to information icon + int nMBoxIcon = QMessageBox::Information; + int nNotifyIcon = Notificator::Information; + + // Check for usage of predefined title + switch (style) { + case CClientUIInterface::MSG_ERROR: + strTitle += tr("Error"); + break; + case CClientUIInterface::MSG_WARNING: + strTitle += tr("Warning"); + break; + case CClientUIInterface::MSG_INFORMATION: + strTitle += tr("Information"); + break; + default: + strTitle += title; // Use supplied title + } + + // Check for error/warning icon + if (style & CClientUIInterface::ICON_ERROR) { + nMBoxIcon = QMessageBox::Critical; + nNotifyIcon = Notificator::Critical; + } + else if (style & CClientUIInterface::ICON_WARNING) { + nMBoxIcon = QMessageBox::Warning; + nNotifyIcon = Notificator::Warning; + } + + // Display message + if (style & CClientUIInterface::MODAL) { + // Check for buttons, use OK as default, if none was supplied + QMessageBox::StandardButton buttons; + if (!(buttons = (QMessageBox::StandardButton)(style & CClientUIInterface::BTN_MASK))) + buttons = QMessageBox::Ok; + + QMessageBox mBox((QMessageBox::Icon)nMBoxIcon, strTitle, message, buttons); + mBox.exec(); } + else + notificator->notify((Notificator::Class)nNotifyIcon, strTitle, message); } void BitcoinGUI::changeEvent(QEvent *e) @@ -622,11 +688,9 @@ void BitcoinGUI::closeEvent(QCloseEvent *event) void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee) { - QString strMessage = - tr("This transaction is over the size limit. You can still send it for a fee of %1, " - "which goes to the nodes that process your transaction and helps to support the network. " - "Do you want to pay the fee?").arg( - BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nFeeRequired)); + 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(BitcoinUnits::BTC, nFeeRequired)); QMessageBox::StandardButton retval = QMessageBox::question( this, tr("Confirm transaction fee"), strMessage, QMessageBox::Yes|QMessageBox::Cancel, QMessageBox::Yes); @@ -763,6 +827,18 @@ void BitcoinGUI::dropEvent(QDropEvent *event) event->acceptProposedAction(); } +bool BitcoinGUI::eventFilter(QObject *object, QEvent *event) +{ + // Catch status tip events + if (event->type() == QEvent::StatusTip) + { + // Prevent adding text from setStatusTip(), if we currently use the status bar for displaying other stuff + if (progressBarLabel->isVisible() && progressBar->isVisible()) + return true; + } + return QMainWindow::eventFilter(object, event); +} + void BitcoinGUI::handleURI(QString strURI) { // URI has to be valid diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index c67e887c0f..3faf6d948c 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -52,6 +52,7 @@ protected: void closeEvent(QCloseEvent *event); void dragEnterEvent(QDragEnterEvent *event); void dropEvent(QDropEvent *event); + bool eventFilter(QObject *object, QEvent *event); private: ClientModel *clientModel; @@ -104,8 +105,10 @@ private: void createMenuBar(); /** Create the toolbars */ void createToolBars(); - /** Create system tray (notification) icon */ + /** Create system tray icon and notification */ void createTrayIcon(); + /** Create system tray menu (or setup the dock menu) */ + void createTrayIconMenu(); public slots: /** Set number of connections shown in the UI */ @@ -118,8 +121,13 @@ public slots: */ void setEncryptionStatus(int status); - /** Notify the user of an error in the network or transaction handling code. */ - void error(const QString &title, const QString &message, bool modal); + /** Notify the user of an event from the core network or transaction handling code. + @param[in] title the message box / notification title + @param[in] message the displayed text + @param[in] style modality and style definitions (icon and used buttons - buttons only for message boxes) + @see CClientUIInterface::MessageBoxFlags + */ + void message(const QString &title, const QString &message, unsigned int style); /** 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. @@ -172,8 +180,8 @@ private slots: /** Show window if hidden, unminimize when minimized, rise when obscured or show if hidden and fToggleHidden is true */ void showNormalIfMinimized(bool fToggleHidden = false); - /** simply calls showNormalIfMinimized(true) for use in SLOT() macro */ + /** Simply calls showNormalIfMinimized(true) for use in SLOT() macro */ void toggleHidden(); }; -#endif +#endif // BITCOINGUI_H diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp index b92a26c4a4..497c05976b 100644 --- a/src/qt/bitcoinstrings.cpp +++ b/src/qt/bitcoinstrings.cpp @@ -13,28 +13,33 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "rpcuser=bitcoinrpc\n" "rpcpassword=%s\n" "(you do not need to remember this password)\n" +"The username and password MUST NOT be the same.\n" "If the file does not exist, create it with owner-readable-only file " "permissions.\n"), QT_TRANSLATE_NOOP("bitcoin-core", "" "Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:" "@STRENGTH)"), QT_TRANSLATE_NOOP("bitcoin-core", "" +"An error occurred while setting up the RPC port %u for listening on IPv4: %s"), +QT_TRANSLATE_NOOP("bitcoin-core", "" "An error occurred while setting up the RPC port %u for listening on IPv6, " "falling back to IPv4: %s"), QT_TRANSLATE_NOOP("bitcoin-core", "" -"An error occurred while setting up the RPC port %u for listening on IPv4: %s"), +"Bind to given address and always listen on it. Use [host]:port notation for " +"IPv6"), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Cannot obtain a lock on data directory %s. Bitcoin is probably already " +"Cannot obtain a lock on data directory %s. Bitcoin is probably already " "running."), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Detach block and address databases. Increases shutdown time (default: 0)"), +"Error initializing database environment %s! To recover, BACKUP THAT " +"DIRECTORY, then remove everything from it except for wallet.dat."), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Error: The transaction was rejected. This might happen if some of the coins " +"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."), QT_TRANSLATE_NOOP("bitcoin-core", "" "Error: This transaction requires a transaction fee of at least %s because of " -"its amount, complexity, or use of recently received funds "), +"its amount, complexity, or use of recently received funds!"), QT_TRANSLATE_NOOP("bitcoin-core", "" "Execute command when the best block changes (%s in cmd is replaced by block " "hash)"), @@ -47,6 +52,9 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "Set maximum size of high-priority/low-fee transactions in bytes (default: " "27000)"), QT_TRANSLATE_NOOP("bitcoin-core", "" +"This is a pre-release test build - use at your own risk - do not use for " +"mining or merchant applications"), +QT_TRANSLATE_NOOP("bitcoin-core", "" "Unable to bind to %s on this computer. Bitcoin is probably already running."), QT_TRANSLATE_NOOP("bitcoin-core", "" "Warning: -paytxfee is set very high! This is the transaction fee you will " @@ -58,6 +66,13 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "Warning: Please check that your computer's date and time are correct! If " "your clock is wrong Bitcoin will not work properly."), QT_TRANSLATE_NOOP("bitcoin-core", "" +"Warning: error reading wallet.dat! All keys read correctly, but transaction " +"data or address book entries might be missing or incorrect."), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"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."), +QT_TRANSLATE_NOOP("bitcoin-core", "" "You must set rpcpassword=<password> in the configuration file:\n" "%s\n" "If the file does not exist, create it with owner-readable-only file " @@ -67,7 +82,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Accept connections from outside (default: 1 i 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", "Bind to given address. Use [host]:port notation for IPv6"), +QT_TRANSLATE_NOOP("bitcoin-core", "Attempt to recover private keys from a corrupt wallet.dat"), QT_TRANSLATE_NOOP("bitcoin-core", "Bitcoin version"), QT_TRANSLATE_NOOP("bitcoin-core", "Bitcoin"), QT_TRANSLATE_NOOP("bitcoin-core", "Block creation options:"), @@ -87,8 +102,8 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Error loading wallet.dat"), QT_TRANSLATE_NOOP("bitcoin-core", "Error loading wallet.dat: Wallet corrupted"), QT_TRANSLATE_NOOP("bitcoin-core", "Error loading wallet.dat: Wallet requires newer version of Bitcoin"), QT_TRANSLATE_NOOP("bitcoin-core", "Error"), -QT_TRANSLATE_NOOP("bitcoin-core", "Error: Transaction creation failed "), -QT_TRANSLATE_NOOP("bitcoin-core", "Error: Wallet locked, unable to create transaction "), +QT_TRANSLATE_NOOP("bitcoin-core", "Error: Transaction creation failed!"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error: Wallet locked, unable to create transaction!"), QT_TRANSLATE_NOOP("bitcoin-core", "Error: could not start node"), QT_TRANSLATE_NOOP("bitcoin-core", "Failed to listen on any port. Use -listen=0 if you want this."), QT_TRANSLATE_NOOP("bitcoin-core", "Fee per KB to add to transactions you send"), @@ -98,8 +113,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Generate coins"), QT_TRANSLATE_NOOP("bitcoin-core", "Get help for a command"), QT_TRANSLATE_NOOP("bitcoin-core", "How many blocks to check at startup (default: 2500, 0 = all)"), QT_TRANSLATE_NOOP("bitcoin-core", "How thorough the block verification is (0-6, default: 1)"), -QT_TRANSLATE_NOOP("bitcoin-core", "Importing blockchain data file."), -QT_TRANSLATE_NOOP("bitcoin-core", "Importing bootstrap blockchain data file."), +QT_TRANSLATE_NOOP("bitcoin-core", "Importing blocks from block database..."), QT_TRANSLATE_NOOP("bitcoin-core", "Imports blocks from external blk000?.dat file"), QT_TRANSLATE_NOOP("bitcoin-core", "Insufficient funds"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -proxy address: '%s'"), @@ -120,6 +134,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Output extra debugging information. Implies a 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", "Rebuild blockchain 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"), @@ -133,7 +148,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Sending..."), 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 database disk log size in megabytes (default: 100)"), 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 minimum block size in bytes (default: 0)"), @@ -157,7 +171,9 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Use UPnP to map the listening port (default: 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 database integrity..."), QT_TRANSLATE_NOOP("bitcoin-core", "Wallet needed to be rewritten: restart Bitcoin to complete"), QT_TRANSLATE_NOOP("bitcoin-core", "Warning: Disk space is low!"), QT_TRANSLATE_NOOP("bitcoin-core", "Warning: This version is obsolete, upgrade required!"), +QT_TRANSLATE_NOOP("bitcoin-core", "wallet.dat corrupt, salvage failed"), };
\ No newline at end of file diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 990b364a94..ce112803f8 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -84,13 +84,11 @@ void ClientModel::updateAlert(const QString &hash, int status) CAlert alert = CAlert::getAlertByHash(hash_256); if(!alert.IsNull()) { - emit error(tr("Network Alert"), QString::fromStdString(alert.strStatusBar), false); + emit message(tr("Network Alert"), QString::fromStdString(alert.strStatusBar), CClientUIInterface::ICON_ERROR); } } - // Emit a numBlocksChanged when the status message changes, - // so that the view recomputes and updates the status bar. - emit numBlocksChanged(getNumBlocks(), getNumBlocksOfPeers()); + emit alertsChanged(getStatusBarWarnings()); } bool ClientModel::isTestNet() const @@ -103,9 +101,13 @@ bool ClientModel::inInitialBlockDownload() const return IsInitialBlockDownload(); } -bool ClientModel::isImporting() const +enum BlockSource ClientModel::getBlockSource() const { - return fImporting; + if (fReindex) + return BLOCK_SOURCE_REINDEX; + if (fImporting) + return BLOCK_SOURCE_DISK; + return BLOCK_SOURCE_NETWORK; } int ClientModel::getNumBlocksOfPeers() const @@ -133,6 +135,11 @@ QString ClientModel::formatBuildDate() const return QString::fromStdString(CLIENT_DATE); } +bool ClientModel::isReleaseVersion() const +{ + return CLIENT_VERSION_IS_RELEASE; +} + QString ClientModel::clientName() const { return QString::fromStdString(CLIENT_NAME); diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 926390a07a..1afccb7859 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -13,6 +13,13 @@ class QDateTime; class QTimer; QT_END_NAMESPACE +enum BlockSource { + BLOCK_SOURCE_NONE, + BLOCK_SOURCE_NETWORK, + BLOCK_SOURCE_DISK, + BLOCK_SOURCE_REINDEX +}; + /** Model for Bitcoin network client. */ class ClientModel : public QObject { @@ -34,7 +41,7 @@ public: //! Return true if core is doing initial block download bool inInitialBlockDownload() const; //! Return true if core is importing blocks - bool isImporting() const; + enum BlockSource getBlockSource() const; //! Return conservative estimate of total number of blocks, or 0 if unknown int getNumBlocksOfPeers() const; //! Return warnings to be displayed in status bar @@ -42,6 +49,7 @@ public: QString formatFullVersion() const; QString formatBuildDate() const; + bool isReleaseVersion() const; QString clientName() const; QString formatClientStartupTime() const; @@ -60,9 +68,10 @@ private: signals: void numConnectionsChanged(int count); void numBlocksChanged(int count, int countOfPeers); + void alertsChanged(const QString &warnings); - //! Asynchronous error notification - void error(const QString &title, const QString &message, bool modal); + //! Asynchronous message notification + void message(const QString &title, const QString &message, unsigned int style); public slots: void updateTimer(); diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 1b81b0cdc8..6a13361974 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -87,16 +87,6 @@ </widget> </item> <item> - <widget class="QCheckBox" name="detachDatabases"> - <property name="toolTip"> - <string>Detach block and address databases at shutdown. This means they can be moved to another data directory, but it slows down shutdown. The wallet is always detached.</string> - </property> - <property name="text"> - <string>&Detach databases at shutdown</string> - </property> - </widget> - </item> - <item> <spacer name="verticalSpacer_Main"> <property name="orientation"> <enum>Qt::Vertical</enum> diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui index 98cb63e9ff..4c4dec6c9c 100644 --- a/src/qt/forms/overviewpage.ui +++ b/src/qt/forms/overviewpage.ui @@ -13,282 +13,303 @@ <property name="windowTitle"> <string>Form</string> </property> - <layout class="QHBoxLayout" name="horizontalLayout" stretch="1,1"> + <layout class="QVBoxLayout" name="topLayout"> <item> - <layout class="QVBoxLayout" name="verticalLayout_2"> + <widget class="QLabel" name="labelAlerts"> + <property name="visible"> + <bool>false</bool> + </property> + <property name="styleSheet"> + <string notr="true">background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop:0 #F0D0A0, stop:1 #F8D488); color:#000000 +</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + <property name="margin"> + <number>3</number> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout" stretch="1,1"> <item> - <widget class="QFrame" name="frame"> - <property name="frameShape"> - <enum>QFrame::StyledPanel</enum> - </property> - <property name="frameShadow"> - <enum>QFrame::Raised</enum> - </property> - <layout class="QVBoxLayout" name="verticalLayout_4"> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_4"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QFrame" name="frame"> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <layout class="QVBoxLayout" name="verticalLayout_4"> <item> - <widget class="QLabel" name="label_5"> - <property name="font"> - <font> - <pointsize>11</pointsize> - <weight>75</weight> - <bold>true</bold> - </font> - </property> - <property name="text"> - <string>Wallet</string> - </property> - </widget> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <widget class="QLabel" name="label_5"> + <property name="font"> + <font> + <pointsize>11</pointsize> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>Wallet</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="labelWalletStatus"> + <property name="toolTip"> + <string>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.</string> + </property> + <property name="styleSheet"> + <string notr="true">QLabel { color: red; }</string> + </property> + <property name="text"> + <string notr="true">(out of sync)</string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </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> <item> - <widget class="QLabel" name="labelWalletStatus"> - <property name="toolTip"> - <string>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.</string> - </property> - <property name="styleSheet"> - <string notr="true">QLabel { color: red; }</string> - </property> - <property name="text"> - <string notr="true">(out of sync)</string> - </property> - <property name="alignment"> - <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> - </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> - <item> - <layout class="QFormLayout" name="formLayout_2"> - <property name="fieldGrowthPolicy"> - <enum>QFormLayout::AllNonFixedFieldsGrow</enum> - </property> - <property name="horizontalSpacing"> - <number>12</number> - </property> - <property name="verticalSpacing"> - <number>12</number> - </property> - <item row="0" column="0"> - <widget class="QLabel" name="label"> - <property name="text"> - <string>Balance:</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QLabel" name="labelBalance"> - <property name="font"> - <font> - <weight>75</weight> - <bold>true</bold> - </font> - </property> - <property name="cursor"> - <cursorShape>IBeamCursor</cursorShape> - </property> - <property name="toolTip"> - <string>Your current balance</string> - </property> - <property name="text"> - <string notr="true">0 BTC</string> - </property> - <property name="textInteractionFlags"> - <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> - </property> - </widget> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="label_3"> - <property name="text"> - <string>Unconfirmed:</string> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QLabel" name="labelUnconfirmed"> - <property name="font"> - <font> - <weight>75</weight> - <bold>true</bold> - </font> - </property> - <property name="cursor"> - <cursorShape>IBeamCursor</cursorShape> - </property> - <property name="toolTip"> - <string>Total of transactions that have yet to be confirmed, and do not yet count toward the current balance</string> - </property> - <property name="text"> - <string notr="true">0 BTC</string> - </property> - <property name="textInteractionFlags"> - <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> - </property> - </widget> - </item> - <item row="3" column="0"> - <widget class="QLabel" name="label_2"> - <property name="text"> - <string>Number of transactions:</string> - </property> - </widget> - </item> - <item row="3" column="1"> - <widget class="QLabel" name="labelNumTransactions"> - <property name="toolTip"> - <string>Total number of transactions in wallet</string> - </property> - <property name="text"> - <string notr="true">0</string> - </property> - </widget> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="labelImmatureText"> - <property name="text"> - <string>Immature:</string> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="QLabel" name="labelImmature"> - <property name="font"> - <font> - <weight>75</weight> - <bold>true</bold> - </font> - </property> - <property name="toolTip"> - <string>Mined balance that has not yet matured</string> - </property> - <property name="text"> - <string notr="true">0 BTC</string> - </property> - <property name="textInteractionFlags"> - <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> - </property> - </widget> + <layout class="QFormLayout" name="formLayout_2"> + <property name="fieldGrowthPolicy"> + <enum>QFormLayout::AllNonFixedFieldsGrow</enum> + </property> + <property name="horizontalSpacing"> + <number>12</number> + </property> + <property name="verticalSpacing"> + <number>12</number> + </property> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Balance:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLabel" name="labelBalance"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="cursor"> + <cursorShape>IBeamCursor</cursorShape> + </property> + <property name="toolTip"> + <string>Your current balance</string> + </property> + <property name="text"> + <string notr="true">0 BTC</string> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Unconfirmed:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="labelUnconfirmed"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="cursor"> + <cursorShape>IBeamCursor</cursorShape> + </property> + <property name="toolTip"> + <string>Total of transactions that have yet to be confirmed, and do not yet count toward the current balance</string> + </property> + <property name="text"> + <string notr="true">0 BTC</string> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Number of transactions:</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QLabel" name="labelNumTransactions"> + <property name="toolTip"> + <string>Total number of transactions in wallet</string> + </property> + <property name="text"> + <string notr="true">0</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="labelImmatureText"> + <property name="text"> + <string>Immature:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLabel" name="labelImmature"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="toolTip"> + <string>Mined balance that has not yet matured</string> + </property> + <property name="text"> + <string notr="true">0 BTC</string> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + </layout> </item> </layout> - </item> - </layout> - </widget> - </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> + </widget> + </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> + </layout> </item> - </layout> - </item> - <item> - <layout class="QVBoxLayout" name="verticalLayout_3"> <item> - <widget class="QFrame" name="frame_2"> - <property name="frameShape"> - <enum>QFrame::StyledPanel</enum> - </property> - <property name="frameShadow"> - <enum>QFrame::Raised</enum> - </property> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_2"> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QFrame" name="frame_2"> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> <item> - <widget class="QLabel" name="label_4"> - <property name="text"> - <string><b>Recent transactions</b></string> - </property> - </widget> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string><b>Recent transactions</b></string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="labelTransactionsStatus"> + <property name="toolTip"> + <string>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.</string> + </property> + <property name="styleSheet"> + <string notr="true">QLabel { color: red; }</string> + </property> + <property name="text"> + <string notr="true">(out of sync)</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> </item> <item> - <widget class="QLabel" name="labelTransactionsStatus"> - <property name="toolTip"> - <string>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.</string> - </property> + <widget class="QListView" name="listTransactions"> <property name="styleSheet"> - <string notr="true">QLabel { color: red; }</string> + <string notr="true">QListView { background: transparent; }</string> </property> - <property name="text"> - <string notr="true">(out of sync)</string> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + <property name="verticalScrollBarPolicy"> + <enum>Qt::ScrollBarAlwaysOff</enum> </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> + <property name="horizontalScrollBarPolicy"> + <enum>Qt::ScrollBarAlwaysOff</enum> </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> + <property name="selectionMode"> + <enum>QAbstractItemView::NoSelection</enum> </property> - </spacer> + </widget> </item> </layout> - </item> - <item> - <widget class="QListView" name="listTransactions"> - <property name="styleSheet"> - <string notr="true">QListView { background: transparent; }</string> - </property> - <property name="frameShape"> - <enum>QFrame::NoFrame</enum> - </property> - <property name="verticalScrollBarPolicy"> - <enum>Qt::ScrollBarAlwaysOff</enum> - </property> - <property name="horizontalScrollBarPolicy"> - <enum>Qt::ScrollBarAlwaysOff</enum> - </property> - <property name="selectionMode"> - <enum>QAbstractItemView::NoSelection</enum> - </property> - </widget> - </item> - </layout> - </widget> - </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> + </widget> + </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> + </layout> </item> </layout> </item> diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui index 22a3f8fdc6..28553c5508 100644 --- a/src/qt/forms/sendcoinsentry.ui +++ b/src/qt/forms/sendcoinsentry.ui @@ -52,23 +52,6 @@ <item row="5" column="1"> <widget class="BitcoinAmountField" name="payAmount"/> </item> - <item row="4" column="1"> - <layout class="QHBoxLayout" name="horizontalLayout_2"> - <property name="spacing"> - <number>0</number> - </property> - <item> - <widget class="QValidatedLineEdit" name="addAsLabel"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="toolTip"> - <string>Enter a label for this address to add it to your address book</string> - </property> - </widget> - </item> - </layout> - </item> <item row="4" column="0"> <widget class="QLabel" name="label_4"> <property name="text"> @@ -90,7 +73,7 @@ <item> <widget class="QValidatedLineEdit" name="payTo"> <property name="toolTip"> - <string>The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</string> + <string>The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</string> </property> <property name="maxLength"> <number>34</number> @@ -147,6 +130,13 @@ </item> </layout> </item> + <item row="4" column="1"> + <widget class="QValidatedLineEdit" name="addAsLabel"> + <property name="toolTip"> + <string>Enter a label for this address to add it to your address book</string> + </property> + </widget> + </item> </layout> </widget> <customwidgets> diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts index ee9967e15d..c70ea652de 100644 --- a/src/qt/locale/bitcoin_en.ts +++ b/src/qt/locale/bitcoin_en.ts @@ -300,17 +300,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="+257"/> + <location filename="../bitcoingui.cpp" line="+266"/> <source>Sign &message...</source> <translation>Sign &message...</translation> </message> <message> - <location line="+237"/> + <location line="+241"/> <source>Synchronizing with network...</source> <translation>Synchronizing with network...</translation> </message> <message> - <location line="-299"/> + <location line="-309"/> <source>&Overview</source> <translation>&Overview</translation> </message> @@ -320,7 +320,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Show general overview of wallet</translation> </message> <message> - <location line="+17"/> + <location line="+20"/> <source>&Transactions</source> <translation>&Transactions</translation> </message> @@ -330,7 +330,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Browse transaction history</translation> </message> <message> - <location line="+5"/> + <location line="+6"/> <source>&Address Book</source> <translation>&Address Book</translation> </message> @@ -340,7 +340,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Edit the list of stored addresses and labels</translation> </message> <message> - <location line="-13"/> + <location line="-15"/> <source>&Receive coins</source> <translation>&Receive coins</translation> </message> @@ -350,12 +350,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Show the list of addresses for receiving payments</translation> </message> <message> - <location line="-7"/> + <location line="-8"/> <source>&Send coins</source> <translation>&Send coins</translation> </message> <message> - <location line="+35"/> + <location line="+39"/> <source>E&xit</source> <translation>E&xit</translation> </message> @@ -385,7 +385,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Options...</translation> </message> <message> - <location line="+4"/> + <location line="+5"/> <source>&Encrypt Wallet...</source> <translation>&Encrypt Wallet...</translation> </message> @@ -399,8 +399,18 @@ This product includes software developed by the OpenSSL Project for use in the O <source>&Change Passphrase...</source> <translation>&Change Passphrase...</translation> </message> + <message> + <location line="+246"/> + <source>Importing blocks from disk...</source> + <translation>Importing blocks from disk...</translation> + </message> + <message> + <location line="+3"/> + <source>Reindexing blocks on disk...</source> + <translation>Reindexing blocks on disk...</translation> + </message> <message numerus="yes"> - <location line="+241"/> + <location line="+10"/> <source>~%n block(s) remaining</source> <translation> <numerusform>~%n block remaining</numerusform> @@ -408,37 +418,27 @@ This product includes software developed by the OpenSSL Project for use in the O </translation> </message> <message> - <location line="+6"/> - <source>Downloaded %1 of %2 blocks of transaction history (%3% done).</source> - <translation>Downloaded %1 of %2 blocks of transaction history (%3% done).</translation> - </message> - <message> - <location line="-242"/> + <location line="-252"/> <source>&Export...</source> <translation>&Export...</translation> </message> <message> - <location line="-58"/> + <location line="-65"/> <source>Send coins to a Bitcoin address</source> <translation>Send coins to a Bitcoin address</translation> </message> <message> - <location line="+45"/> + <location line="+49"/> <source>Modify configuration options for Bitcoin</source> <translation>Modify configuration options for Bitcoin</translation> </message> <message> - <location line="+14"/> + <location line="+17"/> <source>Export the data in the current tab to a file</source> <translation>Export the data in the current tab to a file</translation> </message> <message> - <location line="-10"/> - <source>Encrypt or decrypt wallet</source> - <translation>Encrypt or decrypt wallet</translation> - </message> - <message> - <location line="+3"/> + <location line="-9"/> <source>Backup wallet to another location</source> <translation>Backup wallet to another location</translation> </message> @@ -448,7 +448,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Change the passphrase used for wallet encryption</translation> </message> <message> - <location line="+6"/> + <location line="+9"/> <source>&Debug window</source> <translation>&Debug window</translation> </message> @@ -458,12 +458,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Open debugging and diagnostic console</translation> </message> <message> - <location line="-5"/> + <location line="-7"/> <source>&Verify message...</source> <translation>&Verify message...</translation> </message> <message> - <location line="-186"/> + <location line="-196"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -473,7 +473,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Wallet</translation> </message> <message> - <location line="+168"/> + <location line="+176"/> <source>&About Bitcoin</source> <translation>&About Bitcoin</translation> </message> @@ -483,7 +483,27 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Show / Hide</translation> </message> <message> - <location line="+39"/> + <location line="+1"/> + <source>Show or hide the main Window</source> + <translation>Show or hide the main Window</translation> + </message> + <message> + <location line="+2"/> + <source>Encrypt the private keys that belong to your wallet</source> + <translation>Encrypt the private keys that belong to your wallet</translation> + </message> + <message> + <location line="+7"/> + <source>Sign messages with your Bitcoin addresses to prove you own them</source> + <translation>Sign messages with your Bitcoin addresses to prove you own them</translation> + </message> + <message> + <location line="+2"/> + <source>Verify messages to ensure they were signed with specified Bitcoin addresses</source> + <translation>Verify messages to ensure they were signed with specified Bitcoin addresses</translation> + </message> + <message> + <location line="+31"/> <source>&File</source> <translation>&File</translation> </message> @@ -509,12 +529,11 @@ This product includes software developed by the OpenSSL Project for use in the O </message> <message> <location line="+13"/> - <location line="+9"/> + <location line="+10"/> <source>[testnet]</source> <translation>[testnet]</translation> </message> <message> - <location line="+0"/> <location line="+60"/> <source>Bitcoin client</source> <translation>Bitcoin client</translation> @@ -528,12 +547,22 @@ This product includes software developed by the OpenSSL Project for use in the O </translation> </message> <message> - <location line="+40"/> - <source>Downloaded %1 blocks of transaction history.</source> - <translation>Downloaded %1 blocks of transaction history.</translation> + <location line="+45"/> + <source>Processed %1 of %2 blocks of transaction history (%3% done).</source> + <translation>Processed %1 of %2 blocks of transaction history (%3% done).</translation> + </message> + <message> + <location line="+7"/> + <source>Processed %1 blocks of transaction history.</source> + <translation>Processed %1 blocks of transaction history.</translation> + </message> + <message> + <location line="+107"/> + <source>This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee?</source> + <translation>This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee?</translation> </message> <message numerus="yes"> - <location line="+22"/> + <location line="-93"/> <source>%n second(s) ago</source> <translation> <numerusform>%n second ago</numerusform> @@ -580,12 +609,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Last received block was generated %1.</translation> </message> <message> - <location line="+59"/> - <source>This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee?</source> - <translation>This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee?</translation> - </message> - <message> - <location line="+5"/> + <location line="+62"/> <source>Confirm transaction fee</source> <translation>Confirm transaction fee</translation> </message> @@ -614,13 +638,13 @@ Address: %4 </message> <message> <location line="+100"/> - <location line="+15"/> + <location line="+27"/> <source>URI handling</source> <translation>URI handling</translation> </message> <message> - <location line="-15"/> - <location line="+15"/> + <location line="-27"/> + <location line="+27"/> <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> @@ -813,16 +837,6 @@ Address: %4 <translation>&Start Bitcoin on system login</translation> </message> <message> - <location line="+7"/> - <source>Detach block and address databases at shutdown. This means they can be moved to another data directory, but it slows down shutdown. The wallet is always detached.</source> - <translation>Detach block and address databases at shutdown. This means they can be moved to another data directory, but it slows down shutdown. The wallet is always detached.</translation> - </message> - <message> - <location line="+3"/> - <source>&Detach databases at shutdown</source> - <translation>&Detach databases at shutdown</translation> - </message> - <message> <location line="+21"/> <source>&Network</source> <translation>&Network</translation> @@ -958,7 +972,7 @@ Address: %4 <translation>default</translation> </message> <message> - <location line="+147"/> + <location line="+146"/> <location line="+9"/> <source>Warning</source> <translation>Warning</translation> @@ -983,7 +997,7 @@ Address: %4 <translation>Form</translation> </message> <message> - <location line="+33"/> + <location line="+52"/> <location line="+183"/> <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> @@ -1039,7 +1053,7 @@ Address: %4 <translation>Total number of transactions in wallet</translation> </message> <message> - <location filename="../overviewpage.cpp" line="+112"/> + <location filename="../overviewpage.cpp" line="+115"/> <location line="+1"/> <source>out of sync</source> <translation>out of sync</translation> @@ -1120,7 +1134,7 @@ Address: %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+348"/> + <location filename="../rpcconsole.cpp" line="+349"/> <source>N/A</source> <translation>N/A</translation> </message> @@ -1351,8 +1365,8 @@ Address: %4 </message> <message> <location line="+5"/> - <source>Error: Transaction creation failed.</source> - <translation>Error: Transaction creation failed.</translation> + <source>Error: Transaction creation failed!</source> + <translation>Error: Transaction creation failed!</translation> </message> <message> <location line="+5"/> @@ -1378,23 +1392,23 @@ Address: %4 <translation>Pay &To:</translation> </message> <message> - <location line="+24"/> + <location line="+34"/> + <source>The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</source> + <translation>The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</translation> + </message> + <message> + <location line="+60"/> <location filename="../sendcoinsentry.cpp" line="+25"/> <source>Enter a label for this address to add it to your address book</source> <translation>Enter a label for this address to add it to your address book</translation> </message> <message> - <location line="+9"/> + <location line="-78"/> <source>&Label:</source> <translation>&Label:</translation> </message> <message> - <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 line="+10"/> + <location line="+28"/> <source>Choose address from address book</source> <translation>Choose address from address book</translation> </message> @@ -1757,7 +1771,7 @@ Address: %4 <translation>Transaction</translation> </message> <message> - <location line="+5"/> + <location line="+3"/> <source>Inputs</source> <translation>Inputs</translation> </message> @@ -1777,7 +1791,7 @@ Address: %4 <translation>false</translation> </message> <message> - <location line="-211"/> + <location line="-209"/> <source>, has not been successfully broadcast yet</source> <translation>, has not been successfully broadcast yet</translation> </message> @@ -2099,32 +2113,32 @@ Address: %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+71"/> + <location filename="../bitcoinstrings.cpp" line="+86"/> <source>Bitcoin version</source> <translation>Bitcoin version</translation> </message> <message> - <location line="+82"/> + <location line="+81"/> <source>Usage:</source> <translation>Usage:</translation> </message> <message> - <location line="-25"/> + <location line="-24"/> <source>Send command to -server or bitcoind</source> <translation>Send command to -server or bitcoind</translation> </message> <message> - <location line="-19"/> + <location line="-20"/> <source>List commands</source> <translation>List commands</translation> </message> <message> - <location line="-11"/> + <location line="-10"/> <source>Get help for a command</source> <translation>Get help for a command</translation> </message> <message> - <location line="+20"/> + <location line="+19"/> <source>Options:</source> <translation>Options:</translation> </message> @@ -2139,7 +2153,7 @@ Address: %4 <translation>Specify pid file (default: bitcoind.pid)</translation> </message> <message> - <location line="-47"/> + <location line="-46"/> <source>Generate coins</source> <translation>Generate coins</translation> </message> @@ -2149,21 +2163,16 @@ Address: %4 <translation>Don't generate coins</translation> </message> <message> - <location line="+60"/> + <location line="+59"/> <source>Specify data directory</source> <translation>Specify data directory</translation> </message> <message> - <location line="-8"/> + <location line="-7"/> <source>Set database cache size in megabytes (default: 25)</source> <translation>Set database cache size in megabytes (default: 25)</translation> </message> <message> - <location line="+1"/> - <source>Set database disk log size in megabytes (default: 100)</source> - <translation>Set database disk log size in megabytes (default: 100)</translation> - </message> - <message> <location line="-26"/> <source>Listen for connections on <port> (default: 8333 or testnet: 18333)</source> <translation>Listen for connections on <port> (default: 8333 or testnet: 18333)</translation> @@ -2174,86 +2183,116 @@ Address: %4 <translation>Maintain at most <n> connections to peers (default: 125)</translation> </message> <message> - <location line="-33"/> + <location line="-32"/> <source>Connect to a node to retrieve peer addresses, and disconnect</source> <translation>Connect to a node to retrieve peer addresses, and disconnect</translation> </message> <message> - <location line="+64"/> + <location line="+63"/> <source>Specify your own public address</source> <translation>Specify your own public address</translation> </message> <message> - <location line="-75"/> - <source>Bind to given address. Use [host]:port notation for IPv6</source> - <translation>Bind to given address. Use [host]:port notation for IPv6</translation> - </message> - <message> - <location line="+77"/> + <location line="+2"/> <source>Threshold for disconnecting misbehaving peers (default: 100)</source> <translation>Threshold for disconnecting misbehaving peers (default: 100)</translation> </message> <message> - <location line="-104"/> + <location line="-113"/> <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> <message> - <location line="-19"/> + <location line="-26"/> <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> <translation>An error occurred while setting up the RPC port %u for listening on IPv4: %s</translation> </message> <message> - <location line="+5"/> - <source>Detach block and address databases. Increases shutdown time (default: 0)</source> - <translation>Detach block and address databases. Increases shutdown time (default: 0)</translation> - </message> - <message> - <location line="+12"/> + <location line="+24"/> <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> <translation>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</translation> </message> <message> - <location line="+24"/> + <location line="+34"/> <source>Accept command line and JSON-RPC commands</source> <translation>Accept command line and JSON-RPC commands</translation> </message> <message> - <location line="+36"/> - <source>Importing blockchain data file.</source> - <translation>Importing blockchain data file.</translation> - </message> - <message> - <location line="+1"/> - <source>Importing bootstrap blockchain data file.</source> - <translation>Importing bootstrap blockchain data file.</translation> - </message> - <message> - <location line="+23"/> + <location line="+60"/> <source>Run in the background as a daemon and accept commands</source> <translation>Run in the background as a daemon and accept commands</translation> </message> <message> - <location line="+33"/> + <location line="+32"/> <source>Use the test network</source> <translation>Use the test network</translation> </message> <message> - <location line="-92"/> + <location line="-91"/> <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="-45"/> + <location line="-72"/> + <source>%s, you must set a rpcpassword in the configuration file: + %s +It is recommended you use the following random password: +rpcuser=bitcoinrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +</source> + <translation>%s, you must set a rpcpassword in the configuration file: + %s +It is recommended you use the following random password: +rpcuser=bitcoinrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions.</translation> + </message> + <message> + <location line="+15"/> <source>An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s</source> <translation>An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s</translation> </message> <message> - <location line="+25"/> + <location line="+3"/> + <source>Bind to given address and always listen on it. Use [host]:port notation for IPv6</source> + <translation>Bind to given address and always listen on it. Use [host]:port notation for IPv6</translation> + </message> + <message> + <location line="+3"/> + <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> + <translation>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</translation> + </message> + <message> + <location line="+3"/> + <source>Error initializing database environment %s! To recover, BACKUP THAT DIRECTORY, then remove everything from it except for wallet.dat.</source> + <translation>Error initializing database environment %s! To recover, BACKUP THAT DIRECTORY, then remove everything from it except for wallet.dat.</translation> + </message> + <message> + <location line="+3"/> + <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> + </message> + <message> + <location line="+4"/> + <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds!</source> + <translation>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds!</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="+3"/> + <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>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> @@ -2269,7 +2308,22 @@ Address: %4 <translation>Warning: Please check that your computer's date and time are correct! If your clock is wrong Bitcoin will not work properly.</translation> </message> <message> - <location line="+16"/> + <location line="+3"/> + <source>Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</source> + <translation>Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</translation> + </message> + <message> + <location line="+3"/> + <source>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.</source> + <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"/> + <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="+3"/> <source>Block creation options:</source> <translation>Block creation options:</translation> </message> @@ -2284,7 +2338,17 @@ Address: %4 <translation>Discover own IP address (default: 1 when listening and no -externalip)</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> + <source>Error: Transaction creation failed!</source> + <translation>Error: Transaction creation failed!</translation> + </message> + <message> + <location line="+1"/> + <source>Error: Wallet locked, unable to create transaction!</source> + <translation>Error: Wallet locked, unable to create transaction!</translation> + </message> + <message> + <location line="+2"/> <source>Failed to listen on any port. Use -listen=0 if you want this.</source> <translation>Failed to listen on any port. Use -listen=0 if you want this.</translation> </message> @@ -2294,7 +2358,12 @@ Address: %4 <translation>Find peers using DNS lookup (default: 1 unless -connect)</translation> </message> <message> - <location line="+11"/> + <location line="+6"/> + <source>Importing blocks from block database...</source> + <translation>Importing blocks from block database...</translation> + </message> + <message> + <location line="+4"/> <source>Invalid -tor address: '%s'</source> <translation>Invalid -tor address: '%s'</translation> </message> @@ -2329,6 +2398,11 @@ Address: %4 <translation>Prepend debug output with timestamp</translation> </message> <message> + <location line="+1"/> + <source>Rebuild blockchain index from current blk000??.dat files</source> + <translation>Rebuild blockchain index from current blk000??.dat files</translation> + </message> + <message> <location line="+4"/> <source>SSL options: (see the Bitcoin Wiki for SSL setup instructions)</source> <translation>SSL options: (see the Bitcoin Wiki for SSL setup instructions)</translation> @@ -2349,7 +2423,7 @@ Address: %4 <translation>Send trace/debug info to debugger</translation> </message> <message> - <location line="+7"/> + <location line="+6"/> <source>Set maximum block size in bytes (default: 250000)</source> <translation>Set maximum block size in bytes (default: 250000)</translation> </message> @@ -2389,6 +2463,11 @@ Address: %4 <translation>Username for JSON-RPC connections</translation> </message> <message> + <location line="+1"/> + <source>Verifying database integrity...</source> + <translation>Verifying database integrity...</translation> + </message> + <message> <location line="+2"/> <source>Warning: Disk space is low!</source> <translation>Warning: Disk space is low!</translation> @@ -2399,12 +2478,17 @@ Address: %4 <translation>Warning: This version is obsolete, upgrade required!</translation> </message> <message> - <location line="-41"/> + <location line="+1"/> + <source>wallet.dat corrupt, salvage failed</source> + <translation>wallet.dat corrupt, salvage failed</translation> + </message> + <message> + <location line="-43"/> <source>Password for JSON-RPC connections</source> <translation>Password for JSON-RPC connections</translation> </message> <message> - <location line="-52"/> + <location line="-51"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>Allow JSON-RPC connections from specified IP address</translation> </message> @@ -2414,12 +2498,12 @@ Address: %4 <translation>Send commands to node running on <ip> (default: 127.0.0.1)</translation> </message> <message> - <location line="-91"/> + <location line="-101"/> <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="+114"/> + <location line="+123"/> <source>Upgrade wallet to latest format</source> <translation>Upgrade wallet to latest format</translation> </message> @@ -2429,7 +2513,7 @@ Address: %4 <translation>Set key pool size to <n> (default: 100)</translation> </message> <message> - <location line="-14"/> + <location line="-13"/> <source>Rescan the block chain for missing wallet transactions</source> <translation>Rescan the block chain for missing wallet transactions</translation> </message> @@ -2444,7 +2528,7 @@ Address: %4 <translation>How thorough the block verification is (0-6, default: 1)</translation> </message> <message> - <location line="+3"/> + <location line="+2"/> <source>Imports blocks from external blk000?.dat file</source> <translation>Imports blocks from external blk000?.dat file</translation> </message> @@ -2454,7 +2538,7 @@ Address: %4 <translation>Use OpenSSL (https) for JSON-RPC connections</translation> </message> <message> - <location line="-21"/> + <location line="-20"/> <source>Server certificate file (default: server.cert)</source> <translation>Server certificate file (default: server.cert)</translation> </message> @@ -2464,32 +2548,27 @@ Address: %4 <translation>Server private key (default: server.pem)</translation> </message> <message> - <location line="-116"/> + <location line="-130"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> </message> <message> - <location line="+128"/> + <location line="+141"/> <source>This help message</source> <translation>This help message</translation> </message> <message> - <location line="-120"/> - <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> - <translation>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</translation> - </message> - <message> - <location line="+46"/> + <location line="-73"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> <message> - <location line="+77"/> + <location line="+76"/> <source>Unable to bind to %s on this computer (bind returned error %d, %s)</source> <translation>Unable to bind to %s on this computer (bind returned error %d, %s)</translation> </message> <message> - <location line="-69"/> + <location line="-68"/> <source>Connect through socks proxy</source> <translation>Connect through socks proxy</translation> </message> @@ -2499,12 +2578,12 @@ Address: %4 <translation>Allow DNS lookups for -addnode, -seednode and -connect</translation> </message> <message> - <location line="+43"/> + <location line="+42"/> <source>Loading addresses...</source> <translation>Loading addresses...</translation> </message> <message> - <location line="-26"/> + <location line="-25"/> <source>Error loading blkindex.dat</source> <translation>Error loading blkindex.dat</translation> </message> @@ -2529,7 +2608,7 @@ Address: %4 <translation>Error loading wallet.dat</translation> </message> <message> - <location line="+19"/> + <location line="+18"/> <source>Invalid -proxy address: '%s'</source> <translation>Invalid -proxy address: '%s'</translation> </message> @@ -2544,7 +2623,7 @@ Address: %4 <translation>Unknown -socks proxy version requested: %i</translation> </message> <message> - <location line="-74"/> + <location line="-73"/> <source>Cannot resolve -bind address: '%s'</source> <translation>Cannot resolve -bind address: '%s'</translation> </message> @@ -2554,42 +2633,22 @@ Address: %4 <translation>Cannot resolve -externalip address: '%s'</translation> </message> <message> - <location line="+30"/> + <location line="+29"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>Invalid amount for -paytxfee=<amount>: '%s'</translation> </message> <message> - <location line="-15"/> + <location line="-14"/> <source>Error: could not start node</source> <translation>Error: could not start node</translation> </message> <message> - <location line="-1"/> - <source>Error: Wallet locked, unable to create transaction </source> - <translation>Error: Wallet locked, unable to create transaction </translation> - </message> - <message> - <location line="-56"/> - <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> - <translation>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </translation> - </message> - <message> - <location line="+55"/> - <source>Error: Transaction creation failed </source> - <translation>Error: Transaction creation failed </translation> - </message> - <message> - <location line="+42"/> + <location line="+40"/> <source>Sending...</source> <translation>Sending...</translation> </message> <message> - <location line="-101"/> - <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> - </message> - <message> - <location line="+77"/> + <location line="-25"/> <source>Invalid amount</source> <translation>Invalid amount</translation> </message> @@ -2604,17 +2663,17 @@ Address: %4 <translation>Loading block index...</translation> </message> <message> - <location line="-45"/> + <location line="-44"/> <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="-18"/> + <location line="-25"/> <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="+47"/> + <location line="+54"/> <source>Find peers using internet relay chat (default: 0)</source> <translation>Find peers using internet relay chat (default: 0)</translation> </message> @@ -2624,12 +2683,12 @@ Address: %4 <translation>Fee per KB to add to transactions you send</translation> </message> <message> - <location line="+19"/> + <location line="+18"/> <source>Loading wallet...</source> <translation>Loading wallet...</translation> </message> <message> - <location line="-39"/> + <location line="-38"/> <source>Cannot downgrade wallet</source> <translation>Cannot downgrade wallet</translation> </message> @@ -2654,31 +2713,12 @@ Address: %4 <translation>Done loading</translation> </message> <message> - <location line="+64"/> + <location line="+63"/> <source>To use the %s option</source> <translation>To use the %s option</translation> </message> <message> - <location line="-139"/> - <source>%s, you must set a rpcpassword in the configuration file: - %s -It is recommended you use the following random password: -rpcuser=bitcoinrpc -rpcpassword=%s -(you do not need to remember this password) -If the file does not exist, create it with owner-readable-only file permissions. -</source> - <translation>%s, you must set a rpcpassword in the configuration file: - %s -It is recommended you use the following random password: -rpcuser=bitcoinrpc -rpcpassword=%s -(you do not need to remember this password) -If the file does not exist, create it with owner-readable-only file permissions. -</translation> - </message> - <message> - <location line="+80"/> + <location line="-58"/> <source>Error</source> <translation>Error</translation> </message> diff --git a/src/qt/macdockiconhandler.h b/src/qt/macdockiconhandler.h index 2092fb26b3..dd85e0c33d 100644 --- a/src/qt/macdockiconhandler.h +++ b/src/qt/macdockiconhandler.h @@ -1,7 +1,7 @@ #ifndef MACDOCKICONHANDLER_H #define MACDOCKICONHANDLER_H -#include <QtCore/QObject> +#include <QObject> class QMenu; class QIcon; diff --git a/src/qt/notificator.h b/src/qt/notificator.h index abb47109b3..833b52cb15 100644 --- a/src/qt/notificator.h +++ b/src/qt/notificator.h @@ -46,11 +46,11 @@ public slots: private: QWidget *parent; enum Mode { - None, /**< Ignore informational notifications, and show a modal pop-up dialog for Critical notifications. */ - Freedesktop, /**< Use DBus org.freedesktop.Notifications */ - QSystemTray, /**< Use QSystemTray::showMessage */ + None, /**< Ignore informational notifications, and show a modal pop-up dialog for Critical notifications. */ + Freedesktop, /**< Use DBus org.freedesktop.Notifications */ + QSystemTray, /**< Use QSystemTray::showMessage */ Growl12, /**< Use the Growl 1.2 notification system (Mac only) */ - Growl13 /**< Use the Growl 1.3 notification system (Mac only) */ + Growl13 /**< Use the Growl 1.3 notification system (Mac only) */ }; QString programName; Mode mode; diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 3fb30a9518..03dcb0b538 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -128,7 +128,6 @@ void OptionsDialog::setMapper() /* Main */ mapper->addMapping(ui->transactionFee, OptionsModel::Fee); mapper->addMapping(ui->bitcoinAtStartup, OptionsModel::StartAtStartup); - mapper->addMapping(ui->detachDatabases, OptionsModel::DetachDatabases); /* Network */ mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP); diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 1b2f18eab3..e3c9413f1b 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -56,8 +56,6 @@ void OptionsModel::Init() SoftSetArg("-proxy", settings.value("addrProxy").toString().toStdString()); if (settings.contains("nSocksVersion") && settings.value("fUseProxy").toBool()) SoftSetArg("-socks", settings.value("nSocksVersion").toString().toStdString()); - if (settings.contains("detachDB")) - SoftSetBoolArg("-detachdb", settings.value("detachDB").toBool()); if (!language.isEmpty()) SoftSetArg("-lang", language.toStdString()); } @@ -142,8 +140,10 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const return settings.value("fUseUPnP", GetBoolArg("-upnp", true)); case MinimizeOnClose: return QVariant(fMinimizeOnClose); - case ProxyUse: - return settings.value("fUseProxy", false); + case ProxyUse: { + proxyType proxy; + return QVariant(GetProxy(NET_IPV4, proxy)); + } case ProxyIP: { proxyType proxy; if (GetProxy(NET_IPV4, proxy)) @@ -158,16 +158,19 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const else return QVariant(9050); } - case ProxySocksVersion: - return settings.value("nSocksVersion", 5); + case ProxySocksVersion: { + proxyType proxy; + if (GetProxy(NET_IPV4, proxy)) + return QVariant(proxy.second); + else + return QVariant(5); + } case Fee: return QVariant(nTransactionFee); case DisplayUnit: return QVariant(nDisplayUnit); case DisplayAddresses: return QVariant(bDisplayAddresses); - case DetachDatabases: - return QVariant(bitdb.GetDetach()); case Language: return settings.value("language", ""); default: @@ -203,7 +206,7 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in break; case ProxyUse: settings.setValue("fUseProxy", value.toBool()); - ApplyProxySettings(); + successful = ApplyProxySettings(); break; case ProxyIP: { proxyType proxy; @@ -249,12 +252,6 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in bDisplayAddresses = value.toBool(); settings.setValue("bDisplayAddresses", bDisplayAddresses); break; - case DetachDatabases: { - bool fDetachDB = value.toBool(); - bitdb.SetDetach(fDetachDB); - settings.setValue("detachDB", fDetachDB); - } - break; case Language: settings.setValue("language", value); break; diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 2d86a7a9ca..4f893bb44e 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -28,7 +28,6 @@ public: Fee, // qint64 DisplayUnit, // BitcoinUnits::Unit DisplayAddresses, // bool - DetachDatabases, // bool Language, // QString OptionIDRowCount, }; diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 07be9c520a..8f1ff5325e 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -1,6 +1,7 @@ #include "overviewpage.h" #include "ui_overviewpage.h" +#include "clientmodel.h" #include "walletmodel.h" #include "bitcoinunits.h" #include "optionsmodel.h" @@ -92,6 +93,8 @@ public: OverviewPage::OverviewPage(QWidget *parent) : QWidget(parent), ui(new Ui::OverviewPage), + clientModel(0), + walletModel(0), currentBalance(-1), currentUnconfirmedBalance(-1), currentImmatureBalance(-1), @@ -129,7 +132,7 @@ OverviewPage::~OverviewPage() void OverviewPage::setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance) { - int unit = model->getOptionsModel()->getDisplayUnit(); + int unit = walletModel->getOptionsModel()->getDisplayUnit(); currentBalance = balance; currentUnconfirmedBalance = unconfirmedBalance; currentImmatureBalance = immatureBalance; @@ -149,9 +152,20 @@ void OverviewPage::setNumTransactions(int count) ui->labelNumTransactions->setText(QLocale::system().toString(count)); } -void OverviewPage::setModel(WalletModel *model) +void OverviewPage::setClientModel(ClientModel *model) { - this->model = model; + this->clientModel = model; + if(model) + { + // Show warning if this is a prerelease version + connect(model, SIGNAL(alertsChanged(QString)), this, SLOT(updateAlerts(QString))); + updateAlerts(model->getStatusBarWarnings()); + } +} + +void OverviewPage::setWalletModel(WalletModel *model) +{ + this->walletModel = model; if(model && model->getOptionsModel()) { // Set up transaction list @@ -181,18 +195,24 @@ void OverviewPage::setModel(WalletModel *model) void OverviewPage::updateDisplayUnit() { - if(model && model->getOptionsModel()) + if(walletModel && walletModel->getOptionsModel()) { if(currentBalance != -1) setBalance(currentBalance, currentUnconfirmedBalance, currentImmatureBalance); // Update txdelegate->unit with the current unit - txdelegate->unit = model->getOptionsModel()->getDisplayUnit(); + txdelegate->unit = walletModel->getOptionsModel()->getDisplayUnit(); ui->listTransactions->update(); } } +void OverviewPage::updateAlerts(const QString &warnings) +{ + this->ui->labelAlerts->setVisible(!warnings.isEmpty()); + this->ui->labelAlerts->setText(warnings); +} + void OverviewPage::showOutOfSyncWarning(bool fShow) { ui->labelWalletStatus->setVisible(fShow); diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index 00048cc8f8..bb32a0c33f 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -10,6 +10,7 @@ QT_END_NAMESPACE namespace Ui { class OverviewPage; } +class ClientModel; class WalletModel; class TxViewDelegate; class TransactionFilterProxy; @@ -23,7 +24,8 @@ public: explicit OverviewPage(QWidget *parent = 0); ~OverviewPage(); - void setModel(WalletModel *model); + void setClientModel(ClientModel *clientModel); + void setWalletModel(WalletModel *walletModel); void showOutOfSyncWarning(bool fShow); public slots: @@ -35,7 +37,8 @@ signals: private: Ui::OverviewPage *ui; - WalletModel *model; + ClientModel *clientModel; + WalletModel *walletModel; qint64 currentBalance; qint64 currentUnconfirmedBalance; qint64 currentImmatureBalance; @@ -46,6 +49,7 @@ private: private slots: void updateDisplayUnit(); void handleTransactionClicked(const QModelIndex &index); + void updateAlerts(const QString &warnings); }; #endif // OVERVIEWPAGE_H diff --git a/src/qt/qtipcserver.cpp b/src/qt/qtipcserver.cpp index 74f44fac57..b26c37581c 100644 --- a/src/qt/qtipcserver.cpp +++ b/src/qt/qtipcserver.cpp @@ -26,7 +26,7 @@ using namespace boost; using namespace boost::interprocess; using namespace boost::posix_time; -#ifdef MAC_OSX +#if defined MAC_OSX || defined __FreeBSD__ // URI handling not implemented on OSX yet void ipcScanRelay(int argc, char *argv[]) { } diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 2b8a0c049b..3dc32d0e47 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -15,12 +15,11 @@ #include <openssl/crypto.h> +// TODO: add a scrollback limit, as there is currently none // TODO: make it possible to filter out categories (esp debug messages when implemented) // TODO: receive errors and debug messages through ClientModel -const int CONSOLE_SCROLLBACK = 50; const int CONSOLE_HISTORY = 50; - const QSize ICON_SIZE(24, 24); const struct { @@ -289,6 +288,8 @@ static QString categoryClass(int category) void RPCConsole::clear() { ui->messagesWidget->clear(); + history.clear(); + historyPtr = 0; ui->lineEdit->clear(); ui->lineEdit->setFocus(); diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index ca2c615333..0c1547bd8e 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -148,7 +148,7 @@ void SendCoinsDialog::on_sendButton_clicked() break; case WalletModel::TransactionCreationFailed: QMessageBox::warning(this, tr("Send Coins"), - tr("Error: Transaction creation failed."), + tr("Error: Transaction creation failed!"), QMessageBox::Ok, QMessageBox::Ok); break; case WalletModel::TransactionCommitFailed: diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index fd321ce280..b0687d5399 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -81,5 +81,4 @@ public slots: friend class TransactionTablePriv; }; -#endif - +#endif // TRANSACTIONTABLEMODEL_H diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 3568616cd3..9d5a2c04ff 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -189,7 +189,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList<SendCoinsRecipie } return TransactionCreationFailed; } - if(!uiInterface.ThreadSafeAskFee(nFeeRequired, tr("Sending...").toStdString())) + if(!uiInterface.ThreadSafeAskFee(nFeeRequired)) { return Aborted; } diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 62558a49df..fd5c8c4d4f 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -147,8 +147,8 @@ signals: // this means that the unlocking failed or was cancelled. void requireUnlock(); - // Asynchronous error notification - void error(const QString &title, const QString &message, bool modal); + // Asynchronous message notification + void message(const QString &title, const QString &message, unsigned int style); public slots: /* Wallet status might have changed */ diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 3fde463cd3..5554f039a7 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -154,7 +154,7 @@ Value getblock(const Array& params, bool fHelp) CBlock block; CBlockIndex* pblockindex = mapBlockIndex[hash]; - block.ReadFromDisk(pblockindex, true); + block.ReadFromDisk(pblockindex); return blockToJSON(block, pblockindex); } diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 0270b5f102..0591f35392 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -198,7 +198,7 @@ Value getwork(const Array& params, bool fHelp) Value getblocktemplate(const Array& params, bool fHelp) { - if (fHelp || params.size() != 1) + if (fHelp || params.size() > 1) throw runtime_error( "getblocktemplate [params]\n" "Returns data needed to construct a block to work on:\n" diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index 72a89c08c1..9531b12678 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -18,6 +18,39 @@ using namespace boost; using namespace boost::assign; using namespace json_spirit; +// +// Utilities: convert hex-encoded Values +// (throws error if not hex). +// +uint256 ParseHashV(const Value& v, string strName) +{ + string strHex; + if (v.type() == str_type) + strHex = v.get_str(); + if (!IsHex(strHex)) // Note: IsHex("") is false + throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); + uint256 result; + result.SetHex(strHex); + return result; +} +uint256 ParseHashO(const Object& o, string strKey) +{ + return ParseHashV(find_value(o, strKey), strKey); +} +vector<unsigned char> ParseHexV(const Value& v, string strName) +{ + string strHex; + if (v.type() == str_type) + strHex = v.get_str(); + if (!IsHex(strHex)) + throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); + return ParseHex(strHex); +} +vector<unsigned char> ParseHexO(const Object& o, string strKey) +{ + return ParseHexV(find_value(o, strKey), strKey); +} + void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out) { txnouttype type; @@ -109,8 +142,7 @@ Value getrawtransaction(const Array& params, bool fHelp) "If verbose is non-zero, returns an Object\n" "with information about <txid>."); - uint256 hash; - hash.SetHex(params[0].get_str()); + uint256 hash = ParseHashV(params[0], "parameter 1"); bool fVerbose = false; if (params.size() > 1) @@ -178,10 +210,10 @@ Value listunspent(const Array& params, bool fHelp) if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) continue; - if(setAddress.size()) + if (setAddress.size()) { CTxDestination address; - if(!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) + if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) continue; if (!setAddress.count(address)) @@ -194,6 +226,17 @@ Value listunspent(const Array& params, bool fHelp) entry.push_back(Pair("txid", out.tx->GetHash().GetHex())); entry.push_back(Pair("vout", out.i)); entry.push_back(Pair("scriptPubKey", HexStr(pk.begin(), pk.end()))); + if (pk.IsPayToScriptHash()) + { + CTxDestination address; + if (ExtractDestination(pk, address)) + { + const CScriptID& hash = boost::get<const CScriptID&>(address); + CScript redeemScript; + if (pwalletMain->GetCScript(hash, redeemScript)) + entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()))); + } + } entry.push_back(Pair("amount",ValueFromAmount(nValue))); entry.push_back(Pair("confirmations",out.nDepth)); results.push_back(entry); @@ -221,16 +264,11 @@ Value createrawtransaction(const Array& params, bool fHelp) CTransaction rawTx; - BOOST_FOREACH(Value& input, inputs) + BOOST_FOREACH(const Value& input, inputs) { const Object& o = input.get_obj(); - const Value& txid_v = find_value(o, "txid"); - if (txid_v.type() != str_type) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing txid key"); - string txid = txid_v.get_str(); - if (!IsHex(txid)) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid"); + uint256 txid = ParseHashO(o, "txid"); const Value& vout_v = find_value(o, "vout"); if (vout_v.type() != int_type) @@ -239,7 +277,7 @@ Value createrawtransaction(const Array& params, bool fHelp) if (nOutput < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); - CTxIn in(COutPoint(uint256(txid), nOutput)); + CTxIn in(COutPoint(txid, nOutput)); rawTx.vin.push_back(in); } @@ -274,9 +312,7 @@ Value decoderawtransaction(const Array& params, bool fHelp) "decoderawtransaction <hex string>\n" "Return a JSON object representing the serialized, hex-encoded transaction."); - RPCTypeCheck(params, list_of(str_type)); - - vector<unsigned char> txData(ParseHex(params[0].get_str())); + vector<unsigned char> txData(ParseHexV(params[0], "argument")); CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); CTransaction tx; try { @@ -296,10 +332,10 @@ Value signrawtransaction(const Array& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 4) throw runtime_error( - "signrawtransaction <hex string> [{\"txid\":txid,\"vout\":n,\"scriptPubKey\":hex},...] [<privatekey1>,...] [sighashtype=\"ALL\"]\n" + "signrawtransaction <hex string> [{\"txid\":txid,\"vout\":n,\"scriptPubKey\":hex,\"redeemScript\":hex},...] [<privatekey1>,...] [sighashtype=\"ALL\"]\n" "Sign inputs for raw transaction (serialized, hex-encoded).\n" "Second optional argument (may be null) is an array of previous transaction outputs that\n" - "this transaction depends on but may not yet be in the blockchain.\n" + "this transaction depends on but may not yet be in the block chain.\n" "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" "Fourth optional argument is a string that is one of six values; ALL, NONE, SINGLE or\n" @@ -311,7 +347,7 @@ Value signrawtransaction(const Array& params, bool fHelp) RPCTypeCheck(params, list_of(str_type)(array_type)(array_type)(str_type), true); - vector<unsigned char> txData(ParseHex(params[0].get_str())); + vector<unsigned char> txData(ParseHexV(params[0], "argument 1")); CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); vector<CTransaction> txVariants; while (!ssData.empty()) @@ -352,6 +388,28 @@ Value signrawtransaction(const Array& params, bool fHelp) view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long } + bool fGivenKeys = false; + CBasicKeyStore tempKeystore; + if (params.size() > 2 && params[2].type() != null_type) + { + fGivenKeys = true; + Array keys = params[2].get_array(); + BOOST_FOREACH(Value k, keys) + { + CBitcoinSecret vchSecret; + bool fGood = vchSecret.SetString(k.get_str()); + if (!fGood) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); + CKey key; + bool fCompressed; + CSecret secret = vchSecret.GetSecret(fCompressed); + key.SetSecret(secret, fCompressed); + tempKeystore.AddKey(key); + } + } + else + EnsureWalletIsUnlocked(); + // Add previous txouts given in the RPC call: if (params.size() > 1 && params[1].type() != null_type) { @@ -363,22 +421,15 @@ Value signrawtransaction(const Array& params, bool fHelp) Object prevOut = p.get_obj(); - RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type)); + RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type)("redeemScript",str_type)); - string txidHex = find_value(prevOut, "txid").get_str(); - if (!IsHex(txidHex)) - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "txid must be hexadecimal"); - uint256 txid; - txid.SetHex(txidHex); + uint256 txid = ParseHashO(prevOut, "txid"); int nOut = find_value(prevOut, "vout").get_int(); if (nOut < 0) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive"); - string pkHex = find_value(prevOut, "scriptPubKey").get_str(); - if (!IsHex(pkHex)) - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "scriptPubKey must be hexadecimal"); - vector<unsigned char> pkData(ParseHex(pkHex)); + vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey")); CScript scriptPubKey(pkData.begin(), pkData.end()); CCoins coins; @@ -391,33 +442,23 @@ Value signrawtransaction(const Array& params, bool fHelp) } // what todo if txid is known, but the actual output isn't? } + if ((unsigned int)nOut >= coins.vout.size()) + coins.vout.resize(nOut+1); coins.vout[nOut].scriptPubKey = scriptPubKey; coins.vout[nOut].nValue = 0; // we don't know the actual output value view.SetCoins(txid, coins); - } - } - bool fGivenKeys = false; - CBasicKeyStore tempKeystore; - if (params.size() > 2 && params[2].type() != null_type) - { - fGivenKeys = true; - Array keys = params[2].get_array(); - BOOST_FOREACH(Value k, keys) - { - CBitcoinSecret vchSecret; - bool fGood = vchSecret.SetString(k.get_str()); - if (!fGood) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,"Invalid private key"); - CKey key; - bool fCompressed; - CSecret secret = vchSecret.GetSecret(fCompressed); - key.SetSecret(secret, fCompressed); - tempKeystore.AddKey(key); + // if redeemScript given and not using the local wallet (private keys + // given), add redeemScript to the tempKeystore so it can be signed: + Value v = find_value(prevOut, "redeemScript"); + if (fGivenKeys && scriptPubKey.IsPayToScriptHash() && !(v == Value::null)) + { + vector<unsigned char> rsData(ParseHexV(v, "redeemScript")); + CScript redeemScript(rsData.begin(), rsData.end()); + tempKeystore.AddCScript(redeemScript); + } } } - else - EnsureWalletIsUnlocked(); const CKeyStore& keystore = (fGivenKeys ? tempKeystore : *pwalletMain); @@ -464,7 +505,7 @@ Value signrawtransaction(const Array& params, bool fHelp) { txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig); } - if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, true, true, 0)) + if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, 0)) fComplete = false; } @@ -484,10 +525,8 @@ Value sendrawtransaction(const Array& params, bool fHelp) "sendrawtransaction <hex string>\n" "Submits raw transaction (serialized, hex-encoded) to local node and network."); - RPCTypeCheck(params, list_of(str_type)); - // parse hex string from parameter - vector<unsigned char> txData(ParseHex(params[0].get_str())); + vector<unsigned char> txData(ParseHexV(params[0], "parameter")); CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); CTransaction tx; diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 6f8728e979..90a68f560a 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -3,14 +3,18 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include <boost/assign/list_of.hpp> + #include "wallet.h" #include "walletdb.h" #include "bitcoinrpc.h" #include "init.h" #include "base58.h" -using namespace json_spirit; using namespace std; +using namespace boost; +using namespace boost::assign; +using namespace json_spirit; int64 nWalletUnlockTime; static CCriticalSection cs_nWalletUnlockTime; @@ -573,7 +577,7 @@ Value movecmd(const Array& params, bool fHelp) // Debit CAccountingEntry debit; - debit.nOrderPos = pwalletMain->IncOrderPosNext(); + debit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb); debit.strAccount = strFrom; debit.nCreditDebit = -nAmount; debit.nTime = nNow; @@ -583,7 +587,7 @@ Value movecmd(const Array& params, bool fHelp) // Credit CAccountingEntry credit; - credit.nOrderPos = pwalletMain->IncOrderPosNext(); + credit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb); credit.strAccount = strTo; credit.nCreditDebit = nAmount; credit.nTime = nNow; @@ -702,22 +706,13 @@ Value sendmany(const Array& params, bool fHelp) return wtx.GetHash().GetHex(); } -Value addmultisigaddress(const Array& params, bool fHelp) +// +// Used by addmultisigaddress / createmultisig: +// +static CScript _createmultisig(const Array& params) { - if (fHelp || params.size() < 2 || params.size() > 3) - { - string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n" - "Add a nrequired-to-sign multisignature address to the wallet\"\n" - "each key is a Bitcoin address or hex-encoded public key\n" - "If [account] is specified, assign address to [account]."; - throw runtime_error(msg); - } - int nRequired = params[0].get_int(); const Array& keys = params[1].get_array(); - string strAccount; - if (params.size() > 2) - strAccount = AccountFromValue(params[2]); // Gather public keys if (nRequired < 1) @@ -760,10 +755,28 @@ Value addmultisigaddress(const Array& params, bool fHelp) throw runtime_error(" Invalid public key: "+ks); } } + CScript result; + result.SetMultisig(nRequired, pubkeys); + return result; +} + +Value addmultisigaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 3) + { + string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n" + "Add a nrequired-to-sign multisignature address to the wallet\"\n" + "each key is a Bitcoin address or hex-encoded public key\n" + "If [account] is specified, assign address to [account]."; + throw runtime_error(msg); + } + + string strAccount; + if (params.size() > 2) + strAccount = AccountFromValue(params[2]); // Construct using pay-to-script-hash: - CScript inner; - inner.SetMultisig(nRequired, pubkeys); + CScript inner = _createmultisig(params); CScriptID innerID = inner.GetID(); pwalletMain->AddCScript(inner); @@ -771,6 +784,30 @@ 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\",\"key\"]'>\n" + "Creates a multi-signature address and returns a json object\n" + "with keys:\n" + "address : bitcoin address\n" + "redeemScript : hex-encoded redemption script"; + 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 { @@ -1195,7 +1232,8 @@ Value backupwallet(const Array& params, bool fHelp) "Safely copies wallet.dat to destination, which can be a directory or a path with filename."); string strDest = params[0].get_str(); - BackupWallet(*pwalletMain, strDest); + if (!BackupWallet(*pwalletMain, strDest)) + throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!"); return Value::null; } @@ -1396,7 +1434,7 @@ Value encryptwallet(const Array& params, bool fHelp) // slack space in .dat files; that is bad if the old data is // unencrypted private keys. So: StartShutdown(); - return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup."; + 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> @@ -1463,3 +1501,74 @@ Value validateaddress(const Array& params, bool fHelp) return ret; } +Value lockunspent(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "lockunspent unlock? [array-of-Objects]\n" + "Updates list of temporarily unspendable outputs."); + + if (params.size() == 1) + RPCTypeCheck(params, list_of(bool_type)); + else + RPCTypeCheck(params, list_of(bool_type)(array_type)); + + bool fUnlock = params[0].get_bool(); + + if (params.size() == 1) { + if (fUnlock) + pwalletMain->UnlockAllCoins(); + return true; + } + + Array outputs = params[1].get_array(); + BOOST_FOREACH(Value& output, outputs) + { + if (output.type() != obj_type) + throw JSONRPCError(-8, "Invalid parameter, expected object"); + const Object& o = output.get_obj(); + + RPCTypeCheck(o, map_list_of("txid", str_type)("vout", int_type)); + + string txid = find_value(o, "txid").get_str(); + if (!IsHex(txid)) + throw JSONRPCError(-8, "Invalid parameter, expected hex txid"); + + int nOutput = find_value(o, "vout").get_int(); + if (nOutput < 0) + throw JSONRPCError(-8, "Invalid parameter, vout must be positive"); + + COutPoint outpt(uint256(txid), nOutput); + + if (fUnlock) + pwalletMain->UnlockCoin(outpt); + else + pwalletMain->LockCoin(outpt); + } + + return true; +} + +Value listlockunspent(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 0) + throw runtime_error( + "listlockunspent\n" + "Returns list of temporarily unspendable outputs."); + + vector<COutPoint> vOutpts; + pwalletMain->ListLockedCoins(vOutpts); + + Array ret; + + BOOST_FOREACH(COutPoint &outpt, vOutpts) { + Object o; + + o.push_back(Pair("txid", outpt.hash.GetHex())); + o.push_back(Pair("vout", (int)outpt.n)); + ret.push_back(o); + } + + return ret; +} + diff --git a/src/script.cpp b/src/script.cpp index 36d208c247..f65508aacc 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -314,7 +314,7 @@ bool IsCanonicalSignature(const valtype &vchSig) { return true; } -bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, bool fStrictEncodings, int nHashType) +bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType) { CAutoBN_CTX pctx; CScript::const_iterator pc = script.begin(); @@ -327,7 +327,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co if (script.size() > 10000) return false; int nOpCount = 0; - + bool fStrictEncodings = flags & SCRIPT_VERIFY_STRICTENC; try { @@ -1637,14 +1637,14 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vecto } bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, - bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType) + unsigned int flags, int nHashType) { vector<vector<unsigned char> > stack, stackCopy; - if (!EvalScript(stack, scriptSig, txTo, nIn, fStrictEncodings, nHashType)) + if (!EvalScript(stack, scriptSig, txTo, nIn, flags, nHashType)) return false; - if (fValidatePayToScriptHash) + if (flags & SCRIPT_VERIFY_P2SH) stackCopy = stack; - if (!EvalScript(stack, scriptPubKey, txTo, nIn, fStrictEncodings, nHashType)) + if (!EvalScript(stack, scriptPubKey, txTo, nIn, flags, nHashType)) return false; if (stack.empty()) return false; @@ -1653,16 +1653,21 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C return false; // Additional validation for spend-to-script-hash transactions: - if (fValidatePayToScriptHash && scriptPubKey.IsPayToScriptHash()) + if ((flags & SCRIPT_VERIFY_P2SH) && scriptPubKey.IsPayToScriptHash()) { if (!scriptSig.IsPushOnly()) // scriptSig must be literals-only return false; // or validation fails + // stackCopy cannot be empty here, because if it was the + // P2SH HASH <> EQUAL scriptPubKey would be evaluated with + // an empty stack and the EvalScript above would return false. + assert(!stackCopy.empty()); + const valtype& pubKeySerialized = stackCopy.back(); CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end()); popstack(stackCopy); - if (!EvalScript(stackCopy, pubKey2, txTo, nIn, fStrictEncodings, nHashType)) + if (!EvalScript(stackCopy, pubKey2, txTo, nIn, flags, nHashType)) return false; if (stackCopy.empty()) return false; @@ -1705,7 +1710,7 @@ bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CTransa } // Test solution - return VerifyScript(txin.scriptSig, fromPubKey, txTo, nIn, true, true, 0); + return VerifyScript(txin.scriptSig, fromPubKey, txTo, nIn, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, 0); } bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType) @@ -1718,7 +1723,7 @@ bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTrans return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, nHashType); } -bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType) +bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType) { assert(nIn < txTo.vin.size()); const CTxIn& txin = txTo.vin[nIn]; @@ -1726,7 +1731,7 @@ bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned in return false; const CTxOut& txout = txFrom.vout[txin.prevout.n]; - return VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, fValidatePayToScriptHash, fStrictEncodings, nHashType); + return VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, flags, nHashType); } static CScript PushAll(const vector<valtype>& values) @@ -1844,9 +1849,9 @@ CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsign Solver(scriptPubKey, txType, vSolutions); vector<valtype> stack1; - EvalScript(stack1, scriptSig1, CTransaction(), 0, true, 0); + EvalScript(stack1, scriptSig1, CTransaction(), 0, SCRIPT_VERIFY_STRICTENC, 0); vector<valtype> stack2; - EvalScript(stack2, scriptSig2, CTransaction(), 0, true, 0); + EvalScript(stack2, scriptSig2, CTransaction(), 0, SCRIPT_VERIFY_STRICTENC, 0); return CombineSignatures(scriptPubKey, txTo, nIn, txType, vSolutions, stack1, stack2); } diff --git a/src/script.h b/src/script.h index f9df587ca3..7b0643f70a 100644 --- a/src/script.h +++ b/src/script.h @@ -26,6 +26,13 @@ enum SIGHASH_ANYONECANPAY = 0x80, }; +/** Script verification flags */ +enum +{ + SCRIPT_VERIFY_NONE = 0, + SCRIPT_VERIFY_P2SH = (1U << 0), + SCRIPT_VERIFY_STRICTENC = (1U << 1), +}; enum txnouttype { @@ -656,7 +663,7 @@ public: bool IsCanonicalPubKey(const std::vector<unsigned char> &vchPubKey); bool IsCanonicalSignature(const std::vector<unsigned char> &vchSig); -bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, bool fStrictEncodings, int nHashType); +bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet); int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned char> >& vSolutions); bool IsStandard(const CScript& scriptPubKey); @@ -667,8 +674,8 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std:: bool SignSignature(const CKeyStore& keystore, const CScript& fromPubKey, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, - bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType); -bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType); + unsigned int flags, int nHashType); +bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); // Given two sets of signatures for scriptPubKey, possibly with OP_0 placeholders, // combine them intelligently and return the result. diff --git a/src/serialize.h b/src/serialize.h index 549e46cbc3..f2626281c1 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -50,10 +50,6 @@ enum SER_NETWORK = (1 << 0), SER_DISK = (1 << 1), SER_GETHASH = (1 << 2), - - // modifiers - SER_SKIPSIG = (1 << 16), - SER_BLOCKHEADERONLY = (1 << 17), }; #define IMPLEMENT_SERIALIZE(statements) \ @@ -1225,4 +1221,148 @@ public: } }; +/** Wrapper around a FILE* that implements a ring buffer to + * deserialize from. It guarantees the ability to rewind + * a given number of bytes. */ +class CBufferedFile +{ +private: + FILE *src; // source file + uint64 nSrcPos; // how many bytes have been read from source + uint64 nReadPos; // how many bytes have been read from this + uint64 nReadLimit; // up to which position we're allowed to read + uint64 nRewind; // how many bytes we guarantee to rewind + std::vector<char> vchBuf; // the buffer + + short state; + short exceptmask; + +protected: + void setstate(short bits, const char *psz) { + state |= bits; + if (state & exceptmask) + throw std::ios_base::failure(psz); + } + + // read data from the source to fill the buffer + bool Fill() { + unsigned int pos = nSrcPos % vchBuf.size(); + unsigned int readNow = vchBuf.size() - pos; + unsigned int nAvail = vchBuf.size() - (nSrcPos - nReadPos) - nRewind; + if (nAvail < readNow) + readNow = nAvail; + if (readNow == 0) + return false; + size_t read = fread((void*)&vchBuf[pos], 1, readNow, src); + if (read == 0) { + setstate(std::ios_base::failbit, feof(src) ? "CBufferedFile::Fill : end of file" : "CBufferedFile::Fill : fread failed"); + return false; + } else { + nSrcPos += read; + return true; + } + } + +public: + int nType; + int nVersion; + + CBufferedFile(FILE *fileIn, uint64 nBufSize, uint64 nRewindIn, int nTypeIn, int nVersionIn) : + src(fileIn), nSrcPos(0), nReadPos(0), nReadLimit((uint64)(-1)), nRewind(nRewindIn), vchBuf(nBufSize, 0), + state(0), exceptmask(std::ios_base::badbit | std::ios_base::failbit), nType(nTypeIn), nVersion(nVersionIn) { + } + + // check whether no error occurred + bool good() const { + return state == 0; + } + + // check whether we're at the end of the source file + bool eof() const { + return nReadPos == nSrcPos && feof(src); + } + + // read a number of bytes + CBufferedFile& read(char *pch, size_t nSize) { + if (nSize + nReadPos > nReadLimit) + throw std::ios_base::failure("Read attempted past buffer limit"); + if (nSize + nRewind > vchBuf.size()) + throw std::ios_base::failure("Read larger than buffer size"); + while (nSize > 0) { + if (nReadPos == nSrcPos) + Fill(); + unsigned int pos = nReadPos % vchBuf.size(); + size_t nNow = nSize; + if (nNow + pos > vchBuf.size()) + nNow = vchBuf.size() - pos; + if (nNow + nReadPos > nSrcPos) + nNow = nSrcPos - nReadPos; + memcpy(pch, &vchBuf[pos], nNow); + nReadPos += nNow; + pch += nNow; + nSize -= nNow; + } + return (*this); + } + + // return the current reading position + uint64 GetPos() { + return nReadPos; + } + + // rewind to a given reading position + bool SetPos(uint64 nPos) { + nReadPos = nPos; + if (nReadPos + nRewind < nSrcPos) { + nReadPos = nSrcPos - nRewind; + return false; + } else if (nReadPos > nSrcPos) { + nReadPos = nSrcPos; + return false; + } else { + return true; + } + } + + bool Seek(uint64 nPos) { + long nLongPos = nPos; + if (nPos != (uint64)nLongPos) + return false; + if (fseek(src, nLongPos, SEEK_SET)) + return false; + nLongPos = ftell(src); + nSrcPos = nLongPos; + nReadPos = nLongPos; + state = 0; + return true; + } + + // prevent reading beyond a certain position + // no argument removes the limit + bool SetLimit(uint64 nPos = (uint64)(-1)) { + if (nPos < nReadPos) + return false; + nReadLimit = nPos; + return true; + } + + template<typename T> + CBufferedFile& operator>>(T& obj) { + // Unserialize from this stream + ::Unserialize(*this, obj, nType, nVersion); + return (*this); + } + + // search for a given byte in the stream, and remain positioned on it + void FindByte(char ch) { + while (true) { + if (nReadPos == nSrcPos) + Fill(); + if (vchBuf[nReadPos % vchBuf.size()] == ch) + break; + nReadPos++; + } + } +}; + #endif diff --git a/src/sync.h b/src/sync.h index e80efbe001..9dfc6697c6 100644 --- a/src/sync.h +++ b/src/sync.h @@ -37,46 +37,31 @@ class CMutexLock { private: boost::unique_lock<Mutex> lock; -public: void Enter(const char* pszName, const char* pszFile, int nLine) { - if (!lock.owns_lock()) - { - EnterCritical(pszName, pszFile, nLine, (void*)(lock.mutex())); + EnterCritical(pszName, pszFile, nLine, (void*)(lock.mutex())); #ifdef DEBUG_LOCKCONTENTION - if (!lock.try_lock()) - { - PrintLockContention(pszName, pszFile, nLine); + if (!lock.try_lock()) + { + PrintLockContention(pszName, pszFile, nLine); #endif - lock.lock(); + lock.lock(); #ifdef DEBUG_LOCKCONTENTION - } -#endif - } - } - - void Leave() - { - if (lock.owns_lock()) - { - lock.unlock(); - LeaveCritical(); } +#endif } bool TryEnter(const char* pszName, const char* pszFile, int nLine) { + EnterCritical(pszName, pszFile, nLine, (void*)(lock.mutex()), true); + lock.try_lock(); if (!lock.owns_lock()) - { - EnterCritical(pszName, pszFile, nLine, (void*)(lock.mutex()), true); - lock.try_lock(); - if (!lock.owns_lock()) - LeaveCritical(); - } + LeaveCritical(); return lock.owns_lock(); } +public: CMutexLock(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) : lock(mutexIn, boost::defer_lock) { if (fTry) @@ -95,11 +80,6 @@ public: { return lock.owns_lock(); } - - boost::unique_lock<Mutex> &GetLock() - { - return lock; - } }; typedef CMutexLock<CCriticalSection> CCriticalBlock; diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp index 8235b0bda7..b1e98f65ed 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -46,7 +46,7 @@ BOOST_AUTO_TEST_CASE(DoS_banning) BOOST_CHECK(CNode::IsBanned(addr1)); // ... but 1 still should be dummyNode2.Misbehaving(50); BOOST_CHECK(CNode::IsBanned(addr2)); -} +} BOOST_AUTO_TEST_CASE(DoS_banscore) { @@ -99,7 +99,7 @@ BOOST_AUTO_TEST_CASE(DoS_checknbits) { using namespace boost::assign; // for 'map_list_of()' - // Timestamps,nBits from the bitcoin blockchain. + // Timestamps,nBits from the bitcoin block chain. // These are the block-chain checkpoint blocks typedef std::map<int64, unsigned int> BlockData; BlockData chainData = @@ -129,7 +129,6 @@ BOOST_AUTO_TEST_CASE(DoS_checknbits) // ... but OK if enough time passed for difficulty to adjust downward: BOOST_CHECK(CheckNBits(firstcheck.second, lastcheck.first+60*60*24*365*4, lastcheck.second, lastcheck.first)); - } CTransaction RandomOrphan() @@ -231,6 +230,7 @@ BOOST_AUTO_TEST_CASE(DoS_checkSig) key.MakeNewKey(true); CBasicKeyStore keystore; keystore.AddKey(key); + unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC; // 100 orphan transactions: static const int NPREV=100; @@ -278,7 +278,7 @@ BOOST_AUTO_TEST_CASE(DoS_checkSig) mst1 = boost::posix_time::microsec_clock::local_time(); for (unsigned int i = 0; i < 5; i++) for (unsigned int j = 0; j < tx.vin.size(); j++) - BOOST_CHECK(VerifySignature(CCoins(orphans[j], MEMPOOL_HEIGHT), tx, j, true, true, SIGHASH_ALL)); + BOOST_CHECK(VerifySignature(CCoins(orphans[j], MEMPOOL_HEIGHT), tx, j, flags, SIGHASH_ALL)); mst2 = boost::posix_time::microsec_clock::local_time(); msdiff = mst2 - mst1; long nManyValidate = msdiff.total_milliseconds(); @@ -289,13 +289,13 @@ BOOST_AUTO_TEST_CASE(DoS_checkSig) // Empty a signature, validation should fail: CScript save = tx.vin[0].scriptSig; tx.vin[0].scriptSig = CScript(); - BOOST_CHECK(!VerifySignature(CCoins(orphans[0], MEMPOOL_HEIGHT), tx, 0, true, true, SIGHASH_ALL)); + BOOST_CHECK(!VerifySignature(CCoins(orphans[0], MEMPOOL_HEIGHT), tx, 0, flags, SIGHASH_ALL)); tx.vin[0].scriptSig = save; // Swap signatures, validation should fail: std::swap(tx.vin[0].scriptSig, tx.vin[1].scriptSig); - BOOST_CHECK(!VerifySignature(CCoins(orphans[0], MEMPOOL_HEIGHT), tx, 0, true, true, SIGHASH_ALL)); - BOOST_CHECK(!VerifySignature(CCoins(orphans[1], MEMPOOL_HEIGHT), tx, 1, true, true, SIGHASH_ALL)); + BOOST_CHECK(!VerifySignature(CCoins(orphans[0], MEMPOOL_HEIGHT), tx, 0, flags, SIGHASH_ALL)); + BOOST_CHECK(!VerifySignature(CCoins(orphans[1], MEMPOOL_HEIGHT), tx, 1, flags, SIGHASH_ALL)); std::swap(tx.vin[0].scriptSig, tx.vin[1].scriptSig); // Exercise -maxsigcachesize code: @@ -305,7 +305,7 @@ BOOST_AUTO_TEST_CASE(DoS_checkSig) BOOST_CHECK(SignSignature(keystore, orphans[0], tx, 0)); BOOST_CHECK(tx.vin[0].scriptSig != oldSig); for (unsigned int j = 0; j < tx.vin.size(); j++) - BOOST_CHECK(VerifySignature(CCoins(orphans[j], MEMPOOL_HEIGHT), tx, j, true, true, SIGHASH_ALL)); + BOOST_CHECK(VerifySignature(CCoins(orphans[j], MEMPOOL_HEIGHT), tx, j, flags, SIGHASH_ALL)); mapArgs.erase("-maxsigcachesize"); LimitOrphanTxSize(0); diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index aa6feb7201..7297bb9a75 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -20,8 +20,6 @@ using namespace boost::assign; typedef vector<unsigned char> valtype; extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); -extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, - bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType); BOOST_AUTO_TEST_SUITE(multisig_tests) @@ -44,6 +42,8 @@ sign_multisig(CScript scriptPubKey, vector<CKey> keys, CTransaction transaction, BOOST_AUTO_TEST_CASE(multisig_verify) { + unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC; + CKey key[4]; for (int i = 0; i < 4; i++) key[i].MakeNewKey(true); @@ -80,19 +80,19 @@ BOOST_AUTO_TEST_CASE(multisig_verify) keys.clear(); keys += key[0],key[1]; // magic operator+= from boost.assign s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK(VerifyScript(s, a_and_b, txTo[0], 0, true, true, 0)); + BOOST_CHECK(VerifyScript(s, a_and_b, txTo[0], 0, flags, 0)); for (int i = 0; i < 4; i++) { keys.clear(); keys += key[i]; s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, true, true, 0), strprintf("a&b 1: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, flags, 0), strprintf("a&b 1: %d", i)); keys.clear(); keys += key[1],key[i]; s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, true, true, 0), strprintf("a&b 2: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, flags, 0), strprintf("a&b 2: %d", i)); } // Test a OR b: @@ -102,16 +102,16 @@ BOOST_AUTO_TEST_CASE(multisig_verify) keys += key[i]; s = sign_multisig(a_or_b, keys, txTo[1], 0); if (i == 0 || i == 1) - BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, txTo[1], 0, true, true, 0), strprintf("a|b: %d", i)); + BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, txTo[1], 0, flags, 0), strprintf("a|b: %d", i)); else - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, txTo[1], 0, true, true, 0), strprintf("a|b: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, txTo[1], 0, flags, 0), strprintf("a|b: %d", i)); } s.clear(); s << OP_0 << OP_0; - BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, true, true, 0)); + BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, flags, 0)); s.clear(); s << OP_0 << OP_1; - BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, true, true, 0)); + BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, flags, 0)); for (int i = 0; i < 4; i++) @@ -121,9 +121,9 @@ BOOST_AUTO_TEST_CASE(multisig_verify) keys += key[i],key[j]; s = sign_multisig(escrow, keys, txTo[2], 0); if (i < j && i < 3 && j < 3) - BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, txTo[2], 0, true, true, 0), strprintf("escrow 1: %d %d", i, j)); + BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, txTo[2], 0, flags, 0), strprintf("escrow 1: %d %d", i, j)); else - BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, txTo[2], 0, true, true, 0), strprintf("escrow 2: %d %d", i, j)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, txTo[2], 0, flags, 0), strprintf("escrow 2: %d %d", i, j)); } } diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index eb820ade6d..f8fe443b87 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -1,5 +1,6 @@ -#include <boost/test/unit_test.hpp> +#include <boost/algorithm/string.hpp> #include <boost/foreach.hpp> +#include <boost/test/unit_test.hpp> #include "base58.h" #include "util.h" @@ -22,14 +23,7 @@ createArgs(int nRequired, const char* address1=NULL, const char* address2=NULL) return result; } -// This can be removed this when addmultisigaddress is enabled on main net: -struct TestNetFixture -{ - TestNetFixture() { fTestNet = true; } - ~TestNetFixture() { fTestNet = false; } -}; - -BOOST_FIXTURE_TEST_CASE(rpc_addmultisig, TestNetFixture) +BOOST_AUTO_TEST_CASE(rpc_addmultisig) { rpcfn_type addmultisig = tableRPC["addmultisigaddress"]->actor; @@ -66,4 +60,91 @@ BOOST_FIXTURE_TEST_CASE(rpc_addmultisig, TestNetFixture) BOOST_CHECK_THROW(addmultisig(createArgs(2, short2.c_str()), false), runtime_error); } +static Value CallRPC(string args) +{ + vector<string> vArgs; + boost::split(vArgs, args, boost::is_any_of(" \t")); + string strMethod = vArgs[0]; + vArgs.erase(vArgs.begin()); + Array params = RPCConvertValues(strMethod, vArgs); + + rpcfn_type method = tableRPC[strMethod]->actor; + try { + Value result = (*method)(params, false); + return result; + } + catch (Object& objError) + { + throw runtime_error(find_value(objError, "message").get_str()); + } +} + +BOOST_AUTO_TEST_CASE(rpc_rawparams) +{ + // Test raw transaction API argument handling + Value r; + + BOOST_CHECK_THROW(CallRPC("getrawtransaction"), runtime_error); + BOOST_CHECK_THROW(CallRPC("getrawtransaction not_hex"), runtime_error); + BOOST_CHECK_THROW(CallRPC("getrawtransaction a3b807410df0b60fcb9736768df5823938b2f838694939ba45f3c0a1bff150ed not_int"), runtime_error); + + 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_NO_THROW(r=CallRPC("listunspent 0 1 []")); + BOOST_CHECK_THROW(r=CallRPC("listunspent 0 1 [] extra"), runtime_error); + BOOST_CHECK(r.get_array().empty()); + + BOOST_CHECK_THROW(CallRPC("createrawtransaction"), runtime_error); + BOOST_CHECK_THROW(CallRPC("createrawtransaction null null"), runtime_error); + BOOST_CHECK_THROW(CallRPC("createrawtransaction not_array"), runtime_error); + BOOST_CHECK_THROW(CallRPC("createrawtransaction [] []"), runtime_error); + BOOST_CHECK_THROW(CallRPC("createrawtransaction {} {}"), runtime_error); + BOOST_CHECK_NO_THROW(CallRPC("createrawtransaction [] {}")); + BOOST_CHECK_THROW(CallRPC("createrawtransaction [] {} extra"), runtime_error); + + BOOST_CHECK_THROW(CallRPC("decoderawtransaction"), runtime_error); + BOOST_CHECK_THROW(CallRPC("decoderawtransaction null"), runtime_error); + BOOST_CHECK_THROW(CallRPC("decoderawtransaction DEADBEEF"), runtime_error); + string rawtx = "0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000"; + BOOST_CHECK_NO_THROW(r = CallRPC(string("decoderawtransaction ")+rawtx)); + BOOST_CHECK_EQUAL(find_value(r.get_obj(), "version").get_int(), 1); + BOOST_CHECK_EQUAL(find_value(r.get_obj(), "locktime").get_int(), 0); + BOOST_CHECK_THROW(r = CallRPC(string("decoderawtransaction ")+rawtx+" extra"), runtime_error); + + BOOST_CHECK_THROW(CallRPC("signrawtransaction"), runtime_error); + BOOST_CHECK_THROW(CallRPC("signrawtransaction null"), runtime_error); + BOOST_CHECK_THROW(CallRPC("signrawtransaction ff00"), runtime_error); + BOOST_CHECK_NO_THROW(CallRPC(string("signrawtransaction ")+rawtx)); + BOOST_CHECK_NO_THROW(CallRPC(string("signrawtransaction ")+rawtx+" null null NONE|ANYONECANPAY")); + BOOST_CHECK_NO_THROW(CallRPC(string("signrawtransaction ")+rawtx+" [] [] NONE|ANYONECANPAY")); + BOOST_CHECK_THROW(CallRPC(string("signrawtransaction ")+rawtx+" null null badenum"), runtime_error); + + // Only check failure cases for sendrawtransaction, there's no network to send to... + BOOST_CHECK_THROW(CallRPC("sendrawtransaction"), runtime_error); + BOOST_CHECK_THROW(CallRPC("sendrawtransaction null"), runtime_error); + BOOST_CHECK_THROW(CallRPC("sendrawtransaction DEADBEEF"), runtime_error); + BOOST_CHECK_THROW(CallRPC(string("sendrawtransaction ")+rawtx+" extra"), runtime_error); +} + +BOOST_AUTO_TEST_CASE(rpc_rawsign) +{ + Value r; + // input is a 1-of-2 multisig (so is output): + string prevout = + "[{\"txid\":\"b4cc287e58f87cdae59417329f710f3ecd75a4ee1d2872b7248f50977c8493f3\"," + "\"vout\":1,\"scriptPubKey\":\"a914b10c9df5f7edf436c697f02f1efdba4cf399615187\"," + "\"redeemScript\":\"512103debedc17b3df2badbcdd86d5feb4562b86fe182e5998abd8bcd4f122c6155b1b21027e940bb73ab8732bfdf7f9216ecefca5b94d6df834e77e108f68e66f126044c052ae\"}]"; + r = CallRPC(string("createrawtransaction ")+prevout+" "+ + "{\"3HqAe9LtNBjnsfM4CyYaWTnvCaUYT7v4oZ\":11}"); + string notsigned = r.get_str(); + string privkey1 = "\"KzsXybp9jX64P5ekX1KUxRQ79Jht9uzW7LorgwE65i5rWACL6LQe\""; + string privkey2 = "\"Kyhdf5LuKTRx4ge69ybABsiUAWjVRK4XGxAKk2FQLp2HjGMy87Z4\""; + r = CallRPC(string("signrawtransaction ")+notsigned+" "+prevout+" "+"[]"); + BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == false); + r = CallRPC(string("signrawtransaction ")+notsigned+" "+prevout+" "+"["+privkey1+","+privkey2+"]"); + BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == true); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index 35069a3fdd..b5107193fd 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -13,8 +13,6 @@ using namespace std; // Test routines internal to script.cpp: extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); -extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, - bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType); // Helpers: static std::vector<unsigned char> @@ -40,7 +38,7 @@ Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict) txTo.vin[0].scriptSig = scriptSig; txTo.vout[0].nValue = 1; - return VerifyScript(scriptSig, scriptPubKey, txTo, 0, fStrict, true, 0); + return VerifyScript(scriptSig, scriptPubKey, txTo, 0, fStrict ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, 0); } @@ -105,7 +103,7 @@ BOOST_AUTO_TEST_CASE(sign) { CScript sigSave = txTo[i].vin[0].scriptSig; txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig; - bool sigOK = VerifySignature(CCoins(txFrom, 0), txTo[i], 0, true, true, 0); + bool sigOK = VerifySignature(CCoins(txFrom, 0), txTo[i], 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, 0); if (i == j) BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j)); else diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index f8e4fa8a49..5d5a1525f7 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -20,8 +20,8 @@ using namespace json_spirit; using namespace boost::algorithm; extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); -extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, - bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType); + +static const unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC; CScript ParseScript(string s) @@ -79,7 +79,7 @@ ParseScript(string s) { BOOST_ERROR("Parse error: " << s); return CScript(); - } + } } return result; @@ -143,7 +143,7 @@ BOOST_AUTO_TEST_CASE(script_valid) CScript scriptPubKey = ParseScript(scriptPubKeyString); CTransaction tx; - BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, tx, 0, true, true, SIGHASH_NONE), strTest); + BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, tx, 0, flags, SIGHASH_NONE), strTest); } } @@ -167,7 +167,7 @@ BOOST_AUTO_TEST_CASE(script_invalid) CScript scriptPubKey = ParseScript(scriptPubKeyString); CTransaction tx; - BOOST_CHECK_MESSAGE(!VerifyScript(scriptSig, scriptPubKey, tx, 0, true, true, SIGHASH_NONE), strTest); + BOOST_CHECK_MESSAGE(!VerifyScript(scriptSig, scriptPubKey, tx, 0, flags, SIGHASH_NONE), strTest); } } @@ -206,7 +206,7 @@ sign_multisig(CScript scriptPubKey, std::vector<CKey> keys, CTransaction transac // NOTE: CHECKMULTISIG has an unfortunate bug; it requires // one extra item on the stack, before the signatures. // Putting OP_0 on the stack is the workaround; - // fixing the bug would mean splitting the blockchain (old + // fixing the bug would mean splitting the block chain (old // clients would not accept new CHECKMULTISIG transactions, // and vice-versa) // @@ -250,15 +250,15 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12) txTo12.vout[0].nValue = 1; CScript goodsig1 = sign_multisig(scriptPubKey12, key1, txTo12); - BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, true, true, 0)); + BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, flags, 0)); txTo12.vout[0].nValue = 2; - BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, true, true, 0)); + BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, flags, 0)); CScript goodsig2 = sign_multisig(scriptPubKey12, key2, txTo12); - BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, txTo12, 0, true, true, 0)); + BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, txTo12, 0, flags, 0)); CScript badsig1 = sign_multisig(scriptPubKey12, key3, txTo12); - BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, txTo12, 0, true, true, 0)); + BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, txTo12, 0, flags, 0)); } BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23) @@ -286,46 +286,46 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23) std::vector<CKey> keys; keys.push_back(key1); keys.push_back(key2); CScript goodsig1 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, txTo23, 0, true, true, 0)); + BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, txTo23, 0, flags, 0)); keys.clear(); keys.push_back(key1); keys.push_back(key3); CScript goodsig2 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, txTo23, 0, true, true, 0)); + BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, txTo23, 0, flags, 0)); keys.clear(); keys.push_back(key2); keys.push_back(key3); CScript goodsig3 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, txTo23, 0, true, true, 0)); + BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, txTo23, 0, flags, 0)); keys.clear(); keys.push_back(key2); keys.push_back(key2); // Can't re-use sig CScript badsig1 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, txTo23, 0, true, true, 0)); + BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, txTo23, 0, flags, 0)); keys.clear(); keys.push_back(key2); keys.push_back(key1); // sigs must be in correct order CScript badsig2 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, txTo23, 0, true, true, 0)); + BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, txTo23, 0, flags, 0)); keys.clear(); keys.push_back(key3); keys.push_back(key2); // sigs must be in correct order CScript badsig3 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, txTo23, 0, true, true, 0)); + BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, txTo23, 0, flags, 0)); keys.clear(); keys.push_back(key4); keys.push_back(key2); // sigs must match pubkeys CScript badsig4 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, txTo23, 0, true, true, 0)); + BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, txTo23, 0, flags, 0)); keys.clear(); keys.push_back(key1); keys.push_back(key4); // sigs must match pubkeys CScript badsig5 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, txTo23, 0, true, true, 0)); + BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, txTo23, 0, flags, 0)); keys.clear(); // Must have signatures CScript badsig6 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, txTo23, 0, true, true, 0)); + BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, txTo23, 0, flags, 0)); } BOOST_AUTO_TEST_CASE(script_combineSigs) diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index c1f47f786b..b98816d53d 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -1,10 +1,12 @@ #define BOOST_TEST_MODULE Bitcoin Test Suite #include <boost/test/unit_test.hpp> +#include <boost/filesystem.hpp> #include "db.h" #include "txdb.h" #include "main.h" #include "wallet.h" +#include "util.h" CWallet* pwalletMain; CClientUIInterface uiInterface; @@ -14,15 +16,19 @@ extern void noui_connect(); struct TestingSetup { CCoinsViewDB *pcoinsdbview; + boost::filesystem::path pathTemp; TestingSetup() { fPrintToDebugger = true; // don't want to write to debug.log file noui_connect(); bitdb.MakeMock(); - pblocktree = new CBlockTreeDB(true); - pcoinsdbview = new CCoinsViewDB(true); + pathTemp = GetTempPath() / strprintf("test_bitcoin_%lu_%i", (unsigned long)GetTime(), (int)(GetRand(100000))); + boost::filesystem::create_directories(pathTemp); + mapArgs["-datadir"] = pathTemp.string(); + pblocktree = new CBlockTreeDB(1 << 20, true); + pcoinsdbview = new CCoinsViewDB(1 << 23, true); pcoinsTip = new CCoinsViewCache(*pcoinsdbview); - LoadBlockIndex(true); + LoadBlockIndex(); bool fFirstRun; pwalletMain = new CWallet("wallet.dat"); pwalletMain->LoadWallet(fFirstRun); @@ -36,6 +42,7 @@ struct TestingSetup { delete pcoinsdbview; delete pblocktree; bitdb.Flush(true); + boost::filesystem::remove_all(pathTemp); } }; diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 4385b9ba37..23837c6c15 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -76,7 +76,7 @@ BOOST_AUTO_TEST_CASE(tx_valid) break; } - BOOST_CHECK_MESSAGE(VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], tx, i, test[2].get_bool(), false, 0), strTest); + BOOST_CHECK_MESSAGE(VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], tx, i, test[2].get_bool() ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, 0), strTest); } } } @@ -143,7 +143,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid) break; } - fValid = VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], tx, i, test[2].get_bool(), true, 0); + fValid = VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], tx, i, test[2].get_bool() ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, 0); } BOOST_CHECK_MESSAGE(!fValid, strTest); diff --git a/src/txdb.cpp b/src/txdb.cpp index 67d15cb58f..93c5f23d8b 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -19,7 +19,7 @@ void static BatchWriteHashBestChain(CLevelDBBatch &batch, const uint256 &hash) { batch.Write('B', hash); } -CCoinsViewDB::CCoinsViewDB(bool fMemory) : db(GetDataDir() / "coins", fMemory) { +CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "coins", nCacheSize, fMemory, fWipe) { } bool CCoinsViewDB::GetCoins(uint256 txid, CCoins &coins) { @@ -58,12 +58,13 @@ bool CCoinsViewDB::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockI CLevelDBBatch batch; for (std::map<uint256, CCoins>::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++) BatchWriteCoins(batch, it->first, it->second); - BatchWriteHashBestChain(batch, pindex->GetBlockHash()); + if (pindex) + BatchWriteHashBestChain(batch, pindex->GetBlockHash()); return db.WriteBatch(batch); } -CBlockTreeDB::CBlockTreeDB(bool fMemory) : CLevelDB(GetDataDir() / "blktree", fMemory) { +CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CLevelDB(GetDataDir() / "blktree", nCacheSize, fMemory, fWipe) { } bool CBlockTreeDB::WriteBlockIndex(const CDiskBlockIndex& blockindex) @@ -93,6 +94,18 @@ bool CBlockTreeDB::WriteLastBlockFile(int nFile) { return Write('l', nFile); } +bool CBlockTreeDB::WriteReindexing(bool fReindexing) { + if (fReindexing) + return Write('R', '1'); + else + return Erase('R'); +} + +bool CBlockTreeDB::ReadReindexing(bool &fReindexing) { + fReindexing = Exists('R'); + return true; +} + bool CBlockTreeDB::ReadLastBlockFile(int &nFile) { return Read('l', nFile); } diff --git a/src/txdb.h b/src/txdb.h index 123ec00d23..d7d327069f 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -14,7 +14,7 @@ class CCoinsViewDB : public CCoinsView protected: CLevelDB db; public: - CCoinsViewDB(bool fMemory = false); + CCoinsViewDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); bool GetCoins(uint256 txid, CCoins &coins); bool SetCoins(uint256 txid, const CCoins &coins); @@ -29,7 +29,7 @@ public: class CBlockTreeDB : public CLevelDB { public: - CBlockTreeDB(bool fMemory = false); + CBlockTreeDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); private: CBlockTreeDB(const CBlockTreeDB&); void operator=(const CBlockTreeDB&); @@ -41,6 +41,8 @@ public: bool WriteBlockFileInfo(int nFile, const CBlockFileInfo &fileinfo); bool ReadLastBlockFile(int &nFile); bool WriteLastBlockFile(int nFile); + bool WriteReindexing(bool fReindex); + bool ReadReindexing(bool &fReindex); bool LoadBlockIndexGuts(); }; diff --git a/src/ui_interface.h b/src/ui_interface.h index 0f7fdef264..703e15f095 100644 --- a/src/ui_interface.h +++ b/src/ui_interface.h @@ -29,41 +29,49 @@ public: /** Flags for CClientUIInterface::ThreadSafeMessageBox */ enum MessageBoxFlags { - YES = 0x00000002, - OK = 0x00000004, - NO = 0x00000008, - YES_NO = (YES|NO), - CANCEL = 0x00000010, - APPLY = 0x00000020, - CLOSE = 0x00000040, - OK_DEFAULT = 0x00000000, - YES_DEFAULT = 0x00000000, - NO_DEFAULT = 0x00000080, - CANCEL_DEFAULT = 0x80000000, - ICON_EXCLAMATION = 0x00000100, - ICON_HAND = 0x00000200, - ICON_WARNING = ICON_EXCLAMATION, - ICON_ERROR = ICON_HAND, - ICON_QUESTION = 0x00000400, - ICON_INFORMATION = 0x00000800, - ICON_STOP = ICON_HAND, - ICON_ASTERISK = ICON_INFORMATION, - ICON_MASK = (0x00000100|0x00000200|0x00000400|0x00000800), - FORWARD = 0x00001000, - BACKWARD = 0x00002000, - RESET = 0x00004000, - HELP = 0x00008000, - MORE = 0x00010000, - SETUP = 0x00020000, - // Force blocking, modal message box dialog (not just OS notification) - MODAL = 0x00040000 + ICON_INFORMATION = 0, + ICON_WARNING = (1U << 0), + ICON_ERROR = (1U << 1), + /** + * Mask of all available icons in CClientUIInterface::MessageBoxFlags + * This needs to be updated, when icons are changed there! + */ + ICON_MASK = (ICON_INFORMATION | ICON_WARNING | ICON_ERROR), + + /** These values are taken from qmessagebox.h "enum StandardButton" to be directly usable */ + BTN_OK = 0x00000400U, // QMessageBox::Ok + BTN_YES = 0x00004000U, // QMessageBox::Yes + BTN_NO = 0x00010000U, // QMessageBox::No + BTN_ABORT = 0x00040000U, // QMessageBox::Abort + BTN_RETRY = 0x00080000U, // QMessageBox::Retry + BTN_IGNORE = 0x00100000U, // QMessageBox::Ignore + BTN_CLOSE = 0x00200000U, // QMessageBox::Close + BTN_CANCEL = 0x00400000U, // QMessageBox::Cancel + BTN_DISCARD = 0x00800000U, // QMessageBox::Discard + BTN_HELP = 0x01000000U, // QMessageBox::Help + BTN_APPLY = 0x02000000U, // QMessageBox::Apply + BTN_RESET = 0x04000000U, // QMessageBox::Reset + /** + * Mask of all available buttons in CClientUIInterface::MessageBoxFlags + * This needs to be updated, when buttons are changed there! + */ + BTN_MASK = (BTN_OK | BTN_YES | BTN_NO | BTN_ABORT | BTN_RETRY | BTN_IGNORE | + BTN_CLOSE | BTN_CANCEL | BTN_DISCARD | BTN_HELP | BTN_APPLY | BTN_RESET), + + /** Force blocking, modal message box dialog (not just OS notification) */ + MODAL = 0x10000000U, + + /** Predefined combinations for certain default usage cases */ + MSG_INFORMATION = ICON_INFORMATION, + MSG_WARNING = (ICON_WARNING | BTN_OK | MODAL), + MSG_ERROR = (ICON_ERROR | BTN_OK | MODAL) }; /** Show message box. */ - boost::signals2::signal<void (const std::string& message, const std::string& caption, int style)> ThreadSafeMessageBox; + boost::signals2::signal<void (const std::string& message, const std::string& caption, unsigned int style)> ThreadSafeMessageBox; /** Ask the user whether they want to pay a fee or not. */ - boost::signals2::signal<bool (int64 nFeeRequired, const std::string& strCaption), boost::signals2::last_value<bool> > ThreadSafeAskFee; + boost::signals2::signal<bool (int64 nFeeRequired), boost::signals2::last_value<bool> > ThreadSafeAskFee; /** Handle a URL passed at the command line. */ boost::signals2::signal<void (const std::string& strURI)> ThreadSafeHandleURI; diff --git a/src/util.cpp b/src/util.cpp index 9162886450..3cbc6b196b 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -64,7 +64,7 @@ bool fDebug = false; bool fDebugNet = false; bool fPrintToConsole = false; bool fPrintToDebugger = false; -bool fRequestShutdown = false; +volatile bool fRequestShutdown = false; bool fShutdown = false; bool fDaemon = false; bool fServer = false; @@ -156,7 +156,7 @@ void RandAddSeedPerfmon() if (ret == ERROR_SUCCESS) { RAND_add(pdata, nSize, nSize/100.0); - memset(pdata, 0, nSize); + OPENSSL_cleanse(pdata, nSize); printf("RandAddSeed() %lu bytes\n", nSize); } #endif @@ -1248,7 +1248,7 @@ void AddTimeData(const CNetAddr& ip, int64 nTime) string strMessage = _("Warning: Please check that your computer's date and time are correct! If your clock is wrong Bitcoin will not work properly."); strMiscWarning = strMessage; printf("*** %s\n", strMessage.c_str()); - uiInterface.ThreadSafeMessageBox(strMessage+" ", string("Bitcoin"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION); + uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_WARNING); } } } @@ -1310,6 +1310,28 @@ boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate) } #endif +boost::filesystem::path GetTempPath() { +#if BOOST_FILESYSTEM_VERSION == 3 + return boost::filesystem::temp_directory_path(); +#else + // TODO: remove when we don't support filesystem v2 anymore + boost::filesystem::path path; +#ifdef WIN32 + char pszPath[MAX_PATH] = ""; + + if (GetTempPathA(MAX_PATH, pszPath)) + path = boost::filesystem::path(pszPath); +#else + path = boost::filesystem::path("/tmp"); +#endif + if (path.empty() || !boost::filesystem::is_directory(path)) { + printf("GetTempPath(): failed to find temp path\n"); + return boost::filesystem::path(""); + } + return path; +#endif +} + void runCommand(std::string strCommand) { int nErr = ::system(strCommand.c_str()); diff --git a/src/util.h b/src/util.h index b798f60aa7..ab921e6f05 100644 --- a/src/util.h +++ b/src/util.h @@ -132,7 +132,7 @@ extern bool fDebug; extern bool fDebugNet; extern bool fPrintToConsole; extern bool fPrintToDebugger; -extern bool fRequestShutdown; +extern volatile bool fRequestShutdown; extern bool fShutdown; extern bool fDaemon; extern bool fServer; @@ -207,6 +207,7 @@ void ReadConfigFile(std::map<std::string, std::string>& mapSettingsRet, std::map #ifdef WIN32 boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate = true); #endif +boost::filesystem::path GetTempPath(); void ShrinkDebugFile(); int GetRandInt(int nMax); uint64 GetRand(uint64 nMax); @@ -329,6 +330,12 @@ inline int64 GetTimeMillis() boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_milliseconds(); } +inline int64 GetTimeMicros() +{ + return (boost::posix_time::ptime(boost::posix_time::microsec_clock::universal_time()) - + boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_microseconds(); +} + inline std::string DateTimeStrFormat(const char* pszFormat, int64 nTime) { time_t n = nTime; diff --git a/src/wallet.cpp b/src/wallet.cpp index 1498ff28b7..c07adff6c6 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -291,10 +291,14 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) return true; } -int64 CWallet::IncOrderPosNext() +int64 CWallet::IncOrderPosNext(CWalletDB *pwalletdb) { - int64 nRet = nOrderPosNext; - CWalletDB(strWalletFile).WriteOrderPosNext(++nOrderPosNext); + int64 nRet = nOrderPosNext++; + if (pwalletdb) { + pwalletdb->WriteOrderPosNext(nOrderPosNext); + } else { + CWalletDB(strWalletFile).WriteOrderPosNext(nOrderPosNext); + } return nRet; } @@ -752,7 +756,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) while (pindex) { CBlock block; - block.ReadFromDisk(pindex, true); + block.ReadFromDisk(pindex); BOOST_FOREACH(CTransaction& tx, block.vtx) { if (AddToWalletIfInvolvingMe(tx.GetHash(), tx, &block, fUpdate)) @@ -780,8 +784,8 @@ void CWallet::ReacceptWalletTransactions() CCoins coins; bool fUpdated = false; - bool fNotFound = pcoinsTip->GetCoins(wtx.GetHash(), coins); - if (!fNotFound || wtx.GetDepthInMainChain() > 0) + bool fFound = pcoinsTip->GetCoins(wtx.GetHash(), coins); + if (fFound || wtx.GetDepthInMainChain() > 0) { // Update fSpent if a tx got spent somewhere else by a copy of wallet.dat for (unsigned int i = 0; i < wtx.vout.size(); i++) @@ -820,21 +824,17 @@ void CWallet::ReacceptWalletTransactions() void CWalletTx::RelayWalletTransaction() { - CCoinsViewCache& coins = *pcoinsTip; BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) { - if (!tx.IsCoinBase()) - { - uint256 hash = tx.GetHash(); - if (!coins.HaveCoins(hash)) - RelayMessage(CInv(MSG_TX, hash), (CTransaction)tx); + if (!tx.IsCoinBase()) { + if (tx.GetDepthInMainChain() == 0) + RelayMessage(CInv(MSG_TX, tx.GetHash()), (CTransaction)tx); } } if (!IsCoinBase()) { - uint256 hash = GetHash(); - if (!coins.HaveCoins(hash)) - { + if (GetDepthInMainChain() == 0) { + uint256 hash = GetHash(); printf("Relaying wtx %s\n", hash.ToString().substr(0,10).c_str()); RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this); } @@ -930,9 +930,8 @@ int64 CWallet::GetImmatureBalance() const LOCK(cs_wallet); for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { - const CWalletTx& pcoin = (*it).second; - if (pcoin.IsCoinBase() && pcoin.GetBlocksToMaturity() > 0 && pcoin.IsInMainChain()) - nTotal += GetCredit(pcoin); + const CWalletTx* pcoin = &(*it).second; + nTotal += pcoin->GetImmatureCredit(); } } return nTotal; @@ -958,9 +957,11 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed) const if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) continue; - for (unsigned int i = 0; i < pcoin->vout.size(); i++) - if (!(pcoin->IsSpent(i)) && IsMine(pcoin->vout[i]) && pcoin->vout[i].nValue > 0) + for (unsigned int i = 0; i < pcoin->vout.size(); i++) { + if (!(pcoin->IsSpent(i)) && IsMine(pcoin->vout[i]) && + !IsLockedCoin((*it).first, i) && pcoin->vout[i].nValue > 0) vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain())); + } } } } @@ -1296,7 +1297,7 @@ string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, if (IsLocked()) { - string strError = _("Error: Wallet locked, unable to create transaction "); + string strError = _("Error: Wallet locked, unable to create transaction!"); printf("SendMoney() : %s", strError.c_str()); return strError; } @@ -1304,18 +1305,18 @@ string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, { string strError; if (nValue + nFeeRequired > GetBalance()) - strError = strprintf(_("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds "), FormatMoney(nFeeRequired).c_str()); + strError = strprintf(_("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds!"), FormatMoney(nFeeRequired).c_str()); else - strError = _("Error: Transaction creation failed "); + strError = _("Error: Transaction creation failed!"); printf("SendMoney() : %s", strError.c_str()); return strError; } - if (fAskFee && !uiInterface.ThreadSafeAskFee(nFeeRequired, _("Sending..."))) + 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."); + 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."); return ""; } @@ -1771,3 +1772,35 @@ void CWallet::UpdatedTransaction(const uint256 &hashTx) NotifyTransactionChanged(this, hashTx, CT_UPDATED); } } + +void CWallet::LockCoin(COutPoint& output) +{ + setLockedCoins.insert(output); +} + +void CWallet::UnlockCoin(COutPoint& output) +{ + setLockedCoins.erase(output); +} + +void CWallet::UnlockAllCoins() +{ + setLockedCoins.clear(); +} + +bool CWallet::IsLockedCoin(uint256 hash, unsigned int n) const +{ + COutPoint outpt(hash, n); + + return (setLockedCoins.count(outpt) > 0); +} + +void CWallet::ListLockedCoins(std::vector<COutPoint>& vOutpts) +{ + for (std::set<COutPoint>::iterator it = setLockedCoins.begin(); + it != setLockedCoins.end(); it++) { + COutPoint outpt = (*it); + vOutpts.push_back(outpt); + } +} + diff --git a/src/wallet.h b/src/wallet.h index 43b695c597..ecfdafe2eb 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -119,11 +119,18 @@ public: CPubKey vchDefaultKey; + std::set<COutPoint> setLockedCoins; + // check whether we are allowed to upgrade (or already support) to the named feature bool CanSupportFeature(enum WalletFeature wf) { return nWalletMaxVersion >= wf; } void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlyConfirmed=true) const; bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, std::vector<COutput> vCoins, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const; + bool IsLockedCoin(uint256 hash, unsigned int n) const; + void LockCoin(COutPoint& output); + void UnlockCoin(COutPoint& output); + void UnlockAllCoins(); + void ListLockedCoins(std::vector<COutPoint>& vOutpts); // keystore implementation // Generate a new key @@ -149,7 +156,7 @@ public: /** Increment the next transaction order id @return next transaction order id */ - int64 IncOrderPosNext(); + int64 IncOrderPosNext(CWalletDB *pwalletdb = NULL); typedef std::pair<CWalletTx*, CAccountingEntry*> TxPair; typedef std::multimap<int64, TxPair > TxItems; @@ -375,10 +382,12 @@ public: // memory only mutable bool fDebitCached; mutable bool fCreditCached; + mutable bool fImmatureCreditCached; mutable bool fAvailableCreditCached; mutable bool fChangeCached; mutable int64 nDebitCached; mutable int64 nCreditCached; + mutable int64 nImmatureCreditCached; mutable int64 nAvailableCreditCached; mutable int64 nChangeCached; @@ -416,10 +425,12 @@ public: vfSpent.clear(); fDebitCached = false; fCreditCached = false; + fImmatureCreditCached = false; fAvailableCreditCached = false; fChangeCached = false; nDebitCached = 0; nCreditCached = 0; + nImmatureCreditCached = 0; nAvailableCreditCached = 0; nChangeCached = 0; nOrderPos = -1; @@ -563,6 +574,20 @@ public: return nCreditCached; } + int64 GetImmatureCredit(bool fUseCache=true) const + { + if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) + { + if (fUseCache && fImmatureCreditCached) + return nImmatureCreditCached; + nImmatureCreditCached = pwallet->GetCredit(*this); + fImmatureCreditCached = true; + return nImmatureCreditCached; + } + + return 0; + } + int64 GetAvailableCredit(bool fUseCache=true) const { // Must wait until coinbase is safely deep enough in the chain before valuing it |