@@ -186,7 +186,7 @@ impl MempoolPolicy {
186186 {
187187 return Err ( MempoolPolicyError :: SelectionTxMismatch ) ;
188188 }
189-
189+
190190 if !selection
191191 . outputs
192192 . iter ( )
@@ -351,3 +351,197 @@ impl core::fmt::Display for MempoolPolicyError {
351351
352352#[ cfg( feature = "std" ) ]
353353impl std:: error:: Error for MempoolPolicyError { }
354+
355+ #[ cfg( test) ]
356+ mod tests {
357+ use super :: * ;
358+ use crate :: { test_utils:: * , Output } ;
359+ use alloc:: vec:: Vec ;
360+ use bitcoin:: { transaction:: Version , Amount , ScriptBuf , Transaction , TxOut } ;
361+
362+ fn default_policy ( ) -> MempoolPolicy {
363+ MempoolPolicy {
364+ tip_height : absolute:: Height :: from_consensus ( 3_000 ) . unwrap ( ) ,
365+ tip_mtp : absolute:: Time :: from_consensus ( 500_001_000 ) . unwrap ( ) ,
366+ }
367+ }
368+
369+ #[ test]
370+ fn test_tx_version ( ) {
371+ let policy = default_policy ( ) ;
372+ let input = setup_test_input ( 2_000 ) . unwrap ( ) ;
373+ let output = create_output ( p2tr_script ( ) , 9_000 ) ;
374+ let ( selection, mut tx) = build_selection_with_tx ( & [ input] , & [ output] ) ;
375+
376+ // Default version is 2, which is standard.
377+ assert ! ( policy. check_all( & selection, & tx) . is_ok( ) ) ;
378+
379+ // Test version 1, which is also standard.
380+ tx. version = Version :: ONE ;
381+ assert ! ( policy. check_all( & selection, & tx) . is_ok( ) ) ;
382+
383+ // Version 3 (TRUC) is standard under v30+.
384+ tx. version = Version ( 3 ) ;
385+ assert ! ( policy. check_all( & selection, & tx) . is_ok( ) ) ;
386+
387+ // Test version 4, which is non-standard.
388+ tx. version = Version ( 4 ) ;
389+ assert ! ( matches!(
390+ policy. check_all( & selection, & tx) ,
391+ Err ( MempoolPolicyError :: UnsupportedVersion ( _) )
392+ ) ) ;
393+ }
394+
395+ #[ test]
396+ fn test_tx_locktime ( ) {
397+ let policy = default_policy ( ) ;
398+ let input = setup_test_input ( 2_000 ) . unwrap ( ) ;
399+ let output = create_output ( p2tr_script ( ) , 9_000 ) ;
400+ let ( selection, mut tx) = build_selection_with_tx ( & [ input] , & [ output] ) ;
401+
402+ // Locktime exactly equal to the tip height.
403+ tx. lock_time = absolute:: LockTime :: from_consensus ( 3_000 ) ;
404+ assert ! ( policy. check_all( & selection, & tx) . is_ok( ) ) ;
405+
406+ // Locktime below the tip height.
407+ tx. lock_time = absolute:: LockTime :: from_consensus ( 2_500 ) ;
408+ assert ! ( policy. check_all( & selection, & tx) . is_ok( ) ) ;
409+
410+ // Locktime above the tip height.
411+ tx. lock_time = absolute:: LockTime :: from_consensus ( 3_001 ) ;
412+ assert ! ( matches!(
413+ policy. check_all( & selection, & tx) ,
414+ Err ( MempoolPolicyError :: LockTimeNotMet ( _) )
415+ ) ) ;
416+
417+ // Locktime one second below the tip MTP.
418+ tx. lock_time = absolute:: LockTime :: from_consensus ( 500_000_999 ) ;
419+ assert ! ( policy. check_all( & selection, & tx) . is_ok( ) ) ;
420+
421+ // Locktime exactly equal to the tip MTP.
422+ tx. lock_time = absolute:: LockTime :: from_consensus ( 500_001_000 ) ;
423+ assert ! ( matches!(
424+ policy. check_all( & selection, & tx) ,
425+ Err ( MempoolPolicyError :: LockTimeNotMet ( _) )
426+ ) ) ;
427+
428+ // Locktime above the tip MTP.
429+ tx. lock_time = absolute:: LockTime :: from_consensus ( 500_002_000 ) ;
430+ assert ! ( matches!(
431+ policy. check_all( & selection, & tx) ,
432+ Err ( MempoolPolicyError :: LockTimeNotMet ( _) )
433+ ) ) ;
434+ }
435+
436+ #[ test]
437+ fn test_max_tx_weight ( ) {
438+ let policy = default_policy ( ) ;
439+
440+ // A normal transaction with 1 input and 1 output.
441+ let input = setup_test_input ( 2_000 ) . unwrap ( ) ;
442+ let output = create_output ( p2tr_script ( ) , 9_000 ) ;
443+ let ( selection, tx) = build_selection_with_tx ( core:: slice:: from_ref ( & input) , & [ output] ) ;
444+ assert ! ( policy. check_all( & selection, & tx) . is_ok( ) ) ;
445+
446+ // Heavy transaction with excess weight.
447+ let outputs_with_excess_weight: Vec < Output > = ( 0 ..2_350 )
448+ . map ( |_| create_output ( p2tr_script ( ) , 1_000 ) )
449+ . collect ( ) ;
450+
451+ let ( _, heavy_tx) =
452+ build_selection_with_tx ( & [ input] , outputs_with_excess_weight. as_slice ( ) ) ;
453+
454+ assert ! ( heavy_tx. weight( ) . to_wu( ) > MAX_STANDARD_TX_WEIGHT as u64 ) ;
455+ assert ! ( matches!(
456+ policy. check_max_tx_weight( heavy_tx. weight( ) , heavy_tx. version) ,
457+ Err ( MempoolPolicyError :: MaxWeightExceeded { .. } )
458+ ) ) ;
459+ }
460+
461+ #[ test]
462+ fn test_tx_min_non_witness_size ( ) {
463+ let policy = default_policy ( ) ;
464+ let input = setup_test_input ( 2_000 ) . unwrap ( ) ;
465+ let output = create_output ( p2tr_script ( ) , 9_000 ) ;
466+
467+ // Transaction with 1 input and 1 output.
468+ let ( selection, tx) = build_selection_with_tx ( & [ input] , & [ output] ) ;
469+ assert ! ( policy. check_all( & selection, & tx) . is_ok( ) ) ;
470+
471+ // Transaction with no inputs and 1 output.
472+ let tx_below_min_non_witness_size = Transaction {
473+ version : Version :: TWO ,
474+ lock_time : absolute:: LockTime :: ZERO ,
475+ input : vec ! [ ] ,
476+ output : vec ! [ TxOut {
477+ script_pubkey: ScriptBuf :: new( ) ,
478+ value: Amount :: ZERO ,
479+ } ] ,
480+ } ;
481+ let empty_selection = Selection {
482+ inputs : vec ! [ ] ,
483+ outputs : vec ! [ Output :: with_script( ScriptBuf :: new( ) , Amount :: ZERO ) ] ,
484+ } ;
485+ assert ! (
486+ tx_below_min_non_witness_size. base_size( ) < MIN_STANDARD_TX_NONWITNESS_SIZE as usize
487+ ) ;
488+ assert ! ( matches!(
489+ policy. check_all( & empty_selection, & tx_below_min_non_witness_size) ,
490+ Err ( MempoolPolicyError :: TxTooSmall { .. } )
491+ ) ) ;
492+ }
493+
494+ #[ test]
495+ fn test_min_fee_relay ( ) {
496+ let policy = default_policy ( ) ;
497+
498+ // Sufficient fee passes.
499+ let input = setup_test_input ( 2_000 ) . unwrap ( ) ;
500+ let output = create_output ( p2tr_script ( ) , 9_000 ) ;
501+
502+ let ( selection, tx) = build_selection_with_tx ( & [ input] , & [ output] ) ;
503+ assert ! ( policy. check_all( & selection, & tx) . is_ok( ) ) ;
504+
505+ // Fee below the 1 sat/vB minimum is rejected.
506+ let input_with_insufficient_fee = setup_test_input ( 2_000 ) . unwrap ( ) ;
507+ let output_with_insufficient_fee = create_output ( p2tr_script ( ) , 9_999 ) ;
508+
509+ let ( selection_with_insufficient_fee, tx_with_insufficient_fee) = build_selection_with_tx (
510+ & [ input_with_insufficient_fee] ,
511+ & [ output_with_insufficient_fee] ,
512+ ) ;
513+ assert ! ( matches!(
514+ policy. check_all( & selection_with_insufficient_fee, & tx_with_insufficient_fee) ,
515+ Err ( MempoolPolicyError :: MinRelayFeeNotMet { .. } )
516+ ) ) ;
517+ }
518+
519+ #[ test]
520+ fn test_max_witness_stack ( ) {
521+ let policy = default_policy ( ) ;
522+ let input = setup_test_input ( 2_000 ) . unwrap ( ) ;
523+
524+ assert ! ( policy. check_max_witness_stack( & [ input] ) . is_ok( ) ) ;
525+ }
526+
527+ #[ test]
528+ fn test_input_spendability ( ) {
529+ let policy = default_policy ( ) ;
530+ // A confirmed input
531+ let input = setup_test_input ( 2_000 ) . unwrap ( ) ;
532+ assert ! ( policy. check_input_spendability( & [ input] ) . is_ok( ) ) ;
533+
534+ // An immature input
535+ let input_with_immature_coinbase = setup_test_input ( 2_950 ) . unwrap ( ) ;
536+ assert ! ( policy
537+ . check_input_spendability( & [ input_with_immature_coinbase] )
538+ . is_err( ) ) ;
539+ }
540+
541+ #[ test]
542+ fn test_input_script_type ( ) {
543+ let policy = default_policy ( ) ;
544+ let input = setup_test_input ( 2_000 ) . unwrap ( ) ;
545+ assert ! ( policy. check_input_script_type( & [ input] ) . is_ok( ) ) ;
546+ }
547+ }
0 commit comments