Merge #14437: Refactor: Start to separate wallet from node

081accb875 Pass chain locked variables where needed (Russell Yanofsky)
79d579f4e1 Remove uses of cs_main in wallet code (Russell Yanofsky)
ea961c3d72 Remove direct node->wallet calls in init.cpp (Russell Yanofsky)
8db11dd0b1 Pass chain and client variables where needed (Russell Yanofsky)
7e2e62cf7c Add skeleton chain and client classes (Russell Yanofsky)

Pull request description:

  This creates an incomplete [`Chain`](https://github.com/ryanofsky/bitcoin/blob/pr/wipc-sep/src/interfaces/chain.h) interface in [`src/interfaces/`](https://github.com/ryanofsky/bitcoin/tree/pr/wipc-sep/src/interfaces) and begins to update wallet code to use it.

  #10973 builds on this, changing the wallet to use the new interface to access chain state, instead of using CBlockIndex pointers and global variables like `chainActive`.

Tree-SHA512: 6ef05a4d8ebf57f2ad71835e4d970c9c59e34057e39e48cee76b887492c2fee907e3f6a74a9861e5a9f97cdc6823f4865ebc41ec556ab371ebca1b664c20dbea
This commit is contained in:
MarcoFalke 2018-11-09 09:36:34 -05:00
commit cbf00939b5
No known key found for this signature in database
GPG Key ID: D2EA4850E7528B25
34 changed files with 722 additions and 372 deletions

View File

@ -16,10 +16,6 @@ libs = [
] ]
ignore_list = [ ignore_list = [
'rpc/net.cpp',
'interfaces/handler.cpp',
'interfaces/node.cpp',
'interfaces/wallet.cpp',
] ]
lib_sources = {} lib_sources = {}
@ -32,7 +28,9 @@ def parse_makefile(makefile):
if current_lib: if current_lib:
source = line.split()[0] source = line.split()[0]
if source.endswith('.cpp') and not source.startswith('$') and source not in ignore_list: if source.endswith('.cpp') and not source.startswith('$') and source not in ignore_list:
lib_sources[current_lib].append(source.replace('/', '\\')) source_filename = source.replace('/', '\\')
object_filename = source.replace('/', '_')[:-4] + ".obj"
lib_sources[current_lib].append((source_filename, object_filename))
if not line.endswith('\\'): if not line.endswith('\\'):
current_lib = '' current_lib = ''
continue continue
@ -51,8 +49,10 @@ def main():
for key, value in lib_sources.items(): for key, value in lib_sources.items():
vcxproj_filename = os.path.abspath(os.path.join(os.path.dirname(__file__), key, key + '.vcxproj')) vcxproj_filename = os.path.abspath(os.path.join(os.path.dirname(__file__), key, key + '.vcxproj'))
content = '' content = ''
for source_filename in value: for source_filename, object_filename in value:
content += ' <ClCompile Include="..\\..\\src\\' + source_filename + '" />\n' content += ' <ClCompile Include="..\\..\\src\\' + source_filename + '">\n'
content += ' <ObjectFileName>$(IntDir)' + object_filename + '</ObjectFileName>\n'
content += ' </ClCompile>\n'
with open(vcxproj_filename + '.in', 'r', encoding='utf-8') as vcxproj_in_file: with open(vcxproj_filename + '.in', 'r', encoding='utf-8') as vcxproj_in_file:
with open(vcxproj_filename, 'w', encoding='utf-8') as vcxproj_file: with open(vcxproj_filename, 'w', encoding='utf-8') as vcxproj_file:
vcxproj_file.write(vcxproj_in_file.read().replace( vcxproj_file.write(vcxproj_in_file.read().replace(

View File

@ -125,6 +125,7 @@ BITCOIN_CORE_H = \
index/txindex.h \ index/txindex.h \
indirectmap.h \ indirectmap.h \
init.h \ init.h \
interfaces/chain.h \
interfaces/handler.h \ interfaces/handler.h \
interfaces/node.h \ interfaces/node.h \
interfaces/wallet.h \ interfaces/wallet.h \
@ -233,6 +234,7 @@ libbitcoin_server_a_SOURCES = \
httpserver.cpp \ httpserver.cpp \
index/base.cpp \ index/base.cpp \
index/txindex.cpp \ index/txindex.cpp \
interfaces/chain.cpp \
interfaces/handler.cpp \ interfaces/handler.cpp \
interfaces/node.cpp \ interfaces/node.cpp \
init.cpp \ init.cpp \
@ -465,6 +467,7 @@ endif
bitcoind_LDADD = \ bitcoind_LDADD = \
$(LIBBITCOIN_SERVER) \ $(LIBBITCOIN_SERVER) \
$(LIBBITCOIN_WALLET) \ $(LIBBITCOIN_WALLET) \
$(LIBBITCOIN_SERVER) \
$(LIBBITCOIN_COMMON) \ $(LIBBITCOIN_COMMON) \
$(LIBUNIVALUE) \ $(LIBUNIVALUE) \
$(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_UTIL) \

View File

@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <bench/bench.h> #include <bench/bench.h>
#include <interfaces/chain.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
#include <wallet/coinselection.h> #include <wallet/coinselection.h>
@ -33,7 +34,8 @@ static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<Ou
// (https://github.com/bitcoin/bitcoin/issues/7883#issuecomment-224807484) // (https://github.com/bitcoin/bitcoin/issues/7883#issuecomment-224807484)
static void CoinSelection(benchmark::State& state) static void CoinSelection(benchmark::State& state)
{ {
const CWallet wallet(WalletLocation(), WalletDatabase::CreateDummy()); auto chain = interfaces::MakeChain();
const CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy());
LOCK(wallet.cs_wallet); LOCK(wallet.cs_wallet);
// Add coins. // Add coins.
@ -57,7 +59,8 @@ static void CoinSelection(benchmark::State& state)
} }
typedef std::set<CInputCoin> CoinSet; typedef std::set<CInputCoin> CoinSet;
static const CWallet testWallet(WalletLocation(), WalletDatabase::CreateDummy()); static auto testChain = interfaces::MakeChain();
static const CWallet testWallet(*testChain, WalletLocation(), WalletDatabase::CreateDummy());
std::vector<std::unique_ptr<CWalletTx>> wtxn; std::vector<std::unique_ptr<CWalletTx>> wtxn;
// Copied from src/wallet/test/coinselector_tests.cpp // Copied from src/wallet/test/coinselector_tests.cpp

View File

@ -11,6 +11,7 @@
#include <clientversion.h> #include <clientversion.h>
#include <compat.h> #include <compat.h>
#include <fs.h> #include <fs.h>
#include <interfaces/chain.h>
#include <rpc/server.h> #include <rpc/server.h>
#include <init.h> #include <init.h>
#include <noui.h> #include <noui.h>
@ -58,6 +59,9 @@ static void WaitForShutdown()
// //
static bool AppInit(int argc, char* argv[]) static bool AppInit(int argc, char* argv[])
{ {
InitInterfaces interfaces;
interfaces.chain = interfaces::MakeChain();
bool fRet = false; bool fRet = false;
// //
@ -164,7 +168,7 @@ static bool AppInit(int argc, char* argv[])
// If locking the data directory failed, exit immediately // If locking the data directory failed, exit immediately
return false; return false;
} }
fRet = AppInitMain(); fRet = AppInitMain(interfaces);
} }
catch (const std::exception& e) { catch (const std::exception& e) {
PrintExceptionContinue(&e, "AppInit()"); PrintExceptionContinue(&e, "AppInit()");
@ -178,7 +182,7 @@ static bool AppInit(int argc, char* argv[])
} else { } else {
WaitForShutdown(); WaitForShutdown();
} }
Shutdown(); Shutdown(interfaces);
return fRet; return fRet;
} }

View File

@ -14,13 +14,7 @@ public:
bool HasWalletSupport() const override {return false;} bool HasWalletSupport() const override {return false;}
void AddWalletOptions() const override; void AddWalletOptions() const override;
bool ParameterInteraction() const override {return true;} bool ParameterInteraction() const override {return true;}
void RegisterRPC(CRPCTable &) const override {} void Construct(InitInterfaces& interfaces) const override {LogPrintf("No wallet support compiled in!\n");}
bool Verify() const override {return true;}
bool Open() const override {LogPrintf("No wallet support compiled in!\n"); return true;}
void Start(CScheduler& scheduler) const override {}
void Flush() const override {}
void Stop() const override {}
void Close() const override {}
}; };
void DummyWalletInit::AddWalletOptions() const void DummyWalletInit::AddWalletOptions() const

View File

@ -19,6 +19,7 @@
#include <fs.h> #include <fs.h>
#include <httpserver.h> #include <httpserver.h>
#include <httprpc.h> #include <httprpc.h>
#include <interfaces/chain.h>
#include <index/txindex.h> #include <index/txindex.h>
#include <key.h> #include <key.h>
#include <validation.h> #include <validation.h>
@ -32,6 +33,7 @@
#include <rpc/server.h> #include <rpc/server.h>
#include <rpc/register.h> #include <rpc/register.h>
#include <rpc/blockchain.h> #include <rpc/blockchain.h>
#include <rpc/util.h>
#include <script/standard.h> #include <script/standard.h>
#include <script/sigcache.h> #include <script/sigcache.h>
#include <scheduler.h> #include <scheduler.h>
@ -157,7 +159,7 @@ void Interrupt()
} }
} }
void Shutdown() void Shutdown(InitInterfaces& interfaces)
{ {
LogPrintf("%s: In progress...\n", __func__); LogPrintf("%s: In progress...\n", __func__);
static CCriticalSection cs_Shutdown; static CCriticalSection cs_Shutdown;
@ -176,7 +178,9 @@ void Shutdown()
StopREST(); StopREST();
StopRPC(); StopRPC();
StopHTTPServer(); StopHTTPServer();
g_wallet_init_interface.Flush(); for (const auto& client : interfaces.chain_clients) {
client->flush();
}
StopMapPort(); StopMapPort();
// Because these depend on each-other, we make sure that neither can be // Because these depend on each-other, we make sure that neither can be
@ -239,7 +243,9 @@ void Shutdown()
pcoinsdbview.reset(); pcoinsdbview.reset();
pblocktree.reset(); pblocktree.reset();
} }
g_wallet_init_interface.Stop(); for (const auto& client : interfaces.chain_clients) {
client->stop();
}
#if ENABLE_ZMQ #if ENABLE_ZMQ
if (g_zmq_notification_interface) { if (g_zmq_notification_interface) {
@ -259,7 +265,7 @@ void Shutdown()
UnregisterAllValidationInterfaces(); UnregisterAllValidationInterfaces();
GetMainSignals().UnregisterBackgroundSignalScheduler(); GetMainSignals().UnregisterBackgroundSignalScheduler();
GetMainSignals().UnregisterWithMempoolSignals(mempool); GetMainSignals().UnregisterWithMempoolSignals(mempool);
g_wallet_init_interface.Close(); interfaces.chain_clients.clear();
globalVerifyHandle.reset(); globalVerifyHandle.reset();
ECC_Stop(); ECC_Stop();
LogPrintf("%s: done\n", __func__); LogPrintf("%s: done\n", __func__);
@ -1158,7 +1164,7 @@ bool AppInitLockDataDirectory()
return true; return true;
} }
bool AppInitMain() bool AppInitMain(InitInterfaces& interfaces)
{ {
const CChainParams& chainparams = Params(); const CChainParams& chainparams = Params();
// ********************************************************* Step 4a: application initialization // ********************************************************* Step 4a: application initialization
@ -1221,11 +1227,20 @@ bool AppInitMain()
GetMainSignals().RegisterBackgroundSignalScheduler(scheduler); GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
GetMainSignals().RegisterWithMempoolSignals(mempool); GetMainSignals().RegisterWithMempoolSignals(mempool);
// Create client interfaces for wallets that are supposed to be loaded
// according to -wallet and -disablewallet options. This only constructs
// the interfaces, it doesn't load wallet data. Wallets actually get loaded
// when load() and start() interface methods are called below.
g_wallet_init_interface.Construct(interfaces);
/* Register RPC commands regardless of -server setting so they will be /* Register RPC commands regardless of -server setting so they will be
* available in the GUI RPC console even if external calls are disabled. * available in the GUI RPC console even if external calls are disabled.
*/ */
RegisterAllCoreRPCCommands(tableRPC); RegisterAllCoreRPCCommands(tableRPC);
g_wallet_init_interface.RegisterRPC(tableRPC); for (const auto& client : interfaces.chain_clients) {
client->registerRpcs();
}
g_rpc_interfaces = &interfaces;
#if ENABLE_ZMQ #if ENABLE_ZMQ
RegisterZMQRPCCommands(tableRPC); RegisterZMQRPCCommands(tableRPC);
#endif #endif
@ -1243,7 +1258,11 @@ bool AppInitMain()
} }
// ********************************************************* Step 5: verify wallet database integrity // ********************************************************* Step 5: verify wallet database integrity
if (!g_wallet_init_interface.Verify()) return false; for (const auto& client : interfaces.chain_clients) {
if (!client->verify()) {
return false;
}
}
// ********************************************************* Step 6: network initialization // ********************************************************* Step 6: network initialization
// Note that we absolutely cannot open any actual connections // Note that we absolutely cannot open any actual connections
@ -1562,7 +1581,11 @@ bool AppInitMain()
} }
// ********************************************************* Step 9: load wallet // ********************************************************* Step 9: load wallet
if (!g_wallet_init_interface.Open()) return false; for (const auto& client : interfaces.chain_clients) {
if (!client->load()) {
return false;
}
}
// ********************************************************* Step 10: data directory maintenance // ********************************************************* Step 10: data directory maintenance
@ -1708,7 +1731,9 @@ bool AppInitMain()
SetRPCWarmupFinished(); SetRPCWarmupFinished();
uiInterface.InitMessage(_("Done loading")); uiInterface.InitMessage(_("Done loading"));
g_wallet_init_interface.Start(scheduler); for (const auto& client : interfaces.chain_clients) {
client->start(scheduler);
}
return true; return true;
} }

View File

