add hd scanning to wallet.

This commit is contained in:
Christopher Jeffrey 2016-02-13 15:44:15 -08:00
parent 239902b752
commit 2a5c35cf05
2 changed files with 172 additions and 1 deletions

View File

@ -268,6 +268,8 @@ HDPrivateKey.prototype.scan44 = function scan44(options, txByAddress, callback)
if (total === 0) {
if (chainConstant === 0)
return chainCheck(1);
if (isAccount)
return callback(null, accounts[accountIndex]);
return callback(null, accounts);
}
@ -276,7 +278,7 @@ HDPrivateKey.prototype.scan44 = function scan44(options, txByAddress, callback)
if (isAccount) {
if (chainConstant === 0)
return chainCheck(1);
return callback(null, accounts[0]);
return callback(null, accounts[accountIndex]);
}
return scanner(accountIndex + 1);

View File

@ -664,6 +664,175 @@ Wallet.prototype.syncOutputDepth = function syncOutputDepth(tx) {
this.setReceiveDepth(depth.receive + 1);
};
Wallet.prototype.scan = function scan(txByAddress, callback) {
function done(err, depths) {
if (err)
return callback(err);
if (depths.changeDepth >= this.changeDepth)
this.setChangeDepth(depths.changeDepth + 1);
if (depths.receiveDepth >= this.receiveDepth)
this.setReceiveDepth(depths.receiveDepth + 1);
}
if (this.derivation === 'bip44')
return this._scan44({ current: true }, txByAddress, done);
if (this.derivation === 'bip45')
return this._scan45({}, txByAddress, done);
};
Wallet.prototype._scan44 = function scan44(options, txByAddress, callback) {
var self = this;
var accounts = [];
var isAccount = this.master.isAccount44();
var _accountKey = this.accountKey;
assert(this._initialized);
return (function chainCheck(change) {
return (function scanner(accountIndex) {
var addressIndex = 0;
var total = 0;
var gap = 0;
if (options.current)
accountIndex = self.accountIndex;
// 1. derive the first account's node (index = 0)
self.accountKey = isAccount
? self.master
: self.master.deriveAccount44(accountIndex);
if (isAccount)
accountIndex = new bn(self.master.childIndex).toNumber() - constants.hd.hardened;
// 2. derive the external chain node of this account
// 3. scan addresses of the external chain;
// respect the gap limit described below
return (function next() {
var address = self.deriveAddress(change, addressIndex++);
var addr = address.getAddress();
return txByAddress(addr, function(err, txs) {
var result;
if (err) {
self.accountKey = _accountKey;
return callback(err);
}
if (txs) {
if (typeof txs === 'boolean')
result = txs;
else if (typeof txs === 'number')
result = txs > 0;
else if (Array.isArray(txs))
result = txs.length > 0;
else
result = false;
}
if (result) {
total++;
gap = 0;
return next();
}
if (++gap < 20)
return next();
assert(accounts[accountIndex] == null || change === true);
if (change === false)
accounts[accountIndex] = { receiveDepth: addressIndex };
else
accounts[accountIndex].changeDepth = addressIndex;
// 4. if no transactions are found on the
// external chain, stop discovery
if (total === 0) {
if (change === false)
return chainCheck(true);
self.accountKey = _accountKey;
if (isAccount || options.current)
return callback(null, accounts[accountIndex]);
return callback(null, accounts);
}
// 5. if there are some transactions, increase
// the account index and go to step 1
if (isAccount || options.current) {
if (change === false)
return chainCheck(true);
self.accountKey = _accountKey;
return callback(null, accounts[accountIndex]);
}
return scanner(accountIndex + 1);
});
})();
})(0);
})(false);
};
Wallet.prototype._scan45 = function scan45(options, txByAddress, callback) {
var depths = { changeDepth: 0, receiveDepth: 0 };
assert(this._initialized);
return (function chainCheck(change) {
var addressIndex = 0;
var total = 0;
var gap = 0;
return (function next() {
var address = self.deriveAddress(change, addressIndex++);
var addr = address.getAddress();
return txByAddress(addr, function(err, txs) {
var result;
if (err)
return callback(err);
if (txs) {
if (typeof txs === 'boolean')
result = txs;
else if (typeof txs === 'number')
result = txs > 0;
else if (Array.isArray(txs))
result = txs.length > 0;
else
result = false;
}
if (result) {
total++;
gap = 0;
return next();
}
if (++gap < 20)
return next();
assert(depths.receiveDepth === 0 || change === true);
if (change === false)
depths.receiveDepth = addressIndex;
else
depths.changeDepth = addressIndex;
if (change === false)
return chainCheck(true);
return callback(null, depths);
});
})();
})(false);
};
Wallet.prototype.scriptInputs = function scriptInputs(tx, index) {
this.fillPrevout(tx);
var addresses = this.deriveInputs(tx);