bitcoinjs / bitcoinjs-lib

A javascript Bitcoin library for node.js and browsers.
MIT License
5.69k stars 2.11k forks source link

How to get segwit extended public and private keys #1937

Closed learntheropes closed 1 year ago

learntheropes commented 1 year ago

When I derive a segwit extended public and private keys, they start with xpub and xprv instead of zpub and zprv.

What I'm doing wrong?

import bip39 from  'bip39';
import { networks } from 'bitcoinjs-lib'
import BIP32Factory from 'bip32';
import * as ecc from 'tiny-secp256k1'
const bip32 = BIP32Factory.default(ecc);

const mnemonic = 'drip disagree original hood slim put idle this embrace glove spirit allow'
const seed = bip39.mnemonicToSeedSync(mnemonic);
const root = bip32.fromSeed(seed, networks.bitcoin);
const bip32PrivateKey = root.toBase58();
const AccountExtendedPublicKey = root.derivePath("m/84'/0'/0'").neutered().toBase58();
console.log('bip32PrivateKey', bip32PrivateKey)
// xprv9s21ZrQH143K3g1R1ppuAyziMEwkqSBarL3Ligt4LVP33ZF1rdrHKekp92wc7P9HUdwJgYtjST8ZF2qnSTGVZsRiV3JyjGrhPVswSifx68G
console.log('AccountExtendedPublicKey', AccountExtendedPublicKey)
// xpub6CjbQJcLWqfre7YHfLLMz27otNFTTeZV2Q8cUCfB8nr1FwUaVmkvVmiuWTC9f9JSnxFQRdru8hZHu68VyVe3rd51mt5JCc6B1QBJHpAk6te

I'm also surpised that the fist generated address matches the one generated by https://iancoleman.io/bip39/

junderw commented 1 year ago

The first 4 letters of the output is decided by the bip32 version numbers in the network you use.

BitcoinJS was created in a time before zpub etc.

Plus, zpub etc. are not officially a part of BIP32 the standard. It was just created after the fact by Electrum Wallet, and the community just kind of decided to use that.

You can get the behavior you want by modifying the network object and passing a custom network to bip32.

const customNetwork = Object.assign({}, networks.bitcoin, { bip32: { public: 1, private: 2 } });

The closest thing to a spec is Electrum (the wallet that decided the whole zpub thing) documentation.

https://electrum.readthedocs.io/en/latest/xpub_version_bytes.html#specification

BitcoinJS network objects can only handle one pair of public and private prefixes at a time.

junderw commented 1 year ago

Even if we COULD handle multiple version numbers, the information needed to decide which one to use (according to the Electrum documentation) is "How will the keys of this bip32 be used to encode outputs / addresses?" which bip32 has no way of knowing.

The information encoded in the BIP32 standard explicitly avoids dictating anything about scripts and addresses, BIP32 is only concerned with key derivation. BIP32 keys don't even include their full paths (only depth + the index of self) so we couldn't even glean it from the path (m/84'/0'/0') because all the BIP32 key "sees" is "depth = 3, index = 0'"

The only way to parse / output zpubs is for the wallet application (the thing that actually can understand which script / addresses it is trying to use) to explicitly choose the prefixes to match itself.

junderw commented 1 year ago

If you were trying to parse an arbitrary extended key, you could create a custom network for each type, and run try catch for each, and based on the one it matched, you could set a flag to tell you which type of script to use.