@@ -727,15 +727,72 @@ fn eval_native_script(
727727 }
728728 NativeScript :: InvalidBefore ( val) => {
729729 match low_bnd {
730- Some ( time) => val >= time,
730+ // The script is satisfied when the transaction cannot be applied
731+ // before `val`, i.e. its validity interval starts at or after it.
732+ Some ( time) => val <= time,
731733 None => false , // as per mary-ledger.pdf, p.20
732734 }
733735 }
734736 NativeScript :: InvalidHereafter ( val) => {
735737 match upp_bnd {
736- Some ( time) => val <= time,
738+ // The script is satisfied when the transaction cannot be applied
739+ // at or after `val`, i.e. its validity interval ends at or before it.
740+ Some ( time) => val >= time,
737741 None => false , // as per mary-ledger.pdf, p.20
738742 }
739743 }
740744 }
741745}
746+
747+ #[ cfg( test) ]
748+ mod tests {
749+ use super :: eval_native_script;
750+ use pallas_primitives:: alonzo:: NativeScript ;
751+
752+ // Evaluates `native_script` with no vkey witnesses and the given validity
753+ // interval bounds (`low_bnd` is `validity_interval_start`, `upp_bnd` is `ttl`).
754+ fn eval ( native_script : & NativeScript , low_bnd : Option < u64 > , upp_bnd : Option < u64 > ) -> bool {
755+ eval_native_script ( & Vec :: new ( ) , native_script, & low_bnd, & upp_bnd)
756+ }
757+
758+ #[ test]
759+ // `InvalidBefore(n)` holds only when the transaction's validity interval
760+ // starts at or after slot `n` (mary-ledger.pdf, p.20).
761+ fn invalid_before ( ) {
762+ let script = NativeScript :: InvalidBefore ( 100 ) ;
763+ assert ! ( eval( & script, Some ( 150 ) , None ) , "starts after the lock" ) ;
764+ assert ! ( eval( & script, Some ( 100 ) , None ) , "starts exactly at the lock" ) ;
765+ assert ! ( !eval( & script, Some ( 50 ) , None ) , "starts before the lock" ) ;
766+ assert ! ( !eval( & script, None , None ) , "no lower bound" ) ;
767+ }
768+
769+ #[ test]
770+ // `InvalidHereafter(n)` holds only when the transaction's validity interval
771+ // ends at or before slot `n` (mary-ledger.pdf, p.20).
772+ fn invalid_hereafter ( ) {
773+ let script = NativeScript :: InvalidHereafter ( 200 ) ;
774+ assert ! ( eval( & script, None , Some ( 150 ) ) , "ends before the lock" ) ;
775+ assert ! ( eval( & script, None , Some ( 200 ) ) , "ends exactly at the lock" ) ;
776+ assert ! ( !eval( & script, None , Some ( 250 ) ) , "ends after the lock" ) ;
777+ assert ! ( !eval( & script, None , None ) , "no upper bound" ) ;
778+ }
779+
780+ #[ test]
781+ // Recursion combinators must thread the bounds down to nested timelocks.
782+ fn nested_timelocks ( ) {
783+ let all = NativeScript :: ScriptAll ( vec ! [
784+ NativeScript :: InvalidBefore ( 100 ) ,
785+ NativeScript :: InvalidHereafter ( 200 ) ,
786+ ] ) ;
787+ assert ! ( eval( & all, Some ( 120 ) , Some ( 180 ) ) , "interval within the window" ) ;
788+ assert ! ( !eval( & all, Some ( 80 ) , Some ( 180 ) ) , "starts too early" ) ;
789+ assert ! ( !eval( & all, Some ( 120 ) , Some ( 220 ) ) , "ends too late" ) ;
790+
791+ let any = NativeScript :: ScriptAny ( vec ! [
792+ NativeScript :: InvalidBefore ( 100 ) ,
793+ NativeScript :: InvalidHereafter ( 200 ) ,
794+ ] ) ;
795+ assert ! ( eval( & any, Some ( 80 ) , Some ( 180 ) ) , "satisfies the hereafter arm" ) ;
796+ assert ! ( !eval( & any, Some ( 80 ) , Some ( 220 ) ) , "satisfies neither arm" ) ;
797+ }
798+ }
0 commit comments