Skip to content

Commit

Permalink
translator: make target pk and error associated types
Browse files Browse the repository at this point in the history
This is an annoying breaking change for users of the Translator trait
but I think it greatly improves the ergonomics of using the trait.
Rather than having it be parameterized over 3 types, it is now
parameterized over just one (the "source pk type").

This matches how this trait is used in practice -- you typically have a
miniscript/policy/whatever with a keytype Pk, and you want to use a
translator from Pk to "whatever the translator maps to" with "whatever
error the translator yields". So the only type parameter you really need
to type is Pk; the others are irrelevant, and making the user name and
type them is annoying.

Since this eliminates the need to explicitly write out the error types
except when actually implementing the trait, this also changes a ton of
error types from () to Infallible, which is more efficient and correct.
  • Loading branch information
apoelstra committed Aug 30, 2024
1 parent c330d0b commit 36b0659
Show file tree
Hide file tree
Showing 17 changed files with 192 additions and 128 deletions.
30 changes: 18 additions & 12 deletions bitcoind-tests/tests/setup/test_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,11 @@ pub fn parse_insane_ms<Ctx: ScriptContext>(
#[derive(Debug, Clone)]
struct StrDescPubKeyTranslator<'a>(usize, &'a PubData);

impl<'a> Translator<String, DescriptorPublicKey, ()> for StrDescPubKeyTranslator<'a> {
fn pk(&mut self, pk_str: &String) -> Result<DescriptorPublicKey, ()> {
impl<'a> Translator<String> for StrDescPubKeyTranslator<'a> {
type TargetPk = DescriptorPublicKey;
type Error = core::convert::Infallible;

fn pk(&mut self, pk_str: &String) -> Result<Self::TargetPk, Self::Error> {
let avail = !pk_str.ends_with('!');
if avail {
self.0 += 1;
Expand All @@ -181,22 +184,22 @@ impl<'a> Translator<String, DescriptorPublicKey, ()> for StrDescPubKeyTranslator
}
}

fn sha256(&mut self, sha256: &String) -> Result<sha256::Hash, ()> {
fn sha256(&mut self, sha256: &String) -> Result<sha256::Hash, Self::Error> {
let sha = sha256::Hash::from_str(sha256).unwrap();
Ok(sha)
}

fn hash256(&mut self, hash256: &String) -> Result<hash256::Hash, ()> {
fn hash256(&mut self, hash256: &String) -> Result<hash256::Hash, Self::Error> {
let hash256 = hash256::Hash::from_str(hash256).unwrap();
Ok(hash256)
}

fn ripemd160(&mut self, ripemd160: &String) -> Result<ripemd160::Hash, ()> {
fn ripemd160(&mut self, ripemd160: &String) -> Result<ripemd160::Hash, Self::Error> {
let ripemd160 = ripemd160::Hash::from_str(ripemd160).unwrap();
Ok(ripemd160)
}

fn hash160(&mut self, hash160: &String) -> Result<hash160::Hash, ()> {
fn hash160(&mut self, hash160: &String) -> Result<hash160::Hash, Self::Error> {
let hash160 = hash160::Hash::from_str(hash160).unwrap();
Ok(hash160)
}
Expand All @@ -208,8 +211,11 @@ impl<'a> Translator<String, DescriptorPublicKey, ()> for StrDescPubKeyTranslator
#[derive(Debug, Clone)]
struct StrTranslatorLoose<'a>(usize, &'a PubData);

impl<'a> Translator<String, DescriptorPublicKey, ()> for StrTranslatorLoose<'a> {
fn pk(&mut self, pk_str: &String) -> Result<DescriptorPublicKey, ()> {
impl<'a> Translator<String> for StrTranslatorLoose<'a> {
type TargetPk = DescriptorPublicKey;
type Error = core::convert::Infallible;

fn pk(&mut self, pk_str: &String) -> Result<Self::TargetPk, Self::Error> {
let avail = !pk_str.ends_with('!');
if avail {
self.0 += 1;
Expand Down Expand Up @@ -238,22 +244,22 @@ impl<'a> Translator<String, DescriptorPublicKey, ()> for StrTranslatorLoose<'a>
}
}

fn sha256(&mut self, sha256: &String) -> Result<sha256::Hash, ()> {
fn sha256(&mut self, sha256: &String) -> Result<sha256::Hash, Self::Error> {
let sha = sha256::Hash::from_str(sha256).unwrap();
Ok(sha)
}

fn hash256(&mut self, hash256: &String) -> Result<hash256::Hash, ()> {
fn hash256(&mut self, hash256: &String) -> Result<hash256::Hash, Self::Error> {
let hash256 = hash256::Hash::from_str(hash256).unwrap();
Ok(hash256)
}

fn ripemd160(&mut self, ripemd160: &String) -> Result<ripemd160::Hash, ()> {
fn ripemd160(&mut self, ripemd160: &String) -> Result<ripemd160::Hash, Self::Error> {
let ripemd160 = ripemd160::Hash::from_str(ripemd160).unwrap();
Ok(ripemd160)
}

fn hash160(&mut self, hash160: &String) -> Result<hash160::Hash, ()> {
fn hash160(&mut self, hash160: &String) -> Result<hash160::Hash, Self::Error> {
let hash160 = hash160::Hash::from_str(hash160).unwrap();
Ok(hash160)
}
Expand Down
9 changes: 6 additions & 3 deletions examples/big.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,15 @@ struct StrPkTranslator {
pk_map: HashMap<String, XOnlyPublicKey>,
}

impl Translator<String, XOnlyPublicKey, ()> for StrPkTranslator {
fn pk(&mut self, pk: &String) -> Result<XOnlyPublicKey, ()> {
impl Translator<String> for StrPkTranslator {
type TargetPk = XOnlyPublicKey;
type Error = ();

fn pk(&mut self, pk: &String) -> Result<XOnlyPublicKey, Self::Error> {
self.pk_map.get(pk).copied().ok_or(())
}

// We don't need to implement these methods as we are not using them in the policy.
// Fail if we encounter any hash fragments. See also translate_hash_clone! macro.
translate_hash_fail!(String, XOnlyPublicKey, ());
translate_hash_fail!(String, XOnlyPublicKey, Self::Error);
}
9 changes: 6 additions & 3 deletions examples/taproot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,17 @@ struct StrPkTranslator {
pk_map: HashMap<String, XOnlyPublicKey>,
}

impl Translator<String, XOnlyPublicKey, ()> for StrPkTranslator {
fn pk(&mut self, pk: &String) -> Result<XOnlyPublicKey, ()> {
impl Translator<String> for StrPkTranslator {
type TargetPk = XOnlyPublicKey;
type Error = ();

fn pk(&mut self, pk: &String) -> Result<XOnlyPublicKey, Self::Error> {
self.pk_map.get(pk).copied().ok_or(())
}

// We don't need to implement these methods as we are not using them in the policy.
// Fail if we encounter any hash fragments. See also translate_hash_clone! macro.
translate_hash_fail!(String, XOnlyPublicKey, ());
translate_hash_fail!(String, XOnlyPublicKey, Self::Error);
}

fn main() {
Expand Down
10 changes: 4 additions & 6 deletions src/descriptor/bare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,9 @@ impl<Pk: MiniscriptKey> Bare<Pk> {
}

/// Converts the keys in the script from one type to another.
pub fn translate_pk<Q, T, E>(&self, t: &mut T) -> Result<Bare<Q>, TranslateErr<E>>
pub fn translate_pk<T>(&self, t: &mut T) -> Result<Bare<T::TargetPk>, TranslateErr<T::Error>>
where
T: Translator<Pk, Q, E>,
Q: MiniscriptKey,
T: Translator<Pk>,
{
Bare::new(self.ms.translate_pk(t)?).map_err(TranslateErr::OuterError)
}
Expand Down Expand Up @@ -256,10 +255,9 @@ impl<Pk: MiniscriptKey> Pkh<Pk> {
pub fn max_satisfaction_weight(&self) -> usize { 4 * (1 + 73 + BareCtx::pk_len(&self.pk)) }

/// Converts the keys in a script from one type to another.
pub fn translate_pk<Q, T, E>(&self, t: &mut T) -> Result<Pkh<Q>, TranslateErr<E>>
pub fn translate_pk<T>(&self, t: &mut T) -> Result<Pkh<T::TargetPk>, TranslateErr<T::Error>>
where
T: Translator<Pk, Q, E>,
Q: MiniscriptKey,
T: Translator<Pk>,
{
let res = Pkh::new(t.pk(&self.pk)?);
match res {
Expand Down
53 changes: 34 additions & 19 deletions src/descriptor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,10 +361,12 @@ impl<Pk: MiniscriptKey> Descriptor<Pk> {
}

/// Converts a descriptor using one kind of keys to another kind of key.
pub fn translate_pk<T, Q, E>(&self, t: &mut T) -> Result<Descriptor<Q>, TranslateErr<E>>
pub fn translate_pk<T>(
&self,
t: &mut T,
) -> Result<Descriptor<T::TargetPk>, TranslateErr<T::Error>>
where
T: Translator<Pk, Q, E>,
Q: MiniscriptKey,
T: Translator<Pk>,
{
let desc = match *self {
Descriptor::Bare(ref bare) => Descriptor::Bare(bare.translate_pk(t)?),
Expand Down Expand Up @@ -600,7 +602,10 @@ impl Descriptor<DescriptorPublicKey> {
) -> Result<Descriptor<DefiniteDescriptorKey>, ConversionError> {
struct Derivator(u32);

impl Translator<DescriptorPublicKey, DefiniteDescriptorKey, ConversionError> for Derivator {
impl Translator<DescriptorPublicKey> for Derivator {
type TargetPk = DefiniteDescriptorKey;
type Error = ConversionError;

fn pk(
&mut self,
pk: &DescriptorPublicKey,
Expand Down Expand Up @@ -691,9 +696,10 @@ impl Descriptor<DescriptorPublicKey> {

struct KeyMapWrapper<'a, C: secp256k1::Signing>(KeyMap, &'a secp256k1::Secp256k1<C>);

impl<'a, C: secp256k1::Signing> Translator<String, DescriptorPublicKey, Error>
for KeyMapWrapper<'a, C>
{
impl<'a, C: secp256k1::Signing> Translator<String> for KeyMapWrapper<'a, C> {
type TargetPk = DescriptorPublicKey;
type Error = Error;

fn pk(&mut self, pk: &String) -> Result<DescriptorPublicKey, Error> {
parse_key(pk, &mut self.0, self.1)
}
Expand Down Expand Up @@ -738,29 +744,35 @@ impl Descriptor<DescriptorPublicKey> {
pub fn to_string_with_secret(&self, key_map: &KeyMap) -> String {
struct KeyMapLookUp<'a>(&'a KeyMap);

impl<'a> Translator<DescriptorPublicKey, String, ()> for KeyMapLookUp<'a> {
fn pk(&mut self, pk: &DescriptorPublicKey) -> Result<String, ()> {
impl<'a> Translator<DescriptorPublicKey> for KeyMapLookUp<'a> {
type TargetPk = String;
type Error = core::convert::Infallible;

fn pk(&mut self, pk: &DescriptorPublicKey) -> Result<String, Self::Error> {
key_to_string(pk, self.0)
}

fn sha256(&mut self, sha256: &sha256::Hash) -> Result<String, ()> {
fn sha256(&mut self, sha256: &sha256::Hash) -> Result<String, Self::Error> {
Ok(sha256.to_string())
}

fn hash256(&mut self, hash256: &hash256::Hash) -> Result<String, ()> {
fn hash256(&mut self, hash256: &hash256::Hash) -> Result<String, Self::Error> {
Ok(hash256.to_string())
}

fn ripemd160(&mut self, ripemd160: &ripemd160::Hash) -> Result<String, ()> {
fn ripemd160(&mut self, ripemd160: &ripemd160::Hash) -> Result<String, Self::Error> {
Ok(ripemd160.to_string())
}

fn hash160(&mut self, hash160: &hash160::Hash) -> Result<String, ()> {
fn hash160(&mut self, hash160: &hash160::Hash) -> Result<String, Self::Error> {
Ok(hash160.to_string())
}
}

fn key_to_string(pk: &DescriptorPublicKey, key_map: &KeyMap) -> Result<String, ()> {
fn key_to_string(
pk: &DescriptorPublicKey,
key_map: &KeyMap,
) -> Result<String, core::convert::Infallible> {
Ok(match key_map.get(pk) {
Some(secret) => secret.to_string(),
None => pk.to_string(),
Expand Down Expand Up @@ -835,7 +847,10 @@ impl Descriptor<DescriptorPublicKey> {

// Now, transform the multipath key of each descriptor into a single-key using each index.
struct IndexChoser(usize);
impl Translator<DescriptorPublicKey, DescriptorPublicKey, Error> for IndexChoser {
impl Translator<DescriptorPublicKey> for IndexChoser {
type TargetPk = DescriptorPublicKey;
type Error = Error;

fn pk(&mut self, pk: &DescriptorPublicKey) -> Result<DescriptorPublicKey, Error> {
match pk {
DescriptorPublicKey::Single(..) | DescriptorPublicKey::XPub(..) => {
Expand Down Expand Up @@ -892,10 +907,10 @@ impl Descriptor<DefiniteDescriptorKey> {
) -> Result<Descriptor<bitcoin::PublicKey>, ConversionError> {
struct Derivator<'a, C: secp256k1::Verification>(&'a secp256k1::Secp256k1<C>);

impl<'a, C: secp256k1::Verification>
Translator<DefiniteDescriptorKey, bitcoin::PublicKey, ConversionError>
for Derivator<'a, C>
{
impl<'a, C: secp256k1::Verification> Translator<DefiniteDescriptorKey> for Derivator<'a, C> {
type TargetPk = bitcoin::PublicKey;
type Error = ConversionError;

fn pk(
&mut self,
pk: &DefiniteDescriptorKey,
Expand Down
10 changes: 4 additions & 6 deletions src/descriptor/segwitv0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,9 @@ impl<Pk: MiniscriptKey> Wsh<Pk> {
}

/// Converts the keys in a script from one type to another.
pub fn translate_pk<Q, T, E>(&self, t: &mut T) -> Result<Wsh<Q>, TranslateErr<E>>
pub fn translate_pk<T>(&self, t: &mut T) -> Result<Wsh<T::TargetPk>, TranslateErr<T::Error>>
where
T: Translator<Pk, Q, E>,
Q: MiniscriptKey,
T: Translator<Pk>,
{
let inner = match self.inner {
WshInner::SortedMulti(ref smv) => WshInner::SortedMulti(smv.translate_pk(t)?),
Expand Down Expand Up @@ -366,10 +365,9 @@ impl<Pk: MiniscriptKey> Wpkh<Pk> {
pub fn max_satisfaction_weight(&self) -> usize { 4 + 1 + 73 + Segwitv0::pk_len(&self.pk) }

/// Converts the keys in a script from one type to another.
pub fn translate_pk<Q, T, E>(&self, t: &mut T) -> Result<Wpkh<Q>, TranslateErr<E>>
pub fn translate_pk<T>(&self, t: &mut T) -> Result<Wpkh<T::TargetPk>, TranslateErr<T::Error>>
where
T: Translator<Pk, Q, E>,
Q: MiniscriptKey,
T: Translator<Pk>,
{
let res = Wpkh::new(t.pk(&self.pk)?);
match res {
Expand Down
5 changes: 2 additions & 3 deletions src/descriptor/sh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,10 +261,9 @@ impl<Pk: MiniscriptKey> Sh<Pk> {
}

/// Converts the keys in a script from one type to another.
pub fn translate_pk<Q, T, E>(&self, t: &mut T) -> Result<Sh<Q>, TranslateErr<E>>
pub fn translate_pk<T>(&self, t: &mut T) -> Result<Sh<T::TargetPk>, TranslateErr<T::Error>>
where
T: Translator<Pk, Q, E>,
Q: MiniscriptKey,
T: Translator<Pk>,
{
let inner = match self.inner {
ShInner::Wsh(ref wsh) => ShInner::Wsh(wsh.translate_pk(t)?),
Expand Down
7 changes: 3 additions & 4 deletions src/descriptor/sortedmulti.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,12 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
/// This will panic if fpk returns an uncompressed key when
/// converting to a Segwit descriptor. To prevent this panic, ensure
/// fpk returns an error in this case instead.
pub fn translate_pk<T, Q, FuncError>(
pub fn translate_pk<T>(
&self,
t: &mut T,
) -> Result<SortedMultiVec<Q, Ctx>, TranslateErr<FuncError>>
) -> Result<SortedMultiVec<T::TargetPk, Ctx>, TranslateErr<T::Error>>
where
T: Translator<Pk, Q, FuncError>,
Q: MiniscriptKey,
T: Translator<Pk>,
{
let ret = SortedMultiVec {
inner: self.inner.translate_ref(|pk| t.pk(pk))?,
Expand Down
17 changes: 9 additions & 8 deletions src/descriptor/tr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,9 @@ impl<Pk: MiniscriptKey> TapTree<Pk> {
pub fn iter(&self) -> TapTreeIter<Pk> { TapTreeIter { stack: vec![(0, self)] } }

// Helper function to translate keys
fn translate_helper<T, Q, E>(&self, t: &mut T) -> Result<TapTree<Q>, TranslateErr<E>>
fn translate_helper<T>(&self, t: &mut T) -> Result<TapTree<T::TargetPk>, TranslateErr<T::Error>>
where
T: Translator<Pk, Q, E>,
Q: MiniscriptKey,
T: Translator<Pk>,
{
let frag = match *self {
TapTree::Tree { ref left, ref right, ref height } => TapTree::Tree {
Expand Down Expand Up @@ -353,17 +352,19 @@ impl<Pk: MiniscriptKey> Tr<Pk> {
}

/// Converts keys from one type of public key to another.
pub fn translate_pk<Q, T, E>(&self, translate: &mut T) -> Result<Tr<Q>, TranslateErr<E>>
pub fn translate_pk<T>(
&self,
translate: &mut T,
) -> Result<Tr<T::TargetPk>, TranslateErr<T::Error>>
where
T: Translator<Pk, Q, E>,
Q: MiniscriptKey,
T: Translator<Pk>,
{
let tree = match &self.tree {
Some(tree) => Some(tree.translate_helper(translate)?),
None => None,
};
let translate_desc = Tr::new(translate.pk(&self.internal_key)?, tree)
.map_err(|e| TranslateErr::OuterError(e))?;
let translate_desc =
Tr::new(translate.pk(&self.internal_key)?, tree).map_err(TranslateErr::OuterError)?;
Ok(translate_desc)
}
}
Expand Down
19 changes: 12 additions & 7 deletions src/interpreter/inner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,12 +356,15 @@ impl<Ctx: ScriptContext> ToNoChecks for Miniscript<bitcoin::PublicKey, Ctx> {
fn to_no_checks_ms(&self) -> Miniscript<BitcoinKey, NoChecks> {
struct TranslateFullPk;

impl Translator<bitcoin::PublicKey, BitcoinKey, ()> for TranslateFullPk {
fn pk(&mut self, pk: &bitcoin::PublicKey) -> Result<BitcoinKey, ()> {
impl Translator<bitcoin::PublicKey> for TranslateFullPk {
type TargetPk = BitcoinKey;
type Error = core::convert::Infallible;

fn pk(&mut self, pk: &bitcoin::PublicKey) -> Result<BitcoinKey, Self::Error> {
Ok(BitcoinKey::Fullkey(*pk))
}

translate_hash_clone!(bitcoin::PublicKey, BitcoinKey, ());
translate_hash_clone!(bitcoin::PublicKey, BitcoinKey, Self::Error);
}

self.translate_pk_ctx(&mut TranslateFullPk)
Expand All @@ -371,15 +374,17 @@ impl<Ctx: ScriptContext> ToNoChecks for Miniscript<bitcoin::PublicKey, Ctx> {

impl<Ctx: ScriptContext> ToNoChecks for Miniscript<bitcoin::key::XOnlyPublicKey, Ctx> {
fn to_no_checks_ms(&self) -> Miniscript<BitcoinKey, NoChecks> {
// specify the () error type as this cannot error
struct TranslateXOnlyPk;

impl Translator<bitcoin::key::XOnlyPublicKey, BitcoinKey, ()> for TranslateXOnlyPk {
fn pk(&mut self, pk: &bitcoin::key::XOnlyPublicKey) -> Result<BitcoinKey, ()> {
impl Translator<bitcoin::key::XOnlyPublicKey> for TranslateXOnlyPk {
type TargetPk = BitcoinKey;
type Error = core::convert::Infallible;

fn pk(&mut self, pk: &bitcoin::key::XOnlyPublicKey) -> Result<BitcoinKey, Self::Error> {
Ok(BitcoinKey::XOnlyPublicKey(*pk))
}

translate_hash_clone!(bitcoin::key::XOnlyPublicKey, BitcoinKey, ());
translate_hash_clone!(bitcoin::key::XOnlyPublicKey, BitcoinKey, Self::Error);
}
self.translate_pk_ctx(&mut TranslateXOnlyPk)
.expect("Translation should succeed")
Expand Down
Loading

0 comments on commit 36b0659

Please sign in to comment.