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
20
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
+
26
+ use super :: limits:: MAX_STACK_ITEM_LIMIT ;
24
27
use { Miniscript , MiniscriptKey , Terminal } ;
25
28
26
29
/// Error for Script Context
@@ -39,6 +42,9 @@ pub enum ScriptContextError {
39
42
/// Only Compressed keys allowed under current descriptor
40
43
/// Segwitv0 fragments do not allow uncompressed pubkeys
41
44
CompressedOnly ,
45
+ /// Tapscript descriptors cannot contain uncompressed keys
46
+ /// Tap context can contain compressed or xonly
47
+ UncompressedKeysNotAllowed ,
42
48
/// At least one satisfaction path in the Miniscript fragment has more than
43
49
/// `MAX_STANDARD_P2WSH_STACK_ITEMS` (100) witness elements.
44
50
MaxWitnessItemssExceeded ,
@@ -55,6 +61,8 @@ pub enum ScriptContextError {
55
61
MaxScriptSigSizeExceeded ,
56
62
/// Impossible to satisfy the miniscript under the current context
57
63
ImpossibleSatisfaction ,
64
+ /// No Multi Node in Taproot context
65
+ TaprootMultiDisabled ,
58
66
}
59
67
60
68
impl fmt:: Display for ScriptContextError {
@@ -66,7 +74,17 @@ impl fmt::Display for ScriptContextError {
66
74
write ! ( f, "DupIf is malleable under Legacy rules" )
67
75
}
68
76
ScriptContextError :: CompressedOnly => {
69
- write ! ( f, "Uncompressed pubkeys not allowed in segwit context" )
77
+ write ! (
78
+ f,
79
+ "Only Compressed pubkeys not allowed in segwit context. X-only and uncompressed keys are forbidden"
80
+ )
81
+ }
82
+ ScriptContextError :: UncompressedKeysNotAllowed => {
83
+ write ! (
84
+ f,
85
+ "Only x-only keys are allowed in tapscript checksig. \
86
+ Compressed keys maybe specified in descriptor."
87
+ )
70
88
}
71
89
ScriptContextError :: MaxWitnessItemssExceeded => write ! (
72
90
f,
@@ -99,6 +117,9 @@ impl fmt::Display for ScriptContextError {
99
117
"Impossible to satisfy Miniscript under the current context"
100
118
)
101
119
}
120
+ ScriptContextError :: TaprootMultiDisabled => {
121
+ write ! ( f, "No Multi node in taproot context" )
122
+ }
102
123
}
103
124
}
104
125
}
@@ -243,6 +264,13 @@ pub trait ScriptContext:
243
264
Self :: top_level_type_check ( ms) ?;
244
265
Self :: other_top_level_checks ( ms)
245
266
}
267
+
268
+ /// Reverse lookup to store whether the context is tapscript.
269
+ /// pk(33-byte key) is a valid under both tapscript context and segwitv0 context
270
+ /// We need to context decide whether the serialize pk to 33 byte or 32 bytes.
271
+ fn is_tapctx ( ) -> bool {
272
+ return false ;
273
+ }
246
274
}
247
275
248
276
/// Legacy ScriptContext
@@ -353,6 +381,12 @@ impl ScriptContext for Segwitv0 {
353
381
}
354
382
Ok ( ( ) )
355
383
}
384
+ Terminal :: Multi ( _k, ref pks) => {
385
+ if pks. iter ( ) . any ( |pk| pk. is_uncompressed ( ) ) {
386
+ return Err ( ScriptContextError :: CompressedOnly ) ;
387
+ }
388
+ Ok ( ( ) )
389
+ }
356
390
_ => Ok ( ( ) ) ,
357
391
}
358
392
}
@@ -402,6 +436,97 @@ impl ScriptContext for Segwitv0 {
402
436
}
403
437
}
404
438
439
+ /// Tap ScriptContext
440
+ #[ derive( Debug , Clone , Eq , PartialEq , Ord , PartialOrd , Hash ) ]
441
+ pub enum Tap { }
442
+
443
+ impl ScriptContext for Tap {
444
+ fn check_terminal_non_malleable < Pk : MiniscriptKey , Ctx : ScriptContext > (
445
+ _frag : & Terminal < Pk , Ctx > ,
446
+ ) -> Result < ( ) , ScriptContextError > {
447
+ // No fragment is malleable in tapscript context.
448
+ // Certain fragments like Multi are invalid, but are not malleable
449
+ Ok ( ( ) )
450
+ }
451
+
452
+ fn check_witness < Pk : MiniscriptKey , Ctx : ScriptContext > (
453
+ witness : & [ Vec < u8 > ] ,
454
+ ) -> Result < ( ) , ScriptContextError > {
455
+ if witness. len ( ) > MAX_STACK_ITEM_LIMIT {
456
+ return Err ( ScriptContextError :: MaxWitnessItemssExceeded ) ;
457
+ }
458
+ Ok ( ( ) )
459
+ }
460
+
461
+ fn check_global_consensus_validity < Pk : MiniscriptKey , Ctx : ScriptContext > (
462
+ ms : & Miniscript < Pk , Ctx > ,
463
+ ) -> Result < ( ) , ScriptContextError > {
464
+ // No script size checks for global consensus rules
465
+ // Should we really check for block limits here.
466
+ // When the transaction sizes get close to block limits,
467
+ // some guarantees are not easy to satisfy because of knapsack
468
+ // constraints
469
+ if ms. ext . pk_cost > MAX_BLOCK_WEIGHT as usize {
470
+ return Err ( ScriptContextError :: MaxWitnessScriptSizeExceeded ) ;
471
+ }
472
+
473
+ match ms. node {
474
+ Terminal :: PkK ( ref pk) => {
475
+ if pk. is_uncompressed ( ) {
476
+ return Err ( ScriptContextError :: UncompressedKeysNotAllowed ) ;
477
+ }
478
+ Ok ( ( ) )
479
+ }
480
+ Terminal :: Multi ( ..) => {
481
+ return Err ( ScriptContextError :: TaprootMultiDisabled ) ;
482
+ }
483
+ // What happens to the Multi node in tapscript? Do we use it, create
484
+ // a new fragment?
485
+ _ => Ok ( ( ) ) ,
486
+ }
487
+ }
488
+
489
+ fn check_local_consensus_validity < Pk : MiniscriptKey , Ctx : ScriptContext > (
490
+ _ms : & Miniscript < Pk , Ctx > ,
491
+ ) -> Result < ( ) , ScriptContextError > {
492
+ // Taproot introduces the concept of sigops budget.
493
+ // In all possible valid miniscripts satisfy the given sigops constraint
494
+ // Whenever we add new fragment that uses pk(pk() or multi based on checksigadd)
495
+ // miniscript typing rules ensure that pk when executed successfully has it's
496
+ // own unique signature. That is there is no way to re-use signatures for another
497
+ // checksig. Therefore, for each successfully executed checksig, we will have
498
+ // 64 bytes signature and thus sigops budget is always covered.
499
+ // There is overall limit of consensus
500
+ // TODO: track height during execution
501
+ Ok ( ( ) )
502
+ }
503
+
504
+ fn check_global_policy_validity < Pk : MiniscriptKey , Ctx : ScriptContext > (
505
+ _ms : & Miniscript < Pk , Ctx > ,
506
+ ) -> Result < ( ) , ScriptContextError > {
507
+ // No script rules, rules are subject to entire tx rules
508
+ Ok ( ( ) )
509
+ }
510
+
511
+ fn check_local_policy_validity < Pk : MiniscriptKey , Ctx : ScriptContext > (
512
+ _ms : & Miniscript < Pk , Ctx > ,
513
+ ) -> Result < ( ) , ScriptContextError > {
514
+ // TODO: check for policy execution.
515
+ Ok ( ( ) )
516
+ }
517
+
518
+ fn max_satisfaction_size < Pk : MiniscriptKey , Ctx : ScriptContext > (
519
+ ms : & Miniscript < Pk , Ctx > ,
520
+ ) -> Option < usize > {
521
+ // The witness stack cost is the first element of the tuple
522
+ ms. ext . max_sat_size . map ( |x| x. 0 )
523
+ }
524
+
525
+ fn is_tapctx ( ) -> bool {
526
+ true
527
+ }
528
+ }
529
+
405
530
/// Bare ScriptContext
406
531
/// To be used as raw script pubkeys
407
532
/// In general, it is not recommended to use Bare descriptors
@@ -510,13 +635,14 @@ impl ScriptContext for NoChecks {
510
635
511
636
/// Private Mod to prevent downstream from implementing this public trait
512
637
mod private {
513
- use super :: { BareCtx , Legacy , NoChecks , Segwitv0 } ;
638
+ use super :: { BareCtx , Legacy , NoChecks , Segwitv0 , Tap } ;
514
639
515
640
pub trait Sealed { }
516
641
517
642
// Implement for those same types, but no others.
518
643
impl Sealed for BareCtx { }
519
644
impl Sealed for Legacy { }
520
645
impl Sealed for Segwitv0 { }
646
+ impl Sealed for Tap { }
521
647
impl Sealed for NoChecks { }
522
648
}
0 commit comments