Skip to content

Commit 2607d96

Browse files
committed
Merge #10973: Refactor: separate wallet from node
d358466 Remove remaining wallet accesses to node globals (Russell Yanofsky) b1b2b23 Remove use of CCoinsViewMemPool::GetCoin in wallet code (Russell Yanofsky) 4e4d9e9 Remove use of CRPCTable::appendCommand in wallet code (Russell Yanofsky) 91868e6 Remove use CValidationInterface in wallet code (Russell Yanofsky) Pull request description: This PR is the last in a chain of PRs (#14437, #14711, and #15288) that make the wallet code access node state through an abstract [`Chain`](https://github.com/ryanofsky/bitcoin/blob/pr/wipc-sep/src/interfaces/chain.h) class in [`src/interfaces/`](https://github.com/ryanofsky/bitcoin/tree/pr/wipc-sep/src/interfaces) instead of using global variables like `cs_main`, `chainActive`, and `g_connman`. After this PR, wallet code no longer accesses global variables declared outside the wallet directory, and no longer calls functions accessing those globals (as verified by the `hide-globals` script in #10244). This PR and the previous PRs have been refactoring changes that do not affect behavior. Previous PRs have consisted of lots of mechanical changes like: ```diff - wtx.nTimeReceived = GetAdjustedTime(); + wtx.nTimeReceived = m_chain->getAdjustedTime(); ``` This PR is smaller, but less mechanical. It replaces last few bits of wallet code that access node state directly (through `CValidationInterface`, `CRPCTable`, and `CCoinsViewMemPool` interfaces) with code that uses the `Chain` interface. These changes allow followup PR #10102 (multiprocess gui & wallet PR) to work without any significant updates to wallet code. Additionally they: * Provide a single place to describe the interface between wallet and node code. * Can make better wallet testing possible, because the `Chain` object consists of virtual methods that can be overloaded for mocking. (This could be used to test edge cases in the rescan code, for example). Tree-SHA512: e6291d8a3c50bdff18a9c8ad11e729beb30b5b7040d7aaf31ba678800b4a97b2dd2be76340b1e5c01fe2827d67d37ed1bb4c8380cf8ed653aadfea003e9b22e7
2 parents b3f8228 + d358466 commit 2607d96

22 files changed

+373
-137
lines changed

src/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ BITCOIN_CORE_H = \
154154
netaddress.h \
155155
netbase.h \
156156
netmessagemaker.h \
157+
node/coin.h \
157158
node/transaction.h \
158159
noui.h \
159160
optional.h \
@@ -262,6 +263,7 @@ libbitcoin_server_a_SOURCES = \
262263
miner.cpp \
263264
net.cpp \
264265
net_processing.cpp \
266+
node/coin.cpp \
265267
node/transaction.cpp \
266268
noui.cpp \
267269
outputtype.cpp \

src/interfaces/chain.cpp

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,29 @@
66

77
#include <chain.h>
88
#include <chainparams.h>
9+
#include <interfaces/handler.h>
910
#include <interfaces/wallet.h>
1011
#include <net.h>
12+
#include <node/coin.h>
1113
#include <policy/fees.h>
1214
#include <policy/policy.h>
1315
#include <policy/rbf.h>
1416
#include <primitives/block.h>
1517
#include <primitives/transaction.h>
1618
#include <protocol.h>
19+
#include <rpc/protocol.h>
20+
#include <rpc/server.h>
21+
#include <shutdown.h>
1722
#include <sync.h>
1823
#include <threadsafety.h>
1924
#include <timedata.h>
2025
#include <txmempool.h>
2126
#include <ui_interface.h>
2227
#include <uint256.h>
28+
#include <univalue.h>
2329
#include <util/system.h>
2430
#include <validation.h>
31+
#include <validationinterface.h>
2532

2633
#include <memory>
2734
#include <utility>
@@ -161,6 +168,94 @@ class LockingStateImpl : public LockImpl, public UniqueLock<CCriticalSection>
161168
using UniqueLock::UniqueLock;
162169
};
163170

171+
class NotificationsHandlerImpl : public Handler, CValidationInterface
172+
{
173+
public:
174+
explicit NotificationsHandlerImpl(Chain& chain, Chain::Notifications& notifications)
175+
: m_chain(chain), m_notifications(&notifications)
176+
{
177+
RegisterValidationInterface(this);
178+
}
179+
~NotificationsHandlerImpl() override { disconnect(); }
180+
void disconnect() override
181+
{
182+
if (m_notifications) {
183+
m_notifications = nullptr;
184+
UnregisterValidationInterface(this);
185+
}
186+
}
187+
void TransactionAddedToMempool(const CTransactionRef& tx) override
188+
{
189+
m_notifications->TransactionAddedToMempool(tx);
190+
}
191+
void TransactionRemovedFromMempool(const CTransactionRef& tx) override
192+
{
193+
m_notifications->TransactionRemovedFromMempool(tx);
194+
}
195+
void BlockConnected(const std::shared_ptr<const CBlock>& block,
196+
const CBlockIndex* index,
197+
const std::vector<CTransactionRef>& tx_conflicted) override
198+
{
199+
m_notifications->BlockConnected(*block, tx_conflicted);
200+
}
201+
void BlockDisconnected(const std::shared_ptr<const CBlock>& block) override
202+
{
203+
m_notifications->BlockDisconnected(*block);
204+
}
205+
void ChainStateFlushed(const CBlockLocator& locator) override { m_notifications->ChainStateFlushed(locator); }
206+
void ResendWalletTransactions(int64_t best_block_time, CConnman*) override
207+
{
208+
// `cs_main` is always held when this method is called, so it is safe to
209+
// call `assumeLocked`. This is awkward, and the `assumeLocked` method
210+
// should be able to be removed entirely if `ResendWalletTransactions`
211+
// is replaced by a wallet timer as suggested in
212+
// https://github.com/bitcoin/bitcoin/issues/15619
213+
auto locked_chain = m_chain.assumeLocked();
214+
m_notifications->ResendWalletTransactions(*locked_chain, best_block_time);
215+
}
216+
Chain& m_chain;
217+
Chain::Notifications* m_notifications;
218+
};
219+
220+
class RpcHandlerImpl : public Handler
221+
{
222+
public:
223+
RpcHandlerImpl(const CRPCCommand& command) : m_command(command), m_wrapped_command(&command)
224+
{
225+
m_command.actor = [this](const JSONRPCRequest& request, UniValue& result, bool last_handler) {
226+
if (!m_wrapped_command) return false;
227+
try {
228+
return m_wrapped_command->actor(request, result, last_handler);
229+
} catch (const UniValue& e) {
230+
// If this is not the last handler and a wallet not found
231+
// exception was thrown, return false so the next handler can
232+
// try to handle the request. Otherwise, reraise the exception.
233+
if (!last_handler) {
234+
const UniValue& code = e["code"];
235+
if (code.isNum() && code.get_int() == RPC_WALLET_NOT_FOUND) {
236+
return false;
237+
}
238+
}
239+
throw;
240+
}
241+
};
242+
::tableRPC.appendCommand(m_command.name, &m_command);
243+
}
244+
245+
void disconnect() override final
246+
{
247+
if (m_wrapped_command) {
248+
m_wrapped_command = nullptr;
249+
::tableRPC.removeCommand(m_command.name, &m_command);
250+
}
251+
}
252+
253+
~RpcHandlerImpl() override { disconnect(); }
254+
255+
CRPCCommand m_command;
256+
const CRPCCommand* m_wrapped_command;
257+
};
258+
164259
class ChainImpl : public Chain
165260
{
166261
public:
@@ -194,6 +289,7 @@ class ChainImpl : public Chain
194289
}
195290
return true;
196291
}
292+
void findCoins(std::map<COutPoint, Coin>& coins) override { return FindCoins(coins); }
197293
double guessVerificationProgress(const uint256& block_hash) override
198294
{
199295
LOCK(cs_main);
@@ -245,17 +341,33 @@ class ChainImpl : public Chain
245341
{
246342
return ::mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
247343
}
344+
CFeeRate relayMinFee() override { return ::minRelayTxFee; }
345+
CFeeRate relayIncrementalFee() override { return ::incrementalRelayFee; }
346+
CFeeRate relayDustFee() override { return ::dustRelayFee; }
248347
CAmount maxTxFee() override { return ::maxTxFee; }
249348
bool getPruneMode() override { return ::fPruneMode; }
250349
bool p2pEnabled() override { return g_connman != nullptr; }
251350
bool isInitialBlockDownload() override { return IsInitialBlockDownload(); }
351+
bool shutdownRequested() override { return ShutdownRequested(); }
252352
int64_t getAdjustedTime() override { return GetAdjustedTime(); }
253353
void initMessage(const std::string& message) override { ::uiInterface.InitMessage(message); }
254354
void initWarning(const std::string& message) override { InitWarning(message); }
255355
void initError(const std::string& message) override { InitError(message); }
256356
void loadWallet(std::unique_ptr<Wallet> wallet) override { ::uiInterface.LoadWallet(wallet); }
357+
void showProgress(const std::string& title, int progress, bool resume_possible) override
358+
{
359+
::uiInterface.ShowProgress(title, progress, resume_possible);
360+
}
361+
std::unique_ptr<Handler> handleNotifications(Notifications& notifications) override
362+
{
363+
return MakeUnique<NotificationsHandlerImpl>(*this, notifications);
364+
}
365+
void waitForNotifications() override { SyncWithValidationInterfaceQueue(); }
366+
std::unique_ptr<Handler> handleRpc(const CRPCCommand& command) override
367+
{
368+
return MakeUnique<RpcHandlerImpl>(command);
369+
}
257370
};
258-
259371
} // namespace
260372

