2019-10-07 15:11:34 -03:00
// Copyright (c) 2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
# include <key_io.h>
# include <outputtype.h>
# include <script/descriptor.h>
# include <util/bip32.h>
# include <util/strencodings.h>
# include <util/translation.h>
# include <wallet/scriptpubkeyman.h>
# include <wallet/wallet.h>
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : GetNewDestination ( const OutputType type , CTxDestination & dest , std : : string & error )
2019-10-07 15:11:34 -03:00
{
error . clear ( ) ;
2019-11-05 12:43:36 -03:00
TopUp ( ) ;
2019-10-07 15:11:34 -03:00
// Generate a new key that is added to wallet
CPubKey new_key ;
2019-11-26 13:52:51 -03:00
if ( ! GetKeyFromPool ( new_key , type ) ) {
2019-10-07 15:11:34 -03:00
error = " Error: Keypool ran out, please call keypoolrefill first " ;
return false ;
}
LearnRelatedScripts ( new_key , type ) ;
dest = GetDestinationForKey ( new_key , type ) ;
return true ;
}
typedef std : : vector < unsigned char > valtype ;
namespace {
/**
* This is an enum that tracks the execution context of a script , similar to
* SigVersion in script / interpreter . It is separate however because we want to
* distinguish between top - level scriptPubKey execution and P2SH redeemScript
* execution ( a distinction that has no impact on consensus rules ) .
*/
enum class IsMineSigVersion
{
TOP = 0 , //!< scriptPubKey execution
P2SH = 1 , //!< P2SH redeemScript
WITNESS_V0 = 2 , //!< P2WSH witness script execution
} ;
/**
* This is an internal representation of isminetype + invalidity .
* Its order is significant , as we return the max of all explored
* possibilities .
*/
enum class IsMineResult
{
NO = 0 , //!< Not ours
WATCH_ONLY = 1 , //!< Included in watch-only balance
SPENDABLE = 2 , //!< Included in all balances
INVALID = 3 , //!< Not spendable by anyone (uncompressed pubkey in segwit, P2SH inside P2SH or witness, witness inside witness)
} ;
bool PermitsUncompressed ( IsMineSigVersion sigversion )
{
return sigversion = = IsMineSigVersion : : TOP | | sigversion = = IsMineSigVersion : : P2SH ;
}
2019-10-07 15:11:34 -03:00
bool HaveKeys ( const std : : vector < valtype > & pubkeys , const LegacyScriptPubKeyMan & keystore )
2019-10-07 15:11:34 -03:00
{
for ( const valtype & pubkey : pubkeys ) {
CKeyID keyID = CPubKey ( pubkey ) . GetID ( ) ;
if ( ! keystore . HaveKey ( keyID ) ) return false ;
}
return true ;
}
2019-10-07 15:11:34 -03:00
IsMineResult IsMineInner ( const LegacyScriptPubKeyMan & keystore , const CScript & scriptPubKey , IsMineSigVersion sigversion )
2019-10-07 15:11:34 -03:00
{
IsMineResult ret = IsMineResult : : NO ;
std : : vector < valtype > vSolutions ;
txnouttype whichType = Solver ( scriptPubKey , vSolutions ) ;
CKeyID keyID ;
switch ( whichType )
{
case TX_NONSTANDARD :
case TX_NULL_DATA :
case TX_WITNESS_UNKNOWN :
break ;
case TX_PUBKEY :
keyID = CPubKey ( vSolutions [ 0 ] ) . GetID ( ) ;
if ( ! PermitsUncompressed ( sigversion ) & & vSolutions [ 0 ] . size ( ) ! = 33 ) {
return IsMineResult : : INVALID ;
}
if ( keystore . HaveKey ( keyID ) ) {
ret = std : : max ( ret , IsMineResult : : SPENDABLE ) ;
}
break ;
case TX_WITNESS_V0_KEYHASH :
{
if ( sigversion = = IsMineSigVersion : : WITNESS_V0 ) {
// P2WPKH inside P2WSH is invalid.
return IsMineResult : : INVALID ;
}
if ( sigversion = = IsMineSigVersion : : TOP & & ! keystore . HaveCScript ( CScriptID ( CScript ( ) < < OP_0 < < vSolutions [ 0 ] ) ) ) {
// We do not support bare witness outputs unless the P2SH version of it would be
// acceptable as well. This protects against matching before segwit activates.
// This also applies to the P2WSH case.
break ;
}
ret = std : : max ( ret , IsMineInner ( keystore , GetScriptForDestination ( PKHash ( uint160 ( vSolutions [ 0 ] ) ) ) , IsMineSigVersion : : WITNESS_V0 ) ) ;
break ;
}
case TX_PUBKEYHASH :
keyID = CKeyID ( uint160 ( vSolutions [ 0 ] ) ) ;
if ( ! PermitsUncompressed ( sigversion ) ) {
CPubKey pubkey ;
if ( keystore . GetPubKey ( keyID , pubkey ) & & ! pubkey . IsCompressed ( ) ) {
return IsMineResult : : INVALID ;
}
}
if ( keystore . HaveKey ( keyID ) ) {
ret = std : : max ( ret , IsMineResult : : SPENDABLE ) ;
}
break ;
case TX_SCRIPTHASH :
{
if ( sigversion ! = IsMineSigVersion : : TOP ) {
// P2SH inside P2WSH or P2SH is invalid.
return IsMineResult : : INVALID ;
}
CScriptID scriptID = CScriptID ( uint160 ( vSolutions [ 0 ] ) ) ;
CScript subscript ;
if ( keystore . GetCScript ( scriptID , subscript ) ) {
ret = std : : max ( ret , IsMineInner ( keystore , subscript , IsMineSigVersion : : P2SH ) ) ;
}
break ;
}
case TX_WITNESS_V0_SCRIPTHASH :
{
if ( sigversion = = IsMineSigVersion : : WITNESS_V0 ) {
// P2WSH inside P2WSH is invalid.
return IsMineResult : : INVALID ;
}
if ( sigversion = = IsMineSigVersion : : TOP & & ! keystore . HaveCScript ( CScriptID ( CScript ( ) < < OP_0 < < vSolutions [ 0 ] ) ) ) {
break ;
}
uint160 hash ;
CRIPEMD160 ( ) . Write ( & vSolutions [ 0 ] [ 0 ] , vSolutions [ 0 ] . size ( ) ) . Finalize ( hash . begin ( ) ) ;
CScriptID scriptID = CScriptID ( hash ) ;
CScript subscript ;
if ( keystore . GetCScript ( scriptID , subscript ) ) {
ret = std : : max ( ret , IsMineInner ( keystore , subscript , IsMineSigVersion : : WITNESS_V0 ) ) ;
}
break ;
}
case TX_MULTISIG :
{
// Never treat bare multisig outputs as ours (they can still be made watchonly-though)
if ( sigversion = = IsMineSigVersion : : TOP ) {
break ;
}
// Only consider transactions "mine" if we own ALL the
// keys involved. Multi-signature transactions that are
// partially owned (somebody else has a key that can spend
// them) enable spend-out-from-under-you attacks, especially
// in shared-wallet situations.
std : : vector < valtype > keys ( vSolutions . begin ( ) + 1 , vSolutions . begin ( ) + vSolutions . size ( ) - 1 ) ;
if ( ! PermitsUncompressed ( sigversion ) ) {
for ( size_t i = 0 ; i < keys . size ( ) ; i + + ) {
if ( keys [ i ] . size ( ) ! = 33 ) {
return IsMineResult : : INVALID ;
}
}
}
if ( HaveKeys ( keys , keystore ) ) {
ret = std : : max ( ret , IsMineResult : : SPENDABLE ) ;
}
break ;
}
}
if ( ret = = IsMineResult : : NO & & keystore . HaveWatchOnly ( scriptPubKey ) ) {
ret = std : : max ( ret , IsMineResult : : WATCH_ONLY ) ;
}
return ret ;
}
} // namespace
2019-10-07 15:11:34 -03:00
isminetype LegacyScriptPubKeyMan : : IsMine ( const CScript & script ) const
2019-10-07 15:11:34 -03:00
{
2019-10-07 15:11:34 -03:00
switch ( IsMineInner ( * this , script , IsMineSigVersion : : TOP ) ) {
2019-10-07 15:11:34 -03:00
case IsMineResult : : INVALID :
case IsMineResult : : NO :
return ISMINE_NO ;
case IsMineResult : : WATCH_ONLY :
return ISMINE_WATCH_ONLY ;
case IsMineResult : : SPENDABLE :
return ISMINE_SPENDABLE ;
}
assert ( false ) ;
}
bool CWallet : : Unlock ( const CKeyingMaterial & vMasterKeyIn , bool accept_no_keys )
{
{
LOCK ( cs_KeyStore ) ;
if ( ! SetCrypted ( ) )
return false ;
bool keyPass = mapCryptedKeys . empty ( ) ; // Always pass when there are no encrypted keys
bool keyFail = false ;
CryptedKeyMap : : const_iterator mi = mapCryptedKeys . begin ( ) ;
for ( ; mi ! = mapCryptedKeys . end ( ) ; + + mi )
{
const CPubKey & vchPubKey = ( * mi ) . second . first ;
const std : : vector < unsigned char > & vchCryptedSecret = ( * mi ) . second . second ;
CKey key ;
if ( ! DecryptKey ( vMasterKeyIn , vchCryptedSecret , vchPubKey , key ) )
{
keyFail = true ;
break ;
}
keyPass = true ;
if ( fDecryptionThoroughlyChecked )
break ;
}
if ( keyPass & & keyFail )
{
LogPrintf ( " The wallet is probably corrupted: Some keys decrypt but not all. \n " ) ;
throw std : : runtime_error ( " Error unlocking wallet: some keys decrypt but not all. Your wallet file may be corrupt. " ) ;
}
if ( keyFail | | ( ! keyPass & & ! accept_no_keys ) )
return false ;
vMasterKey = vMasterKeyIn ;
fDecryptionThoroughlyChecked = true ;
}
NotifyStatusChanged ( this ) ;
return true ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : EncryptKeys ( CKeyingMaterial & vMasterKeyIn )
2019-10-07 15:11:34 -03:00
{
LOCK ( cs_KeyStore ) ;
if ( ! mapCryptedKeys . empty ( ) | | IsCrypted ( ) )
return false ;
fUseCrypto = true ;
for ( const KeyMap : : value_type & mKey : mapKeys )
{
const CKey & key = mKey . second ;
CPubKey vchPubKey = key . GetPubKey ( ) ;
CKeyingMaterial vchSecret ( key . begin ( ) , key . end ( ) ) ;
std : : vector < unsigned char > vchCryptedSecret ;
if ( ! EncryptSecret ( vMasterKeyIn , vchSecret , vchPubKey . GetHash ( ) , vchCryptedSecret ) )
return false ;
if ( ! AddCryptedKey ( vchPubKey , vchCryptedSecret ) )
return false ;
}
mapKeys . clear ( ) ;
return true ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : GetReservedDestination ( const OutputType type , bool internal , CTxDestination & address , int64_t & index , CKeyPool & keypool )
2019-10-07 15:11:34 -03:00
{
2019-10-07 15:11:34 -03:00
if ( ! CanGetAddresses ( internal ) ) {
return false ;
}
2019-11-05 12:47:07 -03:00
if ( ! ReserveKeyFromKeyPool ( index , keypool , internal ) ) {
return false ;
2019-10-07 15:11:34 -03:00
}
2019-10-07 15:11:34 -03:00
address = GetDestinationForKey ( keypool . vchPubKey , type ) ;
2019-10-07 15:11:34 -03:00
return true ;
}
2019-10-07 15:11:34 -03:00
void LegacyScriptPubKeyMan : : MarkUnusedAddresses ( const CScript & script )
{
AssertLockHeld ( cs_wallet ) ;
// extract addresses and check if they match with an unused keypool key
for ( const auto & keyid : GetAffectedKeys ( script , * this ) ) {
std : : map < CKeyID , int64_t > : : const_iterator mi = m_pool_key_to_index . find ( keyid ) ;
if ( mi ! = m_pool_key_to_index . end ( ) ) {
WalletLogPrintf ( " %s: Detected a used keypool key, mark all keypool key up to this key as used \n " , __func__ ) ;
MarkReserveKeysAsUsed ( mi - > second ) ;
2019-11-05 12:43:36 -03:00
if ( ! TopUp ( ) ) {
2019-10-07 15:11:34 -03:00
WalletLogPrintf ( " %s: Topping up keypool failed (locked wallet) \n " , __func__ ) ;
}
}
}
}
2019-10-07 15:11:34 -03:00
void LegacyScriptPubKeyMan : : UpgradeKeyMetadata ( )
2019-10-07 15:11:34 -03:00
{
AssertLockHeld ( cs_wallet ) ;
2019-10-07 15:11:34 -03:00
if ( m_storage . IsLocked ( ) | | m_storage . IsWalletFlagSet ( WALLET_FLAG_KEY_ORIGIN_METADATA ) ) {
2019-10-07 15:11:34 -03:00
return ;
}
2019-10-07 15:11:34 -03:00
std : : unique_ptr < WalletBatch > batch = MakeUnique < WalletBatch > ( m_storage . GetDatabase ( ) ) ;
2019-10-07 15:11:34 -03:00
for ( auto & meta_pair : mapKeyMetadata ) {
CKeyMetadata & meta = meta_pair . second ;
if ( ! meta . hd_seed_id . IsNull ( ) & & ! meta . has_key_origin & & meta . hdKeypath ! = " s " ) { // If the hdKeypath is "s", that's the seed and it doesn't have a key origin
CKey key ;
GetKey ( meta . hd_seed_id , key ) ;
CExtKey masterKey ;
masterKey . SetSeed ( key . begin ( ) , key . size ( ) ) ;
// Add to map
CKeyID master_id = masterKey . key . GetPubKey ( ) . GetID ( ) ;
std : : copy ( master_id . begin ( ) , master_id . begin ( ) + 4 , meta . key_origin . fingerprint ) ;
if ( ! ParseHDKeypath ( meta . hdKeypath , meta . key_origin . path ) ) {
throw std : : runtime_error ( " Invalid stored hdKeypath " ) ;
}
meta . has_key_origin = true ;
if ( meta . nVersion < CKeyMetadata : : VERSION_WITH_KEY_ORIGIN ) {
meta . nVersion = CKeyMetadata : : VERSION_WITH_KEY_ORIGIN ;
}
// Write meta to wallet
CPubKey pubkey ;
if ( GetPubKey ( meta_pair . first , pubkey ) ) {
batch - > WriteKeyMetadata ( meta , pubkey , true ) ;
}
}
}
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : SetupGeneration ( bool force )
{
if ( ( CanGenerateKeys ( ) & & ! force ) | | m_storage . IsLocked ( ) ) {
return false ;
}
SetHDSeed ( GenerateNewSeed ( ) ) ;
if ( ! NewKeyPool ( ) ) {
return false ;
}
return true ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : IsHDEnabled ( ) const
2019-10-07 15:11:34 -03:00
{
return ! hdChain . seed_id . IsNull ( ) ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : CanGetAddresses ( bool internal )
2019-10-07 15:11:34 -03:00
{
LOCK ( cs_wallet ) ;
// Check if the keypool has keys
bool keypool_has_keys ;
2019-10-07 15:11:34 -03:00
if ( internal & & m_storage . CanSupportFeature ( FEATURE_HD_SPLIT ) ) {
2019-10-07 15:11:34 -03:00
keypool_has_keys = setInternalKeyPool . size ( ) > 0 ;
} else {
keypool_has_keys = KeypoolCountExternalKeys ( ) > 0 ;
}
// If the keypool doesn't have keys, check if we can generate them
if ( ! keypool_has_keys ) {
return CanGenerateKeys ( ) ;
}
return keypool_has_keys ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : Upgrade ( int prev_version , std : : string & error )
{
AssertLockHeld ( cs_wallet ) ;
error = " " ;
bool hd_upgrade = false ;
bool split_upgrade = false ;
if ( m_storage . CanSupportFeature ( FEATURE_HD ) & & ! IsHDEnabled ( ) ) {
WalletLogPrintf ( " Upgrading wallet to HD \n " ) ;
m_storage . SetMinVersion ( FEATURE_HD ) ;
// generate a new master key
CPubKey masterPubKey = GenerateNewSeed ( ) ;
SetHDSeed ( masterPubKey ) ;
hd_upgrade = true ;
}
// Upgrade to HD chain split if necessary
if ( m_storage . CanSupportFeature ( FEATURE_HD_SPLIT ) ) {
WalletLogPrintf ( " Upgrading wallet to use HD chain split \n " ) ;
m_storage . SetMinVersion ( FEATURE_PRE_SPLIT_KEYPOOL ) ;
split_upgrade = FEATURE_HD_SPLIT > prev_version ;
}
// Mark all keys currently in the keypool as pre-split
if ( split_upgrade ) {
MarkPreSplitKeys ( ) ;
}
// Regenerate the keypool if upgraded to HD
if ( hd_upgrade ) {
2019-11-05 12:43:36 -03:00
if ( ! TopUp ( ) ) {
2019-10-07 15:11:34 -03:00
error = _ ( " Unable to generate keys " ) . translated ;
return false ;
}
}
return true ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : HavePrivateKeys ( ) const
{
LOCK ( cs_KeyStore ) ;
return ! mapKeys . empty ( ) | | ! mapCryptedKeys . empty ( ) ;
}
2019-10-07 15:11:34 -03:00
void LegacyScriptPubKeyMan : : RewriteDB ( )
{
AssertLockHeld ( cs_wallet ) ;
setInternalKeyPool . clear ( ) ;
setExternalKeyPool . clear ( ) ;
m_pool_key_to_index . clear ( ) ;
// Note: can't top-up keypool here, because wallet is locked.
// User will be prompted to unlock wallet the next operation
// that requires a new key.
}
2019-10-07 15:11:34 -03:00
static int64_t GetOldestKeyTimeInPool ( const std : : set < int64_t > & setKeyPool , WalletBatch & batch ) {
if ( setKeyPool . empty ( ) ) {
return GetTime ( ) ;
}
CKeyPool keypool ;
int64_t nIndex = * ( setKeyPool . begin ( ) ) ;
if ( ! batch . ReadPool ( nIndex , keypool ) ) {
throw std : : runtime_error ( std : : string ( __func__ ) + " : read oldest key in keypool failed " ) ;
}
assert ( keypool . vchPubKey . IsValid ( ) ) ;
return keypool . nTime ;
}
2019-10-07 15:11:34 -03:00
int64_t LegacyScriptPubKeyMan : : GetOldestKeyPoolTime ( )
2019-10-07 15:11:34 -03:00
{
LOCK ( cs_wallet ) ;
2019-10-07 15:11:34 -03:00
WalletBatch batch ( m_storage . GetDatabase ( ) ) ;
2019-10-07 15:11:34 -03:00
// load oldest key from keypool, get time and return
int64_t oldestKey = GetOldestKeyTimeInPool ( setExternalKeyPool , batch ) ;
2019-10-07 15:11:34 -03:00
if ( IsHDEnabled ( ) & & m_storage . CanSupportFeature ( FEATURE_HD_SPLIT ) ) {
2019-10-07 15:11:34 -03:00
oldestKey = std : : max ( GetOldestKeyTimeInPool ( setInternalKeyPool , batch ) , oldestKey ) ;
if ( ! set_pre_split_keypool . empty ( ) ) {
oldestKey = std : : max ( GetOldestKeyTimeInPool ( set_pre_split_keypool , batch ) , oldestKey ) ;
}
}
return oldestKey ;
}
2019-10-07 15:11:34 -03:00
size_t LegacyScriptPubKeyMan : : KeypoolCountExternalKeys ( )
2019-10-07 15:11:34 -03:00
{
AssertLockHeld ( cs_wallet ) ;
return setExternalKeyPool . size ( ) + set_pre_split_keypool . size ( ) ;
}
2019-10-07 15:11:34 -03:00
unsigned int LegacyScriptPubKeyMan : : GetKeyPoolSize ( ) const
{
AssertLockHeld ( cs_wallet ) ;
return setInternalKeyPool . size ( ) + setExternalKeyPool . size ( ) ;
}
2019-10-07 15:11:34 -03:00
int64_t LegacyScriptPubKeyMan : : GetTimeFirstKey ( ) const
{
AssertLockHeld ( cs_wallet ) ;
return nTimeFirstKey ;
}
2019-11-05 12:36:55 -03:00
const CKeyMetadata * LegacyScriptPubKeyMan : : GetMetadata ( const CTxDestination & dest ) const
2019-10-07 15:11:34 -03:00
{
AssertLockHeld ( cs_wallet ) ;
2019-11-05 12:36:55 -03:00
CKeyID key_id = GetKeyForDestination ( * this , dest ) ;
if ( ! key_id . IsNull ( ) ) {
auto it = mapKeyMetadata . find ( key_id ) ;
if ( it ! = mapKeyMetadata . end ( ) ) {
return & it - > second ;
2019-10-07 15:11:34 -03:00
}
}
2019-11-05 12:36:55 -03:00
CScript scriptPubKey = GetScriptForDestination ( dest ) ;
auto it = m_script_metadata . find ( CScriptID ( scriptPubKey ) ) ;
if ( it ! = m_script_metadata . end ( ) ) {
return & it - > second ;
}
2019-10-07 15:11:34 -03:00
return nullptr ;
}
2019-10-07 15:11:34 -03:00
/**
* Update wallet first key creation time . This should be called whenever keys
* are added to the wallet , with the oldest key creation time .
*/
2019-10-07 15:11:34 -03:00
void LegacyScriptPubKeyMan : : UpdateTimeFirstKey ( int64_t nCreateTime )
2019-10-07 15:11:34 -03:00
{
AssertLockHeld ( cs_wallet ) ;
if ( nCreateTime < = 1 ) {
// Cannot determine birthday information, so set the wallet birthday to
// the beginning of time.
nTimeFirstKey = 1 ;
} else if ( ! nTimeFirstKey | | nCreateTime < nTimeFirstKey ) {
nTimeFirstKey = nCreateTime ;
}
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : LoadKey ( const CKey & key , const CPubKey & pubkey )
{
return AddKeyPubKeyInner ( key , pubkey ) ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : AddKeyPubKey ( const CKey & secret , const CPubKey & pubkey )
2019-10-07 15:11:34 -03:00
{
2019-10-07 15:11:34 -03:00
WalletBatch batch ( m_storage . GetDatabase ( ) ) ;
return LegacyScriptPubKeyMan : : AddKeyPubKeyWithDB ( batch , secret , pubkey ) ;
2019-10-07 15:11:34 -03:00
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : AddKeyPubKeyWithDB ( WalletBatch & batch , const CKey & secret , const CPubKey & pubkey )
2019-10-07 15:11:34 -03:00
{
2019-10-29 13:21:57 -03:00
AssertLockHeld ( cs_wallet ) ;
2019-10-07 15:11:34 -03:00
// Make sure we aren't adding private keys to private key disabled wallets
2019-10-07 15:11:34 -03:00
assert ( ! m_storage . IsWalletFlagSet ( WALLET_FLAG_DISABLE_PRIVATE_KEYS ) ) ;
2019-10-07 15:11:34 -03:00
// FillableSigningProvider has no concept of wallet databases, but calls AddCryptedKey
// which is overridden below. To avoid flushes, the database handle is
// tunneled through to it.
bool needsDB = ! encrypted_batch ;
if ( needsDB ) {
encrypted_batch = & batch ;
}
if ( ! AddKeyPubKeyInner ( secret , pubkey ) ) {
if ( needsDB ) encrypted_batch = nullptr ;
return false ;
}
if ( needsDB ) encrypted_batch = nullptr ;
// check if we need to remove from watch-only
CScript script ;
script = GetScriptForDestination ( PKHash ( pubkey ) ) ;
if ( HaveWatchOnly ( script ) ) {
RemoveWatchOnly ( script ) ;
}
script = GetScriptForRawPubKey ( pubkey ) ;
if ( HaveWatchOnly ( script ) ) {
RemoveWatchOnly ( script ) ;
}
if ( ! IsCrypted ( ) ) {
return batch . WriteKey ( pubkey ,
secret . GetPrivKey ( ) ,
mapKeyMetadata [ pubkey . GetID ( ) ] ) ;
}
2019-10-29 18:53:35 -03:00
m_storage . UnsetBlankWalletFlag ( batch ) ;
2019-10-07 15:11:34 -03:00
return true ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : LoadCScript ( const CScript & redeemScript )
2019-10-07 15:11:34 -03:00
{
/* A sanity check was added in pull #3843 to avoid adding redeemScripts
* that never can be redeemed . However , old wallets may still contain
* these . Do not add them to the wallet and warn . */
if ( redeemScript . size ( ) > MAX_SCRIPT_ELEMENT_SIZE )
{
std : : string strAddr = EncodeDestination ( ScriptHash ( redeemScript ) ) ;
WalletLogPrintf ( " %s: Warning: This wallet contains a redeemScript of size %i which exceeds maximum size %i thus can never be redeemed. Do not use address %s. \n " , __func__ , redeemScript . size ( ) , MAX_SCRIPT_ELEMENT_SIZE , strAddr ) ;
return true ;
}
return FillableSigningProvider : : AddCScript ( redeemScript ) ;
}
2019-10-07 15:11:34 -03:00
void LegacyScriptPubKeyMan : : LoadKeyMetadata ( const CKeyID & keyID , const CKeyMetadata & meta )
2019-10-07 15:11:34 -03:00
{
AssertLockHeld ( cs_wallet ) ;
UpdateTimeFirstKey ( meta . nCreateTime ) ;
mapKeyMetadata [ keyID ] = meta ;
}
2019-10-07 15:11:34 -03:00
void LegacyScriptPubKeyMan : : LoadScriptMetadata ( const CScriptID & script_id , const CKeyMetadata & meta )
2019-10-07 15:11:34 -03:00
{
AssertLockHeld ( cs_wallet ) ;
UpdateTimeFirstKey ( meta . nCreateTime ) ;
m_script_metadata [ script_id ] = meta ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : AddKeyPubKeyInner ( const CKey & key , const CPubKey & pubkey )
2019-10-07 15:11:34 -03:00
{
LOCK ( cs_KeyStore ) ;
if ( ! IsCrypted ( ) ) {
return FillableSigningProvider : : AddKeyPubKey ( key , pubkey ) ;
}
2019-10-07 15:11:34 -03:00
if ( m_storage . IsLocked ( ) ) {
2019-10-07 15:11:34 -03:00
return false ;
}
std : : vector < unsigned char > vchCryptedSecret ;
CKeyingMaterial vchSecret ( key . begin ( ) , key . end ( ) ) ;
if ( ! EncryptSecret ( vMasterKey , vchSecret , pubkey . GetHash ( ) , vchCryptedSecret ) ) {
return false ;
}
if ( ! AddCryptedKey ( pubkey , vchCryptedSecret ) ) {
return false ;
}
return true ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : LoadCryptedKey ( const CPubKey & vchPubKey , const std : : vector < unsigned char > & vchCryptedSecret )
2019-10-07 15:11:34 -03:00
{
return AddCryptedKeyInner ( vchPubKey , vchCryptedSecret ) ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : AddCryptedKeyInner ( const CPubKey & vchPubKey , const std : : vector < unsigned char > & vchCryptedSecret )
2019-10-07 15:11:34 -03:00
{
LOCK ( cs_KeyStore ) ;
if ( ! SetCrypted ( ) ) {
return false ;
}
mapCryptedKeys [ vchPubKey . GetID ( ) ] = make_pair ( vchPubKey , vchCryptedSecret ) ;
ImplicitlyLearnRelatedKeyScripts ( vchPubKey ) ;
return true ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : AddCryptedKey ( const CPubKey & vchPubKey ,
2019-10-07 15:11:34 -03:00
const std : : vector < unsigned char > & vchCryptedSecret )
{
if ( ! AddCryptedKeyInner ( vchPubKey , vchCryptedSecret ) )
return false ;
{
LOCK ( cs_wallet ) ;
if ( encrypted_batch )
return encrypted_batch - > WriteCryptedKey ( vchPubKey ,
vchCryptedSecret ,
mapKeyMetadata [ vchPubKey . GetID ( ) ] ) ;
else
2019-10-07 15:11:34 -03:00
return WalletBatch ( m_storage . GetDatabase ( ) ) . WriteCryptedKey ( vchPubKey ,
2019-10-07 15:11:34 -03:00
vchCryptedSecret ,
mapKeyMetadata [ vchPubKey . GetID ( ) ] ) ;
}
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : HaveWatchOnly ( const CScript & dest ) const
2019-10-07 15:11:34 -03:00
{
LOCK ( cs_KeyStore ) ;
return setWatchOnly . count ( dest ) > 0 ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : HaveWatchOnly ( ) const
2019-10-07 15:11:34 -03:00
{
LOCK ( cs_KeyStore ) ;
return ( ! setWatchOnly . empty ( ) ) ;
}
static bool ExtractPubKey ( const CScript & dest , CPubKey & pubKeyOut )
{
std : : vector < std : : vector < unsigned char > > solutions ;
return Solver ( dest , solutions ) = = TX_PUBKEY & &
( pubKeyOut = CPubKey ( solutions [ 0 ] ) ) . IsFullyValid ( ) ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : RemoveWatchOnly ( const CScript & dest )
2019-10-07 15:11:34 -03:00
{
AssertLockHeld ( cs_wallet ) ;
{
LOCK ( cs_KeyStore ) ;
setWatchOnly . erase ( dest ) ;
CPubKey pubKey ;
if ( ExtractPubKey ( dest , pubKey ) ) {
mapWatchKeys . erase ( pubKey . GetID ( ) ) ;
}
// Related CScripts are not removed; having superfluous scripts around is
// harmless (see comment in ImplicitlyLearnRelatedKeyScripts).
}
if ( ! HaveWatchOnly ( ) )
NotifyWatchonlyChanged ( false ) ;
2019-10-07 15:11:34 -03:00
if ( ! WalletBatch ( m_storage . GetDatabase ( ) ) . EraseWatchOnly ( dest ) )
2019-10-07 15:11:34 -03:00
return false ;
return true ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : LoadWatchOnly ( const CScript & dest )
2019-10-07 15:11:34 -03:00
{
return AddWatchOnlyInMem ( dest ) ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : AddWatchOnlyInMem ( const CScript & dest )
2019-10-07 15:11:34 -03:00
{
LOCK ( cs_KeyStore ) ;
setWatchOnly . insert ( dest ) ;
CPubKey pubKey ;
if ( ExtractPubKey ( dest , pubKey ) ) {
mapWatchKeys [ pubKey . GetID ( ) ] = pubKey ;
ImplicitlyLearnRelatedKeyScripts ( pubKey ) ;
}
return true ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : AddWatchOnlyWithDB ( WalletBatch & batch , const CScript & dest )
2019-10-07 15:11:34 -03:00
{
if ( ! AddWatchOnlyInMem ( dest ) )
return false ;
const CKeyMetadata & meta = m_script_metadata [ CScriptID ( dest ) ] ;
UpdateTimeFirstKey ( meta . nCreateTime ) ;
NotifyWatchonlyChanged ( true ) ;
if ( batch . WriteWatchOnly ( dest , meta ) ) {
2019-10-29 18:53:35 -03:00
m_storage . UnsetBlankWalletFlag ( batch ) ;
2019-10-07 15:11:34 -03:00
return true ;
}
return false ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : AddWatchOnlyWithDB ( WalletBatch & batch , const CScript & dest , int64_t create_time )
2019-10-07 15:11:34 -03:00
{
m_script_metadata [ CScriptID ( dest ) ] . nCreateTime = create_time ;
return AddWatchOnlyWithDB ( batch , dest ) ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : AddWatchOnly ( const CScript & dest )
2019-10-07 15:11:34 -03:00
{
2019-10-07 15:11:34 -03:00
WalletBatch batch ( m_storage . GetDatabase ( ) ) ;
2019-10-07 15:11:34 -03:00
return AddWatchOnlyWithDB ( batch , dest ) ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : AddWatchOnly ( const CScript & dest , int64_t nCreateTime )
2019-10-07 15:11:34 -03:00
{
m_script_metadata [ CScriptID ( dest ) ] . nCreateTime = nCreateTime ;
return AddWatchOnly ( dest ) ;
}
2019-10-07 15:11:34 -03:00
void LegacyScriptPubKeyMan : : SetHDChain ( const CHDChain & chain , bool memonly )
2019-10-07 15:11:34 -03:00
{
LOCK ( cs_wallet ) ;
2019-10-07 15:11:34 -03:00
if ( ! memonly & & ! WalletBatch ( m_storage . GetDatabase ( ) ) . WriteHDChain ( chain ) )
2019-10-07 15:11:34 -03:00
throw std : : runtime_error ( std : : string ( __func__ ) + " : writing chain failed " ) ;
hdChain = chain ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : HaveKey ( const CKeyID & address ) const
2019-10-07 15:11:34 -03:00
{
LOCK ( cs_KeyStore ) ;
if ( ! IsCrypted ( ) ) {
return FillableSigningProvider : : HaveKey ( address ) ;
}
return mapCryptedKeys . count ( address ) > 0 ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : GetKey ( const CKeyID & address , CKey & keyOut ) const
2019-10-07 15:11:34 -03:00
{
LOCK ( cs_KeyStore ) ;
if ( ! IsCrypted ( ) ) {
return FillableSigningProvider : : GetKey ( address , keyOut ) ;
}
CryptedKeyMap : : const_iterator mi = mapCryptedKeys . find ( address ) ;
if ( mi ! = mapCryptedKeys . end ( ) )
{
const CPubKey & vchPubKey = ( * mi ) . second . first ;
const std : : vector < unsigned char > & vchCryptedSecret = ( * mi ) . second . second ;
return DecryptKey ( vMasterKey , vchCryptedSecret , vchPubKey , keyOut ) ;
}
return false ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : GetKeyOrigin ( const CKeyID & keyID , KeyOriginInfo & info ) const
2019-10-07 15:11:34 -03:00
{
CKeyMetadata meta ;
{
LOCK ( cs_wallet ) ;
auto it = mapKeyMetadata . find ( keyID ) ;
if ( it ! = mapKeyMetadata . end ( ) ) {
meta = it - > second ;
}
}
if ( meta . has_key_origin ) {
std : : copy ( meta . key_origin . fingerprint , meta . key_origin . fingerprint + 4 , info . fingerprint ) ;
info . path = meta . key_origin . path ;
} else { // Single pubkeys get the master fingerprint of themselves
std : : copy ( keyID . begin ( ) , keyID . begin ( ) + 4 , info . fingerprint ) ;
}
return true ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : GetWatchPubKey ( const CKeyID & address , CPubKey & pubkey_out ) const
2019-10-07 15:11:34 -03:00
{
LOCK ( cs_KeyStore ) ;
WatchKeyMap : : const_iterator it = mapWatchKeys . find ( address ) ;
if ( it ! = mapWatchKeys . end ( ) ) {
pubkey_out = it - > second ;
return true ;
}
return false ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : GetPubKey ( const CKeyID & address , CPubKey & vchPubKeyOut ) const
2019-10-07 15:11:34 -03:00
{
LOCK ( cs_KeyStore ) ;
if ( ! IsCrypted ( ) ) {
if ( ! FillableSigningProvider : : GetPubKey ( address , vchPubKeyOut ) ) {
return GetWatchPubKey ( address , vchPubKeyOut ) ;
}
return true ;
}
CryptedKeyMap : : const_iterator mi = mapCryptedKeys . find ( address ) ;
if ( mi ! = mapCryptedKeys . end ( ) )
{
vchPubKeyOut = ( * mi ) . second . first ;
return true ;
}
// Check for watch-only pubkeys
return GetWatchPubKey ( address , vchPubKeyOut ) ;
}
2019-10-07 15:11:34 -03:00
CPubKey LegacyScriptPubKeyMan : : GenerateNewKey ( WalletBatch & batch , bool internal )
2019-10-07 15:11:34 -03:00
{
2019-10-07 15:11:34 -03:00
assert ( ! m_storage . IsWalletFlagSet ( WALLET_FLAG_DISABLE_PRIVATE_KEYS ) ) ;
assert ( ! m_storage . IsWalletFlagSet ( WALLET_FLAG_BLANK_WALLET ) ) ;
2019-10-07 15:11:34 -03:00
AssertLockHeld ( cs_wallet ) ;
2019-10-07 15:11:34 -03:00
bool fCompressed = m_storage . CanSupportFeature ( FEATURE_COMPRPUBKEY ) ; // default to compressed public keys if we want 0.6.0 wallets
2019-10-07 15:11:34 -03:00
CKey secret ;
// Create new metadata
int64_t nCreationTime = GetTime ( ) ;
CKeyMetadata metadata ( nCreationTime ) ;
// use HD key derivation if HD was enabled during wallet creation and a seed is present
if ( IsHDEnabled ( ) ) {
2019-10-07 15:11:34 -03:00
DeriveNewChildKey ( batch , metadata , secret , ( m_storage . CanSupportFeature ( FEATURE_HD_SPLIT ) ? internal : false ) ) ;
2019-10-07 15:11:34 -03:00
} else {
secret . MakeNewKey ( fCompressed ) ;
}
// Compressed public keys were introduced in version 0.6.0
if ( fCompressed ) {
2019-10-07 15:11:34 -03:00
m_storage . SetMinVersion ( FEATURE_COMPRPUBKEY ) ;
2019-10-07 15:11:34 -03:00
}
CPubKey pubkey = secret . GetPubKey ( ) ;
assert ( secret . VerifyPubKey ( pubkey ) ) ;
mapKeyMetadata [ pubkey . GetID ( ) ] = metadata ;
UpdateTimeFirstKey ( nCreationTime ) ;
if ( ! AddKeyPubKeyWithDB ( batch , secret , pubkey ) ) {
throw std : : runtime_error ( std : : string ( __func__ ) + " : AddKey failed " ) ;
}
return pubkey ;
}
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000 ;
2019-10-07 15:11:34 -03:00
void LegacyScriptPubKeyMan : : DeriveNewChildKey ( WalletBatch & batch , CKeyMetadata & metadata , CKey & secret , bool internal )
2019-10-07 15:11:34 -03:00
{
// for now we use a fixed keypath scheme of m/0'/0'/k
CKey seed ; //seed (256bit)
CExtKey masterKey ; //hd master key
CExtKey accountKey ; //key at m/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 seed
if ( ! GetKey ( hdChain . seed_id , seed ) )
throw std : : runtime_error ( std : : string ( __func__ ) + " : seed not found " ) ;
masterKey . SetSeed ( seed . begin ( ) , seed . 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' (external chain) OR m/0'/1' (internal chain)
2019-10-07 15:11:34 -03:00
assert ( internal ? m_storage . CanSupportFeature ( FEATURE_HD_SPLIT ) : true ) ;
2019-10-07 15:11:34 -03:00
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
if ( internal ) {
chainChildKey . Derive ( childKey , hdChain . nInternalChainCounter | BIP32_HARDENED_KEY_LIMIT ) ;
metadata . hdKeypath = " m/0'/1'/ " + std : : to_string ( hdChain . nInternalChainCounter ) + " ' " ;
metadata . key_origin . path . push_back ( 0 | BIP32_HARDENED_KEY_LIMIT ) ;
metadata . key_origin . path . push_back ( 1 | BIP32_HARDENED_KEY_LIMIT ) ;
metadata . key_origin . path . push_back ( hdChain . nInternalChainCounter | BIP32_HARDENED_KEY_LIMIT ) ;
hdChain . nInternalChainCounter + + ;
}
else {
chainChildKey . Derive ( childKey , hdChain . nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT ) ;
metadata . hdKeypath = " m/0'/0'/ " + std : : to_string ( hdChain . nExternalChainCounter ) + " ' " ;
metadata . key_origin . path . push_back ( 0 | BIP32_HARDENED_KEY_LIMIT ) ;
metadata . key_origin . path . push_back ( 0 | BIP32_HARDENED_KEY_LIMIT ) ;
metadata . key_origin . path . push_back ( hdChain . nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT ) ;
hdChain . nExternalChainCounter + + ;
}
} while ( HaveKey ( childKey . key . GetPubKey ( ) . GetID ( ) ) ) ;
secret = childKey . key ;
metadata . hd_seed_id = hdChain . seed_id ;
CKeyID master_id = masterKey . key . GetPubKey ( ) . GetID ( ) ;
std : : copy ( master_id . begin ( ) , master_id . begin ( ) + 4 , metadata . key_origin . fingerprint ) ;
metadata . has_key_origin = true ;
// update the chain model in the database
if ( ! batch . WriteHDChain ( hdChain ) )
throw std : : runtime_error ( std : : string ( __func__ ) + " : Writing HD chain model failed " ) ;
}
2019-10-07 15:11:34 -03:00
void LegacyScriptPubKeyMan : : LoadKeyPool ( int64_t nIndex , const CKeyPool & keypool )
2019-10-07 15:11:34 -03:00
{
AssertLockHeld ( cs_wallet ) ;
if ( keypool . m_pre_split ) {
set_pre_split_keypool . insert ( nIndex ) ;
} else if ( keypool . fInternal ) {
setInternalKeyPool . insert ( nIndex ) ;
} else {
setExternalKeyPool . insert ( nIndex ) ;
}
m_max_keypool_index = std : : max ( m_max_keypool_index , nIndex ) ;
m_pool_key_to_index [ keypool . vchPubKey . GetID ( ) ] = nIndex ;
// If no metadata exists yet, create a default with the pool key's
// creation time. Note that this may be overwritten by actually
// stored metadata for that key later, which is fine.
CKeyID keyid = keypool . vchPubKey . GetID ( ) ;
if ( mapKeyMetadata . count ( keyid ) = = 0 )
mapKeyMetadata [ keyid ] = CKeyMetadata ( keypool . nTime ) ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : CanGenerateKeys ( )
2019-10-07 15:11:34 -03:00
{
// A wallet can generate keys if it has an HD seed (IsHDEnabled) or it is a non-HD wallet (pre FEATURE_HD)
LOCK ( cs_wallet ) ;
2019-10-07 15:11:34 -03:00
return IsHDEnabled ( ) | | ! m_storage . CanSupportFeature ( FEATURE_HD ) ;
2019-10-07 15:11:34 -03:00
}
2019-10-07 15:11:34 -03:00
CPubKey LegacyScriptPubKeyMan : : GenerateNewSeed ( )
2019-10-07 15:11:34 -03:00
{
2019-10-07 15:11:34 -03:00
assert ( ! m_storage . IsWalletFlagSet ( WALLET_FLAG_DISABLE_PRIVATE_KEYS ) ) ;
2019-10-07 15:11:34 -03:00
CKey key ;
key . MakeNewKey ( true ) ;
return DeriveNewSeed ( key ) ;
}
2019-10-07 15:11:34 -03:00
CPubKey LegacyScriptPubKeyMan : : DeriveNewSeed ( const CKey & key )
2019-10-07 15:11:34 -03:00
{
int64_t nCreationTime = GetTime ( ) ;
CKeyMetadata metadata ( nCreationTime ) ;
// calculate the seed
CPubKey seed = key . GetPubKey ( ) ;
assert ( key . VerifyPubKey ( seed ) ) ;
// set the hd keypath to "s" -> Seed, refers the seed to itself
metadata . hdKeypath = " s " ;
metadata . has_key_origin = false ;
metadata . hd_seed_id = seed . GetID ( ) ;
{
LOCK ( cs_wallet ) ;
// mem store the metadata
mapKeyMetadata [ seed . GetID ( ) ] = metadata ;
// write the key&metadata to the database
if ( ! AddKeyPubKey ( key , seed ) )
throw std : : runtime_error ( std : : string ( __func__ ) + " : AddKeyPubKey failed " ) ;
}
return seed ;
}
2019-10-07 15:11:34 -03:00
void LegacyScriptPubKeyMan : : SetHDSeed ( const CPubKey & seed )
2019-10-07 15:11:34 -03:00
{
LOCK ( cs_wallet ) ;
// store the keyid (hash160) together with
// the child index counter in the database
// as a hdchain object
CHDChain newHdChain ;
2019-10-07 15:11:34 -03:00
newHdChain . nVersion = m_storage . CanSupportFeature ( FEATURE_HD_SPLIT ) ? CHDChain : : VERSION_HD_CHAIN_SPLIT : CHDChain : : VERSION_HD_BASE ;
2019-10-07 15:11:34 -03:00
newHdChain . seed_id = seed . GetID ( ) ;
SetHDChain ( newHdChain , false ) ;
NotifyCanGetAddressesChanged ( ) ;
2019-10-07 15:11:34 -03:00
WalletBatch batch ( m_storage . GetDatabase ( ) ) ;
2019-10-29 18:53:35 -03:00
m_storage . UnsetBlankWalletFlag ( batch ) ;
2019-10-07 15:11:34 -03:00
}
/**
* Mark old keypool keys as used ,
* and generate all new keys
*/
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : NewKeyPool ( )
2019-10-07 15:11:34 -03:00
{
2019-10-07 15:11:34 -03:00
if ( m_storage . IsWalletFlagSet ( WALLET_FLAG_DISABLE_PRIVATE_KEYS ) ) {
2019-10-07 15:11:34 -03:00
return false ;
}
{
LOCK ( cs_wallet ) ;
2019-10-07 15:11:34 -03:00
WalletBatch batch ( m_storage . GetDatabase ( ) ) ;
2019-10-07 15:11:34 -03:00
for ( const int64_t nIndex : setInternalKeyPool ) {
batch . ErasePool ( nIndex ) ;
}
setInternalKeyPool . clear ( ) ;
for ( const int64_t nIndex : setExternalKeyPool ) {
batch . ErasePool ( nIndex ) ;
}
setExternalKeyPool . clear ( ) ;
for ( const int64_t nIndex : set_pre_split_keypool ) {
batch . ErasePool ( nIndex ) ;
}
set_pre_split_keypool . clear ( ) ;
m_pool_key_to_index . clear ( ) ;
2019-11-05 12:43:36 -03:00
if ( ! TopUp ( ) ) {
2019-10-07 15:11:34 -03:00
return false ;
}
2019-10-07 15:11:34 -03:00
WalletLogPrintf ( " LegacyScriptPubKeyMan::NewKeyPool rewrote keypool \n " ) ;
2019-10-07 15:11:34 -03:00
}
return true ;
}
2019-11-05 12:43:36 -03:00
bool LegacyScriptPubKeyMan : : TopUp ( unsigned int kpSize )
2019-10-07 15:11:34 -03:00
{
if ( ! CanGenerateKeys ( ) ) {
return false ;
}
{
LOCK ( cs_wallet ) ;
2019-10-07 15:11:34 -03:00
if ( m_storage . IsLocked ( ) ) return false ;
2019-10-07 15:11:34 -03:00
// Top up key pool
unsigned int nTargetSize ;
if ( kpSize > 0 )
nTargetSize = kpSize ;
else
nTargetSize = std : : max ( gArgs . GetArg ( " -keypool " , DEFAULT_KEYPOOL_SIZE ) , ( int64_t ) 0 ) ;
// count amount of available keys (internal, external)
// make sure the keypool of external and internal keys fits the user selected target (-keypool)
int64_t missingExternal = std : : max ( std : : max ( ( int64_t ) nTargetSize , ( int64_t ) 1 ) - ( int64_t ) setExternalKeyPool . size ( ) , ( int64_t ) 0 ) ;
int64_t missingInternal = std : : max ( std : : max ( ( int64_t ) nTargetSize , ( int64_t ) 1 ) - ( int64_t ) setInternalKeyPool . size ( ) , ( int64_t ) 0 ) ;
2019-10-07 15:11:34 -03:00
if ( ! IsHDEnabled ( ) | | ! m_storage . CanSupportFeature ( FEATURE_HD_SPLIT ) )
2019-10-07 15:11:34 -03:00
{
// don't create extra internal keys
missingInternal = 0 ;
}
bool internal = false ;
2019-10-07 15:11:34 -03:00
WalletBatch batch ( m_storage . GetDatabase ( ) ) ;
2019-10-07 15:11:34 -03:00
for ( int64_t i = missingInternal + missingExternal ; i - - ; )
{
if ( i < missingInternal ) {
internal = true ;
}
CPubKey pubkey ( GenerateNewKey ( batch , internal ) ) ;
AddKeypoolPubkeyWithDB ( pubkey , internal , batch ) ;
}
if ( missingInternal + missingExternal > 0 ) {
WalletLogPrintf ( " keypool added %d keys (%d internal), size=%u (%u internal) \n " , missingInternal + missingExternal , missingInternal , setInternalKeyPool . size ( ) + setExternalKeyPool . size ( ) + set_pre_split_keypool . size ( ) , setInternalKeyPool . size ( ) ) ;
}
}
NotifyCanGetAddressesChanged ( ) ;
return true ;
}
2019-10-07 15:11:34 -03:00
void LegacyScriptPubKeyMan : : AddKeypoolPubkeyWithDB ( const CPubKey & pubkey , const bool internal , WalletBatch & batch )
2019-10-07 15:11:34 -03:00
{
LOCK ( cs_wallet ) ;
assert ( m_max_keypool_index < std : : numeric_limits < int64_t > : : max ( ) ) ; // How in the hell did you use so many keys?
int64_t index = + + m_max_keypool_index ;
if ( ! batch . WritePool ( index , CKeyPool ( pubkey , internal ) ) ) {
throw std : : runtime_error ( std : : string ( __func__ ) + " : writing imported pubkey failed " ) ;
}
if ( internal ) {
setInternalKeyPool . insert ( index ) ;
} else {
setExternalKeyPool . insert ( index ) ;
}
m_pool_key_to_index [ pubkey . GetID ( ) ] = index ;
}
2019-11-26 13:52:51 -03:00
void LegacyScriptPubKeyMan : : KeepDestination ( int64_t nIndex , const OutputType & type , const CPubKey & pubkey )
2019-10-07 15:11:34 -03:00
{
// Remove from key pool
2019-10-07 15:11:34 -03:00
WalletBatch batch ( m_storage . GetDatabase ( ) ) ;
2019-10-07 15:11:34 -03:00
batch . ErasePool ( nIndex ) ;
2019-10-07 15:11:34 -03:00
LearnRelatedScripts ( pubkey , type ) ;
2019-10-07 15:11:34 -03:00
WalletLogPrintf ( " keypool keep %d \n " , nIndex ) ;
}
2019-11-19 20:33:20 -03:00
void LegacyScriptPubKeyMan : : ReturnDestination ( int64_t nIndex , bool fInternal , const CPubKey & pubkey )
2019-10-07 15:11:34 -03:00
{
// Return to key pool
{
LOCK ( cs_wallet ) ;
if ( fInternal ) {
setInternalKeyPool . insert ( nIndex ) ;
} else if ( ! set_pre_split_keypool . empty ( ) ) {
set_pre_split_keypool . insert ( nIndex ) ;
} else {
setExternalKeyPool . insert ( nIndex ) ;
}
m_pool_key_to_index [ pubkey . GetID ( ) ] = nIndex ;
NotifyCanGetAddressesChanged ( ) ;
}
WalletLogPrintf ( " keypool return %d \n " , nIndex ) ;
}
2019-11-26 13:52:51 -03:00
bool LegacyScriptPubKeyMan : : GetKeyFromPool ( CPubKey & result , const OutputType type , bool internal )
2019-10-07 15:11:34 -03:00
{
if ( ! CanGetAddresses ( internal ) ) {
return false ;
}
CKeyPool keypool ;
{
LOCK ( cs_wallet ) ;
int64_t nIndex ;
2019-10-07 15:11:34 -03:00
if ( ! ReserveKeyFromKeyPool ( nIndex , keypool , internal ) & & ! m_storage . IsWalletFlagSet ( WALLET_FLAG_DISABLE_PRIVATE_KEYS ) ) {
if ( m_storage . IsLocked ( ) ) return false ;
WalletBatch batch ( m_storage . GetDatabase ( ) ) ;
2019-10-07 15:11:34 -03:00
result = GenerateNewKey ( batch , internal ) ;
return true ;
}
2019-11-26 13:52:51 -03:00
KeepDestination ( nIndex , type , keypool . vchPubKey ) ;
2019-10-07 15:11:34 -03:00
result = keypool . vchPubKey ;
}
return true ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : ReserveKeyFromKeyPool ( int64_t & nIndex , CKeyPool & keypool , bool fRequestedInternal )
2019-10-07 15:11:34 -03:00
{
nIndex = - 1 ;
keypool . vchPubKey = CPubKey ( ) ;
{
LOCK ( cs_wallet ) ;
2019-11-05 12:43:36 -03:00
TopUp ( ) ;
2019-10-07 15:11:34 -03:00
bool fReturningInternal = fRequestedInternal ;
2019-10-07 15:11:34 -03:00
fReturningInternal & = ( IsHDEnabled ( ) & & m_storage . CanSupportFeature ( FEATURE_HD_SPLIT ) ) | | m_storage . IsWalletFlagSet ( WALLET_FLAG_DISABLE_PRIVATE_KEYS ) ;
2019-10-07 15:11:34 -03:00
bool use_split_keypool = set_pre_split_keypool . empty ( ) ;
std : : set < int64_t > & setKeyPool = use_split_keypool ? ( fReturningInternal ? setInternalKeyPool : setExternalKeyPool ) : set_pre_split_keypool ;
// Get the oldest key
if ( setKeyPool . empty ( ) ) {
return false ;
}
2019-10-07 15:11:34 -03:00
WalletBatch batch ( m_storage . GetDatabase ( ) ) ;
2019-10-07 15:11:34 -03:00
auto it = setKeyPool . begin ( ) ;
nIndex = * it ;
setKeyPool . erase ( it ) ;
if ( ! batch . ReadPool ( nIndex , keypool ) ) {
throw std : : runtime_error ( std : : string ( __func__ ) + " : read failed " ) ;
}
CPubKey pk ;
if ( ! GetPubKey ( keypool . vchPubKey . GetID ( ) , pk ) ) {
throw std : : runtime_error ( std : : string ( __func__ ) + " : unknown key in key pool " ) ;
}
// If the key was pre-split keypool, we don't care about what type it is
if ( use_split_keypool & & keypool . fInternal ! = fReturningInternal ) {
throw std : : runtime_error ( std : : string ( __func__ ) + " : keypool entry misclassified " ) ;
}
if ( ! keypool . vchPubKey . IsValid ( ) ) {
throw std : : runtime_error ( std : : string ( __func__ ) + " : keypool entry invalid " ) ;
}
m_pool_key_to_index . erase ( keypool . vchPubKey . GetID ( ) ) ;
WalletLogPrintf ( " keypool reserve %d \n " , nIndex ) ;
}
NotifyCanGetAddressesChanged ( ) ;
return true ;
}
2019-10-07 15:11:34 -03:00
void LegacyScriptPubKeyMan : : LearnRelatedScripts ( const CPubKey & key , OutputType type )
2019-10-07 15:11:34 -03:00
{
if ( key . IsCompressed ( ) & & ( type = = OutputType : : P2SH_SEGWIT | | type = = OutputType : : BECH32 ) ) {
CTxDestination witdest = WitnessV0KeyHash ( key . GetID ( ) ) ;
CScript witprog = GetScriptForDestination ( witdest ) ;
// Make sure the resulting program is solvable.
assert ( IsSolvable ( * this , witprog ) ) ;
AddCScript ( witprog ) ;
}
}
2019-10-07 15:11:34 -03:00
void LegacyScriptPubKeyMan : : LearnAllRelatedScripts ( const CPubKey & key )
2019-10-07 15:11:34 -03:00
{
// OutputType::P2SH_SEGWIT always adds all necessary scripts for all types.
LearnRelatedScripts ( key , OutputType : : P2SH_SEGWIT ) ;
}
2019-10-07 15:11:34 -03:00
void LegacyScriptPubKeyMan : : MarkReserveKeysAsUsed ( int64_t keypool_id )
2019-10-07 15:11:34 -03:00
{
AssertLockHeld ( cs_wallet ) ;
bool internal = setInternalKeyPool . count ( keypool_id ) ;
if ( ! internal ) assert ( setExternalKeyPool . count ( keypool_id ) | | set_pre_split_keypool . count ( keypool_id ) ) ;
std : : set < int64_t > * setKeyPool = internal ? & setInternalKeyPool : ( set_pre_split_keypool . empty ( ) ? & setExternalKeyPool : & set_pre_split_keypool ) ;
auto it = setKeyPool - > begin ( ) ;
2019-10-07 15:11:34 -03:00
WalletBatch batch ( m_storage . GetDatabase ( ) ) ;
2019-10-07 15:11:34 -03:00
while ( it ! = std : : end ( * setKeyPool ) ) {
const int64_t & index = * ( it ) ;
if ( index > keypool_id ) break ; // set*KeyPool is ordered
CKeyPool keypool ;
if ( batch . ReadPool ( index , keypool ) ) { //TODO: This should be unnecessary
m_pool_key_to_index . erase ( keypool . vchPubKey . GetID ( ) ) ;
}
LearnAllRelatedScripts ( keypool . vchPubKey ) ;
batch . ErasePool ( index ) ;
WalletLogPrintf ( " keypool index %d removed \n " , index ) ;
it = setKeyPool - > erase ( it ) ;
}
}
std : : vector < CKeyID > GetAffectedKeys ( const CScript & spk , const SigningProvider & provider )
{
std : : vector < CScript > dummy ;
FlatSigningProvider out ;
InferDescriptor ( spk , provider ) - > Expand ( 0 , DUMMY_SIGNING_PROVIDER , dummy , out ) ;
std : : vector < CKeyID > ret ;
for ( const auto & entry : out . pubkeys ) {
ret . push_back ( entry . first ) ;
}
return ret ;
}
2019-10-07 15:11:34 -03:00
void LegacyScriptPubKeyMan : : MarkPreSplitKeys ( )
2019-10-07 15:11:34 -03:00
{
2019-10-07 15:11:34 -03:00
WalletBatch batch ( m_storage . GetDatabase ( ) ) ;
2019-10-07 15:11:34 -03:00
for ( auto it = setExternalKeyPool . begin ( ) ; it ! = setExternalKeyPool . end ( ) ; ) {
int64_t index = * it ;
CKeyPool keypool ;
if ( ! batch . ReadPool ( index , keypool ) ) {
throw std : : runtime_error ( std : : string ( __func__ ) + " : read keypool entry failed " ) ;
}
keypool . m_pre_split = true ;
if ( ! batch . WritePool ( index , keypool ) ) {
throw std : : runtime_error ( std : : string ( __func__ ) + " : writing modified keypool entry failed " ) ;
}
set_pre_split_keypool . insert ( index ) ;
it = setExternalKeyPool . erase ( it ) ;
}
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : AddCScript ( const CScript & redeemScript )
2019-10-07 15:11:34 -03:00
{
2019-10-07 15:11:34 -03:00
WalletBatch batch ( m_storage . GetDatabase ( ) ) ;
2019-10-07 15:11:34 -03:00
return AddCScriptWithDB ( batch , redeemScript ) ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : AddCScriptWithDB ( WalletBatch & batch , const CScript & redeemScript )
2019-10-07 15:11:34 -03:00
{
if ( ! FillableSigningProvider : : AddCScript ( redeemScript ) )
return false ;
if ( batch . WriteCScript ( Hash160 ( redeemScript ) , redeemScript ) ) {
2019-10-29 18:53:35 -03:00
m_storage . UnsetBlankWalletFlag ( batch ) ;
2019-10-07 15:11:34 -03:00
return true ;
}
return false ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : AddKeyOriginWithDB ( WalletBatch & batch , const CPubKey & pubkey , const KeyOriginInfo & info )
2019-10-07 15:11:34 -03:00
{
LOCK ( cs_wallet ) ;
std : : copy ( info . fingerprint , info . fingerprint + 4 , mapKeyMetadata [ pubkey . GetID ( ) ] . key_origin . fingerprint ) ;
mapKeyMetadata [ pubkey . GetID ( ) ] . key_origin . path = info . path ;
mapKeyMetadata [ pubkey . GetID ( ) ] . has_key_origin = true ;
mapKeyMetadata [ pubkey . GetID ( ) ] . hdKeypath = WriteHDKeypath ( info . path ) ;
return batch . WriteKeyMetadata ( mapKeyMetadata [ pubkey . GetID ( ) ] , pubkey , true ) ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : ImportScripts ( const std : : set < CScript > scripts , int64_t timestamp )
2019-10-07 15:11:34 -03:00
{
2019-10-07 15:11:34 -03:00
WalletBatch batch ( m_storage . GetDatabase ( ) ) ;
2019-10-07 15:11:34 -03:00
for ( const auto & entry : scripts ) {
CScriptID id ( entry ) ;
if ( HaveCScript ( id ) ) {
WalletLogPrintf ( " Already have script %s, skipping \n " , HexStr ( entry ) ) ;
continue ;
}
if ( ! AddCScriptWithDB ( batch , entry ) ) {
return false ;
}
if ( timestamp > 0 ) {
m_script_metadata [ CScriptID ( entry ) ] . nCreateTime = timestamp ;
}
}
if ( timestamp > 0 ) {
UpdateTimeFirstKey ( timestamp ) ;
}
return true ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : ImportPrivKeys ( const std : : map < CKeyID , CKey > & privkey_map , const int64_t timestamp )
2019-10-07 15:11:34 -03:00
{
2019-10-07 15:11:34 -03:00
WalletBatch batch ( m_storage . GetDatabase ( ) ) ;
2019-10-07 15:11:34 -03:00
for ( const auto & entry : privkey_map ) {
const CKey & key = entry . second ;
CPubKey pubkey = key . GetPubKey ( ) ;
const CKeyID & id = entry . first ;
assert ( key . VerifyPubKey ( pubkey ) ) ;
// Skip if we already have the key
if ( HaveKey ( id ) ) {
WalletLogPrintf ( " Already have key with pubkey %s, skipping \n " , HexStr ( pubkey ) ) ;
continue ;
}
mapKeyMetadata [ id ] . nCreateTime = timestamp ;
// If the private key is not present in the wallet, insert it.
if ( ! AddKeyPubKeyWithDB ( batch , key , pubkey ) ) {
return false ;
}
UpdateTimeFirstKey ( timestamp ) ;
}
return true ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : ImportPubKeys ( const std : : vector < CKeyID > & ordered_pubkeys , const std : : map < CKeyID , CPubKey > & pubkey_map , const std : : map < CKeyID , std : : pair < CPubKey , KeyOriginInfo > > & key_origins , const bool add_keypool , const bool internal , const int64_t timestamp )
2019-10-07 15:11:34 -03:00
{
2019-10-07 15:11:34 -03:00
WalletBatch batch ( m_storage . GetDatabase ( ) ) ;
2019-10-07 15:11:34 -03:00
for ( const auto & entry : key_origins ) {
AddKeyOriginWithDB ( batch , entry . second . first , entry . second . second ) ;
}
for ( const CKeyID & id : ordered_pubkeys ) {
auto entry = pubkey_map . find ( id ) ;
if ( entry = = pubkey_map . end ( ) ) {
continue ;
}
const CPubKey & pubkey = entry - > second ;
CPubKey temp ;
if ( GetPubKey ( id , temp ) ) {
// Already have pubkey, skipping
WalletLogPrintf ( " Already have pubkey %s, skipping \n " , HexStr ( temp ) ) ;
continue ;
}
if ( ! AddWatchOnlyWithDB ( batch , GetScriptForRawPubKey ( pubkey ) , timestamp ) ) {
return false ;
}
mapKeyMetadata [ id ] . nCreateTime = timestamp ;
// Add to keypool only works with pubkeys
if ( add_keypool ) {
AddKeypoolPubkeyWithDB ( pubkey , internal , batch ) ;
NotifyCanGetAddressesChanged ( ) ;
}
}
return true ;
}
2019-10-07 15:11:34 -03:00
bool LegacyScriptPubKeyMan : : ImportScriptPubKeys ( const std : : set < CScript > & script_pub_keys , const bool have_solving_data , const int64_t timestamp )
2019-10-07 15:11:34 -03:00
{
2019-10-07 15:11:34 -03:00
WalletBatch batch ( m_storage . GetDatabase ( ) ) ;
2019-10-07 15:11:34 -03:00
for ( const CScript & script : script_pub_keys ) {
2019-10-07 15:11:34 -03:00
if ( ! have_solving_data | | ! IsMine ( script ) ) { // Always call AddWatchOnly for non-solvable watch-only, so that watch timestamp gets updated
2019-10-07 15:11:34 -03:00
if ( ! AddWatchOnlyWithDB ( batch , script , timestamp ) ) {
return false ;
}
}
}
return true ;
}
2019-10-07 15:11:34 -03:00
std : : set < CKeyID > LegacyScriptPubKeyMan : : GetKeys ( ) const
2019-10-07 15:11:34 -03:00
{
LOCK ( cs_KeyStore ) ;
if ( ! IsCrypted ( ) ) {
return FillableSigningProvider : : GetKeys ( ) ;
}
std : : set < CKeyID > set_address ;
for ( const auto & mi : mapCryptedKeys ) {
set_address . insert ( mi . first ) ;
}
return set_address ;
}
2019-10-07 15:11:34 -03:00
// Temporary CWallet accessors and aliases.
LegacyScriptPubKeyMan : : LegacyScriptPubKeyMan ( CWallet & wallet )
: ScriptPubKeyMan ( wallet ) ,
m_wallet ( wallet ) ,
cs_wallet ( wallet . cs_wallet ) ,
vMasterKey ( wallet . vMasterKey ) ,
fUseCrypto ( wallet . fUseCrypto ) ,
fDecryptionThoroughlyChecked ( wallet . fDecryptionThoroughlyChecked ) { }
bool LegacyScriptPubKeyMan : : SetCrypted ( ) { return m_wallet . SetCrypted ( ) ; }
bool LegacyScriptPubKeyMan : : IsCrypted ( ) const { return m_wallet . IsCrypted ( ) ; }
void LegacyScriptPubKeyMan : : NotifyWatchonlyChanged ( bool fHaveWatchOnly ) const { return m_wallet . NotifyWatchonlyChanged ( fHaveWatchOnly ) ; }
void LegacyScriptPubKeyMan : : NotifyCanGetAddressesChanged ( ) const { return m_wallet . NotifyCanGetAddressesChanged ( ) ; }
template < typename . . . Params > void LegacyScriptPubKeyMan : : WalletLogPrintf ( const std : : string & fmt , const Params & . . . parameters ) const { return m_wallet . WalletLogPrintf ( fmt , parameters . . . ) ; }