bip32: Add support for ypubs and zpubs#279
bip32: Add support for ypubs and zpubs#279shesek wants to merge 1 commit intorust-bitcoin:masterfrom
Conversation
|
Haha, I literally had an error yesterday where I tried parsing an xpub that turned out to be a ypub instead (Trezkr returns it). I asked around and they're not part of BIP-32 and are only really supported by Trezor and Electrum. I have to say I personally don't like them. I think BIP-32 is about "key derivation" and shouldn't have anything to do with addresses. Also, script descriptors do that a lot better and make these weird subtypes obsolete, like So I have mixed feelings because I don't like them in principle but then I could have used them yesterday for the Trezor crate. Eventually ended up creating the BIP32 ext key with the ext key info fields instead of the serialized zpub and that worked as well. |
|
I agree that SLIP-0132 and the ypub/zpub convention are problematic in many ways, but it seems like its getting adopted nonetheless (its not just Electrum and Trezor, I believe Edge, Samurai and Eclair all do that too, to name a few -- there are probably more). It was around since before script descriptors, when people needed some solution and didn't really have any good alternatives (that I'm aware of), so we kinda got stuck with this. For my particular case, an electrum personal server implementation in Rust that I'm working on, I need to be able to process [xyz]pubs that users get out of their electrum wallets. How would you suggest to go about doing this? Perhaps a separate crate that uses rust-bitcoin::util::bip32 internally and swaps out the versions to get it to accept it, then keep track of the real versions somewhere outside? Or just maintain a fork of the code? Both options sounds kind of messy. :-\ |
|
Perhaps rust-bitcoin::util::bip32 could simply be relaxed to accept any version bytes and just store them as-is on a field, without attempting to parse it into a network? That would make it easier to implement [yz]pub support externally. Edit: getting the network could be a method on |
|
In that case I would remove the That would also make it a lot easier to use this with Liquid, f.e. |
|
So I'd just have |
|
So keep the SLIP-0132 version bytes and script types parser, but only trigger it when explicitly calling the methods on How would you prefer to handle the different bytes for public/private versions? I see a few options:
|
|
Yeah there's many issues. I was indeed not thinking about the issue when someone parses an |
|
In that case by default it's possible to parse an invalid one, though. So personally I think we should have the default parsing code error when it's an unknown prefix and have a special method to parse ignoring the prefix. |
|
After a discussion with @stevenroose, I'm trying an alternative approach where the version prefix constraints are relaxed if the keys are initialized using a new An implementation of this can be seen here: master...shesek:bip32-relax-version This has a few caveats:
|
|
Regarding (1)/(2): another option is to have a new |
|
re re (3):
I've always argued against a |
So that we can use the mainline rust-bitcoin without rust-bitcoin/rust-bitcoin#279. For now, only support regular xpubs with p2pkh. Support for p2wpkh/p2shp2wpkh will be added back later.
There was a problem hiding this comment.
I found this PR while was working on PSBT implementation for the new key/values from BIP-174. It has a global xpub key, which must be serialized according to BIP32 binary serialization; however it is not being implemented for ExtendedPubkey in rust-bitcoin yet. Why? B/c it requires implementation of this version byte serialization and deserialization.
My proposal is the following: we need a single BIP32 key version type, that is able (a) to serialize/deserialize with consensus binary encoding, (b) to a human-readable form, (c) produce Network value and (d) be compatible with miniscript descriptors (i.e. it must return whether BIP 32 key may be applied for a given miniscript descriptor).
Probably version may be a trait, with two concrete types for public and private key.
|
|
||
| /// Script type for addresses | ||
| #[derive(PartialEq, Eq, Clone, Copy, Debug)] | ||
| pub enum ScriptType { |
There was a problem hiding this comment.
I think ScriptType is a very confusing name. First, it's scriptPubkey type, second, it may mix with other cases where script type name may be used in the future. So I propose to rename it to something like UsageScenario
| P2pkh, | ||
| /// Pay to witness pubkey hash | ||
| P2wpkh, | ||
| /// Pay to witness pubkey hash, wrapped in pay to script hash | ||
| P2shP2wpkh, |
There was a problem hiding this comment.
Also, quite confusing names. May be Legacy, SegWitV0, and SegWitLegacy will work better?
|
I am working on draft PR which will propose the following API, which seem to cover all discussed cases: #[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct KeyVersion([u8; 4]);
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum KeyApplications {
/// xprv/xpub: keys that can be used for P2PKH and multisig P2SH scriptPubkey
/// descriptors.
Legacy,
/// zprv/zpub: keys that can be used for P2WPKH scriptPubkey descriptors
SegWitV0Singlesig,
/// yprv/ypub: keys that can be used for P2WPKH-in-P2SH scriptPubkey descriptors
SegWitLegacySinglesig,
/// Zprv/Zpub: keys that can be used for multisig P2WSH scriptPubkey descriptors
SegWitV0Miltisig,
/// Yprv/Ypub: keys that can be used for multisig P2WSH-in-P2SH scriptPubkey descriptors
SegWitLegacyMultisig,
}
pub trait VersionResolver {
type Network;
type Applications;
fn resolve(network: Self::Network, applicable_for: Self::Applications) -> Option<KeyVersion>;
fn network(_: &KeyVersion) -> Option<Self::Network>;
fn applications(_: &KeyVersion) -> Option<Self::Applications>;
fn derivation_path(_: &KeyVersion) -> Option<DerivationPath>;
}
impl KeyVersion {
fn network<R: VersionResolver>(&self) -> Option<R::Network> { R::network(&self) }
fn applications<R: VersionResolver>(&self) -> Option<R::Applications> { R::applications(&self) }
fn derivation_path<R: VersionResolver>(&self) -> Option<DerivationPath> { R::derivation_path(&self) }
}
pub struct DefaultResolver;
impl VersionResolver for DefaultResolver {
type Network = Network;
type Applications = KeyApplications;
fn resolve(network: Self::Network, applicable_for: Self::Applications) -> Option<KeyVersion> {
match (network, applicable_for) {
(Network::Bitcoin, KeyApplications::Legacy) => Some(KeyVersion(VERSION_MAGIC_XPUB)),
// ....
_ => None
}
}
// ....
}
/// Extended private key
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct ExtendedPrivKey {
/// Version bytes specifying to which network the key belongs
/// and for which types of scriptPubkey it may be used
pub version: KeyVersion,
/// How many derivations this key is from the master (which is 0)
pub depth: u8,
/// Fingerprint of the parent key (0 for master)
pub parent_fingerprint: Fingerprint,
/// Child number of the key used to derive from parent (0 for master)
pub child_number: ChildNumber,
/// Private key
pub private_key: PrivateKey,
/// Chain code
pub chain_code: ChainCode
}Downstream crates will be able to provide their concrete implementations for |
|
Have completed my variant of implementation: https://github.com/rust-bitcoin/rust-bitcoin/pull/469/files |
|
concept NACK. If descriptors aren't in rust-bitcoin then ypubs/zpubs definitely shouldn't be. |
|
Made a separate, simple, stand-alone tool for electrum [?]pubs to descriptors if someone is interested https://github.com/RCasatta/electrum2descriptors |
|
Agree with concept nACK and propose to close this PR as I did with the alternative #469 |
|
The concept NACK from #469 applies here too, there's also https://crates.io/crates/slip132 now. |
* removed version from PersistentSyncState * removed persist and recovery logic of teh ChainState * dropped dead code in sync_state * removed logic to store sync state * sync_state.rs deleted * ffi docs udpated
This commit adds a new
ScriptTypeenum, with three possible values:P2pkhfor xpubs,P2wpkhfor zpubs andP2shP2wpkhfor ypubs. It also adds a newVersiontype and parses it into the network and script type when deserializing extended keys from string.I think that
ExtendedPrivKey::new_master()should ideally be changed to take aVersioninstead of aNetwork, but I wasn't sure if breaking the backwards compatibility of the public API is considered acceptable. Let me know if you prefer that.