api keys.
This commit is contained in:
parent
2b41cfb9bd
commit
4a8768bcd2
@ -45,7 +45,6 @@ var USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1)'
|
||||
function request(options, callback, stream) {
|
||||
var qs = require('querystring');
|
||||
var url = require('url');
|
||||
|
||||
var uri = options.uri;
|
||||
var query = options.query;
|
||||
var body = options.body;
|
||||
|
||||
@ -43,6 +43,16 @@ function HTTPServer(options) {
|
||||
this.pool = this.node.pool;
|
||||
this.logger = options.logger || this.node.logger;
|
||||
this.loaded = false;
|
||||
this.apiKey = options.apiKey;
|
||||
|
||||
if (this.apiKey) {
|
||||
if (typeof this.apiKey === 'string') {
|
||||
assert(utils.isHex(this.apiKey), 'API key must be a hex string.');
|
||||
this.apiKey = new Buffer(this.apiKey, 'hex');
|
||||
}
|
||||
assert(Buffer.isBuffer(this.apiKey));
|
||||
assert(this.apiKey.length === 32, 'API key must be 32 bytes.');
|
||||
}
|
||||
|
||||
options.sockets = true;
|
||||
|
||||
@ -62,8 +72,8 @@ HTTPServer.prototype._init = function _init() {
|
||||
var self = this;
|
||||
|
||||
this.server.on('request', function(req, res) {
|
||||
self.logger.debug('Request from %s path=%s',
|
||||
req.socket.remoteAddress, req.pathname);
|
||||
self.logger.debug('Request for path=%s (%s).',
|
||||
req.pathname, req.socket.remoteAddress);
|
||||
});
|
||||
|
||||
this.use(function(req, res, next, send) {
|
||||
@ -87,6 +97,30 @@ HTTPServer.prototype._init = function _init() {
|
||||
next();
|
||||
});
|
||||
|
||||
this.use(function(req, res, next, send) {
|
||||
var auth = req.headers['authorization'];
|
||||
var parts;
|
||||
|
||||
if (!auth) {
|
||||
req.username = null;
|
||||
req.password = null;
|
||||
return next();
|
||||
}
|
||||
|
||||
parts = auth.split(' ');
|
||||
assert(parts.length === 2, 'Invalid auth token.');
|
||||
assert(parts[0] === 'Basic', 'Invalid auth token.');
|
||||
|
||||
auth = new Buffer(parts[1], 'base64').toString('utf8');
|
||||
parts = auth.split(':');
|
||||
assert(parts.length >= 2, 'Invalid auth token.');
|
||||
|
||||
req.username = parts.shift();
|
||||
req.password = parts.join(':');
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
this.use(function(req, res, next, send) {
|
||||
var params = utils.merge({}, req.params, req.query, req.body);
|
||||
var options = {};
|
||||
@ -184,16 +218,52 @@ HTTPServer.prototype._init = function _init() {
|
||||
if (params.passphrase)
|
||||
options.passphrase = params.passphrase;
|
||||
|
||||
if (req.password) {
|
||||
assert(utils.isHex(req.password), 'API key must be a hex string.');
|
||||
assert(req.password.length === 64, 'API key must be 32 bytes.');
|
||||
options.apiKey = new Buffer(req.password, 'hex');
|
||||
}
|
||||
|
||||
req.options = options;
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
this.use(function(req, res, next, send) {
|
||||
if (req.path.length < 2 || req.path[0] !== 'wallet') {
|
||||
if (self.apiKey) {
|
||||
if (!utils.ccmp(req.options.apiKey, self.apiKey)) {
|
||||
res.setHeader('WWW-Authenticate', 'Basic realm="node"');
|
||||
send(401, { error: 'Unauthorized.' });
|
||||
return;
|
||||
}
|
||||
}
|
||||
return next();
|
||||
}
|
||||
|
||||
if (!self.options.auth)
|
||||
return next();
|
||||
|
||||
self.walletdb.auth(req.options.id, req.options.apiKey, function(err) {
|
||||
if (err) {
|
||||
if (err.message === 'Wallet not found.')
|
||||
return next();
|
||||
self.logger.info('Auth failure for %s: %s.',
|
||||
req.options.id, err.message);
|
||||
res.setHeader('WWW-Authenticate', 'Basic realm="wallet"');
|
||||
send(401, { error: err.message });
|
||||
return;
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
function decodeScript(script) {
|
||||
if (!script)
|
||||
return;
|
||||
if (typeof script === 'string')
|
||||
return new bcoin.script(new Buffer(script, 'hex'));
|
||||
return bcoin.script.fromRaw(script, 'hex');
|
||||
return new bcoin.script(script);
|
||||
}
|
||||
|
||||
@ -356,9 +426,7 @@ HTTPServer.prototype._init = function _init() {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
send(200, {
|
||||
success: true
|
||||
});
|
||||
send(200, { success: true });
|
||||
});
|
||||
});
|
||||
|
||||
@ -421,6 +489,42 @@ HTTPServer.prototype._init = function _init() {
|
||||
});
|
||||
});
|
||||
|
||||
// Change passphrase
|
||||
this.post('/wallet/:id/passphrase', function(req, res, next, send) {
|
||||
var id = req.options.id;
|
||||
var options = req.options;
|
||||
var old = options.old;
|
||||
var new_ = options.passphrase;
|
||||
self.walletdb.setPassphrase(id, old, _new, function(err) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
send(200, { success: true });
|
||||
});
|
||||
});
|
||||
|
||||
// Generate new token
|
||||
this.post('/wallet/:id/retoken', function(req, res, next, send) {
|
||||
var id = req.options.id;
|
||||
var options = req.options;
|
||||
self.walletdb.retoken(id, options.passphrase, function(err, token) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
send(200, { token: token.toString('hex') });
|
||||
});
|
||||
});
|
||||
|
||||
// Broadcast TX
|
||||
this.post('/wallet/:id/broadcast', function(req, res, next, send) {
|
||||
self.node.sendTX(req.options.tx, function(err) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
send(200, { success: true });
|
||||
});
|
||||
});
|
||||
|
||||
// Send TX
|
||||
this.post('/wallet/:id/send', function(req, res, next, send) {
|
||||
var id = req.options.id;
|
||||
@ -498,9 +602,7 @@ HTTPServer.prototype._init = function _init() {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
send(200, {
|
||||
success: true
|
||||
});
|
||||
send(200, { success: true });
|
||||
});
|
||||
});
|
||||
|
||||
@ -731,8 +833,27 @@ HTTPServer.prototype._initIO = function _initIO() {
|
||||
self.emit('error', err);
|
||||
});
|
||||
|
||||
socket.on('join', function(id) {
|
||||
socket.join(id);
|
||||
socket.on('join', function(id, apiKey) {
|
||||
if (!self.options.auth) {
|
||||
socket.join(id);
|
||||
return;
|
||||
}
|
||||
if (id === '!all') {
|
||||
if (self.apiKey) {
|
||||
if (!utils.ccmp(apiKey, self.apiKey)) {
|
||||
self.logger.info('Auth failure for %s.', id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
return socket.join(id);
|
||||
}
|
||||
self.walletdb.auth(id, apiKey, function(err) {
|
||||
if (err) {
|
||||
self.logger.info('Auth failure for %s: %s.', id, err.message);
|
||||
return;
|
||||
}
|
||||
socket.join(id);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('leave', function(id) {
|
||||
@ -748,37 +869,30 @@ HTTPServer.prototype._initIO = function _initIO() {
|
||||
});
|
||||
|
||||
this.walletdb.on('tx', function(tx, map) {
|
||||
var summary = map.toJSON();
|
||||
tx = tx.toJSON();
|
||||
map.all.forEach(function(id) {
|
||||
self.server.io.to(id).emit('tx', tx);
|
||||
map.getWallets().forEach(function(id) {
|
||||
self.server.io.to(id).emit('tx', tx, summary);
|
||||
});
|
||||
self.server.io.to('!all').emit('tx', tx, map);
|
||||
self.server.io.to('!all').emit('tx', tx, summary);
|
||||
});
|
||||
|
||||
this.walletdb.on('confirmed', function(tx, map) {
|
||||
var summary = map.toJSON();
|
||||
tx = tx.toJSON();
|
||||
map.all.forEach(function(id) {
|
||||
self.server.io.to(id).emit('confirmed', tx);
|
||||
map.getWallets().forEach(function(id) {
|
||||
self.server.io.to(id).emit('confirmed', tx, summary);
|
||||
});
|
||||
self.server.io.to('!all').emit('confirmed', tx, map);
|
||||
self.server.io.to('!all').emit('confirmed', tx, summary);
|
||||
});
|
||||
|
||||
this.walletdb.on('updated', function(tx, map) {
|
||||
var summary = map.toJSON();
|
||||
tx = tx.toJSON();
|
||||
map.all.forEach(function(id) {
|
||||
self.server.io.to(id).emit('updated', tx);
|
||||
map.getWallets().forEach(function(id) {
|
||||
self.server.io.to(id).emit('updated', tx, summary);
|
||||
});
|
||||
self.server.io.to('!all').emit('updated', tx, map);
|
||||
});
|
||||
|
||||
this.walletdb.on('balance', function(balance, id) {
|
||||
var json = {
|
||||
confirmed: utils.btc(balance.confirmed),
|
||||
unconfirmed: utils.btc(balance.unconfirmed),
|
||||
total: utils.btc(balance.total)
|
||||
};
|
||||
self.server.io.to(id).emit('balance', json);
|
||||
self.server.io.to('!all').emit('balance', json, id);
|
||||
self.server.io.to('!all').emit('updated', tx, summary);
|
||||
});
|
||||
|
||||
this.walletdb.on('balances', function(balances) {
|
||||
@ -789,11 +903,15 @@ HTTPServer.prototype._initIO = function _initIO() {
|
||||
unconfirmed: utils.btc(balances[id].unconfirmed),
|
||||
total: utils.btc(balances[id].total)
|
||||
};
|
||||
self.server.io.to(id).emit('balance', json[id], id);
|
||||
self.server.io.to('!all').emit('balance', json[id], id);
|
||||
});
|
||||
self.server.io.to('!all').emit('balances', json);
|
||||
});
|
||||
|
||||
this.walletdb.on('address', function(receive, change, map) {
|
||||
var summary = map.toJSON();
|
||||
|
||||
if (receive) {
|
||||
receive = receive.map(function(address) {
|
||||
return address.toJSON();
|
||||
@ -806,11 +924,11 @@ HTTPServer.prototype._initIO = function _initIO() {
|
||||
});
|
||||
}
|
||||
|
||||
map.all.forEach(function(id) {
|
||||
self.server.io.to(id).emit('address', receive, change, map);
|
||||
map.getWallets().forEach(function(id) {
|
||||
self.server.io.to(id).emit('address', receive, change, summary);
|
||||
});
|
||||
|
||||
self.server.io.to('!all').emit('address', receive, change, map);
|
||||
self.server.io.to('!all').emit('address', receive, change, summary);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -2031,6 +2031,58 @@ WalletMap.prototype.hasPaths = function hasPaths(address) {
|
||||
return paths && paths.length !== 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a unique list of wallet IDs for the map.
|
||||
* @returns {WalletID[]}
|
||||
*/
|
||||
|
||||
WalletMap.prototype.getWallets = function getWallets() {
|
||||
var ids = {};
|
||||
var i, member;
|
||||
|
||||
for (i = 0; i < this.accounts.length; i++) {
|
||||
member = this.accounts[i];
|
||||
ids[member.id] = true;
|
||||
}
|
||||
|
||||
return Object.keys(ids);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a unique list of wallet IDs for the map.
|
||||
* @returns {WalletID[]}
|
||||
*/
|
||||
|
||||
WalletMap.prototype.getInputWallets = function getInputWallets() {
|
||||
var ids = {};
|
||||
var i, member;
|
||||
|
||||
for (i = 0; i < this.inputs.length; i++) {
|
||||
member = this.inputs[i];
|
||||
ids[member.id] = true;
|
||||
}
|
||||
|
||||
return Object.keys(ids);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Return a unique list of wallet IDs for the map.
|
||||
* @returns {WalletID[]}
|
||||
*/
|
||||
|
||||
WalletMap.prototype.getOutputWallets = function getOutputWallets() {
|
||||
var ids = {};
|
||||
var i, member;
|
||||
|
||||
for (i = 0; i < this.outputs.length; i++) {
|
||||
member = this.outputs[i];
|
||||
ids[member.id] = true;
|
||||
}
|
||||
|
||||
return Object.keys(ids);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get paths for a given address hash.
|
||||
* @param {Hash} address
|
||||
|
||||
@ -1928,6 +1928,11 @@ utils.ccmp = function ccmp(a, b) {
|
||||
if (!Buffer.isBuffer(b))
|
||||
return false;
|
||||
|
||||
// It's assumed the target length
|
||||
// would be known to an attacker anyway.
|
||||
if (a.length !== b.length)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < a.length; i++)
|
||||
res |= a[i] ^ b[i];
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
|
||||
var bcoin = require('./env');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var constants = bcoin.protocol.constants;
|
||||
var utils = require('./utils');
|
||||
var assert = utils.assert;
|
||||
var BufferReader = require('./reader');
|
||||
@ -55,6 +56,8 @@ function Wallet(db, options) {
|
||||
this.master = null;
|
||||
this.initialized = false;
|
||||
this.accountDepth = 0;
|
||||
this.token = constants.ZERO_HASH;
|
||||
this.tokenDepth = 0;
|
||||
|
||||
this.account = null;
|
||||
|
||||
@ -72,6 +75,7 @@ utils.inherits(Wallet, EventEmitter);
|
||||
|
||||
Wallet.prototype.fromOptions = function fromOptions(options) {
|
||||
var master = options.master;
|
||||
var id, token;
|
||||
|
||||
if (!master)
|
||||
master = bcoin.hd.fromMnemonic(null, this.network);
|
||||
@ -79,9 +83,11 @@ Wallet.prototype.fromOptions = function fromOptions(options) {
|
||||
if (!bcoin.hd.isHD(master) && !MasterKey.isMasterKey(master))
|
||||
master = bcoin.hd.from(master, this.network);
|
||||
|
||||
if (!MasterKey.isMasterKey(master))
|
||||
if (bcoin.hd.isHD(master))
|
||||
master = MasterKey.fromKey(master);
|
||||
|
||||
assert(MasterKey.isMasterKey(master));
|
||||
|
||||
this.master = master;
|
||||
|
||||
if (options.initialized != null) {
|
||||
@ -96,11 +102,28 @@ Wallet.prototype.fromOptions = function fromOptions(options) {
|
||||
|
||||
if (options.id) {
|
||||
assert(utils.isAlpha(options.id), 'Wallet ID must be alphanumeric.');
|
||||
this.id = options.id;
|
||||
id = options.id;
|
||||
}
|
||||
|
||||
if (!this.id)
|
||||
this.id = this.getID();
|
||||
if (!id)
|
||||
id = this.getID();
|
||||
|
||||
if (options.token) {
|
||||
assert(Buffer.isBuffer(options.token));
|
||||
assert(options.token.length === 32);
|
||||
token = options.token;
|
||||
}
|
||||
|
||||
if (options.tokenDepth != null) {
|
||||
assert(utils.isNumber(options.tokenDepth));
|
||||
this.tokenDepth = options.tokenDepth;
|
||||
}
|
||||
|
||||
if (!token)
|
||||
token = this.getToken(this.master.key, this.tokenDepth);
|
||||
|
||||
this.id = id;
|
||||
this.token = token;
|
||||
|
||||
return this;
|
||||
};
|
||||
@ -294,6 +317,43 @@ Wallet.prototype.setPassphrase = function setPassphrase(old, new_, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate a new token.
|
||||
* @param {(String|Buffer)?} passphrase
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Wallet.prototype.retoken = function retoken(passphrase, callback) {
|
||||
var self = this;
|
||||
var unlock;
|
||||
|
||||
if (typeof passphrase === 'function') {
|
||||
callback = passphrase;
|
||||
passphrase = null;
|
||||
}
|
||||
|
||||
unlock = this.locker.lock(retoken, [passphrase, callback]);
|
||||
|
||||
if (!unlock)
|
||||
return;
|
||||
|
||||
callback = utils.wrap(callback, unlock);
|
||||
|
||||
this.unlock(passphrase, null, function(err, master) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
self.tokenDepth++;
|
||||
self.token = self.getToken(master, self.tokenDepth);
|
||||
|
||||
self.save(function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
return callback(null, self.token);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Lock the wallet, destroy decrypted key.
|
||||
*/
|
||||
@ -314,7 +374,7 @@ Wallet.prototype.unlock = function unlock(passphrase, timeout, callback) {
|
||||
|
||||
/**
|
||||
* Generate the wallet ID if none was passed in.
|
||||
* It is represented as `m/44'` (public) hashed
|
||||
* It is represented as `m/44` (public) hashed
|
||||
* and converted to an address with a prefix
|
||||
* of `0x03be04` (`WLT` in base58).
|
||||
* @returns {Base58String}
|
||||
@ -325,7 +385,7 @@ Wallet.prototype.getID = function getID() {
|
||||
|
||||
assert(this.master.key, 'Cannot derive id.');
|
||||
|
||||
key = this.master.key.derive(44, true);
|
||||
key = this.master.key.derive(44);
|
||||
|
||||
p = new BufferWriter();
|
||||
p.writeU8(0x03);
|
||||
@ -337,6 +397,27 @@ Wallet.prototype.getID = function getID() {
|
||||
return utils.toBase58(p.render());
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate the wallet api key if none was passed in.
|
||||
* It is represented as HASH256(m/44'->public|nonce).
|
||||
* @param {Number} nonce
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
Wallet.prototype.getToken = function getToken(master, nonce) {
|
||||
var key, p;
|
||||
|
||||
assert(master, 'Cannot derive token.');
|
||||
|
||||
key = master.derive(44, true);
|
||||
|
||||
p = new BufferWriter();
|
||||
p.writeBytes(key.publicKey);
|
||||
p.writeU32(nonce);
|
||||
|
||||
return utils.hash256(p.render());
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an account. Requires passphrase if master key is encrypted.
|
||||
* @param {Object} options - See {@link Account} options.
|
||||
@ -1437,6 +1518,8 @@ Wallet.prototype.inspect = function inspect() {
|
||||
network: this.network.type,
|
||||
initialized: this.initialized,
|
||||
accountDepth: this.accountDepth,
|
||||
token: this.token.toString('hex'),
|
||||
tokenDepth: this.tokenDepth,
|
||||
master: this.master,
|
||||
account: this.account
|
||||
};
|
||||
@ -1455,6 +1538,8 @@ Wallet.prototype.toJSON = function toJSON() {
|
||||
id: this.id,
|
||||
initialized: this.initialized,
|
||||
accountDepth: this.accountDepth,
|
||||
token: this.token.toString('hex'),
|
||||
tokenDepth: this.tokenDepth,
|
||||
master: this.master.toJSON(),
|
||||
account: this.account ? this.account.toJSON() : null
|
||||
};
|
||||
@ -1470,11 +1555,15 @@ Wallet.prototype.fromJSON = function fromJSON(json) {
|
||||
assert(utils.isAlpha(json.id), 'Wallet ID must be alphanumeric.');
|
||||
assert(typeof json.initialized === 'boolean');
|
||||
assert(utils.isNumber(json.accountDepth));
|
||||
assert(typeof json.token === 'string');
|
||||
assert(json.token.length === 64);
|
||||
assert(utils.isNumber(json.tokenDepth));
|
||||
|
||||
this.network = bcoin.network.get(json.network);
|
||||
this.id = json.id;
|
||||
this.initialized = json.initialized;
|
||||
this.accountDepth = json.accountDepth;
|
||||
this.token = new Buffer(json.token, 'hex');
|
||||
this.master = MasterKey.fromJSON(json.master);
|
||||
|
||||
return this;
|
||||
@ -1492,6 +1581,8 @@ Wallet.prototype.toRaw = function toRaw(writer) {
|
||||
p.writeVarString(this.id, 'utf8');
|
||||
p.writeU8(this.initialized ? 1 : 0);
|
||||
p.writeU32(this.accountDepth);
|
||||
p.writeBytes(this.token);
|
||||
p.writeU32(this.tokenDepth);
|
||||
p.writeVarBytes(this.master.toRaw());
|
||||
|
||||
if (!writer)
|
||||
@ -1512,6 +1603,8 @@ Wallet.prototype.fromRaw = function fromRaw(data) {
|
||||
this.id = p.readVarString('utf8');
|
||||
this.initialized = p.readU8() === 1;
|
||||
this.accountDepth = p.readU32();
|
||||
this.token = p.readBytes(32);
|
||||
this.tokenDepth = p.readU32();
|
||||
this.master = MasterKey.fromRaw(p.readVarBytes());
|
||||
return this;
|
||||
};
|
||||
@ -1585,8 +1678,6 @@ function Account(db, options) {
|
||||
this.network = db.network;
|
||||
this.lookahead = Account.MAX_LOOKAHEAD;
|
||||
|
||||
this.loaded = false;
|
||||
this.loading = false;
|
||||
this.receiveAddress = null;
|
||||
this.changeAddress = null;
|
||||
|
||||
@ -2268,13 +2359,13 @@ Account.prototype.toJSON = function toJSON() {
|
||||
receiveDepth: this.receiveDepth,
|
||||
changeDepth: this.changeDepth,
|
||||
receiveAddress: this.receiveAddress
|
||||
? this.receiveAddress.getAddress()
|
||||
? this.receiveAddress.getAddress('base58')
|
||||
: null,
|
||||
programAddress: this.receiveAddress
|
||||
? this.receiveAddress.getProgramAddress()
|
||||
? this.receiveAddress.getProgramAddress('base58')
|
||||
: null,
|
||||
changeAddress: this.changeAddress
|
||||
? this.changeAddress.getAddress()
|
||||
? this.changeAddress.getAddress('base58')
|
||||
: null,
|
||||
accountKey: this.accountKey.xpubkey,
|
||||
keys: this.keys.map(function(key) {
|
||||
@ -2469,7 +2560,7 @@ MasterKey.prototype.fromOptions = function fromOptions(options) {
|
||||
}
|
||||
|
||||
if (options.key) {
|
||||
assert(Buffer.isBuffer(options.key));
|
||||
assert(bcoin.hd.isHD(options.key));
|
||||
this.key = options.key;
|
||||
}
|
||||
|
||||
|
||||
@ -185,17 +185,12 @@ WalletDB.prototype.updateBalances = function updateBalances(tx, map, callback) {
|
||||
var balances = {};
|
||||
var i, id, keys;
|
||||
|
||||
utils.forEachSerial(map.outputs, function(output, next) {
|
||||
id = output.id;
|
||||
|
||||
if (self.listeners('balance').length === 0
|
||||
utils.forEachSerial(map.getOutputWallets(), function(id, next) {
|
||||
if (self.listeners('balances').length === 0
|
||||
&& !self.hasListener(id, 'balance')) {
|
||||
return next();
|
||||
}
|
||||
|
||||
if (balances[id] != null)
|
||||
return next();
|
||||
|
||||
self.getBalance(id, function(err, balance) {
|
||||
if (err)
|
||||
return next(err);
|
||||
@ -215,7 +210,7 @@ WalletDB.prototype.updateBalances = function updateBalances(tx, map, callback) {
|
||||
self.fire(id, 'balance', balances[id]);
|
||||
}
|
||||
|
||||
self.emit('balance', balances, map);
|
||||
self.emit('balances', balances, map);
|
||||
|
||||
return callback(null, balances);
|
||||
});
|
||||
@ -231,11 +226,8 @@ WalletDB.prototype.updateBalances = function updateBalances(tx, map, callback) {
|
||||
|
||||
WalletDB.prototype.syncOutputs = function syncOutputs(tx, map, callback) {
|
||||
var self = this;
|
||||
var id;
|
||||
|
||||
utils.forEachSerial(map.outputs, function(output, next) {
|
||||
id = output.id;
|
||||
|
||||
utils.forEachSerial(map.getOutputWallets(), function(id, next) {
|
||||
self.syncOutputDepth(id, tx, function(err, receive, change) {
|
||||
if (err)
|
||||
return next(err);
|
||||
@ -474,6 +466,46 @@ WalletDB.prototype.save = function save(wallet, callback) {
|
||||
this.db.put('w/' + wallet.id, wallet.toRaw(), callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test an api key against a wallet's api key.
|
||||
* @param {WalletID} id
|
||||
* @param {String} token
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
WalletDB.prototype.auth = function auth(id, token, callback) {
|
||||
var wallet;
|
||||
|
||||
if (!id)
|
||||
return callback(new Error('Wallet not found.'));
|
||||
|
||||
this.db.get('w/' + id, function(err, data) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (!data)
|
||||
return callback(new Error('Wallet not found.'));
|
||||
|
||||
try {
|
||||
wallet = bcoin.wallet.fromRaw(self, data);
|
||||
} catch (e) {
|
||||
return callback(e);
|
||||
}
|
||||
|
||||
if (typeof token === 'string') {
|
||||
if (!utils.isHex(token))
|
||||
return callback(new Error('Authentication error.'));
|
||||
token = new Buffer(token, 'hex');
|
||||
}
|
||||
|
||||
// Compare in constant time:
|
||||
if (!utils.ccmp(token, wallet.token))
|
||||
return callback(new Error('Authentication error.'));
|
||||
|
||||
return callback();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new wallet, save to database, setup watcher.
|
||||
* @param {Object} options - See {@link Wallet}.
|
||||
@ -613,6 +645,9 @@ WalletDB.prototype.getAccount = function getAccount(id, name, callback) {
|
||||
WalletDB.prototype.getAccounts = function getAccounts(id, callback) {
|
||||
var accounts = [];
|
||||
|
||||
if (!utils.isAlpha(id))
|
||||
return callback(new Error('Wallet IDs must be alphanumeric.'));
|
||||
|
||||
this.db.iterate({
|
||||
gte: 'i/' + id + '/',
|
||||
lte: 'i/' + id + '/~',
|
||||
@ -1184,6 +1219,29 @@ WalletDB.prototype.removeKey = function removeKey(id, name, key, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
WalletDB.prototype.setPassphrase = function setPassphrase(id, old, new_, callback) {
|
||||
if (typeof new_ === 'function') {
|
||||
callback = new_;
|
||||
new_ = old;
|
||||
old = null;
|
||||
}
|
||||
|
||||
this.fetchWallet(id, callback, function(wallet, callback) {
|
||||
wallet.setPassphrase(old, new_, callback);
|
||||
});
|
||||
};
|
||||
|
||||
WalletDB.prototype.retoken = function retoken(id, passphrase, callback) {
|
||||
if (typeof passphrase === 'function') {
|
||||
callback = passphrase;
|
||||
passphrase = null;
|
||||
}
|
||||
|
||||
this.fetchWallet(id, callback, function(wallet, callback) {
|
||||
wallet.retoken(passphrase, callback);
|
||||
});
|
||||
};
|
||||
|
||||
WalletDB.prototype.getInfo = function getInfo(id, callback) {
|
||||
this.fetchWallet(id, callback, function(wallet, callback) {
|
||||
callback(null, wallet);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user