diff options
Diffstat (limited to 'src/wallet/wallet.cpp')
-rw-r--r-- | src/wallet/wallet.cpp | 97 |
1 files changed, 93 insertions, 4 deletions
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 9faf21591f..723b2eceff 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -42,6 +42,7 @@ bool bSpendZeroConfChange = DEFAULT_SPEND_ZEROCONF_CHANGE; bool fSendFreeTransactions = DEFAULT_SEND_FREE_TRANSACTIONS; const char * DEFAULT_WALLET_DAT = "wallet.dat"; +const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000; /** * Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) @@ -91,7 +92,51 @@ 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, BIP32_HARDENED_KEY_LIMIT); + + // derive m/0'/0' + accountKey.Derive(externalChainChildKey, BIP32_HARDENED_KEY_LIMIT); + + // derive child key at next index, skip keys already known to the wallet + do + { + // always derive hardened keys + // childIndex | BIP32_HARDENED_KEY_LIMIT = derive childIndex in hardened child-index-range + // example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649 + externalChainChildKey.Derive(childKey, hdChain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT); + // 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 +145,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; @@ -1121,6 +1164,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; @@ -3135,6 +3209,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)); @@ -3222,6 +3297,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); @@ -3231,6 +3313,13 @@ bool CWallet::InitLoadWallet() walletInstance->SetBestChain(chainActive.GetLocator()); } + else if (mapArgs.count("-usehd")) { + bool useHD = GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET); + if (!walletInstance->hdChain.masterKeyID.IsNull() && !useHD) + return InitError(strprintf(_("Error loading %s: You can't disable HD on a already existing HD wallet"), walletFile)); + if (walletInstance->hdChain.masterKeyID.IsNull() && useHD) + return InitError(strprintf(_("Error loading %s: You can't enable HD on a already existing non-HD wallet"), walletFile)); + } LogPrintf(" wallet %15dms\n", GetTimeMillis() - nStart); |