@ -10,8 +10,17 @@
#include <string> #include <string>
#include <util/system.h> #include <util/system.h>
class CScheduler; namespace interfaces {
class CWallet; class Chain;
class ChainClient;
} // namespace interfaces
//! Pointers to interfaces used during init and destroyed on shutdown.
struct InitInterfaces
{
std::unique_ptr<interfaces::Chain> chain;
std::vector<std::unique_ptr<interfaces::ChainClient>> chain_clients;
};
namespace boost namespace boost
{ {
@ -20,7 +29,7 @@ class thread_group;
/** Interrupt threads */ /** Interrupt threads */
void Interrupt(); void Interrupt();
void Shutdown(); void Shutdown(InitInterfaces& interfaces);
//!Initialize the logging infrastructure //!Initialize the logging infrastructure
void InitLogging(); void InitLogging();
//!Parameter interaction: change current parameters depending on various rules //!Parameter interaction: change current parameters depending on various rules
@ -54,7 +63,7 @@ bool AppInitLockDataDirectory();
* @note This should only be done after daemonization. Call Shutdown() if this function fails. * @note This should only be done after daemonization. Call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read, AppInitLockDataDirectory should have been called. * @pre Parameters should be parsed and config file should be read, AppInitLockDataDirectory should have been called.
*/ */
bool AppInitMain(); bool AppInitMain(InitInterfaces& interfaces);
/** /**
* Setup the arguments for gArgs * Setup the arguments for gArgs

View File

@ -4,7 +4,7 @@ The following interfaces are defined here:
* [`Chain`](chain.h) — used by wallet to access blockchain and mempool state. Added in [#10973](https://github.com/bitcoin/bitcoin/pull/10973). * [`Chain`](chain.h) — used by wallet to access blockchain and mempool state. Added in [#10973](https://github.com/bitcoin/bitcoin/pull/10973).
* [`Chain::Client`](chain.h) — used by node to start & stop `Chain` clients. Added in [#10973](https://github.com/bitcoin/bitcoin/pull/10973). * [`ChainClient`](chain.h) — used by node to start & stop `Chain` clients. Added in [#10973](https://github.com/bitcoin/bitcoin/pull/10973).
* [`Node`](node.h) — used by GUI to start & stop bitcoin node. Added in [#10244](https://github.com/bitcoin/bitcoin/pull/10244). * [`Node`](node.h) — used by GUI to start & stop bitcoin node. Added in [#10244](https://github.com/bitcoin/bitcoin/pull/10244).

44
src/interfaces/chain.cpp Normal file
View File

@ -0,0 +1,44 @@
// Copyright (c) 2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <interfaces/chain.h>
#include <sync.h>
#include <util/system.h>
#include <validation.h>
#include <memory>
#include <utility>
namespace interfaces {
namespace {
class LockImpl : public Chain::Lock
{
};
class LockingStateImpl : public LockImpl, public UniqueLock<CCriticalSection>
{
using UniqueLock::UniqueLock;
};
class ChainImpl : public Chain
{
public:
std::unique_ptr<Chain::Lock> lock(bool try_lock) override
{
auto result = MakeUnique<LockingStateImpl>(::cs_main, "cs_main", __FILE__, __LINE__, try_lock);
if (try_lock && result && !*result) return {};
// std::move necessary on some compilers due to conversion from
// LockingStateImpl to Lock pointer
return std::move(result);
}
std::unique_ptr<Chain::Lock> assumeLocked() override { return MakeUnique<LockImpl>(); }
};
} // namespace
std::unique_ptr<Chain> MakeChain() { return MakeUnique<ChainImpl>(); }
} // namespace interfaces

84
src/interfaces/chain.h Normal file
View File

@ -0,0 +1,84 @@
// Copyright (c) 2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_INTERFACES_CHAIN_H
#define BITCOIN_INTERFACES_CHAIN_H
#include <memory>
#include <string>
#include <vector>
class CScheduler;
namespace interfaces {
//! Interface for giving wallet processes access to blockchain state.
class Chain
{
public:
virtual ~Chain() {}
//! Interface for querying locked chain state, used by legacy code that
//! assumes state won't change between calls. New code should avoid using
//! the Lock interface and instead call higher-level Chain methods
//! that return more information so the chain doesn't need to stay locked
//! between calls.
class Lock
{
public:
virtual ~Lock() {}
};
//! Return Lock interface. Chain is locked when this is called, and
//! unlocked when the returned interface is freed.
virtual std::unique_ptr<Lock> lock(bool try_lock = false) = 0;
//! Return Lock interface assuming chain is already locked. This
//! method is temporary and is only used in a few places to avoid changing
//! behavior while code is transitioned to use the Chain::Lock interface.
virtual std::unique_ptr<Lock> assumeLocked() = 0;
};
//! Interface to let node manage chain clients (wallets, or maybe tools for
//! monitoring and analysis in the future).
class ChainClient
{
public:
virtual ~ChainClient() {}
//! Register rpcs.
virtual void registerRpcs() = 0;
//! Check for errors before loading.
virtual bool verify() = 0;
//! Load saved state.
virtual bool load() = 0;
//! Start client execution and provide a scheduler.
virtual void start(CScheduler& scheduler) = 0;
//! Save state to disk.
virtual void flush() = 0;
//! Shut down client.
virtual void stop() = 0;
};
//! Return implementation of Chain interface.
std::unique_ptr<Chain> MakeChain();
//! Return implementation of ChainClient interface for a wallet client. This
//! function will be undefined in builds where ENABLE_WALLET is false.
//!
//! Currently, wallets are the only chain clients. But in the future, other
//! types of chain clients could be added, such as tools for monitoring,
//! analysis, or fee estimation. These clients need to expose their own
//! MakeXXXClient functions returning their implementations of the ChainClient
//! interface.
std::unique_ptr<ChainClient> MakeWalletClient(Chain& chain, std::vector<std::string> wallet_filenames);
} // namespace interfaces
#endif // BITCOIN_INTERFACES_CHAIN_H

View File

@ -9,6 +9,7 @@
#include <chain.h> #include <chain.h>
#include <chainparams.h> #include <chainparams.h>
#include <init.h> #include <init.h>
#include <interfaces/chain.h>
#include <interfaces/handler.h> #include <interfaces/handler.h>
#include <interfaces/wallet.h> #include <interfaces/wallet.h>
#include <net.h> #include <net.h>
@ -50,6 +51,8 @@ namespace {
class NodeImpl : public Node class NodeImpl : public Node
{ {
public:
NodeImpl() { m_interfaces.chain = MakeChain(); }
bool parseParameters(int argc, const char* const argv[], std::string& error) override bool parseParameters(int argc, const char* const argv[], std::string& error) override
{ {
return gArgs.ParseParameters(argc, argv, error); return gArgs.ParseParameters(argc, argv, error);
@ -68,11 +71,11 @@ class NodeImpl : public Node
return AppInitBasicSetup() && AppInitParameterInteraction() && AppInitSanityChecks() && return AppInitBasicSetup() && AppInitParameterInteraction() && AppInitSanityChecks() &&
AppInitLockDataDirectory(); AppInitLockDataDirectory();
} }
bool appInitMain() override { return AppInitMain(); } bool appInitMain() override { return AppInitMain(m_interfaces); }
void appShutdown() override void appShutdown() override
{ {
Interrupt(); Interrupt();
Shutdown(); Shutdown(m_interfaces);
} }
void startShutdown() override { StartShutdown(); } void startShutdown() override { StartShutdown(); }
bool shutdownRequested() override { return ShutdownRequested(); } bool shutdownRequested() override { return ShutdownRequested(); }
@ -291,6 +294,7 @@ class NodeImpl : public Node
GuessVerificationProgress(Params().TxData(), block)); GuessVerificationProgress(Params().TxData(), block));
})); }));
} }
InitInterfaces m_interfaces;
}; };
} // namespace } // namespace

View File

@ -7,12 +7,16 @@
#include <amount.h> #include <amount.h>
#include <chain.h> #include <chain.h>
#include <consensus/validation.h> #include <consensus/validation.h>
#include <init.h>
#include <interfaces/chain.h>
#include <interfaces/handler.h> #include <interfaces/handler.h>
#include <net.h> #include <net.h>
#include <policy/feerate.h> #include <policy/feerate.h>
#include <policy/fees.h> #include <policy/fees.h>
#include <policy/policy.h> #include <policy/policy.h>
#include <primitives/transaction.h> #include <primitives/transaction.h>
#include <rpc/server.h>
#include <scheduler.h>
#include <script/ismine.h> #include <script/ismine.h>
#include <script/standard.h> #include <script/standard.h>
#include <support/allocators/secure.h> #include <support/allocators/secure.h>
@ -20,10 +24,18 @@
#include <timedata.h> #include <timedata.h>
#include <ui_interface.h> #include <ui_interface.h>
#include <uint256.h> #include <uint256.h>
#include <util/system.h>
#include <validation.h> #include <validation.h>
#include <wallet/feebumper.h> #include <wallet/feebumper.h>
#include <wallet/fees.h> #include <wallet/fees.h>
#include <wallet/rpcwallet.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
#include <wallet/walletutil.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
namespace interfaces { namespace interfaces {
namespace { namespace {
@ -41,7 +53,8 @@ public:
WalletOrderForm order_form, WalletOrderForm order_form,
std::string& reject_reason) override std::string& reject_reason) override
{ {
LOCK2(cs_main, m_wallet.cs_wallet); auto locked_chain = m_wallet.chain().lock();
LOCK(m_wallet.cs_wallet);
CValidationState state; CValidationState state;
if (!m_wallet.CommitTransaction(m_tx, std::move(value_map), std::move(order_form), m_key, g_connman.get(), state)) { if (!m_wallet.CommitTransaction(m_tx, std::move(value_map), std::move(order_form), m_key, g_connman.get(), state)) {
reject_reason = state.GetRejectReason(); reject_reason = state.GetRejectReason();
@ -56,7 +69,7 @@ public:
}; };
//! Construct wallet tx struct. //! Construct wallet tx struct.
static WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_main) WalletTx MakeWalletTx(interfaces::Chain::Lock& locked_chain, CWallet& wallet, const CWalletTx& wtx)
{ {
WalletTx result; WalletTx result;
result.tx = wtx.tx; result.tx = wtx.tx;
@ -74,7 +87,7 @@ static WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx) EXCLUSIVE_LO
IsMine(wallet, result.txout_address.back()) : IsMine(wallet, result.txout_address.back()) :
ISMINE_NO); ISMINE_NO);
} }
result.credit = wtx.GetCredit(ISMINE_ALL); result.credit = wtx.GetCredit(locked_chain, ISMINE_ALL);
result.debit = wtx.GetDebit(ISMINE_ALL); result.debit = wtx.GetDebit(ISMINE_ALL);
result.change = wtx.GetChange(); result.change = wtx.GetChange();
result.time = wtx.GetTxTime(); result.time = wtx.GetTxTime();
@ -84,32 +97,38 @@ static WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx) EXCLUSIVE_LO
} }
//! Construct wallet tx status struct. //! Construct wallet tx status struct.
static WalletTxStatus MakeWalletTxStatus(const CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_main) WalletTxStatus MakeWalletTxStatus(interfaces::Chain::Lock& locked_chain, const CWalletTx& wtx)
{ {
LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
WalletTxStatus result; WalletTxStatus result;
auto mi = ::mapBlockIndex.find(wtx.hashBlock); auto mi = ::mapBlockIndex.find(wtx.hashBlock);
CBlockIndex* block = mi != ::mapBlockIndex.end() ? mi->second : nullptr; CBlockIndex* block = mi != ::mapBlockIndex.end() ? mi->second : nullptr;
result.block_height = (block ? block->nHeight : std::numeric_limits<int>::max()); result.block_height = (block ? block->nHeight : std::numeric_limits<int>::max());
result.blocks_to_maturity = wtx.GetBlocksToMaturity(); result.blocks_to_maturity = wtx.GetBlocksToMaturity(locked_chain);
result.depth_in_main_chain = wtx.GetDepthInMainChain(); result.depth_in_main_chain = wtx.GetDepthInMainChain(locked_chain);
result.time_received = wtx.nTimeReceived; result.time_received = wtx.nTimeReceived;
result.lock_time = wtx.tx->nLockTime; result.lock_time = wtx.tx->nLockTime;
result.is_final = CheckFinalTx(*wtx.tx); result.is_final = CheckFinalTx(*wtx.tx);
result.is_trusted = wtx.IsTrusted(); result.is_trusted = wtx.IsTrusted(locked_chain);
result.is_abandoned = wtx.isAbandoned(); result.is_abandoned = wtx.isAbandoned();
result.is_coinbase = wtx.IsCoinBase(); result.is_coinbase = wtx.IsCoinBase();
result.is_in_main_chain = wtx.IsInMainChain(); result.is_in_main_chain = wtx.IsInMainChain(locked_chain);
return result; return result;
} }
//! Construct wallet TxOut struct. //! Construct wallet TxOut struct.
static WalletTxOut MakeWalletTxOut(CWallet& wallet, const CWalletTx& wtx, int n, int depth) EXCLUSIVE_LOCKS_REQUIRED(cs_main, wallet.cs_wallet) WalletTxOut MakeWalletTxOut(interfaces::Chain::Lock& locked_chain,
CWallet& wallet,
const CWalletTx& wtx,
int n,
int depth) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{ {
WalletTxOut result; WalletTxOut result;
result.txout = wtx.tx->vout[n]; result.txout = wtx.tx->vout[n];
result.time = wtx.GetTxTime(); result.time = wtx.GetTxTime();
result.depth_in_main_chain = depth; result.depth_in_main_chain = depth;
result.is_spent = wallet.IsSpent(wtx.GetHash(), n); result.is_spent = wallet.IsSpent(locked_chain, wtx.GetHash(), n);
return result; return result;
} }
@ -197,22 +216,26 @@ public:
} }
void lockCoin(const COutPoint& output) override void lockCoin(const COutPoint& output) override
{ {
LOCK2(cs_main, m_wallet.cs_wallet); auto locked_chain = m_wallet.chain().lock();
LOCK(m_wallet.cs_wallet);
return m_wallet.LockCoin(output); return m_wallet.LockCoin(output);
} }
void unlockCoin(const COutPoint& output) override void unlockCoin(const COutPoint& output) override
{ {
LOCK2(cs_main, m_wallet.cs_wallet); auto locked_chain = m_wallet.chain().lock();
LOCK(m_wallet.cs_wallet);
return m_wallet.UnlockCoin(output); return m_wallet.UnlockCoin(output);
} }
bool isLockedCoin(const COutPoint& output) override bool isLockedCoin(const COutPoint& output) override
{ {
LOCK2(cs_main, m_wallet.cs_wallet); auto locked_chain = m_wallet.chain().lock();
LOCK(m_wallet.cs_wallet);
return m_wallet.IsLockedCoin(output.hash, output.n); return m_wallet.IsLockedCoin(output.hash, output.n);
} }
void listLockedCoins(std::vector<COutPoint>& outputs) override void listLockedCoins(std::vector<COutPoint>& outputs) override
{ {
LOCK2(cs_main, m_wallet.cs_wallet); auto locked_chain = m_wallet.chain().lock();
LOCK(m_wallet.cs_wallet);
return m_wallet.ListLockedCoins(outputs); return m_wallet.ListLockedCoins(outputs);
} }
std::unique_ptr<PendingWalletTx> createTransaction(const std::vector<CRecipient>& recipients, std::unique_ptr<PendingWalletTx> createTransaction(const std::vector<CRecipient>& recipients,
@ -222,9 +245,10 @@ public:
CAmount& fee, CAmount& fee,
std::string& fail_reason) override std::string& fail_reason) override
{ {
LOCK2(cs_main, m_wallet.cs_wallet); auto locked_chain = m_wallet.chain().lock();
LOCK(m_wallet.cs_wallet);
auto pending = MakeUnique<PendingWalletTxImpl>(m_wallet); auto pending = MakeUnique<PendingWalletTxImpl>(m_wallet);
if (!m_wallet.CreateTransaction(recipients, pending->m_tx, pending->m_key, fee, change_pos, if (!m_wallet.CreateTransaction(*locked_chain, recipients, pending->m_tx, pending->m_key, fee, change_pos,
fail_reason, coin_control, sign)) { fail_reason, coin_control, sign)) {
return {}; return {};
} }
@ -233,8 +257,9 @@ public:
bool transactionCanBeAbandoned(const uint256& txid) override { return m_wallet.TransactionCanBeAbandoned(txid); } bool transactionCanBeAbandoned(const uint256& txid) override { return m_wallet.TransactionCanBeAbandoned(txid); }
bool abandonTransaction(const uint256& txid) override bool abandonTransaction(const uint256& txid) override
{ {
LOCK2(cs_main, m_wallet.cs_wallet); auto locked_chain = m_wallet.chain().lock();
return m_wallet.AbandonTransaction(txid); LOCK(m_wallet.cs_wallet);
return m_wallet.AbandonTransaction(*locked_chain, txid);
} }
bool transactionCanBeBumped(const uint256& txid) override bool transactionCanBeBumped(const uint256& txid) override
{ {
@ -262,7 +287,8 @@ public:
} }
CTransactionRef getTx(const uint256& txid) override CTransactionRef getTx(const uint256& txid) override
{ {
LOCK2(::cs_main, m_wallet.cs_wallet); auto locked_chain = m_wallet.chain().lock();
LOCK(m_wallet.cs_wallet);
auto mi = m_wallet.mapWallet.find(txid); auto mi = m_wallet.mapWallet.find(txid);
if (mi != m_wallet.mapWallet.end()) { if (mi != m_wallet.mapWallet.end()) {
return mi->second.tx; return mi->second.tx;
@ -271,20 +297,22 @@ public:
} }
WalletTx getWalletTx(const uint256& txid) override WalletTx getWalletTx(const uint256& txid) override
{ {
LOCK2(::cs_main, m_wallet.cs_wallet); auto locked_chain = m_wallet.chain().lock();
LOCK(m_wallet.cs_wallet);
auto mi = m_wallet.mapWallet.find(txid); auto mi = m_wallet.mapWallet.find(txid);
if (mi != m_wallet.mapWallet.end()) { if (mi != m_wallet.mapWallet.end()) {
return MakeWalletTx(m_wallet, mi->second); return MakeWalletTx(*locked_chain, m_wallet, mi->second);
} }
return {}; return {};
} }
std::vector<WalletTx> getWalletTxs() override std::vector<WalletTx> getWalletTxs() override
{ {
LOCK2(::cs_main, m_wallet.cs_wallet); auto locked_chain = m_wallet.chain().lock();
LOCK(m_wallet.cs_wallet);
std::vector<WalletTx> result; std::vector<WalletTx> result;
result.reserve(m_wallet.mapWallet.size()); result.reserve(m_wallet.mapWallet.size());
for (const auto& entry : m_wallet.mapWallet) { for (const auto& entry : m_wallet.mapWallet) {
result.emplace_back(MakeWalletTx(m_wallet, entry.second)); result.emplace_back(MakeWalletTx(*locked_chain, m_wallet, entry.second));
} }
return result; return result;
} }
@ -292,7 +320,7 @@ public:
interfaces::WalletTxStatus& tx_status, interfaces::WalletTxStatus& tx_status,
int& num_blocks) override int& num_blocks) override
{ {
TRY_LOCK(::cs_main, locked_chain); auto locked_chain = m_wallet.chain().lock(true /* try_lock */);
if (!locked_chain) { if (!locked_chain) {
return false; return false;
} }
@ -305,7 +333,7 @@ public:
return false; return false;
} }
num_blocks = ::chainActive.Height(); num_blocks = ::chainActive.Height();
tx_status = MakeWalletTxStatus(mi->second); tx_status = MakeWalletTxStatus(*locked_chain, mi->second);
return true; return true;
} }
WalletTx getWalletTxDetails(const uint256& txid, WalletTx getWalletTxDetails(const uint256& txid,
@ -314,14 +342,15 @@ public:
bool& in_mempool, bool& in_mempool,
int& num_blocks) override int& num_blocks) override
{ {
LOCK2(::cs_main, m_wallet.cs_wallet); auto locked_chain = m_wallet.chain().lock();
LOCK(m_wallet.cs_wallet);
auto mi = m_wallet.mapWallet.find(txid); auto mi = m_wallet.mapWallet.find(txid);
if (mi != m_wallet.mapWallet.end()) { if (mi != m_wallet.mapWallet.end()) {
num_blocks = ::chainActive.Height(); num_blocks = ::chainActive.Height();
in_mempool = mi->second.InMempool(); in_mempool = mi->second.InMempool();
order_form = mi->second.vOrderForm; order_form = mi->second.vOrderForm;
tx_status = MakeWalletTxStatus(mi->second); tx_status = MakeWalletTxStatus(*locked_chain, mi->second);
return MakeWalletTx(m_wallet, mi->second); return MakeWalletTx(*locked_chain, m_wallet, mi->second);
} }
return {}; return {};
} }
@ -341,7 +370,7 @@ public:
} }
bool tryGetBalances(WalletBalances& balances, int& num_blocks) override bool tryGetBalances(WalletBalances& balances, int& num_blocks) override
{ {
TRY_LOCK(cs_main, locked_chain); auto locked_chain = m_wallet.chain().lock(true /* try_lock */);
if (!locked_chain) return false; if (!locked_chain) return false;
TRY_LOCK(m_wallet.cs_wallet, locked_wallet); TRY_LOCK(m_wallet.cs_wallet, locked_wallet);
if (!locked_wallet) { if (!locked_wallet) {
@ -358,49 +387,55 @@ public:
} }
isminetype txinIsMine(const CTxIn& txin) override isminetype txinIsMine(const CTxIn& txin) override
{ {
LOCK2(::cs_main, m_wallet.cs_wallet); auto locked_chain = m_wallet.chain().lock();
LOCK(m_wallet.cs_wallet);
return m_wallet.IsMine(txin); return m_wallet.IsMine(txin);
} }
isminetype txoutIsMine(const CTxOut& txout) override isminetype txoutIsMine(const CTxOut& txout) override
{ {
LOCK2(::cs_main, m_wallet.cs_wallet); auto locked_chain = m_wallet.chain().lock();
LOCK(m_wallet.cs_wallet);
return m_wallet.IsMine(txout); return m_wallet.IsMine(txout);
} }
CAmount getDebit(const CTxIn& txin, isminefilter filter) override CAmount getDebit(const CTxIn& txin, isminefilter filter) override
{ {
LOCK2(::cs_main, m_wallet.cs_wallet); auto locked_chain = m_wallet.chain().lock();
LOCK(m_wallet.cs_wallet);
return m_wallet.GetDebit(txin, filter); return m_wallet.GetDebit(txin, filter);
} }
CAmount getCredit(const CTxOut& txout, isminefilter filter) override CAmount getCredit(const CTxOut& txout, isminefilter filter) override
{ {
LOCK2(::cs_main, m_wallet.cs_wallet); auto locked_chain = m_wallet.chain().lock();
LOCK(m_wallet.cs_wallet);
return m_wallet.GetCredit(txout, filter); return m_wallet.GetCredit(txout, filter);
} }
CoinsList listCoins() override CoinsList listCoins() override
{ {
LOCK2(::cs_main, m_wallet.cs_wallet); auto locked_chain = m_wallet.chain().lock();
LOCK(m_wallet.cs_wallet);
CoinsList result; CoinsList result;
for (const auto& entry : m_wallet.ListCoins()) { for (const auto& entry : m_wallet.ListCoins(*locked_chain)) {
auto& group = result[entry.first]; auto& group = result[entry.first];
for (const auto& coin : entry.second) { for (const auto& coin : entry.second) {
group.emplace_back( group.emplace_back(COutPoint(coin.tx->GetHash(), coin.i),
COutPoint(coin.tx->GetHash(), coin.i), MakeWalletTxOut(m_wallet, *coin.tx, coin.i, coin.nDepth)); MakeWalletTxOut(*locked_chain, m_wallet, *coin.tx, coin.i, coin.nDepth));
} }
} }
return result; return result;
} }
std::vector<WalletTxOut> getCoins(const std::vector<COutPoint>& outputs) override std::vector<WalletTxOut> getCoins(const std::vector<COutPoint>& outputs) override
{ {
LOCK2(::cs_main, m_wallet.cs_wallet); auto locked_chain = m_wallet.chain().lock();
LOCK(m_wallet.cs_wallet);
std::vector<WalletTxOut> result; std::vector<WalletTxOut> result;
result.reserve(outputs.size()); result.reserve(outputs.size());
for (const auto& output : outputs) { for (const auto& output : outputs) {
result.emplace_back(); result.emplace_back();
auto it = m_wallet.mapWallet.find(output.hash); auto it = m_wallet.mapWallet.find(output.hash);
if (it != m_wallet.mapWallet.end()) { if (it != m_wallet.mapWallet.end()) {
int depth = it->second.GetDepthInMainChain(); int depth = it->second.GetDepthInMainChain(*locked_chain);
if (depth >= 0) { if (depth >= 0) {
result.back() = MakeWalletTxOut(m_wallet, it->second, output.n, depth); result.back() = MakeWalletTxOut(*locked_chain, m_wallet, it->second, output.n, depth);
} }
} }
} }
@ -456,8 +491,32 @@ public:
CWallet& m_wallet; CWallet& m_wallet;
}; };
class WalletClientImpl : public ChainClient
{
public:
WalletClientImpl(Chain& chain, std::vector<std::string> wallet_filenames)
: m_chain(chain), m_wallet_filenames(std::move(wallet_filenames))
{
}
void registerRpcs() override { return RegisterWalletRPCCommands(::tableRPC); }
bool verify() override { return VerifyWallets(m_chain, m_wallet_filenames); }
bool load() override { return LoadWallets(m_chain, m_wallet_filenames); }
void start(CScheduler& scheduler) override { return StartWallets(scheduler); }
void flush() override { return FlushWallets(); }
void stop() override { return StopWallets(); }
~WalletClientImpl() override { UnloadWallets(); }
Chain& m_chain;
std::vector<std::string> m_wallet_filenames;
};
} // namespace } // namespace
std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet) { return MakeUnique<WalletImpl>(wallet); } std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet) { return MakeUnique<WalletImpl>(wallet); }
std::unique_ptr<ChainClient> MakeWalletClient(Chain& chain, std::vector<std::string> wallet_filenames)
{
return MakeUnique<WalletClientImpl>(chain, std::move(wallet_filenames));
}
} // namespace interfaces } // namespace interfaces

