rust-bitcoin / rust-miniscript

Support for Miniscript and Output Descriptors for rust-bitcoin
Creative Commons Zero v1.0 Universal
344 stars 135 forks source link

Helpers for constructing standard HD wallet descriptors #513

Open casey opened 1 year ago

casey commented 1 year ago

Constructing standard HD wallet descriptors takes a fair bit of code. For example, to construct a BIP-86 single-key taproot HD wallet descriptor:

let master_xkey = ExtendedPrivKey::new_master(network, &seed)?;

let fingerprint = master_xkey.fingerprint(&secp);

let derivation_path = DerivationPath::master()
  .child(ChildNumber::Hardened { index: 86 })
  .child(ChildNumber::Hardened { index: 0 })
  .child(ChildNumber::Hardened { index: 0 });

let derived_xkey = master_xkey.derive_priv(&secp, &derivation_path)?;

let secret_key = DescriptorSecretKey::XPrv(DescriptorXKey {
  origin: Some((fingerprint, derivation_path.clone())),
  xkey: derived_xkey,
  derivation_path: DerivationPath::master().child(ChildNumber::Normal { index: 0 }),
  wildcard: Wildcard::Unhardened,
});

let public_key = secret_key.to_public(&secp)?;

let mut key_map = std::collections::HashMap::new();
key_map.insert(public_key.clone(), secret_key);

let receive_desc = Descriptor::new_tr(public_key, None)?;

It could be nice to provide helpers that would create standard HD wallet descriptors from seeds, in this case something like:

impl Descriptor {
  fn new_bip_86(secp: &Secp256k1, network: Network, seed: [u8; 32], change: bool) -> Result<(Self, DescriptorSecretKey)> {
    let master_xkey = ExtendedPrivKey::new_master(network, &seed)?;

    let fingerprint = master_xkey.fingerprint(&secp);

    let derivation_path = DerivationPath::master()
      .child(ChildNumber::Hardened { index: 86 })
      .child(ChildNumber::Hardened { index: 0 })
      .child(ChildNumber::Hardened { index: 0 });

    let derived_xkey = master_xkey.derive_priv(&secp, &derivation_path)?;

    let secret_key = DescriptorSecretKey::XPrv(DescriptorXKey {
      origin: Some((fingerprint, derivation_path.clone())),
      xkey: derived_xkey,
      derivation_path: DerivationPath::master().child(ChildNumber::Normal { index: 0 }),
      wildcard: Wildcard::Unhardened,
    });

    let public_key = secret_key.to_public(&secp)?;

    (Descriptor::new_tr(public_key, None), secret_key)
  }
}
apoelstra commented 1 year ago

concept ACK. I'm not sure how we ought to organize this, since probably we want to support BIP 48, 84, 86, etc. (I'm not sure if I'm remembering all the numbers right :)), as well as generic BIP-49.

I'm not really thrilled with any of these BIPs, exactly because they're supposed to be supplanted by descriptors, where you don't need a "standard" as long as you've written out the descriptor. And because the BIP86-style standards don't scale well, as evidenced by the large number of them. (One could make the argument that descriptors suffer from the same problem :) but descriptors are much more general.) So I don't want to make them a first-class thing, but I definitely agree that we should support them.

Maybe have a standard or bips module and put everything in there?

casey commented 1 year ago

Maybe standard would be a good name, in case there are ever standard derivation paths that aren't documented in BIPs.