[Wallet] Add simplest BIP32/deterministic key generation implementation

This commit is contained in:
Jonas Schnelli 2016-01-02 12:34:08 +01:00
parent 950be19727
commit f19025106d
No known key found for this signature in database
GPG Key ID: 29D4BCB6416F53EC
4 changed files with 143 additions and 4 deletions

View File

@ -91,7 +91,48 @@ CPubKey CWallet::GenerateNewKey()
bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets
CKey secret; CKey secret;
// Create new metadata
int64_t nCreationTime = GetTime();
CKeyMetadata metadata(nCreationTime);
// use HD key derivation if HD was enabled during wallet creation
if (!hdChain.masterKeyID.IsNull()) {
// for now we use a fixed keypath scheme of m/0'/0'/k
CKey key; //master key seed (256bit)
CExtKey masterKey; //hd master key
CExtKey accountKey; //key at m/0'
CExtKey externalChainChildKey; //key at m/0'/0'
CExtKey childKey; //key at m/0'/0'/<n>'
// try to get the master key
if (!GetKey(hdChain.masterKeyID, key))
throw std::runtime_error("CWallet::GenerateNewKey(): Master key not found");
masterKey.SetMaster(key.begin(), key.size());
// derive m/0'
// use hardened derivation (child keys > 0x80000000 are hardened after bip32)
masterKey.Derive(accountKey, 0 | 0x80000000);
// derive m/0'/0'
accountKey.Derive(externalChainChildKey, 0 | 0x80000000);
// derive child key at next index, skip keys already known to the wallet
do
{
externalChainChildKey.Derive(childKey, hdChain.nExternalChainCounter | 0x80000000);
// increment childkey index
hdChain.nExternalChainCounter++;
} while(HaveKey(childKey.key.GetPubKey().GetID()));
secret = childKey.key;
// update the chain model in the database
if (!CWalletDB(strWalletFile).WriteHDChain(hdChain))
throw std::runtime_error("CWallet::GenerateNewKey(): Writing HD chain model failed");
} else {
secret.MakeNewKey(fCompressed); secret.MakeNewKey(fCompressed);
}
// Compressed public keys were introduced in version 0.6.0 // Compressed public keys were introduced in version 0.6.0
if (fCompressed) if (fCompressed)
@ -100,9 +141,7 @@ CPubKey CWallet::GenerateNewKey()
CPubKey pubkey = secret.GetPubKey(); CPubKey pubkey = secret.GetPubKey();
assert(secret.VerifyPubKey(pubkey)); assert(secret.VerifyPubKey(pubkey));
// Create new metadata mapKeyMetadata[pubkey.GetID()] = metadata;
int64_t nCreationTime = GetTime();
mapKeyMetadata[pubkey.GetID()] = CKeyMetadata(nCreationTime);
if (!nTimeFirstKey || nCreationTime < nTimeFirstKey) if (!nTimeFirstKey || nCreationTime < nTimeFirstKey)
nTimeFirstKey = nCreationTime; nTimeFirstKey = nCreationTime;
@ -1049,6 +1088,37 @@ CAmount CWallet::GetChange(const CTransaction& tx) const
return nChange; return nChange;
} }
bool CWallet::SetHDMasterKey(const CKey& key)
{
LOCK(cs_wallet);
// store the key as normal "key"/"ckey" object
// in the database
// key metadata is not required
CPubKey pubkey = key.GetPubKey();
if (!AddKeyPubKey(key, pubkey))
throw std::runtime_error("CWallet::GenerateNewKey(): AddKey failed");
// store the keyid (hash160) together with
// the child index counter in the database
// as a hdchain object
CHDChain newHdChain;
newHdChain.masterKeyID = pubkey.GetID();
SetHDChain(newHdChain, false);
return true;
}
bool CWallet::SetHDChain(const CHDChain& chain, bool memonly)
{
LOCK(cs_wallet);
if (!memonly && !CWalletDB(strWalletFile).WriteHDChain(chain))
throw runtime_error("AddHDChain(): writing chain failed");
hdChain = chain;
return true;
}
int64_t CWalletTx::GetTxTime() const int64_t CWalletTx::GetTxTime() const
{ {
int64_t n = nTimeSmart; int64_t n = nTimeSmart;
@ -3058,6 +3128,7 @@ std::string CWallet::GetWalletHelpString(bool showDebug)
strUsage += HelpMessageOpt("-sendfreetransactions", strprintf(_("Send transactions as zero-fee transactions if possible (default: %u)"), DEFAULT_SEND_FREE_TRANSACTIONS)); strUsage += HelpMessageOpt("-sendfreetransactions", strprintf(_("Send transactions as zero-fee transactions if possible (default: %u)"), DEFAULT_SEND_FREE_TRANSACTIONS));
strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE)); strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE));
strUsage += HelpMessageOpt("-txconfirmtarget=<n>", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET)); strUsage += HelpMessageOpt("-txconfirmtarget=<n>", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET));
strUsage += HelpMessageOpt("-usehd", _("Use hierarchical deterministic key generation (HD) after bip32. Only has effect during wallet creation/first start") + " " + strprintf(_("(default: %u)"), DEFAULT_USE_HD_WALLET));
strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format on startup")); strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format on startup"));
strUsage += HelpMessageOpt("-wallet=<file>", _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), DEFAULT_WALLET_DAT)); strUsage += HelpMessageOpt("-wallet=<file>", _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), DEFAULT_WALLET_DAT));
strUsage += HelpMessageOpt("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), DEFAULT_WALLETBROADCAST)); strUsage += HelpMessageOpt("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), DEFAULT_WALLETBROADCAST));
@ -3145,6 +3216,13 @@ bool CWallet::InitLoadWallet()
if (fFirstRun) if (fFirstRun)
{ {
// Create new keyUser and set as default key // Create new keyUser and set as default key
if (GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET)) {
// generate a new master key
CKey key;
key.MakeNewKey(true);
if (!walletInstance->SetHDMasterKey(key))
throw std::runtime_error("CWallet::GenerateNewKey(): Storing master key failed");
}
CPubKey newDefaultKey; CPubKey newDefaultKey;
if (walletInstance->GetKeyFromPool(newDefaultKey)) { if (walletInstance->GetKeyFromPool(newDefaultKey)) {
walletInstance->SetDefaultKey(newDefaultKey); walletInstance->SetDefaultKey(newDefaultKey);

View File

@ -57,6 +57,9 @@ static const unsigned int DEFAULT_TX_CONFIRM_TARGET = 2;
static const unsigned int MAX_FREE_TRANSACTION_CREATE_SIZE = 1000; static const unsigned int MAX_FREE_TRANSACTION_CREATE_SIZE = 1000;
static const bool DEFAULT_WALLETBROADCAST = true; static const bool DEFAULT_WALLETBROADCAST = true;
//! if set, all keys will be derived by using BIP32
static const bool DEFAULT_USE_HD_WALLET = true;
extern const char * DEFAULT_WALLET_DAT; extern const char * DEFAULT_WALLET_DAT;
class CBlockIndex; class CBlockIndex;
@ -574,6 +577,9 @@ private:
void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>); void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>);
/* the hd chain data model (external chain counters) */
CHDChain hdChain;
public: public:
/* /*
* Main wallet lock. * Main wallet lock.
@ -887,6 +893,12 @@ public:
static bool ParameterInteraction(); static bool ParameterInteraction();
bool BackupWallet(const std::string& strDest); bool BackupWallet(const std::string& strDest);
/* Set the hd chain model (chain child index counters) */
bool SetHDChain(const CHDChain& chain, bool memonly);
/* Set the current hd master key (will reset the chain child index counters) */
bool SetHDMasterKey(const CKey& key);
}; };
/** A key allocated from the key pool. */ /** A key allocated from the key pool. */

