diff --git a/lib/bcoin/chaindb.js b/lib/bcoin/chaindb.js index 1a26f7b7..9f028cc5 100644 --- a/lib/bcoin/chaindb.js +++ b/lib/bcoin/chaindb.js @@ -1168,6 +1168,9 @@ ChainDB.prototype.scan = function scan(start, hashes, iter, callback) { if (err) return next(err); + if (!block) + return next(); + self.logger.info('Scanning block %s.', utils.revHex(hash)); utils.forEachSerial(block.txs, function(tx, next) { @@ -1177,7 +1180,7 @@ ChainDB.prototype.scan = function scan(start, hashes, iter, callback) { for (i = 0; i < hashes.length; i++) { hash = hashes[i]; if (hashMap[hash]) - return iter(tx, next); + return iter(tx, block.toHeaders(), next); } next(); diff --git a/lib/bcoin/fullnode.js b/lib/bcoin/fullnode.js index 6f5e99b9..9492eb5f 100644 --- a/lib/bcoin/fullnode.js +++ b/lib/bcoin/fullnode.js @@ -258,26 +258,27 @@ Fullnode.prototype._open = function open(callback) { this.mempool.open.bind(this.mempool), this.miner.open.bind(this.miner), this.pool.open.bind(this.pool), + this.walletdb.open.bind(this.walletdb), function(next) { - self.walletdb.open(function(err) { + self.createWallet(options, function(err, wallet) { if (err) return next(err); - self.createWallet(options, function(err, wallet) { - if (err) - return next(err); + // Set the miner payout address if the + // programmer didn't pass one in. + if (!self.miner.address) + self.miner.address = wallet.getAddress(); - // Set the miner payout address if the - // programmer didn't pass one in. - if (!self.miner.address) - self.miner.address = wallet.getAddress(); + self.wallet = wallet; - self.wallet = wallet; - - next(); - }); + next(); }); }, + function(next) { + // Always rescan to make sure we didn't miss anything: + // there is no atomicity between the chaindb and walletdb. + self.walletdb.rescan(self.chain.db, next); + }, function(next) { var i; self.walletdb.getUnconfirmed(function(err, txs) { diff --git a/lib/bcoin/txdb.js b/lib/bcoin/txdb.js index 247e2740..ec244271 100644 --- a/lib/bcoin/txdb.js +++ b/lib/bcoin/txdb.js @@ -309,6 +309,17 @@ TXDB.prototype.writeGenesis = function writeGenesis(callback) { }); }; +/** + * Get the best block hash. + * @param {Function} callback + */ + +TXDB.prototype.getTip = function getTip(callback) { + this.db.fetch('R', function(data) { + return data.toString('hex'); + }, callback); +}; + /** * Add a block's transactions and write the new best hash. * @param {Block} block diff --git a/lib/bcoin/wallet.js b/lib/bcoin/wallet.js index 1ca85241..37c1924b 100644 --- a/lib/bcoin/wallet.js +++ b/lib/bcoin/wallet.js @@ -1112,71 +1112,6 @@ Wallet.prototype.getRedeem = function getRedeem(hash, callback) { }); }; -/** - * Scan for active accounts and addresses. Used for importing a wallet. - * @param {Function} scanner - Must be a function which accepts - * a {@link Base58Address} as well as a callback and returns - * transactions by address. - * @param {Function} callback - Return [Error, Number] (total number - * of addresses allocated). - */ - -Wallet.prototype.scan = function scan(maxGap, scanner, callback) { - var self = this; - var total = 0; - var index = 0; - var unlock; - - if (typeof maxGap === 'function') { - callback = scanner; - scanner = maxGap; - maxGap = null; - } - - unlock = this.writeLock.lock(scan, [maxGap, scanner, callback]); - - if (!unlock) - return; - - callback = utils.wrap(callback, unlock); - - if (!this.initialized) - return callback(new Error('Wallet is not initialized.')); - - self.start(); - - function done(err, total) { - if (err) { - self.drop(); - return callback(err); - } - self.commit(function(err) { - if (err) - return callback(err); - return callback(null, total); - }); - } - - (function next() { - self.getAccount(index++, function(err, account) { - if (err) - return done(err); - - if (!account) - return done(null, total); - - account.scan(maxGap, scanner, function(err, result) { - if (err) - return done(err); - - total += result; - - next(); - }); - }, true); - })(); -}; - /** * Build input scripts templates for a transaction (does not * sign, only creates signature slots). Only builds scripts @@ -1908,13 +1843,6 @@ Account.fromOptions = function fromOptions(db, options) { Account.MAX_LOOKAHEAD = 5; -/* - * Default address gap for scanning. - * @const {Number} - */ - -Account.MAX_GAP = 20; - /** * Attempt to intialize the account (generating * the first addresses along with the lookahead @@ -2285,102 +2213,6 @@ Account.prototype.setDepth = function setDepth(receiveDepth, changeDepth, callba }); }; -/** - * Scan for addresses. - * @param {Function} scanner - Must be a callback which accepts - * a callback and returns transactions by address. - * @param {Function} callback - Return [Error, Number] (total number - * of addresses allocated). - */ - -Account.prototype.scan = function scan(maxGap, scanner, callback) { - var self = this; - var total = 0; - - if (typeof maxGap === 'function') { - callback = scanner; - scanner = maxGap; - maxGap = null; - } - - if (maxGap == null) - maxGap = Account.MAX_GAP; - - if (!this.initialized) - return callback(new Error('Account is not initialized.')); - - function addTX(txs, calback) { - if (!Array.isArray(txs) || txs.length === 0) - return callback(null, false); - - utils.forEachSerial(txs, function(tx, next) { - self.db.addTX(tx, next); - }, function(err) { - if (err) - return callback(err); - - return callback(null, true); - }); - } - - (function chainCheck(change) { - var depth = change ? self.changeDepth : self.receiveDepth; - var index = 0; - var gap = 0; - - function createAddress(callback) { - if (index === depth) - return self.createAddress(change, callback); - return callback(null, self.deriveAddress(change, index++)); - } - - (function next() { - createAddress(function(err, address) { - if (err) - return callback(err); - - scanner(address.getAddress(), function(err, txs) { - if (err) - return callback(err); - - addTX(txs, function(err, result) { - if (err) - return callback(err); - - // Special case for maxGap=0 - if (maxGap === 0 && index === depth) { - if (!change) - return chainCheck(true); - self.save(); - return callback(null, total); - } - - if (result) { - total++; - gap = 0; - return next(); - } - - if (++gap < Account.MAX_GAP) - return next(); - - if (!change) { - self.receiveDepth = Math.max(depth, self.receiveDepth - gap); - self.receiveAddress = self.deriveReceive(self.receiveDepth - 1); - return chainCheck(true); - } - - self.changeDepth = Math.max(depth, self.changeDepth - gap); - self.changeAddress = self.deriveChange(self.changeDepth - 1); - - return callback(null, total); - }); - }); - }); - })(); - })(false); -}; - /** * Convert the account to a more inspection-friendly object. * @returns {Object} diff --git a/lib/bcoin/walletdb.js b/lib/bcoin/walletdb.js index 1231ebfd..497ea0a5 100644 --- a/lib/bcoin/walletdb.js +++ b/lib/bcoin/walletdb.js @@ -1048,6 +1048,38 @@ WalletDB.prototype.getAddresses = function getAddresses(id, callback) { }, callback); }; +/** + * Rescan the blockchain. + * @param {ChainDB} chaindb + * @param {Function} callback + */ + +WalletDB.prototype.rescan = function rescan(chaindb, callback) { + var self = this; + this.tx.getTip(function(err, hash) { + if (err) + return callback(err); + + if (!hash) + return callback(new Error('Best hash not found.')); + + self.getAddresses(function(err, hashes) { + if (err) + return callback(err); + + self.logger.info('Scanning for %d addresses.', hashes.length); + + chaindb.scan(hash, hashes, function(tx, block, next) { + self.tx.add(tx, function(err) { + if (err) + return next(err); + self.db.put('R', block.hash(), next); + }); + }, callback); + }); + }); +}; + /** * Get the corresponding path for an address hash. * @param {WalletID} id