generators: refactor http.

This commit is contained in:
Christopher Jeffrey 2016-09-22 01:29:48 -07:00
parent ae83aa6fba
commit 8c11a2aa3f
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
11 changed files with 452 additions and 463 deletions

13
bin/cli
View File

@ -8,6 +8,7 @@ var spawn = require('../lib/utils/spawn');
var Client = require('../lib/http/client');
var Wallet = require('../lib/http/wallet');
var assert = utils.assert;
var main;
function CLI() {
this.config = config({
@ -444,13 +445,11 @@ CLI.prototype.destroy = function destroy() {
return Promise.resolve(null);
};
function main() {
return spawn(function *() {
var cli = new CLI();
yield cli.open();
yield cli.destroy();
}, this);
}
main = co(function* main() {
var cli = new CLI();
yield cli.open();
yield cli.destroy();
});
main().then(process.exit).catch(function(err) {
console.error(err.stack + '');

View File

@ -25,6 +25,19 @@ node.on('error', function(err) {
;
});
process.on('uncaughtException', function(err) {
node.logger.debug(err.stack);
node.logger.error(err);
process.exit(1);
});
process.on('unhandledRejection', function(err, promise) {
node.logger.debug('Unhandled Rejection');
node.logger.debug(err.stack);
node.logger.error(err);
process.exit(1);
});
node.open().then(function() {
if (process.argv.indexOf('--test') !== -1) {
node.pool.watchAddress('1VayNert3x1KzbpzMGt2qdqrAThiRovi8');

View File

@ -1185,7 +1185,7 @@ ChainDB.prototype.scan = co(function* scan(start, filter, iter) {
}
}
yield* iter(entry, txs);
yield iter(entry, txs);
entry = yield entry.getNext();
}

View File

@ -126,6 +126,7 @@ function Environment() {
this.require('bloom', './utils/bloom');
this.require('uri', './utils/uri');
this.require('errors', './utils/errors');
this.require('spawn', './utils/spawn');
// Crypto
this.require('ec', './crypto/ec');
@ -207,6 +208,7 @@ function Environment() {
// HTTP
this.require('http', './http');
this.require('rpc', './http/rpc');
// Workers
this.require('workers', './workers/workers');
@ -343,6 +345,7 @@ Environment.prototype.cache = function cache() {
require('./utils/bloom');
require('./utils/uri');
require('./utils/errors');
require('./utils/spawn');
require('./crypto/ec');
require('./crypto/crypto');
require('./crypto/chachapoly');
@ -399,6 +402,7 @@ Environment.prototype.cache = function cache() {
require('./wallet/walletdb');
require('./wallet/path');
require('./http');
require('./http/rpc');
require('./workers/workers');
require('./bip70/bip70');
};

View File

@ -162,7 +162,7 @@ HTTPBase.prototype._initRouter = function _initRouter() {
// Avoid stack overflows
utils.nextTick(function() {
try {
callback(req, res, next, _send);
callback.call(route.ctx, req, res, _send, next);
} catch (e) {
done(e);
}
@ -264,11 +264,11 @@ HTTPBase.prototype._handle = function _handle(req, res, _send, callback) {
handler = self.stack[i++];
utils.nextTick(function() {
if (handler.path && req.pathname.indexOf(handler.path) === -1)
if (handler.path && req.pathname.indexOf(handler.path) !== 0)
return next();
try {
handler.callback(req, res, next, _send);
handler.callback.call(handler.ctx, req, res, _send, next);
} catch (e) {
next(e);
}
@ -291,7 +291,7 @@ HTTPBase.prototype._handle = function _handle(req, res, _send, callback) {
* @param {RouteCallback} callback
*/
HTTPBase.prototype.use = function use(path, callback) {
HTTPBase.prototype.use = function use(path, callback, ctx) {
if (!callback) {
callback = path;
path = null;
@ -299,12 +299,12 @@ HTTPBase.prototype.use = function use(path, callback) {
if (Array.isArray(path)) {
path.forEach(function(path) {
this.use(path, callback);
this.use(path, callback, ctx);
}, this);
return;
}
this.stack.push({ path: path, callback: callback });
this.stack.push({ ctx: ctx, path: path, callback: callback });
};
/**
@ -313,14 +313,14 @@ HTTPBase.prototype.use = function use(path, callback) {
* @param {RouteCallback} callback
*/
HTTPBase.prototype.get = function get(path, callback) {
HTTPBase.prototype.get = function get(path, callback, ctx) {
if (Array.isArray(path)) {
path.forEach(function(path) {
this.get(path, callback);
this.get(path, callback, ctx);
}, this);
return;
}
this.routes.get.push({ path: path, callback: callback });
this.routes.get.push({ ctx: ctx, path: path, callback: callback });
};
/**
@ -329,14 +329,14 @@ HTTPBase.prototype.get = function get(path, callback) {
* @param {RouteCallback} callback
*/
HTTPBase.prototype.post = function post(path, callback) {
HTTPBase.prototype.post = function post(path, callback, ctx) {
if (Array.isArray(path)) {
path.forEach(function(path) {
this.post(path, callback);
this.post(path, callback, ctx);
}, this);
return;
}
this.routes.post.push({ path: path, callback: callback });
this.routes.post.push({ ctx: ctx, path: path, callback: callback });
};
/**
@ -345,14 +345,14 @@ HTTPBase.prototype.post = function post(path, callback) {
* @param {RouteCallback} callback
*/
HTTPBase.prototype.put = function put(path, callback) {
HTTPBase.prototype.put = function put(path, callback, ctx) {
if (Array.isArray(path)) {
path.forEach(function(path) {
this.put(path, callback);
this.put(path, callback, ctx);
}, this);
return;
}
this.routes.put.push({ path: path, callback: callback });
this.routes.put.push({ ctx: ctx, path: path, callback: callback });
};
/**
@ -361,14 +361,14 @@ HTTPBase.prototype.put = function put(path, callback) {
* @param {RouteCallback} callback
*/
HTTPBase.prototype.del = function del(path, callback) {
HTTPBase.prototype.del = function del(path, callback, ctx) {
if (Array.isArray(path)) {
path.forEach(function(path) {
this.del(path, callback);
this.del(path, callback, ctx);
}, this);
return;
}
this.routes.del.push({ path: path, callback: callback });
this.routes.del.push({ ctx: ctx, path: path, callback: callback });
};
/**

View File

@ -1393,14 +1393,10 @@ RPC.prototype._getwork = co(function* _getwork() {
};
});
RPC.prototype.getworklp = function getworklp(args) {
var self = this;
return new Promise(function(resolve, reject) {
self.once('clear block', function() {
self._getwork().then(resolve).catch(reject);
});
});
};
RPC.prototype.getworklp = co(function* getworklp(args) {
yield this._onBlock();
return yield this._getwork();
});
RPC.prototype.getwork = co(function* getwork(args) {
var unlock = yield this.locker.lock();
@ -1686,27 +1682,32 @@ RPC.prototype._tmpl = co(function* _tmpl(version, coinbase, rules) {
return template;
});
RPC.prototype._poll = function _poll(lpid) {
RPC.prototype._poll = co(function* _poll(lpid) {
var self = this;
var watched, lastTX;
if (typeof lpid !== 'string')
return Promise.resolve(null);
return null;
if (lpid.length !== 74)
return Promise.reject(new RPCError('Invalid parameter.'));
throw new RPCError('Invalid parameter.');
watched = lpid.slice(0, 64);
lastTX = +lpid.slice(64, 74);
if (!utils.isHex(watched) || !utils.isNumber(lastTX))
return Promise.reject(new RPCError('Invalid parameter.'));
throw new RPCError('Invalid parameter.');
watched = utils.revHex(watched);
if (this.chain.tip.hash !== watched)
return Promise.resolve(null);
return null;
yield this._onBlock();
});
RPC.prototype._onBlock = function _onBlock() {
var self = this;
return new Promise(function(resolve, reject) {
self.once('clear block', resolve);
});
@ -1855,42 +1856,39 @@ RPC.prototype.prioritisetransaction = function prioritisetransaction(args) {
};
RPC.prototype._hashps = co(function* _hashps(lookup, height) {
var i, minTime, maxTime, pb0, time;
var workDiff, timeDiff, ps, pb, entry;
var i, minTime, maxTime, workDiff, timeDiff, ps, tip, entry;
pb = this.chain.tip;
tip = this.chain.tip;
if (height >= 0 && height < this.chain.tip.height)
pb = yield this.chain.db.get(height);
tip = yield this.chain.db.get(height);
if (!pb)
if (!tip)
return 0;
if (lookup <= 0)
lookup = pb.height % this.network.pow.retargetInterval + 1;
lookup = tip.height % this.network.pow.retargetInterval + 1;
if (lookup > pb.height)
lookup = pb.height;
if (lookup > tip.height)
lookup = tip.height;
minTime = pb.ts;
minTime = tip.ts;
maxTime = minTime;
pb0 = pb;
entry = tip;
for (i = 0; i < lookup; i++) {
entry = yield pb0.getPrevious();
entry = yield entry.getPrevious();
if (!entry)
throw new RPCError('Not found.');
pb0 = entry;
time = pb0.ts;
minTime = Math.min(time, minTime);
maxTime = Math.max(time, maxTime);
minTime = Math.min(entry.ts, minTime);
maxTime = Math.max(entry.ts, maxTime);
}
if (minTime === maxTime)
return 0;
workDiff = pb.chainwork.sub(pb0.chainwork);
workDiff = tip.chainwork.sub(entry.chainwork);
timeDiff = maxTime - minTime;
ps = +workDiff.toString(10) / timeDiff;

View File

@ -17,6 +17,7 @@ var HTTPBase = http.base;
var utils = require('../utils/utils');
var spawn = require('../utils/spawn');
var co = spawn.co;
var con = spawn.con;
var crypto = require('../crypto/crypto');
var assert = utils.assert;
var RPC; /*= require('./rpc'); - load lazily */
@ -101,7 +102,7 @@ HTTPServer.prototype._init = function _init() {
address.address, address.port);
});
this.use(function(req, res, next, send) {
this.use(function(req, res, send, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader(
@ -115,14 +116,14 @@ HTTPServer.prototype._init = function _init() {
res.setHeader('X-Bcoin-Version', constants.USER_VERSION);
res.setHeader('X-Bcoin-Agent', constants.USER_AGENT);
res.setHeader('X-Bcoin-Network', self.network.type);
res.setHeader('X-Bcoin-Height', self.chain.height + '');
res.setHeader('X-Bcoin-Tip', utils.revHex(self.chain.tip.hash));
res.setHeader('X-Bcoin-Network', this.network.type);
res.setHeader('X-Bcoin-Height', this.chain.height + '');
res.setHeader('X-Bcoin-Tip', utils.revHex(this.chain.tip.hash));
next();
});
this.use(function(req, res, next, send) {
this.use(function(req, res, send, next) {
var auth = req.headers['authorization'];
var parts;
@ -145,11 +146,11 @@ HTTPServer.prototype._init = function _init() {
next();
});
this.use(function(req, res, next, send) {
if (!self.apiHash)
this.use(function(req, res, send, next) {
if (!this.apiHash)
return next();
if (crypto.ccmp(hash256(req.password), self.apiHash))
if (crypto.ccmp(hash256(req.password), this.apiHash))
return next();
res.setHeader('WWW-Authenticate', 'Basic realm="node"');
@ -170,7 +171,7 @@ HTTPServer.prototype._init = function _init() {
send(401, { error: 'Bad API key.' });
});
this.use(function(req, res, next, send) {
this.use(function(req, res, send, next) {
var i, params, options, output, address;
if (req.method === 'POST' && req.pathname === '/') {
@ -187,8 +188,8 @@ HTTPServer.prototype._init = function _init() {
softMerge(params, req.query, true);
softMerge(params, req.body);
self.logger.debug('Params:');
self.logger.debug(params);
this.logger.debug('Params:');
this.logger.debug(params);
if (params.id) {
assert(typeof params.id === 'string', 'ID must be a string.');
@ -374,37 +375,16 @@ HTTPServer.prototype._init = function _init() {
next();
});
this.use(function(req, res, next, send) {
spawn(function *() {
var wallet;
this.use(con(function *(req, res, send, next) {
var wallet;
if (req.path.length < 2 || req.path[0] !== 'wallet') {
next();
return;
}
if (req.path.length < 2 || req.path[0] !== 'wallet') {
next();
return;
}
if (!self.options.walletAuth) {
wallet = yield self.walletdb.get(req.options.id);
if (!wallet) {
send(404);
return;
}
req.wallet = wallet;
next();
return;
}
try {
wallet = yield self.walletdb.auth(req.options.id, req.options.token);
} catch (err) {
self.logger.info('Auth failure for %s: %s.',
req.options.id, err.message);
send(403, { error: err.message });
return;
}
if (!this.options.walletAuth) {
wallet = yield this.walletdb.get(req.options.id);
if (!wallet) {
send(404);
@ -412,458 +392,411 @@ HTTPServer.prototype._init = function _init() {
}
req.wallet = wallet;
self.logger.info('Successful auth for %s.', req.options.id);
next();
}).catch(next);
});
return;
}
try {
wallet = yield this.walletdb.auth(req.options.id, req.options.token);
} catch (err) {
this.logger.info('Auth failure for %s: %s.',
req.options.id, err.message);
send(403, { error: err.message });
return;
}
if (!wallet) {
send(404);
return;
}
req.wallet = wallet;
this.logger.info('Successful auth for %s.', req.options.id);
next();
}));
// JSON RPC
this.post('/', function(req, res, next, send) {
spawn(function *() {
var json;
this.post('/', con(function *(req, res, send, next) {
var json;
if (!self.rpc) {
RPC = require('./rpc');
self.rpc = new RPC(self.node);
}
if (!this.rpc) {
RPC = require('./rpc');
this.rpc = new RPC(this.node);
}
if (req.body.method === 'getwork') {
res.setHeader('X-Long-Polling', '/?longpoll=1');
if (req.query.longpoll)
req.body.method = 'getworklp';
}
if (req.body.method === 'getwork') {
res.setHeader('X-Long-Polling', '/?longpoll=1');
if (req.query.longpoll)
req.body.method = 'getworklp';
}
try {
json = yield self.rpc.execute(req.body);
} catch (err) {
self.logger.error(err);
try {
json = yield this.rpc.execute(req.body);
} catch (err) {
this.logger.error(err);
if (err.type === 'RPCError') {
return send(400, {
result: err.message,
error: null,
id: req.body.id
});
}
return send(500, {
result: null,
error: {
message: err.message,
code: 1
},
if (err.type === 'RPCError') {
return send(400, {
result: err.message,
error: null,
id: req.body.id
});
}
send(200, {
result: json != null ? json : null,
error: null,
return send(500, {
result: null,
error: {
message: err.message,
code: 1
},
id: req.body.id
});
}).catch(next);
});
}
this.get('/', function(req, res, next, send) {
send(200, {
result: json != null ? json : null,
error: null,
id: req.body.id
});
}));
this.get('/', function(req, res, send, next) {
send(200, {
version: constants.USER_VERSION,
agent: constants.USER_AGENT,
services: self.pool.services,
network: self.network.type,
height: self.chain.height,
tip: self.chain.tip.rhash,
peers: self.pool.peers.all.length,
progress: self.chain.getProgress()
services: this.pool.services,
network: this.network.type,
height: this.chain.height,
tip: this.chain.tip.rhash,
peers: this.pool.peers.all.length,
progress: this.chain.getProgress()
});
});
// UTXO by address
this.get('/coin/address/:address', function(req, res, next, send) {
spawn(function *() {
var coins = yield self.node.getCoinsByAddress(req.options.address);
send(200, coins.map(function(coin) {
return coin.toJSON();
}));
}).catch(next);
});
this.get('/coin/address/:address', con(function *(req, res, send, next) {
var coins = yield this.node.getCoinsByAddress(req.options.address);
send(200, coins.map(function(coin) {
return coin.toJSON();
}));
}));
// UTXO by id
this.get('/coin/:hash/:index', function(req, res, next, send) {
spawn(function *() {
var coin = yield self.node.getCoin(req.options.hash, req.options.index);
this.get('/coin/:hash/:index', con(function *(req, res, send, next) {
var coin = yield this.node.getCoin(req.options.hash, req.options.index);
if (!coin)
return send(404);
if (!coin)
return send(404);
send(200, coin.toJSON());
}).catch(next);
});
send(200, coin.toJSON());
}));
// Bulk read UTXOs
this.post('/coin/address', function(req, res, next, send) {
spawn(function *() {
var coins = yield self.node.getCoinsByAddress(req.options.address);
send(200, coins.map(function(coin) {
return coin.toJSON();
}));
}).catch(next);
});
this.post('/coin/address', con(function *(req, res, send, next) {
var coins = yield this.node.getCoinsByAddress(req.options.address);
send(200, coins.map(function(coin) {
return coin.toJSON();
}));
}));
// TX by hash
this.get('/tx/:hash', function(req, res, next, send) {
spawn(function *() {
var tx = yield self.node.getTX(req.options.hash);
this.get('/tx/:hash', con(function *(req, res, send, next) {
var tx = yield this.node.getTX(req.options.hash);
if (!tx)
return send(404);
if (!tx)
return send(404);
yield self.node.fillHistory(tx);
yield this.node.fillHistory(tx);
send(200, tx.toJSON());
}).catch(next);
});
send(200, tx.toJSON());
}));
// TX by address
this.get('/tx/address/:address', function(req, res, next, send) {
spawn(function *() {
var txs = yield self.node.getTXByAddress(req.options.address);
var i, tx;
this.get('/tx/address/:address', con(function *(req, res, send, next) {
var txs = yield this.node.getTXByAddress(req.options.address);
var i, tx;
for (i = 0; i < txs.length; i++) {
tx = txs[i];
yield self.node.fillHistory(tx);
}
for (i = 0; i < txs.length; i++) {
tx = txs[i];
yield this.node.fillHistory(tx);
}
send(200, txs.map(function(tx) {
return tx.toJSON();
}));
}).catch(next);
});
send(200, txs.map(function(tx) {
return tx.toJSON();
}));
}));
// Bulk read TXs
this.post('/tx/address', function(req, res, next, send) {
spawn(function *() {
var txs = yield self.node.getTXByAddress(req.options.address);
var i, tx;
this.post('/tx/address', con(function *(req, res, send, next) {
var txs = yield this.node.getTXByAddress(req.options.address);
var i, tx;
for (i = 0; i < txs.length; i++) {
tx = txs[i];
yield self.node.fillHistory(tx);
}
for (i = 0; i < txs.length; i++) {
tx = txs[i];
yield this.node.fillHistory(tx);
}
send(200, txs.map(function(tx) {
return tx.toJSON();
}));
}).catch(next);
});
send(200, txs.map(function(tx) {
return tx.toJSON();
}));
}));
// Block by hash/height
this.get('/block/:hash', function(req, res, next, send) {
spawn(function *() {
var hash = req.options.hash || req.options.height;
var block = yield self.node.getFullBlock(hash);
this.get('/block/:hash', con(function *(req, res, send, next) {
var hash = req.options.hash || req.options.height;
var block = yield this.node.getFullBlock(hash);
if (!block)
return send(404);
if (!block)
return send(404);
send(200, block.toJSON());
}).catch(next);
});
send(200, block.toJSON());
}));
// Mempool snapshot
this.get('/mempool', function(req, res, next, send) {
spawn(function *() {
var i, txs, tx;
this.get('/mempool', con(function *(req, res, send, next) {
var i, txs, tx;
if (!self.mempool)
return send(400, { error: 'No mempool available.' });
if (!this.mempool)
return send(400, { error: 'No mempool available.' });
txs = self.mempool.getHistory();
txs = this.mempool.getHistory();
for (i = 0; i < txs.length; i++) {
tx = txs[i];
yield self.node.fillHistory(tx);
}
for (i = 0; i < txs.length; i++) {
tx = txs[i];
yield this.node.fillHistory(tx);
}
send(200, txs.map(function(tx) {
return tx.toJSON();
}));
}).catch(next);
});
send(200, txs.map(function(tx) {
return tx.toJSON();
}));
}));
// Broadcast TX
this.post('/broadcast', function(req, res, next, send) {
spawn(function *() {
yield self.node.sendTX(req.options.tx);
send(200, { success: true });
}).catch(next);
});
this.post('/broadcast', con(function *(req, res, send, next) {
yield this.node.sendTX(req.options.tx);
send(200, { success: true });
}));
// Estimate fee
this.get('/fee', function(req, res, next, send) {
this.get('/fee', function(req, res, send, next) {
var fee;
if (!self.fees)
if (!this.fees)
return send(400, { error: 'Fee estimation not available.' });
fee = self.fees.estimateFee(req.options.blocks);
fee = this.fees.estimateFee(req.options.blocks);
send(200, { rate: utils.btc(fee) });
});
// Get wallet
this.get('/wallet/:id', function(req, res, next, send) {
this.get('/wallet/:id', function(req, res, send, next) {
send(200, req.wallet.toJSON());
});
// Create wallet
this.post('/wallet/:id?', function(req, res, next, send) {
spawn(function *() {
var wallet = yield self.walletdb.create(req.options);
send(200, wallet.toJSON());
}).catch(next);
});
this.post('/wallet/:id?', con(function *(req, res, send, next) {
var wallet = yield this.walletdb.create(req.options);
send(200, wallet.toJSON());
}));
// List accounts
this.get('/wallet/:id/account', function(req, res, next, send) {
spawn(function *() {
var accounts = yield req.wallet.getAccounts();
send(200, accounts);
}).catch(next);
});
this.get('/wallet/:id/account', con(function *(req, res, send, next) {
var accounts = yield req.wallet.getAccounts();
send(200, accounts);
}));
// Get account
this.get('/wallet/:id/account/:account', function(req, res, next, send) {
spawn(function *() {
var account = yield req.wallet.getAccount(req.options.account);
this.get('/wallet/:id/account/:account', con(function *(req, res, send, next) {
var account = yield req.wallet.getAccount(req.options.account);
if (!account)
return send(404);
if (!account)
return send(404);
send(200, account.toJSON());
}).catch(next);
});
send(200, account.toJSON());
}));
// Create/get account
this.post('/wallet/:id/account/:account?', function(req, res, next, send) {
spawn(function *() {
var account = yield req.wallet.createAccount(req.options);
this.post('/wallet/:id/account/:account?', con(function *(req, res, send, next) {
var account = yield req.wallet.createAccount(req.options);
if (!account)
return send(404);
if (!account)
return send(404);
send(200, account.toJSON());
}).catch(next);
});
send(200, account.toJSON());
}));
// Change passphrase
this.post('/wallet/:id/passphrase', function(req, res, next, send) {
spawn(function *() {
var options = req.options;
var old = options.old;
var new_ = options.passphrase;
yield req.wallet.setPassphrase(old, new_);
send(200, { success: true });
}).catch(next);
});
this.post('/wallet/:id/passphrase', con(function *(req, res, send, next) {
var options = req.options;
var old = options.old;
var new_ = options.passphrase;
yield req.wallet.setPassphrase(old, new_);
send(200, { success: true });
}));
// Generate new token
this.post('/wallet/:id/retoken', function(req, res, next, send) {
spawn(function *() {
var options = req.options;
var token = yield req.wallet.retoken(options.passphrase);
send(200, { token: token.toString('hex') });
}).catch(next);
});
this.post('/wallet/:id/retoken', con(function *(req, res, send, next) {
var options = req.options;
var token = yield req.wallet.retoken(options.passphrase);
send(200, { token: token.toString('hex') });
}));
// Send TX
this.post('/wallet/:id/send', function(req, res, next, send) {
spawn(function *() {
var options = req.options;
var tx = yield req.wallet.send(options);
send(200, tx.toJSON());
}).catch(next);
});
this.post('/wallet/:id/send', con(function *(req, res, send, next) {
var options = req.options;
var tx = yield req.wallet.send(options);
send(200, tx.toJSON());
}));
// Create TX
this.post('/wallet/:id/create', function(req, res, next, send) {
spawn(function *() {
var options = req.options;
var tx = yield req.wallet.createTX(options);
yield req.wallet.sign(tx, options);
send(200, tx.toJSON());
}).catch(next);
});
this.post('/wallet/:id/create', con(function *(req, res, send, next) {
var options = req.options;
var tx = yield req.wallet.createTX(options);
yield req.wallet.sign(tx, options);
send(200, tx.toJSON());
}));
// Sign TX
this.post('/wallet/:id/sign', function(req, res, next, send) {
spawn(function *() {
var options = req.options;
var tx = req.options.tx;
yield req.wallet.sign(tx, options);
send(200, tx.toJSON());
}).catch(next);
});
this.post('/wallet/:id/sign', con(function *(req, res, send, next) {
var options = req.options;
var tx = req.options.tx;
yield req.wallet.sign(tx, options);
send(200, tx.toJSON());
}));
// Fill TX
this.post('/wallet/:id/fill', function(req, res, next, send) {
spawn(function *() {
var tx = req.options.tx;
yield req.wallet.fillHistory(tx);
send(200, tx.toJSON());
}).catch(next);
});
this.post('/wallet/:id/fill', con(function *(req, res, send, next) {
var tx = req.options.tx;
yield req.wallet.fillHistory(tx);
send(200, tx.toJSON());
}));
// Zap Wallet TXs
this.post('/wallet/:id/zap', function(req, res, next, send) {
spawn(function *() {
var account = req.options.account;
var age = req.options.age;
yield req.wallet.zap(account, age);
send(200, { success: true });
}).catch(next);
});
this.post('/wallet/:id/zap', con(function *(req, res, send, next) {
var account = req.options.account;
var age = req.options.age;
yield req.wallet.zap(account, age);
send(200, { success: true });
}));
// Abandon Wallet TX
this.del('/wallet/:id/tx/:hash', function(req, res, next, send) {
spawn(function *() {
var hash = req.options.hash;
yield req.wallet.abandon(hash);
send(200, { success: true });
}).catch(next);
});
this.del('/wallet/:id/tx/:hash', con(function *(req, res, send, next) {
var hash = req.options.hash;
yield req.wallet.abandon(hash);
send(200, { success: true });
}));
// Add key
this.put('/wallet/:id/key', function(req, res, next, send) {
spawn(function *() {
var account = req.options.account;
var key = req.options.key;
yield req.wallet.addKey(account, key);
send(200, { success: true });
}).catch(next);
});
this.put('/wallet/:id/key', con(function *(req, res, send, next) {
var account = req.options.account;
var key = req.options.key;
yield req.wallet.addKey(account, key);
send(200, { success: true });
}));
// Remove key
this.del('/wallet/:id/key', function(req, res, next, send) {
spawn(function *() {
var account = req.options.account;
var key = req.options.key;
yield req.wallet.removeKey(account, key);
send(200, { success: true });
}).catch(next);
});
this.del('/wallet/:id/key', con(function *(req, res, send, next) {
var account = req.options.account;
var key = req.options.key;
yield req.wallet.removeKey(account, key);
send(200, { success: true });
}));
// Create address
this.post('/wallet/:id/address', function(req, res, next, send) {
spawn(function *() {
var account = req.options.account;
var address = yield req.wallet.createReceive(account);
send(200, address.toJSON());
}).catch(next);
});
this.post('/wallet/:id/address', con(function *(req, res, send, next) {
var account = req.options.account;
var address = yield req.wallet.createReceive(account);
send(200, address.toJSON());
}));
// Wallet Balance
this.get('/wallet/:id/balance', function(req, res, next, send) {
spawn(function *() {
var account = req.options.account;
var balance = yield req.wallet.getBalance(account);
this.get('/wallet/:id/balance', con(function *(req, res, send, next) {
var account = req.options.account;
var balance = yield req.wallet.getBalance(account);
if (!balance)
return send(404);
if (!balance)
return send(404);
send(200, balance.toJSON());
}).catch(next);
});
send(200, balance.toJSON());
}));
// Wallet UTXOs
this.get('/wallet/:id/coin', function(req, res, next, send) {
spawn(function *() {
var account = req.options.account;
var coins = yield req.wallet.getCoins(account);
send(200, coins.map(function(coin) {
return coin.toJSON();
}));
}).catch(next);
});
this.get('/wallet/:id/coin', con(function *(req, res, send, next) {
var account = req.options.account;
var coins = yield req.wallet.getCoins(account);
send(200, coins.map(function(coin) {
return coin.toJSON();
}));
}));
// Wallet Coin
this.get('/wallet/:id/coin/:hash/:index', function(req, res, next, send) {
spawn(function *() {
var hash = req.options.hash;
var index = req.options.index;
var coin = yield req.wallet.getCoin(hash, index);
this.get('/wallet/:id/coin/:hash/:index', con(function *(req, res, send, next) {
var hash = req.options.hash;
var index = req.options.index;
var coin = yield req.wallet.getCoin(hash, index);
if (!coin)
return send(404);
if (!coin)
return send(404);
send(200, coin.toJSON());
}).catch(next);
});
send(200, coin.toJSON());
}));
// Wallet TXs
this.get('/wallet/:id/tx/history', function(req, res, next, send) {
spawn(function *() {
var account = req.options.account;
var txs = yield req.wallet.getHistory(account);
var details = yield req.wallet.toDetails(txs);
send(200, details.map(function(tx) {
return tx.toJSON();
}));
}).catch(next);
});
this.get('/wallet/:id/tx/history', con(function *(req, res, send, next) {
var account = req.options.account;
var txs = yield req.wallet.getHistory(account);
var details = yield req.wallet.toDetails(txs);
send(200, details.map(function(tx) {
return tx.toJSON();
}));
}));
// Wallet Pending TXs
this.get('/wallet/:id/tx/unconfirmed', function(req, res, next, send) {
spawn(function *() {
var account = req.options.account;
var txs = yield req.wallet.getUnconfirmed(account);
var details = yield req.wallet.toDetails(txs);
send(200, details.map(function(tx) {
return tx.toJSON();
}));
}).catch(next);
});
this.get('/wallet/:id/tx/unconfirmed', con(function *(req, res, send, next) {
var account = req.options.account;
var txs = yield req.wallet.getUnconfirmed(account);
var details = yield req.wallet.toDetails(txs);
send(200, details.map(function(tx) {
return tx.toJSON();
}));
}));
// Wallet TXs within time range
this.get('/wallet/:id/tx/range', function(req, res, next, send) {
spawn(function *() {
var account = req.options.account;
var options = req.options;
var txs = yield req.wallet.getRange(account, options);
var details = yield req.wallet.toDetails(txs);
send(200, details.map(function(tx) {
return tx.toJSON();
}));
}).catch(next);
});
this.get('/wallet/:id/tx/range', con(function *(req, res, send, next) {
var account = req.options.account;
var options = req.options;
var txs = yield req.wallet.getRange(account, options);
var details = yield req.wallet.toDetails(txs);
send(200, details.map(function(tx) {
return tx.toJSON();
}));
}));
// Last Wallet TXs
this.get('/wallet/:id/tx/last', function(req, res, next, send) {
spawn(function *() {
var account = req.options.account;
var limit = req.options.limit;
var txs = yield req.wallet.getLast(account, limit);
var details = yield req.wallet.toDetails(txs);
send(200, details.map(function(tx) {
return tx.toJSON();
}));
}).catch(next);
});
this.get('/wallet/:id/tx/last', con(function *(req, res, send, next) {
var account = req.options.account;
var limit = req.options.limit;
var txs = yield req.wallet.getLast(account, limit);
var details = yield req.wallet.toDetails(txs);
send(200, details.map(function(tx) {
return tx.toJSON();
}));
}));
// Wallet TX
this.get('/wallet/:id/tx/:hash', function(req, res, next, send) {
spawn(function *() {
var hash = req.options.hash;
var tx = yield req.wallet.getTX(hash);
var details;
this.get('/wallet/:id/tx/:hash', con(function *(req, res, send, next) {
var hash = req.options.hash;
var tx = yield req.wallet.getTX(hash);
var details;
if (!tx)
return send(404);
if (!tx)
return send(404);
details = yield req.wallet.toDetails(tx);
send(200, details.toJSON());
}).catch(next);
});
details = yield req.wallet.toDetails(tx);
send(200, details.toJSON());
}));
this.server.on('error', function(err) {
self.emit('error', err);
@ -1090,7 +1023,7 @@ HTTPServer.prototype.close = function close() {
*/
HTTPServer.prototype.use = function use(path, callback) {
return this.server.use(path, callback);
return this.server.use(path, callback, this);
};
/**
@ -1098,7 +1031,7 @@ HTTPServer.prototype.use = function use(path, callback) {
*/
HTTPServer.prototype.get = function get(path, callback) {
return this.server.get(path, callback);
return this.server.get(path, callback, this);
};
/**
@ -1106,7 +1039,7 @@ HTTPServer.prototype.get = function get(path, callback) {
*/
HTTPServer.prototype.post = function post(path, callback) {
return this.server.post(path, callback);
return this.server.post(path, callback, this);
};
/**
@ -1114,7 +1047,7 @@ HTTPServer.prototype.post = function post(path, callback) {
*/
HTTPServer.prototype.put = function put(path, callback) {
return this.server.put(path, callback);
return this.server.put(path, callback, this);
};
/**
@ -1122,7 +1055,7 @@ HTTPServer.prototype.put = function put(path, callback) {
*/
HTTPServer.prototype.del = function del(path, callback) {
return this.server.del(path, callback);
return this.server.del(path, callback, this);
};
/**
@ -1337,14 +1270,14 @@ ClientSocket.prototype.scan = function scan(start) {
if (this.chain.db.options.prune)
return Promise.reject(new Error('Cannot scan in pruned mode.'));
return this.chain.db.scan(start, this.filter, function *(entry, txs) {
return this.chain.db.scan(start, this.filter, co(function *(entry, txs) {
for (i = 0; i < txs.length; i++)
txs[i] = txs[i].toJSON();
self.emit('block tx', entry.toJSON(), txs);
yield utils.wait();
});
}));
};
ClientSocket.prototype.join = function join(id) {

View File

@ -253,36 +253,36 @@ Miner.prototype.createBlock = co(function* createBlock(tip) {
// Find target
target = yield this.chain.getTargetAsync(ts, tip);
if (this.version != null) {
version = this.version;
} else {
if (this.version != null) {
version = this.version;
} else {
// Calculate version with versionbits
version = yield this.chain.computeBlockVersion(tip);
version = yield this.chain.computeBlockVersion(tip);
}
attempt = new MinerBlock({
workerPool: this.workerPool,
tip: tip,
version: version,
target: target,
address: this.address,
coinbaseFlags: this.coinbaseFlags,
witness: this.chain.segwitActive,
parallel: this.options.parallel,
network: this.network
});
attempt = new MinerBlock({
workerPool: this.workerPool,
tip: tip,
version: version,
target: target,
address: this.address,
coinbaseFlags: this.coinbaseFlags,
witness: this.chain.segwitActive,
parallel: this.options.parallel,
network: this.network
});
if (!this.mempool)
return attempt;
if (!this.mempool)
return attempt;
txs = this.mempool.getHistory();
txs = this.mempool.getHistory();
for (i = 0; i < txs.length; i++) {
tx = txs[i];
attempt.addTX(tx);
}
for (i = 0; i < txs.length; i++) {
tx = txs[i];
attempt.addTX(tx);
}
return attempt;
return attempt;
});
/**
@ -292,8 +292,8 @@ Miner.prototype.createBlock = co(function* createBlock(tip) {
*/
Miner.prototype.mineBlock = co(function* mineBlock(tip) {
// Create a new block and start hashing
var attempt = yield this.createBlock(tip);
// Create a new block and start hashing
var attempt = yield this.createBlock(tip);
return yield attempt.mineAsync();
});

View File

@ -102,7 +102,47 @@ function cob(generator) {
gen = generator.apply(this, args);
return cb(exec(gen), callback);
return cb(exec(gen), function(err, result) {
// Escape the promise's scope:
utils.nextTick(function() {
callback(err, result);
});
});
};
}
/**
* Wrap a generator function to be
* executed into a function that
* accepts a node.js style callback.
* Only executes callback on error.
* @param {GeneratorFunction}
* @returns {Function}
*/
function con(generator) {
return function() {
var i, args, callback, gen;
if (arguments.length === 0
|| typeof arguments[arguments.length - 1] !== 'function') {
throw new Error('Function must accept a callback.');
}
args = new Array(arguments.length);
callback = arguments[arguments.length - 1];
for (i = 0; i < args.length; i++)
args[i] = arguments[i];
gen = generator.apply(this, args);
return exec(gen).catch(function(err) {
// Escape the promise's scope:
utils.nextTick(function() {
callback(err);
});
});
};
}
@ -223,6 +263,7 @@ exports.exec = exec;
exports.spawn = spawn;
exports.co = co;
exports.cob = cob;
exports.con = con;
exports.cb = cb;
exports.wait = wait;
exports.timeout = timeout;

View File

@ -975,9 +975,9 @@ WalletDB.prototype.rescan = co(function* rescan(chaindb, height) {
this.logger.info('Scanning for %d addresses.', hashes.length);
try {
yield chaindb.scan(height, hashes, function *(block, txs) {
yield chaindb.scan(height, hashes, co(function *(block, txs) {
yield self.addBlock(block, txs, true);
});
}));
} catch (e) {
unlock();
throw e;

View File

@ -8,6 +8,7 @@ var crypto = require('../lib/crypto/crypto');
var assert = require('assert');
var opcodes = constants.opcodes;
var spawn = require('../lib/utils/spawn');
var co = require('../lib/utils/spawn').co;
var c = require('../lib/utils/spawn').cb;
describe('Chain', function() {
@ -234,10 +235,10 @@ describe('Chain', function() {
var total = 0;
c(walletdb.getAddressHashes(), function(err, hashes) {
assert.ifError(err);
c(chain.db.scan(null, hashes, function *(block, txs) {
c(chain.db.scan(null, hashes, co(function *(block, txs) {
total += txs.length;
yield spawn.wait();
}), function(err) {
})), function(err) {
assert.ifError(err);
assert.equal(total, 25);
cb();