2011-08-09 07:27:58 -04:00
// Copyright (c) 2009-2010 Satoshi Nakamoto
2020-01-14 16:17:38 -03:00
// Copyright (c) 2009-2020 The Bitcoin Core developers
2014-10-26 04:03:12 -03:00
// Distributed under the MIT software license, see the accompanying
2012-05-18 10:02:28 -04:00
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2011-06-26 13:23:24 -04:00
2017-11-09 21:57:53 -03:00
# include <wallet/wallet.h>
2013-04-13 02:13:08 -03:00
2017-11-09 21:57:53 -03:00
# include <chain.h>
2021-09-10 23:29:00 -03:00
# include <consensus/amount.h>
2017-11-09 21:57:53 -03:00
# include <consensus/consensus.h>
# include <consensus/validation.h>
2021-05-13 10:19:44 -04:00
# include <external_signer.h>
2017-11-09 21:57:53 -03:00
# include <fs.h>
2017-07-26 10:23:01 -04:00
# include <interfaces/chain.h>
2018-04-08 15:37:50 -03:00
# include <interfaces/wallet.h>
2017-11-09 21:57:53 -03:00
# include <key.h>
2017-09-19 22:12:25 -03:00
# include <key_io.h>
2019-10-31 06:27:47 -03:00
# include <outputtype.h>
2017-11-09 21:57:53 -03:00
# include <policy/fees.h>
# include <policy/policy.h>
# include <primitives/block.h>
# include <primitives/transaction.h>
2019-08-04 17:26:01 -04:00
# include <psbt.h>
2018-11-10 17:29:07 -03:00
# include <script/descriptor.h>
2017-11-09 21:57:53 -03:00
# include <script/script.h>
2019-06-06 16:52:24 -04:00
# include <script/signingprovider.h>
2020-05-15 09:23:55 -04:00
# include <txmempool.h>
2018-11-06 11:23:43 -03:00
# include <util/bip32.h>
2020-02-24 16:34:17 -03:00
# include <util/check.h>
2019-04-02 18:03:37 -03:00
# include <util/error.h>
# include <util/fees.h>
2018-10-22 19:51:11 -03:00
# include <util/moneystr.h>
2019-04-02 18:03:37 -03:00
# include <util/rbf.h>
2019-08-19 18:12:35 -04:00
# include <util/string.h>
2019-06-17 03:56:52 -04:00
# include <util/translation.h>
2019-04-17 13:27:02 -04:00
# include <wallet/coincontrol.h>
2020-05-28 13:06:43 -04:00
# include <wallet/context.h>
2017-11-09 21:57:53 -03:00
# include <wallet/fees.h>
2020-02-19 12:40:00 -03:00
# include <wallet/external_signer_scriptpubkeyman.h>
2013-04-13 02:13:08 -03:00
2020-08-17 15:21:50 -04:00
# include <univalue.h>
2018-04-17 14:22:23 -03:00
# include <algorithm>
2014-10-01 03:50:24 -03:00
# include <assert.h>
2021-03-15 00:59:05 -03:00
# include <optional>
2014-10-01 03:50:24 -03:00
2012-11-03 11:58:41 -03:00
# include <boost/algorithm/string/replace.hpp>
2011-06-26 13:23:24 -04:00
2020-02-24 16:34:17 -03:00
using interfaces : : FoundBlock ;
2018-07-25 03:24:55 -04:00
const std : : map < uint64_t , std : : string > WALLET_FLAG_CAVEATS {
{ WALLET_FLAG_AVOID_REUSE ,
" You need to rescan the blockchain in order to correctly mark used "
" destinations in the past. Until this is done, some destinations may "
" be considered unused, even if the opposite is the case. "
} ,
} ;
2020-08-17 15:21:50 -04:00
bool AddWalletSetting ( interfaces : : Chain & chain , const std : : string & wallet_name )
{
util : : SettingsValue setting_value = chain . getRwSetting ( " wallet " ) ;
if ( ! setting_value . isArray ( ) ) setting_value . setArray ( ) ;
for ( const util : : SettingsValue & value : setting_value . getValues ( ) ) {
if ( value . isStr ( ) & & value . get_str ( ) = = wallet_name ) return true ;
}
setting_value . push_back ( wallet_name ) ;
return chain . updateRwSetting ( " wallet " , setting_value ) ;
}
bool RemoveWalletSetting ( interfaces : : Chain & chain , const std : : string & wallet_name )
{
util : : SettingsValue setting_value = chain . getRwSetting ( " wallet " ) ;
if ( ! setting_value . isArray ( ) ) return true ;
util : : SettingsValue new_value ( util : : SettingsValue : : VARR ) ;
for ( const util : : SettingsValue & value : setting_value . getValues ( ) ) {
if ( ! value . isStr ( ) | | value . get_str ( ) ! = wallet_name ) new_value . push_back ( value ) ;
}
if ( new_value . size ( ) = = setting_value . size ( ) ) return true ;
return chain . updateRwSetting ( " wallet " , new_value ) ;
}
static void UpdateWalletSetting ( interfaces : : Chain & chain ,
const std : : string & wallet_name ,
2021-03-14 23:41:30 -03:00
std : : optional < bool > load_on_startup ,
2020-08-17 15:21:50 -04:00
std : : vector < bilingual_str > & warnings )
{
2021-03-15 00:59:05 -03:00
if ( ! load_on_startup ) return ;
2020-06-05 17:02:44 -04:00
if ( load_on_startup . value ( ) & & ! AddWalletSetting ( chain , wallet_name ) ) {
2020-08-17 15:21:50 -04:00
warnings . emplace_back ( Untranslated ( " Wallet load on startup setting could not be updated, so wallet may not be loaded next node startup. " ) ) ;
2020-06-05 17:02:44 -04:00
} else if ( ! load_on_startup . value ( ) & & ! RemoveWalletSetting ( chain , wallet_name ) ) {
2020-08-17 15:21:50 -04:00
warnings . emplace_back ( Untranslated ( " Wallet load on startup setting could not be updated, so wallet may still be loaded next node startup. " ) ) ;
}
}
2021-06-28 05:15:12 -04:00
/**
* Refresh mempool status so the wallet is in an internally consistent state and
* immediately knows the transaction ' s status : Whether it can be considered
* trusted and is eligible to be abandoned . . .
*/
static void RefreshMempoolStatus ( CWalletTx & tx , interfaces : : Chain & chain )
{
tx . fInMempool = chain . isInMempool ( tx . GetHash ( ) ) ;
}
2020-05-28 13:06:43 -04:00
bool AddWallet ( WalletContext & context , const std : : shared_ptr < CWallet > & wallet )
2018-04-17 14:22:23 -03:00
{
2020-05-28 13:06:43 -04:00
LOCK ( context . wallets_mutex ) ;
2018-04-17 14:22:23 -03:00
assert ( wallet ) ;
2020-05-28 13:06:43 -04:00
std : : vector < std : : shared_ptr < CWallet > > : : const_iterator i = std : : find ( context . wallets . begin ( ) , context . wallets . end ( ) , wallet ) ;
if ( i ! = context . wallets . end ( ) ) return false ;
context . wallets . push_back ( wallet ) ;
2019-10-07 15:11:34 -03:00
wallet - > ConnectScriptPubKeyManNotifiers ( ) ;
2020-10-23 13:24:24 -03:00
wallet - > NotifyCanGetAddressesChanged ( ) ;
2018-04-17 14:22:23 -03:00
return true ;
}
2020-05-28 13:06:43 -04:00
bool RemoveWallet ( WalletContext & context , const std : : shared_ptr < CWallet > & wallet , std : : optional < bool > load_on_start , std : : vector < bilingual_str > & warnings )
2018-04-17 14:22:23 -03:00
{
assert ( wallet ) ;
2020-08-17 15:21:50 -04:00
interfaces : : Chain & chain = wallet - > chain ( ) ;
std : : string name = wallet - > GetName ( ) ;
2020-03-10 16:46:20 -03:00
// Unregister with the validation interface which also drops shared ponters.
wallet - > m_chain_notifications_handler . reset ( ) ;
2020-05-28 13:06:43 -04:00
LOCK ( context . wallets_mutex ) ;
std : : vector < std : : shared_ptr < CWallet > > : : iterator i = std : : find ( context . wallets . begin ( ) , context . wallets . end ( ) , wallet ) ;
if ( i = = context . wallets . end ( ) ) return false ;
context . wallets . erase ( i ) ;
2020-08-17 15:21:50 -04:00
// Write the wallet setting
UpdateWalletSetting ( chain , name , load_on_start , warnings ) ;
2018-04-17 14:22:23 -03:00
return true ;
}
2020-05-28 13:06:43 -04:00
bool RemoveWallet ( WalletContext & context , const std : : shared_ptr < CWallet > & wallet , std : : optional < bool > load_on_start )
2020-08-17 15:21:50 -04:00
{
std : : vector < bilingual_str > warnings ;
2020-05-28 13:06:43 -04:00
return RemoveWallet ( context , wallet , load_on_start , warnings ) ;
2020-08-17 15:21:50 -04:00
}
2020-05-28 13:06:43 -04:00
std : : vector < std : : shared_ptr < CWallet > > GetWallets ( WalletContext & context )
2018-04-17 14:22:23 -03:00
{
2020-05-28 13:06:43 -04:00
LOCK ( context . wallets_mutex ) ;
return context . wallets ;
2018-04-17 14:22:23 -03:00
}
2020-05-28 13:06:43 -04:00
std : : shared_ptr < CWallet > GetWallet ( WalletContext & context , const std : : string & name )
2018-04-17 14:22:23 -03:00
{
2020-05-28 13:06:43 -04:00
LOCK ( context . wallets_mutex ) ;
for ( const std : : shared_ptr < CWallet > & wallet : context . wallets ) {
2018-04-17 14:22:23 -03:00
if ( wallet - > GetName ( ) = = name ) return wallet ;
}
return nullptr ;
}
2020-05-28 13:06:43 -04:00
std : : unique_ptr < interfaces : : Handler > HandleLoadWallet ( WalletContext & context , LoadWalletFn load_wallet )
2019-09-27 08:31:44 -03:00
{
2020-05-28 13:06:43 -04:00
LOCK ( context . wallets_mutex ) ;
auto it = context . wallet_load_fns . emplace ( context . wallet_load_fns . end ( ) , std : : move ( load_wallet ) ) ;
return interfaces : : MakeHandler ( [ & context , it ] { LOCK ( context . wallets_mutex ) ; context . wallet_load_fns . erase ( it ) ; } ) ;
2019-09-27 08:31:44 -03:00
}
2020-06-17 05:47:29 -04:00
static Mutex g_loading_wallet_mutex ;
2018-12-12 20:21:19 -03:00
static Mutex g_wallet_release_mutex ;
static std : : condition_variable g_wallet_release_cv ;
2020-06-17 05:47:29 -04:00
static std : : set < std : : string > g_loading_wallet_set GUARDED_BY ( g_loading_wallet_mutex ) ;
static std : : set < std : : string > g_unloading_wallet_set GUARDED_BY ( g_wallet_release_mutex ) ;
2018-12-12 20:21:19 -03:00
2018-04-28 18:36:43 -03:00
// Custom deleter for shared_ptr<CWallet>.
static void ReleaseWallet ( CWallet * wallet )
{
2019-08-24 21:07:04 -04:00
const std : : string name = wallet - > GetName ( ) ;
2018-06-15 19:02:52 -04:00
wallet - > WalletLogPrintf ( " Releasing wallet \n " ) ;
2018-04-28 18:36:43 -03:00
wallet - > Flush ( ) ;
delete wallet ;
2018-12-12 20:21:19 -03:00
// Wallet is now released, notify UnloadWallet, if any.
{
LOCK ( g_wallet_release_mutex ) ;
2019-08-24 21:07:04 -04:00
if ( g_unloading_wallet_set . erase ( name ) = = 0 ) {
2018-12-12 20:21:19 -03:00
// UnloadWallet was not called for this wallet, all done.
return ;
}
}
g_wallet_release_cv . notify_all ( ) ;
}
void UnloadWallet ( std : : shared_ptr < CWallet > & & wallet )
{
// Mark wallet for unloading.
2019-08-24 21:07:04 -04:00
const std : : string name = wallet - > GetName ( ) ;
2018-12-12 20:21:19 -03:00
{
LOCK ( g_wallet_release_mutex ) ;
2019-08-24 21:07:04 -04:00
auto it = g_unloading_wallet_set . insert ( name ) ;
2018-12-12 20:21:19 -03:00
assert ( it . second ) ;
}
// The wallet can be in use so it's not possible to explicitly unload here.
// Notify the unload intent so that all remaining shared pointers are
// released.
2019-08-24 21:07:04 -04:00
wallet - > NotifyUnload ( ) ;
2020-03-10 16:46:20 -03:00
2018-12-12 20:21:19 -03:00
// Time to ditch our shared_ptr and wait for ReleaseWallet call.
wallet . reset ( ) ;
{
WAIT_LOCK ( g_wallet_release_mutex , lock ) ;
2019-08-24 21:07:04 -04:00
while ( g_unloading_wallet_set . count ( name ) = = 1 ) {
2018-12-12 20:21:19 -03:00
g_wallet_release_cv . wait ( lock ) ;
}
}
2018-04-28 18:36:43 -03:00
}
2020-06-17 05:47:29 -04:00
namespace {
2020-05-28 13:06:43 -04:00
std : : shared_ptr < CWallet > LoadWalletInternal ( WalletContext & context , const std : : string & name , std : : optional < bool > load_on_start , const DatabaseOptions & options , DatabaseStatus & status , bilingual_str & error , std : : vector < bilingual_str > & warnings )
2019-01-12 08:47:04 -03:00
{
2020-03-27 23:14:08 -03:00
try {
2020-08-04 20:45:28 -04:00
std : : unique_ptr < WalletDatabase > database = MakeWalletDatabase ( name , options , status , error ) ;
if ( ! database ) {
2020-04-21 07:53:24 -04:00
error = Untranslated ( " Wallet file verification failed. " ) + Untranslated ( " " ) + error ;
2020-03-27 23:14:08 -03:00
return nullptr ;
}
2019-01-12 08:47:04 -03:00
2020-05-28 13:06:43 -04:00
context . chain - > initMessage ( _ ( " Loading wallet… " ) . translated ) ;
std : : shared_ptr < CWallet > wallet = CWallet : : Create ( context , name , std : : move ( database ) , options . create_flags , error , warnings ) ;
2020-03-27 23:14:08 -03:00
if ( ! wallet ) {
2020-04-21 07:53:24 -04:00
error = Untranslated ( " Wallet loading failed. " ) + Untranslated ( " " ) + error ;
2020-09-08 13:11:20 -03:00
status = DatabaseStatus : : FAILED_LOAD ;
2020-03-27 23:14:08 -03:00
return nullptr ;
}
2020-05-28 13:06:43 -04:00
AddWallet ( context , wallet ) ;
2020-03-27 23:14:08 -03:00
wallet - > postInitProcess ( ) ;
2020-08-17 15:21:50 -04:00
// Write the wallet setting
2020-05-28 13:06:43 -04:00
UpdateWalletSetting ( * context . chain , name , load_on_start , warnings ) ;
2020-08-17 15:21:50 -04:00
2020-03-27 23:14:08 -03:00
return wallet ;
} catch ( const std : : runtime_error & e ) {
2019-08-19 18:12:35 -04:00
error = Untranslated ( e . what ( ) ) ;
2020-09-08 13:11:20 -03:00
status = DatabaseStatus : : FAILED_LOAD ;
2019-01-12 08:47:04 -03:00
return nullptr ;
}
}
2020-06-17 05:47:29 -04:00
} // namespace
2020-05-28 13:06:43 -04:00
std : : shared_ptr < CWallet > LoadWallet ( WalletContext & context , const std : : string & name , std : : optional < bool > load_on_start , const DatabaseOptions & options , DatabaseStatus & status , bilingual_str & error , std : : vector < bilingual_str > & warnings )
2020-06-17 05:47:29 -04:00
{
2020-07-28 19:25:14 -04:00
auto result = WITH_LOCK ( g_loading_wallet_mutex , return g_loading_wallet_set . insert ( name ) ) ;
2020-06-17 05:47:29 -04:00
if ( ! result . second ) {
2021-02-03 19:19:11 -03:00
error = Untranslated ( " Wallet already loading. " ) ;
2020-09-08 13:11:20 -03:00
status = DatabaseStatus : : FAILED_LOAD ;
2020-06-17 05:47:29 -04:00
return nullptr ;
}
2020-05-28 13:06:43 -04:00
auto wallet = LoadWalletInternal ( context , name , load_on_start , options , status , error , warnings ) ;
2020-06-17 05:47:29 -04:00
WITH_LOCK ( g_loading_wallet_mutex , g_loading_wallet_set . erase ( result . first ) ) ;
return wallet ;
}
2019-01-12 08:47:04 -03:00
2020-05-28 13:06:43 -04:00
std : : shared_ptr < CWallet > CreateWallet ( WalletContext & context , const std : : string & name , std : : optional < bool > load_on_start , DatabaseOptions & options , DatabaseStatus & status , bilingual_str & error , std : : vector < bilingual_str > & warnings )
2019-05-24 17:13:13 -04:00
{
2020-08-04 17:55:13 -04:00
uint64_t wallet_creation_flags = options . create_flags ;
const SecureString & passphrase = options . create_passphrase ;
2020-05-26 20:54:13 -04:00
if ( wallet_creation_flags & WALLET_FLAG_DESCRIPTORS ) options . require_format = DatabaseFormat : : SQLITE ;
2019-05-24 17:13:13 -04:00
// Indicate that the wallet is actually supposed to be blank and not just blank to make it encrypted
bool create_blank = ( wallet_creation_flags & WALLET_FLAG_BLANK_WALLET ) ;
// Born encrypted wallets need to be created blank first.
if ( ! passphrase . empty ( ) ) {
wallet_creation_flags | = WALLET_FLAG_BLANK_WALLET ;
}
2019-08-04 11:55:31 -04:00
// Private keys must be disabled for an external signer wallet
if ( ( wallet_creation_flags & WALLET_FLAG_EXTERNAL_SIGNER ) & & ! ( wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS ) ) {
error = Untranslated ( " Private keys must be disabled when using an external signer " ) ;
status = DatabaseStatus : : FAILED_CREATE ;
return nullptr ;
}
// Descriptor support must be enabled for an external signer wallet
if ( ( wallet_creation_flags & WALLET_FLAG_EXTERNAL_SIGNER ) & & ! ( wallet_creation_flags & WALLET_FLAG_DESCRIPTORS ) ) {
error = Untranslated ( " Descriptor support must be enabled when using an external signer " ) ;
status = DatabaseStatus : : FAILED_CREATE ;
return nullptr ;
}
2019-05-24 17:13:13 -04:00
// Wallet::Verify will check if we're trying to create a wallet with a duplicate name.
2020-08-04 20:45:28 -04:00
std : : unique_ptr < WalletDatabase > database = MakeWalletDatabase ( name , options , status , error ) ;
if ( ! database ) {
2019-08-19 18:12:35 -04:00
error = Untranslated ( " Wallet file verification failed. " ) + Untranslated ( " " ) + error ;
2020-08-04 17:55:13 -04:00
status = DatabaseStatus : : FAILED_VERIFY ;
return nullptr ;
2019-07-10 17:51:39 -04:00
}
// Do not allow a passphrase when private keys are disabled
if ( ! passphrase . empty ( ) & & ( wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS ) ) {
2019-08-19 18:12:35 -04:00
error = Untranslated ( " Passphrase provided but private keys are disabled. A passphrase is only used to encrypt private keys, so cannot be used for wallets with private keys disabled. " ) ;
2020-08-04 17:55:13 -04:00
status = DatabaseStatus : : FAILED_CREATE ;
return nullptr ;
2019-05-24 17:13:13 -04:00
}
// Make the wallet
2020-05-28 13:06:43 -04:00
context . chain - > initMessage ( _ ( " Loading wallet… " ) . translated ) ;
std : : shared_ptr < CWallet > wallet = CWallet : : Create ( context , name , std : : move ( database ) , wallet_creation_flags , error , warnings ) ;
2019-05-24 17:13:13 -04:00
if ( ! wallet ) {
2019-08-19 18:12:35 -04:00
error = Untranslated ( " Wallet creation failed. " ) + Untranslated ( " " ) + error ;
2020-08-04 17:55:13 -04:00
status = DatabaseStatus : : FAILED_CREATE ;
return nullptr ;
2019-05-24 17:13:13 -04:00
}
// Encrypt the wallet
if ( ! passphrase . empty ( ) & & ! ( wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS ) ) {
if ( ! wallet - > EncryptWallet ( passphrase ) ) {
2019-08-19 18:12:35 -04:00
error = Untranslated ( " Error: Wallet created but failed to encrypt. " ) ;
2020-08-04 17:55:13 -04:00
status = DatabaseStatus : : FAILED_ENCRYPT ;
return nullptr ;
2019-05-24 17:13:13 -04:00
}
if ( ! create_blank ) {
// Unlock the wallet
if ( ! wallet - > Unlock ( passphrase ) ) {
2019-08-19 18:12:35 -04:00
error = Untranslated ( " Error: Wallet was encrypted but could not be unlocked " ) ;
2020-08-04 17:55:13 -04:00
status = DatabaseStatus : : FAILED_ENCRYPT ;
return nullptr ;
2019-05-24 17:13:13 -04:00
}
// Set a seed for the wallet
2019-10-07 15:11:34 -03:00
{
2019-10-07 15:11:34 -03:00
LOCK ( wallet - > cs_wallet ) ;
2019-07-11 18:21:21 -04:00
if ( wallet - > IsWalletFlagSet ( WALLET_FLAG_DESCRIPTORS ) ) {
wallet - > SetupDescriptorScriptPubKeyMans ( ) ;
} else {
for ( auto spk_man : wallet - > GetActiveScriptPubKeyMans ( ) ) {
if ( ! spk_man - > SetupGeneration ( ) ) {
2019-08-19 18:12:35 -04:00
error = Untranslated ( " Unable to generate initial keys " ) ;
2020-08-04 17:55:13 -04:00
status = DatabaseStatus : : FAILED_CREATE ;
return nullptr ;
2019-07-11 18:21:21 -04:00
}
2019-10-07 15:11:34 -03:00
}
}
}
2019-05-24 17:13:13 -04:00
// Relock the wallet
wallet - > Lock ( ) ;
}
}
2020-05-28 13:06:43 -04:00
AddWallet ( context , wallet ) ;
2019-05-24 17:13:13 -04:00
wallet - > postInitProcess ( ) ;
2020-08-17 15:21:50 -04:00
// Write the wallet settings
2020-05-28 13:06:43 -04:00
UpdateWalletSetting ( * context . chain , name , load_on_start , warnings ) ;
2020-08-17 15:21:50 -04:00
2020-08-04 17:55:13 -04:00
status = DatabaseStatus : : SUCCESS ;
return wallet ;
2019-05-24 17:13:13 -04:00
}
2014-10-26 04:03:12 -03:00
/** @defgroup mapWallet
*
* @ {
*/
2011-06-26 13:23:24 -04:00
2014-02-15 18:38:28 -03:00
const CWalletTx * CWallet : : GetWalletTx ( const uint256 & hash ) const
{
2020-06-15 17:57:57 -04:00
AssertLockHeld ( cs_wallet ) ;
2014-02-15 18:38:28 -03:00
std : : map < uint256 , CWalletTx > : : const_iterator it = mapWallet . find ( hash ) ;
if ( it = = mapWallet . end ( ) )
2017-08-07 01:36:37 -04:00
return nullptr ;
2014-02-15 18:38:28 -03:00
return & ( it - > second ) ;
}
2019-10-07 15:11:34 -03:00
void CWallet : : UpgradeKeyMetadata ( )
{
2019-10-07 15:11:34 -03:00
if ( IsLocked ( ) | | IsWalletFlagSet ( WALLET_FLAG_KEY_ORIGIN_METADATA ) ) {
return ;
}
2019-11-04 13:00:26 -03:00
auto spk_man = GetLegacyScriptPubKeyMan ( ) ;
if ( ! spk_man ) {
return ;
2019-10-07 15:11:34 -03:00
}
2019-11-04 13:00:26 -03:00
spk_man - > UpgradeKeyMetadata ( ) ;
2019-10-07 15:11:34 -03:00
SetWalletFlag ( WALLET_FLAG_KEY_ORIGIN_METADATA ) ;
2019-10-07 15:11:34 -03:00
}
2021-03-01 19:03:52 -03:00
void CWallet : : UpgradeDescriptorCache ( )
{
if ( ! IsWalletFlagSet ( WALLET_FLAG_DESCRIPTORS ) | | IsLocked ( ) | | IsWalletFlagSet ( WALLET_FLAG_LAST_HARDENED_XPUB_CACHED ) ) {
return ;
}
for ( ScriptPubKeyMan * spkm : GetAllScriptPubKeyMans ( ) ) {
DescriptorScriptPubKeyMan * desc_spkm = dynamic_cast < DescriptorScriptPubKeyMan * > ( spkm ) ;
desc_spkm - > UpgradeDescriptorCache ( ) ;
}
SetWalletFlag ( WALLET_FLAG_LAST_HARDENED_XPUB_CACHED ) ;
}
2016-09-16 11:45:36 -03:00
bool CWallet : : Unlock ( const SecureString & strWalletPassphrase , bool accept_no_keys )
Add wallet privkey encryption.
This commit adds support for ckeys, or enCrypted private keys, to the wallet.
All keys are stored in memory in their encrypted form and thus the passphrase
is required from the user to spend coins, or to create new addresses.
Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
a random salt.
By default, the user's wallet remains unencrypted until they call the RPC
command encryptwallet <passphrase> or, from the GUI menu, Options->
Encrypt Wallet.
When the user is attempting to call RPC functions which require the password
to unlock the wallet, an error will be returned unless they call
walletpassphrase <passphrase> <time to keep key in memory> first.
A keypoolrefill command has been added which tops up the users keypool
(requiring the passphrase via walletpassphrase first).
keypoolsize has been added to the output of getinfo to show the user the
number of keys left before they need to specify their passphrase (and call
keypoolrefill).
Note that walletpassphrase will automatically fill keypool in a separate
thread which it spawns when the passphrase is set. This could cause some
delays in other threads waiting for locks on the wallet passphrase, including
one which could cause the passphrase to be stored longer than expected,
however it will not allow the passphrase to be used longer than expected as
ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
as the specified lock time has arrived.
When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
returns vchDefaultKey, meaning miners may start to generate many blocks to
vchDefaultKey instead of a new key each time.
A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
allow the user to change their password via RPC.
Whenever keying material (unencrypted private keys, the user's passphrase,
the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
is made to mlock/VirtualLock that memory before storing the keying material.
This is not true in several (commented) cases where mlock/VirtualLocking the
memory is not possible.
Although encryption of private keys in memory can be very useful on desktop
systems (as some small amount of protection against stupid viruses), on an
RPC server, the password is entered fairly insecurely. Thus, the only main
advantage encryption has for RPC servers is for RPC servers that do not spend
coins, except in rare cases, eg. a webserver of a merchant which only receives
payment except for cases of manual intervention.
Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
for all their input.
Conflicts:
src/wallet.cpp
2011-07-08 09:47:35 -04:00
{
2011-08-26 15:37:23 -03:00
CCrypter crypter ;
2016-11-10 04:00:05 -03:00
CKeyingMaterial _vMasterKey ;
Add wallet privkey encryption.
This commit adds support for ckeys, or enCrypted private keys, to the wallet.
All keys are stored in memory in their encrypted form and thus the passphrase
is required from the user to spend coins, or to create new addresses.
Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
a random salt.
By default, the user's wallet remains unencrypted until they call the RPC
command encryptwallet <passphrase> or, from the GUI menu, Options->
Encrypt Wallet.
When the user is attempting to call RPC functions which require the password
to unlock the wallet, an error will be returned unless they call
walletpassphrase <passphrase> <time to keep key in memory> first.
A keypoolrefill command has been added which tops up the users keypool
(requiring the passphrase via walletpassphrase first).
keypoolsize has been added to the output of getinfo to show the user the
number of keys left before they need to specify their passphrase (and call
keypoolrefill).
Note that walletpassphrase will automatically fill keypool in a separate
thread which it spawns when the passphrase is set. This could cause some
delays in other threads waiting for locks on the wallet passphrase, including
one which could cause the passphrase to be stored longer than expected,
however it will not allow the passphrase to be used longer than expected as
ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
as the specified lock time has arrived.
When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
returns vchDefaultKey, meaning miners may start to generate many blocks to
vchDefaultKey instead of a new key each time.
A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
allow the user to change their password via RPC.
Whenever keying material (unencrypted private keys, the user's passphrase,
the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
is made to mlock/VirtualLock that memory before storing the keying material.
This is not true in several (commented) cases where mlock/VirtualLocking the
memory is not possible.
Although encryption of private keys in memory can be very useful on desktop
systems (as some small amount of protection against stupid viruses), on an
RPC server, the password is entered fairly insecurely. Thus, the only main
advantage encryption has for RPC servers is for RPC servers that do not spend
coins, except in rare cases, eg. a webserver of a merchant which only receives
payment except for cases of manual intervention.
Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
for all their input.
Conflicts:
src/wallet.cpp
2011-07-08 09:47:35 -04:00
2012-04-06 13:39:12 -03:00
{
LOCK ( cs_wallet ) ;
2017-06-01 21:18:57 -04:00
for ( const MasterKeyMap : : value_type & pMasterKey : mapMasterKeys )
Add wallet privkey encryption.
This commit adds support for ckeys, or enCrypted private keys, to the wallet.
All keys are stored in memory in their encrypted form and thus the passphrase
is required from the user to spend coins, or to create new addresses.
Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
a random salt.
By default, the user's wallet remains unencrypted until they call the RPC
command encryptwallet <passphrase> or, from the GUI menu, Options->
Encrypt Wallet.
When the user is attempting to call RPC functions which require the password
to unlock the wallet, an error will be returned unless they call
walletpassphrase <passphrase> <time to keep key in memory> first.
A keypoolrefill command has been added which tops up the users keypool
(requiring the passphrase via walletpassphrase first).
keypoolsize has been added to the output of getinfo to show the user the
number of keys left before they need to specify their passphrase (and call
keypoolrefill).
Note that walletpassphrase will automatically fill keypool in a separate
thread which it spawns when the passphrase is set. This could cause some
delays in other threads waiting for locks on the wallet passphrase, including
one which could cause the passphrase to be stored longer than expected,
however it will not allow the passphrase to be used longer than expected as
ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
as the specified lock time has arrived.
When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
returns vchDefaultKey, meaning miners may start to generate many blocks to
vchDefaultKey instead of a new key each time.
A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
allow the user to change their password via RPC.
Whenever keying material (unencrypted private keys, the user's passphrase,
the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
is made to mlock/VirtualLock that memory before storing the keying material.
This is not true in several (commented) cases where mlock/VirtualLocking the
memory is not possible.
Although encryption of private keys in memory can be very useful on desktop
systems (as some small amount of protection against stupid viruses), on an
RPC server, the password is entered fairly insecurely. Thus, the only main
advantage encryption has for RPC servers is for RPC servers that do not spend
coins, except in rare cases, eg. a webserver of a merchant which only receives
payment except for cases of manual intervention.
Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
for all their input.
Conflicts:
src/wallet.cpp
2011-07-08 09:47:35 -04:00
{
if ( ! crypter . SetKeyFromPassphrase ( strWalletPassphrase , pMasterKey . second . vchSalt , pMasterKey . second . nDeriveIterations , pMasterKey . second . nDerivationMethod ) )
return false ;
2016-11-10 04:00:05 -03:00
if ( ! crypter . Decrypt ( pMasterKey . second . vchCryptedKey , _vMasterKey ) )
2013-05-07 10:47:00 -04:00
continue ; // try another master key
2019-06-06 17:58:21 -04:00
if ( Unlock ( _vMasterKey , accept_no_keys ) ) {
2018-11-06 11:23:37 -03:00
// Now that we've unlocked, upgrade the key metadata
UpgradeKeyMetadata ( ) ;
2021-03-01 19:03:52 -03:00
// Now that we've unlocked, upgrade the descriptor cache
UpgradeDescriptorCache ( ) ;
Add wallet privkey encryption.
This commit adds support for ckeys, or enCrypted private keys, to the wallet.
All keys are stored in memory in their encrypted form and thus the passphrase
is required from the user to spend coins, or to create new addresses.
Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
a random salt.
By default, the user's wallet remains unencrypted until they call the RPC
command encryptwallet <passphrase> or, from the GUI menu, Options->
Encrypt Wallet.
When the user is attempting to call RPC functions which require the password
to unlock the wallet, an error will be returned unless they call
walletpassphrase <passphrase> <time to keep key in memory> first.
A keypoolrefill command has been added which tops up the users keypool
(requiring the passphrase via walletpassphrase first).
keypoolsize has been added to the output of getinfo to show the user the
number of keys left before they need to specify their passphrase (and call
keypoolrefill).
Note that walletpassphrase will automatically fill keypool in a separate
thread which it spawns when the passphrase is set. This could cause some
delays in other threads waiting for locks on the wallet passphrase, including
one which could cause the passphrase to be stored longer than expected,
however it will not allow the passphrase to be used longer than expected as
ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
as the specified lock time has arrived.
When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
returns vchDefaultKey, meaning miners may start to generate many blocks to
vchDefaultKey instead of a new key each time.
A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
allow the user to change their password via RPC.
Whenever keying material (unencrypted private keys, the user's passphrase,
the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
is made to mlock/VirtualLock that memory before storing the keying material.
This is not true in several (commented) cases where mlock/VirtualLocking the
memory is not possible.
Although encryption of private keys in memory can be very useful on desktop
systems (as some small amount of protection against stupid viruses), on an
RPC server, the password is entered fairly insecurely. Thus, the only main
advantage encryption has for RPC servers is for RPC servers that do not spend
coins, except in rare cases, eg. a webserver of a merchant which only receives
payment except for cases of manual intervention.
Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
for all their input.
Conflicts:
src/wallet.cpp
2011-07-08 09:47:35 -04:00
return true ;
2018-11-06 11:23:37 -03:00
}
Add wallet privkey encryption.
This commit adds support for ckeys, or enCrypted private keys, to the wallet.
All keys are stored in memory in their encrypted form and thus the passphrase
is required from the user to spend coins, or to create new addresses.
Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
a random salt.
By default, the user's wallet remains unencrypted until they call the RPC
command encryptwallet <passphrase> or, from the GUI menu, Options->
Encrypt Wallet.
When the user is attempting to call RPC functions which require the password
to unlock the wallet, an error will be returned unless they call
walletpassphrase <passphrase> <time to keep key in memory> first.
A keypoolrefill command has been added which tops up the users keypool
(requiring the passphrase via walletpassphrase first).
keypoolsize has been added to the output of getinfo to show the user the
number of keys left before they need to specify their passphrase (and call
keypoolrefill).
Note that walletpassphrase will automatically fill keypool in a separate
thread which it spawns when the passphrase is set. This could cause some
delays in other threads waiting for locks on the wallet passphrase, including
one which could cause the passphrase to be stored longer than expected,
however it will not allow the passphrase to be used longer than expected as
ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
as the specified lock time has arrived.
When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
returns vchDefaultKey, meaning miners may start to generate many blocks to
vchDefaultKey instead of a new key each time.
A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
allow the user to change their password via RPC.
Whenever keying material (unencrypted private keys, the user's passphrase,
the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
is made to mlock/VirtualLock that memory before storing the keying material.
This is not true in several (commented) cases where mlock/VirtualLocking the
memory is not possible.
Although encryption of private keys in memory can be very useful on desktop
systems (as some small amount of protection against stupid viruses), on an
RPC server, the password is entered fairly insecurely. Thus, the only main
advantage encryption has for RPC servers is for RPC servers that do not spend
coins, except in rare cases, eg. a webserver of a merchant which only receives
payment except for cases of manual intervention.
Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
for all their input.
Conflicts:
src/wallet.cpp
2011-07-08 09:47:35 -04:00
}
2012-04-06 13:39:12 -03:00
}
Add wallet privkey encryption.
This commit adds support for ckeys, or enCrypted private keys, to the wallet.
All keys are stored in memory in their encrypted form and thus the passphrase
is required from the user to spend coins, or to create new addresses.
Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
a random salt.
By default, the user's wallet remains unencrypted until they call the RPC
command encryptwallet <passphrase> or, from the GUI menu, Options->
Encrypt Wallet.
When the user is attempting to call RPC functions which require the password
to unlock the wallet, an error will be returned unless they call
walletpassphrase <passphrase> <time to keep key in memory> first.
A keypoolrefill command has been added which tops up the users keypool
(requiring the passphrase via walletpassphrase first).
keypoolsize has been added to the output of getinfo to show the user the
number of keys left before they need to specify their passphrase (and call
keypoolrefill).
Note that walletpassphrase will automatically fill keypool in a separate
thread which it spawns when the passphrase is set. This could cause some
delays in other threads waiting for locks on the wallet passphrase, including
one which could cause the passphrase to be stored longer than expected,
however it will not allow the passphrase to be used longer than expected as
ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
as the specified lock time has arrived.
When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
returns vchDefaultKey, meaning miners may start to generate many blocks to
vchDefaultKey instead of a new key each time.
A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
allow the user to change their password via RPC.
Whenever keying material (unencrypted private keys, the user's passphrase,
the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
is made to mlock/VirtualLock that memory before storing the keying material.
This is not true in several (commented) cases where mlock/VirtualLocking the
memory is not possible.
Although encryption of private keys in memory can be very useful on desktop
systems (as some small amount of protection against stupid viruses), on an
RPC server, the password is entered fairly insecurely. Thus, the only main
advantage encryption has for RPC servers is for RPC servers that do not spend
coins, except in rare cases, eg. a webserver of a merchant which only receives
payment except for cases of manual intervention.
Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
for all their input.
Conflicts:
src/wallet.cpp
2011-07-08 09:47:35 -04:00
return false ;
}
2011-11-26 03:02:04 -03:00
bool CWallet : : ChangeWalletPassphrase ( const SecureString & strOldWalletPassphrase , const SecureString & strNewWalletPassphrase )
Add wallet privkey encryption.
This commit adds support for ckeys, or enCrypted private keys, to the wallet.
All keys are stored in memory in their encrypted form and thus the passphrase
is required from the user to spend coins, or to create new addresses.
Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
a random salt.
By default, the user's wallet remains unencrypted until they call the RPC
command encryptwallet <passphrase> or, from the GUI menu, Options->
Encrypt Wallet.
When the user is attempting to call RPC functions which require the password
to unlock the wallet, an error will be returned unless they call
walletpassphrase <passphrase> <time to keep key in memory> first.
A keypoolrefill command has been added which tops up the users keypool
(requiring the passphrase via walletpassphrase first).
keypoolsize has been added to the output of getinfo to show the user the
number of keys left before they need to specify their passphrase (and call
keypoolrefill).
Note that walletpassphrase will automatically fill keypool in a separate
thread which it spawns when the passphrase is set. This could cause some
delays in other threads waiting for locks on the wallet passphrase, including
one which could cause the passphrase to be stored longer than expected,
however it will not allow the passphrase to be used longer than expected as
ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
as the specified lock time has arrived.
When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
returns vchDefaultKey, meaning miners may start to generate many blocks to
vchDefaultKey instead of a new key each time.
A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
allow the user to change their password via RPC.
Whenever keying material (unencrypted private keys, the user's passphrase,
the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
is made to mlock/VirtualLock that memory before storing the keying material.
This is not true in several (commented) cases where mlock/VirtualLocking the
memory is not possible.
Although encryption of private keys in memory can be very useful on desktop
systems (as some small amount of protection against stupid viruses), on an
RPC server, the password is entered fairly insecurely. Thus, the only main
advantage encryption has for RPC servers is for RPC servers that do not spend
coins, except in rare cases, eg. a webserver of a merchant which only receives
payment except for cases of manual intervention.
Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
for all their input.
Conflicts:
src/wallet.cpp
2011-07-08 09:47:35 -04:00
{
2011-08-26 15:37:23 -03:00
bool fWasLocked = IsLocked ( ) ;
Add wallet privkey encryption.
This commit adds support for ckeys, or enCrypted private keys, to the wallet.
All keys are stored in memory in their encrypted form and thus the passphrase
is required from the user to spend coins, or to create new addresses.
Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
a random salt.
By default, the user's wallet remains unencrypted until they call the RPC
command encryptwallet <passphrase> or, from the GUI menu, Options->
Encrypt Wallet.
When the user is attempting to call RPC functions which require the password
to unlock the wallet, an error will be returned unless they call
walletpassphrase <passphrase> <time to keep key in memory> first.
A keypoolrefill command has been added which tops up the users keypool
(requiring the passphrase via walletpassphrase first).
keypoolsize has been added to the output of getinfo to show the user the
number of keys left before they need to specify their passphrase (and call
keypoolrefill).
Note that walletpassphrase will automatically fill keypool in a separate
thread which it spawns when the passphrase is set. This could cause some
delays in other threads waiting for locks on the wallet passphrase, including
one which could cause the passphrase to be stored longer than expected,
however it will not allow the passphrase to be used longer than expected as
ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
as the specified lock time has arrived.
When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
returns vchDefaultKey, meaning miners may start to generate many blocks to
vchDefaultKey instead of a new key each time.
A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
allow the user to change their password via RPC.
Whenever keying material (unencrypted private keys, the user's passphrase,
the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
is made to mlock/VirtualLock that memory before storing the keying material.
This is not true in several (commented) cases where mlock/VirtualLocking the
memory is not possible.
Although encryption of private keys in memory can be very useful on desktop
systems (as some small amount of protection against stupid viruses), on an
RPC server, the password is entered fairly insecurely. Thus, the only main
advantage encryption has for RPC servers is for RPC servers that do not spend
coins, except in rare cases, eg. a webserver of a merchant which only receives
payment except for cases of manual intervention.
Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
for all their input.
Conflicts:
src/wallet.cpp
2011-07-08 09:47:35 -04:00
2011-08-26 15:37:23 -03:00
{
2012-04-06 13:39:12 -03:00
LOCK ( cs_wallet ) ;
Add wallet privkey encryption.
This commit adds support for ckeys, or enCrypted private keys, to the wallet.
All keys are stored in memory in their encrypted form and thus the passphrase
is required from the user to spend coins, or to create new addresses.
Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
a random salt.
By default, the user's wallet remains unencrypted until they call the RPC
command encryptwallet <passphrase> or, from the GUI menu, Options->
Encrypt Wallet.
When the user is attempting to call RPC functions which require the password
to unlock the wallet, an error will be returned unless they call
walletpassphrase <passphrase> <time to keep key in memory> first.
A keypoolrefill command has been added which tops up the users keypool
(requiring the passphrase via walletpassphrase first).
keypoolsize has been added to the output of getinfo to show the user the
number of keys left before they need to specify their passphrase (and call
keypoolrefill).
Note that walletpassphrase will automatically fill keypool in a separate
thread which it spawns when the passphrase is set. This could cause some
delays in other threads waiting for locks on the wallet passphrase, including
one which could cause the passphrase to be stored longer than expected,
however it will not allow the passphrase to be used longer than expected as
ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
as the specified lock time has arrived.
When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
returns vchDefaultKey, meaning miners may start to generate many blocks to
vchDefaultKey instead of a new key each time.
A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
allow the user to change their password via RPC.
Whenever keying material (unencrypted private keys, the user's passphrase,
the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
is made to mlock/VirtualLock that memory before storing the keying material.
This is not true in several (commented) cases where mlock/VirtualLocking the
memory is not possible.
Although encryption of private keys in memory can be very useful on desktop
systems (as some small amount of protection against stupid viruses), on an
RPC server, the password is entered fairly insecurely. Thus, the only main
advantage encryption has for RPC servers is for RPC servers that do not spend
coins, except in rare cases, eg. a webserver of a merchant which only receives
payment except for cases of manual intervention.
Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
for all their input.
Conflicts:
src/wallet.cpp
2011-07-08 09:47:35 -04:00
Lock ( ) ;
CCrypter crypter ;
2016-11-10 04:00:05 -03:00
CKeyingMaterial _vMasterKey ;
2017-06-01 21:18:57 -04:00
for ( MasterKeyMap : : value_type & pMasterKey : mapMasterKeys )
Add wallet privkey encryption.
This commit adds support for ckeys, or enCrypted private keys, to the wallet.
All keys are stored in memory in their encrypted form and thus the passphrase
is required from the user to spend coins, or to create new addresses.
Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
a random salt.
By default, the user's wallet remains unencrypted until they call the RPC
command encryptwallet <passphrase> or, from the GUI menu, Options->
Encrypt Wallet.
When the user is attempting to call RPC functions which require the password
to unlock the wallet, an error will be returned unless they call
walletpassphrase <passphrase> <time to keep key in memory> first.
A keypoolrefill command has been added which tops up the users keypool
(requiring the passphrase via walletpassphrase first).
keypoolsize has been added to the output of getinfo to show the user the
number of keys left before they need to specify their passphrase (and call
keypoolrefill).
Note that walletpassphrase will automatically fill keypool in a separate
thread which it spawns when the passphrase is set. This could cause some
delays in other threads waiting for locks on the wallet passphrase, including
one which could cause the passphrase to be stored longer than expected,
however it will not allow the passphrase to be used longer than expected as
ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
as the specified lock time has arrived.
When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
returns vchDefaultKey, meaning miners may start to generate many blocks to
vchDefaultKey instead of a new key each time.
A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
allow the user to change their password via RPC.
Whenever keying material (unencrypted private keys, the user's passphrase,
the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
is made to mlock/VirtualLock that memory before storing the keying material.
This is not true in several (commented) cases where mlock/VirtualLocking the
memory is not possible.
Although encryption of private keys in memory can be very useful on desktop
systems (as some small amount of protection against stupid viruses), on an
RPC server, the password is entered fairly insecurely. Thus, the only main
advantage encryption has for RPC servers is for RPC servers that do not spend
coins, except in rare cases, eg. a webserver of a merchant which only receives
payment except for cases of manual intervention.
Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
for all their input.
Conflicts:
src/wallet.cpp
2011-07-08 09:47:35 -04:00
{
if ( ! crypter . SetKeyFromPassphrase ( strOldWalletPassphrase , pMasterKey . second . vchSalt , pMasterKey . second . nDeriveIterations , pMasterKey . second . nDerivationMethod ) )
return false ;
2016-11-10 04:00:05 -03:00
if ( ! crypter . Decrypt ( pMasterKey . second . vchCryptedKey , _vMasterKey ) )
Add wallet privkey encryption.
This commit adds support for ckeys, or enCrypted private keys, to the wallet.
All keys are stored in memory in their encrypted form and thus the passphrase
is required from the user to spend coins, or to create new addresses.
Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
a random salt.
By default, the user's wallet remains unencrypted until they call the RPC
command encryptwallet <passphrase> or, from the GUI menu, Options->
Encrypt Wallet.
When the user is attempting to call RPC functions which require the password
to unlock the wallet, an error will be returned unless they call
walletpassphrase <passphrase> <time to keep key in memory> first.
A keypoolrefill command has been added which tops up the users keypool
(requiring the passphrase via walletpassphrase first).
keypoolsize has been added to the output of getinfo to show the user the
number of keys left before they need to specify their passphrase (and call
keypoolrefill).
Note that walletpassphrase will automatically fill keypool in a separate
thread which it spawns when the passphrase is set. This could cause some
delays in other threads waiting for locks on the wallet passphrase, including
one which could cause the passphrase to be stored longer than expected,
however it will not allow the passphrase to be used longer than expected as
ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
as the specified lock time has arrived.
When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
returns vchDefaultKey, meaning miners may start to generate many blocks to
vchDefaultKey instead of a new key each time.
A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
allow the user to change their password via RPC.
Whenever keying material (unencrypted private keys, the user's passphrase,
the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
is made to mlock/VirtualLock that memory before storing the keying material.
This is not true in several (commented) cases where mlock/VirtualLocking the
memory is not possible.
Although encryption of private keys in memory can be very useful on desktop
systems (as some small amount of protection against stupid viruses), on an
RPC server, the password is entered fairly insecurely. Thus, the only main
advantage encryption has for RPC servers is for RPC servers that do not spend
coins, except in rare cases, eg. a webserver of a merchant which only receives
payment except for cases of manual intervention.
Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
for all their input.
Conflicts:
src/wallet.cpp
2011-07-08 09:47:35 -04:00
return false ;
2019-06-06 17:58:21 -04:00
if ( Unlock ( _vMasterKey ) )
Add wallet privkey encryption.
This commit adds support for ckeys, or enCrypted private keys, to the wallet.
All keys are stored in memory in their encrypted form and thus the passphrase
is required from the user to spend coins, or to create new addresses.
Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
a random salt.
By default, the user's wallet remains unencrypted until they call the RPC
command encryptwallet <passphrase> or, from the GUI menu, Options->
Encrypt Wallet.
When the user is attempting to call RPC functions which require the password
to unlock the wallet, an error will be returned unless they call
walletpassphrase <passphrase> <time to keep key in memory> first.
A keypoolrefill command has been added which tops up the users keypool
(requiring the passphrase via walletpassphrase first).
keypoolsize has been added to the output of getinfo to show the user the
number of keys left before they need to specify their passphrase (and call
keypoolrefill).
Note that walletpassphrase will automatically fill keypool in a separate
thread which it spawns when the passphrase is set. This could cause some
delays in other threads waiting for locks on the wallet passphrase, including
one which could cause the passphrase to be stored longer than expected,
however it will not allow the passphrase to be used longer than expected as
ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
as the specified lock time has arrived.
When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
returns vchDefaultKey, meaning miners may start to generate many blocks to
vchDefaultKey instead of a new key each time.
A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
allow the user to change their password via RPC.
Whenever keying material (unencrypted private keys, the user's passphrase,
the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
is made to mlock/VirtualLock that memory before storing the keying material.
This is not true in several (commented) cases where mlock/VirtualLocking the
memory is not possible.
Although encryption of private keys in memory can be very useful on desktop
systems (as some small amount of protection against stupid viruses), on an
RPC server, the password is entered fairly insecurely. Thus, the only main
advantage encryption has for RPC servers is for RPC servers that do not spend
coins, except in rare cases, eg. a webserver of a merchant which only receives
payment except for cases of manual intervention.
Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
for all their input.
Conflicts:
src/wallet.cpp
2011-07-08 09:47:35 -04:00
{
2013-04-13 02:13:08 -03:00
int64_t nStartTime = GetTimeMillis ( ) ;
2011-06-28 09:31:09 -04:00
crypter . SetKeyFromPassphrase ( strNewWalletPassphrase , pMasterKey . second . vchSalt , pMasterKey . second . nDeriveIterations , pMasterKey . second . nDerivationMethod ) ;
2017-09-11 16:43:49 -03:00
pMasterKey . second . nDeriveIterations = static_cast < unsigned int > ( pMasterKey . second . nDeriveIterations * ( 100 / ( ( double ) ( GetTimeMillis ( ) - nStartTime ) ) ) ) ;
2011-06-28 09:31:09 -04:00
nStartTime = GetTimeMillis ( ) ;
crypter . SetKeyFromPassphrase ( strNewWalletPassphrase , pMasterKey . second . vchSalt , pMasterKey . second . nDeriveIterations , pMasterKey . second . nDerivationMethod ) ;
2017-09-11 16:43:49 -03:00
pMasterKey . second . nDeriveIterations = ( pMasterKey . second . nDeriveIterations + static_cast < unsigned int > ( pMasterKey . second . nDeriveIterations * 100 / ( ( double ) ( GetTimeMillis ( ) - nStartTime ) ) ) ) / 2 ;
2011-06-28 09:31:09 -04:00
if ( pMasterKey . second . nDeriveIterations < 25000 )
pMasterKey . second . nDeriveIterations = 25000 ;
2018-06-15 19:02:52 -04:00
WalletLogPrintf ( " Wallet passphrase changed to an nDeriveIterations of %i \n " , pMasterKey . second . nDeriveIterations ) ;
2011-06-28 09:31:09 -04:00
Add wallet privkey encryption.
This commit adds support for ckeys, or enCrypted private keys, to the wallet.
All keys are stored in memory in their encrypted form and thus the passphrase
is required from the user to spend coins, or to create new addresses.
Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
a random salt.
By default, the user's wallet remains unencrypted until they call the RPC
command encryptwallet <passphrase> or, from the GUI menu, Options->
Encrypt Wallet.
When the user is attempting to call RPC functions which require the password
to unlock the wallet, an error will be returned unless they call
walletpassphrase <passphrase> <time to keep key in memory> first.
A keypoolrefill command has been added which tops up the users keypool
(requiring the passphrase via walletpassphrase first).
keypoolsize has been added to the output of getinfo to show the user the
number of keys left before they need to specify their passphrase (and call
keypoolrefill).
Note that walletpassphrase will automatically fill keypool in a separate
thread which it spawns when the passphrase is set. This could cause some
delays in other threads waiting for locks on the wallet passphrase, including
one which could cause the passphrase to be stored longer than expected,
however it will not allow the passphrase to be used longer than expected as
ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
as the specified lock time has arrived.
When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
returns vchDefaultKey, meaning miners may start to generate many blocks to
vchDefaultKey instead of a new key each time.
A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
allow the user to change their password via RPC.
Whenever keying material (unencrypted private keys, the user's passphrase,
the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
is made to mlock/VirtualLock that memory before storing the keying material.
This is not true in several (commented) cases where mlock/VirtualLocking the
memory is not possible.
Although encryption of private keys in memory can be very useful on desktop
systems (as some small amount of protection against stupid viruses), on an
RPC server, the password is entered fairly insecurely. Thus, the only main
advantage encryption has for RPC servers is for RPC servers that do not spend
coins, except in rare cases, eg. a webserver of a merchant which only receives
payment except for cases of manual intervention.
Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
for all their input.
Conflicts:
src/wallet.cpp
2011-07-08 09:47:35 -04:00
if ( ! crypter . SetKeyFromPassphrase ( strNewWalletPassphrase , pMasterKey . second . vchSalt , pMasterKey . second . nDeriveIterations , pMasterKey . second . nDerivationMethod ) )
return false ;
2016-11-10 04:00:05 -03:00
if ( ! crypter . Encrypt ( _vMasterKey , pMasterKey . second . vchCryptedKey ) )
Add wallet privkey encryption.
This commit adds support for ckeys, or enCrypted private keys, to the wallet.
All keys are stored in memory in their encrypted form and thus the passphrase
is required from the user to spend coins, or to create new addresses.
Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
a random salt.
By default, the user's wallet remains unencrypted until they call the RPC
command encryptwallet <passphrase> or, from the GUI menu, Options->
Encrypt Wallet.
When the user is attempting to call RPC functions which require the password
to unlock the wallet, an error will be returned unless they call
walletpassphrase <passphrase> <time to keep key in memory> first.
A keypoolrefill command has been added which tops up the users keypool
(requiring the passphrase via walletpassphrase first).
keypoolsize has been added to the output of getinfo to show the user the
number of keys left before they need to specify their passphrase (and call
keypoolrefill).
Note that walletpassphrase will automatically fill keypool in a separate
thread which it spawns when the passphrase is set. This could cause some
delays in other threads waiting for locks on the wallet passphrase, including
one which could cause the passphrase to be stored longer than expected,
however it will not allow the passphrase to be used longer than expected as
ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
as the specified lock time has arrived.
When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
returns vchDefaultKey, meaning miners may start to generate many blocks to
vchDefaultKey instead of a new key each time.
A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
allow the user to change their password via RPC.
Whenever keying material (unencrypted private keys, the user's passphrase,
the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
is made to mlock/VirtualLock that memory before storing the keying material.
This is not true in several (commented) cases where mlock/VirtualLocking the
memory is not possible.
Although encryption of private keys in memory can be very useful on desktop
systems (as some small amount of protection against stupid viruses), on an
RPC server, the password is entered fairly insecurely. Thus, the only main
advantage encryption has for RPC servers is for RPC servers that do not spend
coins, except in rare cases, eg. a webserver of a merchant which only receives
payment except for cases of manual intervention.
Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
for all their input.
Conflicts:
src/wallet.cpp
2011-07-08 09:47:35 -04:00
return false ;
2020-09-19 20:25:45 -03:00
WalletBatch ( GetDatabase ( ) ) . WriteMasterKey ( pMasterKey . first , pMasterKey . second ) ;
Add wallet privkey encryption.
This commit adds support for ckeys, or enCrypted private keys, to the wallet.
All keys are stored in memory in their encrypted form and thus the passphrase
is required from the user to spend coins, or to create new addresses.
Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
a random salt.
By default, the user's wallet remains unencrypted until they call the RPC
command encryptwallet <passphrase> or, from the GUI menu, Options->
Encrypt Wallet.
When the user is attempting to call RPC functions which require the password
to unlock the wallet, an error will be returned unless they call
walletpassphrase <passphrase> <time to keep key in memory> first.
A keypoolrefill command has been added which tops up the users keypool
(requiring the passphrase via walletpassphrase first).
keypoolsize has been added to the output of getinfo to show the user the
number of keys left before they need to specify their passphrase (and call
keypoolrefill).
Note that walletpassphrase will automatically fill keypool in a separate
thread which it spawns when the passphrase is set. This could cause some
delays in other threads waiting for locks on the wallet passphrase, including
one which could cause the passphrase to be stored longer than expected,
however it will not allow the passphrase to be used longer than expected as
ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
as the specified lock time has arrived.
When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
returns vchDefaultKey, meaning miners may start to generate many blocks to
vchDefaultKey instead of a new key each time.
A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
allow the user to change their password via RPC.
Whenever keying material (unencrypted private keys, the user's passphrase,
the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
is made to mlock/VirtualLock that memory before storing the keying material.
This is not true in several (commented) cases where mlock/VirtualLocking the
memory is not possible.
Although encryption of private keys in memory can be very useful on desktop
systems (as some small amount of protection against stupid viruses), on an
RPC server, the password is entered fairly insecurely. Thus, the only main
advantage encryption has for RPC servers is for RPC servers that do not spend
coins, except in rare cases, eg. a webserver of a merchant which only receives
payment except for cases of manual intervention.
Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
for all their input.
Conflicts:
src/wallet.cpp
2011-07-08 09:47:35 -04:00
if ( fWasLocked )
Lock ( ) ;
return true ;
}
}
}
2011-08-26 15:37:23 -03:00
Add wallet privkey encryption.
This commit adds support for ckeys, or enCrypted private keys, to the wallet.
All keys are stored in memory in their encrypted form and thus the passphrase
is required from the user to spend coins, or to create new addresses.
Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
a random salt.
By default, the user's wallet remains unencrypted until they call the RPC
command encryptwallet <passphrase> or, from the GUI menu, Options->
Encrypt Wallet.
When the user is attempting to call RPC functions which require the password
to unlock the wallet, an error will be returned unless they call
walletpassphrase <passphrase> <time to keep key in memory> first.
A keypoolrefill command has been added which tops up the users keypool
(requiring the passphrase via walletpassphrase first).
keypoolsize has been added to the output of getinfo to show the user the
number of keys left before they need to specify their passphrase (and call
keypoolrefill).
Note that walletpassphrase will automatically fill keypool in a separate
thread which it spawns when the passphrase is set. This could cause some
delays in other threads waiting for locks on the wallet passphrase, including
one which could cause the passphrase to be stored longer than expected,
however it will not allow the passphrase to be used longer than expected as
ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
as the specified lock time has arrived.
When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
returns vchDefaultKey, meaning miners may start to generate many blocks to
vchDefaultKey instead of a new key each time.
A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
allow the user to change their password via RPC.
Whenever keying material (unencrypted private keys, the user's passphrase,
the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
is made to mlock/VirtualLock that memory before storing the keying material.
This is not true in several (commented) cases where mlock/VirtualLocking the
memory is not possible.
Although encryption of private keys in memory can be very useful on desktop
systems (as some small amount of protection against stupid viruses), on an
RPC server, the password is entered fairly insecurely. Thus, the only main
advantage encryption has for RPC servers is for RPC servers that do not spend
coins, except in rare cases, eg. a webserver of a merchant which only receives
payment except for cases of manual intervention.
Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
for all their input.
Conflicts:
src/wallet.cpp
2011-07-08 09:47:35 -04:00
return false ;
}
2020-02-26 18:05:49 -03:00
void CWallet : : chainStateFlushed ( const CBlockLocator & loc )
2012-04-15 17:10:54 -03:00
{
2020-09-19 20:25:45 -03:00
WalletBatch batch ( GetDatabase ( ) ) ;
2017-12-08 08:39:22 -03:00
batch . WriteBestBlock ( loc ) ;
2012-04-15 17:10:54 -03:00
}
2011-07-10 10:07:22 -04:00
2020-04-29 17:07:52 -04:00
void CWallet : : SetMinVersion ( enum WalletFeature nVersion , WalletBatch * batch_in )
2012-02-18 10:55:02 -03:00
{
2019-02-18 18:09:45 -03:00
LOCK ( cs_wallet ) ;
2012-02-18 10:55:02 -03:00
if ( nWalletVersion > = nVersion )
2018-07-27 02:22:42 -04:00
return ;
2012-02-18 10:55:02 -03:00
nWalletVersion = nVersion ;
{
2020-09-19 20:25:45 -03:00
WalletBatch * batch = batch_in ? batch_in : new WalletBatch ( GetDatabase ( ) ) ;
2012-02-18 10:55:02 -03:00
if ( nWalletVersion > 40000 )
2017-12-08 08:39:22 -03:00
batch - > WriteMinVersion ( nWalletVersion ) ;
if ( ! batch_in )
delete batch ;
2012-02-18 10:55:02 -03:00
}
}
2017-01-26 22:33:45 -03:00
std : : set < uint256 > CWallet : : GetConflicts ( const uint256 & txid ) const
2014-02-13 22:12:51 -03:00
{
2017-01-26 22:33:45 -03:00
std : : set < uint256 > result ;
2014-02-13 22:12:51 -03:00
AssertLockHeld ( cs_wallet ) ;
std : : map < uint256 , CWalletTx > : : const_iterator it = mapWallet . find ( txid ) ;
if ( it = = mapWallet . end ( ) )
return result ;
const CWalletTx & wtx = it - > second ;
2014-02-15 18:38:28 -03:00
std : : pair < TxSpends : : const_iterator , TxSpends : : const_iterator > range ;
2014-02-13 22:12:51 -03:00
2017-06-01 21:18:57 -04:00
for ( const CTxIn & txin : wtx . tx - > vin )
2014-02-13 22:12:51 -03:00
{
2014-02-15 18:38:28 -03:00
if ( mapTxSpends . count ( txin . prevout ) < = 1 )
continue ; // No conflict if zero or one spends
range = mapTxSpends . equal_range ( txin . prevout ) ;
2016-09-02 13:19:01 -03:00
for ( TxSpends : : const_iterator _it = range . first ; _it ! = range . second ; + + _it )
result . insert ( _it - > second ) ;
2014-02-13 22:12:51 -03:00
}
return result ;
}
2016-12-09 15:45:27 -03:00
bool CWallet : : HasWalletSpend ( const uint256 & txid ) const
{
AssertLockHeld ( cs_wallet ) ;
auto iter = mapTxSpends . lower_bound ( COutPoint ( txid , 0 ) ) ;
return ( iter ! = mapTxSpends . end ( ) & & iter - > first . hash = = txid ) ;
}
2020-06-15 17:59:24 -04:00
void CWallet : : Flush ( )
2015-02-04 17:19:27 -03:00
{
2020-09-19 20:25:45 -03:00
GetDatabase ( ) . Flush ( ) ;
2020-06-15 17:59:24 -04:00
}
void CWallet : : Close ( )
{
2020-09-19 20:25:45 -03:00
GetDatabase ( ) . Close ( ) ;
2015-02-04 17:19:27 -03:00
}
2017-01-26 22:33:45 -03:00
void CWallet : : SyncMetaData ( std : : pair < TxSpends : : iterator , TxSpends : : iterator > range )
2014-02-13 22:12:51 -03:00
{
// We want all the wallet transactions in range to have the same metadata as
// the oldest (smallest nOrderPos).
// So: find smallest nOrderPos:
int nMinOrderPos = std : : numeric_limits < int > : : max ( ) ;
2017-08-07 01:36:37 -04:00
const CWalletTx * copyFrom = nullptr ;
2018-01-26 13:28:31 -03:00
for ( TxSpends : : iterator it = range . first ; it ! = range . second ; + + it ) {
2017-01-19 18:08:03 -03:00
const CWalletTx * wtx = & mapWallet . at ( it - > second ) ;
2018-01-26 13:28:31 -03:00
if ( wtx - > nOrderPos < nMinOrderPos ) {
2018-06-29 05:30:25 -04:00
nMinOrderPos = wtx - > nOrderPos ;
2018-01-26 13:28:31 -03:00
copyFrom = wtx ;
2014-02-13 22:12:51 -03:00
}
}
2017-08-17 10:26:32 -03:00
2018-05-17 16:07:32 -04:00
if ( ! copyFrom ) {
return ;
}
2017-08-17 10:26:32 -03:00
2014-02-13 22:12:51 -03:00
// Now copy data from copyFrom to rest:
2014-02-15 18:38:28 -03:00
for ( TxSpends : : iterator it = range . first ; it ! = range . second ; + + it )
2014-02-13 22:12:51 -03:00
{
const uint256 & hash = it - > second ;
2017-01-19 18:08:03 -03:00
CWalletTx * copyTo = & mapWallet . at ( hash ) ;
2014-02-13 22:12:51 -03:00
if ( copyFrom = = copyTo ) continue ;
2017-08-28 04:24:17 -03:00
assert ( copyFrom & & " Oldest wallet transaction in range assumed to have been found. " ) ;
2015-03-11 20:48:53 -03:00
if ( ! copyFrom - > IsEquivalentTo ( * copyTo ) ) continue ;
2014-02-13 22:12:51 -03:00
copyTo - > mapValue = copyFrom - > mapValue ;
copyTo - > vOrderForm = copyFrom - > vOrderForm ;
// fTimeReceivedIsTxTime not copied on purpose
// nTimeReceived not copied on purpose
copyTo - > nTimeSmart = copyFrom - > nTimeSmart ;
copyTo - > fFromMe = copyFrom - > fFromMe ;
// nOrderPos not copied on purpose
// cached members not copied on purpose
}
}
2014-10-26 04:03:12 -03:00
/**
* Outpoint is spent if any non - conflicted transaction
* spends it :
*/
2019-04-29 09:52:01 -04:00
bool CWallet : : IsSpent ( const uint256 & hash , unsigned int n ) const
2014-02-13 22:12:51 -03:00
{
2014-02-15 18:38:28 -03:00
const COutPoint outpoint ( hash , n ) ;
2017-01-26 22:33:45 -03:00
std : : pair < TxSpends : : const_iterator , TxSpends : : const_iterator > range ;
2014-02-15 18:38:28 -03:00
range = mapTxSpends . equal_range ( outpoint ) ;
2014-02-13 22:12:51 -03:00
2014-02-15 18:38:28 -03:00
for ( TxSpends : : const_iterator it = range . first ; it ! = range . second ; + + it )
2014-02-13 22:12:51 -03:00
{
2014-02-15 18:38:28 -03:00
const uint256 & wtxid = it - > second ;
std : : map < uint256 , CWalletTx > : : const_iterator mit = mapWallet . find ( wtxid ) ;
2016-01-07 18:31:27 -03:00
if ( mit ! = mapWallet . end ( ) ) {
2021-02-12 20:01:22 -03:00
int depth = GetTxDepthInMainChain ( mit - > second ) ;
2016-01-07 18:31:27 -03:00
if ( depth > 0 | | ( depth = = 0 & & ! mit - > second . isAbandoned ( ) ) )
return true ; // Spent
}
2014-02-13 22:12:51 -03:00
}
2014-02-15 18:38:28 -03:00
return false ;
}
2021-09-22 09:17:55 -03:00
void CWallet : : AddToSpends ( const COutPoint & outpoint , const uint256 & wtxid , WalletBatch * batch )
2014-02-15 18:38:28 -03:00
{
2017-01-26 22:33:45 -03:00
mapTxSpends . insert ( std : : make_pair ( outpoint , wtxid ) ) ;
2014-02-15 18:38:28 -03:00
2021-09-22 09:17:55 -03:00
if ( batch ) {
UnlockCoin ( outpoint , batch ) ;
} else {
WalletBatch temp_batch ( GetDatabase ( ) ) ;
UnlockCoin ( outpoint , & temp_batch ) ;
}
2018-05-25 09:27:58 -04:00
2017-01-26 22:33:45 -03:00
std : : pair < TxSpends : : iterator , TxSpends : : iterator > range ;
2014-02-15 18:38:28 -03:00
range = mapTxSpends . equal_range ( outpoint ) ;
SyncMetaData ( range ) ;
}
2021-09-22 09:17:55 -03:00
void CWallet : : AddToSpends ( const uint256 & wtxid , WalletBatch * batch )
2014-02-15 18:38:28 -03:00
{
2017-08-13 11:04:57 -03:00
auto it = mapWallet . find ( wtxid ) ;
assert ( it ! = mapWallet . end ( ) ) ;
2020-12-06 13:11:39 -03:00
const CWalletTx & thisTx = it - > second ;
2014-02-15 18:38:28 -03:00
if ( thisTx . IsCoinBase ( ) ) // Coinbases don't spend anything!
return ;
2017-06-01 21:18:57 -04:00
for ( const CTxIn & txin : thisTx . tx - > vin )
2021-09-22 09:17:55 -03:00
AddToSpends ( txin . prevout , wtxid , batch ) ;
2014-02-13 22:12:51 -03:00
}
2011-11-26 03:02:04 -03:00
bool CWallet : : EncryptWallet ( const SecureString & strWalletPassphrase )
Add wallet privkey encryption.
This commit adds support for ckeys, or enCrypted private keys, to the wallet.
All keys are stored in memory in their encrypted form and thus the passphrase
is required from the user to spend coins, or to create new addresses.
Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
a random salt.
By default, the user's wallet remains unencrypted until they call the RPC
command encryptwallet <passphrase> or, from the GUI menu, Options->
Encrypt Wallet.
When the user is attempting to call RPC functions which require the password
to unlock the wallet, an error will be returned unless they call
walletpassphrase <passphrase> <time to keep key in memory> first.
A keypoolrefill command has been added which tops up the users keypool
(requiring the passphrase via walletpassphrase first).
keypoolsize has been added to the output of getinfo to show the user the
number of keys left before they need to specify their passphrase (and call
keypoolrefill).
Note that walletpassphrase will automatically fill keypool in a separate
thread which it spawns when the passphrase is set. This could cause some
delays in other threads waiting for locks on the wallet passphrase, including
one which could cause the passphrase to be stored longer than expected,
however it will not allow the passphrase to be used longer than expected as
ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
as the specified lock time has arrived.
When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
returns vchDefaultKey, meaning miners may start to generate many blocks to
vchDefaultKey instead of a new key each time.
A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
allow the user to change their password via RPC.
Whenever keying material (unencrypted private keys, the user's passphrase,
the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
is made to mlock/VirtualLock that memory before storing the keying material.
This is not true in several (commented) cases where mlock/VirtualLocking the
memory is not possible.
Although encryption of private keys in memory can be very useful on desktop
systems (as some small amount of protection against stupid viruses), on an
RPC server, the password is entered fairly insecurely. Thus, the only main
advantage encryption has for RPC servers is for RPC servers that do not spend
coins, except in rare cases, eg. a webserver of a merchant which only receives
payment except for cases of manual intervention.
Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
for all their input.
Conflicts:
src/wallet.cpp
2011-07-08 09:47:35 -04:00
{
2011-08-26 15:37:23 -03:00
if ( IsCrypted ( ) )
return false ;
Add wallet privkey encryption.
This commit adds support for ckeys, or enCrypted private keys, to the wallet.
All keys are stored in memory in their encrypted form and thus the passphrase
is required from the user to spend coins, or to create new addresses.
Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
a random salt.
By default, the user's wallet remains unencrypted until they call the RPC
command encryptwallet <passphrase> or, from the GUI menu, Options->
Encrypt Wallet.
When the user is attempting to call RPC functions which require the password
to unlock the wallet, an error will be returned unless they call
walletpassphrase <passphrase> <time to keep key in memory> first.
A keypoolrefill command has been added which tops up the users keypool
(requiring the passphrase via walletpassphrase first).
keypoolsize has been added to the output of getinfo to show the user the
number of keys left before they need to specify their passphrase (and call
keypoolrefill).
Note that walletpassphrase will automatically fill keypool in a separate
thread which it spawns when the passphrase is set. This could cause some
delays in other threads waiting for locks on the wallet passphrase, including
one which could cause the passphrase to be stored longer than expected,
however it will not allow the passphrase to be used longer than expected as
ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
as the specified lock time has arrived.
When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
returns vchDefaultKey, meaning miners may start to generate many blocks to
vchDefaultKey instead of a new key each time.
A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
allow the user to change their password via RPC.
Whenever keying material (unencrypted private keys, the user's passphrase,
the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
is made to mlock/VirtualLock that memory before storing the keying material.
This is not true in several (commented) cases where mlock/VirtualLocking the
memory is not possible.
Although encryption of private keys in memory can be very useful on desktop
systems (as some small amount of protection against stupid viruses), on an
RPC server, the password is entered fairly insecurely. Thus, the only main
advantage encryption has for RPC servers is for RPC servers that do not spend
coins, except in rare cases, eg. a webserver of a merchant which only receives
payment except for cases of manual intervention.
Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
for all their input.
Conflicts:
src/wallet.cpp
2011-07-08 09:47:35 -04:00
2016-11-10 04:00:05 -03:00
CKeyingMaterial _vMasterKey ;
Add wallet privkey encryption.
This commit adds support for ckeys, or enCrypted private keys, to the wallet.
All keys are stored in memory in their encrypted form and thus the passphrase
is required from the user to spend coins, or to create new addresses.
Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
a random salt.
By default, the user's wallet remains unencrypted until they call the RPC
command encryptwallet <passphrase> or, from the GUI menu, Options->
Encrypt Wallet.
When the user is attempting to call RPC functions which require the password
to unlock the wallet, an error will be returned unless they call
walletpassphrase <passphrase> <time to keep key in memory> first.
A keypoolrefill command has been added which tops up the users keypool
(requiring the passphrase via walletpassphrase first).
keypoolsize has been added to the output of getinfo to show the user the
number of keys left before they need to specify their passphrase (and call
keypoolrefill).
Note that walletpassphrase will automatically fill keypool in a separate
thread which it spawns when the passphrase is set. This could cause some
delays in other threads waiting for locks on the wallet passphrase, including
one which could cause the passphrase to be stored longer than expected,
however it will not allow the passphrase to be used longer than expected as
ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
as the specified lock time has arrived.
When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
returns vchDefaultKey, meaning miners may start to generate many blocks to
vchDefaultKey instead of a new key each time.
A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
allow the user to change their password via RPC.
Whenever keying material (unencrypted private keys, the user's passphrase,
the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
is made to mlock/VirtualLock that memory before storing the keying material.
This is not true in several (commented) cases where mlock/VirtualLocking the
memory is not possible.
Although encryption of private keys in memory can be very useful on desktop
systems (as some small amount of protection against stupid viruses), on an
RPC server, the password is entered fairly insecurely. Thus, the only main
advantage encryption has for RPC servers is for RPC servers that do not spend
coins, except in rare cases, eg. a webserver of a merchant which only receives
payment except for cases of manual intervention.
Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
for all their input.
Conflicts:
src/wallet.cpp
2011-07-08 09:47:35 -04:00
2016-11-10 04:00:05 -03:00
_vMasterKey . resize ( WALLET_CRYPTO_KEY_SIZE ) ;
2021-04-30 14:03:35 -04:00
GetStrongRandBytes ( _vMasterKey . data ( ) , WALLET_CRYPTO_KEY_SIZE ) ;
Add wallet privkey encryption.
This commit adds support for ckeys, or enCrypted private keys, to the wallet.
All keys are stored in memory in their encrypted form and thus the passphrase
is required from the user to spend coins, or to create new addresses.
Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
a random salt.
By default, the user's wallet remains unencrypted until they call the RPC
command encryptwallet <passphrase> or, from the GUI menu, Options->
Encrypt Wallet.
When the user is attempting to call RPC functions which require the password
to unlock the wallet, an error will be returned unless they call
walletpassphrase <passphrase> <time to keep key in memory> first.
A keypoolrefill command has been added which tops up the users keypool
(requiring the passphrase via walletpassphrase first).
keypoolsize has been added to the output of getinfo to show the user the
number of keys left before they need to specify their passphrase (and call
keypoolrefill).
Note that walletpassphrase will automatically fill keypool in a separate
thread which it spawns when the passphrase is set. This could cause some
delays in other threads waiting for locks on the wallet passphrase, including
one which could cause the passphrase to be stored longer than expected,
however it will not allow the passphrase to be used longer than expected as
ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
as the specified lock time has arrived.
When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
returns vchDefaultKey, meaning miners may start to generate many blocks to
vchDefaultKey instead of a new key each time.
A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
allow the user to change their password via RPC.
Whenever keying material (unencrypted private keys, the user's passphrase,
the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
is made to mlock/VirtualLock that memory before storing the keying material.
This is not true in several (commented) cases where mlock/VirtualLocking the
memory is not possible.
Although encryption of private keys in memory can be very useful on desktop
systems (as some small amount of protection against stupid viruses), on an
RPC server, the password is entered fairly insecurely. Thus, the only main
advantage encryption has for RPC servers is for RPC servers that do not spend
coins, except in rare cases, eg. a webserver of a merchant which only receives
payment except for cases of manual intervention.
Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
for all their input.
Conflicts:
src/wallet.cpp
2011-07-08 09:47:35 -04:00
2011-08-26 15:37:23 -03:00
CMasterKey kMasterKey ;
2014-06-24 08:27:32 -04:00
2011-08-26 15:37:23 -03:00
kMasterKey . vchSalt . resize ( WALLET_CRYPTO_SALT_SIZE ) ;
2021-04-30 14:03:35 -04:00
GetStrongRandBytes ( kMasterKey . vchSalt . data ( ) , WALLET_CRYPTO_SALT_SIZE ) ;
Add wallet privkey encryption.
This commit adds support for ckeys, or enCrypted private keys, to the wallet.
All keys are stored in memory in their encrypted form and thus the passphrase
is required from the user to spend coins, or to create new addresses.
Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
a random salt.
By default, the user's wallet remains unencrypted until they call the RPC
command encryptwallet <passphrase> or, from the GUI menu, Options->
Encrypt Wallet.
When the user is attempting to call RPC functions which require the password
to unlock the wallet, an error will be returned unless they call
walletpassphrase <passphrase> <time to keep key in memory> first.
A keypoolrefill command has been added which tops up the users keypool
(requiring the passphrase via walletpassphrase first).
keypoolsize has been added to the output of getinfo to show the user the
number of keys left before they need to specify their passphrase (and call
keypoolrefill).
Note that walletpassphrase will automatically fill keypool in a separate
thread which it spawns when the passphrase is set. This could cause some
delays in other threads waiting for locks on the wallet passphrase, including
one which could cause the passphrase to be stored longer than expected,
however it will not allow the passphrase to be used longer than expected as
ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
as the specified lock time has arrived.
When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
returns vchDefaultKey, meaning miners may start to generate many blocks to
vchDefaultKey instead of a new key each time.
A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
allow the user to change their password via RPC.
Whenever keying material (unencrypted private keys, the user's passphrase,
the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
is made to mlock/VirtualLock that memory before storing the keying material.
This is not true in several (commented) cases where mlock/VirtualLocking the
memory is not possible.
Although encryption of private keys in memory can be very useful on desktop
systems (as some small amount of protection against stupid viruses), on an
RPC server, the password is entered fairly insecurely. Thus, the only main
advantage encryption has for RPC servers is for RPC servers that do not spend
coins, except in rare cases, eg. a webserver of a merchant which only receives
payment except for cases of manual intervention.
Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
for all their input.
Conflicts:
src/wallet.cpp
2011-07-08 09:47:35 -04:00
2011-08-26 15:37:23 -03:00
CCrypter crypter ;
2013-04-13 02:13:08 -03:00
int64_t nStartTime = GetTimeMillis ( ) ;
2011-08-26 15:37:23 -03:00
crypter . SetKeyFromPassphrase ( strWalletPassphrase , kMasterKey . vchSalt , 25000 , kMasterKey . nDerivationMethod ) ;
2017-09-11 16:43:49 -03:00
kMasterKey . nDeriveIterations = static_cast < unsigned int > ( 2500000 / ( ( double ) ( GetTimeMillis ( ) - nStartTime ) ) ) ;
2011-06-28 09:31:09 -04:00
2011-08-26 15:37:23 -03:00
nStartTime = GetTimeMillis ( ) ;
crypter . SetKeyFromPassphrase ( strWalletPassphrase , kMasterKey . vchSalt , kMasterKey . nDeriveIterations , kMasterKey . nDerivationMethod ) ;
2017-09-11 16:43:49 -03:00
kMasterKey . nDeriveIterations = ( kMasterKey . nDeriveIterations + static_cast < unsigned int > ( kMasterKey . nDeriveIterations * 100 / ( ( double ) ( GetTimeMillis ( ) - nStartTime ) ) ) ) / 2 ;
2011-06-28 09:31:09 -04:00
2011-08-26 15:37:23 -03:00
if ( kMasterKey . nDeriveIterations < 25000 )
kMasterKey . nDeriveIterations = 25000 ;
2011-06-28 09:31:09 -04:00
2018-06-15 19:02:52 -04:00
WalletLogPrintf ( " Encrypting Wallet with an nDeriveIterations of %i \n " , kMasterKey . nDeriveIterations ) ;
2011-06-28 09:31:09 -04:00
2011-08-26 15:37:23 -03:00
if ( ! crypter . SetKeyFromPassphrase ( strWalletPassphrase , kMasterKey . vchSalt , kMasterKey . nDeriveIterations , kMasterKey . nDerivationMethod ) )
return false ;
2016-11-10 04:00:05 -03:00
if ( ! crypter . Encrypt ( _vMasterKey , kMasterKey . vchCryptedKey ) )
2011-08-26 15:37:23 -03:00
return false ;
Add wallet privkey encryption.
This commit adds support for ckeys, or enCrypted private keys, to the wallet.
All keys are stored in memory in their encrypted form and thus the passphrase
is required from the user to spend coins, or to create new addresses.
Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
a random salt.
By default, the user's wallet remains unencrypted until they call the RPC
command encryptwallet <passphrase> or, from the GUI menu, Options->
Encrypt Wallet.
When the user is attempting to call RPC functions which require the password
to unlock the wallet, an error will be returned unless they call
walletpassphrase <passphrase> <time to keep key in memory> first.
A keypoolrefill command has been added which tops up the users keypool
(requiring the passphrase via walletpassphrase first).
keypoolsize has been added to the output of getinfo to show the user the
number of keys left before they need to specify their passphrase (and call
keypoolrefill).
Note that walletpassphrase will automatically fill keypool in a separate
thread which it spawns when the passphrase is set. This could cause some
delays in other threads waiting for locks on the wallet passphrase, including
one which could cause the passphrase to be stored longer than expected,
however it will not allow the passphrase to be used longer than expected as
ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
as the specified lock time has arrived.
When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
returns vchDefaultKey, meaning miners may start to generate many blocks to
vchDefaultKey instead of a new key each time.
A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
allow the user to change their password via RPC.
Whenever keying material (unencrypted private keys, the user's passphrase,
the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
is made to mlock/VirtualLock that memory before storing the keying material.
This is not true in several (commented) cases where mlock/VirtualLocking the
memory is not possible.
Although encryption of private keys in memory can be very useful on desktop
systems (as some small amount of protection against stupid viruses), on an
RPC server, the password is entered fairly insecurely. Thus, the only main
advantage encryption has for RPC servers is for RPC servers that do not spend
coins, except in rare cases, eg. a webserver of a merchant which only receives
payment except for cases of manual intervention.
Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
for all their input.
Conflicts:
src/wallet.cpp
2011-07-08 09:47:35 -04:00
2011-08-26 15:37:23 -03:00
{
2012-04-06 13:39:12 -03:00
LOCK ( cs_wallet ) ;
Add wallet privkey encryption.
This commit adds support for ckeys, or enCrypted private keys, to the wallet.
All keys are stored in memory in their encrypted form and thus the passphrase
is required from the user to spend coins, or to create new addresses.
Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
a random salt.
By default, the user's wallet remains unencrypted until they call the RPC
command encryptwallet <passphrase> or, from the GUI menu, Options->
Encrypt Wallet.
When the user is attempting to call RPC functions which require the password
to unlock the wallet, an error will be returned unless they call
walletpassphrase <passphrase> <time to keep key in memory> first.
A keypoolrefill command has been added which tops up the users keypool
(requiring the passphrase via walletpassphrase first).
keypoolsize has been added to the output of getinfo to show the user the
number of keys left before they need to specify their passphrase (and call
keypoolrefill).
Note that walletpassphrase will automatically fill keypool in a separate
thread which it spawns when the passphrase is set. This could cause some
delays in other threads waiting for locks on the wallet passphrase, including
one which could cause the passphrase to be stored longer than expected,
however it will not allow the passphrase to be used longer than expected as
ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
as the specified lock time has arrived.
When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
returns vchDefaultKey, meaning miners may start to generate many blocks to
vchDefaultKey instead of a new key each time.
A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
allow the user to change their password via RPC.
Whenever keying material (unencrypted private keys, the user's passphrase,
the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
is made to mlock/VirtualLock that memory before storing the keying material.
This is not true in several (commented) cases where mlock/VirtualLocking the
memory is not possible.
Although encryption of private keys in memory can be very useful on desktop
systems (as some small amount of protection against stupid viruses), on an
RPC server, the password is entered fairly insecurely. Thus, the only main
advantage encryption has for RPC servers is for RPC servers that do not spend
coins, except in rare cases, eg. a webserver of a merchant which only receives
payment except for cases of manual intervention.
Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
for all their input.
Conflicts:
src/wallet.cpp
2011-07-08 09:47:35 -04:00
mapMasterKeys [ + + nMasterKeyMaxID ] = kMasterKey ;
2020-09-19 20:25:45 -03:00
WalletBatch * encrypted_batch = new WalletBatch ( GetDatabase ( ) ) ;
2017-12-08 08:39:22 -03:00
if ( ! encrypted_batch - > TxnBegin ( ) ) {
delete encrypted_batch ;
encrypted_batch = nullptr ;
2017-03-08 09:08:26 -03:00
return false ;
Add wallet privkey encryption.
This commit adds support for ckeys, or enCrypted private keys, to the wallet.
All keys are stored in memory in their encrypted form and thus the passphrase
is required from the user to spend coins, or to create new addresses.
Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
a random salt.
By default, the user's wallet remains unencrypted until they call the RPC
command encryptwallet <passphrase> or, from the GUI menu, Options->
Encrypt Wallet.
When the user is attempting to call RPC functions which require the password
to unlock the wallet, an error will be returned unless they call
walletpassphrase <passphrase> <time to keep key in memory> first.
A keypoolrefill command has been added which tops up the users keypool
(requiring the passphrase via walletpassphrase first).
keypoolsize has been added to the output of getinfo to show the user the
number of keys left before they need to specify their passphrase (and call
keypoolrefill).
Note that walletpassphrase will automatically fill keypool in a separate
thread which it spawns when the passphrase is set. This could cause some
delays in other threads waiting for locks on the wallet passphrase, including
one which could cause the passphrase to be stored longer than expected,
however it will not allow the passphrase to be used longer than expected as
ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
as the specified lock time has arrived.
When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
returns vchDefaultKey, meaning miners may start to generate many blocks to
vchDefaultKey instead of a new key each time.
A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
allow the user to change their password via RPC.
Whenever keying material (unencrypted private keys, the user's passphrase,
the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
is made to mlock/VirtualLock that memory before storing the keying material.
This is not true in several (commented) cases where mlock/VirtualLocking the
memory is not possible.
Although encryption of private keys in memory can be very useful on desktop
systems (as some small amount of protection against stupid viruses), on an
RPC server, the password is entered fairly insecurely. Thus, the only main
advantage encryption has for RPC servers is for RPC servers that do not spend
coins, except in rare cases, eg. a webserver of a merchant which only receives
payment except for cases of manual intervention.
Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
for all their input.
Conflicts:
src/wallet.cpp
2011-07-08 09:47:35 -04:00
}
2017-12-08 08:39:22 -03:00
encrypted_batch - > WriteMasterKey ( nMasterKeyMaxID , kMasterKey ) ;
Add wallet privkey encryption.
This commit adds support for ckeys, or enCrypted private keys, to the wallet.
All keys are stored in memory in their encrypted form and thus the passphrase
is required from the user to spend coins, or to create new addresses.
Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
a random salt.
By default, the user's wallet remains unencrypted until they call the RPC
command encryptwallet <passphrase> or, from the GUI menu, Options->
Encrypt Wallet.
When the user is attempting to call RPC functions which require the password
to unlock the wallet, an error will be returned unless they call
walletpassphrase <passphrase> <time to keep key in memory> first.
A keypoolrefill command has been added which tops up the users keypool
(requiring the passphrase via walletpassphrase first).
keypoolsize has been added to the output of getinfo to show the user the
number of keys left before they need to specify their passphrase (and call
keypoolrefill).
Note that walletpassphrase will automatically fill keypool in a separate
thread which it spawns when the passphrase is set. This could cause some
delays in other threads waiting for locks on the wallet passphrase, including
one which could cause the passphrase to be stored longer than expected,
however it will not allow the passphrase to be used longer than expected as
ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
as the specified lock time has arrived.
When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
returns vchDefaultKey, meaning miners may start to generate many blocks to
vchDefaultKey instead of a new key each time.
A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
allow the user to change their password via RPC.
Whenever keying material (unencrypted private keys, the user's passphrase,
the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
is made to mlock/VirtualLock that memory before storing the keying material.
This is not true in several (commented) cases where mlock/VirtualLocking the
memory is not possible.
Although encryption of private keys in memory can be very useful on desktop
systems (as some small amount of protection against stupid viruses), on an
RPC server, the password is entered fairly insecurely. Thus, the only main
advantage encryption has for RPC servers is for RPC servers that do not spend
coins, except in rare cases, eg. a webserver of a merchant which only receives
payment except for cases of manual intervention.
Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
for all their input.
Conflicts:
src/wallet.cpp
2011-07-08 09:47:35 -04:00
2019-10-07 15:11:34 -03:00
for ( const auto & spk_man_pair : m_spk_managers ) {
auto spk_man = spk_man_pair . second . get ( ) ;
2019-12-05 20:23:57 -03:00
if ( ! spk_man - > Encrypt ( _vMasterKey , encrypted_batch ) ) {
2019-10-07 15:11:34 -03:00
encrypted_batch - > TxnAbort ( ) ;
delete encrypted_batch ;
encrypted_batch = nullptr ;
// We now probably have half of our keys encrypted in memory, and half not...
// die and let the user reload the unencrypted wallet.
assert ( false ) ;
}
2011-07-08 09:08:27 -04:00
}
2012-02-18 10:55:02 -03:00
// Encryption was introduced in version 0.4.0
2020-04-29 17:07:52 -04:00
SetMinVersion ( FEATURE_WALLETCRYPT , encrypted_batch ) ;
2012-02-18 10:55:02 -03:00
2017-12-08 08:39:22 -03:00
if ( ! encrypted_batch - > TxnCommit ( ) ) {
delete encrypted_batch ;
2018-09-03 10:41:02 -03:00
encrypted_batch = nullptr ;
2017-03-08 09:08:26 -03:00
// We now have keys encrypted in memory, but not on disk...
// die to avoid confusion and let the user reload the unencrypted wallet.
assert ( false ) ;
2011-07-08 09:08:27 -04:00
}
Add wallet privkey encryption.
This commit adds support for ckeys, or enCrypted private keys, to the wallet.
All keys are stored in memory in their encrypted form and thus the passphrase
is required from the user to spend coins, or to create new addresses.
Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
a random salt.
By default, the user's wallet remains unencrypted until they call the RPC
command encryptwallet <passphrase> or, from the GUI menu, Options->
Encrypt Wallet.
When the user is attempting to call RPC functions which require the password
to unlock the wallet, an error will be returned unless they call
walletpassphrase <passphrase> <time to keep key in memory> first.
A keypoolrefill command has been added which tops up the users keypool
(requiring the passphrase via walletpassphrase first).
keypoolsize has been added to the output of getinfo to show the user the
number of keys left before they need to specify their passphrase (and call
keypoolrefill).
Note that walletpassphrase will automatically fill keypool in a separate
thread which it spawns when the passphrase is set. This could cause some
delays in other threads waiting for locks on the wallet passphrase, including
one which could cause the passphrase to be stored longer than expected,
however it will not allow the passphrase to be used longer than expected as
ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
as the specified lock time has arrived.
When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
returns vchDefaultKey, meaning miners may start to generate many blocks to
vchDefaultKey instead of a new key each time.
A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
allow the user to change their password via RPC.
Whenever keying material (unencrypted private keys, the user's passphrase,
the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
is made to mlock/VirtualLock that memory before storing the keying material.
This is not true in several (commented) cases where mlock/VirtualLocking the
memory is not possible.
Although encryption of private keys in memory can be very useful on desktop
systems (as some small amount of protection against stupid viruses), on an
RPC server, the password is entered fairly insecurely. Thus, the only main
advantage encryption has for RPC servers is for RPC servers that do not spend
coins, except in rare cases, eg. a webserver of a merchant which only receives
payment except for cases of manual intervention.
Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
for all their input.
Conflicts:
src/wallet.cpp
2011-07-08 09:47:35 -04:00
2017-12-08 08:39:22 -03:00
delete encrypted_batch ;
encrypted_batch = nullptr ;
2017-03-08 09:08:26 -03:00
2011-11-17 16:01:25 -03:00
Lock ( ) ;
Unlock ( strWalletPassphrase ) ;
2016-07-21 15:19:02 -04:00
2019-07-16 15:22:08 -04:00
// If we are using descriptors, make new descriptors with a new seed
if ( IsWalletFlagSet ( WALLET_FLAG_DESCRIPTORS ) & & ! IsWalletFlagSet ( WALLET_FLAG_BLANK_WALLET ) ) {
SetupDescriptorScriptPubKeyMans ( ) ;
} else if ( auto spk_man = GetLegacyScriptPubKeyMan ( ) ) {
// if we are using HD, replace the HD seed with a new one
2019-10-07 15:11:34 -03:00
if ( spk_man - > IsHDEnabled ( ) ) {
2019-11-05 12:53:07 -03:00
if ( ! spk_man - > SetupGeneration ( true ) ) {
return false ;
}
2019-10-07 15:11:34 -03:00
}
2016-07-21 15:19:02 -04:00
}
Add wallet privkey encryption.
This commit adds support for ckeys, or enCrypted private keys, to the wallet.
All keys are stored in memory in their encrypted form and thus the passphrase
is required from the user to spend coins, or to create new addresses.
Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
a random salt.
By default, the user's wallet remains unencrypted until they call the RPC
command encryptwallet <passphrase> or, from the GUI menu, Options->
Encrypt Wallet.
When the user is attempting to call RPC functions which require the password
to unlock the wallet, an error will be returned unless they call
walletpassphrase <passphrase> <time to keep key in memory> first.
A keypoolrefill command has been added which tops up the users keypool
(requiring the passphrase via walletpassphrase first).
keypoolsize has been added to the output of getinfo to show the user the
number of keys left before they need to specify their passphrase (and call
keypoolrefill).
Note that walletpassphrase will automatically fill keypool in a separate
thread which it spawns when the passphrase is set. This could cause some
delays in other threads waiting for locks on the wallet passphrase, including
one which could cause the passphrase to be stored longer than expected,
however it will not allow the passphrase to be used longer than expected as
ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
as the specified lock time has arrived.
When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
returns vchDefaultKey, meaning miners may start to generate many blocks to
vchDefaultKey instead of a new key each time.
A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
allow the user to change their password via RPC.
Whenever keying material (unencrypted private keys, the user's passphrase,
the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
is made to mlock/VirtualLock that memory before storing the keying material.
This is not true in several (commented) cases where mlock/VirtualLocking the
memory is not possible.
Although encryption of private keys in memory can be very useful on desktop
systems (as some small amount of protection against stupid viruses), on an
RPC server, the password is entered fairly insecurely. Thus, the only main
advantage encryption has for RPC servers is for RPC servers that do not spend
coins, except in rare cases, eg. a webserver of a merchant which only receives
payment except for cases of manual intervention.
Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
for all their input.
Conflicts:
src/wallet.cpp
2011-07-08 09:47:35 -04:00
Lock ( ) ;
2011-08-26 15:37:23 -03:00
2011-11-10 23:12:46 -03:00
// Need to completely rewrite the wallet file; if we don't, bdb might keep
// bits of the unencrypted private key in slack space in the database file.
2020-09-19 20:25:45 -03:00
GetDatabase ( ) . Rewrite ( ) ;
2012-05-05 10:07:14 -04:00
2018-02-20 18:08:36 -03:00
// BDB seems to have a bad habit of writing old data into
// slack space in .dat files; that is bad if the old data is
// unencrypted private keys. So:
2020-09-19 20:25:45 -03:00
GetDatabase ( ) . ReloadDbEnv ( ) ;
2018-02-20 18:08:36 -03:00
2011-11-10 23:12:46 -03:00
}
2012-05-06 13:40:58 -04:00
NotifyStatusChanged ( this ) ;
2011-11-10 17:29:23 -03:00
Add wallet privkey encryption.
This commit adds support for ckeys, or enCrypted private keys, to the wallet.
All keys are stored in memory in their encrypted form and thus the passphrase
is required from the user to spend coins, or to create new addresses.
Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
a random salt.
By default, the user's wallet remains unencrypted until they call the RPC
command encryptwallet <passphrase> or, from the GUI menu, Options->
Encrypt Wallet.
When the user is attempting to call RPC functions which require the password
to unlock the wallet, an error will be returned unless they call
walletpassphrase <passphrase> <time to keep key in memory> first.
A keypoolrefill command has been added which tops up the users keypool
(requiring the passphrase via walletpassphrase first).
keypoolsize has been added to the output of getinfo to show the user the
number of keys left before they need to specify their passphrase (and call
keypoolrefill).
Note that walletpassphrase will automatically fill keypool in a separate
thread which it spawns when the passphrase is set. This could cause some
delays in other threads waiting for locks on the wallet passphrase, including
one which could cause the passphrase to be stored longer than expected,
however it will not allow the passphrase to be used longer than expected as
ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
as the specified lock time has arrived.
When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
returns vchDefaultKey, meaning miners may start to generate many blocks to
vchDefaultKey instead of a new key each time.
A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
allow the user to change their password via RPC.
Whenever keying material (unencrypted private keys, the user's passphrase,
the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
is made to mlock/VirtualLock that memory before storing the keying material.
This is not true in several (commented) cases where mlock/VirtualLocking the
memory is not possible.
Although encryption of private keys in memory can be very useful on desktop
systems (as some small amount of protection against stupid viruses), on an
RPC server, the password is entered fairly insecurely. Thus, the only main
advantage encryption has for RPC servers is for RPC servers that do not spend
coins, except in rare cases, eg. a webserver of a merchant which only receives
payment except for cases of manual intervention.
Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
for all their input.
Conflicts:
src/wallet.cpp
2011-07-08 09:47:35 -04:00
return true ;
2011-06-26 13:23:24 -04:00
}
2016-09-09 23:21:44 -03:00
DBErrors CWallet : : ReorderTransactions ( )
{
2016-09-28 12:57:25 -03:00
LOCK ( cs_wallet ) ;
2020-09-19 20:25:45 -03:00
WalletBatch batch ( GetDatabase ( ) ) ;
2016-09-28 12:57:25 -03:00
// Old wallets didn't have any defined order for transactions
// Probably a bad idea to change the output of this
2018-07-31 12:23:26 -04:00
// First: get all CWalletTx into a sorted-by-time multimap.
typedef std : : multimap < int64_t , CWalletTx * > TxItems ;
2016-09-28 12:57:25 -03:00
TxItems txByTime ;
2017-06-04 16:02:43 -04:00
for ( auto & entry : mapWallet )
2016-09-28 12:57:25 -03:00
{
2017-06-04 16:02:43 -04:00
CWalletTx * wtx = & entry . second ;
2018-07-31 12:23:26 -04:00
txByTime . insert ( std : : make_pair ( wtx - > nTimeReceived , wtx ) ) ;
2016-09-28 12:57:25 -03:00
}
nOrderPosNext = 0 ;
std : : vector < int64_t > nOrderPosOffsets ;
for ( TxItems : : iterator it = txByTime . begin ( ) ; it ! = txByTime . end ( ) ; + + it )
{
2018-07-31 12:23:26 -04:00
CWalletTx * const pwtx = ( * it ) . second ;
int64_t & nOrderPos = pwtx - > nOrderPos ;
2016-09-28 12:57:25 -03:00
if ( nOrderPos = = - 1 )
{
nOrderPos = nOrderPosNext + + ;
nOrderPosOffsets . push_back ( nOrderPos ) ;
2018-07-31 12:23:26 -04:00
if ( ! batch . WriteTx ( * pwtx ) )
return DBErrors : : LOAD_FAIL ;
2016-09-28 12:57:25 -03:00
}
else
{
int64_t nOrderPosOff = 0 ;
2017-06-01 21:18:57 -04:00
for ( const int64_t & nOffsetStart : nOrderPosOffsets )
2016-09-28 12:57:25 -03:00
{
if ( nOrderPos > = nOffsetStart )
+ + nOrderPosOff ;
}
nOrderPos + = nOrderPosOff ;
nOrderPosNext = std : : max ( nOrderPosNext , nOrderPos + 1 ) ;
if ( ! nOrderPosOff )
continue ;
// Since we're changing the order, write it back
2018-07-31 12:23:26 -04:00
if ( ! batch . WriteTx ( * pwtx ) )
return DBErrors : : LOAD_FAIL ;
2016-09-28 12:57:25 -03:00
}
}
2017-12-08 08:39:22 -03:00
batch . WriteOrderPosNext ( nOrderPosNext ) ;
2016-09-28 12:57:25 -03:00
2018-03-09 11:03:40 -03:00
return DBErrors : : LOAD_OK ;
2016-09-09 23:21:44 -03:00
}
2019-02-18 18:09:45 -03:00
int64_t CWallet : : IncOrderPosNext ( WalletBatch * batch )
2012-09-08 01:55:36 -03:00
{
2019-02-18 18:09:45 -03:00
AssertLockHeld ( cs_wallet ) ;
2013-04-13 02:13:08 -03:00
int64_t nRet = nOrderPosNext + + ;
2017-12-08 08:39:22 -03:00
if ( batch ) {
batch - > WriteOrderPosNext ( nOrderPosNext ) ;
2012-11-13 19:52:37 -03:00
} else {
2020-09-19 20:25:45 -03:00
WalletBatch ( GetDatabase ( ) ) . WriteOrderPosNext ( nOrderPosNext ) ;
2012-11-13 19:52:37 -03:00
}
2012-09-08 01:55:36 -03:00
return nRet ;
}
2011-07-13 05:56:38 -04:00
void CWallet : : MarkDirty ( )
{
{
2012-04-06 13:39:12 -03:00
LOCK ( cs_wallet ) ;
2017-06-01 21:28:42 -04:00
for ( std : : pair < const uint256 , CWalletTx > & item : mapWallet )
2011-07-13 05:56:38 -04:00
item . second . MarkDirty ( ) ;
}
}
2016-12-09 15:45:27 -03:00
bool CWallet : : MarkReplaced ( const uint256 & originalHash , const uint256 & newHash )
{
LOCK ( cs_wallet ) ;
auto mi = mapWallet . find ( originalHash ) ;
// There is a bug if MarkReplaced is not called on an existing wallet transaction.
assert ( mi ! = mapWallet . end ( ) ) ;
CWalletTx & wtx = ( * mi ) . second ;
// Ensure for now that we're not overwriting data
assert ( wtx . mapValue . count ( " replaced_by_txid " ) = = 0 ) ;
wtx . mapValue [ " replaced_by_txid " ] = newHash . ToString ( ) ;
2020-05-01 11:36:17 -04:00
// Refresh mempool status without waiting for transactionRemovedFromMempool
2021-06-28 05:15:12 -04:00
RefreshMempoolStatus ( wtx , chain ( ) ) ;
2020-05-01 11:36:17 -04:00
2020-09-19 20:25:45 -03:00
WalletBatch batch ( GetDatabase ( ) ) ;
2016-12-09 15:45:27 -03:00
bool success = true ;
2017-12-08 08:39:22 -03:00
if ( ! batch . WriteTx ( wtx ) ) {
2018-06-15 19:02:52 -04:00
WalletLogPrintf ( " %s: Updating batch tx %s failed \n " , __func__ , wtx . GetHash ( ) . ToString ( ) ) ;
2016-12-09 15:45:27 -03:00
success = false ;
}
2021-06-28 04:25:29 -04:00
NotifyTransactionChanged ( originalHash , CT_UPDATED ) ;
2016-12-09 15:45:27 -03:00
return success ;
}
2020-02-21 18:15:13 -03:00
void CWallet : : SetSpentKeyState ( WalletBatch & batch , const uint256 & hash , unsigned int n , bool used , std : : set < CTxDestination > & tx_destinations )
2018-09-13 01:53:19 -03:00
{
2019-11-02 13:14:36 -03:00
AssertLockHeld ( cs_wallet ) ;
2018-09-13 01:53:19 -03:00
const CWalletTx * srctx = GetWalletTx ( hash ) ;
if ( ! srctx ) return ;
CTxDestination dst ;
if ( ExtractDestination ( srctx - > tx - > vout [ n ] . scriptPubKey , dst ) ) {
2019-10-07 15:11:34 -03:00
if ( IsMine ( dst ) ) {
2020-04-12 13:40:43 -04:00
if ( used ! = IsAddressUsed ( dst ) ) {
if ( used ) {
2019-12-31 15:55:18 -03:00
tx_destinations . insert ( dst ) ;
}
2020-04-12 13:40:43 -04:00
SetAddressUsed ( batch , dst , used ) ;
2018-09-13 01:53:19 -03:00
}
}
}
}
2020-02-21 18:15:13 -03:00
bool CWallet : : IsSpentKey ( const uint256 & hash , unsigned int n ) const
2018-09-13 01:53:19 -03:00
{
2019-11-27 12:56:04 -03:00
AssertLockHeld ( cs_wallet ) ;
2018-09-13 01:53:19 -03:00
const CWalletTx * srctx = GetWalletTx ( hash ) ;
2019-11-27 12:56:04 -03:00
if ( srctx ) {
assert ( srctx - > tx - > vout . size ( ) > n ) ;
2020-03-31 16:30:04 -03:00
CTxDestination dest ;
if ( ! ExtractDestination ( srctx - > tx - > vout [ n ] . scriptPubKey , dest ) ) {
return false ;
}
2020-04-12 13:40:43 -04:00
if ( IsAddressUsed ( dest ) ) {
2020-03-31 16:30:04 -03:00
return true ;
}
if ( IsLegacy ( ) ) {
LegacyScriptPubKeyMan * spk_man = GetLegacyScriptPubKeyMan ( ) ;
assert ( spk_man ! = nullptr ) ;
for ( const auto & keyid : GetAffectedKeys ( srctx - > tx - > vout [ n ] . scriptPubKey , * spk_man ) ) {
WitnessV0KeyHash wpkh_dest ( keyid ) ;
2020-04-12 13:40:43 -04:00
if ( IsAddressUsed ( wpkh_dest ) ) {
2020-03-31 16:30:04 -03:00
return true ;
}
ScriptHash sh_wpkh_dest ( GetScriptForDestination ( wpkh_dest ) ) ;
2020-04-12 13:40:43 -04:00
if ( IsAddressUsed ( sh_wpkh_dest ) ) {
2020-03-31 16:30:04 -03:00
return true ;
}
PKHash pkh_dest ( keyid ) ;
2020-04-12 13:40:43 -04:00
if ( IsAddressUsed ( pkh_dest ) ) {
2020-03-31 16:30:04 -03:00
return true ;
}
2019-11-27 12:56:04 -03:00
}
}
}
return false ;
2018-09-13 01:53:19 -03:00
}
2020-12-07 13:29:48 -03:00
CWalletTx * CWallet : : AddToWallet ( CTransactionRef tx , const CWalletTx : : Confirmation & confirm , const UpdateWalletTxFn & update_wtx , bool fFlushOnClose , bool rescanning_old_block )
2011-06-26 13:23:24 -04:00
{
2016-06-08 00:41:03 -04:00
LOCK ( cs_wallet ) ;
2020-09-19 20:25:45 -03:00
WalletBatch batch ( GetDatabase ( ) , fFlushOnClose ) ;
2016-06-08 00:41:03 -04:00
2016-12-19 13:25:15 -03:00
uint256 hash = tx - > GetHash ( ) ;
2014-02-13 22:12:51 -03:00
2018-09-13 01:53:19 -03:00
if ( IsWalletFlagSet ( WALLET_FLAG_AVOID_REUSE ) ) {
// Mark used destinations
2019-12-31 15:55:18 -03:00
std : : set < CTxDestination > tx_destinations ;
2016-12-19 13:25:15 -03:00
for ( const CTxIn & txin : tx - > vin ) {
2018-09-13 01:53:19 -03:00
const COutPoint & op = txin . prevout ;
2020-02-21 18:15:13 -03:00
SetSpentKeyState ( batch , op . hash , op . n , true , tx_destinations ) ;
2018-09-13 01:53:19 -03:00
}
2019-12-31 15:55:18 -03:00
MarkDestinationsDirty ( tx_destinations ) ;
2018-09-13 01:53:19 -03:00
}
2016-06-08 00:25:31 -04:00
// Inserts only if not already there, returns tx inserted or tx found
2021-02-12 20:01:22 -03:00
auto ret = mapWallet . emplace ( std : : piecewise_construct , std : : forward_as_tuple ( hash ) , std : : forward_as_tuple ( tx ) ) ;
2016-06-08 00:25:31 -04:00
CWalletTx & wtx = ( * ret . first ) . second ;
bool fInsertedNew = ret . second ;
2016-12-19 13:25:15 -03:00
bool fUpdated = update_wtx & & update_wtx ( wtx , fInsertedNew ) ;
2018-06-11 14:09:16 -04:00
if ( fInsertedNew ) {
2016-12-19 13:25:15 -03:00
wtx . m_confirm = confirm ;
2017-07-28 22:54:31 -04:00
wtx . nTimeReceived = chain ( ) . getAdjustedTime ( ) ;
2017-12-08 08:39:22 -03:00
wtx . nOrderPos = IncOrderPosNext ( & batch ) ;
2018-07-31 12:23:26 -04:00
wtx . m_it_wtxOrdered = wtxOrdered . insert ( std : : make_pair ( wtx . nOrderPos , & wtx ) ) ;
2020-12-07 13:29:48 -03:00
wtx . nTimeSmart = ComputeTimeSmart ( wtx , rescanning_old_block ) ;
2021-09-22 09:17:55 -03:00
AddToSpends ( hash , & batch ) ;
2016-06-08 00:25:31 -04:00
}
2011-06-26 13:23:24 -04:00
2016-06-08 00:25:31 -04:00
if ( ! fInsertedNew )
{
2016-12-19 13:25:15 -03:00
if ( confirm . status ! = wtx . m_confirm . status ) {
wtx . m_confirm . status = confirm . status ;
wtx . m_confirm . nIndex = confirm . nIndex ;
wtx . m_confirm . hashBlock = confirm . hashBlock ;
wtx . m_confirm . block_height = confirm . block_height ;
2016-06-08 00:25:31 -04:00
fUpdated = true ;
2019-08-12 18:12:12 -04:00
} else {
2016-12-19 13:25:15 -03:00
assert ( wtx . m_confirm . nIndex = = confirm . nIndex ) ;
assert ( wtx . m_confirm . hashBlock = = confirm . hashBlock ) ;
assert ( wtx . m_confirm . block_height = = confirm . block_height ) ;
2016-06-08 00:25:31 -04:00
}
2017-09-03 09:43:35 -03:00
// If we have a witness-stripped version of this transaction, and we
// see a new version with a witness, then we must be upgrading a pre-segwit
// wallet. Store the new version of the transaction with the witness,
// as the stripped-version must be invalid.
// TODO: Store all versions of the transaction, instead of just one.
2016-12-19 13:25:15 -03:00
if ( tx - > HasWitness ( ) & & ! wtx . tx - > HasWitness ( ) ) {
wtx . SetTx ( tx ) ;
2017-09-03 09:43:35 -03:00
fUpdated = true ;
}
2016-06-08 00:25:31 -04:00
}
2011-06-26 13:23:24 -04:00
2016-06-08 00:25:31 -04:00
//// debug print
2016-12-19 13:25:15 -03:00
WalletLogPrintf ( " AddToWallet %s %s%s \n " , hash . ToString ( ) , ( fInsertedNew ? " new " : " " ) , ( fUpdated ? " update " : " " ) ) ;
2011-06-26 13:23:24 -04:00
2016-06-08 00:25:31 -04:00
// Write to disk
if ( fInsertedNew | | fUpdated )
2017-12-08 08:39:22 -03:00
if ( ! batch . WriteTx ( wtx ) )
2016-12-19 13:25:15 -03:00
return nullptr ;
2013-05-26 14:17:18 -04:00
2016-06-08 00:25:31 -04:00
// Break debit/credit balance caches:
wtx . MarkDirty ( ) ;
2011-06-26 13:23:24 -04:00
2016-06-08 00:25:31 -04:00
// Notify UI of new or updated transaction
2021-06-28 04:25:29 -04:00
NotifyTransactionChanged ( hash , fInsertedNew ? CT_NEW : CT_UPDATED ) ;
2012-11-03 11:58:41 -03:00
2019-07-05 12:30:15 -04:00
# if HAVE_SYSTEM
2016-06-08 00:25:31 -04:00
// notify an external script when a wallet transaction comes in or is updated
2017-08-01 15:17:40 -04:00
std : : string strCmd = gArgs . GetArg ( " -walletnotify " , " " ) ;
2012-11-03 11:58:41 -03:00
2017-07-26 19:09:05 -04:00
if ( ! strCmd . empty ( ) )
2016-06-08 00:25:31 -04:00
{
2016-12-19 13:25:15 -03:00
boost : : replace_all ( strCmd , " %s " , hash . GetHex ( ) ) ;
2021-03-15 11:26:05 -03:00
if ( confirm . status = = CWalletTx : : Status : : CONFIRMED )
{
boost : : replace_all ( strCmd , " %b " , confirm . hashBlock . GetHex ( ) ) ;
boost : : replace_all ( strCmd , " %h " , ToString ( confirm . block_height ) ) ;
} else {
boost : : replace_all ( strCmd , " %b " , " unconfirmed " ) ;
boost : : replace_all ( strCmd , " %h " , " -1 " ) ;
}
2018-05-29 09:37:53 -04:00
# ifndef WIN32
// Substituting the wallet name isn't currently supported on windows
// because windows shell escaping has not been implemented yet:
// https://github.com/bitcoin/bitcoin/pull/13339#issuecomment-537384875
// A few ways it could be implemented in the future are described in:
// https://github.com/bitcoin/bitcoin/pull/13339#issuecomment-461288094
boost : : replace_all ( strCmd , " %w " , ShellEscape ( GetName ( ) ) ) ;
# endif
2018-02-07 21:19:34 -03:00
std : : thread t ( runCommand , strCmd ) ;
t . detach ( ) ; // thread runs free
2016-06-08 00:25:31 -04:00
}
2019-03-14 07:30:37 -03:00
# endif
2016-06-08 00:25:31 -04:00
2016-12-19 13:25:15 -03:00
return & wtx ;
2016-06-08 00:25:31 -04:00
}
2019-11-08 11:03:51 -03:00
bool CWallet : : LoadToWallet ( const uint256 & hash , const UpdateWalletTxFn & fill_wtx )
2016-06-08 00:25:31 -04:00
{
2021-02-12 20:01:22 -03:00
const auto & ins = mapWallet . emplace ( std : : piecewise_construct , std : : forward_as_tuple ( hash ) , std : : forward_as_tuple ( nullptr ) ) ;
2019-11-08 11:03:51 -03:00
CWalletTx & wtx = ins . first - > second ;
if ( ! fill_wtx ( wtx , ins . second ) ) {
return false ;
}
2019-07-15 18:20:12 -04:00
// If wallet doesn't have a chain (e.g wallet-tool), don't bother to update txn.
if ( HaveChain ( ) ) {
2020-06-09 18:03:26 -04:00
bool active ;
int height ;
if ( chain ( ) . findBlock ( wtx . m_confirm . hashBlock , FoundBlock ( ) . inActiveChain ( active ) . height ( height ) ) & & active ) {
2019-04-20 11:22:59 -04:00
// Update cached block height variable since it not stored in the
// serialized transaction.
2020-06-09 18:03:26 -04:00
wtx . m_confirm . block_height = height ;
2019-11-08 11:03:51 -03:00
} else if ( wtx . isConflicted ( ) | | wtx . isConfirmed ( ) ) {
2019-04-20 11:22:59 -04:00
// If tx block (or conflicting block) was reorged out of chain
// while the wallet was shutdown, change tx status to UNCONFIRMED
// and reset block height, hash, and index. ABANDONED tx don't have
// associated blocks and don't need to be updated. The case where a
// transaction was reorged out while online and then reconfirmed
// while offline is covered by the rescan logic.
2019-11-08 11:03:51 -03:00
wtx . setUnconfirmed ( ) ;
wtx . m_confirm . hashBlock = uint256 ( ) ;
wtx . m_confirm . block_height = 0 ;
wtx . m_confirm . nIndex = 0 ;
2019-08-22 13:16:40 -04:00
}
}
2018-06-11 14:09:16 -04:00
if ( /* insertion took place */ ins . second ) {
2018-07-31 12:23:26 -04:00
wtx . m_it_wtxOrdered = wtxOrdered . insert ( std : : make_pair ( wtx . nOrderPos , & wtx ) ) ;
2018-06-11 14:09:16 -04:00
}
2016-06-08 00:25:31 -04:00
AddToSpends ( hash ) ;
2017-06-01 21:18:57 -04:00
for ( const CTxIn & txin : wtx . tx - > vin ) {
2017-08-13 11:04:57 -03:00
auto it = mapWallet . find ( txin . prevout . hash ) ;
if ( it ! = mapWallet . end ( ) ) {
CWalletTx & prevtx = it - > second ;
2019-08-12 18:12:12 -04:00
if ( prevtx . isConflicted ( ) ) {
2019-04-20 11:22:59 -04:00
MarkConflicted ( prevtx . m_confirm . hashBlock , prevtx . m_confirm . block_height , wtx . GetHash ( ) ) ;
2016-06-08 00:25:31 -04:00
}
}
2012-05-05 10:07:14 -04:00
}
2019-11-08 11:03:51 -03:00
return true ;
2011-06-26 13:23:24 -04:00
}
2020-12-07 13:29:48 -03:00
bool CWallet : : AddToWalletIfInvolvingMe ( const CTransactionRef & ptx , CWalletTx : : Confirmation confirm , bool fUpdate , bool rescanning_old_block )
2011-06-26 13:23:24 -04:00
{
2017-03-06 20:21:27 -03:00
const CTransaction & tx = * ptx ;
2011-06-26 13:23:24 -04:00
{
2014-03-09 08:41:22 -03:00
AssertLockHeld ( cs_wallet ) ;
2015-11-26 14:42:07 -03:00
2019-10-24 13:53:57 -03:00
if ( ! confirm . hashBlock . IsNull ( ) ) {
2017-06-01 21:18:57 -04:00
for ( const CTxIn & txin : tx . vin ) {
2015-11-26 14:42:07 -03:00
std : : pair < TxSpends : : const_iterator , TxSpends : : const_iterator > range = mapTxSpends . equal_range ( txin . prevout ) ;
while ( range . first ! = range . second ) {
if ( range . first - > second ! = tx . GetHash ( ) ) {
2019-10-24 13:53:57 -03:00
WalletLogPrintf ( " Transaction %s (in block %s) conflicts with wallet transaction %s (both spend %s:%i) \n " , tx . GetHash ( ) . ToString ( ) , confirm . hashBlock . ToString ( ) , range . first - > second . ToString ( ) , range . first - > first . hash . ToString ( ) , range . first - > first . n ) ;
2019-04-20 11:22:59 -04:00
MarkConflicted ( confirm . hashBlock , confirm . block_height , range . first - > second ) ;
2015-11-26 14:42:07 -03:00
}
range . first + + ;
}
}
}
2014-09-06 15:59:59 -04:00
bool fExisted = mapWallet . count ( tx . GetHash ( ) ) ! = 0 ;
2011-08-26 15:37:23 -03:00
if ( fExisted & & ! fUpdate ) return false ;
2014-07-17 08:09:46 -04:00
if ( fExisted | | IsMine ( tx ) | | IsFromMe ( tx ) )
2011-08-26 15:37:23 -03:00
{
2017-07-18 15:49:56 -04:00
/* Check if any keys in the wallet keypool that were supposed to be unused
* have appeared in a new transaction . If so , remove those keys from the keypool .
* This can happen when restoring an old wallet backup that does not contain
* the mostly recently created transactions from newer versions of the wallet .
*/
// loop though all outputs
for ( const CTxOut & txout : tx . vout ) {
2019-10-07 15:11:34 -03:00
for ( const auto & spk_man_pair : m_spk_managers ) {
spk_man_pair . second - > MarkUnusedAddresses ( txout . scriptPubKey ) ;
2017-07-18 15:49:56 -04:00
}
}
2019-08-12 18:12:12 -04:00
// Block disconnection override an abandoned tx as unconfirmed
// which means user may have to call abandontransaction again
2020-12-07 13:29:48 -03:00
return AddToWallet ( MakeTransactionRef ( tx ) , confirm , /* update_wtx= */ nullptr , /* fFlushOnClose= */ false , rescanning_old_block ) ;
2011-08-26 15:37:23 -03:00
}
2011-06-26 13:23:24 -04:00
}
return false ;
}
2017-04-28 15:10:21 -03:00
bool CWallet : : TransactionCanBeAbandoned ( const uint256 & hashTx ) const
{
2017-07-26 10:23:01 -04:00
LOCK ( cs_wallet ) ;
2017-04-28 15:10:21 -03:00
const CWalletTx * wtx = GetWalletTx ( hashTx ) ;
2021-02-12 20:01:22 -03:00
return wtx & & ! wtx - > isAbandoned ( ) & & GetTxDepthInMainChain ( * wtx ) = = 0 & & ! wtx - > InMempool ( ) ;
2017-04-28 15:10:21 -03:00
}
2018-07-12 16:02:31 -04:00
void CWallet : : MarkInputsDirty ( const CTransactionRef & tx )
{
for ( const CTxIn & txin : tx - > vin ) {
auto it = mapWallet . find ( txin . prevout . hash ) ;
if ( it ! = mapWallet . end ( ) ) {
it - > second . MarkDirty ( ) ;
}
}
}
2019-04-29 09:52:01 -04:00
bool CWallet : : AbandonTransaction ( const uint256 & hashTx )
2016-01-07 18:31:27 -03:00
{
2017-07-26 10:23:01 -04:00
LOCK ( cs_wallet ) ;
2016-01-07 18:31:27 -03:00
2020-09-19 20:25:45 -03:00
WalletBatch batch ( GetDatabase ( ) ) ;
2016-01-07 18:31:27 -03:00
std : : set < uint256 > todo ;
std : : set < uint256 > done ;
// Can't mark abandoned if confirmed or in mempool
2017-08-13 11:04:57 -03:00
auto it = mapWallet . find ( hashTx ) ;
assert ( it ! = mapWallet . end ( ) ) ;
2020-12-06 13:11:39 -03:00
const CWalletTx & origtx = it - > second ;
2021-02-12 20:01:22 -03:00
if ( GetTxDepthInMainChain ( origtx ) ! = 0 | | origtx . InMempool ( ) ) {
2016-01-07 18:31:27 -03:00
return false ;
}
todo . insert ( hashTx ) ;
while ( ! todo . empty ( ) ) {
uint256 now = * todo . begin ( ) ;
todo . erase ( now ) ;
done . insert ( now ) ;
2017-08-13 11:04:57 -03:00
auto it = mapWallet . find ( now ) ;
assert ( it ! = mapWallet . end ( ) ) ;
CWalletTx & wtx = it - > second ;
2021-02-12 20:01:22 -03:00
int currentconfirm = GetTxDepthInMainChain ( wtx ) ;
2016-01-07 18:31:27 -03:00
// If the orig tx was not in block, none of its spends can be
assert ( currentconfirm < = 0 ) ;
// if (currentconfirm < 0) {Tx and spends are already conflicted, no need to abandon}
if ( currentconfirm = = 0 & & ! wtx . isAbandoned ( ) ) {
// If the orig tx was not in block/mempool, none of its spends can be in mempool
assert ( ! wtx . InMempool ( ) ) ;
wtx . setAbandoned ( ) ;
wtx . MarkDirty ( ) ;
2017-12-08 08:39:22 -03:00
batch . WriteTx ( wtx ) ;
2021-06-28 04:25:29 -04:00
NotifyTransactionChanged ( wtx . GetHash ( ) , CT_UPDATED ) ;
2016-01-07 18:31:27 -03:00
// Iterate over all its outputs, and mark transactions in the wallet that spend them abandoned too
2018-07-12 17:19:00 -04:00
TxSpends : : const_iterator iter = mapTxSpends . lower_bound ( COutPoint ( now , 0 ) ) ;
2016-01-07 18:31:27 -03:00
while ( iter ! = mapTxSpends . end ( ) & & iter - > first . hash = = now ) {
if ( ! done . count ( iter - > second ) ) {
todo . insert ( iter - > second ) ;
}
iter + + ;
}
// If a transaction changes 'conflicted' state, that changes the balance
// available of the outputs it spends. So force those to be recomputed
2018-07-12 16:02:31 -04:00
MarkInputsDirty ( wtx . tx ) ;
2016-01-07 18:31:27 -03:00
}
}
return true ;
}
2019-04-20 11:22:59 -04:00
void CWallet : : MarkConflicted ( const uint256 & hashBlock , int conflicting_height , const uint256 & hashTx )
2015-11-26 14:42:07 -03:00
{
2017-07-26 10:23:01 -04:00
LOCK ( cs_wallet ) ;
2015-11-26 14:42:07 -03:00
2019-04-29 10:18:50 -04:00
int conflictconfirms = ( m_last_block_processed_height - conflicting_height + 1 ) * - 1 ;
2016-02-09 16:23:09 -03:00
// If number of conflict confirms cannot be determined, this means
// that the block is still unknown or not yet part of the main chain,
// for example when loading the wallet during a reindex. Do nothing in that
// case.
if ( conflictconfirms > = 0 )
return ;
2015-11-26 14:42:07 -03:00
// Do not flush the wallet here for performance reasons
2020-09-19 20:25:45 -03:00
WalletBatch batch ( GetDatabase ( ) , false ) ;
2015-11-26 14:42:07 -03:00
2016-01-07 18:31:12 -03:00
std : : set < uint256 > todo ;
2015-11-26 14:42:07 -03:00
std : : set < uint256 > done ;
2016-01-07 18:31:12 -03:00
todo . insert ( hashTx ) ;
2015-11-26 14:42:07 -03:00
while ( ! todo . empty ( ) ) {
2016-01-07 18:31:12 -03:00
uint256 now = * todo . begin ( ) ;
todo . erase ( now ) ;
2015-11-26 14:42:07 -03:00
done . insert ( now ) ;
2017-08-13 11:04:57 -03:00
auto it = mapWallet . find ( now ) ;
assert ( it ! = mapWallet . end ( ) ) ;
CWalletTx & wtx = it - > second ;
2021-02-12 20:01:22 -03:00
int currentconfirm = GetTxDepthInMainChain ( wtx ) ;
2015-11-26 14:42:07 -03:00
if ( conflictconfirms < currentconfirm ) {
// Block is 'more conflicted' than current confirm; update.
// Mark transaction as conflicted with this block.
2019-08-12 18:12:12 -04:00
wtx . m_confirm . nIndex = 0 ;
wtx . m_confirm . hashBlock = hashBlock ;
2019-04-20 11:22:59 -04:00
wtx . m_confirm . block_height = conflicting_height ;
2019-08-12 18:12:12 -04:00
wtx . setConflicted ( ) ;
2015-11-26 14:42:07 -03:00
wtx . MarkDirty ( ) ;
2017-12-08 08:39:22 -03:00
batch . WriteTx ( wtx ) ;
2015-11-26 14:42:07 -03:00
// Iterate over all its outputs, and mark transactions in the wallet that spend them conflicted too
TxSpends : : const_iterator iter = mapTxSpends . lower_bound ( COutPoint ( now , 0 ) ) ;
while ( iter ! = mapTxSpends . end ( ) & & iter - > first . hash = = now ) {
if ( ! done . count ( iter - > second ) ) {
2016-01-07 18:31:12 -03:00
todo . insert ( iter - > second ) ;
2015-11-26 14:42:07 -03:00
}
iter + + ;
}
2016-01-06 19:24:30 -03:00
// If a transaction changes 'conflicted' state, that changes the balance
// available of the outputs it spends. So force those to be recomputed
2018-07-12 16:02:31 -04:00
MarkInputsDirty ( wtx . tx ) ;
2015-11-26 14:42:07 -03:00
}
}
}
2020-12-07 13:29:48 -03:00
void CWallet : : SyncTransaction ( const CTransactionRef & ptx , CWalletTx : : Confirmation confirm , bool update_tx , bool rescanning_old_block )
2019-08-12 18:12:12 -04:00
{
2020-12-07 13:29:48 -03:00
if ( ! AddToWalletIfInvolvingMe ( ptx , confirm , update_tx , rescanning_old_block ) )
2014-02-15 18:38:28 -03:00
return ; // Not one of ours
// If a transaction changes 'conflicted' state, that changes the balance
// available of the outputs it spends. So force those to be
// recomputed, also:
2018-07-12 16:02:31 -04:00
MarkInputsDirty ( ptx ) ;
2013-10-19 13:34:06 -03:00
}
Add 'sequence' zmq publisher to track all block (dis)connects, mempool deltas
Using the zmq notifications to avoid excessive mempool polling can be difficult
given the current notifications available. It announces all transactions
being added to mempool or included in blocks, but announces no evictions
and gives no indication if the transaction is in the mempool or a block.
Block notifications for zmq are also substandard, in that it only announces
block tips, while all block transactions are still announced.
This commit adds a unified stream which can be used to closely track mempool:
1) getrawmempool to fill out mempool knowledge
2) if txhash is announced, add or remove from set
based on add/remove flag
3) if blockhash is announced, get block txn list,
remove from those transactions local view of mempool
4) if we drop a sequence number, go to (1)
The mempool sequence number starts at the value 1, and
increments each time a transaction enters the mempool,
or is evicted from the mempool for any reason, including
block inclusion. The mempool sequence number is published
via ZMQ for any transaction-related notification.
These features allow for ZMQ/RPC consumer to track mempool
state in a more exacting way, without unnecesarily polling
getrawmempool. See interface_zmq.py::test_mempool_sync for
example usage.
2020-09-04 11:55:58 -04:00
void CWallet : : transactionAddedToMempool ( const CTransactionRef & tx , uint64_t mempool_sequence ) {
2017-07-26 10:23:01 -04:00
LOCK ( cs_wallet ) ;
2020-05-22 16:30:07 -04:00
SyncTransaction ( tx , { CWalletTx : : Status : : UNCONFIRMED , /* block height */ 0 , /* block hash */ { } , /* index */ 0 } ) ;
Use callbacks to cache whether wallet transactions are in mempool
This avoid calling out to mempool state during coin selection,
balance calculation, etc. In the next commit we ensure all wallet
callbacks from CValidationInterface happen in the same queue,
serialized with each other. This helps to avoid re-introducing one
of the issues described in #9584 [1] by further disconnecting
wallet from current chain/mempool state.
Thanks to @morcos for the suggestion to do this.
Note that there are several race conditions introduced here:
* If a user calls sendrawtransaction from RPC, adding a
transaction which is "trusted" (ie from them) and pays them
change, it may not be immediately used by coin selection until
the notification callbacks finish running. No such race is
introduced in normal transaction-sending RPCs as this case is
explicitly handled.
* Until Block{Connected,Disconnected} and
TransactionAddedToMempool calls also run in the CSceduler
background thread, there is a race where
TransactionAddedToMempool might be called after a
Block{Connected,Disconnected} call happens.
* Wallet will write a new best chain from the SetBestChain
callback prior to having processed the transaction from that
block.
[1] "you could go to select coins, need to use 0-conf change, but
such 0-conf change may have been included in a block who's
callbacks have not yet been processed - resulting in thinking they
are not in mempool and, thus, not selectable."
2017-01-20 18:38:07 -03:00
2020-05-22 16:30:07 -04:00
auto it = mapWallet . find ( tx - > GetHash ( ) ) ;
Use callbacks to cache whether wallet transactions are in mempool
This avoid calling out to mempool state during coin selection,
balance calculation, etc. In the next commit we ensure all wallet
callbacks from CValidationInterface happen in the same queue,
serialized with each other. This helps to avoid re-introducing one
of the issues described in #9584 [1] by further disconnecting
wallet from current chain/mempool state.
Thanks to @morcos for the suggestion to do this.
Note that there are several race conditions introduced here:
* If a user calls sendrawtransaction from RPC, adding a
transaction which is "trusted" (ie from them) and pays them
change, it may not be immediately used by coin selection until
the notification callbacks finish running. No such race is
introduced in normal transaction-sending RPCs as this case is
explicitly handled.
* Until Block{Connected,Disconnected} and
TransactionAddedToMempool calls also run in the CSceduler
background thread, there is a race where
TransactionAddedToMempool might be called after a
Block{Connected,Disconnected} call happens.
* Wallet will write a new best chain from the SetBestChain
callback prior to having processed the transaction from that
block.
[1] "you could go to select coins, need to use 0-conf change, but
such 0-conf change may have been included in a block who's
callbacks have not yet been processed - resulting in thinking they
are not in mempool and, thus, not selectable."
2017-01-20 18:38:07 -03:00
if ( it ! = mapWallet . end ( ) ) {
2021-06-28 05:15:12 -04:00
RefreshMempoolStatus ( it - > second , chain ( ) ) ;
Use callbacks to cache whether wallet transactions are in mempool
This avoid calling out to mempool state during coin selection,
balance calculation, etc. In the next commit we ensure all wallet
callbacks from CValidationInterface happen in the same queue,
serialized with each other. This helps to avoid re-introducing one
of the issues described in #9584 [1] by further disconnecting
wallet from current chain/mempool state.
Thanks to @morcos for the suggestion to do this.
Note that there are several race conditions introduced here:
* If a user calls sendrawtransaction from RPC, adding a
transaction which is "trusted" (ie from them) and pays them
change, it may not be immediately used by coin selection until
the notification callbacks finish running. No such race is
introduced in normal transaction-sending RPCs as this case is
explicitly handled.
* Until Block{Connected,Disconnected} and
TransactionAddedToMempool calls also run in the CSceduler
background thread, there is a race where
TransactionAddedToMempool might be called after a
Block{Connected,Disconnected} call happens.
* Wallet will write a new best chain from the SetBestChain
callback prior to having processed the transaction from that
block.
[1] "you could go to select coins, need to use 0-conf change, but
such 0-conf change may have been included in a block who's
callbacks have not yet been processed - resulting in thinking they
are not in mempool and, thus, not selectable."
2017-01-20 18:38:07 -03:00
}
}
Add 'sequence' zmq publisher to track all block (dis)connects, mempool deltas
Using the zmq notifications to avoid excessive mempool polling can be difficult
given the current notifications available. It announces all transactions
being added to mempool or included in blocks, but announces no evictions
and gives no indication if the transaction is in the mempool or a block.
Block notifications for zmq are also substandard, in that it only announces
block tips, while all block transactions are still announced.
This commit adds a unified stream which can be used to closely track mempool:
1) getrawmempool to fill out mempool knowledge
2) if txhash is announced, add or remove from set
based on add/remove flag
3) if blockhash is announced, get block txn list,
remove from those transactions local view of mempool
4) if we drop a sequence number, go to (1)
The mempool sequence number starts at the value 1, and
increments each time a transaction enters the mempool,
or is evicted from the mempool for any reason, including
block inclusion. The mempool sequence number is published
via ZMQ for any transaction-related notification.
These features allow for ZMQ/RPC consumer to track mempool
state in a more exacting way, without unnecesarily polling
getrawmempool. See interface_zmq.py::test_mempool_sync for
example usage.
2020-09-04 11:55:58 -04:00
void CWallet : : transactionRemovedFromMempool ( const CTransactionRef & tx , MemPoolRemovalReason reason , uint64_t mempool_sequence ) {
Use callbacks to cache whether wallet transactions are in mempool
This avoid calling out to mempool state during coin selection,
balance calculation, etc. In the next commit we ensure all wallet
callbacks from CValidationInterface happen in the same queue,
serialized with each other. This helps to avoid re-introducing one
of the issues described in #9584 [1] by further disconnecting
wallet from current chain/mempool state.
Thanks to @morcos for the suggestion to do this.
Note that there are several race conditions introduced here:
* If a user calls sendrawtransaction from RPC, adding a
transaction which is "trusted" (ie from them) and pays them
change, it may not be immediately used by coin selection until
the notification callbacks finish running. No such race is
introduced in normal transaction-sending RPCs as this case is
explicitly handled.
* Until Block{Connected,Disconnected} and
TransactionAddedToMempool calls also run in the CSceduler
background thread, there is a race where
TransactionAddedToMempool might be called after a
Block{Connected,Disconnected} call happens.
* Wallet will write a new best chain from the SetBestChain
callback prior to having processed the transaction from that
block.
[1] "you could go to select coins, need to use 0-conf change, but
such 0-conf change may have been included in a block who's
callbacks have not yet been processed - resulting in thinking they
are not in mempool and, thus, not selectable."
2017-01-20 18:38:07 -03:00
LOCK ( cs_wallet ) ;
2020-05-22 16:30:07 -04:00
auto it = mapWallet . find ( tx - > GetHash ( ) ) ;
Use callbacks to cache whether wallet transactions are in mempool
This avoid calling out to mempool state during coin selection,
balance calculation, etc. In the next commit we ensure all wallet
callbacks from CValidationInterface happen in the same queue,
serialized with each other. This helps to avoid re-introducing one
of the issues described in #9584 [1] by further disconnecting
wallet from current chain/mempool state.
Thanks to @morcos for the suggestion to do this.
Note that there are several race conditions introduced here:
* If a user calls sendrawtransaction from RPC, adding a
transaction which is "trusted" (ie from them) and pays them
change, it may not be immediately used by coin selection until
the notification callbacks finish running. No such race is
introduced in normal transaction-sending RPCs as this case is
explicitly handled.
* Until Block{Connected,Disconnected} and
TransactionAddedToMempool calls also run in the CSceduler
background thread, there is a race where
TransactionAddedToMempool might be called after a
Block{Connected,Disconnected} call happens.
* Wallet will write a new best chain from the SetBestChain
callback prior to having processed the transaction from that
block.
[1] "you could go to select coins, need to use 0-conf change, but
such 0-conf change may have been included in a block who's
callbacks have not yet been processed - resulting in thinking they
are not in mempool and, thus, not selectable."
2017-01-20 18:38:07 -03:00
if ( it ! = mapWallet . end ( ) ) {
2021-06-28 05:15:12 -04:00
RefreshMempoolStatus ( it - > second , chain ( ) ) ;
Use callbacks to cache whether wallet transactions are in mempool
This avoid calling out to mempool state during coin selection,
balance calculation, etc. In the next commit we ensure all wallet
callbacks from CValidationInterface happen in the same queue,
serialized with each other. This helps to avoid re-introducing one
of the issues described in #9584 [1] by further disconnecting
wallet from current chain/mempool state.
Thanks to @morcos for the suggestion to do this.
Note that there are several race conditions introduced here:
* If a user calls sendrawtransaction from RPC, adding a
transaction which is "trusted" (ie from them) and pays them
change, it may not be immediately used by coin selection until
the notification callbacks finish running. No such race is
introduced in normal transaction-sending RPCs as this case is
explicitly handled.
* Until Block{Connected,Disconnected} and
TransactionAddedToMempool calls also run in the CSceduler
background thread, there is a race where
TransactionAddedToMempool might be called after a
Block{Connected,Disconnected} call happens.
* Wallet will write a new best chain from the SetBestChain
callback prior to having processed the transaction from that
block.
[1] "you could go to select coins, need to use 0-conf change, but
such 0-conf change may have been included in a block who's
callbacks have not yet been processed - resulting in thinking they
are not in mempool and, thus, not selectable."
2017-01-20 18:38:07 -03:00
}
2020-05-15 09:23:55 -04:00
// Handle transactions that were removed from the mempool because they
// conflict with transactions in a newly connected block.
if ( reason = = MemPoolRemovalReason : : CONFLICT ) {
2020-12-12 01:55:09 -03:00
// Trigger external -walletnotify notifications for these transactions.
// Set Status::UNCONFIRMED instead of Status::CONFLICTED for a few reasons:
2020-05-15 09:23:55 -04:00
//
// 1. The transactionRemovedFromMempool callback does not currently
// provide the conflicting block's hash and height, and for backwards
// compatibility reasons it may not be not safe to store conflicted
// wallet transactions with a null block hash. See
// https://github.com/bitcoin/bitcoin/pull/18600#discussion_r420195993.
// 2. For most of these transactions, the wallet's internal conflict
// detection in the blockConnected handler will subsequently call
// MarkConflicted and update them with CONFLICTED status anyway. This
// applies to any wallet transaction that has inputs spent in the
// block, or that has ancestors in the wallet with inputs spent by
// the block.
// 3. Longstanding behavior since the sync implementation in
// https://github.com/bitcoin/bitcoin/pull/9371 and the prior sync
// implementation before that was to mark these transactions
// unconfirmed rather than conflicted.
//
// Nothing described above should be seen as an unchangeable requirement
// when improving this code in the future. The wallet's heuristics for
// distinguishing between conflicted and unconfirmed transactions are
// imperfect, and could be improved in general, see
// https://github.com/bitcoin-core/bitcoin-devwiki/wiki/Wallet-Transaction-Conflict-Tracking
2020-05-22 16:30:07 -04:00
SyncTransaction ( tx , { CWalletTx : : Status : : UNCONFIRMED , /* block height */ 0 , /* block hash */ { } , /* index */ 0 } ) ;
2020-05-15 09:23:55 -04:00
}
2017-03-29 22:12:42 -03:00
}
2020-02-26 18:05:49 -03:00
void CWallet : : blockConnected ( const CBlock & block , int height )
2019-07-24 15:41:41 -04:00
{
2017-07-30 16:00:56 -04:00
const uint256 & block_hash = block . GetHash ( ) ;
2017-07-26 10:23:01 -04:00
LOCK ( cs_wallet ) ;
2017-03-29 22:12:42 -03:00
2019-04-20 12:02:52 -04:00
m_last_block_processed_height = height ;
m_last_block_processed = block_hash ;
2019-04-20 11:22:59 -04:00
for ( size_t index = 0 ; index < block . vtx . size ( ) ; index + + ) {
2020-05-22 16:30:07 -04:00
SyncTransaction ( block . vtx [ index ] , { CWalletTx : : Status : : CONFIRMED , height , block_hash , ( int ) index } ) ;
Add 'sequence' zmq publisher to track all block (dis)connects, mempool deltas
Using the zmq notifications to avoid excessive mempool polling can be difficult
given the current notifications available. It announces all transactions
being added to mempool or included in blocks, but announces no evictions
and gives no indication if the transaction is in the mempool or a block.
Block notifications for zmq are also substandard, in that it only announces
block tips, while all block transactions are still announced.
This commit adds a unified stream which can be used to closely track mempool:
1) getrawmempool to fill out mempool knowledge
2) if txhash is announced, add or remove from set
based on add/remove flag
3) if blockhash is announced, get block txn list,
remove from those transactions local view of mempool
4) if we drop a sequence number, go to (1)
The mempool sequence number starts at the value 1, and
increments each time a transaction enters the mempool,
or is evicted from the mempool for any reason, including
block inclusion. The mempool sequence number is published
via ZMQ for any transaction-related notification.
These features allow for ZMQ/RPC consumer to track mempool
state in a more exacting way, without unnecesarily polling
getrawmempool. See interface_zmq.py::test_mempool_sync for
example usage.
2020-09-04 11:55:58 -04:00
transactionRemovedFromMempool ( block . vtx [ index ] , MemPoolRemovalReason : : BLOCK , 0 /* mempool_sequence */ ) ;
2017-03-29 22:12:42 -03:00
}
}
2020-02-26 18:05:49 -03:00
void CWallet : : blockDisconnected ( const CBlock & block , int height )
2019-07-24 15:41:41 -04:00
{
2017-07-26 10:23:01 -04:00
LOCK ( cs_wallet ) ;
2017-03-08 14:55:33 -03:00
2019-08-12 18:12:12 -04:00
// At block disconnection, this will change an abandoned transaction to
// be unconfirmed, whether or not the transaction is added back to the mempool.
// User may have to call abandontransaction again. It may be addressed in the
// future with a stickier abandoned state or even removing abandontransaction call.
2019-04-20 12:02:52 -04:00
m_last_block_processed_height = height - 1 ;
m_last_block_processed = block . hashPrevBlock ;
2017-07-30 16:00:56 -04:00
for ( const CTransactionRef & ptx : block . vtx ) {
2020-05-22 16:30:07 -04:00
SyncTransaction ( ptx , { CWalletTx : : Status : : UNCONFIRMED , /* block height */ 0 , /* block hash */ { } , /* index */ 0 } ) ;
2017-03-29 22:12:42 -03:00
}
}
2020-02-26 18:05:49 -03:00
void CWallet : : updatedBlockTip ( )
2019-03-28 14:15:47 -03:00
{
m_best_block_time = GetTime ( ) ;
}
2017-03-29 22:12:42 -03:00
2011-06-26 13:23:24 -04:00
2020-03-02 03:57:25 -03:00
void CWallet : : BlockUntilSyncedToCurrentChain ( ) const {
2017-01-17 20:06:16 -03:00
AssertLockNotHeld ( cs_wallet ) ;
2019-04-18 08:21:35 -04:00
// Skip the queue-draining stuff if we know we're caught up with
2019-03-27 12:14:25 -03:00
// ::ChainActive().Tip(), otherwise put a callback in the validation interface queue and wait
2017-01-17 20:06:16 -03:00
// for the queue to drain enough to execute it (indicating we are caught up
// at least with the time we entered this function).
2019-04-18 08:21:35 -04:00
uint256 last_block_hash = WITH_LOCK ( cs_wallet , return m_last_block_processed ) ;
2019-06-24 19:07:09 -04:00
chain ( ) . waitForNotificationsIfTipChanged ( last_block_hash ) ;
2017-01-17 20:06:16 -03:00
}
2016-12-09 17:31:06 -03:00
// Note that this function doesn't distinguish between a 0-valued input,
// and a not-"is mine" (according to the filter) input.
2014-04-22 19:46:19 -03:00
CAmount CWallet : : GetDebit ( const CTxIn & txin , const isminefilter & filter ) const
2011-06-26 13:23:24 -04:00
{
{
2012-04-06 13:39:12 -03:00
LOCK ( cs_wallet ) ;
2017-01-26 22:33:45 -03:00
std : : map < uint256 , CWalletTx > : : const_iterator mi = mapWallet . find ( txin . prevout . hash ) ;
2011-06-26 13:23:24 -04:00
if ( mi ! = mapWallet . end ( ) )
{
const CWalletTx & prev = ( * mi ) . second ;
2016-11-11 21:54:51 -03:00
if ( txin . prevout . n < prev . tx - > vout . size ( ) )
if ( IsMine ( prev . tx - > vout [ txin . prevout . n ] ) & filter )
return prev . tx - > vout [ txin . prevout . n ] . nValue ;
2011-06-26 13:23:24 -04:00
}
}
return 0 ;
}
2015-02-04 19:19:29 -03:00
isminetype CWallet : : IsMine ( const CTxOut & txout ) const
{
2020-06-15 18:03:17 -04:00
AssertLockHeld ( cs_wallet ) ;
2019-10-07 15:11:34 -03:00
return IsMine ( txout . scriptPubKey ) ;
}
isminetype CWallet : : IsMine ( const CTxDestination & dest ) const
{
2020-06-15 18:03:17 -04:00
AssertLockHeld ( cs_wallet ) ;
2019-10-07 15:11:34 -03:00
return IsMine ( GetScriptForDestination ( dest ) ) ;
2015-02-04 19:19:29 -03:00
}
2019-10-07 15:11:34 -03:00
isminetype CWallet : : IsMine ( const CScript & script ) const
2019-10-07 15:11:34 -03:00
{
2020-06-15 18:03:17 -04:00
AssertLockHeld ( cs_wallet ) ;
2019-10-07 15:11:34 -03:00
isminetype result = ISMINE_NO ;
2019-10-07 15:11:34 -03:00
for ( const auto & spk_man_pair : m_spk_managers ) {
result = std : : max ( result , spk_man_pair . second - > IsMine ( script ) ) ;
2019-10-07 15:11:34 -03:00
}
return result ;
2019-10-07 15:11:34 -03:00
}
2015-02-04 19:19:29 -03:00
bool CWallet : : IsMine ( const CTransaction & tx ) const
{
2020-06-15 18:03:17 -04:00
AssertLockHeld ( cs_wallet ) ;
2017-06-01 21:18:57 -04:00
for ( const CTxOut & txout : tx . vout )
2015-02-04 19:19:29 -03:00
if ( IsMine ( txout ) )
return true ;
return false ;
}
bool CWallet : : IsFromMe ( const CTransaction & tx ) const
{
return ( GetDebit ( tx , ISMINE_ALL ) > 0 ) ;
}
CAmount CWallet : : GetDebit ( const CTransaction & tx , const isminefilter & filter ) const
{
CAmount nDebit = 0 ;
2017-06-01 21:18:57 -04:00
for ( const CTxIn & txin : tx . vin )
2015-02-04 19:19:29 -03:00
{
nDebit + = GetDebit ( txin , filter ) ;
if ( ! MoneyRange ( nDebit ) )
2016-08-19 13:31:35 -03:00
throw std : : runtime_error ( std : : string ( __func__ ) + " : value out of range " ) ;
2015-02-04 19:19:29 -03:00
}
return nDebit ;
}
2019-10-07 15:11:34 -03:00
bool CWallet : : IsHDEnabled ( ) const
{
2019-07-08 17:05:05 -04:00
// All Active ScriptPubKeyMans must be HD for this to be true
2021-08-23 12:49:10 -04:00
bool result = false ;
2019-07-08 17:05:05 -04:00
for ( const auto & spk_man : GetActiveScriptPubKeyMans ( ) ) {
2021-08-23 12:49:10 -04:00
if ( ! spk_man - > IsHDEnabled ( ) ) return false ;
result = true ;
2019-10-07 15:11:34 -03:00
}
return result ;
}
2020-03-02 04:35:58 -03:00
bool CWallet : : CanGetAddresses ( bool internal ) const
2019-10-07 15:11:34 -03:00
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_wallet ) ;
2019-10-07 15:11:34 -03:00
if ( m_spk_managers . empty ( ) ) return false ;
for ( OutputType t : OUTPUT_TYPES ) {
auto spk_man = GetScriptPubKeyMan ( t , internal ) ;
2019-10-07 15:11:34 -03:00
if ( spk_man & & spk_man - > CanGetAddresses ( internal ) ) {
return true ;
}
}
return false ;
}
2017-02-16 10:22:18 -03:00
void CWallet : : SetWalletFlag ( uint64_t flags )
{
LOCK ( cs_wallet ) ;
m_wallet_flags | = flags ;
2020-09-19 20:25:45 -03:00
if ( ! WalletBatch ( GetDatabase ( ) ) . WriteWalletFlags ( m_wallet_flags ) )
2017-02-16 10:22:18 -03:00
throw std : : runtime_error ( std : : string ( __func__ ) + " : writing wallet flags failed " ) ;
}
2019-02-06 23:26:55 -03:00
void CWallet : : UnsetWalletFlag ( uint64_t flag )
2019-05-18 06:01:56 -04:00
{
2020-09-19 20:25:45 -03:00
WalletBatch batch ( GetDatabase ( ) ) ;
2019-05-18 06:01:56 -04:00
UnsetWalletFlagWithDB ( batch , flag ) ;
}
void CWallet : : UnsetWalletFlagWithDB ( WalletBatch & batch , uint64_t flag )
2019-02-06 23:26:55 -03:00
{
LOCK ( cs_wallet ) ;
m_wallet_flags & = ~ flag ;
2019-05-18 06:01:56 -04:00
if ( ! batch . WriteWalletFlags ( m_wallet_flags ) )
2019-02-06 23:26:55 -03:00
throw std : : runtime_error ( std : : string ( __func__ ) + " : writing wallet flags failed " ) ;
}
2019-10-29 18:53:35 -03:00
void CWallet : : UnsetBlankWalletFlag ( WalletBatch & batch )
{
UnsetWalletFlagWithDB ( batch , WALLET_FLAG_BLANK_WALLET ) ;
}
2018-08-02 03:43:55 -04:00
bool CWallet : : IsWalletFlagSet ( uint64_t flag ) const
2017-02-16 10:22:18 -03:00
{
return ( m_wallet_flags & flag ) ;
}
2020-05-21 23:15:41 -04:00
bool CWallet : : LoadWalletFlags ( uint64_t flags )
2017-02-16 10:22:18 -03:00
{
LOCK ( cs_wallet ) ;
2020-05-21 23:15:41 -04:00
if ( ( ( flags & KNOWN_WALLET_FLAGS ) > > 32 ) ^ ( flags > > 32 ) ) {
2017-05-05 03:53:39 -03:00
// contains unknown non-tolerable wallet flags
return false ;
}
2020-05-21 23:15:41 -04:00
m_wallet_flags = flags ;
return true ;
}
bool CWallet : : AddWalletFlags ( uint64_t flags )
{
LOCK ( cs_wallet ) ;
2020-07-11 07:56:13 -04:00
// We should never be writing unknown non-tolerable wallet flags
assert ( ( ( flags & KNOWN_WALLET_FLAGS ) > > 32 ) = = ( flags > > 32 ) ) ;
2020-09-19 20:25:45 -03:00
if ( ! WalletBatch ( GetDatabase ( ) ) . WriteWalletFlags ( flags ) ) {
2017-02-16 10:22:18 -03:00
throw std : : runtime_error ( std : : string ( __func__ ) + " : writing wallet flags failed " ) ;
}
2017-05-05 03:53:39 -03:00
2020-05-21 23:15:41 -04:00
return LoadWalletFlags ( flags ) ;
2017-02-16 10:22:18 -03:00
}
2018-07-14 22:19:44 -04:00
// Helper for producing a max-sized low-S low-R signature (eg 71 bytes)
2018-08-07 19:59:53 -04:00
// or a max-sized low-S signature (e.g. 72 bytes) if use_max_sig is true
2019-10-18 18:17:17 -03:00
bool DummySignInput ( const SigningProvider & provider , CTxIn & tx_in , const CTxOut & txout , bool use_max_sig )
2018-03-05 18:37:24 -03:00
{
// Fill in dummy signatures for fee calculation.
const CScript & scriptPubKey = txout . scriptPubKey ;
SignatureData sigdata ;
2019-10-18 18:17:17 -03:00
if ( ! ProduceSignature ( provider , use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR , scriptPubKey , sigdata ) ) {
2018-03-05 18:37:24 -03:00
return false ;
}
2018-08-07 19:59:53 -04:00
UpdateInput ( tx_in , sigdata ) ;
2018-03-05 18:37:24 -03:00
return true ;
}
2018-07-14 22:19:44 -04:00
// Helper for producing a bunch of max-sized low-S low-R signatures (eg 71 bytes)
2019-10-18 18:17:17 -03:00
bool CWallet : : DummySignTx ( CMutableTransaction & txNew , const std : : vector < CTxOut > & txouts , const CCoinControl * coin_control ) const
2018-03-05 18:37:24 -03:00
{
// Fill in dummy signatures for fee calculation.
int nIn = 0 ;
for ( const auto & txout : txouts )
{
2019-10-18 18:17:17 -03:00
CTxIn & txin = txNew . vin [ nIn ] ;
// Use max sig if watch only inputs were used or if this particular input is an external input
// to ensure a sufficient fee is attained for the requested feerate.
const bool use_max_sig = coin_control & & ( coin_control - > fAllowWatchOnly | | coin_control - > IsExternalSelected ( txin . prevout ) ) ;
const std : : unique_ptr < SigningProvider > provider = GetSolvingProvider ( txout . scriptPubKey ) ;
if ( ! provider | | ! DummySignInput ( * provider , txin , txout , use_max_sig ) ) {
if ( ! coin_control | | ! DummySignInput ( coin_control - > m_external_provider , txin , txout , use_max_sig ) ) {
return false ;
}
2018-03-05 18:37:24 -03:00
}
nIn + + ;
}
return true ;
}
2019-10-07 15:11:34 -03:00
bool CWallet : : ImportScripts ( const std : : set < CScript > scripts , int64_t timestamp )
{
auto spk_man = GetLegacyScriptPubKeyMan ( ) ;
if ( ! spk_man ) {
return false ;
}
2019-10-07 15:11:34 -03:00
LOCK ( spk_man - > cs_KeyStore ) ;
2019-10-07 15:11:34 -03:00
return spk_man - > ImportScripts ( scripts , timestamp ) ;
}
bool CWallet : : ImportPrivKeys ( const std : : map < CKeyID , CKey > & privkey_map , const int64_t timestamp )
{
auto spk_man = GetLegacyScriptPubKeyMan ( ) ;
if ( ! spk_man ) {
return false ;
}
2019-10-07 15:11:34 -03:00
LOCK ( spk_man - > cs_KeyStore ) ;
2019-10-07 15:11:34 -03:00
return spk_man - > ImportPrivKeys ( privkey_map , timestamp ) ;
}
bool CWallet : : 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 )
{
auto spk_man = GetLegacyScriptPubKeyMan ( ) ;
if ( ! spk_man ) {
return false ;
}
2019-10-07 15:11:34 -03:00
LOCK ( spk_man - > cs_KeyStore ) ;
2019-10-07 15:11:34 -03:00
return spk_man - > ImportPubKeys ( ordered_pubkeys , pubkey_map , key_origins , add_keypool , internal , timestamp ) ;
}
bool CWallet : : ImportScriptPubKeys ( const std : : string & label , const std : : set < CScript > & script_pub_keys , const bool have_solving_data , const bool apply_label , const int64_t timestamp )
{
auto spk_man = GetLegacyScriptPubKeyMan ( ) ;
if ( ! spk_man ) {
return false ;
}
2019-10-07 15:11:34 -03:00
LOCK ( spk_man - > cs_KeyStore ) ;
2019-10-07 15:11:34 -03:00
if ( ! spk_man - > ImportScriptPubKeys ( script_pub_keys , have_solving_data , timestamp ) ) {
2019-10-07 15:11:34 -03:00
return false ;
}
2019-10-07 15:11:34 -03:00
if ( apply_label ) {
2020-09-19 20:25:45 -03:00
WalletBatch batch ( GetDatabase ( ) ) ;
2019-10-07 15:11:34 -03:00
for ( const CScript & script : script_pub_keys ) {
CTxDestination dest ;
ExtractDestination ( script , dest ) ;
if ( IsValidDestination ( dest ) ) {
SetAddressBookWithDB ( batch , dest , label , " receive " ) ;
}
}
}
2019-10-07 15:11:34 -03:00
return true ;
}
2017-03-02 17:24:50 -03:00
/**
* Scan active chain for relevant transactions after importing keys . This should
* be called whenever new keys are added to the wallet , with the oldest key
2017-06-22 17:16:24 -04:00
* creation time .
2017-03-02 17:24:50 -03:00
*
* @ return Earliest timestamp that could be successfully scanned from . Timestamp
2017-06-22 17:14:40 -04:00
* returned will be higher than startTime if relevant blocks could not be read .
2017-03-02 17:24:50 -03:00
*/
2017-12-12 20:13:58 -03:00
int64_t CWallet : : RescanFromTime ( int64_t startTime , const WalletRescanReserver & reserver , bool update )
2017-03-02 17:24:50 -03:00
{
// Find starting block. May be null if nCreateTime is greater than the
// highest blockchain timestamp, in which case there is nothing that needs
// to be scanned.
2020-01-21 17:55:19 -03:00
int start_height = 0 ;
2019-01-08 04:38:53 -03:00
uint256 start_block ;
2020-01-21 17:55:19 -03:00
bool start = chain ( ) . findFirstBlockWithTimeAndHeight ( startTime - TIMESTAMP_WINDOW , 0 , FoundBlock ( ) . hash ( start_block ) . height ( start_height ) ) ;
WalletLogPrintf ( " %s: Rescanning last %i blocks \n " , __func__ , start ? WITH_LOCK ( cs_wallet , return GetLastBlockHeight ( ) ) - start_height + 1 : 0 ) ;
2017-03-02 17:24:50 -03:00
2020-01-21 17:55:19 -03:00
if ( start ) {
2018-04-29 12:45:44 -03:00
// TODO: this should take into account failure by ScanResult::USER_ABORT
2020-01-22 18:53:42 -03:00
ScanResult result = ScanForWalletTransactions ( start_block , start_height , { } /* max_height */ , reserver , update ) ;
2019-01-08 04:38:53 -03:00
if ( result . status = = ScanResult : : FAILURE ) {
int64_t time_max ;
2020-02-24 16:34:17 -03:00
CHECK_NONFATAL ( chain ( ) . findBlock ( result . last_failed_block , FoundBlock ( ) . maxTime ( time_max ) ) ) ;
2019-01-08 04:38:53 -03:00
return time_max + TIMESTAMP_WINDOW + 1 ;
2017-03-02 17:24:50 -03:00
}
}
return startTime ;
}
2014-10-26 04:03:12 -03:00
/**
2019-01-08 04:38:53 -03:00
* Scan the block chain ( starting in start_block ) for transactions
2014-10-26 04:03:12 -03:00
* from or to us . If fUpdate is true , found transactions that already
* exist in the wallet will be updated .
2017-02-16 12:49:03 -03:00
*
2019-02-01 18:15:13 -03:00
* @ param [ in ] start_block Scan starting block . If block is not on the active
* chain , the scan will return SUCCESS immediately .
2020-01-22 18:53:42 -03:00
* @ param [ in ] start_height Height of start_block
2020-01-21 19:08:12 -03:00
* @ param [ in ] max_height Optional max scanning height . If unset there is
* no maximum and scanning can continue to the tip
2015-11-19 12:05:37 -03:00
*
2019-01-31 19:48:51 -03:00
* @ return ScanResult returning scan information and indicating success or
* failure . Return status will be set to SUCCESS if scan was
* successful . FAILURE if a complete rescan was not possible ( due to
* pruning or corruption ) . USER_ABORT if the rescan was aborted before
* it could complete .
2017-09-07 20:29:59 -03:00
*
2019-01-08 04:38:53 -03:00
* @ pre Caller needs to make sure start_block ( and the optional stop_block ) are on
2017-09-07 20:29:59 -03:00
* the main chain after to the addition of any new keys you want to detect
* transactions for .
2014-10-26 04:03:12 -03:00
*/
2021-03-14 23:41:30 -03:00
CWallet : : ScanResult CWallet : : ScanForWalletTransactions ( const uint256 & start_block , int start_height , std : : optional < int > max_height , const WalletRescanReserver & reserver , bool fUpdate )
2011-06-26 13:23:24 -04:00
{
2014-02-17 21:35:37 -03:00
int64_t nNow = GetTime ( ) ;
2019-04-27 10:15:56 -04:00
int64_t start_time = GetTimeMillis ( ) ;
2011-06-26 13:23:24 -04:00
2017-12-12 20:13:58 -03:00
assert ( reserver . isReserved ( ) ) ;
2015-11-19 12:05:37 -03:00
2019-01-08 04:38:53 -03:00
uint256 block_hash = start_block ;
ScanResult result ;
2018-04-09 13:48:19 -03:00
2019-01-08 04:38:53 -03:00
WalletLogPrintf ( " Rescan started from block %s... \n " , start_block . ToString ( ) ) ;
2018-04-09 13:48:19 -03:00
2019-04-27 10:16:33 -04:00
fAbortRescan = false ;
2021-09-29 00:32:07 -03:00
ShowProgress ( strprintf ( " %s " + _ ( " Rescanning… " ) . translated , GetDisplayName ( ) ) , 0 ) ; // show rescan progress in GUI as dialog or on splashscreen, if rescan required on startup (e.g. due to corruption)
2020-01-22 18:53:42 -03:00
uint256 tip_hash = WITH_LOCK ( cs_wallet , return GetLastBlockHash ( ) ) ;
uint256 end_hash = tip_hash ;
if ( max_height ) chain ( ) . findAncestorByHeight ( tip_hash , * max_height , FoundBlock ( ) . hash ( end_hash ) ) ;
double progress_begin = chain ( ) . guessVerificationProgress ( block_hash ) ;
double progress_end = chain ( ) . guessVerificationProgress ( end_hash ) ;
2019-04-27 10:16:33 -04:00
double progress_current = progress_begin ;
2020-01-22 18:53:42 -03:00
int block_height = start_height ;
while ( ! fAbortRescan & & ! chain ( ) . shutdownRequested ( ) ) {
2020-11-08 14:37:47 -03:00
if ( progress_end - progress_begin > 0.0 ) {
m_scanning_progress = ( progress_current - progress_begin ) / ( progress_end - progress_begin ) ;
} else { // avoid divide-by-zero for single block scan range (i.e. start and stop hashes are equal)
m_scanning_progress = 0 ;
}
2020-01-22 18:53:42 -03:00
if ( block_height % 100 = = 0 & & progress_end - progress_begin > 0.0 ) {
2021-05-02 14:53:36 -04:00
ShowProgress ( strprintf ( " %s " + _ ( " Rescanning… " ) . translated , GetDisplayName ( ) ) , std : : max ( 1 , std : : min ( 99 , ( int ) ( m_scanning_progress * 100 ) ) ) ) ;
2019-04-27 10:16:33 -04:00
}
if ( GetTime ( ) > = nNow + 60 ) {
nNow = GetTime ( ) ;
2020-01-22 18:53:42 -03:00
WalletLogPrintf ( " Still rescanning. At block %d. Progress=%f \n " , block_height , progress_current ) ;
2017-09-07 20:29:59 -03:00
}
2013-06-10 09:38:13 -04:00
2020-06-09 18:03:26 -04:00
// Read block data
2019-04-27 10:16:33 -04:00
CBlock block ;
2020-06-09 18:03:26 -04:00
chain ( ) . findBlock ( block_hash , FoundBlock ( ) . data ( block ) ) ;
// Find next block separately from reading data above, because reading
// is slow and there might be a reorg while it is read.
bool block_still_active = false ;
bool next_block = false ;
2020-01-22 18:53:42 -03:00
uint256 next_block_hash ;
2020-06-09 18:03:26 -04:00
chain ( ) . findBlock ( block_hash , FoundBlock ( ) . inActiveChain ( block_still_active ) . nextBlock ( FoundBlock ( ) . inActiveChain ( next_block ) . hash ( next_block_hash ) ) ) ;
if ( ! block . IsNull ( ) ) {
2019-04-27 10:16:33 -04:00
LOCK ( cs_wallet ) ;
2020-06-09 18:03:26 -04:00
if ( ! block_still_active ) {
2019-04-27 10:16:33 -04:00
// Abort scan if current block is no longer active, to prevent
// marking transactions as coming from the wrong block.
2019-01-31 19:42:56 -03:00
result . last_failed_block = block_hash ;
2019-01-08 04:38:53 -03:00
result . status = ScanResult : : FAILURE ;
2019-04-27 10:16:33 -04:00
break ;
}
for ( size_t posInBlock = 0 ; posInBlock < block . vtx . size ( ) ; + + posInBlock ) {
2020-12-07 13:29:48 -03:00
SyncTransaction ( block . vtx [ posInBlock ] , { CWalletTx : : Status : : CONFIRMED , block_height , block_hash , ( int ) posInBlock } , fUpdate , /* rescanning_old_block */ true ) ;
2011-06-26 13:23:24 -04:00
}
2019-04-27 10:16:33 -04:00
// scan succeeded, record block as most recent successfully scanned
result . last_scanned_block = block_hash ;
2020-01-22 18:53:42 -03:00
result . last_scanned_height = block_height ;
2019-04-27 10:16:33 -04:00
} else {
// could not scan block, keep scanning but record this block as the most recent failure
result . last_failed_block = block_hash ;
result . status = ScanResult : : FAILURE ;
}
2020-01-22 18:53:42 -03:00
if ( max_height & & block_height > = * max_height ) {
2019-04-27 10:16:33 -04:00
break ;
}
{
2020-06-09 18:03:26 -04:00
if ( ! next_block ) {
2019-04-27 10:16:33 -04:00
// break successfully when rescan has reached the tip, or
// previous block is no longer on the chain due to a reorg
2015-11-19 12:05:37 -03:00
break ;
}
2019-01-08 04:38:53 -03:00
2019-04-27 10:16:33 -04:00
// increment block and verification progress
2020-01-22 18:53:42 -03:00
block_hash = next_block_hash ;
+ + block_height ;
2019-04-27 10:16:33 -04:00
progress_current = chain ( ) . guessVerificationProgress ( block_hash ) ;
2019-01-08 04:38:53 -03:00
2019-04-27 10:16:33 -04:00
// handle updated tip hash
const uint256 prev_tip_hash = tip_hash ;
2020-01-22 18:53:42 -03:00
tip_hash = WITH_LOCK ( cs_wallet , return GetLastBlockHash ( ) ) ;
2020-01-21 19:08:12 -03:00
if ( ! max_height & & prev_tip_hash ! = tip_hash ) {
2019-04-27 10:16:33 -04:00
// in case the tip has changed, update progress max
progress_end = chain ( ) . guessVerificationProgress ( tip_hash ) ;
2017-09-07 20:29:59 -03:00
}
2011-06-26 13:23:24 -04:00
}
2019-04-27 10:16:33 -04:00
}
2021-05-02 14:53:36 -04:00
ShowProgress ( strprintf ( " %s " + _ ( " Rescanning… " ) . translated , GetDisplayName ( ) ) , 100 ) ; // hide progress dialog in GUI
2019-04-27 10:16:33 -04:00
if ( block_height & & fAbortRescan ) {
2020-01-22 18:53:42 -03:00
WalletLogPrintf ( " Rescan aborted at block %d. Progress=%f \n " , block_height , progress_current ) ;
2019-04-27 10:16:33 -04:00
result . status = ScanResult : : USER_ABORT ;
} else if ( block_height & & chain ( ) . shutdownRequested ( ) ) {
2020-01-22 18:53:42 -03:00
WalletLogPrintf ( " Rescan interrupted by shutdown request at block %d. Progress=%f \n " , block_height , progress_current ) ;
2019-04-27 10:16:33 -04:00
result . status = ScanResult : : USER_ABORT ;
} else {
WalletLogPrintf ( " Rescan completed in %15dms \n " , GetTimeMillis ( ) - start_time ) ;
2011-06-26 13:23:24 -04:00
}
2019-01-08 04:38:53 -03:00
return result ;
2011-06-26 13:23:24 -04:00
}
2019-04-29 09:52:01 -04:00
void CWallet : : ReacceptWalletTransactions ( )
2011-06-26 13:23:24 -04:00
{
2015-04-28 11:48:28 -03:00
// If transactions aren't being broadcasted, don't let them into local mempool either
2015-03-27 06:34:48 -03:00
if ( ! fBroadcastTransactions )
return ;
2014-12-19 02:59:16 -03:00
std : : map < int64_t , CWalletTx * > mapSorted ;
// Sort pending wallet transactions based on their initial wallet insertion order
2019-04-11 11:58:53 -04:00
for ( std : : pair < const uint256 , CWalletTx > & item : mapWallet ) {
2014-02-15 18:38:28 -03:00
const uint256 & wtxid = item . first ;
CWalletTx & wtx = item . second ;
assert ( wtx . GetHash ( ) = = wtxid ) ;
2011-06-26 13:23:24 -04:00
2021-02-12 20:01:22 -03:00
int nDepth = GetTxDepthInMainChain ( wtx ) ;
2014-02-15 18:38:28 -03:00
2016-01-07 18:31:27 -03:00
if ( ! wtx . IsCoinBase ( ) & & ( nDepth = = 0 & & ! wtx . isAbandoned ( ) ) ) {
2014-12-19 02:59:16 -03:00
mapSorted . insert ( std : : make_pair ( wtx . nOrderPos , & wtx ) ) ;
2011-06-26 13:23:24 -04:00
}
}
2014-12-19 02:59:16 -03:00
// Try to add wallet transactions to memory pool
2018-06-18 01:58:28 -04:00
for ( const std : : pair < const int64_t , CWalletTx * > & item : mapSorted ) {
2014-12-19 02:59:16 -03:00
CWalletTx & wtx = * ( item . second ) ;
2019-04-11 11:58:53 -04:00
std : : string unused_err_string ;
2021-02-12 20:01:22 -03:00
SubmitTxMemoryPoolAndRelay ( wtx , unused_err_string , false ) ;
2014-12-19 02:59:16 -03:00
}
2011-06-26 13:23:24 -04:00
}
2021-02-12 20:01:22 -03:00
bool CWallet : : SubmitTxMemoryPoolAndRelay ( const CWalletTx & wtx , std : : string & err_string , bool relay ) const
2011-06-26 13:23:24 -04:00
{
2019-03-26 15:56:40 -03:00
// Can't relay if wallet is not broadcasting
2021-02-12 20:01:22 -03:00
if ( ! GetBroadcastTransactions ( ) ) return false ;
2019-03-26 15:56:40 -03:00
// Don't relay abandoned transactions
2021-02-12 20:01:22 -03:00
if ( wtx . isAbandoned ( ) ) return false ;
2019-08-06 14:38:34 -04:00
// Don't try to submit coinbase transactions. These would fail anyway but would
// cause log spam.
2021-02-12 20:01:22 -03:00
if ( wtx . IsCoinBase ( ) ) return false ;
2019-08-09 11:07:30 -04:00
// Don't try to submit conflicted or confirmed transactions.
2021-02-12 20:01:22 -03:00
if ( GetTxDepthInMainChain ( wtx ) ! = 0 ) return false ;
2019-03-26 15:56:40 -03:00
2019-04-11 11:58:53 -04:00
// Submit transaction to mempool for relay
2021-02-12 20:01:22 -03:00
WalletLogPrintf ( " Submitting wtx %s to mempool for relay \n " , wtx . GetHash ( ) . ToString ( ) ) ;
2019-04-11 11:58:53 -04:00
// We must set fInMempool here - while it will be re-set to true by the
// entered-mempool callback, if we did not there would be a race where a
// user could call sendmoney in a loop and hit spurious out of funds errors
// because we think that this newly generated transaction's change is
// unavailable as we're not yet aware that it is in the mempool.
//
// Irrespective of the failure reason, un-marking fInMempool
// out-of-order is incorrect - it should be unmarked when
// TransactionRemovedFromMempool fires.
2021-02-12 20:01:22 -03:00
bool ret = chain ( ) . broadcastTransaction ( wtx . tx , m_default_max_tx_fee , relay , err_string ) ;
wtx . fInMempool | = ret ;
2019-04-11 11:58:53 -04:00
return ret ;
2011-06-26 13:23:24 -04:00
}
2021-02-12 20:01:22 -03:00
std : : set < uint256 > CWallet : : GetTxConflicts ( const CWalletTx & wtx ) const
2014-02-13 22:12:51 -03:00
{
2017-01-26 22:33:45 -03:00
std : : set < uint256 > result ;
2014-02-13 22:12:51 -03:00
{
2021-02-12 20:01:22 -03:00
uint256 myHash = wtx . GetHash ( ) ;
result = GetConflicts ( myHash ) ;
2014-02-13 22:12:51 -03:00
result . erase ( myHash ) ;
}
return result ;
}
2019-03-20 18:46:38 -03:00
// Rebroadcast transactions from the wallet. We do this on a random timer
// to slightly obfuscate which transactions come from our wallet.
//
// Ideally, we'd only resend transactions that we think should have been
// mined in the most recent block. Any transaction that wasn't in the top
// blockweight of transactions in the mempool shouldn't have been mined,
// and so is probably just sitting in the mempool waiting to be confirmed.
// Rebroadcasting does nothing to speed up confirmation and only damages
// privacy.
2019-03-20 19:07:52 -03:00
void CWallet : : ResendWalletTransactions ( )
2011-06-26 13:23:24 -04:00
{
2019-03-20 18:46:38 -03:00
// During reindex, importing and IBD, old wallet transactions become
// unconfirmed. Don't resend them as that would spam other nodes.
if ( ! chain ( ) . isReadyToBroadcast ( ) ) return ;
2011-06-26 13:23:24 -04:00
// Do this infrequently and randomly to avoid giving away
// that these are our transactions.
2019-03-22 15:37:30 -03:00
if ( GetTime ( ) < nNextResend | | ! fBroadcastTransactions ) return ;
2013-04-19 18:28:25 -03:00
bool fFirst = ( nNextResend = = 0 ) ;
2020-01-29 13:21:02 -03:00
// resend 12-36 hours from now, ~1 day on average.
nNextResend = GetTime ( ) + ( 12 * 60 * 60 ) + GetRand ( 24 * 60 * 60 ) ;
2019-03-22 15:37:30 -03:00
if ( fFirst ) return ;
2011-06-26 13:23:24 -04:00
2019-04-11 11:58:53 -04:00
int submitted_tx_count = 0 ;
2019-03-22 15:37:30 -03:00
2019-12-18 19:46:53 -03:00
{ // cs_wallet scope
2019-03-22 15:37:30 -03:00
LOCK ( cs_wallet ) ;
// Relay transactions
for ( std : : pair < const uint256 , CWalletTx > & item : mapWallet ) {
CWalletTx & wtx = item . second ;
2019-08-09 11:07:30 -04:00
// Attempt to rebroadcast all txes more than 5 minutes older than
2021-02-12 20:01:22 -03:00
// the last block. SubmitTxMemoryPoolAndRelay() will not rebroadcast
2019-08-09 11:07:30 -04:00
// any confirmed or conflicting txs.
2019-03-28 14:15:47 -03:00
if ( wtx . nTimeReceived > m_best_block_time - 5 * 60 ) continue ;
2019-04-11 11:58:53 -04:00
std : : string unused_err_string ;
2021-02-12 20:01:22 -03:00
if ( SubmitTxMemoryPoolAndRelay ( wtx , unused_err_string , true ) ) + + submitted_tx_count ;
2019-03-22 15:37:30 -03:00
}
2019-12-18 19:46:53 -03:00
} // cs_wallet
2019-03-22 15:37:30 -03:00
2019-04-11 11:58:53 -04:00
if ( submitted_tx_count > 0 ) {
WalletLogPrintf ( " %s: resubmit %u unconfirmed transactions \n " , __func__ , submitted_tx_count ) ;
2019-03-22 15:37:30 -03:00
}
2011-06-26 13:23:24 -04:00
}
2014-10-26 04:03:12 -03:00
/** @} */ // end of mapWallet
2011-06-26 13:23:24 -04:00
2020-05-28 13:06:43 -04:00
void MaybeResendWalletTxs ( WalletContext & context )
2019-03-20 18:46:38 -03:00
{
2020-05-28 13:06:43 -04:00
for ( const std : : shared_ptr < CWallet > & pwallet : GetWallets ( context ) ) {
2019-03-20 19:07:52 -03:00
pwallet - > ResendWalletTransactions ( ) ;
2019-03-20 18:46:38 -03:00
}
}
2011-06-26 13:23:24 -04:00
2014-10-26 04:03:12 -03:00
/** @defgroup Actions
*
* @ {
*/
2011-06-26 13:23:24 -04:00
2021-02-10 18:06:01 -03:00
bool CWallet : : SignTransaction ( CMutableTransaction & tx ) const
2011-06-26 13:23:24 -04:00
{
2021-02-10 18:06:01 -03:00
AssertLockHeld ( cs_wallet ) ;
// Build coins map
std : : map < COutPoint , Coin > coins ;
for ( auto & input : tx . vin ) {
std : : map < uint256 , CWalletTx > : : const_iterator mi = mapWallet . find ( input . prevout . hash ) ;
if ( mi = = mapWallet . end ( ) | | input . prevout . n > = mi - > second . tx - > vout . size ( ) ) {
return false ;
2011-06-26 13:23:24 -04:00
}
2021-02-10 18:06:01 -03:00
const CWalletTx & wtx = mi - > second ;
coins [ input . prevout ] = Coin ( wtx . tx - > vout [ input . prevout . n ] , wtx . m_confirm . block_height , wtx . IsCoinBase ( ) ) ;
2011-06-26 13:23:24 -04:00
}
2021-06-23 17:28:54 -04:00
std : : map < int , bilingual_str > input_errors ;
2021-03-04 19:27:20 -03:00
return SignTransaction ( tx , coins , SIGHASH_DEFAULT , input_errors ) ;
2014-03-29 01:15:28 -03:00
}
2021-06-23 17:28:54 -04:00
bool CWallet : : SignTransaction ( CMutableTransaction & tx , const std : : map < COutPoint , Coin > & coins , int sighash , std : : map < int , bilingual_str > & input_errors ) const
2017-04-28 15:10:21 -03:00
{
2021-02-10 18:06:01 -03:00
// Try to sign with all ScriptPubKeyMans
for ( ScriptPubKeyMan * spk_man : GetAllScriptPubKeyMans ( ) ) {
// spk_man->SignTransaction will return true if the transaction is complete,
// so we can exit early and return true if that happens
if ( spk_man - > SignTransaction ( tx , coins , sighash , input_errors ) ) {
return true ;
2017-04-28 15:10:21 -03:00
}
}
2021-02-10 18:06:01 -03:00
// At this point, one input was not fully signed otherwise we would have exited already
return false ;
2017-04-28 15:10:21 -03:00
}
2021-02-10 18:06:01 -03:00
TransactionError CWallet : : FillPSBT ( PartiallySignedTransaction & psbtx , bool & complete , int sighash_type , bool sign , bool bip32derivs , size_t * n_signed ) const
2012-02-27 09:19:32 -03:00
{
2021-02-10 18:06:01 -03:00
if ( n_signed ) {
* n_signed = 0 ;
}
2021-03-03 21:47:44 -03:00
const PrecomputedTransactionData txdata = PrecomputePSBTData ( psbtx ) ;
2021-02-10 18:06:01 -03:00
LOCK ( cs_wallet ) ;
// Get all of the previous transactions
for ( unsigned int i = 0 ; i < psbtx . tx - > vin . size ( ) ; + + i ) {
const CTxIn & txin = psbtx . tx - > vin [ i ] ;
PSBTInput & input = psbtx . inputs . at ( i ) ;
2012-05-31 16:01:16 -04:00
2021-02-10 18:06:01 -03:00
if ( PSBTInputSigned ( input ) ) {
2018-02-08 15:18:51 -03:00
continue ;
}
2014-02-12 15:43:07 -03:00
2021-02-10 18:06:01 -03:00
// If we have no utxo, grab it from the wallet.
if ( ! input . non_witness_utxo ) {
const uint256 & txhash = txin . prevout . hash ;
const auto it = mapWallet . find ( txhash ) ;
if ( it ! = mapWallet . end ( ) ) {
const CWalletTx & wtx = it - > second ;
// We only need the non_witness_utxo, which is a superset of the witness_utxo.
// The signing code will switch to the smaller witness_utxo if this is ok.
input . non_witness_utxo = wtx . tx ;
}
2018-02-08 15:18:51 -03:00
}
2021-02-10 18:06:01 -03:00
}
2016-03-17 13:48:05 -03:00
2021-02-10 18:06:01 -03:00
// Fill in information from ScriptPubKeyMans
for ( ScriptPubKeyMan * spk_man : GetAllScriptPubKeyMans ( ) ) {
int n_signed_this_spkm = 0 ;
2021-03-03 21:47:44 -03:00
TransactionError res = spk_man - > FillPSBT ( psbtx , txdata , sighash_type , sign , bip32derivs , & n_signed_this_spkm ) ;
2021-02-10 18:06:01 -03:00
if ( res ! = TransactionError : : OK ) {
return res ;
2018-02-08 15:18:51 -03:00
}
2016-12-09 15:45:27 -03:00
2021-02-10 18:06:01 -03:00
if ( n_signed ) {
( * n_signed ) + = n_signed_this_spkm ;
2019-05-09 21:34:38 -04:00
}
2021-02-10 18:06:01 -03:00
}
2017-02-23 13:20:16 -03:00
2021-02-10 18:06:01 -03:00
// Complete if every input is now signed
complete = true ;
for ( const auto & input : psbtx . inputs ) {
complete & = PSBTInputSigned ( input ) ;
}
2016-12-09 15:45:27 -03:00
2021-02-10 18:06:01 -03:00
return TransactionError : : OK ;
}
2020-02-10 21:50:56 -03:00
2020-02-13 19:09:12 -03:00
SigningResult CWallet : : SignMessage ( const std : : string & message , const PKHash & pkhash , std : : string & str_sig ) const
{
SignatureData sigdata ;
CScript script_pub_key = GetScriptForDestination ( pkhash ) ;
for ( const auto & spk_man_pair : m_spk_managers ) {
if ( spk_man_pair . second - > CanProvide ( script_pub_key , sigdata ) ) {
return spk_man_pair . second - > SignMessage ( message , pkhash , str_sig ) ;
}
}
return SigningResult : : PRIVATE_KEY_NOT_AVAILABLE ;
}
2021-03-14 23:41:30 -03:00
OutputType CWallet : : TransactionChangeType ( const std : : optional < OutputType > & change_type , const std : : vector < CRecipient > & vecSend ) const
2018-01-23 13:56:15 -03:00
{
// If -changetype is specified, always use that change type.
2020-06-27 11:05:41 -04:00
if ( change_type ) {
return * change_type ;
2018-01-23 13:56:15 -03:00
}
2018-02-10 23:06:35 -03:00
// if m_default_address_type is legacy, use legacy address as change (even
2018-01-23 13:56:15 -03:00
// if some of the outputs are P2WPKH or P2WSH).
2018-02-10 23:06:35 -03:00
if ( m_default_address_type = = OutputType : : LEGACY ) {
return OutputType : : LEGACY ;
2018-01-23 13:56:15 -03:00
}
// if any destination is P2WPKH or P2WSH, use P2WPKH for the change
// output.
for ( const auto & recipient : vecSend ) {
// Check if any destination contains a witness program:
int witnessversion = 0 ;
std : : vector < unsigned char > witnessprogram ;
if ( recipient . scriptPubKey . IsWitnessProgram ( witnessversion , witnessprogram ) ) {
2021-06-04 16:40:48 -04:00
if ( GetScriptPubKeyMan ( OutputType : : BECH32M , true ) ) {
return OutputType : : BECH32M ;
} else if ( GetScriptPubKeyMan ( OutputType : : BECH32 , true ) ) {
return OutputType : : BECH32 ;
} else {
return m_default_address_type ;
}
2018-01-23 13:56:15 -03:00
}
}
2018-02-10 23:06:35 -03:00
// else use m_default_address_type for change
return m_default_address_type ;
2018-01-23 13:56:15 -03:00
}
2019-10-18 10:37:40 -03:00
void CWallet : : CommitTransaction ( CTransactionRef tx , mapValue_t mapValue , std : : vector < std : : pair < std : : string , std : : string > > orderForm )
2011-06-26 13:23:24 -04:00
{
2019-04-03 18:29:21 -03:00
LOCK ( cs_wallet ) ;
2016-12-19 13:25:15 -03:00
WalletLogPrintf ( " CommitTransaction: \n %s " , tx - > ToString ( ) ) ; /* Continued */
2011-06-26 13:23:24 -04:00
2019-04-03 18:29:21 -03:00
// Add tx to wallet, because if it has change it's also ours,
// otherwise just for transaction history.
2016-12-19 13:25:15 -03:00
AddToWallet ( tx , { } , [ & ] ( CWalletTx & wtx , bool new_tx ) {
CHECK_NONFATAL ( wtx . mapValue . empty ( ) ) ;
CHECK_NONFATAL ( wtx . vOrderForm . empty ( ) ) ;
wtx . mapValue = std : : move ( mapValue ) ;
wtx . vOrderForm = std : : move ( orderForm ) ;
wtx . fTimeReceivedIsTxTime = true ;
wtx . fFromMe = true ;
return true ;
} ) ;
2011-06-26 13:23:24 -04:00
2019-04-03 18:29:21 -03:00
// Notify that old coins are spent
2016-12-19 13:25:15 -03:00
for ( const CTxIn & txin : tx - > vin ) {
2019-04-03 18:29:21 -03:00
CWalletTx & coin = mapWallet . at ( txin . prevout . hash ) ;
2017-11-20 14:52:47 -03:00
coin . MarkDirty ( ) ;
2021-06-28 04:25:29 -04:00
NotifyTransactionChanged ( coin . GetHash ( ) , CT_UPDATED ) ;
2019-04-03 18:29:21 -03:00
}
2011-06-26 13:23:24 -04:00
2019-04-03 18:29:21 -03:00
// Get the inserted-CWalletTx from mapWallet so that the
// fInMempool flag is cached properly
2016-12-19 13:25:15 -03:00
CWalletTx & wtx = mapWallet . at ( tx - > GetHash ( ) ) ;
Use callbacks to cache whether wallet transactions are in mempool
This avoid calling out to mempool state during coin selection,
balance calculation, etc. In the next commit we ensure all wallet
callbacks from CValidationInterface happen in the same queue,
serialized with each other. This helps to avoid re-introducing one
of the issues described in #9584 [1] by further disconnecting
wallet from current chain/mempool state.
Thanks to @morcos for the suggestion to do this.
Note that there are several race conditions introduced here:
* If a user calls sendrawtransaction from RPC, adding a
transaction which is "trusted" (ie from them) and pays them
change, it may not be immediately used by coin selection until
the notification callbacks finish running. No such race is
introduced in normal transaction-sending RPCs as this case is
explicitly handled.
* Until Block{Connected,Disconnected} and
TransactionAddedToMempool calls also run in the CSceduler
background thread, there is a race where
TransactionAddedToMempool might be called after a
Block{Connected,Disconnected} call happens.
* Wallet will write a new best chain from the SetBestChain
callback prior to having processed the transaction from that
block.
[1] "you could go to select coins, need to use 0-conf change, but
such 0-conf change may have been included in a block who's
callbacks have not yet been processed - resulting in thinking they
are not in mempool and, thus, not selectable."
2017-01-20 18:38:07 -03:00
2019-04-03 18:55:24 -03:00
if ( ! fBroadcastTransactions ) {
// Don't submit tx to the mempool
return ;
2011-06-26 13:23:24 -04:00
}
2019-04-03 18:29:21 -03:00
2019-04-03 18:55:24 -03:00
std : : string err_string ;
2021-02-12 20:01:22 -03:00
if ( ! SubmitTxMemoryPoolAndRelay ( wtx , err_string , true ) ) {
2019-04-03 18:55:24 -03:00
WalletLogPrintf ( " CommitTransaction(): Transaction cannot be broadcast immediately, %s \n " , err_string ) ;
// TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure.
2011-06-26 13:23:24 -04:00
}
}
2020-12-18 13:45:11 -03:00
DBErrors CWallet : : LoadWallet ( )
2011-06-26 13:23:24 -04:00
{
2017-07-26 10:23:01 -04:00
LOCK ( cs_wallet ) ;
2017-08-24 15:12:21 -03:00
2020-09-19 20:25:45 -03:00
DBErrors nLoadWalletRet = WalletBatch ( GetDatabase ( ) ) . LoadWallet ( this ) ;
2018-03-09 11:03:40 -03:00
if ( nLoadWalletRet = = DBErrors : : NEED_REWRITE )
2011-11-10 17:29:23 -03:00
{
2020-09-19 20:25:45 -03:00
if ( GetDatabase ( ) . Rewrite ( " \x04 pool " ) )
2011-11-10 23:12:46 -03:00
{
2019-10-07 15:11:34 -03:00
for ( const auto & spk_man_pair : m_spk_managers ) {
spk_man_pair . second - > RewriteDB ( ) ;
2019-10-07 15:11:34 -03:00
}
2011-11-10 23:12:46 -03:00
}
2011-11-10 17:29:23 -03:00
}
2020-12-18 13:45:11 -03:00
if ( m_spk_managers . empty ( ) ) {
2019-10-07 15:11:34 -03:00
assert ( m_external_spk_managers . empty ( ) ) ;
assert ( m_internal_spk_managers . empty ( ) ) ;
2018-04-29 15:15:05 -03:00
}
2017-07-28 20:00:49 -04:00
2021-09-28 06:00:44 -03:00
return nLoadWalletRet ;
2011-06-26 13:23:24 -04:00
}
2017-01-26 22:33:45 -03:00
DBErrors CWallet : : ZapSelectTx ( std : : vector < uint256 > & vHashIn , std : : vector < uint256 > & vHashOut )
2016-03-07 10:51:06 -03:00
{
2019-02-18 18:09:45 -03:00
AssertLockHeld ( cs_wallet ) ;
2020-09-19 20:25:45 -03:00
DBErrors nZapSelectTxRet = WalletBatch ( GetDatabase ( ) ) . ZapSelectTx ( vHashIn , vHashOut ) ;
2020-05-02 05:28:14 -04:00
for ( const uint256 & hash : vHashOut ) {
2018-06-11 14:09:16 -04:00
const auto & it = mapWallet . find ( hash ) ;
wtxOrdered . erase ( it - > second . m_it_wtxOrdered ) ;
2020-05-02 05:28:14 -04:00
for ( const auto & txin : it - > second . tx - > vin )
mapTxSpends . erase ( txin . prevout ) ;
2018-06-11 14:09:16 -04:00
mapWallet . erase ( it ) ;
2021-06-28 04:25:29 -04:00
NotifyTransactionChanged ( hash , CT_DELETED ) ;
2018-06-11 14:09:16 -04:00
}
2016-11-12 06:53:18 -03:00
2018-03-09 11:03:40 -03:00
if ( nZapSelectTxRet = = DBErrors : : NEED_REWRITE )
2016-03-07 10:51:06 -03:00
{
2020-09-19 20:25:45 -03:00
if ( GetDatabase ( ) . Rewrite ( " \x04 pool " ) )
2016-03-07 10:51:06 -03:00
{
2019-10-07 15:11:34 -03:00
for ( const auto & spk_man_pair : m_spk_managers ) {
spk_man_pair . second - > RewriteDB ( ) ;
2019-10-07 15:11:34 -03:00
}
2016-03-07 10:51:06 -03:00
}
}
2018-03-09 11:03:40 -03:00
if ( nZapSelectTxRet ! = DBErrors : : LOAD_OK )
2016-03-07 10:51:06 -03:00
return nZapSelectTxRet ;
MarkDirty ( ) ;
2018-03-09 11:03:40 -03:00
return DBErrors : : LOAD_OK ;
2016-03-07 10:51:06 -03:00
}
2011-07-07 09:22:54 -04:00
2019-04-04 06:33:02 -03:00
bool CWallet : : SetAddressBookWithDB ( WalletBatch & batch , const CTxDestination & address , const std : : string & strName , const std : : string & strPurpose )
2011-07-07 09:22:54 -04:00
{
2014-02-18 14:11:46 -03:00
bool fUpdated = false ;
2020-06-15 18:03:17 -04:00
bool is_mine ;
2014-02-18 14:11:46 -03:00
{
2019-02-07 16:46:08 -03:00
LOCK ( cs_wallet ) ;
2020-02-21 18:52:52 -03:00
std : : map < CTxDestination , CAddressBookData > : : iterator mi = m_address_book . find ( address ) ;
2020-02-22 01:16:36 -03:00
fUpdated = ( mi ! = m_address_book . end ( ) & & ! mi - > second . IsChange ( ) ) ;
2020-02-21 22:52:47 -03:00
m_address_book [ address ] . SetLabel ( strName ) ;
2014-02-18 14:11:46 -03:00
if ( ! strPurpose . empty ( ) ) /* update purpose only if requested */
2020-02-21 18:52:52 -03:00
m_address_book [ address ] . purpose = strPurpose ;
2020-06-15 18:03:17 -04:00
is_mine = IsMine ( address ) ! = ISMINE_NO ;
2014-02-18 14:11:46 -03:00
}
2021-06-28 04:51:32 -04:00
NotifyAddressBookChanged ( address , strName , is_mine ,
strPurpose , ( fUpdated ? CT_UPDATED : CT_NEW ) ) ;
2019-04-04 06:33:02 -03:00
if ( ! strPurpose . empty ( ) & & ! batch . WritePurpose ( EncodeDestination ( address ) , strPurpose ) )
2013-07-22 02:50:39 -04:00
return false ;
2019-04-04 06:33:02 -03:00
return batch . WriteName ( EncodeDestination ( address ) , strName ) ;
}
bool CWallet : : SetAddressBook ( const CTxDestination & address , const std : : string & strName , const std : : string & strPurpose )
{
2020-09-19 20:25:45 -03:00
WalletBatch batch ( GetDatabase ( ) ) ;
2019-04-04 06:33:02 -03:00
return SetAddressBookWithDB ( batch , address , strName , strPurpose ) ;
2011-07-07 09:22:54 -04:00
}
2013-07-22 02:50:39 -04:00
bool CWallet : : DelAddressBook ( const CTxDestination & address )
2011-07-07 09:22:54 -04:00
{
2020-06-15 18:03:17 -04:00
bool is_mine ;
2020-09-19 20:25:45 -03:00
WalletBatch batch ( GetDatabase ( ) ) ;
2013-11-18 12:55:54 -03:00
{
2019-02-07 16:46:08 -03:00
LOCK ( cs_wallet ) ;
2020-06-15 18:03:17 -04:00
// If we want to delete receiving addresses, we need to take care that DestData "used" (and possibly newer DestData) gets preserved (and the "deleted" address transformed into a change entry instead of actually being deleted)
// NOTE: This isn't a problem for sending addresses because they never have any DestData yet!
// When adding new DestData, it should be considered here whether to retain or delete it (or move it?).
if ( IsMine ( address ) ) {
WalletLogPrintf ( " %s called with IsMine address, NOT SUPPORTED. Please report this bug! %s \n " , __func__ , PACKAGE_BUGREPORT ) ;
return false ;
}
2017-03-08 09:08:26 -03:00
// Delete destdata tuples associated with address
2017-08-22 22:02:33 -03:00
std : : string strAddress = EncodeDestination ( address ) ;
2020-02-21 18:52:52 -03:00
for ( const std : : pair < const std : : string , std : : string > & item : m_address_book [ address ] . destdata )
2013-11-18 12:55:54 -03:00
{
2020-06-15 18:08:06 -04:00
batch . EraseDestData ( strAddress , item . first ) ;
2013-11-18 12:55:54 -03:00
}
2020-02-21 18:52:52 -03:00
m_address_book . erase ( address ) ;
2020-06-15 18:03:17 -04:00
is_mine = IsMine ( address ) ! = ISMINE_NO ;
2013-11-18 12:55:54 -03:00
}
2021-06-28 04:51:32 -04:00
NotifyAddressBookChanged ( address , " " , is_mine , " " , CT_DELETED ) ;
2014-02-18 14:11:46 -03:00
2020-06-15 18:08:06 -04:00
batch . ErasePurpose ( EncodeDestination ( address ) ) ;
return batch . EraseName ( EncodeDestination ( address ) ) ;
2011-07-07 09:22:54 -04:00
}
2020-03-02 04:33:45 -03:00
size_t CWallet : : KeypoolCountExternalKeys ( ) const
2019-10-07 15:11:34 -03:00
{
AssertLockHeld ( cs_wallet ) ;
2021-06-29 02:29:25 -04:00
auto legacy_spk_man = GetLegacyScriptPubKeyMan ( ) ;
if ( legacy_spk_man ) {
return legacy_spk_man - > KeypoolCountExternalKeys ( ) ;
}
2019-10-07 15:11:34 -03:00
unsigned int count = 0 ;
2021-06-29 02:29:25 -04:00
for ( auto spk_man : m_external_spk_managers ) {
count + = spk_man . second - > GetKeyPoolSize ( ) ;
2019-10-07 15:11:34 -03:00
}
return count ;
}
2019-10-07 15:11:34 -03:00
unsigned int CWallet : : GetKeyPoolSize ( ) const
{
AssertLockHeld ( cs_wallet ) ;
unsigned int count = 0 ;
2019-10-07 15:11:34 -03:00
for ( auto spk_man : GetActiveScriptPubKeyMans ( ) ) {
2019-10-07 15:11:34 -03:00
count + = spk_man - > GetKeyPoolSize ( ) ;
}
return count ;
}
2019-10-07 15:11:34 -03:00
bool CWallet : : TopUpKeyPool ( unsigned int kpSize )
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_wallet ) ;
2019-10-07 15:11:34 -03:00
bool res = true ;
2019-10-07 15:11:34 -03:00
for ( auto spk_man : GetActiveScriptPubKeyMans ( ) ) {
2019-10-07 15:11:34 -03:00
res & = spk_man - > TopUp ( kpSize ) ;
2019-10-07 15:11:34 -03:00
}
return res ;
}
2021-06-23 16:54:13 -04:00
bool CWallet : : GetNewDestination ( const OutputType type , const std : : string label , CTxDestination & dest , bilingual_str & error )
2019-10-07 15:11:34 -03:00
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_wallet ) ;
2019-10-07 15:11:34 -03:00
error . clear ( ) ;
bool result = false ;
2019-10-07 15:11:34 -03:00
auto spk_man = GetScriptPubKeyMan ( type , false /* internal */ ) ;
2019-10-07 15:11:34 -03:00
if ( spk_man ) {
2019-11-20 14:42:10 -03:00
spk_man - > TopUp ( ) ;
2019-10-07 15:11:34 -03:00
result = spk_man - > GetNewDestination ( type , dest , error ) ;
2020-02-13 23:06:29 -03:00
} else {
2021-06-23 16:54:13 -04:00
error = strprintf ( _ ( " Error: No %s addresses available. " ) , FormatOutputType ( type ) ) ;
2019-10-07 15:11:34 -03:00
}
if ( result ) {
SetAddressBook ( dest , label , " receive " ) ;
2019-10-07 15:11:34 -03:00
}
2019-10-07 15:11:34 -03:00
2019-10-07 15:11:34 -03:00
return result ;
}
2021-06-23 16:54:13 -04:00
bool CWallet : : GetNewChangeDestination ( const OutputType type , CTxDestination & dest , bilingual_str & error )
2011-11-17 16:01:25 -03:00
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_wallet ) ;
2019-10-07 15:11:34 -03:00
error . clear ( ) ;
2019-07-09 10:09:56 -04:00
2019-10-24 05:44:20 -03:00
ReserveDestination reservedest ( this , type ) ;
2021-06-12 21:36:05 -04:00
if ( ! reservedest . GetReservedDestination ( dest , true , error ) ) {
2019-06-18 15:49:02 -04:00
return false ;
}
reservedest . KeepDestination ( ) ;
return true ;
}
2020-03-02 04:54:59 -03:00
int64_t CWallet : : GetOldestKeyPoolTime ( ) const
2019-10-07 15:11:34 -03:00
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_wallet ) ;
2019-10-07 15:11:34 -03:00
int64_t oldestKey = std : : numeric_limits < int64_t > : : max ( ) ;
2019-10-07 15:11:34 -03:00
for ( const auto & spk_man_pair : m_spk_managers ) {
oldestKey = std : : min ( oldestKey , spk_man_pair . second - > GetOldestKeyPoolTime ( ) ) ;
2019-10-07 15:11:34 -03:00
}
return oldestKey ;
}
2019-12-31 15:55:18 -03:00
void CWallet : : MarkDestinationsDirty ( const std : : set < CTxDestination > & destinations ) {
for ( auto & entry : mapWallet ) {
CWalletTx & wtx = entry . second ;
2020-01-07 10:47:20 -03:00
if ( wtx . m_is_cache_empty ) continue ;
2019-12-31 15:55:18 -03:00
for ( unsigned int i = 0 ; i < wtx . tx - > vout . size ( ) ; i + + ) {
CTxDestination dst ;
if ( ExtractDestination ( wtx . tx - > vout [ i ] . scriptPubKey , dst ) & & destinations . count ( dst ) ) {
wtx . MarkDirty ( ) ;
break ;
}
}
}
}
2017-10-20 14:27:55 -03:00
std : : set < CTxDestination > CWallet : : GetLabelAddresses ( const std : : string & label ) const
2013-07-15 19:01:09 -04:00
{
2020-08-29 10:26:39 -04:00
AssertLockHeld ( cs_wallet ) ;
2017-01-26 22:33:45 -03:00
std : : set < CTxDestination > result ;
2020-02-21 18:52:52 -03:00
for ( const std : : pair < const CTxDestination , CAddressBookData > & item : m_address_book )
2013-07-15 19:01:09 -04:00
{
2020-02-22 01:16:36 -03:00
if ( item . second . IsChange ( ) ) continue ;
2013-07-15 19:01:09 -04:00
const CTxDestination & address = item . first ;
2020-04-03 00:02:16 -03:00
const std : : string & strName = item . second . GetLabel ( ) ;
2017-10-20 14:27:55 -03:00
if ( strName = = label )
2013-07-15 19:01:09 -04:00
result . insert ( address ) ;
}
return result ;
}
2021-06-23 16:54:13 -04:00
bool ReserveDestination : : GetReservedDestination ( CTxDestination & dest , bool internal , bilingual_str & error )
2011-06-26 13:23:24 -04:00
{
2019-10-07 15:11:34 -03:00
m_spk_man = pwallet - > GetScriptPubKeyMan ( type , internal ) ;
2019-10-07 15:11:34 -03:00
if ( ! m_spk_man ) {
2021-06-23 16:54:13 -04:00
error = strprintf ( _ ( " Error: No %s addresses available. " ) , FormatOutputType ( type ) ) ;
2019-10-07 15:11:34 -03:00
return false ;
}
2019-02-06 23:26:55 -03:00
2011-06-26 13:23:24 -04:00
if ( nIndex = = - 1 )
{
2019-11-20 14:42:10 -03:00
m_spk_man - > TopUp ( ) ;
2011-06-26 13:23:24 -04:00
CKeyPool keypool ;
2021-06-12 21:36:05 -04:00
if ( ! m_spk_man - > GetReservedDestination ( type , internal , address , nIndex , keypool , error ) ) {
2014-06-16 08:45:32 -04:00
return false ;
2011-07-13 21:28:31 -04:00
}
2017-04-19 13:55:32 -03:00
fInternal = keypool . fInternal ;
2011-06-26 13:23:24 -04:00
}
2019-06-18 15:48:20 -04:00
dest = address ;
2013-04-25 14:30:28 -03:00
return true ;
2011-06-26 13:23:24 -04:00
}
2019-06-18 15:48:20 -04:00
void ReserveDestination : : KeepDestination ( )
2011-06-26 13:23:24 -04:00
{
2019-10-24 05:45:20 -03:00
if ( nIndex ! = - 1 ) {
2019-10-07 15:11:34 -03:00
m_spk_man - > KeepDestination ( nIndex , type ) ;
2019-10-24 05:45:20 -03:00
}
2011-06-26 13:23:24 -04:00
nIndex = - 1 ;
2019-06-18 15:48:20 -04:00
address = CNoDestination ( ) ;
2011-06-26 13:23:24 -04:00
}
2019-06-18 15:48:20 -04:00
void ReserveDestination : : ReturnDestination ( )
2011-06-26 13:23:24 -04:00
{
2017-04-19 14:11:16 -03:00
if ( nIndex ! = - 1 ) {
2019-10-07 15:11:34 -03:00
m_spk_man - > ReturnDestination ( nIndex , fInternal , address ) ;
2017-04-19 14:11:16 -03:00
}
2011-06-26 13:23:24 -04:00
nIndex = - 1 ;
2019-06-18 15:48:20 -04:00
address = CNoDestination ( ) ;
2011-06-26 13:23:24 -04:00
}
2011-07-07 09:22:54 -04:00
2020-02-19 10:33:37 -03:00
bool CWallet : : DisplayAddress ( const CTxDestination & dest )
{
CScript scriptPubKey = GetScriptForDestination ( dest ) ;
const auto spk_man = GetScriptPubKeyMan ( scriptPubKey ) ;
if ( spk_man = = nullptr ) {
return false ;
}
auto signer_spk_man = dynamic_cast < ExternalSignerScriptPubKeyMan * > ( spk_man ) ;
if ( signer_spk_man = = nullptr ) {
return false ;
}
2021-04-13 06:12:39 -04:00
ExternalSigner signer = ExternalSignerScriptPubKeyMan : : GetExternalSigner ( ) ;
2020-02-19 10:33:37 -03:00
return signer_spk_man - > DisplayAddress ( scriptPubKey , signer ) ;
}
2021-09-22 09:17:55 -03:00
bool CWallet : : LockCoin ( const COutPoint & output , WalletBatch * batch )
2012-09-27 14:52:09 -03:00
{
2019-02-18 18:09:45 -03:00
AssertLockHeld ( cs_wallet ) ;
2012-09-27 14:52:09 -03:00
setLockedCoins . insert ( output ) ;
2021-09-22 09:17:55 -03:00
if ( batch ) {
return batch - > WriteLockedUTXO ( output ) ;
}
return true ;
2012-09-27 14:52:09 -03:00
}
2021-09-22 09:17:55 -03:00
bool CWallet : : UnlockCoin ( const COutPoint & output , WalletBatch * batch )
2012-09-27 14:52:09 -03:00
{
2019-02-18 18:09:45 -03:00
AssertLockHeld ( cs_wallet ) ;
2021-09-22 09:17:55 -03:00
bool was_locked = setLockedCoins . erase ( output ) ;
if ( batch & & was_locked ) {
return batch - > EraseLockedUTXO ( output ) ;
}
return true ;
2012-09-27 14:52:09 -03:00
}
2021-09-22 09:17:55 -03:00
bool CWallet : : UnlockAllCoins ( )
2012-09-27 14:52:09 -03:00
{
2019-02-18 18:09:45 -03:00
AssertLockHeld ( cs_wallet ) ;
2021-09-22 09:17:55 -03:00
bool success = true ;
WalletBatch batch ( GetDatabase ( ) ) ;
for ( auto it = setLockedCoins . begin ( ) ; it ! = setLockedCoins . end ( ) ; + + it ) {
success & = batch . EraseLockedUTXO ( * it ) ;
}
2012-09-27 14:52:09 -03:00
setLockedCoins . clear ( ) ;
2021-09-22 09:17:55 -03:00
return success ;
2012-09-27 14:52:09 -03:00
}
bool CWallet : : IsLockedCoin ( uint256 hash , unsigned int n ) const
{
2019-02-18 18:09:45 -03:00
AssertLockHeld ( cs_wallet ) ;
2012-09-27 14:52:09 -03:00
COutPoint outpt ( hash , n ) ;
return ( setLockedCoins . count ( outpt ) > 0 ) ;
}
2017-04-28 15:10:21 -03:00
void CWallet : : ListLockedCoins ( std : : vector < COutPoint > & vOutpts ) const
2012-09-27 14:52:09 -03:00
{
2019-02-18 18:09:45 -03:00
AssertLockHeld ( cs_wallet ) ;
2012-09-27 14:52:09 -03:00
for ( std : : set < COutPoint > : : iterator it = setLockedCoins . begin ( ) ;
it ! = setLockedCoins . end ( ) ; it + + ) {
COutPoint outpt = ( * it ) ;
vOutpts . push_back ( outpt ) ;
}
}
2014-10-26 04:03:12 -03:00
/** @} */ // end of Actions
2014-08-27 11:46:30 -04:00
2019-12-18 19:46:53 -03:00
void CWallet : : GetKeyBirthTimes ( std : : map < CKeyID , int64_t > & mapKeyBirth ) const {
2019-02-18 18:09:45 -03:00
AssertLockHeld ( cs_wallet ) ;
2013-04-29 13:50:40 -04:00
mapKeyBirth . clear ( ) ;
// map in which we'll infer heights of other keys
2020-01-22 19:15:17 -03:00
std : : map < CKeyID , const CWalletTx : : Confirmation * > mapKeyFirstBlock ;
CWalletTx : : Confirmation max_confirm ;
max_confirm . block_height = GetLastBlockHeight ( ) > 144 ? GetLastBlockHeight ( ) - 144 : 0 ; // the tip can be reorganized; use a 144-block safety margin
CHECK_NONFATAL ( chain ( ) . findAncestorByHeight ( GetLastBlockHash ( ) , max_confirm . block_height , FoundBlock ( ) . hash ( max_confirm . hashBlock ) ) ) ;
2013-04-29 13:50:40 -04:00
2021-07-18 22:53:26 -04:00
{
LegacyScriptPubKeyMan * spk_man = GetLegacyScriptPubKeyMan ( ) ;
assert ( spk_man ! = nullptr ) ;
LOCK ( spk_man - > cs_KeyStore ) ;
// get birth times for keys with metadata
for ( const auto & entry : spk_man - > mapKeyMetadata ) {
if ( entry . second . nCreateTime ) {
mapKeyBirth [ entry . first ] = entry . second . nCreateTime ;
}
}
// Prepare to infer birth heights for keys without metadata
for ( const CKeyID & keyid : spk_man - > GetKeys ( ) ) {
if ( mapKeyBirth . count ( keyid ) = = 0 )
mapKeyFirstBlock [ keyid ] = & max_confirm ;
}
2013-04-29 13:50:40 -04:00
2021-07-18 22:53:26 -04:00
// if there are no such keys, we're done
if ( mapKeyFirstBlock . empty ( ) )
return ;
// find first block that affects those keys, if there are any left
for ( const auto & entry : mapWallet ) {
// iterate over all wallet transactions...
const CWalletTx & wtx = entry . second ;
if ( wtx . m_confirm . status = = CWalletTx : : CONFIRMED ) {
// ... which are already in a block
for ( const CTxOut & txout : wtx . tx - > vout ) {
// iterate over all their outputs
for ( const auto & keyid : GetAffectedKeys ( txout . scriptPubKey , * spk_man ) ) {
// ... and all their affected keys
auto rit = mapKeyFirstBlock . find ( keyid ) ;
if ( rit ! = mapKeyFirstBlock . end ( ) & & wtx . m_confirm . block_height < rit - > second - > block_height ) {
rit - > second = & wtx . m_confirm ;
}
2020-01-22 19:15:17 -03:00
}
2013-04-29 13:50:40 -04:00
}
}
}
}
// Extract block timestamps for those keys
2020-01-22 19:15:17 -03:00
for ( const auto & entry : mapKeyFirstBlock ) {
int64_t block_time ;
CHECK_NONFATAL ( chain ( ) . findBlock ( entry . second - > hashBlock , FoundBlock ( ) . time ( block_time ) ) ) ;
mapKeyBirth [ entry . first ] = block_time - TIMESTAMP_WINDOW ; // block times can be 2h off
}
2013-04-29 13:50:40 -04:00
}
2013-11-18 12:55:54 -03:00
2016-12-19 12:51:45 -03:00
/**
* Compute smart timestamp for a transaction being added to the wallet .
*
* Logic :
* - If sending a transaction , assign its timestamp to the current time .
* - If receiving a transaction outside a block , assign its timestamp to the
* current time .
2020-12-10 20:00:43 -03:00
* - If receiving a transaction during a rescanning process , assign all its
* ( not already known ) transactions ' timestamps to the block time .
2016-12-19 12:51:45 -03:00
* - If receiving a block with a future timestamp , assign all its ( not already
* known ) transactions ' timestamps to the current time .
* - If receiving a block with a past timestamp , before the most recent known
* transaction ( that we care about ) , assign all its ( not already known )
* transactions ' timestamps to the same timestamp as that most - recent - known
* transaction .
* - If receiving a block with a past timestamp , but after the most recent known
* transaction , assign all its ( not already known ) transactions ' timestamps to
* the block time .
*
* For more information see CWalletTx : : nTimeSmart ,
* https : //bitcointalk.org/?topic=54527, or
* https : //github.com/bitcoin/bitcoin/pull/1393.
*/
2020-12-07 13:29:48 -03:00
unsigned int CWallet : : ComputeTimeSmart ( const CWalletTx & wtx , bool rescanning_old_block ) const
2016-12-16 12:00:26 -03:00
{
unsigned int nTimeSmart = wtx . nTimeReceived ;
2019-08-12 18:12:12 -04:00
if ( ! wtx . isUnconfirmed ( ) & & ! wtx . isAbandoned ( ) ) {
2019-01-08 03:56:46 -03:00
int64_t blocktime ;
2020-12-07 13:29:48 -03:00
int64_t block_max_time ;
if ( chain ( ) . findBlock ( wtx . m_confirm . hashBlock , FoundBlock ( ) . time ( blocktime ) . maxTime ( block_max_time ) ) ) {
if ( rescanning_old_block ) {
nTimeSmart = block_max_time ;
} else {
int64_t latestNow = wtx . nTimeReceived ;
int64_t latestEntry = 0 ;
// Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future
int64_t latestTolerated = latestNow + 300 ;
const TxItems & txOrdered = wtxOrdered ;
for ( auto it = txOrdered . rbegin ( ) ; it ! = txOrdered . rend ( ) ; + + it ) {
CWalletTx * const pwtx = it - > second ;
if ( pwtx = = & wtx ) {
continue ;
}
int64_t nSmartTime ;
nSmartTime = pwtx - > nTimeSmart ;
if ( ! nSmartTime ) {
nSmartTime = pwtx - > nTimeReceived ;
}
if ( nSmartTime < = latestTolerated ) {
latestEntry = nSmartTime ;
if ( nSmartTime > latestNow ) {
latestNow = nSmartTime ;
}
break ;
2016-12-16 12:00:26 -03:00
}
}
2020-12-07 13:29:48 -03:00
nTimeSmart = std : : max ( latestEntry , std : : min ( blocktime , latestNow ) ) ;
}
2017-02-10 17:00:30 -03:00
} else {
2019-08-12 18:12:12 -04:00
WalletLogPrintf ( " %s: found %s in block %s not in index \n " , __func__ , wtx . GetHash ( ) . ToString ( ) , wtx . m_confirm . hashBlock . ToString ( ) ) ;
2016-12-16 12:00:26 -03:00
}
}
return nTimeSmart ;
}
2020-04-12 13:40:43 -04:00
bool CWallet : : SetAddressUsed ( WalletBatch & batch , const CTxDestination & dest , bool used )
2013-11-18 12:55:54 -03:00
{
2020-04-12 13:40:43 -04:00
const std : : string key { " used " } ;
2021-01-04 07:20:02 -03:00
if ( std : : get_if < CNoDestination > ( & dest ) )
2014-01-14 01:05:43 -03:00
return false ;
2020-04-12 13:40:43 -04:00
if ( ! used ) {
if ( auto * data = util : : FindKey ( m_address_book , dest ) ) data - > destdata . erase ( key ) ;
return batch . EraseDestData ( EncodeDestination ( dest ) , key ) ;
}
const std : : string value { " 1 " } ;
2020-02-21 18:52:52 -03:00
m_address_book [ dest ] . destdata . insert ( std : : make_pair ( key , value ) ) ;
2019-11-02 13:20:45 -03:00
return batch . WriteDestData ( EncodeDestination ( dest ) , key , value ) ;
2013-11-18 12:55:54 -03:00
}
2018-07-27 02:22:42 -04:00
void CWallet : : LoadDestData ( const CTxDestination & dest , const std : : string & key , const std : : string & value )
2013-11-18 12:55:54 -03:00
{
2020-02-21 18:52:52 -03:00
m_address_book [ dest ] . destdata . insert ( std : : make_pair ( key , value ) ) ;
2013-11-18 12:55:54 -03:00
}
2020-04-12 13:40:43 -04:00
bool CWallet : : IsAddressUsed ( const CTxDestination & dest ) const
2013-11-18 12:55:54 -03:00
{
2020-04-12 13:40:43 -04:00
const std : : string key { " used " } ;
2020-02-21 18:52:52 -03:00
std : : map < CTxDestination , CAddressBookData > : : const_iterator i = m_address_book . find ( dest ) ;
if ( i ! = m_address_book . end ( ) )
2013-11-18 12:55:54 -03:00
{
CAddressBookData : : StringMap : : const_iterator j = i - > second . destdata . find ( key ) ;
if ( j ! = i - > second . destdata . end ( ) )
{
return true ;
}
}
return false ;
}
2014-08-20 23:04:43 -04:00
2020-04-12 13:40:43 -04:00
std : : vector < std : : string > CWallet : : GetAddressReceiveRequests ( ) const
2017-04-28 15:10:21 -03:00
{
2020-04-12 13:40:43 -04:00
const std : : string prefix { " rr " } ;
2017-04-28 15:10:21 -03:00
std : : vector < std : : string > values ;
2020-02-21 18:52:52 -03:00
for ( const auto & address : m_address_book ) {
2017-04-28 15:10:21 -03:00
for ( const auto & data : address . second . destdata ) {
if ( ! data . first . compare ( 0 , prefix . size ( ) , prefix ) ) {
values . emplace_back ( data . second ) ;
}
}
}
return values ;
}
2020-04-12 13:40:43 -04:00
bool CWallet : : SetAddressReceiveRequest ( WalletBatch & batch , const CTxDestination & dest , const std : : string & id , const std : : string & value )
{
const std : : string key { " rr " + id } ; // "rr" prefix = "receive request" in destdata
CAddressBookData & data = m_address_book . at ( dest ) ;
if ( value . empty ( ) ) {
if ( ! batch . EraseDestData ( EncodeDestination ( dest ) , key ) ) return false ;
data . destdata . erase ( key ) ;
} else {
if ( ! batch . WriteDestData ( EncodeDestination ( dest ) , key , value ) ) return false ;
data . destdata [ key ] = value ;
}
return true ;
}
2020-08-04 19:33:37 -04:00
std : : unique_ptr < WalletDatabase > MakeWalletDatabase ( const std : : string & name , const DatabaseOptions & options , DatabaseStatus & status , bilingual_str & error_string )
2018-04-18 14:11:28 -03:00
{
// Do some checking on wallet path. It should be either a:
//
// 1. Path where a directory can be created.
// 2. Path to an existing directory.
// 3. Path to a symlink to a directory.
// 4. For backwards compatibility, the name of a data file in -walletdir.
2021-01-13 11:44:40 -03:00
const fs : : path wallet_path = fsbridge : : AbsPathJoin ( GetWalletDir ( ) , name ) ;
2018-04-18 14:11:28 -03:00
fs : : file_type path_type = fs : : symlink_status ( wallet_path ) . type ( ) ;
if ( ! ( path_type = = fs : : file_not_found | | path_type = = fs : : directory_file | |
( path_type = = fs : : symlink_file & & fs : : is_directory ( wallet_path ) ) | |
2020-07-28 19:25:14 -04:00
( path_type = = fs : : regular_file & & fs : : path ( name ) . filename ( ) = = name ) ) ) {
2019-08-19 18:12:35 -04:00
error_string = Untranslated ( strprintf (
2018-04-18 15:17:09 -03:00
" Invalid -wallet path '%s'. -wallet path should point to a directory where wallet.dat and "
2018-04-18 14:11:28 -03:00
" database/log.?????????? files can be stored, a location where such a directory could be created, "
2018-04-18 15:17:09 -03:00
" or (for backwards compatibility) the name of an existing data file in -walletdir (%s) " ,
2020-07-28 19:25:14 -04:00
name , GetWalletDir ( ) ) ) ;
2020-08-04 19:33:37 -04:00
status = DatabaseStatus : : FAILED_BAD_PATH ;
return nullptr ;
2018-04-18 14:11:28 -03:00
}
2020-08-04 19:33:37 -04:00
return MakeDatabase ( wallet_path , options , status , error_string ) ;
2018-04-18 14:11:28 -03:00
}
2020-05-28 13:06:43 -04:00
std : : shared_ptr < CWallet > CWallet : : Create ( WalletContext & context , const std : : string & name , std : : unique_ptr < WalletDatabase > database , uint64_t wallet_creation_flags , bilingual_str & error , std : : vector < bilingual_str > & warnings )
2016-02-22 08:07:55 -03:00
{
2020-05-28 13:06:43 -04:00
interfaces : : Chain * chain = context . chain ;
2021-08-19 15:36:16 -04:00
ArgsManager & args = * Assert ( context . args ) ;
2020-08-04 20:45:28 -04:00
const std : : string & walletFile = database - > Filename ( ) ;
2017-11-13 23:25:46 -03:00
2016-02-22 08:07:55 -03:00
int64_t nStart = GetTimeMillis ( ) ;
2018-04-28 18:36:43 -03:00
// TODO: Can't use std::make_shared because we need a custom deleter but
// should be possible to use std::allocate_shared.
2021-01-18 05:26:33 -03:00
std : : shared_ptr < CWallet > walletInstance ( new CWallet ( chain , name , std : : move ( database ) ) , ReleaseWallet ) ;
2021-09-28 06:00:44 -03:00
bool rescan_required = false ;
2020-12-18 13:45:11 -03:00
DBErrors nLoadWalletRet = walletInstance - > LoadWallet ( ) ;
2019-10-06 18:52:05 -03:00
if ( nLoadWalletRet ! = DBErrors : : LOAD_OK ) {
2018-03-09 11:03:40 -03:00
if ( nLoadWalletRet = = DBErrors : : CORRUPT ) {
2019-08-19 18:12:35 -04:00
error = strprintf ( _ ( " Error loading %s: Wallet corrupted " ) , walletFile ) ;
2017-08-07 01:36:37 -04:00
return nullptr ;
2016-09-09 05:44:47 -03:00
}
2018-03-09 11:03:40 -03:00
else if ( nLoadWalletRet = = DBErrors : : NONCRITICAL_ERROR )
2016-02-22 08:07:55 -03:00
{
2019-10-06 18:52:05 -03:00
warnings . push_back ( strprintf ( _ ( " Error reading %s! All keys read correctly, but transaction data "
2019-08-19 18:12:35 -04:00
" or address book entries might be missing or incorrect. " ) ,
2016-03-15 06:30:37 -03:00
walletFile ) ) ;
2016-02-22 08:07:55 -03:00
}
2018-03-09 11:03:40 -03:00
else if ( nLoadWalletRet = = DBErrors : : TOO_NEW ) {
2019-08-19 18:12:35 -04:00
error = strprintf ( _ ( " Error loading %s: Wallet requires newer version of %s " ) , walletFile , PACKAGE_NAME ) ;
2017-08-07 01:36:37 -04:00
return nullptr ;
2016-09-09 05:44:47 -03:00
}
2018-03-09 11:03:40 -03:00
else if ( nLoadWalletRet = = DBErrors : : NEED_REWRITE )
2016-02-22 08:07:55 -03:00
{
2019-08-19 18:12:35 -04:00
error = strprintf ( _ ( " Wallet needed to be rewritten: restart %s to complete " ) , PACKAGE_NAME ) ;
2017-08-07 01:36:37 -04:00
return nullptr ;
2021-09-30 18:31:06 -03:00
} else if ( nLoadWalletRet = = DBErrors : : NEED_RESCAN ) {
2021-09-28 06:00:44 -03:00
warnings . push_back ( strprintf ( _ ( " Error reading %s! Transaction data may be missing or incorrect. "
" Rescanning wallet. " ) , walletFile ) ) ;
rescan_required = true ;
2016-09-09 05:44:47 -03:00
}
else {
2019-08-19 18:12:35 -04:00
error = strprintf ( _ ( " Error loading %s " ) , walletFile ) ;
2017-08-07 01:36:37 -04:00
return nullptr ;
2016-02-22 08:07:55 -03:00
}
}
2020-12-18 13:45:11 -03:00
// This wallet is in its first run if there are no ScriptPubKeyMans and it isn't blank or no privkeys
const bool fFirstRun = walletInstance - > m_spk_managers . empty ( ) & &
! walletInstance - > IsWalletFlagSet ( WALLET_FLAG_DISABLE_PRIVATE_KEYS ) & &
! walletInstance - > IsWalletFlagSet ( WALLET_FLAG_BLANK_WALLET ) ;
2016-02-22 08:07:55 -03:00
if ( fFirstRun )
{
2017-09-05 19:54:11 -03:00
// ensure this wallet.dat can only be opened by clients supporting HD with chain split and expects no default key
2018-03-31 14:37:27 -03:00
walletInstance - > SetMinVersion ( FEATURE_LATEST ) ;
2017-03-24 06:53:35 -03:00
2020-05-21 23:15:41 -04:00
walletInstance - > AddWalletFlags ( wallet_creation_flags ) ;
2019-10-07 15:11:34 -03:00
2019-07-17 17:54:15 -04:00
// Only create LegacyScriptPubKeyMan when not descriptor wallet
if ( ! walletInstance - > IsWalletFlagSet ( WALLET_FLAG_DESCRIPTORS ) ) {
walletInstance - > SetupLegacyScriptPubKeyMan ( ) ;
}
2019-10-07 15:11:34 -03:00
2019-10-31 06:27:47 -03:00
if ( ( wallet_creation_flags & WALLET_FLAG_EXTERNAL_SIGNER ) | | ! ( wallet_creation_flags & ( WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_BLANK_WALLET ) ) ) {
2019-10-07 15:11:34 -03:00
LOCK ( walletInstance - > cs_wallet ) ;
2019-07-11 18:21:21 -04:00
if ( walletInstance - > IsWalletFlagSet ( WALLET_FLAG_DESCRIPTORS ) ) {
walletInstance - > SetupDescriptorScriptPubKeyMans ( ) ;
// SetupDescriptorScriptPubKeyMans already calls SetupGeneration for us so we don't need to call SetupGeneration separately
} else {
// Legacy wallets need SetupGeneration here.
for ( auto spk_man : walletInstance - > GetActiveScriptPubKeyMans ( ) ) {
if ( ! spk_man - > SetupGeneration ( ) ) {
2019-08-19 18:12:35 -04:00
error = _ ( " Unable to generate initial keys " ) ;
2019-07-11 18:21:21 -04:00
return nullptr ;
}
2019-10-07 15:11:34 -03:00
}
}
2016-02-22 08:07:55 -03:00
}
2021-01-18 05:26:33 -03:00
if ( chain ) {
walletInstance - > chainStateFlushed ( chain - > getTipLocator ( ) ) ;
}
2017-05-05 03:53:39 -03:00
} else if ( wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS ) {
// Make it impossible to disable private keys after creation
2019-08-19 18:12:35 -04:00
error = strprintf ( _ ( " Error loading %s: Private keys can only be disabled during creation " ) , walletFile ) ;
2017-05-05 03:53:39 -03:00
return NULL ;
} else if ( walletInstance - > IsWalletFlagSet ( WALLET_FLAG_DISABLE_PRIVATE_KEYS ) ) {
2019-10-07 15:11:34 -03:00
for ( auto spk_man : walletInstance - > GetActiveScriptPubKeyMans ( ) ) {
if ( spk_man - > HavePrivateKeys ( ) ) {
2019-08-19 18:12:35 -04:00
warnings . push_back ( strprintf ( _ ( " Warning: Private keys detected in wallet {%s} with disabled private keys " ) , walletFile ) ) ;
2019-10-07 15:11:34 -03:00
break ;
2019-10-07 15:11:34 -03:00
}
2017-05-05 03:53:39 -03:00
}
2016-06-13 10:27:41 -04:00
}
2016-02-22 08:07:55 -03:00
2021-08-19 15:36:16 -04:00
if ( ! args . GetArg ( " -addresstype " , " " ) . empty ( ) ) {
std : : optional < OutputType > parsed = ParseOutputType ( args . GetArg ( " -addresstype " , " " ) ) ;
2021-08-04 01:38:36 -04:00
if ( ! parsed ) {
2021-08-19 15:36:16 -04:00
error = strprintf ( _ ( " Unknown address type '%s' " ) , args . GetArg ( " -addresstype " , " " ) ) ;
2020-06-27 11:05:41 -04:00
return nullptr ;
}
2021-08-04 01:38:36 -04:00
walletInstance - > m_default_address_type = parsed . value ( ) ;
2018-02-10 23:06:35 -03:00
}
2021-08-19 15:36:16 -04:00
if ( ! args . GetArg ( " -changetype " , " " ) . empty ( ) ) {
std : : optional < OutputType > parsed = ParseOutputType ( args . GetArg ( " -changetype " , " " ) ) ;
2021-08-04 01:38:36 -04:00
if ( ! parsed ) {
2021-08-19 15:36:16 -04:00
error = strprintf ( _ ( " Unknown change type '%s' " ) , args . GetArg ( " -changetype " , " " ) ) ;
2020-06-27 11:05:41 -04:00
return nullptr ;
}
2021-08-04 01:38:36 -04:00
walletInstance - > m_default_change_type = parsed . value ( ) ;
2018-02-10 23:06:35 -03:00
}
2021-08-19 15:36:16 -04:00
if ( args . IsArgSet ( " -mintxfee " ) ) {
std : : optional < CAmount > min_tx_fee = ParseMoney ( args . GetArg ( " -mintxfee " , " " ) ) ;
2021-06-11 00:33:20 -04:00
if ( ! min_tx_fee | | min_tx_fee . value ( ) = = 0 ) {
2021-08-19 15:36:16 -04:00
error = AmountErrMsg ( " mintxfee " , args . GetArg ( " -mintxfee " , " " ) ) ;
2018-04-07 13:12:46 -03:00
return nullptr ;
2021-06-11 00:33:20 -04:00
} else if ( min_tx_fee . value ( ) > HIGH_TX_FEE_PER_KB ) {
2019-08-19 18:12:35 -04:00
warnings . push_back ( AmountHighWarn ( " -mintxfee " ) + Untranslated ( " " ) +
_ ( " This is the minimum transaction fee you pay on every transaction. " ) ) ;
2018-04-07 13:12:46 -03:00
}
2021-06-11 00:33:20 -04:00
walletInstance - > m_min_fee = CFeeRate { min_tx_fee . value ( ) } ;
2018-04-07 13:12:46 -03:00
}
2021-08-19 15:36:16 -04:00
if ( args . IsArgSet ( " -maxapsfee " ) ) {
const std : : string max_aps_fee { args . GetArg ( " -maxapsfee " , " " ) } ;
2020-08-17 01:38:21 -04:00
if ( max_aps_fee = = " -1 " ) {
2021-06-11 00:33:20 -04:00
walletInstance - > m_max_aps_fee = - 1 ;
} else if ( std : : optional < CAmount > max_fee = ParseMoney ( max_aps_fee ) ) {
if ( max_fee . value ( ) > HIGH_APS_FEE ) {
warnings . push_back ( AmountHighWarn ( " -maxapsfee " ) + Untranslated ( " " ) +
_ ( " This is the maximum transaction fee you pay (in addition to the normal fee) to prioritize partial spend avoidance over regular coin selection. " ) ) ;
}
walletInstance - > m_max_aps_fee = max_fee . value ( ) ;
} else {
2020-08-17 01:38:21 -04:00
error = AmountErrMsg ( " maxapsfee " , max_aps_fee ) ;
2018-10-25 21:36:56 -03:00
return nullptr ;
}
}
2021-08-19 15:36:16 -04:00
if ( args . IsArgSet ( " -fallbackfee " ) ) {
std : : optional < CAmount > fallback_fee = ParseMoney ( args . GetArg ( " -fallbackfee " , " " ) ) ;
2021-06-11 00:33:20 -04:00
if ( ! fallback_fee ) {
2021-08-19 15:36:16 -04:00
error = strprintf ( _ ( " Invalid amount for -fallbackfee=<amount>: '%s' " ) , args . GetArg ( " -fallbackfee " , " " ) ) ;
2018-04-07 13:12:46 -03:00
return nullptr ;
2021-06-11 00:33:20 -04:00
} else if ( fallback_fee . value ( ) > HIGH_TX_FEE_PER_KB ) {
2019-08-19 18:12:35 -04:00
warnings . push_back ( AmountHighWarn ( " -fallbackfee " ) + Untranslated ( " " ) +
_ ( " This is the transaction fee you may pay when fee estimates are not available. " ) ) ;
2018-04-07 13:12:46 -03:00
}
2021-06-11 00:33:20 -04:00
walletInstance - > m_fallback_fee = CFeeRate { fallback_fee . value ( ) } ;
2018-04-07 13:12:46 -03:00
}
2021-06-11 00:33:20 -04:00
2019-08-01 16:39:46 -04:00
// Disable fallback fee in case value was set to 0, enable if non-null value
walletInstance - > m_allow_fallback_fee = walletInstance - > m_fallback_fee . GetFeePerK ( ) ! = 0 ;
2021-08-19 15:36:16 -04:00
if ( args . IsArgSet ( " -discardfee " ) ) {
std : : optional < CAmount > discard_fee = ParseMoney ( args . GetArg ( " -discardfee " , " " ) ) ;
2021-06-11 00:33:20 -04:00
if ( ! discard_fee ) {
2021-08-19 15:36:16 -04:00
error = strprintf ( _ ( " Invalid amount for -discardfee=<amount>: '%s' " ) , args . GetArg ( " -discardfee " , " " ) ) ;
2018-04-07 13:12:46 -03:00
return nullptr ;
2021-06-11 00:33:20 -04:00
} else if ( discard_fee . value ( ) > HIGH_TX_FEE_PER_KB ) {
2019-08-19 18:12:35 -04:00
warnings . push_back ( AmountHighWarn ( " -discardfee " ) + Untranslated ( " " ) +
_ ( " This is the transaction fee you may discard if change is smaller than dust at this level " ) ) ;
2018-04-07 13:12:46 -03:00
}
2021-06-11 00:33:20 -04:00
walletInstance - > m_discard_rate = CFeeRate { discard_fee . value ( ) } ;
2018-04-07 13:12:46 -03:00
}
2021-06-11 00:33:20 -04:00
2021-08-19 15:36:16 -04:00
if ( args . IsArgSet ( " -paytxfee " ) ) {
std : : optional < CAmount > pay_tx_fee = ParseMoney ( args . GetArg ( " -paytxfee " , " " ) ) ;
2021-06-11 00:33:20 -04:00
if ( ! pay_tx_fee ) {
2021-08-19 15:36:16 -04:00
error = AmountErrMsg ( " paytxfee " , args . GetArg ( " -paytxfee " , " " ) ) ;
2018-04-07 13:12:46 -03:00
return nullptr ;
2021-06-11 00:33:20 -04:00
} else if ( pay_tx_fee . value ( ) > HIGH_TX_FEE_PER_KB ) {
2019-08-19 18:12:35 -04:00
warnings . push_back ( AmountHighWarn ( " -paytxfee " ) + Untranslated ( " " ) +
_ ( " This is the transaction fee you will pay if you send a transaction. " ) ) ;
2018-04-07 13:12:46 -03:00
}
2021-06-11 00:33:20 -04:00
walletInstance - > m_pay_tx_fee = CFeeRate { pay_tx_fee . value ( ) , 1000 } ;
2021-01-18 05:26:33 -03:00
if ( chain & & walletInstance - > m_pay_tx_fee < chain - > relayMinFee ( ) ) {
2019-08-19 18:12:35 -04:00
error = strprintf ( _ ( " Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s) " ) ,
2021-08-19 15:36:16 -04:00
args . GetArg ( " -paytxfee " , " " ) , chain - > relayMinFee ( ) . ToString ( ) ) ;
2018-04-07 13:12:46 -03:00
return nullptr ;
}
}
2019-02-20 15:45:16 -03:00
2021-08-19 15:36:16 -04:00
if ( args . IsArgSet ( " -maxtxfee " ) ) {
std : : optional < CAmount > max_fee = ParseMoney ( args . GetArg ( " -maxtxfee " , " " ) ) ;
2021-06-11 00:33:20 -04:00
if ( ! max_fee ) {
2021-08-19 15:36:16 -04:00
error = AmountErrMsg ( " maxtxfee " , args . GetArg ( " -maxtxfee " , " " ) ) ;
2019-02-20 15:45:16 -03:00
return nullptr ;
2021-06-11 00:33:20 -04:00
} else if ( max_fee . value ( ) > HIGH_MAX_TX_FEE ) {
2019-08-19 18:12:35 -04:00
warnings . push_back ( _ ( " -maxtxfee is set very high! Fees this large could be paid on a single transaction. " ) ) ;
2019-02-20 15:45:16 -03:00
}
2021-06-11 00:33:20 -04:00
if ( chain & & CFeeRate { max_fee . value ( ) , 1000 } < chain - > relayMinFee ( ) ) {
2019-08-19 18:12:35 -04:00
error = strprintf ( _ ( " Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions) " ) ,
2021-08-19 15:36:16 -04:00
args . GetArg ( " -maxtxfee " , " " ) , chain - > relayMinFee ( ) . ToString ( ) ) ;
2019-02-20 15:45:16 -03:00
return nullptr ;
}
2021-06-11 00:33:20 -04:00
walletInstance - > m_default_max_tx_fee = max_fee . value ( ) ;
2019-02-20 15:45:16 -03:00
}
2021-05-20 16:54:23 -04:00
if ( gArgs . IsArgSet ( " -consolidatefeerate " ) ) {
if ( std : : optional < CAmount > consolidate_feerate = ParseMoney ( gArgs . GetArg ( " -consolidatefeerate " , " " ) ) ) {
walletInstance - > m_consolidate_feerate = CFeeRate ( * consolidate_feerate ) ;
} else {
error = AmountErrMsg ( " consolidatefeerate " , gArgs . GetArg ( " -consolidatefeerate " , " " ) ) ;
return nullptr ;
}
}
2021-01-18 05:26:33 -03:00
if ( chain & & chain - > relayMinFee ( ) . GetFeePerK ( ) > HIGH_TX_FEE_PER_KB ) {
2019-08-19 18:12:35 -04:00
warnings . push_back ( AmountHighWarn ( " -minrelaytxfee " ) + Untranslated ( " " ) +
_ ( " The wallet will avoid paying less than the minimum relay fee. " ) ) ;
2019-08-20 13:17:05 -04:00
}
2019-02-20 15:45:16 -03:00
2019-08-22 21:40:41 -04:00
walletInstance - > m_confirm_target = args . GetIntArg ( " -txconfirmtarget " , DEFAULT_TX_CONFIRM_TARGET ) ;
2021-08-19 15:36:16 -04:00
walletInstance - > m_spend_zero_conf_change = args . GetBoolArg ( " -spendzeroconfchange " , DEFAULT_SPEND_ZEROCONF_CHANGE ) ;
walletInstance - > m_signal_rbf = args . GetBoolArg ( " -walletrbf " , DEFAULT_WALLET_RBF ) ;
2018-04-07 13:12:46 -03:00
2018-08-08 10:42:13 -04:00
walletInstance - > WalletLogPrintf ( " Wallet completed loading in %15dms \n " , GetTimeMillis ( ) - nStart ) ;
2016-02-22 08:07:55 -03:00
2017-07-18 15:49:56 -04:00
// Try to top up keypool. No-op if the wallet is locked.
walletInstance - > TopUpKeyPool ( ) ;
2017-07-26 10:23:01 -04:00
LOCK ( walletInstance - > cs_wallet ) ;
2018-01-11 21:56:27 -03:00
2021-09-28 06:00:44 -03:00
if ( chain & & ! AttachChain ( walletInstance , * chain , rescan_required , error , warnings ) ) {
2020-02-14 14:07:00 -03:00
return nullptr ;
}
{
2020-05-28 13:06:43 -04:00
LOCK ( context . wallets_mutex ) ;
for ( auto & load_wallet : context . wallet_load_fns ) {
load_wallet ( interfaces : : MakeWallet ( context , walletInstance ) ) ;
2020-02-14 14:07:00 -03:00
}
}
2021-08-19 15:36:16 -04:00
walletInstance - > SetBroadcastTransactions ( args . GetBoolArg ( " -walletbroadcast " , DEFAULT_WALLETBROADCAST ) ) ;
2020-02-14 14:07:00 -03:00
{
walletInstance - > WalletLogPrintf ( " setKeyPool.size() = %u \n " , walletInstance - > GetKeyPoolSize ( ) ) ;
walletInstance - > WalletLogPrintf ( " mapWallet.size() = %u \n " , walletInstance - > mapWallet . size ( ) ) ;
walletInstance - > WalletLogPrintf ( " m_address_book.size() = %u \n " , walletInstance - > m_address_book . size ( ) ) ;
}
return walletInstance ;
}
2021-09-28 06:00:44 -03:00
bool CWallet : : AttachChain ( const std : : shared_ptr < CWallet > & walletInstance , interfaces : : Chain & chain , const bool rescan_required , bilingual_str & error , std : : vector < bilingual_str > & warnings )
2020-02-14 14:07:00 -03:00
{
LOCK ( walletInstance - > cs_wallet ) ;
// allow setting the chain if it hasn't been set already but prevent changing it
assert ( ! walletInstance - > m_chain | | walletInstance - > m_chain = = & chain ) ;
walletInstance - > m_chain = & chain ;
2019-12-18 19:46:53 -03:00
// Register wallet with validationinterface. It's done before rescan to avoid
// missing block connections between end of rescan and validation subscribing.
// Because of wallet lock being hold, block connection notifications are going to
// be pending on the validation-side until lock release. It's likely to have
// block processing duplicata (if rescan block range overlaps with notification one)
// but we guarantee at least than wallet state is correct after notifications delivery.
// This is temporary until rescan and notifications delivery are unified under same
// interface.
walletInstance - > m_chain_notifications_handler = walletInstance - > chain ( ) . handleNotifications ( walletInstance ) ;
2021-09-29 00:32:07 -03:00
// If rescan_required = true, rescan_height remains equal to 0
2019-01-08 05:06:24 -03:00
int rescan_height = 0 ;
2021-09-29 00:32:07 -03:00
if ( ! rescan_required )
2016-02-22 08:07:55 -03:00
{
2020-09-19 20:25:45 -03:00
WalletBatch batch ( walletInstance - > GetDatabase ( ) ) ;
2016-02-22 08:07:55 -03:00
CBlockLocator locator ;
2019-01-08 05:06:24 -03:00
if ( batch . ReadBestBlock ( locator ) ) {
2021-03-14 23:41:30 -03:00
if ( const std : : optional < int > fork_height = chain . findLocatorFork ( locator ) ) {
2019-01-08 05:06:24 -03:00
rescan_height = * fork_height ;
}
}
2016-02-22 08:07:55 -03:00
}
2017-01-17 20:06:16 -03:00
2021-03-14 23:41:30 -03:00
const std : : optional < int > tip_height = chain . getHeight ( ) ;
2019-01-08 05:06:24 -03:00
if ( tip_height ) {
2019-07-16 15:20:01 -04:00
walletInstance - > m_last_block_processed = chain . getBlockHash ( * tip_height ) ;
2019-04-20 12:02:52 -04:00
walletInstance - > m_last_block_processed_height = * tip_height ;
2019-01-08 05:06:24 -03:00
} else {
walletInstance - > m_last_block_processed . SetNull ( ) ;
2019-04-20 12:02:52 -04:00
walletInstance - > m_last_block_processed_height = - 1 ;
2019-01-08 05:06:24 -03:00
}
2017-01-17 20:06:16 -03:00
2019-01-08 05:06:24 -03:00
if ( tip_height & & * tip_height ! = rescan_height )
2016-02-22 08:07:55 -03:00
{
2019-04-22 14:30:02 -04:00
if ( chain . havePruned ( ) ) {
2019-01-08 05:06:24 -03:00
int block_height = * tip_height ;
2019-07-16 17:01:46 -04:00
while ( block_height > 0 & & chain . haveBlockOnDisk ( block_height - 1 ) & & rescan_height ! = block_height ) {
2019-01-08 05:06:24 -03:00
- - block_height ;
}
2016-02-22 08:07:55 -03:00
2019-01-08 05:06:24 -03:00
if ( rescan_height ! = block_height ) {
2020-02-14 14:07:00 -03:00
// We can't rescan beyond non-pruned blocks, stop and throw an error.
// This might happen if a user uses an old wallet within a pruned node
// or if they ran -disablewallet for a longer time, then decided to re-enable
// Exit early and print an error.
// If a block is pruned after this check, we will load the wallet,
// but fail the rescan with a generic error.
2019-08-19 18:12:35 -04:00
error = _ ( " Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node) " ) ;
2020-02-14 14:07:00 -03:00
return false ;
2016-09-09 05:44:47 -03:00
}
2016-02-22 08:07:55 -03:00
}
2021-05-02 14:53:36 -04:00
chain . initMessage ( _ ( " Rescanning… " ) . translated ) ;
2019-01-08 05:06:24 -03:00
walletInstance - > WalletLogPrintf ( " Rescanning last %i blocks (from block %i)... \n " , * tip_height - rescan_height , rescan_height ) ;
2017-03-02 18:14:39 -03:00
// No need to read and scan block if block was created before
// our wallet birthday (as adjusted for block time variability)
2021-03-14 23:41:30 -03:00
std : : optional < int64_t > time_first_key ;
2019-10-07 15:11:34 -03:00
for ( auto spk_man : walletInstance - > GetAllScriptPubKeyMans ( ) ) {
2019-10-07 15:11:34 -03:00
int64_t time = spk_man - > GetTimeFirstKey ( ) ;
if ( ! time_first_key | | time < * time_first_key ) time_first_key = time ;
}
if ( time_first_key ) {
2020-06-09 18:03:26 -04:00
chain . findFirstBlockWithTimeAndHeight ( * time_first_key - TIMESTAMP_WINDOW , rescan_height , FoundBlock ( ) . height ( rescan_height ) ) ;
2017-03-02 18:14:39 -03:00
}
2017-12-12 20:13:58 -03:00
{
2020-04-11 19:42:15 -04:00
WalletRescanReserver reserver ( * walletInstance ) ;
2019-07-16 15:20:01 -04:00
if ( ! reserver . reserve ( ) | | ( ScanResult : : SUCCESS ! = walletInstance - > ScanForWalletTransactions ( chain . getBlockHash ( rescan_height ) , rescan_height , { } /* max height */ , reserver , true /* update */ ) . status ) ) {
2019-08-19 18:12:35 -04:00
error = _ ( " Failed to rescan the wallet during initialization " ) ;
2020-02-14 14:07:00 -03:00
return false ;
2017-12-12 20:13:58 -03:00
}
}
2019-07-16 17:01:46 -04:00
walletInstance - > chainStateFlushed ( chain . getTipLocator ( ) ) ;
2020-09-19 20:25:45 -03:00
walletInstance - > GetDatabase ( ) . IncrementUpdateCounter ( ) ;
2016-02-22 08:07:55 -03:00
}
2018-05-07 18:08:03 -03:00
2020-02-14 14:07:00 -03:00
return true ;
2016-09-09 05:44:47 -03:00
}
2020-02-21 23:50:46 -03:00
const CAddressBookData * CWallet : : FindAddressBookEntry ( const CTxDestination & dest , bool allow_change ) const
{
const auto & address_book_it = m_address_book . find ( dest ) ;
if ( address_book_it = = m_address_book . end ( ) ) return nullptr ;
if ( ( ! allow_change ) & & address_book_it - > second . IsChange ( ) ) {
return nullptr ;
}
return & address_book_it - > second ;
}
2020-10-12 18:05:24 -03:00
bool CWallet : : UpgradeWallet ( int version , bilingual_str & error )
2019-04-06 13:55:34 -03:00
{
2019-04-22 00:23:33 -04:00
int prev_version = GetVersion ( ) ;
2020-04-21 18:55:20 -04:00
if ( version = = 0 ) {
2019-04-22 00:23:33 -04:00
WalletLogPrintf ( " Performing wallet upgrade to %i \n " , FEATURE_LATEST ) ;
2020-04-29 17:07:52 -04:00
version = FEATURE_LATEST ;
2019-08-19 18:12:35 -04:00
} else {
2020-04-29 17:07:52 -04:00
WalletLogPrintf ( " Allowing wallet upgrade up to %i \n " , version ) ;
2019-08-19 18:12:35 -04:00
}
2020-11-17 15:18:12 -03:00
if ( version < prev_version ) {
error = strprintf ( _ ( " Cannot downgrade wallet from version %i to version %i. Wallet version unchanged. " ) , prev_version , version ) ;
2019-04-22 00:09:39 -04:00
return false ;
}
2019-04-06 13:55:34 -03:00
2019-04-22 00:23:33 -04:00
LOCK ( cs_wallet ) ;
2019-04-06 13:55:34 -03:00
2019-04-22 00:09:39 -04:00
// Do not upgrade versions to any version between HD_SPLIT and FEATURE_PRE_SPLIT_KEYPOOL unless already supporting HD_SPLIT
2020-04-29 17:07:52 -04:00
if ( ! CanSupportFeature ( FEATURE_HD_SPLIT ) & & version > = FEATURE_HD_SPLIT & & version < FEATURE_PRE_SPLIT_KEYPOOL ) {
2020-11-17 15:18:12 -03:00
error = strprintf ( _ ( " Cannot upgrade a non HD split wallet from version %i to version %i without upgrading to support pre-split keypool. Please use version %i or no version specified. " ) , prev_version , version , FEATURE_PRE_SPLIT_KEYPOOL ) ;
2019-04-22 00:09:39 -04:00
return false ;
}
2019-04-06 13:55:34 -03:00
2020-04-29 17:07:52 -04:00
// Permanently upgrade to the version
SetMinVersion ( GetClosestWalletFeature ( version ) ) ;
2019-04-22 00:23:33 -04:00
for ( auto spk_man : GetActiveScriptPubKeyMans ( ) ) {
2020-04-29 17:00:24 -04:00
if ( ! spk_man - > Upgrade ( prev_version , version , error ) ) {
2019-04-22 00:23:33 -04:00
return false ;
2019-04-06 13:55:34 -03:00
}
}
return true ;
}
2018-04-19 18:42:40 -03:00
void CWallet : : postInitProcess ( )
2016-10-20 04:22:13 -03:00
{
2019-03-27 13:59:47 -03:00
LOCK ( cs_wallet ) ;
2016-10-20 04:22:13 -03:00
// Add wallet transactions that aren't already in a block to mempool
// Do this here as mempool requires genesis block to be loaded
2019-04-29 09:52:01 -04:00
ReacceptWalletTransactions ( ) ;
2019-03-27 14:00:08 -03:00
// Update wallet transactions with current mempool transactions.
chain ( ) . requestMempoolTransactions ( * this ) ;
2016-10-20 04:22:13 -03:00
}
2020-03-02 05:23:34 -03:00
bool CWallet : : BackupWallet ( const std : : string & strDest ) const
2016-05-16 20:31:16 -04:00
{
2020-09-19 20:25:45 -03:00
return GetDatabase ( ) . Backup ( strDest ) ;
2016-05-16 20:31:16 -04:00
}
2014-08-20 23:04:43 -04:00
CKeyPool : : CKeyPool ( )
{
nTime = GetTime ( ) ;
2017-01-16 07:10:12 -03:00
fInternal = false ;
2018-04-21 04:10:12 -03:00
m_pre_split = false ;
2014-08-20 23:04:43 -04:00
}
2017-01-10 12:45:30 -03:00
CKeyPool : : CKeyPool ( const CPubKey & vchPubKeyIn , bool internalIn )
2014-08-20 23:04:43 -04:00
{
nTime = GetTime ( ) ;
vchPubKey = vchPubKeyIn ;
2017-01-10 12:45:30 -03:00
fInternal = internalIn ;
2018-04-21 04:10:12 -03:00
m_pre_split = false ;
2014-08-20 23:04:43 -04:00
}
2021-02-12 20:01:22 -03:00
int CWallet : : GetTxDepthInMainChain ( const CWalletTx & wtx ) const
2014-08-28 11:15:21 -04:00
{
2021-02-12 20:01:22 -03:00
AssertLockHeld ( cs_wallet ) ;
if ( wtx . isUnconfirmed ( ) | | wtx . isAbandoned ( ) ) return 0 ;
2016-01-07 18:31:27 -03:00
2021-02-12 20:01:22 -03:00
return ( GetLastBlockHeight ( ) - wtx . m_confirm . block_height + 1 ) * ( wtx . isConflicted ( ) ? - 1 : 1 ) ;
2014-08-28 11:15:21 -04:00
}
2021-02-12 20:01:22 -03:00
int CWallet : : GetTxBlocksToMaturity ( const CWalletTx & wtx ) const
2014-08-28 11:15:21 -04:00
{
2021-02-12 20:01:22 -03:00
if ( ! wtx . IsCoinBase ( ) )
2014-08-28 11:15:21 -04:00
return 0 ;
2021-02-12 20:01:22 -03:00
int chain_depth = GetTxDepthInMainChain ( wtx ) ;
2018-07-13 12:41:42 -04:00
assert ( chain_depth > = 0 ) ; // coinbase tx should not be conflicted
return std : : max ( 0 , ( COINBASE_MATURITY + 1 ) - chain_depth ) ;
2014-08-28 11:15:21 -04:00
}
2021-02-12 20:01:22 -03:00
bool CWallet : : IsTxImmatureCoinBase ( const CWalletTx & wtx ) const
2018-07-11 01:17:59 -04:00
{
// note GetBlocksToMaturity is 0 for non-coinbase tx
2021-02-12 20:01:22 -03:00
return GetTxBlocksToMaturity ( wtx ) > 0 ;
2018-07-11 01:17:59 -04:00
}
2014-08-28 11:15:21 -04:00
2019-12-05 20:14:53 -03:00
bool CWallet : : IsCrypted ( ) const
2019-06-06 17:58:21 -04:00
{
2019-12-05 20:14:53 -03:00
return HasEncryptionKeys ( ) ;
2019-06-06 17:58:21 -04:00
}
bool CWallet : : IsLocked ( ) const
{
if ( ! IsCrypted ( ) ) {
return false ;
}
2019-10-07 15:11:34 -03:00
LOCK ( cs_wallet ) ;
2019-06-06 17:58:21 -04:00
return vMasterKey . empty ( ) ;
}
bool CWallet : : Lock ( )
{
2019-12-05 20:14:53 -03:00
if ( ! IsCrypted ( ) )
2019-06-06 17:58:21 -04:00
return false ;
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_wallet ) ;
2019-06-06 17:58:21 -04:00
vMasterKey . clear ( ) ;
}
NotifyStatusChanged ( this ) ;
return true ;
}
2019-10-07 15:11:34 -03:00
2019-12-05 20:02:11 -03:00
bool CWallet : : Unlock ( const CKeyingMaterial & vMasterKeyIn , bool accept_no_keys )
{
{
2019-10-07 15:11:34 -03:00
LOCK ( cs_wallet ) ;
2019-10-07 15:11:34 -03:00
for ( const auto & spk_man_pair : m_spk_managers ) {
if ( ! spk_man_pair . second - > CheckDecryptionKey ( vMasterKeyIn , accept_no_keys ) ) {
2019-12-05 20:02:11 -03:00
return false ;
}
}
vMasterKey = vMasterKeyIn ;
}
NotifyStatusChanged ( this ) ;
return true ;
}
2019-10-07 15:11:34 -03:00
std : : set < ScriptPubKeyMan * > CWallet : : GetActiveScriptPubKeyMans ( ) const
{
std : : set < ScriptPubKeyMan * > spk_mans ;
for ( bool internal : { false , true } ) {
for ( OutputType t : OUTPUT_TYPES ) {
auto spk_man = GetScriptPubKeyMan ( t , internal ) ;
if ( spk_man ) {
spk_mans . insert ( spk_man ) ;
}
}
}
return spk_mans ;
}
std : : set < ScriptPubKeyMan * > CWallet : : GetAllScriptPubKeyMans ( ) const
{
std : : set < ScriptPubKeyMan * > spk_mans ;
for ( const auto & spk_man_pair : m_spk_managers ) {
spk_mans . insert ( spk_man_pair . second . get ( ) ) ;
}
return spk_mans ;
}
ScriptPubKeyMan * CWallet : : GetScriptPubKeyMan ( const OutputType & type , bool internal ) const
{
const std : : map < OutputType , ScriptPubKeyMan * > & spk_managers = internal ? m_internal_spk_managers : m_external_spk_managers ;
std : : map < OutputType , ScriptPubKeyMan * > : : const_iterator it = spk_managers . find ( type ) ;
if ( it = = spk_managers . end ( ) ) {
return nullptr ;
}
return it - > second ;
}
2020-02-10 21:49:56 -03:00
std : : set < ScriptPubKeyMan * > CWallet : : GetScriptPubKeyMans ( const CScript & script , SignatureData & sigdata ) const
{
std : : set < ScriptPubKeyMan * > spk_mans ;
for ( const auto & spk_man_pair : m_spk_managers ) {
if ( spk_man_pair . second - > CanProvide ( script , sigdata ) ) {
spk_mans . insert ( spk_man_pair . second . get ( ) ) ;
}
}
return spk_mans ;
}
2019-10-07 15:11:34 -03:00
ScriptPubKeyMan * CWallet : : GetScriptPubKeyMan ( const CScript & script ) const
{
2019-10-07 15:11:34 -03:00
SignatureData sigdata ;
for ( const auto & spk_man_pair : m_spk_managers ) {
if ( spk_man_pair . second - > CanProvide ( script , sigdata ) ) {
return spk_man_pair . second . get ( ) ;
}
}
return nullptr ;
}
ScriptPubKeyMan * CWallet : : GetScriptPubKeyMan ( const uint256 & id ) const
{
if ( m_spk_managers . count ( id ) > 0 ) {
return m_spk_managers . at ( id ) . get ( ) ;
}
return nullptr ;
2019-10-07 15:11:34 -03:00
}
2020-02-10 23:27:59 -03:00
std : : unique_ptr < SigningProvider > CWallet : : GetSolvingProvider ( const CScript & script ) const
2019-10-07 15:11:34 -03:00
{
2019-10-07 15:11:34 -03:00
SignatureData sigdata ;
2020-02-10 23:27:59 -03:00
return GetSolvingProvider ( script , sigdata ) ;
2019-10-07 15:11:34 -03:00
}
2020-02-10 23:27:59 -03:00
std : : unique_ptr < SigningProvider > CWallet : : GetSolvingProvider ( const CScript & script , SignatureData & sigdata ) const
2019-10-07 15:11:34 -03:00
{
2019-10-07 15:11:34 -03:00
for ( const auto & spk_man_pair : m_spk_managers ) {
if ( spk_man_pair . second - > CanProvide ( script , sigdata ) ) {
2020-02-10 23:27:59 -03:00
return spk_man_pair . second - > GetSolvingProvider ( script ) ;
2019-10-07 15:11:34 -03:00
}
}
return nullptr ;
2019-10-07 15:11:34 -03:00
}
LegacyScriptPubKeyMan * CWallet : : GetLegacyScriptPubKeyMan ( ) const
{
2019-07-08 11:21:31 -04:00
if ( IsWalletFlagSet ( WALLET_FLAG_DESCRIPTORS ) ) {
return nullptr ;
}
2019-10-07 15:11:34 -03:00
// Legacy wallets only have one ScriptPubKeyMan which is a LegacyScriptPubKeyMan.
// Everything in m_internal_spk_managers and m_external_spk_managers point to the same legacyScriptPubKeyMan.
auto it = m_internal_spk_managers . find ( OutputType : : LEGACY ) ;
if ( it = = m_internal_spk_managers . end ( ) ) return nullptr ;
return dynamic_cast < LegacyScriptPubKeyMan * > ( it - > second ) ;
2019-10-07 15:11:34 -03:00
}
2019-12-05 20:01:30 -03:00
2019-10-07 15:11:34 -03:00
LegacyScriptPubKeyMan * CWallet : : GetOrCreateLegacyScriptPubKeyMan ( )
{
SetupLegacyScriptPubKeyMan ( ) ;
return GetLegacyScriptPubKeyMan ( ) ;
}
void CWallet : : SetupLegacyScriptPubKeyMan ( )
{
2019-07-08 11:21:31 -04:00
if ( ! m_internal_spk_managers . empty ( ) | | ! m_external_spk_managers . empty ( ) | | ! m_spk_managers . empty ( ) | | IsWalletFlagSet ( WALLET_FLAG_DESCRIPTORS ) ) {
2019-10-07 15:11:34 -03:00
return ;
}
auto spk_manager = std : : unique_ptr < ScriptPubKeyMan > ( new LegacyScriptPubKeyMan ( * this ) ) ;
2021-06-04 16:37:41 -04:00
for ( const auto & type : LEGACY_OUTPUT_TYPES ) {
2019-10-07 15:11:34 -03:00
m_internal_spk_managers [ type ] = spk_manager . get ( ) ;
m_external_spk_managers [ type ] = spk_manager . get ( ) ;
}
m_spk_managers [ spk_manager - > GetID ( ) ] = std : : move ( spk_manager ) ;
2019-10-07 15:11:34 -03:00
}
2019-12-05 20:01:30 -03:00
const CKeyingMaterial & CWallet : : GetEncryptionKey ( ) const
{
return vMasterKey ;
}
bool CWallet : : HasEncryptionKeys ( ) const
{
return ! mapMasterKeys . empty ( ) ;
}
2019-10-07 15:11:34 -03:00
void CWallet : : ConnectScriptPubKeyManNotifiers ( )
{
for ( const auto & spk_man : GetActiveScriptPubKeyMans ( ) ) {
spk_man - > NotifyWatchonlyChanged . connect ( NotifyWatchonlyChanged ) ;
spk_man - > NotifyCanGetAddressesChanged . connect ( NotifyCanGetAddressesChanged ) ;
}
}
2019-07-08 15:41:31 -04:00
void CWallet : : LoadDescriptorScriptPubKeyMan ( uint256 id , WalletDescriptor & desc )
{
2020-02-19 12:40:00 -03:00
if ( IsWalletFlagSet ( WALLET_FLAG_EXTERNAL_SIGNER ) ) {
auto spk_manager = std : : unique_ptr < ScriptPubKeyMan > ( new ExternalSignerScriptPubKeyMan ( * this , desc ) ) ;
m_spk_managers [ id ] = std : : move ( spk_manager ) ;
} else {
auto spk_manager = std : : unique_ptr < ScriptPubKeyMan > ( new DescriptorScriptPubKeyMan ( * this , desc ) ) ;
m_spk_managers [ id ] = std : : move ( spk_manager ) ;
}
2019-07-08 15:41:31 -04:00
}
2019-07-11 18:21:21 -04:00
void CWallet : : SetupDescriptorScriptPubKeyMans ( )
{
AssertLockHeld ( cs_wallet ) ;
2019-10-31 06:27:47 -03:00
if ( ! IsWalletFlagSet ( WALLET_FLAG_EXTERNAL_SIGNER ) ) {
// Make a seed
CKey seed_key ;
seed_key . MakeNewKey ( true ) ;
CPubKey seed = seed_key . GetPubKey ( ) ;
assert ( seed_key . VerifyPubKey ( seed ) ) ;
// Get the extended key
CExtKey master_key ;
master_key . SetSeed ( seed_key . begin ( ) , seed_key . size ( ) ) ;
for ( bool internal : { false , true } ) {
for ( OutputType t : OUTPUT_TYPES ) {
2021-06-04 16:38:47 -04:00
if ( t = = OutputType : : BECH32M ) {
// Skip taproot (bech32m) for now
// TODO: Setup taproot (bech32m) descriptors by default
continue ;
}
2021-06-29 02:29:25 -04:00
auto spk_manager = std : : unique_ptr < DescriptorScriptPubKeyMan > ( new DescriptorScriptPubKeyMan ( * this ) ) ;
2019-10-31 06:27:47 -03:00
if ( IsCrypted ( ) ) {
if ( IsLocked ( ) ) {
throw std : : runtime_error ( std : : string ( __func__ ) + " : Wallet is locked, cannot setup new descriptors " ) ;
}
if ( ! spk_manager - > CheckDecryptionKey ( vMasterKey ) & & ! spk_manager - > Encrypt ( vMasterKey , nullptr ) ) {
throw std : : runtime_error ( std : : string ( __func__ ) + " : Could not encrypt new descriptors " ) ;
}
2019-07-11 18:21:21 -04:00
}
2021-06-29 02:29:25 -04:00
spk_manager - > SetupDescriptorGeneration ( master_key , t , internal ) ;
2019-10-31 06:27:47 -03:00
uint256 id = spk_manager - > GetID ( ) ;
m_spk_managers [ id ] = std : : move ( spk_manager ) ;
AddActiveScriptPubKeyMan ( id , t , internal ) ;
}
}
} else {
ExternalSigner signer = ExternalSignerScriptPubKeyMan : : GetExternalSigner ( ) ;
// TODO: add account parameter
int account = 0 ;
UniValue signer_res = signer . GetDescriptors ( account ) ;
if ( ! signer_res . isObject ( ) ) throw std : : runtime_error ( std : : string ( __func__ ) + " : Unexpected result " ) ;
for ( bool internal : { false , true } ) {
const UniValue & descriptor_vals = find_value ( signer_res , internal ? " internal " : " receive " ) ;
if ( ! descriptor_vals . isArray ( ) ) throw std : : runtime_error ( std : : string ( __func__ ) + " : Unexpected result " ) ;
for ( const UniValue & desc_val : descriptor_vals . get_array ( ) . getValues ( ) ) {
std : : string desc_str = desc_val . getValStr ( ) ;
FlatSigningProvider keys ;
std : : string dummy_error ;
std : : unique_ptr < Descriptor > desc = Parse ( desc_str , keys , dummy_error , false ) ;
if ( ! desc - > GetOutputType ( ) ) {
continue ;
2019-07-11 18:21:21 -04:00
}
2019-10-31 06:27:47 -03:00
OutputType t = * desc - > GetOutputType ( ) ;
2021-06-29 02:29:25 -04:00
auto spk_manager = std : : unique_ptr < ExternalSignerScriptPubKeyMan > ( new ExternalSignerScriptPubKeyMan ( * this ) ) ;
2019-10-31 06:27:47 -03:00
spk_manager - > SetupDescriptor ( std : : move ( desc ) ) ;
uint256 id = spk_manager - > GetID ( ) ;
m_spk_managers [ id ] = std : : move ( spk_manager ) ;
AddActiveScriptPubKeyMan ( id , t , internal ) ;
2019-07-11 18:21:21 -04:00
}
}
}
}
2020-05-21 23:01:24 -04:00
void CWallet : : AddActiveScriptPubKeyMan ( uint256 id , OutputType type , bool internal )
{
2020-09-19 20:25:45 -03:00
WalletBatch batch ( GetDatabase ( ) ) ;
2020-05-21 23:01:24 -04:00
if ( ! batch . WriteActiveScriptPubKeyMan ( static_cast < uint8_t > ( type ) , id , internal ) ) {
throw std : : runtime_error ( std : : string ( __func__ ) + " : writing active ScriptPubKeyMan id failed " ) ;
}
LoadActiveScriptPubKeyMan ( id , type , internal ) ;
}
void CWallet : : LoadActiveScriptPubKeyMan ( uint256 id , OutputType type , bool internal )
2019-07-08 15:41:31 -04:00
{
2021-06-28 15:37:47 -04:00
// Activating ScriptPubKeyManager for a given output and change type is incompatible with legacy wallets.
// Legacy wallets have only one ScriptPubKeyManager and it's active for all output and change types.
Assert ( IsWalletFlagSet ( WALLET_FLAG_DESCRIPTORS ) ) ;
2019-08-01 18:08:47 -04:00
WalletLogPrintf ( " Setting spkMan to active: id = %s, type = %d, internal = %d \n " , id . ToString ( ) , static_cast < int > ( type ) , static_cast < int > ( internal ) ) ;
2019-07-08 15:41:31 -04:00
auto & spk_mans = internal ? m_internal_spk_managers : m_external_spk_managers ;
2021-06-28 15:37:47 -04:00
auto & spk_mans_other = internal ? m_external_spk_managers : m_internal_spk_managers ;
2019-07-08 15:41:31 -04:00
auto spk_man = m_spk_managers . at ( id ) . get ( ) ;
spk_mans [ type ] = spk_man ;
2021-06-28 15:37:47 -04:00
if ( spk_mans_other [ type ] = = spk_man ) {
2021-07-01 01:22:38 -04:00
spk_mans_other . erase ( type ) ;
2021-06-28 15:37:47 -04:00
}
2019-07-08 15:41:31 -04:00
NotifyCanGetAddressesChanged ( ) ;
}
2019-08-14 14:25:53 -04:00
2021-06-28 15:37:53 -04:00
void CWallet : : DeactivateScriptPubKeyMan ( uint256 id , OutputType type , bool internal )
{
auto spk_man = GetScriptPubKeyMan ( type , internal ) ;
if ( spk_man ! = nullptr & & spk_man - > GetID ( ) = = id ) {
WalletLogPrintf ( " Deactivate spkMan: id = %s, type = %d, internal = %d \n " , id . ToString ( ) , static_cast < int > ( type ) , static_cast < int > ( internal ) ) ;
WalletBatch batch ( GetDatabase ( ) ) ;
if ( ! batch . EraseActiveScriptPubKeyMan ( static_cast < uint8_t > ( type ) , internal ) ) {
throw std : : runtime_error ( std : : string ( __func__ ) + " : erasing active ScriptPubKeyMan id failed " ) ;
}
auto & spk_mans = internal ? m_internal_spk_managers : m_external_spk_managers ;
2021-07-01 01:22:38 -04:00
spk_mans . erase ( type ) ;
2021-06-28 15:37:53 -04:00
}
2019-07-08 15:41:31 -04:00
NotifyCanGetAddressesChanged ( ) ;
}
2019-08-14 14:25:53 -04:00
bool CWallet : : IsLegacy ( ) const
{
if ( m_internal_spk_managers . count ( OutputType : : LEGACY ) = = 0 ) {
return false ;
}
auto spk_man = dynamic_cast < LegacyScriptPubKeyMan * > ( m_internal_spk_managers . at ( OutputType : : LEGACY ) ) ;
return spk_man ! = nullptr ;
}
2019-08-01 18:08:47 -04:00
DescriptorScriptPubKeyMan * CWallet : : GetDescriptorScriptPubKeyMan ( const WalletDescriptor & desc ) const
{
for ( auto & spk_man_pair : m_spk_managers ) {
// Try to downcast to DescriptorScriptPubKeyMan then check if the descriptors match
DescriptorScriptPubKeyMan * spk_manager = dynamic_cast < DescriptorScriptPubKeyMan * > ( spk_man_pair . second . get ( ) ) ;
if ( spk_manager ! = nullptr & & spk_manager - > HasWalletDescriptor ( desc ) ) {
return spk_manager ;
}
}
return nullptr ;
}
2020-10-29 18:52:50 -03:00
ScriptPubKeyMan * CWallet : : AddWalletDescriptor ( WalletDescriptor & desc , const FlatSigningProvider & signing_provider , const std : : string & label , bool internal )
2019-08-01 18:08:47 -04:00
{
2020-08-29 10:29:00 -04:00
AssertLockHeld ( cs_wallet ) ;
2019-08-01 18:08:47 -04:00
if ( ! IsWalletFlagSet ( WALLET_FLAG_DESCRIPTORS ) ) {
WalletLogPrintf ( " Cannot add WalletDescriptor to a non-descriptor wallet \n " ) ;
return nullptr ;
}
2021-06-28 15:37:37 -04:00
auto spk_man = GetDescriptorScriptPubKeyMan ( desc ) ;
if ( spk_man ) {
2019-08-01 18:08:47 -04:00
WalletLogPrintf ( " Update existing descriptor: %s \n " , desc . descriptor - > ToString ( ) ) ;
2021-06-28 15:37:37 -04:00
spk_man - > UpdateWalletDescriptor ( desc ) ;
} else {
auto new_spk_man = std : : unique_ptr < DescriptorScriptPubKeyMan > ( new DescriptorScriptPubKeyMan ( * this , desc ) ) ;
spk_man = new_spk_man . get ( ) ;
2019-08-01 18:08:47 -04:00
2021-06-28 15:37:37 -04:00
// Save the descriptor to memory
m_spk_managers [ new_spk_man - > GetID ( ) ] = std : : move ( new_spk_man ) ;
2019-08-01 18:08:47 -04:00
}
// Add the private keys to the descriptor
for ( const auto & entry : signing_provider . keys ) {
const CKey & key = entry . second ;
2021-06-28 15:37:37 -04:00
spk_man - > AddDescriptorKey ( key , key . GetPubKey ( ) ) ;
2019-08-01 18:08:47 -04:00
}
// Top up key pool, the manager will generate new scriptPubKeys internally
2021-06-28 15:37:37 -04:00
if ( ! spk_man - > TopUp ( ) ) {
2020-10-15 08:02:58 -03:00
WalletLogPrintf ( " Could not top up scriptPubKeys \n " ) ;
return nullptr ;
}
2019-08-01 18:08:47 -04:00
// Apply the label if necessary
// Note: we disable labels for ranged descriptors
if ( ! desc . descriptor - > IsRange ( ) ) {
2021-06-28 15:37:37 -04:00
auto script_pub_keys = spk_man - > GetScriptPubKeys ( ) ;
2019-08-01 18:08:47 -04:00
if ( script_pub_keys . empty ( ) ) {
WalletLogPrintf ( " Could not generate scriptPubKeys (cache is empty) \n " ) ;
return nullptr ;
}
CTxDestination dest ;
2020-10-29 18:52:50 -03:00
if ( ! internal & & ExtractDestination ( script_pub_keys . at ( 0 ) , dest ) ) {
2019-08-01 18:08:47 -04:00
SetAddressBook ( dest , label , " receive " ) ;
}
}
// Save the descriptor to DB
2021-06-28 15:37:37 -04:00
spk_man - > WriteDescriptor ( ) ;
2019-08-01 18:08:47 -04:00
2021-06-28 15:37:37 -04:00
return spk_man ;
2019-08-01 18:08:47 -04:00
}