mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-05-05 09:21:01 +02:00
Allow Coin Selection be able to take external inputs
This commit is contained in:
parent
a00eb388e8
commit
d5cfb864ae
@ -9,9 +9,14 @@
|
|||||||
#include <policy/feerate.h>
|
#include <policy/feerate.h>
|
||||||
#include <policy/fees.h>
|
#include <policy/fees.h>
|
||||||
#include <primitives/transaction.h>
|
#include <primitives/transaction.h>
|
||||||
|
#include <script/keyorigin.h>
|
||||||
|
#include <script/signingprovider.h>
|
||||||
#include <script/standard.h>
|
#include <script/standard.h>
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
const int DEFAULT_MIN_DEPTH = 0;
|
const int DEFAULT_MIN_DEPTH = 0;
|
||||||
const int DEFAULT_MAX_DEPTH = 9999999;
|
const int DEFAULT_MAX_DEPTH = 9999999;
|
||||||
@ -53,6 +58,8 @@ public:
|
|||||||
int m_min_depth = DEFAULT_MIN_DEPTH;
|
int m_min_depth = DEFAULT_MIN_DEPTH;
|
||||||
//! Maximum chain depth value for coin availability
|
//! Maximum chain depth value for coin availability
|
||||||
int m_max_depth = DEFAULT_MAX_DEPTH;
|
int m_max_depth = DEFAULT_MAX_DEPTH;
|
||||||
|
//! SigningProvider that has pubkeys and scripts to do spend size estimation for external inputs
|
||||||
|
FlatSigningProvider m_external_provider;
|
||||||
|
|
||||||
CCoinControl();
|
CCoinControl();
|
||||||
|
|
||||||
@ -66,11 +73,32 @@ public:
|
|||||||
return (setSelected.count(output) > 0);
|
return (setSelected.count(output) > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsExternalSelected(const COutPoint& output) const
|
||||||
|
{
|
||||||
|
return (m_external_txouts.count(output) > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetExternalOutput(const COutPoint& outpoint, CTxOut& txout) const
|
||||||
|
{
|
||||||
|
const auto ext_it = m_external_txouts.find(outpoint);
|
||||||
|
if (ext_it == m_external_txouts.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
txout = ext_it->second;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void Select(const COutPoint& output)
|
void Select(const COutPoint& output)
|
||||||
{
|
{
|
||||||
setSelected.insert(output);
|
setSelected.insert(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Select(const COutPoint& outpoint, const CTxOut& txout)
|
||||||
|
{
|
||||||
|
setSelected.insert(outpoint);
|
||||||
|
m_external_txouts.emplace(outpoint, txout);
|
||||||
|
}
|
||||||
|
|
||||||
void UnSelect(const COutPoint& output)
|
void UnSelect(const COutPoint& output)
|
||||||
{
|
{
|
||||||
setSelected.erase(output);
|
setSelected.erase(output);
|
||||||
@ -88,6 +116,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::set<COutPoint> setSelected;
|
std::set<COutPoint> setSelected;
|
||||||
|
std::map<COutPoint, CTxOut> m_external_txouts;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // BITCOIN_WALLET_COINCONTROL_H
|
#endif // BITCOIN_WALLET_COINCONTROL_H
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <consensus/validation.h>
|
#include <consensus/validation.h>
|
||||||
#include <interfaces/chain.h>
|
#include <interfaces/chain.h>
|
||||||
#include <policy/policy.h>
|
#include <policy/policy.h>
|
||||||
|
#include <script/signingprovider.h>
|
||||||
#include <util/check.h>
|
#include <util/check.h>
|
||||||
#include <util/fees.h>
|
#include <util/fees.h>
|
||||||
#include <util/moneystr.h>
|
#include <util/moneystr.h>
|
||||||
@ -31,21 +32,27 @@ std::string COutput::ToString() const
|
|||||||
return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue));
|
return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* wallet, bool use_max_sig)
|
int CalculateMaximumSignedInputSize(const CTxOut& txout, const SigningProvider* provider, bool use_max_sig)
|
||||||
{
|
{
|
||||||
CMutableTransaction txn;
|
CMutableTransaction txn;
|
||||||
txn.vin.push_back(CTxIn(COutPoint()));
|
txn.vin.push_back(CTxIn(COutPoint()));
|
||||||
if (!wallet->DummySignInput(txn.vin[0], txout, use_max_sig)) {
|
if (!provider || !DummySignInput(*provider, txn.vin[0], txout, use_max_sig)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return GetVirtualTransactionInputSize(txn.vin[0]);
|
return GetVirtualTransactionInputSize(txn.vin[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* wallet, bool use_max_sig)
|
||||||
|
{
|
||||||
|
const std::unique_ptr<SigningProvider> provider = wallet->GetSolvingProvider(txout.scriptPubKey);
|
||||||
|
return CalculateMaximumSignedInputSize(txout, provider.get(), use_max_sig);
|
||||||
|
}
|
||||||
|
|
||||||
// txouts needs to be in the order of tx.vin
|
// txouts needs to be in the order of tx.vin
|
||||||
TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig)
|
TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, const CCoinControl* coin_control)
|
||||||
{
|
{
|
||||||
CMutableTransaction txNew(tx);
|
CMutableTransaction txNew(tx);
|
||||||
if (!wallet->DummySignTx(txNew, txouts, use_max_sig)) {
|
if (!wallet->DummySignTx(txNew, txouts, coin_control)) {
|
||||||
return TxSize{-1, -1};
|
return TxSize{-1, -1};
|
||||||
}
|
}
|
||||||
CTransaction ctx(txNew);
|
CTransaction ctx(txNew);
|
||||||
@ -54,19 +61,27 @@ TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *walle
|
|||||||
return TxSize{vsize, weight};
|
return TxSize{vsize, weight};
|
||||||
}
|
}
|
||||||
|
|
||||||
TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig)
|
TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const CCoinControl* coin_control)
|
||||||
{
|
{
|
||||||
std::vector<CTxOut> txouts;
|
std::vector<CTxOut> txouts;
|
||||||
|
// Look up the inputs. The inputs are either in the wallet, or in coin_control.
|
||||||
for (const CTxIn& input : tx.vin) {
|
for (const CTxIn& input : tx.vin) {
|
||||||
const auto mi = wallet->mapWallet.find(input.prevout.hash);
|
const auto mi = wallet->mapWallet.find(input.prevout.hash);
|
||||||
// Can not estimate size without knowing the input details
|
// Can not estimate size without knowing the input details
|
||||||
if (mi == wallet->mapWallet.end()) {
|
if (mi != wallet->mapWallet.end()) {
|
||||||
|
assert(input.prevout.n < mi->second.tx->vout.size());
|
||||||
|
txouts.emplace_back(mi->second.tx->vout.at(input.prevout.n));
|
||||||
|
} else if (coin_control) {
|
||||||
|
CTxOut txout;
|
||||||
|
if (!coin_control->GetExternalOutput(input.prevout, txout)) {
|
||||||
return TxSize{-1, -1};
|
return TxSize{-1, -1};
|
||||||
}
|
}
|
||||||
assert(input.prevout.n < mi->second.tx->vout.size());
|
txouts.emplace_back(txout);
|
||||||
txouts.emplace_back(mi->second.tx->vout[input.prevout.n]);
|
} else {
|
||||||
|
return TxSize{-1, -1};
|
||||||
}
|
}
|
||||||
return CalculateMaximumSignedTxSize(tx, wallet, txouts, use_max_sig);
|
}
|
||||||
|
return CalculateMaximumSignedTxSize(tx, wallet, txouts, coin_control);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount)
|
void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount)
|
||||||
@ -435,18 +450,29 @@ bool SelectCoins(const CWallet& wallet, const std::vector<COutput>& vAvailableCo
|
|||||||
|
|
||||||
std::vector<COutPoint> vPresetInputs;
|
std::vector<COutPoint> vPresetInputs;
|
||||||
coin_control.ListSelected(vPresetInputs);
|
coin_control.ListSelected(vPresetInputs);
|
||||||
for (const COutPoint& outpoint : vPresetInputs)
|
for (const COutPoint& outpoint : vPresetInputs) {
|
||||||
{
|
int input_bytes = -1;
|
||||||
|
CTxOut txout;
|
||||||
std::map<uint256, CWalletTx>::const_iterator it = wallet.mapWallet.find(outpoint.hash);
|
std::map<uint256, CWalletTx>::const_iterator it = wallet.mapWallet.find(outpoint.hash);
|
||||||
if (it != wallet.mapWallet.end())
|
if (it != wallet.mapWallet.end()) {
|
||||||
{
|
|
||||||
const CWalletTx& wtx = it->second;
|
const CWalletTx& wtx = it->second;
|
||||||
// Clearly invalid input, fail
|
// Clearly invalid input, fail
|
||||||
if (wtx.tx->vout.size() <= outpoint.n) {
|
if (wtx.tx->vout.size() <= outpoint.n) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Just to calculate the marginal byte size
|
input_bytes = GetTxSpendSize(wallet, wtx, outpoint.n, false);
|
||||||
CInputCoin coin(wtx.tx, outpoint.n, GetTxSpendSize(wallet, wtx, outpoint.n, false));
|
txout = wtx.tx->vout.at(outpoint.n);
|
||||||
|
}
|
||||||
|
if (input_bytes == -1) {
|
||||||
|
// The input is external. We either did not find the tx in mapWallet, or we did but couldn't compute the input size with wallet data
|
||||||
|
if (!coin_control.GetExternalOutput(outpoint, txout)) {
|
||||||
|
// Not ours, and we don't have solving data.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
input_bytes = CalculateMaximumSignedInputSize(txout, &coin_control.m_external_provider, /* use_max_sig */ true);
|
||||||
|
}
|
||||||
|
|
||||||
|
CInputCoin coin(outpoint, txout, input_bytes);
|
||||||
nValueFromPresetInputs += coin.txout.nValue;
|
nValueFromPresetInputs += coin.txout.nValue;
|
||||||
if (coin.m_input_bytes <= 0) {
|
if (coin.m_input_bytes <= 0) {
|
||||||
return false; // Not solvable, can't estimate size for fee
|
return false; // Not solvable, can't estimate size for fee
|
||||||
@ -458,9 +484,6 @@ bool SelectCoins(const CWallet& wallet, const std::vector<COutput>& vAvailableCo
|
|||||||
value_to_select -= coin.effective_value;
|
value_to_select -= coin.effective_value;
|
||||||
}
|
}
|
||||||
setPresetCoins.insert(coin);
|
setPresetCoins.insert(coin);
|
||||||
} else {
|
|
||||||
return false; // TODO: Allow non-wallet inputs
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove preset inputs from vCoins so that Coin Selection doesn't pick them.
|
// remove preset inputs from vCoins so that Coin Selection doesn't pick them.
|
||||||
@ -788,10 +811,10 @@ static bool CreateTransactionInternal(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the transaction fee
|
// Calculate the transaction fee
|
||||||
TxSize tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, coin_control.fAllowWatchOnly);
|
TxSize tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, &coin_control);
|
||||||
int nBytes = tx_sizes.vsize;
|
int nBytes = tx_sizes.vsize;
|
||||||
if (nBytes < 0) {
|
if (nBytes < 0) {
|
||||||
error = _("Signing transaction failed");
|
error = _("Missing solving data for estimating transaction size");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
nFeeRet = coin_selection_params.m_effective_feerate.GetFee(nBytes);
|
nFeeRet = coin_selection_params.m_effective_feerate.GetFee(nBytes);
|
||||||
@ -813,7 +836,7 @@ static bool CreateTransactionInternal(
|
|||||||
txNew.vout.erase(change_position);
|
txNew.vout.erase(change_position);
|
||||||
|
|
||||||
// Because we have dropped this change, the tx size and required fee will be different, so let's recalculate those
|
// Because we have dropped this change, the tx size and required fee will be different, so let's recalculate those
|
||||||
tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, coin_control.fAllowWatchOnly);
|
tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, &coin_control);
|
||||||
nBytes = tx_sizes.vsize;
|
nBytes = tx_sizes.vsize;
|
||||||
fee_needed = coin_selection_params.m_effective_feerate.GetFee(nBytes);
|
fee_needed = coin_selection_params.m_effective_feerate.GetFee(nBytes);
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,7 @@ public:
|
|||||||
|
|
||||||
//Get the marginal bytes of spending the specified output
|
//Get the marginal bytes of spending the specified output
|
||||||
int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* pwallet, bool use_max_sig = false);
|
int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* pwallet, bool use_max_sig = false);
|
||||||
|
int CalculateMaximumSignedInputSize(const CTxOut& txout, const SigningProvider* pwallet, bool use_max_sig = false);
|
||||||
|
|
||||||
struct TxSize {
|
struct TxSize {
|
||||||
int64_t vsize{-1};
|
int64_t vsize{-1};
|
||||||
@ -76,8 +77,8 @@ struct TxSize {
|
|||||||
* Use DummySignatureCreator, which inserts 71 byte signatures everywhere.
|
* Use DummySignatureCreator, which inserts 71 byte signatures everywhere.
|
||||||
* NOTE: this requires that all inputs must be in mapWallet (eg the tx should
|
* NOTE: this requires that all inputs must be in mapWallet (eg the tx should
|
||||||
* be AllInputsMine). */
|
* be AllInputsMine). */
|
||||||
TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, const std::vector<CTxOut>& txouts, bool use_max_sig = false);
|
TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, const std::vector<CTxOut>& txouts, const CCoinControl* coin_control = nullptr);
|
||||||
TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, bool use_max_sig = false) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet);
|
TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, const CCoinControl* coin_control = nullptr) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* populate vCoins with vector of available COutputs.
|
* populate vCoins with vector of available COutputs.
|
||||||
|
@ -1448,19 +1448,13 @@ bool CWallet::AddWalletFlags(uint64_t flags)
|
|||||||
|
|
||||||
// Helper for producing a max-sized low-S low-R signature (eg 71 bytes)
|
// Helper for producing a max-sized low-S low-R signature (eg 71 bytes)
|
||||||
// or a max-sized low-S signature (e.g. 72 bytes) if use_max_sig is true
|
// or a max-sized low-S signature (e.g. 72 bytes) if use_max_sig is true
|
||||||
bool CWallet::DummySignInput(CTxIn &tx_in, const CTxOut &txout, bool use_max_sig) const
|
bool DummySignInput(const SigningProvider& provider, CTxIn &tx_in, const CTxOut &txout, bool use_max_sig)
|
||||||
{
|
{
|
||||||
// Fill in dummy signatures for fee calculation.
|
// Fill in dummy signatures for fee calculation.
|
||||||
const CScript& scriptPubKey = txout.scriptPubKey;
|
const CScript& scriptPubKey = txout.scriptPubKey;
|
||||||
SignatureData sigdata;
|
SignatureData sigdata;
|
||||||
|
|
||||||
std::unique_ptr<SigningProvider> provider = GetSolvingProvider(scriptPubKey);
|
if (!ProduceSignature(provider, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigdata)) {
|
||||||
if (!provider) {
|
|
||||||
// We don't know about this scriptpbuKey;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ProduceSignature(*provider, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigdata)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
UpdateInput(tx_in, sigdata);
|
UpdateInput(tx_in, sigdata);
|
||||||
@ -1468,15 +1462,22 @@ bool CWallet::DummySignInput(CTxIn &tx_in, const CTxOut &txout, bool use_max_sig
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Helper for producing a bunch of max-sized low-S low-R signatures (eg 71 bytes)
|
// Helper for producing a bunch of max-sized low-S low-R signatures (eg 71 bytes)
|
||||||
bool CWallet::DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts, bool use_max_sig) const
|
bool CWallet::DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts, const CCoinControl* coin_control) const
|
||||||
{
|
{
|
||||||
// Fill in dummy signatures for fee calculation.
|
// Fill in dummy signatures for fee calculation.
|
||||||
int nIn = 0;
|
int nIn = 0;
|
||||||
for (const auto& txout : txouts)
|
for (const auto& txout : txouts)
|
||||||
{
|
{
|
||||||
if (!DummySignInput(txNew.vin[nIn], txout, use_max_sig)) {
|
CTxIn& txin = txNew.vin[nIn];
|
||||||
|
// Use max sig if watch only inputs were used or if this particular input is an external input
|
||||||
|
// to ensure a sufficient fee is attained for the requested feerate.
|
||||||
|
const bool use_max_sig = coin_control && (coin_control->fAllowWatchOnly || coin_control->IsExternalSelected(txin.prevout));
|
||||||
|
const std::unique_ptr<SigningProvider> provider = GetSolvingProvider(txout.scriptPubKey);
|
||||||
|
if (!provider || !DummySignInput(*provider, txin, txout, use_max_sig)) {
|
||||||
|
if (!coin_control || !DummySignInput(coin_control->m_external_provider, txin, txout, use_max_sig)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
nIn++;
|
nIn++;
|
||||||
}
|
}
|
||||||
|
@ -576,14 +576,13 @@ public:
|
|||||||
/** Pass this transaction to node for mempool insertion and relay to peers if flag set to true */
|
/** Pass this transaction to node for mempool insertion and relay to peers if flag set to true */
|
||||||
bool SubmitTxMemoryPoolAndRelay(const CWalletTx& wtx, std::string& err_string, bool relay) const;
|
bool SubmitTxMemoryPoolAndRelay(const CWalletTx& wtx, std::string& err_string, bool relay) const;
|
||||||
|
|
||||||
bool DummySignTx(CMutableTransaction &txNew, const std::set<CTxOut> &txouts, bool use_max_sig = false) const
|
bool DummySignTx(CMutableTransaction &txNew, const std::set<CTxOut> &txouts, const CCoinControl* coin_control = nullptr) const
|
||||||
{
|
{
|
||||||
std::vector<CTxOut> v_txouts(txouts.size());
|
std::vector<CTxOut> v_txouts(txouts.size());
|
||||||
std::copy(txouts.begin(), txouts.end(), v_txouts.begin());
|
std::copy(txouts.begin(), txouts.end(), v_txouts.begin());
|
||||||
return DummySignTx(txNew, v_txouts, use_max_sig);
|
return DummySignTx(txNew, v_txouts, coin_control);
|
||||||
}
|
}
|
||||||
bool DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts, bool use_max_sig = false) const;
|
bool DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts, const CCoinControl* coin_control = nullptr) const;
|
||||||
bool DummySignInput(CTxIn &tx_in, const CTxOut &txout, bool use_max_sig = false) const;
|
|
||||||
|
|
||||||
bool ImportScripts(const std::set<CScript> scripts, int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
bool ImportScripts(const std::set<CScript> scripts, int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||||
bool ImportPrivKeys(const std::map<CKeyID, CKey>& privkey_map, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
bool ImportPrivKeys(const std::map<CKeyID, CKey>& privkey_map, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||||
@ -928,4 +927,6 @@ bool AddWalletSetting(interfaces::Chain& chain, const std::string& wallet_name);
|
|||||||
//! Remove wallet name from persistent configuration so it will not be loaded on startup.
|
//! Remove wallet name from persistent configuration so it will not be loaded on startup.
|
||||||
bool RemoveWalletSetting(interfaces::Chain& chain, const std::string& wallet_name);
|
bool RemoveWalletSetting(interfaces::Chain& chain, const std::string& wallet_name);
|
||||||
|
|
||||||
|
bool DummySignInput(const SigningProvider& provider, CTxIn &tx_in, const CTxOut &txout, bool use_max_sig);
|
||||||
|
|
||||||
#endif // BITCOIN_WALLET_WALLET_H
|
#endif // BITCOIN_WALLET_WALLET_H
|
||||||
|
Loading…
x
Reference in New Issue
Block a user