aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/wallet.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet/wallet.cpp')
-rw-r--r--src/wallet/wallet.cpp198
1 files changed, 140 insertions, 58 deletions
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 445e40b043..68d4bc35ee 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -85,7 +85,7 @@ const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
return &(it->second);
}
-CPubKey CWallet::GenerateNewKey()
+CPubKey CWallet::GenerateNewKey(bool internal)
{
AssertLockHeld(cs_wallet); // mapKeyMetadata
bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets
@@ -98,7 +98,7 @@ CPubKey CWallet::GenerateNewKey()
// use HD key derivation if HD was enabled during wallet creation
if (IsHDEnabled()) {
- DeriveNewChildKey(metadata, secret);
+ DeriveNewChildKey(metadata, secret, (CanSupportFeature(FEATURE_HD_SPLIT) ? internal : false));
} else {
secret.MakeNewKey(fCompressed);
}
@@ -118,13 +118,13 @@ CPubKey CWallet::GenerateNewKey()
return pubkey;
}
-void CWallet::DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret)
+void CWallet::DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret, bool internal)
{
// 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 chainChildKey; //key at m/0'/0' (external) or m/0'/1' (internal)
CExtKey childKey; //key at m/0'/0'/<n>'
// try to get the master key
@@ -137,22 +137,28 @@ void CWallet::DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret)
// 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 m/0'/0' (external chain) OR m/0'/1' (internal chain)
+ assert(internal ? CanSupportFeature(FEATURE_HD_SPLIT) : true);
+ accountKey.Derive(chainChildKey, BIP32_HARDENED_KEY_LIMIT+(internal ? 1 : 0));
// 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);
- metadata.hdKeypath = "m/0'/0'/" + std::to_string(hdChain.nExternalChainCounter) + "'";
- metadata.hdMasterKeyID = hdChain.masterKeyID;
- // increment childkey index
- hdChain.nExternalChainCounter++;
+ if (internal) {
+ chainChildKey.Derive(childKey, hdChain.nInternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
+ metadata.hdKeypath = "m/0'/1'/" + std::to_string(hdChain.nInternalChainCounter) + "'";
+ hdChain.nInternalChainCounter++;
+ }
+ else {
+ chainChildKey.Derive(childKey, hdChain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
+ metadata.hdKeypath = "m/0'/0'/" + std::to_string(hdChain.nExternalChainCounter) + "'";
+ hdChain.nExternalChainCounter++;
+ }
} while (HaveKey(childKey.key.GetPubKey().GetID()));
secret = childKey.key;
-
+ metadata.hdMasterKeyID = hdChain.masterKeyID;
// update the chain model in the database
if (!CWalletDB(strWalletFile).WriteHDChain(hdChain))
throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed");
@@ -633,7 +639,9 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
if (IsHDEnabled()) {
CKey key;
CPubKey masterPubKey = GenerateNewHDMasterKey();
- if (!SetHDMasterKey(masterPubKey))
+ // preserve the old chains version to not break backward compatibility
+ CHDChain oldChain = GetHDChain();
+ if (!SetHDMasterKey(masterPubKey, &oldChain))
return false;
}
@@ -799,7 +807,7 @@ bool CWallet::GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bFo
// Generate a new key
if (bForceNew) {
- if (!GetKeyFromPool(account.vchPubKey))
+ if (!GetKeyFromPool(account.vchPubKey, false))
return false;
SetAddressBook(account.vchPubKey.GetID(), strAccount, "receive");
@@ -1300,17 +1308,17 @@ CPubKey CWallet::GenerateNewHDMasterKey()
return pubkey;
}
-bool CWallet::SetHDMasterKey(const CPubKey& pubkey)
+bool CWallet::SetHDMasterKey(const CPubKey& pubkey, CHDChain *possibleOldChain)
{
LOCK(cs_wallet);
-
- // ensure this wallet.dat can only be opened by clients supporting HD
- SetMinVersion(FEATURE_HD);
-
// store the keyid (hash160) together with
// the child index counter in the database
// as a hdchain object
CHDChain newHdChain;
+ if (possibleOldChain) {
+ // preserve the old chains version
+ newHdChain.nVersion = possibleOldChain->nVersion;
+ }
newHdChain.masterKeyID = pubkey.GetID();
SetHDChain(newHdChain, false);
@@ -2023,7 +2031,7 @@ void CWallet::AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe, const
}
}
-static void ApproximateBestSubset(std::vector<std::pair<CAmount, std::pair<const CWalletTx*,unsigned int> > >vValue, const CAmount& nTotalLower, const CAmount& nTargetValue,
+static void ApproximateBestSubset(const std::vector<std::pair<CAmount, std::pair<const CWalletTx*,unsigned int> > >& vValue, const CAmount& nTotalLower, const CAmount& nTargetValue,
std::vector<char>& vfBest, CAmount& nBest, int iterations = 1000)
{
std::vector<char> vfIncluded;
@@ -2164,11 +2172,15 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMin
nValueRet += vValue[i].first;
}
- LogPrint("selectcoins", "SelectCoins() best subset: ");
- for (unsigned int i = 0; i < vValue.size(); i++)
- if (vfBest[i])
- LogPrint("selectcoins", "%s ", FormatMoney(vValue[i].first));
- LogPrint("selectcoins", "total %s\n", FormatMoney(nBest));
+ if (LogAcceptCategory(BCLog::SELECTCOINS)) {
+ LogPrint(BCLog::SELECTCOINS, "SelectCoins() best subset: ");
+ for (unsigned int i = 0; i < vValue.size(); i++) {
+ if (vfBest[i]) {
+ LogPrint(BCLog::SELECTCOINS, "%s ", FormatMoney(vValue[i].first));
+ }
+ }
+ LogPrint(BCLog::SELECTCOINS, "total %s\n", FormatMoney(nBest));
+ }
}
return true;
@@ -2445,7 +2457,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
// Reserve a new key pair from key pool
CPubKey vchPubKey;
bool ret;
- ret = reservekey.GetReservedKey(vchPubKey);
+ ret = reservekey.GetReservedKey(vchPubKey, true);
if (!ret)
{
strFailReason = _("Keypool ran out, please call keypoolrefill first");
@@ -2885,7 +2897,7 @@ bool CWallet::SetDefaultKey(const CPubKey &vchPubKey)
/**
* Mark old keypool keys as used,
- * and generate all new keys
+ * and generate all new keys
*/
bool CWallet::NewKeyPool()
{
@@ -2896,21 +2908,37 @@ bool CWallet::NewKeyPool()
walletdb.ErasePool(nIndex);
setKeyPool.clear();
- if (IsLocked())
+ if (!TopUpKeyPool()) {
return false;
-
- int64_t nKeys = std::max(GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t)0);
- for (int i = 0; i < nKeys; i++)
- {
- int64_t nIndex = i+1;
- walletdb.WritePool(nIndex, CKeyPool(GenerateNewKey()));
- setKeyPool.insert(nIndex);
}
- LogPrintf("CWallet::NewKeyPool wrote %d new keys\n", nKeys);
+ LogPrintf("CWallet::NewKeyPool rewrote keypool\n");
}
return true;
}
+size_t CWallet::KeypoolCountExternalKeys()
+{
+ AssertLockHeld(cs_wallet); // setKeyPool
+
+ // immediately return setKeyPool's size if HD or HD_SPLIT is disabled or not supported
+ if (!IsHDEnabled() || !CanSupportFeature(FEATURE_HD_SPLIT))
+ return setKeyPool.size();
+
+ CWalletDB walletdb(strWalletFile);
+
+ // count amount of external keys
+ size_t amountE = 0;
+ for(const int64_t& id : setKeyPool)
+ {
+ CKeyPool tmpKeypool;
+ if (!walletdb.ReadPool(id, tmpKeypool))
+ throw std::runtime_error(std::string(__func__) + ": read failed");
+ amountE += !tmpKeypool.fInternal;
+ }
+
+ return amountE;
+}
+
bool CWallet::TopUpKeyPool(unsigned int kpSize)
{
{
@@ -2919,8 +2947,6 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
if (IsLocked())
return false;
- CWalletDB walletdb(strWalletFile);
-
// Top up key pool
unsigned int nTargetSize;
if (kpSize > 0)
@@ -2928,21 +2954,37 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
else
nTargetSize = std::max(GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 0);
- while (setKeyPool.size() < (nTargetSize + 1))
+ // count amount of available keys (internal, external)
+ // make sure the keypool of external and internal keys fits the user selected target (-keypool)
+ int64_t amountExternal = KeypoolCountExternalKeys();
+ int64_t amountInternal = setKeyPool.size() - amountExternal;
+ int64_t missingExternal = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - amountExternal, (int64_t) 0);
+ int64_t missingInternal = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - amountInternal, (int64_t) 0);
+
+ if (!IsHDEnabled() || !CanSupportFeature(FEATURE_HD_SPLIT))
+ {
+ // don't create extra internal keys
+ missingInternal = 0;
+ }
+ bool internal = false;
+ CWalletDB walletdb(strWalletFile);
+ for (int64_t i = missingInternal + missingExternal; i--;)
{
int64_t nEnd = 1;
+ if (i < missingInternal)
+ internal = true;
if (!setKeyPool.empty())
nEnd = *(--setKeyPool.end()) + 1;
- if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey())))
+ if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey(internal), internal)))
throw std::runtime_error(std::string(__func__) + ": writing generated key failed");
setKeyPool.insert(nEnd);
- LogPrintf("keypool added key %d, size=%u\n", nEnd, setKeyPool.size());
+ LogPrintf("keypool added key %d, size=%u, internal=%d\n", nEnd, setKeyPool.size(), internal);
}
}
return true;
}
-void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool)
+void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool internal)
{
nIndex = -1;
keypool.vchPubKey = CPubKey();
@@ -2958,14 +3000,24 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool)
CWalletDB walletdb(strWalletFile);
- nIndex = *(setKeyPool.begin());
- setKeyPool.erase(setKeyPool.begin());
- if (!walletdb.ReadPool(nIndex, keypool))
- throw std::runtime_error(std::string(__func__) + ": read failed");
- if (!HaveKey(keypool.vchPubKey.GetID()))
- throw std::runtime_error(std::string(__func__) + ": unknown key in key pool");
- assert(keypool.vchPubKey.IsValid());
- LogPrintf("keypool reserve %d\n", nIndex);
+ // try to find a key that matches the internal/external filter
+ for(const int64_t& id : setKeyPool)
+ {
+ CKeyPool tmpKeypool;
+ if (!walletdb.ReadPool(id, tmpKeypool))
+ throw std::runtime_error(std::string(__func__) + ": read failed");
+ if (!HaveKey(tmpKeypool.vchPubKey.GetID()))
+ throw std::runtime_error(std::string(__func__) + ": unknown key in key pool");
+ if (!IsHDEnabled() || !CanSupportFeature(FEATURE_HD_SPLIT) || tmpKeypool.fInternal == internal)
+ {
+ nIndex = id;
+ keypool = tmpKeypool;
+ setKeyPool.erase(id);
+ assert(keypool.vchPubKey.IsValid());
+ LogPrintf("keypool reserve %d\n", nIndex);
+ return;
+ }
+ }
}
}
@@ -2990,17 +3042,17 @@ void CWallet::ReturnKey(int64_t nIndex)
LogPrintf("keypool return %d\n", nIndex);
}
-bool CWallet::GetKeyFromPool(CPubKey& result)
+bool CWallet::GetKeyFromPool(CPubKey& result, bool internal)
{
int64_t nIndex = 0;
CKeyPool keypool;
{
LOCK(cs_wallet);
- ReserveKeyFromKeyPool(nIndex, keypool);
+ ReserveKeyFromKeyPool(nIndex, keypool, internal);
if (nIndex == -1)
{
if (IsLocked()) return false;
- result = GenerateNewKey();
+ result = GenerateNewKey(internal);
return true;
}
KeepKey(nIndex);
@@ -3017,9 +3069,33 @@ int64_t CWallet::GetOldestKeyPoolTime()
if (setKeyPool.empty())
return GetTime();
- // load oldest key from keypool, get time and return
CKeyPool keypool;
CWalletDB walletdb(strWalletFile);
+
+ if (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT))
+ {
+ // if HD & HD Chain Split is enabled, response max(oldest-internal-key, oldest-external-key)
+ int64_t now = GetTime();
+ int64_t oldest_external = now, oldest_internal = now;
+
+ for(const int64_t& id : setKeyPool)
+ {
+ if (!walletdb.ReadPool(id, keypool)) {
+ throw std::runtime_error(std::string(__func__) + ": read failed");
+ }
+ if (keypool.fInternal && keypool.nTime < oldest_internal) {
+ oldest_internal = keypool.nTime;
+ }
+ else if (!keypool.fInternal && keypool.nTime < oldest_external) {
+ oldest_external = keypool.nTime;
+ }
+ if (oldest_internal != now && oldest_external != now) {
+ break;
+ }
+ }
+ return std::max(oldest_internal, oldest_external);
+ }
+ // load oldest key from keypool, get time and return
int64_t nIndex = *(setKeyPool.begin());
if (!walletdb.ReadPool(nIndex, keypool))
throw std::runtime_error(std::string(__func__) + ": read oldest key in keypool failed");
@@ -3205,12 +3281,12 @@ std::set<CTxDestination> CWallet::GetAccountAddresses(const std::string& strAcco
return result;
}
-bool CReserveKey::GetReservedKey(CPubKey& pubkey)
+bool CReserveKey::GetReservedKey(CPubKey& pubkey, bool internal)
{
if (nIndex == -1)
{
CKeyPool keypool;
- pwallet->ReserveKeyFromKeyPool(nIndex, keypool);
+ pwallet->ReserveKeyFromKeyPool(nIndex, keypool, internal);
if (nIndex != -1)
vchPubKey = keypool.vchPubKey;
else {
@@ -3623,13 +3699,17 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
{
// Create new keyUser and set as default key
if (GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET) && !walletInstance->IsHDEnabled()) {
+
+ // ensure this wallet.dat can only be opened by clients supporting HD with chain split
+ walletInstance->SetMinVersion(FEATURE_HD_SPLIT);
+
// generate a new master key
CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey();
if (!walletInstance->SetHDMasterKey(masterPubKey))
throw std::runtime_error(std::string(__func__) + ": Storing master key failed");
}
CPubKey newDefaultKey;
- if (walletInstance->GetKeyFromPool(newDefaultKey)) {
+ if (walletInstance->GetKeyFromPool(newDefaultKey, false)) {
walletInstance->SetDefaultKey(newDefaultKey);
if (!walletInstance->SetAddressBook(walletInstance->vchDefaultKey.GetID(), "", "receive")) {
InitError(_("Cannot write default address") += "\n");
@@ -3888,12 +3968,14 @@ bool CWallet::BackupWallet(const std::string& strDest)
CKeyPool::CKeyPool()
{
nTime = GetTime();
+ fInternal = false;
}
-CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn)
+CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn)
{
nTime = GetTime();
vchPubKey = vchPubKeyIn;
+ fInternal = internalIn;
}
CWalletKey::CWalletKey(int64_t nExpires)