Merge pull request #3115 from sipa/walletmain

Interaction cleanups between main and wallet
This commit is contained in:
Gavin Andresen 2013-10-29 18:01:57 -07:00
commit e13934c94e
10 changed files with 109 additions and 178 deletions

View File

@ -3,7 +3,10 @@
// Distributed under the MIT/X11 software license, see the accompanying // Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "ui_interface.h"
#include "init.h" #include "init.h"
#include "util.h"
#include "main.h"
#include "bitcoinrpc.h" #include "bitcoinrpc.h"
#include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/predicate.hpp>

View File

@ -4,6 +4,8 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "chainparams.h" #include "chainparams.h"
#include "main.h"
#include "wallet.h"
#include "init.h" #include "init.h"
#include "util.h" #include "util.h"
#include "sync.h" #include "sync.h"

View File

@ -5,7 +5,10 @@
#ifndef BITCOIN_INIT_H #ifndef BITCOIN_INIT_H
#define BITCOIN_INIT_H #define BITCOIN_INIT_H
#include "wallet.h" #include <string>
#include <boost/thread.hpp>
class CWallet;
extern std::string strWalletFile; extern std::string strWalletFile;
extern CWallet* pwalletMain; extern CWallet* pwalletMain;

View File

@ -74,93 +74,52 @@ int64 nTransactionFee = 0;
// These functions dispatch to one or all registered wallets // These functions dispatch to one or all registered wallets
namespace {
void RegisterWallet(CWallet* pwalletIn) struct CMainSignals {
{ // Notifies listeners of updated transaction data (passing hash, transaction, and optionally the block it is found in.
{ boost::signals2::signal<void (const uint256 &, const CTransaction &, const CBlock *)> SyncTransaction;
LOCK(cs_setpwalletRegistered); // Notifies listeners of an erased transaction (currently disabled, requires transaction replacement).
setpwalletRegistered.insert(pwalletIn); boost::signals2::signal<void (const uint256 &)> EraseTransaction;
} // Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible).
boost::signals2::signal<void (const uint256 &)> UpdatedTransaction;
// Notifies listeners of a new active block chain.
boost::signals2::signal<void (const CBlockLocator &)> SetBestChain;
// Notifies listeners about an inventory item being seen on the network.
boost::signals2::signal<void (const uint256 &)> Inventory;
// Tells listeners to broadcast their data.
boost::signals2::signal<void ()> Broadcast;
} g_signals;
} }
void UnregisterWallet(CWallet* pwalletIn) void RegisterWallet(CWalletInterface* pwalletIn) {
{ g_signals.SyncTransaction.connect(boost::bind(&CWalletInterface::SyncTransaction, pwalletIn, _1, _2, _3));
{ g_signals.EraseTransaction.connect(boost::bind(&CWalletInterface::EraseFromWallet, pwalletIn, _1));
LOCK(cs_setpwalletRegistered); g_signals.UpdatedTransaction.connect(boost::bind(&CWalletInterface::UpdatedTransaction, pwalletIn, _1));
setpwalletRegistered.erase(pwalletIn); g_signals.SetBestChain.connect(boost::bind(&CWalletInterface::SetBestChain, pwalletIn, _1));
} g_signals.Inventory.connect(boost::bind(&CWalletInterface::Inventory, pwalletIn, _1));
g_signals.Broadcast.connect(boost::bind(&CWalletInterface::ResendWalletTransactions, pwalletIn));
} }
void UnregisterAllWallets() void UnregisterWallet(CWalletInterface* pwalletIn) {
{ g_signals.Broadcast.disconnect(boost::bind(&CWalletInterface::ResendWalletTransactions, pwalletIn));
LOCK(cs_setpwalletRegistered); g_signals.Inventory.disconnect(boost::bind(&CWalletInterface::Inventory, pwalletIn, _1));
setpwalletRegistered.clear(); g_signals.SetBestChain.disconnect(boost::bind(&CWalletInterface::SetBestChain, pwalletIn, _1));
g_signals.UpdatedTransaction.disconnect(boost::bind(&CWalletInterface::UpdatedTransaction, pwalletIn, _1));
g_signals.EraseTransaction.disconnect(boost::bind(&CWalletInterface::EraseFromWallet, pwalletIn, _1));
g_signals.SyncTransaction.disconnect(boost::bind(&CWalletInterface::SyncTransaction, pwalletIn, _1, _2, _3));
} }
// get the wallet transaction with the given hash (if it exists) void UnregisterAllWallets() {
bool static GetTransaction(const uint256& hashTx, CWalletTx& wtx) g_signals.Broadcast.disconnect_all_slots();
{ g_signals.Inventory.disconnect_all_slots();
LOCK(cs_setpwalletRegistered); g_signals.SetBestChain.disconnect_all_slots();
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) g_signals.UpdatedTransaction.disconnect_all_slots();
if (pwallet->GetTransaction(hashTx,wtx)) g_signals.EraseTransaction.disconnect_all_slots();
return true; g_signals.SyncTransaction.disconnect_all_slots();
return false;
} }
// erases transaction with the given hash from all wallets void SyncWithWallets(const uint256 &hash, const CTransaction &tx, const CBlock *pblock) {
void static EraseFromWallets(uint256 hash) g_signals.SyncTransaction(hash, tx, pblock);
{
LOCK(cs_setpwalletRegistered);
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->EraseFromWallet(hash);
}
// make sure all wallets know about the given transaction, in the given block
void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate)
{
LOCK(cs_setpwalletRegistered);
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->AddToWalletIfInvolvingMe(hash, tx, pblock, fUpdate);
}
// notify wallets about a new best chain
void static SetBestChain(const CBlockLocator& loc)
{
LOCK(cs_setpwalletRegistered);
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->SetBestChain(loc);
}
// notify wallets about an updated transaction
void static UpdatedTransaction(const uint256& hashTx)
{
LOCK(cs_setpwalletRegistered);
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->UpdatedTransaction(hashTx);
}
// dump all wallets
void static PrintWallets(const CBlock& block)
{
LOCK(cs_setpwalletRegistered);
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->PrintWallet(block);
}
// notify wallets about an incoming inventory (for request counts)
void static Inventory(const uint256& hash)
{
LOCK(cs_setpwalletRegistered);
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->Inventory(hash);
}
// ask wallets to resend their transactions
void static ResendWalletTransactions()
{
LOCK(cs_setpwalletRegistered);
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->ResendWalletTransactions();
} }
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
@ -931,8 +890,8 @@ bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fL
///// are we sure this is ok when loading transactions or restoring block txes ///// are we sure this is ok when loading transactions or restoring block txes
// If updated, erase old tx from wallet // If updated, erase old tx from wallet
if (ptxOld) if (ptxOld)
EraseFromWallets(ptxOld->GetHash()); g_signals.EraseTransaction(ptxOld->GetHash());
SyncWithWallets(hash, tx, NULL, true); g_signals.SyncTransaction(hash, tx, NULL);
LogPrint("mempool", "CTxMemPool::accept() : accepted %s (poolsz %"PRIszu")\n", LogPrint("mempool", "CTxMemPool::accept() : accepted %s (poolsz %"PRIszu")\n",
hash.ToString().c_str(), hash.ToString().c_str(),
@ -1095,27 +1054,6 @@ bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree)
} }
bool CWalletTx::AcceptWalletTransaction()
{
{
LOCK(mempool.cs);
// Add previous supporting transactions first
BOOST_FOREACH(CMerkleTx& tx, vtxPrev)
{
if (!tx.IsCoinBase())
{
uint256 hash = tx.GetHash();
if (!mempool.exists(hash) && pcoinsTip->HaveCoins(hash))
tx.AcceptToMemoryPool(false);
}
}
return AcceptToMemoryPool(false);
}
return false;
}
// Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock // Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock
bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow) bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow)
{ {
@ -1992,7 +1930,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
// Watch for transactions paying to me // Watch for transactions paying to me
for (unsigned int i = 0; i < block.vtx.size(); i++) for (unsigned int i = 0; i < block.vtx.size(); i++)
SyncWithWallets(block.GetTxHash(i), block.vtx[i], &block, true); g_signals.SyncTransaction(block.GetTxHash(i), block.vtx[i], &block);
return true; return true;
} }
@ -2126,7 +2064,7 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew)
// Update best block in wallet (so we can detect restored wallets) // Update best block in wallet (so we can detect restored wallets)
if ((pindexNew->nHeight % 20160) == 0 || (!fIsInitialDownload && (pindexNew->nHeight % 144) == 0)) if ((pindexNew->nHeight % 20160) == 0 || (!fIsInitialDownload && (pindexNew->nHeight % 144) == 0))
::SetBestChain(chainActive.GetLocator(pindexNew)); g_signals.SetBestChain(chainActive.GetLocator(pindexNew));
// New best block // New best block
nTimeBestReceived = GetTime(); nTimeBestReceived = GetTime();
@ -2206,7 +2144,7 @@ bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos
CheckForkWarningConditions(); CheckForkWarningConditions();
// Notify UI to display prev block's coinbase if it was ours // Notify UI to display prev block's coinbase if it was ours
static uint256 hashPrevBestCoinBase; static uint256 hashPrevBestCoinBase;
UpdatedTransaction(hashPrevBestCoinBase); g_signals.UpdatedTransaction(hashPrevBestCoinBase);
hashPrevBestCoinBase = block.GetTxHash(0); hashPrevBestCoinBase = block.GetTxHash(0);
} else } else
CheckForkWarningConditionsOnNewFork(pindexNew); CheckForkWarningConditionsOnNewFork(pindexNew);
@ -3041,8 +2979,6 @@ void PrintBlockTree()
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", block.GetBlockTime()).c_str(), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", block.GetBlockTime()).c_str(),
block.vtx.size()); block.vtx.size());
PrintWallets(block);
// put the main time-chain first // put the main time-chain first
vector<CBlockIndex*>& vNext = mapNext[pindex]; vector<CBlockIndex*>& vNext = mapNext[pindex];
for (unsigned int i = 0; i < vNext.size(); i++) for (unsigned int i = 0; i < vNext.size(); i++)
@ -3331,7 +3267,7 @@ void static ProcessGetData(CNode* pfrom)
} }
// Track requests for our stuff. // Track requests for our stuff.
Inventory(inv.hash); g_signals.Inventory(inv.hash);
} }
} }
@ -3593,7 +3529,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
} }
// Track requests for our stuff // Track requests for our stuff
Inventory(inv.hash); g_signals.Inventory(inv.hash);
} }
} }
@ -4215,7 +4151,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
// transactions become unconfirmed and spams other nodes. // transactions become unconfirmed and spams other nodes.
if (!fReindex && !fImporting && !IsInitialBlockDownload()) if (!fReindex && !fImporting && !IsInitialBlockDownload())
{ {
ResendWalletTransactions(); g_signals.Broadcast();
} }
// //
@ -4243,15 +4179,6 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
hashRand = Hash(BEGIN(hashRand), END(hashRand)); hashRand = Hash(BEGIN(hashRand), END(hashRand));
bool fTrickleWait = ((hashRand & 3) != 0); bool fTrickleWait = ((hashRand & 3) != 0);
// always trickle our own transactions
if (!fTrickleWait)
{
CWalletTx wtx;
if (GetTransaction(inv.hash, wtx))
if (wtx.fFromMe)
fTrickleWait = true;
}
if (fTrickleWait) if (fTrickleWait)
{ {
vInvWait.push_back(inv); vInvWait.push_back(inv);

View File

@ -17,7 +17,6 @@
#include <list> #include <list>
class CWallet;
class CBlock; class CBlock;
class CBlockIndex; class CBlockIndex;
class CKeyItem; class CKeyItem;
@ -81,8 +80,6 @@ extern uint64 nLastBlockTx;
extern uint64 nLastBlockSize; extern uint64 nLastBlockSize;
extern const std::string strMessageMagic; extern const std::string strMessageMagic;
extern int64 nTimeBestReceived; extern int64 nTimeBestReceived;
extern CCriticalSection cs_setpwalletRegistered;
extern std::set<CWallet*> setpwalletRegistered;
extern bool fImporting; extern bool fImporting;
extern bool fReindex; extern bool fReindex;
extern bool fBenchmark; extern bool fBenchmark;
@ -108,17 +105,18 @@ class CCoinsView;
class CCoinsViewCache; class CCoinsViewCache;
class CScriptCheck; class CScriptCheck;
class CValidationState; class CValidationState;
class CWalletInterface;
struct CBlockTemplate; struct CBlockTemplate;
/** Register a wallet to receive updates from core */ /** Register a wallet to receive updates from core */
void RegisterWallet(CWallet* pwalletIn); void RegisterWallet(CWalletInterface* pwalletIn);
/** Unregister a wallet from core */ /** Unregister a wallet from core */
void UnregisterWallet(CWallet* pwalletIn); void UnregisterWallet(CWalletInterface* pwalletIn);
/** Unregister all wallets from core */ /** Unregister all wallets from core */
void UnregisterAllWallets(); void UnregisterAllWallets();
/** Push an updated transaction to all registered wallets */ /** Push an updated transaction to all registered wallets */
void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false); void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock = NULL);
/** Register with a network node to receive its signals */ /** Register with a network node to receive its signals */
void RegisterNodeSignals(CNodeSignals& nodeSignals); void RegisterNodeSignals(CNodeSignals& nodeSignals);
@ -190,9 +188,6 @@ bool AbortNode(const std::string &msg);
bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut);
struct CDiskBlockPos struct CDiskBlockPos
{ {
int nFile; int nFile;
@ -1261,4 +1256,18 @@ public:
) )
}; };
class CWalletInterface {
protected:
virtual void SyncTransaction(const uint256 &hash, const CTransaction &tx, const CBlock *pblock) =0;
virtual void EraseFromWallet(const uint256 &hash) =0;
virtual void SetBestChain(const CBlockLocator &locator) =0;
virtual void UpdatedTransaction(const uint256 &hash) =0;
virtual void Inventory(const uint256 &hash) =0;
virtual void ResendWalletTransactions() =0;
friend void ::RegisterWallet(CWalletInterface*);
friend void ::UnregisterWallet(CWalletInterface*);
friend void ::UnregisterAllWallets();
};
#endif #endif

