Skip to content

Commit be07a9c

Browse files
committed
Update TranslatePk again
This fixes of the long last standing bug in rust-miniscript that allowed creating unsound miniscript using the translate APIs
1 parent ee8de9a commit be07a9c

File tree

13 files changed

+177
-98
lines changed

13 files changed

+177
-98
lines changed

bitcoind-tests/tests/setup/test_util.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,8 +279,10 @@ pub fn parse_test_desc(
279279
let desc = subs_hash_frag(desc, pubdata);
280280
let desc = Descriptor::<String>::from_str(&desc)?;
281281
let mut translator = StrDescPubKeyTranslator(0, pubdata);
282-
let desc: Result<_, ()> = desc.translate_pk(&mut translator);
283-
Ok(desc.expect("Translate must succeed"))
282+
let desc = desc
283+
.translate_pk(&mut translator)
284+
.expect("Translation failed");
285+
Ok(desc)
284286
}
285287

286288
// substitute hash fragments in the string as the per rules

src/descriptor/bare.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ use crate::policy::{semantic, Liftable};
2020
use crate::prelude::*;
2121
use crate::util::{varint_len, witness_to_scriptsig};
2222
use crate::{
23-
BareCtx, Error, ForEachKey, Miniscript, MiniscriptKey, Satisfier, ToPublicKey, TranslatePk,
24-
Translator,
23+
BareCtx, Error, ForEachKey, Miniscript, MiniscriptKey, Satisfier, ToPublicKey, TranslateErr,
24+
TranslatePk, Translator,
2525
};
2626

2727
/// Create a Bare Descriptor. That is descriptor that is
@@ -188,11 +188,11 @@ where
188188
{
189189
type Output = Bare<Q>;
190190

191-
fn translate_pk<T, E>(&self, t: &mut T) -> Result<Self::Output, E>
191+
fn translate_pk<T, E>(&self, t: &mut T) -> Result<Bare<Q>, TranslateErr<E>>
192192
where
193193
T: Translator<P, Q, E>,
194194
{
195-
Ok(Bare::new(self.ms.translate_pk(t)?).expect("Translation cannot fail inside Bare"))
195+
Ok(Bare::new(self.ms.translate_pk(t)?).map_err(TranslateErr::OuterError)?)
196196
}
197197
}
198198

@@ -373,11 +373,14 @@ where
373373
{
374374
type Output = Pkh<Q>;
375375

376-
fn translate_pk<T, E>(&self, t: &mut T) -> Result<Self::Output, E>
376+
fn translate_pk<T, E>(&self, t: &mut T) -> Result<Self::Output, TranslateErr<E>>
377377
where
378378
T: Translator<P, Q, E>,
379379
{
380380
let res = Pkh::new(t.pk(&self.pk)?);
381-
Ok(res.expect("Expect will be fixed in next commit"))
381+
match res {
382+
Ok(pk) => Ok(pk),
383+
Err(e) => Err(TranslateErr::OuterError(Error::from(e))),
384+
}
382385
}
383386
}

src/descriptor/mod.rs

Lines changed: 25 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use crate::miniscript::{Legacy, Miniscript, Segwitv0};
2626
use crate::prelude::*;
2727
use crate::{
2828
expression, hash256, miniscript, BareCtx, Error, ForEachKey, MiniscriptKey, Satisfier,
29-
ToPublicKey, TranslatePk, Translator,
29+
ToPublicKey, TranslateErr, TranslatePk, Translator,
3030
};
3131

3232
mod bare;
@@ -519,7 +519,7 @@ where
519519
type Output = Descriptor<Q>;
520520

521521
/// Converts a descriptor using abstract keys to one using specific keys.
522-
fn translate_pk<T, E>(&self, t: &mut T) -> Result<Self::Output, E>
522+
fn translate_pk<T, E>(&self, t: &mut T) -> Result<Self::Output, TranslateErr<E>>
523523
where
524524
T: Translator<P, Q, E>,
525525
{
@@ -581,7 +581,10 @@ impl Descriptor<DescriptorPublicKey> {
581581

582582
translate_hash_clone!(DescriptorPublicKey, DescriptorPublicKey, ConversionError);
583583
}
584-
self.translate_pk(&mut Derivator(index))
584+
self.translate_pk(&mut Derivator(index)).map_err(|e| {
585+
e.try_into_translator_err()
586+
.expect("No Context errors while translating")
587+
})
585588
}
586589

587590
#[deprecated(note = "use at_derivation_index instead")]
@@ -694,9 +697,13 @@ impl Descriptor<DescriptorPublicKey> {
694697
}
695698

