2020-04-16 13:14:08 -04:00
// Copyright (c) 2019-2020 The Bitcoin Core developers
2019-10-07 15:11:34 -03:00
// 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>
2020-02-10 21:49:31 -03:00
# include <script/sign.h>
2019-10-07 15:11:34 -03:00
# include <util/bip32.h>
# include <util/strencodings.h>
2020-02-13 01:01:45 -03:00
# include <util/string.h>
2019-10-07 15:11:34 -03:00
# include <util/translation.h>
# include <wallet/scriptpubkeyman.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
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_KeyStore ) ;
2019-10-07 15:11:34 -03:00
error . clear ( ) ;
// 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-12 07:27:56 -03:00
error = _ ( " Error: Keypool ran out, please call keypoolrefill first " ) . translated ;
2019-10-07 15:11:34 -03:00
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 ;
}
2020-02-04 19:24:48 -03:00
//! Recursively solve script and return spendable/watchonly/invalid status.
//!
//! @param keystore legacy key and script store
//! @param script script to solve
//! @param sigversion script type (top-level / redeemscript / witnessscript)
//! @param recurse_scripthash whether to recurse into nested p2sh and p2wsh
//! scripts or simply treat any script that has been
//! stored in the keystore as spendable
IsMineResult IsMineInner ( const LegacyScriptPubKeyMan & keystore , const CScript & scriptPubKey , IsMineSigVersion sigversion , bool recurse_scripthash = true )
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 ) ) {
2020-02-04 19:24:48 -03:00
ret = std : : max ( ret , recurse_scripthash ? IsMineInner ( keystore , subscript , IsMineSigVersion : : P2SH ) : IsMineResult : : SPENDABLE ) ;
2019-10-07 15:11:34 -03:00
}
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 ) ) {
2020-02-04 19:24:48 -03:00
ret = std : : max ( ret , recurse_scripthash ? IsMineInner ( keystore , subscript , IsMineSigVersion : : WITNESS_V0 ) : IsMineResult : : SPENDABLE ) ;
2019-10-07 15:11:34 -03:00
}
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 ) ;
}
2019-12-05 20:02:11 -03:00
bool LegacyScriptPubKeyMan : : CheckDecryptionKey ( const CKeyingMaterial & master_key , bool accept_no_keys )
2019-10-07 15:11:34 -03:00
{
{
LOCK ( cs_KeyStore ) ;
2019-12-05 20:14:53 -03:00
assert ( mapKeys . empty ( ) ) ;
2019-10-07 15:11:34 -03:00
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 ;
2019-12-05 20:02:11 -03:00
if ( ! DecryptKey ( master_key , vchCryptedSecret , vchPubKey , key ) )
2019-10-07 15:11:34 -03:00
{
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 ;
fDecryptionThoroughlyChecked = true ;
}
return true ;
}
2019-12-05 20:23:57 -03:00
bool LegacyScriptPubKeyMan : : Encrypt ( const CKeyingMaterial & master_key , WalletBatch * batch )
2019-10-07 15:11:34 -03:00
{
LOCK ( cs_KeyStore ) ;
2019-12-05 20:23:57 -03:00
encrypted_batch = batch ;
2019-12-05 20:14:53 -03:00
if ( ! mapCryptedKeys . empty ( ) ) {
2019-12-05 20:23:57 -03:00
encrypted_batch = nullptr ;
2019-10-07 15:11:34 -03:00
return false ;
2019-12-05 20:23:57 -03:00
}
2019-10-07 15:11:34 -03:00
2019-12-05 20:04:06 -03:00
KeyMap keys_to_encrypt ;
keys_to_encrypt . swap ( mapKeys ) ; // Clear mapKeys so AddCryptedKeyInner will succeed.
for ( const KeyMap : : value_type & mKey : keys_to_encrypt )
2019-10-07 15:11:34 -03:00
{
const CKey & key = mKey . second ;
CPubKey vchPubKey = key . GetPubKey ( ) ;
CKeyingMaterial vchSecret ( key . begin ( ) , key . end ( ) ) ;
std : : vector < unsigned char > vchCryptedSecret ;
2019-12-05 20:23:57 -03:00
if ( ! EncryptSecret ( master_key , vchSecret , vchPubKey . GetHash ( ) , vchCryptedSecret ) ) {
encrypted_batch = nullptr ;
2019-10-07 15:11:34 -03:00
return false ;
2019-12-05 20:23:57 -03:00
}
if ( ! AddCryptedKey ( vchPubKey , vchCryptedSecret ) ) {
encrypted_batch = nullptr ;
2019-10-07 15:11:34 -03:00
return false ;
2019-12-05 20:23:57 -03:00
}
2019-10-07 15:11:34 -03:00
}
2019-12-05 20:23:57 -03:00
encrypted_batch = nullptr ;
2019-10-07 15:11:34 -03:00
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
LOCK ( cs_KeyStore ) ;
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 )
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_KeyStore ) ;
2019-10-07 15:11:34 -03:00
// 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
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_KeyStore ) ;
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 ( ) ;
}
2020-03-02 04:35:58 -03:00
bool LegacyScriptPubKeyMan : : CanGetAddresses ( bool internal ) const
2019-10-07 15:11:34 -03:00
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_KeyStore ) ;
2019-10-07 15:11:34 -03:00
// 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 )
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_KeyStore ) ;
2019-10-07 15:11:34 -03:00
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
2019-10-07 15:11:34 -03:00
if ( m_storage . CanSupportFeature ( FEATURE_HD_SPLIT ) & & CHDChain : : VERSION_HD_CHAIN_SPLIT ) {
2019-10-07 15:11:34 -03:00
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 ( )
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_KeyStore ) ;
2019-10-07 15:11:34 -03:00
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 ;
}
2020-03-02 04:54:59 -03:00
int64_t LegacyScriptPubKeyMan : : GetOldestKeyPoolTime ( ) const
2019-10-07 15:11:34 -03:00
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_KeyStore ) ;
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
// 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 ;
}
2020-03-02 04:33:45 -03:00
size_t LegacyScriptPubKeyMan : : KeypoolCountExternalKeys ( ) const
2019-10-07 15:11:34 -03:00
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_KeyStore ) ;
2019-10-07 15:11:34 -03:00
return setExternalKeyPool . size ( ) + set_pre_split_keypool . size ( ) ;
}
2019-10-07 15:11:34 -03:00
unsigned int LegacyScriptPubKeyMan : : GetKeyPoolSize ( ) const
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_KeyStore ) ;
2019-10-07 15:11:34 -03:00
return setInternalKeyPool . size ( ) + setExternalKeyPool . size ( ) + set_pre_split_keypool . size ( ) ;
2019-10-07 15:11:34 -03:00
}
2019-10-07 15:11:34 -03:00
int64_t LegacyScriptPubKeyMan : : GetTimeFirstKey ( ) const
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_KeyStore ) ;
2019-10-07 15:11:34 -03:00
return nTimeFirstKey ;
}
2020-02-10 23:27:59 -03:00
std : : unique_ptr < SigningProvider > LegacyScriptPubKeyMan : : GetSolvingProvider ( const CScript & script ) const
2019-10-07 15:11:34 -03:00
{
2019-10-07 15:11:34 -03:00
return MakeUnique < LegacySigningProvider > ( * this ) ;
2019-10-07 15:11:34 -03:00
}
bool LegacyScriptPubKeyMan : : CanProvide ( const CScript & script , SignatureData & sigdata )
{
2020-02-04 19:24:48 -03:00
IsMineResult ismine = IsMineInner ( * this , script , IsMineSigVersion : : TOP , /* recurse_scripthash= */ false ) ;
if ( ismine = = IsMineResult : : SPENDABLE | | ismine = = IsMineResult : : WATCH_ONLY ) {
// If ismine, it means we recognize keys or script ids in the script, or
// are watching the script itself, and we can at least provide metadata
// or solving information, even if not able to sign fully.
2019-10-07 15:11:34 -03:00
return true ;
} else {
// If, given the stuff in sigdata, we could make a valid sigature, then we can provide for this script
ProduceSignature ( * this , DUMMY_SIGNATURE_CREATOR , script , sigdata ) ;
if ( ! sigdata . signatures . empty ( ) ) {
// If we could make signatures, make sure we have a private key to actually make a signature
bool has_privkeys = false ;
for ( const auto & key_sig_pair : sigdata . signatures ) {
has_privkeys | = HaveKey ( key_sig_pair . first ) ;
}
return has_privkeys ;
}
return false ;
}
}
2020-02-10 21:49:31 -03:00
bool LegacyScriptPubKeyMan : : SignTransaction ( CMutableTransaction & tx , const std : : map < COutPoint , Coin > & coins , int sighash , std : : map < int , std : : string > & input_errors ) const
{
return : : SignTransaction ( tx , this , coins , sighash , input_errors ) ;
}
2020-02-13 19:09:12 -03:00
SigningResult LegacyScriptPubKeyMan : : SignMessage ( const std : : string & message , const PKHash & pkhash , std : : string & str_sig ) const
{
CKeyID key_id ( pkhash ) ;
CKey key ;
if ( ! GetKey ( key_id , key ) ) {
return SigningResult : : PRIVATE_KEY_NOT_AVAILABLE ;
}
if ( MessageSign ( key , message , str_sig ) ) {
return SigningResult : : OK ;
}
return SigningResult : : SIGNING_FAILED ;
}
2020-02-10 21:50:59 -03:00
TransactionError LegacyScriptPubKeyMan : : FillPSBT ( PartiallySignedTransaction & psbtx , int sighash_type , bool sign , bool bip32derivs ) const
{
for ( unsigned int i = 0 ; i < psbtx . tx - > vin . size ( ) ; + + i ) {
const CTxIn & txin = psbtx . tx - > vin [ i ] ;
PSBTInput & input = psbtx . inputs . at ( i ) ;
if ( PSBTInputSigned ( input ) ) {
continue ;
}
// Verify input looks sane. This will check that we have at most one uxto, witness or non-witness.
if ( ! input . IsSane ( ) ) {
return TransactionError : : INVALID_PSBT ;
}
// Get the Sighash type
if ( sign & & input . sighash_type > 0 & & input . sighash_type ! = sighash_type ) {
return TransactionError : : SIGHASH_MISMATCH ;
}
// Check non_witness_utxo has specified prevout
if ( input . non_witness_utxo ) {
if ( txin . prevout . n > = input . non_witness_utxo - > vout . size ( ) ) {
return TransactionError : : MISSING_INPUTS ;
}
} else if ( input . witness_utxo . IsNull ( ) ) {
// There's no UTXO so we can just skip this now
continue ;
}
SignatureData sigdata ;
input . FillSignatureData ( sigdata ) ;
SignPSBTInput ( HidingSigningProvider ( this , ! sign , ! bip32derivs ) , psbtx , i , sighash_type ) ;
}
// Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change
for ( unsigned int i = 0 ; i < psbtx . tx - > vout . size ( ) ; + + i ) {
UpdatePSBTOutput ( HidingSigningProvider ( this , true , ! bip32derivs ) , psbtx , i ) ;
}
return TransactionError : : OK ;
}
2019-11-05 12:36:55 -03:00
const CKeyMetadata * LegacyScriptPubKeyMan : : GetMetadata ( const CTxDestination & dest ) const
2019-10-07 15:11:34 -03:00
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_KeyStore ) ;
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
uint256 LegacyScriptPubKeyMan : : GetID ( ) const
{
return UINT256_ONE ( ) ;
}
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
{
2019-10-07 15:11:34 -03:00
AssertLockHeld ( cs_KeyStore ) ;
2019-10-07 15:11:34 -03:00
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
LOCK ( cs_KeyStore ) ;
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-07 15:11:34 -03:00
AssertLockHeld ( cs_KeyStore ) ;
2019-10-29 13:21:57 -03:00
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 ) ;
}
2019-12-05 19:33:00 -03:00
if ( ! m_storage . HasEncryptionKeys ( ) ) {
2019-10-07 15:11:34 -03:00
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
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_KeyStore ) ;
2019-10-07 15:11:34 -03:00
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
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_KeyStore ) ;
2019-10-07 15:11:34 -03:00
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 ) ;
2019-12-05 19:33:00 -03:00
if ( ! m_storage . HasEncryptionKeys ( ) ) {
2019-10-07 15:11:34 -03:00
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 ( ) ) ;
2019-12-05 20:02:01 -03:00
if ( ! EncryptSecret ( m_storage . GetEncryptionKey ( ) , vchSecret , pubkey . GetHash ( ) , vchCryptedSecret ) ) {
2019-10-07 15:11:34 -03:00
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 ) ;
2019-12-05 20:14:53 -03:00
assert ( mapKeys . empty ( ) ) ;
2019-10-07 15:11:34 -03:00
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 ;
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_KeyStore ) ;
2019-10-07 15:11:34 -03:00
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
{
{
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
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_KeyStore ) ;
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 ) ;
2019-12-05 19:33:00 -03:00
if ( ! m_storage . HasEncryptionKeys ( ) ) {
2019-10-07 15:11:34 -03:00
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 ) ;
2019-12-05 19:33:00 -03:00
if ( ! m_storage . HasEncryptionKeys ( ) ) {
2019-10-07 15:11:34 -03:00
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 ;
2019-12-05 20:02:01 -03:00
return DecryptKey ( m_storage . GetEncryptionKey ( ) , vchCryptedSecret , vchPubKey , keyOut ) ;
2019-10-07 15:11:34 -03:00
}
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 ;
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_KeyStore ) ;
2019-10-07 15:11:34 -03:00
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 ) ;
2019-12-05 19:33:00 -03:00
if ( ! m_storage . HasEncryptionKeys ( ) ) {
2019-10-07 15:11:34 -03:00
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_KeyStore ) ;
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 ) ;
2020-02-13 01:01:45 -03:00
metadata . hdKeypath = " m/0'/1'/ " + ToString ( hdChain . nInternalChainCounter ) + " ' " ;
2019-10-07 15:11:34 -03:00
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 ) ;
2020-02-13 01:01:45 -03:00
metadata . hdKeypath = " m/0'/0'/ " + ToString ( hdChain . nExternalChainCounter ) + " ' " ;
2019-10-07 15:11:34 -03:00
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
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_KeyStore ) ;
2019-10-07 15:11:34 -03:00
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 ) ;
}
2020-03-02 04:23:04 -03:00
bool LegacyScriptPubKeyMan : : CanGenerateKeys ( ) const
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)
2019-10-07 15:11:34 -03:00
LOCK ( cs_KeyStore ) ;
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 ( ) ;
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_KeyStore ) ;
2019-10-07 15:11:34 -03:00
// 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
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_KeyStore ) ;
2019-10-07 15:11:34 -03:00
// 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 ;
}
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_KeyStore ) ;
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 ;
}
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_KeyStore ) ;
2019-10-07 15:11:34 -03:00
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
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_KeyStore ) ;
2019-10-07 15:11:34 -03:00
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-10-07 15:11:34 -03:00
void LegacyScriptPubKeyMan : : KeepDestination ( int64_t nIndex , const OutputType & type )
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
CPubKey pubkey ;
bool have_pk = GetPubKey ( m_index_to_reserved_key . at ( nIndex ) , pubkey ) ;
assert ( have_pk ) ;
2019-10-07 15:11:34 -03:00
LearnRelatedScripts ( pubkey , type ) ;
2019-10-07 15:11:34 -03:00
m_index_to_reserved_key . erase ( nIndex ) ;
2019-10-07 15:11:34 -03:00
WalletLogPrintf ( " keypool keep %d \n " , nIndex ) ;
}
2019-10-07 15:11:34 -03:00
void LegacyScriptPubKeyMan : : ReturnDestination ( int64_t nIndex , bool fInternal , const CTxDestination & )
2019-10-07 15:11:34 -03:00
{
// Return to key pool
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_KeyStore ) ;
2019-10-07 15:11:34 -03:00
if ( fInternal ) {
setInternalKeyPool . insert ( nIndex ) ;
} else if ( ! set_pre_split_keypool . empty ( ) ) {
set_pre_split_keypool . insert ( nIndex ) ;
} else {
setExternalKeyPool . insert ( nIndex ) ;
}
2019-10-07 15:11:34 -03:00
CKeyID & pubkey_id = m_index_to_reserved_key . at ( nIndex ) ;
m_pool_key_to_index [ pubkey_id ] = nIndex ;
m_index_to_reserved_key . erase ( nIndex ) ;
2019-10-07 15:11:34 -03:00
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 ;
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_KeyStore ) ;
2019-10-07 15:11:34 -03:00
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-10-07 15:11:34 -03:00
KeepDestination ( nIndex , type ) ;
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 ( ) ;
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_KeyStore ) ;
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 " ) ;
}
2019-10-07 15:11:34 -03:00
assert ( m_index_to_reserved_key . count ( nIndex ) = = 0 ) ;
m_index_to_reserved_key [ nIndex ] = keypool . vchPubKey . GetID ( ) ;
2019-10-07 15:11:34 -03:00
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
{
2019-10-07 15:11:34 -03:00
AssertLockHeld ( cs_KeyStore ) ;
2019-10-07 15:11:34 -03:00
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
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_KeyStore ) ;
2019-10-07 15:11:34 -03:00
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 ) ;
2019-12-05 19:33:00 -03:00
if ( ! m_storage . HasEncryptionKeys ( ) ) {
2019-10-07 15:11:34 -03:00
return FillableSigningProvider : : GetKeys ( ) ;
}
std : : set < CKeyID > set_address ;
for ( const auto & mi : mapCryptedKeys ) {
set_address . insert ( mi . first ) ;
}
return set_address ;
}
2019-07-16 13:34:35 -04:00
void LegacyScriptPubKeyMan : : SetType ( OutputType type , bool internal ) { }
2019-07-05 18:16:48 -04:00
bool DescriptorScriptPubKeyMan : : GetNewDestination ( const OutputType type , CTxDestination & dest , std : : string & error )
{
2019-07-10 18:01:40 -04:00
// Returns true if this descriptor supports getting new addresses. Conditions where we may be unable to fetch them (e.g. locked) are caught later
if ( ! CanGetAddresses ( m_internal ) ) {
error = " No addresses available " ;
return false ;
}
{
LOCK ( cs_desc_man ) ;
assert ( m_wallet_descriptor . descriptor - > IsSingleType ( ) ) ; // This is a combo descriptor which should not be an active descriptor
if ( type ! = m_address_type ) {
throw std : : runtime_error ( std : : string ( __func__ ) + " : Types are inconsistent " ) ;
}
TopUp ( ) ;
// Get the scriptPubKey from the descriptor
FlatSigningProvider out_keys ;
std : : vector < CScript > scripts_temp ;
if ( m_wallet_descriptor . range_end < = m_max_cached_index & & ! TopUp ( 1 ) ) {
// We can't generate anymore keys
error = " Error: Keypool ran out, please call keypoolrefill first " ;
return false ;
}
if ( ! m_wallet_descriptor . descriptor - > ExpandFromCache ( m_wallet_descriptor . next_index , m_wallet_descriptor . cache , scripts_temp , out_keys ) ) {
// We can't generate anymore keys
error = " Error: Keypool ran out, please call keypoolrefill first " ;
return false ;
}
Optional < OutputType > out_script_type = m_wallet_descriptor . descriptor - > GetOutputType ( ) ;
if ( out_script_type & & out_script_type = = type ) {
ExtractDestination ( scripts_temp [ 0 ] , dest ) ;
} else {
throw std : : runtime_error ( std : : string ( __func__ ) + " : Types are inconsistent. Stored type does not match type of newly generated address " ) ;
}
m_wallet_descriptor . next_index + + ;
WalletBatch ( m_storage . GetDatabase ( ) ) . WriteDescriptor ( GetID ( ) , m_wallet_descriptor ) ;
return true ;
}
2019-07-05 18:16:48 -04:00
}
isminetype DescriptorScriptPubKeyMan : : IsMine ( const CScript & script ) const
{
2019-07-05 18:33:55 -04:00
LOCK ( cs_desc_man ) ;
if ( m_map_script_pub_keys . count ( script ) > 0 ) {
return ISMINE_SPENDABLE ;
}
2019-07-05 18:16:48 -04:00
return ISMINE_NO ;
}
bool DescriptorScriptPubKeyMan : : CheckDecryptionKey ( const CKeyingMaterial & master_key , bool accept_no_keys )
{
return false ;
}
bool DescriptorScriptPubKeyMan : : Encrypt ( const CKeyingMaterial & master_key , WalletBatch * batch )
{
return false ;
}
bool DescriptorScriptPubKeyMan : : GetReservedDestination ( const OutputType type , bool internal , CTxDestination & address , int64_t & index , CKeyPool & keypool )
{
return false ;
}
void DescriptorScriptPubKeyMan : : ReturnDestination ( int64_t index , bool internal , const CTxDestination & addr )
{
}
2019-07-10 16:38:12 -04:00
std : : map < CKeyID , CKey > DescriptorScriptPubKeyMan : : GetKeys ( ) const
{
AssertLockHeld ( cs_desc_man ) ;
if ( m_storage . HasEncryptionKeys ( ) & & ! m_storage . IsLocked ( ) ) {
KeyMap keys ;
for ( auto key_pair : m_map_crypted_keys ) {
const CPubKey & pubkey = key_pair . second . first ;
const std : : vector < unsigned char > & crypted_secret = key_pair . second . second ;
CKey key ;
DecryptKey ( m_storage . GetEncryptionKey ( ) , crypted_secret , pubkey , key ) ;
keys [ pubkey . GetID ( ) ] = key ;
}
return keys ;
}
return m_map_keys ;
}
2019-07-05 18:16:48 -04:00
bool DescriptorScriptPubKeyMan : : TopUp ( unsigned int size )
{
2019-07-10 16:38:12 -04:00
LOCK ( cs_desc_man ) ;
unsigned int target_size ;
if ( size > 0 ) {
target_size = size ;
} else {
target_size = std : : max ( gArgs . GetArg ( " -keypool " , DEFAULT_KEYPOOL_SIZE ) , ( int64_t ) 1 ) ;
}
// Calculate the new range_end
int32_t new_range_end = std : : max ( m_wallet_descriptor . next_index + ( int32_t ) target_size , m_wallet_descriptor . range_end ) ;
// If the descriptor is not ranged, we actually just want to fill the first cache item
if ( ! m_wallet_descriptor . descriptor - > IsRange ( ) ) {
new_range_end = 1 ;
m_wallet_descriptor . range_end = 1 ;
m_wallet_descriptor . range_start = 0 ;
}
FlatSigningProvider provider ;
provider . keys = GetKeys ( ) ;
WalletBatch batch ( m_storage . GetDatabase ( ) ) ;
uint256 id = GetID ( ) ;
for ( int32_t i = m_max_cached_index + 1 ; i < new_range_end ; + + i ) {
FlatSigningProvider out_keys ;
std : : vector < CScript > scripts_temp ;
DescriptorCache temp_cache ;
// Maybe we have a cached xpub and we can expand from the cache first
if ( ! m_wallet_descriptor . descriptor - > ExpandFromCache ( i , m_wallet_descriptor . cache , scripts_temp , out_keys ) ) {
if ( ! m_wallet_descriptor . descriptor - > Expand ( i , provider , scripts_temp , out_keys , & temp_cache ) ) return false ;
}
// Add all of the scriptPubKeys to the scriptPubKey set
for ( const CScript & script : scripts_temp ) {
m_map_script_pub_keys [ script ] = i ;
}
// Write the cache
for ( const auto & parent_xpub_pair : temp_cache . GetCachedParentExtPubKeys ( ) ) {
CExtPubKey xpub ;
if ( m_wallet_descriptor . cache . GetCachedParentExtPubKey ( parent_xpub_pair . first , xpub ) ) {
if ( xpub ! = parent_xpub_pair . second ) {
throw std : : runtime_error ( std : : string ( __func__ ) + " : New cached parent xpub does not match already cached parent xpub " ) ;
}
continue ;
}
if ( ! batch . WriteDescriptorParentCache ( parent_xpub_pair . second , id , parent_xpub_pair . first ) ) {
throw std : : runtime_error ( std : : string ( __func__ ) + " : writing cache item failed " ) ;
}
m_wallet_descriptor . cache . CacheParentExtPubKey ( parent_xpub_pair . first , parent_xpub_pair . second ) ;
}
for ( const auto & derived_xpub_map_pair : temp_cache . GetCachedDerivedExtPubKeys ( ) ) {
for ( const auto & derived_xpub_pair : derived_xpub_map_pair . second ) {
CExtPubKey xpub ;
if ( m_wallet_descriptor . cache . GetCachedDerivedExtPubKey ( derived_xpub_map_pair . first , derived_xpub_pair . first , xpub ) ) {
if ( xpub ! = derived_xpub_pair . second ) {
throw std : : runtime_error ( std : : string ( __func__ ) + " : New cached derived xpub does not match already cached derived xpub " ) ;
}
continue ;
}
if ( ! batch . WriteDescriptorDerivedCache ( derived_xpub_pair . second , id , derived_xpub_map_pair . first , derived_xpub_pair . first ) ) {
throw std : : runtime_error ( std : : string ( __func__ ) + " : writing cache item failed " ) ;
}
m_wallet_descriptor . cache . CacheDerivedExtPubKey ( derived_xpub_map_pair . first , derived_xpub_pair . first , derived_xpub_pair . second ) ;
}
}
m_max_cached_index + + ;
}
m_wallet_descriptor . range_end = new_range_end ;
batch . WriteDescriptor ( GetID ( ) , m_wallet_descriptor ) ;
// By this point, the cache size should be the size of the entire range
assert ( m_wallet_descriptor . range_end - 1 = = m_max_cached_index ) ;
NotifyCanGetAddressesChanged ( ) ;
return true ;
2019-07-05 18:16:48 -04:00
}
void DescriptorScriptPubKeyMan : : MarkUnusedAddresses ( const CScript & script )
{
2019-07-08 16:25:10 -04:00
LOCK ( cs_desc_man ) ;
if ( IsMine ( script ) ) {
int32_t index = m_map_script_pub_keys [ script ] ;
if ( index > = m_wallet_descriptor . next_index ) {
WalletLogPrintf ( " %s: Detected a used keypool item at index %d, mark all keypool items up to this item as used \n " , __func__ , index ) ;
m_wallet_descriptor . next_index = index + 1 ;
}
if ( ! TopUp ( ) ) {
WalletLogPrintf ( " %s: Topping up keypool failed (locked wallet) \n " , __func__ ) ;
}
}
2019-07-05 18:16:48 -04:00
}
2019-07-10 16:02:43 -04:00
bool DescriptorScriptPubKeyMan : : AddDescriptorKeyWithDB ( WalletBatch & batch , const CKey & key , const CPubKey & pubkey )
{
AssertLockHeld ( cs_desc_man ) ;
assert ( ! m_storage . IsWalletFlagSet ( WALLET_FLAG_DISABLE_PRIVATE_KEYS ) ) ;
if ( m_storage . HasEncryptionKeys ( ) ) {
if ( m_storage . IsLocked ( ) ) {
return false ;
}
std : : vector < unsigned char > crypted_secret ;
CKeyingMaterial secret ( key . begin ( ) , key . end ( ) ) ;
if ( ! EncryptSecret ( m_storage . GetEncryptionKey ( ) , secret , pubkey . GetHash ( ) , crypted_secret ) ) {
return false ;
}
m_map_crypted_keys [ pubkey . GetID ( ) ] = make_pair ( pubkey , crypted_secret ) ;
return batch . WriteCryptedDescriptorKey ( GetID ( ) , pubkey , crypted_secret ) ;
} else {
m_map_keys [ pubkey . GetID ( ) ] = key ;
return batch . WriteDescriptorKey ( GetID ( ) , pubkey , key . GetPrivKey ( ) ) ;
}
}
2019-07-10 16:03:17 -04:00
bool DescriptorScriptPubKeyMan : : SetupDescriptorGeneration ( const CExtKey & master_key )
{
LOCK ( cs_desc_man ) ;
assert ( m_storage . IsWalletFlagSet ( WALLET_FLAG_DESCRIPTORS ) ) ;
// Ignore when there is already a descriptor
if ( m_wallet_descriptor . descriptor ) {
return false ;
}
int64_t creation_time = GetTime ( ) ;
std : : string xpub = EncodeExtPubKey ( master_key . Neuter ( ) ) ;
// Build descriptor string
std : : string desc_prefix ;
std : : string desc_suffix = " /*) " ;
switch ( m_address_type ) {
case OutputType : : LEGACY : {
desc_prefix = " pkh( " + xpub + " /44' " ;
break ;
}
case OutputType : : P2SH_SEGWIT : {
desc_prefix = " sh(wpkh( " + xpub + " /49' " ;
desc_suffix + = " ) " ;
break ;
}
case OutputType : : BECH32 : {
desc_prefix = " wpkh( " + xpub + " /84' " ;
break ;
}
default : assert ( false ) ;
}
// Mainnet derives at 0', testnet and regtest derive at 1'
if ( Params ( ) . IsTestChain ( ) ) {
desc_prefix + = " /1' " ;
} else {
desc_prefix + = " /0' " ;
}
std : : string internal_path = m_internal ? " /1 " : " /0 " ;
std : : string desc_str = desc_prefix + " /0' " + internal_path + desc_suffix ;
// Make the descriptor
FlatSigningProvider keys ;
std : : string error ;
std : : unique_ptr < Descriptor > desc = Parse ( desc_str , keys , error , false ) ;
WalletDescriptor w_desc ( std : : move ( desc ) , creation_time , 0 , 0 , 0 ) ;
m_wallet_descriptor = w_desc ;
// Store the master private key, and descriptor
WalletBatch batch ( m_storage . GetDatabase ( ) ) ;
if ( ! AddDescriptorKeyWithDB ( batch , master_key . key , master_key . key . GetPubKey ( ) ) ) {
throw std : : runtime_error ( std : : string ( __func__ ) + " : writing descriptor master private key failed " ) ;
}
if ( ! batch . WriteDescriptor ( GetID ( ) , m_wallet_descriptor ) ) {
throw std : : runtime_error ( std : : string ( __func__ ) + " : writing descriptor failed " ) ;
}
// TopUp
TopUp ( ) ;
m_storage . UnsetBlankWalletFlag ( batch ) ;
return true ;
}
2019-07-05 18:16:48 -04:00
bool DescriptorScriptPubKeyMan : : IsHDEnabled ( ) const
{
2019-07-08 17:05:05 -04:00
LOCK ( cs_desc_man ) ;
return m_wallet_descriptor . descriptor - > IsRange ( ) ;
2019-07-05 18:16:48 -04:00
}
bool DescriptorScriptPubKeyMan : : CanGetAddresses ( bool internal ) const
{
2019-07-09 19:34:39 -04:00
// We can only give out addresses from descriptors that are single type (not combo), ranged,
// and either have cached keys or can generate more keys (ignoring encryption)
LOCK ( cs_desc_man ) ;
return m_wallet_descriptor . descriptor - > IsSingleType ( ) & &
m_wallet_descriptor . descriptor - > IsRange ( ) & &
( HavePrivateKeys ( ) | | m_wallet_descriptor . next_index < m_wallet_descriptor . range_end ) ;
2019-07-05 18:16:48 -04:00
}
bool DescriptorScriptPubKeyMan : : HavePrivateKeys ( ) const
{
2019-07-09 19:34:39 -04:00
LOCK ( cs_desc_man ) ;
return m_map_keys . size ( ) > 0 | | m_map_crypted_keys . size ( ) > 0 ;
2019-07-05 18:16:48 -04:00
}
int64_t DescriptorScriptPubKeyMan : : GetOldestKeyPoolTime ( ) const
{
return GetTime ( ) ;
}
size_t DescriptorScriptPubKeyMan : : KeypoolCountExternalKeys ( ) const
{
2019-07-09 19:34:39 -04:00
if ( m_internal ) {
return 0 ;
}
return GetKeyPoolSize ( ) ;
2019-07-05 18:16:48 -04:00
}
unsigned int DescriptorScriptPubKeyMan : : GetKeyPoolSize ( ) const
{
2019-07-09 19:34:39 -04:00
LOCK ( cs_desc_man ) ;
return m_wallet_descriptor . range_end - m_wallet_descriptor . next_index ;
2019-07-05 18:16:48 -04:00
}
int64_t DescriptorScriptPubKeyMan : : GetTimeFirstKey ( ) const
{
2019-07-09 19:34:39 -04:00
LOCK ( cs_desc_man ) ;
return m_wallet_descriptor . creation_time ;
2019-07-05 18:16:48 -04:00
}
std : : unique_ptr < SigningProvider > DescriptorScriptPubKeyMan : : GetSolvingProvider ( const CScript & script ) const
{
return nullptr ;
}
bool DescriptorScriptPubKeyMan : : CanProvide ( const CScript & script , SignatureData & sigdata )
{
2019-07-05 18:33:55 -04:00
return IsMine ( script ) ;
2019-07-05 18:16:48 -04:00
}
bool DescriptorScriptPubKeyMan : : SignTransaction ( CMutableTransaction & tx , const std : : map < COutPoint , Coin > & coins , int sighash , std : : map < int , std : : string > & input_errors ) const
{
return false ;
}
SigningResult DescriptorScriptPubKeyMan : : SignMessage ( const std : : string & message , const PKHash & pkhash , std : : string & str_sig ) const
{
return SigningResult : : SIGNING_FAILED ;
}
TransactionError DescriptorScriptPubKeyMan : : FillPSBT ( PartiallySignedTransaction & psbt , int sighash_type , bool sign , bool bip32derivs ) const
{
return TransactionError : : INVALID_PSBT ;
}
const CKeyMetadata * DescriptorScriptPubKeyMan : : GetMetadata ( const CTxDestination & dest ) const
{
return nullptr ;
}
uint256 DescriptorScriptPubKeyMan : : GetID ( ) const
{
2019-07-08 17:28:17 -04:00
LOCK ( cs_desc_man ) ;
std : : string desc_str = m_wallet_descriptor . descriptor - > ToString ( ) ;
uint256 id ;
CSHA256 ( ) . Write ( ( unsigned char * ) desc_str . data ( ) , desc_str . size ( ) ) . Finalize ( id . begin ( ) ) ;
return id ;
2019-07-05 18:16:48 -04:00
}
2019-07-16 13:50:32 -04:00
void DescriptorScriptPubKeyMan : : SetType ( OutputType type , bool internal )
{
this - > m_address_type = type ;
this - > m_internal = internal ;
}
2019-07-09 13:40:34 -04:00
void DescriptorScriptPubKeyMan : : SetCache ( const DescriptorCache & cache )
{
LOCK ( cs_desc_man ) ;
m_wallet_descriptor . cache = cache ;
for ( int32_t i = m_wallet_descriptor . range_start ; i < m_wallet_descriptor . range_end ; + + i ) {
FlatSigningProvider out_keys ;
std : : vector < CScript > scripts_temp ;
if ( ! m_wallet_descriptor . descriptor - > ExpandFromCache ( i , m_wallet_descriptor . cache , scripts_temp , out_keys ) ) {
throw std : : runtime_error ( " Error: Unable to expand wallet descriptor from cache " ) ;
}
// Add all of the scriptPubKeys to the scriptPubKey set
for ( const CScript & script : scripts_temp ) {
if ( m_map_script_pub_keys . count ( script ) ! = 0 ) {
throw std : : runtime_error ( strprintf ( " Error: Already loaded script at index %d as being at index %d " , i , m_map_script_pub_keys [ script ] ) ) ;
}
m_map_script_pub_keys [ script ] = i ;
}
2019-07-10 16:38:12 -04:00
m_max_cached_index + + ;
2019-07-09 13:40:34 -04:00
}
}
2019-07-09 18:23:51 -04:00
bool DescriptorScriptPubKeyMan : : AddKey ( const CKeyID & key_id , const CKey & key )
{
LOCK ( cs_desc_man ) ;
m_map_keys [ key_id ] = key ;
return true ;
}
bool DescriptorScriptPubKeyMan : : AddCryptedKey ( const CKeyID & key_id , const CPubKey & pubkey , const std : : vector < unsigned char > & crypted_key )
{
LOCK ( cs_desc_man ) ;
if ( ! m_map_keys . empty ( ) ) {
return false ;
}
m_map_crypted_keys [ key_id ] = make_pair ( pubkey , crypted_key ) ;
return true ;
}