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.
This commit is contained in:
Pieter Wuille 2011-11-21 02:46:28 +01:00
parent 1684f98b27
commit 11529c6e4f
7 changed files with 79 additions and 50 deletions

View file

@ -359,22 +359,25 @@ public:
class CBitcoinSecret : public CBase58Data class CBitcoinSecret : public CBase58Data
{ {
public: 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()); SetData(fTestNet ? 239 : 128, &vchSecret[0], vchSecret.size());
if (fCompressed)
vchData.push_back(1);
} }
CSecret GetSecret() CSecret GetSecret(bool &fCompressedOut)
{ {
CSecret vchSecret; CSecret vchSecret;
vchSecret.resize(vchData.size()); vchSecret.resize(32);
memcpy(&vchSecret[0], &vchData[0], vchData.size()); memcpy(&vchSecret[0], &vchData[0], 32);
fCompressedOut = vchData.size() == 33;
return vchSecret; return vchSecret;
} }
bool IsValid() const bool IsValid() const
{ {
int nExpectedSize = 32;
bool fExpectTestNet = false; bool fExpectTestNet = false;
switch(nVersion) switch(nVersion)
{ {
@ -388,12 +391,12 @@ public:
default: default:
return false; 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() CBitcoinSecret()

View file

@ -1701,6 +1701,9 @@ Value validateaddress(const Array& params, bool fHelp)
ret.push_back(Pair("pubkey", HexStr(vchPubKey))); ret.push_back(Pair("pubkey", HexStr(vchPubKey)));
std::string strPubKey(vchPubKey.begin(), vchPubKey.end()); std::string strPubKey(vchPubKey.begin(), vchPubKey.end());
ret.push_back(Pair("pubkey58", EncodeBase58(vchPubKey))); 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())) else if (pwalletMain->HaveCScript(address.GetHash160()))
{ {

View file

@ -860,12 +860,14 @@ int CWalletDB::LoadWallet(CWallet* pwallet)
{ {
CPrivKey pkey; CPrivKey pkey;
ssValue >> pkey; ssValue >> pkey;
key.SetPubKey(vchPubKey);
key.SetPrivKey(pkey); key.SetPrivKey(pkey);
} }
else else
{ {
CWalletKey wkey; CWalletKey wkey;
ssValue >> wkey; ssValue >> wkey;
key.SetPubKey(vchPubKey);
key.SetPrivKey(wkey.vchPrivKey); key.SetPrivKey(wkey.vchPrivKey);
} }
if (!pwallet->LoadKey(key)) if (!pwallet->LoadKey(key))

View file

@ -59,16 +59,30 @@ class CKey
protected: protected:
EC_KEY* pkey; EC_KEY* pkey;
bool fSet; bool fSet;
bool fCompressedPubKey;
void SetCompressedPubKey()
{
EC_KEY_set_conv_form(pkey, POINT_CONVERSION_COMPRESSED);
fCompressedPubKey = true;
}
public: public:
CKey()
void Reset()
{ {
fCompressedPubKey = false;
pkey = EC_KEY_new_by_curve_name(NID_secp256k1); pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
if (pkey == NULL) if (pkey == NULL)
throw key_error("CKey::CKey() : EC_KEY_new_by_curve_name failed"); throw key_error("CKey::CKey() : EC_KEY_new_by_curve_name failed");
fSet = false; fSet = false;
} }
CKey()
{
Reset();
}
CKey(const CKey& b) CKey(const CKey& b)
{ {
pkey = EC_KEY_dup(b.pkey); pkey = EC_KEY_dup(b.pkey);
@ -95,10 +109,17 @@ public:
return !fSet; return !fSet;
} }
void MakeNewKey() bool IsCompressed() const
{
return fCompressedPubKey;
}
void MakeNewKey(bool fCompressed = true)
{ {
if (!EC_KEY_generate_key(pkey)) if (!EC_KEY_generate_key(pkey))
throw key_error("CKey::MakeNewKey() : EC_KEY_generate_key failed"); throw key_error("CKey::MakeNewKey() : EC_KEY_generate_key failed");
if (fCompressed)
SetCompressedPubKey();
fSet = true; fSet = true;
} }
@ -111,7 +132,7 @@ public:
return true; return true;
} }
bool SetSecret(const CSecret& vchSecret) bool SetSecret(const CSecret& vchSecret, bool fCompressed = false)
{ {
EC_KEY_free(pkey); EC_KEY_free(pkey);
pkey = EC_KEY_new_by_curve_name(NID_secp256k1); pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
@ -126,10 +147,12 @@ public:
throw key_error("CKey::SetSecret() : EC_KEY_regenerate_key failed"); throw key_error("CKey::SetSecret() : EC_KEY_regenerate_key failed");
BN_clear_free(bn); BN_clear_free(bn);
fSet = true; fSet = true;
if (fCompressed || fCompressedPubKey)
SetCompressedPubKey();
return true; return true;
} }
CSecret GetSecret() const CSecret GetSecret(bool &fCompressed) const
{ {
CSecret vchRet; CSecret vchRet;
vchRet.resize(32); vchRet.resize(32);
@ -140,6 +163,7 @@ public:
int n=BN_bn2bin(bn,&vchRet[32 - nBytes]); int n=BN_bn2bin(bn,&vchRet[32 - nBytes]);
if (n != nBytes) if (n != nBytes)
throw key_error("CKey::GetSecret(): BN_bn2bin failed"); throw key_error("CKey::GetSecret(): BN_bn2bin failed");
fCompressed = fCompressedPubKey;
return vchRet; return vchRet;
} }
@ -161,6 +185,8 @@ public:
if (!o2i_ECPublicKey(&pkey, &pbegin, vchPubKey.size())) if (!o2i_ECPublicKey(&pkey, &pbegin, vchPubKey.size()))
return false; return false;
fSet = true; fSet = true;
if (vchPubKey.size() == 33)
SetCompressedPubKey();
return true; return true;
} }

View file

@ -29,8 +29,10 @@ bool CKeyStore::GetPubKey(const CBitcoinAddress &address, std::vector<unsigned c
bool CBasicKeyStore::AddKey(const CKey& key) bool CBasicKeyStore::AddKey(const CKey& key)
{ {
bool fCompressed = false;
CSecret secret = key.GetSecret(fCompressed);
CRITICAL_BLOCK(cs_KeyStore) CRITICAL_BLOCK(cs_KeyStore)
mapKeys[CBitcoinAddress(key.GetPubKey())] = key.GetSecret(); mapKeys[CBitcoinAddress(key.GetPubKey())] = make_pair(secret, fCompressed);
return true; return true;
} }
@ -77,16 +79,6 @@ bool CCryptoKeyStore::SetCrypted()
return true; return true;
} }
std::vector<unsigned char> 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) bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn)
{ {
CRITICAL_BLOCK(cs_KeyStore) CRITICAL_BLOCK(cs_KeyStore)
@ -103,6 +95,7 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn)
if(!DecryptSecret(vMasterKeyIn, vchCryptedSecret, Hash(vchPubKey.begin(), vchPubKey.end()), vchSecret)) if(!DecryptSecret(vMasterKeyIn, vchCryptedSecret, Hash(vchPubKey.begin(), vchPubKey.end()), vchSecret))
return false; return false;
CKey key; CKey key;
key.SetPubKey(vchPubKey);
key.SetSecret(vchSecret); key.SetSecret(vchSecret);
if (key.GetPubKey() == vchPubKey) if (key.GetPubKey() == vchPubKey)
break; break;
@ -125,7 +118,8 @@ bool CCryptoKeyStore::AddKey(const CKey& key)
std::vector<unsigned char> vchCryptedSecret; std::vector<unsigned char> vchCryptedSecret;
std::vector<unsigned char> vchPubKey = key.GetPubKey(); std::vector<unsigned char> 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; return false;
if (!AddCryptedKey(key.GetPubKey(), vchCryptedSecret)) if (!AddCryptedKey(key.GetPubKey(), vchCryptedSecret))
@ -147,19 +141,24 @@ bool CCryptoKeyStore::AddCryptedKey(const std::vector<unsigned char> &vchPubKey,
return true; return true;
} }
bool CCryptoKeyStore::GetSecret(const CBitcoinAddress &address, CSecret& vchSecretOut) const bool CCryptoKeyStore::GetKey(const CBitcoinAddress &address, CKey& keyOut) const
{ {
CRITICAL_BLOCK(cs_KeyStore) CRITICAL_BLOCK(cs_KeyStore)
{ {
if (!IsCrypted()) if (!IsCrypted())
return CBasicKeyStore::GetSecret(address, vchSecretOut); return CBasicKeyStore::GetKey(address, keyOut);
CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address);
if (mi != mapCryptedKeys.end()) if (mi != mapCryptedKeys.end())
{ {
const std::vector<unsigned char> &vchPubKey = (*mi).second.first; const std::vector<unsigned char> &vchPubKey = (*mi).second.first;
const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second; const std::vector<unsigned char> &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; return false;
@ -190,14 +189,15 @@ bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn)
return false; return false;
fUseCrypto = true; fUseCrypto = true;
CKey key;
BOOST_FOREACH(KeyMap::value_type& mKey, mapKeys) BOOST_FOREACH(KeyMap::value_type& mKey, mapKeys)
{ {
if (!key.SetSecret(mKey.second)) CKey key;
if (!key.SetSecret(mKey.second.first, false))
return false; return false;
const std::vector<unsigned char> vchPubKey = key.GetPubKey(); const std::vector<unsigned char> vchPubKey = key.GetPubKey();
std::vector<unsigned char> vchCryptedSecret; std::vector<unsigned char> 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; return false;
if (!AddCryptedKey(vchPubKey, vchCryptedSecret)) if (!AddCryptedKey(vchPubKey, vchCryptedSecret))
return false; return false;

View file

@ -20,15 +20,7 @@ public:
// Check whether a key corresponding to a given address is present in the store. // Check whether a key corresponding to a given address is present in the store.
virtual bool HaveKey(const CBitcoinAddress &address) const =0; virtual bool HaveKey(const CBitcoinAddress &address) const =0;
virtual bool GetKey(const CBitcoinAddress &address, CKey& keyOut) const virtual bool GetKey(const CBitcoinAddress &address, CKey& keyOut) const =0;
{
CSecret vchSecret;
if (!GetSecret(address, vchSecret))
return false;
if (!keyOut.SetSecret(vchSecret))
return false;
return true;
}
virtual void GetKeys(std::set<CBitcoinAddress> &setAddress) const =0; virtual void GetKeys(std::set<CBitcoinAddress> &setAddress) const =0;
virtual bool GetPubKey(const CBitcoinAddress &address, std::vector<unsigned char>& vchPubKeyOut) const; virtual bool GetPubKey(const CBitcoinAddress &address, std::vector<unsigned char>& vchPubKeyOut) const;
@ -39,17 +31,17 @@ public:
// Generate a new key, and add it to the store // Generate a new key, and add it to the store
virtual std::vector<unsigned char> GenerateNewKey(); virtual std::vector<unsigned char> GenerateNewKey();
virtual bool GetSecret(const CBitcoinAddress &address, CSecret& vchSecret) const virtual bool GetSecret(const CBitcoinAddress &address, CSecret& vchSecret, bool &fCompressed) const
{ {
CKey key; CKey key;
if (!GetKey(address, key)) if (!GetKey(address, key))
return false; return false;
vchSecret = key.GetSecret(); vchSecret = key.GetSecret(fCompressed);
return true; return true;
} }
}; };
typedef std::map<CBitcoinAddress, CSecret> KeyMap; typedef std::map<CBitcoinAddress, std::pair<CSecret, bool> > KeyMap;
typedef std::map<uint160, CScript > ScriptMap; typedef std::map<uint160, CScript > ScriptMap;
// Basic key store, that keeps keys in an address->secret map // 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) CRITICAL_BLOCK(cs_KeyStore)
{ {
KeyMap::const_iterator mi = mapKeys.find(address); KeyMap::const_iterator mi = mapKeys.find(address);
if (mi != mapKeys.end()) if (mi != mapKeys.end())
{ {
vchSecret = (*mi).second; keyOut.Reset();
keyOut.SetSecret((*mi).second.first, (*mi).second.second);
return true; return true;
} }
} }
@ -154,7 +147,6 @@ public:
} }
virtual bool AddCryptedKey(const std::vector<unsigned char> &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret); virtual bool AddCryptedKey(const std::vector<unsigned char> &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
std::vector<unsigned char> GenerateNewKey();
bool AddKey(const CKey& key); bool AddKey(const CKey& key);
bool HaveKey(const CBitcoinAddress &address) const bool HaveKey(const CBitcoinAddress &address) const
{ {
@ -166,7 +158,7 @@ public:
} }
return false; 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<unsigned char>& vchPubKeyOut) const; bool GetPubKey(const CBitcoinAddress &address, std::vector<unsigned char>& vchPubKeyOut) const;
void GetKeys(std::set<CBitcoinAddress> &setAddress) const void GetKeys(std::set<CBitcoinAddress> &setAddress) const
{ {

View file

@ -62,7 +62,9 @@ Value importprivkey(const Array& params, bool fHelp)
if (!fGood) throw JSONRPCError(-5,"Invalid private key"); if (!fGood) throw JSONRPCError(-5,"Invalid private key");
CKey key; CKey key;
key.SetSecret(vchSecret.GetSecret()); bool fCompressed;
CSecret secret = vchSecret.GetSecret(fCompressed);
key.SetSecret(secret, fCompressed);
CBitcoinAddress vchAddress = CBitcoinAddress(key.GetPubKey()); CBitcoinAddress vchAddress = CBitcoinAddress(key.GetPubKey());
CRITICAL_BLOCK(cs_main) CRITICAL_BLOCK(cs_main)
@ -95,7 +97,8 @@ Value dumpprivkey(const Array& params, bool fHelp)
if (!address.SetString(strAddress)) if (!address.SetString(strAddress))
throw JSONRPCError(-5, "Invalid bitcoin address"); throw JSONRPCError(-5, "Invalid bitcoin address");
CSecret vchSecret; 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"); throw JSONRPCError(-4,"Private key for address " + strAddress + " is not known");
return CBitcoinSecret(vchSecret).ToString(); return CBitcoinSecret(vchSecret, fCompressed).ToString();
} }