aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonas Schnelli <dev@jonasschnelli.ch>2016-01-02 12:34:08 +0100
committerJonas Schnelli <dev@jonasschnelli.ch>2016-05-31 14:47:00 +0200
commitf19025106de47a92396f9fb98e6d3bbc568c40b5 (patch)
tree54d64f8e6aad10ec6866e5f25cad88d65dd3b4ab
parent950be19727a581970591d8f8138dfe4725750382 (diff)
[Wallet] Add simplest BIP32/deterministic key generation implementation
-rw-r--r--src/wallet/wallet.cpp86
-rw-r--r--src/wallet/wallet.h12
-rw-r--r--src/wallet/walletdb.cpp17
-rw-r--r--src/wallet/walletdb.h32
4 files changed, 143 insertions, 4 deletions
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index da0d6f272b..fdb46472a8 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -91,7 +91,48 @@ CPubKey CWallet::GenerateNewKey()
bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets
CKey secret;
- secret.MakeNewKey(fCompressed);
+
+ // 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);
+ }
// Compressed public keys were introduced in version 0.6.0
if (fCompressed)
@@ -100,9 +141,7 @@ CPubKey CWallet::GenerateNewKey()
CPubKey pubkey = secret.GetPubKey();
assert(secret.VerifyPubKey(pubkey));
- // Create new metadata
- int64_t nCreationTime = GetTime();
- mapKeyMetadata[pubkey.GetID()] = CKeyMetadata(nCreationTime);
+ mapKeyMetadata[pubkey.GetID()] = metadata;
if (!nTimeFirstKey || nCreationTime < nTimeFirstKey)
nTimeFirstKey = nCreationTime;
@@ -1049,6 +1088,37 @@ CAmount CWallet::GetChange(const CTransaction& tx) const
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 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("-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("-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("-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));
@@ -3145,6 +3216,13 @@ bool CWallet::InitLoadWallet()
if (fFirstRun)
{
// 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;
if (walletInstance->GetKeyFromPool(newDefaultKey)) {
walletInstance->SetDefaultKey(newDefaultKey);
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index b2180a5a26..a819c03266 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -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 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;
class CBlockIndex;
@@ -574,6 +577,9 @@ private:
void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>);
+ /* the hd chain data model (external chain counters) */
+ CHDChain hdChain;
+
public:
/*
* Main wallet lock.
@@ -887,6 +893,12 @@ public:
static bool ParameterInteraction();
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. */
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index b5037c9a65..7bfd490950 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -599,6 +599,16 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
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 (...)
{
return false;
@@ -1003,3 +1013,10 @@ bool CWalletDB::EraseDestData(const std::string &address, const std::string &key
nWalletDBUpdated++;
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);
+}
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index 00c10ea70f..71b0ff26db 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -40,6 +40,35 @@ enum DBErrors
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
{
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);
+ //! write the hdchain model (external chain child index counter)
+ bool WriteHDChain(const CHDChain& chain);
+
private:
CWalletDB(const CWalletDB&);
void operator=(const CWalletDB&);