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