http: more validation.
This commit is contained in:
parent
f42d60769b
commit
59d4fa22ce
@ -233,6 +233,7 @@ Address.fromRaw = function fromRaw(data) {
|
||||
*/
|
||||
|
||||
Address.prototype.fromBase58 = function fromBase58(data) {
|
||||
assert(typeof data === 'string');
|
||||
return this.fromRaw(utils.fromBase58(data));
|
||||
};
|
||||
|
||||
|
||||
@ -306,19 +306,8 @@ Fullnode.prototype._open = function open(callback) {
|
||||
self.walletdb.rescan(self.chain.db, next);
|
||||
},
|
||||
function(next) {
|
||||
var i;
|
||||
self.wallet.getUnconfirmed(function(err, txs) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
if (txs.length > 0)
|
||||
self.logger.info('Rebroadcasting %d transactions.', txs.length);
|
||||
|
||||
for (i = 0; i < txs.length; i++)
|
||||
self.pool.broadcast(txs[i]);
|
||||
|
||||
next();
|
||||
});
|
||||
// Rebroadcast pending transactions.
|
||||
self.wallet.resend(next);
|
||||
},
|
||||
function(next) {
|
||||
if (!self.http)
|
||||
|
||||
@ -287,7 +287,7 @@ HTTPClient.prototype.getInfo = function getInfo(callback) {
|
||||
*/
|
||||
|
||||
HTTPClient.prototype.getCoinsByAddress = function getCoinsByAddress(address, callback) {
|
||||
var body = { addresses: address };
|
||||
var body = { address: address };
|
||||
|
||||
return this._post('/coin/address', body, callback);
|
||||
};
|
||||
@ -312,7 +312,7 @@ HTTPClient.prototype.getCoin = function getCoin(hash, index, callback) {
|
||||
*/
|
||||
|
||||
HTTPClient.prototype.getTXByAddress = function getTXByAddress(address, callback) {
|
||||
var body = { addresses: address };
|
||||
var body = { address: address };
|
||||
|
||||
return this._post('/tx/address', body, callback);
|
||||
};
|
||||
|
||||
@ -43,9 +43,13 @@ function HTTPServer(options) {
|
||||
assert(this.node, 'HTTP requires a Node.');
|
||||
|
||||
this.network = this.node.network;
|
||||
this.walletdb = this.node.walletdb;
|
||||
this.chain = this.node.chain;
|
||||
this.mempool = this.node.mempool;
|
||||
this.pool = this.node.pool;
|
||||
this.fees = this.node.fees;
|
||||
this.miner = this.node.miner;
|
||||
this.wallet = this.node.wallet;
|
||||
this.walletdb = this.node.walletdb;
|
||||
this.logger = options.logger || this.node.logger;
|
||||
this.loaded = false;
|
||||
this.apiKey = options.apiKey;
|
||||
@ -102,8 +106,8 @@ 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.node.chain.height + '');
|
||||
res.setHeader('X-Bcoin-Tip', utils.revHex(self.node.chain.tip.hash));
|
||||
res.setHeader('X-Bcoin-Height', self.chain.height + '');
|
||||
res.setHeader('X-Bcoin-Tip', utils.revHex(self.chain.tip.hash));
|
||||
|
||||
next();
|
||||
});
|
||||
@ -157,109 +161,184 @@ HTTPServer.prototype._init = function _init() {
|
||||
});
|
||||
|
||||
this.use(function(req, res, next, send) {
|
||||
var params, options;
|
||||
var i, params, options, output, address;
|
||||
|
||||
if (req.method === 'POST'
|
||||
&& req.pathname === '/') {
|
||||
if (req.method === 'POST' && req.pathname === '/') {
|
||||
assert(typeof req.body.method === 'string', 'Method must be a string.');
|
||||
assert(Array.isArray(req.body.params), 'Params must be an array.');
|
||||
req.options = {};
|
||||
return next();
|
||||
}
|
||||
|
||||
params = utils.merge({}, req.params, req.query, req.body);
|
||||
params = {};
|
||||
options = {};
|
||||
|
||||
softMerge(params, req.params, true);
|
||||
softMerge(params, req.query, true);
|
||||
softMerge(params, req.body);
|
||||
|
||||
self.logger.debug('Params:');
|
||||
self.logger.debug(params);
|
||||
|
||||
if (params.id) {
|
||||
assert(params.id !== '!all');
|
||||
assert(typeof params.id === 'string', 'ID must be a string.');
|
||||
options.id = params.id;
|
||||
}
|
||||
|
||||
if (params.hash) {
|
||||
if (params.hash.length !== 64)
|
||||
options.height = params.hash >>> 0;
|
||||
else
|
||||
assert(typeof params.hash === 'string', 'Hash must be a string.');
|
||||
if (params.hash.length !== 64) {
|
||||
options.height = Number(params.hash);
|
||||
assert(utils.isUInt32(options.height), 'Hash must be a number.');
|
||||
} else {
|
||||
options.hash = utils.revHex(params.hash);
|
||||
}
|
||||
}
|
||||
|
||||
if (params.index != null)
|
||||
options.index = params.index >>> 0;
|
||||
if (params.index != null) {
|
||||
options.index = Number(params.index);
|
||||
assert(utils.isUInt32(options.index), 'Index must be a number.');
|
||||
}
|
||||
|
||||
if (params.height != null)
|
||||
options.height = params.height >>> 0;
|
||||
if (params.height != null) {
|
||||
options.height = Number(params.height);
|
||||
assert(utils.isUInt32(options.height), 'Height must be a number.');
|
||||
}
|
||||
|
||||
if (params.start != null)
|
||||
options.start = params.start >>> 0;
|
||||
if (params.start != null) {
|
||||
options.start = Number(params.start);
|
||||
assert(utils.isUInt32(options.height), 'Start must be a number.');
|
||||
}
|
||||
|
||||
if (params.end != null)
|
||||
options.end = params.end >>> 0;
|
||||
if (params.end != null) {
|
||||
options.end = Number(params.end);
|
||||
assert(utils.isUInt32(options.end), 'End must be a number.');
|
||||
}
|
||||
|
||||
if (params.limit != null)
|
||||
options.limit = params.limit >>> 0;
|
||||
if (params.limit != null) {
|
||||
options.limit = Number(params.limit);
|
||||
assert(utils.isUInt32(options.limit), 'Limit must be a number.');
|
||||
}
|
||||
|
||||
if (params.address) {
|
||||
params.addresses = params.address;
|
||||
options.address = params.address;
|
||||
if (params.age != null) {
|
||||
options.age = Number(params.age);
|
||||
assert(utils.isUInt32(options.age), 'Age must be a number.');
|
||||
}
|
||||
|
||||
if (params.rate)
|
||||
options.rate = utils.satoshi(params.rate);
|
||||
|
||||
if (params.subtractFee)
|
||||
options.subtractFee = params.subtractFee;
|
||||
|
||||
if (Array.isArray(params.outputs)) {
|
||||
options.outputs = params.outputs.map(function(output) {
|
||||
return {
|
||||
address: output.address,
|
||||
script: decodeScript(output.script),
|
||||
value: utils.satoshi(output.value)
|
||||
};
|
||||
});
|
||||
if (params.m != null) {
|
||||
options.m = Number(params.m);
|
||||
assert(utils.isUInt32(options.m), 'm must be a number.');
|
||||
}
|
||||
|
||||
if (params.addresses) {
|
||||
if (typeof params.addresses === 'string')
|
||||
options.addresses = params.addresses.split(',');
|
||||
else
|
||||
options.addresses = params.addresses;
|
||||
if (params.n != null) {
|
||||
options.n = Number(params.n);
|
||||
assert(utils.isUInt32(options.n), 'n must be a number.');
|
||||
}
|
||||
|
||||
if (params.tx) {
|
||||
try {
|
||||
if (typeof params.tx === 'object')
|
||||
options.tx = bcoin.tx.fromJSON(params.tx);
|
||||
else
|
||||
options.tx = bcoin.tx.fromRaw(params.tx, 'hex');
|
||||
} catch (e) {
|
||||
return next(e);
|
||||
if (params.blocks != null) {
|
||||
options.blocks = Number(params.blocks);
|
||||
assert(utils.isUInt32(options.blocks), 'Blocks must be a number.');
|
||||
}
|
||||
|
||||
if (params.subtractFee != null) {
|
||||
if (typeof params.subtractFee === 'number') {
|
||||
options.subtractFee = params.subtractFee;
|
||||
assert(utils.isUInt32(options.subtractFee), 'subtractFee must be a number.');
|
||||
} else if (params.subtractFee === 'true') {
|
||||
options.subtractFee = true;
|
||||
} else {
|
||||
assert(typeof options.subtractFee === 'boolean', 'subtractFee must be a boolean.');
|
||||
options.subtractFee = params.subtractFee;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof params.account === 'string')
|
||||
options.account = params.account || null;
|
||||
else if (typeof params.account === 'number')
|
||||
options.account = params.account;
|
||||
if (params.outputs) {
|
||||
assert(Array.isArray(params.outputs), 'Outputs must be an array.');
|
||||
options.outputs = [];
|
||||
for (i = 0; i < params.outputs.length; i++) {
|
||||
output = params.outputs[i];
|
||||
|
||||
if (params.name)
|
||||
options.name = params.name;
|
||||
if (output.address)
|
||||
assert(typeof output.address === 'string', 'Address must be a string.');
|
||||
else if (output.script)
|
||||
assert(typeof output.script === 'string', 'Script must be a string.');
|
||||
else
|
||||
assert(false, 'No address or script present.');
|
||||
|
||||
if (params.age)
|
||||
options.age = params.age >>> 0;
|
||||
|
||||
if (params.key)
|
||||
params.keys = params.key;
|
||||
|
||||
if (params.keys) {
|
||||
if (typeof params.keys === 'string')
|
||||
options.keys = params.keys.split(',');
|
||||
else
|
||||
options.keys = params.keys;
|
||||
options.outputs.push({
|
||||
address: output.address
|
||||
? bcoin.address.fromBase58(output.address)
|
||||
: null,
|
||||
script: output.script
|
||||
? bcoin.script.fromRaw(output.script, 'hex')
|
||||
: null,
|
||||
value: utils.satoshi(output.value)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (params.passphrase)
|
||||
if (params.address) {
|
||||
if (Array.isArray(options.address)) {
|
||||
options.address = [];
|
||||
for (i = 0; i < params.address.length; i++) {
|
||||
address = params.address[i];
|
||||
assert(typeof address === 'string', 'Address must be a string.');
|
||||
address = bcoin.address.fromBase58(address);
|
||||
}
|
||||
} else {
|
||||
assert(typeof params.address === 'string', 'Address must be a string.');
|
||||
options.address = bcoin.address.fromBase58(params.address);
|
||||
}
|
||||
}
|
||||
|
||||
if (params.tx) {
|
||||
if (typeof params.tx === 'object') {
|
||||
options.tx = bcoin.tx.fromJSON(params.tx);
|
||||
} else {
|
||||
assert(typeof params.tx === 'string', 'TX must be a hex string.');
|
||||
options.tx = bcoin.tx.fromRaw(params.tx, 'hex');
|
||||
}
|
||||
}
|
||||
|
||||
if (params.account != null) {
|
||||
if (typeof params.account === 'number') {
|
||||
options.account = params.account;
|
||||
assert(utils.isUInt32(options.account), 'Account must be a number.');
|
||||
} else {
|
||||
assert(typeof params.account === 'string', 'Account must be a string.');
|
||||
options.account = params.account;
|
||||
}
|
||||
}
|
||||
|
||||
if (params.type) {
|
||||
assert(typeof params.type === 'string', 'Type must be a string.');
|
||||
options.type = params.type;
|
||||
}
|
||||
|
||||
if (params.name) {
|
||||
assert(typeof params.name === 'string', 'Name must be a string.');
|
||||
options.name = params.name;
|
||||
}
|
||||
|
||||
if (params.key) {
|
||||
assert(typeof params.key === 'string', 'Key must be a string.');
|
||||
options.key = params.key;
|
||||
}
|
||||
|
||||
if (params.old) {
|
||||
assert(typeof params.old === 'string', 'Passphrase must be a string.');
|
||||
assert(params.old.length > 0, 'Passphrase must be a string.');
|
||||
options.old = params.old;
|
||||
}
|
||||
|
||||
if (params.passphrase) {
|
||||
assert(typeof params.passphrase === 'string', 'Passphrase must be a string.');
|
||||
assert(params.passphrase.length > 0, 'Passphrase must be a string.');
|
||||
options.passphrase = params.passphrase;
|
||||
}
|
||||
|
||||
if (params.token) {
|
||||
assert(utils.isHex(params.token), 'API key must be a hex string.');
|
||||
@ -307,19 +386,8 @@ HTTPServer.prototype._init = function _init() {
|
||||
});
|
||||
});
|
||||
|
||||
function decodeScript(script) {
|
||||
if (!script)
|
||||
return;
|
||||
if (typeof script === 'string')
|
||||
return bcoin.script.fromRaw(script, 'hex');
|
||||
return new bcoin.script(script);
|
||||
}
|
||||
|
||||
// JSON RPC
|
||||
this.post('/', function(req, res, next, send) {
|
||||
if (!(req.body.method && req.body.params))
|
||||
return next(new Error('Method not found.'));
|
||||
|
||||
if (!self.rpc) {
|
||||
RPC = require('./rpc');
|
||||
self.rpc = new RPC(self.node);
|
||||
@ -359,23 +427,21 @@ HTTPServer.prototype._init = function _init() {
|
||||
send(200, {
|
||||
version: constants.USER_VERSION,
|
||||
agent: constants.USER_AGENT,
|
||||
services: self.pool.services,
|
||||
network: self.network.type,
|
||||
height: self.node.chain.height,
|
||||
tip: utils.revHex(self.node.chain.tip.hash),
|
||||
peers: self.node.pool.peers.all.length,
|
||||
progress: self.node.chain.getProgress()
|
||||
height: self.chain.height,
|
||||
tip: self.chain.tip.rhash,
|
||||
peers: self.pool.peers.all.length,
|
||||
progress: self.chain.getProgress()
|
||||
});
|
||||
});
|
||||
|
||||
// UTXO by address
|
||||
this.get('/coin/address/:address', function(req, res, next, send) {
|
||||
self.node.getCoinsByAddress(req.options.addresses, function(err, coins) {
|
||||
self.node.getCoinsByAddress(req.options.address, function(err, coins) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
if (!coins.length)
|
||||
return send(404);
|
||||
|
||||
send(200, coins.map(function(coin) {
|
||||
return coin.toJSON();
|
||||
}));
|
||||
@ -397,13 +463,10 @@ HTTPServer.prototype._init = function _init() {
|
||||
|
||||
// Bulk read UTXOs
|
||||
this.post('/coin/address', function(req, res, next, send) {
|
||||
self.node.getCoinsByAddress(req.options.addresses, function(err, coins) {
|
||||
self.node.getCoinsByAddress(req.options.address, function(err, coins) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
if (!coins.length)
|
||||
return send(404);
|
||||
|
||||
send(200, coins.map(function(coin) {
|
||||
return coin.toJSON();
|
||||
}));
|
||||
@ -430,7 +493,7 @@ HTTPServer.prototype._init = function _init() {
|
||||
|
||||
// TX by address
|
||||
this.get('/tx/address/:address', function(req, res, next, send) {
|
||||
self.node.getTXByAddress(req.options.addresses, function(err, txs) {
|
||||
self.node.getTXByAddress(req.options.address, function(err, txs) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
@ -449,13 +512,10 @@ HTTPServer.prototype._init = function _init() {
|
||||
|
||||
// Bulk read TXs
|
||||
this.post('/tx/address', function(req, res, next, send) {
|
||||
self.node.getTXByAddress(req.options.addresses, function(err, txs) {
|
||||
self.node.getTXByAddress(req.options.address, function(err, txs) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
if (!txs.length)
|
||||
return send(404);
|
||||
|
||||
utils.forEachSerial(txs, function(tx, next) {
|
||||
self.node.fillHistory(tx, next);
|
||||
}, function(err) {
|
||||
@ -485,13 +545,10 @@ HTTPServer.prototype._init = function _init() {
|
||||
|
||||
// Mempool snapshot
|
||||
this.get('/mempool', function(req, res, next, send) {
|
||||
self.node.mempool.getHistory(function(err, txs) {
|
||||
self.mempool.getHistory(function(err, txs) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
if (!txs.length)
|
||||
return send(404);
|
||||
|
||||
utils.forEachSerial(txs, function(tx, next) {
|
||||
self.node.fillHistory(tx, next);
|
||||
}, function(err) {
|
||||
@ -515,6 +572,12 @@ HTTPServer.prototype._init = function _init() {
|
||||
});
|
||||
});
|
||||
|
||||
// Estimate fee
|
||||
this.get('/fee', function(req, res, next, send) {
|
||||
var fee = self.fees.estimateFee(req.options.blocks);
|
||||
send(200, { rate: utils.btc(fee) });
|
||||
});
|
||||
|
||||
// Get wallet
|
||||
this.get('/wallet/:id', function(req, res, next, send) {
|
||||
send(200, req.wallet.toJSON());
|
||||
@ -645,6 +708,19 @@ HTTPServer.prototype._init = function _init() {
|
||||
});
|
||||
});
|
||||
|
||||
// Abandon Wallet TX
|
||||
this.del('/wallet/:id/tx/:hash', function(req, res, next, send) {
|
||||
var hash = req.options.hash;
|
||||
var account = req.options.account;
|
||||
|
||||
req.wallet.abandon(hash, function(err) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
send(200, { success: true });
|
||||
});
|
||||
});
|
||||
|
||||
// Add key
|
||||
this.put('/wallet/:id/key', function(req, res, next, send) {
|
||||
var account = req.options.account;
|
||||
@ -672,7 +748,7 @@ HTTPServer.prototype._init = function _init() {
|
||||
// Create address
|
||||
this.post('/wallet/:id/address', function(req, res, next, send) {
|
||||
var account = req.options.account;
|
||||
req.wallet.createAddress(account, false, function(err, address) {
|
||||
req.wallet.createReceive(account, function(err, address) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
@ -832,29 +908,32 @@ HTTPServer.prototype._initIO = function _initIO() {
|
||||
if (!this.server.io)
|
||||
return;
|
||||
|
||||
this.server.on('websocket', function(socket) {
|
||||
socket.bcoin = new ClientSocket(self, socket);
|
||||
socket.bcoin.startTimeout();
|
||||
this.server.on('websocket', function(ws) {
|
||||
var socket = new ClientSocket(self, ws);
|
||||
|
||||
socket.start();
|
||||
|
||||
socket.on('error', function(err) {
|
||||
self.emit('error', err);
|
||||
});
|
||||
|
||||
socket.on('auth', function(apiKey, callback) {
|
||||
callback = utils.ensure(callback);
|
||||
socket.once('auth', function(apiKey, callback) {
|
||||
if (typeof callback !== 'function')
|
||||
return socket.destroy();
|
||||
|
||||
if (!self.apiHash) {
|
||||
self.logger.info('Successful auth.');
|
||||
socket.bcoin.stopTimeout();
|
||||
self.emit('websocket', socket);
|
||||
return callback();
|
||||
socket.stop();
|
||||
|
||||
if (self.apiHash) {
|
||||
if (!utils.ccmp(hash256(apiKey), self.apiHash)) {
|
||||
socket.destroy();
|
||||
return callback({ error: 'Bad key.' });
|
||||
}
|
||||
}
|
||||
|
||||
if (!utils.ccmp(hash256(apiKey), self.apiHash))
|
||||
return callback({ error: 'Bad key.' });
|
||||
socket.auth = true;
|
||||
|
||||
self.logger.info('Successful auth from %s.', socket.host);
|
||||
|
||||
self.logger.info('Successful auth.');
|
||||
socket.bcoin.stopTimeout();
|
||||
self.emit('websocket', socket);
|
||||
|
||||
return callback();
|
||||
@ -869,13 +948,20 @@ HTTPServer.prototype._initIO = function _initIO() {
|
||||
|
||||
this.on('websocket', function(socket) {
|
||||
socket.on('wallet join', function(id, token, callback) {
|
||||
callback = utils.ensure(callback);
|
||||
if (typeof callback !== 'function')
|
||||
return socket.destroy();
|
||||
|
||||
if (typeof id !== 'string')
|
||||
return callback({ error: 'Invalid parameter.' });
|
||||
|
||||
if (!self.options.walletAuth) {
|
||||
socket.join(id);
|
||||
return callback();
|
||||
}
|
||||
|
||||
if (typeof token !== 'string')
|
||||
return callback({ error: 'Invalid parameter.' });
|
||||
|
||||
self.walletdb.auth(id, token, function(err, wallet) {
|
||||
if (err) {
|
||||
self.logger.info('Wallet auth failure for %s: %s.', id, err.message);
|
||||
@ -886,14 +972,22 @@ HTTPServer.prototype._initIO = function _initIO() {
|
||||
return callback({ error: 'Wallet does not exist.' });
|
||||
|
||||
self.logger.info('Successful wallet auth for %s.', id);
|
||||
|
||||
socket.join(id);
|
||||
|
||||
return callback();
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('wallet leave', function(id, callback) {
|
||||
callback = utils.ensure(callback);
|
||||
if (typeof callback !== 'function')
|
||||
return socket.destroy();
|
||||
|
||||
if (typeof id !== 'string')
|
||||
return callback({ error: 'Invalid parameter.' });
|
||||
|
||||
socket.leave(id);
|
||||
|
||||
return callback();
|
||||
});
|
||||
});
|
||||
@ -1034,20 +1128,57 @@ HTTPServer.prototype.listen = function listen(port, host, callback) {
|
||||
*/
|
||||
|
||||
function ClientSocket(server, socket) {
|
||||
if (!(this instanceof ClientSocket))
|
||||
return new ClientSocket(server, socket);
|
||||
|
||||
EventEmitter.call(this);
|
||||
|
||||
this.server = server;
|
||||
this.socket = socket;
|
||||
this.host = socket.conn.remoteAddress;
|
||||
this.timeout = null;
|
||||
this.auth = false;
|
||||
|
||||
this._init();
|
||||
}
|
||||
|
||||
ClientSocket.prototype.startTimeout = function startTimeout() {
|
||||
utils.inherits(ClientSocket, EventEmitter);
|
||||
|
||||
ClientSocket.prototype._init = function _init() {
|
||||
var self = this;
|
||||
this.stopTimeout();
|
||||
var socket = this.socket;
|
||||
var emit = EventEmitter.prototype.emit;
|
||||
var onevent = socket.onevent.bind(socket);
|
||||
socket.onevent = function(packet) {
|
||||
var result = onevent(packet);
|
||||
var args = packet.data || [];
|
||||
emit.apply(self, args);
|
||||
return result;
|
||||
};
|
||||
};
|
||||
|
||||
ClientSocket.prototype.join = function join(id) {
|
||||
this.socket.join(id);
|
||||
};
|
||||
|
||||
ClientSocket.prototype.leave = function leave(id) {
|
||||
this.socket.leave(id);
|
||||
};
|
||||
|
||||
ClientSocket.prototype.emit = function emit() {
|
||||
this.socket.emit.apply(this.socket, arguments);
|
||||
};
|
||||
|
||||
ClientSocket.prototype.start = function start() {
|
||||
var self = this;
|
||||
this.stop();
|
||||
this.timeout = setTimeout(function() {
|
||||
self.timeout = null;
|
||||
self.destroy();
|
||||
}, 60000);
|
||||
};
|
||||
|
||||
ClientSocket.prototype.stopTimeout = function stopTimeout() {
|
||||
ClientSocket.prototype.stop = function stop() {
|
||||
if (this.timeout != null) {
|
||||
clearTimeout(this.timeout);
|
||||
this.timeout = null;
|
||||
@ -1055,6 +1186,7 @@ ClientSocket.prototype.stopTimeout = function stopTimeout() {
|
||||
};
|
||||
|
||||
ClientSocket.prototype.destroy = function() {
|
||||
this.stop();
|
||||
this.socket.disconnect();
|
||||
};
|
||||
|
||||
@ -1070,6 +1202,17 @@ function hash256(data) {
|
||||
return utils.hash256(new Buffer(data, 'utf8'));
|
||||
}
|
||||
|
||||
function softMerge(a, b, soft) {
|
||||
var keys = Object.keys(b);
|
||||
var i, key, value;
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
key = keys[i];
|
||||
value = b[key];
|
||||
if (!soft || value)
|
||||
a[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
@ -212,19 +212,8 @@ SPVNode.prototype._open = function open(callback) {
|
||||
});
|
||||
},
|
||||
function(next) {
|
||||
var i;
|
||||
self.wallet.getUnconfirmed(function(err, txs) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
if (txs.length > 0)
|
||||
self.logger.info('Rebroadcasting %d transactions.', txs.length);
|
||||
|
||||
for (i = 0; i < txs.length; i++)
|
||||
self.pool.broadcast(txs[i]);
|
||||
|
||||
next();
|
||||
});
|
||||
// Rebroadcast pending transactions.
|
||||
self.wallet.resend(next);
|
||||
},
|
||||
function(next) {
|
||||
if (!self.http)
|
||||
|
||||
@ -915,11 +915,34 @@ Wallet.prototype.send = function send(options, callback) {
|
||||
}, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Resend pending wallet transactions.
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Wallet.prototype.resend = function resend(callback) {
|
||||
var self = this;
|
||||
var i;
|
||||
|
||||
this.getUnconfirmed(function(err, txs) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (txs.length > 0)
|
||||
self.logger.info('Rebroadcasting %d transactions.', txs.length);
|
||||
|
||||
for (i = 0; i < txs.length; i++)
|
||||
self.db.emit('send', txs[i]);
|
||||
|
||||
return callback();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Derive necessary addresses for signing a transaction.
|
||||
* @param {TX|Input} tx
|
||||
* @param {Number?} index - Input index.
|
||||
* @returns {KeyRing[]}
|
||||
* @param {Function} callback - Returns [Error, {@link KeyRing}[]].
|
||||
*/
|
||||
|
||||
Wallet.prototype.deriveInputs = function deriveInputs(tx, callback) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user