View File

@ -2,6 +2,7 @@
#include <qt/test/util.h> #include <qt/test/util.h>
#include <test/test_bitcoin.h> #include <test/test_bitcoin.h>
#include <interfaces/chain.h>
#include <interfaces/node.h> #include <interfaces/node.h>
#include <qt/addressbookpage.h> #include <qt/addressbookpage.h>
#include <qt/addresstablemodel.h> #include <qt/addresstablemodel.h>
@ -56,7 +57,8 @@ void EditAddressAndSubmit(
void TestAddAddressesToSendBook() void TestAddAddressesToSendBook()
{ {
TestChain100Setup test; TestChain100Setup test;
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(WalletLocation(), WalletDatabase::CreateMock()); auto chain = interfaces::MakeChain();
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateMock());
bool firstRun; bool firstRun;
wallet->LoadWallet(firstRun); wallet->LoadWallet(firstRun);

View File

@ -1,6 +1,7 @@
#include <qt/test/wallettests.h> #include <qt/test/wallettests.h>
#include <qt/test/util.h> #include <qt/test/util.h>
#include <interfaces/chain.h>
#include <interfaces/node.h> #include <interfaces/node.h>
#include <base58.h> #include <base58.h>
#include <qt/bitcoinamountfield.h> #include <qt/bitcoinamountfield.h>
@ -132,7 +133,8 @@ void TestGUI()
for (int i = 0; i < 5; ++i) { for (int i = 0; i < 5; ++i) {
test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey())); test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));
} }
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(WalletLocation(), WalletDatabase::CreateMock()); auto chain = interfaces::MakeChain();
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateMock());
bool firstRun; bool firstRun;
wallet->LoadWallet(firstRun); wallet->LoadWallet(firstRun);
{ {
@ -141,7 +143,7 @@ void TestGUI()
wallet->AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey()); wallet->AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey());
} }
{ {
LOCK(cs_main); auto locked_chain = wallet->chain().lock();
WalletRescanReserver reserver(wallet.get()); WalletRescanReserver reserver(wallet.get());
reserver.reserve(); reserver.reserve();
wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, true); wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, true);

View File

@ -9,6 +9,7 @@
#include <consensus/validation.h> #include <consensus/validation.h>
#include <core_io.h> #include <core_io.h>
#include <index/txindex.h> #include <index/txindex.h>
#include <init.h>
#include <keystore.h> #include <keystore.h>
#include <validation.h> #include <validation.h>
#include <validationinterface.h> #include <validationinterface.h>
@ -20,6 +21,7 @@
#include <primitives/transaction.h> #include <primitives/transaction.h>
#include <rpc/rawtransaction.h> #include <rpc/rawtransaction.h>
#include <rpc/server.h> #include <rpc/server.h>
#include <rpc/util.h>
#include <script/script.h> #include <script/script.h>
#include <script/script_error.h> #include <script/script_error.h>
#include <script/sign.h> #include <script/sign.h>
@ -754,7 +756,7 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request)
return EncodeHexTx(mergedTx); return EncodeHexTx(mergedTx);
} }
UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival, CBasicKeyStore *keystore, bool is_temp_keystore, const UniValue& hashType) UniValue SignTransaction(interfaces::Chain& chain, CMutableTransaction& mtx, const UniValue& prevTxsUnival, CBasicKeyStore *keystore, bool is_temp_keystore, const UniValue& hashType)
{ {
// Fetch previous transactions (inputs): // Fetch previous transactions (inputs):
CCoinsView viewDummy; CCoinsView viewDummy;
@ -969,7 +971,7 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
keystore.AddKey(key); keystore.AddKey(key);
} }
return SignTransaction(mtx, request.params[2], &keystore, true, request.params[3]); return SignTransaction(*g_rpc_interfaces->chain, mtx, request.params[2], &keystore, true, request.params[3]);
} }
UniValue signrawtransaction(const JSONRPCRequest& request) UniValue signrawtransaction(const JSONRPCRequest& request)

View File

@ -9,8 +9,12 @@ class CBasicKeyStore;
struct CMutableTransaction; struct CMutableTransaction;
class UniValue; class UniValue;
namespace interfaces {
class Chain;
} // namespace interfaces
/** Sign a transaction with the given keystore and previous transactions */ /** Sign a transaction with the given keystore and previous transactions */
UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxs, CBasicKeyStore *keystore, bool tempKeystore, const UniValue& hashType); UniValue SignTransaction(interfaces::Chain& chain, CMutableTransaction& mtx, const UniValue& prevTxs, CBasicKeyStore *keystore, bool tempKeystore, const UniValue& hashType);
/** Create a transaction from univalue parameters */ /** Create a transaction from univalue parameters */
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf); CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf);

View File

@ -9,6 +9,8 @@
#include <tinyformat.h> #include <tinyformat.h>
#include <util/strencodings.h> #include <util/strencodings.h>
InitInterfaces* g_rpc_interfaces = nullptr;
// Converts a hex string to a public key if possible // Converts a hex string to a public key if possible
CPubKey HexToPubKey(const std::string& hex_in) CPubKey HexToPubKey(const std::string& hex_in)
{ {

View File

@ -17,6 +17,12 @@
class CKeyStore; class CKeyStore;
class CPubKey; class CPubKey;
class CScript; class CScript;
struct InitInterfaces;
//! Pointers to interfaces that need to be accessible from RPC methods. Due to
//! limitations of the RPC framework, there's currently no direct way to pass in
//! state to RPC method implementations.
extern InitInterfaces* g_rpc_interfaces;
CPubKey HexToPubKey(const std::string& hex_in); CPubKey HexToPubKey(const std::string& hex_in);
CPubKey AddrToPubKey(CKeyStore* const keystore, const std::string& addr_in); CPubKey AddrToPubKey(CKeyStore* const keystore, const std::string& addr_in);

View File

@ -4,8 +4,11 @@
#include <rpc/server.h> #include <rpc/server.h>
#include <rpc/client.h> #include <rpc/client.h>
#include <rpc/util.h>
#include <core_io.h> #include <core_io.h>
#include <init.h>
#include <interfaces/chain.h>
#include <key_io.h> #include <key_io.h>
#include <netbase.h> #include <netbase.h>
@ -112,10 +115,14 @@ BOOST_AUTO_TEST_CASE(rpc_rawsign)
std::string notsigned = r.get_str(); std::string notsigned = r.get_str();
std::string privkey1 = "\"KzsXybp9jX64P5ekX1KUxRQ79Jht9uzW7LorgwE65i5rWACL6LQe\""; std::string privkey1 = "\"KzsXybp9jX64P5ekX1KUxRQ79Jht9uzW7LorgwE65i5rWACL6LQe\"";
std::string privkey2 = "\"Kyhdf5LuKTRx4ge69ybABsiUAWjVRK4XGxAKk2FQLp2HjGMy87Z4\""; std::string privkey2 = "\"Kyhdf5LuKTRx4ge69ybABsiUAWjVRK4XGxAKk2FQLp2HjGMy87Z4\"";
InitInterfaces interfaces;
interfaces.chain = interfaces::MakeChain();
g_rpc_interfaces = &interfaces;
r = CallRPC(std::string("signrawtransactionwithkey ")+notsigned+" [] "+prevout); r = CallRPC(std::string("signrawtransactionwithkey ")+notsigned+" [] "+prevout);
BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == false); BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == false);
r = CallRPC(std::string("signrawtransactionwithkey ")+notsigned+" ["+privkey1+","+privkey2+"] "+prevout); r = CallRPC(std::string("signrawtransactionwithkey ")+notsigned+" ["+privkey1+","+privkey2+"] "+prevout);
BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == true); BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == true);
g_rpc_interfaces = nullptr;
} }
BOOST_AUTO_TEST_CASE(rpc_createraw_op_return) BOOST_AUTO_TEST_CASE(rpc_createraw_op_return)

View File

@ -54,4 +54,15 @@
#define ASSERT_EXCLUSIVE_LOCK(...) #define ASSERT_EXCLUSIVE_LOCK(...)
#endif // __GNUC__ #endif // __GNUC__
// Utility class for indicating to compiler thread analysis that a mutex is
// locked (when it couldn't be determined otherwise).
struct SCOPED_LOCKABLE LockAnnotation
{
template <typename Mutex>
explicit LockAnnotation(Mutex& mutex) EXCLUSIVE_LOCK_FUNCTION(mutex)
{
}
~LockAnnotation() UNLOCK_FUNCTION() {}
};
#endif // BITCOIN_THREADSAFETY_H #endif // BITCOIN_THREADSAFETY_H

View File

