14
14
15
15
use std:: { fmt, hash} ;
16
16
17
+ use bitcoin:: blockdata:: constants:: MAX_BLOCK_WEIGHT ;
17
18
use miniscript:: limits:: {
18
19
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 ,
20
21
} ;
21
22
use miniscript:: types;
22
23
use util:: witness_to_scriptsig;
23
24
use Error ;
25
+
24
26
use { Miniscript , MiniscriptKey , Terminal } ;
25
27
26
28
/// Error for Script Context
@@ -39,6 +41,9 @@ pub enum ScriptContextError {
39
41
/// Only Compressed keys allowed under current descriptor
40
42
/// Segwitv0 fragments do not allow uncompressed pubkeys
41
43
CompressedOnly ,
44
+ /// Tapscript descriptors cannot contain uncompressed keys
45
+ /// Tap context can contain compressed or xonly
46
+ UncompressedKeysNotAllowed ,
42
47
/// At least one satisfaction path in the Miniscript fragment has more than
43
48
/// `MAX_STANDARD_P2WSH_STACK_ITEMS` (100) witness elements.
44
49
MaxWitnessItemssExceeded ,
@@ -55,6 +60,8 @@ pub enum ScriptContextError {
55
60
MaxScriptSigSizeExceeded ,
56
61
/// Impossible to satisfy the miniscript under the current context
57
62
ImpossibleSatisfaction ,
63
+ /// No Multi Node in Taproot context
64
+ TaprootMultiDisabled ,
58
65
}
59
66
60
67
impl fmt:: Display for ScriptContextError {
@@ -66,7 +73,17 @@ impl fmt::Display for ScriptContextError {
66
73
write ! ( f, "DupIf is malleable under Legacy rules" )
67
74
}
68
75
ScriptContextError :: CompressedOnly => {
69
- write ! ( f, "Uncompressed pubkeys not allowed in segwit context" )
76
+ write ! (
77
+ f,
78
+ "Only Compressed pubkeys not 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
+ )
70
87
}
71
88
ScriptContextError :: MaxWitnessItemssExceeded => write ! (
72
89
f,
@@ -99,6 +116,9 @@ impl fmt::Display for ScriptContextError {
99
116
"Impossible to satisfy Miniscript under the current context"
100
117
)
101
118
}
119
+ ScriptContextError :: TaprootMultiDisabled => {
120
+ write ! ( f, "No Multi node in taproot context" )
121
+ }
102
122
}
103
123
}
104
124
}
@@ -243,6 +263,19 @@ pub trait ScriptContext:
243
263
Self :: top_level_type_check ( ms) ?;
244
264
Self :: other_top_level_checks ( ms)
245
265
}
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_tapctx ( ) -> 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 ;
246
279
}
247
280
248
281
/// Legacy ScriptContext
@@ -317,6 +350,14 @@ impl ScriptContext for Legacy {
317
350
// The scriptSig cost is the second element of the tuple
318
351
ms. ext . max_sat_size . map ( |x| x. 1 )
319
352
}
353
+
354
+ fn pk_len < Pk : MiniscriptKey > ( pk : & Pk ) -> usize {
355
+ if pk. is_uncompressed ( ) {
356
+ 66
357
+ } else {
358
+ 34
359
+ }
360
+ }
320
361
}
321
362
322
363
/// Segwitv0 ScriptContext
@@ -353,6 +394,12 @@ impl ScriptContext for Segwitv0 {
353
394
}
354
395
Ok ( ( ) )
355
396
}
397
+ Terminal :: Multi ( _k, ref pks) => {
398
+ if pks. iter ( ) . any ( |pk| pk. is_uncompressed ( ) ) {
399
+ return Err ( ScriptContextError :: CompressedOnly ) ;
400
+ }
401
+ Ok ( ( ) )
402
+ }
356
403
_ => Ok ( ( ) ) ,
357
404
}
358
405
}
@@ -400,6 +447,105 @@ impl ScriptContext for Segwitv0 {
400
447
// The witness stack cost is the first element of the tuple
401
448
ms. ext . max_sat_size . map ( |x| x. 0 )
402
449
}
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
+ // In all possible valid miniscripts satisfy the given 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 for another
514
+ // checksig. Therefore, for each successfully executed checksig, we will have
515
+ // 64 bytes signature and thus sigops budget is always covered.
516
+ // There is overall limit of consensus
517
+ // TODO: track height during execution
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_tapctx ( ) -> bool {
543
+ true
544
+ }
545
+
546
+ fn pk_len < Pk : MiniscriptKey > ( _pk : & Pk ) -> usize {
547
+ 33
548
+ }
403
549
}
404
550
405
551
/// Bare ScriptContext
@@ -461,6 +607,14 @@ impl ScriptContext for BareCtx {
461
607
// The witness stack cost is the first element of the tuple
462
608
ms. ext . max_sat_size . map ( |x| x. 1 )
463
609
}
610
+
611
+ fn pk_len < Pk : MiniscriptKey > ( pk : & Pk ) -> usize {
612
+ if pk. is_uncompressed ( ) {
613
+ 65
614
+ } else {
615
+ 33
616
+ }
617
+ }
464
618
}
465
619
466
620
/// "No Checks" Context
@@ -506,17 +660,22 @@ impl ScriptContext for NoChecks {
506
660
) -> Option < usize > {
507
661
panic ! ( "Tried to compute a satisfaction size bound on a no-checks miniscript" )
508
662
}
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
+ }
509
667
}
510
668
511
669
/// Private Mod to prevent downstream from implementing this public trait
512
670
mod private {
513
- use super :: { BareCtx , Legacy , NoChecks , Segwitv0 } ;
671
+ use super :: { BareCtx , Legacy , NoChecks , Segwitv0 , Tap } ;
514
672
515
673
pub trait Sealed { }
516
674
517
675
// Implement for those same types, but no others.
518
676
impl Sealed for BareCtx { }
519
677
impl Sealed for Legacy { }
520
678
impl Sealed for Segwitv0 { }
679
+ impl Sealed for Tap { }
521
680
impl Sealed for NoChecks { }
522
681
}
0 commit comments