diff options
Diffstat (limited to 'src/wallet/rpcwallet.cpp')
-rw-r--r-- | src/wallet/rpcwallet.cpp | 166 |
1 files changed, 93 insertions, 73 deletions
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index e571501221..00742aed7a 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -5,9 +5,9 @@ #include <amount.h> #include <core_io.h> -#include <init.h> #include <interfaces/chain.h> #include <key_io.h> +#include <node/context.h> #include <outputtype.h> #include <policy/feerate.h> #include <policy/fees.h> @@ -68,7 +68,7 @@ static bool ParseIncludeWatchonly(const UniValue& include_watchonly, const CWall /** Checks if a CKey is in the given CWallet compressed or otherwise*/ -bool HaveKey(const CWallet& wallet, const CKey& key) +bool HaveKey(const SigningProvider& wallet, const CKey& key) { CKey key2; key2.Set(key.begin(), key.end(), !key.IsCompressed()); @@ -124,19 +124,29 @@ void EnsureWalletIsUnlocked(const CWallet* pwallet) } } +LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet) +{ + LegacyScriptPubKeyMan* spk_man = wallet.GetLegacyScriptPubKeyMan(); + if (!spk_man) { + throw JSONRPCError(RPC_WALLET_ERROR, "This type of wallet does not support this command"); + } + return *spk_man; +} + static void WalletTxToJSON(interfaces::Chain& chain, interfaces::Chain::Lock& locked_chain, const CWalletTx& wtx, UniValue& entry) { - int confirms = wtx.GetDepthInMainChain(locked_chain); + int confirms = wtx.GetDepthInMainChain(); entry.pushKV("confirmations", confirms); if (wtx.IsCoinBase()) entry.pushKV("generated", true); if (confirms > 0) { entry.pushKV("blockhash", wtx.m_confirm.hashBlock.GetHex()); + entry.pushKV("blockheight", wtx.m_confirm.block_height); entry.pushKV("blockindex", wtx.m_confirm.nIndex); int64_t block_time; bool found_block = chain.findBlock(wtx.m_confirm.hashBlock, nullptr /* block */, &block_time); - assert(found_block); + CHECK_NONFATAL(found_block); entry.pushKV("blocktime", block_time); } else { entry.pushKV("trusted", wtx.IsTrusted(locked_chain)); @@ -303,7 +313,7 @@ static UniValue setlabel(const JSONRPCRequest& request) std::string label = LabelFromValue(request.params[1]); - if (IsMine(*pwallet, dest)) { + if (pwallet->IsMine(dest)) { pwallet->SetAddressBook(dest, label, "receive"); } else { pwallet->SetAddressBook(dest, label, "send"); @@ -550,9 +560,11 @@ static UniValue signmessage(const JSONRPCRequest& request) throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); } + const SigningProvider* provider = pwallet->GetSigningProvider(); + CKey key; CKeyID keyID(*pkhash); - if (!pwallet->GetKey(keyID, key)) { + if (!provider->GetKey(keyID, key)) { throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available"); } @@ -610,7 +622,7 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); } CScript scriptPubKey = GetScriptForDestination(dest); - if (!IsMine(*pwallet, scriptPubKey)) { + if (!pwallet->IsMine(scriptPubKey)) { throw JSONRPCError(RPC_WALLET_ERROR, "Address not found in wallet"); } @@ -629,7 +641,7 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request) for (const CTxOut& txout : wtx.tx->vout) if (txout.scriptPubKey == scriptPubKey) - if (wtx.GetDepthInMainChain(*locked_chain) >= nMinDepth) + if (wtx.GetDepthInMainChain() >= nMinDepth) nAmount += txout.nValue; } @@ -694,8 +706,8 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request) for (const CTxOut& txout : wtx.tx->vout) { CTxDestination address; - if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwallet, address) && setAddress.count(address)) { - if (wtx.GetDepthInMainChain(*locked_chain) >= nMinDepth) + if (ExtractDestination(txout.scriptPubKey, address) && pwallet->IsMine(address) && setAddress.count(address)) { + if (wtx.GetDepthInMainChain() >= nMinDepth) nAmount += txout.nValue; } } @@ -964,6 +976,8 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request) }, }.Check(request); + LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet); + auto locked_chain = pwallet->chain().lock(); LOCK(pwallet->cs_wallet); @@ -980,7 +994,7 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request) if (IsHex(keys_or_addrs[i].get_str()) && (keys_or_addrs[i].get_str().length() == 66 || keys_or_addrs[i].get_str().length() == 130)) { pubkeys.push_back(HexToPubKey(keys_or_addrs[i].get_str())); } else { - pubkeys.push_back(AddrToPubKey(pwallet, keys_or_addrs[i].get_str())); + pubkeys.push_back(AddrToPubKey(&spk_man, keys_or_addrs[i].get_str())); } } @@ -993,7 +1007,7 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request) // Construct using pay-to-script-hash: CScript inner; - CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, output_type, *pwallet, inner); + CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, output_type, spk_man, inner); pwallet->SetAddressBook(dest, label, "send"); UniValue result(UniValue::VOBJ); @@ -1050,7 +1064,7 @@ static UniValue ListReceived(interfaces::Chain::Lock& locked_chain, CWallet * co continue; } - int nDepth = wtx.GetDepthInMainChain(locked_chain); + int nDepth = wtx.GetDepthInMainChain(); if (nDepth < nMinDepth) continue; @@ -1064,7 +1078,7 @@ static UniValue ListReceived(interfaces::Chain::Lock& locked_chain, CWallet * co continue; } - isminefilter mine = IsMine(*pwallet, address); + isminefilter mine = pwallet->IsMine(address); if(!(mine & filter)) continue; @@ -1288,7 +1302,7 @@ static void ListTransactions(interfaces::Chain::Lock& locked_chain, CWallet* con for (const COutputEntry& s : listSent) { UniValue entry(UniValue::VOBJ); - if (involvesWatchonly || (::IsMine(*pwallet, s.destination) & ISMINE_WATCH_ONLY)) { + if (involvesWatchonly || (pwallet->IsMine(s.destination) & ISMINE_WATCH_ONLY)) { entry.pushKV("involvesWatchonly", true); } MaybePushAddress(entry, s.destination); @@ -1307,8 +1321,7 @@ static void ListTransactions(interfaces::Chain::Lock& locked_chain, CWallet* con } // Received - if (listReceived.size() > 0 && wtx.GetDepthInMainChain(locked_chain) >= nMinDepth) - { + if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) { for (const COutputEntry& r : listReceived) { std::string label; @@ -1319,15 +1332,15 @@ static void ListTransactions(interfaces::Chain::Lock& locked_chain, CWallet* con continue; } UniValue entry(UniValue::VOBJ); - if (involvesWatchonly || (::IsMine(*pwallet, r.destination) & ISMINE_WATCH_ONLY)) { + if (involvesWatchonly || (pwallet->IsMine(r.destination) & ISMINE_WATCH_ONLY)) { entry.pushKV("involvesWatchonly", true); } MaybePushAddress(entry, r.destination); if (wtx.IsCoinBase()) { - if (wtx.GetDepthInMainChain(locked_chain) < 1) + if (wtx.GetDepthInMainChain() < 1) entry.pushKV("category", "orphan"); - else if (wtx.IsImmatureCoinBase(locked_chain)) + else if (wtx.IsImmatureCoinBase()) entry.pushKV("category", "immature"); else entry.pushKV("category", "generate"); @@ -1355,6 +1368,7 @@ static const std::string TransactionDescriptionString() " \"generated\": xxx, (bool) Only present if transaction only input is a coinbase one.\n" " \"trusted\": xxx, (bool) Only present if we consider transaction to be trusted and so safe to spend from.\n" " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction.\n" + " \"blockheight\": n, (numeric) The block height containing the transaction.\n" " \"blockindex\": n, (numeric) The index of the transaction in the block that includes it.\n" " \"blocktime\": xxx, (numeric) The block time in seconds since epoch (1 Jan 1970 GMT).\n" " \"txid\": \"transactionid\", (string) The transaction id.\n" @@ -1591,7 +1605,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request) for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) { CWalletTx tx = pairWtx.second; - if (depth == -1 || tx.GetDepthInMainChain(*locked_chain) < depth) { + if (depth == -1 || abs(tx.GetDepthInMainChain()) < depth) { ListTransactions(*locked_chain, pwallet, tx, 0, true, transactions, filter, nullptr /* filter_label */); } } @@ -1708,7 +1722,7 @@ static UniValue gettransaction(const JSONRPCRequest& request) } const CWalletTx& wtx = it->second; - CAmount nCredit = wtx.GetCredit(*locked_chain, filter); + CAmount nCredit = wtx.GetCredit(filter); CAmount nDebit = wtx.GetDebit(filter); CAmount nNet = nCredit - nDebit; CAmount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut() - nDebit : 0); @@ -1772,7 +1786,7 @@ static UniValue abandontransaction(const JSONRPCRequest& request) if (!pwallet->mapWallet.count(hash)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); } - if (!pwallet->AbandonTransaction(*locked_chain, hash)) { + if (!pwallet->AbandonTransaction(hash)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not eligible for abandonment"); } @@ -2203,7 +2217,7 @@ static UniValue lockunspent(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout index out of bounds"); } - if (pwallet->IsSpent(*locked_chain, outpt.hash, outpt.n)) { + if (pwallet->IsSpent(outpt.hash, outpt.n)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected unspent output"); } @@ -2379,7 +2393,8 @@ static UniValue getbalances(const JSONRPCRequest& request) } balances.pushKV("mine", balances_mine); } - if (wallet.HaveWatchOnly()) { + auto spk_man = wallet.GetLegacyScriptPubKeyMan(); + if (spk_man && spk_man->HaveWatchOnly()) { UniValue balances_watchonly{UniValue::VOBJ}; balances_watchonly.pushKV("trusted", ValueFromAmount(bal.m_watchonly_trusted)); balances_watchonly.pushKV("untrusted_pending", ValueFromAmount(bal.m_watchonly_untrusted_pending)); @@ -2449,7 +2464,15 @@ static UniValue getwalletinfo(const JSONRPCRequest& request) obj.pushKV("txcount", (int)pwallet->mapWallet.size()); obj.pushKV("keypoololdest", pwallet->GetOldestKeyPoolTime()); obj.pushKV("keypoolsize", (int64_t)kpExternalSize); - CKeyID seed_id = pwallet->GetHDChain().seed_id; + + LegacyScriptPubKeyMan* spk_man = pwallet->GetLegacyScriptPubKeyMan(); + if (spk_man) { + CKeyID seed_id = spk_man->GetHDChain().seed_id; + if (!seed_id.IsNull()) { + obj.pushKV("hdseedid", seed_id.GetHex()); + } + } + if (pwallet->CanSupportFeature(FEATURE_HD_SPLIT)) { obj.pushKV("keypoolsize_hd_internal", (int64_t)(pwallet->GetKeyPoolSize() - kpExternalSize)); } @@ -2457,9 +2480,6 @@ static UniValue getwalletinfo(const JSONRPCRequest& request) obj.pushKV("unlocked_until", pwallet->nRelockTime); } obj.pushKV("paytxfee", ValueFromAmount(pwallet->m_pay_tx_fee.GetFeePerK())); - if (!seed_id.IsNull()) { - obj.pushKV("hdseedid", seed_id.GetHex()); - } obj.pushKV("private_keys_enabled", !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); obj.pushKV("avoid_reuse", pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)); if (pwallet->IsScanning()) { @@ -2574,7 +2594,7 @@ static UniValue loadwallet(const JSONRPCRequest& request) std::string error; std::vector<std::string> warning; - std::shared_ptr<CWallet> const wallet = LoadWallet(*g_rpc_interfaces->chain, location, error, warning); + std::shared_ptr<CWallet> const wallet = LoadWallet(*g_rpc_chain, location, error, warning); if (!wallet) throw JSONRPCError(RPC_WALLET_ERROR, error); UniValue obj(UniValue::VOBJ); @@ -2700,7 +2720,7 @@ static UniValue createwallet(const JSONRPCRequest& request) std::string error; std::shared_ptr<CWallet> wallet; - WalletCreationStatus status = CreateWallet(*g_rpc_interfaces->chain, passphrase, flags, request.params[0].get_str(), error, warnings, wallet); + WalletCreationStatus status = CreateWallet(*g_rpc_chain, passphrase, flags, request.params[0].get_str(), error, warnings, wallet); switch (status) { case WalletCreationStatus::CREATION_FAILED: throw JSONRPCError(RPC_WALLET_ERROR, error); @@ -2920,22 +2940,23 @@ static UniValue listunspent(const JSONRPCRequest& request) entry.pushKV("label", i->second.name); } + const SigningProvider* provider = pwallet->GetSigningProvider(); if (scriptPubKey.IsPayToScriptHash()) { const CScriptID& hash = CScriptID(boost::get<ScriptHash>(address)); CScript redeemScript; - if (pwallet->GetCScript(hash, redeemScript)) { + if (provider->GetCScript(hash, redeemScript)) { entry.pushKV("redeemScript", HexStr(redeemScript.begin(), redeemScript.end())); // Now check if the redeemScript is actually a P2WSH script CTxDestination witness_destination; if (redeemScript.IsPayToWitnessScriptHash()) { bool extracted = ExtractDestination(redeemScript, witness_destination); - assert(extracted); + CHECK_NONFATAL(extracted); // Also return the witness script const WitnessV0ScriptHash& whash = boost::get<WitnessV0ScriptHash>(witness_destination); CScriptID id; CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin()); CScript witnessScript; - if (pwallet->GetCScript(id, witnessScript)) { + if (provider->GetCScript(id, witnessScript)) { entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end())); } } @@ -2945,7 +2966,7 @@ static UniValue listunspent(const JSONRPCRequest& request) CScriptID id; CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin()); CScript witnessScript; - if (pwallet->GetCScript(id, witnessScript)) { + if (provider->GetCScript(id, witnessScript)) { entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end())); } } @@ -2957,7 +2978,7 @@ static UniValue listunspent(const JSONRPCRequest& request) entry.pushKV("spendable", out.fSpendable); entry.pushKV("solvable", out.fSolvable); if (out.fSolvable) { - auto descriptor = InferDescriptor(scriptPubKey, *pwallet); + auto descriptor = InferDescriptor(scriptPubKey, *pwallet->GetLegacyScriptPubKeyMan()); entry.pushKV("desc", descriptor->ToString()); } if (avoid_reuse) entry.pushKV("reused", reused); @@ -3267,7 +3288,7 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request) // Parse the prevtxs array ParsePrevouts(request.params[1], nullptr, coins); - return SignTransaction(mtx, pwallet, coins, request.params[2]); + return SignTransaction(mtx, &*pwallet->GetLegacyScriptPubKeyMan(), coins, request.params[2]); } static UniValue bumpfee(const JSONRPCRequest& request) @@ -3539,7 +3560,7 @@ UniValue rescanblockchain(const JSONRPCRequest& request) class DescribeWalletAddressVisitor : public boost::static_visitor<UniValue> { public: - CWallet * const pwallet; + const SigningProvider * const provider; void ProcessSubScript(const CScript& subscript, UniValue& obj) const { @@ -3575,7 +3596,7 @@ public: } } - explicit DescribeWalletAddressVisitor(CWallet* _pwallet) : pwallet(_pwallet) {} + explicit DescribeWalletAddressVisitor(const SigningProvider* _provider) : provider(_provider) {} UniValue operator()(const CNoDestination& dest) const { return UniValue(UniValue::VOBJ); } @@ -3584,7 +3605,7 @@ public: CKeyID keyID(pkhash); UniValue obj(UniValue::VOBJ); CPubKey vchPubKey; - if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) { + if (provider && provider->GetPubKey(keyID, vchPubKey)) { obj.pushKV("pubkey", HexStr(vchPubKey)); obj.pushKV("iscompressed", vchPubKey.IsCompressed()); } @@ -3596,7 +3617,7 @@ public: CScriptID scriptID(scripthash); UniValue obj(UniValue::VOBJ); CScript subscript; - if (pwallet && pwallet->GetCScript(scriptID, subscript)) { + if (provider && provider->GetCScript(scriptID, subscript)) { ProcessSubScript(subscript, obj); } return obj; @@ -3606,7 +3627,7 @@ public: { UniValue obj(UniValue::VOBJ); CPubKey pubkey; - if (pwallet && pwallet->GetPubKey(CKeyID(id), pubkey)) { + if (provider && provider->GetPubKey(CKeyID(id), pubkey)) { obj.pushKV("pubkey", HexStr(pubkey)); } return obj; @@ -3619,7 +3640,7 @@ public: CRIPEMD160 hasher; uint160 hash; hasher.Write(id.begin(), 32).Finalize(hash.begin()); - if (pwallet && pwallet->GetCScript(CScriptID(hash), subscript)) { + if (provider && provider->GetCScript(CScriptID(hash), subscript)) { ProcessSubScript(subscript, obj); } return obj; @@ -3632,8 +3653,12 @@ static UniValue DescribeWalletAddress(CWallet* pwallet, const CTxDestination& de { UniValue ret(UniValue::VOBJ); UniValue detail = DescribeAddress(dest); + const SigningProvider* provider = nullptr; + if (pwallet) { + provider = pwallet->GetSigningProvider(); + } ret.pushKVs(detail); - ret.pushKVs(boost::apply_visitor(DescribeWalletAddressVisitor(pwallet), dest)); + ret.pushKVs(boost::apply_visitor(DescribeWalletAddressVisitor(provider), dest)); return ret; } @@ -3722,13 +3747,14 @@ UniValue getaddressinfo(const JSONRPCRequest& request) CScript scriptPubKey = GetScriptForDestination(dest); ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end())); + const SigningProvider* provider = pwallet->GetSigningProvider(); - isminetype mine = IsMine(*pwallet, dest); + isminetype mine = pwallet->IsMine(dest); ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE)); - bool solvable = IsSolvable(*pwallet, scriptPubKey); + bool solvable = IsSolvable(*provider, scriptPubKey); ret.pushKV("solvable", solvable); if (solvable) { - ret.pushKV("desc", InferDescriptor(scriptPubKey, *pwallet)->ToString()); + ret.pushKV("desc", InferDescriptor(scriptPubKey, *provider)->ToString()); } ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY)); UniValue detail = DescribeWalletAddress(pwallet, dest); @@ -3737,26 +3763,16 @@ UniValue getaddressinfo(const JSONRPCRequest& request) ret.pushKV("label", pwallet->mapAddressBook[dest].name); } ret.pushKV("ischange", pwallet->IsChange(scriptPubKey)); - const CKeyMetadata* meta = nullptr; - CKeyID key_id = GetKeyForDestination(*pwallet, dest); - if (!key_id.IsNull()) { - auto it = pwallet->mapKeyMetadata.find(key_id); - if (it != pwallet->mapKeyMetadata.end()) { - meta = &it->second; - } - } - if (!meta) { - auto it = pwallet->m_script_metadata.find(CScriptID(scriptPubKey)); - if (it != pwallet->m_script_metadata.end()) { - meta = &it->second; - } - } - if (meta) { - ret.pushKV("timestamp", meta->nCreateTime); - if (meta->has_key_origin) { - ret.pushKV("hdkeypath", WriteHDKeypath(meta->key_origin.path)); - ret.pushKV("hdseedid", meta->hd_seed_id.GetHex()); - ret.pushKV("hdmasterfingerprint", HexStr(meta->key_origin.fingerprint, meta->key_origin.fingerprint + 4)); + + ScriptPubKeyMan* spk_man = pwallet->GetScriptPubKeyMan(); + if (spk_man) { + if (const CKeyMetadata* meta = spk_man->GetMetadata(dest)) { + ret.pushKV("timestamp", meta->nCreateTime); + if (meta->has_key_origin) { + ret.pushKV("hdkeypath", WriteHDKeypath(meta->key_origin.path)); + ret.pushKV("hdseedid", meta->hd_seed_id.GetHex()); + ret.pushKV("hdmasterfingerprint", HexStr(meta->key_origin.fingerprint, meta->key_origin.fingerprint + 4)); + } } } @@ -3814,7 +3830,7 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request) // address strings, but build a separate set as a precaution just in // case it does. bool unique = addresses.emplace(address).second; - assert(unique); + CHECK_NONFATAL(unique); // UniValue::pushKV checks if the key exists in O(N) // and since duplicate addresses are unexpected (checked with // std::set in O(log(N))), UniValue::__pushKV is used instead, @@ -3916,6 +3932,8 @@ UniValue sethdseed(const JSONRPCRequest& request) }, }.Check(request); + LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet); + if (pwallet->chain().isInitialBlockDownload()) { throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Cannot set a new HD seed while still in Initial Block Download"); } @@ -3941,22 +3959,22 @@ UniValue sethdseed(const JSONRPCRequest& request) CPubKey master_pub_key; if (request.params[1].isNull()) { - master_pub_key = pwallet->GenerateNewSeed(); + master_pub_key = spk_man.GenerateNewSeed(); } else { CKey key = DecodeSecret(request.params[1].get_str()); if (!key.IsValid()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); } - if (HaveKey(*pwallet, key)) { + if (HaveKey(spk_man, key)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key (either as an HD seed or as a loose private key)"); } - master_pub_key = pwallet->DeriveNewSeed(key); + master_pub_key = spk_man.DeriveNewSeed(key); } - pwallet->SetHDSeed(master_pub_key); - if (flush_key_pool) pwallet->NewKeyPool(); + spk_man.SetHDSeed(master_pub_key); + if (flush_key_pool) spk_man.NewKeyPool(); return NullUniValue; } @@ -4231,3 +4249,5 @@ void RegisterWalletRPCCommands(interfaces::Chain& chain, std::vector<std::unique for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) handlers.emplace_back(chain.handleRpc(commands[vcidx])); } + +interfaces::Chain* g_rpc_chain = nullptr; |