@@ -85,7 +85,7 @@ const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
8585 return &(it->second );
8686}
8787
88- CPubKey CWallet::GenerateNewKey ()
88+ CPubKey CWallet::GenerateNewKey (bool internal )
8989{
9090 AssertLockHeld (cs_wallet); // mapKeyMetadata
9191 bool fCompressed = CanSupportFeature (FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets
@@ -98,7 +98,7 @@ CPubKey CWallet::GenerateNewKey()
9898
9999 // use HD key derivation if HD was enabled during wallet creation
100100 if (IsHDEnabled ()) {
101- DeriveNewChildKey (metadata, secret);
101+ DeriveNewChildKey (metadata, secret, ( CanSupportFeature (FEATURE_HD_SPLIT) ? internal : false ) );
102102 } else {
103103 secret.MakeNewKey (fCompressed );
104104 }
@@ -118,13 +118,13 @@ CPubKey CWallet::GenerateNewKey()
118118 return pubkey;
119119}
120120
121- void CWallet::DeriveNewChildKey (CKeyMetadata& metadata, CKey& secret)
121+ void CWallet::DeriveNewChildKey (CKeyMetadata& metadata, CKey& secret, bool internal )
122122{
123123 // for now we use a fixed keypath scheme of m/0'/0'/k
124124 CKey key; // master key seed (256bit)
125125 CExtKey masterKey; // hd master key
126126 CExtKey accountKey; // key at m/0'
127- CExtKey externalChainChildKey; // key at m/0'/0'
127+ CExtKey chainChildKey; // key at m/0'/0' (external) or m/0'/1' (internal)
128128 CExtKey childKey; // key at m/0'/0'/<n>'
129129
130130 // try to get the master key
@@ -137,19 +137,22 @@ void CWallet::DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret)
137137 // use hardened derivation (child keys >= 0x80000000 are hardened after bip32)
138138 masterKey.Derive (accountKey, BIP32_HARDENED_KEY_LIMIT);
139139
140- // derive m/0'/0'
141- accountKey.Derive (externalChainChildKey , BIP32_HARDENED_KEY_LIMIT);
140+ // derive m/0'/0' (external chain) OR m/0'/1' (internal chain)
141+ accountKey.Derive (chainChildKey , BIP32_HARDENED_KEY_LIMIT+(internal ? 1 : 0 ) );
142142
143143 // derive child key at next index, skip keys already known to the wallet
144144 do {
145145 // always derive hardened keys
146146 // childIndex | BIP32_HARDENED_KEY_LIMIT = derive childIndex in hardened child-index-range
147147 // example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649
148- externalChainChildKey .Derive (childKey, hdChain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
149- metadata.hdKeypath = " m/0'/0'/" + std::to_string (hdChain.nExternalChainCounter ) + " '" ;
148+ chainChildKey .Derive (childKey, (internal ? hdChain.nInternalChainCounter : hdChain. nExternalChainCounter ) | BIP32_HARDENED_KEY_LIMIT);
149+ metadata.hdKeypath = " m/0'/" + std::string (internal ? " 1'/ " + std::to_string (hdChain. nInternalChainCounter ) : " 0'/" + std::to_string (hdChain.nExternalChainCounter ) ) + " '" ;
150150 metadata.hdMasterKeyID = hdChain.masterKeyID ;
151151 // increment childkey index
152- hdChain.nExternalChainCounter ++;
152+ if (internal)
153+ hdChain.nInternalChainCounter ++;
154+ else
155+ hdChain.nExternalChainCounter ++;
153156 } while (HaveKey (childKey.key .GetPubKey ().GetID ()));
154157 secret = childKey.key ;
155158
@@ -799,7 +802,7 @@ bool CWallet::GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bFo
799802
800803 // Generate a new key
801804 if (bForceNew) {
802- if (!GetKeyFromPool (account.vchPubKey ))
805+ if (!GetKeyFromPool (account.vchPubKey , false ))
803806 return false ;
804807
805808 SetAddressBook (account.vchPubKey .GetID (), strAccount, " receive" );
@@ -1304,8 +1307,8 @@ bool CWallet::SetHDMasterKey(const CPubKey& pubkey)
13041307{
13051308 LOCK (cs_wallet);
13061309
1307- // ensure this wallet.dat can only be opened by clients supporting HD
1308- SetMinVersion (FEATURE_HD );
1310+ // ensure this wallet.dat can only be opened by clients supporting HD with chain split
1311+ SetMinVersion (FEATURE_HD_SPLIT );
13091312
13101313 // store the keyid (hash160) together with
13111314 // the child index counter in the database
@@ -2445,7 +2448,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
24452448 // Reserve a new key pair from key pool
24462449 CPubKey vchPubKey;
24472450 bool ret;
2448- ret = reservekey.GetReservedKey (vchPubKey);
2451+ ret = reservekey.GetReservedKey (vchPubKey, true );
24492452 if (!ret)
24502453 {
24512454 strFailReason = _ (" Keypool ran out, please call keypoolrefill first" );
@@ -2899,18 +2902,31 @@ bool CWallet::NewKeyPool()
28992902 if (IsLocked ())
29002903 return false ;
29012904
2902- int64_t nKeys = std::max (GetArg (" -keypool" , DEFAULT_KEYPOOL_SIZE), (int64_t )0 );
2903- for (int i = 0 ; i < nKeys; i++)
2904- {
2905- int64_t nIndex = i+1 ;
2906- walletdb.WritePool (nIndex, CKeyPool (GenerateNewKey ()));
2907- setKeyPool.insert (nIndex);
2908- }
2909- LogPrintf (" CWallet::NewKeyPool wrote %d new keys\n " , nKeys);
2905+ TopUpKeyPool ();
2906+ LogPrintf (" CWallet::NewKeyPool rewrote keypool\n " );
29102907 }
29112908 return true ;
29122909}
29132910
2911+ size_t CWallet::KeypoolCountExternalKeys ()
2912+ {
2913+ AssertLockHeld (cs_wallet); // setKeyPool
2914+
2915+ CWalletDB walletdb (strWalletFile);
2916+
2917+ // count amount of external keys
2918+ size_t amountE = 0 ;
2919+ for (const int64_t & id : setKeyPool)
2920+ {
2921+ CKeyPool tmpKeypool;
2922+ if (!walletdb.ReadPool (id, tmpKeypool))
2923+ throw std::runtime_error (std::string (__func__) + " : read failed" );
2924+ amountE += !tmpKeypool.fInternal ;
2925+ }
2926+
2927+ return amountE;
2928+ }
2929+
29142930bool CWallet::TopUpKeyPool (unsigned int kpSize)
29152931{
29162932 {
@@ -2919,30 +2935,46 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
29192935 if (IsLocked ())
29202936 return false ;
29212937
2922- CWalletDB walletdb (strWalletFile);
2923-
29242938 // Top up key pool
29252939 unsigned int nTargetSize;
29262940 if (kpSize > 0 )
29272941 nTargetSize = kpSize;
29282942 else
29292943 nTargetSize = std::max (GetArg (" -keypool" , DEFAULT_KEYPOOL_SIZE), (int64_t ) 0 );
29302944
2931- while (setKeyPool.size () < (nTargetSize + 1 ))
2945+ // count amount of available keys (internal, external)
2946+ // make sure the keypool of external keys fits the user selected target (-keypool)
2947+ // generate +20% internal keys (minimum 2 keys)
2948+ int64_t amountExternal = KeypoolCountExternalKeys ();
2949+ int64_t amountInternal = setKeyPool.size () - amountExternal;
2950+ int64_t targetInternal = max ((int64_t )ceil (nTargetSize * 0.2 ), (int64_t ) 2 );
2951+ int64_t missingExternal = max ( (int64_t )(nTargetSize - amountExternal), (int64_t ) 0 );
2952+ int64_t missingInternal = max (targetInternal - amountInternal, (int64_t ) 0 );
2953+
2954+ if (!IsHDEnabled () || !CanSupportFeature (FEATURE_HD_SPLIT))
2955+ {
2956+ // don't create extra internal keys
2957+ missingInternal = 0 ;
2958+ }
2959+ bool internal = false ;
2960+ CWalletDB walletdb (strWalletFile);
2961+ for (int64_t i = missingInternal + missingExternal; i--;)
29322962 {
29332963 int64_t nEnd = 1 ;
2964+ if (i < missingInternal)
2965+ internal = true ;
29342966 if (!setKeyPool.empty ())
29352967 nEnd = *(--setKeyPool.end ()) + 1 ;
2936- if (!walletdb.WritePool (nEnd, CKeyPool (GenerateNewKey () )))
2968+ if (!walletdb.WritePool (nEnd, CKeyPool (GenerateNewKey (internal), internal )))
29372969 throw std::runtime_error (std::string (__func__) + " : writing generated key failed" );
29382970 setKeyPool.insert (nEnd);
2939- LogPrintf (" keypool added key %d, size=%u\n " , nEnd, setKeyPool.size ());
2971+ LogPrintf (" keypool added key %d, size=%u, internal=%d \n " , nEnd, setKeyPool.size (), internal );
29402972 }
29412973 }
29422974 return true ;
29432975}
29442976
2945- void CWallet::ReserveKeyFromKeyPool (int64_t & nIndex, CKeyPool& keypool)
2977+ void CWallet::ReserveKeyFromKeyPool (int64_t & nIndex, CKeyPool& keypool, bool internal )
29462978{
29472979 nIndex = -1 ;
29482980 keypool.vchPubKey = CPubKey ();
@@ -2958,14 +2990,24 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool)
29582990
29592991 CWalletDB walletdb (strWalletFile);
29602992
2961- nIndex = *(setKeyPool.begin ());
2962- setKeyPool.erase (setKeyPool.begin ());
2963- if (!walletdb.ReadPool (nIndex, keypool))
2964- throw std::runtime_error (std::string (__func__) + " : read failed" );
2965- if (!HaveKey (keypool.vchPubKey .GetID ()))
2966- throw std::runtime_error (std::string (__func__) + " : unknown key in key pool" );
2967- assert (keypool.vchPubKey .IsValid ());
2968- LogPrintf (" keypool reserve %d\n " , nIndex);
2993+ // try to find a key that matches the internal/external filter
2994+ for (const int64_t & id : setKeyPool)
2995+ {
2996+ CKeyPool tmpKeypool;
2997+ if (!walletdb.ReadPool (id, tmpKeypool))
2998+ throw std::runtime_error (std::string (__func__) + " : read failed" );
2999+ if (!HaveKey (tmpKeypool.vchPubKey .GetID ()))
3000+ throw std::runtime_error (std::string (__func__) + " : unknown key in key pool" );
3001+ if (!IsHDEnabled () || tmpKeypool.fInternal == internal)
3002+ {
3003+ nIndex = id;
3004+ keypool = tmpKeypool;
3005+ setKeyPool.erase (id);
3006+ assert (keypool.vchPubKey .IsValid ());
3007+ LogPrintf (" keypool reserve %d\n " , nIndex);
3008+ return ;
3009+ }
3010+ }
29693011 }
29703012}
29713013
@@ -2990,17 +3032,17 @@ void CWallet::ReturnKey(int64_t nIndex)
29903032 LogPrintf (" keypool return %d\n " , nIndex);
29913033}
29923034
2993- bool CWallet::GetKeyFromPool (CPubKey& result)
3035+ bool CWallet::GetKeyFromPool (CPubKey& result, bool internal )
29943036{
29953037 int64_t nIndex = 0 ;
29963038 CKeyPool keypool;
29973039 {
29983040 LOCK (cs_wallet);
2999- ReserveKeyFromKeyPool (nIndex, keypool);
3041+ ReserveKeyFromKeyPool (nIndex, keypool, internal );
30003042 if (nIndex == -1 )
30013043 {
30023044 if (IsLocked ()) return false ;
3003- result = GenerateNewKey ();
3045+ result = GenerateNewKey (internal );
30043046 return true ;
30053047 }
30063048 KeepKey (nIndex);
@@ -3205,12 +3247,12 @@ std::set<CTxDestination> CWallet::GetAccountAddresses(const std::string& strAcco
32053247 return result;
32063248}
32073249
3208- bool CReserveKey::GetReservedKey (CPubKey& pubkey)
3250+ bool CReserveKey::GetReservedKey (CPubKey& pubkey, bool internal )
32093251{
32103252 if (nIndex == -1 )
32113253 {
32123254 CKeyPool keypool;
3213- pwallet->ReserveKeyFromKeyPool (nIndex, keypool);
3255+ pwallet->ReserveKeyFromKeyPool (nIndex, keypool, internal );
32143256 if (nIndex != -1 )
32153257 vchPubKey = keypool.vchPubKey ;
32163258 else {
@@ -3629,7 +3671,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
36293671 throw std::runtime_error (std::string (__func__) + " : Storing master key failed" );
36303672 }
36313673 CPubKey newDefaultKey;
3632- if (walletInstance->GetKeyFromPool (newDefaultKey)) {
3674+ if (walletInstance->GetKeyFromPool (newDefaultKey, false )) {
36333675 walletInstance->SetDefaultKey (newDefaultKey);
36343676 if (!walletInstance->SetAddressBook (walletInstance->vchDefaultKey .GetID (), " " , " receive" )) {
36353677 InitError (_ (" Cannot write default address" ) += " \n " );
@@ -3890,10 +3932,11 @@ CKeyPool::CKeyPool()
38903932 nTime = GetTime ();
38913933}
38923934
3893- CKeyPool::CKeyPool (const CPubKey& vchPubKeyIn)
3935+ CKeyPool::CKeyPool (const CPubKey& vchPubKeyIn, bool internalIn )
38943936{
38953937 nTime = GetTime ();
38963938 vchPubKey = vchPubKeyIn;
3939+ fInternal = internalIn;
38973940}
38983941
38993942CWalletKey::CWalletKey (int64_t nExpires)
0 commit comments