|
49 | 49 | #include <txdb.h>
|
50 | 50 | #include <txmempool.h>
|
51 | 51 | #include <util/chaintype.h>
|
| 52 | +#include <util/rbf.h> |
52 | 53 | #include <util/strencodings.h>
|
53 | 54 | #include <util/string.h>
|
54 | 55 | #include <util/thread.h>
|
@@ -336,56 +337,106 @@ CBlock TestChain100Setup::CreateAndProcessBlock(
|
336 | 337 | return block;
|
337 | 338 | }
|
338 | 339 |
|
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) |
347 | 347 | {
|
348 |
| - // Transaction we will submit to the mempool |
349 | 348 | CMutableTransaction mempool_txn;
|
| 349 | + mempool_txn.vin.reserve(inputs.size()); |
| 350 | + mempool_txn.vout.reserve(outputs.size()); |
350 | 351 |
|
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; |
359 | 356 |
|
360 |
| - // Sign the transaction |
361 | 357 | // - Add the signing key to a keystore
|
362 | 358 | 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 | + } |
364 | 362 | // - Populate a CoinsViewCache with the unspent output
|
365 | 363 | CCoinsView coins_view;
|
366 | 364 | 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 |
372 | 369 | 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 | + } |
374 | 378 | // - Default signature hashing type
|
375 | 379 | int nHashType = SIGHASH_ALL;
|
376 | 380 | std::map<int, bilingual_str> input_errors;
|
377 | 381 | 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 | +} |
378 | 404 |
|
| 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; |
379 | 413 | // If submit=true, add transaction to the mempool.
|
380 | 414 | if (submit) {
|
381 | 415 | LOCK(cs_main);
|
382 | 416 | const MempoolAcceptResult result = m_node.chainman->ProcessTransaction(MakeTransactionRef(mempool_txn));
|
383 | 417 | assert(result.m_result_type == MempoolAcceptResult::ResultType::VALID);
|
384 | 418 | }
|
385 |
| - |
386 | 419 | return mempool_txn;
|
387 | 420 | }
|
388 | 421 |
|
| 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 | + |
389 | 440 | std::vector<CTransactionRef> TestChain100Setup::PopulateMempool(FastRandomContext& det_rand, size_t num_transactions, bool submit)
|
390 | 441 | {
|
391 | 442 | std::vector<CTransactionRef> mempool_transactions;
|
|
0 commit comments