@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <consensus/validation.h> #include <consensus/validation.h>
#include <interfaces/chain.h>
#include <wallet/coincontrol.h> #include <wallet/coincontrol.h>
#include <wallet/feebumper.h> #include <wallet/feebumper.h>
#include <wallet/fees.h> #include <wallet/fees.h>
@ -18,7 +19,7 @@
//! Check whether transaction has descendant in wallet or mempool, or has been //! Check whether transaction has descendant in wallet or mempool, or has been
//! mined, or conflicts with a mined transaction. Return a feebumper::Result. //! mined, or conflicts with a mined transaction. Return a feebumper::Result.
static feebumper::Result PreconditionChecks(const CWallet* wallet, const CWalletTx& wtx, std::vector<std::string>& errors) EXCLUSIVE_LOCKS_REQUIRED(cs_main, wallet->cs_wallet) static feebumper::Result PreconditionChecks(interfaces::Chain::Lock& locked_chain, const CWallet* wallet, const CWalletTx& wtx, std::vector<std::string>& errors) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet)
{ {
if (wallet->HasWalletSpend(wtx.GetHash())) { if (wallet->HasWalletSpend(wtx.GetHash())) {
errors.push_back("Transaction has descendants in the wallet"); errors.push_back("Transaction has descendants in the wallet");
@ -34,7 +35,7 @@ static feebumper::Result PreconditionChecks(const CWallet* wallet, const CWallet
} }
} }
if (wtx.GetDepthInMainChain() != 0) { if (wtx.GetDepthInMainChain(locked_chain) != 0) {
errors.push_back("Transaction has been mined, or is conflicted with a mined transaction"); errors.push_back("Transaction has been mined, or is conflicted with a mined transaction");
return feebumper::Result::WALLET_ERROR; return feebumper::Result::WALLET_ERROR;
} }
@ -64,19 +65,21 @@ namespace feebumper {
bool TransactionCanBeBumped(const CWallet* wallet, const uint256& txid) bool TransactionCanBeBumped(const CWallet* wallet, const uint256& txid)
{ {
LOCK2(cs_main, wallet->cs_wallet); auto locked_chain = wallet->chain().lock();
LOCK(wallet->cs_wallet);
const CWalletTx* wtx = wallet->GetWalletTx(txid); const CWalletTx* wtx = wallet->GetWalletTx(txid);
if (wtx == nullptr) return false; if (wtx == nullptr) return false;
std::vector<std::string> errors_dummy; std::vector<std::string> errors_dummy;
feebumper::Result res = PreconditionChecks(wallet, *wtx, errors_dummy); feebumper::Result res = PreconditionChecks(*locked_chain, wallet, *wtx, errors_dummy);
return res == feebumper::Result::OK; return res == feebumper::Result::OK;
} }
Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoinControl& coin_control, CAmount total_fee, std::vector<std::string>& errors, Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoinControl& coin_control, CAmount total_fee, std::vector<std::string>& errors,
CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx) CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx)
{ {
LOCK2(cs_main, wallet->cs_wallet); auto locked_chain = wallet->chain().lock();
LOCK(wallet->cs_wallet);
errors.clear(); errors.clear();
auto it = wallet->mapWallet.find(txid); auto it = wallet->mapWallet.find(txid);
if (it == wallet->mapWallet.end()) { if (it == wallet->mapWallet.end()) {
@ -85,7 +88,7 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
} }
const CWalletTx& wtx = it->second; const CWalletTx& wtx = it->second;
Result result = PreconditionChecks(wallet, wtx, errors); Result result = PreconditionChecks(*locked_chain, wallet, wtx, errors);
if (result != Result::OK) { if (result != Result::OK) {
return result; return result;
} }
@ -212,13 +215,15 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
} }
bool SignTransaction(CWallet* wallet, CMutableTransaction& mtx) { bool SignTransaction(CWallet* wallet, CMutableTransaction& mtx) {
LOCK2(cs_main, wallet->cs_wallet); auto locked_chain = wallet->chain().lock();
LOCK(wallet->cs_wallet);
return wallet->SignTransaction(mtx); return wallet->SignTransaction(mtx);
} }
Result CommitTransaction(CWallet* wallet, const uint256& txid, CMutableTransaction&& mtx, std::vector<std::string>& errors, uint256& bumped_txid) Result CommitTransaction(CWallet* wallet, const uint256& txid, CMutableTransaction&& mtx, std::vector<std::string>& errors, uint256& bumped_txid)
{ {
LOCK2(cs_main, wallet->cs_wallet); auto locked_chain = wallet->chain().lock();
LOCK(wallet->cs_wallet);
if (!errors.empty()) { if (!errors.empty()) {
return Result::MISC_ERROR; return Result::MISC_ERROR;
} }
@ -230,7 +235,7 @@ Result CommitTransaction(CWallet* wallet, const uint256& txid, CMutableTransacti
CWalletTx& oldWtx = it->second; CWalletTx& oldWtx = it->second;
// make sure the transaction still has no descendants and hasn't been mined in the meantime // make sure the transaction still has no descendants and hasn't been mined in the meantime
Result result = PreconditionChecks(wallet, oldWtx, errors); Result result = PreconditionChecks(*locked_chain, wallet, oldWtx, errors);
if (result != Result::OK) { if (result != Result::OK) {
return result; return result;
} }

View File

@ -5,6 +5,7 @@
#include <chainparams.h> #include <chainparams.h>
#include <init.h> #include <init.h>
#include <interfaces/chain.h>
#include <net.h> #include <net.h>
#include <scheduler.h> #include <scheduler.h>
#include <outputtype.h> #include <outputtype.h>
@ -28,28 +29,8 @@ public:
//! Wallets parameter interaction //! Wallets parameter interaction
bool ParameterInteraction() const override; bool ParameterInteraction() const override;
//! Register wallet RPCs. //! Add wallets that should be opened to list of init interfaces.
void RegisterRPC(CRPCTable &tableRPC) const override; void Construct(InitInterfaces& interfaces) const override;
//! Responsible for reading and validating the -wallet arguments and verifying the wallet database.
// This function will perform salvage on the wallet if requested, as long as only one wallet is
// being loaded (WalletParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet).
bool Verify() const override;
//! Load wallet databases.
bool Open() const override;
//! Complete startup of wallets.
void Start(CScheduler& scheduler) const override;
//! Flush all wallets in preparation for shutdown.
void Flush() const override;
//! Stop all wallets. Wallets will be flushed first.
void Stop() const override;
//! Close all wallets.
void Close() const override;
}; };
const WalletInitInterface& g_wallet_init_interface = WalletInit(); const WalletInitInterface& g_wallet_init_interface = WalletInit();
@ -99,7 +80,6 @@ bool WalletInit::ParameterInteraction() const
return true; return true;
} }
gArgs.SoftSetArg("-wallet", "");
const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1; const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1;
if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && gArgs.SoftSetBoolArg("-walletbroadcast", false)) { if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && gArgs.SoftSetBoolArg("-walletbroadcast", false)) {
@ -165,21 +145,8 @@ bool WalletInit::ParameterInteraction() const
return true; return true;
} }
void WalletInit::RegisterRPC(CRPCTable &t) const bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files)
{ {
if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
return;
}
RegisterWalletRPCCommands(t);
}
bool WalletInit::Verify() const
{
if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
return true;
}
if (gArgs.IsArgSet("-walletdir")) { if (gArgs.IsArgSet("-walletdir")) {
fs::path wallet_dir = gArgs.GetArg("-walletdir", ""); fs::path wallet_dir = gArgs.GetArg("-walletdir", "");
boost::system::error_code error; boost::system::error_code error;
@ -200,8 +167,6 @@ bool WalletInit::Verify() const
uiInterface.InitMessage(_("Verifying wallet(s)...")); uiInterface.InitMessage(_("Verifying wallet(s)..."));
std::vector<std::string> wallet_files = gArgs.GetArgs("-wallet");
// Parameter interaction code should have thrown an error if -salvagewallet // Parameter interaction code should have thrown an error if -salvagewallet
// was enabled with more than wallet file, so the wallet_files size check // was enabled with more than wallet file, so the wallet_files size check
// here should have no effect. // here should have no effect.
@ -219,7 +184,7 @@ bool WalletInit::Verify() const
std::string error_string; std::string error_string;
std::string warning_string; std::string warning_string;
bool verify_success = CWallet::Verify(location, salvage_wallet, error_string, warning_string); bool verify_success = CWallet::Verify(chain, location, salvage_wallet, error_string, warning_string);
if (!error_string.empty()) InitError(error_string); if (!error_string.empty()) InitError(error_string);
if (!warning_string.empty()) InitWarning(warning_string); if (!warning_string.empty()) InitWarning(warning_string);
if (!verify_success) return false; if (!verify_success) return false;
@ -228,15 +193,20 @@ bool WalletInit::Verify() const
return true; return true;
} }
bool WalletInit::Open() const void WalletInit::Construct(InitInterfaces& interfaces) const
{ {
if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
LogPrintf("Wallet disabled!\n"); LogPrintf("Wallet disabled!\n");
return true; return;
} }
gArgs.SoftSetArg("-wallet", "");
interfaces.chain_clients.emplace_back(interfaces::MakeWalletClient(*interfaces.chain, gArgs.GetArgs("-wallet")));
}
for (const std::string& walletFile : gArgs.GetArgs("-wallet")) { bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files)
std::shared_ptr<CWallet> pwallet = CWallet::CreateWalletFromFile(WalletLocation(walletFile)); {
for (const std::string& walletFile : wallet_files) {
std::shared_ptr<CWallet> pwallet = CWallet::CreateWalletFromFile(chain, WalletLocation(walletFile));
if (!pwallet) { if (!pwallet) {
return false; return false;
} }
@ -246,7 +216,7 @@ bool WalletInit::Open() const
return true; return true;
} }
void WalletInit::Start(CScheduler& scheduler) const void StartWallets(CScheduler& scheduler)
{ {
for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) { for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
pwallet->postInitProcess(); pwallet->postInitProcess();
@ -256,21 +226,21 @@ void WalletInit::Start(CScheduler& scheduler) const
scheduler.scheduleEvery(MaybeCompactWalletDB, 500); scheduler.scheduleEvery(MaybeCompactWalletDB, 500);
} }
void WalletInit::Flush() const void FlushWallets()
{ {
for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) { for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
pwallet->Flush(false); pwallet->Flush(false);
} }
} }
void WalletInit::Stop() const void StopWallets()
{ {
for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) { for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
pwallet->Flush(true); pwallet->Flush(true);
} }
} }
void WalletInit::Close() const void UnloadWallets()
{ {
for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) { for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
RemoveWallet(pwallet); RemoveWallet(pwallet);

View File

@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chain.h> #include <chain.h>
#include <interfaces/chain.h>
#include <key_io.h> #include <key_io.h>
#include <rpc/server.h> #include <rpc/server.h>
#include <validation.h> #include <validation.h>
@ -133,7 +134,8 @@ UniValue importprivkey(const JSONRPCRequest& request)
WalletRescanReserver reserver(pwallet); WalletRescanReserver reserver(pwallet);
bool fRescan = true; bool fRescan = true;
{ {
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet); EnsureWalletIsUnlocked(pwallet);
@ -305,7 +307,8 @@ UniValue importaddress(const JSONRPCRequest& request)
fP2SH = request.params[3].get_bool(); fP2SH = request.params[3].get_bool();
{ {
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
CTxDestination dest = DecodeDestination(request.params[0].get_str()); CTxDestination dest = DecodeDestination(request.params[0].get_str());
if (IsValidDestination(dest)) { if (IsValidDestination(dest)) {
@ -362,7 +365,7 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
unsigned int txnIndex = 0; unsigned int txnIndex = 0;
if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) == merkleBlock.header.hashMerkleRoot) { if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) == merkleBlock.header.hashMerkleRoot) {
LOCK(cs_main); auto locked_chain = pwallet->chain().lock();
const CBlockIndex* pindex = LookupBlockIndex(merkleBlock.header.GetHash()); const CBlockIndex* pindex = LookupBlockIndex(merkleBlock.header.GetHash());
if (!pindex || !chainActive.Contains(pindex)) { if (!pindex || !chainActive.Contains(pindex)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
@ -382,7 +385,8 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
wtx.nIndex = txnIndex; wtx.nIndex = txnIndex;
wtx.hashBlock = merkleBlock.header.GetHash(); wtx.hashBlock = merkleBlock.header.GetHash();
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
if (pwallet->IsMine(*wtx.tx)) { if (pwallet->IsMine(*wtx.tx)) {
pwallet->AddToWallet(wtx, false); pwallet->AddToWallet(wtx, false);
@ -412,7 +416,8 @@ UniValue removeprunedfunds(const JSONRPCRequest& request)
+ HelpExampleRpc("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"") + HelpExampleRpc("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"")
); );
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
uint256 hash(ParseHashV(request.params[0], "txid")); uint256 hash(ParseHashV(request.params[0], "txid"));
std::vector<uint256> vHash; std::vector<uint256> vHash;
@ -483,7 +488,8 @@ UniValue importpubkey(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key");
{ {
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
for (const auto& dest : GetAllDestinationsForKey(pubKey)) { for (const auto& dest : GetAllDestinationsForKey(pubKey)) {
ImportAddress(pwallet, dest, strLabel); ImportAddress(pwallet, dest, strLabel);
@ -535,7 +541,8 @@ UniValue importwallet(const JSONRPCRequest& request)
int64_t nTimeBegin = 0; int64_t nTimeBegin = 0;
bool fGood = true; bool fGood = true;
{ {
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet); EnsureWalletIsUnlocked(pwallet);
@ -653,7 +660,8 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
+ HelpExampleRpc("dumpprivkey", "\"myaddress\"") + HelpExampleRpc("dumpprivkey", "\"myaddress\"")
); );
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet); EnsureWalletIsUnlocked(pwallet);
@ -700,7 +708,8 @@ UniValue dumpwallet(const JSONRPCRequest& request)
+ HelpExampleRpc("dumpwallet", "\"test\"") + HelpExampleRpc("dumpwallet", "\"test\"")
); );
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet); EnsureWalletIsUnlocked(pwallet);
@ -723,7 +732,7 @@ UniValue dumpwallet(const JSONRPCRequest& request)
std::map<CTxDestination, int64_t> mapKeyBirth; std::map<CTxDestination, int64_t> mapKeyBirth;
const std::map<CKeyID, int64_t>& mapKeyPool = pwallet->GetAllReserveKeys(); const std::map<CKeyID, int64_t>& mapKeyPool = pwallet->GetAllReserveKeys();
pwallet->GetKeyBirthTimes(mapKeyBirth); pwallet->GetKeyBirthTimes(*locked_chain, mapKeyBirth);
std::set<CScriptID> scripts = pwallet->GetCScripts(); std::set<CScriptID> scripts = pwallet->GetCScripts();
// TODO: include scripts in GetKeyBirthTimes() output instead of separate // TODO: include scripts in GetKeyBirthTimes() output instead of separate
@ -1134,7 +1143,8 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
int64_t nLowestTimestamp = 0; int64_t nLowestTimestamp = 0;
UniValue response(UniValue::VARR); UniValue response(UniValue::VARR);
{ {
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet); EnsureWalletIsUnlocked(pwallet);
// Verify all timestamps are present before importing any keys. // Verify all timestamps are present before importing any keys.

View File

@ -8,6 +8,8 @@
#include <consensus/validation.h> #include <consensus/validation.h>
#include <core_io.h> #include <core_io.h>
#include <httpserver.h> #include <httpserver.h>
#include <init.h>
#include <interfaces/chain.h>
#include <validation.h> #include <validation.h>
#include <key_io.h> #include <key_io.h>
#include <net.h> #include <net.h>
@ -89,9 +91,9 @@ void EnsureWalletIsUnlocked(CWallet * const pwallet)
} }
} }
static void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) EXCLUSIVE_LOCKS_REQUIRED(cs_main) static void WalletTxToJSON(interfaces::Chain& chain, interfaces::Chain::Lock& locked_chain, const CWalletTx& wtx, UniValue& entry)
{ {
int confirms = wtx.GetDepthInMainChain(); int confirms = wtx.GetDepthInMainChain(locked_chain);
entry.pushKV("confirmations", confirms); entry.pushKV("confirmations", confirms);
if (wtx.IsCoinBase()) if (wtx.IsCoinBase())
entry.pushKV("generated", true); entry.pushKV("generated", true);
@ -101,7 +103,7 @@ static void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) EXCLUSIVE_LOCK
entry.pushKV("blockindex", wtx.nIndex); entry.pushKV("blockindex", wtx.nIndex);
entry.pushKV("blocktime", LookupBlockIndex(wtx.hashBlock)->GetBlockTime()); entry.pushKV("blocktime", LookupBlockIndex(wtx.hashBlock)->GetBlockTime());
} else { } else {
entry.pushKV("trusted", wtx.IsTrusted()); entry.pushKV("trusted", wtx.IsTrusted(locked_chain));
} }
uint256 hash = wtx.GetHash(); uint256 hash = wtx.GetHash();
entry.pushKV("txid", hash.GetHex()); entry.pushKV("txid", hash.GetHex());
@ -290,7 +292,7 @@ static UniValue setlabel(const JSONRPCRequest& request)
} }
static CTransactionRef SendMoney(CWallet * const pwallet, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, const CCoinControl& coin_control, mapValue_t mapValue) static CTransactionRef SendMoney(interfaces::Chain::Lock& locked_chain, CWallet * const pwallet, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, const CCoinControl& coin_control, mapValue_t mapValue)
{ {
CAmount curBalance = pwallet->GetBalance(); CAmount curBalance = pwallet->GetBalance();
@ -317,7 +319,7 @@ static CTransactionRef SendMoney(CWallet * const pwallet, const CTxDestination &
CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount}; CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount};
vecSend.push_back(recipient); vecSend.push_back(recipient);
CTransactionRef tx; CTransactionRef tx;
if (!pwallet->CreateTransaction(vecSend, tx, reservekey, nFeeRequired, nChangePosRet, strError, coin_control)) { if (!pwallet->CreateTransaction(locked_chain, vecSend, tx, reservekey, nFeeRequired, nChangePosRet, strError, coin_control)) {
if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance)
strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired)); strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired));
throw JSONRPCError(RPC_WALLET_ERROR, strError); throw JSONRPCError(RPC_WALLET_ERROR, strError);
@ -373,7 +375,8 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now // the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain(); pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
CTxDestination dest = DecodeDestination(request.params[0].get_str()); CTxDestination dest = DecodeDestination(request.params[0].get_str());
if (!IsValidDestination(dest)) { if (!IsValidDestination(dest)) {
@ -415,7 +418,7 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
EnsureWalletIsUnlocked(pwallet); EnsureWalletIsUnlocked(pwallet);
CTransactionRef tx = SendMoney(pwallet, dest, nAmount, fSubtractFeeFromAmount, coin_control, std::move(mapValue)); CTransactionRef tx = SendMoney(*locked_chain, pwallet, dest, nAmount, fSubtractFeeFromAmount, coin_control, std::move(mapValue));
return tx->GetHash().GetHex(); return tx->GetHash().GetHex();
} }
@ -455,10 +458,11 @@ static UniValue listaddressgroupings(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now // the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain(); pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
UniValue jsonGroupings(UniValue::VARR); UniValue jsonGroupings(UniValue::VARR);
std::map<CTxDestination, CAmount> balances = pwallet->GetAddressBalances(); std::map<CTxDestination, CAmount> balances = pwallet->GetAddressBalances(*locked_chain);
for (const std::set<CTxDestination>& grouping : pwallet->GetAddressGroupings()) { for (const std::set<CTxDestination>& grouping : pwallet->GetAddressGroupings()) {
UniValue jsonGrouping(UniValue::VARR); UniValue jsonGrouping(UniValue::VARR);
for (const CTxDestination& address : grouping) for (const CTxDestination& address : grouping)
@ -508,7 +512,8 @@ static UniValue signmessage(const JSONRPCRequest& request)
+ HelpExampleRpc("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"my message\"") + HelpExampleRpc("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"my message\"")
); );
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet); EnsureWalletIsUnlocked(pwallet);
@ -574,7 +579,9 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now // the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain(); pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet); LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
// Bitcoin address // Bitcoin address
CTxDestination dest = DecodeDestination(request.params[0].get_str()); CTxDestination dest = DecodeDestination(request.params[0].get_str());
@ -600,7 +607,7 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
for (const CTxOut& txout : wtx.tx->vout) for (const CTxOut& txout : wtx.tx->vout)
if (txout.scriptPubKey == scriptPubKey) if (txout.scriptPubKey == scriptPubKey)
if (wtx.GetDepthInMainChain() >= nMinDepth) if (wtx.GetDepthInMainChain(*locked_chain) >= nMinDepth)
nAmount += txout.nValue; nAmount += txout.nValue;
} }
@ -641,7 +648,9 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now // the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain(); pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet); LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
// Minimum confirmations // Minimum confirmations
int nMinDepth = 1; int nMinDepth = 1;
@ -663,7 +672,7 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
{ {
CTxDestination address; CTxDestination address;
if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwallet, address) && setAddress.count(address)) { if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwallet, address) && setAddress.count(address)) {
if (wtx.GetDepthInMainChain() >= nMinDepth) if (wtx.GetDepthInMainChain(*locked_chain) >= nMinDepth)
nAmount += txout.nValue; nAmount += txout.nValue;
} }
} }
@ -707,7 +716,8 @@ static UniValue getbalance(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now // the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain(); pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
const UniValue& dummy_value = request.params[0]; const UniValue& dummy_value = request.params[0];
if (!dummy_value.isNull() && dummy_value.get_str() != "*") { if (!dummy_value.isNull() && dummy_value.get_str() != "*") {
@ -745,7 +755,8 @@ static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
// the user could have gotten from another RPC command prior to now // the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain(); pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
return ValueFromAmount(pwallet->GetUnconfirmedBalance()); return ValueFromAmount(pwallet->GetUnconfirmedBalance());
} }
@ -806,7 +817,8 @@ static UniValue sendmany(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now // the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain(); pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
if (pwallet->GetBroadcastTransactions() && !g_connman) { if (pwallet->GetBroadcastTransactions() && !g_connman) {
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
@ -892,7 +904,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
int nChangePosRet = -1; int nChangePosRet = -1;
std::string strFailReason; std::string strFailReason;
CTransactionRef tx; CTransactionRef tx;
bool fCreated = pwallet->CreateTransaction(vecSend, tx, keyChange, nFeeRequired, nChangePosRet, strFailReason, coin_control); bool fCreated = pwallet->CreateTransaction(*locked_chain, vecSend, tx, keyChange, nFeeRequired, nChangePosRet, strFailReason, coin_control);
if (!fCreated) if (!fCreated)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason); throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
CValidationState state; CValidationState state;
@ -945,7 +957,8 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
throw std::runtime_error(msg); throw std::runtime_error(msg);
} }
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
std::string label; std::string label;
if (!request.params[2].isNull()) if (!request.params[2].isNull())
@ -996,8 +1009,10 @@ struct tallyitem
} }
}; };
static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(cs_main, pwallet->cs_wallet) static UniValue ListReceived(interfaces::Chain::Lock& locked_chain, CWallet * const pwallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
{ {
LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
// Minimum confirmations // Minimum confirmations
int nMinDepth = 1; int nMinDepth = 1;
if (!params[0].isNull()) if (!params[0].isNull())
@ -1031,7 +1046,7 @@ static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bo
if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx)) if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx))
continue; continue;
int nDepth = wtx.GetDepthInMainChain(); int nDepth = wtx.GetDepthInMainChain(locked_chain);
if (nDepth < nMinDepth) if (nDepth < nMinDepth)
continue; continue;
@ -1185,9 +1200,10 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now // the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain(); pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
return ListReceived(pwallet, request.params, false); return ListReceived(*locked_chain, pwallet, request.params, false);
} }
static UniValue listreceivedbylabel(const JSONRPCRequest& request) static UniValue listreceivedbylabel(const JSONRPCRequest& request)
@ -1229,9 +1245,10 @@ static UniValue listreceivedbylabel(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now // the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain(); pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
return ListReceived(pwallet, request.params, true); return ListReceived(*locked_chain, pwallet, request.params, true);
} }
static void MaybePushAddress(UniValue & entry, const CTxDestination &dest) static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
@ -1251,7 +1268,7 @@ static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
* @param ret The UniValue into which the result is stored. * @param ret The UniValue into which the result is stored.
* @param filter The "is mine" filter bool. * @param filter The "is mine" filter bool.
*/ */
static void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter) EXCLUSIVE_LOCKS_REQUIRED(cs_main) static void ListTransactions(interfaces::Chain::Lock& locked_chain, CWallet* const pwallet, const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter)
{ {
CAmount nFee; CAmount nFee;
std::list<COutputEntry> listReceived; std::list<COutputEntry> listReceived;
@ -1279,14 +1296,14 @@ static void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, int n
entry.pushKV("vout", s.vout); entry.pushKV("vout", s.vout);
entry.pushKV("fee", ValueFromAmount(-nFee)); entry.pushKV("fee", ValueFromAmount(-nFee));
if (fLong) if (fLong)
WalletTxToJSON(wtx, entry); WalletTxToJSON(pwallet->chain(), locked_chain, wtx, entry);
entry.pushKV("abandoned", wtx.isAbandoned()); entry.pushKV("abandoned", wtx.isAbandoned());
ret.push_back(entry); ret.push_back(entry);
} }
} }
// Received // Received
if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) if (listReceived.size() > 0 && wtx.GetDepthInMainChain(locked_chain) >= nMinDepth)
{ {
for (const COutputEntry& r : listReceived) for (const COutputEntry& r : listReceived)
{ {
@ -1301,9 +1318,9 @@ static void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, int n
MaybePushAddress(entry, r.destination); MaybePushAddress(entry, r.destination);
if (wtx.IsCoinBase()) if (wtx.IsCoinBase())
{ {
if (wtx.GetDepthInMainChain() < 1) if (wtx.GetDepthInMainChain(locked_chain) < 1)
entry.pushKV("category", "orphan"); entry.pushKV("category", "orphan");
else if (wtx.IsImmatureCoinBase()) else if (wtx.IsImmatureCoinBase(locked_chain))
entry.pushKV("category", "immature"); entry.pushKV("category", "immature");
else else
entry.pushKV("category", "generate"); entry.pushKV("category", "generate");
@ -1318,7 +1335,7 @@ static void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, int n
} }
entry.pushKV("vout", r.vout); entry.pushKV("vout", r.vout);
if (fLong) if (fLong)
WalletTxToJSON(wtx, entry); WalletTxToJSON(pwallet->chain(), locked_chain, wtx, entry);
ret.push_back(entry); ret.push_back(entry);
} }
} }
@ -1405,7 +1422,8 @@ UniValue listtransactions(const JSONRPCRequest& request)
UniValue ret(UniValue::VARR); UniValue ret(UniValue::VARR);
{ {
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
const CWallet::TxItems & txOrdered = pwallet->wtxOrdered; const CWallet::TxItems & txOrdered = pwallet->wtxOrdered;
@ -1413,7 +1431,7 @@ UniValue listtransactions(const JSONRPCRequest& request)
for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
{ {
CWalletTx *const pwtx = (*it).second; CWalletTx *const pwtx = (*it).second;
ListTransactions(pwallet, *pwtx, 0, true, ret, filter); ListTransactions(*locked_chain, pwallet, *pwtx, 0, true, ret, filter);
if ((int)ret.size() >= (nCount+nFrom)) break; if ((int)ret.size() >= (nCount+nFrom)) break;
} }
} }
@ -1505,7 +1523,8 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now // the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain(); pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
const CBlockIndex* pindex = nullptr; // Block index of the specified block or the common ancestor, if the block provided was in a deactivated chain. const CBlockIndex* pindex = nullptr; // Block index of the specified block or the common ancestor, if the block provided was in a deactivated chain.
const CBlockIndex* paltindex = nullptr; // Block index of the specified block, even if it's in a deactivated chain. const CBlockIndex* paltindex = nullptr; // Block index of the specified block, even if it's in a deactivated chain.
@ -1548,8 +1567,8 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) { for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
CWalletTx tx = pairWtx.second; CWalletTx tx = pairWtx.second;
if (depth == -1 || tx.GetDepthInMainChain() < depth) { if (depth == -1 || tx.GetDepthInMainChain(*locked_chain) < depth) {
ListTransactions(pwallet, tx, 0, true, transactions, filter); ListTransactions(*locked_chain, pwallet, tx, 0, true, transactions, filter);
} }
} }
@ -1566,7 +1585,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
if (it != pwallet->mapWallet.end()) { if (it != pwallet->mapWallet.end()) {
// We want all transactions regardless of confirmation count to appear here, // We want all transactions regardless of confirmation count to appear here,
// even negative confirmation ones, hence the big negative. // even negative confirmation ones, hence the big negative.
ListTransactions(pwallet, it->second, -100000000, true, removed, filter); ListTransactions(*locked_chain, pwallet, it->second, -100000000, true, removed, filter);
} }
} }
paltindex = paltindex->pprev; paltindex = paltindex->pprev;
@ -1640,7 +1659,8 @@ static UniValue gettransaction(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now // the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain(); pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
uint256 hash(ParseHashV(request.params[0], "txid")); uint256 hash(ParseHashV(request.params[0], "txid"));
@ -1656,7 +1676,7 @@ static UniValue gettransaction(const JSONRPCRequest& request)
} }
const CWalletTx& wtx = it->second; const CWalletTx& wtx = it->second;
CAmount nCredit = wtx.GetCredit(filter); CAmount nCredit = wtx.GetCredit(*locked_chain, filter);
CAmount nDebit = wtx.GetDebit(filter); CAmount nDebit = wtx.GetDebit(filter);
CAmount nNet = nCredit - nDebit; CAmount nNet = nCredit - nDebit;
CAmount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut() - nDebit : 0); CAmount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut() - nDebit : 0);
@ -1665,10 +1685,10 @@ static UniValue gettransaction(const JSONRPCRequest& request)
if (wtx.IsFromMe(filter)) if (wtx.IsFromMe(filter))
entry.pushKV("fee", ValueFromAmount(nFee)); entry.pushKV("fee", ValueFromAmount(nFee));
WalletTxToJSON(wtx, entry); WalletTxToJSON(pwallet->chain(), *locked_chain, wtx, entry);
UniValue details(UniValue::VARR); UniValue details(UniValue::VARR);
ListTransactions(pwallet, wtx, 0, false, details, filter); ListTransactions(*locked_chain, pwallet, wtx, 0, false, details, filter);
entry.pushKV("details", details); entry.pushKV("details", details);
std::string strHex = EncodeHexTx(*wtx.tx, RPCSerializationFlags()); std::string strHex = EncodeHexTx(*wtx.tx, RPCSerializationFlags());
@ -1707,14 +1727,15 @@ static UniValue abandontransaction(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now // the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain(); pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
uint256 hash(ParseHashV(request.params[0], "txid")); uint256 hash(ParseHashV(request.params[0], "txid"));
if (!pwallet->mapWallet.count(hash)) { if (!pwallet->mapWallet.count(hash)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id");
} }
if (!pwallet->AbandonTransaction(hash)) { if (!pwallet->AbandonTransaction(*locked_chain, hash)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not eligible for abandonment"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not eligible for abandonment");
} }
@ -1746,7 +1767,8 @@ static UniValue backupwallet(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now // the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain(); pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
std::string strDest = request.params[0].get_str(); std::string strDest = request.params[0].get_str();
if (!pwallet->BackupWallet(strDest)) { if (!pwallet->BackupWallet(strDest)) {
@ -1782,7 +1804,8 @@ static UniValue keypoolrefill(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet"); throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
} }
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
// 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool // 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool
unsigned int kpSize = 0; unsigned int kpSize = 0;
@ -1833,7 +1856,8 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
); );
} }
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
if (!pwallet->IsCrypted()) { if (!pwallet->IsCrypted()) {
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called."); throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
@ -1911,7 +1935,8 @@ static UniValue walletpassphrasechange(const JSONRPCRequest& request)
); );
} }
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
if (!pwallet->IsCrypted()) { if (!pwallet->IsCrypted()) {
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called."); throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
@ -1967,7 +1992,8 @@ static UniValue walletlock(const JSONRPCRequest& request)
); );
} }
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
if (!pwallet->IsCrypted()) { if (!pwallet->IsCrypted()) {
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called."); throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
@ -2013,7 +2039,8 @@ static UniValue encryptwallet(const JSONRPCRequest& request)
); );
} }
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
if (pwallet->IsCrypted()) { if (pwallet->IsCrypted()) {
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called."); throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
@ -2087,7 +2114,8 @@ static UniValue lockunspent(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now // the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain(); pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
RPCTypeCheckArgument(request.params[0], UniValue::VBOOL); RPCTypeCheckArgument(request.params[0], UniValue::VBOOL);
@ -2136,7 +2164,7 @@ static UniValue lockunspent(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout index out of bounds"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout index out of bounds");
} }
if (pwallet->IsSpent(outpt.hash, outpt.n)) { if (pwallet->IsSpent(*locked_chain, outpt.hash, outpt.n)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected unspent output"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected unspent output");
} }
@ -2197,7 +2225,8 @@ static UniValue listlockunspent(const JSONRPCRequest& request)
+ HelpExampleRpc("listlockunspent", "") + HelpExampleRpc("listlockunspent", "")
); );
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
std::vector<COutPoint> vOutpts; std::vector<COutPoint> vOutpts;
pwallet->ListLockedCoins(vOutpts); pwallet->ListLockedCoins(vOutpts);
@ -2238,7 +2267,8 @@ static UniValue settxfee(const JSONRPCRequest& request)
); );
} }
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
CAmount nAmount = AmountFromValue(request.params[0]); CAmount nAmount = AmountFromValue(request.params[0]);
CFeeRate tx_fee_rate(nAmount, 1000); CFeeRate tx_fee_rate(nAmount, 1000);
@ -2293,7 +2323,8 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now // the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain(); pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
UniValue obj(UniValue::VOBJ); UniValue obj(UniValue::VOBJ);
@ -2420,11 +2451,11 @@ static UniValue loadwallet(const JSONRPCRequest& request)
} }
std::string warning; std::string warning;
if (!CWallet::Verify(location, false, error, warning)) { if (!CWallet::Verify(*g_rpc_interfaces->chain, location, false, error, warning)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet file verification failed: " + error); throw JSONRPCError(RPC_WALLET_ERROR, "Wallet file verification failed: " + error);
} }
std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(location); std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(*g_rpc_interfaces->chain, location);
if (!wallet) { if (!wallet) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet loading failed."); throw JSONRPCError(RPC_WALLET_ERROR, "Wallet loading failed.");
} }
@ -2472,11 +2503,11 @@ static UniValue createwallet(const JSONRPCRequest& request)
} }
// Wallet::Verify will check if we're trying to create a wallet with a duplication name. // Wallet::Verify will check if we're trying to create a wallet with a duplication name.
if (!CWallet::Verify(location, false, error, warning)) { if (!CWallet::Verify(*g_rpc_interfaces->chain, location, false, error, warning)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet file verification failed: " + error); throw JSONRPCError(RPC_WALLET_ERROR, "Wallet file verification failed: " + error);
} }
std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(location, (disable_privatekeys ? (uint64_t)WALLET_FLAG_DISABLE_PRIVATE_KEYS : 0)); std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(*g_rpc_interfaces->chain, location, (disable_privatekeys ? (uint64_t)WALLET_FLAG_DISABLE_PRIVATE_KEYS : 0));
if (!wallet) { if (!wallet) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet creation failed."); throw JSONRPCError(RPC_WALLET_ERROR, "Wallet creation failed.");
} }
@ -2562,13 +2593,14 @@ static UniValue resendwallettransactions(const JSONRPCRequest& request)
if (!g_connman) if (!g_connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
if (!pwallet->GetBroadcastTransactions()) { if (!pwallet->GetBroadcastTransactions()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet transaction broadcasting is disabled with -walletbroadcast"); throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet transaction broadcasting is disabled with -walletbroadcast");
} }
std::vector<uint256> txids = pwallet->ResendWalletTransactionsBefore(GetTime(), g_connman.get()); std::vector<uint256> txids = pwallet->ResendWalletTransactionsBefore(*locked_chain, GetTime(), g_connman.get());
UniValue result(UniValue::VARR); UniValue result(UniValue::VARR);
for (const uint256& txid : txids) for (const uint256& txid : txids)
{ {
@ -2699,8 +2731,9 @@ static UniValue listunspent(const JSONRPCRequest& request)
UniValue results(UniValue::VARR); UniValue results(UniValue::VARR);
std::vector<COutput> vecOutputs; std::vector<COutput> vecOutputs;
{ {
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
pwallet->AvailableCoins(vecOutputs, !include_unsafe, nullptr, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth); LOCK(pwallet->cs_wallet);
pwallet->AvailableCoins(*locked_chain, vecOutputs, !include_unsafe, nullptr, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth);
} }
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
@ -3018,10 +3051,11 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
} }
// Sign the transaction // Sign the transaction
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet); EnsureWalletIsUnlocked(pwallet);
return SignTransaction(mtx, request.params[1], pwallet, false, request.params[2]); return SignTransaction(pwallet->chain(), mtx, request.params[1], pwallet, false, request.params[2]);
} }
static UniValue bumpfee(const JSONRPCRequest& request) static UniValue bumpfee(const JSONRPCRequest& request)
@ -3123,7 +3157,8 @@ static UniValue bumpfee(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now // the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain(); pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet); EnsureWalletIsUnlocked(pwallet);
@ -3263,7 +3298,7 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
CBlockIndex *pindexStop = nullptr; CBlockIndex *pindexStop = nullptr;
CBlockIndex *pChainTip = nullptr; CBlockIndex *pChainTip = nullptr;
{ {
LOCK(cs_main); auto locked_chain = pwallet->chain().lock();
pindexStart = chainActive.Genesis(); pindexStart = chainActive.Genesis();
pChainTip = chainActive.Tip(); pChainTip = chainActive.Tip();
@ -3287,7 +3322,7 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
// We can't rescan beyond non-pruned blocks, stop and throw an error // We can't rescan beyond non-pruned blocks, stop and throw an error
if (fPruneMode) { if (fPruneMode) {
LOCK(cs_main); auto locked_chain = pwallet->chain().lock();
CBlockIndex *block = pindexStop ? pindexStop : pChainTip; CBlockIndex *block = pindexStop ? pindexStop : pChainTip;
while (block && block->nHeight >= pindexStart->nHeight) { while (block && block->nHeight >= pindexStart->nHeight) {
if (!(block->nStatus & BLOCK_HAVE_DATA)) { if (!(block->nStatus & BLOCK_HAVE_DATA)) {
@ -3687,7 +3722,8 @@ UniValue sethdseed(const JSONRPCRequest& request)
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Cannot set a new HD seed while still in Initial Block Download"); throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Cannot set a new HD seed while still in Initial Block Download");
} }
LOCK2(cs_main, pwallet->cs_wallet); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
// Do not do anything to non-HD wallets // Do not do anything to non-HD wallets
if (!pwallet->IsHDEnabled()) { if (!pwallet->IsHDEnabled()) {

View File

@ -28,7 +28,8 @@ std::vector<std::unique_ptr<CWalletTx>> wtxn;
typedef std::set<CInputCoin> CoinSet; typedef std::set<CInputCoin> CoinSet;
static std::vector<COutput> vCoins; static std::vector<COutput> vCoins;
static CWallet testWallet(WalletLocation(), WalletDatabase::CreateDummy()); static auto testChain = interfaces::MakeChain();
static CWallet testWallet(*testChain, WalletLocation(), WalletDatabase::CreateDummy());
static CAmount balance = 0; static CAmount balance = 0;
CoinEligibilityFilter filter_standard(1, 6, 0); CoinEligibilityFilter filter_standard(1, 6, 0);

View File

@ -8,6 +8,8 @@
InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainName): BasicTestingSetup(chainName) InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainName): BasicTestingSetup(chainName)
{ {
m_chain_client = MakeWalletClient(*m_chain, {});
std::string sep; std::string sep;
sep += fs::path::preferred_separator; sep += fs::path::preferred_separator;

View File

@ -5,6 +5,7 @@
#ifndef BITCOIN_WALLET_TEST_INIT_TEST_FIXTURE_H #ifndef BITCOIN_WALLET_TEST_INIT_TEST_FIXTURE_H
#define BITCOIN_WALLET_TEST_INIT_TEST_FIXTURE_H #define BITCOIN_WALLET_TEST_INIT_TEST_FIXTURE_H
#include <interfaces/chain.h>
#include <test/test_bitcoin.h> #include <test/test_bitcoin.h>
@ -16,6 +17,8 @@ struct InitWalletDirTestingSetup: public BasicTestingSetup {
fs::path m_datadir; fs::path m_datadir;
fs::path m_cwd; fs::path m_cwd;
std::map<std::string, fs::path> m_walletdir_path_cases; std::map<std::string, fs::path> m_walletdir_path_cases;
std::unique_ptr<interfaces::Chain> m_chain = interfaces::MakeChain();
std::unique_ptr<interfaces::ChainClient> m_chain_client;
}; };
#endif // BITCOIN_WALLET_TEST_INIT_TEST_FIXTURE_H #endif // BITCOIN_WALLET_TEST_INIT_TEST_FIXTURE_H

View File

@ -17,7 +17,7 @@ BOOST_FIXTURE_TEST_SUITE(init_tests, InitWalletDirTestingSetup)
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_default) BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_default)
{ {
SetWalletDir(m_walletdir_path_cases["default"]); SetWalletDir(m_walletdir_path_cases["default"]);
bool result = g_wallet_init_interface.Verify(); bool result = m_chain_client->verify();
BOOST_CHECK(result == true); BOOST_CHECK(result == true);
fs::path walletdir = gArgs.GetArg("-walletdir", ""); fs::path walletdir = gArgs.GetArg("-walletdir", "");
fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]); fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
@ -27,7 +27,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_default)
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_custom) BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_custom)
{ {
SetWalletDir(m_walletdir_path_cases["custom"]); SetWalletDir(m_walletdir_path_cases["custom"]);
bool result = g_wallet_init_interface.Verify(); bool result = m_chain_client->verify();
BOOST_CHECK(result == true); BOOST_CHECK(result == true);
fs::path walletdir = gArgs.GetArg("-walletdir", ""); fs::path walletdir = gArgs.GetArg("-walletdir", "");
fs::path expected_path = fs::canonical(m_walletdir_path_cases["custom"]); fs::path expected_path = fs::canonical(m_walletdir_path_cases["custom"]);
@ -37,28 +37,28 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_custom)
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_does_not_exist) BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_does_not_exist)
{ {
SetWalletDir(m_walletdir_path_cases["nonexistent"]); SetWalletDir(m_walletdir_path_cases["nonexistent"]);
bool result = g_wallet_init_interface.Verify(); bool result = m_chain_client->verify();
BOOST_CHECK(result == false); BOOST_CHECK(result == false);
} }
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_directory) BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_directory)
{ {
SetWalletDir(m_walletdir_path_cases["file"]); SetWalletDir(m_walletdir_path_cases["file"]);
bool result = g_wallet_init_interface.Verify(); bool result = m_chain_client->verify();
BOOST_CHECK(result == false); BOOST_CHECK(result == false);
} }
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_relative) BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_relative)
{ {
SetWalletDir(m_walletdir_path_cases["relative"]); SetWalletDir(m_walletdir_path_cases["relative"]);
bool result = g_wallet_init_interface.Verify(); bool result = m_chain_client->verify();
BOOST_CHECK(result == false); BOOST_CHECK(result == false);
} }
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing) BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing)
{ {
SetWalletDir(m_walletdir_path_cases["trailing"]); SetWalletDir(m_walletdir_path_cases["trailing"]);
bool result = g_wallet_init_interface.Verify(); bool result = m_chain_client->verify();
BOOST_CHECK(result == true); BOOST_CHECK(result == true);
fs::path walletdir = gArgs.GetArg("-walletdir", ""); fs::path walletdir = gArgs.GetArg("-walletdir", "");
fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]); fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
@ -68,7 +68,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing)
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing2) BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing2)
{ {
SetWalletDir(m_walletdir_path_cases["trailing2"]); SetWalletDir(m_walletdir_path_cases["trailing2"]);
bool result = g_wallet_init_interface.Verify(); bool result = m_chain_client->verify();
BOOST_CHECK(result == true); BOOST_CHECK(result == true);
fs::path walletdir = gArgs.GetArg("-walletdir", ""); fs::path walletdir = gArgs.GetArg("-walletdir", "");
fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]); fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);

View File

@ -9,7 +9,7 @@
#include <wallet/rpcwallet.h> #include <wallet/rpcwallet.h>
WalletTestingSetup::WalletTestingSetup(const std::string& chainName): WalletTestingSetup::WalletTestingSetup(const std::string& chainName):
TestingSetup(chainName), m_wallet(WalletLocation(), WalletDatabase::CreateMock()) TestingSetup(chainName), m_wallet(*m_chain, WalletLocation(), WalletDatabase::CreateMock())
{ {
bool fFirstRun; bool fFirstRun;
m_wallet.LoadWallet(fFirstRun); m_wallet.LoadWallet(fFirstRun);

View File

@ -7,6 +7,8 @@
#include <test/test_bitcoin.h> #include <test/test_bitcoin.h>
#include <interfaces/chain.h>
#include <interfaces/wallet.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
#include <memory> #include <memory>
@ -17,6 +19,7 @@ struct WalletTestingSetup: public TestingSetup {
explicit WalletTestingSetup(const std::string& chainName = CBaseChainParams::MAIN); explicit WalletTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
~WalletTestingSetup(); ~WalletTestingSetup();
std::unique_ptr<interfaces::Chain> m_chain = interfaces::MakeChain();
CWallet m_wallet; CWallet m_wallet;
}; };

View File

@ -11,6 +11,7 @@
#include <vector> #include <vector>
#include <consensus/validation.h> #include <consensus/validation.h>
#include <interfaces/chain.h>
#include <rpc/server.h> #include <rpc/server.h>
#include <test/test_bitcoin.h> #include <test/test_bitcoin.h>
#include <validation.h> #include <validation.h>
@ -34,6 +35,8 @@ static void AddKey(CWallet& wallet, const CKey& key)
BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
{ {
auto chain = interfaces::MakeChain();
// Cap last block file size, and mine new block in a new block file. // Cap last block file size, and mine new block in a new block file.
CBlockIndex* const nullBlock = nullptr; CBlockIndex* const nullBlock = nullptr;
CBlockIndex* oldTip = chainActive.Tip(); CBlockIndex* oldTip = chainActive.Tip();
@ -41,12 +44,12 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
CBlockIndex* newTip = chainActive.Tip(); CBlockIndex* newTip = chainActive.Tip();
LOCK(cs_main); auto locked_chain = chain->lock();
// Verify ScanForWalletTransactions picks up transactions in both the old // Verify ScanForWalletTransactions picks up transactions in both the old
// and new block files. // and new block files.
{ {
CWallet wallet(WalletLocation(), WalletDatabase::CreateDummy()); CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy());
AddKey(wallet, coinbaseKey); AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet); WalletRescanReserver reserver(&wallet);
reserver.reserve(); reserver.reserve();
@ -61,7 +64,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
// Verify ScanForWalletTransactions only picks transactions in the new block // Verify ScanForWalletTransactions only picks transactions in the new block
// file. // file.
{ {
CWallet wallet(WalletLocation(), WalletDatabase::CreateDummy()); CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy());
AddKey(wallet, coinbaseKey); AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet); WalletRescanReserver reserver(&wallet);
reserver.reserve(); reserver.reserve();
@ -73,7 +76,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
// before the missing block, and success for a key whose creation time is // before the missing block, and success for a key whose creation time is
// after. // after.
{ {
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(WalletLocation(), WalletDatabase::CreateDummy()); std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateDummy());
AddWallet(wallet); AddWallet(wallet);
UniValue keys; UniValue keys;
keys.setArray(); keys.setArray();
@ -115,6 +118,8 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
// than or equal to key birthday. // than or equal to key birthday.
BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
{ {
auto chain = interfaces::MakeChain();
// Create two blocks with same timestamp to verify that importwallet rescan // Create two blocks with same timestamp to verify that importwallet rescan
// will pick up both blocks, not just the first. // will pick up both blocks, not just the first.
const int64_t BLOCK_TIME = chainActive.Tip()->GetBlockTimeMax() + 5; const int64_t BLOCK_TIME = chainActive.Tip()->GetBlockTimeMax() + 5;
@ -128,13 +133,13 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
SetMockTime(KEY_TIME); SetMockTime(KEY_TIME);
m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]); m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
LOCK(cs_main); auto locked_chain = chain->lock();
std::string backup_file = (SetDataDir("importwallet_rescan") / "wallet.backup").string(); std::string backup_file = (SetDataDir("importwallet_rescan") / "wallet.backup").string();
// Import key into wallet and call dumpwallet to create backup file. // Import key into wallet and call dumpwallet to create backup file.
{ {
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(WalletLocation(), WalletDatabase::CreateDummy()); std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateDummy());
LOCK(wallet->cs_wallet); LOCK(wallet->cs_wallet);
wallet->mapKeyMetadata[coinbaseKey.GetPubKey().GetID()].nCreateTime = KEY_TIME; wallet->mapKeyMetadata[coinbaseKey.GetPubKey().GetID()].nCreateTime = KEY_TIME;
wallet->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); wallet->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
@ -150,7 +155,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
// Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME // Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME
// were scanned, and no prior blocks were scanned. // were scanned, and no prior blocks were scanned.
{ {
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(WalletLocation(), WalletDatabase::CreateDummy()); std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateDummy());
JSONRPCRequest request; JSONRPCRequest request;
request.params.setArray(); request.params.setArray();
@ -180,21 +185,23 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
// debit functions. // debit functions.
BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup) BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
{ {
CWallet wallet(WalletLocation(), WalletDatabase::CreateDummy()); auto chain = interfaces::MakeChain();
CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy());
CWalletTx wtx(&wallet, m_coinbase_txns.back()); CWalletTx wtx(&wallet, m_coinbase_txns.back());
LOCK2(cs_main, wallet.cs_wallet); auto locked_chain = chain->lock();
LOCK(wallet.cs_wallet);
wtx.hashBlock = chainActive.Tip()->GetBlockHash(); wtx.hashBlock = chainActive.Tip()->GetBlockHash();
wtx.nIndex = 0; wtx.nIndex = 0;
// Call GetImmatureCredit() once before adding the key to the wallet to // Call GetImmatureCredit() once before adding the key to the wallet to
// cache the current immature credit amount, which is 0. // cache the current immature credit amount, which is 0.
BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(), 0); BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(*locked_chain), 0);
// Invalidate the cached value, add the key, and make sure a new immature // Invalidate the cached value, add the key, and make sure a new immature
// credit amount is calculated. // credit amount is calculated.
wtx.MarkDirty(); wtx.MarkDirty();
wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(), 50*COIN); BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(*locked_chain), 50*COIN);
} }
static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64_t blockTime) static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64_t blockTime)
@ -204,7 +211,7 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64
SetMockTime(mockTime); SetMockTime(mockTime);
CBlockIndex* block = nullptr; CBlockIndex* block = nullptr;
if (blockTime > 0) { if (blockTime > 0) {
LOCK(cs_main); auto locked_chain = wallet.chain().lock();
auto inserted = mapBlockIndex.emplace(GetRandHash(), new CBlockIndex); auto inserted = mapBlockIndex.emplace(GetRandHash(), new CBlockIndex);
assert(inserted.second); assert(inserted.second);
const uint256& hash = inserted.first->first; const uint256& hash = inserted.first->first;
@ -273,7 +280,7 @@ public:
ListCoinsTestingSetup() ListCoinsTestingSetup()
{ {
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
wallet = MakeUnique<CWallet>(WalletLocation(), WalletDatabase::CreateMock()); wallet = MakeUnique<CWallet>(*m_chain, WalletLocation(), WalletDatabase::CreateMock());
bool firstRun; bool firstRun;
wallet->LoadWallet(firstRun); wallet->LoadWallet(firstRun);
AddKey(*wallet, coinbaseKey); AddKey(*wallet, coinbaseKey);
@ -295,7 +302,7 @@ public:
int changePos = -1; int changePos = -1;
std::string error; std::string error;
CCoinControl dummy; CCoinControl dummy;
BOOST_CHECK(wallet->CreateTransaction({recipient}, tx, reservekey, fee, changePos, error, dummy)); BOOST_CHECK(wallet->CreateTransaction(*m_locked_chain, {recipient}, tx, reservekey, fee, changePos, error, dummy));
CValidationState state; CValidationState state;
BOOST_CHECK(wallet->CommitTransaction(tx, {}, {}, reservekey, nullptr, state)); BOOST_CHECK(wallet->CommitTransaction(tx, {}, {}, reservekey, nullptr, state));
CMutableTransaction blocktx; CMutableTransaction blocktx;
@ -311,6 +318,8 @@ public:
return it->second; return it->second;
} }
std::unique_ptr<interfaces::Chain> m_chain = interfaces::MakeChain();
std::unique_ptr<interfaces::Chain::Lock> m_locked_chain = m_chain->assumeLocked(); // Temporary. Removed in upcoming lock cleanup
std::unique_ptr<CWallet> wallet; std::unique_ptr<CWallet> wallet;
}; };
@ -323,7 +332,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
std::map<CTxDestination, std::vector<COutput>> list; std::map<CTxDestination, std::vector<COutput>> list;
{ {
LOCK2(cs_main, wallet->cs_wallet); LOCK2(cs_main, wallet->cs_wallet);
list = wallet->ListCoins(); list = wallet->ListCoins(*m_locked_chain);
} }
BOOST_CHECK_EQUAL(list.size(), 1U); BOOST_CHECK_EQUAL(list.size(), 1U);
BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress); BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress);
@ -339,7 +348,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
AddTx(CRecipient{GetScriptForRawPubKey({}), 1 * COIN, false /* subtract fee */}); AddTx(CRecipient{GetScriptForRawPubKey({}), 1 * COIN, false /* subtract fee */});
{ {
LOCK2(cs_main, wallet->cs_wallet); LOCK2(cs_main, wallet->cs_wallet);
list = wallet->ListCoins(); list = wallet->ListCoins(*m_locked_chain);
} }
BOOST_CHECK_EQUAL(list.size(), 1U); BOOST_CHECK_EQUAL(list.size(), 1U);
BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress); BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress);
@ -349,7 +358,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
{ {
LOCK2(cs_main, wallet->cs_wallet); LOCK2(cs_main, wallet->cs_wallet);
std::vector<COutput> available; std::vector<COutput> available;
wallet->AvailableCoins(available); wallet->AvailableCoins(*m_locked_chain, available);
BOOST_CHECK_EQUAL(available.size(), 2U); BOOST_CHECK_EQUAL(available.size(), 2U);
} }
for (const auto& group : list) { for (const auto& group : list) {
@ -361,14 +370,14 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
{ {
LOCK2(cs_main, wallet->cs_wallet); LOCK2(cs_main, wallet->cs_wallet);
std::vector<COutput> available; std::vector<COutput> available;
wallet->AvailableCoins(available); wallet->AvailableCoins(*m_locked_chain, available);
BOOST_CHECK_EQUAL(available.size(), 0U); BOOST_CHECK_EQUAL(available.size(), 0U);
} }
// Confirm ListCoins still returns same result as before, despite coins // Confirm ListCoins still returns same result as before, despite coins
// being locked. // being locked.
{ {
LOCK2(cs_main, wallet->cs_wallet); LOCK2(cs_main, wallet->cs_wallet);
list = wallet->ListCoins(); list = wallet->ListCoins(*m_locked_chain);
} }
BOOST_CHECK_EQUAL(list.size(), 1U); BOOST_CHECK_EQUAL(list.size(), 1U);
BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress); BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress);
@ -377,7 +386,8 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup) BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup)
{ {
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(WalletLocation(), WalletDatabase::CreateDummy()); auto chain = interfaces::MakeChain();
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateDummy());
wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS); wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
BOOST_CHECK(!wallet->TopUpKeyPool(1000)); BOOST_CHECK(!wallet->TopUpKeyPool(1000));
CPubKey pubkey; CPubKey pubkey;

