diff --git a/lib/bcoin/hd.js b/lib/bcoin/hd.js index a7af378f..2be834a8 100644 --- a/lib/bcoin/hd.js +++ b/lib/bcoin/hd.js @@ -171,35 +171,24 @@ function HDPrivateKey(options) { this.isPrivate = true; } -HDPrivateKey.prototype.scan = function scan(options, txByAddress, callback) { +HDPrivateKey.prototype.scan44 = function scan44(options, txByAddress, callback) { + var self = this; var keys = []; - var purpose, coinType, root; - - if (!callback) { - callback = txByAddress; - txByAddress = options; - options = null; - } - - if (!options) - options = {}; - - purpose = options.purpose; - coinType = options.coinType; - - if (purpose == null) - purpose = 44; - - if (coinType == null) - coinType = network.type === 'main' ? 0 : 1; - - assert(utils.isFinite(purpose)); - assert(utils.isFinite(coinType)); + var coinType; // 0. get the root node - root = this - .derive(purpose, true) - .derive(coinType, true); + if (!(this instanceof HDPublicKey)) { + coinType = options.coinType; + + if (coinType == null) + coinType = network.type === 'main' ? 0 : 1; + + assert(utils.isFinite(coinType)); + + root = this + .derive(44, true) + .derive(coinType, true); + } return (function scanner(accountIndex) { var addressIndex = 0; @@ -207,7 +196,9 @@ HDPrivateKey.prototype.scan = function scan(options, txByAddress, callback) { var gap = 0; // 1. derive the first account's node (index = 0) - var account = root.derive(accountIndex, true); + var account = (self instanceof HDPublicKey) + ? self + : root.derive(accountIndex, true); // 2. derive the external chain node of this account var chain = account.derive(0); @@ -252,54 +243,56 @@ HDPrivateKey.prototype.scan = function scan(options, txByAddress, callback) { // 5. if there are some transactions, increase // the account index and go to step 1 + if (self instanceof HDPublicKey) + return callback(null, keys); + return scanner(accountIndex + 1); }); })(); })(0); }; -HDPrivateKey.prototype.deriveBIP44 = function deriveBIP44(options) { - var purpose = options.purpose; +HDPrivateKey.prototype.deriveRoot44 = function deriveRoot44(options) { var coinType = options.coinType; var accountIndex = options.accountIndex; - var chain = options.chain; - var addressIndex = options.addressIndex; - var child; - if (purpose == null) - purpose = 44; + if (this instanceof HDPublicKey) + return this; if (coinType == null) coinType = network.type === 'main' ? 0 : 1; + assert(utils.isFinite(coinType)); + assert(utils.isFinite(accountIndex)); + + return this + .derive(44, true) + .derive(coinType, true) + .derive(accountIndex, true); +}; + +HDPrivateKey.prototype.deriveBIP44 = function deriveBIP44(options, isPublic) { + var chain = options.chain; + var addressIndex = options.addressIndex; + if (chain == null) chain = options.change ? 1 : 0; - assert(utils.isFinite(purpose)); - assert(utils.isFinite(coinType)); - assert(utils.isFinite(accountIndex)); assert(utils.isFinite(chain)); assert(utils.isFinite(addressIndex)); - child = this - .derive(purpose, true) - .derive(coinType, true) - .derive(accountIndex, true) + return this + .deriveRoot44(options) .derive(chain) .derive(addressIndex); - - return child; -}; - -HDPrivateKey.prototype.deriveAccount = function deriveAccount(accountIndex) { - return this.deriveBIP44({ - accountIndex: accountIndex, - chain: 0, - addressIndex: 0 - }); }; HDPrivateKey.prototype.deriveChange = function deriveChange(accountIndex, addressIndex) { + if (this instanceof HDPublicKey) { + addressIndex = accountIndex; + accountIndex = null; + } + return this.deriveBIP44({ accountIndex: accountIndex, chain: 1, @@ -308,6 +301,11 @@ HDPrivateKey.prototype.deriveChange = function deriveChange(accountIndex, addres }; HDPrivateKey.prototype.deriveAddress = function deriveAddress(accountIndex, addressIndex) { + if (this instanceof HDPublicKey) { + addressIndex = accountIndex; + accountIndex = null; + } + return this.deriveBIP44({ accountIndex: accountIndex, chain: 0, @@ -315,6 +313,106 @@ HDPrivateKey.prototype.deriveAddress = function deriveAddress(accountIndex, addr }); }; +HDPrivateKey.prototype.scan45 = function scan45(options, txByAddress, callback) { + var keys = []; + var root; + + root = this.deriveRoot45(options); + + return (function chainCheck(chainConstant) { + return (function scanner(cosignerIndex) { + var addressIndex = 0; + var total = 0; + var gap = 0; + + var cosigner = root.derive(cosignerIndex); + var chain = cosigner.derive(chainConstant); + + return (function next() { + var address = chain.derive(addressIndex++); + var addr = bcoin.address.hash2addr( + bcoin.address.key2hash(address.publicKey), + 'pubkey'); + + return txByAddress(addr, function(err, txs) { + var result; + + if (err) + return callback(err); + + if (txs) { + if (typeof txs === 'boolean') + result = txs; + else if (Array.isArray(txs)) + result = txs.length > 0; + else + result = false; + } + + if (result) { + keys.push(address); + total++; + gap = 0; + return next(); + } + + if (++gap < 20) + return next(); + + if (total === 0) { + if (chainConstant === 0) + return chainCheck(1); + return callback(null, keys); + } + + return scanner(accountIndex + 1); + }); + })(); + })(0); + })(0); +}; + +HDPrivateKey.prototype.deriveRoot45 = function deriveRoot45(options) { + if (this instanceof HDPublicKey) + return this; + return this.derive(45, true); +}; + +HDPrivateKey.prototype.deriveBIP45 = function deriveBIP45(options) { + var cosignerIndex = options.cosignerIndex; + var chain = options.chain; + var addressIndex = options.addressIndex; + + if (chain == null) + chain = options.change ? 1 : 0; + + assert(utils.isFinite(cosignerIndex)); + assert(utils.isFinite(chain)); + assert(utils.isFinite(addressIndex)); + + return this + .deriveRoot45(options) + .derive(cosignerIndex) + .derive(chain) + .derive(addressIndex); +}; + +HDPrivateKey.prototype.deriveCosignerChange = function deriveCosignerChange(cosignerIndex, addressIndex) { + return this.deriveBIP45({ + cosignerIndex: cosignerIndex, + chain: 1, + addressIndex: addressIndex + }); +}; + +HDPrivateKey.prototype.deriveCosignerAddress = function deriveCosignerAddress(cosignerIndex, addressIndex) { + return this.deriveBIP45({ + cosignerIndex: cosignerIndex, + chain: 0, + addressIndex: addressIndex + }); +}; + HDPrivateKey.getPath = function getPath(options) { var purpose, coinType, accountIndex, chain, addressIndex; @@ -666,11 +764,18 @@ function HDPublicKey(options) { this.isPublic = true; } +HDPublicKey.prototype.scan44 = HDPrivateKey.prototype.scan44; +HDPublicKey.prototype.deriveRoot44 = HDPrivateKey.prototype.deriveRoot44; HDPublicKey.prototype.deriveBIP44 = HDPrivateKey.prototype.deriveBIP44; -HDPublicKey.prototype.deriveAccount = HDPrivateKey.prototype.deriveAccount; HDPublicKey.prototype.deriveChange = HDPrivateKey.prototype.deriveChange; HDPublicKey.prototype.deriveAddress = HDPrivateKey.prototype.deriveAddress; +HDPublicKey.prototype.scan45 = HDPrivateKey.prototype.scan45; +HDPublicKey.prototype.deriveRoot45 = HDPrivateKey.prototype.deriveRoot45; +HDPublicKey.prototype.deriveBIP45 = HDPrivateKey.prototype.deriveBIP45; +HDPublicKey.prototype.deriveCosignerChange = HDPrivateKey.prototype.deriveCosignerChange; +HDPublicKey.prototype.deriveCosignerAddress = HDPrivateKey.prototype.deriveCosignerAddress; + HDPublicKey.isExtended = function isExtended(data) { if (typeof data !== 'string') return false;