@@ -39,9 +39,9 @@ use core::cmp;
3939
4040use sync:: Arc ;
4141
42+ pub use self :: context:: ScriptContext ;
4243use self :: lex:: { lex, TokenIter } ;
4344use crate :: expression:: { FromTree , TreeIterItem } ;
44- pub use crate :: miniscript:: context:: ScriptContext ;
4545use crate :: miniscript:: decode:: Terminal ;
4646use crate :: {
4747 expression, plan, Error , ForEachKey , FromStrKey , MiniscriptKey , ToPublicKey , Translator ,
@@ -54,10 +54,13 @@ mod private {
5454
5555 use super :: limits:: { MAX_PUBKEYS_IN_CHECKSIGADD , MAX_PUBKEYS_PER_MULTISIG } ;
5656 use super :: types:: { self , ExtData , Type } ;
57+ use super :: ScriptContext ;
5758 use crate :: iter:: TreeLike as _;
58- pub use crate :: miniscript:: context:: ScriptContext ;
5959 use crate :: prelude:: sync:: Arc ;
60- use crate :: { AbsLockTime , Error , MiniscriptKey , RelLockTime , Terminal , MAX_RECURSION_DEPTH } ;
60+ use crate :: {
61+ AbsLockTime , Error , MiniscriptKey , RelLockTime , Terminal , ValidationError ,
62+ ValidationParams , MAX_RECURSION_DEPTH ,
63+ } ;
6164
6265 /// The top-level miniscript abstract syntax tree (AST).
6366 pub struct Miniscript < Pk : MiniscriptKey , Ctx : ScriptContext > {
@@ -326,6 +329,7 @@ mod private {
326329 node : t,
327330 phantom : PhantomData ,
328331 } ;
332+
329333 // TODO: This recursion depth is based on segwitv0.
330334 // We can relax this in tapscript, but this should be good for almost
331335 // all practical cases and we can revisit this if needed.
@@ -349,6 +353,170 @@ mod private {
349353 ) -> Miniscript < Pk , Ctx > {
350354 Miniscript { node, ty, ext, phantom : PhantomData }
351355 }
356+
357+ /// Validates whether a given fragment meets the given set of
358+ /// validation parameters.
359+ pub fn validate ( & self , params : & ValidationParams ) -> Result < ( ) , ValidationError >
360+ where
361+ Pk : MiniscriptKey ,
362+ Ctx : ScriptContext ,
363+ {
364+ self . validate_non_top_level ( params) ?;
365+
366+ // Malleability is only a top-level check since you can fix malleability
367+ // in some cases by adding wrappers.
368+ if !params. allow_malleability && !self . is_non_malleable ( ) {
369+ return Err ( ValidationError :: Malleable ) ;
370+ }
371+
372+ // The B check is inherently top-level.
373+ if !params. allow_non_b && self . ty . corr . base != types:: Base :: B {
374+ return Err ( ValidationError :: NonBase ( self . ty . corr . base ) ) ;
375+ }
376+
377+ // Sigless branches can be fixed by adding a conjunction with a signature.
378+ if !params. allow_sigless_branch && !self . requires_sig ( ) {
379+ return Err ( ValidationError :: SiglessBranch ) ;
380+ }
381+
382+ // Unsatisifiable scripts can be fixed by adding a disjunction with something
383+ // satisfiable.
384+ if !params. allow_unsatisfiable && self . ext . sat_data . is_none ( ) {
385+ return Err ( ValidationError :: Unsatisfiable ) ;
386+ }
387+
388+ // All checks passed.
389+ Ok ( ( ) )
390+ }
391+
392+ /// Validates a miniscript, doing only the checks that are applicable to all nodes,
393+ /// not just top-level ones.
394+ ///
395+ /// In particular this excludes the "must be B" and "no sigless branches" checks.
396+ /// To get these, run [`Self::validate`] which also calls through to this method.
397+ pub fn validate_non_top_level (
398+ & self ,
399+ params : & ValidationParams ,
400+ ) -> Result < ( ) , ValidationError > {
401+ if self . ext . tree_height > params. max_recursive_depth {
402+ return Err ( ValidationError :: MaxRecursiveDepthExceeded {
403+ limit : params. max_recursive_depth ,
404+ } ) ;
405+ }
406+ if !params. allow_duplicate_keys && self . has_repeated_keys ( ) {
407+ return Err ( ValidationError :: DuplicateKeys ) ;
408+ }
409+ if !params. allow_mixed_time_locks && self . has_mixed_timelocks ( ) {
410+ return Err ( ValidationError :: MixedTimeLocks ) ;
411+ }
412+
413+ let mut multipath_len = None ;
414+ let mut multipath_check = |pk : & Pk | {
415+ if params. allow_inconsistent_multipath_keys {
416+ return Ok ( ( ) ) ;
417+ }
418+ match ( multipath_len, pk. num_der_paths ( ) ) {
419+ ( _, 0 ) | ( _, 1 ) => { }
420+ ( None , n) => multipath_len = Some ( n) ,
421+ ( Some ( x) , y) if x == y => { /* ok */ }
422+ ( Some ( x) , y) => {
423+ return Err ( ValidationError :: MultipathKeyLenMismatch { len1 : x, len2 : y } )
424+ }
425+ }
426+ Ok ( ( ) )
427+ } ;
428+ for ms in self . iter ( ) {
429+ match ms. node {
430+ Terminal :: DupIf ( ..) if !params. allow_dup_if => {
431+ return Err ( ValidationError :: IllegalDupIf )
432+ }
433+ Terminal :: Multi ( ref thresh) | Terminal :: SortedMulti ( ref thresh) => {
434+ if !params. allow_multi {
435+ return Err ( ValidationError :: IllegalMulti ) ;
436+ }
437+ for key in thresh. iter ( ) {
438+ params. validate_pk ( key) . map_err ( ValidationError :: Key ) ?;
439+ multipath_check ( key) ?;
440+ }
441+ }
442+ Terminal :: MultiA ( ref thresh) | Terminal :: SortedMultiA ( ref thresh) => {
443+ if !params. allow_multi_a {
444+ return Err ( ValidationError :: IllegalMultiA ) ;
445+ }
446+ for key in thresh. iter ( ) {
447+ params. validate_pk ( key) . map_err ( ValidationError :: Key ) ?;
448+ multipath_check ( key) ?;
449+ }
450+ }
451+ Terminal :: OrI ( ..) if !params. allow_or_i => {
452+ return Err ( ValidationError :: IllegalOrI )
453+ }
454+ Terminal :: RawPkH ( ..) if !params. allow_raw_pkh => {
455+ return Err ( ValidationError :: IllegalRawPkh )
456+ }
457+ Terminal :: PkK ( ref pk) | Terminal :: PkH ( ref pk) => {
458+ params. validate_pk ( pk) . map_err ( ValidationError :: Key ) ?;
459+ multipath_check ( pk) ?;
460+ }
461+ _ => { }
462+ }
463+ }
464+
465+ // FIXME we have to gate this check on params.max_script_size being finite
466+ // because otherwise we'll attempt to call self.script_size(), which calls
467+ // Ctx::pk_len, when validating NoChecks scripts. But NoChecks::pk_len just
468+ // panics because we've forgotten the length of keys once we're in the
469+ // NoChecks context.
470+ //
471+ // This gate will be removed in a later PR which removes Ctx::pk_len and
472+ // replaces it with a less-fragile solution.
473+ if params. max_script_size < usize:: MAX && self . script_size ( ) > params. max_script_size {
474+ return Err ( ValidationError :: MaxScriptSizeExceeded {
475+ actual : self . script_size ( ) ,
476+ limit : params. max_script_size ,
477+ } ) ;
478+ }
479+ // Satisfiability checks -- if there is no satisfaciton data set then we will
480+ // early-return here, so all other checks should be done before this.
481+ match self . max_satisfaction_witness_elements ( ) {
482+ // No possible satisfactions -- we are doing non-toplevel checks here
483+ // so fail gracefully.
484+ Err ( ..) => return Ok ( ( ) ) ,
485+ Ok ( max_n) if max_n > params. max_witness_items => {
486+ return Err ( ValidationError :: MaxWitnessItemsExceeded {
487+ actual : max_n,
488+ limit : params. max_witness_items ,
489+ } )
490+ }
491+ Ok ( ..) => { }
492+ }
493+
494+ let sat_op_count = self
495+ . ext
496+ . sat_op_count ( )
497+ . expect ( "checked that satisfaction was possible above" ) ;
498+ if sat_op_count > params. max_opcode_count {
499+ return Err ( ValidationError :: MaxOpCountExceeded {
500+ actual : sat_op_count,
501+ limit : params. max_opcode_count ,
502+ } ) ;
503+ }
504+
505+ let sat_data = self
506+ . ext
507+ . sat_data
508+ . expect ( "checked that satisfaction was possible above" ) ;
509+ if sat_data. max_witness_stack_count + sat_data. max_exec_stack_count
510+ > params. max_exec_stack_size
511+ {
512+ return Err ( ValidationError :: MaxExecStackSizeExceeded {
513+ actual : sat_data. max_witness_stack_size + sat_data. max_exec_stack_count ,
514+ limit : params. max_exec_stack_size ,
515+ } ) ;
516+ }
517+
518+ Ok ( ( ) )
519+ }
352520 }
353521}
354522
0 commit comments