diff options
Diffstat (limited to 'src')
65 files changed, 1118 insertions, 905 deletions
diff --git a/src/alert.cpp b/src/alert.cpp index 4b029840dd..44f4d5eec6 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -144,9 +144,7 @@ bool CAlert::RelayTo(CNode* pnode) const bool CAlert::CheckSignature() const { - CKey key; - if (!key.SetPubKey(ParseHex(fTestNet ? pszTestKey : pszMainKey))) - return error("CAlert::CheckSignature() : SetPubKey failed"); + CPubKey key(ParseHex(fTestNet ? pszTestKey : pszMainKey)); if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig)) return error("CAlert::CheckSignature() : verify signature failed"); diff --git a/src/allocators.h b/src/allocators.h index eb2aed6721..85af8fe376 100644 --- a/src/allocators.h +++ b/src/allocators.h @@ -177,6 +177,19 @@ private: }; // +// Functions for directly locking/unlocking memory objects. +// Intended for non-dynamically allocated structures. +// +template<typename T> void LockObject(const T &t) { + LockedPageManager::instance.LockRange((void*)(&t), sizeof(T)); +} + +template<typename T> void UnlockObject(const T &t) { + OPENSSL_cleanse((void*)(&t), sizeof(T)); + LockedPageManager::instance.UnlockRange((void*)(&t), sizeof(T)); +} + +// // Allocator that locks its contents from being paged // out of memory and clears its contents before deletion. // diff --git a/src/base58.h b/src/base58.h index be8a541f67..efe3a95ebd 100644 --- a/src/base58.h +++ b/src/base58.h @@ -398,21 +398,19 @@ bool inline CBitcoinAddressVisitor::operator()(const CNoDestination &id) const { class CBitcoinSecret : public CBase58Data { public: - void SetSecret(const CSecret& vchSecret, bool fCompressed) + void SetKey(const CKey& vchSecret) { - assert(vchSecret.size() == 32); - SetData(fTestNet ? 239 : 128, &vchSecret[0], vchSecret.size()); - if (fCompressed) + assert(vchSecret.IsValid()); + SetData(fTestNet ? 239 : 128, vchSecret.begin(), vchSecret.size()); + if (vchSecret.IsCompressed()) vchData.push_back(1); } - CSecret GetSecret(bool &fCompressedOut) + CKey GetKey() { - CSecret vchSecret; - vchSecret.resize(32); - memcpy(&vchSecret[0], &vchData[0], 32); - fCompressedOut = vchData.size() == 33; - return vchSecret; + CKey ret; + ret.Set(&vchData[0], &vchData[32], vchData.size() > 32 && vchData[32] == 1); + return ret; } bool IsValid() const @@ -443,9 +441,9 @@ public: return SetString(strSecret.c_str()); } - CBitcoinSecret(const CSecret& vchSecret, bool fCompressed) + CBitcoinSecret(const CKey& vchSecret) { - SetSecret(vchSecret, fCompressed); + SetKey(vchSecret); } CBitcoinSecret() diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index a9b73fd5a6..c99b74f183 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -11,17 +11,17 @@ #include "bitcoinrpc.h" #include "db.h" +#include <boost/algorithm/string.hpp> #include <boost/asio.hpp> #include <boost/asio/ip/v6_only.hpp> +#include <boost/asio/ssl.hpp> #include <boost/bind.hpp> #include <boost/filesystem.hpp> +#include <boost/filesystem/fstream.hpp> #include <boost/foreach.hpp> #include <boost/iostreams/concepts.hpp> #include <boost/iostreams/stream.hpp> -#include <boost/algorithm/string.hpp> #include <boost/lexical_cast.hpp> -#include <boost/asio/ssl.hpp> -#include <boost/filesystem/fstream.hpp> #include <boost/shared_ptr.hpp> #include <list> @@ -34,6 +34,7 @@ static std::string strRPCUserColonPass; // These are created by StartRPCThreads, destroyed in StopRPCThreads static asio::io_service* rpc_io_service = NULL; +static map<string, boost::shared_ptr<deadline_timer> > deadlineTimers; static ssl::context* rpc_ssl_context = NULL; static boost::thread_group* rpc_worker_group = NULL; @@ -756,7 +757,7 @@ void StartRPCThreads() rpc_io_service = new asio::io_service(); rpc_ssl_context = new ssl::context(*rpc_io_service, ssl::context::sslv23); - const bool fUseSSL = GetBoolArg("-rpcssl"); + const bool fUseSSL = GetBoolArg("-rpcssl", false); if (fUseSSL) { @@ -843,6 +844,7 @@ void StopRPCThreads() { if (rpc_io_service == NULL) return; + deadlineTimers.clear(); rpc_io_service->stop(); rpc_worker_group->join_all(); delete rpc_worker_group; rpc_worker_group = NULL; @@ -850,6 +852,26 @@ void StopRPCThreads() delete rpc_io_service; rpc_io_service = NULL; } +void RPCRunHandler(const boost::system::error_code& err, boost::function<void(void)> func) +{ + if (!err) + func(); +} + +void RPCRunLater(const std::string& name, boost::function<void(void)> func, int64 nSeconds) +{ + assert(rpc_io_service != NULL); + + if (deadlineTimers.count(name) == 0) + { + deadlineTimers.insert(make_pair(name, + boost::shared_ptr<deadline_timer>(new deadline_timer(*rpc_io_service)))); + } + deadlineTimers[name]->expires_from_now(posix_time::seconds(nSeconds)); + deadlineTimers[name]->async_wait(boost::bind(RPCRunHandler, _1, func)); +} + + class JSONRequest { public: @@ -1015,7 +1037,7 @@ json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_s // Observe safe mode string strWarning = GetWarnings("rpc"); - if (strWarning != "" && !GetBoolArg("-disablesafemode") && + if (strWarning != "" && !GetBoolArg("-disablesafemode", false) && !pcmd->okSafeMode) throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, string("Safe mode: ") + strWarning); @@ -1049,7 +1071,7 @@ Object CallRPC(const string& strMethod, const Array& params) GetConfigFile().string().c_str())); // Connect to localhost - bool fUseSSL = GetBoolArg("-rpcssl"); + bool fUseSSL = GetBoolArg("-rpcssl", false); asio::io_service io_service; ssl::context context(io_service, ssl::context::sslv23); context.set_options(ssl::context::no_sslv2); diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h index 315fd92383..44c657f8dc 100644 --- a/src/bitcoinrpc.h +++ b/src/bitcoinrpc.h @@ -11,6 +11,7 @@ #include <map> class CBlockIndex; +class CReserveKey; #include "json/json_spirit_reader_template.h" #include "json/json_spirit_writer_template.h" @@ -88,6 +89,12 @@ void RPCTypeCheck(const json_spirit::Array& params, void RPCTypeCheck(const json_spirit::Object& o, const std::map<std::string, json_spirit::Value_type>& typesExpected, bool fAllowNull=false); +/* + Run func nSeconds from now. Uses boost deadline timers. + Overrides previous timer <name> (if any). + */ +void RPCRunLater(const std::string& name, boost::function<void(void)> func, int64 nSeconds); + typedef json_spirit::Value(*rpcfn_type)(const json_spirit::Array& params, bool fHelp); class CRPCCommand @@ -123,6 +130,9 @@ public: extern const CRPCTable tableRPC; +extern void InitRPCMining(); +extern void ShutdownRPCMining(); + extern int64 nWalletUnlockTime; extern int64 AmountFromValue(const json_spirit::Value& value); extern json_spirit::Value ValueFromAmount(int64 amount); diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index 9e8e0f7024..7643ec5df8 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -28,6 +28,8 @@ namespace Checkpoints double fTransactionsPerDay; }; + bool fEnabled = true; + // What makes a good checkpoint block? // + Is surrounded by blocks with reasonable timestamps // (no blocks before with a timestamp after, none after with @@ -74,7 +76,7 @@ namespace Checkpoints bool CheckBlock(int nHeight, const uint256& hash) { - if (!GetBoolArg("-checkpoints", true)) + if (!fEnabled) return true; const MapCheckpoints& checkpoints = *Checkpoints().mapCheckpoints; @@ -117,7 +119,7 @@ namespace Checkpoints int GetTotalBlocksEstimate() { - if (!GetBoolArg("-checkpoints", true)) + if (!fEnabled) return 0; const MapCheckpoints& checkpoints = *Checkpoints().mapCheckpoints; @@ -127,7 +129,7 @@ namespace Checkpoints CBlockIndex* GetLastCheckpoint(const std::map<uint256, CBlockIndex*>& mapBlockIndex) { - if (!GetBoolArg("-checkpoints", true)) + if (!fEnabled) return NULL; const MapCheckpoints& checkpoints = *Checkpoints().mapCheckpoints; diff --git a/src/checkpoints.h b/src/checkpoints.h index 3d56885556..a49a908a38 100644 --- a/src/checkpoints.h +++ b/src/checkpoints.h @@ -24,6 +24,8 @@ namespace Checkpoints CBlockIndex* GetLastCheckpoint(const std::map<uint256, CBlockIndex*>& mapBlockIndex); double GuessVerificationProgress(CBlockIndex *pindex); + + extern bool fEnabled; } #endif diff --git a/src/clientversion.h b/src/clientversion.h index aabab4c726..30c0c4072b 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -8,11 +8,11 @@ // These need to be macros, as version.cpp's and bitcoin-qt.rc's voodoo requires it #define CLIENT_VERSION_MAJOR 0 #define CLIENT_VERSION_MINOR 8 -#define CLIENT_VERSION_REVISION 2 -#define CLIENT_VERSION_BUILD 1 +#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 true +#define CLIENT_VERSION_IS_RELEASE false // Copyright year (2009-this) // Todo: update this when changing our copyright comments in the source diff --git a/src/crypter.cpp b/src/crypter.cpp index a2b62a87c8..32baabd674 100644 --- a/src/crypter.cpp +++ b/src/crypter.cpp @@ -100,17 +100,17 @@ bool CCrypter::Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingM } -bool EncryptSecret(CKeyingMaterial& vMasterKey, const CSecret &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext) +bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext) { CCrypter cKeyCrypter; std::vector<unsigned char> chIV(WALLET_CRYPTO_KEY_SIZE); memcpy(&chIV[0], &nIV, WALLET_CRYPTO_KEY_SIZE); if(!cKeyCrypter.SetKey(vMasterKey, chIV)) return false; - return cKeyCrypter.Encrypt((CKeyingMaterial)vchPlaintext, vchCiphertext); + return cKeyCrypter.Encrypt(*((const CKeyingMaterial*)&vchPlaintext), vchCiphertext); } -bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CSecret& vchPlaintext) +bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext) { CCrypter cKeyCrypter; std::vector<unsigned char> chIV(WALLET_CRYPTO_KEY_SIZE); diff --git a/src/crypter.h b/src/crypter.h index 6f75170bac..4134c1b49b 100644 --- a/src/crypter.h +++ b/src/crypter.h @@ -101,7 +101,7 @@ public: } }; -bool EncryptSecret(CKeyingMaterial& vMasterKey, const CSecret &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext); -bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char> &vchCiphertext, const uint256& nIV, CSecret &vchPlaintext); +bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext); +bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext); #endif diff --git a/src/db.cpp b/src/db.cpp index 3133d99bf8..fd4c67d552 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -541,6 +541,8 @@ bool CAddrDB::Read(CAddrMan& addr) // use file size to size memory buffer int fileSize = GetFilesize(filein); int dataSize = fileSize - sizeof(uint256); + //Don't try to resize to a negative number if file is small + if ( dataSize < 0 ) dataSize = 0; vector<unsigned char> vchData; vchData.resize(dataSize); uint256 hashIn; diff --git a/src/hash.h b/src/hash.h index eaa1780c04..536ab71165 100644 --- a/src/hash.h +++ b/src/hash.h @@ -105,15 +105,22 @@ uint256 SerializeHash(const T& obj, int nType=SER_GETHASH, int nVersion=PROTOCOL return ss.GetHash(); } -inline uint160 Hash160(const std::vector<unsigned char>& vch) +template<typename T1> +inline uint160 Hash160(const T1 pbegin, const T1 pend) { + static unsigned char pblank[1]; uint256 hash1; - SHA256(&vch[0], vch.size(), (unsigned char*)&hash1); + SHA256((pbegin == pend ? pblank : (unsigned char*)&pbegin[0]), (pend - pbegin) * sizeof(pbegin[0]), (unsigned char*)&hash1); uint160 hash2; RIPEMD160((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); return hash2; } +inline uint160 Hash160(const std::vector<unsigned char>& vch) +{ + return Hash160(vch.begin(), vch.end()); +} + unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector<unsigned char>& vDataToHash); #endif diff --git a/src/init.cpp b/src/init.cpp index ebd9dee7b2..78f838f7cb 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -10,6 +10,7 @@ #include "init.h" #include "util.h" #include "ui_interface.h" +#include "checkpoints.h" #include <boost/filesystem.hpp> #include <boost/filesystem/fstream.hpp> @@ -96,11 +97,13 @@ void Shutdown() RenameThread("bitcoin-shutoff"); nTransactionsUpdated++; StopRPCThreads(); + ShutdownRPCMining(); bitdb.Flush(false); StopNode(); { LOCK(cs_main); - pwalletMain->SetBestChain(CBlockLocator(pindexBest)); + if (pwalletMain) + pwalletMain->SetBestChain(CBlockLocator(pindexBest)); if (pblocktree) pblocktree->Flush(); if (pcoinsTip) @@ -195,7 +198,7 @@ bool AppInit(int argc, char* argv[]) exit(ret); } #if !defined(WIN32) - fDaemon = GetBoolArg("-daemon"); + fDaemon = GetBoolArg("-daemon", false); if (fDaemon) { // Daemonize @@ -492,7 +495,8 @@ bool AppInit2(boost::thread_group& threadGroup) // ********************************************************* Step 2: parameter interactions - fTestNet = GetBoolArg("-testnet"); + fTestNet = GetBoolArg("-testnet", false); + Checkpoints::fEnabled = GetBoolArg("-checkpoints", true); if (mapArgs.count("-bind")) { // when specifying an explicit binding address, you want to listen on it @@ -522,7 +526,7 @@ bool AppInit2(boost::thread_group& threadGroup) SoftSetBoolArg("-discover", false); } - if (GetBoolArg("-salvagewallet")) { + if (GetBoolArg("-salvagewallet", false)) { // Rewrite just private keys: rescan to find transactions SoftSetBoolArg("-rescan", true); } @@ -530,7 +534,7 @@ bool AppInit2(boost::thread_group& threadGroup) // Make sure enough file descriptors are available int nBind = std::max((int)mapArgs.count("-bind"), 1); nMaxConnections = GetArg("-maxconnections", 125); - nMaxConnections = std::max(std::min(nMaxConnections, FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS), 0); + nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS)), 0); int nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS); if (nFD < MIN_CORE_FILEDESCRIPTORS) return InitError(_("Not enough file descriptors available.")); @@ -539,8 +543,8 @@ bool AppInit2(boost::thread_group& threadGroup) // ********************************************************* Step 3: parameter-to-internal-flags - fDebug = GetBoolArg("-debug"); - fBenchmark = GetBoolArg("-benchmark"); + fDebug = GetBoolArg("-debug", false); + fBenchmark = GetBoolArg("-benchmark", false); // -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency nScriptCheckThreads = GetArg("-par", 0); @@ -555,20 +559,20 @@ bool AppInit2(boost::thread_group& threadGroup) if (fDebug) fDebugNet = true; else - fDebugNet = GetBoolArg("-debugnet"); + fDebugNet = GetBoolArg("-debugnet", false); if (fDaemon) fServer = true; else - fServer = GetBoolArg("-server"); + fServer = GetBoolArg("-server", false); /* force fServer when running without GUI */ #if !defined(QT_GUI) fServer = true; #endif - fPrintToConsole = GetBoolArg("-printtoconsole"); - fPrintToDebugger = GetBoolArg("-printtodebugger"); - fLogTimestamps = GetBoolArg("-logtimestamps"); + fPrintToConsole = GetBoolArg("-printtoconsole", false); + fPrintToDebugger = GetBoolArg("-printtodebugger", false); + fLogTimestamps = GetBoolArg("-logtimestamps", false); if (mapArgs.count("-timeout")) { @@ -673,7 +677,7 @@ bool AppInit2(boost::thread_group& threadGroup) } } - if (GetBoolArg("-salvagewallet")) + if (GetBoolArg("-salvagewallet", false)) { // Recover readable keypairs: if (!CWalletDB::Recover(bitdb, "wallet.dat", true)) @@ -795,7 +799,7 @@ bool AppInit2(boost::thread_group& threadGroup) // ********************************************************* Step 7: load block chain - fReindex = GetBoolArg("-reindex"); + fReindex = GetBoolArg("-reindex", false); // Upgrading to 0.8; hard-link the old blknnnn.dat files into /blocks/ filesystem::path blocksDir = GetDataDir() / "blocks"; @@ -863,6 +867,11 @@ bool AppInit2(boost::thread_group& threadGroup) break; } + // If the loaded chain has a wrong genesis, bail out immediately + // (we're likely using a testnet datadir, or the other way around). + if (!mapBlockIndex.empty() && pindexGenesisBlock == NULL) + return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?")); + // Initialize the block index (no-op if non-empty database was already loaded) if (!InitBlockIndex()) { strLoadError = _("Error initializing block database"); @@ -913,7 +922,7 @@ bool AppInit2(boost::thread_group& threadGroup) } printf(" block index %15"PRI64d"ms\n", GetTimeMillis() - nStart); - if (GetBoolArg("-printblockindex") || GetBoolArg("-printblocktree")) + if (GetBoolArg("-printblockindex", false) || GetBoolArg("-printblocktree", false)) { PrintBlockTree(); return false; @@ -1009,7 +1018,7 @@ bool AppInit2(boost::thread_group& threadGroup) RegisterWallet(pwalletMain); CBlockIndex *pindexRescan = pindexBest; - if (GetBoolArg("-rescan")) + if (GetBoolArg("-rescan", false)) pindexRescan = pindexGenesisBlock; else { @@ -1080,6 +1089,8 @@ bool AppInit2(boost::thread_group& threadGroup) StartNode(threadGroup); + // InitRPCMining is needed here so getwork/getblocktemplate in the GUI debug console works properly. + InitRPCMining(); if (fServer) StartRPCThreads(); diff --git a/src/key.cpp b/src/key.cpp index 20114e6bb2..f73708199a 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -2,13 +2,16 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include <map> - #include <openssl/ecdsa.h> +#include <openssl/rand.h> #include <openssl/obj_mac.h> #include "key.h" + +// anonymous namespace with local implementation code (OpenSSL interaction) +namespace { + // Generate a private key from just the secret parameter int EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key) { @@ -120,287 +123,273 @@ err: return ret; } -void CKey::SetCompressedPubKey(bool fCompressed) -{ - EC_KEY_set_conv_form(pkey, fCompressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED); - fCompressedPubKey = true; -} +// RAII Wrapper around OpenSSL's EC_KEY +class CECKey { +private: + EC_KEY *pkey; -void CKey::Reset() -{ - fCompressedPubKey = false; - if (pkey != NULL) +public: + CECKey() { + pkey = EC_KEY_new_by_curve_name(NID_secp256k1); + assert(pkey != NULL); + } + + ~CECKey() { EC_KEY_free(pkey); - pkey = EC_KEY_new_by_curve_name(NID_secp256k1); - if (pkey == NULL) - throw key_error("CKey::CKey() : EC_KEY_new_by_curve_name failed"); - fSet = false; -} + } -CKey::CKey() -{ - pkey = NULL; - Reset(); -} + void GetSecretBytes(unsigned char vch[32]) const { + const BIGNUM *bn = EC_KEY_get0_private_key(pkey); + assert(bn); + int nBytes = BN_num_bytes(bn); + int n=BN_bn2bin(bn,&vch[32 - nBytes]); + assert(n == nBytes); + memset(vch, 0, 32 - nBytes); + } -CKey::CKey(const CKey& b) -{ - pkey = EC_KEY_dup(b.pkey); - if (pkey == NULL) - throw key_error("CKey::CKey(const CKey&) : EC_KEY_dup failed"); - fSet = b.fSet; -} + void SetSecretBytes(const unsigned char vch[32]) { + BIGNUM bn; + BN_init(&bn); + assert(BN_bin2bn(vch, 32, &bn)); + assert(EC_KEY_regenerate_key(pkey, &bn)); + BN_clear_free(&bn); + } -CKey& CKey::operator=(const CKey& b) -{ - if (!EC_KEY_copy(pkey, b.pkey)) - throw key_error("CKey::operator=(const CKey&) : EC_KEY_copy failed"); - fSet = b.fSet; - return (*this); -} + void GetPrivKey(CPrivKey &privkey) { + int nSize = i2d_ECPrivateKey(pkey, NULL); + assert(nSize); + privkey.resize(nSize); + unsigned char* pbegin = &privkey[0]; + int nSize2 = i2d_ECPrivateKey(pkey, &pbegin); + assert(nSize == nSize2); + } -CKey::~CKey() -{ - EC_KEY_free(pkey); -} + bool SetPrivKey(const CPrivKey &privkey) { + const unsigned char* pbegin = &privkey[0]; + if (d2i_ECPrivateKey(&pkey, &pbegin, privkey.size())) { + // d2i_ECPrivateKey returns true if parsing succeeds. + // This doesn't necessarily mean the key is valid. + if (EC_KEY_check_key(pkey)) + return true; + } + return false; + } -bool CKey::IsNull() const -{ - return !fSet; -} + void GetPubKey(CPubKey &pubkey, bool fCompressed) { + EC_KEY_set_conv_form(pkey, fCompressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED); + int nSize = i2o_ECPublicKey(pkey, NULL); + assert(nSize); + assert(nSize <= 65); + unsigned char c[65]; + unsigned char *pbegin = c; + int nSize2 = i2o_ECPublicKey(pkey, &pbegin); + assert(nSize == nSize2); + pubkey.Set(&c[0], &c[nSize]); + } -bool CKey::IsCompressed() const -{ - return fCompressedPubKey; -} + bool SetPubKey(const CPubKey &pubkey) { + const unsigned char* pbegin = pubkey.begin(); + return o2i_ECPublicKey(&pkey, &pbegin, pubkey.size()); + } -void CKey::MakeNewKey(bool fCompressed) -{ - if (!EC_KEY_generate_key(pkey)) - throw key_error("CKey::MakeNewKey() : EC_KEY_generate_key failed"); - if (fCompressed) - SetCompressedPubKey(); - fSet = true; -} + bool Sign(const uint256 &hash, std::vector<unsigned char>& vchSig) { + unsigned int nSize = ECDSA_size(pkey); + vchSig.resize(nSize); // Make sure it is big enough + assert(ECDSA_sign(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], &nSize, pkey)); + vchSig.resize(nSize); // Shrink to fit actual size + return true; + } -bool CKey::SetPrivKey(const CPrivKey& vchPrivKey) -{ - const unsigned char* pbegin = &vchPrivKey[0]; - if (d2i_ECPrivateKey(&pkey, &pbegin, vchPrivKey.size())) - { - // In testing, d2i_ECPrivateKey can return true - // but fill in pkey with a key that fails - // EC_KEY_check_key, so: - if (EC_KEY_check_key(pkey)) - { - fSet = true; - return true; + bool Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig) { + // -1 = error, 0 = bad sig, 1 = good + if (ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], vchSig.size(), pkey) != 1) + return false; + return true; + } + + bool SignCompact(const uint256 &hash, unsigned char *p64, int &rec) { + bool fOk = false; + ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey); + if (sig==NULL) + return false; + memset(p64, 0, 64); + int nBitsR = BN_num_bits(sig->r); + int nBitsS = BN_num_bits(sig->s); + if (nBitsR <= 256 && nBitsS <= 256) { + CPubKey pubkey; + GetPubKey(pubkey, true); + for (int i=0; i<4; i++) { + CECKey keyRec; + if (ECDSA_SIG_recover_key_GFp(keyRec.pkey, sig, (unsigned char*)&hash, sizeof(hash), i, 1) == 1) { + CPubKey pubkeyRec; + keyRec.GetPubKey(pubkeyRec, true); + if (pubkeyRec == pubkey) { + rec = i; + fOk = true; + break; + } + } + } + assert(fOk); + BN_bn2bin(sig->r,&p64[32-(nBitsR+7)/8]); + BN_bn2bin(sig->s,&p64[64-(nBitsS+7)/8]); } + ECDSA_SIG_free(sig); + return fOk; } - // If vchPrivKey data is bad d2i_ECPrivateKey() can - // leave pkey in a state where calling EC_KEY_free() - // crashes. To avoid that, set pkey to NULL and - // leak the memory (a leak is better than a crash) - pkey = NULL; - Reset(); - return false; -} -bool CKey::SetSecret(const CSecret& vchSecret, bool fCompressed) -{ - EC_KEY_free(pkey); - pkey = EC_KEY_new_by_curve_name(NID_secp256k1); - if (pkey == NULL) - throw key_error("CKey::SetSecret() : EC_KEY_new_by_curve_name failed"); - if (vchSecret.size() != 32) - throw key_error("CKey::SetSecret() : secret must be 32 bytes"); - BIGNUM *bn = BN_bin2bn(&vchSecret[0],32,BN_new()); - if (bn == NULL) - throw key_error("CKey::SetSecret() : BN_bin2bn failed"); - if (!EC_KEY_regenerate_key(pkey,bn)) + // reconstruct public key from a compact signature + // This is only slightly more CPU intensive than just verifying it. + // If this function succeeds, the recovered public key is guaranteed to be valid + // (the signature is a valid signature of the given data for that key) + bool Recover(const uint256 &hash, const unsigned char *p64, int rec) { - BN_clear_free(bn); - throw key_error("CKey::SetSecret() : EC_KEY_regenerate_key failed"); + if (rec<0 || rec>=3) + return false; + ECDSA_SIG *sig = ECDSA_SIG_new(); + BN_bin2bn(&p64[0], 32, sig->r); + BN_bin2bn(&p64[32], 32, sig->s); + bool ret = ECDSA_SIG_recover_key_GFp(pkey, sig, (unsigned char*)&hash, sizeof(hash), rec, 0) == 1; + ECDSA_SIG_free(sig); + return ret; + } +}; + +}; // end of anonymous namespace + +bool CKey::Check(const unsigned char *vch) { + // Do not convert to OpenSSL's data structures for range-checking keys, + // it's easy enough to do directly. + static const unsigned char vchMax[32] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE, + 0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B, + 0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x40 + }; + bool fIsZero = true; + for (int i=0; i<32 && fIsZero; i++) + if (vch[i] != 0) + fIsZero = false; + if (fIsZero) + return false; + for (int i=0; i<32; i++) { + if (vch[i] < vchMax[i]) + return true; + if (vch[i] > vchMax[i]) + return false; } - BN_clear_free(bn); - fSet = true; - if (fCompressed || fCompressedPubKey) - SetCompressedPubKey(); return true; } -CSecret CKey::GetSecret(bool &fCompressed) const -{ - CSecret vchRet; - vchRet.resize(32); - const BIGNUM *bn = EC_KEY_get0_private_key(pkey); - int nBytes = BN_num_bytes(bn); - if (bn == NULL) - throw key_error("CKey::GetSecret() : EC_KEY_get0_private_key failed"); - int n=BN_bn2bin(bn,&vchRet[32 - nBytes]); - if (n != nBytes) - throw key_error("CKey::GetSecret(): BN_bn2bin failed"); - fCompressed = fCompressedPubKey; - return vchRet; +void CKey::MakeNewKey(bool fCompressedIn) { + do { + RAND_bytes(vch, sizeof(vch)); + } while (!Check(vch)); + fValid = true; + fCompressed = fCompressedIn; } -CPrivKey CKey::GetPrivKey() const -{ - int nSize = i2d_ECPrivateKey(pkey, NULL); - if (!nSize) - throw key_error("CKey::GetPrivKey() : i2d_ECPrivateKey failed"); - CPrivKey vchPrivKey(nSize, 0); - unsigned char* pbegin = &vchPrivKey[0]; - if (i2d_ECPrivateKey(pkey, &pbegin) != nSize) - throw key_error("CKey::GetPrivKey() : i2d_ECPrivateKey returned unexpected size"); - return vchPrivKey; +bool CKey::SetPrivKey(const CPrivKey &privkey, bool fCompressedIn) { + CECKey key; + if (!key.SetPrivKey(privkey)) + return false; + key.GetSecretBytes(vch); + fCompressed = fCompressedIn; + fValid = true; + return true; } -bool CKey::SetPubKey(const CPubKey& vchPubKey) -{ - const unsigned char* pbegin = &vchPubKey.vchPubKey[0]; - if (o2i_ECPublicKey(&pkey, &pbegin, vchPubKey.vchPubKey.size())) - { - fSet = true; - if (vchPubKey.vchPubKey.size() == 33) - SetCompressedPubKey(); - return true; - } - pkey = NULL; - Reset(); - return false; +CPrivKey CKey::GetPrivKey() const { + assert(fValid); + CECKey key; + key.SetSecretBytes(vch); + CPrivKey privkey; + key.GetPrivKey(privkey); + return privkey; } -CPubKey CKey::GetPubKey() const -{ - int nSize = i2o_ECPublicKey(pkey, NULL); - if (!nSize) - throw key_error("CKey::GetPubKey() : i2o_ECPublicKey failed"); - std::vector<unsigned char> vchPubKey(nSize, 0); - unsigned char* pbegin = &vchPubKey[0]; - if (i2o_ECPublicKey(pkey, &pbegin) != nSize) - throw key_error("CKey::GetPubKey() : i2o_ECPublicKey returned unexpected size"); - return CPubKey(vchPubKey); +CPubKey CKey::GetPubKey() const { + assert(fValid); + CECKey key; + key.SetSecretBytes(vch); + CPubKey pubkey; + key.GetPubKey(pubkey, fCompressed); + return pubkey; } -bool CKey::Sign(uint256 hash, std::vector<unsigned char>& vchSig) -{ - unsigned int nSize = ECDSA_size(pkey); - vchSig.resize(nSize); // Make sure it is big enough - if (!ECDSA_sign(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], &nSize, pkey)) - { - vchSig.clear(); +bool CKey::Sign(const uint256 &hash, std::vector<unsigned char>& vchSig) const { + if (!fValid) return false; - } - vchSig.resize(nSize); // Shrink to fit actual size - return true; + CECKey key; + key.SetSecretBytes(vch); + return key.Sign(hash, vchSig); } -// create a compact signature (65 bytes), which allows reconstructing the used public key -// The format is one header byte, followed by two times 32 bytes for the serialized r and s values. -// The header byte: 0x1B = first key with even y, 0x1C = first key with odd y, -// 0x1D = second key with even y, 0x1E = second key with odd y -bool CKey::SignCompact(uint256 hash, std::vector<unsigned char>& vchSig) -{ - bool fOk = false; - ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey); - if (sig==NULL) +bool CKey::SignCompact(const uint256 &hash, std::vector<unsigned char>& vchSig) const { + if (!fValid) return false; - vchSig.clear(); - vchSig.resize(65,0); - int nBitsR = BN_num_bits(sig->r); - int nBitsS = BN_num_bits(sig->s); - if (nBitsR <= 256 && nBitsS <= 256) - { - int nRecId = -1; - for (int i=0; i<4; i++) - { - CKey keyRec; - keyRec.fSet = true; - if (fCompressedPubKey) - keyRec.SetCompressedPubKey(); - if (ECDSA_SIG_recover_key_GFp(keyRec.pkey, sig, (unsigned char*)&hash, sizeof(hash), i, 1) == 1) - if (keyRec.GetPubKey() == this->GetPubKey()) - { - nRecId = i; - break; - } - } - - if (nRecId == -1) - throw key_error("CKey::SignCompact() : unable to construct recoverable key"); + CECKey key; + key.SetSecretBytes(vch); + vchSig.resize(65); + int rec = -1; + if (!key.SignCompact(hash, &vchSig[1], rec)) + return false; + assert(rec != -1); + vchSig[0] = 27 + rec + (fCompressed ? 4 : 0); + return true; +} - vchSig[0] = nRecId+27+(fCompressedPubKey ? 4 : 0); - BN_bn2bin(sig->r,&vchSig[33-(nBitsR+7)/8]); - BN_bn2bin(sig->s,&vchSig[65-(nBitsS+7)/8]); - fOk = true; - } - ECDSA_SIG_free(sig); - return fOk; +bool CPubKey::Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig) const { + if (!IsValid()) + return false; + CECKey key; + if (!key.SetPubKey(*this)) + return false; + if (!key.Verify(hash, vchSig)) + return false; + return true; } -// reconstruct public key from a compact signature -// This is only slightly more CPU intensive than just verifying it. -// If this function succeeds, the recovered public key is guaranteed to be valid -// (the signature is a valid signature of the given data for that key) -bool CKey::SetCompactSignature(uint256 hash, const std::vector<unsigned char>& vchSig) -{ +bool CPubKey::RecoverCompact(const uint256 &hash, const std::vector<unsigned char>& vchSig) { if (vchSig.size() != 65) return false; - int nV = vchSig[0]; - if (nV<27 || nV>=35) + CECKey key; + if (!key.Recover(hash, &vchSig[1], (vchSig[0] - 27) & ~4)) return false; - ECDSA_SIG *sig = ECDSA_SIG_new(); - BN_bin2bn(&vchSig[1],32,sig->r); - BN_bin2bn(&vchSig[33],32,sig->s); - - EC_KEY_free(pkey); - pkey = EC_KEY_new_by_curve_name(NID_secp256k1); - if (nV >= 31) - { - SetCompressedPubKey(); - nV -= 4; - } - if (ECDSA_SIG_recover_key_GFp(pkey, sig, (unsigned char*)&hash, sizeof(hash), nV - 27, 0) == 1) - { - fSet = true; - ECDSA_SIG_free(sig); - return true; - } - return false; + key.GetPubKey(*this, (vchSig[0] - 27) & 4); + return true; } -bool CKey::Verify(uint256 hash, const std::vector<unsigned char>& vchSig) -{ - // -1 = error, 0 = bad sig, 1 = good - if (ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], vchSig.size(), pkey) != 1) +bool CPubKey::VerifyCompact(const uint256 &hash, const std::vector<unsigned char>& vchSig) const { + if (!IsValid()) + return false; + if (vchSig.size() != 65) + return false; + CECKey key; + if (!key.Recover(hash, &vchSig[1], (vchSig[0] - 27) & ~4)) + return false; + CPubKey pubkeyRec; + key.GetPubKey(pubkeyRec, IsCompressed()); + if (*this != pubkeyRec) return false; - return true; } -bool CKey::VerifyCompact(uint256 hash, const std::vector<unsigned char>& vchSig) -{ - CKey key; - if (!key.SetCompactSignature(hash, vchSig)) +bool CPubKey::IsFullyValid() const { + if (!IsValid()) return false; - if (GetPubKey() != key.GetPubKey()) + CECKey key; + if (!key.SetPubKey(*this)) return false; - return true; } -bool CKey::IsValid() -{ - if (!fSet) +bool CPubKey::Decompress() { + if (!IsValid()) return false; - - if (!EC_KEY_check_key(pkey)) + CECKey key; + if (!key.SetPubKey(*this)) return false; - - bool fCompr; - CSecret secret = GetSecret(fCompr); - CKey key2; - key2.SetSecret(secret, fCompr); - return GetPubKey() == key2.GetPubKey(); + key.GetPubKey(*this, false); + return true; } @@ -1,11 +1,10 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2009-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_KEY_H #define BITCOIN_KEY_H -#include <stdexcept> #include <vector> #include "allocators.h" @@ -13,23 +12,6 @@ #include "uint256.h" #include "hash.h" -#include <openssl/ec.h> // for EC_KEY definition - -// secp160k1 -// const unsigned int PRIVATE_KEY_SIZE = 192; -// const unsigned int PUBLIC_KEY_SIZE = 41; -// const unsigned int SIGNATURE_SIZE = 48; -// -// secp192k1 -// const unsigned int PRIVATE_KEY_SIZE = 222; -// const unsigned int PUBLIC_KEY_SIZE = 49; -// const unsigned int SIGNATURE_SIZE = 57; -// -// secp224k1 -// const unsigned int PRIVATE_KEY_SIZE = 250; -// const unsigned int PUBLIC_KEY_SIZE = 57; -// const unsigned int SIGNATURE_SIZE = 66; -// // secp256k1: // const unsigned int PRIVATE_KEY_SIZE = 279; // const unsigned int PUBLIC_KEY_SIZE = 65; @@ -38,12 +20,6 @@ // see www.keylength.com // script supports up to 75 for single byte push -class key_error : public std::runtime_error -{ -public: - explicit key_error(const std::string& str) : std::runtime_error(str) {} -}; - /** A reference to a CKey: the Hash160 of its serialized public key */ class CKeyID : public uint160 { @@ -63,99 +39,218 @@ public: /** An encapsulated public key. */ class CPubKey { private: - std::vector<unsigned char> vchPubKey; - friend class CKey; + // Just store the serialized data. + // Its length can very cheaply be computed from the first byte. + unsigned char vch[65]; + + // Compute the length of a pubkey with a given first byte. + unsigned int static GetLen(unsigned char chHeader) { + if (chHeader == 2 || chHeader == 3) + return 33; + if (chHeader == 4 || chHeader == 6 || chHeader == 7) + return 65; + return 0; + } + + // Set this key data to be invalid + void Invalidate() { + vch[0] = 0xFF; + } public: - CPubKey() { } - CPubKey(const std::vector<unsigned char> &vchPubKeyIn) : vchPubKey(vchPubKeyIn) { } - friend bool operator==(const CPubKey &a, const CPubKey &b) { return a.vchPubKey == b.vchPubKey; } - friend bool operator!=(const CPubKey &a, const CPubKey &b) { return a.vchPubKey != b.vchPubKey; } - friend bool operator<(const CPubKey &a, const CPubKey &b) { return a.vchPubKey < b.vchPubKey; } + // Construct an invalid public key. + CPubKey() { + Invalidate(); + } - IMPLEMENT_SERIALIZE( - READWRITE(vchPubKey); - ) + // Initialize a public key using begin/end iterators to byte data. + template<typename T> + void Set(const T pbegin, const T pend) { + int len = pend == pbegin ? 0 : GetLen(pbegin[0]); + if (len && len == (pend-pbegin)) + memcpy(vch, (unsigned char*)&pbegin[0], len); + else + Invalidate(); + } + + // Construct a public key using begin/end iterators to byte data. + template<typename T> + CPubKey(const T pbegin, const T pend) { + Set(pbegin, pend); + } + + // Construct a public key from a byte vector. + CPubKey(const std::vector<unsigned char> &vch) { + Set(vch.begin(), vch.end()); + } + + // Simple read-only vector-like interface to the pubkey data. + unsigned int size() const { return GetLen(vch[0]); } + const unsigned char *begin() const { return vch; } + const unsigned char *end() const { return vch+size(); } + const unsigned char &operator[](unsigned int pos) const { return vch[pos]; } + + // Comparator implementation. + friend bool operator==(const CPubKey &a, const CPubKey &b) { + return a.vch[0] == b.vch[0] && + memcmp(a.vch, b.vch, a.size()) == 0; + } + friend bool operator!=(const CPubKey &a, const CPubKey &b) { + return !(a == b); + } + friend bool operator<(const CPubKey &a, const CPubKey &b) { + return a.vch[0] < b.vch[0] || + (a.vch[0] == b.vch[0] && memcmp(a.vch, b.vch, a.size()) < 0); + } + + // Implement serialization, as if this was a byte vector. + unsigned int GetSerializeSize(int nType, int nVersion) const { + return size() + 1; + } + template<typename Stream> void Serialize(Stream &s, int nType, int nVersion) const { + unsigned int len = size(); + ::WriteCompactSize(s, len); + s.write((char*)vch, len); + } + template<typename Stream> void Unserialize(Stream &s, int nType, int nVersion) { + unsigned int len = ::ReadCompactSize(s); + if (len <= 65) { + s.read((char*)vch, len); + } else { + // invalid pubkey, skip available data + char dummy; + while (len--) + s.read(&dummy, 1); + Invalidate(); + } + } + // Get the KeyID of this public key (hash of its serialization) CKeyID GetID() const { - return CKeyID(Hash160(vchPubKey)); + return CKeyID(Hash160(vch, vch+size())); } + // Get the 256-bit hash of this public key. uint256 GetHash() const { - return Hash(vchPubKey.begin(), vchPubKey.end()); + return Hash(vch, vch+size()); } + // just check syntactic correctness. bool IsValid() const { - return vchPubKey.size() == 33 || vchPubKey.size() == 65; + return size() > 0; } + // fully validate whether this is a valid public key (more expensive than IsValid()) + bool IsFullyValid() const; + + // Check whether this is a compressed public key. bool IsCompressed() const { - return vchPubKey.size() == 33; + return size() == 33; } - std::vector<unsigned char> Raw() const { - return vchPubKey; - } + // Verify a DER signature (~72 bytes). + // If this public key is not fully valid, the return value will be false. + bool Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig) const; + + // Verify a compact signature (~65 bytes). + // See CKey::SignCompact. + bool VerifyCompact(const uint256 &hash, const std::vector<unsigned char>& vchSig) const; + + // Recover a public key from a compact signature. + bool RecoverCompact(const uint256 &hash, const std::vector<unsigned char>& vchSig); + + // Turn this public key into an uncompressed public key. + bool Decompress(); }; // secure_allocator is defined in allocators.h // CPrivKey is a serialized private key, with all parameters included (279 bytes) typedef std::vector<unsigned char, secure_allocator<unsigned char> > CPrivKey; -// CSecret is a serialization of just the secret parameter (32 bytes) -typedef std::vector<unsigned char, secure_allocator<unsigned char> > CSecret; -/** An encapsulated OpenSSL Elliptic Curve key (public and/or private) */ -class CKey -{ -protected: - EC_KEY* pkey; - bool fSet; - bool fCompressedPubKey; +/** An encapsulated private key. */ +class CKey { +private: + // Whether this private key is valid. We check for correctness when modifying the key + // data, so fValid should always correspond to the actual state. + bool fValid; + // Whether the public key corresponding to this private key is (to be) compressed. + bool fCompressed; + + // The actual byte data + unsigned char vch[32]; + + // Check whether the 32-byte array pointed to be vch is valid keydata. + bool static Check(const unsigned char *vch); public: - void SetCompressedPubKey(bool fCompressed = true); - void Reset(); + // Construct an invalid private key. + CKey() : fValid(false) { + LockObject(vch); + } + + // Copy constructor. This is necessary because of memlocking. + CKey(const CKey &secret) : fValid(secret.fValid), fCompressed(secret.fCompressed) { + LockObject(vch); + memcpy(vch, secret.vch, sizeof(vch)); + } + + // Destructor (again necessary because of memlocking). + ~CKey() { + UnlockObject(vch); + } + + // Initialize using begin and end iterators to byte data. + template<typename T> + void Set(const T pbegin, const T pend, bool fCompressedIn) { + if (pend - pbegin != 32) { + fValid = false; + return; + } + if (Check(&pbegin[0])) { + memcpy(vch, (unsigned char*)&pbegin[0], 32); + fValid = true; + fCompressed = fCompressedIn; + } else { + fValid = false; + } + } - CKey(); - CKey(const CKey& b); + // Simple read-only vector-like interface. + unsigned int size() const { return (fValid ? 32 : 0); } + const unsigned char *begin() const { return vch; } + const unsigned char *end() const { return vch + size(); } - CKey& operator=(const CKey& b); + // Check whether this private key is valid. + bool IsValid() const { return fValid; } - ~CKey(); + // Check whether the public key corresponding to this private key is (to be) compressed. + bool IsCompressed() const { return fCompressed; } - bool IsNull() const; - bool IsCompressed() const; + // Initialize from a CPrivKey (serialized OpenSSL private key data). + bool SetPrivKey(const CPrivKey &vchPrivKey, bool fCompressed); + // Generate a new private key using a cryptographic PRNG. void MakeNewKey(bool fCompressed); - bool SetPrivKey(const CPrivKey& vchPrivKey); - bool SetSecret(const CSecret& vchSecret, bool fCompressed = false); - CSecret GetSecret(bool &fCompressed) const; + + // Convert the private key to a CPrivKey (serialized OpenSSL private key data). + // This is expensive. CPrivKey GetPrivKey() const; - bool SetPubKey(const CPubKey& vchPubKey); + + // Compute the public key from a private key. + // This is expensive. CPubKey GetPubKey() const; - bool Sign(uint256 hash, std::vector<unsigned char>& vchSig); + // Create a DER-serialized signature. + bool Sign(const uint256 &hash, std::vector<unsigned char>& vchSig) const; - // create a compact signature (65 bytes), which allows reconstructing the used public key + // Create a compact signature (65 bytes), which allows reconstructing the used public key. // The format is one header byte, followed by two times 32 bytes for the serialized r and s values. // The header byte: 0x1B = first key with even y, 0x1C = first key with odd y, - // 0x1D = second key with even y, 0x1E = second key with odd y - bool SignCompact(uint256 hash, std::vector<unsigned char>& vchSig); - - // reconstruct public key from a compact signature - // This is only slightly more CPU intensive than just verifying it. - // If this function succeeds, the recovered public key is guaranteed to be valid - // (the signature is a valid signature of the given data for that key) - bool SetCompactSignature(uint256 hash, const std::vector<unsigned char>& vchSig); - - bool Verify(uint256 hash, const std::vector<unsigned char>& vchSig); - - // Verify a compact signature - bool VerifyCompact(uint256 hash, const std::vector<unsigned char>& vchSig); - - bool IsValid(); + // 0x1D = second key with even y, 0x1E = second key with odd y, + // add 0x04 for compressed keys. + bool SignCompact(const uint256 &hash, std::vector<unsigned char>& vchSig) const; }; #endif diff --git a/src/keystore.cpp b/src/keystore.cpp index e0cf805a19..808f8c24ef 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -15,61 +15,50 @@ bool CKeyStore::GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const return true; } -bool CBasicKeyStore::AddKey(const CKey& key) +bool CKeyStore::AddKey(const CKey &key) { + return AddKeyPubKey(key, key.GetPubKey()); +} + +bool CBasicKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey) { - bool fCompressed = false; - CSecret secret = key.GetSecret(fCompressed); - { - LOCK(cs_KeyStore); - mapKeys[key.GetPubKey().GetID()] = make_pair(secret, fCompressed); - } + LOCK(cs_KeyStore); + mapKeys[pubkey.GetID()] = key; return true; } bool CBasicKeyStore::AddCScript(const CScript& redeemScript) { - { - LOCK(cs_KeyStore); - mapScripts[redeemScript.GetID()] = redeemScript; - } + LOCK(cs_KeyStore); + mapScripts[redeemScript.GetID()] = redeemScript; return true; } bool CBasicKeyStore::HaveCScript(const CScriptID& hash) const { - bool result; - { - LOCK(cs_KeyStore); - result = (mapScripts.count(hash) > 0); - } - return result; + LOCK(cs_KeyStore); + return mapScripts.count(hash) > 0; } - bool CBasicKeyStore::GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const { + LOCK(cs_KeyStore); + ScriptMap::const_iterator mi = mapScripts.find(hash); + if (mi != mapScripts.end()) { - LOCK(cs_KeyStore); - ScriptMap::const_iterator mi = mapScripts.find(hash); - if (mi != mapScripts.end()) - { - redeemScriptOut = (*mi).second; - return true; - } + redeemScriptOut = (*mi).second; + return true; } return false; } bool CCryptoKeyStore::SetCrypted() { - { - LOCK(cs_KeyStore); - if (fUseCrypto) - return true; - if (!mapKeys.empty()) - return false; - fUseCrypto = true; - } + LOCK(cs_KeyStore); + if (fUseCrypto) + return true; + if (!mapKeys.empty()) + return false; + fUseCrypto = true; return true; } @@ -99,14 +88,13 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) { const CPubKey &vchPubKey = (*mi).second.first; const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second; - CSecret vchSecret; + CKeyingMaterial vchSecret; if(!DecryptSecret(vMasterKeyIn, vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) return false; if (vchSecret.size() != 32) return false; CKey key; - key.SetPubKey(vchPubKey); - key.SetSecret(vchSecret); + key.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed()); if (key.GetPubKey() == vchPubKey) break; return false; @@ -117,23 +105,22 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) return true; } -bool CCryptoKeyStore::AddKey(const CKey& key) +bool CCryptoKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey) { { LOCK(cs_KeyStore); if (!IsCrypted()) - return CBasicKeyStore::AddKey(key); + return CBasicKeyStore::AddKeyPubKey(key, pubkey); if (IsLocked()) return false; std::vector<unsigned char> vchCryptedSecret; - CPubKey vchPubKey = key.GetPubKey(); - bool fCompressed; - if (!EncryptSecret(vMasterKey, key.GetSecret(fCompressed), vchPubKey.GetHash(), vchCryptedSecret)) + CKeyingMaterial vchSecret(key.begin(), key.end()); + if (!EncryptSecret(vMasterKey, vchSecret, pubkey.GetHash(), vchCryptedSecret)) return false; - if (!AddCryptedKey(key.GetPubKey(), vchCryptedSecret)) + if (!AddCryptedKey(pubkey, vchCryptedSecret)) return false; } return true; @@ -164,13 +151,12 @@ bool CCryptoKeyStore::GetKey(const CKeyID &address, CKey& keyOut) const { const CPubKey &vchPubKey = (*mi).second.first; const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second; - CSecret vchSecret; + CKeyingMaterial vchSecret; if (!DecryptSecret(vMasterKey, vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) return false; if (vchSecret.size() != 32) return false; - keyOut.SetPubKey(vchPubKey); - keyOut.SetSecret(vchSecret); + keyOut.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed()); return true; } } @@ -204,13 +190,11 @@ bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn) fUseCrypto = true; BOOST_FOREACH(KeyMap::value_type& mKey, mapKeys) { - CKey key; - if (!key.SetSecret(mKey.second.first, mKey.second.second)) - return false; - const CPubKey vchPubKey = key.GetPubKey(); + const CKey &key = mKey.second; + CPubKey vchPubKey = key.GetPubKey(); + CKeyingMaterial vchSecret(key.begin(), key.end()); std::vector<unsigned char> vchCryptedSecret; - bool fCompressed; - if (!EncryptSecret(vMasterKeyIn, key.GetSecret(fCompressed), vchPubKey.GetHash(), vchCryptedSecret)) + if (!EncryptSecret(vMasterKeyIn, vchSecret, vchPubKey.GetHash(), vchCryptedSecret)) return false; if (!AddCryptedKey(vchPubKey, vchCryptedSecret)) return false; diff --git a/src/keystore.h b/src/keystore.h index ab369bbf47..49a7bf569d 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -21,7 +21,8 @@ public: virtual ~CKeyStore() {} // Add a key to the store. - virtual bool AddKey(const CKey& key) =0; + virtual bool AddKeyPubKey(const CKey &key, const CPubKey &pubkey) =0; + virtual bool AddKey(const CKey &key); // Check whether a key corresponding to a given address is present in the store. virtual bool HaveKey(const CKeyID &address) const =0; @@ -33,18 +34,9 @@ public: virtual bool AddCScript(const CScript& redeemScript) =0; virtual bool HaveCScript(const CScriptID &hash) const =0; virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const =0; - - virtual bool GetSecret(const CKeyID &address, CSecret& vchSecret, bool &fCompressed) const - { - CKey key; - if (!GetKey(address, key)) - return false; - vchSecret = key.GetSecret(fCompressed); - return true; - } }; -typedef std::map<CKeyID, std::pair<CSecret, bool> > KeyMap; +typedef std::map<CKeyID, CKey> KeyMap; typedef std::map<CScriptID, CScript > ScriptMap; /** Basic key store, that keeps keys in an address->secret map */ @@ -55,7 +47,7 @@ protected: ScriptMap mapScripts; public: - bool AddKey(const CKey& key); + bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey); bool HaveKey(const CKeyID &address) const { bool result; @@ -85,8 +77,7 @@ public: KeyMap::const_iterator mi = mapKeys.find(address); if (mi != mapKeys.end()) { - keyOut.Reset(); - keyOut.SetSecret((*mi).second.first, (*mi).second.second); + keyOut = mi->second; return true; } } @@ -146,7 +137,7 @@ public: bool Lock(); virtual bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret); - bool AddKey(const CKey& key); + bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey); bool HaveKey(const CKeyID &address) const { { diff --git a/src/main.cpp b/src/main.cpp index b7efac53b1..a98b83b6af 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -31,6 +31,7 @@ CTxMemPool mempool; unsigned int nTransactionsUpdated = 0; map<uint256, CBlockIndex*> mapBlockIndex; +std::vector<CBlockIndex*> vBlockIndexByHeight; uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); static CBigNum bnProofOfWorkLimit(~uint256(0) >> 32); CBlockIndex* pindexGenesisBlock = NULL; @@ -1036,19 +1037,9 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock static CBlockIndex* pblockindexFBBHLast; CBlockIndex* FindBlockByHeight(int nHeight) { - CBlockIndex *pblockindex; - if (nHeight < nBestHeight / 2) - pblockindex = pindexGenesisBlock; - else - pblockindex = pindexBest; - if (pblockindexFBBHLast && abs(nHeight - pblockindex->nHeight) > abs(nHeight - pblockindexFBBHLast->nHeight)) - pblockindex = pblockindexFBBHLast; - while (pblockindex->nHeight > nHeight) - pblockindex = pblockindex->pprev; - while (pblockindex->nHeight < nHeight) - pblockindex = pblockindex->pnext; - pblockindexFBBHLast = pblockindex; - return pblockindex; + if (nHeight >= (int)vBlockIndexByHeight.size()) + return NULL; + return vBlockIndexByHeight[nHeight]; } bool CBlock::ReadFromDisk(const CBlockIndex* pindex) @@ -1231,7 +1222,7 @@ void static InvalidBlockFound(CBlockIndex *pindex) { pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex)); setBlockIndexValid.erase(pindex); InvalidChainFound(pindex); - if (pindex->pnext) { + if (pindex->GetNextInMainChain()) { CValidationState stateDummy; ConnectBestBlock(stateDummy); // reorganise away from the failed block } @@ -1271,7 +1262,7 @@ bool ConnectBestBlock(CValidationState &state) { if (pindexBest == NULL || pindexTest->nChainWork > pindexBest->nChainWork) vAttach.push_back(pindexTest); - if (pindexTest->pprev == NULL || pindexTest->pnext != NULL) { + if (pindexTest->pprev == NULL || pindexTest->GetNextInMainChain()) { reverse(vAttach.begin(), vAttach.end()); BOOST_FOREACH(CBlockIndex *pindexSwitch, vAttach) { boost::this_thread::interruption_point(); @@ -1846,15 +1837,10 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) // At this point, all changes have been done to the database. // Proceed by updating the memory structures. - // Disconnect shorter branch - BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) - if (pindex->pprev) - pindex->pprev->pnext = NULL; - - // Connect longer branch + // Register new best chain + vBlockIndexByHeight.resize(pindexNew->nHeight + 1); BOOST_FOREACH(CBlockIndex* pindex, vConnect) - if (pindex->pprev) - pindex->pprev->pnext = pindex; + vBlockIndexByHeight[pindex->nHeight] = pindex; // Resurrect memory transactions that were in the disconnected branch BOOST_FOREACH(CTransaction& tx, vResurrect) { @@ -2073,25 +2059,6 @@ bool CBlock::CheckBlock(CValidationState &state, bool fCheckPOW, bool fCheckMerk if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) return state.DoS(100, error("CheckBlock() : size limits failed")); - // Special short-term limits to avoid 10,000 BDB lock limit: - if (GetBlockTime() >= 1363867200 && // start enforcing 21 March 2013, noon GMT - GetBlockTime() < 1368576000) // stop enforcing 15 May 2013 00:00:00 - { - // Rule is: #unique txids referenced <= 4,500 - // ... to prevent 10,000 BDB lock exhaustion on old clients - set<uint256> setTxIn; - for (size_t i = 0; i < vtx.size(); i++) - { - setTxIn.insert(vtx[i].GetHash()); - if (i == 0) continue; // skip coinbase txin - BOOST_FOREACH(const CTxIn& txin, vtx[i].vin) - setTxIn.insert(txin.prevout.hash); - } - size_t nTxids = setTxIn.size(); - if (nTxids > 4500) - return error("CheckBlock() : 15 May maxlocks violation"); - } - // Check proof of work matches claimed amount if (fCheckPOW && !CheckProofOfWork(GetHash(), nBits)) return state.DoS(50, error("CheckBlock() : proof of work failed")); @@ -2606,12 +2573,12 @@ bool static LoadBlockIndexDB() nBestHeight = pindexBest->nHeight; nBestChainWork = pindexBest->nChainWork; - // set 'next' pointers in best chain + // register best chain CBlockIndex *pindex = pindexBest; - while(pindex != NULL && pindex->pprev != NULL) { - CBlockIndex *pindexPrev = pindex->pprev; - pindexPrev->pnext = pindex; - pindex = pindexPrev; + vBlockIndexByHeight.resize(pindexBest->nHeight + 1); + while(pindex != NULL) { + vBlockIndexByHeight[pindex->nHeight] = pindex; + pindex = pindex->pprev; } printf("LoadBlockIndexDB(): hashBestChain=%s height=%d date=%s\n", hashBestChain.ToString().c_str(), nBestHeight, @@ -2680,7 +2647,7 @@ bool VerifyDB() { CBlockIndex *pindex = pindexState; while (pindex != pindexBest) { boost::this_thread::interruption_point(); - pindex = pindex->pnext; + pindex = pindex->GetNextInMainChain(); CBlock block; if (!block.ReadFromDisk(pindex)) return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); @@ -2856,7 +2823,7 @@ void PrintBlockTree() vector<CBlockIndex*>& vNext = mapNext[pindex]; for (unsigned int i = 0; i < vNext.size(); i++) { - if (vNext[i]->pnext) + if (vNext[i]->GetNextInMainChain()) { swap(vNext[0], vNext[i]); break; @@ -2964,7 +2931,7 @@ string GetWarnings(string strFor) string strStatusBar; string strRPC; - if (GetBoolArg("-testsafemode")) + if (GetBoolArg("-testsafemode", false)) strRPC = "test"; if (!CLIENT_VERSION_IS_RELEASE) @@ -3437,10 +3404,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // Send the rest of the chain if (pindex) - pindex = pindex->pnext; + pindex = pindex->GetNextInMainChain(); int nLimit = 500; printf("getblocks %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().c_str(), nLimit); - for (; pindex; pindex = pindex->pnext) + for (; pindex; pindex = pindex->GetNextInMainChain()) { if (pindex->GetBlockHash() == hashStop) { @@ -3480,14 +3447,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // Find the last block the caller has in the main chain pindex = locator.GetBlockIndex(); if (pindex) - pindex = pindex->pnext; + pindex = pindex->GetNextInMainChain(); } // 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().c_str()); - for (; pindex; pindex = pindex->pnext) + for (; pindex; pindex = pindex->GetNextInMainChain()) { vHeaders.push_back(pindex->GetBlockHeader()); if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop) @@ -4188,10 +4155,6 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey) // Limit to betweeen 1K and MAX_BLOCK_SIZE-1K for sanity: nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE-1000), nBlockMaxSize)); - // Special compatibility rule before 15 May: limit size to 500,000 bytes: - if (GetAdjustedTime() < 1368576000) - nBlockMaxSize = std::min(nBlockMaxSize, (unsigned int)(MAX_BLOCK_SIZE_GEN)); - // How much of the block should be dedicated to high-priority transactions, // included regardless of the fees they pay unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", 27000); @@ -4212,7 +4175,7 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey) // Priority order to process transactions list<COrphan> vOrphan; // list memory doesn't move map<uint256, vector<COrphan*> > mapDependers; - bool fPrintPriority = GetBoolArg("-printpriority"); + bool fPrintPriority = GetBoolArg("-printpriority", false); // This vector will be sorted into a priority queue: vector<TxPriority> vecPriority; diff --git a/src/main.h b/src/main.h index 8e71e66cc8..679ae75a47 100644 --- a/src/main.h +++ b/src/main.h @@ -69,6 +69,7 @@ extern CScript COINBASE_FLAGS; extern CCriticalSection cs_main; extern std::map<uint256, CBlockIndex*> mapBlockIndex; +extern std::vector<CBlockIndex*> vBlockIndexByHeight; extern std::set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexValid; extern uint256 hashGenesisBlock; extern CBlockIndex* pindexGenesisBlock; @@ -1589,10 +1590,8 @@ enum BlockStatus { /** The block chain is a tree shaped structure starting with the * genesis block at the root, with each block potentially having multiple - * candidates to be the next block. pprev and pnext link a path through the - * main/longest chain. A blockindex may have multiple pprev pointing back - * to it, but pnext will only point forward to the longest branch, or will - * be null if the block is not part of the longest chain. + * candidates to be the next block. A blockindex may have multiple pprev pointing + * to it, but at most one of them can be part of the currently active branch. */ class CBlockIndex { @@ -1603,9 +1602,6 @@ public: // pointer to the index of the predecessor of this block CBlockIndex* pprev; - // (memory only) pointer to the index of the *active* successor of this block - CBlockIndex* pnext; - // height of the entry in the chain. The genesis block has height 0 int nHeight; @@ -1643,7 +1639,6 @@ public: { phashBlock = NULL; pprev = NULL; - pnext = NULL; nHeight = 0; nFile = 0; nDataPos = 0; @@ -1664,7 +1659,6 @@ public: { phashBlock = NULL; pprev = NULL; - pnext = NULL; nHeight = 0; nFile = 0; nDataPos = 0; @@ -1733,7 +1727,11 @@ public: bool IsInMainChain() const { - return (pnext || this == pindexBest); + return nHeight < (int)vBlockIndexByHeight.size() && vBlockIndexByHeight[nHeight] == this; + } + + CBlockIndex *GetNextInMainChain() const { + return nHeight+1 >= (int)vBlockIndexByHeight.size() ? NULL : vBlockIndexByHeight[nHeight+1]; } bool CheckIndex() const @@ -1762,9 +1760,9 @@ public: const CBlockIndex* pindex = this; for (int i = 0; i < nMedianTimeSpan/2; i++) { - if (!pindex->pnext) + if (!pindex->GetNextInMainChain()) return GetBlockTime(); - pindex = pindex->pnext; + pindex = pindex->GetNextInMainChain(); } return pindex->GetMedianTimePast(); } @@ -1779,7 +1777,7 @@ public: std::string ToString() const { return strprintf("CBlockIndex(pprev=%p, pnext=%p, nHeight=%d, merkle=%s, hashBlock=%s)", - pprev, pnext, nHeight, + pprev, GetNextInMainChain(), nHeight, hashMerkleRoot.ToString().c_str(), GetBlockHash().ToString().c_str()); } diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index 8529c88b39..8906174d7d 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -143,8 +143,13 @@ void AddressBookPage::setModel(AddressTableModel *model) ui->tableView->sortByColumn(0, Qt::AscendingOrder); // Set column widths +#if QT_VERSION < 0x050000 ui->tableView->horizontalHeader()->setResizeMode(AddressTableModel::Label, QHeaderView::Stretch); ui->tableView->horizontalHeader()->setResizeMode(AddressTableModel::Address, QHeaderView::ResizeToContents); +#else + ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Label, QHeaderView::Stretch); + ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Address, QHeaderView::ResizeToContents); +#endif connect(ui->tableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(selectionChanged())); diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index f525d1bb38..9bcac59798 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -17,16 +17,14 @@ #include "splashscreen.h" #include <QMessageBox> +#if QT_VERSION < 0x050000 #include <QTextCodec> +#endif #include <QLocale> #include <QTimer> #include <QTranslator> #include <QLibraryInfo> -#ifdef Q_OS_MAC -#include "macdockiconhandler.h" -#endif - #if defined(BITCOIN_NEED_QT_PLUGINS) && !defined(_BITCOIN_QT_PLUGINS_INCLUDED) #define _BITCOIN_QT_PLUGINS_INCLUDED #define __INSURE__ @@ -118,9 +116,11 @@ int main(int argc, char *argv[]) // Command-line options take precedence: ParseParameters(argc, argv); +#if QT_VERSION < 0x050000 // Internal string conversion is all UTF-8 QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8")); QTextCodec::setCodecForCStrings(QTextCodec::codecForTr()); +#endif Q_INIT_RESOURCE(bitcoin); QApplication app(argc, argv); @@ -152,7 +152,7 @@ int main(int argc, char *argv[]) // as it is used to locate QSettings) QApplication::setOrganizationName("Bitcoin"); QApplication::setOrganizationDomain("bitcoin.org"); - if(GetBoolArg("-testnet")) // Separate UI settings for testnet + if (GetBoolArg("-testnet", false)) // Separate UI settings for testnet QApplication::setApplicationName("Bitcoin-Qt-testnet"); else QApplication::setApplicationName("Bitcoin-Qt"); @@ -202,15 +202,8 @@ int main(int argc, char *argv[]) return 1; } -#ifdef Q_OS_MAC - // on mac, also change the icon now because it would look strange to have a testnet splash (green) and a std app icon (orange) - if(GetBoolArg("-testnet")) { - MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin_testnet")); - } -#endif - SplashScreen splash(QPixmap(), 0); - if (GetBoolArg("-splash", true) && !GetBoolArg("-min")) + if (GetBoolArg("-splash", true) && !GetBoolArg("-min", false)) { splash.show(); splash.setAutoFillBackground(true); @@ -228,7 +221,7 @@ int main(int argc, char *argv[]) boost::thread_group threadGroup; - BitcoinGUI window; + BitcoinGUI window(GetBoolArg("-testnet", false), 0); guiref = &window; QTimer* pollShutdownTimer = new QTimer(guiref); @@ -254,7 +247,7 @@ int main(int argc, char *argv[]) window.setCurrentWallet("~Default"); // If -min option passed, start window minimized. - if(GetBoolArg("-min")) + if(GetBoolArg("-min", false)) { window.showMinimized(); } diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 78a69af8b0..190da6caf8 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -44,7 +44,9 @@ #include <QMovie> #include <QTimer> #include <QDragEnterEvent> +#if QT_VERSION < 0x050000 #include <QUrl> +#endif #include <QMimeData> #include <QStyle> #include <QSettings> @@ -55,7 +57,7 @@ const QString BitcoinGUI::DEFAULT_WALLET = "~Default"; -BitcoinGUI::BitcoinGUI(QWidget *parent) : +BitcoinGUI::BitcoinGUI(bool fIsTestnet, QWidget *parent) : QMainWindow(parent), clientModel(0), encryptWalletAction(0), @@ -67,14 +69,30 @@ BitcoinGUI::BitcoinGUI(QWidget *parent) : prevBlocks(0) { restoreWindowGeometry(); - setWindowTitle(tr("Bitcoin") + " - " + tr("Wallet")); + #ifndef Q_OS_MAC - QApplication::setWindowIcon(QIcon(":icons/bitcoin")); - setWindowIcon(QIcon(":icons/bitcoin")); + if (!fIsTestnet) + { + setWindowTitle(tr("Bitcoin") + " - " + tr("Wallet")); + QApplication::setWindowIcon(QIcon(":icons/bitcoin")); + setWindowIcon(QIcon(":icons/bitcoin")); + } + else + { + setWindowTitle(tr("Bitcoin") + " - " + tr("Wallet") + " " + tr("[testnet]")); + QApplication::setWindowIcon(QIcon(":icons/bitcoin_testnet")); + setWindowIcon(QIcon(":icons/bitcoin_testnet")); + } #else setUnifiedTitleAndToolBarOnMac(true); QApplication::setAttribute(Qt::AA_DontShowIconsInMenus); + + if (!fIsTestnet) + MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin")); + else + MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin_testnet")); #endif + // Create wallet frame and make it the central widget walletFrame = new WalletFrame(this); setCentralWidget(walletFrame); @@ -84,7 +102,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent) : // Create actions for the toolbar, menu bar and tray/dock icon // Needs walletFrame to be initialized - createActions(); + createActions(fIsTestnet); // Create application menu bar createMenuBar(); @@ -93,7 +111,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent) : createToolBars(); // Create system tray icon and notification - createTrayIcon(); + createTrayIcon(fIsTestnet); // Create status bar statusBar(); @@ -157,7 +175,7 @@ BitcoinGUI::~BitcoinGUI() #endif } -void BitcoinGUI::createActions() +void BitcoinGUI::createActions(bool fIsTestnet) { QActionGroup *tabGroup = new QActionGroup(this); @@ -211,7 +229,10 @@ void BitcoinGUI::createActions() 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); + if (!fIsTestnet) + aboutAction = new QAction(QIcon(":/icons/bitcoin"), tr("&About Bitcoin"), this); + else + aboutAction = new QAction(QIcon(":/icons/bitcoin_testnet"), tr("&About Bitcoin"), this); 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); @@ -220,7 +241,10 @@ void BitcoinGUI::createActions() optionsAction = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); optionsAction->setStatusTip(tr("Modify configuration options for Bitcoin")); optionsAction->setMenuRole(QAction::PreferencesRole); - toggleHideAction = new QAction(QIcon(":/icons/bitcoin"), tr("&Show / Hide"), this); + if (!fIsTestnet) + toggleHideAction = new QAction(QIcon(":/icons/bitcoin"), tr("&Show / Hide"), this); + else + toggleHideAction = new QAction(QIcon(":/icons/bitcoin_testnet"), tr("&Show / Hide"), this); toggleHideAction->setStatusTip(tr("Show or hide the main Window")); encryptWalletAction = new QAction(QIcon(":/icons/lock_closed"), tr("&Encrypt Wallet..."), this); @@ -297,27 +321,6 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel) this->clientModel = clientModel; if(clientModel) { - // Replace some strings and icons, when using the testnet - if(clientModel->isTestNet()) - { - setWindowTitle(windowTitle() + QString(" ") + tr("[testnet]")); -#ifndef Q_OS_MAC - QApplication::setWindowIcon(QIcon(":icons/bitcoin_testnet")); - setWindowIcon(QIcon(":icons/bitcoin_testnet")); -#else - MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin_testnet")); -#endif - if(trayIcon) - { - // 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")); - 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 createTrayIconMenu(); @@ -352,13 +355,22 @@ void BitcoinGUI::removeAllWallets() walletFrame->removeAllWallets(); } -void BitcoinGUI::createTrayIcon() +void BitcoinGUI::createTrayIcon(bool fIsTestnet) { #ifndef Q_OS_MAC trayIcon = new QSystemTrayIcon(this); - trayIcon->setToolTip(tr("Bitcoin client")); - trayIcon->setIcon(QIcon(":/icons/toolbar")); + if (!fIsTestnet) + { + trayIcon->setToolTip(tr("Bitcoin client")); + trayIcon->setIcon(QIcon(":/icons/toolbar")); + } + else + { + trayIcon->setToolTip(tr("Bitcoin client") + " " + tr("[testnet]")); + trayIcon->setIcon(QIcon(":/icons/toolbar_testnet")); + } + trayIcon->show(); #endif diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index f361a62a41..685ce8b430 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -43,7 +43,7 @@ class BitcoinGUI : public QMainWindow public: static const QString DEFAULT_WALLET; - explicit BitcoinGUI(QWidget *parent = 0); + explicit BitcoinGUI(bool fIsTestnet = false, QWidget *parent = 0); ~BitcoinGUI(); /** Set the client model. @@ -113,13 +113,13 @@ private: int prevBlocks; /** Create the main UI actions. */ - void createActions(); + void createActions(bool fIsTestnet); /** Create the menu bar and sub-menus. */ void createMenuBar(); /** Create the toolbars */ void createToolBars(); /** Create system tray icon and notification */ - void createTrayIcon(); + void createTrayIcon(bool fIsTestnet); /** Create system tray menu (or setup the dock menu) */ void createTrayIconMenu(); /** Save window size and position */ diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 2105f0730e..6937febfee 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -13,8 +13,12 @@ #include <QDoubleValidator> #include <QFont> #include <QLineEdit> +#if QT_VERSION >= 0x050000 +#include <QUrlQuery> +#else #include <QUrl> -#include <QTextDocument> // For Qt::escape +#endif +#include <QTextDocument> // for Qt::mightBeRichText #include <QAbstractItemView> #include <QClipboard> #include <QFileDialog> @@ -86,7 +90,13 @@ bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out) SendCoinsRecipient rv; rv.address = uri.path(); rv.amount = 0; + +#if QT_VERSION < 0x050000 QList<QPair<QString, QString> > items = uri.queryItems(); +#else + QUrlQuery uriQuery(uri); + QList<QPair<QString, QString> > items = uriQuery.queryItems(); +#endif for (QList<QPair<QString, QString> >::iterator i = items.begin(); i != items.end(); i++) { bool fShouldReturnFalse = false; @@ -139,7 +149,11 @@ bool parseBitcoinURI(QString uri, SendCoinsRecipient *out) QString HtmlEscape(const QString& str, bool fMultiLine) { +#if QT_VERSION < 0x050000 QString escaped = Qt::escape(str); +#else + QString escaped = str.toHtmlEscaped(); +#endif if(fMultiLine) { escaped = escaped.replace("\n", "<br>\n"); @@ -176,7 +190,11 @@ QString getSaveFileName(QWidget *parent, const QString &caption, QString myDir; if(dir.isEmpty()) // Default to user documents location { +#if QT_VERSION < 0x050000 myDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); +#else + myDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); +#endif } else { diff --git a/src/qt/locale/bitcoin_ar.ts b/src/qt/locale/bitcoin_ar.ts index 028080d545..741a25a4da 100644 --- a/src/qt/locale/bitcoin_ar.ts +++ b/src/qt/locale/bitcoin_ar.ts @@ -38,7 +38,7 @@ This product includes software developed by the OpenSSL Project for use in the O <message> <location filename="../forms/addressbookpage.ui" line="+14"/> <source>Address Book</source> - <translation>كتاب العنوانين</translation> + <translation>دفتر العناوين</translation> </message> <message> <location line="+19"/> diff --git a/src/qt/macdockiconhandler.mm b/src/qt/macdockiconhandler.mm index b6ea8e1d03..53b49c42e6 100644 --- a/src/qt/macdockiconhandler.mm +++ b/src/qt/macdockiconhandler.mm @@ -2,8 +2,8 @@ #include <QMenu> #include <QWidget> - -extern void qt_mac_set_dock_menu(QMenu*); +#include <QTemporaryFile> +#include <QImageWriter> #undef slots #include <Cocoa/Cocoa.h> @@ -47,11 +47,11 @@ extern void qt_mac_set_dock_menu(QMenu*); MacDockIconHandler::MacDockIconHandler() : QObject() { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - this->m_dockIconClickEventHandler = [[DockIconClickEventHandler alloc] initWithDockIconHandler:this]; + this->m_dockIconClickEventHandler = [[DockIconClickEventHandler alloc] initWithDockIconHandler:this]; this->m_dummyWidget = new QWidget(); this->m_dockMenu = new QMenu(this->m_dummyWidget); - qt_mac_set_dock_menu(this->m_dockMenu); + [pool release]; } @@ -74,15 +74,29 @@ QMenu *MacDockIconHandler::dockMenu() void MacDockIconHandler::setIcon(const QIcon &icon) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSImage *image; + NSImage *image = nil; if (icon.isNull()) image = [[NSImage imageNamed:@"NSApplicationIcon"] retain]; else { + // generate NSImage from QIcon and use this as dock icon. QSize size = icon.actualSize(QSize(128, 128)); QPixmap pixmap = icon.pixmap(size); - CGImageRef cgImage = pixmap.toMacCGImageRef(); - image = [[NSImage alloc] initWithCGImage:cgImage size:NSZeroSize]; - CFRelease(cgImage); + + // write temp file hack (could also be done through QIODevice [memory]) + QTemporaryFile notificationIconFile; + if (!pixmap.isNull() && notificationIconFile.open()) { + QImageWriter writer(¬ificationIconFile, "PNG"); + if (writer.write(pixmap.toImage())) { + const char *cString = notificationIconFile.fileName().toUtf8().data(); + NSString *macString = [NSString stringWithCString:cString encoding:NSUTF8StringEncoding]; + image = [[NSImage alloc] initWithContentsOfFile:macString]; + } + } + + if(!image) { + // if testnet image could not be created, load std. app icon + image = [[NSImage imageNamed:@"NSApplicationIcon"] retain]; + } } [NSApp setApplicationIconImage:image]; diff --git a/src/qt/macnotificationhandler.h b/src/qt/macnotificationhandler.h new file mode 100644 index 0000000000..cd8064c61c --- /dev/null +++ b/src/qt/macnotificationhandler.h @@ -0,0 +1,25 @@ +#ifndef MACNOTIFICATIONHANDLER_H +#define MACNOTIFICATIONHANDLER_H +#include <QObject> + +/** Macintosh-specific notification handler (supports UserNotificationCenter and Growl). + */ +class MacNotificationHandler : public QObject +{ + Q_OBJECT + +public: + /** shows a 10.8+ UserNotification in the UserNotificationCenter + */ + void showNotification(const QString &title, const QString &text); + + /** executes AppleScript */ + void sendAppleScript(const QString &script); + + /** check if OS can handle UserNotifications */ + bool hasUserNotificationCenterSupport(void); + static MacNotificationHandler *instance(); +}; + + +#endif // MACNOTIFICATIONHANDLER_H diff --git a/src/qt/macnotificationhandler.mm b/src/qt/macnotificationhandler.mm new file mode 100644 index 0000000000..8bb9b887a1 --- /dev/null +++ b/src/qt/macnotificationhandler.mm @@ -0,0 +1,65 @@ +#include "macnotificationhandler.h" + +#undef slots +#include <Cocoa/Cocoa.h> + +void MacNotificationHandler::showNotification(const QString &title, const QString &text) +{ + // check if users OS has support for NSUserNotification + if(this->hasUserNotificationCenterSupport()) { + // okay, seems like 10.8+ + QByteArray utf8 = title.toUtf8(); + char* cString = (char *)utf8.constData(); + NSString *titleMac = [[NSString alloc] initWithUTF8String:cString]; + + utf8 = text.toUtf8(); + cString = (char *)utf8.constData(); + NSString *textMac = [[NSString alloc] initWithUTF8String:cString]; + + // do everything weak linked (because we will keep <10.8 compatibility) + id userNotification = [[NSClassFromString(@"NSUserNotification") alloc] init]; + [userNotification performSelector:@selector(setTitle:) withObject:titleMac]; + [userNotification performSelector:@selector(setInformativeText:) withObject:textMac]; + + id notificationCenterInstance = [NSClassFromString(@"NSUserNotificationCenter") performSelector:@selector(defaultUserNotificationCenter)]; + [notificationCenterInstance performSelector:@selector(deliverNotification:) withObject:userNotification]; + + [titleMac release]; + [textMac release]; + [userNotification release]; + } +} + +// sendAppleScript just take a QString and executes it as apple script +void MacNotificationHandler::sendAppleScript(const QString &script) +{ + QByteArray utf8 = script.toUtf8(); + char* cString = (char *)utf8.constData(); + NSString *scriptApple = [[NSString alloc] initWithUTF8String:cString]; + + NSAppleScript *as = [[NSAppleScript alloc] initWithSource:scriptApple]; + NSDictionary *err = nil; + [as executeAndReturnError:&err]; + [as release]; + [scriptApple release]; +} + +bool MacNotificationHandler::hasUserNotificationCenterSupport(void) +{ + Class possibleClass = NSClassFromString(@"NSUserNotificationCenter"); + + // check if users OS has support for NSUserNotification + if(possibleClass!=nil) { + return true; + } + return false; +} + + +MacNotificationHandler *MacNotificationHandler::instance() +{ + static MacNotificationHandler *s_instance = NULL; + if (!s_instance) + s_instance = new MacNotificationHandler(); + return s_instance; +} diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp index 397e7aa4a0..7cfaef6079 100644 --- a/src/qt/notificator.cpp +++ b/src/qt/notificator.cpp @@ -18,7 +18,7 @@ #ifdef Q_OS_MAC #include <ApplicationServices/ApplicationServices.h> -extern bool qt_mac_execute_apple_script(const QString &script, AEDesc *ret); +#include "macnotificationhandler.h" #endif // https://wiki.ubuntu.com/NotificationDevelopmentGuidelines recommends at least 128 @@ -47,19 +47,25 @@ Notificator::Notificator(const QString &programName, QSystemTrayIcon *trayicon, } #endif #ifdef Q_OS_MAC - // Check if Growl is installed (based on Qt's tray icon implementation) - CFURLRef cfurl; - OSStatus status = LSGetApplicationForInfo(kLSUnknownType, kLSUnknownCreator, CFSTR("growlTicket"), kLSRolesAll, 0, &cfurl); - if (status != kLSApplicationNotFoundErr) { - CFBundleRef bundle = CFBundleCreate(0, cfurl); - if (CFStringCompare(CFBundleGetIdentifier(bundle), CFSTR("com.Growl.GrowlHelperApp"), kCFCompareCaseInsensitive | kCFCompareBackwards) == kCFCompareEqualTo) { - if (CFStringHasSuffix(CFURLGetString(cfurl), CFSTR("/Growl.app/"))) - mode = Growl13; - else - mode = Growl12; + // check if users OS has support for NSUserNotification + if( MacNotificationHandler::instance()->hasUserNotificationCenterSupport()) { + mode = UserNotificationCenter; + } + else { + // Check if Growl is installed (based on Qt's tray icon implementation) + CFURLRef cfurl; + OSStatus status = LSGetApplicationForInfo(kLSUnknownType, kLSUnknownCreator, CFSTR("growlTicket"), kLSRolesAll, 0, &cfurl); + if (status != kLSApplicationNotFoundErr) { + CFBundleRef bundle = CFBundleCreate(0, cfurl); + if (CFStringCompare(CFBundleGetIdentifier(bundle), CFSTR("com.Growl.GrowlHelperApp"), kCFCompareCaseInsensitive | kCFCompareBackwards) == kCFCompareEqualTo) { + if (CFStringHasSuffix(CFURLGetString(cfurl), CFSTR("/Growl.app/"))) + mode = Growl13; + else + mode = Growl12; + } + CFRelease(cfurl); + CFRelease(bundle); } - CFRelease(cfurl); - CFRelease(bundle); } #endif } @@ -269,8 +275,14 @@ void Notificator::notifyGrowl(Class cls, const QString &title, const QString &te quotedTitle.replace("\\", "\\\\").replace("\"", "\\"); quotedText.replace("\\", "\\\\").replace("\"", "\\"); QString growlApp(this->mode == Notificator::Growl13 ? "Growl" : "GrowlHelperApp"); - qt_mac_execute_apple_script(script.arg(notificationApp, quotedTitle, quotedText, notificationIcon, growlApp), 0); + MacNotificationHandler::instance()->sendAppleScript(script.arg(notificationApp, quotedTitle, quotedText, notificationIcon, growlApp)); +} + +void Notificator::notifyMacUserNotificationCenter(Class cls, const QString &title, const QString &text, const QIcon &icon) { + // icon is not supported by the user notification center yet. OSX will use the app icon. + MacNotificationHandler::instance()->showNotification(title, text); } + #endif void Notificator::notify(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout) @@ -286,6 +298,9 @@ void Notificator::notify(Class cls, const QString &title, const QString &text, c notifySystray(cls, title, text, icon, millisTimeout); break; #ifdef Q_OS_MAC + case UserNotificationCenter: + notifyMacUserNotificationCenter(cls, title, text, icon); + break; case Growl12: case Growl13: notifyGrowl(cls, title, text, icon); diff --git a/src/qt/notificator.h b/src/qt/notificator.h index d20673abb6..d1fe37fea5 100644 --- a/src/qt/notificator.h +++ b/src/qt/notificator.h @@ -46,11 +46,12 @@ 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 */ - Growl12, /**< Use the Growl 1.2 notification system (Mac only) */ - Growl13 /**< Use the Growl 1.3 notification system (Mac only) */ + 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) */ + UserNotificationCenter /**< Use the 10.8+ User Notification Center (Mac only) */ }; QString programName; Mode mode; @@ -63,6 +64,7 @@ private: void notifySystray(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout); #ifdef Q_OS_MAC void notifyGrowl(Class cls, const QString &title, const QString &text, const QIcon &icon); + void notifyMacUserNotificationCenter(Class cls, const QString &title, const QString &text, const QIcon &icon); #endif }; diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index a92e6fc329..0b0bce55bb 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -18,7 +18,9 @@ #include <QLocalServer> #include <QLocalSocket> #include <QStringList> +#if QT_VERSION < 0x050000 #include <QUrl> +#endif using namespace boost; diff --git a/src/qt/qrcodedialog.cpp b/src/qt/qrcodedialog.cpp index ca94975128..6ddcaaf5d9 100644 --- a/src/qt/qrcodedialog.cpp +++ b/src/qt/qrcodedialog.cpp @@ -7,7 +7,9 @@ #include "optionsmodel.h" #include <QPixmap> +#if QT_VERSION < 0x050000 #include <QUrl> +#endif #include <qrencode.h> diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 50f22b877a..7f90f4a551 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -8,7 +8,9 @@ #include <QTime> #include <QThread> #include <QKeyEvent> +#if QT_VERSION < 0x050000 #include <QUrl> +#endif #include <QScrollBar> #include <openssl/crypto.h> diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index eb3ce48a6e..2c7bab2045 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -93,7 +93,11 @@ void SendCoinsDialog::on_sendButton_clicked() QStringList formatted; foreach(const SendCoinsRecipient &rcp, recipients) { +#if QT_VERSION < 0x050000 formatted.append(tr("<b>%1</b> to %2 (%3)").arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, rcp.amount), Qt::escape(rcp.label), rcp.address)); +#else + formatted.append(tr("<b>%1</b> to %2 (%3)").arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, rcp.amount), rcp.label.toHtmlEscaped(), rcp.address)); +#endif } fNewRecipientAllowed = false; diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp index 7c43e5839e..9118770f4c 100644 --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -218,8 +218,8 @@ void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked() ss << strMessageMagic; ss << ui->messageIn_VM->document()->toPlainText().toStdString(); - CKey key; - if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig)) + CPubKey pubkey; + if (!pubkey.RecoverCompact(Hash(ss.begin(), ss.end()), vchSig)) { ui->signatureIn_VM->setValid(false); ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); @@ -227,7 +227,7 @@ void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked() return; } - if (!(CBitcoinAddress(key.GetPubKey().GetID()) == addr)) + if (!(CBitcoinAddress(pubkey.GetID()) == addr)) { ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_VM->setText(QString("<nobr>") + tr("Message verification failed.") + QString("</nobr>")); diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index f8d16699ca..e400278123 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -3,6 +3,7 @@ #include "util.h" #include <QPainter> +#undef loop /* ugh, remove this when the #define loop is gone from util.h */ #include <QApplication> SplashScreen::SplashScreen(const QPixmap &pixmap, Qt::WindowFlags f) : @@ -26,7 +27,7 @@ SplashScreen::SplashScreen(const QPixmap &pixmap, Qt::WindowFlags f) : // load the bitmap for writing some text over it QPixmap newPixmap; - if(GetBoolArg("-testnet")) { + if(GetBoolArg("-testnet", false)) { newPixmap = QPixmap(":/images/splash_testnet"); } else { diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 9240b71c71..a43e29c476 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -176,7 +176,11 @@ void TransactionView::setModel(WalletModel *model) transactionView->horizontalHeader()->resizeSection(TransactionTableModel::Status, 23); transactionView->horizontalHeader()->resizeSection(TransactionTableModel::Date, 120); transactionView->horizontalHeader()->resizeSection(TransactionTableModel::Type, 120); +#if QT_VERSION < 0x050000 transactionView->horizontalHeader()->setResizeMode(TransactionTableModel::ToAddress, QHeaderView::Stretch); +#else + transactionView->horizontalHeader()->setSectionResizeMode(TransactionTableModel::ToAddress, QHeaderView::Stretch); +#endif transactionView->horizontalHeader()->resizeSection(TransactionTableModel::Amount, 100); } } diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp index 83e4255c9f..50c03ac62d 100644 --- a/src/qt/walletframe.cpp +++ b/src/qt/walletframe.cpp @@ -8,11 +8,9 @@ #include "bitcoingui.h" #include "walletstack.h" -#include <QVBoxLayout> +#include <QHBoxLayout> #include <QMessageBox> -#include <stdio.h> - WalletFrame::WalletFrame(BitcoinGUI *_gui) : QFrame(_gui), gui(_gui), diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h index 3649185e8a..d7092f9879 100644 --- a/src/qt/walletframe.h +++ b/src/qt/walletframe.h @@ -17,8 +17,9 @@ class WalletStack; class WalletFrame : public QFrame { Q_OBJECT + public: - explicit WalletFrame(BitcoinGUI *_gui); + explicit WalletFrame(BitcoinGUI *_gui = 0); ~WalletFrame(); void setClientModel(ClientModel *clientModel); diff --git a/src/qt/walletstack.h b/src/qt/walletstack.h index ea4cc121d1..506d595c0f 100644 --- a/src/qt/walletstack.h +++ b/src/qt/walletstack.h @@ -40,6 +40,7 @@ QT_END_NAMESPACE class WalletStack : public QStackedWidget { Q_OBJECT + public: explicit WalletStack(QWidget *parent = 0); ~WalletStack(); diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index 6d44c174b7..277c056693 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -21,7 +21,11 @@ #include <QHBoxLayout> #include <QVBoxLayout> #include <QAction> +#if QT_VERSION < 0x050000 #include <QDesktopServices> +#else +#include <QStandardPaths> +#endif #include <QFileDialog> #include <QPushButton> @@ -232,7 +236,11 @@ void WalletView::encryptWallet(bool status) void WalletView::backupWallet() { +#if QT_VERSION < 0x050000 QString saveDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); +#else + QString saveDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); +#endif QString filename = QFileDialog::getSaveFileName(this, tr("Backup Wallet"), saveDir, tr("Wallet Data (*.dat)")); if (!filename.isEmpty()) { if (!walletModel->backupWallet(filename)) { diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 11af1abf5d..b1b0c1ac16 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -65,8 +65,9 @@ Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex) if (blockindex->pprev) result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); - if (blockindex->pnext) - result.push_back(Pair("nextblockhash", blockindex->pnext->GetBlockHash().GetHex())); + CBlockIndex *pnext = blockindex->GetNextInMainChain(); + if (pnext) + result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex())); return result; } diff --git a/src/rpcdump.cpp b/src/rpcdump.cpp index 77cef02736..d46309eaa4 100644 --- a/src/rpcdump.cpp +++ b/src/rpcdump.cpp @@ -54,20 +54,18 @@ Value importprivkey(const Array& params, bool fHelp) 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); - CKeyID vchAddress = key.GetPubKey().GetID(); + CKey key = vchSecret.GetKey(); + CPubKey pubkey = key.GetPubKey(); + CKeyID vchAddress = pubkey.GetID(); { LOCK2(cs_main, pwalletMain->cs_wallet); pwalletMain->MarkDirty(); pwalletMain->SetAddressBookName(vchAddress, strLabel); - if (!pwalletMain->AddKey(key)) + if (!pwalletMain->AddKeyPubKey(key, pubkey)) throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet"); - + if (fRescan) { pwalletMain->ScanForWalletTransactions(pindexGenesisBlock, true); pwalletMain->ReacceptWalletTransactions(); @@ -91,9 +89,8 @@ Value dumpprivkey(const Array& params, bool fHelp) CKeyID keyID; if (!address.GetKeyID(keyID)) throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key"); - CSecret vchSecret; - bool fCompressed; - if (!pwalletMain->GetSecret(keyID, vchSecret, fCompressed)) + CKey vchSecret; + if (!pwalletMain->GetKey(keyID, vchSecret)) throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known"); - return CBitcoinSecret(vchSecret, fCompressed).ToString(); + return CBitcoinSecret(vchSecret).ToString(); } diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index fddda8b5f4..d2cac68706 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -11,6 +11,21 @@ using namespace json_spirit; using namespace std; +// Key used by getwork/getblocktemplate miners. +// Allocated in InitRPCMining, free'd in ShutdownRPCMining +static CReserveKey* pMiningKey = NULL; + +void InitRPCMining() +{ + // getwork/getblocktemplate mining rewards paid here: + pMiningKey = new CReserveKey(pwalletMain); +} + +void ShutdownRPCMining() +{ + delete pMiningKey; pMiningKey = NULL; +} + Value getgenerate(const Array& params, bool fHelp) { if (fHelp || params.size() != 0) @@ -18,7 +33,7 @@ Value getgenerate(const Array& params, bool fHelp) "getgenerate\n" "Returns true or false."); - return GetBoolArg("-gen"); + return GetBoolArg("-gen", false); } @@ -69,16 +84,16 @@ Value getmininginfo(const Array& params, bool fHelp) "Returns an object containing mining-related information."); Object obj; - obj.push_back(Pair("blocks", (int)nBestHeight)); - obj.push_back(Pair("currentblocksize",(uint64_t)nLastBlockSize)); - obj.push_back(Pair("currentblocktx",(uint64_t)nLastBlockTx)); - obj.push_back(Pair("difficulty", (double)GetDifficulty())); - obj.push_back(Pair("errors", GetWarnings("statusbar"))); - obj.push_back(Pair("generate", GetBoolArg("-gen"))); - obj.push_back(Pair("genproclimit", (int)GetArg("-genproclimit", -1))); - obj.push_back(Pair("hashespersec", gethashespersec(params, false))); - obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); - obj.push_back(Pair("testnet", fTestNet)); + obj.push_back(Pair("blocks", (int)nBestHeight)); + obj.push_back(Pair("currentblocksize", (uint64_t)nLastBlockSize)); + obj.push_back(Pair("currentblocktx", (uint64_t)nLastBlockTx)); + obj.push_back(Pair("difficulty", (double)GetDifficulty())); + obj.push_back(Pair("errors", GetWarnings("statusbar"))); + obj.push_back(Pair("generate", GetBoolArg("-gen", false))); + obj.push_back(Pair("genproclimit", (int)GetArg("-genproclimit", -1))); + obj.push_back(Pair("hashespersec", gethashespersec(params, false))); + obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); + obj.push_back(Pair("testnet", fTestNet)); return obj; } @@ -104,7 +119,6 @@ Value getwork(const Array& params, bool fHelp) typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t; static mapNewBlock_t mapNewBlock; // FIXME: thread safety static vector<CBlockTemplate*> vNewBlockTemplate; - static CReserveKey reservekey(pwalletMain); if (params.size() == 0) { @@ -134,7 +148,7 @@ Value getwork(const Array& params, bool fHelp) nStart = GetTime(); // Create new block - pblocktemplate = CreateNewBlock(reservekey); + pblocktemplate = CreateNewBlock(*pMiningKey); if (!pblocktemplate) throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); vNewBlockTemplate.push_back(pblocktemplate); @@ -192,7 +206,7 @@ Value getwork(const Array& params, bool fHelp) pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second; pblock->hashMerkleRoot = pblock->BuildMerkleTree(); - return CheckWork(pblock, *pwalletMain, reservekey); + return CheckWork(pblock, *pwalletMain, *pMiningKey); } } @@ -243,8 +257,6 @@ Value getblocktemplate(const Array& params, bool fHelp) if (IsInitialBlockDownload()) throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Bitcoin is downloading blocks..."); - static CReserveKey reservekey(pwalletMain); - // Update block static unsigned int nTransactionsUpdatedLast; static CBlockIndex* pindexPrev; @@ -267,7 +279,7 @@ Value getblocktemplate(const Array& params, bool fHelp) delete pblocktemplate; pblocktemplate = NULL; } - pblocktemplate = CreateNewBlock(reservekey); + pblocktemplate = CreateNewBlock(*pMiningKey); if (!pblocktemplate) throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index fb9811b921..c1e05466e5 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -407,10 +407,7 @@ Value signrawtransaction(const Array& params, bool fHelp) 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); + CKey key = vchSecret.GetKey(); tempKeystore.AddKey(key); } } diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 5fd400c6bb..fbad1944de 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -84,7 +84,7 @@ Value getinfo(const Array& params, bool fHelp) obj.push_back(Pair("keypoolsize", pwalletMain->GetKeyPoolSize())); obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee))); if (pwalletMain->IsCrypted()) - obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime / 1000)); + obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime)); obj.push_back(Pair("errors", GetWarnings("statusbar"))); return obj; } @@ -374,11 +374,11 @@ Value verifymessage(const Array& params, bool fHelp) ss << strMessageMagic; ss << strMessage; - CKey key; - if (!key.SetCompactSignature(ss.GetHash(), vchSig)) + CPubKey pubkey; + if (!pubkey.RecoverCompact(ss.GetHash(), vchSig)) return false; - return (key.GetPubKey().GetID() == keyID); + return (pubkey.GetID() == keyID); } @@ -719,7 +719,7 @@ static CScript _createmultisig(const Array& params) throw runtime_error( strprintf("not enough keys supplied " "(got %"PRIszu" keys, but need at least %d to redeem)", keys.size(), nRequired)); - std::vector<CKey> pubkeys; + std::vector<CPubKey> pubkeys; pubkeys.resize(keys.size()); for (unsigned int i = 0; i < keys.size(); i++) { @@ -737,16 +737,18 @@ static CScript _createmultisig(const Array& params) if (!pwalletMain->GetPubKey(keyID, vchPubKey)) throw runtime_error( strprintf("no full public key for address %s",ks.c_str())); - if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey)) + if (!vchPubKey.IsFullyValid()) throw runtime_error(" Invalid public key: "+ks); + pubkeys[i] = vchPubKey; } // Case 2: hex public key else if (IsHex(ks)) { CPubKey vchPubKey(ParseHex(ks)); - if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey)) + if (!vchPubKey.IsFullyValid()) throw runtime_error(" Invalid public key: "+ks); + pubkeys[i] = vchPubKey; } else { @@ -811,6 +813,7 @@ struct tallyitem { int64 nAmount; int nConf; + vector<uint256> txids; tallyitem() { nAmount = 0; @@ -852,6 +855,7 @@ Value ListReceived(const Array& params, bool fByAccounts) tallyitem& item = mapTally[address]; item.nAmount += txout.nValue; item.nConf = min(item.nConf, nDepth); + item.txids.push_back(wtx.GetHash()); } } @@ -887,6 +891,15 @@ Value ListReceived(const Array& params, bool fByAccounts) obj.push_back(Pair("account", strAccount)); obj.push_back(Pair("amount", ValueFromAmount(nAmount))); obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf))); + Array transactions; + if (it != mapTally.end()) + { + BOOST_FOREACH(const uint256& item, (*it).second.txids) + { + transactions.push_back(item.GetHex()); + } + } + obj.push_back(Pair("txids", transactions)); ret.push_back(obj); } } @@ -919,7 +932,8 @@ Value listreceivedbyaddress(const Array& params, bool fHelp) " \"address\" : receiving address\n" " \"account\" : the account of the receiving address\n" " \"amount\" : total amount received by the address\n" - " \"confirmations\" : number of confirmations of the most recent transaction included"); + " \"confirmations\" : number of confirmations of the most recent transaction included\n" + " \"txids\" : list of transactions with outputs to the address\n"); return ListReceived(params, false); } @@ -1256,56 +1270,11 @@ Value keypoolrefill(const Array& params, bool fHelp) } -void ThreadTopUpKeyPool(void* parg) -{ - // Make this thread recognisable as the key-topping-up thread - RenameThread("bitcoin-key-top"); - - pwalletMain->TopUpKeyPool(); -} - -void ThreadCleanWalletPassphrase(void* parg) +static void LockWallet(CWallet* pWallet) { - // Make this thread recognisable as the wallet relocking thread - RenameThread("bitcoin-lock-wa"); - - int64 nMyWakeTime = GetTimeMillis() + *((int64*)parg) * 1000; - - ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime); - - if (nWalletUnlockTime == 0) - { - nWalletUnlockTime = nMyWakeTime; - - do - { - if (nWalletUnlockTime==0) - break; - int64 nToSleep = nWalletUnlockTime - GetTimeMillis(); - if (nToSleep <= 0) - break; - - LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime); - MilliSleep(nToSleep); - ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime); - - } while(1); - - if (nWalletUnlockTime) - { - nWalletUnlockTime = 0; - pwalletMain->Lock(); - } - } - else - { - if (nWalletUnlockTime < nMyWakeTime) - nWalletUnlockTime = nMyWakeTime; - } - - LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime); - - delete (int64*)parg; + LOCK(cs_nWalletUnlockTime); + nWalletUnlockTime = 0; + pWallet->Lock(); } Value walletpassphrase(const Array& params, bool fHelp) @@ -1319,9 +1288,6 @@ Value walletpassphrase(const Array& params, bool fHelp) if (!pwalletMain->IsCrypted()) throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called."); - if (!pwalletMain->IsLocked()) - throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked."); - // Note that the walletpassphrase is stored in params[0] which is not mlock()ed SecureString strWalletPass; strWalletPass.reserve(100); @@ -1339,9 +1305,12 @@ Value walletpassphrase(const Array& params, bool fHelp) "walletpassphrase <passphrase> <timeout>\n" "Stores the wallet decryption key in memory for <timeout> seconds."); - NewThread(ThreadTopUpKeyPool, NULL); - int64* pnSleepTime = new int64(params[1].get_int64()); - NewThread(ThreadCleanWalletPassphrase, pnSleepTime); + pwalletMain->TopUpKeyPool(); + + int64 nSleepTime = params[1].get_int64(); + LOCK(cs_nWalletUnlockTime); + nWalletUnlockTime = GetTime() + nSleepTime; + RPCRunLater("lockwallet", boost::bind(LockWallet, pwalletMain), nSleepTime); return Value::null; } @@ -1445,7 +1414,7 @@ public: CPubKey vchPubKey; pwalletMain->GetPubKey(keyID, vchPubKey); obj.push_back(Pair("isscript", false)); - obj.push_back(Pair("pubkey", HexStr(vchPubKey.Raw()))); + obj.push_back(Pair("pubkey", HexStr(vchPubKey))); obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed())); return obj; } diff --git a/src/script.cpp b/src/script.cpp index 90066efd33..b411666353 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -16,7 +16,7 @@ using namespace boost; #include "sync.h" #include "util.h" -bool CheckSig(vector<unsigned char> vchSig, vector<unsigned char> vchPubKey, CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, int flags); +bool CheckSig(vector<unsigned char> vchSig, const vector<unsigned char> &vchPubKey, const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, int flags); @@ -1033,13 +1033,13 @@ class CSignatureCache { private: // sigdata_type is (signature hash, signature, public key): - typedef boost::tuple<uint256, std::vector<unsigned char>, std::vector<unsigned char> > sigdata_type; + typedef boost::tuple<uint256, std::vector<unsigned char>, CPubKey> sigdata_type; std::set< sigdata_type> setValid; boost::shared_mutex cs_sigcache; public: bool - Get(uint256 hash, const std::vector<unsigned char>& vchSig, const std::vector<unsigned char>& pubKey) + Get(const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubKey) { boost::shared_lock<boost::shared_mutex> lock(cs_sigcache); @@ -1050,7 +1050,7 @@ public: return false; } - void Set(uint256 hash, const std::vector<unsigned char>& vchSig, const std::vector<unsigned char>& pubKey) + void Set(const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubKey) { // DoS prevention: limit cache size to less than 10MB // (~200 bytes per cache entry times 50,000 entries) @@ -1081,11 +1081,15 @@ public: } }; -bool CheckSig(vector<unsigned char> vchSig, vector<unsigned char> vchPubKey, CScript scriptCode, +bool CheckSig(vector<unsigned char> vchSig, const vector<unsigned char> &vchPubKey, const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, int flags) { static CSignatureCache signatureCache; + CPubKey pubkey(vchPubKey); + if (!pubkey.IsValid()) + return false; + // Hash type is one byte tacked on to the end of the signature if (vchSig.empty()) return false; @@ -1097,18 +1101,14 @@ bool CheckSig(vector<unsigned char> vchSig, vector<unsigned char> vchPubKey, CSc uint256 sighash = SignatureHash(scriptCode, txTo, nIn, nHashType); - if (signatureCache.Get(sighash, vchSig, vchPubKey)) + if (signatureCache.Get(sighash, vchSig, pubkey)) return true; - CKey key; - if (!key.SetPubKey(vchPubKey)) - return false; - - if (!key.Verify(sighash, vchSig)) + if (!pubkey.Verify(sighash, vchSig)) return false; if (!(flags & SCRIPT_VERIFY_NOCACHE)) - signatureCache.Set(sighash, vchSig, vchPubKey); + signatureCache.Set(sighash, vchSig, pubkey); return true; } @@ -1770,13 +1770,13 @@ void CScript::SetDestination(const CTxDestination& dest) boost::apply_visitor(CScriptVisitor(this), dest); } -void CScript::SetMultisig(int nRequired, const std::vector<CKey>& keys) +void CScript::SetMultisig(int nRequired, const std::vector<CPubKey>& keys) { this->clear(); *this << EncodeOP_N(nRequired); - BOOST_FOREACH(const CKey& key, keys) - *this << key.GetPubKey(); + BOOST_FOREACH(const CPubKey& key, keys) + *this << key; *this << EncodeOP_N(keys.size()) << OP_CHECKMULTISIG; } @@ -1801,20 +1801,17 @@ bool CScriptCompressor::IsToScriptID(CScriptID &hash) const return false; } -bool CScriptCompressor::IsToPubKey(std::vector<unsigned char> &pubkey) const +bool CScriptCompressor::IsToPubKey(CPubKey &pubkey) const { if (script.size() == 35 && script[0] == 33 && script[34] == OP_CHECKSIG && (script[1] == 0x02 || script[1] == 0x03)) { - pubkey.resize(33); - memcpy(&pubkey[0], &script[1], 33); + pubkey.Set(&script[1], &script[34]); return true; } if (script.size() == 67 && script[0] == 65 && script[66] == OP_CHECKSIG && script[1] == 0x04) { - pubkey.resize(65); - memcpy(&pubkey[0], &script[1], 65); - CKey key; - return (key.SetPubKey(CPubKey(pubkey))); // SetPubKey fails if this is not a valid public key, a case that would not be compressible + pubkey.Set(&script[1], &script[66]); + return pubkey.IsFullyValid(); // if not fully valid, a case that would not be compressible } return false; } @@ -1835,7 +1832,7 @@ bool CScriptCompressor::Compress(std::vector<unsigned char> &out) const memcpy(&out[1], &scriptID, 20); return true; } - std::vector<unsigned char> pubkey; + CPubKey pubkey; if (IsToPubKey(pubkey)) { out.resize(33); memcpy(&out[1], &pubkey[1], 32); @@ -1888,17 +1885,16 @@ bool CScriptCompressor::Decompress(unsigned int nSize, const std::vector<unsigne return true; case 0x04: case 0x05: - std::vector<unsigned char> vch(33, 0x00); + unsigned char vch[33] = {}; vch[0] = nSize - 2; memcpy(&vch[1], &in[0], 32); - CKey key; - if (!key.SetPubKey(CPubKey(vch))) + CPubKey pubkey(&vch[0], &vch[33]); + if (!pubkey.Decompress()) return false; - key.SetCompressedPubKey(false); // Decompress public key - CPubKey pubkey = key.GetPubKey(); + assert(pubkey.size() == 65); script.resize(67); script[0] = 65; - memcpy(&script[1], &pubkey.Raw()[0], 65); + memcpy(&script[1], pubkey.begin(), 65); script[66] = OP_CHECKSIG; return true; } diff --git a/src/script.h b/src/script.h index 4b29f6273c..3cbb2cf322 100644 --- a/src/script.h +++ b/src/script.h @@ -348,8 +348,10 @@ public: CScript& operator<<(const CPubKey& key) { - std::vector<unsigned char> vchKey = key.Raw(); - return (*this) << vchKey; + assert(key.size() < OP_PUSHDATA1); + insert(end(), (unsigned char)key.size()); + insert(end(), key.begin(), key.end()); + return *this; } CScript& operator<<(const CBigNum& b) @@ -548,7 +550,7 @@ public: void SetDestination(const CTxDestination& address); - void SetMultisig(int nRequired, const std::vector<CKey>& keys); + void SetMultisig(int nRequired, const std::vector<CPubKey>& keys); void PrintHex() const @@ -619,7 +621,7 @@ protected: // form). bool IsToKeyID(CKeyID &hash) const; bool IsToScriptID(CScriptID &hash) const; - bool IsToPubKey(std::vector<unsigned char> &pubkey) const; + bool IsToPubKey(CPubKey &pubkey) const; bool Compress(std::vector<unsigned char> &out) const; unsigned int GetSpecialSize(unsigned int nSize) const; diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index 7602fa93a6..2741672a88 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -133,9 +133,8 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) // Note: CBitcoinSecret::SetString tests isValid, whereas CBitcoinAddress does not! BOOST_CHECK_MESSAGE(secret.SetString(exp_base58string), "!SetString:"+ strTest); BOOST_CHECK_MESSAGE(secret.IsValid(), "!IsValid:" + strTest); - bool fCompressedOut = false; - CSecret privkey = secret.GetSecret(fCompressedOut); - BOOST_CHECK_MESSAGE(fCompressedOut == isCompressed, "compressed mismatch:" + strTest); + CKey privkey = secret.GetKey(); + BOOST_CHECK_MESSAGE(privkey.IsCompressed() == isCompressed, "compressed mismatch:" + strTest); BOOST_CHECK_MESSAGE(privkey.size() == exp_payload.size() && std::equal(privkey.begin(), privkey.end(), exp_payload.begin()), "key mismatch:" + strTest); // Private key must be invalid public key @@ -187,8 +186,11 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) if(isPrivkey) { bool isCompressed = find_value(metadata, "isCompressed").get_bool(); + CKey key; + key.Set(exp_payload.begin(), exp_payload.end(), isCompressed); + assert(key.IsValid()); CBitcoinSecret secret; - secret.SetSecret(CSecret(exp_payload.begin(), exp_payload.end()), isCompressed); + secret.SetKey(key); BOOST_CHECK_MESSAGE(secret.ToString() == exp_base58string, "result mismatch: " + strTest); } else diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp index 4a2851cf46..0d349a990c 100644 --- a/src/test/bloom_tests.cpp +++ b/src/test/bloom_tests.cpp @@ -73,14 +73,13 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_key) CBitcoinSecret vchSecret; BOOST_CHECK(vchSecret.SetString(strSecret)); - CKey key; - bool fCompressed; - CSecret secret = vchSecret.GetSecret(fCompressed); - key.SetSecret(secret, fCompressed); + CKey key = vchSecret.GetKey(); + CPubKey pubkey = key.GetPubKey(); + vector<unsigned char> vchPubKey(pubkey.begin(), pubkey.end()); CBloomFilter filter(2, 0.001, 0, BLOOM_UPDATE_ALL); - filter.insert(key.GetPubKey().Raw()); - uint160 hash = key.GetPubKey().GetID(); + filter.insert(vchPubKey); + uint160 hash = pubkey.GetID(); filter.insert(vector<unsigned char>(hash.begin(), hash.end())); CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); diff --git a/src/test/checkblock_tests.cpp b/src/test/checkblock_tests.cpp index 3cfb6dbfa4..d626f9a6f4 100644 --- a/src/test/checkblock_tests.cpp +++ b/src/test/checkblock_tests.cpp @@ -52,8 +52,6 @@ BOOST_AUTO_TEST_CASE(May15) if (read_block("Mar12Fork.dat", forkingBlock)) { CValidationState state; - forkingBlock.nTime = tMay15-1; // Invalidates PoW - BOOST_CHECK(!forkingBlock.CheckBlock(state, false, false)); // After May 15'th, big blocks are OK: forkingBlock.nTime = tMay15; // Invalidates PoW diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp index 78953d296f..c89d218f80 100644 --- a/src/test/getarg_tests.cpp +++ b/src/test/getarg_tests.cpp @@ -6,8 +6,7 @@ BOOST_AUTO_TEST_SUITE(getarg_tests) -static void -ResetArgs(const std::string& strArg) +static void ResetArgs(const std::string& strArg) { std::vector<std::string> vecArg; boost::split(vecArg, strArg, boost::is_space(), boost::token_compress_on); @@ -26,62 +25,50 @@ ResetArgs(const std::string& strArg) BOOST_AUTO_TEST_CASE(boolarg) { ResetArgs("-foo"); - BOOST_CHECK(GetBoolArg("-foo")); BOOST_CHECK(GetBoolArg("-foo", false)); BOOST_CHECK(GetBoolArg("-foo", true)); - BOOST_CHECK(!GetBoolArg("-fo")); BOOST_CHECK(!GetBoolArg("-fo", false)); BOOST_CHECK(GetBoolArg("-fo", true)); - BOOST_CHECK(!GetBoolArg("-fooo")); BOOST_CHECK(!GetBoolArg("-fooo", false)); BOOST_CHECK(GetBoolArg("-fooo", true)); ResetArgs("-foo=0"); - BOOST_CHECK(!GetBoolArg("-foo")); BOOST_CHECK(!GetBoolArg("-foo", false)); BOOST_CHECK(!GetBoolArg("-foo", true)); ResetArgs("-foo=1"); - BOOST_CHECK(GetBoolArg("-foo")); BOOST_CHECK(GetBoolArg("-foo", false)); BOOST_CHECK(GetBoolArg("-foo", true)); // New 0.6 feature: auto-map -nosomething to !-something: ResetArgs("-nofoo"); - BOOST_CHECK(!GetBoolArg("-foo")); BOOST_CHECK(!GetBoolArg("-foo", false)); BOOST_CHECK(!GetBoolArg("-foo", true)); ResetArgs("-nofoo=1"); - BOOST_CHECK(!GetBoolArg("-foo")); BOOST_CHECK(!GetBoolArg("-foo", false)); BOOST_CHECK(!GetBoolArg("-foo", true)); ResetArgs("-foo -nofoo"); // -foo should win - BOOST_CHECK(GetBoolArg("-foo")); BOOST_CHECK(GetBoolArg("-foo", false)); BOOST_CHECK(GetBoolArg("-foo", true)); ResetArgs("-foo=1 -nofoo=1"); // -foo should win - BOOST_CHECK(GetBoolArg("-foo")); BOOST_CHECK(GetBoolArg("-foo", false)); BOOST_CHECK(GetBoolArg("-foo", true)); ResetArgs("-foo=0 -nofoo=0"); // -foo should win - BOOST_CHECK(!GetBoolArg("-foo")); BOOST_CHECK(!GetBoolArg("-foo", false)); BOOST_CHECK(!GetBoolArg("-foo", true)); // New 0.6 feature: treat -- same as -: ResetArgs("--foo=1"); - BOOST_CHECK(GetBoolArg("-foo")); BOOST_CHECK(GetBoolArg("-foo", false)); BOOST_CHECK(GetBoolArg("-foo", true)); ResetArgs("--nofoo=1"); - BOOST_CHECK(!GetBoolArg("-foo")); BOOST_CHECK(!GetBoolArg("-foo", false)); BOOST_CHECK(!GetBoolArg("-foo", true)); @@ -133,7 +120,7 @@ BOOST_AUTO_TEST_CASE(intarg) BOOST_AUTO_TEST_CASE(doubledash) { ResetArgs("--foo"); - BOOST_CHECK_EQUAL(GetBoolArg("-foo"), true); + BOOST_CHECK_EQUAL(GetBoolArg("-foo", false), true); ResetArgs("--foo=verbose --bar=1"); BOOST_CHECK_EQUAL(GetArg("-foo", ""), "verbose"); @@ -143,25 +130,24 @@ BOOST_AUTO_TEST_CASE(doubledash) BOOST_AUTO_TEST_CASE(boolargno) { ResetArgs("-nofoo"); - BOOST_CHECK(!GetBoolArg("-foo")); BOOST_CHECK(!GetBoolArg("-foo", true)); BOOST_CHECK(!GetBoolArg("-foo", false)); ResetArgs("-nofoo=1"); - BOOST_CHECK(!GetBoolArg("-foo")); BOOST_CHECK(!GetBoolArg("-foo", true)); BOOST_CHECK(!GetBoolArg("-foo", false)); ResetArgs("-nofoo=0"); - BOOST_CHECK(GetBoolArg("-foo")); BOOST_CHECK(GetBoolArg("-foo", true)); BOOST_CHECK(GetBoolArg("-foo", false)); ResetArgs("-foo --nofoo"); - BOOST_CHECK(GetBoolArg("-foo")); + BOOST_CHECK(GetBoolArg("-foo", true)); + BOOST_CHECK(GetBoolArg("-foo", false)); ResetArgs("-nofoo -foo"); // foo always wins: - BOOST_CHECK(GetBoolArg("-foo")); + BOOST_CHECK(GetBoolArg("-foo", true)); + BOOST_CHECK(GetBoolArg("-foo", false)); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp index 0a6df88fef..c004521d1a 100644 --- a/src/test/key_tests.cpp +++ b/src/test/key_tests.cpp @@ -26,8 +26,8 @@ static const string strAddressBad("1HV9Lc3sNHZxwj4Zk6fB38tEmBryq2cBiF"); #ifdef KEY_TESTS_DUMPINFO void dumpKeyInfo(uint256 privkey) { - CSecret secret; - secret.resize(32); + CKey key; + key.resize(32); memcpy(&secret[0], &privkey, 32); vector<unsigned char> sec; sec.resize(32); @@ -62,29 +62,24 @@ BOOST_AUTO_TEST_CASE(key_test1) BOOST_CHECK( bsecret2C.SetString(strSecret2C)); BOOST_CHECK(!baddress1.SetString(strAddressBad)); - bool fCompressed; - CSecret secret1 = bsecret1.GetSecret (fCompressed); - BOOST_CHECK(fCompressed == false); - CSecret secret2 = bsecret2.GetSecret (fCompressed); - BOOST_CHECK(fCompressed == false); - CSecret secret1C = bsecret1C.GetSecret(fCompressed); - BOOST_CHECK(fCompressed == true); - CSecret secret2C = bsecret2C.GetSecret(fCompressed); - BOOST_CHECK(fCompressed == true); - - BOOST_CHECK(secret1 == secret1C); - BOOST_CHECK(secret2 == secret2C); - - CKey key1, key2, key1C, key2C; - key1.SetSecret(secret1, false); - key2.SetSecret(secret2, false); - key1C.SetSecret(secret1, true); - key2C.SetSecret(secret2, true); - - BOOST_CHECK(addr1.Get() == CTxDestination(key1.GetPubKey().GetID())); - BOOST_CHECK(addr2.Get() == CTxDestination(key2.GetPubKey().GetID())); - BOOST_CHECK(addr1C.Get() == CTxDestination(key1C.GetPubKey().GetID())); - BOOST_CHECK(addr2C.Get() == CTxDestination(key2C.GetPubKey().GetID())); + CKey key1 = bsecret1.GetKey(); + BOOST_CHECK(key1.IsCompressed() == false); + CKey key2 = bsecret2.GetKey(); + BOOST_CHECK(key2.IsCompressed() == false); + CKey key1C = bsecret1C.GetKey(); + BOOST_CHECK(key1C.IsCompressed() == true); + CKey key2C = bsecret2C.GetKey(); + BOOST_CHECK(key1C.IsCompressed() == true); + + CPubKey pubkey1 = key1. GetPubKey(); + CPubKey pubkey2 = key2. GetPubKey(); + CPubKey pubkey1C = key1C.GetPubKey(); + CPubKey pubkey2C = key2C.GetPubKey(); + + BOOST_CHECK(addr1.Get() == CTxDestination(pubkey1.GetID())); + BOOST_CHECK(addr2.Get() == CTxDestination(pubkey2.GetID())); + BOOST_CHECK(addr1C.Get() == CTxDestination(pubkey1C.GetID())); + BOOST_CHECK(addr2C.Get() == CTxDestination(pubkey2C.GetID())); for (int n=0; n<16; n++) { @@ -100,25 +95,25 @@ BOOST_AUTO_TEST_CASE(key_test1) BOOST_CHECK(key1C.Sign(hashMsg, sign1C)); BOOST_CHECK(key2C.Sign(hashMsg, sign2C)); - BOOST_CHECK( key1.Verify(hashMsg, sign1)); - BOOST_CHECK(!key1.Verify(hashMsg, sign2)); - BOOST_CHECK( key1.Verify(hashMsg, sign1C)); - BOOST_CHECK(!key1.Verify(hashMsg, sign2C)); + BOOST_CHECK( pubkey1.Verify(hashMsg, sign1)); + BOOST_CHECK(!pubkey1.Verify(hashMsg, sign2)); + BOOST_CHECK( pubkey1.Verify(hashMsg, sign1C)); + BOOST_CHECK(!pubkey1.Verify(hashMsg, sign2C)); - BOOST_CHECK(!key2.Verify(hashMsg, sign1)); - BOOST_CHECK( key2.Verify(hashMsg, sign2)); - BOOST_CHECK(!key2.Verify(hashMsg, sign1C)); - BOOST_CHECK( key2.Verify(hashMsg, sign2C)); + BOOST_CHECK(!pubkey2.Verify(hashMsg, sign1)); + BOOST_CHECK( pubkey2.Verify(hashMsg, sign2)); + BOOST_CHECK(!pubkey2.Verify(hashMsg, sign1C)); + BOOST_CHECK( pubkey2.Verify(hashMsg, sign2C)); - BOOST_CHECK( key1C.Verify(hashMsg, sign1)); - BOOST_CHECK(!key1C.Verify(hashMsg, sign2)); - BOOST_CHECK( key1C.Verify(hashMsg, sign1C)); - BOOST_CHECK(!key1C.Verify(hashMsg, sign2C)); + BOOST_CHECK( pubkey1C.Verify(hashMsg, sign1)); + BOOST_CHECK(!pubkey1C.Verify(hashMsg, sign2)); + BOOST_CHECK( pubkey1C.Verify(hashMsg, sign1C)); + BOOST_CHECK(!pubkey1C.Verify(hashMsg, sign2C)); - BOOST_CHECK(!key2C.Verify(hashMsg, sign1)); - BOOST_CHECK( key2C.Verify(hashMsg, sign2)); - BOOST_CHECK(!key2C.Verify(hashMsg, sign1C)); - BOOST_CHECK( key2C.Verify(hashMsg, sign2C)); + BOOST_CHECK(!pubkey2C.Verify(hashMsg, sign1)); + BOOST_CHECK( pubkey2C.Verify(hashMsg, sign2)); + BOOST_CHECK(!pubkey2C.Verify(hashMsg, sign1C)); + BOOST_CHECK( pubkey2C.Verify(hashMsg, sign2C)); // compact signatures (with key recovery) @@ -129,18 +124,17 @@ BOOST_AUTO_TEST_CASE(key_test1) BOOST_CHECK(key1C.SignCompact(hashMsg, csign1C)); BOOST_CHECK(key2C.SignCompact(hashMsg, csign2C)); - CKey rkey1, rkey2, rkey1C, rkey2C; + CPubKey rkey1, rkey2, rkey1C, rkey2C; - BOOST_CHECK(rkey1.SetCompactSignature (hashMsg, csign1)); - BOOST_CHECK(rkey2.SetCompactSignature (hashMsg, csign2)); - BOOST_CHECK(rkey1C.SetCompactSignature(hashMsg, csign1C)); - BOOST_CHECK(rkey2C.SetCompactSignature(hashMsg, csign2C)); + BOOST_CHECK(rkey1.RecoverCompact (hashMsg, csign1)); + BOOST_CHECK(rkey2.RecoverCompact (hashMsg, csign2)); + BOOST_CHECK(rkey1C.RecoverCompact(hashMsg, csign1C)); + BOOST_CHECK(rkey2C.RecoverCompact(hashMsg, csign2C)); - - BOOST_CHECK(rkey1.GetPubKey() == key1.GetPubKey()); - BOOST_CHECK(rkey2.GetPubKey() == key2.GetPubKey()); - BOOST_CHECK(rkey1C.GetPubKey() == key1C.GetPubKey()); - BOOST_CHECK(rkey2C.GetPubKey() == key2C.GetPubKey()); + BOOST_CHECK(rkey1 == pubkey1); + BOOST_CHECK(rkey2 == pubkey2); + BOOST_CHECK(rkey1C == pubkey1C); + BOOST_CHECK(rkey2C == pubkey2C); } } diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index d6f836d367..9ef932b5b4 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -30,7 +30,7 @@ sign_multisig(CScript scriptPubKey, vector<CKey> keys, CTransaction transaction, CScript result; result << OP_0; // CHECKMULTISIG bug workaround - BOOST_FOREACH(CKey key, keys) + BOOST_FOREACH(const CKey &key, keys) { vector<unsigned char> vchSig; BOOST_CHECK(key.Sign(hash, vchSig)); diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index f8fe443b87..35eabed0e2 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -79,23 +79,44 @@ static Value CallRPC(string args) } } -BOOST_AUTO_TEST_CASE(rpc_rawparams) +BOOST_AUTO_TEST_CASE(rpc_wallet) { - // Test raw transaction API argument handling + // Test RPC calls for various wallet statistics 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_THROW(CallRPC("listunspent 0 1 [] extra"), 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_NO_THROW(CallRPC("listreceivedbyaddress")); + BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaddress 0")); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaddress not_int"), runtime_error); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaddress 0 not_bool"), runtime_error); + BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaddress 0 true")); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaddress 0 true extra"), runtime_error); + + BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount")); + BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount 0")); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount not_int"), runtime_error); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount 0 not_bool"), runtime_error); + BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount 0 true")); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount 0 true extra"), runtime_error); +} + + +BOOST_AUTO_TEST_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_THROW(CallRPC("createrawtransaction"), runtime_error); BOOST_CHECK_THROW(CallRPC("createrawtransaction null null"), runtime_error); BOOST_CHECK_THROW(CallRPC("createrawtransaction not_array"), runtime_error); diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index ee0d25a2f5..65f0ad0cdc 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -145,19 +145,19 @@ BOOST_AUTO_TEST_CASE(set) // Test the CScript::Set* methods CBasicKeyStore keystore; CKey key[4]; - std::vector<CKey> keys; + std::vector<CPubKey> keys; for (int i = 0; i < 4; i++) { key[i].MakeNewKey(true); keystore.AddKey(key[i]); - keys.push_back(key[i]); + keys.push_back(key[i].GetPubKey()); } CScript inner[4]; inner[0].SetDestination(key[0].GetPubKey().GetID()); - inner[1].SetMultisig(2, std::vector<CKey>(keys.begin(), keys.begin()+2)); - inner[2].SetMultisig(1, std::vector<CKey>(keys.begin(), keys.begin()+2)); - inner[3].SetMultisig(2, std::vector<CKey>(keys.begin(), keys.begin()+3)); + inner[1].SetMultisig(2, std::vector<CPubKey>(keys.begin(), keys.begin()+2)); + inner[2].SetMultisig(1, std::vector<CPubKey>(keys.begin(), keys.begin()+2)); + inner[3].SetMultisig(2, std::vector<CPubKey>(keys.begin(), keys.begin()+3)); CScript outer[4]; for (int i = 0; i < 4; i++) @@ -248,12 +248,12 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) CCoinsViewCache coins(coinsDummy); CBasicKeyStore keystore; CKey key[3]; - vector<CKey> keys; + vector<CPubKey> keys; for (int i = 0; i < 3; i++) { key[i].MakeNewKey(true); keystore.AddKey(key[i]); - keys.push_back(key[i]); + keys.push_back(key[i].GetPubKey()); } CTransaction txFrom; diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 5d5a1525f7..e7ad52627c 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -211,7 +211,7 @@ sign_multisig(CScript scriptPubKey, std::vector<CKey> keys, CTransaction transac // and vice-versa) // result << OP_0; - BOOST_FOREACH(CKey key, keys) + BOOST_FOREACH(const CKey &key, keys) { vector<unsigned char> vchSig; BOOST_CHECK(key.Sign(hash, vchSig)); @@ -221,7 +221,7 @@ sign_multisig(CScript scriptPubKey, std::vector<CKey> keys, CTransaction transac return result; } CScript -sign_multisig(CScript scriptPubKey, CKey key, CTransaction transaction) +sign_multisig(CScript scriptPubKey, const CKey &key, CTransaction transaction) { std::vector<CKey> keys; keys.push_back(key); @@ -333,11 +333,13 @@ BOOST_AUTO_TEST_CASE(script_combineSigs) // Test the CombineSignatures function CBasicKeyStore keystore; vector<CKey> keys; + vector<CPubKey> pubkeys; for (int i = 0; i < 3; i++) { CKey key; key.MakeNewKey(i%2 == 1); keys.push_back(key); + pubkeys.push_back(key.GetPubKey()); keystore.AddKey(key); } @@ -390,7 +392,7 @@ BOOST_AUTO_TEST_CASE(script_combineSigs) BOOST_CHECK(combined == scriptSig); // Hardest case: Multisig 2-of-3 - scriptPubKey.SetMultisig(2, keys); + scriptPubKey.SetMultisig(2, pubkeys); keystore.AddCScript(scriptPubKey); SignSignature(keystore, txFrom, txTo, 0); combined = CombineSignatures(scriptPubKey, txTo, 0, scriptSig, empty); diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp index 1762680adf..5a87f17600 100644 --- a/src/test/sigopcount_tests.cpp +++ b/src/test/sigopcount_tests.cpp @@ -37,12 +37,12 @@ BOOST_AUTO_TEST_CASE(GetSigOpCount) scriptSig << OP_0 << Serialize(s1); BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(scriptSig), 3U); - std::vector<CKey> keys; + std::vector<CPubKey> keys; for (int i = 0; i < 3; i++) { CKey k; k.MakeNewKey(true); - keys.push_back(k); + keys.push_back(k.GetPubKey()); } CScript s2; s2.SetMultisig(1, keys); diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 1b0ccad511..64bd3a1b28 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -31,7 +31,7 @@ BOOST_AUTO_TEST_CASE(util_criticalsection) } BOOST_AUTO_TEST_CASE(util_MedianFilter) -{ +{ CMedianFilter<int> filter(5, 15); BOOST_CHECK_EQUAL(filter.median(), 15); @@ -56,10 +56,10 @@ BOOST_AUTO_TEST_CASE(util_MedianFilter) } static const unsigned char ParseHex_expected[65] = { - 0x04, 0x67, 0x8a, 0xfd, 0xb0, 0xfe, 0x55, 0x48, 0x27, 0x19, 0x67, 0xf1, 0xa6, 0x71, 0x30, 0xb7, - 0x10, 0x5c, 0xd6, 0xa8, 0x28, 0xe0, 0x39, 0x09, 0xa6, 0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, 0xde, - 0xb6, 0x49, 0xf6, 0xbc, 0x3f, 0x4c, 0xef, 0x38, 0xc4, 0xf3, 0x55, 0x04, 0xe5, 0x1e, 0xc1, 0x12, - 0xde, 0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, 0x8d, 0x57, 0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, 0x1d, + 0x04, 0x67, 0x8a, 0xfd, 0xb0, 0xfe, 0x55, 0x48, 0x27, 0x19, 0x67, 0xf1, 0xa6, 0x71, 0x30, 0xb7, + 0x10, 0x5c, 0xd6, 0xa8, 0x28, 0xe0, 0x39, 0x09, 0xa6, 0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, 0xde, + 0xb6, 0x49, 0xf6, 0xbc, 0x3f, 0x4c, 0xef, 0x38, 0xc4, 0xf3, 0x55, 0x04, 0xe5, 0x1e, 0xc1, 0x12, + 0xde, 0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, 0x8d, 0x57, 0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, 0x1d, 0x5f }; BOOST_AUTO_TEST_CASE(util_ParseHex) @@ -123,13 +123,13 @@ BOOST_AUTO_TEST_CASE(util_ParseParameters) BOOST_CHECK(mapArgs.empty() && mapMultiArgs.empty()); ParseParameters(5, (char**)argv_test); - // expectation: -ignored is ignored (program name argument), + // expectation: -ignored is ignored (program name argument), // -a, -b and -ccc end up in map, -d ignored because it is after // a non-option argument (non-GNU option parsing) BOOST_CHECK(mapArgs.size() == 3 && mapMultiArgs.size() == 3); - BOOST_CHECK(mapArgs.count("-a") && mapArgs.count("-b") && mapArgs.count("-ccc") + BOOST_CHECK(mapArgs.count("-a") && mapArgs.count("-b") && mapArgs.count("-ccc") && !mapArgs.count("f") && !mapArgs.count("-d")); - BOOST_CHECK(mapMultiArgs.count("-a") && mapMultiArgs.count("-b") && mapMultiArgs.count("-ccc") + BOOST_CHECK(mapMultiArgs.count("-a") && mapMultiArgs.count("-b") && mapMultiArgs.count("-ccc") && !mapMultiArgs.count("f") && !mapMultiArgs.count("-d")); BOOST_CHECK(mapArgs["-a"] == "" && mapArgs["-ccc"] == "multiple"); @@ -154,10 +154,10 @@ BOOST_AUTO_TEST_CASE(util_GetArg) BOOST_CHECK_EQUAL(GetArg("inttest1", -1), 12345); BOOST_CHECK_EQUAL(GetArg("inttest2", -1), 81985529216486895LL); BOOST_CHECK_EQUAL(GetArg("inttest3", -1), -1); - BOOST_CHECK_EQUAL(GetBoolArg("booltest1"), true); - BOOST_CHECK_EQUAL(GetBoolArg("booltest2"), false); - BOOST_CHECK_EQUAL(GetBoolArg("booltest3"), false); - BOOST_CHECK_EQUAL(GetBoolArg("booltest4"), true); + BOOST_CHECK_EQUAL(GetBoolArg("booltest1", false), true); + BOOST_CHECK_EQUAL(GetBoolArg("booltest2", false), false); + BOOST_CHECK_EQUAL(GetBoolArg("booltest3", false), false); + BOOST_CHECK_EQUAL(GetBoolArg("booltest4", false), true); } BOOST_AUTO_TEST_CASE(util_WildcardMatch) diff --git a/src/util.cpp b/src/util.cpp index 4c9b897f57..0bd2960233 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -518,7 +518,7 @@ static void InterpretNegativeSetting(string name, map<string, string>& mapSettin positive.append(name.begin()+3, name.end()); if (mapSettingsRet.count(positive) == 0) { - bool value = !GetBoolArg(name); + bool value = !GetBoolArg(name, false); mapSettingsRet[positive] = (value ? "1" : "0"); } } @@ -1171,7 +1171,6 @@ bool TruncateFile(FILE *file, unsigned int length) { #endif } - // this function tries to raise the file descriptor limit to the requested number. // It returns the actual file descriptor limit (which may be more or less than nMinFD) int RaiseFileDescriptorLimit(int nMinFD) { @@ -1257,8 +1256,8 @@ void ShrinkDebugFile() fclose(file); } } - else if(file != NULL) - fclose(file); + else if (file != NULL) + fclose(file); } diff --git a/src/util.h b/src/util.h index 3d25364505..941e0d99ac 100644 --- a/src/util.h +++ b/src/util.h @@ -7,12 +7,12 @@ #include "uint256.h" +#include <stdarg.h> + #ifndef WIN32 #include <sys/types.h> #include <sys/time.h> #include <sys/resource.h> -#else -typedef int pid_t; /* define for Windows compatibility */ #endif #include <map> #include <list> @@ -298,7 +298,8 @@ std::string HexStr(const T itbegin, const T itend, bool fSpaces=false) return rv; } -inline std::string HexStr(const std::vector<unsigned char>& vch, bool fSpaces=false) +template<typename T> +inline std::string HexStr(const T& vch, bool fSpaces=false) { return HexStr(vch.begin(), vch.end(), fSpaces); } @@ -389,7 +390,7 @@ int64 GetArg(const std::string& strArg, int64 nDefault); * @param default (true or false) * @return command-line argument or default value */ -bool GetBoolArg(const std::string& strArg, bool fDefault=false); +bool GetBoolArg(const std::string& strArg, bool fDefault); /** * Set an argument if it doesn't already have a value diff --git a/src/wallet.cpp b/src/wallet.cpp index c70ea20e88..27aab08ecb 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -32,26 +32,28 @@ CPubKey CWallet::GenerateNewKey() bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets RandAddSeedPerfmon(); - CKey key; - key.MakeNewKey(fCompressed); + CKey secret; + secret.MakeNewKey(fCompressed); // Compressed public keys were introduced in version 0.6.0 if (fCompressed) SetMinVersion(FEATURE_COMPRPUBKEY); - if (!AddKey(key)) + CPubKey pubkey = secret.GetPubKey(); + if (!AddKeyPubKey(secret, pubkey)) throw std::runtime_error("CWallet::GenerateNewKey() : AddKey failed"); - return key.GetPubKey(); + return pubkey; } -bool CWallet::AddKey(const CKey& key) +bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey) { - if (!CCryptoKeyStore::AddKey(key)) + if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey)) return false; if (!fFileBacked) return true; - if (!IsCrypted()) - return CWalletDB(strWalletFile).WriteKey(key.GetPubKey(), key.GetPrivKey()); + if (!IsCrypted()) { + return CWalletDB(strWalletFile).WriteKey(pubkey, secret.GetPrivKey()); + } return true; } @@ -87,9 +89,6 @@ bool CWallet::AddCScript(const CScript& redeemScript) bool CWallet::Unlock(const SecureString& strWalletPassphrase) { - if (!IsLocked()) - return false; - CCrypter crypter; CKeyingMaterial vMasterKey; @@ -100,7 +99,7 @@ bool CWallet::Unlock(const SecureString& strWalletPassphrase) if(!crypter.SetKeyFromPassphrase(strWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) return false; if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) - return false; + continue; // try another master key if (CCryptoKeyStore::Unlock(vMasterKey)) return true; } @@ -780,7 +779,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) if (AddToWalletIfInvolvingMe(tx.GetHash(), tx, &block, fUpdate)) ret++; } - pindex = pindex->pnext; + pindex = pindex->GetNextInMainChain(); } } return ret; diff --git a/src/wallet.h b/src/wallet.h index dcba4675eb..7fcb8e13ce 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -136,9 +136,9 @@ public: // Generate a new key CPubKey GenerateNewKey(); // Adds a key to the store, and saves it to disk. - bool AddKey(const CKey& key); + bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey); // Adds a key to the store, without saving it to disk (used by LoadWallet) - bool LoadKey(const CKey& key) { return CCryptoKeyStore::AddKey(key); } + bool LoadKey(const CKey& key, const CPubKey &pubkey) { return CCryptoKeyStore::AddKeyPubKey(key, pubkey); } bool LoadMinVersion(int nVersion) { nWalletVersion = nVersion; nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion); return true; } diff --git a/src/walletdb.cpp b/src/walletdb.cpp index 81a21443a1..4a73413d26 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -262,52 +262,33 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, } else if (strType == "key" || strType == "wkey") { - vector<unsigned char> vchPubKey; + CPubKey vchPubKey; ssKey >> vchPubKey; + if (!vchPubKey.IsValid()) + { + strErr = "Error reading wallet database: CPubKey corrupt"; + return false; + } CKey key; + CPrivKey pkey; if (strType == "key") - { - CPrivKey pkey; ssValue >> pkey; - key.SetPubKey(vchPubKey); - if (!key.SetPrivKey(pkey)) - { - strErr = "Error reading wallet database: CPrivKey corrupt"; - return false; - } - if (key.GetPubKey() != vchPubKey) - { - strErr = "Error reading wallet database: CPrivKey pubkey inconsistency"; - return false; - } - if (!key.IsValid()) - { - strErr = "Error reading wallet database: invalid CPrivKey"; - return false; - } - } - else - { + else { CWalletKey wkey; ssValue >> wkey; - key.SetPubKey(vchPubKey); - if (!key.SetPrivKey(wkey.vchPrivKey)) - { - strErr = "Error reading wallet database: CPrivKey corrupt"; - return false; - } - if (key.GetPubKey() != vchPubKey) - { - strErr = "Error reading wallet database: CWalletKey pubkey inconsistency"; - return false; - } - if (!key.IsValid()) - { - strErr = "Error reading wallet database: invalid CWalletKey"; - return false; - } + pkey = wkey.vchPrivKey; + } + if (!key.SetPrivKey(pkey, vchPubKey.IsCompressed())) + { + strErr = "Error reading wallet database: CPrivKey corrupt"; + return false; + } + if (key.GetPubKey() != vchPubKey) + { + strErr = "Error reading wallet database: CPrivKey pubkey inconsistency"; + return false; } - if (!pwallet->LoadKey(key)) + if (!pwallet->LoadKey(key, vchPubKey)) { strErr = "Error reading wallet database: LoadKey failed"; return false; diff --git a/src/walletdb.h b/src/walletdb.h index a3e779ab9d..8ae6c3ff49 100644 --- a/src/walletdb.h +++ b/src/walletdb.h @@ -53,18 +53,18 @@ public: bool WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey) { nWalletDBUpdated++; - return Write(std::make_pair(std::string("key"), vchPubKey.Raw()), vchPrivKey, false); + return Write(std::make_pair(std::string("key"), vchPubKey), vchPrivKey, false); } bool WriteCryptedKey(const CPubKey& vchPubKey, const std::vector<unsigned char>& vchCryptedSecret, bool fEraseUnencryptedKey = true) { nWalletDBUpdated++; - if (!Write(std::make_pair(std::string("ckey"), vchPubKey.Raw()), vchCryptedSecret, false)) + if (!Write(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false)) return false; if (fEraseUnencryptedKey) { - Erase(std::make_pair(std::string("key"), vchPubKey.Raw())); - Erase(std::make_pair(std::string("wkey"), vchPubKey.Raw())); + Erase(std::make_pair(std::string("key"), vchPubKey)); + Erase(std::make_pair(std::string("wkey"), vchPubKey)); } return true; } @@ -101,7 +101,7 @@ public: bool WriteDefaultKey(const CPubKey& vchPubKey) { nWalletDBUpdated++; - return Write(std::string("defaultkey"), vchPubKey.Raw()); + return Write(std::string("defaultkey"), vchPubKey); } bool ReadPool(int64 nPool, CKeyPool& keypool) |