Closed Katafalkas closed 6 years ago
it says ypub.
ypub is not supported in bitcoinjs-lib.
You can base58 decode, replace the version with xpub and re-do if you'd like, but getAddress will not get you the Trezor's "3" addresses.
Is there some way to generate such addresses? Sorry, I am quite a noob on this topic. Not sure what topic to even search :D
var bjs = require('bitcoinjs-lib')
var b58 = require('bs58check')
// this function takes ypub and turns into xpub
function ypubToXpub(ypub) {
var data = b58.decode(ypub)
data = data.slice(4)
data = Buffer.concat([Buffer.from('0488b21e','hex'), data])
return b58.encode(data)
}
// this function takes an HDNode, and turns the pubkey of that node into a Segwit P2SH address
function nodeToP2shSegwitAddress(hdNode) {
var pubkeyBuf = hdNode.keyPair.getPublicKeyBuffer()
var hash = bjs.crypto.hash160(pubkeyBuf)
var redeemScript = bjs.script.witnessPubKeyHash.output.encode(hash)
var hash2 = bjs.crypto.hash160(redeemScript)
var scriptPubkey = bjs.script.scriptHash.output.encode(hash2)
return bjs.address.fromOutputScript(scriptPubkey)
}
// convert ypub string into xpub string
var xpub = ypubToXpub('ypub6XTWVFLfqkFYarn9NArqtBLziffJttqf1Utaur3sTbTGtgfNaTzkGcRpFgiiieBjQ6rV1rJ7iJ9r9oXGpPXZpkq71yfss2mrKLaauxhjXD4')
// grab the HDNode object from the xpub
var hdNode = bjs.HDNode.fromBase58(xpub)
// generate as usual, but instead of getAddress, feed into above function
var address = nodeToP2shSegwitAddress(hdNode.derive(0).derive(0))
// 3FkFtj43U6UDZ7wberPtZwUrR3GLMw2S6x
@dabura667 Doing some testing with an Electrum wallet and the default option in version 3.0.5 is to use zpub and P2WSH addresses. Do you know of any good libraries similar to bitcoinjs-lib that can derive wallet addresses from a zpub?
You can basically do the same thing as above. Slightly modified.
var bjs = require('bitcoinjs-lib')
var b58 = require('bs58check')
// this function takes zpub and turns into xpub
function zpubToXpub(zpub) {
var data = b58.decode(zpub)
data = data.slice(4)
data = Buffer.concat([Buffer.from('0488b21e','hex'), data])
return b58.encode(data)
}
// this function takes an HDNode, and turns the pubkey of that node into a Segwit bech32 address
function nodeToP2wpkhSegwitAddress(hdNode) {
var pubkeyBuf = hdNode.keyPair.getPublicKeyBuffer()
var hash = bjs.crypto.hash160(pubkeyBuf)
var scriptPubkey = bjs.script.witnessPubKeyHash.output.encode(hash)
return bjs.address.fromOutputScript(scriptPubkey)
}
// convert zpub string into xpub string
var xpub = zpubToXpub('zpub6oFHEbYeAMTVmmmcZA5KJinUFVoGZfQhc4dBfzCMwrL1AsxopkyLv9zCKHezgHJNskU8pRUQ9AZqZjAjdZeM1ehkALJE1UNPboorWDfhPSB')
// grab the HDNode object from the xpub
var hdNode = bjs.HDNode.fromBase58(xpub)
// generate as usual, but instead of getAddress, feed into above function
var address = nodeToP2wpkhSegwitAddress(hdNode.derive(0).derive(0))
// bc1qr8mrj8lsmnl5rpewfphj0rrprs7gkqccmn4z4l
@dabura667 much appreciated.
Closing in favour of https://github.com/bitcoinjs/bitcoinjs-lib/pull/927
@dabura667 could you please explain a little bit of hdNode.derive(0).derive(0)
? I'm not sure why derives twice here. Many thanks.
@jackylimel Every derivation scheme has two non-hardened laters after the last hardened layer.
Since xpubs and ypubs can not derive hardened layers, usually apps show the xpub of the last hardened layer.
In Electrum’s case, they use BIP44 which is
H H H S S (hard hard hard soft soft)
The last two softs are 0|1 for is_change and the address index.
So to derive the “first non-change address” is derive(0).derive(0)
first change would be 1 0
Second non-change is 0 1
Tenth non-change is 0 9
Hi @dabura667, thanks for your quick reply. Yeah, I've read BIP0044 and BIP0032 and things are much clear now. However, I still don't quite understand this sentence "Since xpubs and ypubs can not derive hardened layers", may I ask where can I get more information about xpub
, ypub
and hardened layer
. Thanks a lot.
btw, I tried to use @dabura667 's solution to derive address from testnet public key:
tpubDDaftv5ij9iJhHY5ECQSxNiniviKeA6t4aFQGSVKhF3f3uuJZp1sj1WqumHCxjjpVp2A4KB2thuBUaPW5tiKfdzZs4LaFaUP9zoF7SFVYT4
however, I cannot get a correct address out of it...
Ok, I got it now:
var bjs = require('bitcoinjs-lib')
function nodeToP2shSegwitAddressTestNet(hdNode) {
var pubkeyBuf = hdNode.keyPair.getPublicKeyBuffer()
var hash = bjs.crypto.hash160(pubkeyBuf)
var redeemScript = bjs.script.witnessPubKeyHash.output.encode(hash)
var hash2 = bjs.crypto.hash160(redeemScript)
var scriptPubkey = bjs.script.scriptHash.output.encode(hash2)
return bjs.address.fromOutputScript(scriptPubkey, bjs.networks.testnet)
}
var tpub = 'tpubDDaftv5ij9iJhHY5ECQSxNiniviKeA6t4aFQGSVKhF3f3uuJZp1sj1WqumHCxjjpVp2A4KB2thuBUaPW5tiKfdzZs4LaFaUP9zoF7SFVYT4'
var hdNode = bjs.HDNode.fromBase58(tpub, [bjs.networks.testnet])
// generate as usual, but instead of getAddress, feed into above function
var address = nodeToP2shSegwitAddressTestNet(hdNode.derive(0).derive(0))
console.log(address)
bjs.networks.testnet
needs to be used as a parameter of address.fromOutputScript
any idea on how to make this work with latest version (4.0.1) ?
i'm having trouble with the nodeToP2shSegwitAddress(hdNode)
function.
hdNode.keyPair.getPublicKeyBuffer()
does not exist, because the node is not created from HDNode anymore but with bip32.fromBase58
instead.
so I do I get a publickeyBuffer from a node that was created with bip32.fromBase58
thx for the feedback
ok I was able to do this with
bjs.payments.p2sh({
redeem: bjs.payments.p2wpkh({
pubkey: bip32.fromBase58(pubkey, network).derive(0).derive(0).publicKey
})
})
If anyone is still looking for a complete example of how to get addresses from the ypub trezor provides.
Consolidated from off all the answers above (working with version 4.0.2 of bitcoinjs-lib):
var bjs = require('bitcoinjs-lib')
var b58 = require('bs58check')
var bip32 = require('bip32')
//Assuming it's mainnet for trezors
var network = bjs.networks.bitcoin;
function ypubToXpub(ypub) {
var data = b58.decode(ypub)
data = data.slice(4)
data = Buffer.concat([Buffer.from('0488b21e','hex'), data])
return b58.encode(data)
}
var ypub = "ypub6XTWVFLfqkFYarn9NArqtBLziffJttqf1Utaur3sTbTGtgfNaTzkGcRpFgiiieBjQ6rV1rJ7iJ9r9oXGpPXZpkq71yfss2mrKLaauxhjXD4"
//If you have a ybup address, convert it first
var xpub = ypubToXpub(ypub)
//First address created for account
var addressIndex = 0;
var payment = bjs.payments.p2sh({
redeem: bjs.payments.p2wpkh({
pubkey: bip32.fromBase58(xpub, network).derive(0).derive(addressIndex).publicKey
})
})
var address = payment.address;
console.log(address)
@Jasonvdb thanks for the great example - I've been toying with these a lot recently and appreciate the clarity.
Any chance anyone has gotten this process working for the other funny extended keys trezor uses such as dogecoin (dgub...) or dash (drkp...) etc ?
Seems a bit backwards to require a separate adaptation for each token but I'm hoping I'm missing something.
@alexander-morris the function given in the example does not care what the prefix of the input is:
If you look at the function it literally is just throwing away the old prefix without checking it and adding the xpub prefix.
function ypubToXpub(ypub) {
var data = b58.decode(ypub) // decode... this could be dgub or drkp or whatever
data = data.slice(4) // throw away first four letters
data = Buffer.concat([Buffer.from('0488b21e','hex'), data]) // add "xpub"
return b58.encode(data) // re-encode
}
@Jasonvdb @junderw thanks for examples, I have been looking for ypub solution for endless hours.
I have tried above ypubToXpub function with input:
ypub6X9uc4mFxvgtsPBYqxDfVdm15rP7jRZXJKKC42Gm4BNaqTPueZcb4LA1eaFM6sKokUJavLYzm7F5HvKPyQ7HrGHPihkVabahFVCug5HyvbS
received:
xpub6CKeJQ6LpF9R25zS1bS3HYfVutEfnoa2PCnyGdNsgAzhnMagPuT2SGVsdNHm6xftLqBnArxSJStXQdhqFhhH42bnrN44zgmCym9GHVuP69E
which is not valid according to http://bip32.org/ and also fails with function bitcoinjs.bip32.fromBase58
with error: Invalid checksum
What am I missing?
@mareksip looks like that maybe a P2WPKH of a ypub format but you're looking for a P2WSH version. Not sure of the difference myself but Jameson Lopp made a handy open source convertor. You should be able to input your version and get what you're looking for: https://jlopp.github.io/xpub-converter/
@mareksip b58
in the example is the package bs58check
and not bs58
You used:
var b58 = require('bs58')
When you should have used
var b58 = require('bs58check')
@Jasonvdb thanks for the reply, I was using bs58 instead bs58check lib.
Hi. So I am trying to generate HD addresses from xPub that is generated inside the Trezor.
Any clues why? It does work with
const xpub = 'xpub661MyMwAqRbcG4PQrRAT3N2uxTkXWeRq5kpjyDvStBQP7eW65Lu5rZ3MLoBZJQuZFS9FC7mZZEcgxFZxccRdnqSxopraUB6wVjTqp8ZsS4H'