Skip to content

Commit f0745d0

Browse files
committed
Merge bitcoin/bitcoin#30050: refactor, wallet: get serialized size of CRecipients directly
a9c7300 move-only: refactor CreateTransactionInternal (josibake) adc6ab2 wallet: use CRecipient instead of CTxOut (josibake) Pull request description: Broken out from #28201 --- In order to estimate fees properly, we need to know what the final serialized transaction size will be. This PR refactors `CreateTransactionInternal` to: * Get the serialized size directly from the `CRecipient`: this sets us up in a future PR to calculate the serialized size of silent payment `CTxDestinations` (see bitcoin/bitcoin@797e21c) * Use the new `GetSerializeSizeForRecipient` to move the serialize size calculation to *before* coin selection and the output creation to *after* coin selection: this also sets us up for silent payments sending in a future PR in that silent payments outputs cannot be created until after the inputs to the transaction have been selected Aside from the silent payments use case, I think this structure logically makes more sense. As a reminder, move-only commits are best reviewed with something like `git diff -w --color-moved=dimmed-zebra` ACKs for top commit: S3RK: reACK a9c7300 achow101: ACK a9c7300 rkrux: tACK [a9c7300](bitcoin/bitcoin@a9c7300) Tree-SHA512: 412e1764b98f7428c8530c3a68f55e32063d6b66ab2ff613e1c7e12d49b049807cb60055cfe7f7e8ffe7ac7f0f9931427cbfd3efe7d4f97a5a0f6d1bf1aaac58
2 parents b27afb7 + a9c7300 commit f0745d0

File tree

1 file changed

+23
-17
lines changed

1 file changed

+23
-17
lines changed

src/wallet/spend.cpp

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -983,6 +983,16 @@ static void DiscourageFeeSniping(CMutableTransaction& tx, FastRandomContext& rng
983983
}
984984
}
985985

986+
size_t GetSerializeSizeForRecipient(const CRecipient& recipient)
987+
{
988+
return ::GetSerializeSize(CTxOut(recipient.nAmount, GetScriptForDestination(recipient.dest)));
989+
}
990+
991+
bool IsDust(const CRecipient& recipient, const CFeeRate& dustRelayFee)
992+
{
993+
return ::IsDust(CTxOut(recipient.nAmount, GetScriptForDestination(recipient.dest)), dustRelayFee);
994+
}
995+
986996
static util::Result<CreatedTransactionResult> CreateTransactionInternal(
987997
CWallet& wallet,
988998
const std::vector<CRecipient>& vecSend,
@@ -1005,12 +1015,20 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
10051015

10061016
// Set the long term feerate estimate to the wallet's consolidate feerate
10071017
coin_selection_params.m_long_term_feerate = wallet.m_consolidate_feerate;
1018+
// Static vsize overhead + outputs vsize. 4 nVersion, 4 nLocktime, 1 input count, 1 witness overhead (dummy, flag, stack size)
1019+
coin_selection_params.tx_noinputs_size = 10 + GetSizeOfCompactSize(vecSend.size()); // bytes for output count
10081020

10091021
CAmount recipients_sum = 0;
10101022
const OutputType change_type = wallet.TransactionChangeType(coin_control.m_change_type ? *coin_control.m_change_type : wallet.m_default_change_type, vecSend);
10111023
ReserveDestination reservedest(&wallet, change_type);
10121024
unsigned int outputs_to_subtract_fee_from = 0; // The number of outputs which we are subtracting the fee from
10131025
for (const auto& recipient : vecSend) {
1026+
if (IsDust(recipient, wallet.chain().relayDustFee())) {
1027+
return util::Error{_("Transaction amount too small")};
1028+
}
1029+
1030+
// Include the fee cost for outputs.
1031+
coin_selection_params.tx_noinputs_size += GetSerializeSizeForRecipient(recipient);
10141032
recipients_sum += recipient.nAmount;
10151033

10161034
if (recipient.fSubtractFeeFromAmount) {
@@ -1095,23 +1113,6 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
10951113
const auto change_spend_fee = coin_selection_params.m_discard_feerate.GetFee(coin_selection_params.change_spend_size);
10961114
coin_selection_params.min_viable_change = std::max(change_spend_fee + 1, dust);
10971115

1098-
// Static vsize overhead + outputs vsize. 4 version, 4 nLocktime, 1 input count, 1 witness overhead (dummy, flag, stack size)
1099-
coin_selection_params.tx_noinputs_size = 10 + GetSizeOfCompactSize(vecSend.size()); // bytes for output count
1100-
1101-
// vouts to the payees
1102-
for (const auto& recipient : vecSend)
1103-
{
1104-
CTxOut txout(recipient.nAmount, GetScriptForDestination(recipient.dest));
1105-
1106-
// Include the fee cost for outputs.
1107-
coin_selection_params.tx_noinputs_size += ::GetSerializeSize(txout);
1108-
1109-
if (IsDust(txout, wallet.chain().relayDustFee())) {
1110-
return util::Error{_("Transaction amount too small")};
1111-
}
1112-
txNew.vout.push_back(txout);
1113-
}
1114-
11151116
// Include the fees for things that aren't inputs, excluding the change output
11161117
const CAmount not_input_fees = coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.m_subtract_fee_outputs ? 0 : coin_selection_params.tx_noinputs_size);
11171118
CAmount selection_target = recipients_sum + not_input_fees;
@@ -1152,6 +1153,11 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
11521153
result.GetWaste(),
11531154
result.GetSelectedValue());
11541155

1156+
// vouts to the payees
1157+
for (const auto& recipient : vecSend)
1158+
{
1159+
txNew.vout.emplace_back(recipient.nAmount, GetScriptForDestination(recipient.dest));
1160+
}
11551161
const CAmount change_amount = result.GetChange(coin_selection_params.min_viable_change, coin_selection_params.m_change_fee);
11561162
if (change_amount > 0) {
11571163
CTxOut newTxOut(change_amount, scriptChange);

0 commit comments

Comments
 (0)