Remove FundingTxInput type alias in favor of ConfirmedUtxo
What changed, and why it matters
This commit is a simple code cleanup: it removes a type alias named FundingTxInput and replaces every use of it with the existing type ConfirmedUtxo. There is no change to program logic, no bug fix, and no security-related behavior change. It is purely a naming/refactoring change.
No security action required. Review as normal refactoring if desired.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch deletes pub type FundingTxInput = crate::util::wallet_utils::ConfirmedUtxo; from lightning/src/ln/funding.rs and updates all call sites in channel.rs, functional_test_utils.rs, funding.rs, and interactivetxs.rs to use ConfirmedUtxo directly. Imports are adjusted accordingly. The underlying type and its constructors/methods remain unchanged; only the alias is removed.
Changed components
lightning/src/ln/funding.rslightning/src/ln/channel.rslightning/src/ln/functional_test_utils.rslightning/src/ln/interactivetxs.rsInspect captured patch +50 / −58
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index 38cb095..2ba2f2c 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -56,9 +56,7 @@ use crate::ln::channelmanager::{
PendingHTLCStatus, RAACommitmentOrder, SentHTLCId, TrustedChannelFeatures, BREAKDOWN_TIMEOUT,
MAX_LOCAL_BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA,
};
-use crate::ln::funding::{
- FeeRateAdjustmentError, FundingContribution, FundingTemplate, FundingTxInput,
-};
+use crate::ln::funding::{FeeRateAdjustmentError, FundingContribution, FundingTemplate};
use crate::ln::interactivetxs::{
AbortReason, HandleTxCompleteValue, InteractiveTxConstructor, InteractiveTxConstructorArgs,
InteractiveTxMessageSend, InteractiveTxSigningSession, SharedOwnedInput, SharedOwnedOutput,
@@ -87,7 +85,7 @@ use crate::util::errors::APIError;
use crate::util::logger::{Level as LoggerLevel, Logger, Record, WithContext};
use crate::util::scid_utils::{block_from_scid, scid_from_parts};
use crate::util::ser::{Readable, ReadableArgs, RequiredWrapper, Writeable, Writer};
-use crate::util::wallet_utils::Input;
+use crate::util::wallet_utils::{ConfirmedUtxo, Input};
use crate::{impl_readable_for_vec, impl_writeable_for_vec};
use alloc::collections::{btree_map, BTreeMap};
@@ -3100,7 +3098,7 @@ impl FundingNegotiation {
funding: FundingScope, context: &ChannelContext<SP>, entropy_source: &ES,
holder_node_id: &PublicKey, our_funding_contribution: SignedAmount,
prev_funding_input: SharedOwnedInput, locktime: u32, feerate_sat_per_1000_weight: u32,
- our_funding_inputs: Vec<FundingTxInput>, our_funding_outputs: Vec<TxOut>,
+ our_funding_inputs: Vec<ConfirmedUtxo>, our_funding_outputs: Vec<TxOut>,
) -> FundingNegotiation {
let funding_negotiation_context = FundingNegotiationContext {
is_initiator: false,
@@ -6905,7 +6903,7 @@ pub(super) struct FundingNegotiationContext {
pub shared_funding_input: Option<SharedOwnedInput>,
/// The funding inputs we will be contributing to the channel.
#[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
- pub our_funding_inputs: Vec<FundingTxInput>,
+ pub our_funding_inputs: Vec<ConfirmedUtxo>,
/// The funding outputs we will be contributing to the channel.
#[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
pub our_funding_outputs: Vec<TxOut>,
@@ -15402,7 +15400,7 @@ impl<SP: SignerProvider> PendingV2Channel<SP> {
pub fn new_outbound<ES: EntropySource, F: FeeEstimator, L: Logger>(
fee_estimator: &LowerBoundedFeeEstimator<F>, entropy_source: &ES, signer_provider: &SP,
counterparty_node_id: PublicKey, their_features: &InitFeatures, funding_satoshis: u64,
- funding_inputs: Vec<FundingTxInput>, user_id: u128, config: &UserConfig,
+ funding_inputs: Vec<ConfirmedUtxo>, user_id: u128, config: &UserConfig,
current_chain_height: u32, outbound_scid_alias: u64, funding_confirmation_target: ConfirmationTarget,
logger: L, trusted_channel_features: Option<TrustedChannelFeatures>,
) -> Result<Self, APIError> {
diff --git a/lightning/src/ln/functional_test_utils.rs b/lightning/src/ln/functional_test_utils.rs
index 3dd3018..bbb184d 100644
--- a/lightning/src/ln/functional_test_utils.rs
+++ b/lightning/src/ln/functional_test_utils.rs
@@ -29,7 +29,7 @@ use crate::ln::channelmanager::{
AChannelManager, ChainParameters, ChannelManager, ChannelManagerReadArgs, PaymentId,
RAACommitmentOrder, TrustedChannelFeatures, MIN_CLTV_EXPIRY_DELTA,
};
-use crate::ln::funding::{FundingContribution, FundingTxInput};
+use crate::ln::funding::FundingContribution;
use crate::ln::msgs::{self, OpenChannel};
use crate::ln::msgs::{
BaseMessageHandler, ChannelMessageHandler, MessageSendEvent, RoutingMessageHandler,
@@ -55,7 +55,7 @@ use crate::util::test_channel_signer::SignerOp;
use crate::util::test_channel_signer::TestChannelSigner;
use crate::util::test_utils::{self, TestLogger};
use crate::util::test_utils::{TestChainMonitor, TestKeysInterface, TestScorer};
-use crate::util::wallet_utils::{WalletSourceSync, WalletSync};
+use crate::util::wallet_utils::{ConfirmedUtxo, WalletSourceSync, WalletSync};
use bitcoin::amount::Amount;
use bitcoin::block::{Block, Header, Version as BlockVersion};
@@ -1512,7 +1512,7 @@ fn internal_create_funding_transaction<'a, 'b, 'c>(
/// Return the inputs (with prev tx), and the total witness weight for these inputs
pub fn create_dual_funding_utxos_with_prev_txs(
node: &Node<'_, '_, '_>, utxo_values_in_satoshis: &[u64],
-) -> Vec<FundingTxInput> {
+) -> Vec<ConfirmedUtxo> {
// Ensure we have unique transactions per node by using the locktime.
let tx = Transaction {
version: TxVersion::TWO,
@@ -1536,7 +1536,7 @@ pub fn create_dual_funding_utxos_with_prev_txs(
.iter()
.enumerate()
.map(|(index, _)| index as u32)
- .map(|vout| FundingTxInput::new_p2wpkh(tx.clone(), vout).unwrap())
+ .map(|vout| ConfirmedUtxo::new_p2wpkh(tx.clone(), vout).unwrap())
.collect()
}
diff --git a/lightning/src/ln/funding.rs b/lightning/src/ln/funding.rs
index 68055a9..fd9fc29 100644
--- a/lightning/src/ln/funding.rs
+++ b/lightning/src/ln/funding.rs
@@ -24,7 +24,7 @@ use crate::ln::LN_MAX_MSG_LEN;
use crate::prelude::*;
use crate::util::native_async::MaybeSend;
use crate::util::wallet_utils::{
- CoinSelection, CoinSelectionSource, CoinSelectionSourceSync, Input,
+ CoinSelection, CoinSelectionSource, CoinSelectionSourceSync, ConfirmedUtxo, Input,
};
/// Error returned when a [`FundingContribution`] cannot be adjusted to a target feerate.
@@ -370,7 +370,7 @@ impl FundingTemplate {
/// when that is set. `max_feerate` is the highest feerate we are willing to tolerate if we end
/// up as the acceptor, and must be at least `min_feerate`.
pub fn splice_in_inputs(
- self, inputs: Vec<FundingTxInput>, min_feerate: FeeRate, max_feerate: FeeRate,
+ self, inputs: Vec<ConfirmedUtxo>, min_feerate: FeeRate, max_feerate: FeeRate,
) -> Result<FundingContribution, FundingContributionError> {
self.with_prior_contribution(min_feerate, max_feerate).add_inputs(inputs)?.build()
}
@@ -466,8 +466,8 @@ impl FundingTemplate {
}
fn estimate_transaction_fee(
- inputs: &[FundingTxInput], outputs: &[TxOut], change_output: Option<&TxOut>,
- is_initiator: bool, is_splice: bool, feerate: FeeRate,
+ inputs: &[ConfirmedUtxo], outputs: &[TxOut], change_output: Option<&TxOut>, is_initiator: bool,
+ is_splice: bool, feerate: FeeRate,
) -> Amount {
let input_weight: u64 = inputs
.iter()
@@ -515,7 +515,7 @@ fn estimate_transaction_fee(
Weight::from_wu(weight) * feerate
}
-fn validate_inputs(inputs: &[FundingTxInput]) -> Result<(), FundingContributionError> {
+fn validate_inputs(inputs: &[ConfirmedUtxo]) -> Result<(), FundingContributionError> {
let mut total_value = Amount::ZERO;
for (idx, input) in inputs.iter().enumerate() {
if inputs[..idx]
@@ -559,7 +559,7 @@ enum FundingInputs {
/// Replaces the contribution's inputs with the provided set and fully consumes them without a
/// change output. The amount added to the channel is recomputed from the input total minus fees,
/// while explicit withdrawal outputs still reduce the splice's net value.
- ManuallySelected { inputs: Vec<FundingTxInput> },
+ ManuallySelected { inputs: Vec<ConfirmedUtxo> },
}
impl FundingInputs {
@@ -584,7 +584,7 @@ impl FundingInputs {
}
}
- fn manually_selected_inputs(&self) -> &[FundingTxInput] {
+ fn manually_selected_inputs(&self) -> &[ConfirmedUtxo] {
match self {
FundingInputs::ManuallySelected { inputs } => inputs,
FundingInputs::CoinSelected { .. } => &[],
@@ -613,7 +613,7 @@ pub struct FundingContribution {
///
/// For coin-selected contributions, excess value is returned via [`Self::change_output`]. For
/// manually selected inputs, the full input value is consumed and no change output is created.
- inputs: Vec<FundingTxInput>,
+ inputs: Vec<ConfirmedUtxo>,
/// The outputs to include in the funding transaction.
///
@@ -691,7 +691,7 @@ impl FundingContribution {
}
/// Returns the inputs included in this contribution.
- pub fn inputs(&self) -> &[FundingTxInput] {
+ pub fn inputs(&self) -> &[ConfirmedUtxo] {
&self.inputs
}
@@ -827,7 +827,7 @@ impl FundingContribution {
Some(new_contribution_at_target_feerate)
}
- pub(super) fn into_tx_parts(self) -> (Vec<FundingTxInput>, Vec<TxOut>) {
+ pub(super) fn into_tx_parts(self) -> (Vec<ConfirmedUtxo>, Vec<TxOut>) {
let FundingContribution { inputs, mut outputs, change_output, .. } = self;
if let Some(change_output) = change_output {
@@ -1131,10 +1131,6 @@ impl FundingContribution {
}
}
-/// An input to contribute to a channel's funding transaction either when using the v2 channel
-/// establishment protocol or when splicing.
-pub type FundingTxInput = crate::util::wallet_utils::ConfirmedUtxo;
-
#[derive(Debug, Clone, PartialEq, Eq)]
struct NoCoinSelectionSource;
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -1472,7 +1468,7 @@ impl FundingBuilder {
///
/// Returns [`FundingContributionError::InvalidSpliceValue`] if the builder already has a
/// coin-selected value request.
- pub fn add_input(self, input: FundingTxInput) -> Result<Self, FundingContributionError> {
+ pub fn add_input(self, input: ConfirmedUtxo) -> Result<Self, FundingContributionError> {
self.0.add_input_inner(input).map(FundingBuilder)
}
@@ -1490,7 +1486,7 @@ impl FundingBuilder {
///
/// Returns [`FundingContributionError::InvalidSpliceValue`] if the builder already has a
/// coin-selected value request.
- pub fn add_inputs(self, inputs: Vec<FundingTxInput>) -> Result<Self, FundingContributionError> {
+ pub fn add_inputs(self, inputs: Vec<ConfirmedUtxo>) -> Result<Self, FundingContributionError> {
self.0.add_inputs_inner(inputs).map(FundingBuilder)
}
@@ -1585,7 +1581,7 @@ impl<State> FundingBuilderInner<State> {
Ok(self)
}
- fn add_input_inner(mut self, input: FundingTxInput) -> Result<Self, FundingContributionError> {
+ fn add_input_inner(mut self, input: ConfirmedUtxo) -> Result<Self, FundingContributionError> {
match &mut self.funding_inputs {
None => {
self.funding_inputs = Some(FundingInputs::ManuallySelected { inputs: vec![input] })
@@ -1599,7 +1595,7 @@ impl<State> FundingBuilderInner<State> {
}
fn add_inputs_inner(
- mut self, inputs: Vec<FundingTxInput>,
+ mut self, inputs: Vec<ConfirmedUtxo>,
) -> Result<Self, FundingContributionError> {
match &mut self.funding_inputs {
None => self.funding_inputs = Some(FundingInputs::ManuallySelected { inputs }),
@@ -1875,11 +1871,11 @@ impl<W: CoinSelectionSourceSync> SyncFundingBuilder<W> {
mod tests {
use super::{
estimate_transaction_fee, FeeRateAdjustmentError, FundingBuilder, FundingContribution,
- FundingContributionError, FundingInputMode, FundingTemplate, FundingTxInput,
- SyncCoinSelectionSource, SyncFundingBuilder,
+ FundingContributionError, FundingInputMode, FundingTemplate, SyncCoinSelectionSource,
+ SyncFundingBuilder,
};
use crate::chain::ClaimId;
- use crate::util::wallet_utils::{CoinSelection, CoinSelectionSourceSync, Input};
+ use crate::util::wallet_utils::{CoinSelection, CoinSelectionSourceSync, ConfirmedUtxo, Input};
use bitcoin::hashes::Hash;
use bitcoin::transaction::{Transaction, TxOut, Version};
use bitcoin::{Amount, FeeRate, Psbt, ScriptBuf, SignedAmount, WPubkeyHash, WScriptHash};
@@ -1960,7 +1956,7 @@ mod tests {
}
#[rustfmt::skip]
- fn funding_input_sats(input_value_sats: u64) -> FundingTxInput {
+ fn funding_input_sats(input_value_sats: u64) -> ConfirmedUtxo {
let prevout = TxOut {
value: Amount::from_sat(input_value_sats),
script_pubkey: ScriptBuf::new_p2wpkh(&WPubkeyHash::all_zeros()),
@@ -1970,7 +1966,7 @@ mod tests {
version: Version::TWO, lock_time: bitcoin::absolute::LockTime::ZERO,
};
- FundingTxInput::new_p2wpkh(prevtx, 0).unwrap()
+ ConfirmedUtxo::new_p2wpkh(prevtx, 0).unwrap()
}
fn funding_output_sats(output_value_sats: u64) -> TxOut {
@@ -1995,7 +1991,7 @@ mod tests {
}
struct MustPayToWallet {
- utxo: FundingTxInput,
+ utxo: ConfirmedUtxo,
change_output: Option<TxOut>,
expected_must_pay_to_values: Vec<Amount>,
}
@@ -2805,7 +2801,7 @@ mod tests {
assert!(prevtx.serialized_length() > crate::ln::LN_MAX_MSG_LEN);
let wallet = SingleUtxoWallet {
- utxo: FundingTxInput::new_p2wpkh(prevtx, 0).unwrap(),
+ utxo: ConfirmedUtxo::new_p2wpkh(prevtx, 0).unwrap(),
change_output: None,
};
assert!(matches!(
@@ -3713,7 +3709,7 @@ mod tests {
/// A mock wallet that returns a single UTXO for coin selection.
struct SingleUtxoWallet {
- utxo: FundingTxInput,
+ utxo: ConfirmedUtxo,
change_output: Option<TxOut>,
}
diff --git a/lightning/src/ln/interactivetxs.rs b/lightning/src/ln/interactivetxs.rs
index faa352f..0deb119 100644
--- a/lightning/src/ln/interactivetxs.rs
+++ b/lightning/src/ln/interactivetxs.rs
@@ -33,11 +33,11 @@ use crate::ln::chan_utils::{
SEGWIT_MARKER_FLAG_WEIGHT,
};
use crate::ln::channel::TOTAL_BITCOIN_SUPPLY_SATOSHIS;
-use crate::ln::funding::FundingTxInput;
use crate::ln::msgs;
use crate::ln::msgs::{MessageSendEvent, SerialId, TxSignatures};
use crate::ln::types::ChannelId;
use crate::sign::{EntropySource, P2TR_KEY_PATH_WITNESS_WEIGHT, P2WPKH_WITNESS_WEIGHT};
+use crate::util::wallet_utils::ConfirmedUtxo;
use core::fmt::Display;
@@ -2034,7 +2034,7 @@ pub(super) struct InteractiveTxConstructorArgs<'a, ES: EntropySource> {
pub channel_id: ChannelId,
pub feerate_sat_per_kw: u32,
pub funding_tx_locktime: AbsoluteLockTime,
- pub inputs_to_contribute: Vec<FundingTxInput>,
+ pub inputs_to_contribute: Vec<ConfirmedUtxo>,
pub shared_funding_input: Option<SharedOwnedInput>,
pub shared_funding_output: SharedOwnedOutput,
pub outputs_to_contribute: Vec<TxOut>,
@@ -2071,7 +2071,7 @@ impl InteractiveTxConstructor {
let mut inputs_to_contribute: Vec<(SerialId, InputOwned)> = inputs_to_contribute
.into_iter()
- .map(|FundingTxInput { utxo, prevtx: prev_tx }| {
+ .map(|ConfirmedUtxo { utxo, prevtx: prev_tx }| {
let serial_id = generate_holder_serial_id(entropy_source, is_initiator);
let txin = TxIn {
previous_output: utxo.outpoint,
@@ -2322,7 +2322,6 @@ impl InteractiveTxConstructor {
mod tests {
use crate::chain::chaininterface::{fee_for_weight, FEERATE_FLOOR_SATS_PER_KW};
use crate::ln::channel::TOTAL_BITCOIN_SUPPLY_SATOSHIS;
- use crate::ln::funding::FundingTxInput;
use crate::ln::interactivetxs::{
generate_holder_serial_id, AbortReason, HandleTxCompleteValue, InteractiveTxConstructor,
InteractiveTxConstructorArgs, InteractiveTxMessageSend, SharedOwnedInput,
@@ -2332,6 +2331,7 @@ mod tests {
use crate::ln::types::ChannelId;
use crate::sign::EntropySource;
use crate::util::atomic_counter::AtomicCounter;
+ use crate::util::wallet_utils::ConfirmedUtxo;
use bitcoin::absolute::LockTime as AbsoluteLockTime;
use bitcoin::amount::Amount;
use bitcoin::hashes::Hash;
@@ -2395,12 +2395,12 @@ mod tests {
struct TestSession {
description: &'static str,
- inputs_a: Vec<FundingTxInput>,
+ inputs_a: Vec<ConfirmedUtxo>,
a_shared_input: Option<(OutPoint, TxOut, u64)>,
/// The funding output, with the value contributed
shared_output_a: (TxOut, u64),
outputs_a: Vec<TxOut>,
- inputs_b: Vec<FundingTxInput>,
+ inputs_b: Vec<ConfirmedUtxo>,
b_shared_input: Option<(OutPoint, TxOut, u64)>,
/// The funding output, with the value contributed
shared_output_b: (TxOut, u64),
@@ -2642,22 +2642,20 @@ mod tests {
}
}
- fn generate_inputs(outputs: &[TestOutput]) -> Vec<FundingTxInput> {
+ fn generate_inputs(outputs: &[TestOutput]) -> Vec<ConfirmedUtxo> {
let tx = generate_tx(outputs);
outputs
.iter()
.enumerate()
.map(|(idx, output)| match output {
- TestOutput::P2WPKH(_) => {
- FundingTxInput::new_p2wpkh(tx.clone(), idx as u32).unwrap()
- },
+ TestOutput::P2WPKH(_) => ConfirmedUtxo::new_p2wpkh(tx.clone(), idx as u32).unwrap(),
TestOutput::P2WSH(_) => {
- FundingTxInput::new_p2wsh(tx.clone(), idx as u32, Weight::from_wu(42)).unwrap()
+ ConfirmedUtxo::new_p2wsh(tx.clone(), idx as u32, Weight::from_wu(42)).unwrap()
},
TestOutput::P2TR(_) => {
- FundingTxInput::new_p2tr_key_spend(tx.clone(), idx as u32).unwrap()
+ ConfirmedUtxo::new_p2tr_key_spend(tx.clone(), idx as u32).unwrap()
},
- TestOutput::P2PKH(_) => FundingTxInput::new_p2pkh(tx.clone(), idx as u32).unwrap(),
+ TestOutput::P2PKH(_) => ConfirmedUtxo::new_p2pkh(tx.clone(), idx as u32).unwrap(),
})
.collect()
}
@@ -2705,12 +2703,12 @@ mod tests {
(generate_txout(&TestOutput::P2WSH(value)), local_value)
}
- fn generate_fixed_number_of_inputs(count: u16) -> Vec<FundingTxInput> {
+ fn generate_fixed_number_of_inputs(count: u16) -> Vec<ConfirmedUtxo> {
// Generate transactions with a total `count` number of outputs such that no transaction has a
// serialized length greater than u16::MAX.
let max_outputs_per_prevtx = 1_500;
let mut remaining = count;
- let mut inputs: Vec<FundingTxInput> = Vec::with_capacity(count as usize);
+ let mut inputs: Vec<ConfirmedUtxo> = Vec::with_capacity(count as usize);
while remaining > 0 {
let tx_output_count = remaining.min(max_outputs_per_prevtx);
@@ -2721,10 +2719,10 @@ mod tests {
// Use unique locktime for each tx so outpoints are different across transactions
let tx = generate_tx_with_locktime(&outputs, (1337 + remaining).into());
- let mut temp: Vec<FundingTxInput> = outputs
+ let mut temp: Vec<ConfirmedUtxo> = outputs
.iter()
.enumerate()
- .map(|(idx, _)| FundingTxInput::new_p2wpkh(tx.clone(), idx as u32).unwrap())
+ .map(|(idx, _)| ConfirmedUtxo::new_p2wpkh(tx.clone(), idx as u32).unwrap())
.collect();
inputs.append(&mut temp);
@@ -2935,7 +2933,7 @@ mod tests {
});
let tx = generate_tx(&[TestOutput::P2WPKH(1_000_000)]);
- let mut invalid_sequence_input = FundingTxInput::new_p2wpkh(tx.clone(), 0).unwrap();
+ let mut invalid_sequence_input = ConfirmedUtxo::new_p2wpkh(tx.clone(), 0).unwrap();
invalid_sequence_input.set_sequence(Default::default());
do_test_interactive_tx_constructor(TestSession {
description: "Invalid input sequence from initiator",
@@ -2949,7 +2947,7 @@ mod tests {
outputs_b: vec![],
expect_error: Some((AbortReason::IncorrectInputSequenceValue, ErrorCulprit::NodeA)),
});
- let duplicate_input = FundingTxInput::new_p2wpkh(tx.clone(), 0).unwrap();
+ let duplicate_input = ConfirmedUtxo::new_p2wpkh(tx.clone(), 0).unwrap();
do_test_interactive_tx_constructor(TestSession {
description: "Duplicate prevout from initiator",
inputs_a: vec![duplicate_input.clone(), duplicate_input],
@@ -2963,7 +2961,7 @@ mod tests {
expect_error: Some((AbortReason::PrevTxOutInvalid, ErrorCulprit::NodeB)),
});
// Non-initiator uses same prevout as initiator.
- let duplicate_input = FundingTxInput::new_p2wpkh(tx.clone(), 0).unwrap();
+ let duplicate_input = ConfirmedUtxo::new_p2wpkh(tx.clone(), 0).unwrap();
do_test_interactive_tx_constructor(TestSession {
description: "Non-initiator uses same prevout as initiator",
inputs_a: vec![duplicate_input.clone()],
@@ -2976,7 +2974,7 @@ mod tests {
outputs_b: vec![],
expect_error: Some((AbortReason::PrevTxOutInvalid, ErrorCulprit::NodeA)),
});
- let duplicate_input = FundingTxInput::new_p2wpkh(tx.clone(), 0).unwrap();
+ let duplicate_input = ConfirmedUtxo::new_p2wpkh(tx.clone(), 0).unwrap();
do_test_interactive_tx_constructor(TestSession {
description: "Non-initiator uses same prevout as initiator",
inputs_a: vec![duplicate_input.clone()],
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.