Skip to content

Commit 10c0a86

Browse files
glozowachow101
andcommitted
[test util] CreateValidTransaction multi-in/out, configurable feerate, signal BIP125
Support the creation of a transaction with multiple specified inputs or outputs. Also accept a target feerate and return the fee paid. Also, signal BIP125 by default - a subsequent commit needs to RBF something. Co-authored-by: Andrew Chow <achow101@gmail.com>
1 parent 6ff647a commit 10c0a86

File tree

2 files changed

+116
-28
lines changed

2 files changed

+116
-28
lines changed

src/test/util/setup_common.cpp

Lines changed: 77 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
#include <txdb.h>
5050
#include <txmempool.h>
5151
#include <util/chaintype.h>
52+
#include <util/rbf.h>
5253
#include <util/strencodings.h>
5354
#include <util/string.h>
5455
#include <util/thread.h>
@@ -336,56 +337,106 @@ CBlock TestChain100Setup::CreateAndProcessBlock(
336337
return block;
337338
}
338339

339-
340-
CMutableTransaction TestChain100Setup::CreateValidMempoolTransaction(CTransactionRef input_transaction,
341-
int input_vout,
342-
int input_height,
343-
CKey input_signing_key,
344-
CScript output_destination,
345-
CAmount output_amount,
346-
bool submit)
340+
std::pair<CMutableTransaction, CAmount> TestChain100Setup::CreateValidTransaction(const std::vector<CTransactionRef>& input_transactions,
341+
const std::vector<COutPoint>& inputs,
342+
int input_height,
343+
const std::vector<CKey>& input_signing_keys,
344+
const std::vector<CTxOut>& outputs,
345+
const std::optional<CFeeRate>& feerate,
346+
const std::optional<uint32_t>& fee_output)
347347
{
348-
// Transaction we will submit to the mempool
349348
CMutableTransaction mempool_txn;
349+
mempool_txn.vin.reserve(inputs.size());
350+
mempool_txn.vout.reserve(outputs.size());
350351

351-
// Create an input
352-
COutPoint outpoint_to_spend(input_transaction->GetHash(), input_vout);
353-
CTxIn input(outpoint_to_spend);
354-
mempool_txn.vin.push_back(input);
355-
356-
// Create an output
357-
CTxOut output(output_amount, output_destination);
358-
mempool_txn.vout.push_back(output);
352+
for (const auto& outpoint : inputs) {
353+
mempool_txn.vin.emplace_back(outpoint, CScript(), MAX_BIP125_RBF_SEQUENCE);
354+
}
355+
mempool_txn.vout = outputs;
359356

360-
// Sign the transaction
361357
// - Add the signing key to a keystore
362358
FillableSigningProvider keystore;
363-
keystore.AddKey(input_signing_key);
359+
for (const auto& input_signing_key : input_signing_keys) {
360+
keystore.AddKey(input_signing_key);
361+
}
364362
// - Populate a CoinsViewCache with the unspent output
365363
CCoinsView coins_view;
366364
CCoinsViewCache coins_cache(&coins_view);
367-
AddCoins(coins_cache, *input_transaction.get(), input_height);
368-
// - Use GetCoin to properly populate utxo_to_spend,
369-
Coin utxo_to_spend;
370-
assert(coins_cache.GetCoin(outpoint_to_spend, utxo_to_spend));
371-
// - Then add it to a map to pass in to SignTransaction
365+
for (const auto& input_transaction : input_transactions) {
366+
AddCoins(coins_cache, *input_transaction.get(), input_height);
367+
}
368+
// Build Outpoint to Coin map for SignTransaction
372369
std::map<COutPoint, Coin> input_coins;
373-
input_coins.insert({outpoint_to_spend, utxo_to_spend});
370+
CAmount inputs_amount{0};
371+
for (const auto& outpoint_to_spend : inputs) {
372+
// - Use GetCoin to properly populate utxo_to_spend,
373+
Coin utxo_to_spend;
374+
assert(coins_cache.GetCoin(outpoint_to_spend, utxo_to_spend));
375+
input_coins.insert({outpoint_to_spend, utxo_to_spend});
376+
inputs_amount += utxo_to_spend.out.nValue;
377+
}
374378
// - Default signature hashing type
375379
int nHashType = SIGHASH_ALL;
376380
std::map<int, bilingual_str> input_errors;
377381
assert(SignTransaction(mempool_txn, &keystore, input_coins, nHashType, input_errors));
382+
CAmount current_fee = inputs_amount - std::accumulate(outputs.begin(), outputs.end(), CAmount(0),
383+
[](const CAmount& acc, const CTxOut& out) {
384+
return acc + out.nValue;
385+
});
386+
// Deduct fees from fee_output to meet feerate if set
387+
if (feerate.has_value()) {
388+
assert(fee_output.has_value());
389+
assert(fee_output.value() < mempool_txn.vout.size());
390+
CAmount target_fee = feerate.value().GetFee(GetVirtualTransactionSize(CTransaction{mempool_txn}));
391+
CAmount deduction = target_fee - current_fee;
392+
if (deduction > 0) {
393+
// Only deduct fee if there's anything to deduct. If the caller has put more fees than
394+
// the target feerate, don't change the fee.
395+
mempool_txn.vout[fee_output.value()].nValue -= deduction;
396+
// Re-sign since an output has changed
397+
input_errors.clear();
398+
assert(SignTransaction(mempool_txn, &keystore, input_coins, nHashType, input_errors));
399+
current_fee = target_fee;
400+
}
401+
}
402+
return {mempool_txn, current_fee};
403+
}
378404

