Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: pull libbitcoin_server code out of wallet code 3/N #5743

Merged
merged 9 commits into from
Dec 17, 2023
8 changes: 8 additions & 0 deletions src/interfaces/chain.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,14 @@ class Chain
//! Check if transaction will be final given chain height current time.
virtual bool checkFinalTx(const CTransaction& tx) = 0;

//! Check if transaction is locked by InstantSendManager
virtual bool isInstantSendLockedTx(const uint256& hash) = 0;

//! Check if block is chainlocked.
virtual bool hasChainLock(int height, const uint256& hash) = 0;

//! Return list of MN Collateral from outputs
virtual std::vector<COutPoint> listMNCollaterials(const std::vector<std::pair<const CTransactionRef&, unsigned int>>& outputs) = 0;
//! Return whether node has the block and optionally return block metadata
//! or contents.
virtual bool findBlock(const uint256& hash, const FoundBlock& block={}) = 0;
Expand Down
28 changes: 28 additions & 0 deletions src/node/interfaces.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
#include <interfaces/chain.h>
#include <interfaces/handler.h>
#include <interfaces/wallet.h>
#include <llmq/chainlocks.h>
#include <llmq/context.h>
#include <llmq/instantsend.h>
#include <mapport.h>
#include <masternode/sync.h>
Expand Down Expand Up @@ -715,6 +717,32 @@ class ChainImpl : public Chain
assert(std::addressof(::ChainActive()) == std::addressof(m_node.chainman->ActiveChain()));
return CheckFinalTx(m_node.chainman->ActiveChain().Tip(), tx);
}
bool isInstantSendLockedTx(const uint256& hash) override
{
if (m_node.llmq_ctx == nullptr || m_node.llmq_ctx->isman == nullptr) return false;
return m_node.llmq_ctx->isman->IsLocked(hash);
}
bool hasChainLock(int height, const uint256& hash) override
{
if (m_node.llmq_ctx == nullptr || m_node.llmq_ctx->clhandler == nullptr) return false;
return m_node.llmq_ctx->clhandler->HasChainLock(height, hash);
}
std::vector<COutPoint> listMNCollaterials(const std::vector<std::pair<const CTransactionRef&, unsigned int>>& outputs) override
{
const CBlockIndex *tip = WITH_LOCK(::cs_main, return ::ChainActive().Tip());
CDeterministicMNList mnList{};
if (tip != nullptr && deterministicMNManager != nullptr) {
mnList = deterministicMNManager->GetListForBlock(tip);
}
std::vector<COutPoint> listRet;
for (const auto& [tx, index]: outputs) {
COutPoint nextOut{tx->GetHash(), index};
if (CDeterministicMNManager::IsProTxWithCollateral(tx, index) || mnList.HasMNByCollateral(nextOut)) {
listRet.emplace_back(nextOut);
}
}
return listRet;
}
bool findBlock(const uint256& hash, const FoundBlock& block) override
{
WAIT_LOCK(cs_main, lock);
Expand Down
14 changes: 2 additions & 12 deletions src/wallet/coinselection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
#include <util/system.h>
#include <util/moneystr.h>

#include <llmq/instantsend.h>
#include <coinjoin/coinjoin.h>

#include <optional>
Expand Down Expand Up @@ -391,18 +390,9 @@ std::vector<CInputCoin>::iterator OutputGroup::Discard(const CInputCoin& output)
return m_outputs.erase(it);
}

bool OutputGroup::IsLockedByInstantSend() const
bool OutputGroup::EligibleForSpending(const CoinEligibilityFilter& eligibility_filter, bool isISLocked) const
{
for (const auto& output : m_outputs) {
if (!llmq::quorumInstantSendManager->IsLocked(output.outpoint.hash))
return false;
}
return true;
}

