Fetch xpubkey from apps #156
Comments
The getAddress will return the publicKey and chainCode (along with the address). You can use these to generate the xpub via the bip32 bitcoinjs-library for the derivation path you pass to getAddress. Something like:
|
I second this. It's not as simple at @plondon says:
|
Why do you need to get the xpub for both |
When I said "you also need to pull If you want to generate the correct xpub of from: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki ledgerhq has some code to generate the parent fingerprint in their webwallet repo here:
|
Yup you're right @destrys thanks for pointing this out. |
It is definitely a little bit annoying to do it manually. I created that javascript example just in case is helpful: import { map, compose, dropLast, last, length } from 'ramda'
import { crypto, HDNode } from 'bitcoinjs-lib'
import * as BIP32 from 'bip32'
import BIP39 from 'bip39'
import Transport from "@ledgerhq/hw-transport-node-hid"
import AppBtc from "@ledgerhq/hw-app-btc"
import * as bippath from 'bip32-path'
const mnemonic = 'deer scout bonus forward rubber rate embrace street tragic know wife tongue photo stool rival century cruise inspire cinnamon before sudden include strong flip'
export const compressPublicKey = publicKey => {
let prefix = (publicKey[64] & 1) !== 0 ? 0x03 : 0x02
let prefixBuffer = Buffer.alloc(1)
prefixBuffer[0] = prefix
return Buffer.concat([prefixBuffer, publicKey.slice(1, 1 + 32)])
}
export const fingerprint = publickey => {
let pkh = compose(crypto.ripemd160, crypto.sha256)(publickey)
return ((pkh[0] << 24) | (pkh[1] << 16) | (pkh[2] << 8) | pkh[3]) >>> 0
}
const getParentPath =
compose(
array => bippath.fromPathArray(array).toString(),
dropLast(1),
path => bippath.fromString(path).toPathArray()
)
const createXPUB = (path, child, parent) => {
let pathArray = bippath.fromString(path).toPathArray()
let pkChild = compressPublicKey(Buffer.from(child.publicKey, 'hex'))
let pkParent = compressPublicKey(Buffer.from(parent.publicKey, 'hex'))
let hdnode = BIP32.fromPublicKey(pkChild, Buffer.from(child.chainCode, 'hex'))
hdnode.parentFingerprint = fingerprint(pkParent)
hdnode.depth = pathArray.length
hdnode.index = last(pathArray)
return hdnode.toBase58()
}
const getXPUB = async (ledger, path) => {
let parentPath = getParentPath(path)
let child = await ledger.getWalletPublicKey(path)
let parent = await ledger.getWalletPublicKey(parentPath)
return createXPUB(path, child, parent)
}
////////////////////////////////////////////////////////////////////////////////
const testXPUB = (mnemonic, i) => {
// assume bitcoin mainnet for the exercise
let seed = BIP39.mnemonicToSeed(mnemonic)
let masterNode = HDNode.fromSeedBuffer(seed)
return masterNode.deriveHardened(44)
.deriveHardened(0)
.deriveHardened(i)
.neutered()
.toBase58()
}
const connect = async (ledger) => {
let transport = await Transport.create()
let btc = new AppBtc(transport)
return btc
}
const main = async () => {
let ledger = await connect()
let xpub = await getXPUB(ledger, "44'/0'/0'")
console.log('expected XPUB: ', testXPUB(mnemonic, 0))
console.log('created XPUB: ', xpub)
}
// IT REQUIRES YOUR LEDGER CONNECTED WITH YOUR BTC APP RUNNING
main() |
Is there any way to get this included in the packages to make it much simpler for the end users? |
Before we provide such feature, I think we first need to improve our libraries and rearchitecture things a bit ( #231 ). The main goal for ledgerjs is to provide an API to communicate with the device, so it first need to come with low level primitives that exactly represent what the device returns (and maps to APDUs). These often happen to be shared cross apps (same APDU serialization living on many different apps). Then, with these functions we can compose and make higher level functions.
Some usecases might not necessarily live in ledgerjs (one typical example is algorithms to chose UTXO are a bit out of scope of current libraries because implementation choice can differ – yet we can make a smooth API that takes this solver as a function). Anyway, the important part is it needs to be made more easy to build such abstraction & e.g. publish them on NPM. For things that are very common to have, I guess it should still live in NPM. Getting the XPUB is one of this case I think. We just need to make it right so it's an opt-in (because if it needs many dependencies & if you don't need it) |
Hey, where did things end up on this, any movement to get xpubs generated out of the library itself? This is somewhat onerous to add (but, thanks so much to @Pernas for writing that example!) |
This would be really useful function now that the device requires confirmation when using 'unusual' BIP32 paths (LedgerHQ/app-bitcoin#90). This means that to extract an accurate xpub at say |
Is getting the xpub still done manually as of the moment in ledgerjs? IE I see old references to manually getting it done like: AND |
The current status quo is this feature is beyond and out of scope of ledgerjs libs that are focused on providing a minimal bindings for apps as well as providing transport for many platforms. |
I didn't see any functions to programmatically fetch the xpub key from the device. Could we add this feature? I am able to do this with the Trezor Connect library which I am then able to derive a bunch of addresses from the device to display to the user. Otherwise I have to loop through and call
await getAddress
if I want to get a bunch of addresses. Which takes a couple seconds.The text was updated successfully, but these errors were encountered: