mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-14 12:43:40 +02:00
Allow Coin Selection be able to take external inputs
This commit is contained in:
parent
698340b62a
commit
8258aaa733
4 changed files with 79 additions and 29 deletions
|
|
@ -19,5 +19,7 @@ void CCoinControl::SetNull()
|
|||
m_confirm_target.reset();
|
||||
m_signal_bip125_rbf.reset();
|
||||
m_fee_mode = FeeEstimateMode::UNSET;
|
||||
m_external_txouts.clear();
|
||||
m_external_provider = FlatSigningProvider();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ public:
|
|||
bool m_avoid_partial_spends;
|
||||
//! Fee estimation mode to control arguments to estimateSmartFee
|
||||
FeeEstimateMode m_fee_mode;
|
||||
//! SigningProvider that has pubkeys and scripts to do spend size estimation for external inputs
|
||||
FlatSigningProvider m_external_provider;
|
||||
|
||||
CCoinControl()
|
||||
{
|
||||
|
|
@ -55,11 +57,32 @@ public:
|
|||
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)
|
||||
{
|
||||
setSelected.insert(output);
|
||||
}
|
||||
|
||||
void SelectExternal(const COutPoint& outpoint, const CTxOut& txout)
|
||||
{
|
||||
setSelected.insert(outpoint);
|
||||
m_external_txouts.emplace(outpoint, txout);
|
||||
}
|
||||
|
||||
void UnSelect(const COutPoint& output)
|
||||
{
|
||||
setSelected.erase(output);
|
||||
|
|
@ -77,6 +100,7 @@ public:
|
|||
|
||||
private:
|
||||
std::set<COutPoint> setSelected;
|
||||
std::map<COutPoint, CTxOut> m_external_txouts;
|
||||
};
|
||||
|
||||
#endif // BITCOIN_WALLET_COINCONTROL_H
|
||||
|
|
|
|||
|
|
@ -1626,13 +1626,13 @@ int64_t CWalletTx::GetTxTime() const
|
|||
|
||||
// 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
|
||||
bool CWallet::DummySignInput(CMutableTransaction& tx, const size_t nIn, const CTxOut& txout, bool use_max_sig) const
|
||||
static bool DummySignInput(const SigningProvider* provider, CMutableTransaction& tx, const size_t nIn, const CTxOut& txout, bool use_max_sig)
|
||||
{
|
||||
// Fill in dummy signatures for fee calculation.
|
||||
const CScript& scriptPubKey = txout.scriptPubKey;
|
||||
SignatureData sigdata;
|
||||
|
||||
if (!ProduceSignature(*this, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigdata)) {
|
||||
if (!ProduceSignature(*provider, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigdata)) {
|
||||
return false;
|
||||
}
|
||||
UpdateTransaction(tx, nIn, sigdata);
|
||||
|
|
@ -1640,14 +1640,18 @@ bool CWallet::DummySignInput(CMutableTransaction& tx, const size_t nIn, const CT
|
|||
}
|
||||
|
||||
// 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.
|
||||
int nIn = 0;
|
||||
for (const auto& txout : txouts)
|
||||
{
|
||||
if (!DummySignInput(txNew, nIn, txout, use_max_sig)) {
|
||||
return false;
|
||||
// Use max sig if watch only inputs were used or if this particular input is an external input
|
||||
bool use_max_sig = coin_control && (coin_control->fAllowWatchOnly || (coin_control && coin_control->IsExternalSelected(txNew.vin[nIn].prevout)));
|
||||
if (!DummySignInput(this, txNew, nIn, txout, use_max_sig)) {
|
||||
if (!coin_control || !DummySignInput(&coin_control->m_external_provider, txNew, nIn, txout, use_max_sig)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
nIn++;
|
||||
|
|
@ -1655,28 +1659,33 @@ bool CWallet::DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut>
|
|||
return true;
|
||||
}
|
||||
|
||||
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig)
|
||||
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const CCoinControl* coin_control)
|
||||
{
|
||||
std::vector<CTxOut> txouts;
|
||||
// Look up the inputs. We should have already checked that this transaction
|
||||
// IsAllFromMe(ISMINE_SPENDABLE), so every input should already be in our
|
||||
// wallet, with a valid index into the vout array, and the ability to sign.
|
||||
// Look up the inputs. The inputs are either in the wallet, or in coin_control.
|
||||
for (const CTxIn& input : tx.vin) {
|
||||
const auto mi = wallet->mapWallet.find(input.prevout.hash);
|
||||
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[input.prevout.n]);
|
||||
} else if (coin_control) {
|
||||
CTxOut txout;
|
||||
if (!coin_control->GetExternalOutput(input.prevout, txout)) {
|
||||
return -1;
|
||||
}
|
||||
txouts.emplace_back(txout);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
assert(input.prevout.n < mi->second.tx->vout.size());
|
||||
txouts.emplace_back(mi->second.tx->vout[input.prevout.n]);
|
||||
}
|
||||
return CalculateMaximumSignedTxSize(tx, wallet, txouts, use_max_sig);
|
||||
return CalculateMaximumSignedTxSize(tx, wallet, txouts, coin_control);
|
||||
}
|
||||
|
||||
// txouts needs to be in the order of tx.vin
|
||||
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig)
|
||||
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, const CCoinControl* coin_control)
|
||||
{
|
||||
CMutableTransaction txNew(tx);
|
||||
if (!wallet->DummySignTx(txNew, txouts, use_max_sig)) {
|
||||
if (!wallet->DummySignTx(txNew, txouts, coin_control)) {
|
||||
// This should never happen, because IsAllFromMe(ISMINE_SPENDABLE)
|
||||
// implies that we can sign for every input.
|
||||
return -1;
|
||||
|
|
@ -1688,7 +1697,7 @@ int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* wallet,
|
|||
{
|
||||
CMutableTransaction txn;
|
||||
txn.vin.push_back(CTxIn(COutPoint()));
|
||||
if (!wallet->DummySignInput(txn, 0, txout, use_max_sig)) {
|
||||
if (!DummySignInput(wallet, txn, 0, txout, use_max_sig)) {
|
||||
// This should never happen, because IsAllFromMe(ISMINE_SPENDABLE)
|
||||
// implies that we can sign for every input.
|
||||
return -1;
|
||||
|
|
@ -2690,8 +2699,18 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
|
|||
}
|
||||
mapValueFromPresetInputs[pcoin->GetOutputAsset(outpoint.n)] += amt;
|
||||
setPresetCoins.insert(CInputCoin(pcoin, outpoint.n));
|
||||
} else
|
||||
return false; // TODO: Allow non-wallet inputs
|
||||
} else {
|
||||
CTxOut txout;
|
||||
if (coin_control.GetExternalOutput(outpoint, txout)) {
|
||||
if (!txout.nValue.IsExplicit() || !txout.nAsset.IsExplicit()) {
|
||||
return false; // We can't get its value, so abort
|
||||
}
|
||||
mapValueFromPresetInputs[txout.nAsset.GetAsset()] += txout.nValue.GetAmount();
|
||||
setPresetCoins.insert(CInputCoin(outpoint, txout));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove preset inputs from vCoins
|
||||
|
|
@ -2824,8 +2843,11 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
|
|||
coinControl.ListSelected(vPresetInputs);
|
||||
for (const COutPoint& presetInput : vPresetInputs) {
|
||||
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(presetInput.hash);
|
||||
CTxOut txout;
|
||||
if (it != mapWallet.end()) {
|
||||
setAssets.insert(it->second.GetOutputAsset(presetInput.n));
|
||||
} else if (coinControl.GetExternalOutput(presetInput, txout)) {
|
||||
setAssets.insert(txout.nAsset.GetAsset());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3162,13 +3184,18 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
|
|||
std::vector<COutPoint> vPresetInputs;
|
||||
coin_control.ListSelected(vPresetInputs);
|
||||
for (const COutPoint& presetInput : vPresetInputs) {
|
||||
CAsset asset;
|
||||
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(presetInput.hash);
|
||||
if (it == mapWallet.end()) {
|
||||
CTxOut txout;
|
||||
if (it != mapWallet.end()) {
|
||||
asset = it->second.GetOutputAsset(presetInput.n);
|
||||
} else if (coin_control.GetExternalOutput(presetInput, txout)) {
|
||||
asset = txout.nAsset.GetAsset();
|
||||
} else {
|
||||
// Ignore this here, will fail more gracefully later.
|
||||
continue;
|
||||
}
|
||||
|
||||
CAsset asset = it->second.GetOutputAsset(presetInput.n);
|
||||
if (mapScriptChange.find(asset) != mapScriptChange.end()) {
|
||||
// This asset already has a change script.
|
||||
continue;
|
||||
|
|
@ -3509,7 +3536,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
|
|||
}
|
||||
}
|
||||
|
||||
nBytes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly);
|
||||
nBytes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, &coin_control);
|
||||
if (nBytes < 0) {
|
||||
strFailReason = _("Signing transaction failed");
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -1084,14 +1084,13 @@ public:
|
|||
std::string& strFailReason, const CCoinControl& coin_control, bool sign = true, BlindDetails* blind_details = nullptr, const IssuanceDetails* issuance_details = nullptr);
|
||||
bool CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, std::vector<std::unique_ptr<CReserveKey>>& reservekey, CConnman* connman, CValidationState& state, const BlindDetails* blind_details = nullptr);
|
||||
|
||||
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::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 DummySignInput(CMutableTransaction &tx, const size_t nIn, const CTxOut &txout, bool use_max_sig = false) const;
|
||||
bool DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts, const CCoinControl* coin_control = nullptr) const;
|
||||
|
||||
CFeeRate m_pay_tx_fee{DEFAULT_PAY_TX_FEE};
|
||||
unsigned int m_confirm_target{DEFAULT_TX_CONFIRM_TARGET};
|
||||
|
|
@ -1445,8 +1444,6 @@ public:
|
|||
|
||||
// Calculate the size of the transaction assuming all signatures are max size
|
||||
// Use DummySignatureCreator, which inserts 71 byte signatures everywhere.
|
||||
// NOTE: this requires that all inputs must be in mapWallet (eg the tx should
|
||||
// be IsAllFromMe).
|
||||
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig = false) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet);
|
||||
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig = false);
|
||||
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const CCoinControl* coin_control = nullptr) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet);
|
||||
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, const CCoinControl* coin_control = nullptr);
|
||||
#endif // BITCOIN_WALLET_WALLET_H
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue