Skip to content

Instantly share code, notes, and snippets.

@learntheropes
Created August 14, 2019 18:11
Show Gist options
  • Save learntheropes/eedfa2e658851463e3bb379014d7c1a6 to your computer and use it in GitHub Desktop.
Save learntheropes/eedfa2e658851463e3bb379014d7c1a6 to your computer and use it in GitHub Desktop.
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()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment