mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-06-21 22:31:21 +02:00
wallet: CreateTransactionInternal(): return out-params as (optional) struct
This commit is contained in:
parent
6b87fa540c
commit
c9fdaa5e3a
@ -656,12 +656,10 @@ static void DiscourageFeeSniping(CMutableTransaction& tx, FastRandomContext& rng
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool CreateTransactionInternal(
|
static std::optional<CreatedTransactionResult> CreateTransactionInternal(
|
||||||
CWallet& wallet,
|
CWallet& wallet,
|
||||||
const std::vector<CRecipient>& vecSend,
|
const std::vector<CRecipient>& vecSend,
|
||||||
CTransactionRef& tx,
|
int change_pos,
|
||||||
CAmount& nFeeRet,
|
|
||||||
int& nChangePosInOut,
|
|
||||||
bilingual_str& error,
|
bilingual_str& error,
|
||||||
const CCoinControl& coin_control,
|
const CCoinControl& coin_control,
|
||||||
FeeCalculation& fee_calc_out,
|
FeeCalculation& fee_calc_out,
|
||||||
@ -669,6 +667,11 @@ static bool CreateTransactionInternal(
|
|||||||
{
|
{
|
||||||
AssertLockHeld(wallet.cs_wallet);
|
AssertLockHeld(wallet.cs_wallet);
|
||||||
|
|
||||||
|
// out variables, to be packed into returned result structure
|
||||||
|
CTransactionRef tx;
|
||||||
|
CAmount nFeeRet;
|
||||||
|
int nChangePosInOut = change_pos;
|
||||||
|
|
||||||
FastRandomContext rng_fast;
|
FastRandomContext rng_fast;
|
||||||
CMutableTransaction txNew; // The resulting transaction that we make
|
CMutableTransaction txNew; // The resulting transaction that we make
|
||||||
|
|
||||||
@ -742,12 +745,12 @@ static bool CreateTransactionInternal(
|
|||||||
// provided one
|
// provided one
|
||||||
if (coin_control.m_feerate && coin_selection_params.m_effective_feerate > *coin_control.m_feerate) {
|
if (coin_control.m_feerate && coin_selection_params.m_effective_feerate > *coin_control.m_feerate) {
|
||||||
error = strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(FeeEstimateMode::SAT_VB), coin_selection_params.m_effective_feerate.ToString(FeeEstimateMode::SAT_VB));
|
error = strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(FeeEstimateMode::SAT_VB), coin_selection_params.m_effective_feerate.ToString(FeeEstimateMode::SAT_VB));
|
||||||
return false;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
if (feeCalc.reason == FeeReason::FALLBACK && !wallet.m_allow_fallback_fee) {
|
if (feeCalc.reason == FeeReason::FALLBACK && !wallet.m_allow_fallback_fee) {
|
||||||
// eventually allow a fallback fee
|
// eventually allow a fallback fee
|
||||||
error = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
|
error = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
|
||||||
return false;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the cost of change
|
// Calculate the cost of change
|
||||||
@ -774,7 +777,7 @@ static bool CreateTransactionInternal(
|
|||||||
if (IsDust(txout, wallet.chain().relayDustFee()))
|
if (IsDust(txout, wallet.chain().relayDustFee()))
|
||||||
{
|
{
|
||||||
error = _("Transaction amount too small");
|
error = _("Transaction amount too small");
|
||||||
return false;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
txNew.vout.push_back(txout);
|
txNew.vout.push_back(txout);
|
||||||
}
|
}
|
||||||
@ -791,7 +794,7 @@ static bool CreateTransactionInternal(
|
|||||||
std::optional<SelectionResult> result = SelectCoins(wallet, vAvailableCoins, /*nTargetValue=*/selection_target, coin_control, coin_selection_params);
|
std::optional<SelectionResult> result = SelectCoins(wallet, vAvailableCoins, /*nTargetValue=*/selection_target, coin_control, coin_selection_params);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
error = _("Insufficient funds");
|
error = _("Insufficient funds");
|
||||||
return false;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
TRACE5(coin_selection, selected_coins, wallet.GetName().c_str(), GetAlgorithmName(result->m_algo).c_str(), result->m_target, result->GetWaste(), result->GetSelectedValue());
|
TRACE5(coin_selection, selected_coins, wallet.GetName().c_str(), GetAlgorithmName(result->m_algo).c_str(), result->m_target, result->GetWaste(), result->GetSelectedValue());
|
||||||
|
|
||||||
@ -808,7 +811,7 @@ static bool CreateTransactionInternal(
|
|||||||
else if ((unsigned int)nChangePosInOut > txNew.vout.size())
|
else if ((unsigned int)nChangePosInOut > txNew.vout.size())
|
||||||
{
|
{
|
||||||
error = _("Transaction change output index out of range");
|
error = _("Transaction change output index out of range");
|
||||||
return false;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(nChangePosInOut != -1);
|
assert(nChangePosInOut != -1);
|
||||||
@ -836,7 +839,7 @@ static bool CreateTransactionInternal(
|
|||||||
int nBytes = tx_sizes.vsize;
|
int nBytes = tx_sizes.vsize;
|
||||||
if (nBytes == -1) {
|
if (nBytes == -1) {
|
||||||
error = _("Missing solving data for estimating transaction size");
|
error = _("Missing solving data for estimating transaction size");
|
||||||
return false;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
nFeeRet = coin_selection_params.m_effective_feerate.GetFee(nBytes);
|
nFeeRet = coin_selection_params.m_effective_feerate.GetFee(nBytes);
|
||||||
|
|
||||||
@ -900,7 +903,7 @@ static bool CreateTransactionInternal(
|
|||||||
} else {
|
} else {
|
||||||
error = _("The transaction amount is too small to send after the fee has been deducted");
|
error = _("The transaction amount is too small to send after the fee has been deducted");
|
||||||
}
|
}
|
||||||
return false;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
++i;
|
++i;
|
||||||
@ -910,12 +913,12 @@ static bool CreateTransactionInternal(
|
|||||||
|
|
||||||
// Give up if change keypool ran out and change is required
|
// Give up if change keypool ran out and change is required
|
||||||
if (scriptChange.empty() && nChangePosInOut != -1) {
|
if (scriptChange.empty() && nChangePosInOut != -1) {
|
||||||
return false;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sign && !wallet.SignTransaction(txNew)) {
|
if (sign && !wallet.SignTransaction(txNew)) {
|
||||||
error = _("Signing transaction failed");
|
error = _("Signing transaction failed");
|
||||||
return false;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the constructed transaction data.
|
// Return the constructed transaction data.
|
||||||
@ -926,19 +929,19 @@ static bool CreateTransactionInternal(
|
|||||||
(!sign && tx_sizes.weight > MAX_STANDARD_TX_WEIGHT))
|
(!sign && tx_sizes.weight > MAX_STANDARD_TX_WEIGHT))
|
||||||
{
|
{
|
||||||
error = _("Transaction too large");
|
error = _("Transaction too large");
|
||||||
return false;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nFeeRet > wallet.m_default_max_tx_fee) {
|
if (nFeeRet > wallet.m_default_max_tx_fee) {
|
||||||
error = TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED);
|
error = TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED);
|
||||||
return false;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
|
if (gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
|
||||||
// Lastly, ensure this tx will pass the mempool's chain limits
|
// Lastly, ensure this tx will pass the mempool's chain limits
|
||||||
if (!wallet.chain().checkChainLimits(tx)) {
|
if (!wallet.chain().checkChainLimits(tx)) {
|
||||||
error = _("Transaction has too long of a mempool chain");
|
error = _("Transaction has too long of a mempool chain");
|
||||||
return false;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -955,9 +958,11 @@ static bool CreateTransactionInternal(
|
|||||||
feeCalc.est.fail.start, feeCalc.est.fail.end,
|
feeCalc.est.fail.start, feeCalc.est.fail.end,
|
||||||
(feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool) > 0.0 ? 100 * feeCalc.est.fail.withinTarget / (feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool) : 0.0,
|
(feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool) > 0.0 ? 100 * feeCalc.est.fail.withinTarget / (feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool) : 0.0,
|
||||||
feeCalc.est.fail.withinTarget, feeCalc.est.fail.totalConfirmed, feeCalc.est.fail.inMempool, feeCalc.est.fail.leftMempool);
|
feeCalc.est.fail.withinTarget, feeCalc.est.fail.totalConfirmed, feeCalc.est.fail.inMempool, feeCalc.est.fail.leftMempool);
|
||||||
return true;
|
return CreatedTransactionResult(tx, nFeeRet, nChangePosInOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: also return std::optional<CreatedTransactionResult> here in order
|
||||||
|
// to eliminate the out parameters and to simplify the function
|
||||||
bool CreateTransaction(
|
bool CreateTransaction(
|
||||||
CWallet& wallet,
|
CWallet& wallet,
|
||||||
const std::vector<CRecipient>& vecSend,
|
const std::vector<CRecipient>& vecSend,
|
||||||
@ -983,7 +988,14 @@ bool CreateTransaction(
|
|||||||
|
|
||||||
int nChangePosIn = nChangePosInOut;
|
int nChangePosIn = nChangePosInOut;
|
||||||
Assert(!tx); // tx is an out-param. TODO change the return type from bool to tx (or nullptr)
|
Assert(!tx); // tx is an out-param. TODO change the return type from bool to tx (or nullptr)
|
||||||
bool res = CreateTransactionInternal(wallet, vecSend, tx, nFeeRet, nChangePosInOut, error, coin_control, fee_calc_out, sign);
|
std::optional<CreatedTransactionResult> txr_ungrouped = CreateTransactionInternal(wallet, vecSend, nChangePosInOut, error, coin_control, fee_calc_out, sign);
|
||||||
|
bool res = false;
|
||||||
|
if (txr_ungrouped) {
|
||||||
|
tx = txr_ungrouped->tx;
|
||||||
|
nFeeRet = txr_ungrouped->fee;
|
||||||
|
nChangePosInOut = txr_ungrouped->change_pos;
|
||||||
|
res = true;
|
||||||
|
}
|
||||||
TRACE4(coin_selection, normal_create_tx_internal, wallet.GetName().c_str(), res, nFeeRet, nChangePosInOut);
|
TRACE4(coin_selection, normal_create_tx_internal, wallet.GetName().c_str(), res, nFeeRet, nChangePosInOut);
|
||||||
// try with avoidpartialspends unless it's enabled already
|
// try with avoidpartialspends unless it's enabled already
|
||||||
if (res && nFeeRet > 0 /* 0 means non-functional fee rate estimation */ && wallet.m_max_aps_fee > -1 && !coin_control.m_avoid_partial_spends) {
|
if (res && nFeeRet > 0 /* 0 means non-functional fee rate estimation */ && wallet.m_max_aps_fee > -1 && !coin_control.m_avoid_partial_spends) {
|
||||||
@ -994,7 +1006,12 @@ bool CreateTransaction(
|
|||||||
CTransactionRef tx2;
|
CTransactionRef tx2;
|
||||||
int nChangePosInOut2 = nChangePosIn;
|
int nChangePosInOut2 = nChangePosIn;
|
||||||
bilingual_str error2; // fired and forgotten; if an error occurs, we discard the results
|
bilingual_str error2; // fired and forgotten; if an error occurs, we discard the results
|
||||||
if (CreateTransactionInternal(wallet, vecSend, tx2, nFeeRet2, nChangePosInOut2, error2, tmp_cc, fee_calc_out, sign)) {
|
std::optional<CreatedTransactionResult> txr_grouped = CreateTransactionInternal(wallet, vecSend, nChangePosInOut2, error2, tmp_cc, fee_calc_out, sign);
|
||||||
|
if (txr_grouped) {
|
||||||
|
tx2 = txr_grouped->tx;
|
||||||
|
nFeeRet2 = txr_grouped->fee;
|
||||||
|
nChangePosInOut2 = txr_grouped->change_pos;
|
||||||
|
|
||||||
// if fee of this alternative one is within the range of the max fee, we use this one
|
// if fee of this alternative one is within the range of the max fee, we use this one
|
||||||
const bool use_aps = nFeeRet2 <= nFeeRet + wallet.m_max_aps_fee;
|
const bool use_aps = nFeeRet2 <= nFeeRet + wallet.m_max_aps_fee;
|
||||||
wallet.WalletLogPrintf("Fee non-grouped = %lld, grouped = %lld, using %s\n", nFeeRet, nFeeRet2, use_aps ? "grouped" : "non-grouped");
|
wallet.WalletLogPrintf("Fee non-grouped = %lld, grouped = %lld, using %s\n", nFeeRet, nFeeRet2, use_aps ? "grouped" : "non-grouped");
|
||||||
|
@ -82,6 +82,16 @@ std::optional<SelectionResult> AttemptSelection(const CWallet& wallet, const CAm
|
|||||||
std::optional<SelectionResult> SelectCoins(const CWallet& wallet, const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, const CCoinControl& coin_control,
|
std::optional<SelectionResult> SelectCoins(const CWallet& wallet, const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, const CCoinControl& coin_control,
|
||||||
const CoinSelectionParams& coin_selection_params) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
|
const CoinSelectionParams& coin_selection_params) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
|
||||||
|
|
||||||
|
struct CreatedTransactionResult
|
||||||
|
{
|
||||||
|
CTransactionRef tx;
|
||||||
|
CAmount fee;
|
||||||
|
int change_pos;
|
||||||
|
|
||||||
|
CreatedTransactionResult(CTransactionRef tx, CAmount fee, int change_pos)
|
||||||
|
: tx(tx), fee(fee), change_pos(change_pos) {}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new transaction paying the recipients with a set of coins
|
* Create a new transaction paying the recipients with a set of coins
|
||||||
* selected by SelectCoins(); Also create the change output, when needed
|
* selected by SelectCoins(); Also create the change output, when needed
|
||||||
|
Loading…
x
Reference in New Issue
Block a user