Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trezor generated xPub - 'Invalid network version' #966

Closed
Katafalkas opened this issue Dec 23, 2017 · 24 comments
Closed

Trezor generated xPub - 'Invalid network version' #966

Katafalkas opened this issue Dec 23, 2017 · 24 comments

Comments

@Katafalkas
Copy link

Hi. So I am trying to generate HD addresses from xPub that is generated inside the Trezor.

const network = bitcoin.networks.mainnet;
const xpub = 'ypub6XTWVFLfqkFYarn9NArqtBLziffJttqf1Utaur3sTbTGtgfNaTzkGcRpFgiiieBjQ6rV1rJ7iJ9r9oXGpPXZpkq71yfss2mrKLaauxhjXD4';
const node = bitcoin.HDNode.fromBase58(xpub, network).neutered();
const address = node.derive(0).derive(0).getAddress();

console.log('address', address);
# node index.js 
/vagrant/btc-js/node_modules/bitcoinjs-lib/src/hdnode.js:78
    version !== network.bip32.public) throw new Error('Invalid network version')

Any clues why? It does work with const xpub = 'xpub661MyMwAqRbcG4PQrRAT3N2uxTkXWeRq5kpjyDvStBQP7eW65Lu5rZ3MLoBZJQuZFS9FC7mZZEcgxFZxccRdnqSxopraUB6wVjTqp8ZsS4H'

@dabura667
Copy link
Contributor

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.

@Katafalkas
Copy link
Author

Katafalkas commented Dec 23, 2017

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

@dcousens
Copy link
Contributor

#927

@dabura667
Copy link
Contributor

dabura667 commented Dec 24, 2017

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

@bmclain
Copy link

bmclain commented Jan 9, 2018

@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?

@dabura667
Copy link
Contributor

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

@bmclain
Copy link

bmclain commented Jan 9, 2018

@dabura667 much appreciated.

@dcousens
Copy link
Contributor

Closing in favour of #927

@liangfenxiaodao
Copy link

@dabura667 could you please explain a little bit of hdNode.derive(0).derive(0)? I'm not sure why derives twice here. Many thanks.

@dabura667
Copy link
Contributor

@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

@liangfenxiaodao
Copy link

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.

@liangfenxiaodao
Copy link

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...

@liangfenxiaodao
Copy link

liangfenxiaodao commented May 1, 2018

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

@larafale
Copy link

larafale commented Sep 7, 2018

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

@larafale
Copy link

larafale commented Sep 7, 2018

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 
  })
})

@Jasonvdb
Copy link

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)

@alexander-morris
Copy link

@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.

@junderw
Copy link
Member

junderw commented Sep 28, 2018

@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
}

@mareksip
Copy link

@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?

@Jasonvdb
Copy link

@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/

@junderw
Copy link
Member

junderw commented Sep 30, 2018

@mareksip b58 in the example is the package bs58check and not bs58

@junderw
Copy link
Member

junderw commented Sep 30, 2018

You used:
var b58 = require('bs58')
When you should have used
var b58 = require('bs58check')

@mareksip
Copy link

mareksip commented Oct 1, 2018

@Jasonvdb thanks for the reply, I was using bs58 instead bs58check lib.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants