Skip to content

Commit f190251

Browse files
committed
[Wallet] Add simplest BIP32/deterministic key generation implementation
1 parent 950be19 commit f190251

File tree

4 files changed

+143
-4
lines changed

4 files changed

+143
-4
lines changed

src/wallet/wallet.cpp

Lines changed: 82 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,48 @@ CPubKey CWallet::GenerateNewKey()
9191
bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets
9292

9393
CKey secret;
94-
secret.MakeNewKey(fCompressed);
94+
95+
// Create new metadata
96+
int64_t nCreationTime = GetTime();
97+
CKeyMetadata metadata(nCreationTime);
98+
99+
// use HD key derivation if HD was enabled during wallet creation
100+
if (!hdChain.masterKeyID.IsNull()) {
101+
// for now we use a fixed keypath scheme of m/0'/0'/k
102+
CKey key; //master key seed (256bit)
103+
CExtKey masterKey; //hd master key
104+
CExtKey accountKey; //key at m/0'
105+
CExtKey externalChainChildKey; //key at m/0'/0'
106+
CExtKey childKey; //key at m/0'/0'/<n>'
107+
108+
// try to get the master key
109+
if (!GetKey(hdChain.masterKeyID, key))
110+
throw std::runtime_error("CWallet::GenerateNewKey(): Master key not found");
111+
112+
masterKey.SetMaster(key.begin(), key.size());
113+
114+
// derive m/0'
115+
// use hardened derivation (child keys > 0x80000000 are hardened after bip32)
116+
masterKey.Derive(accountKey, 0 | 0x80000000);
117+
118+
// derive m/0'/0'
119+
accountKey.Derive(externalChainChildKey, 0 | 0x80000000);
120+
121+
// derive child key at next index, skip keys already known to the wallet
122+
do
123+
{
124+
externalChainChildKey.Derive(childKey, hdChain.nExternalChainCounter | 0x80000000);
125+
// increment childkey index
126+
hdChain.nExternalChainCounter++;
127+
} while(HaveKey(childKey.key.GetPubKey().GetID()));
128+
secret = childKey.key;
129+
130+
// update the chain model in the database
131+
if (!CWalletDB(strWalletFile).WriteHDChain(hdChain))
132+
throw std::runtime_error("CWallet::GenerateNewKey(): Writing HD chain model failed");
133+
} else {
134+
secret.MakeNewKey(fCompressed);
135+
}
95136

96137
// Compressed public keys were introduced in version 0.6.0
97138
if (fCompressed)
@@ -100,9 +141,7 @@ CPubKey CWallet::GenerateNewKey()
100141
CPubKey pubkey = secret.GetPubKey();
101142
assert(secret.VerifyPubKey(pubkey));
102143

103-
// Create new metadata
104-
int64_t nCreationTime = GetTime();
105-
mapKeyMetadata[pubkey.GetID()] = CKeyMetadata(nCreationTime);
144+
mapKeyMetadata[pubkey.GetID()] = metadata;
106145
if (!nTimeFirstKey || nCreationTime < nTimeFirstKey)
107146
nTimeFirstKey = nCreationTime;
108147

@@ -1049,6 +1088,37 @@ CAmount CWallet::GetChange(const CTransaction& tx) const
10491088
return nChange;
10501089
}
10511090

