mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-02-21 14:34:55 +01:00
WIP: HD wallet
This commit is contained in:
parent
73c3281cbf
commit
0ffb127e82
5 changed files with 122 additions and 40 deletions
|
@ -42,6 +42,15 @@ it('can create a Segwit HD (BIP49)', async function() {
|
|||
assert.equal(hd._getInternalAddressByIndex(hd.next_free_change_address_index), freeChangeAddress);
|
||||
});
|
||||
|
||||
it('Segwit HD (BIP49) can generate addressess only via ypub', async function() {
|
||||
let ypub = 'ypub6WhHmKBmHNjcrUVNCa3sXduH9yxutMipDcwiKW31vWjcMbfhQHjXdyx4rqXbEtVgzdbhFJ5mZJWmfWwnP4Vjzx97admTUYKQt6b9D7jjSCp';
|
||||
let hd = new HDSegwitP2SHWallet();
|
||||
hd._xpub = ypub;
|
||||
assert.equal('3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK', hd._getExternalAddressByIndex(0));
|
||||
assert.equal('35p5LwCAE7mH2css7onyQ1VuS1jgWtQ4U3', hd._getExternalAddressByIndex(1));
|
||||
assert.equal('32yn5CdevZQLk3ckuZuA8fEKBco8mEkLei', hd._getInternalAddressByIndex(0));
|
||||
});
|
||||
|
||||
it('can generate Segwit HD (BIP49)', async () => {
|
||||
let hd = new HDSegwitP2SHWallet();
|
||||
let hashmap = {};
|
||||
|
@ -117,6 +126,14 @@ it('can create a Legacy HD (BIP44)', async function() {
|
|||
assert.equal(hd._getExternalAddressByIndex(hd.next_free_address_index), freeAddress);
|
||||
});
|
||||
|
||||
it('Legacy HD (BIP44) can generate addressess based on xpub', async function() {
|
||||
let xpub = 'xpub6CQdfC3v9gU86eaSn7AhUFcBVxiGhdtYxdC5Cw2vLmFkfth2KXCMmYcPpvZviA89X6DXDs4PJDk5QVL2G2xaVjv7SM4roWHr1gR4xB3Z7Ps';
|
||||
let hd = new HDLegacyP2PKHWallet();
|
||||
hd._xpub = xpub;
|
||||
assert.equal(hd._getExternalAddressByIndex(0), '12eQ9m4sgAwTSQoNXkRABKhCXCsjm2jdVG');
|
||||
assert.equal(hd._getInternalAddressByIndex(0), '1KZjqYHm7a1DjhjcdcjfQvYfF2h6PqatjX');
|
||||
});
|
||||
|
||||
it('HD breadwallet works', async function() {
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 300 * 1000;
|
||||
let hdBread = new HDLegacyBreadwalletWallet();
|
||||
|
|
|
@ -126,7 +126,6 @@ export class AbstractHDWallet extends LegacyWallet {
|
|||
return freeAddress;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Derives from hierarchy, returns next free CHANGE address
|
||||
* (the one that has no transactions). Looks for several,
|
||||
|
|
|
@ -56,29 +56,47 @@ export class HDLegacyP2PKHWallet extends AbstractHDWallet {
|
|||
_getExternalAddressByIndex(index) {
|
||||
index = index * 1; // cast to int
|
||||
if (this.external_addresses_cache[index]) return this.external_addresses_cache[index]; // cache hit
|
||||
let mnemonic = this.secret;
|
||||
let seed = bip39.mnemonicToSeed(mnemonic);
|
||||
let root = bitcoin.HDNode.fromSeedBuffer(seed);
|
||||
let path = "m/44'/0'/0'/0/" + index;
|
||||
let child = root.derivePath(path);
|
||||
if (!this._xpub) {
|
||||
let mnemonic = this.secret;
|
||||
let seed = bip39.mnemonicToSeed(mnemonic);
|
||||
let root = bitcoin.HDNode.fromSeedBuffer(seed);
|
||||
let path = "m/44'/0'/0'/0/" + index;
|
||||
let child = root.derivePath(path);
|
||||
|
||||
let w = new LegacyWallet();
|
||||
w.setSecret(child.keyPair.toWIF());
|
||||
return (this.external_addresses_cache[index] = w.getAddress());
|
||||
let w = new LegacyWallet();
|
||||
w.setSecret(child.keyPair.toWIF());
|
||||
return (this.external_addresses_cache[index] = w.getAddress());
|
||||
} else {
|
||||
let node = bitcoin.HDNode.fromBase58(this._xpub);
|
||||
let address = node
|
||||
.derive(0)
|
||||
.derive(0)
|
||||
.getAddress();
|
||||
return (this.external_addresses_cache[index] = address);
|
||||
}
|
||||
}
|
||||
|
||||
_getInternalAddressByIndex(index) {
|
||||
index = index * 1; // cast to int
|
||||
if (this.internal_addresses_cache[index]) return this.internal_addresses_cache[index]; // cache hit
|
||||
let mnemonic = this.secret;
|
||||
let seed = bip39.mnemonicToSeed(mnemonic);
|
||||
let root = bitcoin.HDNode.fromSeedBuffer(seed);
|
||||
if (!this._xpub) {
|
||||
let mnemonic = this.secret;
|
||||
let seed = bip39.mnemonicToSeed(mnemonic);
|
||||
let root = bitcoin.HDNode.fromSeedBuffer(seed);
|
||||
|
||||
let path = "m/44'/0'/0'/1/" + index;
|
||||
let child = root.derivePath(path);
|
||||
let path = "m/44'/0'/0'/1/" + index;
|
||||
let child = root.derivePath(path);
|
||||
|
||||
let w = new LegacyWallet();
|
||||
w.setSecret(child.keyPair.toWIF());
|
||||
return (this.internal_addresses_cache[index] = w.getAddress());
|
||||
let w = new LegacyWallet();
|
||||
w.setSecret(child.keyPair.toWIF());
|
||||
return (this.internal_addresses_cache[index] = w.getAddress());
|
||||
} else {
|
||||
let node = bitcoin.HDNode.fromBase58(this._xpub);
|
||||
let address = node
|
||||
.derive(1)
|
||||
.derive(0)
|
||||
.getAddress();
|
||||
return (this.internal_addresses_cache[index] = address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,34 +89,82 @@ export class HDSegwitP2SHWallet extends AbstractHDWallet {
|
|||
_getExternalAddressByIndex(index) {
|
||||
index = index * 1; // cast to int
|
||||
if (this.external_addresses_cache[index]) return this.external_addresses_cache[index]; // cache hit
|
||||
let mnemonic = this.secret;
|
||||
let seed = bip39.mnemonicToSeed(mnemonic);
|
||||
let root = bitcoin.HDNode.fromSeedBuffer(seed);
|
||||
let path = "m/49'/0'/0'/0/" + index;
|
||||
let child = root.derivePath(path);
|
||||
if (!this._xpub) {
|
||||
let mnemonic = this.secret;
|
||||
let seed = bip39.mnemonicToSeed(mnemonic);
|
||||
let root = bitcoin.HDNode.fromSeedBuffer(seed);
|
||||
let path = "m/49'/0'/0'/0/" + index;
|
||||
let child = root.derivePath(path);
|
||||
|
||||
let keyhash = bitcoin.crypto.hash160(child.getPublicKeyBuffer());
|
||||
let scriptSig = bitcoin.script.witnessPubKeyHash.output.encode(keyhash);
|
||||
let addressBytes = bitcoin.crypto.hash160(scriptSig);
|
||||
let outputScript = bitcoin.script.scriptHash.output.encode(addressBytes);
|
||||
return (this.external_addresses_cache[index] = bitcoin.address.fromOutputScript(outputScript, bitcoin.networks.bitcoin));
|
||||
let keyhash = bitcoin.crypto.hash160(child.getPublicKeyBuffer());
|
||||
let scriptSig = bitcoin.script.witnessPubKeyHash.output.encode(keyhash);
|
||||
let addressBytes = bitcoin.crypto.hash160(scriptSig);
|
||||
let outputScript = bitcoin.script.scriptHash.output.encode(addressBytes);
|
||||
return (this.external_addresses_cache[index] = bitcoin.address.fromOutputScript(outputScript, bitcoin.networks.bitcoin));
|
||||
} else {
|
||||
let b58 = require('bs58check');
|
||||
// eslint-disable-next-line
|
||||
function ypubToXpub(ypub) {
|
||||
var data = b58.decode(ypub);
|
||||
data = data.slice(4);
|
||||
data = Buffer.concat([Buffer.from('0488b21e', 'hex'), data]);
|
||||
return b58.encode(data);
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
function nodeToP2shSegwitAddress(hdNode) {
|
||||
let pubkeyBuf = hdNode.keyPair.getPublicKeyBuffer();
|
||||
let hash = bitcoin.crypto.hash160(pubkeyBuf);
|
||||
let redeemScript = bitcoin.script.witnessPubKeyHash.output.encode(hash);
|
||||
let hash2 = bitcoin.crypto.hash160(redeemScript);
|
||||
let scriptPubkey = bitcoin.script.scriptHash.output.encode(hash2);
|
||||
return bitcoin.address.fromOutputScript(scriptPubkey);
|
||||
}
|
||||
let xpub = ypubToXpub(this._xpub);
|
||||
let hdNode = bitcoin.HDNode.fromBase58(xpub);
|
||||
let address = nodeToP2shSegwitAddress(hdNode.derive(0).derive(index));
|
||||
return (this.external_addresses_cache[index] = address);
|
||||
}
|
||||
}
|
||||
|
||||
_getInternalAddressByIndex(index) {
|
||||
index = index * 1; // cast to int
|
||||
if (this.internal_addresses_cache[index]) return this.internal_addresses_cache[index]; // cache hit
|
||||
let mnemonic = this.secret;
|
||||
let seed = bip39.mnemonicToSeed(mnemonic);
|
||||
let root = bitcoin.HDNode.fromSeedBuffer(seed);
|
||||
if (!this._xpub) {
|
||||
let mnemonic = this.secret;
|
||||
let seed = bip39.mnemonicToSeed(mnemonic);
|
||||
let root = bitcoin.HDNode.fromSeedBuffer(seed);
|
||||
|
||||
let path = "m/49'/0'/0'/1/" + index;
|
||||
let child = root.derivePath(path);
|
||||
let path = "m/49'/0'/0'/1/" + index;
|
||||
let child = root.derivePath(path);
|
||||
|
||||
let keyhash = bitcoin.crypto.hash160(child.getPublicKeyBuffer());
|
||||
let scriptSig = bitcoin.script.witnessPubKeyHash.output.encode(keyhash);
|
||||
let addressBytes = bitcoin.crypto.hash160(scriptSig);
|
||||
let outputScript = bitcoin.script.scriptHash.output.encode(addressBytes);
|
||||
return (this.internal_addresses_cache[index] = bitcoin.address.fromOutputScript(outputScript, bitcoin.networks.bitcoin));
|
||||
let keyhash = bitcoin.crypto.hash160(child.getPublicKeyBuffer());
|
||||
let scriptSig = bitcoin.script.witnessPubKeyHash.output.encode(keyhash);
|
||||
let addressBytes = bitcoin.crypto.hash160(scriptSig);
|
||||
let outputScript = bitcoin.script.scriptHash.output.encode(addressBytes);
|
||||
return (this.internal_addresses_cache[index] = bitcoin.address.fromOutputScript(outputScript, bitcoin.networks.bitcoin));
|
||||
} else {
|
||||
let b58 = require('bs58check');
|
||||
// eslint-disable-next-line
|
||||
function ypubToXpub(ypub) {
|
||||
var data = b58.decode(ypub);
|
||||
data = data.slice(4);
|
||||
data = Buffer.concat([Buffer.from('0488b21e', 'hex'), data]);
|
||||
return b58.encode(data);
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
function nodeToP2shSegwitAddress(hdNode) {
|
||||
let pubkeyBuf = hdNode.keyPair.getPublicKeyBuffer();
|
||||
let hash = bitcoin.crypto.hash160(pubkeyBuf);
|
||||
let redeemScript = bitcoin.script.witnessPubKeyHash.output.encode(hash);
|
||||
let hash2 = bitcoin.crypto.hash160(redeemScript);
|
||||
let scriptPubkey = bitcoin.script.scriptHash.output.encode(hash2);
|
||||
return bitcoin.address.fromOutputScript(scriptPubkey);
|
||||
}
|
||||
let xpub = ypubToXpub(this._xpub);
|
||||
let hdNode = bitcoin.HDNode.fromBase58(xpub);
|
||||
let address = nodeToP2shSegwitAddress(hdNode.derive(1).derive(index));
|
||||
return (this.internal_addresses_cache[index] = address);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -157,8 +205,8 @@ export class HDSegwitP2SHWallet extends AbstractHDWallet {
|
|||
}
|
||||
|
||||
let addresses = this.usedAddresses.join('|');
|
||||
addresses += '|' + this._getExternalAddressByIndex(this.next_free_address_index)
|
||||
addresses += '|' + this._getInternalAddressByIndex(this.next_free_change_address_index)
|
||||
addresses += '|' + this._getExternalAddressByIndex(this.next_free_address_index);
|
||||
addresses += '|' + this._getInternalAddressByIndex(this.next_free_change_address_index);
|
||||
|
||||
const api = new Frisbee({ baseURI: 'https://blockchain.info' });
|
||||
this.transactions = [];
|
||||
|
|
|
@ -256,7 +256,7 @@ export class LegacyWallet extends AbstractWallet {
|
|||
if (tx.block_height && tx.block_height === -1) {
|
||||
// unconfirmed
|
||||
console.log(tx);
|
||||
if ((+new Date(tx.received)) < ((+new Date()) - 3600*24*1000)) {
|
||||
if (+new Date(tx.received) < +new Date() - 3600 * 24 * 1000) {
|
||||
// nop, too old unconfirmed tx - skipping it
|
||||
} else {
|
||||
txsUnconf.push(tx);
|
||||
|
|
Loading…
Add table
Reference in a new issue