From 11529c6e4f7288d8a64c488a726ee3821c7adefe Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 21 Nov 2011 02:46:28 +0100 Subject: [PATCH] Compressed pubkeys This patch enabled compressed pubkeys when -compressedpubkeys is passed. These are 33 bytes instead of 65, and require only marginally more CPU power when verifying. Compressed pubkeys have a different corresponding address, so it is determined at generation. When -compressedpubkeys is given, all newly generated addresses will use a compressed key, while older/other addresses keep using normal keys. Unpatched clients will relay and verify these transactions. --- src/base58.h | 21 ++++++++++++--------- src/bitcoinrpc.cpp | 3 +++ src/db.cpp | 2 ++ src/key.h | 34 ++++++++++++++++++++++++++++++---- src/keystore.cpp | 36 ++++++++++++++++++------------------ src/keystore.h | 24 ++++++++---------------- src/rpcdump.cpp | 9 ++++++--- 7 files changed, 79 insertions(+), 50 deletions(-) diff --git a/src/base58.h b/src/base58.h index ef6493d..dce932b 100644 --- a/src/base58.h +++ b/src/base58.h @@ -359,22 +359,25 @@ public: class CBitcoinSecret : public CBase58Data { public: - void SetSecret(const CSecret& vchSecret) - { + void SetSecret(const CSecret& vchSecret, bool fCompressed) + { + assert(vchSecret.size() == 32); SetData(fTestNet ? 239 : 128, &vchSecret[0], vchSecret.size()); + if (fCompressed) + vchData.push_back(1); } - CSecret GetSecret() + CSecret GetSecret(bool &fCompressedOut) { CSecret vchSecret; - vchSecret.resize(vchData.size()); - memcpy(&vchSecret[0], &vchData[0], vchData.size()); + vchSecret.resize(32); + memcpy(&vchSecret[0], &vchData[0], 32); + fCompressedOut = vchData.size() == 33; return vchSecret; } bool IsValid() const { - int nExpectedSize = 32; bool fExpectTestNet = false; switch(nVersion) { @@ -388,12 +391,12 @@ public: default: return false; } - return fExpectTestNet == fTestNet && vchData.size() == nExpectedSize; + return fExpectTestNet == fTestNet && (vchData.size() == 32 || (vchData.size() == 33 && vchData[32] == 1)); } - CBitcoinSecret(const CSecret& vchSecret) + CBitcoinSecret(const CSecret& vchSecret, bool fCompressed) { - SetSecret(vchSecret); + SetSecret(vchSecret, fCompressed); } CBitcoinSecret() diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 09be73a..ce29840 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -1701,6 +1701,9 @@ Value validateaddress(const Array& params, bool fHelp) ret.push_back(Pair("pubkey", HexStr(vchPubKey))); std::string strPubKey(vchPubKey.begin(), vchPubKey.end()); ret.push_back(Pair("pubkey58", EncodeBase58(vchPubKey))); + CKey key; + key.SetPubKey(vchPubKey); + ret.push_back(Pair("iscompressed", key.IsCompressed())); } else if (pwalletMain->HaveCScript(address.GetHash160())) { diff --git a/src/db.cpp b/src/db.cpp index f43b2a5..b3bbaca 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -860,12 +860,14 @@ int CWalletDB::LoadWallet(CWallet* pwallet) { CPrivKey pkey; ssValue >> pkey; + key.SetPubKey(vchPubKey); key.SetPrivKey(pkey); } else { CWalletKey wkey; ssValue >> wkey; + key.SetPubKey(vchPubKey); key.SetPrivKey(wkey.vchPrivKey); } if (!pwallet->LoadKey(key)) diff --git a/src/key.h b/src/key.h index 94ec552..b6d805c 100644 --- a/src/key.h +++ b/src/key.h @@ -59,16 +59,30 @@ class CKey protected: EC_KEY* pkey; bool fSet; + bool fCompressedPubKey; + + void SetCompressedPubKey() + { + EC_KEY_set_conv_form(pkey, POINT_CONVERSION_COMPRESSED); + fCompressedPubKey = true; + } public: - CKey() + + void Reset() { + fCompressedPubKey = false; 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() + { + Reset(); + } + CKey(const CKey& b) { pkey = EC_KEY_dup(b.pkey); @@ -95,10 +109,17 @@ public: return !fSet; } - void MakeNewKey() + bool IsCompressed() const + { + return fCompressedPubKey; + } + + void MakeNewKey(bool fCompressed = true) { if (!EC_KEY_generate_key(pkey)) throw key_error("CKey::MakeNewKey() : EC_KEY_generate_key failed"); + if (fCompressed) + SetCompressedPubKey(); fSet = true; } @@ -111,7 +132,7 @@ public: return true; } - bool SetSecret(const CSecret& vchSecret) + bool SetSecret(const CSecret& vchSecret, bool fCompressed = false) { EC_KEY_free(pkey); pkey = EC_KEY_new_by_curve_name(NID_secp256k1); @@ -126,10 +147,12 @@ public: throw key_error("CKey::SetSecret() : EC_KEY_regenerate_key failed"); BN_clear_free(bn); fSet = true; + if (fCompressed || fCompressedPubKey) + SetCompressedPubKey(); return true; } - CSecret GetSecret() const + CSecret GetSecret(bool &fCompressed) const { CSecret vchRet; vchRet.resize(32); @@ -140,6 +163,7 @@ public: int n=BN_bn2bin(bn,&vchRet[32 - nBytes]); if (n != nBytes) throw key_error("CKey::GetSecret(): BN_bn2bin failed"); + fCompressed = fCompressedPubKey; return vchRet; } @@ -161,6 +185,8 @@ public: if (!o2i_ECPublicKey(&pkey, &pbegin, vchPubKey.size())) return false; fSet = true; + if (vchPubKey.size() == 33) + SetCompressedPubKey(); return true; } diff --git a/src/keystore.cpp b/src/keystore.cpp index 21fb0f9..6c3ed34 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -29,8 +29,10 @@ bool CKeyStore::GetPubKey(const CBitcoinAddress &address, std::vector CCryptoKeyStore::GenerateNewKey() -{ - RandAddSeedPerfmon(); - CKey key; - key.MakeNewKey(); - if (!AddKey(key)) - throw std::runtime_error("CCryptoKeyStore::GenerateNewKey() : AddKey failed"); - return key.GetPubKey(); -} - bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) { CRITICAL_BLOCK(cs_KeyStore) @@ -103,6 +95,7 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) if(!DecryptSecret(vMasterKeyIn, vchCryptedSecret, Hash(vchPubKey.begin(), vchPubKey.end()), vchSecret)) return false; CKey key; + key.SetPubKey(vchPubKey); key.SetSecret(vchSecret); if (key.GetPubKey() == vchPubKey) break; @@ -125,7 +118,8 @@ bool CCryptoKeyStore::AddKey(const CKey& key) std::vector vchCryptedSecret; std::vector vchPubKey = key.GetPubKey(); - if (!EncryptSecret(vMasterKey, key.GetSecret(), Hash(vchPubKey.begin(), vchPubKey.end()), vchCryptedSecret)) + bool fCompressed; + if (!EncryptSecret(vMasterKey, key.GetSecret(fCompressed), Hash(vchPubKey.begin(), vchPubKey.end()), vchCryptedSecret)) return false; if (!AddCryptedKey(key.GetPubKey(), vchCryptedSecret)) @@ -147,19 +141,24 @@ bool CCryptoKeyStore::AddCryptedKey(const std::vector &vchPubKey, return true; } -bool CCryptoKeyStore::GetSecret(const CBitcoinAddress &address, CSecret& vchSecretOut) const +bool CCryptoKeyStore::GetKey(const CBitcoinAddress &address, CKey& keyOut) const { CRITICAL_BLOCK(cs_KeyStore) { if (!IsCrypted()) - return CBasicKeyStore::GetSecret(address, vchSecretOut); + return CBasicKeyStore::GetKey(address, keyOut); CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); if (mi != mapCryptedKeys.end()) { const std::vector &vchPubKey = (*mi).second.first; const std::vector &vchCryptedSecret = (*mi).second.second; - return DecryptSecret(vMasterKey, vchCryptedSecret, Hash(vchPubKey.begin(), vchPubKey.end()), vchSecretOut); + CSecret vchSecret; + if (!DecryptSecret(vMasterKey, vchCryptedSecret, Hash(vchPubKey.begin(), vchPubKey.end()), vchSecret)) + return false; + keyOut.SetPubKey(vchPubKey); + keyOut.SetSecret(vchSecret); + return true; } } return false; @@ -190,14 +189,15 @@ bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn) return false; fUseCrypto = true; - CKey key; BOOST_FOREACH(KeyMap::value_type& mKey, mapKeys) { - if (!key.SetSecret(mKey.second)) + CKey key; + if (!key.SetSecret(mKey.second.first, false)) return false; const std::vector vchPubKey = key.GetPubKey(); std::vector vchCryptedSecret; - if (!EncryptSecret(vMasterKeyIn, key.GetSecret(), Hash(vchPubKey.begin(), vchPubKey.end()), vchCryptedSecret)) + bool fCompressed; + if (!EncryptSecret(vMasterKeyIn, key.GetSecret(fCompressed), Hash(vchPubKey.begin(), vchPubKey.end()), vchCryptedSecret)) return false; if (!AddCryptedKey(vchPubKey, vchCryptedSecret)) return false; diff --git a/src/keystore.h b/src/keystore.h index 669bf90..801afbf 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -20,15 +20,7 @@ public: // Check whether a key corresponding to a given address is present in the store. virtual bool HaveKey(const CBitcoinAddress &address) const =0; - virtual bool GetKey(const CBitcoinAddress &address, CKey& keyOut) const - { - CSecret vchSecret; - if (!GetSecret(address, vchSecret)) - return false; - if (!keyOut.SetSecret(vchSecret)) - return false; - return true; - } + virtual bool GetKey(const CBitcoinAddress &address, CKey& keyOut) const =0; virtual void GetKeys(std::set &setAddress) const =0; virtual bool GetPubKey(const CBitcoinAddress &address, std::vector& vchPubKeyOut) const; @@ -39,17 +31,17 @@ public: // Generate a new key, and add it to the store virtual std::vector GenerateNewKey(); - virtual bool GetSecret(const CBitcoinAddress &address, CSecret& vchSecret) const + virtual bool GetSecret(const CBitcoinAddress &address, CSecret& vchSecret, bool &fCompressed) const { CKey key; if (!GetKey(address, key)) return false; - vchSecret = key.GetSecret(); + 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 @@ -81,14 +73,15 @@ public: } } } - bool GetSecret(const CBitcoinAddress &address, CSecret &vchSecret) const + bool GetKey(const CBitcoinAddress &address, CKey &keyOut) const { CRITICAL_BLOCK(cs_KeyStore) { KeyMap::const_iterator mi = mapKeys.find(address); if (mi != mapKeys.end()) { - vchSecret = (*mi).second; + keyOut.Reset(); + keyOut.SetSecret((*mi).second.first, (*mi).second.second); return true; } } @@ -154,7 +147,6 @@ public: } virtual bool AddCryptedKey(const std::vector &vchPubKey, const std::vector &vchCryptedSecret); - std::vector GenerateNewKey(); bool AddKey(const CKey& key); bool HaveKey(const CBitcoinAddress &address) const { @@ -166,7 +158,7 @@ public: } return false; } - bool GetSecret(const CBitcoinAddress &address, CSecret& vchSecret) const; + bool GetKey(const CBitcoinAddress &address, CKey& keyOut) const; bool GetPubKey(const CBitcoinAddress &address, std::vector& vchPubKeyOut) const; void GetKeys(std::set &setAddress) const { diff --git a/src/rpcdump.cpp b/src/rpcdump.cpp index f3978fb..471421e 100644 --- a/src/rpcdump.cpp +++ b/src/rpcdump.cpp @@ -62,7 +62,9 @@ Value importprivkey(const Array& params, bool fHelp) if (!fGood) throw JSONRPCError(-5,"Invalid private key"); CKey key; - key.SetSecret(vchSecret.GetSecret()); + bool fCompressed; + CSecret secret = vchSecret.GetSecret(fCompressed); + key.SetSecret(secret, fCompressed); CBitcoinAddress vchAddress = CBitcoinAddress(key.GetPubKey()); CRITICAL_BLOCK(cs_main) @@ -95,7 +97,8 @@ Value dumpprivkey(const Array& params, bool fHelp) if (!address.SetString(strAddress)) throw JSONRPCError(-5, "Invalid bitcoin address"); CSecret vchSecret; - if (!pwalletMain->GetSecret(address, vchSecret)) + bool fCompressed; + if (!pwalletMain->GetSecret(address, vchSecret, fCompressed)) throw JSONRPCError(-4,"Private key for address " + strAddress + " is not known"); - return CBitcoinSecret(vchSecret).ToString(); + return CBitcoinSecret(vchSecret, fCompressed).ToString(); } -- 1.7.1