Skip to content

Commit 2fb39ec

Browse files
committed
Cleanly check validity rules for DescriptorKeys
Each context has slightly different rules on what Pks are allowed in descriptors Legacy/Bare does not allow x_only keys SegwitV0 does not allow uncompressed keys and x_only keys Tapscript does not allow uncompressed keys Note that String types are allowed everywhere
1 parent 0112298 commit 2fb39ec

File tree

1 file changed

+70
-48
lines changed

1 file changed

+70
-48
lines changed

src/miniscript/context.rs

Lines changed: 70 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,12 @@ where
220220
Ok(())
221221
}
222222

223+
/// Each context has slightly different rules on what Pks are allowed in descriptors
224+
/// Legacy/Bare does not allow x_only keys
225+
/// Segwit does not allow uncompressed keys and x_only keys
226+
/// Tapscript does not allow uncompressed keys
227+
fn check_pk<Pk: MiniscriptKey>(pk: &Pk) -> Result<(), ScriptContextError>;
228+
223229
/// Depending on script context, the size of a satifaction witness may slightly differ.
224230
fn max_satisfaction_size<Pk: MiniscriptKey>(ms: &Miniscript<Pk, Self>) -> Option<usize>;
225231
/// Depending on script Context, some of the Terminals might not
@@ -367,6 +373,18 @@ impl ScriptContext for Legacy {
367373
}
368374
}
369375

376+
// Only compressed and uncompressed public keys are allowed in Legacy context
377+
fn check_pk<Pk: MiniscriptKey>(pk: &Pk) -> Result<(), ScriptContextError> {
378+
if pk.is_x_only_key() {
379+
Err(ScriptContextError::XOnlyKeysNotAllowed(
380+
pk.to_string(),
381+
Self::name_str(),
382+
))
383+
} else {
384+
Ok(())
385+
}
386+
}
387+
370388
fn check_witness<Pk: MiniscriptKey>(witness: &[Vec<u8>]) -> Result<(), ScriptContextError> {
371389
// In future, we could avoid by having a function to count only
372390
// len of script instead of converting it.
@@ -384,31 +402,21 @@ impl ScriptContext for Legacy {
384402
}
385403

386404
match ms.node {
387-
Terminal::PkK(ref key) if key.is_x_only_key() => {
388-
return Err(ScriptContextError::XOnlyKeysNotAllowed(
389-
key.to_string(),
390-
Self::name_str(),
391-
))
392-
}
405+
Terminal::PkK(ref pk) => Self::check_pk(pk),
393406
Terminal::Multi(_k, ref pks) => {
394407
if pks.len() > MAX_PUBKEYS_PER_MULTISIG {
395408
return Err(ScriptContextError::CheckMultiSigLimitExceeded);
396409
}
397410
for pk in pks.iter() {
398-
if pk.is_x_only_key() {
399-
return Err(ScriptContextError::XOnlyKeysNotAllowed(
400-
pk.to_string(),
401-
Self::name_str(),
402-
));
403-
}
411+
Self::check_pk(pk)?;
404412
}
413+
Ok(())
405414
}
406415
Terminal::MultiA(..) => {
407416
return Err(ScriptContextError::MultiANotAllowed);
408417
}
409-
_ => {}
418+
_ => Ok(()),
410419
}
411-
Ok(())
412420
}
413421

