1414
1515use std:: { fmt, hash} ;
1616
17+ use bitcoin:: blockdata:: constants:: MAX_BLOCK_WEIGHT ;
1718use miniscript:: limits:: {
1819 MAX_OPS_PER_SCRIPT , MAX_SCRIPTSIG_SIZE , MAX_SCRIPT_ELEMENT_SIZE , MAX_SCRIPT_SIZE ,
19- MAX_STANDARD_P2WSH_SCRIPT_SIZE , MAX_STANDARD_P2WSH_STACK_ITEMS ,
20+ MAX_STACK_SIZE , MAX_STANDARD_P2WSH_SCRIPT_SIZE , MAX_STANDARD_P2WSH_STACK_ITEMS ,
2021} ;
2122use miniscript:: types;
2223use util:: witness_to_scriptsig;
2324use Error ;
25+
2426use { Miniscript , MiniscriptKey , Terminal } ;
2527
2628/// Error for Script Context
@@ -39,6 +41,9 @@ pub enum ScriptContextError {
3941 /// Only Compressed keys allowed under current descriptor
4042 /// Segwitv0 fragments do not allow uncompressed pubkeys
4143 CompressedOnly ,
44+ /// Tapscript descriptors cannot contain uncompressed keys
45+ /// Tap context can contain compressed or xonly
46+ UncompressedKeysNotAllowed ,
4247 /// At least one satisfaction path in the Miniscript fragment has more than
4348 /// `MAX_STANDARD_P2WSH_STACK_ITEMS` (100) witness elements.
4449 MaxWitnessItemssExceeded ,
@@ -55,6 +60,8 @@ pub enum ScriptContextError {
5560 MaxScriptSigSizeExceeded ,
5661 /// Impossible to satisfy the miniscript under the current context
5762 ImpossibleSatisfaction ,
63+ /// No Multi Node in Taproot context
64+ TaprootMultiDisabled ,
5865}
5966
6067impl fmt:: Display for ScriptContextError {
@@ -66,7 +73,17 @@ impl fmt::Display for ScriptContextError {
6673 write ! ( f, "DupIf is malleable under Legacy rules" )
6774 }
6875 ScriptContextError :: CompressedOnly => {
69- write ! ( f, "Uncompressed pubkeys not allowed in segwit context" )
76+ write ! (
77+ f,
78+ "Only Compressed pubkeys are allowed in segwit context. X-only and uncompressed keys are forbidden"
79+ )
80+ }
81+ ScriptContextError :: UncompressedKeysNotAllowed => {
82+ write ! (
83+ f,
84+ "Only x-only keys are allowed in tapscript checksig. \
85+ Compressed keys maybe specified in descriptor."
86+ )
7087 }
7188 ScriptContextError :: MaxWitnessItemssExceeded => write ! (
7289 f,
@@ -99,6 +116,9 @@ impl fmt::Display for ScriptContextError {
99116 "Impossible to satisfy Miniscript under the current context"
100117 )
101118 }
119+ ScriptContextError :: TaprootMultiDisabled => {
120+ write ! ( f, "No Multi node in taproot context" )
121+ }
102122 }
103123 }
104124}
@@ -243,6 +263,19 @@ pub trait ScriptContext:
243263 Self :: top_level_type_check ( ms) ?;
244264 Self :: other_top_level_checks ( ms)
245265 }
266+
267+ /// Reverse lookup to store whether the context is tapscript.
268+ /// pk(33-byte key) is a valid under both tapscript context and segwitv0 context
269+ /// We need to context decide whether the serialize pk to 33 byte or 32 bytes.
270+ fn is_tap ( ) -> bool {
271+ return false ;
272+ }
273+
274+ /// Get the len of public key when serialized based on context
275+ /// Note that this includes the serialization prefix. Returns
276+ /// 34/66 for Bare/Legacy based on key compressedness
277+ /// 34 for Segwitv0, 33 for Tap
278+ fn pk_len < Pk : MiniscriptKey > ( pk : & Pk ) -> usize ;
246279}
247280
248281/// Legacy ScriptContext
@@ -317,6 +350,14 @@ impl ScriptContext for Legacy {
317350 // The scriptSig cost is the second element of the tuple
318351 ms. ext . max_sat_size . map ( |x| x. 1 )
319352 }
353+
354+ fn pk_len < Pk : MiniscriptKey > ( pk : & Pk ) -> usize {
355+ if pk. is_uncompressed ( ) {
356+ 66
357+ } else {
358+ 34
359+ }
360+ }
320361}
321362
322363/// Segwitv0 ScriptContext
@@ -353,6 +394,12 @@ impl ScriptContext for Segwitv0 {
353394 }
354395 Ok ( ( ) )
355396 }
397+ Terminal :: Multi ( _k, ref pks) => {
398+ if pks. iter ( ) . any ( |pk| pk. is_uncompressed ( ) ) {
399+ return Err ( ScriptContextError :: CompressedOnly ) ;
400+ }
401+ Ok ( ( ) )
402+ }
356403 _ => Ok ( ( ) ) ,
357404 }
358405 }
@@ -400,6 +447,105 @@ impl ScriptContext for Segwitv0 {
400447 // The witness stack cost is the first element of the tuple
401448 ms. ext . max_sat_size . map ( |x| x. 0 )
402449 }
450+
451+ fn pk_len < Pk : MiniscriptKey > ( _pk : & Pk ) -> usize {
452+ 34
453+ }
454+ }
455+
456+ /// Tap ScriptContext
457+ #[ derive( Debug , Clone , Eq , PartialEq , Ord , PartialOrd , Hash ) ]
458+ pub enum Tap { }
459+
460+ impl ScriptContext for Tap {
461+ fn check_terminal_non_malleable < Pk : MiniscriptKey , Ctx : ScriptContext > (
462+ _frag : & Terminal < Pk , Ctx > ,
463+ ) -> Result < ( ) , ScriptContextError > {
464+ // No fragment is malleable in tapscript context.
465+ // Certain fragments like Multi are invalid, but are not malleable
466+ Ok ( ( ) )
467+ }
468+
469+ fn check_witness < Pk : MiniscriptKey , Ctx : ScriptContext > (
470+ witness : & [ Vec < u8 > ] ,
471+ ) -> Result < ( ) , ScriptContextError > {
472+ if witness. len ( ) > MAX_STACK_SIZE {
473+ return Err ( ScriptContextError :: MaxWitnessItemssExceeded ) ;
474+ }
475+ Ok ( ( ) )
476+ }
477+
478+ fn check_global_consensus_validity < Pk : MiniscriptKey , Ctx : ScriptContext > (
479+ ms : & Miniscript < Pk , Ctx > ,
480+ ) -> Result < ( ) , ScriptContextError > {
481+ // No script size checks for global consensus rules
482+ // Should we really check for block limits here.
483+ // When the transaction sizes get close to block limits,
484+ // some guarantees are not easy to satisfy because of knapsack
485+ // constraints
486+ if ms. ext . pk_cost > MAX_BLOCK_WEIGHT as usize {
487+ return Err ( ScriptContextError :: MaxWitnessScriptSizeExceeded ) ;
488+ }
489+
490+ match ms. node {
491+ Terminal :: PkK ( ref pk) => {
492+ if pk. is_uncompressed ( ) {
493+ return Err ( ScriptContextError :: UncompressedKeysNotAllowed ) ;
494+ }
495+ Ok ( ( ) )
496+ }
497+ Terminal :: Multi ( ..) => {
498+ return Err ( ScriptContextError :: TaprootMultiDisabled ) ;
499+ }
500+ // What happens to the Multi node in tapscript? Do we use it, create
501+ // a new fragment?
502+ _ => Ok ( ( ) ) ,
503+ }
504+ }
505+
506+ fn check_local_consensus_validity < Pk : MiniscriptKey , Ctx : ScriptContext > (
507+ _ms : & Miniscript < Pk , Ctx > ,
508+ ) -> Result < ( ) , ScriptContextError > {
509+ // Taproot introduces the concept of sigops budget.
510+ // All valid miniscripts satisfy the sigops constraint
511+ // Whenever we add new fragment that uses pk(pk() or multi based on checksigadd)
512+ // miniscript typing rules ensure that pk when executed successfully has it's
513+ // own unique signature. That is, there is no way to re-use signatures from one CHECKSIG
514+ // to another checksig. In other words, for each successfully executed checksig
515+ // will have it's corresponding 64 bytes signature.
516+ // sigops budget = witness_script.len() + witness.size() + 50
517+ // Each signature will cover it's own cost(64 > 50) and thus will will never exceed the budget
518+ Ok ( ( ) )
519+ }
520+
521+ fn check_global_policy_validity < Pk : MiniscriptKey , Ctx : ScriptContext > (
522+ _ms : & Miniscript < Pk , Ctx > ,
523+ ) -> Result < ( ) , ScriptContextError > {
524+ // No script rules, rules are subject to entire tx rules
525+ Ok ( ( ) )
526+ }
527+
528+ fn check_local_policy_validity < Pk : MiniscriptKey , Ctx : ScriptContext > (
529+ _ms : & Miniscript < Pk , Ctx > ,
530+ ) -> Result < ( ) , ScriptContextError > {
531+ // TODO: check for policy execution.
532+ Ok ( ( ) )
533+ }
534+
535+ fn max_satisfaction_size < Pk : MiniscriptKey , Ctx : ScriptContext > (
536+ ms : & Miniscript < Pk , Ctx > ,
537+ ) -> Option < usize > {
538+ // The witness stack cost is the first element of the tuple
539+ ms. ext . max_sat_size . map ( |x| x. 0 )
540+ }
541+
542+ fn is_tap ( ) -> bool {
543+ true
544+ }
545+
546+ fn pk_len < Pk : MiniscriptKey > ( _pk : & Pk ) -> usize {
547+ 33
548+ }
403549}
404550
405551/// Bare ScriptContext
@@ -461,6 +607,14 @@ impl ScriptContext for BareCtx {
461607 // The witness stack cost is the first element of the tuple
462608 ms. ext . max_sat_size . map ( |x| x. 1 )
463609 }
610+
611+ fn pk_len < Pk : MiniscriptKey > ( pk : & Pk ) -> usize {
612+ if pk. is_uncompressed ( ) {
613+ 65
614+ } else {
615+ 33
616+ }
617+ }
464618}
465619
466620/// "No Checks" Context
@@ -506,17 +660,22 @@ impl ScriptContext for NoChecks {
506660 ) -> Option < usize > {
507661 panic ! ( "Tried to compute a satisfaction size bound on a no-checks miniscript" )
508662 }
663+
664+ fn pk_len < Pk : MiniscriptKey > ( _pk : & Pk ) -> usize {
665+ panic ! ( "Tried to compute a pk len bound on a no-checks miniscript" )
666+ }
509667}
510668
511669/// Private Mod to prevent downstream from implementing this public trait
512670mod private {
513- use super :: { BareCtx , Legacy , NoChecks , Segwitv0 } ;
671+ use super :: { BareCtx , Legacy , NoChecks , Segwitv0 , Tap } ;
514672
515673 pub trait Sealed { }
516674
517675 // Implement for those same types, but no others.
518676 impl Sealed for BareCtx { }
519677 impl Sealed for Legacy { }
520678 impl Sealed for Segwitv0 { }
679+ impl Sealed for Tap { }
521680 impl Sealed for NoChecks { }
522681}
0 commit comments