2012-04-15 17:39:49 -04:00
// Copyright (c) 2009-2010 Satoshi Nakamoto
2022-12-24 23:49:50 +00:00
// Copyright (c) 2009-2022 The Bitcoin Core developers
2014-12-13 12:09:33 +08:00
// Distributed under the MIT software license, see the accompanying
2012-05-18 22:02:28 +08:00
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2012-04-15 17:39:49 -04:00
2017-11-10 13:57:53 +13:00
# include <wallet/walletdb.h>
2023-05-08 11:32:13 +02:00
# include <common/system.h>
2017-09-19 18:12:25 -07:00
# include <key_io.h>
2017-11-10 13:57:53 +13:00
# include <protocol.h>
2023-08-08 10:39:01 -04:00
# include <script/script.h>
2017-11-10 13:57:53 +13:00
# include <serialize.h>
# include <sync.h>
2019-12-05 15:23:05 -05:00
# include <util/bip32.h>
2022-04-12 16:16:49 -04:00
# include <util/check.h>
2023-03-15 11:18:06 +01:00
# include <util/fs.h>
2018-10-22 15:51:11 -07:00
# include <util/time.h>
2020-08-04 16:40:31 -04:00
# include <util/translation.h>
2020-10-19 15:34:21 -04:00
# ifdef USE_BDB
2020-08-04 16:40:31 -04:00
# include <wallet/bdb.h>
2020-10-19 15:34:21 -04:00
# endif
2020-10-15 13:50:00 +00:00
# ifdef USE_SQLITE
2020-05-26 20:54:05 -04:00
# include <wallet/sqlite.h>
2020-10-15 13:50:00 +00:00
# endif
2017-11-10 13:57:53 +13:00
# include <wallet/wallet.h>
2013-04-13 00:13:08 -05:00
2016-11-26 19:32:30 -08:00
# include <atomic>
2021-03-15 11:59:05 +08:00
# include <optional>
2018-07-31 12:23:26 -04:00
# include <string>
2016-11-26 19:32:30 -08:00
2021-11-12 11:13:29 -05:00
namespace wallet {
2019-07-27 15:50:36 -04:00
namespace DBKeys {
const std : : string ACENTRY { " acentry " } ;
2019-07-08 15:41:31 -04:00
const std : : string ACTIVEEXTERNALSPK { " activeexternalspk " } ;
const std : : string ACTIVEINTERNALSPK { " activeinternalspk " } ;
2019-07-27 15:50:36 -04:00
const std : : string BESTBLOCK_NOMERKLE { " bestblock_nomerkle " } ;
const std : : string BESTBLOCK { " bestblock " } ;
const std : : string CRYPTED_KEY { " ckey " } ;
const std : : string CSCRIPT { " cscript " } ;
const std : : string DEFAULTKEY { " defaultkey " } ;
const std : : string DESTDATA { " destdata " } ;
const std : : string FLAGS { " flags " } ;
const std : : string HDCHAIN { " hdchain " } ;
const std : : string KEYMETA { " keymeta " } ;
const std : : string KEY { " key " } ;
2021-09-23 00:17:55 +12:00
const std : : string LOCKED_UTXO { " lockedutxo " } ;
2019-07-27 15:50:36 -04:00
const std : : string MASTER_KEY { " mkey " } ;
const std : : string MINVERSION { " minversion " } ;
const std : : string NAME { " name " } ;
const std : : string OLD_KEY { " wkey " } ;
const std : : string ORDERPOSNEXT { " orderposnext " } ;
const std : : string POOL { " pool " } ;
const std : : string PURPOSE { " purpose " } ;
2019-07-27 13:07:30 +01:00
const std : : string SETTINGS { " settings " } ;
2019-07-27 15:50:36 -04:00
const std : : string TX { " tx " } ;
const std : : string VERSION { " version " } ;
2019-07-08 15:41:31 -04:00
const std : : string WALLETDESCRIPTOR { " walletdescriptor " } ;
2019-07-09 13:40:34 -04:00
const std : : string WALLETDESCRIPTORCACHE { " walletdescriptorcache " } ;
2021-03-01 16:25:36 -05:00
const std : : string WALLETDESCRIPTORLHCACHE { " walletdescriptorlhcache " } ;
2019-07-09 18:23:51 -04:00
const std : : string WALLETDESCRIPTORCKEY { " walletdescriptorckey " } ;
const std : : string WALLETDESCRIPTORKEY { " walletdescriptorkey " } ;
2019-07-27 15:50:36 -04:00
const std : : string WATCHMETA { " watchmeta " } ;
const std : : string WATCHS { " watchs " } ;
2022-08-25 14:35:28 -04:00
const std : : unordered_set < std : : string > LEGACY_TYPES { CRYPTED_KEY , CSCRIPT , DEFAULTKEY , HDCHAIN , KEYMETA , KEY , OLD_KEY , POOL , WATCHMETA , WATCHS } ;
2019-07-27 15:50:36 -04:00
} // namespace DBKeys
2012-04-15 17:39:49 -04:00
//
2017-12-08 06:39:22 -05:00
// WalletBatch
2012-04-15 17:39:49 -04:00
//
2017-12-08 06:39:22 -05:00
bool WalletBatch : : WriteName ( const std : : string & strAddress , const std : : string & strName )
2012-04-15 17:39:49 -04:00
{
2019-07-27 15:50:36 -04:00
return WriteIC ( std : : make_pair ( DBKeys : : NAME , strAddress ) , strName ) ;
2012-04-15 17:39:49 -04:00
}
2017-12-08 06:39:22 -05:00
bool WalletBatch : : EraseName ( const std : : string & strAddress )
2012-04-15 17:39:49 -04:00
{
// This should only be used for sending addresses, never for receiving addresses,
// receiving addresses must always have an address book entry if they're not change return.
2019-07-27 15:50:36 -04:00
return EraseIC ( std : : make_pair ( DBKeys : : NAME , strAddress ) ) ;
2012-04-15 17:39:49 -04:00
}
2017-12-08 06:39:22 -05:00
bool WalletBatch : : WritePurpose ( const std : : string & strAddress , const std : : string & strPurpose )
2013-07-22 16:50:39 +10:00
{
2019-07-27 15:50:36 -04:00
return WriteIC ( std : : make_pair ( DBKeys : : PURPOSE , strAddress ) , strPurpose ) ;
2013-07-22 16:50:39 +10:00
}
2017-12-08 06:39:22 -05:00
bool WalletBatch : : ErasePurpose ( const std : : string & strAddress )
2013-07-22 16:50:39 +10:00
{
2019-07-27 15:50:36 -04:00
return EraseIC ( std : : make_pair ( DBKeys : : PURPOSE , strAddress ) ) ;
2013-07-22 16:50:39 +10:00
}
2017-12-08 06:39:22 -05:00
bool WalletBatch : : WriteTx ( const CWalletTx & wtx )
2013-04-13 00:13:08 -05:00
{
2019-07-27 15:50:36 -04:00
return WriteIC ( std : : make_pair ( DBKeys : : TX , wtx . GetHash ( ) ) , wtx ) ;
2013-04-13 00:13:08 -05:00
}
2017-12-08 06:39:22 -05:00
bool WalletBatch : : EraseTx ( uint256 hash )
2013-04-13 00:13:08 -05:00
{
2019-07-27 15:50:36 -04:00
return EraseIC ( std : : make_pair ( DBKeys : : TX , hash ) ) ;
2013-04-13 00:13:08 -05:00
}
2018-11-06 09:23:28 -05:00
bool WalletBatch : : WriteKeyMetadata ( const CKeyMetadata & meta , const CPubKey & pubkey , const bool overwrite )
{
2019-07-27 15:50:36 -04:00
return WriteIC ( std : : make_pair ( DBKeys : : KEYMETA , pubkey ) , meta , overwrite ) ;
2018-11-06 09:23:28 -05:00
}
2017-12-08 06:39:22 -05:00
bool WalletBatch : : WriteKey ( const CPubKey & vchPubKey , const CPrivKey & vchPrivKey , const CKeyMetadata & keyMeta )
2013-04-13 00:13:08 -05:00
{
2018-11-06 09:23:28 -05:00
if ( ! WriteKeyMetadata ( keyMeta , vchPubKey , false ) ) {
2013-04-13 00:13:08 -05:00
return false ;
2017-06-05 22:23:20 +00:00
}
2013-04-13 00:13:08 -05:00
// hash pubkey/privkey to accelerate wallet load
std : : vector < unsigned char > vchKey ;
vchKey . reserve ( vchPubKey . size ( ) + vchPrivKey . size ( ) ) ;
vchKey . insert ( vchKey . end ( ) , vchPubKey . begin ( ) , vchPubKey . end ( ) ) ;
vchKey . insert ( vchKey . end ( ) , vchPrivKey . begin ( ) , vchPrivKey . end ( ) ) ;
2020-06-26 13:36:41 -07:00
return WriteIC ( std : : make_pair ( DBKeys : : KEY , vchPubKey ) , std : : make_pair ( vchPrivKey , Hash ( vchKey ) ) , false ) ;
2013-04-13 00:13:08 -05:00
}
2017-12-08 06:39:22 -05:00
bool WalletBatch : : WriteCryptedKey ( const CPubKey & vchPubKey ,
2013-11-15 12:24:34 +01:00
const std : : vector < unsigned char > & vchCryptedSecret ,
2013-04-13 00:13:08 -05:00
const CKeyMetadata & keyMeta )
{
2018-11-06 09:23:28 -05:00
if ( ! WriteKeyMetadata ( keyMeta , vchPubKey , true ) ) {
2013-04-13 00:13:08 -05:00
return false ;
2017-06-05 22:23:20 +00:00
}
2013-04-13 00:13:08 -05:00
2019-09-23 18:10:13 -04:00
// Compute a checksum of the encrypted key
2020-06-26 13:36:41 -07:00
uint256 checksum = Hash ( vchCryptedSecret ) ;
2019-09-23 18:10:13 -04:00
const auto key = std : : make_pair ( DBKeys : : CRYPTED_KEY , vchPubKey ) ;
if ( ! WriteIC ( key , std : : make_pair ( vchCryptedSecret , checksum ) , false ) ) {
2019-12-03 19:06:15 -05:00
// It may already exist, so try writing just the checksum
std : : vector < unsigned char > val ;
2020-06-15 16:54:58 -04:00
if ( ! m_batch - > Read ( key , val ) ) {
2019-12-03 19:06:15 -05:00
return false ;
}
if ( ! WriteIC ( key , std : : make_pair ( val , checksum ) , true ) ) {
return false ;
}
2017-06-05 22:23:20 +00:00
}
2019-07-27 15:50:36 -04:00
EraseIC ( std : : make_pair ( DBKeys : : KEY , vchPubKey ) ) ;
2013-04-13 00:13:08 -05:00
return true ;
}
2017-12-08 06:39:22 -05:00
bool WalletBatch : : WriteMasterKey ( unsigned int nID , const CMasterKey & kMasterKey )
2013-04-13 00:13:08 -05:00
{
2019-07-27 15:50:36 -04:00
return WriteIC ( std : : make_pair ( DBKeys : : MASTER_KEY , nID ) , kMasterKey , true ) ;
2013-04-13 00:13:08 -05:00
}
2017-12-08 06:39:22 -05:00
bool WalletBatch : : WriteCScript ( const uint160 & hash , const CScript & redeemScript )
2013-04-13 00:13:08 -05:00
{
2019-07-27 15:50:36 -04:00
return WriteIC ( std : : make_pair ( DBKeys : : CSCRIPT , hash ) , redeemScript , false ) ;
2013-04-13 00:13:08 -05:00
}
2017-12-08 06:39:22 -05:00
bool WalletBatch : : WriteWatchOnly ( const CScript & dest , const CKeyMetadata & keyMeta )
2013-07-26 01:06:01 +02:00
{
2019-07-27 15:50:36 -04:00
if ( ! WriteIC ( std : : make_pair ( DBKeys : : WATCHMETA , dest ) , keyMeta ) ) {
2016-11-08 16:55:02 -05:00
return false ;
2017-06-05 22:23:20 +00:00
}
2021-05-31 14:57:32 +02:00
return WriteIC ( std : : make_pair ( DBKeys : : WATCHS , dest ) , uint8_t { ' 1 ' } ) ;
2013-07-26 01:06:01 +02:00
}
2017-12-08 06:39:22 -05:00
bool WalletBatch : : EraseWatchOnly ( const CScript & dest )
2014-07-26 21:05:11 +02:00
{
2019-07-27 15:50:36 -04:00
if ( ! EraseIC ( std : : make_pair ( DBKeys : : WATCHMETA , dest ) ) ) {
2016-11-08 16:55:02 -05:00
return false ;
2017-06-05 22:23:20 +00:00
}
2019-07-27 15:50:36 -04:00
return EraseIC ( std : : make_pair ( DBKeys : : WATCHS , dest ) ) ;
2014-07-26 21:05:11 +02:00
}
2017-12-08 06:39:22 -05:00
bool WalletBatch : : WriteBestBlock ( const CBlockLocator & locator )
2013-04-13 00:13:08 -05:00
{
2019-07-27 15:50:36 -04:00
WriteIC ( DBKeys : : BESTBLOCK , CBlockLocator ( ) ) ; // Write empty block locator so versions that require a merkle branch automatically rescan
return WriteIC ( DBKeys : : BESTBLOCK_NOMERKLE , locator ) ;
2013-04-13 00:13:08 -05:00
}
2017-12-08 06:39:22 -05:00
bool WalletBatch : : ReadBestBlock ( CBlockLocator & locator )
2013-04-13 00:13:08 -05:00
{
2020-06-15 16:54:58 -04:00
if ( m_batch - > Read ( DBKeys : : BESTBLOCK , locator ) & & ! locator . vHave . empty ( ) ) return true ;
return m_batch - > Read ( DBKeys : : BESTBLOCK_NOMERKLE , locator ) ;
2013-04-13 00:13:08 -05:00
}
2017-12-08 06:39:22 -05:00
bool WalletBatch : : WriteOrderPosNext ( int64_t nOrderPosNext )
2013-04-13 00:13:08 -05:00
{
2019-07-27 15:50:36 -04:00
return WriteIC ( DBKeys : : ORDERPOSNEXT , nOrderPosNext ) ;
2013-04-13 00:13:08 -05:00
}
2017-12-08 06:39:22 -05:00
bool WalletBatch : : ReadPool ( int64_t nPool , CKeyPool & keypool )
2013-04-13 00:13:08 -05:00
{
2020-06-15 16:54:58 -04:00
return m_batch - > Read ( std : : make_pair ( DBKeys : : POOL , nPool ) , keypool ) ;
2013-04-13 00:13:08 -05:00
}
2017-12-08 06:39:22 -05:00
bool WalletBatch : : WritePool ( int64_t nPool , const CKeyPool & keypool )
2013-04-13 00:13:08 -05:00
{
2019-07-27 15:50:36 -04:00
return WriteIC ( std : : make_pair ( DBKeys : : POOL , nPool ) , keypool ) ;
2013-04-13 00:13:08 -05:00
}
2017-12-08 06:39:22 -05:00
bool WalletBatch : : ErasePool ( int64_t nPool )
2013-04-13 00:13:08 -05:00
{
2019-07-27 15:50:36 -04:00
return EraseIC ( std : : make_pair ( DBKeys : : POOL , nPool ) ) ;
2013-04-13 00:13:08 -05:00
}
2017-12-08 06:39:22 -05:00
bool WalletBatch : : WriteMinVersion ( int nVersion )
2013-04-13 00:13:08 -05:00
{
2019-07-27 15:50:36 -04:00
return WriteIC ( DBKeys : : MINVERSION , nVersion ) ;
2013-04-13 00:13:08 -05:00
}
2019-07-08 15:41:31 -04:00
bool WalletBatch : : WriteActiveScriptPubKeyMan ( uint8_t type , const uint256 & id , bool internal )
{
std : : string key = internal ? DBKeys : : ACTIVEINTERNALSPK : DBKeys : : ACTIVEEXTERNALSPK ;
return WriteIC ( make_pair ( key , type ) , id ) ;
}
2021-06-28 21:37:53 +02:00
bool WalletBatch : : EraseActiveScriptPubKeyMan ( uint8_t type , bool internal )
{
const std : : string key { internal ? DBKeys : : ACTIVEINTERNALSPK : DBKeys : : ACTIVEEXTERNALSPK } ;
return EraseIC ( make_pair ( key , type ) ) ;
}
2019-07-10 16:02:43 -04:00
bool WalletBatch : : WriteDescriptorKey ( const uint256 & desc_id , const CPubKey & pubkey , const CPrivKey & privkey )
{
// hash pubkey/privkey to accelerate wallet load
std : : vector < unsigned char > key ;
key . reserve ( pubkey . size ( ) + privkey . size ( ) ) ;
key . insert ( key . end ( ) , pubkey . begin ( ) , pubkey . end ( ) ) ;
key . insert ( key . end ( ) , privkey . begin ( ) , privkey . end ( ) ) ;
2020-06-26 13:36:41 -07:00
return WriteIC ( std : : make_pair ( DBKeys : : WALLETDESCRIPTORKEY , std : : make_pair ( desc_id , pubkey ) ) , std : : make_pair ( privkey , Hash ( key ) ) , false ) ;
2019-07-10 16:02:43 -04:00
}
bool WalletBatch : : WriteCryptedDescriptorKey ( const uint256 & desc_id , const CPubKey & pubkey , const std : : vector < unsigned char > & secret )
{
if ( ! WriteIC ( std : : make_pair ( DBKeys : : WALLETDESCRIPTORCKEY , std : : make_pair ( desc_id , pubkey ) ) , secret , false ) ) {
return false ;
}
EraseIC ( std : : make_pair ( DBKeys : : WALLETDESCRIPTORKEY , std : : make_pair ( desc_id , pubkey ) ) ) ;
return true ;
}
bool WalletBatch : : WriteDescriptor ( const uint256 & desc_id , const WalletDescriptor & descriptor )
{
return WriteIC ( make_pair ( DBKeys : : WALLETDESCRIPTOR , desc_id ) , descriptor ) ;
}
2019-07-10 16:38:12 -04:00
bool WalletBatch : : WriteDescriptorDerivedCache ( const CExtPubKey & xpub , const uint256 & desc_id , uint32_t key_exp_index , uint32_t der_index )
{
std : : vector < unsigned char > ser_xpub ( BIP32_EXTKEY_SIZE ) ;
xpub . Encode ( ser_xpub . data ( ) ) ;
return WriteIC ( std : : make_pair ( std : : make_pair ( DBKeys : : WALLETDESCRIPTORCACHE , desc_id ) , std : : make_pair ( key_exp_index , der_index ) ) , ser_xpub ) ;
}
bool WalletBatch : : WriteDescriptorParentCache ( const CExtPubKey & xpub , const uint256 & desc_id , uint32_t key_exp_index )
{
std : : vector < unsigned char > ser_xpub ( BIP32_EXTKEY_SIZE ) ;
xpub . Encode ( ser_xpub . data ( ) ) ;
return WriteIC ( std : : make_pair ( std : : make_pair ( DBKeys : : WALLETDESCRIPTORCACHE , desc_id ) , key_exp_index ) , ser_xpub ) ;
}
2021-03-01 16:25:36 -05:00
bool WalletBatch : : WriteDescriptorLastHardenedCache ( const CExtPubKey & xpub , const uint256 & desc_id , uint32_t key_exp_index )
{
std : : vector < unsigned char > ser_xpub ( BIP32_EXTKEY_SIZE ) ;
xpub . Encode ( ser_xpub . data ( ) ) ;
return WriteIC ( std : : make_pair ( std : : make_pair ( DBKeys : : WALLETDESCRIPTORLHCACHE , desc_id ) , key_exp_index ) , ser_xpub ) ;
}
2021-04-22 13:24:02 -04:00
bool WalletBatch : : WriteDescriptorCacheItems ( const uint256 & desc_id , const DescriptorCache & cache )
{
for ( const auto & parent_xpub_pair : cache . GetCachedParentExtPubKeys ( ) ) {
if ( ! WriteDescriptorParentCache ( parent_xpub_pair . second , desc_id , parent_xpub_pair . first ) ) {
return false ;
}
}
for ( const auto & derived_xpub_map_pair : cache . GetCachedDerivedExtPubKeys ( ) ) {
for ( const auto & derived_xpub_pair : derived_xpub_map_pair . second ) {
if ( ! WriteDescriptorDerivedCache ( derived_xpub_pair . second , desc_id , derived_xpub_map_pair . first , derived_xpub_pair . first ) ) {
return false ;
}
}
}
2021-03-01 16:25:36 -05:00
for ( const auto & lh_xpub_pair : cache . GetCachedLastHardenedExtPubKeys ( ) ) {
if ( ! WriteDescriptorLastHardenedCache ( lh_xpub_pair . second , desc_id , lh_xpub_pair . first ) ) {
return false ;
}
}
2021-04-22 13:24:02 -04:00
return true ;
}
2021-09-23 00:17:55 +12:00
bool WalletBatch : : WriteLockedUTXO ( const COutPoint & output )
{
return WriteIC ( std : : make_pair ( DBKeys : : LOCKED_UTXO , std : : make_pair ( output . hash , output . n ) ) , uint8_t { ' 1 ' } ) ;
}
bool WalletBatch : : EraseLockedUTXO ( const COutPoint & output )
{
return EraseIC ( std : : make_pair ( DBKeys : : LOCKED_UTXO , std : : make_pair ( output . hash , output . n ) ) ) ;
}
2022-04-13 14:42:00 -04:00
bool LoadKey ( CWallet * pwallet , DataStream & ssKey , DataStream & ssValue , std : : string & strErr )
{
LOCK ( pwallet - > cs_wallet ) ;
try {
CPubKey vchPubKey ;
ssKey > > vchPubKey ;
if ( ! vchPubKey . IsValid ( ) )
{
strErr = " Error reading wallet database: CPubKey corrupt " ;
return false ;
}
CKey key ;
CPrivKey pkey ;
uint256 hash ;
ssValue > > pkey ;
// Old wallets store keys as DBKeys::KEY [pubkey] => [privkey]
// ... which was slow for wallets with lots of keys, because the public key is re-derived from the private key
// using EC operations as a checksum.
// Newer wallets store keys as DBKeys::KEY [pubkey] => [privkey][hash(pubkey,privkey)], which is much faster while
// remaining backwards-compatible.
try
{
ssValue > > hash ;
}
catch ( const std : : ios_base : : failure & ) { }
bool fSkipCheck = false ;
if ( ! hash . IsNull ( ) )
{
// hash pubkey/privkey to accelerate wallet load
std : : vector < unsigned char > vchKey ;
vchKey . reserve ( vchPubKey . size ( ) + pkey . size ( ) ) ;
vchKey . insert ( vchKey . end ( ) , vchPubKey . begin ( ) , vchPubKey . end ( ) ) ;
vchKey . insert ( vchKey . end ( ) , pkey . begin ( ) , pkey . end ( ) ) ;
if ( Hash ( vchKey ) ! = hash )
{
strErr = " Error reading wallet database: CPubKey/CPrivKey corrupt " ;
return false ;
}
fSkipCheck = true ;
}
if ( ! key . Load ( pkey , vchPubKey , fSkipCheck ) )
{
strErr = " Error reading wallet database: CPrivKey corrupt " ;
return false ;
}
if ( ! pwallet - > GetOrCreateLegacyScriptPubKeyMan ( ) - > LoadKey ( key , vchPubKey ) )
{
strErr = " Error reading wallet database: LegacyScriptPubKeyMan::LoadKey failed " ;
return false ;
}
} catch ( const std : : exception & e ) {
if ( strErr . empty ( ) ) {
strErr = e . what ( ) ;
}
return false ;
}
return true ;
}
2022-04-13 15:34:23 -04:00
bool LoadCryptedKey ( CWallet * pwallet , DataStream & ssKey , DataStream & ssValue , std : : string & strErr )
{
LOCK ( pwallet - > cs_wallet ) ;
try {
CPubKey vchPubKey ;
ssKey > > vchPubKey ;
if ( ! vchPubKey . IsValid ( ) )
{
strErr = " Error reading wallet database: CPubKey corrupt " ;
return false ;
}
std : : vector < unsigned char > vchPrivKey ;
ssValue > > vchPrivKey ;
// Get the checksum and check it
bool checksum_valid = false ;
if ( ! ssValue . eof ( ) ) {
uint256 checksum ;
ssValue > > checksum ;
if ( ! ( checksum_valid = Hash ( vchPrivKey ) = = checksum ) ) {
strErr = " Error reading wallet database: Encrypted key corrupt " ;
return false ;
}
}
if ( ! pwallet - > GetOrCreateLegacyScriptPubKeyMan ( ) - > LoadCryptedKey ( vchPubKey , vchPrivKey , checksum_valid ) )
{
strErr = " Error reading wallet database: LegacyScriptPubKeyMan::LoadCryptedKey failed " ;
return false ;
}
} catch ( const std : : exception & e ) {
if ( strErr . empty ( ) ) {
strErr = e . what ( ) ;
}
return false ;
}
return true ;
}
2022-04-13 16:07:59 -04:00
bool LoadEncryptionKey ( CWallet * pwallet , DataStream & ssKey , DataStream & ssValue , std : : string & strErr )
{
LOCK ( pwallet - > cs_wallet ) ;
try {
// Master encryption key is loaded into only the wallet and not any of the ScriptPubKeyMans.
unsigned int nID ;
ssKey > > nID ;
CMasterKey kMasterKey ;
ssValue > > kMasterKey ;
if ( pwallet - > mapMasterKeys . count ( nID ) ! = 0 )
{
strErr = strprintf ( " Error reading wallet database: duplicate CMasterKey id %u " , nID ) ;
return false ;
}
pwallet - > mapMasterKeys [ nID ] = kMasterKey ;
if ( pwallet - > nMasterKeyMaxID < nID )
pwallet - > nMasterKeyMaxID = nID ;
} catch ( const std : : exception & e ) {
if ( strErr . empty ( ) ) {
strErr = e . what ( ) ;
}
return false ;
}
return true ;
}
2022-04-13 16:37:29 -04:00
bool LoadHDChain ( CWallet * pwallet , DataStream & ssValue , std : : string & strErr )
{
LOCK ( pwallet - > cs_wallet ) ;
try {
CHDChain chain ;
ssValue > > chain ;
pwallet - > GetOrCreateLegacyScriptPubKeyMan ( ) - > LoadHDChain ( chain ) ;
} catch ( const std : : exception & e ) {
if ( strErr . empty ( ) ) {
strErr = e . what ( ) ;
}
return false ;
}
return true ;
}
2022-04-12 11:35:22 -04:00
static DBErrors LoadMinVersion ( CWallet * pwallet , DatabaseBatch & batch ) EXCLUSIVE_LOCKS_REQUIRED ( pwallet - > cs_wallet )
{
AssertLockHeld ( pwallet - > cs_wallet ) ;
int nMinVersion = 0 ;
if ( batch . Read ( DBKeys : : MINVERSION , nMinVersion ) ) {
if ( nMinVersion > FEATURE_LATEST )
return DBErrors : : TOO_NEW ;
pwallet - > LoadMinVersion ( nMinVersion ) ;
}
return DBErrors : : LOAD_OK ;
}
2022-04-12 11:39:46 -04:00
static DBErrors LoadWalletFlags ( CWallet * pwallet , DatabaseBatch & batch ) EXCLUSIVE_LOCKS_REQUIRED ( pwallet - > cs_wallet )
{
AssertLockHeld ( pwallet - > cs_wallet ) ;
uint64_t flags ;
if ( batch . Read ( DBKeys : : FLAGS , flags ) ) {
if ( ! pwallet - > LoadWalletFlags ( flags ) ) {
pwallet - > WalletLogPrintf ( " Error reading wallet database: Unknown non-tolerable wallet flags found \n " ) ;
return DBErrors : : TOO_NEW ;
}
}
return DBErrors : : LOAD_OK ;
}
2022-04-12 14:27:39 -04:00
struct LoadResult
{
DBErrors m_result { DBErrors : : LOAD_OK } ;
int m_records { 0 } ;
} ;
2023-11-19 14:58:00 +01:00
using LoadFunc = std : : function < DBErrors ( CWallet * pwallet , DataStream & key , DataStream & value , std : : string & err ) > ;
2022-04-12 16:16:49 -04:00
static LoadResult LoadRecords ( CWallet * pwallet , DatabaseBatch & batch , const std : : string & key , DataStream & prefix , LoadFunc load_func )
2022-04-12 14:27:39 -04:00
{
LoadResult result ;
DataStream ssKey ;
2023-11-19 14:58:00 +01:00
DataStream ssValue { } ;
2022-04-12 14:27:39 -04:00
2022-04-12 16:16:49 -04:00
Assume ( ! prefix . empty ( ) ) ;
2022-04-12 14:27:39 -04:00
std : : unique_ptr < DatabaseCursor > cursor = batch . GetNewPrefixCursor ( prefix ) ;
if ( ! cursor ) {
pwallet - > WalletLogPrintf ( " Error getting database cursor for '%s' records \n " , key ) ;
result . m_result = DBErrors : : CORRUPT ;
return result ;
}
while ( true ) {
DatabaseCursor : : Status status = cursor - > Next ( ssKey , ssValue ) ;
if ( status = = DatabaseCursor : : Status : : DONE ) {
break ;
} else if ( status = = DatabaseCursor : : Status : : FAIL ) {
pwallet - > WalletLogPrintf ( " Error reading next '%s' record for wallet database \n " , key ) ;
result . m_result = DBErrors : : CORRUPT ;
return result ;
}
std : : string type ;
ssKey > > type ;
assert ( type = = key ) ;
std : : string error ;
DBErrors record_res = load_func ( pwallet , ssKey , ssValue , error ) ;
if ( record_res ! = DBErrors : : LOAD_OK ) {
pwallet - > WalletLogPrintf ( " %s \n " , error ) ;
}
result . m_result = std : : max ( result . m_result , record_res ) ;
+ + result . m_records ;
}
return result ;
}
2022-04-12 16:16:49 -04:00
static LoadResult LoadRecords ( CWallet * pwallet , DatabaseBatch & batch , const std : : string & key , LoadFunc load_func )
{
DataStream prefix ;
prefix < < key ;
return LoadRecords ( pwallet , batch , key , prefix , load_func ) ;
}
2022-04-12 14:27:39 -04:00
static DBErrors LoadLegacyWalletRecords ( CWallet * pwallet , DatabaseBatch & batch , int last_client ) EXCLUSIVE_LOCKS_REQUIRED ( pwallet - > cs_wallet )
{
AssertLockHeld ( pwallet - > cs_wallet ) ;
DBErrors result = DBErrors : : LOAD_OK ;
// Make sure descriptor wallets don't have any legacy records
if ( pwallet - > IsWalletFlagSet ( WALLET_FLAG_DESCRIPTORS ) ) {
for ( const auto & type : DBKeys : : LEGACY_TYPES ) {
DataStream key ;
2023-11-19 14:58:00 +01:00
DataStream value { } ;
2022-04-12 14:27:39 -04:00
DataStream prefix ;
prefix < < type ;
std : : unique_ptr < DatabaseCursor > cursor = batch . GetNewPrefixCursor ( prefix ) ;
if ( ! cursor ) {
pwallet - > WalletLogPrintf ( " Error getting database cursor for '%s' records \n " , type ) ;
return DBErrors : : CORRUPT ;
}
DatabaseCursor : : Status status = cursor - > Next ( key , value ) ;
if ( status ! = DatabaseCursor : : Status : : DONE ) {
pwallet - > WalletLogPrintf ( " Error: Unexpected legacy entry found in descriptor wallet %s. The wallet might have been tampered with or created with malicious intent. \n " , pwallet - > GetName ( ) ) ;
return DBErrors : : UNEXPECTED_LEGACY_ENTRY ;
}
}
return DBErrors : : LOAD_OK ;
}
// Load HD Chain
// Note: There should only be one HDCHAIN record with no data following the type
LoadResult hd_chain_res = LoadRecords ( pwallet , batch , DBKeys : : HDCHAIN ,
2023-11-19 14:58:00 +01:00
[ ] ( CWallet * pwallet , DataStream & key , DataStream & value , std : : string & err ) {
2022-04-12 14:27:39 -04:00
return LoadHDChain ( pwallet , value , err ) ? DBErrors : : LOAD_OK : DBErrors : : CORRUPT ;
} ) ;
result = std : : max ( result , hd_chain_res . m_result ) ;
// Load unencrypted keys
LoadResult key_res = LoadRecords ( pwallet , batch , DBKeys : : KEY ,
2023-11-19 14:58:00 +01:00
[ ] ( CWallet * pwallet , DataStream & key , DataStream & value , std : : string & err ) {
2022-04-12 14:27:39 -04:00
return LoadKey ( pwallet , key , value , err ) ? DBErrors : : LOAD_OK : DBErrors : : CORRUPT ;
} ) ;
result = std : : max ( result , key_res . m_result ) ;
// Load encrypted keys
LoadResult ckey_res = LoadRecords ( pwallet , batch , DBKeys : : CRYPTED_KEY ,
2023-11-19 14:58:00 +01:00
[ ] ( CWallet * pwallet , DataStream & key , DataStream & value , std : : string & err ) {
2022-04-12 14:27:39 -04:00
return LoadCryptedKey ( pwallet , key , value , err ) ? DBErrors : : LOAD_OK : DBErrors : : CORRUPT ;
} ) ;
result = std : : max ( result , ckey_res . m_result ) ;
// Load scripts
LoadResult script_res = LoadRecords ( pwallet , batch , DBKeys : : CSCRIPT ,
2023-11-19 14:58:00 +01:00
[ ] ( CWallet * pwallet , DataStream & key , DataStream & value , std : : string & strErr ) {
2022-04-12 14:27:39 -04:00
uint160 hash ;
key > > hash ;
CScript script ;
value > > script ;
if ( ! pwallet - > GetOrCreateLegacyScriptPubKeyMan ( ) - > LoadCScript ( script ) )
{
strErr = " Error reading wallet database: LegacyScriptPubKeyMan::LoadCScript failed " ;
return DBErrors : : NONCRITICAL_ERROR ;
}
return DBErrors : : LOAD_OK ;
} ) ;
result = std : : max ( result , script_res . m_result ) ;
// Check whether rewrite is needed
if ( ckey_res . m_records > 0 ) {
// Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
if ( last_client = = 40000 | | last_client = = 50000 ) result = std : : max ( result , DBErrors : : NEED_REWRITE ) ;
}
// Load keymeta
std : : map < uint160 , CHDChain > hd_chains ;
LoadResult keymeta_res = LoadRecords ( pwallet , batch , DBKeys : : KEYMETA ,
2023-11-19 14:58:00 +01:00
[ & hd_chains ] ( CWallet * pwallet , DataStream & key , DataStream & value , std : : string & strErr ) {
2022-04-12 14:27:39 -04:00
CPubKey vchPubKey ;
key > > vchPubKey ;
CKeyMetadata keyMeta ;
value > > keyMeta ;
pwallet - > GetOrCreateLegacyScriptPubKeyMan ( ) - > LoadKeyMetadata ( vchPubKey . GetID ( ) , keyMeta ) ;
// Extract some CHDChain info from this metadata if it has any
if ( keyMeta . nVersion > = CKeyMetadata : : VERSION_WITH_HDDATA & & ! keyMeta . hd_seed_id . IsNull ( ) & & keyMeta . hdKeypath . size ( ) > 0 ) {
// Get the path from the key origin or from the path string
// Not applicable when path is "s" or "m" as those indicate a seed
// See https://github.com/bitcoin/bitcoin/pull/12924
bool internal = false ;
uint32_t index = 0 ;
if ( keyMeta . hdKeypath ! = " s " & & keyMeta . hdKeypath ! = " m " ) {
std : : vector < uint32_t > path ;
if ( keyMeta . has_key_origin ) {
// We have a key origin, so pull it from its path vector
path = keyMeta . key_origin . path ;
} else {
// No key origin, have to parse the string
if ( ! ParseHDKeypath ( keyMeta . hdKeypath , path ) ) {
strErr = " Error reading wallet database: keymeta with invalid HD keypath " ;
return DBErrors : : NONCRITICAL_ERROR ;
}
}
// Extract the index and internal from the path
// Path string is m/0'/k'/i'
// Path vector is [0', k', i'] (but as ints OR'd with the hardened bit
// k == 0 for external, 1 for internal. i is the index
if ( path . size ( ) ! = 3 ) {
strErr = " Error reading wallet database: keymeta found with unexpected path " ;
return DBErrors : : NONCRITICAL_ERROR ;
}
if ( path [ 0 ] ! = 0x80000000 ) {
strErr = strprintf ( " Unexpected path index of 0x%08x (expected 0x80000000) for the element at index 0 " , path [ 0 ] ) ;
return DBErrors : : NONCRITICAL_ERROR ;
}
if ( path [ 1 ] ! = 0x80000000 & & path [ 1 ] ! = ( 1 | 0x80000000 ) ) {
strErr = strprintf ( " Unexpected path index of 0x%08x (expected 0x80000000 or 0x80000001) for the element at index 1 " , path [ 1 ] ) ;
return DBErrors : : NONCRITICAL_ERROR ;
}
if ( ( path [ 2 ] & 0x80000000 ) = = 0 ) {
strErr = strprintf ( " Unexpected path index of 0x%08x (expected to be greater than or equal to 0x80000000) " , path [ 2 ] ) ;
return DBErrors : : NONCRITICAL_ERROR ;
}
internal = path [ 1 ] = = ( 1 | 0x80000000 ) ;
index = path [ 2 ] & ~ 0x80000000 ;
}
// Insert a new CHDChain, or get the one that already exists
auto [ ins , inserted ] = hd_chains . emplace ( keyMeta . hd_seed_id , CHDChain ( ) ) ;
CHDChain & chain = ins - > second ;
if ( inserted ) {
// For new chains, we want to default to VERSION_HD_BASE until we see an internal
chain . nVersion = CHDChain : : VERSION_HD_BASE ;
chain . seed_id = keyMeta . hd_seed_id ;
}
if ( internal ) {
chain . nVersion = CHDChain : : VERSION_HD_CHAIN_SPLIT ;
chain . nInternalChainCounter = std : : max ( chain . nInternalChainCounter , index + 1 ) ;
} else {
chain . nExternalChainCounter = std : : max ( chain . nExternalChainCounter , index + 1 ) ;
}
}
return DBErrors : : LOAD_OK ;
} ) ;
result = std : : max ( result , keymeta_res . m_result ) ;
// Set inactive chains
if ( ! hd_chains . empty ( ) ) {
LegacyScriptPubKeyMan * legacy_spkm = pwallet - > GetLegacyScriptPubKeyMan ( ) ;
if ( legacy_spkm ) {
for ( const auto & [ hd_seed_id , chain ] : hd_chains ) {
if ( hd_seed_id ! = legacy_spkm - > GetHDChain ( ) . seed_id ) {
legacy_spkm - > AddInactiveHDChain ( chain ) ;
}
}
} else {
pwallet - > WalletLogPrintf ( " Inactive HD Chains found but no Legacy ScriptPubKeyMan \n " ) ;
result = DBErrors : : CORRUPT ;
}
}
// Load watchonly scripts
LoadResult watch_script_res = LoadRecords ( pwallet , batch , DBKeys : : WATCHS ,
2023-11-19 14:58:00 +01:00
[ ] ( CWallet * pwallet , DataStream & key , DataStream & value , std : : string & err ) {
2022-04-12 14:27:39 -04:00
CScript script ;
key > > script ;
uint8_t fYes ;
value > > fYes ;
if ( fYes = = ' 1 ' ) {
pwallet - > GetOrCreateLegacyScriptPubKeyMan ( ) - > LoadWatchOnly ( script ) ;
}
return DBErrors : : LOAD_OK ;
} ) ;
result = std : : max ( result , watch_script_res . m_result ) ;
// Load watchonly meta
LoadResult watch_meta_res = LoadRecords ( pwallet , batch , DBKeys : : WATCHMETA ,
2023-11-19 14:58:00 +01:00
[ ] ( CWallet * pwallet , DataStream & key , DataStream & value , std : : string & err ) {
2022-04-12 14:27:39 -04:00
CScript script ;
key > > script ;
CKeyMetadata keyMeta ;
value > > keyMeta ;
pwallet - > GetOrCreateLegacyScriptPubKeyMan ( ) - > LoadScriptMetadata ( CScriptID ( script ) , keyMeta ) ;
return DBErrors : : LOAD_OK ;
} ) ;
result = std : : max ( result , watch_meta_res . m_result ) ;
// Load keypool
LoadResult pool_res = LoadRecords ( pwallet , batch , DBKeys : : POOL ,
2023-11-19 14:58:00 +01:00
[ ] ( CWallet * pwallet , DataStream & key , DataStream & value , std : : string & err ) {
2022-04-12 14:27:39 -04:00
int64_t nIndex ;
key > > nIndex ;
CKeyPool keypool ;
value > > keypool ;
pwallet - > GetOrCreateLegacyScriptPubKeyMan ( ) - > LoadKeyPool ( nIndex , keypool ) ;
return DBErrors : : LOAD_OK ;
} ) ;
result = std : : max ( result , pool_res . m_result ) ;
2022-04-18 14:43:52 -04:00
// Deal with old "wkey" and "defaultkey" records.
// These are not actually loaded, but we need to check for them
// We don't want or need the default key, but if there is one set,
// we want to make sure that it is valid so that we can detect corruption
// Note: There should only be one DEFAULTKEY with nothing trailing the type
LoadResult default_key_res = LoadRecords ( pwallet , batch , DBKeys : : DEFAULTKEY ,
2023-11-19 14:58:00 +01:00
[ ] ( CWallet * pwallet , DataStream & key , DataStream & value , std : : string & err ) {
2022-04-18 14:43:52 -04:00
CPubKey default_pubkey ;
try {
value > > default_pubkey ;
} catch ( const std : : exception & e ) {
err = e . what ( ) ;
return DBErrors : : CORRUPT ;
}
if ( ! default_pubkey . IsValid ( ) ) {
err = " Error reading wallet database: Default Key corrupt " ;
return DBErrors : : CORRUPT ;
}
return DBErrors : : LOAD_OK ;
} ) ;
result = std : : max ( result , default_key_res . m_result ) ;
// "wkey" records are unsupported, if we see any, throw an error
LoadResult wkey_res = LoadRecords ( pwallet , batch , DBKeys : : OLD_KEY ,
2023-11-19 14:58:00 +01:00
[ ] ( CWallet * pwallet , DataStream & key , DataStream & value , std : : string & err ) {
2022-04-18 14:43:52 -04:00
err = " Found unsupported 'wkey' record, try loading with version 0.18 " ;
return DBErrors : : LOAD_FAIL ;
} ) ;
result = std : : max ( result , wkey_res . m_result ) ;
2022-04-12 14:27:39 -04:00
if ( result < = DBErrors : : NONCRITICAL_ERROR ) {
// Only do logging and time first key update if there were no critical errors
pwallet - > WalletLogPrintf ( " Legacy Wallet Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total. \n " ,
key_res . m_records , ckey_res . m_records , keymeta_res . m_records , key_res . m_records + ckey_res . m_records ) ;
// nTimeFirstKey is only reliable if all keys have metadata
if ( pwallet - > IsLegacy ( ) & & ( key_res . m_records + ckey_res . m_records + watch_script_res . m_records ) ! = ( keymeta_res . m_records + watch_meta_res . m_records ) ) {
auto spk_man = pwallet - > GetOrCreateLegacyScriptPubKeyMan ( ) ;
if ( spk_man ) {
LOCK ( spk_man - > cs_KeyStore ) ;
spk_man - > UpdateTimeFirstKey ( 1 ) ;
}
}
}
return result ;
}
2022-04-12 16:16:49 -04:00
template < typename . . . Args >
static DataStream PrefixStream ( const Args & . . . args )
{
DataStream prefix ;
SerializeMany ( prefix , args . . . ) ;
return prefix ;
}
static DBErrors LoadDescriptorWalletRecords ( CWallet * pwallet , DatabaseBatch & batch , int last_client ) EXCLUSIVE_LOCKS_REQUIRED ( pwallet - > cs_wallet )
{
AssertLockHeld ( pwallet - > cs_wallet ) ;
// Load descriptor record
int num_keys = 0 ;
int num_ckeys = 0 ;
LoadResult desc_res = LoadRecords ( pwallet , batch , DBKeys : : WALLETDESCRIPTOR ,
2023-11-19 14:58:00 +01:00
[ & batch , & num_keys , & num_ckeys , & last_client ] ( CWallet * pwallet , DataStream & key , DataStream & value , std : : string & strErr ) {
2022-04-12 16:16:49 -04:00
DBErrors result = DBErrors : : LOAD_OK ;
uint256 id ;
key > > id ;
WalletDescriptor desc ;
try {
value > > desc ;
2023-07-10 12:20:14 -03:00
} catch ( const std : : ios_base : : failure & e ) {
2022-04-12 16:16:49 -04:00
strErr = strprintf ( " Error: Unrecognized descriptor found in wallet %s. " , pwallet - > GetName ( ) ) ;
strErr + = ( last_client > CLIENT_VERSION ) ? " The wallet might had been created on a newer version. " :
" The database might be corrupted or the software version is not compatible with one of your wallet descriptors. " ;
strErr + = " Please try running the latest software version " ;
2023-07-10 12:20:14 -03:00
// Also include error details
strErr = strprintf ( " %s \n Details: %s " , strErr , e . what ( ) ) ;
2022-04-12 16:16:49 -04:00
return DBErrors : : UNKNOWN_DESCRIPTOR ;
}
pwallet - > LoadDescriptorScriptPubKeyMan ( id , desc ) ;
2023-06-20 13:38:14 -03:00
// Prior to doing anything with this spkm, verify ID compatibility
if ( id ! = pwallet - > GetDescriptorScriptPubKeyMan ( desc ) - > GetID ( ) ) {
strErr = " The descriptor ID calculated by the wallet differs from the one in DB " ;
return DBErrors : : CORRUPT ;
}
2022-04-12 16:16:49 -04:00
DescriptorCache cache ;
// Get key cache for this descriptor
DataStream prefix = PrefixStream ( DBKeys : : WALLETDESCRIPTORCACHE , id ) ;
LoadResult key_cache_res = LoadRecords ( pwallet , batch , DBKeys : : WALLETDESCRIPTORCACHE , prefix ,
2023-11-19 14:58:00 +01:00
[ & id , & cache ] ( CWallet * pwallet , DataStream & key , DataStream & value , std : : string & err ) {
2022-04-12 16:16:49 -04:00
bool parent = true ;
uint256 desc_id ;
uint32_t key_exp_index ;
uint32_t der_index ;
key > > desc_id ;
assert ( desc_id = = id ) ;
key > > key_exp_index ;
// if the der_index exists, it's a derived xpub
try
{
key > > der_index ;
parent = false ;
}
catch ( . . . ) { }
std : : vector < unsigned char > ser_xpub ( BIP32_EXTKEY_SIZE ) ;
value > > ser_xpub ;
CExtPubKey xpub ;
xpub . Decode ( ser_xpub . data ( ) ) ;
if ( parent ) {
cache . CacheParentExtPubKey ( key_exp_index , xpub ) ;
} else {
cache . CacheDerivedExtPubKey ( key_exp_index , der_index , xpub ) ;
}
return DBErrors : : LOAD_OK ;
} ) ;
result = std : : max ( result , key_cache_res . m_result ) ;
// Get last hardened cache for this descriptor
prefix = PrefixStream ( DBKeys : : WALLETDESCRIPTORLHCACHE , id ) ;
LoadResult lh_cache_res = LoadRecords ( pwallet , batch , DBKeys : : WALLETDESCRIPTORLHCACHE , prefix ,
2023-11-19 14:58:00 +01:00
[ & id , & cache ] ( CWallet * pwallet , DataStream & key , DataStream & value , std : : string & err ) {
2022-04-12 16:16:49 -04:00
uint256 desc_id ;
uint32_t key_exp_index ;
key > > desc_id ;
assert ( desc_id = = id ) ;
key > > key_exp_index ;
std : : vector < unsigned char > ser_xpub ( BIP32_EXTKEY_SIZE ) ;
value > > ser_xpub ;
CExtPubKey xpub ;
xpub . Decode ( ser_xpub . data ( ) ) ;
cache . CacheLastHardenedExtPubKey ( key_exp_index , xpub ) ;
return DBErrors : : LOAD_OK ;
} ) ;
result = std : : max ( result , lh_cache_res . m_result ) ;
// Set the cache for this descriptor
auto spk_man = ( DescriptorScriptPubKeyMan * ) pwallet - > GetScriptPubKeyMan ( id ) ;
assert ( spk_man ) ;
spk_man - > SetCache ( cache ) ;
// Get unencrypted keys
prefix = PrefixStream ( DBKeys : : WALLETDESCRIPTORKEY , id ) ;
LoadResult key_res = LoadRecords ( pwallet , batch , DBKeys : : WALLETDESCRIPTORKEY , prefix ,
2023-11-19 14:58:00 +01:00
[ & id , & spk_man ] ( CWallet * pwallet , DataStream & key , DataStream & value , std : : string & strErr ) {
2022-04-12 16:16:49 -04:00
uint256 desc_id ;
CPubKey pubkey ;
key > > desc_id ;
assert ( desc_id = = id ) ;
key > > pubkey ;
if ( ! pubkey . IsValid ( ) )
{
strErr = " Error reading wallet database: descriptor unencrypted key CPubKey corrupt " ;
return DBErrors : : CORRUPT ;
}
CKey privkey ;
CPrivKey pkey ;
uint256 hash ;
value > > pkey ;
value > > hash ;
// hash pubkey/privkey to accelerate wallet load
std : : vector < unsigned char > to_hash ;
to_hash . reserve ( pubkey . size ( ) + pkey . size ( ) ) ;
to_hash . insert ( to_hash . end ( ) , pubkey . begin ( ) , pubkey . end ( ) ) ;
to_hash . insert ( to_hash . end ( ) , pkey . begin ( ) , pkey . end ( ) ) ;
if ( Hash ( to_hash ) ! = hash )
{
strErr = " Error reading wallet database: descriptor unencrypted key CPubKey/CPrivKey corrupt " ;
return DBErrors : : CORRUPT ;
}
if ( ! privkey . Load ( pkey , pubkey , true ) )
{
strErr = " Error reading wallet database: descriptor unencrypted key CPrivKey corrupt " ;
return DBErrors : : CORRUPT ;
}
spk_man - > AddKey ( pubkey . GetID ( ) , privkey ) ;
return DBErrors : : LOAD_OK ;
} ) ;
result = std : : max ( result , key_res . m_result ) ;
num_keys = key_res . m_records ;
// Get encrypted keys
prefix = PrefixStream ( DBKeys : : WALLETDESCRIPTORCKEY , id ) ;
LoadResult ckey_res = LoadRecords ( pwallet , batch , DBKeys : : WALLETDESCRIPTORCKEY , prefix ,
2023-11-19 14:58:00 +01:00
[ & id , & spk_man ] ( CWallet * pwallet , DataStream & key , DataStream & value , std : : string & err ) {
2022-04-12 16:16:49 -04:00
uint256 desc_id ;
CPubKey pubkey ;
key > > desc_id ;
assert ( desc_id = = id ) ;
key > > pubkey ;
if ( ! pubkey . IsValid ( ) )
{
err = " Error reading wallet database: descriptor encrypted key CPubKey corrupt " ;
return DBErrors : : CORRUPT ;
}
std : : vector < unsigned char > privkey ;
value > > privkey ;
spk_man - > AddCryptedKey ( pubkey . GetID ( ) , pubkey , privkey ) ;
return DBErrors : : LOAD_OK ;
} ) ;
result = std : : max ( result , ckey_res . m_result ) ;
num_ckeys = ckey_res . m_records ;
return result ;
} ) ;
if ( desc_res . m_result < = DBErrors : : NONCRITICAL_ERROR ) {
// Only log if there are no critical errors
pwallet - > WalletLogPrintf ( " Descriptors: %u, Descriptor Keys: %u plaintext, %u encrypted, %u total. \n " ,
desc_res . m_records , num_keys , num_ckeys , num_keys + num_ckeys ) ;
}
return desc_res . m_result ;
}
2022-04-12 18:25:10 -04:00
static DBErrors LoadAddressBookRecords ( CWallet * pwallet , DatabaseBatch & batch ) EXCLUSIVE_LOCKS_REQUIRED ( pwallet - > cs_wallet )
{
AssertLockHeld ( pwallet - > cs_wallet ) ;
DBErrors result = DBErrors : : LOAD_OK ;
// Load name record
LoadResult name_res = LoadRecords ( pwallet , batch , DBKeys : : NAME ,
2023-11-19 14:58:00 +01:00
[ ] ( CWallet * pwallet , DataStream & key , DataStream & value , std : : string & err ) EXCLUSIVE_LOCKS_REQUIRED ( pwallet - > cs_wallet ) {
2022-04-12 18:25:10 -04:00
std : : string strAddress ;
key > > strAddress ;
std : : string label ;
value > > label ;
pwallet - > m_address_book [ DecodeDestination ( strAddress ) ] . SetLabel ( label ) ;
return DBErrors : : LOAD_OK ;
} ) ;
result = std : : max ( result , name_res . m_result ) ;
// Load purpose record
LoadResult purpose_res = LoadRecords ( pwallet , batch , DBKeys : : PURPOSE ,
2023-11-19 14:58:00 +01:00
[ ] ( CWallet * pwallet , DataStream & key , DataStream & value , std : : string & err ) EXCLUSIVE_LOCKS_REQUIRED ( pwallet - > cs_wallet ) {
2022-04-12 18:25:10 -04:00
std : : string strAddress ;
key > > strAddress ;
std : : string purpose_str ;
value > > purpose_str ;
std : : optional < AddressPurpose > purpose { PurposeFromString ( purpose_str ) } ;
if ( ! purpose ) {
pwallet - > WalletLogPrintf ( " Warning: nonstandard purpose string '%s' for address '%s' \n " , purpose_str , strAddress ) ;
}
pwallet - > m_address_book [ DecodeDestination ( strAddress ) ] . purpose = purpose ;
return DBErrors : : LOAD_OK ;
} ) ;
result = std : : max ( result , purpose_res . m_result ) ;
// Load destination data record
LoadResult dest_res = LoadRecords ( pwallet , batch , DBKeys : : DESTDATA ,
2023-11-19 14:58:00 +01:00
[ ] ( CWallet * pwallet , DataStream & key , DataStream & value , std : : string & err ) EXCLUSIVE_LOCKS_REQUIRED ( pwallet - > cs_wallet ) {
2022-04-12 18:25:10 -04:00
std : : string strAddress , strKey , strValue ;
key > > strAddress ;
key > > strKey ;
value > > strValue ;
const CTxDestination & dest { DecodeDestination ( strAddress ) } ;
if ( strKey . compare ( " used " ) = = 0 ) {
// Load "used" key indicating if an IsMine address has
// previously been spent from with avoid_reuse option enabled.
// The strValue is not used for anything currently, but could
// hold more information in the future. Current values are just
// "1" or "p" for present (which was written prior to
// f5ba424cd44619d9b9be88b8593d69a7ba96db26).
pwallet - > LoadAddressPreviouslySpent ( dest ) ;
} else if ( strKey . compare ( 0 , 2 , " rr " ) = = 0 ) {
// Load "rr##" keys where ## is a decimal number, and strValue
// is a serialized RecentRequestEntry object.
pwallet - > LoadAddressReceiveRequest ( dest , strKey . substr ( 2 ) , strValue ) ;
}
return DBErrors : : LOAD_OK ;
} ) ;
result = std : : max ( result , dest_res . m_result ) ;
return result ;
}
2022-04-13 12:18:11 -04:00
static DBErrors LoadTxRecords ( CWallet * pwallet , DatabaseBatch & batch , std : : vector < uint256 > & upgraded_txs , bool & any_unordered ) EXCLUSIVE_LOCKS_REQUIRED ( pwallet - > cs_wallet )
{
AssertLockHeld ( pwallet - > cs_wallet ) ;
DBErrors result = DBErrors : : LOAD_OK ;
// Load tx record
any_unordered = false ;
LoadResult tx_res = LoadRecords ( pwallet , batch , DBKeys : : TX ,
2023-11-19 14:58:00 +01:00
[ & any_unordered , & upgraded_txs ] ( CWallet * pwallet , DataStream & key , DataStream & value , std : : string & err ) EXCLUSIVE_LOCKS_REQUIRED ( pwallet - > cs_wallet ) {
2022-04-13 12:18:11 -04:00
DBErrors result = DBErrors : : LOAD_OK ;
uint256 hash ;
key > > hash ;
// LoadToWallet call below creates a new CWalletTx that fill_wtx
// callback fills with transaction metadata.
auto fill_wtx = [ & ] ( CWalletTx & wtx , bool new_tx ) {
if ( ! new_tx ) {
// There's some corruption here since the tx we just tried to load was already in the wallet.
err = " Error: Corrupt transaction found. This can be fixed by removing transactions from wallet and rescanning. " ;
result = DBErrors : : CORRUPT ;
return false ;
}
value > > wtx ;
if ( wtx . GetHash ( ) ! = hash )
return false ;
// Undo serialize changes in 31600
if ( 31404 < = wtx . fTimeReceivedIsTxTime & & wtx . fTimeReceivedIsTxTime < = 31703 )
{
if ( ! value . empty ( ) )
{
uint8_t fTmp ;
uint8_t fUnused ;
std : : string unused_string ;
value > > fTmp > > fUnused > > unused_string ;
pwallet - > WalletLogPrintf ( " LoadWallet() upgrading tx ver=%d %d %s \n " ,
wtx . fTimeReceivedIsTxTime , fTmp , hash . ToString ( ) ) ;
wtx . fTimeReceivedIsTxTime = fTmp ;
}
else
{
pwallet - > WalletLogPrintf ( " LoadWallet() repairing tx ver=%d %s \n " , wtx . fTimeReceivedIsTxTime , hash . ToString ( ) ) ;
wtx . fTimeReceivedIsTxTime = 0 ;
}
upgraded_txs . push_back ( hash ) ;
}
if ( wtx . nOrderPos = = - 1 )
any_unordered = true ;
return true ;
} ;
if ( ! pwallet - > LoadToWallet ( hash , fill_wtx ) ) {
// Use std::max as fill_wtx may have already set result to CORRUPT
result = std : : max ( result , DBErrors : : NEED_RESCAN ) ;
}
return result ;
} ) ;
result = std : : max ( result , tx_res . m_result ) ;
// Load locked utxo record
LoadResult locked_utxo_res = LoadRecords ( pwallet , batch , DBKeys : : LOCKED_UTXO ,
2023-11-19 14:58:00 +01:00
[ ] ( CWallet * pwallet , DataStream & key , DataStream & value , std : : string & err ) EXCLUSIVE_LOCKS_REQUIRED ( pwallet - > cs_wallet ) {
2023-10-11 14:53:04 +01:00
Txid hash ;
2022-04-13 12:18:11 -04:00
uint32_t n ;
key > > hash ;
key > > n ;
pwallet - > LockCoin ( COutPoint ( hash , n ) ) ;
return DBErrors : : LOAD_OK ;
} ) ;
result = std : : max ( result , locked_utxo_res . m_result ) ;
// Load orderposnext record
// Note: There should only be one ORDERPOSNEXT record with nothing trailing the type
LoadResult order_pos_res = LoadRecords ( pwallet , batch , DBKeys : : ORDERPOSNEXT ,
2023-11-19 14:58:00 +01:00
[ ] ( CWallet * pwallet , DataStream & key , DataStream & value , std : : string & err ) EXCLUSIVE_LOCKS_REQUIRED ( pwallet - > cs_wallet ) {
2022-04-13 12:18:11 -04:00
try {
value > > pwallet - > nOrderPosNext ;
} catch ( const std : : exception & e ) {
err = e . what ( ) ;
return DBErrors : : NONCRITICAL_ERROR ;
}
return DBErrors : : LOAD_OK ;
} ) ;
result = std : : max ( result , order_pos_res . m_result ) ;
return result ;
}
2022-04-18 14:26:26 -04:00
static DBErrors LoadActiveSPKMs ( CWallet * pwallet , DatabaseBatch & batch ) EXCLUSIVE_LOCKS_REQUIRED ( pwallet - > cs_wallet )
{
AssertLockHeld ( pwallet - > cs_wallet ) ;
DBErrors result = DBErrors : : LOAD_OK ;
// Load spk records
std : : set < std : : pair < OutputType , bool > > seen_spks ;
for ( const auto & spk_key : { DBKeys : : ACTIVEEXTERNALSPK , DBKeys : : ACTIVEINTERNALSPK } ) {
LoadResult spkm_res = LoadRecords ( pwallet , batch , spk_key ,
2023-11-19 14:58:00 +01:00
[ & seen_spks , & spk_key ] ( CWallet * pwallet , DataStream & key , DataStream & value , std : : string & strErr ) {
2022-04-18 14:26:26 -04:00
uint8_t output_type ;
key > > output_type ;
uint256 id ;
value > > id ;
bool internal = spk_key = = DBKeys : : ACTIVEINTERNALSPK ;
auto [ it , insert ] = seen_spks . emplace ( static_cast < OutputType > ( output_type ) , internal ) ;
if ( ! insert ) {
strErr = " Multiple ScriptpubKeyMans specified for a single type " ;
return DBErrors : : CORRUPT ;
}
pwallet - > LoadActiveScriptPubKeyMan ( id , static_cast < OutputType > ( output_type ) , /*internal=*/ internal ) ;
return DBErrors : : LOAD_OK ;
} ) ;
result = std : : max ( result , spkm_res . m_result ) ;
}
return result ;
}
2022-04-18 15:08:51 -04:00
static DBErrors LoadDecryptionKeys ( CWallet * pwallet , DatabaseBatch & batch ) EXCLUSIVE_LOCKS_REQUIRED ( pwallet - > cs_wallet )
{
AssertLockHeld ( pwallet - > cs_wallet ) ;
// Load decryption key (mkey) records
LoadResult mkey_res = LoadRecords ( pwallet , batch , DBKeys : : MASTER_KEY ,
2023-11-19 14:58:00 +01:00
[ ] ( CWallet * pwallet , DataStream & key , DataStream & value , std : : string & err ) {
2022-04-18 15:08:51 -04:00
if ( ! LoadEncryptionKey ( pwallet , key , value , err ) ) {
return DBErrors : : CORRUPT ;
}
return DBErrors : : LOAD_OK ;
} ) ;
return mkey_res . m_result ;
}
2017-12-08 06:39:22 -05:00
DBErrors WalletBatch : : LoadWallet ( CWallet * pwallet )
2012-04-15 17:39:49 -04:00
{
2018-03-09 15:03:40 +01:00
DBErrors result = DBErrors : : LOAD_OK ;
2022-04-13 12:18:11 -04:00
bool any_unordered = false ;
std : : vector < uint256 > upgraded_txs ;
2012-04-15 17:39:49 -04:00
2017-02-15 17:01:30 -05:00
LOCK ( pwallet - > cs_wallet ) ;
2022-09-06 09:52:59 -03:00
// Last client version to open this wallet
int last_client = CLIENT_VERSION ;
bool has_last_client = m_batch - > Read ( DBKeys : : VERSION , last_client ) ;
pwallet - > WalletLogPrintf ( " Wallet file version = %d, last client version = %d \n " , pwallet - > GetVersion ( ) , last_client ) ;
2012-09-18 14:30:47 -04:00
try {
2022-04-12 11:35:22 -04:00
if ( ( result = LoadMinVersion ( pwallet , * m_batch ) ) ! = DBErrors : : LOAD_OK ) return result ;
2012-04-15 17:39:49 -04:00
2021-02-09 15:53:10 +01:00
// Load wallet flags, so they are known when processing other records.
// The FLAGS key is absent during wallet creation.
2022-04-12 11:39:46 -04:00
if ( ( result = LoadWalletFlags ( pwallet , * m_batch ) ) ! = DBErrors : : LOAD_OK ) return result ;
2021-02-09 15:53:10 +01:00
2021-06-06 13:32:34 -04:00
# ifndef ENABLE_EXTERNAL_SIGNER
if ( pwallet - > IsWalletFlagSet ( WALLET_FLAG_EXTERNAL_SIGNER ) ) {
pwallet - > WalletLogPrintf ( " Error: External signer wallet being loaded without external signer support compiled \n " ) ;
2022-02-20 21:02:47 +02:00
return DBErrors : : EXTERNAL_SIGNER_SUPPORT_REQUIRED ;
2021-06-06 13:32:34 -04:00
}
# endif
2022-04-12 14:27:39 -04:00
// Load legacy wallet keys
result = std : : max ( LoadLegacyWalletRecords ( pwallet , * m_batch , last_client ) , result ) ;
2022-04-12 16:16:49 -04:00
// Load descriptors
result = std : : max ( LoadDescriptorWalletRecords ( pwallet , * m_batch , last_client ) , result ) ;
// Early return if there are unknown descriptors. Later loading of ACTIVEINTERNALSPK and ACTIVEEXTERNALEXPK
// may reference the unknown descriptor's ID which can result in a misleading corruption error
// when in reality the wallet is simply too new.
if ( result = = DBErrors : : UNKNOWN_DESCRIPTOR ) return result ;
2022-04-12 18:25:10 -04:00
// Load address book
result = std : : max ( LoadAddressBookRecords ( pwallet , * m_batch ) , result ) ;
2022-04-13 12:18:11 -04:00
// Load tx records
result = std : : max ( LoadTxRecords ( pwallet , * m_batch , upgraded_txs , any_unordered ) , result ) ;
2022-04-18 14:26:26 -04:00
// Load SPKMs
result = std : : max ( LoadActiveSPKMs ( pwallet , * m_batch ) , result ) ;
2022-04-18 15:08:51 -04:00
// Load decryption keys
result = std : : max ( LoadDecryptionKeys ( pwallet , * m_batch ) , result ) ;
2020-04-24 11:23:40 -04:00
} catch ( . . . ) {
2022-04-12 14:27:39 -04:00
// Exceptions that can be ignored or treated as non-critical are handled by the individual loading functions.
// Any uncaught exceptions will be caught here and treated as critical.
2018-03-09 15:03:40 +01:00
result = DBErrors : : CORRUPT ;
2012-09-18 14:30:47 -04:00
}
2012-04-15 17:39:49 -04:00
2012-09-18 14:30:47 -04:00
// Any wallet corruption at all: skip any rewriting or
// upgrading, we don't want to make it worse.
2018-03-09 15:03:40 +01:00
if ( result ! = DBErrors : : LOAD_OK )
2012-09-18 14:30:47 -04:00
return result ;
2012-04-15 17:39:49 -04:00
2022-04-13 12:18:11 -04:00
for ( const uint256 & hash : upgraded_txs )
2017-01-19 16:08:03 -05:00
WriteTx ( pwallet - > mapWallet . at ( hash ) ) ;
2012-04-15 17:39:49 -04:00
2022-06-16 15:30:37 -03:00
if ( ! has_last_client | | last_client ! = CLIENT_VERSION ) // Update
2020-06-15 16:54:58 -04:00
m_batch - > Write ( DBKeys : : VERSION , CLIENT_VERSION ) ;
2012-04-15 17:39:49 -04:00
2022-04-13 12:18:11 -04:00
if ( any_unordered )
2016-09-28 08:57:25 -07:00
result = pwallet - > ReorderTransactions ( ) ;
2012-05-27 23:06:09 +00:00
2018-11-06 09:23:37 -05:00
// Upgrade all of the wallet keymetadata to have the hd master key id
// This operation is not atomic, but if it fails, updated entries are still backwards compatible with older software
try {
pwallet - > UpgradeKeyMetadata ( ) ;
} catch ( . . . ) {
result = DBErrors : : CORRUPT ;
}
2021-03-01 17:03:52 -05:00
// Upgrade all of the descriptor caches to cache the last hardened xpub
// This operation is not atomic, but if it fails, only new entries are added so it is backwards compatible
try {
pwallet - > UpgradeDescriptorCache ( ) ;
} catch ( . . . ) {
result = DBErrors : : CORRUPT ;
}
2012-09-18 14:30:47 -04:00
return result ;
2012-04-15 17:39:49 -04:00
}
2022-12-15 00:58:12 +01:00
DBErrors WalletBatch : : FindWalletTxHashes ( std : : vector < uint256 > & tx_hashes )
2014-02-14 11:33:07 -05:00
{
2018-03-09 15:03:40 +01:00
DBErrors result = DBErrors : : LOAD_OK ;
2014-02-14 11:33:07 -05:00
try {
int nMinVersion = 0 ;
2020-06-15 16:54:58 -04:00
if ( m_batch - > Read ( DBKeys : : MINVERSION , nMinVersion ) ) {
2018-06-18 15:21:32 -07:00
if ( nMinVersion > FEATURE_LATEST )
2018-03-09 15:03:40 +01:00
return DBErrors : : TOO_NEW ;
2014-02-14 11:33:07 -05:00
}
// Get cursor
2022-04-11 16:07:58 -04:00
std : : unique_ptr < DatabaseCursor > cursor = m_batch - > GetNewCursor ( ) ;
if ( ! cursor )
2014-02-14 11:33:07 -05:00
{
LogPrintf ( " Error getting wallet database cursor \n " ) ;
2018-03-09 15:03:40 +01:00
return DBErrors : : CORRUPT ;
2014-02-14 11:33:07 -05:00
}
while ( true )
{
// Read next record
2023-01-03 13:21:44 +01:00
DataStream ssKey { } ;
DataStream ssValue { } ;
2022-11-29 22:34:26 -05:00
DatabaseCursor : : Status status = cursor - > Next ( ssKey , ssValue ) ;
if ( status = = DatabaseCursor : : Status : : DONE ) {
2014-02-14 11:33:07 -05:00
break ;
2022-11-29 22:34:26 -05:00
} else if ( status = = DatabaseCursor : : Status : : FAIL ) {
2014-02-14 11:33:07 -05:00
LogPrintf ( " Error reading next record from wallet database \n " ) ;
2018-03-09 15:03:40 +01:00
return DBErrors : : CORRUPT ;
2014-02-14 11:33:07 -05:00
}
2017-01-27 10:33:45 +09:00
std : : string strType ;
2014-02-14 11:33:07 -05:00
ssKey > > strType ;
2019-07-27 15:50:36 -04:00
if ( strType = = DBKeys : : TX ) {
2014-02-14 11:33:07 -05:00
uint256 hash ;
ssKey > > hash ;
2022-12-15 00:58:12 +01:00
tx_hashes . push_back ( hash ) ;
2014-02-14 11:33:07 -05:00
}
}
2020-06-02 07:14:04 -04:00
} catch ( . . . ) {
2018-03-09 15:03:40 +01:00
result = DBErrors : : CORRUPT ;
2014-02-14 11:33:07 -05:00
}
return result ;
}
2017-12-08 06:39:22 -05:00
DBErrors WalletBatch : : ZapSelectTx ( std : : vector < uint256 > & vTxHashIn , std : : vector < uint256 > & vTxHashOut )
2016-03-07 08:51:06 -05:00
{
2022-12-15 00:58:12 +01:00
// build list of wallet TX hashes
2017-01-27 10:33:45 +09:00
std : : vector < uint256 > vTxHash ;
2022-12-15 00:58:12 +01:00
DBErrors err = FindWalletTxHashes ( vTxHash ) ;
2018-03-09 15:03:40 +01:00
if ( err ! = DBErrors : : LOAD_OK ) {
2016-03-07 08:51:06 -05:00
return err ;
}
std : : sort ( vTxHash . begin ( ) , vTxHash . end ( ) ) ;
std : : sort ( vTxHashIn . begin ( ) , vTxHashIn . end ( ) ) ;
// erase each matching wallet TX
bool delerror = false ;
2017-01-27 10:33:45 +09:00
std : : vector < uint256 > : : iterator it = vTxHashIn . begin ( ) ;
2018-06-18 07:58:28 +02:00
for ( const uint256 & hash : vTxHash ) {
2016-03-07 08:51:06 -05:00
while ( it < vTxHashIn . end ( ) & & ( * it ) < hash ) {
it + + ;
}
if ( it = = vTxHashIn . end ( ) ) {
break ;
}
else if ( ( * it ) = = hash ) {
if ( ! EraseTx ( hash ) ) {
2019-11-08 08:15:37 +01:00
LogPrint ( BCLog : : WALLETDB , " Transaction was found for deletion but returned database error: %s \n " , hash . GetHex ( ) ) ;
2016-03-07 08:51:06 -05:00
delerror = true ;
}
vTxHashOut . push_back ( hash ) ;
}
}
if ( delerror ) {
2018-03-09 15:03:40 +01:00
return DBErrors : : CORRUPT ;
2016-03-07 08:51:06 -05:00
}
2018-03-09 15:03:40 +01:00
return DBErrors : : LOAD_OK ;
2016-03-07 08:51:06 -05:00
}
2020-05-28 13:06:43 -04:00
void MaybeCompactWalletDB ( WalletContext & context )
2012-04-15 17:39:49 -04:00
{
2017-07-13 23:25:56 +01:00
static std : : atomic < bool > fOneThread ( false ) ;
2017-02-08 13:19:18 -05:00
if ( fOneThread . exchange ( true ) ) {
2012-04-15 17:39:49 -04:00
return ;
2017-02-08 13:19:18 -05:00
}
2012-04-15 17:39:49 -04:00
2020-05-28 13:06:43 -04:00
for ( const std : : shared_ptr < CWallet > & pwallet : GetWallets ( context ) ) {
2020-09-17 22:23:45 +01:00
WalletDatabase & dbh = pwallet - > GetDatabase ( ) ;
2012-04-15 17:39:49 -04:00
2016-09-09 08:42:30 +00:00
unsigned int nUpdateCounter = dbh . nUpdateCounter ;
2012-04-15 17:39:49 -04:00
2016-09-09 08:42:30 +00:00
if ( dbh . nLastSeen ! = nUpdateCounter ) {
dbh . nLastSeen = nUpdateCounter ;
dbh . nLastWalletUpdate = GetTime ( ) ;
}
2012-04-15 17:39:49 -04:00
2016-09-09 08:42:30 +00:00
if ( dbh . nLastFlushed ! = nUpdateCounter & & GetTime ( ) - dbh . nLastWalletUpdate > = 2 ) {
2020-06-15 14:39:26 -04:00
if ( dbh . PeriodicFlush ( ) ) {
2016-09-09 08:42:30 +00:00
dbh . nLastFlushed = nUpdateCounter ;
}
2017-03-08 11:48:58 +01:00
}
2012-04-15 17:39:49 -04:00
}
2016-09-09 08:42:30 +00:00
2017-02-08 13:19:18 -05:00
fOneThread = false ;
2012-04-15 17:39:49 -04:00
}
2020-04-12 13:40:43 -04:00
bool WalletBatch : : WriteAddressPreviouslySpent ( const CTxDestination & dest , bool previously_spent )
{
auto key { std : : make_pair ( DBKeys : : DESTDATA , std : : make_pair ( EncodeDestination ( dest ) , std : : string ( " used " ) ) ) } ;
return previously_spent ? WriteIC ( key , std : : string ( " 1 " ) ) : EraseIC ( key ) ;
}
bool WalletBatch : : WriteAddressReceiveRequest ( const CTxDestination & dest , const std : : string & id , const std : : string & receive_request )
2013-11-18 16:55:54 +01:00
{
2020-04-12 13:40:43 -04:00
return WriteIC ( std : : make_pair ( DBKeys : : DESTDATA , std : : make_pair ( EncodeDestination ( dest ) , " rr " + id ) ) , receive_request ) ;
2013-11-18 16:55:54 +01:00
}
2020-04-12 13:40:43 -04:00
bool WalletBatch : : EraseAddressReceiveRequest ( const CTxDestination & dest , const std : : string & id )
2013-11-18 16:55:54 +01:00
{
2020-04-12 13:40:43 -04:00
return EraseIC ( std : : make_pair ( DBKeys : : DESTDATA , std : : make_pair ( EncodeDestination ( dest ) , " rr " + id ) ) ) ;
2013-11-18 16:55:54 +01:00
}
2016-01-02 12:34:08 +01:00
2020-04-12 13:40:43 -04:00
bool WalletBatch : : EraseAddressData ( const CTxDestination & dest )
{
DataStream prefix ;
prefix < < DBKeys : : DESTDATA < < EncodeDestination ( dest ) ;
return m_batch - > ErasePrefix ( prefix ) ;
}
2016-01-02 12:34:08 +01:00
2017-12-08 06:39:22 -05:00
bool WalletBatch : : WriteHDChain ( const CHDChain & chain )
2016-01-02 12:34:08 +01:00
{
2019-07-27 15:50:36 -04:00
return WriteIC ( DBKeys : : HDCHAIN , chain ) ;
2016-11-26 19:32:30 -08:00
}
2017-03-08 11:48:58 +01:00
2017-02-16 14:22:18 +01:00
bool WalletBatch : : WriteWalletFlags ( const uint64_t flags )
{
2019-07-27 15:50:36 -04:00
return WriteIC ( DBKeys : : FLAGS , flags ) ;
2017-02-16 14:22:18 +01:00
}
2022-08-25 14:35:28 -04:00
bool WalletBatch : : EraseRecords ( const std : : unordered_set < std : : string > & types )
{
2022-12-05 18:04:56 -03:00
// Begin db txn
if ( ! m_batch - > TxnBegin ( ) ) return false ;
2022-08-25 14:35:28 -04:00
// Get cursor
2022-04-11 16:07:58 -04:00
std : : unique_ptr < DatabaseCursor > cursor = m_batch - > GetNewCursor ( ) ;
if ( ! cursor )
2022-08-25 14:35:28 -04:00
{
return false ;
}
// Iterate the DB and look for any records that have the type prefixes
2022-12-05 18:04:56 -03:00
while ( true ) {
2022-08-25 14:35:28 -04:00
// Read next record
2023-01-03 13:21:44 +01:00
DataStream key { } ;
DataStream value { } ;
2022-11-29 22:34:26 -05:00
DatabaseCursor : : Status status = cursor - > Next ( key , value ) ;
if ( status = = DatabaseCursor : : Status : : DONE ) {
2022-08-25 14:35:28 -04:00
break ;
2022-11-29 22:34:26 -05:00
} else if ( status = = DatabaseCursor : : Status : : FAIL ) {
2022-12-05 18:04:56 -03:00
cursor . reset ( nullptr ) ;
m_batch - > TxnAbort ( ) ; // abort db txn
2022-08-25 14:35:28 -04:00
return false ;
}
// Make a copy of key to avoid data being deleted by the following read of the type
2024-01-04 12:18:07 +01:00
const SerializeData key_data { key . begin ( ) , key . end ( ) } ;
2022-08-25 14:35:28 -04:00
std : : string type ;
key > > type ;
if ( types . count ( type ) > 0 ) {
2024-01-04 12:18:07 +01:00
if ( ! m_batch - > Erase ( Span { key_data } ) ) {
2022-12-05 18:04:56 -03:00
cursor . reset ( nullptr ) ;
m_batch - > TxnAbort ( ) ;
return false ; // erase failed
}
2022-08-25 14:35:28 -04:00
}
}
2022-12-05 18:04:56 -03:00
// Finish db txn
cursor . reset ( nullptr ) ;
return m_batch - > TxnCommit ( ) ;
2022-08-25 14:35:28 -04:00
}
2017-12-08 06:39:22 -05:00
bool WalletBatch : : TxnBegin ( )
2017-03-08 16:20:08 +00:00
{
2020-06-15 16:54:58 -04:00
return m_batch - > TxnBegin ( ) ;
2017-03-08 16:20:08 +00:00
}
2017-12-08 06:39:22 -05:00
bool WalletBatch : : TxnCommit ( )
2017-03-08 16:20:08 +00:00
{
2020-06-15 16:54:58 -04:00
return m_batch - > TxnCommit ( ) ;
2017-03-08 16:20:08 +00:00
}
2017-12-08 06:39:22 -05:00
bool WalletBatch : : TxnAbort ( )
2017-03-08 16:20:08 +00:00
{
2020-06-15 16:54:58 -04:00
return m_batch - > TxnAbort ( ) ;
2017-03-08 16:20:08 +00:00
}
2020-06-15 16:10:08 -04:00
2020-08-04 16:40:31 -04:00
std : : unique_ptr < WalletDatabase > MakeDatabase ( const fs : : path & path , const DatabaseOptions & options , DatabaseStatus & status , bilingual_str & error )
{
bool exists ;
try {
2020-06-11 08:58:46 +02:00
exists = fs : : symlink_status ( path ) . type ( ) ! = fs : : file_type : : not_found ;
2020-08-04 16:40:31 -04:00
} catch ( const fs : : filesystem_error & e ) {
2021-09-10 00:17:20 -04:00
error = Untranslated ( strprintf ( " Failed to access database path '%s': %s " , fs : : PathToString ( path ) , fsbridge : : get_filesystem_error_message ( e ) ) ) ;
2020-08-04 16:40:31 -04:00
status = DatabaseStatus : : FAILED_BAD_PATH ;
return nullptr ;
}
2021-03-15 10:41:30 +08:00
std : : optional < DatabaseFormat > format ;
2020-08-04 16:40:31 -04:00
if ( exists ) {
2020-10-30 16:41:23 -04:00
if ( IsBDBFile ( BDBDataFile ( path ) ) ) {
2020-08-04 16:40:31 -04:00
format = DatabaseFormat : : BERKELEY ;
}
2020-10-30 16:41:23 -04:00
if ( IsSQLiteFile ( SQLiteDataFile ( path ) ) ) {
2020-05-26 20:54:05 -04:00
if ( format ) {
2021-09-10 00:17:20 -04:00
error = Untranslated ( strprintf ( " Failed to load database path '%s'. Data is in ambiguous format. " , fs : : PathToString ( path ) ) ) ;
2020-05-26 20:54:05 -04:00
status = DatabaseStatus : : FAILED_BAD_FORMAT ;
return nullptr ;
}
format = DatabaseFormat : : SQLITE ;
}
2020-08-04 16:40:31 -04:00
} else if ( options . require_existing ) {
2021-09-10 00:17:20 -04:00
error = Untranslated ( strprintf ( " Failed to load database path '%s'. Path does not exist. " , fs : : PathToString ( path ) ) ) ;
2020-08-04 16:40:31 -04:00
status = DatabaseStatus : : FAILED_NOT_FOUND ;
return nullptr ;
}
if ( ! format & & options . require_existing ) {
2021-09-10 00:17:20 -04:00
error = Untranslated ( strprintf ( " Failed to load database path '%s'. Data is not in recognized format. " , fs : : PathToString ( path ) ) ) ;
2020-08-04 16:40:31 -04:00
status = DatabaseStatus : : FAILED_BAD_FORMAT ;
return nullptr ;
}
if ( format & & options . require_create ) {
2021-09-10 00:17:20 -04:00
error = Untranslated ( strprintf ( " Failed to create database path '%s'. Database already exists. " , fs : : PathToString ( path ) ) ) ;
2020-08-04 16:40:31 -04:00
status = DatabaseStatus : : FAILED_ALREADY_EXISTS ;
return nullptr ;
}
2020-05-26 20:54:05 -04:00
// A db already exists so format is set, but options also specifies the format, so make sure they agree
if ( format & & options . require_format & & format ! = options . require_format ) {
2021-09-10 00:17:20 -04:00
error = Untranslated ( strprintf ( " Failed to load database path '%s'. Data is not in required format. " , fs : : PathToString ( path ) ) ) ;
2020-05-26 20:54:05 -04:00
status = DatabaseStatus : : FAILED_BAD_FORMAT ;
return nullptr ;
}
// Format is not set when a db doesn't already exist, so use the format specified by the options if it is set.
if ( ! format & & options . require_format ) format = options . require_format ;
2020-10-19 15:34:21 -04:00
// If the format is not specified or detected, choose the default format based on what is available. We prefer BDB over SQLite for now.
if ( ! format ) {
2020-10-15 13:50:00 +00:00
# ifdef USE_SQLITE
2020-10-19 15:34:21 -04:00
format = DatabaseFormat : : SQLITE ;
# endif
# ifdef USE_BDB
format = DatabaseFormat : : BERKELEY ;
# endif
2020-05-26 20:54:05 -04:00
}
2020-10-19 15:34:21 -04:00
if ( format = = DatabaseFormat : : SQLITE ) {
# ifdef USE_SQLITE
2024-01-25 13:46:58 +01:00
if constexpr ( true ) {
return MakeSQLiteDatabase ( path , options , status , error ) ;
} else
2023-12-05 15:26:45 +01:00
# endif
2024-01-25 13:46:58 +01:00
{
error = Untranslated ( strprintf ( " Failed to open database path '%s'. Build does not support SQLite database format. " , fs : : PathToString ( path ) ) ) ;
status = DatabaseStatus : : FAILED_BAD_FORMAT ;
return nullptr ;
}
2020-10-19 15:34:21 -04:00
}
2020-05-26 20:54:05 -04:00
2020-10-19 15:34:21 -04:00
# ifdef USE_BDB
2024-01-25 13:46:58 +01:00
if constexpr ( true ) {
return MakeBerkeleyDatabase ( path , options , status , error ) ;
} else
2023-12-05 15:26:45 +01:00
# endif
2024-01-25 13:46:58 +01:00
{
error = Untranslated ( strprintf ( " Failed to open database path '%s'. Build does not support Berkeley DB database format. " , fs : : PathToString ( path ) ) ) ;
status = DatabaseStatus : : FAILED_BAD_FORMAT ;
return nullptr ;
}
2020-08-04 16:40:31 -04:00
}
2021-11-12 11:13:29 -05:00
} // namespace wallet