1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2016 The Bitcoin Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
6 #include "wallet/walletdb.h"
9 #include "consensus/tx_verify.h"
10 #include "consensus/validation.h"
13 #include "serialize.h"
17 #include "wallet/wallet.h"
21 #include <boost/foreach.hpp>
22 #include <boost/thread.hpp>
28 bool CWalletDB::WriteName(const std::string
& strAddress
, const std::string
& strName
)
30 return WriteIC(std::make_pair(std::string("name"), strAddress
), strName
);
33 bool CWalletDB::EraseName(const std::string
& strAddress
)
35 // This should only be used for sending addresses, never for receiving addresses,
36 // receiving addresses must always have an address book entry if they're not change return.
37 return EraseIC(std::make_pair(std::string("name"), strAddress
));
40 bool CWalletDB::WritePurpose(const std::string
& strAddress
, const std::string
& strPurpose
)
42 return WriteIC(std::make_pair(std::string("purpose"), strAddress
), strPurpose
);
45 bool CWalletDB::ErasePurpose(const std::string
& strPurpose
)
47 return EraseIC(std::make_pair(std::string("purpose"), strPurpose
));
50 bool CWalletDB::WriteTx(const CWalletTx
& wtx
)
52 return WriteIC(std::make_pair(std::string("tx"), wtx
.GetHash()), wtx
);
55 bool CWalletDB::EraseTx(uint256 hash
)
57 return EraseIC(std::make_pair(std::string("tx"), hash
));
60 bool CWalletDB::WriteKey(const CPubKey
& vchPubKey
, const CPrivKey
& vchPrivKey
, const CKeyMetadata
& keyMeta
)
62 if (!WriteIC(std::make_pair(std::string("keymeta"), vchPubKey
), keyMeta
, false)) {
66 // hash pubkey/privkey to accelerate wallet load
67 std::vector
<unsigned char> vchKey
;
68 vchKey
.reserve(vchPubKey
.size() + vchPrivKey
.size());
69 vchKey
.insert(vchKey
.end(), vchPubKey
.begin(), vchPubKey
.end());
70 vchKey
.insert(vchKey
.end(), vchPrivKey
.begin(), vchPrivKey
.end());
72 return WriteIC(std::make_pair(std::string("key"), vchPubKey
), std::make_pair(vchPrivKey
, Hash(vchKey
.begin(), vchKey
.end())), false);
75 bool CWalletDB::WriteCryptedKey(const CPubKey
& vchPubKey
,
76 const std::vector
<unsigned char>& vchCryptedSecret
,
77 const CKeyMetadata
&keyMeta
)
79 if (!WriteIC(std::make_pair(std::string("keymeta"), vchPubKey
), keyMeta
)) {
83 if (!WriteIC(std::make_pair(std::string("ckey"), vchPubKey
), vchCryptedSecret
, false)) {
86 EraseIC(std::make_pair(std::string("key"), vchPubKey
));
87 EraseIC(std::make_pair(std::string("wkey"), vchPubKey
));
91 bool CWalletDB::WriteMasterKey(unsigned int nID
, const CMasterKey
& kMasterKey
)
93 return WriteIC(std::make_pair(std::string("mkey"), nID
), kMasterKey
, true);
96 bool CWalletDB::WriteCScript(const uint160
& hash
, const CScript
& redeemScript
)
98 return WriteIC(std::make_pair(std::string("cscript"), hash
), *(const CScriptBase
*)(&redeemScript
), false);
101 bool CWalletDB::WriteWatchOnly(const CScript
&dest
, const CKeyMetadata
& keyMeta
)
103 if (!WriteIC(std::make_pair(std::string("watchmeta"), *(const CScriptBase
*)(&dest
)), keyMeta
)) {
106 return WriteIC(std::make_pair(std::string("watchs"), *(const CScriptBase
*)(&dest
)), '1');
109 bool CWalletDB::EraseWatchOnly(const CScript
&dest
)
111 if (!EraseIC(std::make_pair(std::string("watchmeta"), *(const CScriptBase
*)(&dest
)))) {
114 return EraseIC(std::make_pair(std::string("watchs"), *(const CScriptBase
*)(&dest
)));
117 bool CWalletDB::WriteBestBlock(const CBlockLocator
& locator
)
119 WriteIC(std::string("bestblock"), CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan
120 return WriteIC(std::string("bestblock_nomerkle"), locator
);
123 bool CWalletDB::ReadBestBlock(CBlockLocator
& locator
)
125 if (batch
.Read(std::string("bestblock"), locator
) && !locator
.vHave
.empty()) return true;
126 return batch
.Read(std::string("bestblock_nomerkle"), locator
);
129 bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext
)
131 return WriteIC(std::string("orderposnext"), nOrderPosNext
);
134 bool CWalletDB::WriteDefaultKey(const CPubKey
& vchPubKey
)
136 return WriteIC(std::string("defaultkey"), vchPubKey
);
139 bool CWalletDB::ReadPool(int64_t nPool
, CKeyPool
& keypool
)
141 return batch
.Read(std::make_pair(std::string("pool"), nPool
), keypool
);
144 bool CWalletDB::WritePool(int64_t nPool
, const CKeyPool
& keypool
)
146 return WriteIC(std::make_pair(std::string("pool"), nPool
), keypool
);
149 bool CWalletDB::ErasePool(int64_t nPool
)
151 return EraseIC(std::make_pair(std::string("pool"), nPool
));
154 bool CWalletDB::WriteMinVersion(int nVersion
)
156 return WriteIC(std::string("minversion"), nVersion
);
159 bool CWalletDB::ReadAccount(const std::string
& strAccount
, CAccount
& account
)
162 return batch
.Read(std::make_pair(std::string("acc"), strAccount
), account
);
165 bool CWalletDB::WriteAccount(const std::string
& strAccount
, const CAccount
& account
)
167 return WriteIC(std::make_pair(std::string("acc"), strAccount
), account
);
170 bool CWalletDB::WriteAccountingEntry(const uint64_t nAccEntryNum
, const CAccountingEntry
& acentry
)
172 return WriteIC(std::make_pair(std::string("acentry"), std::make_pair(acentry
.strAccount
, nAccEntryNum
)), acentry
);
175 CAmount
CWalletDB::GetAccountCreditDebit(const std::string
& strAccount
)
177 std::list
<CAccountingEntry
> entries
;
178 ListAccountCreditDebit(strAccount
, entries
);
180 CAmount nCreditDebit
= 0;
181 BOOST_FOREACH (const CAccountingEntry
& entry
, entries
)
182 nCreditDebit
+= entry
.nCreditDebit
;
187 void CWalletDB::ListAccountCreditDebit(const std::string
& strAccount
, std::list
<CAccountingEntry
>& entries
)
189 bool fAllAccounts
= (strAccount
== "*");
191 Dbc
* pcursor
= batch
.GetCursor();
193 throw std::runtime_error(std::string(__func__
) + ": cannot create DB cursor");
194 bool setRange
= true;
198 CDataStream
ssKey(SER_DISK
, CLIENT_VERSION
);
200 ssKey
<< std::make_pair(std::string("acentry"), std::make_pair((fAllAccounts
? std::string("") : strAccount
), uint64_t(0)));
201 CDataStream
ssValue(SER_DISK
, CLIENT_VERSION
);
202 int ret
= batch
.ReadAtCursor(pcursor
, ssKey
, ssValue
, setRange
);
204 if (ret
== DB_NOTFOUND
)
209 throw std::runtime_error(std::string(__func__
) + ": error scanning DB");
215 if (strType
!= "acentry")
217 CAccountingEntry acentry
;
218 ssKey
>> acentry
.strAccount
;
219 if (!fAllAccounts
&& acentry
.strAccount
!= strAccount
)
223 ssKey
>> acentry
.nEntryNo
;
224 entries
.push_back(acentry
);
230 class CWalletScanState
{
234 unsigned int nWatchKeys
;
235 unsigned int nKeyMeta
;
239 std::vector
<uint256
> vWalletUpgrade
;
242 nKeys
= nCKeys
= nWatchKeys
= nKeyMeta
= 0;
243 fIsEncrypted
= false;
244 fAnyUnordered
= false;
250 ReadKeyValue(CWallet
* pwallet
, CDataStream
& ssKey
, CDataStream
& ssValue
,
251 CWalletScanState
&wss
, std::string
& strType
, std::string
& strErr
)
255 // Taking advantage of the fact that pair serialization
256 // is just the two items serialized one after the other
258 if (strType
== "name")
260 std::string strAddress
;
262 ssValue
>> pwallet
->mapAddressBook
[CBitcoinAddress(strAddress
).Get()].name
;
264 else if (strType
== "purpose")
266 std::string strAddress
;
268 ssValue
>> pwallet
->mapAddressBook
[CBitcoinAddress(strAddress
).Get()].purpose
;
270 else if (strType
== "tx")
276 CValidationState state
;
277 if (!(CheckTransaction(wtx
, state
) && (wtx
.GetHash() == hash
) && state
.IsValid()))
280 // Undo serialize changes in 31600
281 if (31404 <= wtx
.fTimeReceivedIsTxTime
&& wtx
.fTimeReceivedIsTxTime
<= 31703)
283 if (!ssValue
.empty())
287 ssValue
>> fTmp
>> fUnused
>> wtx
.strFromAccount
;
288 strErr
= strprintf("LoadWallet() upgrading tx ver=%d %d '%s' %s",
289 wtx
.fTimeReceivedIsTxTime
, fTmp
, wtx
.strFromAccount
, hash
.ToString());
290 wtx
.fTimeReceivedIsTxTime
= fTmp
;
294 strErr
= strprintf("LoadWallet() repairing tx ver=%d %s", wtx
.fTimeReceivedIsTxTime
, hash
.ToString());
295 wtx
.fTimeReceivedIsTxTime
= 0;
297 wss
.vWalletUpgrade
.push_back(hash
);
300 if (wtx
.nOrderPos
== -1)
301 wss
.fAnyUnordered
= true;
303 pwallet
->LoadToWallet(wtx
);
305 else if (strType
== "acentry")
307 std::string strAccount
;
311 if (nNumber
> pwallet
->nAccountingEntryNumber
) {
312 pwallet
->nAccountingEntryNumber
= nNumber
;
315 if (!wss
.fAnyUnordered
)
317 CAccountingEntry acentry
;
319 if (acentry
.nOrderPos
== -1)
320 wss
.fAnyUnordered
= true;
323 else if (strType
== "watchs")
327 ssKey
>> *(CScriptBase
*)(&script
);
331 pwallet
->LoadWatchOnly(script
);
333 else if (strType
== "key" || strType
== "wkey")
337 if (!vchPubKey
.IsValid())
339 strErr
= "Error reading wallet database: CPubKey corrupt";
346 if (strType
== "key")
353 pkey
= wkey
.vchPrivKey
;
356 // Old wallets store keys as "key" [pubkey] => [privkey]
357 // ... which was slow for wallets with lots of keys, because the public key is re-derived from the private key
358 // using EC operations as a checksum.
359 // Newer wallets store keys as "key"[pubkey] => [privkey][hash(pubkey,privkey)], which is much faster while
360 // remaining backwards-compatible.
367 bool fSkipCheck
= false;
371 // hash pubkey/privkey to accelerate wallet load
372 std::vector
<unsigned char> vchKey
;
373 vchKey
.reserve(vchPubKey
.size() + pkey
.size());
374 vchKey
.insert(vchKey
.end(), vchPubKey
.begin(), vchPubKey
.end());
375 vchKey
.insert(vchKey
.end(), pkey
.begin(), pkey
.end());
377 if (Hash(vchKey
.begin(), vchKey
.end()) != hash
)
379 strErr
= "Error reading wallet database: CPubKey/CPrivKey corrupt";
386 if (!key
.Load(pkey
, vchPubKey
, fSkipCheck
))
388 strErr
= "Error reading wallet database: CPrivKey corrupt";
391 if (!pwallet
->LoadKey(key
, vchPubKey
))
393 strErr
= "Error reading wallet database: LoadKey failed";
397 else if (strType
== "mkey")
401 CMasterKey kMasterKey
;
402 ssValue
>> kMasterKey
;
403 if(pwallet
->mapMasterKeys
.count(nID
) != 0)
405 strErr
= strprintf("Error reading wallet database: duplicate CMasterKey id %u", nID
);
408 pwallet
->mapMasterKeys
[nID
] = kMasterKey
;
409 if (pwallet
->nMasterKeyMaxID
< nID
)
410 pwallet
->nMasterKeyMaxID
= nID
;
412 else if (strType
== "ckey")
416 if (!vchPubKey
.IsValid())
418 strErr
= "Error reading wallet database: CPubKey corrupt";
421 std::vector
<unsigned char> vchPrivKey
;
422 ssValue
>> vchPrivKey
;
425 if (!pwallet
->LoadCryptedKey(vchPubKey
, vchPrivKey
))
427 strErr
= "Error reading wallet database: LoadCryptedKey failed";
430 wss
.fIsEncrypted
= true;
432 else if (strType
== "keymeta" || strType
== "watchmeta")
434 CTxDestination keyID
;
435 if (strType
== "keymeta")
439 keyID
= vchPubKey
.GetID();
441 else if (strType
== "watchmeta")
444 ssKey
>> *(CScriptBase
*)(&script
);
445 keyID
= CScriptID(script
);
448 CKeyMetadata keyMeta
;
452 pwallet
->LoadKeyMetadata(keyID
, keyMeta
);
454 else if (strType
== "defaultkey")
456 ssValue
>> pwallet
->vchDefaultKey
;
458 else if (strType
== "pool")
465 pwallet
->LoadKeyPool(nIndex
, keypool
);
467 else if (strType
== "version")
469 ssValue
>> wss
.nFileVersion
;
470 if (wss
.nFileVersion
== 10300)
471 wss
.nFileVersion
= 300;
473 else if (strType
== "cscript")
478 ssValue
>> *(CScriptBase
*)(&script
);
479 if (!pwallet
->LoadCScript(script
))
481 strErr
= "Error reading wallet database: LoadCScript failed";
485 else if (strType
== "orderposnext")
487 ssValue
>> pwallet
->nOrderPosNext
;
489 else if (strType
== "destdata")
491 std::string strAddress
, strKey
, strValue
;
495 if (!pwallet
->LoadDestData(CBitcoinAddress(strAddress
).Get(), strKey
, strValue
))
497 strErr
= "Error reading wallet database: LoadDestData failed";
501 else if (strType
== "hdchain")
505 if (!pwallet
->SetHDChain(chain
, true))
507 strErr
= "Error reading wallet database: SetHDChain failed";
518 bool CWalletDB::IsKeyType(const std::string
& strType
)
520 return (strType
== "key" || strType
== "wkey" ||
521 strType
== "mkey" || strType
== "ckey");
524 DBErrors
CWalletDB::LoadWallet(CWallet
* pwallet
)
526 pwallet
->vchDefaultKey
= CPubKey();
527 CWalletScanState wss
;
528 bool fNoncriticalErrors
= false;
529 DBErrors result
= DB_LOAD_OK
;
531 LOCK(pwallet
->cs_wallet
);
534 if (batch
.Read((std::string
)"minversion", nMinVersion
))
536 if (nMinVersion
> CLIENT_VERSION
)
538 pwallet
->LoadMinVersion(nMinVersion
);
542 Dbc
* pcursor
= batch
.GetCursor();
545 LogPrintf("Error getting wallet database cursor\n");
552 CDataStream
ssKey(SER_DISK
, CLIENT_VERSION
);
553 CDataStream
ssValue(SER_DISK
, CLIENT_VERSION
);
554 int ret
= batch
.ReadAtCursor(pcursor
, ssKey
, ssValue
);
555 if (ret
== DB_NOTFOUND
)
559 LogPrintf("Error reading next record from wallet database\n");
563 // Try to be tolerant of single corrupt records:
564 std::string strType
, strErr
;
565 if (!ReadKeyValue(pwallet
, ssKey
, ssValue
, wss
, strType
, strErr
))
567 // losing keys is considered a catastrophic error, anything else
568 // we assume the user can live with:
569 if (IsKeyType(strType
))
573 // Leave other errors alone, if we try to fix them we might make things worse.
574 fNoncriticalErrors
= true; // ... but do warn the user there is something wrong.
576 // Rescan if there is a bad transaction record:
577 SoftSetBoolArg("-rescan", true);
581 LogPrintf("%s\n", strErr
);
585 catch (const boost::thread_interrupted
&) {
592 if (fNoncriticalErrors
&& result
== DB_LOAD_OK
)
593 result
= DB_NONCRITICAL_ERROR
;
595 // Any wallet corruption at all: skip any rewriting or
596 // upgrading, we don't want to make it worse.
597 if (result
!= DB_LOAD_OK
)
600 LogPrintf("nFileVersion = %d\n", wss
.nFileVersion
);
602 LogPrintf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total\n",
603 wss
.nKeys
, wss
.nCKeys
, wss
.nKeyMeta
, wss
.nKeys
+ wss
.nCKeys
);
605 // nTimeFirstKey is only reliable if all keys have metadata
606 if ((wss
.nKeys
+ wss
.nCKeys
+ wss
.nWatchKeys
) != wss
.nKeyMeta
)
607 pwallet
->UpdateTimeFirstKey(1);
609 BOOST_FOREACH(uint256 hash
, wss
.vWalletUpgrade
)
610 WriteTx(pwallet
->mapWallet
[hash
]);
612 // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
613 if (wss
.fIsEncrypted
&& (wss
.nFileVersion
== 40000 || wss
.nFileVersion
== 50000))
614 return DB_NEED_REWRITE
;
616 if (wss
.nFileVersion
< CLIENT_VERSION
) // Update
617 WriteVersion(CLIENT_VERSION
);
619 if (wss
.fAnyUnordered
)
620 result
= pwallet
->ReorderTransactions();
622 pwallet
->laccentries
.clear();
623 ListAccountCreditDebit("*", pwallet
->laccentries
);
624 BOOST_FOREACH(CAccountingEntry
& entry
, pwallet
->laccentries
) {
625 pwallet
->wtxOrdered
.insert(make_pair(entry
.nOrderPos
, CWallet::TxPair((CWalletTx
*)0, &entry
)));
631 DBErrors
CWalletDB::FindWalletTx(std::vector
<uint256
>& vTxHash
, std::vector
<CWalletTx
>& vWtx
)
633 bool fNoncriticalErrors
= false;
634 DBErrors result
= DB_LOAD_OK
;
638 if (batch
.Read((std::string
)"minversion", nMinVersion
))
640 if (nMinVersion
> CLIENT_VERSION
)
645 Dbc
* pcursor
= batch
.GetCursor();
648 LogPrintf("Error getting wallet database cursor\n");
655 CDataStream
ssKey(SER_DISK
, CLIENT_VERSION
);
656 CDataStream
ssValue(SER_DISK
, CLIENT_VERSION
);
657 int ret
= batch
.ReadAtCursor(pcursor
, ssKey
, ssValue
);
658 if (ret
== DB_NOTFOUND
)
662 LogPrintf("Error reading next record from wallet database\n");
668 if (strType
== "tx") {
675 vTxHash
.push_back(hash
);
681 catch (const boost::thread_interrupted
&) {
688 if (fNoncriticalErrors
&& result
== DB_LOAD_OK
)
689 result
= DB_NONCRITICAL_ERROR
;
694 DBErrors
CWalletDB::ZapSelectTx(std::vector
<uint256
>& vTxHashIn
, std::vector
<uint256
>& vTxHashOut
)
696 // build list of wallet TXs and hashes
697 std::vector
<uint256
> vTxHash
;
698 std::vector
<CWalletTx
> vWtx
;
699 DBErrors err
= FindWalletTx(vTxHash
, vWtx
);
700 if (err
!= DB_LOAD_OK
) {
704 std::sort(vTxHash
.begin(), vTxHash
.end());
705 std::sort(vTxHashIn
.begin(), vTxHashIn
.end());
707 // erase each matching wallet TX
708 bool delerror
= false;
709 std::vector
<uint256
>::iterator it
= vTxHashIn
.begin();
710 BOOST_FOREACH (uint256 hash
, vTxHash
) {
711 while (it
< vTxHashIn
.end() && (*it
) < hash
) {
714 if (it
== vTxHashIn
.end()) {
717 else if ((*it
) == hash
) {
719 LogPrint(BCLog::DB
, "Transaction was found for deletion but returned database error: %s\n", hash
.GetHex());
722 vTxHashOut
.push_back(hash
);
732 DBErrors
CWalletDB::ZapWalletTx(std::vector
<CWalletTx
>& vWtx
)
734 // build list of wallet TXs
735 std::vector
<uint256
> vTxHash
;
736 DBErrors err
= FindWalletTx(vTxHash
, vWtx
);
737 if (err
!= DB_LOAD_OK
)
740 // erase each wallet TX
741 BOOST_FOREACH (uint256
& hash
, vTxHash
) {
749 void MaybeCompactWalletDB()
751 static std::atomic
<bool> fOneThread
;
752 if (fOneThread
.exchange(true)) {
755 if (!GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET
)) {
759 for (CWalletRef pwallet
: vpwallets
) {
760 CWalletDBWrapper
& dbh
= pwallet
->GetDBHandle();
762 unsigned int nUpdateCounter
= dbh
.nUpdateCounter
;
764 if (dbh
.nLastSeen
!= nUpdateCounter
) {
765 dbh
.nLastSeen
= nUpdateCounter
;
766 dbh
.nLastWalletUpdate
= GetTime();
769 if (dbh
.nLastFlushed
!= nUpdateCounter
&& GetTime() - dbh
.nLastWalletUpdate
>= 2) {
770 if (CDB::PeriodicFlush(dbh
)) {
771 dbh
.nLastFlushed
= nUpdateCounter
;
780 // Try to (very carefully!) recover wallet file if there is a problem.
782 bool CWalletDB::Recover(const std::string
& filename
, void *callbackDataIn
, bool (*recoverKVcallback
)(void* callbackData
, CDataStream ssKey
, CDataStream ssValue
), std::string
& out_backup_filename
)
784 return CDB::Recover(filename
, callbackDataIn
, recoverKVcallback
, out_backup_filename
);
787 bool CWalletDB::Recover(const std::string
& filename
, std::string
& out_backup_filename
)
789 // recover without a key filter callback
790 // results in recovering all record types
791 return CWalletDB::Recover(filename
, NULL
, NULL
, out_backup_filename
);
794 bool CWalletDB::RecoverKeysOnlyFilter(void *callbackData
, CDataStream ssKey
, CDataStream ssValue
)
796 CWallet
*dummyWallet
= reinterpret_cast<CWallet
*>(callbackData
);
797 CWalletScanState dummyWss
;
798 std::string strType
, strErr
;
801 // Required in LoadKeyMetadata():
802 LOCK(dummyWallet
->cs_wallet
);
803 fReadOK
= ReadKeyValue(dummyWallet
, ssKey
, ssValue
,
804 dummyWss
, strType
, strErr
);
806 if (!IsKeyType(strType
) && strType
!= "hdchain")
810 LogPrintf("WARNING: CWalletDB::Recover skipping %s: %s\n", strType
, strErr
);
817 bool CWalletDB::VerifyEnvironment(const std::string
& walletFile
, const fs::path
& dataDir
, std::string
& errorStr
)
819 return CDB::VerifyEnvironment(walletFile
, dataDir
, errorStr
);
822 bool CWalletDB::VerifyDatabaseFile(const std::string
& walletFile
, const fs::path
& dataDir
, std::string
& warningStr
, std::string
& errorStr
)
824 return CDB::VerifyDatabaseFile(walletFile
, dataDir
, warningStr
, errorStr
, CWalletDB::Recover
);
827 bool CWalletDB::WriteDestData(const std::string
&address
, const std::string
&key
, const std::string
&value
)
829 return WriteIC(std::make_pair(std::string("destdata"), std::make_pair(address
, key
)), value
);
832 bool CWalletDB::EraseDestData(const std::string
&address
, const std::string
&key
)
834 return EraseIC(std::make_pair(std::string("destdata"), std::make_pair(address
, key
)));
838 bool CWalletDB::WriteHDChain(const CHDChain
& chain
)
840 return WriteIC(std::string("hdchain"), chain
);
843 bool CWalletDB::TxnBegin()
845 return batch
.TxnBegin();
848 bool CWalletDB::TxnCommit()
850 return batch
.TxnCommit();
853 bool CWalletDB::TxnAbort()
855 return batch
.TxnAbort();
858 bool CWalletDB::ReadVersion(int& nVersion
)
860 return batch
.ReadVersion(nVersion
);
863 bool CWalletDB::WriteVersion(int nVersion
)
865 return batch
.WriteVersion(nVersion
);