1091+
bool CWallet::SetHDMasterKey(const CKey& key)
1092+
{
1093+
LOCK(cs_wallet);
1094+
1095+
// store the key as normal "key"/"ckey" object
1096+
// in the database
1097+
// key metadata is not required
1098+
CPubKey pubkey = key.GetPubKey();
1099+
if (!AddKeyPubKey(key, pubkey))
1100+
throw std::runtime_error("CWallet::GenerateNewKey(): AddKey failed");
1101+
1102+
// store the keyid (hash160) together with
1103+
// the child index counter in the database
1104+
// as a hdchain object
1105+
CHDChain newHdChain;
1106+
newHdChain.masterKeyID = pubkey.GetID();
1107+
SetHDChain(newHdChain, false);
1108+
1109+
return true;
1110+
}
1111+
1112+
bool CWallet::SetHDChain(const CHDChain& chain, bool memonly)
1113+
{
1114+
LOCK(cs_wallet);
1115+
if (!memonly && !CWalletDB(strWalletFile).WriteHDChain(chain))
1116+
throw runtime_error("AddHDChain(): writing chain failed");
1117+
1118+
hdChain = chain;
1119+
return true;
1120+
}
1121+
10521122
int64_t CWalletTx::GetTxTime() const
10531123
{
10541124
int64_t n = nTimeSmart;
@@ -3058,6 +3128,7 @@ std::string CWallet::GetWalletHelpString(bool showDebug)
30583128
strUsage += HelpMessageOpt("-sendfreetransactions", strprintf(_("Send transactions as zero-fee transactions if possible (default: %u)"), DEFAULT_SEND_FREE_TRANSACTIONS));
30593129
strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE));
30603130
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));
3131+
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));
30613132
strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format on startup"));
30623133
strUsage += HelpMessageOpt("-wallet=<file>", _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), DEFAULT_WALLET_DAT));
30633134
strUsage += HelpMessageOpt("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), DEFAULT_WALLETBROADCAST));
@@ -3145,6 +3216,13 @@ bool CWallet::InitLoadWallet()
31453216
if (fFirstRun)
31463217
{
31473218
// Create new keyUser and set as default key
3219+
if (GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET)) {
3220+
// generate a new master key
3221+
CKey key;
3222+
key.MakeNewKey(true);
3223+
if (!walletInstance->SetHDMasterKey(key))
3224+
throw std::runtime_error("CWallet::GenerateNewKey(): Storing master key failed");
3225+
}
31483226
CPubKey newDefaultKey;
31493227
if (walletInstance->GetKeyFromPool(newDefaultKey)) {
31503228
walletInstance->SetDefaultKey(newDefaultKey);

src/wallet/wallet.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ static const unsigned int DEFAULT_TX_CONFIRM_TARGET = 2;
5757
static const unsigned int MAX_FREE_TRANSACTION_CREATE_SIZE = 1000;
5858
static const bool DEFAULT_WALLETBROADCAST = true;
5959

60+
//! if set, all keys will be derived by using BIP32
61+
static const bool DEFAULT_USE_HD_WALLET = true;
62+
6063
extern const char * DEFAULT_WALLET_DAT;
6164

6265
class CBlockIndex;
@@ -574,6 +577,9 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
574577

575578
void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>);
576579

580+
/* the hd chain data model (external chain counters) */
581+
CHDChain hdChain;
582+
577583
public:
578584
/*
579585
* Main wallet lock.
@@ -887,6 +893,12 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
887893
static bool ParameterInteraction();
888894

889895
bool BackupWallet(const std::string& strDest);
896+
897+
/* Set the hd chain model (chain child index counters) */
898+
bool SetHDChain(const CHDChain& chain, bool memonly);
899+
900+
/* Set the current hd master key (will reset the chain child index counters) */
901+
bool SetHDMasterKey(const CKey& key);
890902
};
891903

892904
/** A key allocated from the key pool. */

src/wallet/walletdb.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,16 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
599599
return false;
600600
}
601601
}
602+
else if (strType == "hdchain")
603+
{
604+
CHDChain chain;
605+
ssValue >> chain;
606+
if (!pwallet->SetHDChain(chain, true))
607+
{
608+
strErr = "Error reading wallet database: SetHDChain failed";
609+
return false;
610+
}
611+
}
602612
} catch (...)
603613
{
604614
return false;
@@ -1003,3 +1013,10 @@ bool CWalletDB::EraseDestData(const std::string &address, const std::string &key
10031013
nWalletDBUpdated++;
10041014
return Erase(std::make_pair(std::string("destdata"), std::make_pair(address, key)));
10051015
}
1016+
1017+
1018+
bool CWalletDB::WriteHDChain(const CHDChain& chain)
1019+
{
1020+
nWalletDBUpdated++;
1021+
return Write(std::string("hdchain"), chain);
1022+
}

src/wallet/walletdb.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,35 @@ enum DBErrors
4040
DB_NEED_REWRITE
4141
};
4242

43+
/* simple hd chain data model */
44+
class CHDChain
45+
{
46+
public:
47+
uint32_t nExternalChainCounter;
48+
CKeyID masterKeyID; //!< master key hash160
49+
50+
static const int CURRENT_VERSION = 1;
51+
int nVersion;
52+
53+
CHDChain() { SetNull(); }
54+
ADD_SERIALIZE_METHODS;
55+
template <typename Stream, typename Operation>
56+
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
57+
{
58+
READWRITE(this->nVersion);
59+
nVersion = this->nVersion;
60+
READWRITE(nExternalChainCounter);
61+
READWRITE(masterKeyID);
62+
}
63+
64+
void SetNull()
65+
{
66+
nVersion = CHDChain::CURRENT_VERSION;
67+
nExternalChainCounter = 0;
68+
masterKeyID.SetNull();
69+
}
70+
};
71+
4372
class CKeyMetadata
4473
{
4574
public:
@@ -134,6 +163,9 @@ class CWalletDB : public CDB
134163
static bool Recover(CDBEnv& dbenv, const std::string& filename, bool fOnlyKeys);
135164
static bool Recover(CDBEnv& dbenv, const std::string& filename);
136165

166+
//! write the hdchain model (external chain child index counter)
167+
bool WriteHDChain(const CHDChain& chain);
168+
137169
private:
138170
CWalletDB(const CWalletDB&);
139171
void operator=(const CWalletDB&);

0 commit comments

Comments
 (0)