@@ -86,19 +86,18 @@ impl ScriptTypesMatchingChangeIdentification {
8686 return TxOutChangeAnnotation :: NotChange ;
8787 }
8888
89- let mut matching = tx
89+ let matching_indices : Vec < usize > = tx
9090 . outputs ( )
91- . filter_map ( |o| SpendableTxConstituent :: try_new ( o) . ok ( ) )
92- . filter ( |spendable| spendable. output_type ( ) == input_type) ;
91+ . enumerate ( )
92+ . filter_map ( |( index, output) | ( output. output_type ( ) == input_type) . then_some ( index) )
93+ . collect ( ) ;
9394
94- let Some ( only) = matching. next ( ) else {
95- return TxOutChangeAnnotation :: NotChange ;
96- } ;
97- if matching. next ( ) . is_some ( ) {
95+ // There must be exactly one matching output for it to be considered change
96+ if matching_indices. len ( ) != 1 {
9897 return TxOutChangeAnnotation :: NotChange ;
9998 }
10099
101- if only . vout ( ) as usize == tx_out. vout ( ) {
100+ if matching_indices [ 0 ] == tx_out. vout ( ) {
102101 TxOutChangeAnnotation :: Change
103102 } else {
104103 TxOutChangeAnnotation :: NotChange
@@ -114,7 +113,7 @@ mod tests {
114113 UnifiedStorage ,
115114 loose:: LooseIndexBuilder ,
116115 loose:: { TxId , TxOutId } ,
117- test_utils:: { DummyTxData , DummyTxOut , DummyTxOutData } ,
116+ test_utils:: { DUMMY_UNSPENDABLE_SCRIPT , DummyTxData , DummyTxOut , DummyTxOutData } ,
118117 unified:: AnyOutId ,
119118 } ;
120119
@@ -143,7 +142,7 @@ mod tests {
143142 vout : 0 ,
144143 containing_tx : DummyTxData :: new_with_amounts ( vec ! [ 100 ] ) ,
145144 } ;
146- let spendable = SpendableTxConstituent :: try_new ( txout) . ok ( ) . unwrap ( ) ;
145+ let spendable: SpendableTxConstituent < _ > = txout. try_into ( ) . unwrap ( ) ;
147146 assert_eq ! (
148147 NaiveChangeIdentificationHeuristic :: is_change( spendable) ,
149148 TxOutChangeAnnotation :: Change
@@ -157,7 +156,7 @@ mod tests {
157156 containing_tx : DummyTxData :: new_with_amounts ( vec ! [ 100 ] ) ,
158157 } ;
159158 let spending_tx = DummyTxData :: new_with_amounts ( vec ! [ 100 ] ) ;
160- let spendable = SpendableTxConstituent :: try_new ( tx_out) . ok ( ) . unwrap ( ) ;
159+ let spendable: SpendableTxConstituent < _ > = tx_out. try_into ( ) . unwrap ( ) ;
161160 assert_eq ! (
162161 NLockTimeChangeIdentification :: is_change( spendable, spending_tx) ,
163162 TxOutChangeAnnotation :: NotChange
@@ -169,7 +168,7 @@ mod tests {
169168 containing_tx : DummyTxData :: new ( vec ! [ DummyTxOutData :: new( 100 , 0 ) ] , vec ! [ ] , 1 ) ,
170169 } ;
171170 let spending_tx = DummyTxData :: new ( vec ! [ DummyTxOutData :: new( 100 , 0 ) ] , vec ! [ ] , 1 ) ;
172- let spendable = SpendableTxConstituent :: try_new ( tx_out) . ok ( ) . unwrap ( ) ;
171+ let spendable: SpendableTxConstituent < _ > = tx_out. try_into ( ) . unwrap ( ) ;
173172 assert_eq ! (
174173 NLockTimeChangeIdentification :: is_change( spendable, spending_tx) ,
175174 TxOutChangeAnnotation :: Change
@@ -216,15 +215,11 @@ mod tests {
216215 let change = AnyOutId :: from ( TxOutId :: new ( TxId ( 3 ) , 1 ) ) . with ( & storage) ;
217216
218217 assert_eq ! (
219- ScriptTypesMatchingChangeIdentification :: is_change(
220- SpendableTxConstituent :: try_new( payment) . ok( ) . unwrap( )
221- ) ,
218+ ScriptTypesMatchingChangeIdentification :: is_change( payment. try_into( ) . unwrap( ) ) ,
222219 TxOutChangeAnnotation :: NotChange
223220 ) ;
224221 assert_eq ! (
225- ScriptTypesMatchingChangeIdentification :: is_change(
226- SpendableTxConstituent :: try_new( change) . ok( ) . unwrap( )
227- ) ,
222+ ScriptTypesMatchingChangeIdentification :: is_change( change. try_into( ) . unwrap( ) ) ,
228223 TxOutChangeAnnotation :: Change
229224 ) ;
230225 }
@@ -269,15 +264,11 @@ mod tests {
269264 let change = AnyOutId :: from ( TxOutId :: new ( TxId ( 3 ) , 1 ) ) . with ( & storage) ;
270265
271266 assert_eq ! (
272- ScriptTypesMatchingChangeIdentification :: is_change(
273- SpendableTxConstituent :: try_new( payment) . ok( ) . unwrap( )
274- ) ,
267+ ScriptTypesMatchingChangeIdentification :: is_change( payment. try_into( ) . unwrap( ) ) ,
275268 TxOutChangeAnnotation :: NotChange
276269 ) ;
277270 assert_eq ! (
278- ScriptTypesMatchingChangeIdentification :: is_change(
279- SpendableTxConstituent :: try_new( change) . ok( ) . unwrap( )
280- ) ,
271+ ScriptTypesMatchingChangeIdentification :: is_change( change. try_into( ) . unwrap( ) ) ,
281272 TxOutChangeAnnotation :: NotChange
282273 ) ;
283274 }
@@ -299,11 +290,7 @@ mod tests {
299290 DummyTxData :: new(
300291 vec![
301292 // OP_RETURN output - should never be considered change
302- DummyTxOutData :: new_with_script(
303- 0 ,
304- 0 ,
305- vec![ 0x6a , 0x04 , 0x48 , 0x65 , 0x6c , 0x6c ] , // OP_RETURN "Hell"
306- ) ,
293+ DummyTxOutData :: new_with_script( 0 , 0 , vec![ 0x6a , 0x04 , 0xde , 0xad , 0xbe , 0xef ] ) ,
307294 // P2PKH output - the only spendable P2PKH, so it's change
308295 DummyTxOutData :: new_with_script(
309296 249 ,
@@ -320,23 +307,20 @@ mod tests {
320307 let p2pkh_output = AnyOutId :: from ( TxOutId :: new ( TxId ( 3 ) , 1 ) ) . with ( & storage) ;
321308
322309 // OP_RETURN cannot be wrapped in SpendableTxConstituent -- type system rejects it
323- assert ! ( SpendableTxConstituent :: try_new ( op_return_output) . is_err( ) ) ;
310+ assert ! ( TryInto :: < SpendableTxConstituent <_>> :: try_into ( op_return_output) . is_err( ) ) ;
324311
325312 // P2PKH output is change since it's the only spendable output matching input type
326313 assert_eq ! (
327- ScriptTypesMatchingChangeIdentification :: is_change(
328- SpendableTxConstituent :: try_new( p2pkh_output) . ok( ) . unwrap( )
329- ) ,
314+ ScriptTypesMatchingChangeIdentification :: is_change( p2pkh_output. try_into( ) . unwrap( ) ) ,
330315 TxOutChangeAnnotation :: Change
331316 ) ;
332317 }
333318
334319 #[ test]
335- fn test_op_return_cannot_be_wrapped ( ) {
336- // The type system enforces OP_RETURN exclusion at the wrapper boundary,
320+ fn test_unspendable_cannot_be_wrapped ( ) {
321+ // The type system enforces unspendable output exclusion at the wrapper boundary,
337322 // so heuristics never have to check for it themselves.
338- let op_return_script = vec ! [ 0x6a , 0x04 , 0x48 , 0x65 , 0x6c , 0x6c ] ; // OP_RETURN "Hell"
339- let txout_op_return = DummyTxOut {
323+ let txout_unspendable = DummyTxOut {
340324 vout : 1 ,
341325 containing_tx : DummyTxData :: new (
342326 vec ! [
@@ -345,13 +329,13 @@ mod tests {
345329 0 ,
346330 script_from_address( "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" ) ,
347331 ) ,
348- DummyTxOutData :: new_with_script( 0 , 1 , op_return_script ) ,
332+ DummyTxOutData :: new_with_script( 0 , 1 , DUMMY_UNSPENDABLE_SCRIPT ) ,
349333 ] ,
350334 vec ! [ ] ,
351335 0 ,
352336 ) ,
353337 } ;
354- assert ! ( SpendableTxConstituent :: try_new ( txout_op_return ) . is_err( ) ) ;
338+ assert ! ( TryInto :: < SpendableTxConstituent <_>> :: try_into ( txout_unspendable ) . is_err( ) ) ;
355339 }
356340
357341 #[ test]
@@ -391,15 +375,11 @@ mod tests {
391375 let output1 = AnyOutId :: from ( TxOutId :: new ( TxId ( 3 ) , 1 ) ) . with ( & storage) ;
392376
393377 assert_eq ! (
394- ScriptTypesMatchingChangeIdentification :: is_change(
395- SpendableTxConstituent :: try_new( output0) . ok( ) . unwrap( )
396- ) ,
378+ ScriptTypesMatchingChangeIdentification :: is_change( output0. try_into( ) . unwrap( ) ) ,
397379 TxOutChangeAnnotation :: NotChange
398380 ) ;
399381 assert_eq ! (
400- ScriptTypesMatchingChangeIdentification :: is_change(
401- SpendableTxConstituent :: try_new( output1) . ok( ) . unwrap( )
402- ) ,
382+ ScriptTypesMatchingChangeIdentification :: is_change( output1. try_into( ) . unwrap( ) ) ,
403383 TxOutChangeAnnotation :: NotChange
404384 ) ;
405385 }
0 commit comments