View File

@ -6,6 +6,9 @@
#include "bitcoinunits.h" #include "bitcoinunits.h"
#include "init.h" #include "init.h"
#include "core.h"
#include "wallet.h"
#include "netbase.h"
#include "walletdb.h" #include "walletdb.h"
#include "guiutil.h" #include "guiutil.h"

View File

@ -6,6 +6,7 @@
#include <fstream> #include <fstream>
#include "init.h" // for pwalletMain #include "init.h" // for pwalletMain
#include "wallet.h"
#include "bitcoinrpc.h" #include "bitcoinrpc.h"
#include "ui_interface.h" #include "ui_interface.h"
#include "base58.h" #include "base58.h"

View File

@ -560,7 +560,7 @@ Value sendrawtransaction(const Array& params, bool fHelp)
// Not in block, but already in the memory pool; will drop // Not in block, but already in the memory pool; will drop
// through to re-relay it. // through to re-relay it.
} else { } else {
SyncWithWallets(hashTx, tx, NULL, true); SyncWithWallets(hashTx, tx, NULL);
} }
RelayTransaction(tx, hashTx); RelayTransaction(tx, hashTx);

View File

@ -505,7 +505,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn)
// Add a transaction to the wallet, or update it. // Add a transaction to the wallet, or update it.
// pblock is optional, but should be provided if the transaction is known to be in a block. // pblock is optional, but should be provided if the transaction is known to be in a block.
// If fUpdate is true, existing transactions will be updated. // If fUpdate is true, existing transactions will be updated.
bool CWallet::AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fFindBlock) bool CWallet::AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate)
{ {
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
@ -525,16 +525,20 @@ bool CWallet::AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction&
return false; return false;
} }
bool CWallet::EraseFromWallet(uint256 hash) void CWallet::SyncTransaction(const uint256 &hash, const CTransaction& tx, const CBlock* pblock) {
AddToWalletIfInvolvingMe(hash, tx, pblock, true);
}
void CWallet::EraseFromWallet(const uint256 &hash)
{ {
if (!fFileBacked) if (!fFileBacked)
return false; return;
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
if (mapWallet.erase(hash)) if (mapWallet.erase(hash))
CWalletDB(strWalletFile).EraseTx(hash); CWalletDB(strWalletFile).EraseTx(hash);
} }
return true; return;
} }
@ -773,6 +777,25 @@ void CWalletTx::AddSupportingTransactions()
reverse(vtxPrev.begin(), vtxPrev.end()); reverse(vtxPrev.begin(), vtxPrev.end());
} }
bool CWalletTx::AcceptWalletTransaction()
{
{
LOCK(mempool.cs);
// Add previous supporting transactions first
BOOST_FOREACH(CMerkleTx& tx, vtxPrev)
{
if (!tx.IsCoinBase())
{
uint256 hash = tx.GetHash();
if (!mempool.exists(hash) && pcoinsTip->HaveCoins(hash))
tx.AcceptToMemoryPool(false);
}
}
return AcceptToMemoryPool(false);
}
return false;
}
bool CWalletTx::WriteToDisk() bool CWalletTx::WriteToDisk()
{ {
return CWalletDB(pwallet->strWalletFile).WriteTx(GetHash(), *this); return CWalletDB(pwallet->strWalletFile).WriteTx(GetHash(), *this);
@ -1485,33 +1508,6 @@ bool CWallet::DelAddressBook(const CTxDestination& address)
return CWalletDB(strWalletFile).EraseName(CBitcoinAddress(address).ToString()); return CWalletDB(strWalletFile).EraseName(CBitcoinAddress(address).ToString());
} }
void CWallet::PrintWallet(const CBlock& block)
{
{
LOCK(cs_wallet);
if (mapWallet.count(block.vtx[0].GetHash()))
{
CWalletTx& wtx = mapWallet[block.vtx[0].GetHash()];
LogPrintf(" mine: %d %d %"PRI64d"", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit());
}
}
LogPrintf("\n");
}
bool CWallet::GetTransaction(const uint256 &hashTx, CWalletTx& wtx)
{
{
LOCK(cs_wallet);
map<uint256, CWalletTx>::iterator mi = mapWallet.find(hashTx);
if (mi != mapWallet.end())
{
wtx = (*mi).second;
return true;
}
}
return false;
}
bool CWallet::SetDefaultKey(const CPubKey &vchPubKey) bool CWallet::SetDefaultKey(const CPubKey &vchPubKey)
{ {
if (fFileBacked) if (fFileBacked)
@ -1523,14 +1519,6 @@ bool CWallet::SetDefaultKey(const CPubKey &vchPubKey)
return true; return true;
} }
bool GetWalletFile(CWallet* pwallet, string &strWalletFileOut)
{
if (!pwallet->fFileBacked)
return false;
strWalletFileOut = pwallet->strWalletFile;
return true;
}
// //
// Mark old keypool keys as used, // Mark old keypool keys as used,
// and generate all new keys // and generate all new keys

View File

@ -80,7 +80,7 @@ public:
/** A CWallet is an extension of a keystore, which also maintains a set of transactions and balances, /** A CWallet is an extension of a keystore, which also maintains a set of transactions and balances,
* and provides the ability to create new transactions. * and provides the ability to create new transactions.
*/ */
class CWallet : public CCryptoKeyStore class CWallet : public CCryptoKeyStore, public CWalletInterface
{ {
private: private:
bool SelectCoins(int64 nTargetValue, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const; bool SelectCoins(int64 nTargetValue, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const;
@ -197,8 +197,9 @@ public:
void MarkDirty(); void MarkDirty();
bool AddToWallet(const CWalletTx& wtxIn); bool AddToWallet(const CWalletTx& wtxIn);
bool AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate = false, bool fFindBlock = false); void SyncTransaction(const uint256 &hash, const CTransaction& tx, const CBlock* pblock);
bool EraseFromWallet(uint256 hash); bool AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate);
void EraseFromWallet(const uint256 &hash);
void WalletUpdateSpent(const CTransaction& prevout); void WalletUpdateSpent(const CTransaction& prevout);
int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false);
void ReacceptWalletTransactions(); void ReacceptWalletTransactions();
@ -302,8 +303,6 @@ public:
void UpdatedTransaction(const uint256 &hashTx); void UpdatedTransaction(const uint256 &hashTx);
void PrintWallet(const CBlock& block);
void Inventory(const uint256 &hash) void Inventory(const uint256 &hash)
{ {
{ {
@ -319,8 +318,6 @@ public:
return setKeyPool.size(); return setKeyPool.size();
} }
bool GetTransaction(const uint256 &hashTx, CWalletTx& wtx);
bool SetDefaultKey(const CPubKey &vchPubKey); bool SetDefaultKey(const CPubKey &vchPubKey);
// signify that a particular wallet feature is now used. this may change nWalletVersion and nWalletMaxVersion if those are lower // signify that a particular wallet feature is now used. this may change nWalletVersion and nWalletMaxVersion if those are lower
@ -891,6 +888,4 @@ private:
std::vector<char> _ssExtra; std::vector<char> _ssExtra;
}; };
bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut);
#endif #endif