261373
std::unique_ptr<Chain> MakeChain() { return MakeUnique<ChainImpl>(); }

src/interfaces/chain.h

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,18 @@
1616

1717
class CBlock;
1818
class CFeeRate;
19+
class CRPCCommand;
1920
class CScheduler;
2021
class CValidationState;
22+
class Coin;
2123
class uint256;
2224
enum class RBFTransactionState;
2325
struct CBlockLocator;
2426
struct FeeCalculation;
2527

2628
namespace interfaces {
2729

30+
class Handler;
2831
class Wallet;
2932

3033
//! Interface giving clients (wallet processes, maybe other analysis tools in
@@ -40,6 +43,12 @@ class Wallet;
4043
//! asynchronously
4144
//! (https://github.com/bitcoin/bitcoin/pull/10973#issuecomment-380101269).
4245
//!
46+
//! * The isPotentialTip() and waitForNotifications() methods are too low-level
47+
//! and should be replaced with a higher level
48+
//! waitForNotificationsUpTo(block_hash) method that the wallet can call
49+
//! instead
50+
//! (https://github.com/bitcoin/bitcoin/pull/10973#discussion_r266995234).
51+
//!
4352
//! * The relayTransactions() and submitToMemoryPool() methods could be replaced
4453
//! with a higher-level broadcastTransaction method
4554
//! (https://github.com/bitcoin/bitcoin/pull/14978#issuecomment-459373984).
@@ -160,6 +169,11 @@ class Chain
160169
int64_t* time = nullptr,
161170
int64_t* max_time = nullptr) = 0;
162171

172+
//! Look up unspent output information. Returns coins in the mempool and in
173+
//! the current chain UTXO set. Iterates through all the keys in the map and
174+
//! populates the values.
175+
virtual void findCoins(std::map<COutPoint, Coin>& coins) = 0;
176+
163177
//! Estimate fraction of total transactions verified if blocks up to
164178
//! the specified block hash are verified.
165179
virtual double guessVerificationProgress(const uint256& block_hash) = 0;
@@ -188,6 +202,15 @@ class Chain
188202
//! Mempool minimum fee.
189203
virtual CFeeRate mempoolMinFee() = 0;
190204