View File

@ -11,6 +11,7 @@
#include <consensus/consensus.h> #include <consensus/consensus.h>
#include <consensus/validation.h> #include <consensus/validation.h>
#include <fs.h> #include <fs.h>
#include <interfaces/chain.h>
#include <key.h> #include <key.h>
#include <key_io.h> #include <key_io.h>
#include <keystore.h> #include <keystore.h>
@ -593,7 +594,7 @@ void CWallet::SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator> ran
* Outpoint is spent if any non-conflicted transaction * Outpoint is spent if any non-conflicted transaction
* spends it: * spends it:
*/ */
bool CWallet::IsSpent(const uint256& hash, unsigned int n) const bool CWallet::IsSpent(interfaces::Chain::Lock& locked_chain, const uint256& hash, unsigned int n) const
{ {
const COutPoint outpoint(hash, n); const COutPoint outpoint(hash, n);
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range; std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range;
@ -604,7 +605,7 @@ bool CWallet::IsSpent(const uint256& hash, unsigned int n) const
const uint256& wtxid = it->second; const uint256& wtxid = it->second;
std::map<uint256, CWalletTx>::const_iterator mit = mapWallet.find(wtxid); std::map<uint256, CWalletTx>::const_iterator mit = mapWallet.find(wtxid);
if (mit != mapWallet.end()) { if (mit != mapWallet.end()) {
int depth = mit->second.GetDepthInMainChain(); int depth = mit->second.GetDepthInMainChain(locked_chain);
if (depth > 0 || (depth == 0 && !mit->second.isAbandoned())) if (depth > 0 || (depth == 0 && !mit->second.isAbandoned()))
return true; // Spent return true; // Spent
} }
@ -1005,9 +1006,10 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const CBlockI
bool CWallet::TransactionCanBeAbandoned(const uint256& hashTx) const bool CWallet::TransactionCanBeAbandoned(const uint256& hashTx) const
{ {
LOCK2(cs_main, cs_wallet); auto locked_chain = chain().lock();
LOCK(cs_wallet);
const CWalletTx* wtx = GetWalletTx(hashTx); const CWalletTx* wtx = GetWalletTx(hashTx);
return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain() == 0 && !wtx->InMempool(); return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain(*locked_chain) == 0 && !wtx->InMempool();
} }
void CWallet::MarkInputsDirty(const CTransactionRef& tx) void CWallet::MarkInputsDirty(const CTransactionRef& tx)
@ -1020,9 +1022,10 @@ void CWallet::MarkInputsDirty(const CTransactionRef& tx)
} }
} }
bool CWallet::AbandonTransaction(const uint256& hashTx) bool CWallet::AbandonTransaction(interfaces::Chain::Lock& locked_chain, const uint256& hashTx)
{ {
LOCK2(cs_main, cs_wallet); auto locked_chain_recursive = chain().lock(); // Temporary. Removed in upcoming lock cleanup
LOCK(cs_wallet);
WalletBatch batch(*database, "r+"); WalletBatch batch(*database, "r+");
@ -1033,7 +1036,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
auto it = mapWallet.find(hashTx); auto it = mapWallet.find(hashTx);
assert(it != mapWallet.end()); assert(it != mapWallet.end());
CWalletTx& origtx = it->second; CWalletTx& origtx = it->second;
if (origtx.GetDepthInMainChain() != 0 || origtx.InMempool()) { if (origtx.GetDepthInMainChain(locked_chain) != 0 || origtx.InMempool()) {
return false; return false;
} }
@ -1046,7 +1049,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
auto it = mapWallet.find(now); auto it = mapWallet.find(now);
assert(it != mapWallet.end()); assert(it != mapWallet.end());
CWalletTx& wtx = it->second; CWalletTx& wtx = it->second;
int currentconfirm = wtx.GetDepthInMainChain(); int currentconfirm = wtx.GetDepthInMainChain(locked_chain);
// If the orig tx was not in block, none of its spends can be // If the orig tx was not in block, none of its spends can be
assert(currentconfirm <= 0); assert(currentconfirm <= 0);
// if (currentconfirm < 0) {Tx and spends are already conflicted, no need to abandon} // if (currentconfirm < 0) {Tx and spends are already conflicted, no need to abandon}
@ -1077,7 +1080,8 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx) void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
{ {
LOCK2(cs_main, cs_wallet); auto locked_chain = chain().lock();
LOCK(cs_wallet);
int conflictconfirms = 0; int conflictconfirms = 0;
CBlockIndex* pindex = LookupBlockIndex(hashBlock); CBlockIndex* pindex = LookupBlockIndex(hashBlock);
@ -1106,7 +1110,7 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
auto it = mapWallet.find(now); auto it = mapWallet.find(now);
assert(it != mapWallet.end()); assert(it != mapWallet.end());
CWalletTx& wtx = it->second; CWalletTx& wtx = it->second;
int currentconfirm = wtx.GetDepthInMainChain(); int currentconfirm = wtx.GetDepthInMainChain(*locked_chain);
if (conflictconfirms < currentconfirm) { if (conflictconfirms < currentconfirm) {
// Block is 'more conflicted' than current confirm; update. // Block is 'more conflicted' than current confirm; update.
// Mark transaction as conflicted with this block. // Mark transaction as conflicted with this block.
@ -1140,7 +1144,8 @@ void CWallet::SyncTransaction(const CTransactionRef& ptx, const CBlockIndex *pin
} }
void CWallet::TransactionAddedToMempool(const CTransactionRef& ptx) { void CWallet::TransactionAddedToMempool(const CTransactionRef& ptx) {
LOCK2(cs_main, cs_wallet); auto locked_chain = chain().lock();
LOCK(cs_wallet);
SyncTransaction(ptx); SyncTransaction(ptx);
auto it = mapWallet.find(ptx->GetHash()); auto it = mapWallet.find(ptx->GetHash());
@ -1158,7 +1163,8 @@ void CWallet::TransactionRemovedFromMempool(const CTransactionRef &ptx) {
} }
void CWallet::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) { void CWallet::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) {
LOCK2(cs_main, cs_wallet); auto locked_chain = chain().lock();
LOCK(cs_wallet);
// TODO: Temporarily ensure that mempool removals are notified before // TODO: Temporarily ensure that mempool removals are notified before
// connected transactions. This shouldn't matter, but the abandoned // connected transactions. This shouldn't matter, but the abandoned
// state of transactions in our wallet is currently cleared when we // state of transactions in our wallet is currently cleared when we
@ -1180,7 +1186,8 @@ void CWallet::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const
} }
void CWallet::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) { void CWallet::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) {
LOCK2(cs_main, cs_wallet); auto locked_chain = chain().lock();
LOCK(cs_wallet);
for (const CTransactionRef& ptx : pblock->vtx) { for (const CTransactionRef& ptx : pblock->vtx) {
SyncTransaction(ptx); SyncTransaction(ptx);
@ -1199,7 +1206,7 @@ void CWallet::BlockUntilSyncedToCurrentChain() {
// We could also take cs_wallet here, and call m_last_block_processed // We could also take cs_wallet here, and call m_last_block_processed
// protected by cs_wallet instead of cs_main, but as long as we need // protected by cs_wallet instead of cs_main, but as long as we need
// cs_main here anyway, it's easier to just call it cs_main-protected. // cs_main here anyway, it's easier to just call it cs_main-protected.
LOCK(cs_main); auto locked_chain = chain().lock();
const CBlockIndex* initialChainTip = chainActive.Tip(); const CBlockIndex* initialChainTip = chainActive.Tip();
if (m_last_block_processed && m_last_block_processed->GetAncestor(initialChainTip->nHeight) == initialChainTip) { if (m_last_block_processed && m_last_block_processed->GetAncestor(initialChainTip->nHeight) == initialChainTip) {
@ -1600,7 +1607,7 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r
// to be scanned. // to be scanned.
CBlockIndex* startBlock = nullptr; CBlockIndex* startBlock = nullptr;
{ {
LOCK(cs_main); auto locked_chain = chain().lock();
startBlock = chainActive.FindEarliestAtLeast(startTime - TIMESTAMP_WINDOW); startBlock = chainActive.FindEarliestAtLeast(startTime - TIMESTAMP_WINDOW);
WalletLogPrintf("%s: Rescanning last %i blocks\n", __func__, startBlock ? chainActive.Height() - startBlock->nHeight + 1 : 0); WalletLogPrintf("%s: Rescanning last %i blocks\n", __func__, startBlock ? chainActive.Height() - startBlock->nHeight + 1 : 0);
} }
@ -1652,7 +1659,7 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock
double progress_begin; double progress_begin;
double progress_end; double progress_end;
{ {
LOCK(cs_main); auto locked_chain = chain().lock();
progress_begin = GuessVerificationProgress(chainParams.TxData(), pindex); progress_begin = GuessVerificationProgress(chainParams.TxData(), pindex);
if (pindexStop == nullptr) { if (pindexStop == nullptr) {
tip = chainActive.Tip(); tip = chainActive.Tip();
@ -1674,7 +1681,8 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock
CBlock block; CBlock block;
if (ReadBlockFromDisk(block, pindex, Params().GetConsensus())) { if (ReadBlockFromDisk(block, pindex, Params().GetConsensus())) {
LOCK2(cs_main, cs_wallet); auto locked_chain = chain().lock();
LOCK(cs_wallet);
if (pindex && !chainActive.Contains(pindex)) { if (pindex && !chainActive.Contains(pindex)) {
// Abort scan if current block is no longer active, to prevent // Abort scan if current block is no longer active, to prevent
// marking transactions as coming from the wrong block. // marking transactions as coming from the wrong block.
@ -1691,7 +1699,7 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock
break; break;
} }
{ {
LOCK(cs_main); auto locked_chain = chain().lock();
pindex = chainActive.Next(pindex); pindex = chainActive.Next(pindex);
progress_current = GuessVerificationProgress(chainParams.TxData(), pindex); progress_current = GuessVerificationProgress(chainParams.TxData(), pindex);
if (pindexStop == nullptr && tip != chainActive.Tip()) { if (pindexStop == nullptr && tip != chainActive.Tip()) {
@ -1716,7 +1724,8 @@ void CWallet::ReacceptWalletTransactions()
// If transactions aren't being broadcasted, don't let them into local mempool either // If transactions aren't being broadcasted, don't let them into local mempool either
if (!fBroadcastTransactions) if (!fBroadcastTransactions)
return; return;
LOCK2(cs_main, cs_wallet); auto locked_chain = chain().lock();
LOCK(cs_wallet);
std::map<int64_t, CWalletTx*> mapSorted; std::map<int64_t, CWalletTx*> mapSorted;
// Sort pending wallet transactions based on their initial wallet insertion order // Sort pending wallet transactions based on their initial wallet insertion order
@ -1726,7 +1735,7 @@ void CWallet::ReacceptWalletTransactions()
CWalletTx& wtx = item.second; CWalletTx& wtx = item.second;
assert(wtx.GetHash() == wtxid); assert(wtx.GetHash() == wtxid);
int nDepth = wtx.GetDepthInMainChain(); int nDepth = wtx.GetDepthInMainChain(*locked_chain);
if (!wtx.IsCoinBase() && (nDepth == 0 && !wtx.isAbandoned())) { if (!wtx.IsCoinBase() && (nDepth == 0 && !wtx.isAbandoned())) {
mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx)); mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx));
@ -1737,18 +1746,18 @@ void CWallet::ReacceptWalletTransactions()
for (const std::pair<const int64_t, CWalletTx*>& item : mapSorted) { for (const std::pair<const int64_t, CWalletTx*>& item : mapSorted) {
CWalletTx& wtx = *(item.second); CWalletTx& wtx = *(item.second);
CValidationState state; CValidationState state;
wtx.AcceptToMemoryPool(maxTxFee, state); wtx.AcceptToMemoryPool(*locked_chain, maxTxFee, state);
} }
} }
bool CWalletTx::RelayWalletTransaction(CConnman* connman) bool CWalletTx::RelayWalletTransaction(interfaces::Chain::Lock& locked_chain, CConnman* connman)
{ {
assert(pwallet->GetBroadcastTransactions()); assert(pwallet->GetBroadcastTransactions());
if (!IsCoinBase() && !isAbandoned() && GetDepthInMainChain() == 0) if (!IsCoinBase() && !isAbandoned() && GetDepthInMainChain(locked_chain) == 0)
{ {
CValidationState state; CValidationState state;
/* GetDepthInMainChain already catches known conflicts. */ /* GetDepthInMainChain already catches known conflicts. */
if (InMempool() || AcceptToMemoryPool(maxTxFee, state)) { if (InMempool() || AcceptToMemoryPool(locked_chain, maxTxFee, state)) {
pwallet->WalletLogPrintf("Relaying wtx %s\n", GetHash().ToString()); pwallet->WalletLogPrintf("Relaying wtx %s\n", GetHash().ToString());
if (connman) { if (connman) {
CInv inv(MSG_TX, GetHash()); CInv inv(MSG_TX, GetHash());
@ -1806,10 +1815,10 @@ CAmount CWalletTx::GetDebit(const isminefilter& filter) const
return debit; return debit;
} }
CAmount CWalletTx::GetCredit(const isminefilter& filter) const CAmount CWalletTx::GetCredit(interfaces::Chain::Lock& locked_chain, const isminefilter& filter) const
{ {
// Must wait until coinbase is safely deep enough in the chain before valuing it // Must wait until coinbase is safely deep enough in the chain before valuing it
if (IsImmatureCoinBase()) if (IsImmatureCoinBase(locked_chain))
return 0; return 0;
CAmount credit = 0; CAmount credit = 0;
@ -1839,9 +1848,9 @@ CAmount CWalletTx::GetCredit(const isminefilter& filter) const
return credit; return credit;
} }
CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const CAmount CWalletTx::GetImmatureCredit(interfaces::Chain::Lock& locked_chain, bool fUseCache) const
{ {
if (IsImmatureCoinBase() && IsInMainChain()) { if (IsImmatureCoinBase(locked_chain) && IsInMainChain(locked_chain)) {
if (fUseCache && fImmatureCreditCached) if (fUseCache && fImmatureCreditCached)
return nImmatureCreditCached; return nImmatureCreditCached;
nImmatureCreditCached = pwallet->GetCredit(*tx, ISMINE_SPENDABLE); nImmatureCreditCached = pwallet->GetCredit(*tx, ISMINE_SPENDABLE);
@ -1852,13 +1861,13 @@ CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const
return 0; return 0;
} }
CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter) const CAmount CWalletTx::GetAvailableCredit(interfaces::Chain::Lock& locked_chain, bool fUseCache, const isminefilter& filter) const
{ {
if (pwallet == nullptr) if (pwallet == nullptr)
return 0; return 0;
// Must wait until coinbase is safely deep enough in the chain before valuing it // Must wait until coinbase is safely deep enough in the chain before valuing it
if (IsImmatureCoinBase()) if (IsImmatureCoinBase(locked_chain))
return 0; return 0;
CAmount* cache = nullptr; CAmount* cache = nullptr;
@ -1880,7 +1889,7 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter
uint256 hashTx = GetHash(); uint256 hashTx = GetHash();
for (unsigned int i = 0; i < tx->vout.size(); i++) for (unsigned int i = 0; i < tx->vout.size(); i++)
{ {
if (!pwallet->IsSpent(hashTx, i)) if (!pwallet->IsSpent(locked_chain, hashTx, i))
{ {
const CTxOut &txout = tx->vout[i]; const CTxOut &txout = tx->vout[i];
nCredit += pwallet->GetCredit(txout, filter); nCredit += pwallet->GetCredit(txout, filter);
@ -1897,9 +1906,9 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter
return nCredit; return nCredit;
} }
CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool fUseCache) const CAmount CWalletTx::GetImmatureWatchOnlyCredit(interfaces::Chain::Lock& locked_chain, const bool fUseCache) const
{ {
if (IsImmatureCoinBase() && IsInMainChain()) { if (IsImmatureCoinBase(locked_chain) && IsInMainChain(locked_chain)) {
if (fUseCache && fImmatureWatchCreditCached) if (fUseCache && fImmatureWatchCreditCached)
return nImmatureWatchCreditCached; return nImmatureWatchCreditCached;
nImmatureWatchCreditCached = pwallet->GetCredit(*tx, ISMINE_WATCH_ONLY); nImmatureWatchCreditCached = pwallet->GetCredit(*tx, ISMINE_WATCH_ONLY);
@ -1924,12 +1933,14 @@ bool CWalletTx::InMempool() const
return fInMempool; return fInMempool;
} }
bool CWalletTx::IsTrusted() const bool CWalletTx::IsTrusted(interfaces::Chain::Lock& locked_chain) const
{ {
LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
// Quick answer in most cases // Quick answer in most cases
if (!CheckFinalTx(*tx)) if (!CheckFinalTx(*tx))
return false; return false;
int nDepth = GetDepthInMainChain(); int nDepth = GetDepthInMainChain(locked_chain);
if (nDepth >= 1) if (nDepth >= 1)
return true; return true;
if (nDepth < 0) if (nDepth < 0)
@ -1964,7 +1975,7 @@ bool CWalletTx::IsEquivalentTo(const CWalletTx& _tx) const
return CTransaction(tx1) == CTransaction(tx2); return CTransaction(tx1) == CTransaction(tx2);
} }
std::vector<uint256> CWallet::ResendWalletTransactionsBefore(int64_t nTime, CConnman* connman) std::vector<uint256> CWallet::ResendWalletTransactionsBefore(interfaces::Chain::Lock& locked_chain, int64_t nTime, CConnman* connman)
{ {
std::vector<uint256> result; std::vector<uint256> result;
@ -1983,7 +1994,7 @@ std::vector<uint256> CWallet::ResendWalletTransactionsBefore(int64_t nTime, CCon
for (const std::pair<const unsigned int, CWalletTx*>& item : mapSorted) for (const std::pair<const unsigned int, CWalletTx*>& item : mapSorted)
{ {
CWalletTx& wtx = *item.second; CWalletTx& wtx = *item.second;
if (wtx.RelayWalletTransaction(connman)) if (wtx.RelayWalletTransaction(locked_chain, connman))
result.push_back(wtx.GetHash()); result.push_back(wtx.GetHash());
} }
return result; return result;
@ -2007,7 +2018,8 @@ void CWallet::ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman
// Rebroadcast unconfirmed txes older than 5 minutes before the last // Rebroadcast unconfirmed txes older than 5 minutes before the last
// block was found: // block was found:
std::vector<uint256> relayed = ResendWalletTransactionsBefore(nBestBlockTime-5*60, connman); auto locked_chain = chain().assumeLocked(); // Temporary. Removed in upcoming lock cleanup
std::vector<uint256> relayed = ResendWalletTransactionsBefore(*locked_chain, nBestBlockTime-5*60, connman);
if (!relayed.empty()) if (!relayed.empty())
WalletLogPrintf("%s: rebroadcast %u unconfirmed transactions\n", __func__, relayed.size()); WalletLogPrintf("%s: rebroadcast %u unconfirmed transactions\n", __func__, relayed.size());
} }
@ -2027,12 +2039,13 @@ CAmount CWallet::GetBalance(const isminefilter& filter, const int min_depth) con
{ {
CAmount nTotal = 0; CAmount nTotal = 0;
{ {
LOCK2(cs_main, cs_wallet); auto locked_chain = chain().lock();
LOCK(cs_wallet);
for (const auto& entry : mapWallet) for (const auto& entry : mapWallet)
{ {
const CWalletTx* pcoin = &entry.second; const CWalletTx* pcoin = &entry.second;
if (pcoin->IsTrusted() && pcoin->GetDepthInMainChain() >= min_depth) { if (pcoin->IsTrusted(*locked_chain) && pcoin->GetDepthInMainChain(*locked_chain) >= min_depth) {
nTotal += pcoin->GetAvailableCredit(true, filter); nTotal += pcoin->GetAvailableCredit(*locked_chain, true, filter);
} }
} }
} }
@ -2044,12 +2057,13 @@ CAmount CWallet::GetUnconfirmedBalance() const
{ {
CAmount nTotal = 0; CAmount nTotal = 0;
{ {
LOCK2(cs_main, cs_wallet); auto locked_chain = chain().lock();
LOCK(cs_wallet);
for (const auto& entry : mapWallet) for (const auto& entry : mapWallet)
{ {
const CWalletTx* pcoin = &entry.second; const CWalletTx* pcoin = &entry.second;
if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && pcoin->InMempool()) if (!pcoin->IsTrusted(*locked_chain) && pcoin->GetDepthInMainChain(*locked_chain) == 0 && pcoin->InMempool())
nTotal += pcoin->GetAvailableCredit(); nTotal += pcoin->GetAvailableCredit(*locked_chain);
} }
} }
return nTotal; return nTotal;
@ -2059,11 +2073,12 @@ CAmount CWallet::GetImmatureBalance() const
{ {
CAmount nTotal = 0; CAmount nTotal = 0;
{ {
LOCK2(cs_main, cs_wallet); auto locked_chain = chain().lock();
LOCK(cs_wallet);
for (const auto& entry : mapWallet) for (const auto& entry : mapWallet)
{ {
const CWalletTx* pcoin = &entry.second; const CWalletTx* pcoin = &entry.second;
nTotal += pcoin->GetImmatureCredit(); nTotal += pcoin->GetImmatureCredit(*locked_chain);
} }
} }
return nTotal; return nTotal;
@ -2073,12 +2088,13 @@ CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const
{ {
CAmount nTotal = 0; CAmount nTotal = 0;
{ {
LOCK2(cs_main, cs_wallet); auto locked_chain = chain().lock();
LOCK(cs_wallet);
for (const auto& entry : mapWallet) for (const auto& entry : mapWallet)
{ {
const CWalletTx* pcoin = &entry.second; const CWalletTx* pcoin = &entry.second;
if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && pcoin->InMempool()) if (!pcoin->IsTrusted(*locked_chain) && pcoin->GetDepthInMainChain(*locked_chain) == 0 && pcoin->InMempool())
nTotal += pcoin->GetAvailableCredit(true, ISMINE_WATCH_ONLY); nTotal += pcoin->GetAvailableCredit(*locked_chain, true, ISMINE_WATCH_ONLY);
} }
} }
return nTotal; return nTotal;
@ -2088,11 +2104,12 @@ CAmount CWallet::GetImmatureWatchOnlyBalance() const
{ {
CAmount nTotal = 0; CAmount nTotal = 0;
{ {
LOCK2(cs_main, cs_wallet); auto locked_chain = chain().lock();
LOCK(cs_wallet);
for (const auto& entry : mapWallet) for (const auto& entry : mapWallet)
{ {
const CWalletTx* pcoin = &entry.second; const CWalletTx* pcoin = &entry.second;
nTotal += pcoin->GetImmatureWatchOnlyCredit(); nTotal += pcoin->GetImmatureWatchOnlyCredit(*locked_chain);
} }
} }
return nTotal; return nTotal;
@ -2106,13 +2123,15 @@ CAmount CWallet::GetImmatureWatchOnlyBalance() const
// trusted. // trusted.
CAmount CWallet::GetLegacyBalance(const isminefilter& filter, int minDepth) const CAmount CWallet::GetLegacyBalance(const isminefilter& filter, int minDepth) const
{ {
LOCK2(cs_main, cs_wallet); LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
auto locked_chain = chain().lock();
LOCK(cs_wallet);
CAmount balance = 0; CAmount balance = 0;
for (const auto& entry : mapWallet) { for (const auto& entry : mapWallet) {
const CWalletTx& wtx = entry.second; const CWalletTx& wtx = entry.second;
const int depth = wtx.GetDepthInMainChain(); const int depth = wtx.GetDepthInMainChain(*locked_chain);
if (depth < 0 || !CheckFinalTx(*wtx.tx) || wtx.IsImmatureCoinBase()) { if (depth < 0 || !CheckFinalTx(*wtx.tx) || wtx.IsImmatureCoinBase(*locked_chain)) {
continue; continue;
} }
@ -2139,11 +2158,12 @@ CAmount CWallet::GetLegacyBalance(const isminefilter& filter, int minDepth) cons
CAmount CWallet::GetAvailableBalance(const CCoinControl* coinControl) const CAmount CWallet::GetAvailableBalance(const CCoinControl* coinControl) const
{ {
LOCK2(cs_main, cs_wallet); auto locked_chain = chain().lock();
LOCK(cs_wallet);
CAmount balance = 0; CAmount balance = 0;
std::vector<COutput> vCoins; std::vector<COutput> vCoins;
AvailableCoins(vCoins, true, coinControl); AvailableCoins(*locked_chain, vCoins, true, coinControl);
for (const COutput& out : vCoins) { for (const COutput& out : vCoins) {
if (out.fSpendable) { if (out.fSpendable) {
balance += out.tx->tx->vout[out.i].nValue; balance += out.tx->tx->vout[out.i].nValue;
@ -2152,7 +2172,7 @@ CAmount CWallet::GetAvailableBalance(const CCoinControl* coinControl) const
return balance; return balance;
} }
void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const CCoinControl *coinControl, const CAmount &nMinimumAmount, const CAmount &nMaximumAmount, const CAmount &nMinimumSumAmount, const uint64_t nMaximumCount, const int nMinDepth, const int nMaxDepth) const void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<COutput> &vCoins, bool fOnlySafe, const CCoinControl *coinControl, const CAmount &nMinimumAmount, const CAmount &nMaximumAmount, const CAmount &nMinimumSumAmount, const uint64_t nMaximumCount, const int nMinDepth, const int nMaxDepth) const
{ {
AssertLockHeld(cs_main); AssertLockHeld(cs_main);
AssertLockHeld(cs_wallet); AssertLockHeld(cs_wallet);
@ -2168,10 +2188,10 @@ void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const
if (!CheckFinalTx(*pcoin->tx)) if (!CheckFinalTx(*pcoin->tx))
continue; continue;
if (pcoin->IsImmatureCoinBase()) if (pcoin->IsImmatureCoinBase(locked_chain))
continue; continue;
int nDepth = pcoin->GetDepthInMainChain(); int nDepth = pcoin->GetDepthInMainChain(locked_chain);
if (nDepth < 0) if (nDepth < 0)
continue; continue;
@ -2180,7 +2200,7 @@ void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const
if (nDepth == 0 && !pcoin->InMempool()) if (nDepth == 0 && !pcoin->InMempool())
continue; continue;
bool safeTx = pcoin->IsTrusted(); bool safeTx = pcoin->IsTrusted(locked_chain);
// We should not consider coins from transactions that are replacing // We should not consider coins from transactions that are replacing
// other transactions. // other transactions.
@ -2230,7 +2250,7 @@ void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const
if (IsLockedCoin(entry.first, i)) if (IsLockedCoin(entry.first, i))
continue; continue;
if (IsSpent(wtxid, i)) if (IsSpent(locked_chain, wtxid, i))
continue; continue;
isminetype mine = IsMine(pcoin->tx->vout[i]); isminetype mine = IsMine(pcoin->tx->vout[i]);
@ -2261,7 +2281,7 @@ void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const
} }
} }
std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins() const std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins(interfaces::Chain::Lock& locked_chain) const
{ {
AssertLockHeld(cs_main); AssertLockHeld(cs_main);
AssertLockHeld(cs_wallet); AssertLockHeld(cs_wallet);
@ -2269,7 +2289,7 @@ std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins() const
std::map<CTxDestination, std::vector<COutput>> result; std::map<CTxDestination, std::vector<COutput>> result;
std::vector<COutput> availableCoins; std::vector<COutput> availableCoins;
AvailableCoins(availableCoins); AvailableCoins(locked_chain, availableCoins);
for (const COutput& coin : availableCoins) { for (const COutput& coin : availableCoins) {
CTxDestination address; CTxDestination address;
@ -2284,7 +2304,7 @@ std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins() const
for (const COutPoint& output : lockedCoins) { for (const COutPoint& output : lockedCoins) {
auto it = mapWallet.find(output.hash); auto it = mapWallet.find(output.hash);
if (it != mapWallet.end()) { if (it != mapWallet.end()) {
int depth = it->second.GetDepthInMainChain(); int depth = it->second.GetDepthInMainChain(locked_chain);
if (depth >= 0 && output.n < it->second.tx->vout.size() && if (depth >= 0 && output.n < it->second.tx->vout.size() &&
IsMine(it->second.tx->vout[output.n]) == ISMINE_SPENDABLE) { IsMine(it->second.tx->vout[output.n]) == ISMINE_SPENDABLE) {
CTxDestination address; CTxDestination address;
@ -2499,11 +2519,12 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
// Acquire the locks to prevent races to the new locked unspents between the // Acquire the locks to prevent races to the new locked unspents between the
// CreateTransaction call and LockCoin calls (when lockUnspents is true). // CreateTransaction call and LockCoin calls (when lockUnspents is true).
LOCK2(cs_main, cs_wallet); auto locked_chain = chain().lock();
LOCK(cs_wallet);
CReserveKey reservekey(this); CReserveKey reservekey(this);
CTransactionRef tx_new; CTransactionRef tx_new;
if (!CreateTransaction(vecSend, tx_new, reservekey, nFeeRet, nChangePosInOut, strFailReason, coinControl, false)) { if (!CreateTransaction(*locked_chain, vecSend, tx_new, reservekey, nFeeRet, nChangePosInOut, strFailReason, coinControl, false)) {
return false; return false;
} }
@ -2562,7 +2583,7 @@ OutputType CWallet::TransactionChangeType(OutputType change_type, const std::vec
return m_default_address_type; return m_default_address_type;
} }
bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CReserveKey& reservekey, CAmount& nFeeRet, bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CReserveKey& reservekey, CAmount& nFeeRet,
int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign) int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign)
{ {
CAmount nValue = 0; CAmount nValue = 0;
@ -2624,10 +2645,11 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac
int nBytes; int nBytes;
{ {
std::set<CInputCoin> setCoins; std::set<CInputCoin> setCoins;
LOCK2(cs_main, cs_wallet); auto locked_chain = chain().lock();
LOCK(cs_wallet);
{ {
std::vector<COutput> vAvailableCoins; std::vector<COutput> vAvailableCoins;
AvailableCoins(vAvailableCoins, true, &coin_control); AvailableCoins(*locked_chain, vAvailableCoins, true, &coin_control);
CoinSelectionParams coin_selection_params; // Parameters for coin selection, init with dummy CoinSelectionParams coin_selection_params; // Parameters for coin selection, init with dummy
// Create change script that will be used if we need change // Create change script that will be used if we need change
@ -2962,7 +2984,8 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac
bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, CReserveKey& reservekey, CConnman* connman, CValidationState& state) bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, CReserveKey& reservekey, CConnman* connman, CValidationState& state)
{ {
{ {
LOCK2(cs_main, cs_wallet); auto locked_chain = chain().lock();
LOCK(cs_wallet);
CWalletTx wtxNew(this, std::move(tx)); CWalletTx wtxNew(this, std::move(tx));
wtxNew.mapValue = std::move(mapValue); wtxNew.mapValue = std::move(mapValue);
@ -2995,11 +3018,11 @@ bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
if (fBroadcastTransactions) if (fBroadcastTransactions)
{ {
// Broadcast // Broadcast
if (!wtx.AcceptToMemoryPool(maxTxFee, state)) { if (!wtx.AcceptToMemoryPool(*locked_chain, maxTxFee, state)) {
WalletLogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", FormatStateMessage(state)); WalletLogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", FormatStateMessage(state));
// TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure. // TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure.
} else { } else {
wtx.RelayWalletTransaction(connman); wtx.RelayWalletTransaction(*locked_chain, connman);
} }
} }
} }
@ -3008,7 +3031,8 @@ bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
DBErrors CWallet::LoadWallet(bool& fFirstRunRet) DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
{ {
LOCK2(cs_main, cs_wallet); auto locked_chain = chain().lock();
LOCK(cs_wallet);
fFirstRunRet = false; fFirstRunRet = false;
DBErrors nLoadWalletRet = WalletBatch(*database,"cr+").LoadWallet(this); DBErrors nLoadWalletRet = WalletBatch(*database,"cr+").LoadWallet(this);
@ -3392,7 +3416,7 @@ int64_t CWallet::GetOldestKeyPoolTime()
return oldestKey; return oldestKey;
} }
std::map<CTxDestination, CAmount> CWallet::GetAddressBalances() std::map<CTxDestination, CAmount> CWallet::GetAddressBalances(interfaces::Chain::Lock& locked_chain)
{ {
std::map<CTxDestination, CAmount> balances; std::map<CTxDestination, CAmount> balances;
@ -3402,13 +3426,13 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances()
{ {
const CWalletTx *pcoin = &walletEntry.second; const CWalletTx *pcoin = &walletEntry.second;
if (!pcoin->IsTrusted()) if (!pcoin->IsTrusted(locked_chain))
continue; continue;
if (pcoin->IsImmatureCoinBase()) if (pcoin->IsImmatureCoinBase(locked_chain))
continue; continue;
int nDepth = pcoin->GetDepthInMainChain(); int nDepth = pcoin->GetDepthInMainChain(locked_chain);
if (nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1)) if (nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1))
continue; continue;
@ -3420,7 +3444,7 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances()
if(!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, addr)) if(!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, addr))
continue; continue;
CAmount n = IsSpent(walletEntry.first, i) ? 0 : pcoin->tx->vout[i].nValue; CAmount n = IsSpent(locked_chain, walletEntry.first, i) ? 0 : pcoin->tx->vout[i].nValue;
if (!balances.count(addr)) if (!balances.count(addr))
balances[addr] = 0; balances[addr] = 0;
@ -3645,7 +3669,7 @@ void CWallet::ListLockedCoins(std::vector<COutPoint>& vOutpts) const
/** @} */ // end of Actions /** @} */ // end of Actions
void CWallet::GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) const { void CWallet::GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<CTxDestination, int64_t> &mapKeyBirth) const {
AssertLockHeld(cs_wallet); // mapKeyMetadata AssertLockHeld(cs_wallet); // mapKeyMetadata
mapKeyBirth.clear(); mapKeyBirth.clear();
@ -3825,7 +3849,7 @@ void CWallet::MarkPreSplitKeys()
} }
} }
bool CWallet::Verify(const WalletLocation& location, bool salvage_wallet, std::string& error_string, std::string& warning_string) bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, bool salvage_wallet, std::string& error_string, std::string& warning_string)
{ {
// Do some checking on wallet path. It should be either a: // Do some checking on wallet path. It should be either a:
// //
@ -3866,7 +3890,7 @@ bool CWallet::Verify(const WalletLocation& location, bool salvage_wallet, std::s
if (salvage_wallet) { if (salvage_wallet) {
// Recover readable keypairs: // Recover readable keypairs:
CWallet dummyWallet(WalletLocation(), WalletDatabase::CreateDummy()); CWallet dummyWallet(chain, WalletLocation(), WalletDatabase::CreateDummy());
std::string backup_filename; std::string backup_filename;
if (!WalletBatch::Recover(wallet_path, (void *)&dummyWallet, WalletBatch::RecoverKeysOnlyFilter, backup_filename)) { if (!WalletBatch::Recover(wallet_path, (void *)&dummyWallet, WalletBatch::RecoverKeysOnlyFilter, backup_filename)) {
return false; return false;
@ -3876,7 +3900,7 @@ bool CWallet::Verify(const WalletLocation& location, bool salvage_wallet, std::s
return WalletBatch::VerifyDatabaseFile(wallet_path, warning_string, error_string); return WalletBatch::VerifyDatabaseFile(wallet_path, warning_string, error_string);
} }
std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const WalletLocation& location, uint64_t wallet_creation_flags) std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, uint64_t wallet_creation_flags)
{ {
const std::string& walletFile = location.GetName(); const std::string& walletFile = location.GetName();
@ -3886,7 +3910,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const WalletLocation& loc
if (gArgs.GetBoolArg("-zapwallettxes", false)) { if (gArgs.GetBoolArg("-zapwallettxes", false)) {
uiInterface.InitMessage(_("Zapping all transactions from wallet...")); uiInterface.InitMessage(_("Zapping all transactions from wallet..."));
std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(location, WalletDatabase::Create(location.GetPath())); std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(chain, location, WalletDatabase::Create(location.GetPath()));
DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx); DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);
if (nZapWalletRet != DBErrors::LOAD_OK) { if (nZapWalletRet != DBErrors::LOAD_OK) {
InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile)); InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
@ -3900,7 +3924,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const WalletLocation& loc
bool fFirstRun = true; bool fFirstRun = true;
// TODO: Can't use std::make_shared because we need a custom deleter but // TODO: Can't use std::make_shared because we need a custom deleter but
// should be possible to use std::allocate_shared. // should be possible to use std::allocate_shared.
std::shared_ptr<CWallet> walletInstance(new CWallet(location, WalletDatabase::Create(location.GetPath())), ReleaseWallet); std::shared_ptr<CWallet> walletInstance(new CWallet(chain, location, WalletDatabase::Create(location.GetPath())), ReleaseWallet);
DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun); DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun);
if (nLoadWalletRet != DBErrors::LOAD_OK) if (nLoadWalletRet != DBErrors::LOAD_OK)
{ {
@ -4010,6 +4034,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const WalletLocation& loc
return nullptr; return nullptr;
} }
auto locked_chain = chain.assumeLocked(); // Temporary. Removed in upcoming lock cleanup
walletInstance->ChainStateFlushed(chainActive.GetLocator()); walletInstance->ChainStateFlushed(chainActive.GetLocator());
} else if (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS) { } else if (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS) {
// Make it impossible to disable private keys after creation // Make it impossible to disable private keys after creation
@ -4097,7 +4122,9 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const WalletLocation& loc
// Try to top up keypool. No-op if the wallet is locked. // Try to top up keypool. No-op if the wallet is locked.
walletInstance->TopUpKeyPool(); walletInstance->TopUpKeyPool();
LOCK2(cs_main, walletInstance->cs_wallet); LockAnnotation lock(::cs_main); // Temporary, for FindForkInGlobalIndex below. Removed in upcoming commit.
auto locked_chain = chain.lock();
LOCK(walletInstance->cs_wallet);
CBlockIndex *pindexRescan = chainActive.Genesis(); CBlockIndex *pindexRescan = chainActive.Genesis();
if (!gArgs.GetBoolArg("-rescan", false)) if (!gArgs.GetBoolArg("-rescan", false))
@ -4232,7 +4259,7 @@ void CMerkleTx::SetMerkleBranch(const CBlockIndex* pindex, int posInBlock)
nIndex = posInBlock; nIndex = posInBlock;
} }
int CMerkleTx::GetDepthInMainChain() const int CMerkleTx::GetDepthInMainChain(interfaces::Chain::Lock& locked_chain) const
{ {
if (hashUnset()) if (hashUnset())
return 0; return 0;
@ -4247,23 +4274,25 @@ int CMerkleTx::GetDepthInMainChain() const
return ((nIndex == -1) ? (-1) : 1) * (chainActive.Height() - pindex->nHeight + 1); return ((nIndex == -1) ? (-1) : 1) * (chainActive.Height() - pindex->nHeight + 1);
} }
int CMerkleTx::GetBlocksToMaturity() const int CMerkleTx::GetBlocksToMaturity(interfaces::Chain::Lock& locked_chain) const
{ {
if (!IsCoinBase()) if (!IsCoinBase())
return 0; return 0;
int chain_depth = GetDepthInMainChain(); int chain_depth = GetDepthInMainChain(locked_chain);
assert(chain_depth >= 0); // coinbase tx should not be conflicted assert(chain_depth >= 0); // coinbase tx should not be conflicted
return std::max(0, (COINBASE_MATURITY+1) - chain_depth); return std::max(0, (COINBASE_MATURITY+1) - chain_depth);
} }
bool CMerkleTx::IsImmatureCoinBase() const bool CMerkleTx::IsImmatureCoinBase(interfaces::Chain::Lock& locked_chain) const
{ {
// note GetBlocksToMaturity is 0 for non-coinbase tx // note GetBlocksToMaturity is 0 for non-coinbase tx
return GetBlocksToMaturity() > 0; return GetBlocksToMaturity(locked_chain) > 0;
} }
bool CWalletTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state) bool CWalletTx::AcceptToMemoryPool(interfaces::Chain::Lock& locked_chain, const CAmount& nAbsurdFee, CValidationState& state)
{ {
LockAnnotation lock(::cs_main); // Temporary, for AcceptToMemoryPool below. Removed in upcoming commit.
// We must set fInMempool here - while it will be re-set to true by the // We must set fInMempool here - while it will be re-set to true by the
// entered-mempool callback, if we did not there would be a race where a // entered-mempool callback, if we did not there would be a race where a
// user could call sendmoney in a loop and hit spurious out of funds errors // user could call sendmoney in a loop and hit spurious out of funds errors

View File

@ -7,6 +7,7 @@
#define BITCOIN_WALLET_WALLET_H #define BITCOIN_WALLET_WALLET_H
#include <amount.h> #include <amount.h>
#include <interfaces/chain.h>
#include <outputtype.h> #include <outputtype.h>
#include <policy/feerate.h> #include <policy/feerate.h>
#include <streams.h> #include <streams.h>
@ -33,6 +34,26 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
//! Responsible for reading and validating the -wallet arguments and verifying the wallet database.
// This function will perform salvage on the wallet if requested, as long as only one wallet is
// being loaded (WalletParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet).
bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files);
//! Load wallet databases.
bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files);
//! Complete startup of wallets.
void StartWallets(CScheduler& scheduler);
//! Flush all wallets in preparation for shutdown.
void FlushWallets();
//! Stop all wallets. Wallets will be flushed first.
void StopWallets();
//! Close all wallets.
void UnloadWallets();
bool AddWallet(const std::shared_ptr<CWallet>& wallet); bool AddWallet(const std::shared_ptr<CWallet>& wallet);
bool RemoveWallet(const std::shared_ptr<CWallet>& wallet); bool RemoveWallet(const std::shared_ptr<CWallet>& wallet);
bool HasWallets(); bool HasWallets();
@ -264,22 +285,22 @@ public:
* 0 : in memory pool, waiting to be included in a block * 0 : in memory pool, waiting to be included in a block
* >=1 : this many blocks deep in the main chain * >=1 : this many blocks deep in the main chain
*/ */
int GetDepthInMainChain() const EXCLUSIVE_LOCKS_REQUIRED(cs_main); int GetDepthInMainChain(interfaces::Chain::Lock& locked_chain) const;
bool IsInMainChain() const EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return GetDepthInMainChain() > 0; } bool IsInMainChain(interfaces::Chain::Lock& locked_chain) const { return GetDepthInMainChain(locked_chain) > 0; }
/** /**
* @return number of blocks to maturity for this transaction: * @return number of blocks to maturity for this transaction:
* 0 : is not a coinbase transaction, or is a mature coinbase transaction * 0 : is not a coinbase transaction, or is a mature coinbase transaction
* >0 : is a coinbase transaction which matures in this many blocks * >0 : is a coinbase transaction which matures in this many blocks
*/ */
int GetBlocksToMaturity() const EXCLUSIVE_LOCKS_REQUIRED(cs_main); int GetBlocksToMaturity(interfaces::Chain::Lock& locked_chain) const;
bool hashUnset() const { return (hashBlock.IsNull() || hashBlock == ABANDON_HASH); } bool hashUnset() const { return (hashBlock.IsNull() || hashBlock == ABANDON_HASH); }
bool isAbandoned() const { return (hashBlock == ABANDON_HASH); } bool isAbandoned() const { return (hashBlock == ABANDON_HASH); }
void setAbandoned() { hashBlock = ABANDON_HASH; } void setAbandoned() { hashBlock = ABANDON_HASH; }
const uint256& GetHash() const { return tx->GetHash(); } const uint256& GetHash() const { return tx->GetHash(); }
bool IsCoinBase() const { return tx->IsCoinBase(); } bool IsCoinBase() const { return tx->IsCoinBase(); }
bool IsImmatureCoinBase() const EXCLUSIVE_LOCKS_REQUIRED(cs_main); bool IsImmatureCoinBase(interfaces::Chain::Lock& locked_chain) const;
}; };
//Get the marginal bytes of spending the specified output //Get the marginal bytes of spending the specified output
@ -458,14 +479,14 @@ public:
//! filter decides which addresses will count towards the debit //! filter decides which addresses will count towards the debit
CAmount GetDebit(const isminefilter& filter) const; CAmount GetDebit(const isminefilter& filter) const;
CAmount GetCredit(const isminefilter& filter) const EXCLUSIVE_LOCKS_REQUIRED(cs_main); CAmount GetCredit(interfaces::Chain::Lock& locked_chain, const isminefilter& filter) const;
CAmount GetImmatureCredit(bool fUseCache=true) const EXCLUSIVE_LOCKS_REQUIRED(cs_main); CAmount GetImmatureCredit(interfaces::Chain::Lock& locked_chain, bool fUseCache=true) const;
// TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
// annotation "EXCLUSIVE_LOCKS_REQUIRED(cs_main, pwallet->cs_wallet)". The // annotation "EXCLUSIVE_LOCKS_REQUIRED(cs_main, pwallet->cs_wallet)". The
// annotation "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid // annotation "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid
// having to resolve the issue of member access into incomplete type CWallet. // having to resolve the issue of member access into incomplete type CWallet.
CAmount GetAvailableCredit(bool fUseCache=true, const isminefilter& filter=ISMINE_SPENDABLE) const NO_THREAD_SAFETY_ANALYSIS; CAmount GetAvailableCredit(interfaces::Chain::Lock& locked_chain, bool fUseCache=true, const isminefilter& filter=ISMINE_SPENDABLE) const NO_THREAD_SAFETY_ANALYSIS;
CAmount GetImmatureWatchOnlyCredit(const bool fUseCache=true) const EXCLUSIVE_LOCKS_REQUIRED(cs_main); CAmount GetImmatureWatchOnlyCredit(interfaces::Chain::Lock& locked_chain, const bool fUseCache=true) const;
CAmount GetChange() const; CAmount GetChange() const;
// Get the marginal bytes if spending the specified output from this transaction // Get the marginal bytes if spending the specified output from this transaction
@ -486,15 +507,15 @@ public:
bool IsEquivalentTo(const CWalletTx& tx) const; bool IsEquivalentTo(const CWalletTx& tx) const;
bool InMempool() const; bool InMempool() const;
bool IsTrusted() const EXCLUSIVE_LOCKS_REQUIRED(cs_main); bool IsTrusted(interfaces::Chain::Lock& locked_chain) const;
int64_t GetTxTime() const; int64_t GetTxTime() const;
// RelayWalletTransaction may only be called if fBroadcastTransactions! // RelayWalletTransaction may only be called if fBroadcastTransactions!
bool RelayWalletTransaction(CConnman* connman) EXCLUSIVE_LOCKS_REQUIRED(cs_main); bool RelayWalletTransaction(interfaces::Chain::Lock& locked_chain, CConnman* connman);
/** Pass this transaction to the mempool. Fails if absolute fee exceeds absurd fee. */ /** Pass this transaction to the mempool. Fails if absolute fee exceeds absurd fee. */
bool AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main); bool AcceptToMemoryPool(interfaces::Chain::Lock& locked_chain, const CAmount& nAbsurdFee, CValidationState& state);
// TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
// annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation
@ -676,6 +697,9 @@ private:
*/ */
bool AddWatchOnly(const CScript& dest) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool AddWatchOnly(const CScript& dest) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** Interface for accessing chain state. */
interfaces::Chain& m_chain;
/** Wallet location which includes wallet name (see WalletLocation). */ /** Wallet location which includes wallet name (see WalletLocation). */
WalletLocation m_location; WalletLocation m_location;
@ -737,7 +761,7 @@ public:
unsigned int nMasterKeyMaxID = 0; unsigned int nMasterKeyMaxID = 0;
/** Construct wallet with specified name and database implementation. */ /** Construct wallet with specified name and database implementation. */
CWallet(const WalletLocation& location, std::unique_ptr<WalletDatabase> database) : m_location(location), database(std::move(database)) CWallet(interfaces::Chain& chain, const WalletLocation& location, std::unique_ptr<WalletDatabase> database) : m_chain(chain), m_location(location), database(std::move(database))
{ {
} }
@ -759,6 +783,9 @@ public:
std::set<COutPoint> setLockedCoins GUARDED_BY(cs_wallet); std::set<COutPoint> setLockedCoins GUARDED_BY(cs_wallet);
/** Interface for accessing chain state. */
interfaces::Chain& chain() const { return m_chain; }
const CWalletTx* GetWalletTx(const uint256& hash) const; const CWalletTx* GetWalletTx(const uint256& hash) const;
//! check whether we are allowed to upgrade (or already support) to the named feature //! check whether we are allowed to upgrade (or already support) to the named feature
@ -767,12 +794,12 @@ public:
/** /**
* populate vCoins with vector of available COutputs. * populate vCoins with vector of available COutputs.
*/ */
void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0, const int nMinDepth = 0, const int nMaxDepth = 9999999) const EXCLUSIVE_LOCKS_REQUIRED(cs_main, cs_wallet); void AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<COutput>& vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0, const int nMinDepth = 0, const int nMaxDepth = 9999999) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** /**
* Return list of available coins and locked coins grouped by non-change output address. * Return list of available coins and locked coins grouped by non-change output address.
*/ */
std::map<CTxDestination, std::vector<COutput>> ListCoins() const EXCLUSIVE_LOCKS_REQUIRED(cs_main, cs_wallet); std::map<CTxDestination, std::vector<COutput>> ListCoins(interfaces::Chain::Lock& locked_chain) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** /**
* Find non-change parent output. * Find non-change parent output.
@ -788,7 +815,7 @@ public:
bool SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<OutputGroup> groups, bool SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<OutputGroup> groups,
std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params, bool& bnb_used) const; std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params, bool& bnb_used) const;
bool IsSpent(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_main, cs_wallet); bool IsSpent(interfaces::Chain::Lock& locked_chain, const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
std::vector<OutputGroup> GroupOutputs(const std::vector<COutput>& outputs, bool single_coin) const; std::vector<OutputGroup> GroupOutputs(const std::vector<COutput>& outputs, bool single_coin) const;
bool IsLockedCoin(uint256 hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool IsLockedCoin(uint256 hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@ -852,7 +879,7 @@ public:
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase); bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
bool EncryptWallet(const SecureString& strWalletPassphrase); bool EncryptWallet(const SecureString& strWalletPassphrase);
void GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<CTxDestination, int64_t> &mapKeyBirth) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
unsigned int ComputeTimeSmart(const CWalletTx& wtx) const; unsigned int ComputeTimeSmart(const CWalletTx& wtx) const;
/** /**
@ -874,7 +901,7 @@ public:
void ReacceptWalletTransactions(); void ReacceptWalletTransactions();
void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override EXCLUSIVE_LOCKS_REQUIRED(cs_main); void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override EXCLUSIVE_LOCKS_REQUIRED(cs_main);
// ResendWalletTransactionsBefore may only be called if fBroadcastTransactions! // ResendWalletTransactionsBefore may only be called if fBroadcastTransactions!
std::vector<uint256> ResendWalletTransactionsBefore(int64_t nTime, CConnman* connman) EXCLUSIVE_LOCKS_REQUIRED(cs_main); std::vector<uint256> ResendWalletTransactionsBefore(interfaces::Chain::Lock& locked_chain, int64_t nTime, CConnman* connman);
CAmount GetBalance(const isminefilter& filter=ISMINE_SPENDABLE, const int min_depth=0) const; CAmount GetBalance(const isminefilter& filter=ISMINE_SPENDABLE, const int min_depth=0) const;
CAmount GetUnconfirmedBalance() const; CAmount GetUnconfirmedBalance() const;
CAmount GetImmatureBalance() const; CAmount GetImmatureBalance() const;
@ -897,7 +924,7 @@ public:
* selected by SelectCoins(); Also create the change output, when needed * selected by SelectCoins(); Also create the change output, when needed
* @note passing nChangePosInOut as -1 will result in setting a random position * @note passing nChangePosInOut as -1 will result in setting a random position
*/ */
bool CreateTransaction(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, bool CreateTransaction(interfaces::Chain::Lock& locked_chain, const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut,
std::string& strFailReason, const CCoinControl& coin_control, bool sign = true); std::string& strFailReason, const CCoinControl& coin_control, bool sign = true);
bool CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, CReserveKey& reservekey, CConnman* connman, CValidationState& state); bool CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, CReserveKey& reservekey, CConnman* connman, CValidationState& state);
@ -956,7 +983,7 @@ public:
const std::map<CKeyID, int64_t>& GetAllReserveKeys() const { return m_pool_key_to_index; } const std::map<CKeyID, int64_t>& GetAllReserveKeys() const { return m_pool_key_to_index; }
std::set<std::set<CTxDestination>> GetAddressGroupings() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); std::set<std::set<CTxDestination>> GetAddressGroupings() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
std::map<CTxDestination, CAmount> GetAddressBalances() EXCLUSIVE_LOCKS_REQUIRED(cs_main); std::map<CTxDestination, CAmount> GetAddressBalances(interfaces::Chain::Lock& locked_chain);
std::set<CTxDestination> GetLabelAddresses(const std::string& label) const; std::set<CTxDestination> GetLabelAddresses(const std::string& label) const;
@ -1051,16 +1078,16 @@ public:
bool TransactionCanBeAbandoned(const uint256& hashTx) const; bool TransactionCanBeAbandoned(const uint256& hashTx) const;
/* Mark a transaction (and it in-wallet descendants) as abandoned so its inputs may be respent. */ /* Mark a transaction (and it in-wallet descendants) as abandoned so its inputs may be respent. */
bool AbandonTransaction(const uint256& hashTx); bool AbandonTransaction(interfaces::Chain::Lock& locked_chain, const uint256& hashTx);
/** Mark a transaction as replaced by another transaction (e.g., BIP 125). */ /** Mark a transaction as replaced by another transaction (e.g., BIP 125). */
bool MarkReplaced(const uint256& originalHash, const uint256& newHash); bool MarkReplaced(const uint256& originalHash, const uint256& newHash);
//! Verify wallet naming and perform salvage on the wallet if required //! Verify wallet naming and perform salvage on the wallet if required
static bool Verify(const WalletLocation& location, bool salvage_wallet, std::string& error_string, std::string& warning_string); static bool Verify(interfaces::Chain& chain, const WalletLocation& location, bool salvage_wallet, std::string& error_string, std::string& warning_string);
/* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */ /* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */
static std::shared_ptr<CWallet> CreateWalletFromFile(const WalletLocation& location, uint64_t wallet_creation_flags = 0); static std::shared_ptr<CWallet> CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, uint64_t wallet_creation_flags = 0);
/** /**
* Wallet post-init setup * Wallet post-init setup

View File

@ -9,6 +9,7 @@
class CScheduler; class CScheduler;
class CRPCTable; class CRPCTable;
struct InitInterfaces;
class WalletInitInterface { class WalletInitInterface {
public: public:
@ -18,20 +19,8 @@ public:
virtual void AddWalletOptions() const = 0; virtual void AddWalletOptions() const = 0;
/** Check wallet parameter interaction */ /** Check wallet parameter interaction */
virtual bool ParameterInteraction() const = 0; virtual bool ParameterInteraction() const = 0;
/** Register wallet RPC*/ /** Add wallets that should be opened to list of init interfaces. */
virtual void RegisterRPC(CRPCTable &) const = 0; virtual void Construct(InitInterfaces& interfaces) const = 0;
/** Verify wallets */
virtual bool Verify() const = 0;
/** Open wallets*/
virtual bool Open() const = 0;
/** Start wallets*/
virtual void Start(CScheduler& scheduler) const = 0;
/** Flush Wallets*/
virtual void Flush() const = 0;
/** Stop Wallets*/
virtual void Stop() const = 0;
/** Close wallets */
virtual void Close() const = 0;
virtual ~WalletInitInterface() {} virtual ~WalletInitInterface() {}
}; };