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

Derivation Path of known outputs is incorrect #484

Open
christsim opened this issue Jun 11, 2018 · 7 comments · May be fixed by #502
Open

Derivation Path of known outputs is incorrect #484

christsim opened this issue Jun 11, 2018 · 7 comments · May be fixed by #502
Labels
bug Unexpected or incorrect behavior has PR Issue is addressed with a pull request quick Can be fixed quickly, code change less than 10 lines wallet Wallet related

Comments

@christsim
Copy link

christsim commented Jun 11, 2018

Hi All.

When calling wallet.getTX(hash) the outputs that belong to our wallets have a path included to help you derive that address of the private key again. However, as you can see below, the derivation path is not correct:
derivation:"m/0'/1/2"
but should be:
derivation:"m/1/2"
And therefore you cannot regenerate the correct private key for that input. The hardened account should not be included in the path as it's already included in the base xpub/xprv account key.

Steps to reproduce:

  • create a watchonly wallet with xpub accountKey
  • create a receive address
  • send coins to that address
  • wait for confirmations
  • call wallet.getTX(hash)
  • try derive the receive address from the derivation path provided accountKey.derivePath(path.derivation)

Regards,
Chris

Object {hash: "3338b6b659a654d7cd0d78b6137711aad6e58573e9a3c7d1b3…", height: -1, block: null, time: 0, mtime: 1528675438, …}
VM242:1
block:null
confirmations:0
date:"1970-01-01T00:00:00Z"
fee:2820
hash:"3338b6b659a654d7cd0d78b6137711aad6e58573e9a3c7d1b370e9789fffa1e9"
height:-1
inputs:Array(1) [Object]
mdate:"2018-06-11T00:03:58Z"
mtime:1528675438
outputs:Array(2) [Object, Object]
length:2
__proto__:Array(0) [, …]
0:Object {value: 1000000, address: "2N8hwP1WmJrFF5QWABn38y63uYLhnJYJYTF", path: null}
1:Object {value: 15491080, address: "tb1qt27texeassrk7khny4vyjna5duvz0jxl6fd3gc", path: Object}
address:"tb1qt27texeassrk7khny4vyjna5duvz0jxl6fd3gc"
path:Object {name: "default", account: 0, change: true, …}
account:0
change:true
derivation:"m/0'/1/2"
name:"default"
__proto__:Object {constructor: , __defineGetter__: , __defineSetter__: , …}
value:15491080
__proto__:Object {constructor: , __defineGetter__: , __defineSetter__: , …}
rate:19859
size:224
time:0
tx:"01000000000101986c30b56a72afa74de53e76ae637cc7b38278776d4d55d046d84333b04fa9ee0100000000ffffffff0240420f000000000017a914a9974100aeee974a20cda9a2f545704a0ab54fdc870860ec00000000001600145abcbc9b3d84076f5af32558494fb46f1827c8df02483045022100a28b35d9eef40c9e896ec6ee294dc11b8bce8271ac408b4d4638775b590889fb022066a7386130c6b189f4da4b7bbec95729fc45803514deddc2c87b454327c22b460121024b98885fffad39a5bec38f552bc63cd7d4827e4264c68928bc4c63b908c52a6500000000"
virtualSize:142
__proto__:Object {constructor: , __defineGetter__: , __defineSetter__: , …}
@pinheadmz
Copy link
Member

Hi @christsim I'm looking into this... could you please tell me exactly how you're using the derivePath function? Is that coming from a new Wallet object?

@christsim
Copy link
Author

Hi @pinheadmz,

So just a few notes first. I'm using bclient and therefore WalletClient to interact with bcoin. I've also applied PR patch #444 so that I can create an unsigned transaction.

Then I create a watchOnly wallet and load the xpub account key into it (derived from the masterKey offline). When I want to send a transaction, I call walletClient.createTX which gives me an unsigned transaction.

Then on an offline pc, I first derive the xprv account key from the masterKey. Then to get the private key which matches the inputs of the unsigned transaction, I call, xprvAccountKey.derive(path), and as described above does not work, cause the path is incorrect.

Here is my signing code (simplified):

        var derivedXprv = xprvAccountKey.derivePath(path);
        var ring = bcoin.WalletKey.fromPrivate(derivedXprv.privateKey, true);
        mtx.sign(ring);

Hope that clears it up?

Regards,
Chris

@pinheadmz
Copy link
Member

pinheadmz commented Jun 21, 2018

I'm sorry Chris, how are you getting xprvAccountKey?

And also have you tried just asking the wallet to sign the tx automatically? I think if you have derived enough addresses on the offline computer it should recognize it has the keys and sign. There are also methods to return a private key given its address.

But in general I think I see what you mean. The derivePath method is already inside an account key, so the first index in the path (the account number) is unnecessary. Seems like it would be more useful to have a derivePath function in the masterKey module, then the account index would be required.

@christsim
Copy link
Author

I'm sorry Chris, how are you getting xprvAccountKey?

I'm pretty much deriving it the same way as this https://iancoleman.io/bip39/.

I'm then giving the Account Extended Public Key to the watch only wallet in bcoin as the accountKey. Then taking the Account Extended Private Key and giving it to my offline wallet. Bcoin can then generate the receive addresses for me as well as the unsigned send transactions and the offline wallet can then sign the transactions.

Bcoin will then derive addresses based on m/branch/index using the accountKey. But in the path property of the output it reports it as m/0'/branch/index. Bcoin can't derive a hardened path as it only has an xpub and therefore m/0'/branch/index will be impossible for bcoin to derive.

And also have you tried just asking the wallet to sign the tx automatically? I think if you have derived enough addresses on the offline computer it should recognize it has the keys and sign. There are also methods to return a private key given its address.

So obviously the watchOnly wallet can't sign the transactions. I don't generate addresses on the offline wallet so it can't find the matching private key by itself. I'm currently signing the transaction this like, as disussed above:

        var derivedXprv = xprvAccountKey.derivePath(path);
        var ring = bcoin.WalletKey.fromPrivate(derivedXprv.privateKey, true);
        mtx.sign(ring);

However I have to manipulate the path to get the transactioned sigend.

@pinheadmz
Copy link
Member

pinheadmz commented Jun 21, 2018

OK I figured this out..

So if your tx is saying an address path is this:

m/0'/1/2

Then from the wallet xprv master key, the REAL derivation path is actually this (BTC testnet):

m/44'/1'/0'/1/2

...and I've verified this on the bip39 website.

The bug is in how the tx object reports the derivation path on wallet transactions, and I'll look into fixing that today :-)

@bucko13 bucko13 closed this as completed Jun 22, 2018
@pinheadmz pinheadmz linked a pull request Jun 22, 2018 that will close this issue
@braydonf
Copy link
Contributor

braydonf commented Oct 11, 2018

The PR for this is still open at #502 was this closed as it's not an issue? Seems like there is still an outstanding bug, not having reproduced the issue myself yet.

@bucko13 bucko13 reopened this Oct 11, 2018
@bucko13
Copy link
Contributor

bucko13 commented Oct 11, 2018

Not sure why I closed. Sorry about that.

@pinheadmz pinheadmz added bug Unexpected or incorrect behavior quick Can be fixed quickly, code change less than 10 lines has PR Issue is addressed with a pull request labels Jan 21, 2019
@braydonf braydonf added the wallet Wallet related label Feb 6, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Unexpected or incorrect behavior has PR Issue is addressed with a pull request quick Can be fixed quickly, code change less than 10 lines wallet Wallet related
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants