refactor: more work.

This commit is contained in:
Christopher Jeffrey 2016-09-22 00:24:59 -07:00
parent 60d162eb20
commit ae83aa6fba
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
18 changed files with 841 additions and 817 deletions

748
bin/cli
View File

@ -27,159 +27,135 @@ CLI.prototype.log = function log(json) {
console.log(JSON.stringify(json, null, 2)); console.log(JSON.stringify(json, null, 2));
}; };
CLI.prototype.createWallet = function createWallet() { CLI.prototype.createWallet = co(function* createWallet() {
return spawn(function *() { var options = { id: this.argv[0] };
var options = { id: this.argv[0] }; var wallet;
var wallet;
if (this.config.type) if (this.config.type)
options.type = this.config.type; options.type = this.config.type;
if (this.config.master) if (this.config.master)
options.master = this.config.master; options.master = this.config.master;
if (this.config.key) if (this.config.key)
options.key = this.config.key; options.key = this.config.key;
if (this.config.m) if (this.config.m)
options.m = this.config.m >>> 0; options.m = this.config.m >>> 0;
if (this.config.n) if (this.config.n)
options.n = this.config.n >>> 0; options.n = this.config.n >>> 0;
if (this.config.witness != null) if (this.config.witness != null)
options.witness = !!this.config.witness; options.witness = !!this.config.witness;
if (this.config.passphrase) if (this.config.passphrase)
options.passphrase = this.config.passphrase; options.passphrase = this.config.passphrase;
wallet = yield this.client.createWallet(options); wallet = yield this.client.createWallet(options);
this.log(wallet); this.log(wallet);
}, this); });
};
CLI.prototype.addKey = function addKey() { CLI.prototype.addKey = co(function* addKey() {
return spawn(function *() { var key = this.argv[0];
var key = this.argv[0]; yield this.wallet.addKey(this.config.account, key);
yield this.wallet.addKey(this.config.account, key); this.log('added');
this.log('added'); });
}, this);
};
CLI.prototype.removeKey = function removeKey() { CLI.prototype.removeKey = co(function* removeKey() {
return spawn(function *() { var key = this.argv[0];
var key = this.argv[0]; yield this.wallet.removeKey(this.config.account, key);
yield this.wallet.removeKey(this.config.account, key); this.log('removed');
this.log('removed'); });
}, this);
};
CLI.prototype.getAccount = function getAccount() { CLI.prototype.getAccount = co(function* getAccount() {
return spawn(function *() { var account = this.argv[0] || this.config.account;
var account = this.argv[0] || this.config.account; yield this.wallet.getAccount(account);
yield this.wallet.getAccount(account); this.log(account);
this.log(account); });
}, this);
};
CLI.prototype.createAccount = function createAccount() { CLI.prototype.createAccount = co(function* createAccount() {
return spawn(function *() { var name = this.argv[0];
var name = this.argv[0]; var account = yield this.wallet.createAccount(name);
var account = yield this.wallet.createAccount(name); this.log(account);
this.log(account); });
}, this);
};
CLI.prototype.createAddress = function createAddress() { CLI.prototype.createAddress = co(function* createAddress() {
return spawn(function *() { var account = this.argv[0];
var account = this.argv[0]; var addr = yield this.wallet.createAddress(account);
var addr = yield this.wallet.createAddress(account); this.log(addr);
this.log(addr); });
}, this);
};
CLI.prototype.getAccounts = function getAccounts() { CLI.prototype.getAccounts = co(function* getAccounts() {
return spawn(function *() { var accounts = yield this.wallet.getAccounts();
var accounts = yield this.wallet.getAccounts(); this.log(accounts);
this.log(accounts); });
}, this);
};
CLI.prototype.getWallet = function getWallet() { CLI.prototype.getWallet = co(function* getWallet() {
return spawn(function *() { var info = yield this.wallet.getInfo();
var info = yield this.wallet.getInfo(); this.log(wallet);
this.log(wallet); });
}, this);
};
CLI.prototype.getTX = function getTX() { CLI.prototype.getTX = co(function* getTX() {
return spawn(function *() { var hash = this.argv[0];
var hash = this.argv[0]; var txs, tx;
var txs, tx;
if (utils.isBase58(hash)) { if (utils.isBase58(hash)) {
txs = yield this.client.getTXByAddress(hash); txs = yield this.client.getTXByAddress(hash);
this.log(txs);
return;
}
tx = yield this.client.getTX(hash);
if (!tx) {
this.log('TX not found.');
return;
}
this.log(tx);
}, this);
};
CLI.prototype.getBlock = function getBlock() {
return spawn(function *() {
var hash = this.argv[0];
if (hash.length !== 64)
hash = +hash;
block = yield this.client.getBlock(hash);
if (!block) {
this.log('Block not found.');
return
}
this.log(block);
}, this);
};
CLI.prototype.getCoin = function getCoin() {
return spawn(function *() {
var hash = this.argv[0];
var index = this.argv[1];
var coins, coin;
if (utils.isBase58(hash)) {
coins = yield this.client.getCoinsByAddress(hash);
this.log(coins);
return;
}
coin = yield this.client.getCoin(hash, index);
if (!coin) {
this.log('Coin not found.');
return;
}
this.log(coin);
}, this);
};
CLI.prototype.getWalletHistory = function getWalletHistory() {
return spawn(function *() {
var txs = yield this.wallet.getHistory(this.config.account);
this.log(txs); this.log(txs);
}, this); return;
}; }
tx = yield this.client.getTX(hash);
if (!tx) {
this.log('TX not found.');
return;
}
this.log(tx);
});
CLI.prototype.getBlock = co(function* getBlock() {
var hash = this.argv[0];
if (hash.length !== 64)
hash = +hash;
block = yield this.client.getBlock(hash);
if (!block) {
this.log('Block not found.');
return
}
this.log(block);
});
CLI.prototype.getCoin = co(function* getCoin() {
var hash = this.argv[0];
var index = this.argv[1];
var coins, coin;
if (utils.isBase58(hash)) {
coins = yield this.client.getCoinsByAddress(hash);
this.log(coins);
return;
}
coin = yield this.client.getCoin(hash, index);
if (!coin) {
this.log('Coin not found.');
return;
}
this.log(coin);
});
CLI.prototype.getWalletHistory = co(function* getWalletHistory() {
var txs = yield this.wallet.getHistory(this.config.account);
this.log(txs);
});
CLI.prototype.listenWallet = function listenWallet() { CLI.prototype.listenWallet = function listenWallet() {
var self = this; var self = this;
@ -210,283 +186,255 @@ CLI.prototype.listenWallet = function listenWallet() {
return new Promise(function() {}); return new Promise(function() {});
}; };
CLI.prototype.getBalance = function getBalance() { CLI.prototype.getBalance = co(function* getBalance() {
return spawn(function *() { var balance = yield this.wallet.getBalance(this.config.account);
var balance = yield this.wallet.getBalance(this.config.account); this.log(balance);
this.log(balance); });
}, this);
};
CLI.prototype.getMempool = function getMempool() { CLI.prototype.getMempool = co(function* getMempool() {
return spawn(function *() { var txs = yield this.client.getMempool();
var txs = yield this.client.getMempool(); this.log(txs);
this.log(txs); });
}, this);
};
CLI.prototype.sendTX = function sendTX() { CLI.prototype.sendTX = co(function* sendTX() {
return spawn(function *() { var output = {};
var output = {}; var options, tx;
var options, tx;
if (this.config.script) { if (this.config.script) {
output.script = this.config.script; output.script = this.config.script;
output.value = utils.satoshi(this.config.value || this.argv[0]); output.value = utils.satoshi(this.config.value || this.argv[0]);
} else { } else {
output.address = this.config.address || this.argv[0]; output.address = this.config.address || this.argv[0];
output.value = utils.satoshi(this.config.value || this.argv[1]); output.value = utils.satoshi(this.config.value || this.argv[1]);
}
options = {
account: this.config.account,
passphrase: this.config.passphrase,
outputs: [output]
};
tx = yield this.wallet.send(options);
this.log(tx);
});
CLI.prototype.createTX = co(function* createTX() {
var output = {};
var options, tx;
if (this.config.script) {
output.script = this.config.script;
output.value = utils.satoshi(this.config.value || this.argv[0]);
} else {
output.address = this.config.address || this.argv[0];
output.value = utils.satoshi(this.config.value || this.argv[1]);
}
options = {
account: this.config.account,
passphrase: this.config.passphrase,
outputs: [output]
};
tx = yield this.wallet.createTX(options);
this.log(tx);
});
CLI.prototype.signTX = co(function* signTX() {
var options = { passphrase: this.config.passphrase };
var raw = options.tx || this.argv[0];
var tx = yield this.wallet.sign(raw, options);
this.log(tx);
});
CLI.prototype.zap = co(function* zap() {
var age = (this.config.age >>> 0) || 72 * 60 * 60;
yield this.wallet.zap(this.config.account, age);
this.log('Zapped!');
});
CLI.prototype.broadcast = co(function* broadcast() {
var self = this;
var raw = this.argv[0] || this.config.tx;
var tx = yield this.client.broadcast(raw);
this.log('Broadcasted:');
this.log(tx);
});
CLI.prototype.viewTX = co(function* viewTX() {
var raw = this.argv[0] || this.config.tx;
var tx = yield this.wallet.fill(raw);
this.log(tx);
});
CLI.prototype.getDetails = co(function* getDetails() {
var hash = this.argv[0];
var details = yield this.wallet.getTX(hash);
this.log(details);
});
CLI.prototype.retoken = co(function* retoken() {
var result = yield this.wallet.retoken();
this.log(result);
});
CLI.prototype.rpc = co(function* rpc() {
var method = this.argv.shift();
var params = [];
var i, arg, param, result;
for (i = 0; i < this.argv.length; i++) {
arg = this.argv[i];
try {
param = JSON.parse(arg);
} catch (e) {
param = arg;
} }
params.push(param);
}
options = { result = yield this.client.rpc.call(method, params);
account: this.config.account,
passphrase: this.config.passphrase,
outputs: [output]
};
tx = yield this.wallet.send(options); this.log(result);
});
this.log(tx); CLI.prototype.handleWallet = co(function* handleWallet() {
}, this); var options = {
}; id: this.config.id || 'primary',
token: this.config.token
};
CLI.prototype.createTX = function createTX() { this.wallet = new Wallet({
return spawn(function *() { uri: this.config.url || this.config.uri,
var output = {}; apiKey: this.config.apikey,
var options, tx; network: this.config.network
});
if (this.config.script) { yield this.wallet.open(options);
output.script = this.config.script;
output.value = utils.satoshi(this.config.value || this.argv[0]);
} else {
output.address = this.config.address || this.argv[0];
output.value = utils.satoshi(this.config.value || this.argv[1]);
}
options = { switch (this.argv.shift()) {
account: this.config.account, case 'listen':
passphrase: this.config.passphrase, return yield this.listenWallet();
outputs: [output] case 'get':
}; return yield this.getWallet();
case 'addkey':
tx = yield this.wallet.createTX(options); return yield this.addKey();
case 'rmkey':
this.log(tx); return yield this.removeKey();
}, this); case 'balance':
}; return yield this.getBalance();
case 'history':
CLI.prototype.signTX = function signTX() { return yield this.getWalletHistory();
return spawn(function *() { case 'account':
var options = { passphrase: this.config.passphrase }; if (this.argv[0] === 'list') {
var raw = options.tx || this.argv[0];
var tx = yield this.wallet.sign(raw, options);
this.log(tx);
}, this);
};
CLI.prototype.zap = function zap() {
return spawn(function *() {
var age = (this.config.age >>> 0) || 72 * 60 * 60;
yield this.wallet.zap(this.config.account, age);
this.log('Zapped!');
}, this);
};
CLI.prototype.broadcast = function broadcast() {
return spawn(function *() {
var self = this;
var raw = this.argv[0] || this.config.tx;
var tx = yield this.client.broadcast(raw);
this.log('Broadcasted:');
this.log(tx);
}, this);
};
CLI.prototype.viewTX = function viewTX() {
return spawn(function *() {
var raw = this.argv[0] || this.config.tx;
var tx = yield this.wallet.fill(raw);
this.log(tx);
}, this);
};
CLI.prototype.getDetails = function getDetails() {
return spawn(function *() {
var hash = this.argv[0];
var details = yield this.wallet.getTX(hash);
this.log(details);
}, this);
};
CLI.prototype.retoken = function retoken() {
return spawn(function *() {
var result = yield this.wallet.retoken();
this.log(result);
}, this);
};
CLI.prototype.rpc = function rpc() {
return spawn(function *() {
var method = this.argv.shift();
var params = [];
var i, arg, param, result;
for (i = 0; i < this.argv.length; i++) {
arg = this.argv[i];
try {
param = JSON.parse(arg);
} catch (e) {
param = arg;
}
params.push(param);
}
result = yield this.client.rpc.call(method, params);
this.log(result);
}, this);
};
CLI.prototype.handleWallet = function handleWallet() {
return spawn(function *() {
var options = {
id: this.config.id || 'primary',
token: this.config.token
};
this.wallet = new Wallet({
uri: this.config.url || this.config.uri,
apiKey: this.config.apikey,
network: this.config.network
});
yield this.wallet.open(options);
switch (this.argv.shift()) {
case 'listen':
return yield this.listenWallet();
case 'get':
return yield this.getWallet();
case 'addkey':
return yield this.addKey();
case 'rmkey':
return yield this.removeKey();
case 'balance':
return yield this.getBalance();
case 'history':
return yield this.getWalletHistory();
case 'account':
if (this.argv[0] === 'list') {
this.argv.shift();
return yield this.getAccounts();
}
if (this.argv[0] === 'create') {
this.argv.shift();
return yield this.createAccount();
}
if (this.argv[0] === 'get')
this.argv.shift();
return yield this.getAccount();
case 'address':
return yield this.createAddress();
case 'retoken':
return yield this.retoken();
case 'sign':
return yield this.signTX();
case 'mktx':
return yield this.createTX();
case 'send':
return yield this.sendTX();
case 'zap':
return yield this.zap();
case 'tx':
return yield this.getDetails();
case 'view':
return yield this.viewTX();
default:
this.log('Unrecognized command.');
this.log('Commands:');
this.log(' $ listen: Listen for events.');
this.log(' $ get: View wallet.');
this.log(' $ addkey [xpubkey]: Add key to wallet.');
this.log(' $ rmkey [xpubkey]: Remove key from wallet.');
this.log(' $ balance: Get wallet balance.');
this.log(' $ history: View wallet TX history.');
this.log(' $ account list: List account names.');
this.log(' $ account create [account-name]: Create account.');
this.log(' $ account get [account-name]: Get account details.');
this.log(' $ address: Derive new address.');
this.log(' $ retoken: Create new api key.');
this.log(' $ send [address] [value]: Send transaction.');
this.log(' $ mktx [address] [value]: Create transaction.');
this.log(' $ sign [tx-hex]: Sign transaction.');
this.log(' $ zap --age [age]: Zap pending wallet TXs.');
this.log(' $ tx [hash]: View transaction details.');
this.log(' $ view [tx-hex]: Parse and view transaction.');
this.log('Other Options:');
this.log(' --passphrase [passphrase]: For signing and account creation.');
this.log(' --account [account-name]: Account name.');
return;
}
}, this);
};
CLI.prototype.handleNode = function handleNode() {
return spawn(function *() {
var info;
this.client = new Client({
uri: this.config.url || this.config.uri,
apiKey: this.config.apikey,
network: this.config.network
});
info = yield this.client.getInfo();
switch (this.argv.shift()) {
case 'mkwallet':
return yield this.createWallet();
case 'broadcast':
return yield this.broadcast();
case 'mempool':
return yield this.getMempool();
case 'tx':
return yield this.getTX();
case 'coin':
return yield this.getCoin();
case 'block':
return yield this.getBlock();
case 'rpc':
return yield this.rpc();
default:
this.log('Unrecognized command.');
this.log('Commands:');
this.log(' $ wallet create [id]: Create wallet.');
this.log(' $ broadcast [tx-hex]: Broadcast transaction.');
this.log(' $ mempool: Get mempool snapshot.');
this.log(' $ tx [hash/address]: View transactions.');
this.log(' $ coin [hash+index/address]: View coins.');
this.log(' $ block [hash/height]: View block.');
return;
}
}, this);
};
CLI.prototype.open = function open() {
return spawn(function *() {
switch (this.argv[0]) {
case 'w':
case 'wallet':
this.argv.shift(); this.argv.shift();
if (this.argv[0] === 'create') { return yield this.getAccounts();
this.argv[0] = 'mkwallet'; }
return yield this.handleNode(); if (this.argv[0] === 'create') {
} this.argv.shift();
return yield this.handleWallet(); return yield this.createAccount();
default: }
if (this.argv[0] === 'get')
this.argv.shift();
return yield this.getAccount();
case 'address':
return yield this.createAddress();
case 'retoken':
return yield this.retoken();
case 'sign':
return yield this.signTX();
case 'mktx':
return yield this.createTX();
case 'send':
return yield this.sendTX();
case 'zap':
return yield this.zap();
case 'tx':
return yield this.getDetails();
case 'view':
return yield this.viewTX();
default:
this.log('Unrecognized command.');
this.log('Commands:');
this.log(' $ listen: Listen for events.');
this.log(' $ get: View wallet.');
this.log(' $ addkey [xpubkey]: Add key to wallet.');
this.log(' $ rmkey [xpubkey]: Remove key from wallet.');
this.log(' $ balance: Get wallet balance.');
this.log(' $ history: View wallet TX history.');
this.log(' $ account list: List account names.');
this.log(' $ account create [account-name]: Create account.');
this.log(' $ account get [account-name]: Get account details.');
this.log(' $ address: Derive new address.');
this.log(' $ retoken: Create new api key.');
this.log(' $ send [address] [value]: Send transaction.');
this.log(' $ mktx [address] [value]: Create transaction.');
this.log(' $ sign [tx-hex]: Sign transaction.');
this.log(' $ zap --age [age]: Zap pending wallet TXs.');
this.log(' $ tx [hash]: View transaction details.');
this.log(' $ view [tx-hex]: Parse and view transaction.');
this.log('Other Options:');
this.log(' --passphrase [passphrase]: For signing and account creation.');
this.log(' --account [account-name]: Account name.');
return;
}
});
CLI.prototype.handleNode = co(function* handleNode() {
var info;
this.client = new Client({
uri: this.config.url || this.config.uri,
apiKey: this.config.apikey,
network: this.config.network
});
info = yield this.client.getInfo();
switch (this.argv.shift()) {
case 'mkwallet':
return yield this.createWallet();
case 'broadcast':
return yield this.broadcast();
case 'mempool':
return yield this.getMempool();
case 'tx':
return yield this.getTX();
case 'coin':
return yield this.getCoin();
case 'block':
return yield this.getBlock();
case 'rpc':
return yield this.rpc();
default:
this.log('Unrecognized command.');
this.log('Commands:');
this.log(' $ wallet create [id]: Create wallet.');
this.log(' $ broadcast [tx-hex]: Broadcast transaction.');
this.log(' $ mempool: Get mempool snapshot.');
this.log(' $ tx [hash/address]: View transactions.');
this.log(' $ coin [hash+index/address]: View coins.');
this.log(' $ block [hash/height]: View block.');
return;
}
});
CLI.prototype.open = co(function* open() {
switch (this.argv[0]) {
case 'w':
case 'wallet':
this.argv.shift();
if (this.argv[0] === 'create') {
this.argv[0] = 'mkwallet';
return yield this.handleNode(); return yield this.handleNode();
} }
}, this); return yield this.handleWallet();
}; default:
return yield this.handleNode();
}
});
CLI.prototype.destroy = function destroy() { CLI.prototype.destroy = function destroy() {
if (this.wallet && !this.wallet.client.loading) if (this.wallet && !this.wallet.client.loading)

View File

@ -1223,7 +1223,7 @@ Chain.prototype.add = co(function* add(block) {
if (this.orphan.size > this.orphanLimit) if (this.orphan.size > this.orphanLimit)
this.pruneOrphans(); this.pruneOrphans();
yield utils.wait(); yield spawn.wait();
if (!this.synced && this.isFull()) { if (!this.synced && this.isFull()) {
this.synced = true; this.synced = true;

View File

@ -235,7 +235,7 @@ ChainEntry.prototype.getAncestorByHeight = co(function* getAncestorByHeight(heig
var main, entry; var main, entry;
if (height < 0) if (height < 0)
return yield utils.wait(); return yield spawn.wait();
assert(height >= 0); assert(height >= 0);
assert(height <= this.height); assert(height <= this.height);

View File

@ -14,15 +14,16 @@ var scryptAsync = require('./scrypt-async');
var utils = require('../utils/utils'); var utils = require('../utils/utils');
var spawn = require('../utils/spawn'); var spawn = require('../utils/spawn');
var co = spawn.co; var co = spawn.co;
var wrap = spawn.wrap;
var native = require('../utils/native'); var native = require('../utils/native');
var nativeCrypto, hash, aes; var nodeCrypto, hash, aes;
var isBrowser = var isBrowser =
(typeof process !== 'undefined' && process.browser) (typeof process !== 'undefined' && process.browser)
|| typeof window !== 'undefined'; || typeof window !== 'undefined';
if (!isBrowser) { if (!isBrowser) {
nativeCrypto = require('crypto'); nodeCrypto = require('crypto');
} else { } else {
hash = require('hash.js'); hash = require('hash.js');
aes = require('./aes'); aes = require('./aes');
@ -42,10 +43,10 @@ var crypto = exports;
*/ */
crypto.hash = function _hash(alg, data) { crypto.hash = function _hash(alg, data) {
if (!nativeCrypto) if (!nodeCrypto)
return new Buffer(hash[alg]().update(data).digest()); return new Buffer(hash[alg]().update(data).digest());
return nativeCrypto.createHash(alg).update(data).digest(); return nodeCrypto.createHash(alg).update(data).digest();
}; };
if (native) if (native)
@ -131,12 +132,12 @@ crypto.checksum = function checksum(data) {
crypto.hmac = function hmac(alg, data, salt) { crypto.hmac = function hmac(alg, data, salt) {
var hmac; var hmac;
if (!nativeCrypto) { if (!nodeCrypto) {
hmac = hash.hmac(hash[alg], salt); hmac = hash.hmac(hash[alg], salt);
return new Buffer(hmac.update(data).digest()); return new Buffer(hmac.update(data).digest());
} }
hmac = nativeCrypto.createHmac(alg, salt); hmac = nodeCrypto.createHmac(alg, salt);
return hmac.update(data).digest(); return hmac.update(data).digest();
}; };
@ -160,8 +161,8 @@ crypto.pbkdf2 = function pbkdf2(key, salt, iter, len, alg) {
if (typeof salt === 'string') if (typeof salt === 'string')
salt = new Buffer(salt, 'utf8'); salt = new Buffer(salt, 'utf8');
if (nativeCrypto && nativeCrypto.pbkdf2Sync) if (nodeCrypto && nodeCrypto.pbkdf2Sync)
return nativeCrypto.pbkdf2Sync(key, salt, iter, len, alg); return nodeCrypto.pbkdf2Sync(key, salt, iter, len, alg);
return crypto._pbkdf2(key, salt, iter, len, alg); return crypto._pbkdf2(key, salt, iter, len, alg);
}; };
@ -185,13 +186,9 @@ crypto.pbkdf2Async = function pbkdf2Async(key, salt, iter, len, alg) {
if (typeof salt === 'string') if (typeof salt === 'string')
salt = new Buffer(salt, 'utf8'); salt = new Buffer(salt, 'utf8');
if (nativeCrypto && nativeCrypto.pbkdf2) { if (nodeCrypto && nodeCrypto.pbkdf2) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
nativeCrypto.pbkdf2(key, salt, iter, len, alg, function(err, key) { nodeCrypto.pbkdf2(key, salt, iter, len, alg, wrap(resolve, reject));
if (err)
return reject(err);
resolve(key);
});
}); });
} }
@ -244,11 +241,7 @@ crypto.scryptAsync = function _scrypt(passwd, salt, N, r, p, len) {
salt = new Buffer(salt, 'utf8'); salt = new Buffer(salt, 'utf8');
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
scryptAsync(passwd, salt, N, r, p, len, function(err, key) { scryptAsync(passwd, salt, N, r, p, len, wrap(resolve, reject));
if (err)
return reject(err);
resolve(key);
});
}); });
}; };
@ -270,28 +263,26 @@ crypto.derive = function derive(passphrase) {
* @param {Function} callback * @param {Function} callback
*/ */
crypto.encrypt = function encrypt(data, passphrase, iv) { crypto.encrypt = co(function* encrypt(data, passphrase, iv) {
return spawn(function *() { var key;
var key;
assert(Buffer.isBuffer(data)); assert(Buffer.isBuffer(data));
assert(passphrase, 'No passphrase.'); assert(passphrase, 'No passphrase.');
assert(Buffer.isBuffer(iv)); assert(Buffer.isBuffer(iv));
key = yield crypto.derive(passphrase); key = yield crypto.derive(passphrase);
try {
data = crypto.encipher(data, key, iv);
} catch (e) {
key.fill(0);
throw e;
}
try {
data = crypto.encipher(data, key, iv);
} catch (e) {
key.fill(0); key.fill(0);
throw e;
}
return data; key.fill(0);
});
}; return data;
});
/** /**
* Encrypt with aes-256-cbc. * Encrypt with aes-256-cbc.
@ -304,10 +295,10 @@ crypto.encrypt = function encrypt(data, passphrase, iv) {
crypto.encipher = function encipher(data, key, iv) { crypto.encipher = function encipher(data, key, iv) {
var cipher; var cipher;
if (!nativeCrypto) if (!nodeCrypto)
return aes.cbc.encrypt(data, key, iv); return aes.cbc.encrypt(data, key, iv);
cipher = nativeCrypto.createCipheriv('aes-256-cbc', key, iv); cipher = nodeCrypto.createCipheriv('aes-256-cbc', key, iv);
return Buffer.concat([ return Buffer.concat([
cipher.update(data), cipher.update(data),
@ -323,28 +314,26 @@ crypto.encipher = function encipher(data, key, iv) {
* @param {Function} callback * @param {Function} callback
*/ */
crypto.decrypt = function decrypt(data, passphrase, iv) { crypto.decrypt = co(function* decrypt(data, passphrase, iv) {
return spawn(function *() { var key;
var key;
assert(Buffer.isBuffer(data)); assert(Buffer.isBuffer(data));
assert(passphrase, 'No passphrase.'); assert(passphrase, 'No passphrase.');
assert(Buffer.isBuffer(iv)); assert(Buffer.isBuffer(iv));
key = yield crypto.derive(passphrase); key = yield crypto.derive(passphrase);
try {
data = crypto.decipher(data, key, iv);
} catch (e) {
key.fill(0);
throw e;
}
try {
data = crypto.decipher(data, key, iv);
} catch (e) {
key.fill(0); key.fill(0);
throw e;
}
return data; key.fill(0);
});
}; return data;
});
/** /**
* Decrypt with aes-256-cbc. * Decrypt with aes-256-cbc.
@ -357,10 +346,10 @@ crypto.decrypt = function decrypt(data, passphrase, iv) {
crypto.decipher = function decipher(data, key, iv) { crypto.decipher = function decipher(data, key, iv) {
var decipher; var decipher;
if (!nativeCrypto) if (!nodeCrypto)
return aes.cbc.decrypt(data, key, iv); return aes.cbc.decrypt(data, key, iv);
decipher = nativeCrypto.createDecipheriv('aes-256-cbc', key, iv); decipher = nodeCrypto.createDecipheriv('aes-256-cbc', key, iv);
return Buffer.concat([ return Buffer.concat([
decipher.update(data), decipher.update(data),

View File

@ -12,7 +12,7 @@ var assert = utils.assert;
var AsyncObject = require('../utils/async'); var AsyncObject = require('../utils/async');
var spawn = require('../utils/spawn'); var spawn = require('../utils/spawn');
var co = spawn.co; var co = spawn.co;
var P = utils.P; var wrap = spawn.wrap;
var VERSION_ERROR; var VERSION_ERROR;
/** /**
@ -66,7 +66,7 @@ utils.inherits(LowlevelUp, AsyncObject);
LowlevelUp.prototype._open = function open() { LowlevelUp.prototype._open = function open() {
var self = this; var self = this;
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
self.binding.open(self.options, P(resolve, reject)); self.binding.open(self.options, wrap(resolve, reject));
}); });
}; };
@ -79,7 +79,7 @@ LowlevelUp.prototype._open = function open() {
LowlevelUp.prototype._close = function close() { LowlevelUp.prototype._close = function close() {
var self = this; var self = this;
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
self.binding.close(P(resolve, reject)); self.binding.close(wrap(resolve, reject));
}); });
}; };
@ -98,7 +98,7 @@ LowlevelUp.prototype.destroy = function destroy() {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
if (!self.backend.destroy) if (!self.backend.destroy)
return utils.asyncify(reject)(new Error('Cannot destroy.')); return utils.asyncify(reject)(new Error('Cannot destroy.'));
self.backend.destroy(self.location, P(resolve, reject)); self.backend.destroy(self.location, wrap(resolve, reject));
}); });
}; };
@ -117,7 +117,7 @@ LowlevelUp.prototype.repair = function repair() {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
if (!self.backend.repair) if (!self.backend.repair)
return utils.asyncify(reject)(new Error('Cannot repair.')); return utils.asyncify(reject)(new Error('Cannot repair.'));
self.backend.repair(self.location, P(resolve, reject)); self.backend.repair(self.location, wrap(resolve, reject));
}); });
}; };
@ -138,7 +138,7 @@ LowlevelUp.prototype.backup = function backup(path) {
return this.clone(path); return this.clone(path);
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
self.binding.backup(path, P(resolve, reject)); self.binding.backup(path, wrap(resolve, reject));
}); });
}; };
@ -178,7 +178,7 @@ LowlevelUp.prototype.put = function put(key, value, options) {
var self = this; var self = this;
assert(this.loaded, 'Cannot use database before it is loaded.'); assert(this.loaded, 'Cannot use database before it is loaded.');
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
self.binding.put(key, value, options || {}, P(resolve, reject)); self.binding.put(key, value, options || {}, wrap(resolve, reject));
}); });
}; };
@ -193,7 +193,7 @@ LowlevelUp.prototype.del = function del(key, options) {
var self = this; var self = this;
assert(this.loaded, 'Cannot use database before it is loaded.'); assert(this.loaded, 'Cannot use database before it is loaded.');
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
self.binding.del(key, options || {}, P(resolve, reject)); self.binding.del(key, options || {}, wrap(resolve, reject));
}); });
}; };
@ -214,7 +214,7 @@ LowlevelUp.prototype.batch = function batch(ops, options) {
return new Batch(this); return new Batch(this);
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
self.binding.batch(ops, options || {}, P(resolve, reject)); self.binding.batch(ops, options || {}, wrap(resolve, reject));
}); });
}; };
@ -281,7 +281,7 @@ LowlevelUp.prototype.approximateSize = function approximateSize(start, end) {
if (!self.binding.approximateSize) if (!self.binding.approximateSize)
return utils.asyncify(reject)(new Error('Cannot get size.')); return utils.asyncify(reject)(new Error('Cannot get size.'));
self.binding.approximateSize(start, end, P(resolve, reject)); self.binding.approximateSize(start, end, wrap(resolve, reject));
}); });
}; };
@ -439,7 +439,7 @@ Batch.prototype.del = function del(key) {
Batch.prototype.write = function write() { Batch.prototype.write = function write() {
var self = this; var self = this;
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
self.batch.write(P(resolve, reject)); self.batch.write(wrap(resolve, reject));
}); });
}; };
@ -465,7 +465,7 @@ Iterator.prototype.next = function() {
} }
if (key === undefined && value === undefined) { if (key === undefined && value === undefined) {
self.iter.end(P(resolve, reject)); self.iter.end(wrap(resolve, reject));
return; return;
} }
@ -481,7 +481,7 @@ Iterator.prototype.seek = function seek(key) {
Iterator.prototype.end = function end() { Iterator.prototype.end = function end() {
var self = this; var self = this;
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
self.iter.end(P(resolve, reject)); self.iter.end(wrap(resolve, reject));
}); });
}; };

View File

@ -4145,13 +4145,13 @@ function reverseEndian(data) {
function writeFile(file, data) { function writeFile(file, data) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
fs.writeFile(file, data, utils.P(resolve, reject)); fs.writeFile(file, data, spawn.wrap(resolve, reject));
}); });
} }
function readFile(file, enc) { function readFile(file, enc) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
fs.readFile(file, enc, utils.P(resolve, reject)); fs.readFile(file, enc, spawn.wrap(resolve, reject));
}); });
} }

View File

@ -189,7 +189,7 @@ Mempool.prototype.addBlock = co(function* addBlock(block) {
// There may be a locktime in a TX that is now valid. // There may be a locktime in a TX that is now valid.
this.rejects.reset(); this.rejects.reset();
yield utils.wait(); yield spawn.wait();
unlock(); unlock();
}); });

View File

@ -160,61 +160,57 @@ Miner.prototype._close = function close() {
* @param {Number?} version - Custom block version. * @param {Number?} version - Custom block version.
*/ */
Miner.prototype.start = function start() { Miner.prototype.start = co(function* start() {
var self = this; var self = this;
spawn(function *() { var attempt, block;
var attempt, block;
this.stop(); this.stop();
this.running = true; this.running = true;
// Create a new block and start hashing // Create a new block and start hashing
try { try {
attempt = yield this.createBlock(); attempt = yield this.createBlock();
} catch (e) { } catch (e) {
this.emit('error', e); this.emit('error', e);
return; return;
} }
if (!this.running)
return;
this.attempt = attempt;
attempt.on('status', function(status) {
self.emit('status', status);
});
try {
block = yield attempt.mineAsync();
} catch (e) {
if (!this.running) if (!this.running)
return; return;
this.emit('error', e);
return this.start();
}
this.attempt = attempt; // Add our block to the chain
try {
yield this.chain.add(block);
} catch (err) {
if (err.type === 'VerifyError')
this.logger.warning('%s could not be added to chain.', block.rhash);
this.emit('error', err);
this.start();
return;
}
attempt.on('status', function(status) { // Emit our newly found block
self.emit('status', status); this.emit('block', block);
});
try { // `tip` will now be emitted by chain
block = yield attempt.mineAsync(); // and the whole process starts over.
} catch (e) { });
if (!this.running)
return;
this.emit('error', e);
return this.start();
}
// Add our block to the chain
try {
yield this.chain.add(block);
} catch (err) {
if (err.type === 'VerifyError')
this.logger.warning('%s could not be added to chain.', block.rhash);
this.emit('error', err);
this.start();
return;
}
// Emit our newly found block
this.emit('block', block);
// `tip` will now be emitted by chain
// and the whole process starts over.
}, this).catch(function(err) {
self.emit('error', err);
});
};
/** /**
* Stop mining. * Stop mining.

View File

@ -1119,66 +1119,71 @@ Peer.prototype._handleUTXOs = function _handleUTXOs(utxos) {
* @private * @private
*/ */
Peer.prototype._handleGetUTXOs = function _handleGetUTXOs(packet) { Peer.prototype._handleGetUTXOs = co(function* _handleGetUTXOs(packet) {
var self = this; var unlock = yield this._lock();
spawn(function *() { var i, utxos, prevout, hash, index, coin;
var unlock = yield this._lock();
var i, utxos, prevout, hash, index, coin;
if (!this.chain.synced) if (!this.chain.synced)
return unlock(); return unlock();
if (this.options.selfish) if (this.options.selfish)
return unlock(); return unlock();
if (this.chain.db.options.spv) if (this.chain.db.options.spv)
return unlock(); return unlock();
if (packet.prevout.length > 15) if (packet.prevout.length > 15)
return unlock(); return unlock();
utxos = new packets.GetUTXOsPacket(); utxos = new packets.GetUTXOsPacket();
for (i = 0; i < packet.prevout.length; i++) { for (i = 0; i < packet.prevout.length; i++) {
prevout = packet.prevout[i]; prevout = packet.prevout[i];
hash = prevout.hash; hash = prevout.hash;
index = prevout.index; index = prevout.index;
if (this.mempool && packet.mempool) { if (this.mempool && packet.mempool) {
try {
coin = this.mempool.getCoin(hash, index); coin = this.mempool.getCoin(hash, index);
} catch (e) {
if (coin) { this.emit('error', e);
utxos.hits.push(1); return;
utxos.coins.push(coin);
continue;
}
if (this.mempool.isSpent(hash, index)) {
utxos.hits.push(0);
continue;
}
} }
coin = yield this.chain.db.getCoin(hash, index); if (coin) {
utxos.hits.push(1);
if (!coin) { utxos.coins.push(coin);
utxos.hits.push(0);
continue; continue;
} }
utxos.hits.push(1); if (this.mempool.isSpent(hash, index)) {
utxos.coins.push(coin); utxos.hits.push(0);
continue;
}
} }
utxos.height = this.chain.height; try {
utxos.tip = this.chain.tip.hash; coin = yield this.chain.db.getCoin(hash, index);
} catch (e) {
this.emit('error', e);
return;
}
this.send(utxos); if (!coin) {
unlock(); utxos.hits.push(0);
}, this).catch(function(err) { continue;
self.emit('error', err); }
});
}; utxos.hits.push(1);
utxos.coins.push(coin);
}
utxos.height = this.chain.height;
utxos.tip = this.chain.tip.hash;
this.send(utxos);
unlock();
});
/** /**
* Handle `havewitness` packet. * Handle `havewitness` packet.
@ -1197,55 +1202,50 @@ Peer.prototype._handleHaveWitness = function _handleHaveWitness(packet) {
* @param {GetHeadersPacket} * @param {GetHeadersPacket}
*/ */
Peer.prototype._handleGetHeaders = function _handleGetHeaders(packet) { Peer.prototype._handleGetHeaders = co(function* _handleGetHeaders(packet) {
var self = this; var unlock = yield this._lock();
spawn(function *() { var headers = [];
var unlock = yield this._lock(); var hash, entry;
var headers = [];
var hash, entry;
if (!this.chain.synced) if (!this.chain.synced)
return unlock(); return unlock();
if (this.options.selfish) if (this.options.selfish)
return unlock(); return unlock();
if (this.chain.db.options.spv) if (this.chain.db.options.spv)
return unlock(); return unlock();
if (this.chain.db.options.prune) if (this.chain.db.options.prune)
return unlock(); return unlock();
if (packet.locator.length > 0) {
hash = yield this.chain.findLocator(packet.locator);
if (hash)
hash = yield this.chain.db.getNextHash(hash);
} else {
hash = packet.stop;
}
if (packet.locator.length > 0) {
hash = yield this.chain.findLocator(packet.locator);
if (hash) if (hash)
entry = yield this.chain.db.get(hash); hash = yield this.chain.db.getNextHash(hash);
} else {
hash = packet.stop;
}
while (entry) { if (hash)
headers.push(entry.toHeaders()); entry = yield this.chain.db.get(hash);
if (headers.length === 2000) while (entry) {
break; headers.push(entry.toHeaders());
if (entry.hash === packet.stop) if (headers.length === 2000)
break; break;
entry = yield entry.getNext(); if (entry.hash === packet.stop)
} break;
this.sendHeaders(headers); entry = yield entry.getNext();
}
unlock(); this.sendHeaders(headers);
}, this).catch(function(err) {
self.emit('error', err); unlock();
}); });
};
/** /**
* Handle `getblocks` packet. * Handle `getblocks` packet.
@ -1253,50 +1253,45 @@ Peer.prototype._handleGetHeaders = function _handleGetHeaders(packet) {
* @param {GetBlocksPacket} * @param {GetBlocksPacket}
*/ */
Peer.prototype._handleGetBlocks = function _handleGetBlocks(packet) { Peer.prototype._handleGetBlocks = co(function* _handleGetBlocks(packet) {
var self = this; var unlock = yield this._lock();
spawn(function *() { var blocks = [];
var unlock = yield this._lock(); var hash;
var blocks = [];
var hash;
if (!this.chain.synced) if (!this.chain.synced)
return unlock(); return unlock();
if (this.options.selfish) if (this.options.selfish)
return unlock(); return unlock();
if (this.chain.db.options.spv) if (this.chain.db.options.spv)
return unlock(); return unlock();
if (this.chain.db.options.prune) if (this.chain.db.options.prune)
return unlock(); return unlock();
hash = yield this.chain.findLocator(packet.locator); hash = yield this.chain.findLocator(packet.locator);
if (hash) if (hash)
hash = yield this.chain.db.getNextHash(hash); hash = yield this.chain.db.getNextHash(hash);
while (hash) { while (hash) {
blocks.push(new InvItem(constants.inv.BLOCK, hash)); blocks.push(new InvItem(constants.inv.BLOCK, hash));
if (hash === packet.stop) if (hash === packet.stop)
break; break;
if (blocks.length === 500) { if (blocks.length === 500) {
this.hashContinue = hash; this.hashContinue = hash;
break; break;
}
hash = yield this.chain.db.getNextHash(hash);
} }
this.sendInv(blocks); hash = yield this.chain.db.getNextHash(hash);
unlock(); }
}, this).catch(function(err) {
self.emit('error', err); this.sendInv(blocks);
}); unlock();
}; });
/** /**
* Handle `version` packet. * Handle `version` packet.
@ -1480,119 +1475,114 @@ Peer.prototype._getItem = co(function* _getItem(item) {
* @param {GetDataPacket} * @param {GetDataPacket}
*/ */
Peer.prototype._handleGetData = function _handleGetData(packet) { Peer.prototype._handleGetData = co(function* _handleGetData(packet) {
var self = this; var unlock = yield this._lock();
spawn(function *() { var notFound = [];
var unlock = yield this._lock(); var items = packet.items;
var notFound = []; var i, j, item, entry, tx, block;
var items = packet.items;
var i, j, item, entry, tx, block;
if (items.length > 50000) { if (items.length > 50000) {
this.error('getdata size too large (%s).', items.length); this.error('getdata size too large (%s).', items.length);
return; return;
}
for (i = 0; i < items.length; i++) {
item = items[i];
entry = yield this._getItem(item);
if (!entry) {
notFound.push(item);
continue;
} }
for (i = 0; i < items.length; i++) { if (item.isTX()) {
item = items[i]; tx = entry;
entry = yield this._getItem(item);
if (!entry) { // Coinbases are an insta-ban from any node.
// This should technically never happen, but
// it's worth keeping here just in case. A
// 24-hour ban from any node is rough.
if (tx.isCoinbase()) {
notFound.push(item); notFound.push(item);
this.logger.warning('Failsafe: tried to relay a coinbase.');
continue; continue;
} }
if (item.isTX()) { this.send(new packets.TXPacket(tx, item.hasWitness()));
tx = entry;
// Coinbases are an insta-ban from any node. continue;
// This should technically never happen, but }
// it's worth keeping here just in case. A
// 24-hour ban from any node is rough. block = entry;
if (tx.isCoinbase()) {
switch (item.type) {
case constants.inv.BLOCK:
case constants.inv.WITNESS_BLOCK:
this.send(new packets.BlockPacket(block, item.hasWitness()));
break;
case constants.inv.FILTERED_BLOCK:
case constants.inv.WITNESS_FILTERED_BLOCK:
if (!this.spvFilter) {
notFound.push(item); notFound.push(item);
this.logger.warning('Failsafe: tried to relay a coinbase.');
continue; continue;
} }
this.send(new packets.TXPacket(tx, item.hasWitness())); block = block.toMerkle(this.spvFilter);
continue; this.send(new packets.MerkleBlockPacket(block));
}
block = entry; for (j = 0; j < block.txs.length; j++) {
tx = block.txs[j];
this.send(new packets.TXPacket(tx, item.hasWitness()));
}
switch (item.type) { break;
case constants.inv.BLOCK: case constants.inv.CMPCT_BLOCK:
case constants.inv.WITNESS_BLOCK: // Fallback to full block.
this.send(new packets.BlockPacket(block, item.hasWitness())); if (block.height < this.chain.tip.height - 10) {
this.send(new packets.BlockPacket(block, false));
break; break;
case constants.inv.FILTERED_BLOCK: }
case constants.inv.WITNESS_FILTERED_BLOCK:
if (!this.spvFilter) { // Try again with a new nonce
notFound.push(item); // if we get a siphash collision.
for (;;) {
try {
block = bcoin.bip152.CompactBlock.fromBlock(block);
} catch (e) {
continue; continue;
} }
block = block.toMerkle(this.spvFilter);
this.send(new packets.MerkleBlockPacket(block));
for (j = 0; j < block.txs.length; j++) {
tx = block.txs[j];
this.send(new packets.TXPacket(tx, item.hasWitness()));
}
break; break;
case constants.inv.CMPCT_BLOCK: }
// Fallback to full block.
if (block.height < this.chain.tip.height - 10) {
this.send(new packets.BlockPacket(block, false));
break;
}
// Try again with a new nonce this.send(new packets.CmpctBlockPacket(block, false));
// if we get a siphash collision. break;
for (;;) { default:
try { this.logger.warning(
block = bcoin.bip152.CompactBlock.fromBlock(block); 'Peer sent an unknown getdata type: %s (%s).',
} catch (e) { item.type,
continue; this.hostname);
} notFound.push(item);
break; continue;
}
this.send(new packets.CmpctBlockPacket(block, false));
break;
default:
this.logger.warning(
'Peer sent an unknown getdata type: %s (%s).',
item.type,
this.hostname);
notFound.push(item);
continue;
}
if (item.hash === this.hashContinue) {
this.sendInv(new InvItem(constants.inv.BLOCK, this.chain.tip.hash));
this.hashContinue = null;
}
} }
this.logger.debug( if (item.hash === this.hashContinue) {
'Served %d items with getdata (notfound=%d) (%s).', this.sendInv(new InvItem(constants.inv.BLOCK, this.chain.tip.hash));
items.length - notFound.length, this.hashContinue = null;
notFound.length, }
this.hostname); }
if (notFound.length > 0) this.logger.debug(
this.send(new packets.NotFoundPacket(notFound)); 'Served %d items with getdata (notfound=%d) (%s).',
items.length - notFound.length,
notFound.length,
this.hostname);
unlock(); if (notFound.length > 0)
}, this).catch(function(err) { this.send(new packets.NotFoundPacket(notFound));
self.emit('error', err);
}); unlock();
}; });
/** /**
* Handle `notfound` packet. * Handle `notfound` packet.

View File

@ -431,7 +431,7 @@ Pool.prototype.listen = function listen() {
}); });
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
self.server.listen(self.port, '0.0.0.0', utils.P(resolve, reject)); self.server.listen(self.port, '0.0.0.0', spawn.wrap(resolve, reject));
}); });
}; };
@ -450,7 +450,7 @@ Pool.prototype.unlisten = function unlisten() {
return; return;
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
self.server.close(utils.P(resolve, reject)); self.server.close(spawn.wrap(resolve, reject));
self.server = null; self.server = null;
}); });
}; };
@ -902,7 +902,7 @@ Pool.prototype._handleBlock = co(function* _handleBlock(block, peer) {
this.logger.warning( this.logger.warning(
'Received unrequested block: %s (%s).', 'Received unrequested block: %s (%s).',
block.rhash, peer.hostname); block.rhash, peer.hostname);
return yield utils.wait(); return yield spawn.wait();
} }
try { try {
@ -1062,7 +1062,7 @@ Pool.prototype.createPeer = function createPeer(addr, socket) {
self.startInterval(); self.startInterval();
self.startTimeout(); self.startTimeout();
} }
}).catch(function(err) { }, function(err) {
self.emit('error', err); self.emit('error', err);
}); });
}); });
@ -1081,7 +1081,7 @@ Pool.prototype.createPeer = function createPeer(addr, socket) {
self.startInterval(); self.startInterval();
self.startTimeout(); self.startTimeout();
} }
}).catch(function(err) {; }, function(err) {
self.emit('error', err); self.emit('error', err);
}); });
}); });

View File

@ -10,8 +10,8 @@ var utils = require('../utils/utils');
var spawn = require('../utils/spawn'); var spawn = require('../utils/spawn');
var co = spawn.co; var co = spawn.co;
var assert = utils.assert; var assert = utils.assert;
var wait = spawn.wait;
var EventEmitter = require('events').EventEmitter; var EventEmitter = require('events').EventEmitter;
var wait = utils.wait;
/** /**
* An abstract object that handles state and * An abstract object that handles state and

View File

@ -1,5 +1,20 @@
/*!
* spawn.js - promise and generator control flow for bcoin
* Originally based on yoursnetwork's "asink" module.
* Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
* https://github.com/bcoin-org/bcoin
*/
'use strict'; 'use strict';
var utils = require('./utils');
/**
* Execute an instantiated generator.
* @param {Generator} gen
* @returns {Promise}
*/
function exec(gen) { function exec(gen) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
function step(value, rejection) { function step(value, rejection) {
@ -34,11 +49,27 @@ function exec(gen) {
}); });
} }
/**
* Execute generator function
* with a context and execute.
* @param {GeneratorFunction} generator
* @param {Object} self
* @returns {Promise}
*/
function spawn(generator, self) { function spawn(generator, self) {
var gen = generator.call(self); var gen = generator.call(self);
return exec(gen); return exec(gen);
} }
/**
* Wrap a generator function to be
* executed into a function that
* returns a promise.
* @param {GeneratorFunction}
* @returns {Function}
*/
function co(generator) { function co(generator) {
return function() { return function() {
var gen = generator.apply(this, arguments); var gen = generator.apply(this, arguments);
@ -46,6 +77,157 @@ function co(generator) {
}; };
} }
spawn.co = co; /**
* Wrap a generator function to be
* executed into a function that
* accepts a node.js style callback.
* @param {GeneratorFunction}
* @returns {Function}
*/
function cob(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 - 1);
callback = arguments[arguments.length - 1];
for (i = 0; i < args.length; i++)
args[i] = arguments[i];
gen = generator.apply(this, args);
return cb(exec(gen), callback);
};
}
/**
* Wait for promise to resolve and
* execute a node.js style callback.
* @param {Promise} promise
* @param {Function} callback
*/
function cb(promise, callback) {
promise.then(function(value) {
callback(null, value);
}, function(err) {
callback(err);
});
}
/**
* Wait for a nextTick with a promise.
* @returns {Promise}
*/
function wait() {
return new Promise(function(resolve, reject) {
utils.nextTick(resolve);
});
};
/**
* Wait for a timeout with a promise.
* @param {Number} time
* @returns {Promise}
*/
function timeout(time) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve();
}, time);
});
}
/**
* Wrap `resolve` and `reject` into
* a node.js style callback.
* @param {Function} resolve
* @param {Function} reject
* @returns {Function}
*/
function wrap(resolve, reject) {
return function(err, result) {
if (err)
return reject(err);
resolve(result);
};
}
/**
* Call a function that accepts node.js
* style callbacks, wrap with a promise.
* @param {Function} func
* @returns {Promise}
*/
function call(func) {
var args = new Array(Math.max(0, arguments.length - 1));
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(self, args);
});
}
/**
* Wrap a function that accepts node.js
* style callbacks into a function that
* returns a promise.
* @param {Function} func
* @param {Object?} self
* @returns {Function}
*/
function promisify(func, self) {
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);
});
};
}
/*
* Expose
*/
exports = spawn;
exports.exec = exec;
exports.spawn = spawn;
exports.co = co;
exports.cob = cob;
exports.cb = cb;
exports.wait = wait;
exports.timeout = timeout;
exports.wrap = wrap;
exports.call = call;
exports.promisify = promisify;
module.exports = spawn; module.exports = spawn;

