diff --git a/class/wallets/hd-aezeed-wallet.js b/class/wallets/hd-aezeed-wallet.js index 54b6fcb01..190a495f6 100644 --- a/class/wallets/hd-aezeed-wallet.js +++ b/class/wallets/hd-aezeed-wallet.js @@ -1,4 +1,5 @@ import { AbstractHDElectrumWallet } from './abstract-hd-electrum-wallet'; +import b58 from 'bs58check'; const bitcoin = require('bitcoinjs-lib'); const { CipherSeed } = require('aezeed'); @@ -33,6 +34,23 @@ export class HDAezeedWallet extends AbstractHDElectrumWallet { } } + getXpub() { + // first, getting xpub + const root = bitcoin.bip32.fromSeed(this._getEntropyCached()); + + const path = "m/84'/0'/0'"; + const child = root.derivePath(path).neutered(); + const xpub = child.toBase58(); + + // bitcoinjs does not support zpub yet, so we just convert it from xpub + let data = b58.decode(xpub); + data = data.slice(4); + data = Buffer.concat([Buffer.from('04b24746', 'hex'), data]); + this._xpub = b58.encode(data); + + return this._xpub; + } + validateMnemonic(): boolean { throw new Error('Use validateMnemonicAsync()'); } diff --git a/tests/unit/hd-aezeed.test.js b/tests/unit/hd-aezeed.test.js index b392df2ab..38f964d62 100644 --- a/tests/unit/hd-aezeed.test.js +++ b/tests/unit/hd-aezeed.test.js @@ -1,4 +1,4 @@ -import { HDAezeedWallet } from '../../class'; +import { HDAezeedWallet, WatchOnlyWallet } from '../../class'; const assert = require('assert'); describe('HDAezeedWallet', () => { @@ -50,7 +50,7 @@ describe('HDAezeedWallet', () => { assert.strictEqual( aezeed.getXpub(), - 'zpub6rrqwqM3aF1Jdz6y5Zw18RTppHbZQeQpsrSyf3E2uibcrsEeZAbm5MX41Nq4XBF7HbCvRVASHLzRkFsg6sMgakcceWzJazZH7SaVPBoXzDQ', + 'zpub6rkAmx9z6PmK7tBpGQatqpRweZvRw7uqiEMRS9KuZA9VFKUSoz3GQeJFtRQsQwduWugh5mGHro1tGnt78ci9AiB8qEH4hCRBWxdMaxadGVy', ); let address = aezeed._getExternalAddressByIndex(0); @@ -91,6 +91,30 @@ describe('HDAezeedWallet', () => { ); }); + it('watch-only from zpub produces correct addresses', async () => { + const aezeed = new HDAezeedWallet(); + aezeed.setSecret( + 'abstract rhythm weird food attract treat mosquito sight royal actor surround ride strike remove guilt catch filter summer mushroom protect poverty cruel chaos pattern', + ); + assert.ok(await aezeed.validateMnemonicAsync()); + assert.ok(!(await aezeed.mnemonicInvalidPassword())); + + assert.strictEqual( + aezeed.getXpub(), + 'zpub6rkAmx9z6PmK7tBpGQatqpRweZvRw7uqiEMRS9KuZA9VFKUSoz3GQeJFtRQsQwduWugh5mGHro1tGnt78ci9AiB8qEH4hCRBWxdMaxadGVy', + ); + + const address = aezeed._getExternalAddressByIndex(0); + assert.strictEqual(address, 'bc1qdjj7lhj9lnjye7xq3dzv3r4z0cta294xy78txn'); + assert.ok(aezeed.getAllExternalAddresses().includes('bc1qdjj7lhj9lnjye7xq3dzv3r4z0cta294xy78txn')); + + const watchOnly = new WatchOnlyWallet(); + watchOnly.setSecret(aezeed.getXpub()); + watchOnly.init(); + assert.strictEqual(watchOnly._getExternalAddressByIndex(0), aezeed._getExternalAddressByIndex(0)); + assert.ok(watchOnly.weOwnAddress('bc1qdjj7lhj9lnjye7xq3dzv3r4z0cta294xy78txn')); + }); + it('can sign and verify messages', async () => { const aezeed = new HDAezeedWallet(); aezeed.setSecret(