wallet: CreateTransactionInternal(): return out-params as (optional) struct

This commit is contained in:
Sebastian Falbesoner 2020-12-13 01:37:40 +01:00
parent 6b87fa540c
commit c9fdaa5e3a
2 changed files with 46 additions and 19 deletions

View file

@ -656,12 +656,10 @@ static void DiscourageFeeSniping(CMutableTransaction& tx, FastRandomContext& rng
}
}
static bool CreateTransactionInternal(
static std::optional<CreatedTransactionResult> CreateTransactionInternal(
CWallet& wallet,
const std::vector<CRecipient>& vecSend,
CTransactionRef& tx,
CAmount& nFeeRet,
int& nChangePosInOut,
int change_pos,
bilingual_str& error,
const CCoinControl& coin_control,
FeeCalculation& fee_calc_out,
@ -669,6 +667,11 @@ static bool CreateTransactionInternal(
{
AssertLockHeld(wallet.cs_wallet);
// out variables, to be packed into returned result structure
CTransactionRef tx;
CAmount nFeeRet;
int nChangePosInOut = change_pos;
FastRandomContext rng_fast;
CMutableTransaction txNew; // The resulting transaction that we make
@ -742,12 +745,12 @@ static bool CreateTransactionInternal(
// provided one
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));
return false;
return std::nullopt;
}
if (feeCalc.reason == FeeReason::FALLBACK && !wallet.m_allow_fallback_fee) {
// eventually allow a fallback fee
error = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
return false;
return std::nullopt;
}
// Calculate the cost of change
@ -774,7 +777,7 @@ static bool CreateTransactionInternal(
if (IsDust(txout, wallet.chain().relayDustFee()))
{
error = _("Transaction amount too small");
return false;
return std::nullopt;
}
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);
if (!result) {
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());
@ -808,7 +811,7 @@ static bool CreateTransactionInternal(
else if ((unsigned int)nChangePosInOut > txNew.vout.size())
{
error = _("Transaction change output index out of range");
return false;
return std::nullopt;
}
assert(nChangePosInOut != -1);
@ -836,7 +839,7 @@ static bool CreateTransactionInternal(
int nBytes = tx_sizes.vsize;
if (nBytes == -1) {
error = _("Missing solving data for estimating transaction size");
return false;
return std::nullopt;
}
nFeeRet = coin_selection_params.m_effective_feerate.GetFee(nBytes);
@ -900,7 +903,7 @@ static bool CreateTransactionInternal(
} else {
error = _("The transaction amount is too small to send after the fee has been deducted");
}
return false;
return std::nullopt;
}
}
++i;
@ -910,12 +913,12 @@ static bool CreateTransactionInternal(
// Give up if change keypool ran out and change is required
if (scriptChange.empty() && nChangePosInOut != -1) {
return false;
return std::nullopt;
}
if (sign && !wallet.SignTransaction(txNew)) {
error = _("Signing transaction failed");
return false;
return std::nullopt;
}
// Return the constructed transaction data.
@ -926,19 +929,19 @@ static bool CreateTransactionInternal(
(!sign && tx_sizes.weight > MAX_STANDARD_TX_WEIGHT))
{
error = _("Transaction too large");
return false;
return std::nullopt;
}
if (nFeeRet > wallet.m_default_max_tx_fee) {
error = TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED);
return false;
return std::nullopt;
}
if (gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
// Lastly, ensure this tx will pass the mempool's chain limits
if (!wallet.chain().checkChainLimits(tx)) {
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.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);
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(
CWallet& wallet,
const std::vector<CRecipient>& vecSend,
@ -983,7 +988,14 @@ bool CreateTransaction(
int nChangePosIn = nChangePosInOut;
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);
// 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) {
@ -994,7 +1006,12 @@ bool CreateTransaction(
CTransactionRef tx2;
int nChangePosInOut2 = nChangePosIn;
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
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");

View file

@ -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,
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
* selected by SelectCoins(); Also create the change output, when needed