@ -85,7 +85,7 @@ const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
return & ( it - > second ) ;
return & ( it - > second ) ;
}
}
CPubKey CWallet : : GenerateNewKey ( )
CPubKey CWallet : : GenerateNewKey ( bool internal )
{
{
AssertLockHeld ( cs_wallet ) ; // mapKeyMetadata
AssertLockHeld ( cs_wallet ) ; // mapKeyMetadata
bool fCompressed = CanSupportFeature ( FEATURE_COMPRPUBKEY ) ; // default to compressed public keys if we want 0.6.0 wallets
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
// use HD key derivation if HD was enabled during wallet creation
if ( IsHDEnabled ( ) ) {
if ( IsHDEnabled ( ) ) {
DeriveNewChildKey ( metadata , secret ) ;
DeriveNewChildKey ( metadata , secret , ( CanSupportFeature ( FEATURE_HD_SPLIT ) ? internal : false ) ) ;
} else {
} else {
secret . MakeNewKey ( fCompressed ) ;
secret . MakeNewKey ( fCompressed ) ;
}
}
@ -118,13 +118,13 @@ CPubKey CWallet::GenerateNewKey()
return pubkey ;
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
// for now we use a fixed keypath scheme of m/0'/0'/k
CKey key ; //master key seed (256bit)
CKey key ; //master key seed (256bit)
CExtKey masterKey ; //hd master key
CExtKey masterKey ; //hd master key
CExtKey accountKey ; //key at m/0'
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>'
CExtKey childKey ; //key at m/0'/0'/<n>'
// try to get the master key
// try to get the master key
@ -137,19 +137,22 @@ void CWallet::DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret)
// use hardened derivation (child keys >= 0x80000000 are hardened after bip32)
// use hardened derivation (child keys >= 0x80000000 are hardened after bip32)
masterKey . Derive ( accountKey , BIP32_HARDENED_KEY_LIMIT ) ;
masterKey . Derive ( accountKey , BIP32_HARDENED_KEY_LIMIT ) ;
// derive m/0'/0'
// derive m/0'/0' (external chain) OR m/0'/1' (internal chain)
accountKey . Derive ( externalC hainChildKey, BIP32_HARDENED_KEY_LIMIT ) ;
accountKey . Derive ( c hainChildKey, BIP32_HARDENED_KEY_LIMIT + ( internal ? 1 : 0 ) ) ;
// derive child key at next index, skip keys already known to the wallet
// derive child key at next index, skip keys already known to the wallet
do {
do {
// always derive hardened keys
// always derive hardened keys
// childIndex | BIP32_HARDENED_KEY_LIMIT = derive childIndex in hardened child-index-range
// childIndex | BIP32_HARDENED_KEY_LIMIT = derive childIndex in hardened child-index-range
// example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649
// example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649
externalC hainChildKey. Derive ( childKey , hdChain . nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT ) ;
c hainChildKey. Derive ( childKey , ( internal ? hdChain . nInternalChainCounter : hdChain . nExternalChainCounter ) | BIP32_HARDENED_KEY_LIMIT ) ;
metadata . hdKeypath = " m/0'/0'/ " + std : : to_string ( hdChain . nExternalChainCounter ) + " ' " ;
metadata . hdKeypath = " m/0'/ " + std : : string ( internal ? " 1'/ " + std : : to_string ( hdChain . nInternalChainCounter ) : " 0'/ " + std : : to_string ( hdChain . nExternalChainCounter ) ) + " ' " ;
metadata . hdMasterKeyID = hdChain . masterKeyID ;
metadata . hdMasterKeyID = hdChain . masterKeyID ;
// increment childkey index
// increment childkey index
hdChain . nExternalChainCounter + + ;
if ( internal )
hdChain . nInternalChainCounter + + ;
else
hdChain . nExternalChainCounter + + ;
} while ( HaveKey ( childKey . key . GetPubKey ( ) . GetID ( ) ) ) ;
} while ( HaveKey ( childKey . key . GetPubKey ( ) . GetID ( ) ) ) ;
secret = childKey . key ;
secret = childKey . key ;
@ -799,7 +802,7 @@ bool CWallet::GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bFo
// Generate a new key
// Generate a new key
if ( bForceNew ) {
if ( bForceNew ) {
if ( ! GetKeyFromPool ( account . vchPubKey ) )
if ( ! GetKeyFromPool ( account . vchPubKey , false ) )
return false ;
return false ;
SetAddressBook ( account . vchPubKey . GetID ( ) , strAccount , " receive " ) ;
SetAddressBook ( account . vchPubKey . GetID ( ) , strAccount , " receive " ) ;
@ -1304,8 +1307,8 @@ bool CWallet::SetHDMasterKey(const CPubKey& pubkey)
{
{
LOCK ( cs_wallet ) ;
LOCK ( cs_wallet ) ;
// ensure this wallet.dat can only be opened by clients supporting HD
// ensure this wallet.dat can only be opened by clients supporting HD with chain split
SetMinVersion ( FEATURE_HD ) ;
SetMinVersion ( FEATURE_HD_SPLIT ) ;
// store the keyid (hash160) together with
// store the keyid (hash160) together with
// the child index counter in the database
// the child index counter in the database
@ -2445,7 +2448,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
// Reserve a new key pair from key pool
// Reserve a new key pair from key pool
CPubKey vchPubKey ;
CPubKey vchPubKey ;
bool ret ;
bool ret ;
ret = reservekey . GetReservedKey ( vchPubKey ) ;
ret = reservekey . GetReservedKey ( vchPubKey , true ) ;
if ( ! ret )
if ( ! ret )
{
{
strFailReason = _ ( " Keypool ran out, please call keypoolrefill first " ) ;
strFailReason = _ ( " Keypool ran out, please call keypoolrefill first " ) ;
@ -2899,18 +2902,31 @@ bool CWallet::NewKeyPool()
if ( IsLocked ( ) )
if ( IsLocked ( ) )
return false ;
return false ;
int64_t nKeys = std : : max ( GetArg ( " -keypool " , DEFAULT_KEYPOOL_SIZE ) , ( int64_t ) 0 ) ;
TopUpKeyPool ( ) ;
for ( int i = 0 ; i < nKeys ; i + + )
LogPrintf ( " CWallet::NewKeyPool rewrote keypool \n " ) ;
{
int64_t nIndex = i + 1 ;
walletdb . WritePool ( nIndex , CKeyPool ( GenerateNewKey ( ) ) ) ;
setKeyPool . insert ( nIndex ) ;
}
LogPrintf ( " CWallet::NewKeyPool wrote %d new keys \n " , nKeys ) ;
}
}
return true ;
return true ;
}
}
size_t CWallet : : KeypoolCountExternalKeys ( )
{
AssertLockHeld ( cs_wallet ) ; // setKeyPool
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 )
bool CWallet : : TopUpKeyPool ( unsigned int kpSize )
{
{
{
{
@ -2919,8 +2935,6 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
if ( IsLocked ( ) )
if ( IsLocked ( ) )
return false ;
return false ;
CWalletDB walletdb ( strWalletFile ) ;
// Top up key pool
// Top up key pool
unsigned int nTargetSize ;
unsigned int nTargetSize ;
if ( kpSize > 0 )
if ( kpSize > 0 )
@ -2928,21 +2942,39 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
else
else
nTargetSize = std : : max ( GetArg ( " -keypool " , DEFAULT_KEYPOOL_SIZE ) , ( int64_t ) 0 ) ;
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 keys fits the user selected target (-keypool)
// generate +20% internal keys (minimum 2 keys)
int64_t amountExternal = KeypoolCountExternalKeys ( ) ;
int64_t amountInternal = setKeyPool . size ( ) - amountExternal ;
int64_t targetInternal = max ( ( int64_t ) ceil ( nTargetSize * 0.2 ) , ( int64_t ) 2 ) ;
int64_t missingExternal = max ( ( int64_t ) ( nTargetSize - amountExternal ) , ( int64_t ) 0 ) ;
int64_t missingInternal = max ( targetInternal - 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 ;
int64_t nEnd = 1 ;
if ( i < missingInternal )
internal = true ;
if ( ! setKeyPool . empty ( ) )
if ( ! setKeyPool . empty ( ) )
nEnd = * ( - - setKeyPool . end ( ) ) + 1 ;
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 " ) ;
throw std : : runtime_error ( std : : string ( __func__ ) + " : writing generated key failed " ) ;
setKeyPool . insert ( nEnd ) ;
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 ;
return true ;
}
}
void CWallet : : ReserveKeyFromKeyPool ( int64_t & nIndex , CKeyPool & keypool )
void CWallet : : ReserveKeyFromKeyPool ( int64_t & nIndex , CKeyPool & keypool , bool internal )
{
{
nIndex = - 1 ;
nIndex = - 1 ;
keypool . vchPubKey = CPubKey ( ) ;
keypool . vchPubKey = CPubKey ( ) ;
@ -2958,14 +2990,24 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool)
CWalletDB walletdb ( strWalletFile ) ;
CWalletDB walletdb ( strWalletFile ) ;
nIndex = * ( setKeyPool . begin ( ) ) ;
// try to find a key that matches the internal/external filter
setKeyPool . erase ( setKeyPool . begin ( ) ) ;
for ( const int64_t & id : setKeyPool )
if ( ! walletdb . ReadPool ( nIndex , keypool ) )
{
throw std : : runtime_error ( std : : string ( __func__ ) + " : read failed " ) ;
CKeyPool tmpKeypool ;
if ( ! HaveKey ( keypool . vchPubKey . GetID ( ) ) )
if ( ! walletdb . ReadPool ( id , tmpKeypool ) )
throw std : : runtime_error ( std : : string ( __func__ ) + " : unknown key in key pool " ) ;
throw std : : runtime_error ( std : : string ( __func__ ) + " : read failed " ) ;
assert ( keypool . vchPubKey . IsValid ( ) ) ;
if ( ! HaveKey ( tmpKeypool . vchPubKey . GetID ( ) ) )
LogPrintf ( " keypool reserve %d \n " , nIndex ) ;
throw std : : runtime_error ( std : : string ( __func__ ) + " : unknown key in key pool " ) ;
if ( ! IsHDEnabled ( ) | | tmpKeypool . fInternal = = internal )
{
nIndex = id ;
keypool = tmpKeypool ;
setKeyPool . erase ( id ) ;
assert ( keypool . vchPubKey . IsValid ( ) ) ;
LogPrintf ( " keypool reserve %d \n " , nIndex ) ;
return ;
}
}
}
}
}
}
@ -2990,17 +3032,17 @@ void CWallet::ReturnKey(int64_t nIndex)
LogPrintf ( " keypool return %d \n " , nIndex ) ;
LogPrintf ( " keypool return %d \n " , nIndex ) ;
}
}
bool CWallet : : GetKeyFromPool ( CPubKey & result )
bool CWallet : : GetKeyFromPool ( CPubKey & result , bool internal )
{
{
int64_t nIndex = 0 ;
int64_t nIndex = 0 ;
CKeyPool keypool ;
CKeyPool keypool ;
{
{
LOCK ( cs_wallet ) ;
LOCK ( cs_wallet ) ;
ReserveKeyFromKeyPool ( nIndex , keypool ) ;
ReserveKeyFromKeyPool ( nIndex , keypool , internal ) ;
if ( nIndex = = - 1 )
if ( nIndex = = - 1 )
{
{
if ( IsLocked ( ) ) return false ;
if ( IsLocked ( ) ) return false ;
result = GenerateNewKey ( ) ;
result = GenerateNewKey ( internal ) ;
return true ;
return true ;
}
}
KeepKey ( nIndex ) ;
KeepKey ( nIndex ) ;
@ -3205,12 +3247,12 @@ std::set<CTxDestination> CWallet::GetAccountAddresses(const std::string& strAcco
return result ;
return result ;
}
}
bool CReserveKey : : GetReservedKey ( CPubKey & pubkey )
bool CReserveKey : : GetReservedKey ( CPubKey & pubkey , bool internal )
{
{
if ( nIndex = = - 1 )
if ( nIndex = = - 1 )
{
{
CKeyPool keypool ;
CKeyPool keypool ;
pwallet - > ReserveKeyFromKeyPool ( nIndex , keypool ) ;
pwallet - > ReserveKeyFromKeyPool ( nIndex , keypool , internal ) ;
if ( nIndex ! = - 1 )
if ( nIndex ! = - 1 )
vchPubKey = keypool . vchPubKey ;
vchPubKey = keypool . vchPubKey ;
else {
else {
@ -3629,7 +3671,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
throw std : : runtime_error ( std : : string ( __func__ ) + " : Storing master key failed " ) ;
throw std : : runtime_error ( std : : string ( __func__ ) + " : Storing master key failed " ) ;
}
}
CPubKey newDefaultKey ;
CPubKey newDefaultKey ;
if ( walletInstance - > GetKeyFromPool ( newDefaultKey ) ) {
if ( walletInstance - > GetKeyFromPool ( newDefaultKey , false ) ) {
walletInstance - > SetDefaultKey ( newDefaultKey ) ;
walletInstance - > SetDefaultKey ( newDefaultKey ) ;
if ( ! walletInstance - > SetAddressBook ( walletInstance - > vchDefaultKey . GetID ( ) , " " , " receive " ) ) {
if ( ! walletInstance - > SetAddressBook ( walletInstance - > vchDefaultKey . GetID ( ) , " " , " receive " ) ) {
InitError ( _ ( " Cannot write default address " ) + = " \n " ) ;
InitError ( _ ( " Cannot write default address " ) + = " \n " ) ;
@ -3890,10 +3932,11 @@ CKeyPool::CKeyPool()
nTime = GetTime ( ) ;
nTime = GetTime ( ) ;
}
}
CKeyPool : : CKeyPool ( const CPubKey & vchPubKeyIn )
CKeyPool : : CKeyPool ( const CPubKey & vchPubKeyIn , bool internalIn )
{
{
nTime = GetTime ( ) ;
nTime = GetTime ( ) ;
vchPubKey = vchPubKeyIn ;
vchPubKey = vchPubKeyIn ;
fInternal = internalIn ;
}
}
CWalletKey : : CWalletKey ( int64_t nExpires )
CWalletKey : : CWalletKey ( int64_t nExpires )