View File

@ -326,28 +326,6 @@ if (typeof setImmediate === 'function') {
}; };
} }
utils.wait = function wait() {
return new Promise(function(resolve, reject) {
utils.nextTick(resolve);
});
};
utils.timeout = function timeout(time) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve();
}, time);
});
};
utils.P = function P(resolve, reject) {
return function(err, result) {
if (err)
return reject(err);
resolve(result);
};
};
/** /**
* Wrap a function in a `nextTick`. * Wrap a function in a `nextTick`.
* @param {Function} callback * @param {Function} callback

View File

@ -576,7 +576,7 @@ Worker.prototype._execute = function _execute(method, args, timeout, callback) {
Worker.prototype.execute = function execute(method, args, timeout) { Worker.prototype.execute = function execute(method, args, timeout) {
var self = this; var self = this;
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
self._execute(method, args, timeout, utils.P(resolve, reject)); self._execute(method, args, timeout, spawn.wrap(resolve, reject));
}); });
}; };

View File

@ -7,6 +7,8 @@ var utils = bcoin.utils;
var crypto = require('../lib/crypto/crypto'); var crypto = require('../lib/crypto/crypto');
var assert = require('assert'); var assert = require('assert');
var opcodes = constants.opcodes; var opcodes = constants.opcodes;
var spawn = require('../lib/utils/spawn');
var c = require('../lib/utils/spawn').cb;
describe('Chain', function() { describe('Chain', function() {
var chain, wallet, node, miner, walletdb; var chain, wallet, node, miner, walletdb;
@ -14,22 +16,6 @@ describe('Chain', function() {
this.timeout(5000); this.timeout(5000);
function c(p, cb) {
var called = false;
p.then(function(result) {
called = true;
cb(null, result);
}).catch(function(err) {
if (called) {
utils.nextTick(function() {
throw err;
});
return;
}
cb(err);
});
}
node = new bcoin.fullnode({ db: 'memory' }); node = new bcoin.fullnode({ db: 'memory' });
chain = node.chain; chain = node.chain;
walletdb = node.walletdb; walletdb = node.walletdb;
@ -250,7 +236,7 @@ describe('Chain', function() {
assert.ifError(err); assert.ifError(err);
c(chain.db.scan(null, hashes, function *(block, txs) { c(chain.db.scan(null, hashes, function *(block, txs) {
total += txs.length; total += txs.length;
yield utils.wait(); yield spawn.wait();
}), function(err) { }), function(err) {
assert.ifError(err); assert.ifError(err);
assert.equal(total, 25); assert.equal(total, 25);

View File

@ -8,6 +8,7 @@ var utils = bcoin.utils;
var crypto = require('../lib/crypto/crypto'); var crypto = require('../lib/crypto/crypto');
var assert = require('assert'); var assert = require('assert');
var scriptTypes = constants.scriptTypes; var scriptTypes = constants.scriptTypes;
var c = require('../lib/utils/spawn').cb;
var dummyInput = { var dummyInput = {
prevout: { prevout: {
@ -28,22 +29,6 @@ var dummyInput = {
sequence: 0xffffffff sequence: 0xffffffff
}; };
function c(p, cb) {
var called = false;
p.then(function(result) {
called = true;
cb(null, result);
}).catch(function(err) {
if (called) {
utils.nextTick(function() {
throw err;
});
return;
}
cb(err);
});
}
describe('HTTP', function() { describe('HTTP', function() {
var request = bcoin.http.request; var request = bcoin.http.request;
var w, addr, hash; var w, addr, hash;

View File

@ -7,22 +7,7 @@ var utils = bcoin.utils;
var crypto = require('../lib/crypto/crypto'); var crypto = require('../lib/crypto/crypto');
var assert = require('assert'); var assert = require('assert');
var opcodes = constants.opcodes; var opcodes = constants.opcodes;
var c = require('../lib/utils/spawn').cb;
function c(p, cb) {
var called = false;
p.then(function(result) {
called = true;
cb(null, result);
}).catch(function(err) {
if (called) {
utils.nextTick(function() {
throw err;
});
return;
}
cb(err);
});
}
describe('Mempool', function() { describe('Mempool', function() {
this.timeout(5000); this.timeout(5000);

View File

@ -9,6 +9,7 @@ var crypto = require('../lib/crypto/crypto');
var spawn = require('../lib/utils/spawn'); var spawn = require('../lib/utils/spawn');
var assert = require('assert'); var assert = require('assert');
var scriptTypes = constants.scriptTypes; var scriptTypes = constants.scriptTypes;
var c = require('../lib/utils/spawn').cb;
var FAKE_SIG = new Buffer([0,0,0,0,0,0,0,0,0]); var FAKE_SIG = new Buffer([0,0,0,0,0,0,0,0,0]);
@ -53,22 +54,6 @@ assert.range = function range(value, lo, hi, message) {
} }
}; };
function c(p, cb) {
var called = false;
p.then(function(result) {
called = true;
cb(null, result);
}).catch(function(err) {
if (called) {
utils.nextTick(function() {
throw err;
});
return;
}
cb(err);
});
}
describe('Wallet', function() { describe('Wallet', function() {
var walletdb = new bcoin.walletdb({ var walletdb = new bcoin.walletdb({
name: 'wallet-test', name: 'wallet-test',