From 5251d4450cb6cabb1abb96f5b8f855b02e3b1b45 Mon Sep 17 00:00:00 2001 From: Robert Backhaus Date: Fri, 24 May 2013 23:40:51 +1000 Subject: [PATCH 01/25] Explictly cast calculation to int, to allow std::max to work. --- src/init.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/init.cpp b/src/init.cpp index 59bf32eca..767d7525a 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -531,7 +531,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.")); From 806cf827167ab08da2f93f8aceff3275d6d80dc1 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Sun, 19 May 2013 12:45:52 -0400 Subject: [PATCH 02/25] fix memory leak in CKey::SetCompactSignature() --- src/key.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/key.cpp b/src/key.cpp index 20114e6bb..74b2797d8 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -367,6 +367,7 @@ bool CKey::SetCompactSignature(uint256 hash, const std::vector& v ECDSA_SIG_free(sig); return true; } + ECDSA_SIG_free(sig); return false; } From f237d48e50cb9fc7197161e17e5bc0ea607a1a0b Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Sun, 19 May 2013 15:46:11 -0400 Subject: [PATCH 03/25] Fix memory leak on exception in Key::SignCompact --- src/key.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/key.cpp b/src/key.cpp index 74b2797d8..75114c6af 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -328,7 +328,10 @@ bool CKey::SignCompact(uint256 hash, std::vector& vchSig) } if (nRecId == -1) + { + ECDSA_SIG_free(sig); throw key_error("CKey::SignCompact() : unable to construct recoverable key"); + } vchSig[0] = nRecId+27+(fCompressedPubKey ? 4 : 0); BN_bn2bin(sig->r,&vchSig[33-(nBitsR+7)/8]); From b48905fa29430cf5ef2d363c741acfa955084e0b Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 13 Dec 2012 18:59:10 +0400 Subject: [PATCH 04/25] listreceivedbyaddress now provides tx ids (issue #1149) --- src/rpcwallet.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 5fd400c6b..f3f7b80a9 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -811,6 +811,7 @@ struct tallyitem { int64 nAmount; int nConf; + vector txids; tallyitem() { nAmount = 0; @@ -852,6 +853,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 +889,12 @@ 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::max() ? 0 : nConf))); + Array transactions; + BOOST_FOREACH(const uint256& item, (*it).second.txids) + { + transactions.push_back(item.GetHex()); + } + obj.push_back(Pair("txids", transactions)); ret.push_back(obj); } } From 16a2acc92dc9d801ff3e95cbcfee5036ba70c7af Mon Sep 17 00:00:00 2001 From: Andrey Date: Sun, 16 Dec 2012 23:10:32 +0400 Subject: [PATCH 05/25] Updated help and tests for getreceivedby(account|address) --- src/rpcwallet.cpp | 10 +++++++--- src/test/rpc_tests.cpp | 37 +++++++++++++++++++++++++++++-------- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index f3f7b80a9..e156469d1 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -890,9 +890,12 @@ Value ListReceived(const Array& params, bool fByAccounts) obj.push_back(Pair("amount", ValueFromAmount(nAmount))); obj.push_back(Pair("confirmations", (nConf == std::numeric_limits::max() ? 0 : nConf))); Array transactions; - BOOST_FOREACH(const uint256& item, (*it).second.txids) + if (it != mapTally.end()) { - transactions.push_back(item.GetHex()); + BOOST_FOREACH(const uint256& item, (*it).second.txids) + { + transactions.push_back(item.GetHex()); + } } obj.push_back(Pair("txids", transactions)); ret.push_back(obj); @@ -927,7 +930,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); } diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index f8fe443b8..35eabed0e 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -79,6 +79,35 @@ static Value CallRPC(string args) } } +BOOST_AUTO_TEST_CASE(rpc_wallet) +{ + // Test RPC calls for various wallet statistics + Value r; + + BOOST_CHECK_NO_THROW(CallRPC("listunspent")); + BOOST_CHECK_THROW(CallRPC("listunspent string"), runtime_error); + BOOST_CHECK_THROW(CallRPC("listunspent 0 string"), runtime_error); + BOOST_CHECK_THROW(CallRPC("listunspent 0 1 not_array"), runtime_error); + BOOST_CHECK_THROW(CallRPC("listunspent 0 1 [] extra"), runtime_error); + BOOST_CHECK_NO_THROW(r=CallRPC("listunspent 0 1 []")); + BOOST_CHECK(r.get_array().empty()); + + BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaddress")); + BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaddress 0")); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaddress not_int"), runtime_error); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaddress 0 not_bool"), runtime_error); + BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaddress 0 true")); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaddress 0 true extra"), runtime_error); + + BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount")); + BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount 0")); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount not_int"), runtime_error); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount 0 not_bool"), runtime_error); + BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount 0 true")); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount 0 true extra"), runtime_error); +} + + BOOST_AUTO_TEST_CASE(rpc_rawparams) { // Test raw transaction API argument handling @@ -88,14 +117,6 @@ BOOST_AUTO_TEST_CASE(rpc_rawparams) BOOST_CHECK_THROW(CallRPC("getrawtransaction not_hex"), runtime_error); BOOST_CHECK_THROW(CallRPC("getrawtransaction a3b807410df0b60fcb9736768df5823938b2f838694939ba45f3c0a1bff150ed not_int"), runtime_error); - BOOST_CHECK_NO_THROW(CallRPC("listunspent")); - BOOST_CHECK_THROW(CallRPC("listunspent string"), runtime_error); - BOOST_CHECK_THROW(CallRPC("listunspent 0 string"), runtime_error); - BOOST_CHECK_THROW(CallRPC("listunspent 0 1 not_array"), runtime_error); - BOOST_CHECK_NO_THROW(r=CallRPC("listunspent 0 1 []")); - BOOST_CHECK_THROW(r=CallRPC("listunspent 0 1 [] extra"), runtime_error); - BOOST_CHECK(r.get_array().empty()); - BOOST_CHECK_THROW(CallRPC("createrawtransaction"), runtime_error); BOOST_CHECK_THROW(CallRPC("createrawtransaction null null"), runtime_error); BOOST_CHECK_THROW(CallRPC("createrawtransaction not_array"), runtime_error); From 029c52edb5475ed3f6dfa7d2ad80eda71e8f9758 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 30 Apr 2013 21:56:04 +0200 Subject: [PATCH 06/25] Make CPubKey statically allocated --- src/hash.h | 11 ++++-- src/key.cpp | 14 ++++---- src/key.h | 85 +++++++++++++++++++++++++++++++++++++++-------- src/rpcwallet.cpp | 2 +- src/script.cpp | 5 +-- src/script.h | 6 ++-- src/util.h | 3 +- 7 files changed, 98 insertions(+), 28 deletions(-) diff --git a/src/hash.h b/src/hash.h index eaa1780c0..536ab7116 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& vch) +template +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& vch) +{ + return Hash160(vch.begin(), vch.end()); +} + unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector& vDataToHash); #endif diff --git a/src/key.cpp b/src/key.cpp index 75114c6af..a99363c12 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -258,11 +258,11 @@ CPrivKey CKey::GetPrivKey() const bool CKey::SetPubKey(const CPubKey& vchPubKey) { - const unsigned char* pbegin = &vchPubKey.vchPubKey[0]; - if (o2i_ECPublicKey(&pkey, &pbegin, vchPubKey.vchPubKey.size())) + const unsigned char* pbegin = vchPubKey.begin(); + if (o2i_ECPublicKey(&pkey, &pbegin, vchPubKey.size())) { fSet = true; - if (vchPubKey.vchPubKey.size() == 33) + if (vchPubKey.size() == 33) SetCompressedPubKey(); return true; } @@ -276,11 +276,13 @@ CPubKey CKey::GetPubKey() const int nSize = i2o_ECPublicKey(pkey, NULL); if (!nSize) throw key_error("CKey::GetPubKey() : i2o_ECPublicKey failed"); - std::vector vchPubKey(nSize, 0); - unsigned char* pbegin = &vchPubKey[0]; + assert(nSize <= 65); + CPubKey ret; + unsigned char *pbegin = ret.begin(); if (i2o_ECPublicKey(pkey, &pbegin) != nSize) throw key_error("CKey::GetPubKey() : i2o_ECPublicKey returned unexpected size"); - return CPubKey(vchPubKey); + assert((int)ret.size() == nSize); + return ret; } bool CKey::Sign(uint256 hash, std::vector& vchSig) diff --git a/src/key.h b/src/key.h index 4da16b9cd..1b122112f 100644 --- a/src/key.h +++ b/src/key.h @@ -63,38 +63,95 @@ public: /** An encapsulated public key. */ class CPubKey { private: - std::vector vchPubKey; + unsigned char vch[65]; + + 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; + } + + unsigned char *begin() { + return vch; + } + friend class CKey; public: - CPubKey() { } - CPubKey(const std::vector &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; } + CPubKey() { vch[0] = 0xFF; } - IMPLEMENT_SERIALIZE( - READWRITE(vchPubKey); - ) + CPubKey(const std::vector &vchPubKeyIn) { + int len = vchPubKeyIn.empty() ? 0 : GetLen(vchPubKeyIn[0]); + if (len) { + memcpy(vch, &vchPubKeyIn[0], len); + } else { + vch[0] = 0xFF; + } + } + + unsigned int size() const { + return GetLen(vch[0]); + } + + const unsigned char *begin() const { + return vch; + } + + const unsigned char *end() const { + return vch+size(); + } + + friend bool operator==(const CPubKey &a, const CPubKey &b) { return memcmp(a.vch, b.vch, a.size()) == 0; } + friend bool operator!=(const CPubKey &a, const CPubKey &b) { return memcmp(a.vch, b.vch, a.size()) != 0; } + 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+1, b.vch+1, a.size() - 1) < 0); + } + + unsigned int GetSerializeSize(int nType, int nVersion) const { + return size() + 1; + } + + template void Serialize(Stream &s, int nType, int nVersion) const { + unsigned int len = size(); + ::Serialize(s, VARINT(len), nType, nVersion); + s.write((char*)vch, len); + } + + template void Unserialize(Stream &s, int nType, int nVersion) { + unsigned int len; + ::Unserialize(s, VARINT(len), nType, nVersion); + if (len <= 65) { + s.read((char*)vch, len); + } else { + // invalid pubkey + vch[0] = 0xFF; + char dummy; + while (len--) + s.read(&dummy, 1); + } + } CKeyID GetID() const { - return CKeyID(Hash160(vchPubKey)); + return CKeyID(Hash160(vch, vch+size())); } uint256 GetHash() const { - return Hash(vchPubKey.begin(), vchPubKey.end()); + return Hash(vch, vch+size()); } bool IsValid() const { - return vchPubKey.size() == 33 || vchPubKey.size() == 65; + return size() > 0; } bool IsCompressed() const { - return vchPubKey.size() == 33; + return size() == 33; } std::vector Raw() const { - return vchPubKey; + return std::vector(vch, vch+size()); } }; diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index e156469d1..40891b2f9 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -1457,7 +1457,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 90066efd3..7e1d5785e 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -1895,10 +1895,11 @@ bool CScriptCompressor::Decompress(unsigned int nSize, const std::vector 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) diff --git a/src/util.h b/src/util.h index 3f3dd0f48..cc5fd2350 100644 --- a/src/util.h +++ b/src/util.h @@ -300,7 +300,8 @@ std::string HexStr(const T itbegin, const T itend, bool fSpaces=false) return rv; } -inline std::string HexStr(const std::vector& vch, bool fSpaces=false) +template +inline std::string HexStr(const T& vch, bool fSpaces=false) { return HexStr(vch.begin(), vch.end(), fSpaces); } From 05d5ef46ba0371404a3a4840115b8ef60188340f Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 1 May 2013 06:52:05 +0200 Subject: [PATCH 07/25] CSecret/CKey -> CKey/CPubKey split/refactor --- src/alert.cpp | 4 +- src/allocators.h | 9 + src/base58.h | 22 +- src/crypter.cpp | 6 +- src/crypter.h | 4 +- src/key.cpp | 497 ++++++++++++++--------------- src/key.h | 223 +++++++------ src/keystore.cpp | 86 ++--- src/keystore.h | 21 +- src/qt/signverifymessagedialog.cpp | 6 +- src/rpcdump.cpp | 19 +- src/rpcrawtransaction.cpp | 5 +- src/rpcwallet.cpp | 14 +- src/script.cpp | 33 +- src/script.h | 4 +- src/test/base58_tests.cpp | 10 +- src/test/bloom_tests.cpp | 11 +- src/test/key_tests.cpp | 92 +++--- src/test/multisig_tests.cpp | 2 +- src/test/script_P2SH_tests.cpp | 14 +- src/test/script_tests.cpp | 8 +- src/test/sigopcount_tests.cpp | 4 +- src/wallet.cpp | 18 +- src/wallet.h | 4 +- src/walletdb.cpp | 61 ++-- src/walletdb.h | 10 +- 26 files changed, 578 insertions(+), 609 deletions(-) diff --git a/src/alert.cpp b/src/alert.cpp index 4b029840d..44f4d5eec 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 eb2aed672..b4490ce27 100644 --- a/src/allocators.h +++ b/src/allocators.h @@ -176,6 +176,15 @@ private: {} }; +template void LockObject(const T &t) { + LockedPageManager::instance.LockRange((void*)(&t), sizeof(T)); +} + +template 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 be8a541f6..efe3a95eb 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/crypter.cpp b/src/crypter.cpp index a2b62a87c..32baabd67 100644 --- a/src/crypter.cpp +++ b/src/crypter.cpp @@ -100,17 +100,17 @@ bool CCrypter::Decrypt(const std::vector& vchCiphertext, CKeyingM } -bool EncryptSecret(CKeyingMaterial& vMasterKey, const CSecret &vchPlaintext, const uint256& nIV, std::vector &vchCiphertext) +bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector &vchCiphertext) { CCrypter cKeyCrypter; std::vector 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& vchCiphertext, const uint256& nIV, CSecret& vchPlaintext) +bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext) { CCrypter cKeyCrypter; std::vector chIV(WALLET_CRYPTO_KEY_SIZE); diff --git a/src/crypter.h b/src/crypter.h index 6f75170ba..4134c1b49 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 &vchCiphertext); -bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector &vchCiphertext, const uint256& nIV, CSecret &vchPlaintext); +bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector &vchCiphertext); +bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext); #endif diff --git a/src/key.cpp b/src/key.cpp index a99363c12..f73708199 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 - #include +#include #include #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,293 +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 CKey::IsNull() const -{ - return !fSet; -} - -bool CKey::IsCompressed() const -{ - return fCompressedPubKey; -} - -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 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 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; } - // 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)) - { - BN_clear_free(bn); - throw key_error("CKey::SetSecret() : EC_KEY_regenerate_key failed"); + 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]); } - 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; -} + bool SetPubKey(const CPubKey &pubkey) { + const unsigned char* pbegin = pubkey.begin(); + return o2i_ECPublicKey(&pkey, &pbegin, pubkey.size()); + } -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::SetPubKey(const CPubKey& vchPubKey) -{ - const unsigned char* pbegin = vchPubKey.begin(); - if (o2i_ECPublicKey(&pkey, &pbegin, vchPubKey.size())) - { - fSet = true; - if (vchPubKey.size() == 33) - SetCompressedPubKey(); + bool Sign(const uint256 &hash, std::vector& 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; } - pkey = NULL; - Reset(); - return false; -} -CPubKey CKey::GetPubKey() const -{ - int nSize = i2o_ECPublicKey(pkey, NULL); - if (!nSize) - throw key_error("CKey::GetPubKey() : i2o_ECPublicKey failed"); - assert(nSize <= 65); - CPubKey ret; - unsigned char *pbegin = ret.begin(); - if (i2o_ECPublicKey(pkey, &pbegin) != nSize) - throw key_error("CKey::GetPubKey() : i2o_ECPublicKey returned unexpected size"); - assert((int)ret.size() == nSize); - return ret; -} - -bool CKey::Sign(uint256 hash, std::vector& 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(); - return false; + bool Verify(const uint256 &hash, const std::vector& 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; + } + + // 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) + { + 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; } - vchSig.resize(nSize); // Shrink to fit actual size return true; } -// 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& vchSig) -{ - bool fOk = false; - ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey); - if (sig==NULL) - 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) - { - ECDSA_SIG_free(sig); - throw key_error("CKey::SignCompact() : unable to construct recoverable key"); - } - - 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; +void CKey::MakeNewKey(bool fCompressedIn) { + do { + RAND_bytes(vch, sizeof(vch)); + } while (!Check(vch)); + fValid = true; + fCompressed = fCompressedIn; } -// 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& vchSig) -{ +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; +} + +CPrivKey CKey::GetPrivKey() const { + assert(fValid); + CECKey key; + key.SetSecretBytes(vch); + CPrivKey privkey; + key.GetPrivKey(privkey); + return privkey; +} + +CPubKey CKey::GetPubKey() const { + assert(fValid); + CECKey key; + key.SetSecretBytes(vch); + CPubKey pubkey; + key.GetPubKey(pubkey, fCompressed); + return pubkey; +} + +bool CKey::Sign(const uint256 &hash, std::vector& vchSig) const { + if (!fValid) + return false; + CECKey key; + key.SetSecretBytes(vch); + return key.Sign(hash, vchSig); +} + +bool CKey::SignCompact(const uint256 &hash, std::vector& vchSig) const { + if (!fValid) + return false; + 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; +} + +bool CPubKey::Verify(const uint256 &hash, const std::vector& vchSig) const { + if (!IsValid()) + return false; + CECKey key; + if (!key.SetPubKey(*this)) + return false; + if (!key.Verify(hash, vchSig)) + return false; + return true; +} + +bool CPubKey::RecoverCompact(const uint256 &hash, const std::vector& 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; - } - ECDSA_SIG_free(sig); - return false; -} - -bool CKey::Verify(uint256 hash, const std::vector& 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; - + key.GetPubKey(*this, (vchSig[0] - 27) & 4); return true; } -bool CKey::VerifyCompact(uint256 hash, const std::vector& vchSig) -{ - CKey key; - if (!key.SetCompactSignature(hash, vchSig)) +bool CPubKey::VerifyCompact(const uint256 &hash, const std::vector& vchSig) const { + if (!IsValid()) return false; - if (GetPubKey() != key.GetPubKey()) + 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::IsValid() -{ - if (!fSet) +bool CPubKey::IsFullyValid() const { + 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(); + return true; +} + +bool CPubKey::Decompress() { + if (!IsValid()) + return false; + CECKey key; + if (!key.SetPubKey(*this)) + return false; + key.GetPubKey(*this, false); + return true; } diff --git a/src/key.h b/src/key.h index 1b122112f..b9ecd4857 100644 --- a/src/key.h +++ b/src/key.h @@ -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 #include #include "allocators.h" @@ -13,23 +12,6 @@ #include "uint256.h" #include "hash.h" -#include // 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,8 +39,11 @@ public: /** An encapsulated public key. */ class CPubKey { private: + // 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; @@ -73,146 +52,206 @@ private: return 0; } - unsigned char *begin() { - return vch; + // Set this key data to be invalid + void Invalidate() { + vch[0] = 0xFF; } - friend class CKey; - public: - CPubKey() { vch[0] = 0xFF; } - - CPubKey(const std::vector &vchPubKeyIn) { - int len = vchPubKeyIn.empty() ? 0 : GetLen(vchPubKeyIn[0]); - if (len) { - memcpy(vch, &vchPubKeyIn[0], len); - } else { - vch[0] = 0xFF; - } + // Construct an invalid public key. + CPubKey() { + Invalidate(); } - unsigned int size() const { - return GetLen(vch[0]); + // Initialize a public key using begin/end iterators to byte data. + template + 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(); } - const unsigned char *begin() const { - return vch; + // Construct a public key using begin/end iterators to byte data. + template + CPubKey(const T pbegin, const T pend) { + Set(pbegin, pend); } - const unsigned char *end() const { - return vch+size(); + // Construct a public key from a byte vector. + CPubKey(const std::vector &vch) { + Set(vch.begin(), vch.end()); } - friend bool operator==(const CPubKey &a, const CPubKey &b) { return memcmp(a.vch, b.vch, a.size()) == 0; } - friend bool operator!=(const CPubKey &a, const CPubKey &b) { return memcmp(a.vch, b.vch, a.size()) != 0; } + // Simply 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+1, b.vch+1, a.size() - 1) < 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 void Serialize(Stream &s, int nType, int nVersion) const { unsigned int len = size(); ::Serialize(s, VARINT(len), nType, nVersion); s.write((char*)vch, len); } - template void Unserialize(Stream &s, int nType, int nVersion) { unsigned int len; ::Unserialize(s, VARINT(len), nType, nVersion); if (len <= 65) { s.read((char*)vch, len); } else { - // invalid pubkey - vch[0] = 0xFF; + // 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(vch, vch+size())); } + // Get the 256-bit hash of this public key. uint256 GetHash() const { return Hash(vch, vch+size()); } + // just check syntactic correctness. bool IsValid() const { 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 size() == 33; } - std::vector Raw() const { - return std::vector(vch, vch+size()); - } + // 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& vchSig) const; + + // Verify a compact signature (~65 bytes). + // See CKey::SignCompact. + bool VerifyCompact(const uint256 &hash, const std::vector& vchSig) const; + + // Recover a public key from a compact signature. + bool RecoverCompact(const uint256 &hash, const std::vector& 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 > CPrivKey; -// CSecret is a serialization of just the secret parameter (32 bytes) -typedef std::vector > 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); + } - CKey(); - CKey(const CKey& b); + // 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)); + } - CKey& operator=(const CKey& b); + // Destructor (again necessary because of memlocking). + ~CKey() { + UnlockObject(vch); + } - ~CKey(); + // Initialize using begin and end iterators to byte data. + template + 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; + } + } - bool IsNull() const; - bool IsCompressed() const; + // 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(); } + // Check whether this private key is valid. + bool IsValid() const { return fValid; } + + // Check whether the public key corresponding to this private key is (to be) compressed. + bool IsCompressed() const { return fCompressed; } + + // 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& vchSig); + // Create a DER-serialized signature. + bool Sign(const uint256 &hash, std::vector& 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& 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& vchSig); - - bool Verify(uint256 hash, const std::vector& vchSig); - - // Verify a compact signature - bool VerifyCompact(uint256 hash, const std::vector& 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& vchSig) const; }; #endif diff --git a/src/keystore.cpp b/src/keystore.cpp index e0cf805a1..808f8c24e 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 &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 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 &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 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 ab369bbf4..49a7bf569 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 > KeyMap; +typedef std::map KeyMap; typedef std::map 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 &vchCryptedSecret); - bool AddKey(const CKey& key); + bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey); bool HaveKey(const CKeyID &address) const { { diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp index 7c43e5839..9118770f4 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("") + tr("Message verification failed.") + QString("")); diff --git a/src/rpcdump.cpp b/src/rpcdump.cpp index 77cef0273..d46309eaa 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/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index fb9811b92..c1e05466e 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 40891b2f9..6b49d8e40 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -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 pubkeys; + std::vector 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 { diff --git a/src/script.cpp b/src/script.cpp index 7e1d5785e..2c7fd5987 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -1100,11 +1100,7 @@ bool CheckSig(vector vchSig, vector vchPubKey, CSc if (signatureCache.Get(sighash, vchSig, vchPubKey)) return true; - CKey key; - if (!key.SetPubKey(vchPubKey)) - return false; - - if (!key.Verify(sighash, vchSig)) + if (!CPubKey(vchPubKey).Verify(sighash, vchSig)) return false; if (!(flags & SCRIPT_VERIFY_NOCACHE)) @@ -1770,13 +1766,13 @@ void CScript::SetDestination(const CTxDestination& dest) boost::apply_visitor(CScriptVisitor(this), dest); } -void CScript::SetMultisig(int nRequired, const std::vector& keys) +void CScript::SetMultisig(int nRequired, const std::vector& 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 +1797,17 @@ bool CScriptCompressor::IsToScriptID(CScriptID &hash) const return false; } -bool CScriptCompressor::IsToPubKey(std::vector &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 +1828,7 @@ bool CScriptCompressor::Compress(std::vector &out) const memcpy(&out[1], &scriptID, 20); return true; } - std::vector pubkey; + CPubKey pubkey; if (IsToPubKey(pubkey)) { out.resize(33); memcpy(&out[1], &pubkey[1], 32); @@ -1888,14 +1881,12 @@ bool CScriptCompressor::Decompress(unsigned int nSize, const std::vector 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 - const CPubKey pubkey = key.GetPubKey(); assert(pubkey.size() == 65); script.resize(67); script[0] = 65; diff --git a/src/script.h b/src/script.h index e63900354..3cbb2cf32 100644 --- a/src/script.h +++ b/src/script.h @@ -550,7 +550,7 @@ public: void SetDestination(const CTxDestination& address); - void SetMultisig(int nRequired, const std::vector& keys); + void SetMultisig(int nRequired, const std::vector& keys); void PrintHex() const @@ -621,7 +621,7 @@ protected: // form). bool IsToKeyID(CKeyID &hash) const; bool IsToScriptID(CScriptID &hash) const; - bool IsToPubKey(std::vector &pubkey) const; + bool IsToPubKey(CPubKey &pubkey) const; bool Compress(std::vector &out) const; unsigned int GetSpecialSize(unsigned int nSize) const; diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index 7602fa93a..2741672a8 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 4a2851cf4..0d349a990 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 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(hash.begin(), hash.end())); CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp index 0a6df88fe..c004521d1 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 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); + 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); - BOOST_CHECK(secret1 == secret1C); - BOOST_CHECK(secret2 == secret2C); + CPubKey pubkey1 = key1. GetPubKey(); + CPubKey pubkey2 = key2. GetPubKey(); + CPubKey pubkey1C = key1C.GetPubKey(); + CPubKey pubkey2C = key2C.GetPubKey(); - 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())); + 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 d6f836d36..9ef932b5b 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -30,7 +30,7 @@ sign_multisig(CScript scriptPubKey, vector keys, CTransaction transaction, CScript result; result << OP_0; // CHECKMULTISIG bug workaround - BOOST_FOREACH(CKey key, keys) + BOOST_FOREACH(const CKey &key, keys) { vector vchSig; BOOST_CHECK(key.Sign(hash, vchSig)); diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index ee0d25a2f..65f0ad0cd 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 keys; + std::vector 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(keys.begin(), keys.begin()+2)); - inner[2].SetMultisig(1, std::vector(keys.begin(), keys.begin()+2)); - inner[3].SetMultisig(2, std::vector(keys.begin(), keys.begin()+3)); + inner[1].SetMultisig(2, std::vector(keys.begin(), keys.begin()+2)); + inner[2].SetMultisig(1, std::vector(keys.begin(), keys.begin()+2)); + inner[3].SetMultisig(2, std::vector(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 keys; + vector 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 5d5a1525f..e7ad52627 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -211,7 +211,7 @@ sign_multisig(CScript scriptPubKey, std::vector keys, CTransaction transac // and vice-versa) // result << OP_0; - BOOST_FOREACH(CKey key, keys) + BOOST_FOREACH(const CKey &key, keys) { vector vchSig; BOOST_CHECK(key.Sign(hash, vchSig)); @@ -221,7 +221,7 @@ sign_multisig(CScript scriptPubKey, std::vector 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 keys; keys.push_back(key); @@ -333,11 +333,13 @@ BOOST_AUTO_TEST_CASE(script_combineSigs) // Test the CombineSignatures function CBasicKeyStore keystore; vector keys; + vector 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 1762680ad..5a87f1760 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 keys; + std::vector 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/wallet.cpp b/src/wallet.cpp index c70ea20e8..5efc45583 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; } diff --git a/src/wallet.h b/src/wallet.h index dcba4675e..7fcb8e13c 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 81a21443a..4a73413d2 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 vchPubKey; + CPubKey vchPubKey; ssKey >> vchPubKey; - CKey key; - if (strType == "key") + if (!vchPubKey.IsValid()) { - 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; - } + strErr = "Error reading wallet database: CPubKey corrupt"; + return false; } - else - { + CKey key; + CPrivKey pkey; + if (strType == "key") + ssValue >> pkey; + 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 (!pwallet->LoadKey(key)) + 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, vchPubKey)) { strErr = "Error reading wallet database: LoadKey failed"; return false; diff --git a/src/walletdb.h b/src/walletdb.h index a3e779ab9..8ae6c3ff4 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& 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) From f346774f5adbb2e223347ca98466335e7d41c11c Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 4 May 2013 16:10:09 +0200 Subject: [PATCH 08/25] Make signature cache store CPubKeys --- src/allocators.h | 4 ++++ src/key.h | 7 +++---- src/script.cpp | 20 ++++++++++++-------- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/allocators.h b/src/allocators.h index b4490ce27..85af8fe37 100644 --- a/src/allocators.h +++ b/src/allocators.h @@ -176,6 +176,10 @@ private: {} }; +// +// Functions for directly locking/unlocking memory objects. +// Intended for non-dynamically allocated structures. +// template void LockObject(const T &t) { LockedPageManager::instance.LockRange((void*)(&t), sizeof(T)); } diff --git a/src/key.h b/src/key.h index b9ecd4857..ce469ad29 100644 --- a/src/key.h +++ b/src/key.h @@ -84,7 +84,7 @@ public: Set(vch.begin(), vch.end()); } - // Simply read-only vector-like interface to the pubkey data. + // 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(); } @@ -109,12 +109,11 @@ public: } template void Serialize(Stream &s, int nType, int nVersion) const { unsigned int len = size(); - ::Serialize(s, VARINT(len), nType, nVersion); + ::WriteCompactSize(s, len); s.write((char*)vch, len); } template void Unserialize(Stream &s, int nType, int nVersion) { - unsigned int len; - ::Unserialize(s, VARINT(len), nType, nVersion); + unsigned int len = ::ReadCompactSize(s); if (len <= 65) { s.read((char*)vch, len); } else { diff --git a/src/script.cpp b/src/script.cpp index 2c7fd5987..b41166635 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 vchSig, vector vchPubKey, CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, int flags); +bool CheckSig(vector vchSig, const vector &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, std::vector > sigdata_type; + typedef boost::tuple, CPubKey> sigdata_type; std::set< sigdata_type> setValid; boost::shared_mutex cs_sigcache; public: bool - Get(uint256 hash, const std::vector& vchSig, const std::vector& pubKey) + Get(const uint256 &hash, const std::vector& vchSig, const CPubKey& pubKey) { boost::shared_lock lock(cs_sigcache); @@ -1050,7 +1050,7 @@ public: return false; } - void Set(uint256 hash, const std::vector& vchSig, const std::vector& pubKey) + void Set(const uint256 &hash, const std::vector& 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 vchSig, vector vchPubKey, CScript scriptCode, +bool CheckSig(vector vchSig, const vector &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,14 +1101,14 @@ bool CheckSig(vector vchSig, vector vchPubKey, CSc uint256 sighash = SignatureHash(scriptCode, txTo, nIn, nHashType); - if (signatureCache.Get(sighash, vchSig, vchPubKey)) + if (signatureCache.Get(sighash, vchSig, pubkey)) return true; - if (!CPubKey(vchPubKey).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; } From 9ee8ffcca7b1be1d7836c09c5763334dc4e0481c Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 12 May 2013 12:03:32 +0200 Subject: [PATCH 09/25] Check for correct genesis At startup, check that the expected genesis is loaded. This should prevent cases where accidentally a datadir from the wrong network is loaded (testnet vs mainnet, e.g.). --- src/init.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/init.cpp b/src/init.cpp index 767d7525a..eb148745d 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -864,6 +864,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"); From c2954a117bbc8c1c16d4501912c038f2cbe3760f Mon Sep 17 00:00:00 2001 From: Robert Backhaus Date: Wed, 29 May 2013 10:33:36 +1000 Subject: [PATCH 10/25] Don't attempt to resize vector to negative size. --- src/db.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/db.cpp b/src/db.cpp index 3133d99bf..fd4c67d55 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 vchData; vchData.resize(dataSize); uint256 hashIn; From 896c509dac9179645e94c24768e58e390450fdb7 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Fri, 10 May 2013 22:20:51 +0200 Subject: [PATCH 11/25] osx: make use of the 10.8+ user notification center to display growl like notifications - if 10.8, use user notification center, if <10.8, use growl Signed-off-by: Jonas Schnelli --- bitcoin-qt.pro | 4 +- src/qt/macnotificationhandler.h | 25 ++++++++++++ src/qt/macnotificationhandler.mm | 65 ++++++++++++++++++++++++++++++++ src/qt/notificator.cpp | 43 ++++++++++++++------- src/qt/notificator.h | 12 +++--- 5 files changed, 128 insertions(+), 21 deletions(-) create mode 100644 src/qt/macnotificationhandler.h create mode 100644 src/qt/macnotificationhandler.mm diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 05de05184..3324c6e4e 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -392,8 +392,8 @@ win32:!contains(MINGW_THREAD_BUGFIX, 0) { DEFINES += _FILE_OFFSET_BITS=64 } -macx:HEADERS += src/qt/macdockiconhandler.h -macx:OBJECTIVE_SOURCES += src/qt/macdockiconhandler.mm +macx:HEADERS += src/qt/macdockiconhandler.h src/qt/macnotificationhandler.h +macx:OBJECTIVE_SOURCES += src/qt/macdockiconhandler.mm src/qt/macnotificationhandler.mm macx:LIBS += -framework Foundation -framework ApplicationServices -framework AppKit macx:DEFINES += MAC_OSX MSG_NOSIGNAL=0 macx:ICON = src/qt/res/icons/bitcoin.icns diff --git a/src/qt/macnotificationhandler.h b/src/qt/macnotificationhandler.h new file mode 100644 index 000000000..cd8064c61 --- /dev/null +++ b/src/qt/macnotificationhandler.h @@ -0,0 +1,25 @@ +#ifndef MACNOTIFICATIONHANDLER_H +#define MACNOTIFICATIONHANDLER_H +#include + +/** 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 000000000..8bb9b887a --- /dev/null +++ b/src/qt/macnotificationhandler.mm @@ -0,0 +1,65 @@ +#include "macnotificationhandler.h" + +#undef slots +#include + +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 397e7aa4a..7cfaef607 100644 --- a/src/qt/notificator.cpp +++ b/src/qt/notificator.cpp @@ -18,7 +18,7 @@ #ifdef Q_OS_MAC #include -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 d20673abb..d1fe37fea 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 }; From 5536b22b8d1b88245c5b4b0be85deb32660694f5 Mon Sep 17 00:00:00 2001 From: Philip Kaufmann Date: Tue, 23 Apr 2013 09:54:29 +0200 Subject: [PATCH 12/25] remove obsolete and unused typedef for Windows --- src/util.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/util.h b/src/util.h index cc5fd2350..b3fe91824 100644 --- a/src/util.h +++ b/src/util.h @@ -13,8 +13,6 @@ #include #include #include -#else -typedef int pid_t; /* define for Windows compatibility */ #endif #include #include From 4cea72e80680f0b2bc354616d56866423d67f8fe Mon Sep 17 00:00:00 2001 From: Timon Rapp Date: Fri, 31 May 2013 15:39:28 +0200 Subject: [PATCH 13/25] Added NSHighResolutionCapable flag to Info.plist for better font rendering on Retina displays. --- share/qt/Info.plist | 2 ++ 1 file changed, 2 insertions(+) diff --git a/share/qt/Info.plist b/share/qt/Info.plist index 2312094c4..6f64c3164 100644 --- a/share/qt/Info.plist +++ b/share/qt/Info.plist @@ -31,5 +31,7 @@ + NSHighResolutionCapable + From c21beff608f3e0b96ea84b92792562b8c890cade Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Fri, 3 May 2013 15:18:28 +0200 Subject: [PATCH 14/25] MaxOSX: settings fixes (#2371) - Launch-At-Startup implementation for mac - Remove "Window" tab in settings Signed-off-by: Jonas Schnelli --- bitcoin-qt.pro | 2 +- src/qt/bitcoin.cpp | 3 +++ src/qt/guiutil.cpp | 56 +++++++++++++++++++++++++++++++++++++--- src/qt/optionsdialog.cpp | 3 ++- 4 files changed, 59 insertions(+), 5 deletions(-) diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 3324c6e4e..2aa681d70 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -394,7 +394,7 @@ win32:!contains(MINGW_THREAD_BUGFIX, 0) { macx:HEADERS += src/qt/macdockiconhandler.h src/qt/macnotificationhandler.h macx:OBJECTIVE_SOURCES += src/qt/macdockiconhandler.mm src/qt/macnotificationhandler.mm -macx:LIBS += -framework Foundation -framework ApplicationServices -framework AppKit +macx:LIBS += -framework Foundation -framework ApplicationServices -framework AppKit -framework CoreServices macx:DEFINES += MAC_OSX MSG_NOSIGNAL=0 macx:ICON = src/qt/res/icons/bitcoin.icns macx:QMAKE_CFLAGS_THREAD += -pthread diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index f525d1bb3..d9eab6133 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -222,9 +222,12 @@ int main(int argc, char *argv[]) try { +#ifndef Q_OS_MAC // Regenerate startup link, to fix links to old versions + // OSX: makes no sense on mac and might also scan/mount external (and sleeping) volumes (can take up some secs) if (GUIUtil::GetStartOnSystemStartup()) GUIUtil::SetStartOnSystemStartup(true); +#endif boost::thread_group threadGroup; diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 2105f0730..6ef72b143 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -408,10 +408,60 @@ bool SetStartOnSystemStartup(bool fAutoStart) } return true; } -#else -// TODO: OSX startup stuff; see: -// https://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html +#elif defined(Q_OS_MAC) +// based on: https://github.com/Mozketo/LaunchAtLoginController/blob/master/LaunchAtLoginController.m + +#include +#include + +LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl); +LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl) +{ + // loop through the list of startup items and try to find the bitcoin app + CFArrayRef listSnapshot = LSSharedFileListCopySnapshot(list, NULL); + for(int i = 0; i < CFArrayGetCount(listSnapshot); i++) { + LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(listSnapshot, i); + UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes; + CFURLRef currentItemURL = NULL; + LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, NULL); + if(currentItemURL && CFEqual(currentItemURL, findUrl)) { + // found + CFRelease(currentItemURL); + return item; + } + if(currentItemURL) { + CFRelease(currentItemURL); + } + } + return NULL; +} + +bool GetStartOnSystemStartup() +{ + CFURLRef bitcoinAppUrl = CFBundleCopyBundleURL(CFBundleGetMainBundle()); + LSSharedFileListRef loginItems = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL); + LSSharedFileListItemRef foundItem = findStartupItemInList(loginItems, bitcoinAppUrl); + return !!foundItem; // return boolified object +} + +bool SetStartOnSystemStartup(bool fAutoStart) +{ + CFURLRef bitcoinAppUrl = CFBundleCopyBundleURL(CFBundleGetMainBundle()); + LSSharedFileListRef loginItems = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL); + LSSharedFileListItemRef foundItem = findStartupItemInList(loginItems, bitcoinAppUrl); + + if(fAutoStart && !foundItem) { + // add bitcoin app to startup item list + LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemBeforeFirst, NULL, NULL, bitcoinAppUrl, NULL, NULL); + } + else if(!fAutoStart && foundItem) { + // remove item + LSSharedFileListItemRemove(loginItems, foundItem); + } + return true; +} +#else bool GetStartOnSystemStartup() { return false; } bool SetStartOnSystemStartup(bool fAutoStart) { return false; } diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 4fa1f6092..b2451aea3 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -45,7 +45,8 @@ OptionsDialog::OptionsDialog(QWidget *parent) : /* Window elements init */ #ifdef Q_OS_MAC - ui->tabWindow->setVisible(false); + /* remove Window tab on Mac */ + ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabWindow)); #endif /* Display elements init */ From f948663a809c90646bbab3de9df333f89f3bcbe1 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 31 May 2013 14:02:24 +0200 Subject: [PATCH 15/25] Qt5 compatibility This commit squashes all the changes in the Qt5 branch relative to master. Backward compatibility with Qt4 is retained. Original authors: - Philip Kaufmann - Jonas Schnelli --- bitcoin-qt.pro | 6 +++++- doc/readme-qt.rst | 6 ++++++ src/qt/addressbookpage.cpp | 5 +++++ src/qt/bitcoin.cpp | 4 ++++ src/qt/bitcoingui.cpp | 2 ++ src/qt/guiutil.cpp | 20 +++++++++++++++++++- src/qt/macdockiconhandler.mm | 30 ++++++++++++++++++++++-------- src/qt/paymentserver.cpp | 2 ++ src/qt/qrcodedialog.cpp | 2 ++ src/qt/rpcconsole.cpp | 2 ++ src/qt/sendcoinsdialog.cpp | 4 ++++ src/qt/splashscreen.cpp | 1 + src/qt/transactionview.cpp | 4 ++++ src/qt/walletframe.cpp | 4 +--- src/qt/walletframe.h | 3 ++- src/qt/walletstack.h | 1 + src/qt/walletview.cpp | 8 ++++++++ 17 files changed, 90 insertions(+), 14 deletions(-) diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 2aa681d70..6b64566cc 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -3,7 +3,8 @@ TARGET = bitcoin-qt macx:TARGET = "Bitcoin-Qt" VERSION = 0.8.3 INCLUDEPATH += src src/json src/qt -QT += network +QT += core gui network +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets DEFINES += QT_GUI BOOST_THREAD_USE_LIB BOOST_SPIRIT_THREADSAFE CONFIG += no_include_pwd CONFIG += thread @@ -211,6 +212,7 @@ HEADERS += src/qt/bitcoingui.h \ src/leveldb.h \ src/threadsafety.h \ src/limitedmap.h \ + src/qt/macnotificationhandler.h \ src/qt/splashscreen.h SOURCES += src/qt/bitcoin.cpp \ @@ -312,6 +314,7 @@ DEFINES += BITCOIN_QT_TEST macx: CONFIG -= app_bundle } +# Todo: Remove this line when switching to Qt5, as that option was removed CODECFORTR = UTF-8 # for lrelease/lupdate @@ -335,6 +338,7 @@ QMAKE_EXTRA_COMPILERS += TSQM OTHER_FILES += README.md \ doc/*.rst \ doc/*.txt \ + doc/*.md \ src/qt/res/bitcoin-qt.rc \ src/test/*.cpp \ src/test/*.h \ diff --git a/doc/readme-qt.rst b/doc/readme-qt.rst index 1bc3d701a..95cb33650 100644 --- a/doc/readme-qt.rst +++ b/doc/readme-qt.rst @@ -28,6 +28,12 @@ for Ubuntu >= 12.04 (please read the 'Berkely DB version warning' below): libboost-filesystem-dev libboost-program-options-dev libboost-thread-dev \ libssl-dev libdb++-dev libminiupnpc-dev +For Qt 5 you need the following, otherwise you get an error with lrelease when running qmake: + +:: + + apt-get install qt5-qmake libqt5gui5 libqt5core5 libqt5dbus5 qttools5-dev-tools + then execute the following: :: diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index 8529c88b3..8906174d7 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 d9eab6133..362745661 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -17,7 +17,9 @@ #include "splashscreen.h" #include +#if QT_VERSION < 0x050000 #include +#endif #include #include #include @@ -118,9 +120,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); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 78a69af8b..a4589860b 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -44,7 +44,9 @@ #include #include #include +#if QT_VERSION < 0x050000 #include +#endif #include #include #include diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 6ef72b143..b3d165643 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -13,8 +13,12 @@ #include #include #include +#if QT_VERSION >= 0x050000 +#include +#else #include -#include // For Qt::escape +#endif +#include // for Qt::mightBeRichText #include #include #include @@ -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 > items = uri.queryItems(); +#else + QUrlQuery uriQuery(uri); + QList > items = uriQuery.queryItems(); +#endif for (QList >::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", "
\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/macdockiconhandler.mm b/src/qt/macdockiconhandler.mm index b6ea8e1d0..53b49c42e 100644 --- a/src/qt/macdockiconhandler.mm +++ b/src/qt/macdockiconhandler.mm @@ -2,8 +2,8 @@ #include #include - -extern void qt_mac_set_dock_menu(QMenu*); +#include +#include #undef slots #include @@ -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/paymentserver.cpp b/src/qt/paymentserver.cpp index a92e6fc32..0b0bce55b 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -18,7 +18,9 @@ #include #include #include +#if QT_VERSION < 0x050000 #include +#endif using namespace boost; diff --git a/src/qt/qrcodedialog.cpp b/src/qt/qrcodedialog.cpp index ca9497512..6ddcaaf5d 100644 --- a/src/qt/qrcodedialog.cpp +++ b/src/qt/qrcodedialog.cpp @@ -7,7 +7,9 @@ #include "optionsmodel.h" #include +#if QT_VERSION < 0x050000 #include +#endif #include diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 50f22b877..7f90f4a55 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -8,7 +8,9 @@ #include #include #include +#if QT_VERSION < 0x050000 #include +#endif #include #include diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index eb3ce48a6..2c7bab204 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("%1 to %2 (%3)").arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, rcp.amount), Qt::escape(rcp.label), rcp.address)); +#else + formatted.append(tr("%1 to %2 (%3)").arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, rcp.amount), rcp.label.toHtmlEscaped(), rcp.address)); +#endif } fNewRecipientAllowed = false; diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index f8d16699c..95948a6cb 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -3,6 +3,7 @@ #include "util.h" #include +#undef loop /* ugh, remove this when the #define loop is gone from util.h */ #include SplashScreen::SplashScreen(const QPixmap &pixmap, Qt::WindowFlags f) : diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 9240b71c7..a43e29c47 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 83e4255c9..50c03ac62 100644 --- a/src/qt/walletframe.cpp +++ b/src/qt/walletframe.cpp @@ -8,11 +8,9 @@ #include "bitcoingui.h" #include "walletstack.h" -#include +#include #include -#include - WalletFrame::WalletFrame(BitcoinGUI *_gui) : QFrame(_gui), gui(_gui), diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h index 3649185e8..d7092f987 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 ea4cc121d..506d595c0 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 6d44c174b..277c05669 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -21,7 +21,11 @@ #include #include #include +#if QT_VERSION < 0x050000 #include +#else +#include +#endif #include #include @@ -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)) { From e5bd319136ed1f45f1599c70a490bbd6845123f7 Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Tue, 4 Jun 2013 23:44:53 -0400 Subject: [PATCH 16/25] osx: fix bitcoin-qt startup crash when clicking dock icon Crash probably introduced by 4d17a1b0. Inialize the window to NULL and verify it before use. --- src/qt/macdockiconhandler.mm | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/qt/macdockiconhandler.mm b/src/qt/macdockiconhandler.mm index 53b49c42e..8f826941b 100644 --- a/src/qt/macdockiconhandler.mm +++ b/src/qt/macdockiconhandler.mm @@ -51,6 +51,7 @@ MacDockIconHandler::MacDockIconHandler() : QObject() this->m_dockIconClickEventHandler = [[DockIconClickEventHandler alloc] initWithDockIconHandler:this]; this->m_dummyWidget = new QWidget(); this->m_dockMenu = new QMenu(this->m_dummyWidget); + this->setMainWindow(NULL); [pool release]; } @@ -114,8 +115,11 @@ MacDockIconHandler *MacDockIconHandler::instance() void MacDockIconHandler::handleDockIconClickEvent() { - this->mainWindow->activateWindow(); - this->mainWindow->show(); + if (this->mainWindow) + { + this->mainWindow->activateWindow(); + this->mainWindow->show(); + } emit this->dockIconClicked(); } From 53542dc9a78446de2597208614a64a4bd8a07e77 Mon Sep 17 00:00:00 2001 From: Philip Kaufmann Date: Mon, 13 May 2013 08:26:29 +0200 Subject: [PATCH 17/25] allow txindex to be removed and add a reindex dialog - adds a reindex dialog for Bitcoin-Qt to change -txindex without the need to supply -reindex - now also does a -reindex, when removing the -txindex switch --- src/init.cpp | 11 +++++++---- src/qt/bitcoinstrings.cpp | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index eb148745d..e1f08c635 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -869,6 +869,12 @@ bool AppInit2(boost::thread_group& threadGroup) if (!mapBlockIndex.empty() && pindexGenesisBlock == NULL) return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?")); + // Check for changed -txindex state (only necessary if we are not reindexing anyway) + if (!fReindex && fTxIndex != GetBoolArg("-txindex", false)) { + strLoadError = _("You need to rebuild the database using -reindex to change -txindex"); + break; + } + // Initialize the block index (no-op if non-empty database was already loaded) if (!InitBlockIndex()) { strLoadError = _("Error initializing block database"); @@ -892,7 +898,7 @@ bool AppInit2(boost::thread_group& threadGroup) // first suggest a reindex if (!fReset) { bool fRet = uiInterface.ThreadSafeMessageBox( - strLoadError + ".\n" + _("Do you want to rebuild the block database now?"), + strLoadError + ".\n\n" + _("Do you want to rebuild the block database now?"), "", CClientUIInterface::MSG_ERROR | CClientUIInterface::BTN_ABORT); if (fRet) { fReindex = true; @@ -906,9 +912,6 @@ bool AppInit2(boost::thread_group& threadGroup) } } - if (mapArgs.count("-txindex") && fTxIndex != GetBoolArg("-txindex", false)) - return InitError(_("You need to rebuild the databases using -reindex to change -txindex")); - // as LoadBlockIndex can take several minutes, it's possible the user // requested to kill bitcoin-qt during the last operation. If so, exit. // As the program has not fully started yet, Shutdown() is possibly overkill. diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp index 4dae8999c..1afce2eb7 100644 --- a/src/qt/bitcoinstrings.cpp +++ b/src/qt/bitcoinstrings.cpp @@ -205,6 +205,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Verifying wallet..."), QT_TRANSLATE_NOOP("bitcoin-core", "Wallet needed to be rewritten: restart Bitcoin to complete"), QT_TRANSLATE_NOOP("bitcoin-core", "Warning"), QT_TRANSLATE_NOOP("bitcoin-core", "Warning: This version is obsolete, upgrade required!"), -QT_TRANSLATE_NOOP("bitcoin-core", "You need to rebuild the databases using -reindex to change -txindex"), +QT_TRANSLATE_NOOP("bitcoin-core", "You need to rebuild the database using -reindex to change -txindex"), QT_TRANSLATE_NOOP("bitcoin-core", "wallet.dat corrupt, salvage failed"), }; \ No newline at end of file From 8ae8e623550460c4c1de4a3747b2a3816cee2761 Mon Sep 17 00:00:00 2001 From: theuni Date: Thu, 13 Jun 2013 23:39:54 -0400 Subject: [PATCH 18/25] fixed: include boost header as necessary Without this include, sometimes BOOST_VERSION was defined and sometimes it was not, depending on which includes came before it. The result was a random mix of sleep or sleep_for for boost versions >= 1.50. --- src/util.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util.h b/src/util.h index b3fe91824..c2fb7066e 100644 --- a/src/util.h +++ b/src/util.h @@ -20,6 +20,7 @@ #include #include +#include #include #include #include From f85096b30af357385fa490dc19e73a89ea625764 Mon Sep 17 00:00:00 2001 From: theuni Date: Thu, 13 Jun 2013 23:46:08 -0400 Subject: [PATCH 19/25] fixed: don't use thread::sleep_for where it's known to be broken Fixes #2690. --- src/util.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/util.h b/src/util.h index c2fb7066e..2be01e19f 100644 --- a/src/util.h +++ b/src/util.h @@ -105,7 +105,11 @@ T* alignup(T* p) inline void MilliSleep(int64 n) { -#if BOOST_VERSION >= 105000 +// Boost's sleep_for was uninterruptable when backed by nanosleep from 1.50 +// until fixed in 1.52. Use the deprecated sleep method for the broken case. +// See: https://svn.boost.org/trac/boost/ticket/7238 + +#if BOOST_VERSION >= 105000 && (!defined(BOOST_HAS_NANOSLEEP) || BOOST_VERSION >= 105200) boost::this_thread::sleep_for(boost::chrono::milliseconds(n)); #else boost::this_thread::sleep(boost::posix_time::milliseconds(n)); From 44cada759026951cf022e8792037fd781da6faba Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 22 Jun 2013 16:03:11 +0200 Subject: [PATCH 20/25] Initialize database before checking changed txindex In case no database exists yet, and -txindex(=1) is passed, we currently first check whether fTxIndex differs from -txindex (and ask the user to reindex in that case), and only afterwards initialize the database. By swapping these around (the initialization is a no-op in case the database already exists), we allow it to be born in txindex mode, without warning. That also means we don't need to check -reindex anymore, as the wiping/reinit of the databases happens before checking. --- src/init.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index e1f08c635..9387819f0 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -869,18 +869,18 @@ bool AppInit2(boost::thread_group& threadGroup) if (!mapBlockIndex.empty() && pindexGenesisBlock == NULL) return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?")); - // Check for changed -txindex state (only necessary if we are not reindexing anyway) - if (!fReindex && fTxIndex != GetBoolArg("-txindex", false)) { - strLoadError = _("You need to rebuild the database using -reindex to change -txindex"); - break; - } - // Initialize the block index (no-op if non-empty database was already loaded) if (!InitBlockIndex()) { strLoadError = _("Error initializing block database"); break; } + // Check for changed -txindex state + if (fTxIndex != GetBoolArg("-txindex", false)) { + strLoadError = _("You need to rebuild the database using -reindex to change -txindex"); + break; + } + uiInterface.InitMessage(_("Verifying blocks...")); if (!VerifyDB()) { strLoadError = _("Corrupted block database detected"); From 3b0b36e38005d9b47089c1b7f05d4a57c44657a9 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 29 Jun 2013 14:16:50 +0200 Subject: [PATCH 21/25] Fix build date for from-tarball builds --- src/version.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.cpp b/src/version.cpp index 8af406fea..d9d6724a0 100644 --- a/src/version.cpp +++ b/src/version.cpp @@ -36,7 +36,7 @@ const std::string CLIENT_NAME("Satoshi"); // git will put "#define GIT_ARCHIVE 1" on the next line inside archives. $Format:%n#define GIT_ARCHIVE 1$ #ifdef GIT_ARCHIVE # define GIT_COMMIT_ID "$Format:%h$" -# define GIT_COMMIT_DATE "$Format:%cD" +# define GIT_COMMIT_DATE "$Format:%cD$" #endif #define BUILD_DESC_FROM_COMMIT(maj,min,rev,build,commit) \ From 8535ecba70f2df582c3301e1dc4e673fc51ce826 Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Wed, 26 Jun 2013 20:31:34 -0400 Subject: [PATCH 22/25] Fix Gnome bitcoin: URI handler --- contrib/debian/bitcoin-qt.desktop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/debian/bitcoin-qt.desktop b/contrib/debian/bitcoin-qt.desktop index 5d6f781e4..b2a2cef62 100644 --- a/contrib/debian/bitcoin-qt.desktop +++ b/contrib/debian/bitcoin-qt.desktop @@ -4,7 +4,7 @@ Name=Bitcoin Comment=Bitcoin P2P Cryptocurrency Comment[fr]=Bitcoin, monnaie virtuelle cryptographique pair à pair Comment[tr]=Bitcoin, eşten eşe kriptografik sanal para birimi -Exec=/usr/bin/bitcoin-qt +Exec=/usr/bin/bitcoin-qt %u Terminal=false Type=Application Icon=/usr/share/pixmaps/bitcoin128.png From b018b7f25c29d6edec1137f7c93b037240d06492 Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Thu, 27 Jun 2013 11:11:02 -0400 Subject: [PATCH 23/25] README for contrib/debian --- contrib/debian/README | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 contrib/debian/README diff --git a/contrib/debian/README b/contrib/debian/README new file mode 100644 index 000000000..1cb9b75fb --- /dev/null +++ b/contrib/debian/README @@ -0,0 +1,20 @@ +This directory contains files used to package bitcoind/bitcoin-qt +for Debian-based Linux systems. + +If you compile bitcoind/bitcoin-qt yourself, there are some +useful files here: + +bitcoin: URI support +-------------------- + +bitcoin-qt.desktop (Gnome / Open Desktop) +To install: + sudo desktop-file-install bitcoin-qt.desktop + sudo update-desktop-database + +If you build yourself, you will either need to modify the paths in +the .desktop file or copy or symlink your bitcoin-qt binary to /usr/bin +and the ../../share/pixmaps/bitcoin128.png to /usr/share/pixmaps + +bitcoin-qt.protocol (KDE) + From cb3076a3daa68eebf19c681ab48a0c0d9f0ce7a5 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Wed, 5 Jun 2013 14:52:06 -0400 Subject: [PATCH 24/25] Log reason for non-standard transaction rejection --- src/main.cpp | 36 ++++++++++++++++++++++++++---------- src/main.h | 7 ++++++- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 226d32295..f2c78fccf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -373,37 +373,51 @@ bool CTxOut::IsDust() const return ((nValue*1000)/(3*((int)GetSerializeSize(SER_DISK,0)+148)) < CTransaction::nMinRelayTxFee); } -bool CTransaction::IsStandard() const +bool CTransaction::IsStandard(string& strReason) const { - if (nVersion > CTransaction::CURRENT_VERSION) + if (nVersion > CTransaction::CURRENT_VERSION) { + strReason = "version"; return false; + } - if (!IsFinal()) + if (!IsFinal()) { + strReason = "not-final"; return false; + } // Extremely large transactions with lots of inputs can cost the network // almost as much to process as they cost the sender in fees, because // computing signature hashes is O(ninputs*txsize). Limiting transactions // to MAX_STANDARD_TX_SIZE mitigates CPU exhaustion attacks. unsigned int sz = this->GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); - if (sz >= MAX_STANDARD_TX_SIZE) + if (sz >= MAX_STANDARD_TX_SIZE) { + strReason = "tx-size"; return false; + } BOOST_FOREACH(const CTxIn& txin, vin) { // Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG // pay-to-script-hash, which is 3 ~80-byte signatures, 3 // ~65-byte public keys, plus a few script ops. - if (txin.scriptSig.size() > 500) + if (txin.scriptSig.size() > 500) { + strReason = "scriptsig-size"; return false; - if (!txin.scriptSig.IsPushOnly()) + } + if (!txin.scriptSig.IsPushOnly()) { + strReason = "scriptsig-not-pushonly"; return false; + } } BOOST_FOREACH(const CTxOut& txout, vout) { - if (!::IsStandard(txout.scriptPubKey)) + if (!::IsStandard(txout.scriptPubKey)) { + strReason = "scriptpubkey"; return false; - if (txout.IsDust()) + } + if (txout.IsDust()) { + strReason = "dust"; return false; + } } return true; } @@ -668,8 +682,10 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckIn return error("CTxMemPool::accept() : not accepting nLockTime beyond 2038 yet"); // Rather not work on nonstandard transactions (unless -testnet) - if (!fTestNet && !tx.IsStandard()) - return error("CTxMemPool::accept() : nonstandard transaction type"); + string strNonStd; + if (!fTestNet && !tx.IsStandard(strNonStd)) + return error("CTxMemPool::accept() : nonstandard transaction (%s)", + strNonStd.c_str()); // is it already in the memory pool? uint256 hash = tx.GetHash(); diff --git a/src/main.h b/src/main.h index 8e71e66cc..e77ef6b21 100644 --- a/src/main.h +++ b/src/main.h @@ -559,7 +559,12 @@ public: /** Check for standard transaction types @return True if all outputs (scriptPubKeys) use only standard transaction forms */ - bool IsStandard() const; + bool IsStandard(std::string& strReason) const; + bool IsStandard() const + { + std::string strReason; + return IsStandard(strReason); + } /** Check for standard transaction types @param[in] mapInputs Map of previous transactions that have outputs we're spending From 3a619b01b3f9ee11d67c9504dcb0fdc946428f7f Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 13 Jul 2013 12:56:52 +0200 Subject: [PATCH 25/25] Fix minor backward incompatibility The key refactor changed the way unencrypted private keys with compressed public key are stored in the wallet. Apparently older versions relied on this to verify the correctness of stored keys. Note that earlier pre-release versions do risk creating wallets that can not be opened by 0.8.3 and earlier. --- src/key.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/key.cpp b/src/key.cpp index f73708199..1ab4c62eb 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -155,7 +155,8 @@ public: BN_clear_free(&bn); } - void GetPrivKey(CPrivKey &privkey) { + void GetPrivKey(CPrivKey &privkey, bool fCompressed) { + EC_KEY_set_conv_form(pkey, fCompressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED); int nSize = i2d_ECPrivateKey(pkey, NULL); assert(nSize); privkey.resize(nSize); @@ -304,7 +305,7 @@ CPrivKey CKey::GetPrivKey() const { CECKey key; key.SetSecretBytes(vch); CPrivKey privkey; - key.GetPrivKey(privkey); + key.GetPrivKey(privkey, fCompressed); return privkey; }