414422
fn check_local_consensus_validity<Pk: MiniscriptKey>(
@@ -472,6 +480,20 @@ impl ScriptContext for Segwitv0 {
472480
Ok(())
473481
}
474482

483+
// No x-only keys or uncompressed keys in Segwitv0 context
484+
fn check_pk<Pk: MiniscriptKey>(pk: &Pk) -> Result<(), ScriptContextError> {
485+
if pk.is_uncompressed() {
486+
Err(ScriptContextError::UncompressedKeysNotAllowed)
487+
} else if pk.is_x_only_key() {
488+
Err(ScriptContextError::XOnlyKeysNotAllowed(
489+
pk.to_string(),
490+
Self::name_str(),
491+
))
492+
} else {
493+
Ok(())
494+
}
495+
}
496+
475497
fn check_witness<Pk: MiniscriptKey>(witness: &[Vec<u8>]) -> Result<(), ScriptContextError> {
476498
if witness.len() > MAX_STANDARD_P2WSH_STACK_ITEMS {
477499
return Err(ScriptContextError::MaxWitnessItemssExceeded {
@@ -490,30 +512,13 @@ impl ScriptContext for Segwitv0 {
490512
}
491513

492514
match ms.node {
493-
Terminal::PkK(ref pk) => {
494-
if pk.is_uncompressed() {
495-
return Err(ScriptContextError::CompressedOnly(pk.to_string()));
496-
} else if pk.is_x_only_key() {
497-
return Err(ScriptContextError::XOnlyKeysNotAllowed(
498-
pk.to_string(),
499-
Self::name_str(),
500-
));
501-
}
502-
Ok(())
503-
}
515+
Terminal::PkK(ref pk) => Self::check_pk(pk),
504516
Terminal::Multi(_k, ref pks) => {
505517
if pks.len() > MAX_PUBKEYS_PER_MULTISIG {
506518
return Err(ScriptContextError::CheckMultiSigLimitExceeded);
507519
}
508520
for pk in pks.iter() {
509-
if pk.is_uncompressed() {
510-
return Err(ScriptContextError::CompressedOnly(pk.to_string()));
511-
} else if pk.is_x_only_key() {
512-
return Err(ScriptContextError::XOnlyKeysNotAllowed(
513-
pk.to_string(),
514-
Self::name_str(),
515-
));
516-
}
521+
Self::check_pk(pk)?;
517522
}
518523
Ok(())
519524
}
@@ -594,6 +599,15 @@ impl ScriptContext for Tap {
594599
Ok(())
595600
}
596601

602+
// No uncompressed keys in Tap context
603+
fn check_pk<Pk: MiniscriptKey>(pk: &Pk) -> Result<(), ScriptContextError> {
604+
if pk.is_uncompressed() {
605+
Err(ScriptContextError::UncompressedKeysNotAllowed)
606+
} else {
607+
Ok(())
608+
}
609+
}
610+
597611
fn check_witness<Pk: MiniscriptKey>(witness: &[Vec<u8>]) -> Result<(), ScriptContextError> {
598612
// Note that tapscript has a 1000 limit compared to 100 of segwitv0
599613
if witness.len() > MAX_STACK_SIZE {
@@ -618,9 +632,10 @@ impl ScriptContext for Tap {
618632
}
619633

620634
match ms.node {
621-
Terminal::PkK(ref pk) => {
622-
if pk.is_uncompressed() {
623-
return Err(ScriptContextError::UncompressedKeysNotAllowed);
635+
Terminal::PkK(ref pk) => Self::check_pk(pk),
636+
Terminal::MultiA(_, ref keys) => {
637+
for pk in keys.iter() {
638+
Self::check_pk(pk)?;
624639
}
625640
Ok(())
626641
}
@@ -705,30 +720,32 @@ impl ScriptContext for BareCtx {
705720
Ok(())
706721
}
707722

723+
// No x-only keys in Bare context
724+
fn check_pk<Pk: MiniscriptKey>(pk: &Pk) -> Result<(), ScriptContextError> {
725+
if pk.is_x_only_key() {
726+
Err(ScriptContextError::XOnlyKeysNotAllowed(
727+
pk.to_string(),
728+
Self::name_str(),
729+
))
730+
} else {
731+
Ok(())
732+
}
733+
}
734+
708735
fn check_global_consensus_validity<Pk: MiniscriptKey>(
709736
ms: &Miniscript<Pk, Self>,
710737
) -> Result<(), ScriptContextError> {
711738
if ms.ext.pk_cost > MAX_SCRIPT_SIZE {
712739
return Err(ScriptContextError::MaxWitnessScriptSizeExceeded);
713740
}
714741
match ms.node {
715-
Terminal::PkK(ref key) if key.is_x_only_key() => {
716-
return Err(ScriptContextError::XOnlyKeysNotAllowed(
717-
key.to_string(),
718-
Self::name_str(),
719-
))
720-
}
742+
Terminal::PkK(ref key) => Self::check_pk(key),
721743
Terminal::Multi(_k, ref pks) => {
722744
if pks.len() > MAX_PUBKEYS_PER_MULTISIG {
723745
return Err(ScriptContextError::CheckMultiSigLimitExceeded);
724746
}
725747
for pk in pks.iter() {
726-
if pk.is_x_only_key() {
727-
return Err(ScriptContextError::XOnlyKeysNotAllowed(
728-
pk.to_string(),
729-
Self::name_str(),
730-
));
731-
}
748+
Self::check_pk(pk)?;
732749
}
733750
Ok(())
734751
}
@@ -799,6 +816,11 @@ impl ScriptContext for NoChecks {
799816
Ok(())
800817
}
801818

819+
// No checks in NoChecks
820+
fn check_pk<Pk: MiniscriptKey>(_pk: &Pk) -> Result<(), ScriptContextError> {
821+
Ok(())
822+
}
823+
802824
fn check_global_policy_validity<Pk: MiniscriptKey>(
803825
_ms: &Miniscript<Pk, Self>,
804826
) -> Result<(), ScriptContextError> {

0 commit comments

Comments
 (0)