205+
//! Relay current minimum fee (from -minrelaytxfee and -incrementalrelayfee settings).
206+
virtual CFeeRate relayMinFee() = 0;
207+
208+
//! Relay incremental fee setting (-incrementalrelayfee), reflecting cost of relay.
209+
virtual CFeeRate relayIncrementalFee() = 0;
210+
211+
//! Relay dust fee setting (-dustrelayfee), reflecting lowest rate it's economical to spend.
212+
virtual CFeeRate relayDustFee() = 0;
213+
191214
//! Node max tx fee setting (-maxtxfee).
192215
//! This could be replaced by a per-wallet max fee, as proposed at
193216
//! https://github.com/bitcoin/bitcoin/issues/15355
@@ -200,9 +223,12 @@ class Chain
200223
//! Check if p2p enabled.
201224
virtual bool p2pEnabled() = 0;
202225

203-
// Check if in IBD.
226+
//! Check if in IBD.
204227
virtual bool isInitialBlockDownload() = 0;
205228

229+
//! Check if shutdown requested.
230+
virtual bool shutdownRequested() = 0;
231+
206232
//! Get adjusted time.
207233
virtual int64_t getAdjustedTime() = 0;
208234

@@ -217,6 +243,32 @@ class Chain
217243

218244
//! Send wallet load notification to the GUI.
219245
virtual void loadWallet(std::unique_ptr<Wallet> wallet) = 0;
246+
247+
//! Send progress indicator.
248+
virtual void showProgress(const std::string& title, int progress, bool resume_possible) = 0;
249+
250+
//! Chain notifications.
251+
class Notifications
252+
{
253+
public:
254+
virtual ~Notifications() {}
255+
virtual void TransactionAddedToMempool(const CTransactionRef& tx) {}
256+
virtual void TransactionRemovedFromMempool(const CTransactionRef& ptx) {}
257+
virtual void BlockConnected(const CBlock& block, const std::vector<CTransactionRef>& tx_conflicted) {}
258+
virtual void BlockDisconnected(const CBlock& block) {}
259+
virtual void ChainStateFlushed(const CBlockLocator& locator) {}
260+
virtual void ResendWalletTransactions(Lock& locked_chain, int64_t best_block_time) {}
261+
};
262+
263+
//! Register handler for notifications.
264+
virtual std::unique_ptr<Handler> handleNotifications(Notifications& notifications) = 0;
265+
266+
//! Wait for pending notifications to be handled.
267+
virtual void waitForNotifications() = 0;
268+
269+
//! Register handler for RPC. Command is not copied, so reference
270+
//! needs to remain valid until Handler is disconnected.
271+
virtual std::unique_ptr<Handler> handleRpc(const CRPCCommand& command) = 0;
220272
};
221273

222274
//! Interface to let node manage chain clients (wallets, or maybe tools for

src/interfaces/wallet.cpp

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@ class PendingWalletTxImpl : public PendingWalletTx
4747

4848
const CTransaction& get() override { return *m_tx; }
4949