405+
CMutableTransaction TestChain100Setup::CreateValidMempoolTransaction(const std::vector<CTransactionRef>& input_transactions,
406+
const std::vector<COutPoint>& inputs,
407+
int input_height,
408+
const std::vector<CKey>& input_signing_keys,
409+
const std::vector<CTxOut>& outputs,
410+
bool submit)
411+
{
412+
CMutableTransaction mempool_txn = CreateValidTransaction(input_transactions, inputs, input_height, input_signing_keys, outputs, std::nullopt, std::nullopt).first;
379413
// If submit=true, add transaction to the mempool.
380414
if (submit) {
381415
LOCK(cs_main);
382416
const MempoolAcceptResult result = m_node.chainman->ProcessTransaction(MakeTransactionRef(mempool_txn));
383417
assert(result.m_result_type == MempoolAcceptResult::ResultType::VALID);
384418
}
385-
386419
return mempool_txn;
387420
}
388421

422+
CMutableTransaction TestChain100Setup::CreateValidMempoolTransaction(CTransactionRef input_transaction,
423+
uint32_t input_vout,
424+
int input_height,
425+
CKey input_signing_key,
426+
CScript output_destination,
427+
CAmount output_amount,
428+
bool submit)
429+
{
430+
COutPoint input{input_transaction->GetHash(), input_vout};
431+
CTxOut output{output_amount, output_destination};
432+
return CreateValidMempoolTransaction(/*input_transactions=*/{input_transaction},
433+
/*inputs=*/{input},
434+
/*input_height=*/input_height,
435+
/*input_signing_keys=*/{input_signing_key},
436+
/*outputs=*/{output},
437+
/*submit=*/submit);
438+
}
439+
389440
std::vector<CTransactionRef> TestChain100Setup::PopulateMempool(FastRandomContext& det_rand, size_t num_transactions, bool submit)
390441
{
391442
std::vector<CTransactionRef> mempool_transactions;

src/test/util/setup_common.h

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,44 @@ struct TestChain100Setup : public TestingSetup {
124124
void mineBlocks(int num_blocks);
125125

126126
/**
127-
* Create a transaction and submit to the mempool.
127+
* Create a transaction, optionally setting the fee based on the feerate.
128+
* Note: The feerate may not be met exactly depending on whether the signatures can have different sizes.
129+
*
130+
* @param input_transactions The transactions to spend
131+
* @param inputs Outpoints with which to construct transaction vin.
132+
* @param input_height The height of the block that included the input transactions.
133+
* @param input_signing_keys The keys to spend the input transactions.
134+
* @param outputs Transaction vout.
135+
* @param feerate The feerate the transaction should pay.
136+
* @param fee_output The index of the output to take the fee from.
137+
* @return The transaction and the fee it pays
138+
*/
139+
std::pair<CMutableTransaction, CAmount> CreateValidTransaction(const std::vector<CTransactionRef>& input_transactions,
140+
const std::vector<COutPoint>& inputs,
141+
int input_height,
142+
const std::vector<CKey>& input_signing_keys,
143+
const std::vector<CTxOut>& outputs,
144+
const std::optional<CFeeRate>& feerate,
145+
const std::optional<uint32_t>& fee_output);
146+
/**
147+
* Create a transaction and, optionally, submit to the mempool.
148+
*
149+
* @param input_transactions The transactions to spend
150+
* @param inputs Outpoints with which to construct transaction vin.
151+
* @param input_height The height of the block that included the input transaction(s).
152+
* @param input_signing_keys The keys to spend inputs.
153+
* @param outputs Transaction vout.
154+
* @param submit Whether or not to submit to mempool
155+
*/
156+
CMutableTransaction CreateValidMempoolTransaction(const std::vector<CTransactionRef>& input_transactions,
157+
const std::vector<COutPoint>& inputs,
158+
int input_height,
159+
const std::vector<CKey>& input_signing_keys,
160+
const std::vector<CTxOut>& outputs,
161+
bool submit = true);
162+
163+
/**
164+
* Create a 1-in-1-out transaction and, optionally, submit to the mempool.
128165
*
129166
* @param input_transaction The transaction to spend
130167
* @param input_vout The vout to spend from the input_transaction
@@ -135,7 +172,7 @@ struct TestChain100Setup : public TestingSetup {
135172
* @param submit Whether or not to submit to mempool
136173
*/
137174
CMutableTransaction CreateValidMempoolTransaction(CTransactionRef input_transaction,
138-
int input_vout,
175+
uint32_t input_vout,
139176
int input_height,
140177
CKey input_signing_key,
141178
CScript output_destination,

0 commit comments

Comments
 (0)