View File

@ -599,6 +599,16 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
return false; return false;
} }
} }
else if (strType == "hdchain")
{
CHDChain chain;
ssValue >> chain;
if (!pwallet->SetHDChain(chain, true))
{
strErr = "Error reading wallet database: SetHDChain failed";
return false;
}
}
} catch (...) } catch (...)
{ {
return false; return false;
@ -1003,3 +1013,10 @@ bool CWalletDB::EraseDestData(const std::string &address, const std::string &key
nWalletDBUpdated++; nWalletDBUpdated++;
return Erase(std::make_pair(std::string("destdata"), std::make_pair(address, key))); return Erase(std::make_pair(std::string("destdata"), std::make_pair(address, key)));
} }
bool CWalletDB::WriteHDChain(const CHDChain& chain)
{
nWalletDBUpdated++;
return Write(std::string("hdchain"), chain);
}

View File

@ -40,6 +40,35 @@ enum DBErrors
DB_NEED_REWRITE DB_NEED_REWRITE
}; };
/* simple hd chain data model */
class CHDChain
{
public:
uint32_t nExternalChainCounter;
CKeyID masterKeyID; //!< master key hash160
static const int CURRENT_VERSION = 1;
int nVersion;
CHDChain() { SetNull(); }
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
{
READWRITE(this->nVersion);
nVersion = this->nVersion;
READWRITE(nExternalChainCounter);
READWRITE(masterKeyID);
}
void SetNull()
{
nVersion = CHDChain::CURRENT_VERSION;
nExternalChainCounter = 0;
masterKeyID.SetNull();
}
};
class CKeyMetadata class CKeyMetadata
{ {
public: public:
@ -134,6 +163,9 @@ public:
static bool Recover(CDBEnv& dbenv, const std::string& filename, bool fOnlyKeys); static bool Recover(CDBEnv& dbenv, const std::string& filename, bool fOnlyKeys);
static bool Recover(CDBEnv& dbenv, const std::string& filename); static bool Recover(CDBEnv& dbenv, const std::string& filename);
//! write the hdchain model (external chain child index counter)
bool WriteHDChain(const CHDChain& chain);
private: private:
CWalletDB(const CWalletDB&); CWalletDB(const CWalletDB&);
void operator=(const CWalletDB&); void operator=(const CWalletDB&);