ensure full atomicity in wallet.
This commit is contained in:
parent
850b16fa7d
commit
196f3ca861
@ -96,7 +96,7 @@ TXDB.prototype._lock = function _lock(func, args, force) {
|
|||||||
* @param {Function} callback
|
* @param {Function} callback
|
||||||
*/
|
*/
|
||||||
|
|
||||||
TXDB.prototype._loadFilter = function loadFilter(callback) {
|
TXDB.prototype.loadFilter = function loadFilter(callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
if (!this.filter)
|
if (!this.filter)
|
||||||
@ -119,7 +119,7 @@ TXDB.prototype._loadFilter = function loadFilter(callback) {
|
|||||||
* @returns {Boolean}
|
* @returns {Boolean}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
TXDB.prototype._testFilter = function _testFilter(addresses) {
|
TXDB.prototype.testFilter = function testFilter(addresses) {
|
||||||
var i;
|
var i;
|
||||||
|
|
||||||
if (!this.filter)
|
if (!this.filter)
|
||||||
@ -143,7 +143,7 @@ TXDB.prototype.getMap = function getMap(tx, callback) {
|
|||||||
var addresses = tx.getHashes('hex');
|
var addresses = tx.getHashes('hex');
|
||||||
var map;
|
var map;
|
||||||
|
|
||||||
if (!this._testFilter(addresses))
|
if (!this.testFilter(addresses))
|
||||||
return callback();
|
return callback();
|
||||||
|
|
||||||
this.mapAddresses(addresses, function(err, table) {
|
this.mapAddresses(addresses, function(err, table) {
|
||||||
@ -281,6 +281,34 @@ TXDB.prototype._getOrphans = function _getOrphans(key, callback) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the genesis block as the best hash.
|
||||||
|
* @param {Function} callback
|
||||||
|
*/
|
||||||
|
|
||||||
|
TXDB.prototype.writeGenesis = function writeGenesis(callback) {
|
||||||
|
var self = this;
|
||||||
|
var unlock, hash;
|
||||||
|
|
||||||
|
unlock = this._lock(writeGenesis, [callback]);
|
||||||
|
|
||||||
|
if (!unlock)
|
||||||
|
return;
|
||||||
|
|
||||||
|
callback = utils.wrap(callback, unlock);
|
||||||
|
|
||||||
|
self.db.has('R', function(err, result) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
return callback();
|
||||||
|
|
||||||
|
hash = new Buffer(self.network.genesis.hash, 'hex');
|
||||||
|
self.db.put('R', hash, callback);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a block's transactions and write the new best hash.
|
* Add a block's transactions and write the new best hash.
|
||||||
* @param {Block} block
|
* @param {Block} block
|
||||||
|
|||||||
@ -50,7 +50,8 @@ function Wallet(db, options) {
|
|||||||
|
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.network = db.network;
|
this.network = db.network;
|
||||||
this.locker = new bcoin.locker(this);
|
this.writeLock = new bcoin.locker(this);
|
||||||
|
this.fillLock = new bcoin.locker(this);
|
||||||
|
|
||||||
this.id = null;
|
this.id = null;
|
||||||
this.master = null;
|
this.master = null;
|
||||||
@ -220,6 +221,7 @@ Wallet.prototype.destroy = function destroy(callback) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Wallet.prototype.addKey = function addKey(account, key, callback) {
|
Wallet.prototype.addKey = function addKey(account, key, callback) {
|
||||||
|
var self = this;
|
||||||
var unlock;
|
var unlock;
|
||||||
|
|
||||||
if (typeof key === 'function') {
|
if (typeof key === 'function') {
|
||||||
@ -228,7 +230,7 @@ Wallet.prototype.addKey = function addKey(account, key, callback) {
|
|||||||
account = 0;
|
account = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
unlock = this.locker.lock(addKey, [account, key, callback]);
|
unlock = this.writeLock.lock(addKey, [account, key, callback]);
|
||||||
|
|
||||||
if (!unlock)
|
if (!unlock)
|
||||||
return;
|
return;
|
||||||
@ -242,7 +244,19 @@ Wallet.prototype.addKey = function addKey(account, key, callback) {
|
|||||||
if (!account)
|
if (!account)
|
||||||
return callback(new Error('Account not found.'));
|
return callback(new Error('Account not found.'));
|
||||||
|
|
||||||
account.addKey(key, callback);
|
self.start();
|
||||||
|
|
||||||
|
account.addKey(key, function(err, result) {
|
||||||
|
if (err) {
|
||||||
|
self.drop();
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
self.commit(function(err) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
return callback(null, result);
|
||||||
|
});
|
||||||
|
});
|
||||||
}, true);
|
}, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -254,6 +268,7 @@ Wallet.prototype.addKey = function addKey(account, key, callback) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Wallet.prototype.removeKey = function removeKey(account, key, callback) {
|
Wallet.prototype.removeKey = function removeKey(account, key, callback) {
|
||||||
|
var self = this;
|
||||||
var unlock;
|
var unlock;
|
||||||
|
|
||||||
if (typeof key === 'function') {
|
if (typeof key === 'function') {
|
||||||
@ -262,7 +277,7 @@ Wallet.prototype.removeKey = function removeKey(account, key, callback) {
|
|||||||
account = 0;
|
account = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
unlock = this.locker.lock(removeKey, [account, key, callback]);
|
unlock = this.writeLock.lock(removeKey, [account, key, callback]);
|
||||||
|
|
||||||
if (!unlock)
|
if (!unlock)
|
||||||
return;
|
return;
|
||||||
@ -276,7 +291,19 @@ Wallet.prototype.removeKey = function removeKey(account, key, callback) {
|
|||||||
if (!account)
|
if (!account)
|
||||||
return callback(new Error('Account not found.'));
|
return callback(new Error('Account not found.'));
|
||||||
|
|
||||||
account.removeKey(key, callback);
|
self.start();
|
||||||
|
|
||||||
|
account.removeKey(key, function(err, result) {
|
||||||
|
if (err) {
|
||||||
|
self.drop();
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
self.commit(function(err) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
return callback(null, result);
|
||||||
|
});
|
||||||
|
});
|
||||||
}, true);
|
}, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -297,7 +324,7 @@ Wallet.prototype.setPassphrase = function setPassphrase(old, new_, callback) {
|
|||||||
old = null;
|
old = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
unlock = this.locker.lock(setPassphrase, [old, new_, callback]);
|
unlock = this.writeLock.lock(setPassphrase, [old, new_, callback]);
|
||||||
|
|
||||||
if (!unlock)
|
if (!unlock)
|
||||||
return;
|
return;
|
||||||
@ -312,7 +339,9 @@ Wallet.prototype.setPassphrase = function setPassphrase(old, new_, callback) {
|
|||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
return self.save(callback);
|
self.start();
|
||||||
|
self.save();
|
||||||
|
self.commit(callback);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -332,7 +361,7 @@ Wallet.prototype.retoken = function retoken(passphrase, callback) {
|
|||||||
passphrase = null;
|
passphrase = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
unlock = this.locker.lock(retoken, [passphrase, callback]);
|
unlock = this.writeLock.lock(retoken, [passphrase, callback]);
|
||||||
|
|
||||||
if (!unlock)
|
if (!unlock)
|
||||||
return;
|
return;
|
||||||
@ -346,7 +375,9 @@ Wallet.prototype.retoken = function retoken(passphrase, callback) {
|
|||||||
self.tokenDepth++;
|
self.tokenDepth++;
|
||||||
self.token = self.getToken(master, self.tokenDepth);
|
self.token = self.getToken(master, self.tokenDepth);
|
||||||
|
|
||||||
self.save(function(err) {
|
self.start();
|
||||||
|
self.save();
|
||||||
|
self.commit(function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
return callback(null, self.token);
|
return callback(null, self.token);
|
||||||
@ -431,7 +462,7 @@ Wallet.prototype.createAccount = function createAccount(options, callback, force
|
|||||||
var self = this;
|
var self = this;
|
||||||
var key, unlock;
|
var key, unlock;
|
||||||
|
|
||||||
unlock = this.locker.lock(createAccount, [options, callback], force);
|
unlock = this.writeLock.lock(createAccount, [options, callback], force);
|
||||||
|
|
||||||
if (!unlock)
|
if (!unlock)
|
||||||
return;
|
return;
|
||||||
@ -457,13 +488,17 @@ Wallet.prototype.createAccount = function createAccount(options, callback, force
|
|||||||
n: options.n
|
n: options.n
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.start();
|
||||||
|
|
||||||
self.db.createAccount(options, function(err, account) {
|
self.db.createAccount(options, function(err, account) {
|
||||||
if (err)
|
if (err) {
|
||||||
|
self.drop();
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
self.accountDepth++;
|
self.accountDepth++;
|
||||||
|
self.save();
|
||||||
self.save(function(err) {
|
self.commit(function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
return callback(null, account);
|
return callback(null, account);
|
||||||
@ -487,14 +522,7 @@ Wallet.prototype.getAccounts = function getAccounts(callback) {
|
|||||||
* @param {Function} callback - Returns [Error, {@link Account}].
|
* @param {Function} callback - Returns [Error, {@link Account}].
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Wallet.prototype.getAccount = function getAccount(account, callback, force) {
|
Wallet.prototype.getAccount = function getAccount(account, callback) {
|
||||||
var unlock = this.locker.lock(getAccount, [account, callback], force);
|
|
||||||
|
|
||||||
if (!unlock)
|
|
||||||
return;
|
|
||||||
|
|
||||||
callback = utils.wrap(callback, unlock);
|
|
||||||
|
|
||||||
if (this.account) {
|
if (this.account) {
|
||||||
if (account === 0 || account === 'default')
|
if (account === 0 || account === 'default')
|
||||||
return callback(null, this.account);
|
return callback(null, this.account);
|
||||||
@ -547,7 +575,7 @@ Wallet.prototype.createAddress = function createAddress(account, change, callbac
|
|||||||
account = 0;
|
account = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
unlock = this.locker.lock(createAddress, [account, change, callback]);
|
unlock = this.writeLock.lock(createAddress, [account, change, callback]);
|
||||||
|
|
||||||
if (!unlock)
|
if (!unlock)
|
||||||
return;
|
return;
|
||||||
@ -561,7 +589,19 @@ Wallet.prototype.createAddress = function createAddress(account, change, callbac
|
|||||||
if (!account)
|
if (!account)
|
||||||
return callback(new Error('Account not found.'));
|
return callback(new Error('Account not found.'));
|
||||||
|
|
||||||
account.createAddress(change, callback);
|
self.start();
|
||||||
|
|
||||||
|
account.createAddress(change, function(err, result) {
|
||||||
|
if (err) {
|
||||||
|
self.drop();
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
self.commit(function(err) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
return callback(null, result);
|
||||||
|
});
|
||||||
|
});
|
||||||
}, true);
|
}, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -571,8 +611,35 @@ Wallet.prototype.createAddress = function createAddress(account, change, callbac
|
|||||||
* @param {Function} callback
|
* @param {Function} callback
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Wallet.prototype.save = function save(callback) {
|
Wallet.prototype.save = function save() {
|
||||||
return this.db.save(this, callback);
|
return this.db.save(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start batch.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Wallet.prototype.start = function start() {
|
||||||
|
return this.db.start(this.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drop batch.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Wallet.prototype.drop = function drop() {
|
||||||
|
return this.db.drop(this.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save batch.
|
||||||
|
* @param {Function} callback
|
||||||
|
*/
|
||||||
|
|
||||||
|
Wallet.prototype.commit = function commit(callback) {
|
||||||
|
return this.db.commit(this.id, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -620,7 +687,7 @@ Wallet.prototype.getPath = function getPath(address, callback) {
|
|||||||
|
|
||||||
Wallet.prototype.fill = function fill(tx, options, callback) {
|
Wallet.prototype.fill = function fill(tx, options, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var rate;
|
var unlock, rate;
|
||||||
|
|
||||||
if (typeof options === 'function') {
|
if (typeof options === 'function') {
|
||||||
callback = options;
|
callback = options;
|
||||||
@ -630,6 +697,15 @@ Wallet.prototype.fill = function fill(tx, options, callback) {
|
|||||||
if (!options)
|
if (!options)
|
||||||
options = {};
|
options = {};
|
||||||
|
|
||||||
|
// We use a lock here to ensure we
|
||||||
|
// don't end up double spending coins.
|
||||||
|
unlock = this.fillLock.lock(fill, [tx, options, callback]);
|
||||||
|
|
||||||
|
if (!unlock)
|
||||||
|
return;
|
||||||
|
|
||||||
|
callback = utils.wrap(callback, unlock);
|
||||||
|
|
||||||
if (!this.initialized)
|
if (!this.initialized)
|
||||||
return callback(new Error('Wallet is not initialized.'));
|
return callback(new Error('Wallet is not initialized.'));
|
||||||
|
|
||||||
@ -920,7 +996,7 @@ Wallet.prototype.syncOutputDepth = function syncOutputDepth(tx, callback) {
|
|||||||
var receive = [];
|
var receive = [];
|
||||||
var i, path, unlock;
|
var i, path, unlock;
|
||||||
|
|
||||||
unlock = this.locker.lock(syncOutputDepth, [tx, callback]);
|
unlock = this.writeLock.lock(syncOutputDepth, [tx, callback]);
|
||||||
|
|
||||||
if (!unlock)
|
if (!unlock)
|
||||||
return;
|
return;
|
||||||
@ -940,6 +1016,8 @@ Wallet.prototype.syncOutputDepth = function syncOutputDepth(tx, callback) {
|
|||||||
accounts[path.account].push(path);
|
accounts[path.account].push(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.start();
|
||||||
|
|
||||||
utils.forEachSerial(Object.keys(accounts), function(index, next) {
|
utils.forEachSerial(Object.keys(accounts), function(index, next) {
|
||||||
var paths = accounts[index];
|
var paths = accounts[index];
|
||||||
var receiveDepth = -1;
|
var receiveDepth = -1;
|
||||||
@ -979,12 +1057,18 @@ Wallet.prototype.syncOutputDepth = function syncOutputDepth(tx, callback) {
|
|||||||
|
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
}, true);
|
});
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
if (err)
|
if (err) {
|
||||||
|
self.drop();
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
return callback(null, receive, change);
|
self.commit(function(err) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
return callback(null, receive, change);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -1049,7 +1133,7 @@ Wallet.prototype.scan = function scan(maxGap, scanner, callback) {
|
|||||||
maxGap = null;
|
maxGap = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
unlock = this.locker.lock(scan, [maxGap, scanner, callback]);
|
unlock = this.writeLock.lock(scan, [maxGap, scanner, callback]);
|
||||||
|
|
||||||
if (!unlock)
|
if (!unlock)
|
||||||
return;
|
return;
|
||||||
@ -1059,17 +1143,31 @@ Wallet.prototype.scan = function scan(maxGap, scanner, callback) {
|
|||||||
if (!this.initialized)
|
if (!this.initialized)
|
||||||
return callback(new Error('Wallet is not 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() {
|
(function next() {
|
||||||
self.getAccount(index++, function(err, account) {
|
self.getAccount(index++, function(err, account) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return done(err);
|
||||||
|
|
||||||
if (!account)
|
if (!account)
|
||||||
return callback(null, total);
|
return done(null, total);
|
||||||
|
|
||||||
account.scan(maxGap, scanner, function(err, result) {
|
account.scan(maxGap, scanner, function(err, result) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return done(err);
|
||||||
|
|
||||||
total += result;
|
total += result;
|
||||||
|
|
||||||
@ -1829,7 +1927,8 @@ Account.prototype.init = function init(callback) {
|
|||||||
// Waiting for more keys.
|
// Waiting for more keys.
|
||||||
if (this.keys.length !== this.n) {
|
if (this.keys.length !== this.n) {
|
||||||
assert(!this.initialized);
|
assert(!this.initialized);
|
||||||
return this.save(callback);
|
this.save();
|
||||||
|
return callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(this.receiveDepth === 0);
|
assert(this.receiveDepth === 0);
|
||||||
@ -1870,14 +1969,6 @@ Account.prototype.pushKey = function pushKey(key) {
|
|||||||
|
|
||||||
assert(key, 'Key required.');
|
assert(key, 'Key required.');
|
||||||
|
|
||||||
if (Array.isArray(key)) {
|
|
||||||
for (i = 0; i < key.length; i++) {
|
|
||||||
if (this.pushKey(key[i]))
|
|
||||||
result = true;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key.accountKey)
|
if (key.accountKey)
|
||||||
key = key.accountKey;
|
key = key.accountKey;
|
||||||
|
|
||||||
@ -1924,14 +2015,6 @@ Account.prototype.spliceKey = function spliceKey(key) {
|
|||||||
var index = -1;
|
var index = -1;
|
||||||
var i;
|
var i;
|
||||||
|
|
||||||
if (Array.isArray(key)) {
|
|
||||||
for (i = 0; i < key.length; i++) {
|
|
||||||
if (this.spliceKey(key[i]))
|
|
||||||
result = true;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(key, 'Key required.');
|
assert(key, 'Key required.');
|
||||||
|
|
||||||
if (key.accountKey)
|
if (key.accountKey)
|
||||||
@ -1976,12 +2059,11 @@ Account.prototype.spliceKey = function spliceKey(key) {
|
|||||||
|
|
||||||
Account.prototype.addKey = function addKey(key, callback) {
|
Account.prototype.addKey = function addKey(key, callback) {
|
||||||
var result = false;
|
var result = false;
|
||||||
var error;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
result = this.pushKey(key);
|
result = this.pushKey(key);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e;
|
return callback(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to initialize again.
|
// Try to initialize again.
|
||||||
@ -1989,9 +2071,6 @@ Account.prototype.addKey = function addKey(key, callback) {
|
|||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
if (error)
|
|
||||||
return callback(error);
|
|
||||||
|
|
||||||
return callback(null, result);
|
return callback(null, result);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -2005,23 +2084,16 @@ Account.prototype.addKey = function addKey(key, callback) {
|
|||||||
|
|
||||||
Account.prototype.removeKey = function removeKey(key, callback) {
|
Account.prototype.removeKey = function removeKey(key, callback) {
|
||||||
var result = false;
|
var result = false;
|
||||||
var error;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
result = this.spliceKey(key);
|
result = this.spliceKey(key);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e;
|
return callback(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.save(function(err) {
|
this.save();
|
||||||
if (err)
|
|
||||||
return callback(err);
|
|
||||||
|
|
||||||
if (error)
|
return callback(null, result);
|
||||||
return callback(error);
|
|
||||||
|
|
||||||
return callback(null, result);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2076,11 +2148,9 @@ Account.prototype.createAddress = function createAddress(change, callback) {
|
|||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
self.save(function(err) {
|
self.save();
|
||||||
if (err)
|
|
||||||
return callback(err);
|
return callback(null, address);
|
||||||
return callback(null, address);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2149,8 +2219,8 @@ Account.prototype.deriveAddress = function deriveAddress(change, index) {
|
|||||||
* @param {Function} callback
|
* @param {Function} callback
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Account.prototype.save = function save(callback) {
|
Account.prototype.save = function save() {
|
||||||
return this.db.saveAccount(this, callback);
|
return this.db.saveAccount(this);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2209,12 +2279,9 @@ Account.prototype.setDepth = function setDepth(receiveDepth, changeDepth, callba
|
|||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
self.save(function(err) {
|
self.save();
|
||||||
if (err)
|
|
||||||
return callback(err);
|
|
||||||
|
|
||||||
return callback(null, receive, change);
|
return callback(null, receive, change);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2284,6 +2351,7 @@ Account.prototype.scan = function scan(maxGap, scanner, callback) {
|
|||||||
if (maxGap === 0 && index === depth) {
|
if (maxGap === 0 && index === depth) {
|
||||||
if (!change)
|
if (!change)
|
||||||
return chainCheck(true);
|
return chainCheck(true);
|
||||||
|
self.save();
|
||||||
return callback(null, total);
|
return callback(null, total);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2305,11 +2373,7 @@ Account.prototype.scan = function scan(maxGap, scanner, callback) {
|
|||||||
self.changeDepth = Math.max(depth, self.changeDepth - gap);
|
self.changeDepth = Math.max(depth, self.changeDepth - gap);
|
||||||
self.changeAddress = self.deriveChange(self.changeDepth - 1);
|
self.changeAddress = self.deriveChange(self.changeDepth - 1);
|
||||||
|
|
||||||
self.save(function(err) {
|
return callback(null, total);
|
||||||
if (err)
|
|
||||||
return callback(err);
|
|
||||||
return callback(null, total);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -50,13 +50,14 @@ function WalletDB(options) {
|
|||||||
this.network = bcoin.network.get(options.network);
|
this.network = bcoin.network.get(options.network);
|
||||||
this.fees = options.fees;
|
this.fees = options.fees;
|
||||||
this.logger = options.logger || bcoin.defaultLogger;
|
this.logger = options.logger || bcoin.defaultLogger;
|
||||||
|
this.batches = {};
|
||||||
|
|
||||||
// We need one read lock for `get` and `create`.
|
// We need one read lock for `get` and `create`.
|
||||||
// It will hold locks specific to wallet ids.
|
// It will hold locks specific to wallet ids.
|
||||||
this.readLock = new ReadLock(this);
|
this.readLock = new ReadLock(this);
|
||||||
|
|
||||||
this.accountCache = new bcoin.lru(10000, 1);
|
|
||||||
this.walletCache = new bcoin.lru(10000, 1);
|
this.walletCache = new bcoin.lru(10000, 1);
|
||||||
|
this.accountCache = new bcoin.lru(10000, 1);
|
||||||
this.pathCache = new bcoin.lru(100000, 1);
|
this.pathCache = new bcoin.lru(100000, 1);
|
||||||
|
|
||||||
this.db = bcoin.ldb({
|
this.db = bcoin.ldb({
|
||||||
@ -139,7 +140,12 @@ WalletDB.prototype._open = function open(callback) {
|
|||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
self.tx._loadFilter(callback);
|
self.tx.writeGenesis(function(err) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
self.tx.loadFilter(callback);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -176,6 +182,58 @@ WalletDB.prototype._lock = function lock(id, func, args, force) {
|
|||||||
return this.readLock.lock(id, func, args, force);
|
return this.readLock.lock(id, func, args, force);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start batch.
|
||||||
|
* @private
|
||||||
|
* @param {WalletID} id
|
||||||
|
*/
|
||||||
|
|
||||||
|
WalletDB.prototype.start = function start(id) {
|
||||||
|
assert(utils.isAlpha(id), 'Bad ID for batch.');
|
||||||
|
assert(!this.batches[id], 'Batch already started.');
|
||||||
|
this.batches[id] = this.db.batch();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drop batch.
|
||||||
|
* @private
|
||||||
|
* @param {WalletID} id
|
||||||
|
*/
|
||||||
|
|
||||||
|
WalletDB.prototype.drop = function drop(id) {
|
||||||
|
var batch = this.batch(id);
|
||||||
|
batch.clear();
|
||||||
|
delete this.batches[id];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get batch.
|
||||||
|
* @private
|
||||||
|
* @param {WalletID} id
|
||||||
|
* @returns {Leveldown.Batch}
|
||||||
|
*/
|
||||||
|
|
||||||
|
WalletDB.prototype.batch = function batch(id) {
|
||||||
|
var batch;
|
||||||
|
assert(utils.isAlpha(id), 'Bad ID for batch.');
|
||||||
|
batch = this.batches[id];
|
||||||
|
assert(batch, 'Batch does not exist.');
|
||||||
|
return batch;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save batch.
|
||||||
|
* @private
|
||||||
|
* @param {WalletID} id
|
||||||
|
* @param {Function} callback
|
||||||
|
*/
|
||||||
|
|
||||||
|
WalletDB.prototype.commit = function commit(id, callback) {
|
||||||
|
var batch = this.batch(id);
|
||||||
|
delete this.batches[id];
|
||||||
|
batch.write(callback);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emit balance events after a tx is saved.
|
* Emit balance events after a tx is saved.
|
||||||
* @private
|
* @private
|
||||||
@ -495,13 +553,10 @@ WalletDB.prototype._get = function get(id, callback) {
|
|||||||
* @param {Function} callback
|
* @param {Function} callback
|
||||||
*/
|
*/
|
||||||
|
|
||||||
WalletDB.prototype.save = function save(wallet, callback) {
|
WalletDB.prototype.save = function save(wallet) {
|
||||||
if (!utils.isAlpha(wallet.id))
|
var batch = this.batch(wallet.id);
|
||||||
return callback(new Error('Wallet IDs must be alphanumeric.'));
|
|
||||||
|
|
||||||
this.walletCache.set(wallet.id, wallet);
|
this.walletCache.set(wallet.id, wallet);
|
||||||
|
batch.put('w/' + wallet.id, wallet.toRaw());
|
||||||
this.db.put('w/' + wallet.id, wallet.toRaw(), callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -770,25 +825,17 @@ WalletDB.prototype.getAccountIndex = function getAccountIndex(id, name, callback
|
|||||||
* @param {Function} callback
|
* @param {Function} callback
|
||||||
*/
|
*/
|
||||||
|
|
||||||
WalletDB.prototype.saveAccount = function saveAccount(account, callback) {
|
WalletDB.prototype.saveAccount = function saveAccount(account) {
|
||||||
var index, key, batch;
|
var batch = this.batch(account.id);
|
||||||
|
var index = new Buffer(4);
|
||||||
|
var key = account.id + '/' + account.accountIndex;
|
||||||
|
|
||||||
if (!utils.isAlpha(account.name))
|
|
||||||
return callback(new Error('Account names must be alphanumeric.'));
|
|
||||||
|
|
||||||
batch = this.db.batch();
|
|
||||||
|
|
||||||
index = new Buffer(4);
|
|
||||||
index.writeUInt32LE(account.accountIndex, 0, true);
|
index.writeUInt32LE(account.accountIndex, 0, true);
|
||||||
|
|
||||||
key = account.id + '/' + account.accountIndex;
|
|
||||||
|
|
||||||
batch.put('a/' + key, account.toRaw());
|
batch.put('a/' + key, account.toRaw());
|
||||||
batch.put('i/' + account.id + '/' + account.name, index);
|
batch.put('i/' + account.id + '/' + account.name, index);
|
||||||
|
|
||||||
this.accountCache.set(key, account);
|
this.accountCache.set(key, account);
|
||||||
|
|
||||||
batch.write(callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -863,7 +910,7 @@ WalletDB.prototype.hasAccount = function hasAccount(id, account, callback) {
|
|||||||
WalletDB.prototype.saveAddress = function saveAddress(id, addresses, callback) {
|
WalletDB.prototype.saveAddress = function saveAddress(id, addresses, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var items = [];
|
var items = [];
|
||||||
var batch = this.db.batch();
|
var batch = this.batch(id);
|
||||||
var i, address, path;
|
var i, address, path;
|
||||||
|
|
||||||
if (!Array.isArray(addresses))
|
if (!Array.isArray(addresses))
|
||||||
@ -910,12 +957,7 @@ WalletDB.prototype.saveAddress = function saveAddress(id, addresses, callback) {
|
|||||||
|
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
}, function(err) {
|
}, callback);
|
||||||
if (err)
|
|
||||||
return callback(err);
|
|
||||||
|
|
||||||
batch.write(callback);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user