50-
int64_t getVirtualSize() override { return GetVirtualTransactionSize(*m_tx); }
51-
5250
bool commit(WalletValueMap value_map,
5351
WalletOrderForm order_form,
5452
std::string& reject_reason) override
@@ -99,12 +97,8 @@ WalletTx MakeWalletTx(interfaces::Chain::Lock& locked_chain, CWallet& wallet, co
9997
//! Construct wallet tx status struct.
10098
WalletTxStatus MakeWalletTxStatus(interfaces::Chain::Lock& locked_chain, const CWalletTx& wtx)
10199
{
102-
LockAnnotation lock(::cs_main); // Temporary, for mapBlockIndex below. Removed in upcoming commit.
103-
104100
WalletTxStatus result;
105-
auto mi = ::mapBlockIndex.find(wtx.hashBlock);
106-
CBlockIndex* block = mi != ::mapBlockIndex.end() ? mi->second : nullptr;
107-
result.block_height = (block ? block->nHeight : std::numeric_limits<int>::max());
101+
result.block_height = locked_chain.getBlockHeight(wtx.hashBlock).get_value_or(std::numeric_limits<int>::max());
108102
result.blocks_to_maturity = wtx.GetBlocksToMaturity(locked_chain);
109103
result.depth_in_main_chain = wtx.GetDepthInMainChain(locked_chain);
110104
result.time_received = wtx.nTimeReceived;
@@ -514,7 +508,7 @@ class WalletClientImpl : public ChainClient
514508
: m_chain(chain), m_wallet_filenames(std::move(wallet_filenames))
515509
{
516510
}
517-
void registerRpcs() override { return RegisterWalletRPCCommands(::tableRPC); }
511+
void registerRpcs() override { return RegisterWalletRPCCommands(m_chain, m_rpc_handlers); }
518512
bool verify() override { return VerifyWallets(m_chain, m_wallet_filenames); }
519513
bool load() override { return LoadWallets(m_chain, m_wallet_filenames); }
520514
void start(CScheduler& scheduler) override { return StartWallets(scheduler); }
@@ -524,6 +518,7 @@ class WalletClientImpl : public ChainClient
524518

525519
Chain& m_chain;
526520
std::vector<std::string> m_wallet_filenames;
521+
std::vector<std::unique_ptr<Handler>> m_rpc_handlers;
527522
};
528523

529524
} // namespace

src/interfaces/wallet.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -292,9 +292,6 @@ class PendingWalletTx
292292
//! Get transaction data.
293293
virtual const CTransaction& get() = 0;
294294

295-
//! Get virtual transaction size.
296-
virtual int64_t getVirtualSize() = 0;
297-
298295
//! Send pending transaction and commit to wallet.
299296
virtual bool commit(WalletValueMap value_map,
300297
WalletOrderForm order_form,

src/node/coin.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright (c) 2019 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <node/coin.h>
6+
7+
#include <txmempool.h>
8+
#include <validation.h>
9+
10+
void FindCoins(std::map<COutPoint, Coin>& coins)
11+
{
12+
LOCK2(cs_main, ::mempool.cs);
13+
assert(pcoinsTip);
14+
CCoinsViewCache& chain_view = *::pcoinsTip;
15+
CCoinsViewMemPool mempool_view(&chain_view, ::mempool);
16+
for (auto& coin : coins) {
17+
if (!mempool_view.GetCoin(coin.first, coin.second)) {
18+
// Either the coin is not in the CCoinsViewCache or is spent. Clear it.
19+
coin.second.Clear();
20+
}
21+
}
22+
}

src/node/coin.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright (c) 2019 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef BITCOIN_NODE_COIN_H
6+
#define BITCOIN_NODE_COIN_H
7+
8+
#include <map>
9+
10+
class COutPoint;
11+
class Coin;
12+
13+
/**
14+
* Look up unspent output information. Returns coins in the mempool and in the
15+
* current chain UTXO set. Iterates through all the keys in the map and
16+
* populates the values.
17+
*
18+
* @param[in,out] coins map to fill
19+
*/
20+
void FindCoins(std::map<COutPoint, Coin>& coins);
21+
22+
#endif // BITCOIN_NODE_COIN_H

src/qt/walletmodeltransaction.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ std::unique_ptr<interfaces::PendingWalletTx>& WalletModelTransaction::getWtx()
2929

3030
unsigned int WalletModelTransaction::getTransactionSize()
3131
{
32-
return wtx ? wtx->getVirtualSize() : 0;
32+
return wtx ? GetVirtualTransactionSize(wtx->get()) : 0;
3333
}
3434

3535
CAmount WalletModelTransaction::getTransactionFee() const

0 commit comments

Comments
 (0)