refactor: browser. benchmarks.

This commit is contained in:
Christopher Jeffrey 2016-09-22 03:25:41 -07:00
parent f158a73a2d
commit 8cb6faa078
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
9 changed files with 348 additions and 387 deletions

View File

@ -1,6 +1,5 @@
all:
@npm run browserify
@npm run uglify
clean:
@npm run clean

View File

@ -7,6 +7,7 @@ var utils = bcoin.utils;
var assert = require('assert');
var scriptTypes = constants.scriptTypes;
var bench = require('./bench');
var co = require('../lib/utils/spawn').co;
bcoin.cache();
@ -35,115 +36,89 @@ var walletdb = new bcoin.walletdb({
// db: 'leveldb'
db: 'memory'
});
var wallet;
var addrs = [];
function runBench(callback) {
utils.serial([
function(next) {
walletdb.create(function(err, w) {
assert.ifError(err);
wallet = w;
next();
});
},
function(next) {
var end = bench('accounts');
utils.forRange(0, 1000, function(i, next) {
wallet.createAccount({}, function(err, account) {
assert.ifError(err);
addrs.push(account.receiveAddress.getAddress());
next();
});
}, function(err) {
assert.ifError(err);
end(1000);
next();
});
},
function(next) {
var end = bench('addrs');
utils.forRange(0, 1000, function(i, next) {
utils.forRange(0, 10, function(j, next) {
wallet.createReceive(i, function(err, addr) {
assert.ifError(err);
addrs.push(addr);
next();
});
}, next);
}, function(err) {
assert.ifError(err);
end(1000 * 10);
next();
});
},
function(next) {
var nonce = new bn(0);
var end;
utils.forRange(0, 10000, function(i, next) {
var t1 = bcoin.mtx()
.addOutput(addrs[(i + 0) % addrs.length], 50460)
.addOutput(addrs[(i + 1) % addrs.length], 50460)
.addOutput(addrs[(i + 2) % addrs.length], 50460)
.addOutput(addrs[(i + 3) % addrs.length], 50460);
var runBench = co(function* runBench() {
var i, j, wallet, addrs, jobs, end;
var result, nonce, tx, options;
t1.addInput(dummyInput);
nonce.addn(1);
t1.inputs[0].script.set(0, nonce);
t1.inputs[0].script.compile();
// Open and Create
yield walletdb.open();
wallet = yield walletdb.create();
addrs = [];
walletdb.addTX(t1.toTX(), function(err) {
assert.ifError(err);
next();
});
}, function(err) {
assert.ifError(err);
end(10000);
next();
});
end = bench('tx');
},
function(next) {
var end = bench('balance');
wallet.getBalance(function(err, balance) {
assert.ifError(err);
end(1);
next();
});
},
function(next) {
var end = bench('coins');
wallet.getCoins(function(err) {
assert.ifError(err);
end(1);
next();
});
},
function(next) {
var end = bench('create');
var options = {
rate: 10000,
outputs: [{
value: 50460,
address: addrs[0]
}]
};
wallet.createTX(options, function(err) {
assert.ifError(err);
end(1);
next();
});
}
], function(err) {
assert.ifError(err);
callback();
});
}
// Accounts
jobs = [];
for (i = 0; i < 1000; i++)
jobs.push(wallet.createAccount({}));
walletdb.open(function(err) {
assert.ifError(err);
runBench(function(err) {
assert.ifError(err);
process.exit(0);
end = bench('accounts');
result = yield Promise.all(jobs);
end(1000);
for (i = 0; i < result.length; i++)
addrs.push(result[i].receiveAddress.getAddress());
// Addresses
jobs = [];
for (i = 0; i < 1000; i++) {
for (j = 0; j < 10; j++)
jobs.push(wallet.createReceive(i));
}
end = bench('addrs');
result = yield Promise.all(jobs);
end(1000 * 10);
for (i = 0; i < result.length; i++)
addrs.push(result[i].getAddress());
// TX
jobs = [];
nonce = new bn(0);
for (i = 0; i < 10000; i++) {
tx = bcoin.mtx()
.addOutput(addrs[(i + 0) % addrs.length], 50460)
.addOutput(addrs[(i + 1) % addrs.length], 50460)
.addOutput(addrs[(i + 2) % addrs.length], 50460)
.addOutput(addrs[(i + 3) % addrs.length], 50460);
tx.addInput(dummyInput);
nonce.addn(1);
tx.inputs[0].script.set(0, nonce);
tx.inputs[0].script.compile();
jobs.push(walletdb.addTX(tx.toTX()));
}
end = bench('tx');
result = yield Promise.all(jobs);
end(10000);
// Balance
end = bench('balance');
result = yield wallet.getBalance();
end(1);
// Coins
end = bench('coins');
result = yield wallet.getCoins();
end(1);
// Create
end = bench('create');
options = {
rate: 10000,
outputs: [{
value: 50460,
address: addrs[0]
}]
};
yield wallet.createTX(options);
end(1);
});
runBench().then(process.exit).catch(function(err) {
utils.nextTick(function() {
throw err;
});
});

View File

@ -32,16 +32,7 @@ process.on('uncaughtException', function(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() {
node.pool.connect();
node.startSync();
}).catch(function(e) {
throw e;
});

View File

@ -31,13 +31,6 @@ process.on('uncaughtException', function(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');
@ -50,6 +43,4 @@ node.open().then(function() {
}
node.startSync();
}).catch(function(err) {
throw err;
});

View File

@ -92,220 +92,6 @@ more bitcoin magic).</small>
</form>
<input type="button" id="newaddr" value="New Address">
<div id="floating" class="floating"></div>
<script>
;(function() {
'use strict';
var utils = bcoin.utils;
var body = document.getElementsByTagName('body')[0];
var log = document.getElementById('log');
var wdiv = document.getElementById('wallet');
var tdiv = document.getElementById('tx');
var floating = document.getElementById('floating');
var send = document.getElementById('send');
var newaddr = document.getElementById('newaddr');
var chainState = document.getElementById('state');
var items = [];
var scrollback = 0;
var logger, node, options;
body.onmouseup = function() {
floating.style.display = 'none';
};
floating.onmouseup = function(ev) {
ev.stopPropagation();
return false;
};
function show(obj) {
floating.innerHTML = escape(utils.inspectify(obj, false));
floating.style.display = 'block';
}
logger = new bcoin.logger({ level: 'debug' });
logger.writeConsole = function(level, args) {
var msg = utils.format(args, false);
if (++scrollback > 1000) {
log.innerHTML = '';
scrollback = 1;
}
log.innerHTML += '<span style="color:blue;">' + utils.now() + '</span> ';
if (level === 'error')
log.innerHTML += '<span style="color:red;">[' + level + ']</span> ';
else
log.innerHTML += '[' + level + '] ';
log.innerHTML += escape(msg) + '\n';
log.scrollTop = log.scrollHeight;
};
send.onsubmit = function(ev) {
var value = document.getElementById('amount').value;
var address = document.getElementById('address').value;
var options = {
outputs: [{
address: address,
value: utils.satoshi(value)
}]
};
node.wallet.createTX(options, function(err, tx) {
if (err)
return node.logger.error(err);
node.wallet.sign(tx, function(err) {
if (err)
return node.logger.error(err);
node.sendTX(tx, function(err) {
if (err)
return node.logger.error(err);
show(tx);
});
});
});
ev.preventDefault();
ev.stopPropagation();
return false;
};
newaddr.onmouseup = function() {
node.wallet.createReceive(function(err) {
if (err)
throw err;
formatWallet(node.wallet);
});
};
function kb(size) {
size /= 1000;
return size.toFixed(2) + 'kb';
}
function create(html) {
var el = document.createElement('div');
el.innerHTML = html;
return el.firstChild;
}
function escape(html, encode) {
return html
.replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function addItem(tx) {
var el;
if (items.length === 20) {
el = items.shift();
tdiv.removeChild(el);
el.onmouseup = null;
}
el = create('<a style="display:block;" href="#'
+ tx.rhash + '">' + tx.rhash + ' (' + tx.height
+ ' - ' + kb(tx.getSize()) + ')</a>');
tdiv.appendChild(el);
el.onmouseup = function(ev) {
show(tx);
ev.stopPropagation();
return false;
};
items.push(el);
chainState.innerHTML = ''
+ 'tx=' + node.chain.db.state.tx
+ ' coin=' + node.chain.db.state.coin
+ ' value=' + utils.btc(node.chain.db.state.value);
}
function formatWallet(wallet) {
var html = '';
var key = wallet.master.toJSON().key;
html += '<b>Wallet</b><br>';
if (bcoin.network.get().type === 'segnet4') {
html += 'Current Address (p2wpkh): <b>' + wallet.getAddress() + '</b><br>';
html += 'Current Address (p2wpkh behind p2sh): <b>' + wallet.getProgramAddress() + '</b><br>';
} else {
html += 'Current Address: <b>' + wallet.getAddress() + '</b><br>';
}
html += 'Extended Private Key: <b>' + key.xprivkey + '</b><br>';
html += 'Mnemonic: <b>' + key.mnemonic.phrase + '</b><br>';
wallet.getBalance(function(err, balance) {
if (err)
throw err;
html += 'Confirmed Balance: <b>' + utils.btc(balance.confirmed) + '</b><br>';
html += 'Unconfirmed Balance: <b>' + utils.btc(balance.unconfirmed) + '</b><br>';
html += 'Balance: <b>' + utils.btc(balance.total) + '</b><br>';
wallet.getHistory(function(err, txs) {
if (err)
throw err;
wallet.toDetails(txs, function(err, txs) {
if (err)
throw err;
html += 'TXs:\n';
wdiv.innerHTML = html;
txs.forEach(function(tx) {
var el = create('<a style="display:block;" href="#' + tx.hash + '">' + tx.hash + '</a>');
wdiv.appendChild(el);
el.onmouseup = function(ev) {
show(tx.toJSON());
ev.stopPropagation();
return false;
};
});
});
});
});
}
options = bcoin.config({
query: true,
network: 'segnet4',
db: 'leveldb',
useWorkers: true,
coinCache: true,
logger: logger
});
bcoin.set(options);
node = new bcoin.fullnode(options);
node.on('error', function(err) {
;
});
node.chain.on('block', addItem);
node.mempool.on('tx', addItem);
node.open(function(err) {
if (err)
throw err;
node.startSync();
formatWallet(node.wallet);
node.wallet.on('update', function() {
formatWallet(node.wallet);
});
});
})();
</script>
<script src="/index.js"></script>
</body>
</html>

216
browser/index.js Normal file
View File

@ -0,0 +1,216 @@
;(function() {
'use strict';
var utils = bcoin.utils;
var body = document.getElementsByTagName('body')[0];
var log = document.getElementById('log');
var wdiv = document.getElementById('wallet');
var tdiv = document.getElementById('tx');
var floating = document.getElementById('floating');
var send = document.getElementById('send');
var newaddr = document.getElementById('newaddr');
var chainState = document.getElementById('state');
var cb = bcoin.spawn.cb;
var items = [];
var scrollback = 0;
var logger, node, options;
body.onmouseup = function() {
floating.style.display = 'none';
};
floating.onmouseup = function(ev) {
ev.stopPropagation();
return false;
};
function show(obj) {
floating.innerHTML = escape(utils.inspectify(obj, false));
floating.style.display = 'block';
}
logger = new bcoin.logger({ level: 'debug' });
logger.writeConsole = function(level, args) {
var msg = utils.format(args, false);
if (++scrollback > 1000) {
log.innerHTML = '';
scrollback = 1;
}
log.innerHTML += '<span style="color:blue;">' + utils.now() + '</span> ';
if (level === 'error')
log.innerHTML += '<span style="color:red;">[' + level + ']</span> ';
else
log.innerHTML += '[' + level + '] ';
log.innerHTML += escape(msg) + '\n';
log.scrollTop = log.scrollHeight;
};
send.onsubmit = function(ev) {
var value = document.getElementById('amount').value;
var address = document.getElementById('address').value;
var options = {
outputs: [{
address: address,
value: utils.satoshi(value)
}]
};
cb(node.wallet.createTX(options), function(err, tx) {
if (err)
return node.logger.error(err);
cb(node.wallet.sign(tx), function(err) {
if (err)
return node.logger.error(err);
cb(node.sendTX(tx), function(err) {
if (err)
return node.logger.error(err);
show(tx);
});
});
});
ev.preventDefault();
ev.stopPropagation();
return false;
};
newaddr.onmouseup = function() {
cb(node.wallet.createReceive(), function(err) {
if (err)
throw err;
formatWallet(node.wallet);
});
};
function kb(size) {
size /= 1000;
return size.toFixed(2) + 'kb';
}
function create(html) {
var el = document.createElement('div');
el.innerHTML = html;
return el.firstChild;
}
function escape(html, encode) {
return html
.replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function addItem(tx) {
var el;
if (items.length === 20) {
el = items.shift();
tdiv.removeChild(el);
el.onmouseup = null;
}
el = create('<a style="display:block;" href="#'
+ tx.rhash + '">' + tx.rhash + ' (' + tx.height
+ ' - ' + kb(tx.getSize()) + ')</a>');
tdiv.appendChild(el);
el.onmouseup = function(ev) {
show(tx);
ev.stopPropagation();
return false;
};
items.push(el);
chainState.innerHTML = ''
+ 'tx=' + node.chain.db.state.tx
+ ' coin=' + node.chain.db.state.coin
+ ' value=' + utils.btc(node.chain.db.state.value);
}
function formatWallet(wallet) {
var html = '';
var key = wallet.master.toJSON().key;
html += '<b>Wallet</b><br>';
if (bcoin.network.get().type === 'segnet4') {
html += 'Current Address (p2wpkh): <b>' + wallet.getAddress() + '</b><br>';
html += 'Current Address (p2wpkh behind p2sh): <b>' + wallet.getProgramAddress() + '</b><br>';
} else {
html += 'Current Address: <b>' + wallet.getAddress() + '</b><br>';
}
html += 'Extended Private Key: <b>' + key.xprivkey + '</b><br>';
html += 'Mnemonic: <b>' + key.mnemonic.phrase + '</b><br>';
cb(wallet.getBalance(), function(err, balance) {
if (err)
throw err;
html += 'Confirmed Balance: <b>' + utils.btc(balance.confirmed) + '</b><br>';
html += 'Unconfirmed Balance: <b>' + utils.btc(balance.unconfirmed) + '</b><br>';
html += 'Balance: <b>' + utils.btc(balance.total) + '</b><br>';
cb(wallet.getHistory(), function(err, txs) {
if (err)
throw err;
cb(wallet.toDetails(txs), function(err, txs) {
if (err)
throw err;
html += 'TXs:\n';
wdiv.innerHTML = html;
txs.forEach(function(tx) {
var el = create('<a style="display:block;" href="#' + tx.hash + '">' + tx.hash + '</a>');
wdiv.appendChild(el);
el.onmouseup = function(ev) {
show(tx.toJSON());
ev.stopPropagation();
return false;
};
});
});
});
});
}
options = bcoin.config({
query: true,
network: 'segnet4',
db: 'leveldb',
useWorkers: true,
coinCache: true,
logger: logger
});
bcoin.set(options);
node = new bcoin.fullnode(options);
node.on('error', function(err) {
;
});
node.chain.on('block', addItem);
node.mempool.on('tx', addItem);
cb(node.open(), function(err) {
if (err)
throw err;
node.startSync();
formatWallet(node.wallet);
node.wallet.on('update', function() {
formatWallet(node.wallet);
});
});
})();

View File

@ -15,22 +15,27 @@ proxy.on('error', function(err) {
});
var index = fs.readFileSync(__dirname + '/index.html');
var indexjs = fs.readFileSync(__dirname + '/index.js');
var bcoin = fs.readFileSync(__dirname + '/bcoin.js');
var worker = fs.readFileSync(__dirname + '/../lib/workers/worker.js');
server.get('/favicon.ico', function(req, res, next, send) {
server.get('/favicon.ico', function(req, res, send, next) {
send(404, '', 'text');
});
server.get('/', function(req, res, next, send) {
server.get('/', function(req, res, send, next) {
send(200, index, 'html');
});
server.get('/bcoin.js', function(req, res, next, send) {
server.get('/index.js', function(req, res, send, next) {
send(200, indexjs, 'js');
});
server.get('/bcoin.js', function(req, res, send, next) {
send(200, bcoin, 'js');
});
server.get('/bcoin-worker.js', function(req, res, next, send) {
server.get('/bcoin-worker.js', function(req, res, send, next) {
send(200, worker, 'js');
});

View File

@ -53,12 +53,12 @@ function exec(gen) {
* Execute generator function
* with a context and execute.
* @param {GeneratorFunction} generator
* @param {Object} self
* @param {Object} ctx
* @returns {Promise}
*/
function spawn(generator, self) {
var gen = generator.call(self);
function spawn(generator, ctx) {
var gen = generator.call(ctx);
return exec(gen);
}
@ -80,7 +80,8 @@ function co(generator) {
/**
* Wrap a generator function to be
* executed into a function that
* returns a promise.
* returns a promise (with a lock).
* @param {String} name - lock name.
* @param {GeneratorFunction}
* @returns {Function}
*/
@ -115,7 +116,7 @@ function cob(generator) {
if (arguments.length === 0
|| typeof arguments[arguments.length - 1] !== 'function') {
throw new Error('Function must accept a callback.');
throw new Error((generator.name || 'Function') + ' requires a callback.');
}
args = new Array(arguments.length - 1);
@ -126,12 +127,7 @@ function cob(generator) {
gen = generator.apply(this, args);
return cb(exec(gen), function(err, result) {
// Escape the promise's scope:
utils.nextTick(function() {
callback(err, result);
});
});
return cb(exec(gen), callback);
};
}
@ -150,7 +146,7 @@ function con(generator) {
if (arguments.length === 0
|| typeof arguments[arguments.length - 1] !== 'function') {
throw new Error('Function must accept a callback.');
throw new Error((generator.name || 'Function') + ' requires a callback.');
}
args = new Array(arguments.length);
@ -179,9 +175,13 @@ function con(generator) {
function cb(promise, callback) {
promise.then(function(value) {
callback(null, value);
utils.nextTick(function() {
callback(null, value);
});
}, function(err) {
callback(err);
utils.nextTick(function() {
callback(err);
});
});
}
@ -204,9 +204,7 @@ function wait() {
function timeout(time) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve();
}, time);
setTimeout(resolve, time);
});
}
@ -220,8 +218,10 @@ function timeout(time) {
function wrap(resolve, reject) {
return function(err, result) {
if (err)
return reject(err);
if (err) {
reject(err);
return;
}
resolve(result);
};
}
@ -234,19 +234,16 @@ function wrap(resolve, reject) {
*/
function call(func) {
var args = new Array(Math.max(0, arguments.length - 1));
var self = this;
var args = new Array(arguments.length);
var i;
for (i = 1; i < arguments.length; i++)
args[i] = arguments[i];
return new Promise(function(resolve, reject) {
args.push(function(err, result) {
if (err)
return reject(err);
resolve(result);
});
func.apply(null, args);
args.push(wrap(resolve, reject));
func.apply(self, args);
});
}
@ -255,29 +252,30 @@ function call(func) {
* style callbacks into a function that
* returns a promise.
* @param {Function} func
* @param {Object?} self
* @param {Object?} ctx
* @returns {Function}
*/
function promisify(func, self) {
function promisify(func, ctx) {
return function() {
var args = new Array(arguments.length);
var i;
for (i = 0; i < args.length; i++)
args[i] = arguments[i];
return new Promise(function(resolve, reject) {
args.push(function(err, result) {
if (err)
return reject(err);
resolve(result);
});
func.apply(self, args);
});
return call.call(ctx, arguments);
};
}
/*
* This drives me nuts.
*/
if (typeof window !== 'undefined') {
window.onunhandledrejection = function(event) {
throw event.reason;
};
} else {
process.on('unhandledRejection', function(err, promise) {
throw err;
});
}
/*
* Expose
*/

View File

@ -1435,7 +1435,7 @@ Wallet.prototype.getTX = function getTX(hash) {
*/
Wallet.prototype.addTX = function addTX(tx) {
this.db.addTX(tx);
return this.db.addTX(tx);
};
/**