bool OutputGroup::EligibleForSpending(const CoinEligibilityFilter& eligibility_filter) const
{
return (m_depth >= (m_from_me ? eligibility_filter.conf_mine : eligibility_filter.conf_theirs) || IsLockedByInstantSend())
return (m_depth >= (m_from_me ? eligibility_filter.conf_mine : eligibility_filter.conf_theirs) || isISLocked)
&& m_ancestors <= eligibility_filter.max_ancestors
&& m_descendants <= eligibility_filter.max_descendants;
}
Expand Down
3 changes: 1 addition & 2 deletions src/wallet/coinselection.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,7 @@ struct OutputGroup
}
void Insert(const CInputCoin& output, int depth, bool from_me, size_t ancestors, size_t descendants);
std::vector<CInputCoin>::iterator Discard(const CInputCoin& output);
bool IsLockedByInstantSend() const;
bool EligibleForSpending(const CoinEligibilityFilter& eligibility_filter) const;
bool EligibleForSpending(const CoinEligibilityFilter& eligibility_filter, bool isISLocked) const;

//! Update the OutputGroup's fee, long_term_fee, and effective_value based on the given feerates
void SetFees(const CFeeRate effective_feerate, const CFeeRate long_term_feerate);
Expand Down
2 changes: 1 addition & 1 deletion src/wallet/rpcdump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -638,7 +638,7 @@ UniValue importelectrumwallet(const JSONRPCRequest& request)
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();

if (fPruneMode)
if (pwallet->chain().havePruned())
throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled in pruned mode");

if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
Expand Down
3 changes: 1 addition & 2 deletions src/wallet/rpcwallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
#include <coinjoin/client.h>
#include <coinjoin/options.h>
#include <llmq/chainlocks.h>
#include <llmq/instantsend.h>

#include <stdint.h>

Expand Down Expand Up @@ -158,7 +157,7 @@ WalletContext& EnsureWalletContext(const CoreContext& context)
static void WalletTxToJSON(interfaces::Chain& chain, const CWalletTx& wtx, UniValue& entry)
{
int confirms = wtx.GetDepthInMainChain();
bool fLocked = llmq::quorumInstantSendManager->IsLocked(wtx.GetHash());
bool fLocked = chain.isInstantSendLockedTx(wtx.GetHash());
bool chainlock = false;
if (confirms > 0) {
chainlock = wtx.IsChainLocked();
Expand Down
8 changes: 3 additions & 5 deletions src/wallet/scriptpubkeyman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
#include <script/descriptor.h>
#include <script/sign.h>
#include <shutdown.h>
#include <ui_interface.h>
#include <util/bip32.h>
#include <util/strencodings.h>
#include <util/system.h>
Expand Down Expand Up @@ -1437,7 +1436,7 @@ bool LegacyScriptPubKeyMan::TopUpInner(unsigned int kpSize)
int64_t progress_report_time = GetTime();
WalletLogPrintf("%s\n", strMsg);
if (should_show_progress) {
uiInterface.ShowProgress(strMsg, 0, false);
m_storage.UpdateProgress(strMsg, 0);
}

bool fInternal = false;
Expand All @@ -1458,15 +1457,14 @@ bool LegacyScriptPubKeyMan::TopUpInner(unsigned int kpSize)
progress_report_time = GetTime();
WalletLogPrintf("Still topping up. At key %lld. Progress=%f\n", current_index, dProgress);
if (should_show_progress) {
uiInterface.ShowProgress(strMsg, static_cast<int>(dProgress), false);
m_storage.UpdateProgress(strMsg, static_cast<int>(dProgress));
}
}
if (ShutdownRequested()) break;
}
WalletLogPrintf("Keypool added %d keys, size=%u (%u internal)\n",
current_index + 1, setInternalKeyPool.size() + setExternalKeyPool.size(), setInternalKeyPool.size());
if (should_show_progress) {
uiInterface.ShowProgress("", 100, false);
m_storage.UpdateProgress("", 100);
}
}
NotifyCanGetAddressesChanged();
Expand Down
3 changes: 3 additions & 0 deletions src/wallet/scriptpubkeyman.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ class WalletStorage
virtual bool HasEncryptionKeys() const = 0;
virtual bool IsLocked(bool fForMixing = false) const = 0;

// for LegacyScriptPubKeyMan::TopUpInner needs:
virtual void UpdateProgress(const std::string&, int) = 0;

