| 476 | } |
| 477 | |
| 478 | std::pair<CMutableTransaction, CAmount> TestChain100Setup::CreateValidTransaction(const std::vector<CTransactionRef>& input_transactions, |
| 479 | const std::vector<COutPoint>& inputs, |
| 480 | int input_height, |
| 481 | const std::vector<CKey>& input_signing_keys, |
| 482 | const std::vector<CTxOut>& outputs, |
| 483 | const std::optional<CFeeRate>& feerate, |
| 484 | const std::optional<uint32_t>& fee_output) |
| 485 | { |
| 486 | CMutableTransaction mempool_txn; |
| 487 | mempool_txn.vin.reserve(inputs.size()); |
| 488 | mempool_txn.vout.reserve(outputs.size()); |
| 489 | |
| 490 | for (const auto& outpoint : inputs) { |
| 491 | mempool_txn.vin.emplace_back(outpoint, CScript(), MAX_BIP125_RBF_SEQUENCE); |
| 492 | } |
| 493 | mempool_txn.vout = outputs; |
| 494 | |
| 495 | // - Add the signing key to a keystore |
| 496 | FillableSigningProvider keystore; |
| 497 | for (const auto& input_signing_key : input_signing_keys) { |
| 498 | keystore.AddKey(input_signing_key); |
| 499 | } |
| 500 | // - Populate a CoinsViewCache with the unspent output |
| 501 | CCoinsViewCache coins_cache{&CoinsViewEmpty::Get()}; |
| 502 | for (const auto& input_transaction : input_transactions) { |
| 503 | AddCoins(coins_cache, *input_transaction.get(), input_height); |
| 504 | } |
| 505 | // Build Outpoint to Coin map for SignTransaction |
| 506 | std::map<COutPoint, Coin> input_coins; |
| 507 | CAmount inputs_amount{0}; |
| 508 | for (const auto& outpoint_to_spend : inputs) { |
| 509 | // Use GetCoin to properly populate utxo_to_spend |
| 510 | auto utxo_to_spend{coins_cache.GetCoin(outpoint_to_spend).value()}; |
| 511 | input_coins.insert({outpoint_to_spend, utxo_to_spend}); |
| 512 | inputs_amount += utxo_to_spend.out.nValue; |
| 513 | } |
| 514 | // - Default signature hashing type |
| 515 | int nHashType = SIGHASH_ALL; |
| 516 | std::map<int, bilingual_str> input_errors; |
| 517 | assert(SignTransaction(mempool_txn, &keystore, input_coins, {.sighash_type = nHashType}, input_errors)); |
| 518 | CAmount current_fee = inputs_amount - std::accumulate(outputs.begin(), outputs.end(), CAmount(0), |
| 519 | [](const CAmount& acc, const CTxOut& out) { |
| 520 | return acc + out.nValue; |
| 521 | }); |
| 522 | // Deduct fees from fee_output to meet feerate if set |
| 523 | if (feerate.has_value()) { |
| 524 | assert(fee_output.has_value()); |
| 525 | assert(fee_output.value() < mempool_txn.vout.size()); |
| 526 | CAmount target_fee = feerate.value().GetFee(GetVirtualTransactionSize(CTransaction{mempool_txn})); |
| 527 | CAmount deduction = target_fee - current_fee; |
| 528 | if (deduction > 0) { |
| 529 | // Only deduct fee if there's anything to deduct. If the caller has put more fees than |
| 530 | // the target feerate, don't change the fee. |
| 531 | mempool_txn.vout[fee_output.value()].nValue -= deduction; |
| 532 | // Re-sign since an output has changed |
| 533 | input_errors.clear(); |
| 534 | assert(SignTransaction(mempool_txn, &keystore, input_coins, {.sighash_type = nHashType}, input_errors)); |
| 535 | current_fee = target_fee; |
no test coverage detected