696699
let descriptor = Descriptor::<String>::from_str(s)?;
697-
let descriptor = descriptor
698-
.translate_pk(&mut keymap_pk)
699-
.map_err(|e| Error::Unexpected(e.to_string()))?;
700+
let descriptor = descriptor.translate_pk(&mut keymap_pk).map_err(|e| {
701+
Error::Unexpected(
702+
e.try_into_translator_err()
703+
.expect("No Outer context errors")
704+
.to_string(),
705+
)
706+
})?;
700707

701708
Ok((descriptor, keymap_pk.0))
702709
}
@@ -823,49 +830,16 @@ impl Descriptor<DescriptorPublicKey> {
823830

824831
for (i, desc) in descriptors.iter_mut().enumerate() {
825832
let mut index_choser = IndexChoser(i);
826-
*desc = desc.translate_pk(&mut index_choser)?;
833+
*desc = desc.translate_pk(&mut index_choser).map_err(|e| {
834+
e.try_into_translator_err()
835+
.expect("No Context errors possible")
836+
})?;
827837
}
828838

829839
Ok(descriptors)
830840
}
831841
}
832842

833-
impl<Pk: MiniscriptKey> Descriptor<Pk> {
834-
/// Whether this descriptor is a multipath descriptor that contains any 2 multipath keys
835-
/// with a different number of derivation paths.
836-
/// Such a descriptor is invalid according to BIP389.
837-
pub fn multipath_length_mismatch(&self) -> bool {
838-
// (Ab)use `for_each_key` to record the number of derivation paths a multipath key has.
839-
#[derive(PartialEq)]
840-
enum MultipathLenChecker {
841-
SinglePath,
842-
MultipathLen(usize),
843-
LenMismatch,
844-
}
845-
846-
let mut checker = MultipathLenChecker::SinglePath;
847-
self.for_each_key(|key| {
848-
match key.num_der_paths() {
849-
0 | 1 => {}
850-
n => match checker {
851-
MultipathLenChecker::SinglePath => {
852-
checker = MultipathLenChecker::MultipathLen(n);
853-
}
854-
MultipathLenChecker::MultipathLen(len) => {
855-
if len != n {
856-
checker = MultipathLenChecker::LenMismatch;
857-
}
858-
}
859-
MultipathLenChecker::LenMismatch => {}
860-
},
861-
}
862-
true
863-
});
864-
865-
checker == MultipathLenChecker::LenMismatch
866-
}
867-
}
868-
869843
impl Descriptor<DefiniteDescriptorKey> {
870844
/// Convert all the public keys in the descriptor to [`bitcoin::PublicKey`] by deriving them or
871845
/// otherwise converting them. All [`bitcoin::secp256k1::XOnlyPublicKey`]s are converted to by adding a
@@ -909,8 +883,14 @@ impl Descriptor<DefiniteDescriptorKey> {
909883
translate_hash_clone!(DefiniteDescriptorKey, bitcoin::PublicKey, ConversionError);
910884
}
911885

912-
let derived = self.translate_pk(&mut Derivator(secp))?;
913-
Ok(derived)
886+
let derived = self.translate_pk(&mut Derivator(secp));
887+
match derived {
888+
Ok(derived) => Ok(derived),
889+
Err(e) => match e.try_into_translator_err() {
890+
Ok(e) => Err(e),
891+
Err(_) => unreachable!("No Context errors when deriving keys"),
892+
},
893+
}
914894
}
915895
}
916896

@@ -944,10 +924,6 @@ impl_from_str!(
944924
expression::FromTree::from_tree(&top)
945925
}?;
946926

947-
if desc.multipath_length_mismatch() {
948-
return Err(Error::MultipathDescLenMismatch);
949-
}
950-
951927
Ok(desc)
952928
}
953929
);
@@ -1992,7 +1968,6 @@ pk(03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8))";
19921968
// We can parse a multipath descriptors, and make it into separate single-path descriptors.
19931969
let desc = Descriptor::from_str("wsh(andor(pk(tpubDEN9WSToTyy9ZQfaYqSKfmVqmq1VVLNtYfj3Vkqh67et57eJ5sTKZQBkHqSwPUsoSskJeaYnPttHe2VrkCsKA27kUaN9SDc5zhqeLzKa1rr/0'/<7';8h;20>/*),older(10000),pk(tpubD8LYfn6njiA2inCoxwM7EuN3cuLVcaHAwLYeups13dpevd3nHLRdK9NdQksWXrhLQVxcUZRpnp5CkJ1FhE61WRAsHxDNAkvGkoQkAeWDYjV/8/4567/<0;1;987>/*)))").unwrap();
19941970
assert!(desc.is_multipath());
1995-
assert!(!desc.multipath_length_mismatch());
19961971
assert_eq!(desc.into_single_descriptors().unwrap(), vec![
19971972
Descriptor::from_str("wsh(andor(pk(tpubDEN9WSToTyy9ZQfaYqSKfmVqmq1VVLNtYfj3Vkqh67et57eJ5sTKZQBkHqSwPUsoSskJeaYnPttHe2VrkCsKA27kUaN9SDc5zhqeLzKa1rr/0'/7'/*),older(10000),pk(tpubD8LYfn6njiA2inCoxwM7EuN3cuLVcaHAwLYeups13dpevd3nHLRdK9NdQksWXrhLQVxcUZRpnp5CkJ1FhE61WRAsHxDNAkvGkoQkAeWDYjV/8/4567/0/*)))").unwrap(),
19981973
Descriptor::from_str("wsh(andor(pk(tpubDEN9WSToTyy9ZQfaYqSKfmVqmq1VVLNtYfj3Vkqh67et57eJ5sTKZQBkHqSwPUsoSskJeaYnPttHe2VrkCsKA27kUaN9SDc5zhqeLzKa1rr/0'/8h/*),older(10000),pk(tpubD8LYfn6njiA2inCoxwM7EuN3cuLVcaHAwLYeups13dpevd3nHLRdK9NdQksWXrhLQVxcUZRpnp5CkJ1FhE61WRAsHxDNAkvGkoQkAeWDYjV/8/4567/1/*)))").unwrap(),
@@ -2002,7 +1977,6 @@ pk(03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8))";
20021977
// Even if only one of the keys is multipath.
20031978
let desc = Descriptor::from_str("wsh(andor(pk(tpubDEN9WSToTyy9ZQfaYqSKfmVqmq1VVLNtYfj3Vkqh67et57eJ5sTKZQBkHqSwPUsoSskJeaYnPttHe2VrkCsKA27kUaN9SDc5zhqeLzKa1rr/0'/<0;1>/*),older(10000),pk(tpubD8LYfn6njiA2inCoxwM7EuN3cuLVcaHAwLYeups13dpevd3nHLRdK9NdQksWXrhLQVxcUZRpnp5CkJ1FhE61WRAsHxDNAkvGkoQkAeWDYjV/8/4567/*)))").unwrap();
20041979
assert!(desc.is_multipath());
2005-
assert!(!desc.multipath_length_mismatch());
20061980
assert_eq!(desc.into_single_descriptors().unwrap(), vec![
20071981
Descriptor::from_str("wsh(andor(pk(tpubDEN9WSToTyy9ZQfaYqSKfmVqmq1VVLNtYfj3Vkqh67et57eJ5sTKZQBkHqSwPUsoSskJeaYnPttHe2VrkCsKA27kUaN9SDc5zhqeLzKa1rr/0'/0/*),older(10000),pk(tpubD8LYfn6njiA2inCoxwM7EuN3cuLVcaHAwLYeups13dpevd3nHLRdK9NdQksWXrhLQVxcUZRpnp5CkJ1FhE61WRAsHxDNAkvGkoQkAeWDYjV/8/4567/*)))").unwrap(),
20081982
Descriptor::from_str("wsh(andor(pk(tpubDEN9WSToTyy9ZQfaYqSKfmVqmq1VVLNtYfj3Vkqh67et57eJ5sTKZQBkHqSwPUsoSskJeaYnPttHe2VrkCsKA27kUaN9SDc5zhqeLzKa1rr/0'/1/*),older(10000),pk(tpubD8LYfn6njiA2inCoxwM7EuN3cuLVcaHAwLYeups13dpevd3nHLRdK9NdQksWXrhLQVxcUZRpnp5CkJ1FhE61WRAsHxDNAkvGkoQkAeWDYjV/8/4567/*)))").unwrap(),
@@ -2011,7 +1985,6 @@ pk(03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8))";
20111985
// We can detect regular single-path descriptors.
20121986
let notmulti_desc = Descriptor::from_str("wsh(andor(pk(tpubDEN9WSToTyy9ZQfaYqSKfmVqmq1VVLNtYfj3Vkqh67et57eJ5sTKZQBkHqSwPUsoSskJeaYnPttHe2VrkCsKA27kUaN9SDc5zhqeLzKa1rr/0'/*),older(10000),pk(tpubD8LYfn6njiA2inCoxwM7EuN3cuLVcaHAwLYeups13dpevd3nHLRdK9NdQksWXrhLQVxcUZRpnp5CkJ1FhE61WRAsHxDNAkvGkoQkAeWDYjV/8/4567/*)))").unwrap();
20131987
assert!(!notmulti_desc.is_multipath());
2014-
assert!(!notmulti_desc.multipath_length_mismatch());
20151988
assert_eq!(
20161989
notmulti_desc.clone().into_single_descriptors().unwrap(),
20171990
vec![notmulti_desc]

src/descriptor/segwitv0.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ use crate::policy::{semantic, Liftable};
1818
use crate::prelude::*;
1919
use crate::util::varint_len;
2020
use crate::{
21-
Error, ForEachKey, Miniscript, MiniscriptKey, Satisfier, Segwitv0, ToPublicKey, TranslatePk,
22-
Translator,
21+
Error, ForEachKey, Miniscript, MiniscriptKey, Satisfier, Segwitv0, ToPublicKey, TranslateErr,
22+
TranslatePk, Translator,
2323
};
2424
/// A Segwitv0 wsh descriptor
2525
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
@@ -282,7 +282,7 @@ where
282282
{
283283
type Output = Wsh<Q>;
284284

285-
fn translate_pk<T, E>(&self, t: &mut T) -> Result<Self::Output, E>
285+
fn translate_pk<T, E>(&self, t: &mut T) -> Result<Self::Output, TranslateErr<E>>
286286
where
287287
T: Translator<P, Q, E>,
288288
{
@@ -480,10 +480,14 @@ where
480480
{
481481
type Output = Wpkh<Q>;
482482

483-
fn translate_pk<T, E>(&self, t: &mut T) -> Result<Self::Output, E>
483+
fn translate_pk<T, E>(&self, t: &mut T) -> Result<Self::Output, TranslateErr<E>>
484484
where
485485
T: Translator<P, Q, E>,
486486
{
487-
Ok(Wpkh::new(t.pk(&self.pk)?).expect("Uncompressed keys in Wpkh"))
487+
let res = Wpkh::new(t.pk(&self.pk)?);
488+
match res {
489+
Ok(pk) => Ok(pk),
490+
Err(e) => Err(TranslateErr::OuterError(Error::from(e))),
491+
}
488492
}
489493
}

src/descriptor/sh.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use crate::prelude::*;
2323
use crate::util::{varint_len, witness_to_scriptsig};
2424
use crate::{
2525
push_opcode_size, Error, ForEachKey, Legacy, Miniscript, MiniscriptKey, Satisfier, Segwitv0,
26-
ToPublicKey, TranslatePk, Translator,
26+
ToPublicKey, TranslateErr, TranslatePk, Translator,
2727
};
2828

2929
/// A Legacy p2sh Descriptor
@@ -437,7 +437,7 @@ where
437437
{
438438
type Output = Sh<Q>;
439439

440-
fn translate_pk<T, E>(&self, t: &mut T) -> Result<Self::Output, E>
440+
fn translate_pk<T, E>(&self, t: &mut T) -> Result<Self::Output, TranslateErr<E>>
441441
where
442442
T: Translator<P, Q, E>,
443443
{

src/descriptor/sortedmulti.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG;
1818
use crate::prelude::*;
1919
use crate::{
2020
errstr, expression, miniscript, policy, script_num_size, Error, ForEachKey, Miniscript,
21-
MiniscriptKey, Satisfier, ToPublicKey, Translator,
21+
MiniscriptKey, Satisfier, ToPublicKey, TranslateErr, Translator,
2222
};
2323

2424
/// Contents of a "sortedmulti" descriptor
@@ -87,17 +87,14 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
8787
pub fn translate_pk<T, Q, FuncError>(
8888
&self,
8989
t: &mut T,
90-
) -> Result<SortedMultiVec<Q, Ctx>, FuncError>
90+
) -> Result<SortedMultiVec<Q, Ctx>, TranslateErr<FuncError>>
9191
where
9292
T: Translator<Pk, Q, FuncError>,
9393
Q: MiniscriptKey,
9494
{
9595
let pks: Result<Vec<Q>, _> = self.pks.iter().map(|pk| t.pk(pk)).collect();
96-
Ok(SortedMultiVec {
97-
k: self.k,
98-
pks: pks?,
99-
phantom: PhantomData,
100-
})
96+
let res = SortedMultiVec::new(self.k, pks?).map_err(TranslateErr::OuterError)?;
97+
Ok(res)
10198
}
10299
}
103100

src/descriptor/tr.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::prelude::*;
2020
use crate::util::{varint_len, witness_size};
2121
use crate::{
2222
errstr, Error, ForEachKey, MiniscriptKey, Satisfier, ScriptContext, Tap, ToPublicKey,
23-
TranslatePk, Translator,
23+
TranslateErr, TranslatePk, Translator,
2424
};
2525

2626
/// A Taproot Tree representation.
@@ -129,9 +129,9 @@ impl<Pk: MiniscriptKey> TapTree<Pk> {
129129
}
130130

131131
// Helper function to translate keys
132-
fn translate_helper<T, Q, Error>(&self, t: &mut T) -> Result<TapTree<Q>, Error>
132+
fn translate_helper<T, Q, E>(&self, t: &mut T) -> Result<TapTree<Q>, TranslateErr<E>>
133133
where
134-
T: Translator<Pk, Q, Error>,
134+
T: Translator<Pk, Q, E>,
135135
Q: MiniscriptKey,
136136
{
137137
let frag = match self {
@@ -629,7 +629,7 @@ where
629629
{
630630
type Output = Tr<Q>;
631631

632-
fn translate_pk<T, E>(&self, translate: &mut T) -> Result<Self::Output, E>
632+
fn translate_pk<T, E>(&self, translate: &mut T) -> Result<Self::Output, TranslateErr<E>>
633633
where
634634
T: Translator<P, Q, E>,
635635
{
@@ -638,7 +638,7 @@ where
638638
None => None,
639639
};
640640
let translate_desc = Tr::new(translate.pk(&self.internal_key)?, tree)
641-
.expect("This will be removed in future");
641+
.map_err(|e| TranslateErr::OuterError(e))?;
642642
Ok(translate_desc)
643643
}
644644
}

src/lib.rs

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,57 @@ where
368368
fn hash160(&mut self, hash160: &P::Hash160) -> Result<Q::Hash160, E>;
369369
}
370370

371+
/// An enum for representing translation errors
372+
pub enum TranslateErr<E> {
373+
/// Error inside in the underlying key translation
374+
TranslatorErr(E),
375+
/// Error in the final translated structure. In some cases, the translated
376+
/// structure might not be valid under the given context. For example, translating
377+
/// from string keys to x-only keys in wsh descriptors.
378+
OuterError(Error),
379+
}
380+
381+
impl<E> TranslateErr<E> {
382+
/// Enum used to capture errors from the [`Translator`] trait as well as
383+
/// context errors from the translated structure.
384+
/// The errors occurred in translation are captured in the [`TranslateErr::TranslatorErr`]
385+
/// while the errors in the translated structure are captured in the [`TranslateErr::OuterError`]
386+
///
387+
/// As of taproot upgrade: The following rules apply to the translation of descriptors:
388+
/// - Legacy/Bare does not allow x_only keys
389+
/// - SegwitV0 does not allow uncompressed keys and x_only keys
390+
/// - Tapscript does not allow uncompressed keys
391+
/// - Translating into multi-path descriptors should have same number of path
392+
/// for all the keys in the descriptor
393+
///
394+
/// # Errors
395+
///
396+
/// This function will return an error if the Error is OutError.
397+
pub fn try_into_translator_err(self) -> Result<E, Self> {
398+
if let Self::TranslatorErr(v) = self {
399+
Ok(v)
400+
} else {
401+
Err(self)
402+
}
403+
}
404+
}
405+
406+
impl<E> From<E> for TranslateErr<E> {
407+
fn from(v: E) -> Self {
408+
Self::TranslatorErr(v)
409+
}
410+
}
411+
412+
// Required for unwrap
413+
impl<E: fmt::Debug> fmt::Debug for TranslateErr<E> {
414+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
415+
match self {
416+
Self::TranslatorErr(e) => write!(f, "TranslatorErr({:?})", e),
417+
Self::OuterError(e) => write!(f, "OuterError({:?})", e),
418+
}
419+
}
420+
}
421+
371422
/// Converts a descriptor using abstract keys to one using specific keys. Uses translator `t` to do
372423
/// the actual translation function calls.
373424
pub trait TranslatePk<P, Q>
@@ -380,7 +431,7 @@ where
380431

381432
/// Translates a struct from one generic to another where the translations
382433
/// for Pk are provided by the given [`Translator`].
383-
fn translate_pk<T, E>(&self, translator: &mut T) -> Result<Self::Output, E>
434+
fn translate_pk<T, E>(&self, translator: &mut T) -> Result<Self::Output, TranslateErr<E>>
384435
where
385436
T: Translator<P, Q, E>;
386437
}

0 commit comments

Comments
 (0)