// methods below are unique from Dash due to different implementation of HD
virtual void NewKeyPoolCallback() = 0;
virtual void KeepDestinationCallback(bool erased) = 0;
Expand Down
69 changes: 42 additions & 27 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,13 @@

#include <coinjoin/client.h>
#include <coinjoin/options.h>
#include <evo/providertx.h>
#include <governance/governance.h>
#include <evo/deterministicmns.h>
#include <masternode/sync.h>

#include <univalue.h>

#include <evo/providertx.h>

#include <llmq/instantsend.h>
#include <llmq/chainlocks.h>
#include <algorithm>
#include <assert.h>

using interfaces::FoundBlock;
Expand Down Expand Up @@ -882,15 +879,16 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const CWalletTx::Confirmatio
wtx.nTimeSmart = ComputeTimeSmart(wtx);
AddToSpends(hash);

auto mnList = deterministicMNManager->GetListAtChainTip();
std::vector<std::pair<const CTransactionRef&, unsigned int>> outputs;
for(unsigned int i = 0; i < wtx.tx->vout.size(); ++i) {
if (IsMine(wtx.tx->vout[i]) && !IsSpent(hash, i)) {
setWalletUTXO.insert(COutPoint(hash, i));
if (deterministicMNManager->IsProTxWithCollateral(wtx.tx, i) || mnList.HasMNByCollateral(COutPoint(hash, i))) {
LockCoin(COutPoint(hash, i));
}
outputs.emplace_back(wtx.tx, i);
}
}
for (const auto& outPoint : m_chain->listMNCollaterials(outputs)) {
LockCoin(outPoint);
}
}

if (!fInsertedNew)
Expand All @@ -907,16 +905,19 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const CWalletTx::Confirmatio
assert(wtx.m_confirm.block_height == confirm.block_height);
}

auto mnList = deterministicMNManager->GetListAtChainTip();
for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i) {
std::vector<std::pair<const CTransactionRef&, unsigned int>> outputs;
for(unsigned int i = 0; i < wtx.tx->vout.size(); ++i) {
if (IsMine(wtx.tx->vout[i]) && !IsSpent(hash, i)) {
bool new_utxo = setWalletUTXO.insert(COutPoint(hash, i)).second;
if (new_utxo && (deterministicMNManager->IsProTxWithCollateral(wtx.tx, i) || mnList.HasMNByCollateral(COutPoint(hash, i)))) {
LockCoin(COutPoint(hash, i));
if (new_utxo) {
outputs.emplace_back(wtx.tx, i);
fUpdated = true;
}
fUpdated |= new_utxo;
}
}
for (const auto& outPoint : m_chain->listMNCollaterials(outputs)) {
LockCoin(outPoint);
}
}

//// debug print
Expand Down Expand Up @@ -2720,6 +2721,13 @@ struct CompareByPriority
}
};

static bool isGroupISLocked(const OutputGroup& group, interfaces::Chain& chain)
{
return std::all_of(group.m_outputs.begin(), group.m_outputs.end(), [&chain](const auto& output) {
return chain.isInstantSendLockedTx(output.outpoint.hash);
});
}

bool CWallet::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, CoinType nCoinType) const
{
Expand All @@ -2739,7 +2747,8 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibil

// Filter by the min conf specs and add to utxo_pool and calculate effective value
for (OutputGroup& group : groups) {
if (!group.EligibleForSpending(eligibility_filter)) continue;
bool isISLocked = isGroupISLocked(group, chain());
if (!group.EligibleForSpending(eligibility_filter, isISLocked)) continue;

if (coin_selection_params.m_subtract_fee_outputs) {
// Set the effective feerate to 0 as we don't want to use the effective value since the fees will be deducted from the output
Expand All @@ -2758,7 +2767,8 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibil
} else {
// Filter by the min conf specs and add to utxo_pool
for (const OutputGroup& group : groups) {
if (!group.EligibleForSpending(eligibility_filter)) continue;
bool isISLocked = isGroupISLocked(group, chain());
if (!group.EligibleForSpending(eligibility_filter, isISLocked)) continue;
utxo_pool.push_back(group);
}
bnb_used = false;
Expand Down Expand Up @@ -3856,18 +3866,19 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
// This avoids accidental spending of collaterals. They can still be unlocked manually if a spend is really intended.
void CWallet::AutoLockMasternodeCollaterals()
{
auto mnList = deterministicMNManager->GetListAtChainTip();
std::vector<std::pair<const CTransactionRef&, unsigned int>> outputs;

LOCK(cs_wallet);
for (const auto& pair : mapWallet) {
for (unsigned int i = 0; i < pair.second.tx->vout.size(); ++i) {
if (IsMine(pair.second.tx->vout[i]) && !IsSpent(pair.first, i)) {
if (deterministicMNManager->IsProTxWithCollateral(pair.second.tx, i) || mnList.HasMNByCollateral(COutPoint(pair.first, i))) {
LockCoin(COutPoint(pair.first, i));
}
outputs.emplace_back(pair.second.tx, i);
}
}
}
for (const auto& outPoint : m_chain->listMNCollaterials(outputs)) {
LockCoin(outPoint);
}
}

DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut)
Expand Down Expand Up @@ -4292,18 +4303,17 @@ void CWallet::ListLockedCoins(std::vector<COutPoint>& vOutpts) const

void CWallet::ListProTxCoins(std::vector<COutPoint>& vOutpts) const
{
auto mnList = deterministicMNManager->GetListAtChainTip();
std::vector<std::pair<const CTransactionRef&, unsigned int>> outputs;

AssertLockHeld(cs_wallet);
for (const auto &o : setWalletUTXO) {
auto it = mapWallet.find(o.hash);
if (it != mapWallet.end()) {
const auto &p = it->second;
if (deterministicMNManager->IsProTxWithCollateral(p.tx, o.n) || mnList.HasMNByCollateral(o)) {
vOutpts.emplace_back(o);
}
const auto &ptx = it->second;
outputs.emplace_back(ptx.tx, o.n);
}
}
vOutpts = m_chain->listMNCollaterials(outputs);
}

/** @} */ // end of Actions
Expand Down Expand Up @@ -5132,7 +5142,7 @@ bool CWalletTx::IsLockedByInstantSend() const
if (fIsChainlocked) {
fIsInstantSendLocked = false;
} else if (!fIsInstantSendLocked) {
fIsInstantSendLocked = llmq::quorumInstantSendManager->IsLocked(GetHash());
fIsInstantSendLocked = pwallet->chain().isInstantSendLockedTx(GetHash());
}
return fIsInstantSendLocked;
}
Expand All @@ -5145,7 +5155,7 @@ bool CWalletTx::IsChainLocked() const
bool active;
int height;
if (pwallet->chain().findBlock(m_confirm.hashBlock, FoundBlock().inActiveChain(active).height(height)) && active) {
fIsChainlocked = llmq::chainLocksHandler->HasChainLock(height, m_confirm.hashBlock);
fIsChainlocked = pwallet->chain().hasChainLock(height, m_confirm.hashBlock);
}
}
return fIsChainlocked;
Expand Down Expand Up @@ -5501,3 +5511,8 @@ bool CWallet::GenerateNewHDChainEncrypted(const SecureString& secureMnemonic, co

return false;
}

void CWallet::UpdateProgress(const std::string& title, int nProgress)
{
ShowProgress(title, nProgress);
}
2 changes: 2 additions & 0 deletions src/wallet/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -855,6 +855,8 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati
bool IsLocked(bool fForMixing = false) const override;
bool Lock(bool fForMixing = false);

void UpdateProgress(const std::string& title, int nProgress) override;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it'd be nice to use string_view here but I think ShowProgress probably wants a std::string

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, ShowProgress indeed uses std::string even in the latest version of bitcoin core:

    boost::signals2::signal<void (const std::string &title, int nProgress)> ShowProgress;

The strMsg that is used in scriptpubkeyman.cpp is also std::string, so, using string_view will add a new allocation here, I will keep it as it is.


std::map<uint256, CWalletTx> mapWallet GUARDED_BY(cs_wallet);

typedef std::multimap<int64_t, CWalletTx*> TxItems;
Expand Down