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 const bool fEraseUnencryptedKey
= true;
81 if (!WriteIC(std::make_pair(std::string("keymeta"), vchPubKey
), keyMeta
)) {
85 if (!WriteIC(std::make_pair(std::string("ckey"), vchPubKey
), vchCryptedSecret
, false)) {
88 if (fEraseUnencryptedKey
)
90 EraseIC(std::make_pair(std::string("key"), vchPubKey
));
91 EraseIC(std::make_pair(std::string("wkey"), vchPubKey
));
97 bool CWalletDB::WriteMasterKey(unsigned int nID
, const CMasterKey
& kMasterKey
)
99 return WriteIC(std::make_pair(std::string("mkey"), nID
), kMasterKey
, true);
102 bool CWalletDB::WriteCScript(const uint160
& hash
, const CScript
& redeemScript
)
104 return WriteIC(std::make_pair(std::string("cscript"), hash
), *(const CScriptBase
*)(&redeemScript
), false);
107 bool CWalletDB::WriteWatchOnly(const CScript
&dest
, const CKeyMetadata
& keyMeta
)
109 if (!WriteIC(std::make_pair(std::string("watchmeta"), *(const CScriptBase
*)(&dest
)), keyMeta
)) {
112 return WriteIC(std::make_pair(std::string("watchs"), *(const CScriptBase
*)(&dest
)), '1');
115 bool CWalletDB::EraseWatchOnly(const CScript
&dest
)
117 if (!EraseIC(std::make_pair(std::string("watchmeta"), *(const CScriptBase
*)(&dest
)))) {
120 return EraseIC(std::make_pair(std::string("watchs"), *(const CScriptBase
*)(&dest
)));
123 bool CWalletDB::WriteBestBlock(const CBlockLocator
& locator
)
125 WriteIC(std::string("bestblock"), CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan
126 return WriteIC(std::string("bestblock_nomerkle"), locator
);
129 bool CWalletDB::ReadBestBlock(CBlockLocator
& locator
)
131 if (batch
.Read(std::string("bestblock"), locator
) && !locator
.vHave
.empty()) return true;
132 return batch
.Read(std::string("bestblock_nomerkle"), locator
);
135 bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext
)
137 return WriteIC(std::string("orderposnext"), nOrderPosNext
);
140 bool CWalletDB::WriteDefaultKey(const CPubKey
& vchPubKey
)
142 return WriteIC(std::string("defaultkey"), vchPubKey
);
145 bool CWalletDB::ReadPool(int64_t nPool
, CKeyPool
& keypool
)
147 return batch
.Read(std::make_pair(std::string("pool"), nPool
), keypool
);
150 bool CWalletDB::WritePool(int64_t nPool
, const CKeyPool
& keypool
)
152 return WriteIC(std::make_pair(std::string("pool"), nPool
), keypool
);
155 bool CWalletDB::ErasePool(int64_t nPool
)
157 return EraseIC(std::make_pair(std::string("pool"), nPool
));
160 bool CWalletDB::WriteMinVersion(int nVersion
)
162 return WriteIC(std::string("minversion"), nVersion
);
165 bool CWalletDB::ReadAccount(const std::string
& strAccount
, CAccount
& account
)
168 return batch
.Read(std::make_pair(std::string("acc"), strAccount
), account
);
171 bool CWalletDB::WriteAccount(const std::string
& strAccount
, const CAccount
& account
)
173 return WriteIC(std::make_pair(std::string("acc"), strAccount
), account
);
176 bool CWalletDB::WriteAccountingEntry(const uint64_t nAccEntryNum
, const CAccountingEntry
& acentry
)
178 return WriteIC(std::make_pair(std::string("acentry"), std::make_pair(acentry
.strAccount
, nAccEntryNum
)), acentry
);
181 CAmount
CWalletDB::GetAccountCreditDebit(const std::string
& strAccount
)
183 std::list
<CAccountingEntry
> entries
;
184 ListAccountCreditDebit(strAccount
, entries
);
186 CAmount nCreditDebit
= 0;
187 BOOST_FOREACH (const CAccountingEntry
& entry
, entries
)
188 nCreditDebit
+= entry
.nCreditDebit
;
193 void CWalletDB::ListAccountCreditDebit(const std::string
& strAccount
, std::list
<CAccountingEntry
>& entries
)
195 bool fAllAccounts
= (strAccount
== "*");
197 Dbc
* pcursor
= batch
.GetCursor();
199 throw std::runtime_error(std::string(__func__
) + ": cannot create DB cursor");
200 bool setRange
= true;
204 CDataStream
ssKey(SER_DISK
, CLIENT_VERSION
);
206 ssKey
<< std::make_pair(std::string("acentry"), std::make_pair((fAllAccounts
? std::string("") : strAccount
), uint64_t(0)));
207 CDataStream
ssValue(SER_DISK
, CLIENT_VERSION
);
208 int ret
= batch
.ReadAtCursor(pcursor
, ssKey
, ssValue
, setRange
);
210 if (ret
== DB_NOTFOUND
)
215 throw std::runtime_error(std::string(__func__
) + ": error scanning DB");
221 if (strType
!= "acentry")
223 CAccountingEntry acentry
;
224 ssKey
>> acentry
.strAccount
;
225 if (!fAllAccounts
&& acentry
.strAccount
!= strAccount
)
229 ssKey
>> acentry
.nEntryNo
;
230 entries
.push_back(acentry
);
236 class CWalletScanState
{
240 unsigned int nWatchKeys
;
241 unsigned int nKeyMeta
;
245 std::vector
<uint256
> vWalletUpgrade
;
248 nKeys
= nCKeys
= nWatchKeys
= nKeyMeta
= 0;
249 fIsEncrypted
= false;
250 fAnyUnordered
= false;
256 ReadKeyValue(CWallet
* pwallet
, CDataStream
& ssKey
, CDataStream
& ssValue
,
257 CWalletScanState
&wss
, std::string
& strType
, std::string
& strErr
)
261 // Taking advantage of the fact that pair serialization
262 // is just the two items serialized one after the other
264 if (strType
== "name")
266 std::string strAddress
;
268 ssValue
>> pwallet
->mapAddressBook
[CBitcoinAddress(strAddress
).Get()].name
;
270 else if (strType
== "purpose")
272 std::string strAddress
;
274 ssValue
>> pwallet
->mapAddressBook
[CBitcoinAddress(strAddress
).Get()].purpose
;
276 else if (strType
== "tx")
282 CValidationState state
;
283 if (!(CheckTransaction(wtx
, state
) && (wtx
.GetHash() == hash
) && state
.IsValid()))
286 // Undo serialize changes in 31600
287 if (31404 <= wtx
.fTimeReceivedIsTxTime
&& wtx
.fTimeReceivedIsTxTime
<= 31703)
289 if (!ssValue
.empty())
293 ssValue
>> fTmp
>> fUnused
>> wtx
.strFromAccount
;
294 strErr
= strprintf("LoadWallet() upgrading tx ver=%d %d '%s' %s",
295 wtx
.fTimeReceivedIsTxTime
, fTmp
, wtx
.strFromAccount
, hash
.ToString());
296 wtx
.fTimeReceivedIsTxTime
= fTmp
;
300 strErr
= strprintf("LoadWallet() repairing tx ver=%d %s", wtx
.fTimeReceivedIsTxTime
, hash
.ToString());
301 wtx
.fTimeReceivedIsTxTime
= 0;
303 wss
.vWalletUpgrade
.push_back(hash
);
306 if (wtx
.nOrderPos
== -1)
307 wss
.fAnyUnordered
= true;
309 pwallet
->LoadToWallet(wtx
);
311 else if (strType
== "acentry")
313 std::string strAccount
;
317 if (nNumber
> pwallet
->nAccountingEntryNumber
) {
318 pwallet
->nAccountingEntryNumber
= nNumber
;
321 if (!wss
.fAnyUnordered
)
323 CAccountingEntry acentry
;
325 if (acentry
.nOrderPos
== -1)
326 wss
.fAnyUnordered
= true;
329 else if (strType
== "watchs")
333 ssKey
>> *(CScriptBase
*)(&script
);
337 pwallet
->LoadWatchOnly(script
);
339 else if (strType
== "key" || strType
== "wkey")
343 if (!vchPubKey
.IsValid())
345 strErr
= "Error reading wallet database: CPubKey corrupt";
352 if (strType
== "key")
359 pkey
= wkey
.vchPrivKey
;
362 // Old wallets store keys as "key" [pubkey] => [privkey]
363 // ... which was slow for wallets with lots of keys, because the public key is re-derived from the private key
364 // using EC operations as a checksum.
365 // Newer wallets store keys as "key"[pubkey] => [privkey][hash(pubkey,privkey)], which is much faster while
366 // remaining backwards-compatible.
373 bool fSkipCheck
= false;
377 // hash pubkey/privkey to accelerate wallet load
378 std::vector
<unsigned char> vchKey
;
379 vchKey
.reserve(vchPubKey
.size() + pkey
.size());
380 vchKey
.insert(vchKey
.end(), vchPubKey
.begin(), vchPubKey
.end());
381 vchKey
.insert(vchKey
.end(), pkey
.begin(), pkey
.end());
383 if (Hash(vchKey
.begin(), vchKey
.end()) != hash
)
385 strErr
= "Error reading wallet database: CPubKey/CPrivKey corrupt";
392 if (!key
.Load(pkey
, vchPubKey
, fSkipCheck
))
394 strErr
= "Error reading wallet database: CPrivKey corrupt";
397 if (!pwallet
->LoadKey(key
, vchPubKey
))
399 strErr
= "Error reading wallet database: LoadKey failed";
403 else if (strType
== "mkey")
407 CMasterKey kMasterKey
;
408 ssValue
>> kMasterKey
;
409 if(pwallet
->mapMasterKeys
.count(nID
) != 0)
411 strErr
= strprintf("Error reading wallet database: duplicate CMasterKey id %u", nID
);
414 pwallet
->mapMasterKeys
[nID
] = kMasterKey
;
415 if (pwallet
->nMasterKeyMaxID
< nID
)
416 pwallet
->nMasterKeyMaxID
= nID
;
418 else if (strType
== "ckey")
422 if (!vchPubKey
.IsValid())
424 strErr
= "Error reading wallet database: CPubKey corrupt";
427 std::vector
<unsigned char> vchPrivKey
;
428 ssValue
>> vchPrivKey
;
431 if (!pwallet
->LoadCryptedKey(vchPubKey
, vchPrivKey
))
433 strErr
= "Error reading wallet database: LoadCryptedKey failed";
436 wss
.fIsEncrypted
= true;
438 else if (strType
== "keymeta" || strType
== "watchmeta")
440 CTxDestination keyID
;
441 if (strType
== "keymeta")
445 keyID
= vchPubKey
.GetID();
447 else if (strType
== "watchmeta")
450 ssKey
>> *(CScriptBase
*)(&script
);
451 keyID
= CScriptID(script
);
454 CKeyMetadata keyMeta
;
458 pwallet
->LoadKeyMetadata(keyID
, keyMeta
);
460 else if (strType
== "defaultkey")
462 ssValue
>> pwallet
->vchDefaultKey
;
464 else if (strType
== "pool")
471 pwallet
->LoadKeyPool(nIndex
, keypool
);
473 else if (strType
== "version")
475 ssValue
>> wss
.nFileVersion
;
476 if (wss
.nFileVersion
== 10300)
477 wss
.nFileVersion
= 300;
479 else if (strType
== "cscript")
484 ssValue
>> *(CScriptBase
*)(&script
);
485 if (!pwallet
->LoadCScript(script
))
487 strErr
= "Error reading wallet database: LoadCScript failed";
491 else if (strType
== "orderposnext")
493 ssValue
>> pwallet
->nOrderPosNext
;
495 else if (strType
== "destdata")
497 std::string strAddress
, strKey
, strValue
;
501 if (!pwallet
->LoadDestData(CBitcoinAddress(strAddress
).Get(), strKey
, strValue
))
503 strErr
= "Error reading wallet database: LoadDestData failed";
507 else if (strType
== "hdchain")
511 if (!pwallet
->SetHDChain(chain
, true))
513 strErr
= "Error reading wallet database: SetHDChain failed";
524 bool CWalletDB::IsKeyType(const std::string
& strType
)
526 return (strType
== "key" || strType
== "wkey" ||
527 strType
== "mkey" || strType
== "ckey");
530 DBErrors
CWalletDB::LoadWallet(CWallet
* pwallet
)
532 pwallet
->vchDefaultKey
= CPubKey();
533 CWalletScanState wss
;
534 bool fNoncriticalErrors
= false;
535 DBErrors result
= DB_LOAD_OK
;
537 LOCK(pwallet
->cs_wallet
);
540 if (batch
.Read((std::string
)"minversion", nMinVersion
))
542 if (nMinVersion
> CLIENT_VERSION
)
544 pwallet
->LoadMinVersion(nMinVersion
);
548 Dbc
* pcursor
= batch
.GetCursor();
551 LogPrintf("Error getting wallet database cursor\n");
558 CDataStream
ssKey(SER_DISK
, CLIENT_VERSION
);
559 CDataStream
ssValue(SER_DISK
, CLIENT_VERSION
);
560 int ret
= batch
.ReadAtCursor(pcursor
, ssKey
, ssValue
);
561 if (ret
== DB_NOTFOUND
)
565 LogPrintf("Error reading next record from wallet database\n");
569 // Try to be tolerant of single corrupt records:
570 std::string strType
, strErr
;
571 if (!ReadKeyValue(pwallet
, ssKey
, ssValue
, wss
, strType
, strErr
))
573 // losing keys is considered a catastrophic error, anything else
574 // we assume the user can live with:
575 if (IsKeyType(strType
))
579 // Leave other errors alone, if we try to fix them we might make things worse.
580 fNoncriticalErrors
= true; // ... but do warn the user there is something wrong.
582 // Rescan if there is a bad transaction record:
583 SoftSetBoolArg("-rescan", true);
587 LogPrintf("%s\n", strErr
);
591 catch (const boost::thread_interrupted
&) {
598 if (fNoncriticalErrors
&& result
== DB_LOAD_OK
)
599 result
= DB_NONCRITICAL_ERROR
;
601 // Any wallet corruption at all: skip any rewriting or
602 // upgrading, we don't want to make it worse.
603 if (result
!= DB_LOAD_OK
)
606 LogPrintf("nFileVersion = %d\n", wss
.nFileVersion
);
608 LogPrintf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total\n",
609 wss
.nKeys
, wss
.nCKeys
, wss
.nKeyMeta
, wss
.nKeys
+ wss
.nCKeys
);
611 // nTimeFirstKey is only reliable if all keys have metadata
612 if ((wss
.nKeys
+ wss
.nCKeys
+ wss
.nWatchKeys
) != wss
.nKeyMeta
)
613 pwallet
->UpdateTimeFirstKey(1);
615 BOOST_FOREACH(uint256 hash
, wss
.vWalletUpgrade
)
616 WriteTx(pwallet
->mapWallet
[hash
]);
618 // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
619 if (wss
.fIsEncrypted
&& (wss
.nFileVersion
== 40000 || wss
.nFileVersion
== 50000))
620 return DB_NEED_REWRITE
;
622 if (wss
.nFileVersion
< CLIENT_VERSION
) // Update
623 WriteVersion(CLIENT_VERSION
);
625 if (wss
.fAnyUnordered
)
626 result
= pwallet
->ReorderTransactions();
628 pwallet
->laccentries
.clear();
629 ListAccountCreditDebit("*", pwallet
->laccentries
);
630 BOOST_FOREACH(CAccountingEntry
& entry
, pwallet
->laccentries
) {
631 pwallet
->wtxOrdered
.insert(make_pair(entry
.nOrderPos
, CWallet::TxPair((CWalletTx
*)0, &entry
)));
637 DBErrors
CWalletDB::FindWalletTx(std::vector
<uint256
>& vTxHash
, std::vector
<CWalletTx
>& vWtx
)
639 bool fNoncriticalErrors
= false;
640 DBErrors result
= DB_LOAD_OK
;
644 if (batch
.Read((std::string
)"minversion", nMinVersion
))
646 if (nMinVersion
> CLIENT_VERSION
)
651 Dbc
* pcursor
= batch
.GetCursor();
654 LogPrintf("Error getting wallet database cursor\n");
661 CDataStream
ssKey(SER_DISK
, CLIENT_VERSION
);
662 CDataStream
ssValue(SER_DISK
, CLIENT_VERSION
);
663 int ret
= batch
.ReadAtCursor(pcursor
, ssKey
, ssValue
);
664 if (ret
== DB_NOTFOUND
)
668 LogPrintf("Error reading next record from wallet database\n");
674 if (strType
== "tx") {
681 vTxHash
.push_back(hash
);
687 catch (const boost::thread_interrupted
&) {
694 if (fNoncriticalErrors
&& result
== DB_LOAD_OK
)
695 result
= DB_NONCRITICAL_ERROR
;
700 DBErrors
CWalletDB::ZapSelectTx(std::vector
<uint256
>& vTxHashIn
, std::vector
<uint256
>& vTxHashOut
)
702 // build list of wallet TXs and hashes
703 std::vector
<uint256
> vTxHash
;
704 std::vector
<CWalletTx
> vWtx
;
705 DBErrors err
= FindWalletTx(vTxHash
, vWtx
);
706 if (err
!= DB_LOAD_OK
) {
710 std::sort(vTxHash
.begin(), vTxHash
.end());
711 std::sort(vTxHashIn
.begin(), vTxHashIn
.end());
713 // erase each matching wallet TX
714 bool delerror
= false;
715 std::vector
<uint256
>::iterator it
= vTxHashIn
.begin();
716 BOOST_FOREACH (uint256 hash
, vTxHash
) {
717 while (it
< vTxHashIn
.end() && (*it
) < hash
) {
720 if (it
== vTxHashIn
.end()) {
723 else if ((*it
) == hash
) {
725 LogPrint(BCLog::DB
, "Transaction was found for deletion but returned database error: %s\n", hash
.GetHex());
728 vTxHashOut
.push_back(hash
);
738 DBErrors
CWalletDB::ZapWalletTx(std::vector
<CWalletTx
>& vWtx
)
740 // build list of wallet TXs
741 std::vector
<uint256
> vTxHash
;
742 DBErrors err
= FindWalletTx(vTxHash
, vWtx
);
743 if (err
!= DB_LOAD_OK
)
746 // erase each wallet TX
747 BOOST_FOREACH (uint256
& hash
, vTxHash
) {
755 void MaybeCompactWalletDB()
757 static std::atomic
<bool> fOneThread
;
758 if (fOneThread
.exchange(true)) {
761 if (!GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET
)) {
765 for (CWalletRef pwallet
: vpwallets
) {
766 CWalletDBWrapper
& dbh
= pwallet
->GetDBHandle();
768 unsigned int nUpdateCounter
= dbh
.nUpdateCounter
;
770 if (dbh
.nLastSeen
!= nUpdateCounter
) {
771 dbh
.nLastSeen
= nUpdateCounter
;
772 dbh
.nLastWalletUpdate
= GetTime();
775 if (dbh
.nLastFlushed
!= nUpdateCounter
&& GetTime() - dbh
.nLastWalletUpdate
>= 2) {
776 if (CDB::PeriodicFlush(dbh
)) {
777 dbh
.nLastFlushed
= nUpdateCounter
;
786 // Try to (very carefully!) recover wallet file if there is a problem.
788 bool CWalletDB::Recover(const std::string
& filename
, void *callbackDataIn
, bool (*recoverKVcallback
)(void* callbackData
, CDataStream ssKey
, CDataStream ssValue
), std::string
& out_backup_filename
)
790 return CDB::Recover(filename
, callbackDataIn
, recoverKVcallback
, out_backup_filename
);
793 bool CWalletDB::Recover(const std::string
& filename
, std::string
& out_backup_filename
)
795 // recover without a key filter callback
796 // results in recovering all record types
797 return CWalletDB::Recover(filename
, NULL
, NULL
, out_backup_filename
);
800 bool CWalletDB::RecoverKeysOnlyFilter(void *callbackData
, CDataStream ssKey
, CDataStream ssValue
)
802 CWallet
*dummyWallet
= reinterpret_cast<CWallet
*>(callbackData
);
803 CWalletScanState dummyWss
;
804 std::string strType
, strErr
;
807 // Required in LoadKeyMetadata():
808 LOCK(dummyWallet
->cs_wallet
);
809 fReadOK
= ReadKeyValue(dummyWallet
, ssKey
, ssValue
,
810 dummyWss
, strType
, strErr
);
812 if (!IsKeyType(strType
) && strType
!= "hdchain")
816 LogPrintf("WARNING: CWalletDB::Recover skipping %s: %s\n", strType
, strErr
);
823 bool CWalletDB::VerifyEnvironment(const std::string
& walletFile
, const fs::path
& dataDir
, std::string
& errorStr
)
825 return CDB::VerifyEnvironment(walletFile
, dataDir
, errorStr
);
828 bool CWalletDB::VerifyDatabaseFile(const std::string
& walletFile
, const fs::path
& dataDir
, std::string
& warningStr
, std::string
& errorStr
)
830 return CDB::VerifyDatabaseFile(walletFile
, dataDir
, warningStr
, errorStr
, CWalletDB::Recover
);
833 bool CWalletDB::WriteDestData(const std::string
&address
, const std::string
&key
, const std::string
&value
)
835 return WriteIC(std::make_pair(std::string("destdata"), std::make_pair(address
, key
)), value
);
838 bool CWalletDB::EraseDestData(const std::string
&address
, const std::string
&key
)
840 return EraseIC(std::make_pair(std::string("destdata"), std::make_pair(address
, key
)));
844 bool CWalletDB::WriteHDChain(const CHDChain
& chain
)
846 return WriteIC(std::string("hdchain"), chain
);
849 bool CWalletDB::TxnBegin()
851 return batch
.TxnBegin();
854 bool CWalletDB::TxnCommit()
856 return batch
.TxnCommit();
859 bool CWalletDB::TxnAbort()
861 return batch
.TxnAbort();
864 bool CWalletDB::ReadVersion(int& nVersion
)
866 return batch
.ReadVersion(nVersion
);
869 bool CWalletDB::WriteVersion(int nVersion
)
871 return batch
.WriteVersion(nVersion
);