refactor: improve generator perf.
This commit is contained in:
parent
2899219033
commit
ec0d50d506
2072
lib/chain/chain.js
2072
lib/chain/chain.js
File diff suppressed because it is too large
Load Diff
1498
lib/chain/chaindb.js
1498
lib/chain/chaindb.js
File diff suppressed because it is too large
Load Diff
@ -177,45 +177,43 @@ ChainEntry.prototype.getRetargetAncestors = function getRetargetAncestors() {
|
||||
* @param {Function} callback - Returns [Error, ChainEntry[]].
|
||||
*/
|
||||
|
||||
ChainEntry.prototype.getAncestors = function getAncestors(max) {
|
||||
return spawn(function *() {
|
||||
var entry = this;
|
||||
var ancestors = [];
|
||||
var cached;
|
||||
ChainEntry.prototype.getAncestors = spawn.co(function* getAncestors(max) {
|
||||
var entry = this;
|
||||
var ancestors = [];
|
||||
var cached;
|
||||
|
||||
if (max === 0)
|
||||
if (max === 0)
|
||||
return ancestors;
|
||||
|
||||
assert(utils.isNumber(max));
|
||||
|
||||
// Try to do this iteratively and synchronously
|
||||
// so we don't have to wait on nextTicks.
|
||||
for (;;) {
|
||||
ancestors.push(entry);
|
||||
|
||||
if (ancestors.length >= max)
|
||||
return ancestors;
|
||||
|
||||
assert(utils.isNumber(max));
|
||||
cached = this.chain.db.getCache(entry.prevBlock);
|
||||
|
||||
// Try to do this iteratively and synchronously
|
||||
// so we don't have to wait on nextTicks.
|
||||
for (;;) {
|
||||
ancestors.push(entry);
|
||||
|
||||
if (ancestors.length >= max)
|
||||
return ancestors;
|
||||
|
||||
cached = this.chain.db.getCache(entry.prevBlock);
|
||||
|
||||
if (!cached) {
|
||||
ancestors.pop();
|
||||
break;
|
||||
}
|
||||
|
||||
entry = cached;
|
||||
if (!cached) {
|
||||
ancestors.pop();
|
||||
break;
|
||||
}
|
||||
|
||||
while (entry) {
|
||||
ancestors.push(entry);
|
||||
if (ancestors.length >= max)
|
||||
break;
|
||||
entry = yield entry.getPrevious();
|
||||
}
|
||||
entry = cached;
|
||||
}
|
||||
|
||||
return ancestors;
|
||||
}, this);
|
||||
};
|
||||
while (entry) {
|
||||
ancestors.push(entry);
|
||||
if (ancestors.length >= max)
|
||||
break;
|
||||
entry = yield entry.getPrevious();
|
||||
}
|
||||
|
||||
return ancestors;
|
||||
});
|
||||
|
||||
/**
|
||||
* Test whether the entry is in the main chain.
|
||||
@ -232,31 +230,29 @@ ChainEntry.prototype.isMainChain = function isMainChain() {
|
||||
* @param {Function} callback - Returns [Error, ChainEntry[]].
|
||||
*/
|
||||
|
||||
ChainEntry.prototype.getAncestorByHeight = function getAncestorByHeight(height) {
|
||||
return spawn(function *() {
|
||||
var main, entry;
|
||||
ChainEntry.prototype.getAncestorByHeight = spawn.co(function* getAncestorByHeight(height) {
|
||||
var main, entry;
|
||||
|
||||
if (height < 0)
|
||||
return yield utils.wait();
|
||||
if (height < 0)
|
||||
return yield utils.wait();
|
||||
|
||||
assert(height >= 0);
|
||||
assert(height <= this.height);
|
||||
assert(height >= 0);
|
||||
assert(height <= this.height);
|
||||
|
||||
main = yield this.isMainChain();
|
||||
main = yield this.isMainChain();
|
||||
|
||||
if (main)
|
||||
return yield this.chain.db.get(height);
|
||||
if (main)
|
||||
return yield this.chain.db.get(height);
|
||||
|
||||
entry = yield this.getAncestor(this.height - height);
|
||||
entry = yield this.getAncestor(this.height - height);
|
||||
|
||||
if (!entry)
|
||||
return;
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
assert(entry.height === height);
|
||||
assert(entry.height === height);
|
||||
|
||||
return entry;
|
||||
}, this);
|
||||
};
|
||||
return entry;
|
||||
});
|
||||
|
||||
/**
|
||||
* Get a single ancestor by index. Note that index-0 is
|
||||
@ -266,20 +262,18 @@ ChainEntry.prototype.getAncestorByHeight = function getAncestorByHeight(height)
|
||||
* @returns {Function} callback - Returns [Error, ChainEntry].
|
||||
*/
|
||||
|
||||
ChainEntry.prototype.getAncestor = function getAncestor(index) {
|
||||
return spawn(function *() {
|
||||
var ancestors;
|
||||
ChainEntry.prototype.getAncestor = spawn.co(function* getAncestor(index) {
|
||||
var ancestors;
|
||||
|
||||
assert(index >= 0);
|
||||
assert(index >= 0);
|
||||
|
||||
ancestors = yield this.getAncestors(index + 1);
|
||||
ancestors = yield this.getAncestors(index + 1);
|
||||
|
||||
if (ancestors.length < index + 1)
|
||||
return;
|
||||
if (ancestors.length < index + 1)
|
||||
return;
|
||||
|
||||
return ancestors[index];
|
||||
}, this);
|
||||
};
|
||||
return ancestors[index];
|
||||
});
|
||||
|
||||
/**
|
||||
* Get previous entry.
|
||||
@ -295,14 +289,12 @@ ChainEntry.prototype.getPrevious = function getPrevious() {
|
||||
* @param {Function} callback - Returns [Error, ChainEntry].
|
||||
*/
|
||||
|
||||
ChainEntry.prototype.getNext = function getNext() {
|
||||
return spawn(function *() {
|
||||
var hash = yield this.chain.db.getNextHash(this.hash);
|
||||
if (!hash)
|
||||
return;
|
||||
return yield this.chain.db.get(hash);
|
||||
}, this);
|
||||
};
|
||||
ChainEntry.prototype.getNext = spawn.co(function* getNext() {
|
||||
var hash = yield this.chain.db.getNextHash(this.hash);
|
||||
if (!hash)
|
||||
return;
|
||||
return yield this.chain.db.get(hash);
|
||||
});
|
||||
|
||||
/**
|
||||
* Get median time past.
|
||||
@ -330,13 +322,11 @@ ChainEntry.prototype.getMedianTime = function getMedianTime(ancestors) {
|
||||
* @param {Function} callback - Returns [Error, Number].
|
||||
*/
|
||||
|
||||
ChainEntry.prototype.getMedianTimeAsync = function getMedianTimeAsync() {
|
||||
return spawn(function *() {
|
||||
var MEDIAN_TIMESPAN = constants.block.MEDIAN_TIMESPAN;
|
||||
var ancestors = yield this.getAncestors(MEDIAN_TIMESPAN);
|
||||
return this.getMedianTime(ancestors);
|
||||
}, this);
|
||||
};
|
||||
ChainEntry.prototype.getMedianTimeAsync = spawn.co(function* getMedianTimeAsync() {
|
||||
var MEDIAN_TIMESPAN = constants.block.MEDIAN_TIMESPAN;
|
||||
var ancestors = yield this.getAncestors(MEDIAN_TIMESPAN);
|
||||
return this.getMedianTime(ancestors);
|
||||
});
|
||||
|
||||
/**
|
||||
* Check isSuperMajority against majorityRejectOutdated.
|
||||
@ -419,13 +409,11 @@ ChainEntry.prototype.isSuperMajority = function isSuperMajority(version, require
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
ChainEntry.prototype.isSuperMajorityAsync = function isSuperMajorityAsync(version, required) {
|
||||
return spawn(function *() {
|
||||
var majorityWindow = this.network.block.majorityWindow;
|
||||
var ancestors = yield this.getAncestors(majorityWindow);
|
||||
return this.isSuperMajority(version, required, ancestors);
|
||||
}, this);
|
||||
};
|
||||
ChainEntry.prototype.isSuperMajorityAsync = spawn.co(function* isSuperMajorityAsync(version, required) {
|
||||
var majorityWindow = this.network.block.majorityWindow;
|
||||
var ancestors = yield this.getAncestors(majorityWindow);
|
||||
return this.isSuperMajority(version, required, ancestors);
|
||||
});
|
||||
|
||||
/**
|
||||
* Test whether the entry is potentially an ancestor of a checkpoint.
|
||||
|
||||
@ -290,12 +290,10 @@ LowlevelUp.prototype.approximateSize = function approximateSize(start, end) {
|
||||
* @param {Function} callback - Returns [Error, Boolean].
|
||||
*/
|
||||
|
||||
LowlevelUp.prototype.has = function has(key) {
|
||||
return spawn(function *() {
|
||||
var value = yield this.get(key);
|
||||
return value != null;
|
||||
}, this);
|
||||
};
|
||||
LowlevelUp.prototype.has = spawn.co(function* has(key) {
|
||||
var value = yield this.get(key);
|
||||
return value != null;
|
||||
});
|
||||
|
||||
/**
|
||||
* Get and deserialize a record with a callback.
|
||||
@ -305,15 +303,13 @@ LowlevelUp.prototype.has = function has(key) {
|
||||
* @param {Function} callback - Returns [Error, Object].
|
||||
*/
|
||||
|
||||
LowlevelUp.prototype.fetch = function fetch(key, parse) {
|
||||
return spawn(function *() {
|
||||
var value = yield this.get(key);
|
||||
if (!value)
|
||||
return;
|
||||
LowlevelUp.prototype.fetch = spawn.co(function* fetch(key, parse) {
|
||||
var value = yield this.get(key);
|
||||
if (!value)
|
||||
return;
|
||||
|
||||
return parse(value, key);
|
||||
}, this);
|
||||
};
|
||||
return parse(value, key);
|
||||
});
|
||||
|
||||
/**
|
||||
* Collect all keys from iterator options.
|
||||
@ -321,29 +317,27 @@ LowlevelUp.prototype.fetch = function fetch(key, parse) {
|
||||
* @param {Function} callback - Returns [Error, Array].
|
||||
*/
|
||||
|
||||
LowlevelUp.prototype.iterate = function iterate(options) {
|
||||
return spawn(function *() {
|
||||
var items = [];
|
||||
var iter, kv, result;
|
||||
LowlevelUp.prototype.iterate = spawn.co(function* iterate(options) {
|
||||
var items = [];
|
||||
var iter, kv, result;
|
||||
|
||||
assert(typeof options.parse === 'function', 'Parse must be a function.');
|
||||
assert(typeof options.parse === 'function', 'Parse must be a function.');
|
||||
|
||||
iter = this.iterator(options);
|
||||
iter = this.iterator(options);
|
||||
|
||||
for (;;) {
|
||||
kv = yield iter.next();
|
||||
if (!kv)
|
||||
return items;
|
||||
for (;;) {
|
||||
kv = yield iter.next();
|
||||
if (!kv)
|
||||
return items;
|
||||
|
||||
result = options.parse(kv[0], kv[1]);
|
||||
result = options.parse(kv[0], kv[1]);
|
||||
|
||||
if (result)
|
||||
items.push(result);
|
||||
}
|
||||
if (result)
|
||||
items.push(result);
|
||||
}
|
||||
|
||||
return items;
|
||||
}, this);
|
||||
};
|
||||
return items;
|
||||
});
|
||||
|
||||
/**
|
||||
* Write and assert a version number for the database.
|
||||
@ -351,23 +345,21 @@ LowlevelUp.prototype.iterate = function iterate(options) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
LowlevelUp.prototype.checkVersion = function checkVersion(key, version) {
|
||||
return spawn(function *() {
|
||||
var data = yield this.get(key);
|
||||
LowlevelUp.prototype.checkVersion = spawn.co(function* checkVersion(key, version) {
|
||||
var data = yield this.get(key);
|
||||
|
||||
if (!data) {
|
||||
data = new Buffer(4);
|
||||
data.writeUInt32LE(version, 0, true);
|
||||
yield this.put(key, data);
|
||||
return;
|
||||
}
|
||||
if (!data) {
|
||||
data = new Buffer(4);
|
||||
data.writeUInt32LE(version, 0, true);
|
||||
yield this.put(key, data);
|
||||
return;
|
||||
}
|
||||
|
||||
data = data.readUInt32LE(0, true);
|
||||
data = data.readUInt32LE(0, true);
|
||||
|
||||
if (data !== version)
|
||||
throw new Error(VERSION_ERROR);
|
||||
}, this);
|
||||
};
|
||||
if (data !== version)
|
||||
throw new Error(VERSION_ERROR);
|
||||
});
|
||||
|
||||
/**
|
||||
* Clone the database.
|
||||
@ -375,60 +367,58 @@ LowlevelUp.prototype.checkVersion = function checkVersion(key, version) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
LowlevelUp.prototype.clone = function clone(path) {
|
||||
return spawn(function *() {
|
||||
var opt = { keys: true, values: true };
|
||||
var options = utils.merge({}, this.options);
|
||||
var hwm = 256 << 20;
|
||||
var total = 0;
|
||||
var tmp, batch, iter, items, key, value;
|
||||
LowlevelUp.prototype.clone = spawn.co(function* clone(path) {
|
||||
var opt = { keys: true, values: true };
|
||||
var options = utils.merge({}, this.options);
|
||||
var hwm = 256 << 20;
|
||||
var total = 0;
|
||||
var tmp, batch, iter, items, key, value;
|
||||
|
||||
assert(!this.loading);
|
||||
assert(!this.closing);
|
||||
assert(this.loaded);
|
||||
assert(!this.loading);
|
||||
assert(!this.closing);
|
||||
assert(this.loaded);
|
||||
|
||||
options.createIfMissing = true;
|
||||
options.errorIfExists = true;
|
||||
options.createIfMissing = true;
|
||||
options.errorIfExists = true;
|
||||
|
||||
tmp = new LowlevelUp(path, options);
|
||||
tmp = new LowlevelUp(path, options);
|
||||
|
||||
yield tmp.open();
|
||||
yield tmp.open();
|
||||
|
||||
batch = tmp.batch();
|
||||
iter = this.iterator(opt);
|
||||
batch = tmp.batch();
|
||||
iter = this.iterator(opt);
|
||||
|
||||
for (;;) {
|
||||
items = yield iter.next();
|
||||
for (;;) {
|
||||
items = yield iter.next();
|
||||
|
||||
if (!items) {
|
||||
try {
|
||||
yield batch.write();
|
||||
} catch (e) {
|
||||
yield tmp.close();
|
||||
throw e;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
key = items[0];
|
||||
value = items[0];
|
||||
|
||||
batch.put(key, value);
|
||||
total += value.length;
|
||||
|
||||
if (total >= hwm) {
|
||||
total = 0;
|
||||
try {
|
||||
yield batch.write();
|
||||
} catch (e) {
|
||||
yield tmp.close();
|
||||
throw e;
|
||||
}
|
||||
batch = tmp.batch();
|
||||
if (!items) {
|
||||
try {
|
||||
yield batch.write();
|
||||
} catch (e) {
|
||||
yield tmp.close();
|
||||
throw e;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
|
||||
key = items[0];
|
||||
value = items[0];
|
||||
|
||||
batch.put(key, value);
|
||||
total += value.length;
|
||||
|
||||
if (total >= hwm) {
|
||||
total = 0;
|
||||
try {
|
||||
yield batch.write();
|
||||
} catch (e) {
|
||||
yield tmp.close();
|
||||
throw e;
|
||||
}
|
||||
batch = tmp.batch();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function Batch(db) {
|
||||
this.db = db;
|
||||
|
||||
@ -56,71 +56,69 @@ utils.inherits(HTTPClient, AsyncObject);
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
HTTPClient.prototype._open = function _open() {
|
||||
return spawn(function *() {
|
||||
var self = this;
|
||||
var IOClient;
|
||||
HTTPClient.prototype._open = spawn.co(function* _open() {
|
||||
var self = this;
|
||||
var IOClient;
|
||||
|
||||
try {
|
||||
IOClient = require('socket.io-client');
|
||||
} catch (e) {
|
||||
;
|
||||
}
|
||||
try {
|
||||
IOClient = require('socket.io-client');
|
||||
} catch (e) {
|
||||
;
|
||||
}
|
||||
|
||||
if (!IOClient)
|
||||
return;
|
||||
if (!IOClient)
|
||||
return;
|
||||
|
||||
this.socket = new IOClient(this.uri, {
|
||||
transports: ['websocket'],
|
||||
forceNew: true
|
||||
this.socket = new IOClient(this.uri, {
|
||||
transports: ['websocket'],
|
||||
forceNew: true
|
||||
});
|
||||
|
||||
this.socket.on('error', function(err) {
|
||||
self.emit('error', err);
|
||||
});
|
||||
|
||||
this.socket.on('version', function(info) {
|
||||
if (info.network !== self.network.type)
|
||||
self.emit('error', new Error('Wrong network.'));
|
||||
});
|
||||
|
||||
this.socket.on('wallet tx', function(details) {
|
||||
self.emit('tx', details);
|
||||
});
|
||||
|
||||
this.socket.on('wallet confirmed', function(details) {
|
||||
self.emit('confirmed', details);
|
||||
});
|
||||
|
||||
this.socket.on('wallet unconfirmed', function(details) {
|
||||
self.emit('unconfirmed', details);
|
||||
});
|
||||
|
||||
this.socket.on('wallet conflict', function(details) {
|
||||
self.emit('conflict', details);
|
||||
});
|
||||
|
||||
this.socket.on('wallet updated', function(details) {
|
||||
self.emit('updated', details);
|
||||
});
|
||||
|
||||
this.socket.on('wallet address', function(receive) {
|
||||
self.emit('address', receive);
|
||||
});
|
||||
|
||||
this.socket.on('wallet balance', function(balance) {
|
||||
self.emit('balance', {
|
||||
id: balance.id,
|
||||
confirmed: utils.satoshi(balance.confirmed),
|
||||
unconfirmed: utils.satoshi(balance.unconfirmed),
|
||||
total: utils.satoshi(balance.total)
|
||||
});
|
||||
});
|
||||
|
||||
this.socket.on('error', function(err) {
|
||||
self.emit('error', err);
|
||||
});
|
||||
|
||||
this.socket.on('version', function(info) {
|
||||
if (info.network !== self.network.type)
|
||||
self.emit('error', new Error('Wrong network.'));
|
||||
});
|
||||
|
||||
this.socket.on('wallet tx', function(details) {
|
||||
self.emit('tx', details);
|
||||
});
|
||||
|
||||
this.socket.on('wallet confirmed', function(details) {
|
||||
self.emit('confirmed', details);
|
||||
});
|
||||
|
||||
this.socket.on('wallet unconfirmed', function(details) {
|
||||
self.emit('unconfirmed', details);
|
||||
});
|
||||
|
||||
this.socket.on('wallet conflict', function(details) {
|
||||
self.emit('conflict', details);
|
||||
});
|
||||
|
||||
this.socket.on('wallet updated', function(details) {
|
||||
self.emit('updated', details);
|
||||
});
|
||||
|
||||
this.socket.on('wallet address', function(receive) {
|
||||
self.emit('address', receive);
|
||||
});
|
||||
|
||||
this.socket.on('wallet balance', function(balance) {
|
||||
self.emit('balance', {
|
||||
id: balance.id,
|
||||
confirmed: utils.satoshi(balance.confirmed),
|
||||
unconfirmed: utils.satoshi(balance.unconfirmed),
|
||||
total: utils.satoshi(balance.total)
|
||||
});
|
||||
});
|
||||
|
||||
yield this._onConnect();
|
||||
yield this._sendAuth();
|
||||
}, this);
|
||||
};
|
||||
yield this._onConnect();
|
||||
yield this._sendAuth();
|
||||
});
|
||||
|
||||
HTTPClient.prototype._onConnect = function _onConnect() {
|
||||
var self = this;
|
||||
@ -165,58 +163,56 @@ HTTPClient.prototype._close = function close() {
|
||||
* @param {Function} callback - Returns [Error, Object?].
|
||||
*/
|
||||
|
||||
HTTPClient.prototype._request = function _request(method, endpoint, json) {
|
||||
return spawn(function *() {
|
||||
var query, network, height, res;
|
||||
HTTPClient.prototype._request = spawn.co(function* _request(method, endpoint, json) {
|
||||
var query, network, height, res;
|
||||
|
||||
if (this.token) {
|
||||
if (!json)
|
||||
json = {};
|
||||
json.token = this.token;
|
||||
}
|
||||
if (this.token) {
|
||||
if (!json)
|
||||
json = {};
|
||||
json.token = this.token;
|
||||
}
|
||||
|
||||
if (json && method === 'get') {
|
||||
query = json;
|
||||
json = null;
|
||||
}
|
||||
if (json && method === 'get') {
|
||||
query = json;
|
||||
json = null;
|
||||
}
|
||||
|
||||
res = yield request({
|
||||
method: method,
|
||||
uri: this.uri + endpoint,
|
||||
query: query,
|
||||
json: json,
|
||||
auth: {
|
||||
username: 'bitcoinrpc',
|
||||
password: this.apiKey || ''
|
||||
},
|
||||
expect: 'json'
|
||||
});
|
||||
res = yield request({
|
||||
method: method,
|
||||
uri: this.uri + endpoint,
|
||||
query: query,
|
||||
json: json,
|
||||
auth: {
|
||||
username: 'bitcoinrpc',
|
||||
password: this.apiKey || ''
|
||||
},
|
||||
expect: 'json'
|
||||
});
|
||||
|
||||
network = res.headers['x-bcoin-network'];
|
||||
network = res.headers['x-bcoin-network'];
|
||||
|
||||
if (network !== this.network.type)
|
||||
throw new Error('Wrong network.');
|
||||
if (network !== this.network.type)
|
||||
throw new Error('Wrong network.');
|
||||
|
||||
height = +res.headers['x-bcoin-height'];
|
||||
height = +res.headers['x-bcoin-height'];
|
||||
|
||||
if (utils.isNumber(height))
|
||||
this.network.updateHeight(height);
|
||||
if (utils.isNumber(height))
|
||||
this.network.updateHeight(height);
|
||||
|
||||
if (res.statusCode === 404)
|
||||
return;
|
||||
if (res.statusCode === 404)
|
||||
return;
|
||||
|
||||
if (!res.body)
|
||||
throw new Error('No body.');
|
||||
if (!res.body)
|
||||
throw new Error('No body.');
|
||||
|
||||
if (res.statusCode !== 200) {
|
||||
if (res.body.error)
|
||||
throw new Error(res.body.error);
|
||||
throw new Error('Status code: ' + res.statusCode);
|
||||
}
|
||||
if (res.statusCode !== 200) {
|
||||
if (res.body.error)
|
||||
throw new Error(res.body.error);
|
||||
throw new Error('Status code: ' + res.statusCode);
|
||||
}
|
||||
|
||||
return res.body;
|
||||
}, this);
|
||||
};
|
||||
return res.body;
|
||||
});
|
||||
|
||||
/**
|
||||
* Make a GET http request to endpoint.
|
||||
@ -569,13 +565,11 @@ HTTPClient.prototype.send = function send(id, options) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
HTTPClient.prototype.retoken = function retoken(id, passphrase) {
|
||||
return spawn(function *() {
|
||||
var options = { passphrase: passphrase };
|
||||
var body = yield this._post('/wallet/' + id + '/retoken', options);
|
||||
return body.token;
|
||||
}, this);
|
||||
};
|
||||
HTTPClient.prototype.retoken = spawn.co(function* retoken(id, passphrase) {
|
||||
var options = { passphrase: passphrase };
|
||||
var body = yield this._post('/wallet/' + id + '/retoken', options);
|
||||
return body.token;
|
||||
});
|
||||
|
||||
/**
|
||||
* Change or set master key's passphrase.
|
||||
|
||||
3984
lib/http/rpc.js
3984
lib/http/rpc.js
File diff suppressed because it is too large
Load Diff
@ -44,38 +44,36 @@ function RPCClient(options) {
|
||||
* @param {Function} callback - Returns [Error, Object?].
|
||||
*/
|
||||
|
||||
RPCClient.prototype.call = function call(method, params) {
|
||||
return spawn(function *() {
|
||||
var res = yield request.promise({
|
||||
method: 'POST',
|
||||
uri: this.uri,
|
||||
json: {
|
||||
method: method,
|
||||
params: params,
|
||||
id: this.id++
|
||||
},
|
||||
auth: {
|
||||
username: 'bitcoinrpc',
|
||||
password: this.apiKey || ''
|
||||
},
|
||||
expect: 'json'
|
||||
});
|
||||
RPCClient.prototype.call = spawn.co(function* call(method, params) {
|
||||
var res = yield request.promise({
|
||||
method: 'POST',
|
||||
uri: this.uri,
|
||||
json: {
|
||||
method: method,
|
||||
params: params,
|
||||
id: this.id++
|
||||
},
|
||||
auth: {
|
||||
username: 'bitcoinrpc',
|
||||
password: this.apiKey || ''
|
||||
},
|
||||
expect: 'json'
|
||||
});
|
||||
|
||||
if (!res.body)
|
||||
return;
|
||||
|
||||
if (res.statusCode === 400)
|
||||
return res.body.result;
|
||||
|
||||
if (res.statusCode !== 200) {
|
||||
if (res.body.error)
|
||||
throw new Error(res.body.error.message);
|
||||
throw new Error('Status code: ' + res.statusCode);
|
||||
}
|
||||
if (!res.body)
|
||||
return;
|
||||
|
||||
if (res.statusCode === 400)
|
||||
return res.body.result;
|
||||
}, this);
|
||||
};
|
||||
|
||||
if (res.statusCode !== 200) {
|
||||
if (res.body.error)
|
||||
throw new Error(res.body.error.message);
|
||||
throw new Error('Status code: ' + res.statusCode);
|
||||
}
|
||||
|
||||
return res.body.result;
|
||||
});
|
||||
|
||||
/*
|
||||
* Expose
|
||||
|
||||
@ -1062,20 +1062,18 @@ HTTPServer.prototype._initIO = function _initIO() {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
HTTPServer.prototype.open = function open() {
|
||||
return spawn(function *() {
|
||||
yield this.server.open();
|
||||
HTTPServer.prototype.open = spawn.co(function* open() {
|
||||
yield this.server.open();
|
||||
|
||||
this.logger.info('HTTP server loaded.');
|
||||
this.logger.info('HTTP server loaded.');
|
||||
|
||||
if (this.apiKey) {
|
||||
this.logger.info('HTTP API key: %s', this.apiKey);
|
||||
this.apiKey = null;
|
||||
} else if (!this.apiHash) {
|
||||
this.logger.warning('WARNING: Your http server is open to the world.');
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
if (this.apiKey) {
|
||||
this.logger.info('HTTP API key: %s', this.apiKey);
|
||||
this.apiKey = null;
|
||||
} else if (!this.apiHash) {
|
||||
this.logger.warning('WARNING: Your http server is open to the world.');
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Close the server, wait for server socket to close.
|
||||
|
||||
@ -89,28 +89,26 @@ HTTPWallet.prototype._init = function _init() {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
HTTPWallet.prototype.open = function open(options) {
|
||||
return spawn(function *() {
|
||||
var wallet;
|
||||
HTTPWallet.prototype.open = spawn.co(function* open(options) {
|
||||
var wallet;
|
||||
|
||||
this.id = options.id;
|
||||
this.id = options.id;
|
||||
|
||||
if (options.token) {
|
||||
this.token = options.token;
|
||||
if (Buffer.isBuffer(this.token))
|
||||
this.token = this.token.toString('hex');
|
||||
this.client.token = this.token;
|
||||
}
|
||||
if (options.token) {
|
||||
this.token = options.token;
|
||||
if (Buffer.isBuffer(this.token))
|
||||
this.token = this.token.toString('hex');
|
||||
this.client.token = this.token;
|
||||
}
|
||||
|
||||
yield this.client.open();
|
||||
yield this.client.open();
|
||||
|
||||
wallet = yield this.client.getWallet(this.id);
|
||||
wallet = yield this.client.getWallet(this.id);
|
||||
|
||||
yield this.client.join(this.id, wallet.token);
|
||||
yield this.client.join(this.id, wallet.token);
|
||||
|
||||
return wallet;
|
||||
}, this);
|
||||
};
|
||||
return wallet;
|
||||
});
|
||||
|
||||
/**
|
||||
* Open the client and create a wallet.
|
||||
@ -118,17 +116,15 @@ HTTPWallet.prototype.open = function open(options) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
HTTPWallet.prototype.create = function create(options) {
|
||||
return spawn(function *() {
|
||||
var wallet;
|
||||
yield this.client.open();
|
||||
wallet = yield this.client.createWallet(options);
|
||||
return yield this.open({
|
||||
id: wallet.id,
|
||||
token: wallet.token
|
||||
});
|
||||
}, this);
|
||||
};
|
||||
HTTPWallet.prototype.create = spawn.co(function* create(options) {
|
||||
var wallet;
|
||||
yield this.client.open();
|
||||
wallet = yield this.client.createWallet(options);
|
||||
return yield this.open({
|
||||
id: wallet.id,
|
||||
token: wallet.token
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Close the client, wait for the socket to close.
|
||||
@ -296,16 +292,14 @@ HTTPWallet.prototype.setPassphrase = function setPassphrase(old, new_) {
|
||||
* @see Wallet#retoken
|
||||
*/
|
||||
|
||||
HTTPWallet.prototype.retoken = function retoken(passphrase) {
|
||||
return spawn(function *() {
|
||||
var token = yield this.client.retoken(this.id, passphrase);
|
||||
HTTPWallet.prototype.retoken = spawn.co(function* retoken(passphrase) {
|
||||
var token = yield this.client.retoken(this.id, passphrase);
|
||||
|
||||
this.token = token;
|
||||
this.client.token = token;
|
||||
this.token = token;
|
||||
this.client.token = token;
|
||||
|
||||
return token;
|
||||
}, this);
|
||||
};
|
||||
return token;
|
||||
});
|
||||
|
||||
/*
|
||||
* Expose
|
||||
|
||||
@ -119,13 +119,11 @@ utils.inherits(Mempool, AsyncObject);
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Mempool.prototype._open = function open() {
|
||||
return spawn(function *() {
|
||||
var size = (this.maxSize / 1024).toFixed(2);
|
||||
yield this.chain.open();
|
||||
this.logger.info('Mempool loaded (maxsize=%dkb).', size);
|
||||
}, this);
|
||||
};
|
||||
Mempool.prototype._open = spawn.co(function* open() {
|
||||
var size = (this.maxSize / 1024).toFixed(2);
|
||||
yield this.chain.open();
|
||||
this.logger.info('Mempool loaded (maxsize=%dkb).', size);
|
||||
});
|
||||
|
||||
/**
|
||||
* Close the chain, wait for the database to close.
|
||||
@ -155,46 +153,44 @@ Mempool.prototype._lock = function _lock(tx, force) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Mempool.prototype.addBlock = function addBlock(block) {
|
||||
return spawn(function *() {
|
||||
var unlock = yield this._lock();
|
||||
var entries = [];
|
||||
var i, entry, tx, hash;
|
||||
Mempool.prototype.addBlock = spawn.co(function* addBlock(block) {
|
||||
var unlock = yield this._lock();
|
||||
var entries = [];
|
||||
var i, entry, tx, hash;
|
||||
|
||||
for (i = block.txs.length - 1; i >= 0; i--) {
|
||||
tx = block.txs[i];
|
||||
hash = tx.hash('hex');
|
||||
for (i = block.txs.length - 1; i >= 0; i--) {
|
||||
tx = block.txs[i];
|
||||
hash = tx.hash('hex');
|
||||
|
||||
if (tx.isCoinbase())
|
||||
continue;
|
||||
if (tx.isCoinbase())
|
||||
continue;
|
||||
|
||||
entry = this.getEntry(hash);
|
||||
entry = this.getEntry(hash);
|
||||
|
||||
if (!entry) {
|
||||
this.removeOrphan(hash);
|
||||
continue;
|
||||
}
|
||||
|
||||
this.removeUnchecked(entry);
|
||||
this.emit('confirmed', tx, block);
|
||||
|
||||
entries.push(entry);
|
||||
if (!entry) {
|
||||
this.removeOrphan(hash);
|
||||
continue;
|
||||
}
|
||||
|
||||
this.blockSinceBump = true;
|
||||
this.lastFeeUpdate = utils.now();
|
||||
this.removeUnchecked(entry);
|
||||
this.emit('confirmed', tx, block);
|
||||
|
||||
if (this.fees)
|
||||
this.fees.processBlock(block.height, entries, this.chain.isFull());
|
||||
entries.push(entry);
|
||||
}
|
||||
|
||||
// We need to reset the rejects filter periodically.
|
||||
// There may be a locktime in a TX that is now valid.
|
||||
this.rejects.reset();
|
||||
this.blockSinceBump = true;
|
||||
this.lastFeeUpdate = utils.now();
|
||||
|
||||
yield utils.wait();
|
||||
unlock();
|
||||
}, this);
|
||||
};
|
||||
if (this.fees)
|
||||
this.fees.processBlock(block.height, entries, this.chain.isFull());
|
||||
|
||||
// We need to reset the rejects filter periodically.
|
||||
// There may be a locktime in a TX that is now valid.
|
||||
this.rejects.reset();
|
||||
|
||||
yield utils.wait();
|
||||
unlock();
|
||||
});
|
||||
|
||||
/**
|
||||
* Notify the mempool that a block has been disconnected
|
||||
@ -203,38 +199,36 @@ Mempool.prototype.addBlock = function addBlock(block) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Mempool.prototype.removeBlock = function removeBlock(block) {
|
||||
return spawn(function *() {
|
||||
var unlock = yield this.lock();
|
||||
var i, entry, tx, hash;
|
||||
Mempool.prototype.removeBlock = spawn.co(function* removeBlock(block) {
|
||||
var unlock = yield this.lock();
|
||||
var i, entry, tx, hash;
|
||||
|
||||
for (i = 0; i < block.txs.length; i++) {
|
||||
tx = block.txs[i];
|
||||
hash = tx.hash('hex');
|
||||
for (i = 0; i < block.txs.length; i++) {
|
||||
tx = block.txs[i];
|
||||
hash = tx.hash('hex');
|
||||
|
||||
if (tx.isCoinbase())
|
||||
continue;
|
||||
if (tx.isCoinbase())
|
||||
continue;
|
||||
|
||||
if (this.hasTX(hash))
|
||||
continue;
|
||||
if (this.hasTX(hash))
|
||||
continue;
|
||||
|
||||
entry = MempoolEntry.fromTX(tx, block.height);
|
||||
entry = MempoolEntry.fromTX(tx, block.height);
|
||||
|
||||
try {
|
||||
yield this.addUnchecked(entry, true);
|
||||
} catch (e) {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
|
||||
this.emit('unconfirmed', tx, block);
|
||||
try {
|
||||
yield this.addUnchecked(entry, true);
|
||||
} catch (e) {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
|
||||
this.rejects.reset();
|
||||
this.emit('unconfirmed', tx, block);
|
||||
}
|
||||
|
||||
unlock();
|
||||
}, this);
|
||||
};
|
||||
this.rejects.reset();
|
||||
|
||||
unlock();
|
||||
});
|
||||
|
||||
/**
|
||||
* Ensure the size of the mempool stays below 300mb.
|
||||
@ -530,26 +524,24 @@ Mempool.prototype.hasReject = function hasReject(hash) {
|
||||
* @param {Function} callback - Returns [{@link VerifyError}].
|
||||
*/
|
||||
|
||||
Mempool.prototype.addTX = function addTX(tx) {
|
||||
return spawn(function *() {
|
||||
var unlock = yield this._lock(tx);
|
||||
var missing;
|
||||
Mempool.prototype.addTX = spawn.co(function* addTX(tx) {
|
||||
var unlock = yield this._lock(tx);
|
||||
var missing;
|
||||
|
||||
try {
|
||||
missing = yield this._addTX(tx);
|
||||
} catch (err) {
|
||||
if (err.type === 'VerifyError') {
|
||||
if (!tx.hasWitness() && !err.malleated)
|
||||
this.rejects.add(tx.hash());
|
||||
}
|
||||
unlock();
|
||||
throw err;
|
||||
try {
|
||||
missing = yield this._addTX(tx);
|
||||
} catch (err) {
|
||||
if (err.type === 'VerifyError') {
|
||||
if (!tx.hasWitness() && !err.malleated)
|
||||
this.rejects.add(tx.hash());
|
||||
}
|
||||
|
||||
unlock();
|
||||
return missing;
|
||||
}, this);
|
||||
};
|
||||
throw err;
|
||||
}
|
||||
|
||||
unlock();
|
||||
return missing;
|
||||
});
|
||||
|
||||
/**
|
||||
* Add a transaction to the mempool.
|
||||
@ -558,117 +550,115 @@ Mempool.prototype.addTX = function addTX(tx) {
|
||||
* @param {Function} callback - Returns [{@link VerifyError}].
|
||||
*/
|
||||
|
||||
Mempool.prototype._addTX = function _addTX(tx) {
|
||||
return spawn(function *() {
|
||||
var lockFlags = constants.flags.STANDARD_LOCKTIME_FLAGS;
|
||||
var hash = tx.hash('hex');
|
||||
var ret, entry, missing;
|
||||
var result, exists;
|
||||
Mempool.prototype._addTX = spawn.co(function* _addTX(tx) {
|
||||
var lockFlags = constants.flags.STANDARD_LOCKTIME_FLAGS;
|
||||
var hash = tx.hash('hex');
|
||||
var ret, entry, missing;
|
||||
var result, exists;
|
||||
|
||||
assert(!tx.mutable, 'Cannot add mutable TX to mempool.');
|
||||
assert(!tx.mutable, 'Cannot add mutable TX to mempool.');
|
||||
|
||||
ret = new VerifyResult();
|
||||
ret = new VerifyResult();
|
||||
|
||||
if (tx.ts !== 0) {
|
||||
if (tx.ts !== 0) {
|
||||
throw new VerifyError(tx,
|
||||
'alreadyknown',
|
||||
'txn-already-known',
|
||||
0);
|
||||
}
|
||||
|
||||
if (!tx.isSane(ret)) {
|
||||
throw new VerifyError(tx,
|
||||
'invalid',
|
||||
ret.reason,
|
||||
ret.score);
|
||||
}
|
||||
|
||||
if (tx.isCoinbase()) {
|
||||
throw new VerifyError(tx,
|
||||
'invalid',
|
||||
'coinbase',
|
||||
100);
|
||||
}
|
||||
|
||||
if (this.requireStandard) {
|
||||
if (!this.chain.state.hasCSV() && tx.version >= 2) {
|
||||
throw new VerifyError(tx,
|
||||
'alreadyknown',
|
||||
'txn-already-known',
|
||||
'nonstandard',
|
||||
'premature-version2-tx',
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!tx.isSane(ret)) {
|
||||
if (!this.chain.state.hasWitness() && !this.prematureWitness) {
|
||||
if (tx.hasWitness()) {
|
||||
throw new VerifyError(tx,
|
||||
'invalid',
|
||||
'nonstandard',
|
||||
'no-witness-yet',
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.requireStandard) {
|
||||
if (!tx.isStandard(ret)) {
|
||||
throw new VerifyError(tx,
|
||||
'nonstandard',
|
||||
ret.reason,
|
||||
ret.score);
|
||||
}
|
||||
}
|
||||
|
||||
if (tx.isCoinbase()) {
|
||||
throw new VerifyError(tx,
|
||||
'invalid',
|
||||
'coinbase',
|
||||
100);
|
||||
}
|
||||
result = yield this.chain.checkFinal(this.chain.tip, tx, lockFlags);
|
||||
|
||||
if (this.requireStandard) {
|
||||
if (!this.chain.state.hasCSV() && tx.version >= 2) {
|
||||
throw new VerifyError(tx,
|
||||
'nonstandard',
|
||||
'premature-version2-tx',
|
||||
0);
|
||||
}
|
||||
}
|
||||
if (!result) {
|
||||
throw new VerifyError(tx,
|
||||
'nonstandard',
|
||||
'non-final',
|
||||
0);
|
||||
}
|
||||
|
||||
if (!this.chain.state.hasWitness() && !this.prematureWitness) {
|
||||
if (tx.hasWitness()) {
|
||||
throw new VerifyError(tx,
|
||||
'nonstandard',
|
||||
'no-witness-yet',
|
||||
0);
|
||||
}
|
||||
}
|
||||
if (this.has(hash)) {
|
||||
throw new VerifyError(tx,
|
||||
'alreadyknown',
|
||||
'txn-already-in-mempool',
|
||||
0);
|
||||
}
|
||||
|
||||
if (this.requireStandard) {
|
||||
if (!tx.isStandard(ret)) {
|
||||
throw new VerifyError(tx,
|
||||
'nonstandard',
|
||||
ret.reason,
|
||||
ret.score);
|
||||
}
|
||||
}
|
||||
exists = yield this.chain.db.hasCoins(hash);
|
||||
|
||||
result = yield this.chain.checkFinal(this.chain.tip, tx, lockFlags);
|
||||
if (exists) {
|
||||
throw new VerifyError(tx,
|
||||
'alreadyknown',
|
||||
'txn-already-known',
|
||||
0);
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
throw new VerifyError(tx,
|
||||
'nonstandard',
|
||||
'non-final',
|
||||
0);
|
||||
}
|
||||
if (this.isDoubleSpend(tx)) {
|
||||
throw new VerifyError(tx,
|
||||
'duplicate',
|
||||
'bad-txns-inputs-spent',
|
||||
0);
|
||||
}
|
||||
|
||||
if (this.has(hash)) {
|
||||
throw new VerifyError(tx,
|
||||
'alreadyknown',
|
||||
'txn-already-in-mempool',
|
||||
0);
|
||||
}
|
||||
yield this.fillAllCoins(tx);
|
||||
|
||||
exists = yield this.chain.db.hasCoins(hash);
|
||||
if (!tx.hasCoins()) {
|
||||
missing = this.storeOrphan(tx);
|
||||
return missing;
|
||||
}
|
||||
|
||||
if (exists) {
|
||||
throw new VerifyError(tx,
|
||||
'alreadyknown',
|
||||
'txn-already-known',
|
||||
0);
|
||||
}
|
||||
entry = MempoolEntry.fromTX(tx, this.chain.height);
|
||||
|
||||
if (this.isDoubleSpend(tx)) {
|
||||
throw new VerifyError(tx,
|
||||
'duplicate',
|
||||
'bad-txns-inputs-spent',
|
||||
0);
|
||||
}
|
||||
yield this.verify(entry);
|
||||
yield this.addUnchecked(entry, true);
|
||||
|
||||
yield this.fillAllCoins(tx);
|
||||
|
||||
if (!tx.hasCoins()) {
|
||||
missing = this.storeOrphan(tx);
|
||||
return missing;
|
||||
}
|
||||
|
||||
entry = MempoolEntry.fromTX(tx, this.chain.height);
|
||||
|
||||
yield this.verify(entry);
|
||||
yield this.addUnchecked(entry, true);
|
||||
|
||||
if (this.limitMempoolSize(hash)) {
|
||||
throw new VerifyError(tx,
|
||||
'insufficientfee',
|
||||
'mempool full',
|
||||
0);
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
if (this.limitMempoolSize(hash)) {
|
||||
throw new VerifyError(tx,
|
||||
'insufficientfee',
|
||||
'mempool full',
|
||||
0);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Add a transaction to the mempool without performing any
|
||||
@ -680,57 +670,55 @@ Mempool.prototype._addTX = function _addTX(tx) {
|
||||
* @param {Function} callback - Returns [{@link VerifyError}].
|
||||
*/
|
||||
|
||||
Mempool.prototype.addUnchecked = function addUnchecked(entry, force) {
|
||||
return spawn(function *() {
|
||||
var unlock = yield this._lock(null, force);
|
||||
var i, resolved, tx, orphan;
|
||||
Mempool.prototype.addUnchecked = spawn.co(function* addUnchecked(entry, force) {
|
||||
var unlock = yield this._lock(null, force);
|
||||
var i, resolved, tx, orphan;
|
||||
|
||||
this.trackEntry(entry);
|
||||
this.trackEntry(entry);
|
||||
|
||||
this.emit('tx', entry.tx);
|
||||
this.emit('add tx', entry.tx);
|
||||
this.emit('tx', entry.tx);
|
||||
this.emit('add tx', entry.tx);
|
||||
|
||||
if (this.fees)
|
||||
this.fees.processTX(entry, this.chain.isFull());
|
||||
if (this.fees)
|
||||
this.fees.processTX(entry, this.chain.isFull());
|
||||
|
||||
this.logger.debug('Added tx %s to mempool.', entry.tx.rhash);
|
||||
this.logger.debug('Added tx %s to mempool.', entry.tx.rhash);
|
||||
|
||||
resolved = this.resolveOrphans(entry.tx);
|
||||
resolved = this.resolveOrphans(entry.tx);
|
||||
|
||||
for (i = 0; i < resolved.length; i++) {
|
||||
tx = resolved[i];
|
||||
orphan = MempoolEntry.fromTX(tx, this.chain.height);
|
||||
for (i = 0; i < resolved.length; i++) {
|
||||
tx = resolved[i];
|
||||
orphan = MempoolEntry.fromTX(tx, this.chain.height);
|
||||
|
||||
try {
|
||||
yield this.verify(orphan);
|
||||
} catch (err) {
|
||||
if (err.type === 'VerifyError') {
|
||||
this.logger.debug('Could not resolve orphan %s: %s.',
|
||||
tx.rhash,
|
||||
err.message);
|
||||
try {
|
||||
yield this.verify(orphan);
|
||||
} catch (err) {
|
||||
if (err.type === 'VerifyError') {
|
||||
this.logger.debug('Could not resolve orphan %s: %s.',
|
||||
tx.rhash,
|
||||
err.message);
|
||||
|
||||
if (!tx.hasWitness() && !err.malleated)
|
||||
this.rejects.add(tx.hash());
|
||||
if (!tx.hasWitness() && !err.malleated)
|
||||
this.rejects.add(tx.hash());
|
||||
|
||||
continue;
|
||||
}
|
||||
this.emit('error', err);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
yield this.addUnchecked(orphan, true);
|
||||
} catch (err) {
|
||||
this.emit('error', err);
|
||||
continue;
|
||||
}
|
||||
|
||||
this.logger.spam('Resolved orphan %s in mempool.', orphan.tx.rhash);
|
||||
this.emit('error', err);
|
||||
continue;
|
||||
}
|
||||
|
||||
unlock();
|
||||
}, this);
|
||||
};
|
||||
try {
|
||||
yield this.addUnchecked(orphan, true);
|
||||
} catch (err) {
|
||||
this.emit('error', err);
|
||||
continue;
|
||||
}
|
||||
|
||||
this.logger.spam('Resolved orphan %s in mempool.', orphan.tx.rhash);
|
||||
}
|
||||
|
||||
unlock();
|
||||
});
|
||||
|
||||
/**
|
||||
* Remove a transaction from the mempool. Generally
|
||||
@ -816,155 +804,153 @@ Mempool.prototype.getMinRate = function getMinRate() {
|
||||
* @param {Function} callback - Returns [{@link VerifyError}].
|
||||
*/
|
||||
|
||||
Mempool.prototype.verify = function verify(entry) {
|
||||
return spawn(function *() {
|
||||
var height = this.chain.height + 1;
|
||||
var lockFlags = flags.STANDARD_LOCKTIME_FLAGS;
|
||||
var flags1 = flags.STANDARD_VERIFY_FLAGS;
|
||||
var flags2 = flags1 & ~(flags.VERIFY_WITNESS | flags.VERIFY_CLEANSTACK);
|
||||
var flags3 = flags1 & ~flags.VERIFY_CLEANSTACK;
|
||||
var mandatory = flags.MANDATORY_VERIFY_FLAGS;
|
||||
var tx = entry.tx;
|
||||
var ret = new VerifyResult();
|
||||
var fee, modFee, now, size, minRate;
|
||||
var rejectFee, minRelayFee, count, result;
|
||||
Mempool.prototype.verify = spawn.co(function* verify(entry) {
|
||||
var height = this.chain.height + 1;
|
||||
var lockFlags = flags.STANDARD_LOCKTIME_FLAGS;
|
||||
var flags1 = flags.STANDARD_VERIFY_FLAGS;
|
||||
var flags2 = flags1 & ~(flags.VERIFY_WITNESS | flags.VERIFY_CLEANSTACK);
|
||||
var flags3 = flags1 & ~flags.VERIFY_CLEANSTACK;
|
||||
var mandatory = flags.MANDATORY_VERIFY_FLAGS;
|
||||
var tx = entry.tx;
|
||||
var ret = new VerifyResult();
|
||||
var fee, modFee, now, size, minRate;
|
||||
var rejectFee, minRelayFee, count, result;
|
||||
|
||||
result = yield this.checkLocks(tx, lockFlags);
|
||||
result = yield this.checkLocks(tx, lockFlags);
|
||||
|
||||
if (!result) {
|
||||
if (!result) {
|
||||
throw new VerifyError(tx,
|
||||
'nonstandard',
|
||||
'non-BIP68-final',
|
||||
0);
|
||||
}
|
||||
|
||||
if (this.requireStandard) {
|
||||
if (!tx.hasStandardInputs()) {
|
||||
throw new VerifyError(tx,
|
||||
'nonstandard',
|
||||
'non-BIP68-final',
|
||||
'bad-txns-nonstandard-inputs',
|
||||
0);
|
||||
}
|
||||
|
||||
if (this.requireStandard) {
|
||||
if (!tx.hasStandardInputs()) {
|
||||
throw new VerifyError(tx,
|
||||
if (this.chain.state.hasWitness()) {
|
||||
if (!tx.hasStandardWitness(ret)) {
|
||||
ret = new VerifyError(tx,
|
||||
'nonstandard',
|
||||
'bad-txns-nonstandard-inputs',
|
||||
0);
|
||||
}
|
||||
if (this.chain.state.hasWitness()) {
|
||||
if (!tx.hasStandardWitness(ret)) {
|
||||
ret = new VerifyError(tx,
|
||||
'nonstandard',
|
||||
ret.reason,
|
||||
ret.score);
|
||||
ret.malleated = ret.score > 0;
|
||||
throw ret;
|
||||
}
|
||||
ret.reason,
|
||||
ret.score);
|
||||
ret.malleated = ret.score > 0;
|
||||
throw ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tx.getSigopsWeight(flags) > constants.tx.MAX_SIGOPS_WEIGHT) {
|
||||
throw new VerifyError(tx,
|
||||
'nonstandard',
|
||||
'bad-txns-too-many-sigops',
|
||||
0);
|
||||
}
|
||||
if (tx.getSigopsWeight(flags) > constants.tx.MAX_SIGOPS_WEIGHT) {
|
||||
throw new VerifyError(tx,
|
||||
'nonstandard',
|
||||
'bad-txns-too-many-sigops',
|
||||
0);
|
||||
}
|
||||
|
||||
fee = tx.getFee();
|
||||
modFee = entry.fees;
|
||||
size = entry.size;
|
||||
minRate = this.getMinRate();
|
||||
fee = tx.getFee();
|
||||
modFee = entry.fees;
|
||||
size = entry.size;
|
||||
minRate = this.getMinRate();
|
||||
|
||||
if (minRate > this.minRelayFee)
|
||||
this.network.updateMinRelay(minRate);
|
||||
if (minRate > this.minRelayFee)
|
||||
this.network.updateMinRelay(minRate);
|
||||
|
||||
rejectFee = tx.getMinFee(size, minRate);
|
||||
minRelayFee = tx.getMinFee(size, this.minRelayFee);
|
||||
rejectFee = tx.getMinFee(size, minRate);
|
||||
minRelayFee = tx.getMinFee(size, this.minRelayFee);
|
||||
|
||||
if (rejectFee > 0 && modFee < rejectFee) {
|
||||
if (rejectFee > 0 && modFee < rejectFee) {
|
||||
throw new VerifyError(tx,
|
||||
'insufficientfee',
|
||||
'mempool min fee not met',
|
||||
0);
|
||||
}
|
||||
|
||||
if (this.relayPriority && modFee < minRelayFee) {
|
||||
if (!entry.isFree(height)) {
|
||||
throw new VerifyError(tx,
|
||||
'insufficientfee',
|
||||
'mempool min fee not met',
|
||||
'insufficient priority',
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.relayPriority && modFee < minRelayFee) {
|
||||
if (!entry.isFree(height)) {
|
||||
throw new VerifyError(tx,
|
||||
'insufficientfee',
|
||||
'insufficient priority',
|
||||
0);
|
||||
}
|
||||
}
|
||||
// Continuously rate-limit free (really, very-low-fee)
|
||||
// transactions. This mitigates 'penny-flooding'. i.e.
|
||||
// sending thousands of free transactions just to be
|
||||
// annoying or make others' transactions take longer
|
||||
// to confirm.
|
||||
if (this.limitFree && modFee < minRelayFee) {
|
||||
now = utils.now();
|
||||
|
||||
// Continuously rate-limit free (really, very-low-fee)
|
||||
// transactions. This mitigates 'penny-flooding'. i.e.
|
||||
// sending thousands of free transactions just to be
|
||||
// annoying or make others' transactions take longer
|
||||
// to confirm.
|
||||
if (this.limitFree && modFee < minRelayFee) {
|
||||
now = utils.now();
|
||||
// Use an exponentially decaying ~10-minute window:
|
||||
this.freeCount *= Math.pow(1 - 1 / 600, now - this.lastTime);
|
||||
this.lastTime = now;
|
||||
|
||||
// Use an exponentially decaying ~10-minute window:
|
||||
this.freeCount *= Math.pow(1 - 1 / 600, now - this.lastTime);
|
||||
this.lastTime = now;
|
||||
|
||||
// The limitFreeRelay unit is thousand-bytes-per-minute
|
||||
// At default rate it would take over a month to fill 1GB
|
||||
if (this.freeCount > this.limitFreeRelay * 10 * 1000) {
|
||||
throw new VerifyError(tx,
|
||||
'insufficientfee',
|
||||
'rate limited free transaction',
|
||||
0);
|
||||
}
|
||||
|
||||
this.freeCount += size;
|
||||
}
|
||||
|
||||
if (this.rejectAbsurdFees && fee > minRelayFee * 10000)
|
||||
throw new VerifyError(tx, 'highfee', 'absurdly-high-fee', 0);
|
||||
|
||||
count = this.countAncestors(tx);
|
||||
|
||||
if (count > constants.mempool.ANCESTOR_LIMIT) {
|
||||
// The limitFreeRelay unit is thousand-bytes-per-minute
|
||||
// At default rate it would take over a month to fill 1GB
|
||||
if (this.freeCount > this.limitFreeRelay * 10 * 1000) {
|
||||
throw new VerifyError(tx,
|
||||
'nonstandard',
|
||||
'too-long-mempool-chain',
|
||||
'insufficientfee',
|
||||
'rate limited free transaction',
|
||||
0);
|
||||
}
|
||||
|
||||
if (!tx.checkInputs(height, ret))
|
||||
throw new VerifyError(tx, 'invalid', ret.reason, ret.score);
|
||||
this.freeCount += size;
|
||||
}
|
||||
|
||||
// Standard verification
|
||||
try {
|
||||
yield this.checkInputs(tx, flags1);
|
||||
} catch (error) {
|
||||
if (tx.hasWitness())
|
||||
throw error;
|
||||
if (this.rejectAbsurdFees && fee > minRelayFee * 10000)
|
||||
throw new VerifyError(tx, 'highfee', 'absurdly-high-fee', 0);
|
||||
|
||||
// Try without segwit and cleanstack.
|
||||
result = yield this.checkResult(tx, flags2);
|
||||
count = this.countAncestors(tx);
|
||||
|
||||
// If it failed, the first verification
|
||||
// was the only result we needed.
|
||||
if (!result)
|
||||
throw error;
|
||||
if (count > constants.mempool.ANCESTOR_LIMIT) {
|
||||
throw new VerifyError(tx,
|
||||
'nonstandard',
|
||||
'too-long-mempool-chain',
|
||||
0);
|
||||
}
|
||||
|
||||
// If it succeeded, segwit may be causing the
|
||||
// failure. Try with segwit but without cleanstack.
|
||||
result = yield this.checkResult(tx, flags3);
|
||||
if (!tx.checkInputs(height, ret))
|
||||
throw new VerifyError(tx, 'invalid', ret.reason, ret.score);
|
||||
|
||||
// Cleanstack was causing the failure.
|
||||
if (result)
|
||||
throw error;
|
||||
|
||||
// Do not insert into reject cache.
|
||||
error.malleated = true;
|
||||
// Standard verification
|
||||
try {
|
||||
yield this.checkInputs(tx, flags1);
|
||||
} catch (error) {
|
||||
if (tx.hasWitness())
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Paranoid checks.
|
||||
if (this.paranoid) {
|
||||
result = yield this.checkResult(tx, mandatory);
|
||||
assert(result, 'BUG: Verify failed for mandatory but not standard.');
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
// Try without segwit and cleanstack.
|
||||
result = yield this.checkResult(tx, flags2);
|
||||
|
||||
// If it failed, the first verification
|
||||
// was the only result we needed.
|
||||
if (!result)
|
||||
throw error;
|
||||
|
||||
// If it succeeded, segwit may be causing the
|
||||
// failure. Try with segwit but without cleanstack.
|
||||
result = yield this.checkResult(tx, flags3);
|
||||
|
||||
// Cleanstack was causing the failure.
|
||||
if (result)
|
||||
throw error;
|
||||
|
||||
// Do not insert into reject cache.
|
||||
error.malleated = true;
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Paranoid checks.
|
||||
if (this.paranoid) {
|
||||
result = yield this.checkResult(tx, mandatory);
|
||||
assert(result, 'BUG: Verify failed for mandatory but not standard.');
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify inputs, return a boolean
|
||||
@ -974,18 +960,16 @@ Mempool.prototype.verify = function verify(entry) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Mempool.prototype.checkResult = function checkResult(tx, flags) {
|
||||
return spawn(function *() {
|
||||
try {
|
||||
yield this.checkInputs(tx, flags);
|
||||
} catch (err) {
|
||||
if (err.type === 'VerifyError')
|
||||
return false;
|
||||
throw err;
|
||||
}
|
||||
return true;
|
||||
}, this);
|
||||
};
|
||||
Mempool.prototype.checkResult = spawn.co(function* checkResult(tx, flags) {
|
||||
try {
|
||||
yield this.checkInputs(tx, flags);
|
||||
} catch (err) {
|
||||
if (err.type === 'VerifyError')
|
||||
return false;
|
||||
throw err;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify inputs for standard
|
||||
@ -995,36 +979,34 @@ Mempool.prototype.checkResult = function checkResult(tx, flags) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Mempool.prototype.checkInputs = function checkInputs(tx, flags) {
|
||||
return spawn(function *() {
|
||||
var result = yield tx.verifyAsync(flags);
|
||||
if (result)
|
||||
return;
|
||||
|
||||
if (!(flags & constants.flags.UNSTANDARD_VERIFY_FLAGS)) {
|
||||
throw new VerifyError(tx,
|
||||
'nonstandard',
|
||||
'non-mandatory-script-verify-flag',
|
||||
0);
|
||||
}
|
||||
|
||||
flags &= ~constants.flags.UNSTANDARD_VERIFY_FLAGS;
|
||||
|
||||
result = yield tx.verifyAsync(flags);
|
||||
|
||||
if (result) {
|
||||
throw new VerifyError(tx,
|
||||
'nonstandard',
|
||||
'non-mandatory-script-verify-flag',
|
||||
0);
|
||||
}
|
||||
Mempool.prototype.checkInputs = spawn.co(function* checkInputs(tx, flags) {
|
||||
var result = yield tx.verifyAsync(flags);
|
||||
if (result)
|
||||
return;
|
||||
|
||||
if (!(flags & constants.flags.UNSTANDARD_VERIFY_FLAGS)) {
|
||||
throw new VerifyError(tx,
|
||||
'nonstandard',
|
||||
'mandatory-script-verify-flag',
|
||||
100);
|
||||
}, this);
|
||||
};
|
||||
'non-mandatory-script-verify-flag',
|
||||
0);
|
||||
}
|
||||
|
||||
flags &= ~constants.flags.UNSTANDARD_VERIFY_FLAGS;
|
||||
|
||||
result = yield tx.verifyAsync(flags);
|
||||
|
||||
if (result) {
|
||||
throw new VerifyError(tx,
|
||||
'nonstandard',
|
||||
'non-mandatory-script-verify-flag',
|
||||
0);
|
||||
}
|
||||
|
||||
throw new VerifyError(tx,
|
||||
'nonstandard',
|
||||
'mandatory-script-verify-flag',
|
||||
100);
|
||||
});
|
||||
|
||||
/**
|
||||
* Count the highest number of
|
||||
@ -1405,32 +1387,30 @@ Mempool.prototype.fillAllHistory = function fillAllHistory(tx) {
|
||||
* @param {Function} callback - Returns [Error, {@link TX}].
|
||||
*/
|
||||
|
||||
Mempool.prototype.fillAllCoins = function fillAllCoins(tx) {
|
||||
return spawn(function *() {
|
||||
var i, input, hash, index, coin;
|
||||
Mempool.prototype.fillAllCoins = spawn.co(function* fillAllCoins(tx) {
|
||||
var i, input, hash, index, coin;
|
||||
|
||||
this.fillCoins(tx);
|
||||
|
||||
if (tx.hasCoins())
|
||||
return tx;
|
||||
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
input = tx.inputs[i];
|
||||
hash = input.prevout.hash;
|
||||
index = input.prevout.index;
|
||||
|
||||
if (this.isSpent(hash, index))
|
||||
continue;
|
||||
|
||||
coin = yield this.chain.db.getCoin(hash, index);
|
||||
|
||||
if (coin)
|
||||
input.coin = coin;
|
||||
}
|
||||
this.fillCoins(tx);
|
||||
|
||||
if (tx.hasCoins())
|
||||
return tx;
|
||||
}, this);
|
||||
};
|
||||
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
input = tx.inputs[i];
|
||||
hash = input.prevout.hash;
|
||||
index = input.prevout.index;
|
||||
|
||||
if (this.isSpent(hash, index))
|
||||
continue;
|
||||
|
||||
coin = yield this.chain.db.getCoin(hash, index);
|
||||
|
||||
if (coin)
|
||||
input.coin = coin;
|
||||
}
|
||||
|
||||
return tx;
|
||||
});
|
||||
|
||||
/**
|
||||
* Get a snapshot of all transaction hashes in the mempool. Used
|
||||
@ -1483,38 +1463,36 @@ Mempool.prototype.isDoubleSpend = function isDoubleSpend(tx) {
|
||||
* @param {Function} callback - Returns [Error, Number].
|
||||
*/
|
||||
|
||||
Mempool.prototype.getConfidence = function getConfidence(hash) {
|
||||
return spawn(function *() {
|
||||
var tx, result;
|
||||
Mempool.prototype.getConfidence = spawn.co(function* getConfidence(hash) {
|
||||
var tx, result;
|
||||
|
||||
if (hash instanceof bcoin.tx) {
|
||||
tx = hash;
|
||||
hash = hash.hash('hex');
|
||||
} else {
|
||||
tx = this.getTX(hash);
|
||||
}
|
||||
if (hash instanceof bcoin.tx) {
|
||||
tx = hash;
|
||||
hash = hash.hash('hex');
|
||||
} else {
|
||||
tx = this.getTX(hash);
|
||||
}
|
||||
|
||||
if (this.hasTX(hash))
|
||||
return constants.confidence.PENDING;
|
||||
if (this.hasTX(hash))
|
||||
return constants.confidence.PENDING;
|
||||
|
||||
if (tx && this.isDoubleSpend(tx))
|
||||
return constants.confidence.INCONFLICT;
|
||||
|
||||
if (tx && tx.block) {
|
||||
result = yield this.chain.db.isMainChain(tx.block);
|
||||
if (result)
|
||||
return constants.confidence.BUILDING;
|
||||
return constants.confidence.DEAD;
|
||||
}
|
||||
|
||||
result = yield this.chain.db.hasCoins(hash);
|
||||
if (tx && this.isDoubleSpend(tx))
|
||||
return constants.confidence.INCONFLICT;
|
||||
|
||||
if (tx && tx.block) {
|
||||
result = yield this.chain.db.isMainChain(tx.block);
|
||||
if (result)
|
||||
return constants.confidence.BUILDING;
|
||||
return constants.confidence.DEAD;
|
||||
}
|
||||
|
||||
return constants.confidence.UNKNOWN;
|
||||
}, this);
|
||||
};
|
||||
result = yield this.chain.db.hasCoins(hash);
|
||||
|
||||
if (result)
|
||||
return constants.confidence.BUILDING;
|
||||
|
||||
return constants.confidence.UNKNOWN;
|
||||
});
|
||||
|
||||
/**
|
||||
* Map a transaction to the mempool.
|
||||
|
||||
@ -134,17 +134,15 @@ Miner.prototype._init = function _init() {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Miner.prototype._open = function open() {
|
||||
return spawn(function *() {
|
||||
if (this.mempool)
|
||||
yield this.mempool.open();
|
||||
else
|
||||
yield this.chain.open();
|
||||
Miner.prototype._open = spawn.co(function* open() {
|
||||
if (this.mempool)
|
||||
yield this.mempool.open();
|
||||
else
|
||||
yield this.chain.open();
|
||||
|
||||
this.logger.info('Miner loaded (flags=%s).',
|
||||
this.coinbaseFlags.toString('utf8'));
|
||||
}, this);
|
||||
};
|
||||
this.logger.info('Miner loaded (flags=%s).',
|
||||
this.coinbaseFlags.toString('utf8'));
|
||||
});
|
||||
|
||||
/**
|
||||
* Close the miner.
|
||||
@ -242,29 +240,28 @@ Miner.prototype.stop = function stop() {
|
||||
* @param {Function} callback - Returns [Error, {@link MinerBlock}].
|
||||
*/
|
||||
|
||||
Miner.prototype.createBlock = function createBlock(tip) {
|
||||
return spawn(function *() {
|
||||
var i, ts, attempt, txs, tx, target, version;
|
||||
Miner.prototype.createBlock = spawn.co(function* createBlock(tip) {
|
||||
var i, ts, attempt, txs, tx, target, version;
|
||||
|
||||
if (!this.loaded)
|
||||
yield this.open();
|
||||
if (!this.loaded)
|
||||
yield this.open();
|
||||
|
||||
if (!tip)
|
||||
tip = this.chain.tip;
|
||||
if (!tip)
|
||||
tip = this.chain.tip;
|
||||
|
||||
assert(tip);
|
||||
assert(tip);
|
||||
|
||||
ts = Math.max(bcoin.now(), tip.ts + 1);
|
||||
ts = Math.max(bcoin.now(), tip.ts + 1);
|
||||
|
||||
// Find target
|
||||
target = yield this.chain.getTargetAsync(ts, tip);
|
||||
// Find target
|
||||
target = yield this.chain.getTargetAsync(ts, tip);
|
||||
|
||||
if (this.version != null) {
|
||||
version = this.version;
|
||||
} else {
|
||||
// Calculate version with versionbits
|
||||
// Calculate version with versionbits
|
||||
version = yield this.chain.computeBlockVersion(tip);
|
||||
}
|
||||
}
|
||||
|
||||
attempt = new MinerBlock({
|
||||
workerPool: this.workerPool,
|
||||
@ -289,8 +286,7 @@ Miner.prototype.createBlock = function createBlock(tip) {
|
||||
}
|
||||
|
||||
return attempt;
|
||||
}, this);
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Mine a single block.
|
||||
@ -298,13 +294,11 @@ Miner.prototype.createBlock = function createBlock(tip) {
|
||||
* @param {Function} callback - Returns [Error, [{@link Block}]].
|
||||
*/
|
||||
|
||||
Miner.prototype.mineBlock = function mineBlock(tip) {
|
||||
return spawn(function *() {
|
||||
Miner.prototype.mineBlock = spawn.co(function* mineBlock(tip) {
|
||||
// Create a new block and start hashing
|
||||
var attempt = yield this.createBlock(tip);
|
||||
return yield attempt.mineAsync();
|
||||
}, this);
|
||||
};
|
||||
return yield attempt.mineAsync();
|
||||
});
|
||||
|
||||
/*
|
||||
* Expose
|
||||
|
||||
@ -349,19 +349,17 @@ MinerBlock.prototype.sendStatus = function sendStatus() {
|
||||
* @param {Function} callback - Returns [Error, {@link Block}].
|
||||
*/
|
||||
|
||||
MinerBlock.prototype.mine = function mine() {
|
||||
return spawn(function *() {
|
||||
yield this.wait(100);
|
||||
MinerBlock.prototype.mine = spawn.co(function* mine() {
|
||||
yield this.wait(100);
|
||||
|
||||
// Try to find a block: do one iteration of extraNonce
|
||||
if (!this.findNonce()) {
|
||||
yield this.mine();
|
||||
return;
|
||||
}
|
||||
// Try to find a block: do one iteration of extraNonce
|
||||
if (!this.findNonce()) {
|
||||
yield this.mine();
|
||||
return;
|
||||
}
|
||||
|
||||
return this.block;
|
||||
}, this);
|
||||
};
|
||||
return this.block;
|
||||
});
|
||||
|
||||
/**
|
||||
* Wait for a timeout.
|
||||
@ -393,20 +391,18 @@ MinerBlock.prototype.mineSync = function mineSync() {
|
||||
* @param {Function} callback - Returns [Error, {@link Block}].
|
||||
*/
|
||||
|
||||
MinerBlock.prototype.mineAsync = function mineAsync() {
|
||||
return spawn(function *() {
|
||||
var block;
|
||||
MinerBlock.prototype.mineAsync = spawn.co(function* mineAsync() {
|
||||
var block;
|
||||
|
||||
if (!this.workerPool)
|
||||
return yield this.mine();
|
||||
if (!this.workerPool)
|
||||
return yield this.mine();
|
||||
|
||||
block = yield this.workerPool.mine(this);
|
||||
block = yield this.workerPool.mine(this);
|
||||
|
||||
this.workerPool.destroy();
|
||||
this.workerPool.destroy();
|
||||
|
||||
return block;
|
||||
}, this);
|
||||
};
|
||||
return block;
|
||||
});
|
||||
|
||||
/**
|
||||
* Destroy the minerblock. Stop mining. Clear timeout.
|
||||
|
||||
112
lib/net/peer.js
112
lib/net/peer.js
@ -1430,50 +1430,48 @@ Peer.prototype._handleMempool = function _handleMempool(packet) {
|
||||
* [Error, {@link Block}|{@link MempoolEntry}].
|
||||
*/
|
||||
|
||||
Peer.prototype._getItem = function _getItem(item) {
|
||||
return spawn(function *() {
|
||||
var entry = this.pool.invMap[item.hash];
|
||||
Peer.prototype._getItem = spawn.co(function* _getItem(item) {
|
||||
var entry = this.pool.invMap[item.hash];
|
||||
|
||||
if (entry) {
|
||||
this.logger.debug(
|
||||
'Peer requested %s %s as a %s packet (%s).',
|
||||
entry.type === constants.inv.TX ? 'tx' : 'block',
|
||||
utils.revHex(entry.hash),
|
||||
item.hasWitness() ? 'witness' : 'normal',
|
||||
this.hostname);
|
||||
if (entry) {
|
||||
this.logger.debug(
|
||||
'Peer requested %s %s as a %s packet (%s).',
|
||||
entry.type === constants.inv.TX ? 'tx' : 'block',
|
||||
utils.revHex(entry.hash),
|
||||
item.hasWitness() ? 'witness' : 'normal',
|
||||
this.hostname);
|
||||
|
||||
entry.ack(this);
|
||||
entry.ack(this);
|
||||
|
||||
if (entry.msg) {
|
||||
if (item.isTX()) {
|
||||
if (entry.type === constants.inv.TX)
|
||||
return entry.msg;
|
||||
} else {
|
||||
if (entry.type === constants.inv.BLOCK)
|
||||
return entry.msg;
|
||||
}
|
||||
return;
|
||||
if (entry.msg) {
|
||||
if (item.isTX()) {
|
||||
if (entry.type === constants.inv.TX)
|
||||
return entry.msg;
|
||||
} else {
|
||||
if (entry.type === constants.inv.BLOCK)
|
||||
return entry.msg;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.selfish)
|
||||
if (this.options.selfish)
|
||||
return;
|
||||
|
||||
if (item.isTX()) {
|
||||
if (!this.mempool)
|
||||
return;
|
||||
return this.mempool.getTX(item.hash);
|
||||
}
|
||||
|
||||
if (item.isTX()) {
|
||||
if (!this.mempool)
|
||||
return;
|
||||
return this.mempool.getTX(item.hash);
|
||||
}
|
||||
if (this.chain.db.options.spv)
|
||||
return;
|
||||
|
||||
if (this.chain.db.options.spv)
|
||||
return;
|
||||
if (this.chain.db.options.prune)
|
||||
return;
|
||||
|
||||
if (this.chain.db.options.prune)
|
||||
return;
|
||||
|
||||
return yield this.chain.db.getBlock(item.hash);
|
||||
}, this);
|
||||
};
|
||||
return yield this.chain.db.getBlock(item.hash);
|
||||
});
|
||||
|
||||
/**
|
||||
* Handle `getdata` packet.
|
||||
@ -2351,24 +2349,22 @@ Peer.prototype.reject = function reject(obj, code, reason, score) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Peer.prototype.resolveOrphan = function resolveOrphan(tip, orphan) {
|
||||
return spawn(function *() {
|
||||
var root, locator;
|
||||
Peer.prototype.resolveOrphan = spawn.co(function* resolveOrphan(tip, orphan) {
|
||||
var root, locator;
|
||||
|
||||
assert(orphan);
|
||||
assert(orphan);
|
||||
|
||||
locator = yield this.chain.getLocator(tip);
|
||||
root = this.chain.getOrphanRoot(orphan);
|
||||
locator = yield this.chain.getLocator(tip);
|
||||
root = this.chain.getOrphanRoot(orphan);
|
||||
|
||||
// Was probably resolved.
|
||||
if (!root) {
|
||||
this.logger.debug('Orphan root was already resolved.');
|
||||
return;
|
||||
}
|
||||
// Was probably resolved.
|
||||
if (!root) {
|
||||
this.logger.debug('Orphan root was already resolved.');
|
||||
return;
|
||||
}
|
||||
|
||||
this.sendGetBlocks(locator, root);
|
||||
}, this);
|
||||
};
|
||||
this.sendGetBlocks(locator, root);
|
||||
});
|
||||
|
||||
/**
|
||||
* Send `getheaders` to peer after building locator.
|
||||
@ -2377,12 +2373,10 @@ Peer.prototype.resolveOrphan = function resolveOrphan(tip, orphan) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Peer.prototype.getHeaders = function getHeaders(tip, stop) {
|
||||
return spawn(function *() {
|
||||
var locator = yield this.chain.getLocator(tip);
|
||||
this.sendGetHeaders(locator, stop);
|
||||
}, this);
|
||||
};
|
||||
Peer.prototype.getHeaders = spawn.co(function* getHeaders(tip, stop) {
|
||||
var locator = yield this.chain.getLocator(tip);
|
||||
this.sendGetHeaders(locator, stop);
|
||||
});
|
||||
|
||||
/**
|
||||
* Send `getblocks` to peer after building locator.
|
||||
@ -2391,12 +2385,10 @@ Peer.prototype.getHeaders = function getHeaders(tip, stop) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Peer.prototype.getBlocks = function getBlocks(tip, stop) {
|
||||
return spawn(function *() {
|
||||
var locator = yield this.chain.getLocator(tip);
|
||||
this.sendGetBlocks(locator, stop);
|
||||
}, this);
|
||||
};
|
||||
Peer.prototype.getBlocks = spawn.co(function* getBlocks(tip, stop) {
|
||||
var locator = yield this.chain.getLocator(tip);
|
||||
this.sendGetBlocks(locator, stop);
|
||||
});
|
||||
|
||||
/**
|
||||
* Start syncing from peer.
|
||||
|
||||
690
lib/net/pool.js
690
lib/net/pool.js
@ -290,40 +290,38 @@ Pool.prototype._lock = function _lock(force) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Pool.prototype._open = function _open() {
|
||||
return spawn(function *() {
|
||||
var ip, key;
|
||||
Pool.prototype._open = spawn.co(function* _open() {
|
||||
var ip, key;
|
||||
|
||||
try {
|
||||
ip = yield this.getIP();
|
||||
} catch (e) {
|
||||
this.logger.error(e);
|
||||
}
|
||||
try {
|
||||
ip = yield this.getIP();
|
||||
} catch (e) {
|
||||
this.logger.error(e);
|
||||
}
|
||||
|
||||
if (ip) {
|
||||
this.address.setHost(ip);
|
||||
this.logger.info('External IP found: %s.', ip);
|
||||
}
|
||||
if (ip) {
|
||||
this.address.setHost(ip);
|
||||
this.logger.info('External IP found: %s.', ip);
|
||||
}
|
||||
|
||||
if (this.mempool)
|
||||
yield this.mempool.open();
|
||||
else
|
||||
yield this.chain.open();
|
||||
if (this.mempool)
|
||||
yield this.mempool.open();
|
||||
else
|
||||
yield this.chain.open();
|
||||
|
||||
this.logger.info('Pool loaded (maxpeers=%d).', this.maxPeers);
|
||||
this.logger.info('Pool loaded (maxpeers=%d).', this.maxPeers);
|
||||
|
||||
if (this.identityKey) {
|
||||
key = bcoin.ec.publicKeyCreate(this.identityKey, true);
|
||||
this.logger.info('Identity public key: %s.', key.toString('hex'));
|
||||
this.logger.info('Identity address: %s.', bcoin.bip150.address(key));
|
||||
}
|
||||
if (this.identityKey) {
|
||||
key = bcoin.ec.publicKeyCreate(this.identityKey, true);
|
||||
this.logger.info('Identity public key: %s.', key.toString('hex'));
|
||||
this.logger.info('Identity address: %s.', bcoin.bip150.address(key));
|
||||
}
|
||||
|
||||
if (!this.options.listen)
|
||||
return;
|
||||
if (!this.options.listen)
|
||||
return;
|
||||
|
||||
yield this.listen();
|
||||
}, this);
|
||||
};
|
||||
yield this.listen();
|
||||
});
|
||||
|
||||
/**
|
||||
* Close and destroy the pool.
|
||||
@ -331,37 +329,35 @@ Pool.prototype._open = function _open() {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Pool.prototype._close = function close() {
|
||||
return spawn(function *() {
|
||||
var i, items, hashes, hash;
|
||||
Pool.prototype._close = spawn.co(function* close() {
|
||||
var i, items, hashes, hash;
|
||||
|
||||
this.stopSync();
|
||||
this.stopSync();
|
||||
|
||||
items = this.invItems.slice();
|
||||
items = this.invItems.slice();
|
||||
|
||||
for (i = 0; i < items.length; i++)
|
||||
items[i].finish();
|
||||
for (i = 0; i < items.length; i++)
|
||||
items[i].finish();
|
||||
|
||||
hashes = Object.keys(this.requestMap);
|
||||
hashes = Object.keys(this.requestMap);
|
||||
|
||||
for (i = 0; i < hashes.length; i++) {
|
||||
hash = hashes[i];
|
||||
this.requestMap[hash].finish(new Error('Pool closed.'));
|
||||
}
|
||||
for (i = 0; i < hashes.length; i++) {
|
||||
hash = hashes[i];
|
||||
this.requestMap[hash].finish(new Error('Pool closed.'));
|
||||
}
|
||||
|
||||
this.peers.destroy();
|
||||
this.peers.destroy();
|
||||
|
||||
this.stopInterval();
|
||||
this.stopTimeout();
|
||||
this.stopInterval();
|
||||
this.stopTimeout();
|
||||
|
||||
if (this.pendingWatch != null) {
|
||||
clearTimeout(this.pendingWatch);
|
||||
this.pendingWatch = null;
|
||||
}
|
||||
if (this.pendingWatch != null) {
|
||||
clearTimeout(this.pendingWatch);
|
||||
this.pendingWatch = null;
|
||||
}
|
||||
|
||||
yield this.unlisten();
|
||||
}, this);
|
||||
};
|
||||
yield this.unlisten();
|
||||
});
|
||||
|
||||
/**
|
||||
* Connect to the network.
|
||||
@ -724,68 +720,66 @@ Pool.prototype.stopSync = function stopSync() {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Pool.prototype._handleHeaders = function _handleHeaders(headers, peer) {
|
||||
return spawn(function *() {
|
||||
var i, unlock, ret, header, hash, last;
|
||||
Pool.prototype._handleHeaders = spawn.co(function* _handleHeaders(headers, peer) {
|
||||
var i, unlock, ret, header, hash, last;
|
||||
|
||||
if (!this.options.headers)
|
||||
return;
|
||||
if (!this.options.headers)
|
||||
return;
|
||||
|
||||
unlock = yield this._lock();
|
||||
unlock = yield this._lock();
|
||||
|
||||
ret = new VerifyResult();
|
||||
ret = new VerifyResult();
|
||||
|
||||
this.logger.debug(
|
||||
'Received %s headers from peer (%s).',
|
||||
headers.length,
|
||||
peer.hostname);
|
||||
this.logger.debug(
|
||||
'Received %s headers from peer (%s).',
|
||||
headers.length,
|
||||
peer.hostname);
|
||||
|
||||
this.emit('headers', headers);
|
||||
this.emit('headers', headers);
|
||||
|
||||
if (peer.isLoader()) {
|
||||
// Reset interval to avoid stall behavior.
|
||||
this.startInterval();
|
||||
// Reset timeout to avoid killing the loader.
|
||||
this.startTimeout();
|
||||
if (peer.isLoader()) {
|
||||
// Reset interval to avoid stall behavior.
|
||||
this.startInterval();
|
||||
// Reset timeout to avoid killing the loader.
|
||||
this.startTimeout();
|
||||
}
|
||||
|
||||
for (i = 0; i < headers.length; i++) {
|
||||
header = headers[i];
|
||||
hash = header.hash('hex');
|
||||
|
||||
if (last && header.prevBlock !== last) {
|
||||
peer.setMisbehavior(100);
|
||||
unlock();
|
||||
throw new Error('Bad header chain.');
|
||||
}
|
||||
|
||||
for (i = 0; i < headers.length; i++) {
|
||||
header = headers[i];
|
||||
hash = header.hash('hex');
|
||||
|
||||
if (last && header.prevBlock !== last) {
|
||||
peer.setMisbehavior(100);
|
||||
unlock();
|
||||
throw new Error('Bad header chain.');
|
||||
}
|
||||
|
||||
if (!header.verify(ret)) {
|
||||
peer.reject(header, 'invalid', ret.reason, 100);
|
||||
unlock();
|
||||
throw new Error('Invalid header.');
|
||||
}
|
||||
|
||||
last = hash;
|
||||
|
||||
yield this.getData(peer, this.blockType, hash);
|
||||
if (!header.verify(ret)) {
|
||||
peer.reject(header, 'invalid', ret.reason, 100);
|
||||
unlock();
|
||||
throw new Error('Invalid header.');
|
||||
}
|
||||
|
||||
// Schedule the getdata's we just added.
|
||||
this.scheduleRequests(peer);
|
||||
last = hash;
|
||||
|
||||
// Restart the getheaders process
|
||||
// Technically `last` is not indexed yet so
|
||||
// the locator hashes will not be entirely
|
||||
// accurate. However, it shouldn't matter
|
||||
// that much since FindForkInGlobalIndex
|
||||
// simply tries to find the latest block in
|
||||
// the peer's chain.
|
||||
if (last && headers.length === 2000)
|
||||
yield peer.getHeaders(last, null);
|
||||
yield this.getData(peer, this.blockType, hash);
|
||||
}
|
||||
|
||||
unlock();
|
||||
}, this);
|
||||
};
|
||||
// Schedule the getdata's we just added.
|
||||
this.scheduleRequests(peer);
|
||||
|
||||
// Restart the getheaders process
|
||||
// Technically `last` is not indexed yet so
|
||||
// the locator hashes will not be entirely
|
||||
// accurate. However, it shouldn't matter
|
||||
// that much since FindForkInGlobalIndex
|
||||
// simply tries to find the latest block in
|
||||
// the peer's chain.
|
||||
if (last && headers.length === 2000)
|
||||
yield peer.getHeaders(last, null);
|
||||
|
||||
unlock();
|
||||
});
|
||||
|
||||
/**
|
||||
* Handle `inv` packet from peer (containing only BLOCK types).
|
||||
@ -795,66 +789,64 @@ Pool.prototype._handleHeaders = function _handleHeaders(headers, peer) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Pool.prototype._handleBlocks = function _handleBlocks(hashes, peer) {
|
||||
return spawn(function *() {
|
||||
var i, hash, exists;
|
||||
Pool.prototype._handleBlocks = spawn.co(function* _handleBlocks(hashes, peer) {
|
||||
var i, hash, exists;
|
||||
|
||||
assert(!this.options.headers);
|
||||
assert(!this.options.headers);
|
||||
|
||||
this.logger.debug(
|
||||
'Received %s block hashes from peer (%s).',
|
||||
hashes.length,
|
||||
peer.hostname);
|
||||
this.logger.debug(
|
||||
'Received %s block hashes from peer (%s).',
|
||||
hashes.length,
|
||||
peer.hostname);
|
||||
|
||||
this.emit('blocks', hashes);
|
||||
this.emit('blocks', hashes);
|
||||
|
||||
if (peer.isLoader()) {
|
||||
// Reset interval to avoid stall behavior.
|
||||
this.startInterval();
|
||||
// Reset timeout to avoid killing the loader.
|
||||
this.startTimeout();
|
||||
if (peer.isLoader()) {
|
||||
// Reset interval to avoid stall behavior.
|
||||
this.startInterval();
|
||||
// Reset timeout to avoid killing the loader.
|
||||
this.startTimeout();
|
||||
}
|
||||
|
||||
for (i = 0; i < hashes.length; i++) {
|
||||
hash = hashes[i];
|
||||
|
||||
// Resolve orphan chain.
|
||||
if (this.chain.hasOrphan(hash)) {
|
||||
// There is a possible race condition here.
|
||||
// The orphan may get resolved by the time
|
||||
// we create the locator. In that case, we
|
||||
// should probably actually move to the
|
||||
// `exists` clause below if it is the last
|
||||
// hash.
|
||||
this.logger.debug('Received known orphan hash (%s).', peer.hostname);
|
||||
yield peer.resolveOrphan(null, hash);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (i = 0; i < hashes.length; i++) {
|
||||
hash = hashes[i];
|
||||
exists = yield this.getData(peer, this.blockType, hash);
|
||||
|
||||
// Resolve orphan chain.
|
||||
if (this.chain.hasOrphan(hash)) {
|
||||
// There is a possible race condition here.
|
||||
// The orphan may get resolved by the time
|
||||
// we create the locator. In that case, we
|
||||
// should probably actually move to the
|
||||
// `exists` clause below if it is the last
|
||||
// hash.
|
||||
this.logger.debug('Received known orphan hash (%s).', peer.hostname);
|
||||
yield peer.resolveOrphan(null, hash);
|
||||
// Normally we request the hashContinue.
|
||||
// In the odd case where we already have
|
||||
// it, we can do one of two things: either
|
||||
// force re-downloading of the block to
|
||||
// continue the sync, or do a getblocks
|
||||
// from the last hash (this will reset
|
||||
// the hashContinue on the remote node).
|
||||
if (exists && i === hashes.length - 1) {
|
||||
// Make sure we _actually_ have this block.
|
||||
if (!this.requestMap[hash]) {
|
||||
this.logger.debug('Received existing hash (%s).', peer.hostname);
|
||||
yield peer.getBlocks(hash, null);
|
||||
continue;
|
||||
}
|
||||
|
||||
exists = yield this.getData(peer, this.blockType, hash);
|
||||
|
||||
// Normally we request the hashContinue.
|
||||
// In the odd case where we already have
|
||||
// it, we can do one of two things: either
|
||||
// force re-downloading of the block to
|
||||
// continue the sync, or do a getblocks
|
||||
// from the last hash (this will reset
|
||||
// the hashContinue on the remote node).
|
||||
if (exists && i === hashes.length - 1) {
|
||||
// Make sure we _actually_ have this block.
|
||||
if (!this.requestMap[hash]) {
|
||||
this.logger.debug('Received existing hash (%s).', peer.hostname);
|
||||
yield peer.getBlocks(hash, null);
|
||||
continue;
|
||||
}
|
||||
// Otherwise, we're still requesting it. Ignore.
|
||||
this.logger.debug('Received requested hash (%s).', peer.hostname);
|
||||
}
|
||||
// Otherwise, we're still requesting it. Ignore.
|
||||
this.logger.debug('Received requested hash (%s).', peer.hostname);
|
||||
}
|
||||
}
|
||||
|
||||
this.scheduleRequests(peer);
|
||||
}, this);
|
||||
};
|
||||
this.scheduleRequests(peer);
|
||||
});
|
||||
|
||||
/**
|
||||
* Handle `inv` packet from peer (containing only BLOCK types).
|
||||
@ -865,30 +857,28 @@ Pool.prototype._handleBlocks = function _handleBlocks(hashes, peer) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Pool.prototype._handleInv = function _handleInv(hashes, peer) {
|
||||
return spawn(function *() {
|
||||
var unlock = yield this._lock();
|
||||
var i, hash;
|
||||
Pool.prototype._handleInv = spawn.co(function* _handleInv(hashes, peer) {
|
||||
var unlock = yield this._lock();
|
||||
var i, hash;
|
||||
|
||||
// Ignore for now if we're still syncing
|
||||
if (!this.chain.synced && !peer.isLoader())
|
||||
return;
|
||||
// Ignore for now if we're still syncing
|
||||
if (!this.chain.synced && !peer.isLoader())
|
||||
return;
|
||||
|
||||
if (!this.options.headers) {
|
||||
yield this._handleBlocks(hashes, peer);
|
||||
unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < hashes.length; i++) {
|
||||
hash = hashes[i];
|
||||
yield peer.getHeaders(null, hash);
|
||||
}
|
||||
|
||||
this.scheduleRequests(peer);
|
||||
if (!this.options.headers) {
|
||||
yield this._handleBlocks(hashes, peer);
|
||||
unlock();
|
||||
}, this);
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < hashes.length; i++) {
|
||||
hash = hashes[i];
|
||||
yield peer.getHeaders(null, hash);
|
||||
}
|
||||
|
||||
this.scheduleRequests(peer);
|
||||
unlock();
|
||||
});
|
||||
|
||||
/**
|
||||
* Handle `block` packet. Attempt to add to chain.
|
||||
@ -898,81 +888,79 @@ Pool.prototype._handleInv = function _handleInv(hashes, peer) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Pool.prototype._handleBlock = function _handleBlock(block, peer) {
|
||||
return spawn(function *() {
|
||||
var requested;
|
||||
Pool.prototype._handleBlock = spawn.co(function* _handleBlock(block, peer) {
|
||||
var requested;
|
||||
|
||||
// Fulfill the load request.
|
||||
requested = this.fulfill(block);
|
||||
// Fulfill the load request.
|
||||
requested = this.fulfill(block);
|
||||
|
||||
// Someone is sending us blocks without
|
||||
// us requesting them.
|
||||
if (!requested) {
|
||||
peer.invFilter.add(block.hash());
|
||||
this.logger.warning(
|
||||
'Received unrequested block: %s (%s).',
|
||||
block.rhash, peer.hostname);
|
||||
return yield utils.wait();
|
||||
// Someone is sending us blocks without
|
||||
// us requesting them.
|
||||
if (!requested) {
|
||||
peer.invFilter.add(block.hash());
|
||||
this.logger.warning(
|
||||
'Received unrequested block: %s (%s).',
|
||||
block.rhash, peer.hostname);
|
||||
return yield utils.wait();
|
||||
}
|
||||
|
||||
try {
|
||||
yield this.chain.add(block);
|
||||
} catch (err) {
|
||||
if (err.type !== 'VerifyError') {
|
||||
this.scheduleRequests(peer);
|
||||
throw err;
|
||||
}
|
||||
|
||||
try {
|
||||
yield this.chain.add(block);
|
||||
} catch (err) {
|
||||
if (err.type !== 'VerifyError') {
|
||||
this.scheduleRequests(peer);
|
||||
if (err.score !== -1)
|
||||
peer.reject(block, err.code, err.reason, err.score);
|
||||
|
||||
if (err.reason === 'bad-prevblk') {
|
||||
if (this.options.headers) {
|
||||
peer.setMisbehavior(10);
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (err.score !== -1)
|
||||
peer.reject(block, err.code, err.reason, err.score);
|
||||
|
||||
if (err.reason === 'bad-prevblk') {
|
||||
if (this.options.headers) {
|
||||
peer.setMisbehavior(10);
|
||||
throw err;
|
||||
}
|
||||
this.logger.debug('Peer sent an orphan block. Resolving.');
|
||||
yield peer.resolveOrphan(null, block.hash('hex'));
|
||||
this.scheduleRequests(peer);
|
||||
throw err;
|
||||
}
|
||||
|
||||
this.logger.debug('Peer sent an orphan block. Resolving.');
|
||||
yield peer.resolveOrphan(null, block.hash('hex'));
|
||||
this.scheduleRequests(peer);
|
||||
throw err;
|
||||
}
|
||||
|
||||
this.scheduleRequests(peer);
|
||||
throw err;
|
||||
}
|
||||
|
||||
this.emit('chain-progress', this.chain.getProgress(), peer);
|
||||
this.scheduleRequests(peer);
|
||||
|
||||
if (this.logger.level >= 4 && this.chain.total % 20 === 0) {
|
||||
this.logger.debug('Status:'
|
||||
+ ' ts=%s height=%d highest=%d progress=%s'
|
||||
+ ' blocks=%d orphans=%d active=%d'
|
||||
+ ' queue=%d target=%s peers=%d'
|
||||
+ ' pending=%d jobs=%d',
|
||||
utils.date(block.ts),
|
||||
this.chain.height,
|
||||
this.chain.bestHeight,
|
||||
(this.chain.getProgress() * 100).toFixed(2) + '%',
|
||||
this.chain.total,
|
||||
this.chain.orphan.count,
|
||||
this.activeBlocks,
|
||||
peer.queueBlock.length,
|
||||
block.bits,
|
||||
this.peers.all.length,
|
||||
this.chain.locker.pending.length,
|
||||
this.chain.locker.jobs.length);
|
||||
}
|
||||
this.emit('chain-progress', this.chain.getProgress(), peer);
|
||||
|
||||
if (this.chain.total % 2000 === 0) {
|
||||
this.logger.info(
|
||||
'Received 2000 more blocks (height=%d, hash=%s).',
|
||||
this.chain.height,
|
||||
block.rhash);
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
if (this.logger.level >= 4 && this.chain.total % 20 === 0) {
|
||||
this.logger.debug('Status:'
|
||||
+ ' ts=%s height=%d highest=%d progress=%s'
|
||||
+ ' blocks=%d orphans=%d active=%d'
|
||||
+ ' queue=%d target=%s peers=%d'
|
||||
+ ' pending=%d jobs=%d',
|
||||
utils.date(block.ts),
|
||||
this.chain.height,
|
||||
this.chain.bestHeight,
|
||||
(this.chain.getProgress() * 100).toFixed(2) + '%',
|
||||
this.chain.total,
|
||||
this.chain.orphan.count,
|
||||
this.activeBlocks,
|
||||
peer.queueBlock.length,
|
||||
block.bits,
|
||||
this.peers.all.length,
|
||||
this.chain.locker.pending.length,
|
||||
this.chain.locker.jobs.length);
|
||||
}
|
||||
|
||||
if (this.chain.total % 2000 === 0) {
|
||||
this.logger.info(
|
||||
'Received 2000 more blocks (height=%d, hash=%s).',
|
||||
this.chain.height,
|
||||
block.rhash);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Send `mempool` to all peers.
|
||||
@ -1304,54 +1292,52 @@ Pool.prototype.hasReject = function hasReject(hash) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Pool.prototype._handleTX = function _handleTX(tx, peer) {
|
||||
return spawn(function *() {
|
||||
var i, requested, missing;
|
||||
Pool.prototype._handleTX = spawn.co(function* _handleTX(tx, peer) {
|
||||
var i, requested, missing;
|
||||
|
||||
// Fulfill the load request.
|
||||
requested = this.fulfill(tx);
|
||||
// Fulfill the load request.
|
||||
requested = this.fulfill(tx);
|
||||
|
||||
if (!requested) {
|
||||
peer.invFilter.add(tx.hash());
|
||||
if (!requested) {
|
||||
peer.invFilter.add(tx.hash());
|
||||
|
||||
if (!this.mempool)
|
||||
this.txFilter.add(tx.hash());
|
||||
if (!this.mempool)
|
||||
this.txFilter.add(tx.hash());
|
||||
|
||||
this.logger.warning('Peer sent unrequested tx: %s (%s).',
|
||||
tx.rhash, peer.hostname);
|
||||
this.logger.warning('Peer sent unrequested tx: %s (%s).',
|
||||
tx.rhash, peer.hostname);
|
||||
|
||||
if (this.hasReject(tx.hash())) {
|
||||
throw new VerifyError(tx,
|
||||
'alreadyknown',
|
||||
'txn-already-in-mempool',
|
||||
0);
|
||||
}
|
||||
if (this.hasReject(tx.hash())) {
|
||||
throw new VerifyError(tx,
|
||||
'alreadyknown',
|
||||
'txn-already-in-mempool',
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.mempool) {
|
||||
this.emit('tx', tx, peer);
|
||||
return;
|
||||
}
|
||||
if (!this.mempool) {
|
||||
this.emit('tx', tx, peer);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
missing = yield this.mempool.addTX(tx);
|
||||
} catch (err) {
|
||||
if (err.type === 'VerifyError') {
|
||||
if (err.score !== -1)
|
||||
peer.reject(tx, err.code, err.reason, err.score);
|
||||
throw err;
|
||||
}
|
||||
try {
|
||||
missing = yield this.mempool.addTX(tx);
|
||||
} catch (err) {
|
||||
if (err.type === 'VerifyError') {
|
||||
if (err.score !== -1)
|
||||
peer.reject(tx, err.code, err.reason, err.score);
|
||||
throw err;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (missing) {
|
||||
for (i = 0; i < missing.length; i++)
|
||||
yield this.getData(peer, this.txType, missing[i]);
|
||||
}
|
||||
if (missing) {
|
||||
for (i = 0; i < missing.length; i++)
|
||||
yield this.getData(peer, this.txType, missing[i]);
|
||||
}
|
||||
|
||||
this.emit('tx', tx, peer);
|
||||
}, this);
|
||||
};
|
||||
this.emit('tx', tx, peer);
|
||||
});
|
||||
|
||||
/**
|
||||
* Create a leech peer from an existing socket.
|
||||
@ -1517,45 +1503,43 @@ Pool.prototype.watchAddress = function watchAddress(address) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Pool.prototype.getData = function getData(peer, type, hash) {
|
||||
return spawn(function *() {
|
||||
var self = this;
|
||||
var item, exists;
|
||||
Pool.prototype.getData = spawn.co(function* getData(peer, type, hash) {
|
||||
var self = this;
|
||||
var item, exists;
|
||||
|
||||
if (!this.loaded)
|
||||
return;
|
||||
if (!this.loaded)
|
||||
return;
|
||||
|
||||
exists = yield this.has(peer, type, hash);
|
||||
exists = yield this.has(peer, type, hash);
|
||||
|
||||
if (exists)
|
||||
return true;
|
||||
if (exists)
|
||||
return true;
|
||||
|
||||
item = new LoadRequest(this, peer, type, hash);
|
||||
item = new LoadRequest(this, peer, type, hash);
|
||||
|
||||
if (type === this.txType) {
|
||||
if (peer.queueTX.length === 0) {
|
||||
utils.nextTick(function() {
|
||||
self.logger.debug(
|
||||
'Requesting %d/%d txs from peer with getdata (%s).',
|
||||
peer.queueTX.length,
|
||||
self.activeTX,
|
||||
peer.hostname);
|
||||
if (type === this.txType) {
|
||||
if (peer.queueTX.length === 0) {
|
||||
utils.nextTick(function() {
|
||||
self.logger.debug(
|
||||
'Requesting %d/%d txs from peer with getdata (%s).',
|
||||
peer.queueTX.length,
|
||||
self.activeTX,
|
||||
peer.hostname);
|
||||
|
||||
peer.getData(peer.queueTX);
|
||||
peer.queueTX.length = 0;
|
||||
});
|
||||
}
|
||||
|
||||
peer.queueTX.push(item.start());
|
||||
|
||||
return false;
|
||||
peer.getData(peer.queueTX);
|
||||
peer.queueTX.length = 0;
|
||||
});
|
||||
}
|
||||
|
||||
peer.queueBlock.push(item);
|
||||
peer.queueTX.push(item.start());
|
||||
|
||||
return false;
|
||||
}, this);
|
||||
};
|
||||
}
|
||||
|
||||
peer.queueBlock.push(item);
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
/**
|
||||
* Queue a `getdata` request to be sent. Promise
|
||||
@ -1581,31 +1565,29 @@ Pool.prototype.getDataSync = function getDataSync(peer, type, hash) {
|
||||
* @param {Function} callback - Returns [Error, Boolean].
|
||||
*/
|
||||
|
||||
Pool.prototype.has = function has(peer, type, hash) {
|
||||
return spawn(function *() {
|
||||
var exists = yield this.exists(type, hash);
|
||||
Pool.prototype.has = spawn.co(function* has(peer, type, hash) {
|
||||
var exists = yield this.exists(type, hash);
|
||||
|
||||
if (exists)
|
||||
return true;
|
||||
if (exists)
|
||||
return true;
|
||||
|
||||
// Check the pending requests.
|
||||
if (this.requestMap[hash])
|
||||
return true;
|
||||
|
||||
if (type !== this.txType)
|
||||
return false;
|
||||
|
||||
// If we recently rejected this item. Ignore.
|
||||
if (this.hasReject(hash)) {
|
||||
this.logger.spam(
|
||||
'Peer sent a known reject of %s (%s).',
|
||||
utils.revHex(hash), peer.hostname);
|
||||
return true;
|
||||
}
|
||||
// Check the pending requests.
|
||||
if (this.requestMap[hash])
|
||||
return true;
|
||||
|
||||
if (type !== this.txType)
|
||||
return false;
|
||||
}, this);
|
||||
};
|
||||
|
||||
// If we recently rejected this item. Ignore.
|
||||
if (this.hasReject(hash)) {
|
||||
this.logger.spam(
|
||||
'Peer sent a known reject of %s (%s).',
|
||||
utils.revHex(hash), peer.hostname);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
/**
|
||||
* Test whether the chain or mempool has seen an item.
|
||||
@ -1873,64 +1855,60 @@ Pool.prototype.isIgnored = function isIgnored(addr) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Pool.prototype.getIP = function getIP() {
|
||||
return spawn(function *() {
|
||||
var request, res, ip;
|
||||
Pool.prototype.getIP = spawn.co(function* getIP() {
|
||||
var request, res, ip;
|
||||
|
||||
if (utils.isBrowser)
|
||||
throw new Error('Could not find IP.');
|
||||
if (utils.isBrowser)
|
||||
throw new Error('Could not find IP.');
|
||||
|
||||
request = require('../http/request');
|
||||
request = require('../http/request');
|
||||
|
||||
try {
|
||||
res = yield request.promise({
|
||||
method: 'GET',
|
||||
uri: 'http://icanhazip.com',
|
||||
expect: 'text',
|
||||
timeout: 3000
|
||||
});
|
||||
} catch (e) {
|
||||
return yield this.getIP2();
|
||||
}
|
||||
try {
|
||||
res = yield request.promise({
|
||||
method: 'GET',
|
||||
uri: 'http://icanhazip.com',
|
||||
expect: 'text',
|
||||
timeout: 3000
|
||||
});
|
||||
} catch (e) {
|
||||
return yield this.getIP2();
|
||||
}
|
||||
|
||||
ip = res.body.trim();
|
||||
ip = res.body.trim();
|
||||
|
||||
if (IP.version(ip) === -1)
|
||||
return yield this.getIP2();
|
||||
if (IP.version(ip) === -1)
|
||||
return yield this.getIP2();
|
||||
|
||||
return IP.normalize(ip);
|
||||
}, this);
|
||||
};
|
||||
return IP.normalize(ip);
|
||||
});
|
||||
|
||||
/**
|
||||
* Attempt to retrieve external IP from dyndns.org.
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Pool.prototype.getIP2 = function getIP2() {
|
||||
return spawn(function *() {
|
||||
var request, res, ip;
|
||||
Pool.prototype.getIP2 = spawn.co(function* getIP2() {
|
||||
var request, res, ip;
|
||||
|
||||
if (utils.isBrowser)
|
||||
throw new Error('Could not find IP.');
|
||||
if (utils.isBrowser)
|
||||
throw new Error('Could not find IP.');
|
||||
|
||||
request = require('../http/request');
|
||||
request = require('../http/request');
|
||||
|
||||
res = yield request.promise({
|
||||
method: 'GET',
|
||||
uri: 'http://checkip.dyndns.org',
|
||||
expect: 'html',
|
||||
timeout: 3000
|
||||
});
|
||||
res = yield request.promise({
|
||||
method: 'GET',
|
||||
uri: 'http://checkip.dyndns.org',
|
||||
expect: 'html',
|
||||
timeout: 3000
|
||||
});
|
||||
|
||||
ip = /IP Address:\s*([0-9a-f.:]+)/i.exec(res.body);
|
||||
ip = /IP Address:\s*([0-9a-f.:]+)/i.exec(res.body);
|
||||
|
||||
if (!ip || IP.version(ip[1]) === -1)
|
||||
throw new Error('Could not find IP.');
|
||||
if (!ip || IP.version(ip[1]) === -1)
|
||||
throw new Error('Could not find IP.');
|
||||
|
||||
return IP.normalize(ip[1]);
|
||||
}, this);
|
||||
};
|
||||
return IP.normalize(ip[1]);
|
||||
});
|
||||
|
||||
/**
|
||||
* Peer List
|
||||
|
||||
@ -223,29 +223,27 @@ Fullnode.prototype._init = function _init() {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Fullnode.prototype._open = function open() {
|
||||
return spawn(function *() {
|
||||
yield this.chain.open();
|
||||
yield this.mempool.open();
|
||||
yield this.miner.open();
|
||||
yield this.pool.open();
|
||||
yield this.walletdb.open();
|
||||
Fullnode.prototype._open = spawn.co(function* open() {
|
||||
yield this.chain.open();
|
||||
yield this.mempool.open();
|
||||
yield this.miner.open();
|
||||
yield this.pool.open();
|
||||
yield this.walletdb.open();
|
||||
|
||||
// Ensure primary wallet.
|
||||
yield this.openWallet();
|
||||
// Ensure primary wallet.
|
||||
yield this.openWallet();
|
||||
|
||||
// Rescan for any missed transactions.
|
||||
yield this.rescan();
|
||||
// Rescan for any missed transactions.
|
||||
yield this.rescan();
|
||||
|
||||
// Rebroadcast pending transactions.
|
||||
yield this.resend();
|
||||
// Rebroadcast pending transactions.
|
||||
yield this.resend();
|
||||
|
||||
if (this.http)
|
||||
yield this.http.open();
|
||||
if (this.http)
|
||||
yield this.http.open();
|
||||
|
||||
this.logger.info('Node is loaded.');
|
||||
}, this);
|
||||
};
|
||||
this.logger.info('Node is loaded.');
|
||||
});
|
||||
|
||||
/**
|
||||
* Close the node, wait for the database to close.
|
||||
@ -253,22 +251,20 @@ Fullnode.prototype._open = function open() {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Fullnode.prototype._close = function close() {
|
||||
return spawn(function *() {
|
||||
this.wallet = null;
|
||||
Fullnode.prototype._close = spawn.co(function* close() {
|
||||
this.wallet = null;
|
||||
|
||||
if (this.http)
|
||||
yield this.http.close();
|
||||
if (this.http)
|
||||
yield this.http.close();
|
||||
|
||||
this.walletdb.close();
|
||||
this.pool.close();
|
||||
this.miner.close();
|
||||
this.mempool.close();
|
||||
this.chain.close();
|
||||
this.walletdb.close();
|
||||
this.pool.close();
|
||||
this.miner.close();
|
||||
this.mempool.close();
|
||||
this.chain.close();
|
||||
|
||||
this.logger.info('Node is closed.');
|
||||
}, this);
|
||||
};
|
||||
this.logger.info('Node is closed.');
|
||||
});
|
||||
|
||||
/**
|
||||
* Rescan for any missed transactions.
|
||||
@ -309,26 +305,24 @@ Fullnode.prototype.broadcast = function broadcast(item, callback) {
|
||||
* @param {TX} tx
|
||||
*/
|
||||
|
||||
Fullnode.prototype.sendTX = function sendTX(tx) {
|
||||
return spawn(function *() {
|
||||
try {
|
||||
yield this.mempool.addTX(tx);
|
||||
} catch (err) {
|
||||
if (err.type === 'VerifyError') {
|
||||
this._error(err);
|
||||
this.logger.warning('Verification failed for tx: %s.', tx.rhash);
|
||||
this.logger.warning('Attempting to broadcast anyway...');
|
||||
return this.pool.broadcast(tx);
|
||||
}
|
||||
throw err;
|
||||
Fullnode.prototype.sendTX = spawn.co(function* sendTX(tx) {
|
||||
try {
|
||||
yield this.mempool.addTX(tx);
|
||||
} catch (err) {
|
||||
if (err.type === 'VerifyError') {
|
||||
this._error(err);
|
||||
this.logger.warning('Verification failed for tx: %s.', tx.rhash);
|
||||
this.logger.warning('Attempting to broadcast anyway...');
|
||||
return this.pool.broadcast(tx);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (!this.options.selfish)
|
||||
tx = tx.toInv();
|
||||
if (!this.options.selfish)
|
||||
tx = tx.toInv();
|
||||
|
||||
return this.pool.broadcast(tx);
|
||||
}, this);
|
||||
};
|
||||
return this.pool.broadcast(tx);
|
||||
});
|
||||
|
||||
/**
|
||||
* Listen on a server socket on
|
||||
@ -410,24 +404,22 @@ Fullnode.prototype.getCoin = function getCoin(hash, index) {
|
||||
* @param {Function} callback - Returns [Error, {@link Coin}[]].
|
||||
*/
|
||||
|
||||
Fullnode.prototype.getCoinsByAddress = function getCoinsByAddress(addresses) {
|
||||
return spawn(function *() {
|
||||
var coins = this.mempool.getCoinsByAddress(addresses);
|
||||
var i, blockCoins, coin, spent;
|
||||
Fullnode.prototype.getCoinsByAddress = spawn.co(function* getCoinsByAddress(addresses) {
|
||||
var coins = this.mempool.getCoinsByAddress(addresses);
|
||||
var i, blockCoins, coin, spent;
|
||||
|
||||
blockCoins = yield this.chain.db.getCoinsByAddress(addresses);
|
||||
blockCoins = yield this.chain.db.getCoinsByAddress(addresses);
|
||||
|
||||
for (i = 0; i < blockCoins.length; i++) {
|
||||
coin = blockCoins[i];
|
||||
spent = this.mempool.isSpent(coin.hash, coin.index);
|
||||
for (i = 0; i < blockCoins.length; i++) {
|
||||
coin = blockCoins[i];
|
||||
spent = this.mempool.isSpent(coin.hash, coin.index);
|
||||
|
||||
if (!spent)
|
||||
coins.push(coin);
|
||||
}
|
||||
if (!spent)
|
||||
coins.push(coin);
|
||||
}
|
||||
|
||||
return coins;
|
||||
}, this);
|
||||
};
|
||||
return coins;
|
||||
});
|
||||
|
||||
/**
|
||||
* Retrieve transactions pertaining to an
|
||||
@ -436,13 +428,11 @@ Fullnode.prototype.getCoinsByAddress = function getCoinsByAddress(addresses) {
|
||||
* @param {Function} callback - Returns [Error, {@link TX}[]].
|
||||
*/
|
||||
|
||||
Fullnode.prototype.getTXByAddress = function getTXByAddress(addresses) {
|
||||
return spawn(function *() {
|
||||
var mempool = this.mempool.getTXByAddress(addresses);
|
||||
var txs = yield this.chain.db.getTXByAddress(addresses);
|
||||
return mempool.concat(txs);
|
||||
}, this);
|
||||
};
|
||||
Fullnode.prototype.getTXByAddress = spawn.co(function* getTXByAddress(addresses) {
|
||||
var mempool = this.mempool.getTXByAddress(addresses);
|
||||
var txs = yield this.chain.db.getTXByAddress(addresses);
|
||||
return mempool.concat(txs);
|
||||
});
|
||||
|
||||
/**
|
||||
* Retrieve a transaction from the mempool or chain database.
|
||||
|
||||
@ -233,33 +233,31 @@ Node.prototype.location = function location(name) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Node.prototype.openWallet = function openWallet() {
|
||||
return spawn(function *() {
|
||||
var options, wallet;
|
||||
Node.prototype.openWallet = spawn.co(function* openWallet() {
|
||||
var options, wallet;
|
||||
|
||||
assert(!this.wallet);
|
||||
assert(!this.wallet);
|
||||
|
||||
options = {
|
||||
id: 'primary',
|
||||
passphrase: this.options.passphrase
|
||||
};
|
||||
options = {
|
||||
id: 'primary',
|
||||
passphrase: this.options.passphrase
|
||||
};
|
||||
|
||||
wallet = yield this.walletdb.ensure(options);
|
||||
wallet = yield this.walletdb.ensure(options);
|
||||
|
||||
this.logger.info(
|
||||
'Loaded wallet with id=%s wid=%d address=%s',
|
||||
wallet.id, wallet.wid, wallet.getAddress());
|
||||
this.logger.info(
|
||||
'Loaded wallet with id=%s wid=%d address=%s',
|
||||
wallet.id, wallet.wid, wallet.getAddress());
|
||||
|
||||
// Set the miner payout address if the
|
||||
// programmer didn't pass one in.
|
||||
if (this.miner) {
|
||||
if (!this.options.payoutAddress)
|
||||
this.miner.address = wallet.getAddress();
|
||||
}
|
||||
// Set the miner payout address if the
|
||||
// programmer didn't pass one in.
|
||||
if (this.miner) {
|
||||
if (!this.options.payoutAddress)
|
||||
this.miner.address = wallet.getAddress();
|
||||
}
|
||||
|
||||
this.wallet = wallet;
|
||||
}, this);
|
||||
};
|
||||
this.wallet = wallet;
|
||||
});
|
||||
|
||||
/**
|
||||
* Resend all pending transactions.
|
||||
|
||||
@ -147,30 +147,28 @@ SPVNode.prototype._init = function _init() {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
SPVNode.prototype._open = function open(callback) {
|
||||
return spawn(function *() {
|
||||
yield this.chain.open();
|
||||
yield this.pool.open();
|
||||
yield this.walletdb.open();
|
||||
SPVNode.prototype._open = spawn.co(function* open(callback) {
|
||||
yield this.chain.open();
|
||||
yield this.pool.open();
|
||||
yield this.walletdb.open();
|
||||
|
||||
// Ensure primary wallet.
|
||||
yield this.openWallet();
|
||||
// Ensure primary wallet.
|
||||
yield this.openWallet();
|
||||
|
||||
// Load bloom filter.
|
||||
yield this.openFilter();
|
||||
// Load bloom filter.
|
||||
yield this.openFilter();
|
||||
|
||||
// Rescan for any missed transactions.
|
||||
yield this.rescan();
|
||||
// Rescan for any missed transactions.
|
||||
yield this.rescan();
|
||||
|
||||
// Rebroadcast pending transactions.
|
||||
yield this.resend();
|
||||
// Rebroadcast pending transactions.
|
||||
yield this.resend();
|
||||
|
||||
if (this.http)
|
||||
yield this.http.open();
|
||||
if (this.http)
|
||||
yield this.http.open();
|
||||
|
||||
this.logger.info('Node is loaded.');
|
||||
}, this);
|
||||
};
|
||||
this.logger.info('Node is loaded.');
|
||||
});
|
||||
|
||||
/**
|
||||
* Close the node, wait for the database to close.
|
||||
@ -178,34 +176,30 @@ SPVNode.prototype._open = function open(callback) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
SPVNode.prototype._close = function close() {
|
||||
return spawn(function *() {
|
||||
this.wallet = null;
|
||||
if (this.http)
|
||||
yield this.http.close();
|
||||
yield this.walletdb.close();
|
||||
yield this.pool.close();
|
||||
yield this.chain.close();
|
||||
}, this);
|
||||
};
|
||||
SPVNode.prototype._close = spawn.co(function* close() {
|
||||
this.wallet = null;
|
||||
if (this.http)
|
||||
yield this.http.close();
|
||||
yield this.walletdb.close();
|
||||
yield this.pool.close();
|
||||
yield this.chain.close();
|
||||
});
|
||||
|
||||
/**
|
||||
* Initialize p2p bloom filter for address watching.
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
SPVNode.prototype.openFilter = function openFilter() {
|
||||
return spawn(function *() {
|
||||
var hashes = yield this.walletdb.getAddressHashes();
|
||||
var i;
|
||||
SPVNode.prototype.openFilter = spawn.co(function* openFilter() {
|
||||
var hashes = yield this.walletdb.getAddressHashes();
|
||||
var i;
|
||||
|
||||
if (hashes.length > 0)
|
||||
this.logger.info('Adding %d addresses to filter.', hashes.length);
|
||||
if (hashes.length > 0)
|
||||
this.logger.info('Adding %d addresses to filter.', hashes.length);
|
||||
|
||||
for (i = 0; i < hashes.length; i++)
|
||||
this.pool.watch(hashes[i], 'hex');
|
||||
}, this);
|
||||
};
|
||||
for (i = 0; i < hashes.length; i++)
|
||||
this.pool.watch(hashes[i], 'hex');
|
||||
});
|
||||
|
||||
/**
|
||||
* Rescan for any missed transactions.
|
||||
|
||||
@ -53,98 +53,94 @@ AsyncObject.prototype._onClose = function _onClose() {
|
||||
});
|
||||
};
|
||||
|
||||
AsyncObject.prototype.open = function open() {
|
||||
return spawn(function *() {
|
||||
var err, unlock;
|
||||
AsyncObject.prototype.open = spawn.co(function* open() {
|
||||
var err, unlock;
|
||||
|
||||
assert(!this.closing, 'Cannot open while closing.');
|
||||
assert(!this.closing, 'Cannot open while closing.');
|
||||
|
||||
if (this.loaded)
|
||||
return yield wait();
|
||||
if (this.loaded)
|
||||
return yield wait();
|
||||
|
||||
if (this.loading)
|
||||
return yield this._onOpen();
|
||||
if (this.loading)
|
||||
return yield this._onOpen();
|
||||
|
||||
if (this.locker)
|
||||
unlock = yield this.locker.lock();
|
||||
if (this.locker)
|
||||
unlock = yield this.locker.lock();
|
||||
|
||||
this.emit('preopen');
|
||||
this.emit('preopen');
|
||||
|
||||
this.loading = true;
|
||||
this.loading = true;
|
||||
|
||||
try {
|
||||
yield this._open();
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
try {
|
||||
yield this._open();
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
|
||||
yield wait();
|
||||
|
||||
if (err) {
|
||||
this.loading = false;
|
||||
this._error('open', err);
|
||||
if (unlock)
|
||||
unlock();
|
||||
throw err;
|
||||
}
|
||||
yield wait();
|
||||
|
||||
if (err) {
|
||||
this.loading = false;
|
||||
this.loaded = true;
|
||||
this.emit('open');
|
||||
|
||||
this._error('open', err);
|
||||
if (unlock)
|
||||
unlock();
|
||||
}, this);
|
||||
};
|
||||
throw err;
|
||||
}
|
||||
|
||||
this.loading = false;
|
||||
this.loaded = true;
|
||||
this.emit('open');
|
||||
|
||||
if (unlock)
|
||||
unlock();
|
||||
});
|
||||
|
||||
/**
|
||||
* Close the object (recallable).
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
AsyncObject.prototype.close = function close() {
|
||||
return spawn(function *() {
|
||||
var unlock, err;
|
||||
AsyncObject.prototype.close = spawn.co(function* close() {
|
||||
var unlock, err;
|
||||
|
||||
assert(!this.loading, 'Cannot close while loading.');
|
||||
assert(!this.loading, 'Cannot close while loading.');
|
||||
|
||||
if (!this.loaded)
|
||||
return yield wait();
|
||||
if (!this.loaded)
|
||||
return yield wait();
|
||||
|
||||
if (this.closing)
|
||||
return yield this._onClose();
|
||||
if (this.closing)
|
||||
return yield this._onClose();
|
||||
|
||||
if (this.locker)
|
||||
unlock = yield this.locker.lock();
|
||||
if (this.locker)
|
||||
unlock = yield this.locker.lock();
|
||||
|
||||
this.emit('preclose');
|
||||
this.emit('preclose');
|
||||
|
||||
this.closing = true;
|
||||
this.loaded = false;
|
||||
this.closing = true;
|
||||
this.loaded = false;
|
||||
|
||||
try {
|
||||
yield this._close();
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
try {
|
||||
yield this._close();
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
|
||||
yield wait();
|
||||
|
||||
if (err) {
|
||||
this.closing = false;
|
||||
this._error('close', err);
|
||||
if (unlock)
|
||||
unlock();
|
||||
throw err;
|
||||
}
|
||||
yield wait();
|
||||
|
||||
if (err) {
|
||||
this.closing = false;
|
||||
this.emit('close');
|
||||
|
||||
this._error('close', err);
|
||||
if (unlock)
|
||||
unlock();
|
||||
}, this);
|
||||
};
|
||||
throw err;
|
||||
}
|
||||
|
||||
this.closing = false;
|
||||
this.emit('close');
|
||||
|
||||
if (unlock)
|
||||
unlock();
|
||||
});
|
||||
|
||||
/**
|
||||
* Close the object (recallable).
|
||||
|
||||
@ -1,11 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
// See: https://github.com/yoursnetwork/asink
|
||||
|
||||
function spawn(generator, self) {
|
||||
function exec(gen) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var gen = generator.call(self);
|
||||
|
||||
function step(value, rejection) {
|
||||
var next;
|
||||
|
||||
@ -38,4 +34,18 @@ function spawn(generator, self) {
|
||||
});
|
||||
}
|
||||
|
||||
function spawn(generator, self) {
|
||||
var gen = generator.call(self);
|
||||
return exec(gen);
|
||||
}
|
||||
|
||||
function co(generator) {
|
||||
return function() {
|
||||
var gen = generator.apply(this, arguments);
|
||||
return exec(gen);
|
||||
};
|
||||
}
|
||||
|
||||
spawn.co = co;
|
||||
|
||||
module.exports = spawn;
|
||||
|
||||
@ -204,22 +204,20 @@ Account.MAX_LOOKAHEAD = 5;
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Account.prototype.init = function init() {
|
||||
return spawn(function *() {
|
||||
// Waiting for more keys.
|
||||
if (this.keys.length !== this.n - 1) {
|
||||
assert(!this.initialized);
|
||||
this.save();
|
||||
return;
|
||||
}
|
||||
Account.prototype.init = spawn.co(function* init() {
|
||||
// Waiting for more keys.
|
||||
if (this.keys.length !== this.n - 1) {
|
||||
assert(!this.initialized);
|
||||
this.save();
|
||||
return;
|
||||
}
|
||||
|
||||
assert(this.receiveDepth === 0);
|
||||
assert(this.changeDepth === 0);
|
||||
assert(this.receiveDepth === 0);
|
||||
assert(this.changeDepth === 0);
|
||||
|
||||
this.initialized = true;
|
||||
yield this.setDepth(1, 1);
|
||||
}, this);
|
||||
};
|
||||
this.initialized = true;
|
||||
yield this.setDepth(1, 1);
|
||||
});
|
||||
|
||||
/**
|
||||
* Open the account (done after retrieval).
|
||||
@ -306,30 +304,28 @@ Account.prototype.spliceKey = function spliceKey(key) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Account.prototype.addKey = function addKey(key) {
|
||||
return spawn(function *() {
|
||||
var result = false;
|
||||
var exists;
|
||||
Account.prototype.addKey = spawn.co(function* addKey(key) {
|
||||
var result = false;
|
||||
var exists;
|
||||
|
||||
try {
|
||||
result = this.pushKey(key);
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
try {
|
||||
result = this.pushKey(key);
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
exists = yield this._checkKeys();
|
||||
exists = yield this._checkKeys();
|
||||
|
||||
if (exists) {
|
||||
this.spliceKey(key);
|
||||
throw new Error('Cannot add a key from another account.');
|
||||
}
|
||||
if (exists) {
|
||||
this.spliceKey(key);
|
||||
throw new Error('Cannot add a key from another account.');
|
||||
}
|
||||
|
||||
// Try to initialize again.
|
||||
yield this.init();
|
||||
// Try to initialize again.
|
||||
yield this.init();
|
||||
|
||||
return result;
|
||||
}, this);
|
||||
};
|
||||
return result;
|
||||
});
|
||||
|
||||
/**
|
||||
* Ensure accounts are not sharing keys.
|
||||
@ -337,27 +333,25 @@ Account.prototype.addKey = function addKey(key) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Account.prototype._checkKeys = function _checkKeys() {
|
||||
return spawn(function *() {
|
||||
var ring, hash, paths;
|
||||
Account.prototype._checkKeys = spawn.co(function* _checkKeys() {
|
||||
var ring, hash, paths;
|
||||
|
||||
if (this.initialized || this.type !== Account.types.MULTISIG)
|
||||
return false;
|
||||
if (this.initialized || this.type !== Account.types.MULTISIG)
|
||||
return false;
|
||||
|
||||
if (this.keys.length !== this.n - 1)
|
||||
return false;
|
||||
if (this.keys.length !== this.n - 1)
|
||||
return false;
|
||||
|
||||
ring = this.deriveReceive(0);
|
||||
hash = ring.getScriptHash('hex');
|
||||
ring = this.deriveReceive(0);
|
||||
hash = ring.getScriptHash('hex');
|
||||
|
||||
paths = yield this.db.getAddressPaths(hash);
|
||||
paths = yield this.db.getAddressPaths(hash);
|
||||
|
||||
if (!paths)
|
||||
return false;
|
||||
if (!paths)
|
||||
return false;
|
||||
|
||||
return paths[this.wid] != null;
|
||||
}, this);
|
||||
};
|
||||
return paths[this.wid] != null;
|
||||
});
|
||||
|
||||
/**
|
||||
* Remove a public account key from the account (multisig).
|
||||
@ -404,29 +398,27 @@ Account.prototype.createChange = function createChange() {
|
||||
* @param {Function} callback - Returns [Error, {@link KeyRing}].
|
||||
*/
|
||||
|
||||
Account.prototype.createAddress = function createAddress(change) {
|
||||
return spawn(function *() {
|
||||
var ring, lookahead;
|
||||
Account.prototype.createAddress = spawn.co(function* createAddress(change) {
|
||||
var ring, lookahead;
|
||||
|
||||
if (change) {
|
||||
ring = this.deriveChange(this.changeDepth);
|
||||
lookahead = this.deriveChange(this.changeDepth + this.lookahead);
|
||||
this.changeDepth++;
|
||||
this.changeAddress = ring;
|
||||
} else {
|
||||
ring = this.deriveReceive(this.receiveDepth);
|
||||
lookahead = this.deriveReceive(this.receiveDepth + this.lookahead);
|
||||
this.receiveDepth++;
|
||||
this.receiveAddress = ring;
|
||||
}
|
||||
if (change) {
|
||||
ring = this.deriveChange(this.changeDepth);
|
||||
lookahead = this.deriveChange(this.changeDepth + this.lookahead);
|
||||
this.changeDepth++;
|
||||
this.changeAddress = ring;
|
||||
} else {
|
||||
ring = this.deriveReceive(this.receiveDepth);
|
||||
lookahead = this.deriveReceive(this.receiveDepth + this.lookahead);
|
||||
this.receiveDepth++;
|
||||
this.receiveAddress = ring;
|
||||
}
|
||||
|
||||
yield this.saveAddress([ring, lookahead]);
|
||||
yield this.saveAddress([ring, lookahead]);
|
||||
|
||||
this.save();
|
||||
this.save();
|
||||
|
||||
return ring;
|
||||
}, this);
|
||||
};
|
||||
return ring;
|
||||
});
|
||||
|
||||
/**
|
||||
* Derive a receiving address at `index`. Do not increment depth.
|
||||
@ -568,47 +560,45 @@ Account.prototype.saveAddress = function saveAddress(rings) {
|
||||
* @param {Function} callback - Returns [Error, {@link KeyRing}, {@link KeyRing}].
|
||||
*/
|
||||
|
||||
Account.prototype.setDepth = function setDepth(receiveDepth, changeDepth) {
|
||||
return spawn(function *() {
|
||||
var rings = [];
|
||||
var i, receive, change;
|
||||
Account.prototype.setDepth = spawn.co(function* setDepth(receiveDepth, changeDepth) {
|
||||
var rings = [];
|
||||
var i, receive, change;
|
||||
|
||||
if (receiveDepth > this.receiveDepth) {
|
||||
for (i = this.receiveDepth; i < receiveDepth; i++) {
|
||||
receive = this.deriveReceive(i);
|
||||
rings.push(receive);
|
||||
}
|
||||
|
||||
for (i = receiveDepth; i < receiveDepth + this.lookahead; i++)
|
||||
rings.push(this.deriveReceive(i));
|
||||
|
||||
this.receiveAddress = receive;
|
||||
this.receiveDepth = receiveDepth;
|
||||
if (receiveDepth > this.receiveDepth) {
|
||||
for (i = this.receiveDepth; i < receiveDepth; i++) {
|
||||
receive = this.deriveReceive(i);
|
||||
rings.push(receive);
|
||||
}
|
||||
|
||||
if (changeDepth > this.changeDepth) {
|
||||
for (i = this.changeDepth; i < changeDepth; i++) {
|
||||
change = this.deriveChange(i);
|
||||
rings.push(change);
|
||||
}
|
||||
for (i = receiveDepth; i < receiveDepth + this.lookahead; i++)
|
||||
rings.push(this.deriveReceive(i));
|
||||
|
||||
for (i = changeDepth; i < changeDepth + this.lookahead; i++)
|
||||
rings.push(this.deriveChange(i));
|
||||
this.receiveAddress = receive;
|
||||
this.receiveDepth = receiveDepth;
|
||||
}
|
||||
|
||||
this.changeAddress = change;
|
||||
this.changeDepth = changeDepth;
|
||||
if (changeDepth > this.changeDepth) {
|
||||
for (i = this.changeDepth; i < changeDepth; i++) {
|
||||
change = this.deriveChange(i);
|
||||
rings.push(change);
|
||||
}
|
||||
|
||||
if (rings.length === 0)
|
||||
return [];
|
||||
for (i = changeDepth; i < changeDepth + this.lookahead; i++)
|
||||
rings.push(this.deriveChange(i));
|
||||
|
||||
yield this.saveAddress(rings);
|
||||
this.changeAddress = change;
|
||||
this.changeDepth = changeDepth;
|
||||
}
|
||||
|
||||
this.save();
|
||||
if (rings.length === 0)
|
||||
return [];
|
||||
|
||||
return [receive, change];
|
||||
}, this);
|
||||
};
|
||||
yield this.saveAddress(rings);
|
||||
|
||||
this.save();
|
||||
|
||||
return [receive, change];
|
||||
});
|
||||
|
||||
/**
|
||||
* Convert the account to a more inspection-friendly object.
|
||||
|
||||
1478
lib/wallet/txdb.js
1478
lib/wallet/txdb.js
File diff suppressed because it is too large
Load Diff
1552
lib/wallet/wallet.js
1552
lib/wallet/wallet.js
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -247,25 +247,23 @@ Workers.prototype.verify = function verify(tx, flags) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Workers.prototype.sign = function sign(tx, ring, type) {
|
||||
return spawn(function *() {
|
||||
var i, result, input, sig, sigs, total;
|
||||
Workers.prototype.sign = spawn.co(function* sign(tx, ring, type) {
|
||||
var i, result, input, sig, sigs, total;
|
||||
|
||||
result = yield this.execute('sign', [tx, ring, type], -1);
|
||||
result = yield this.execute('sign', [tx, ring, type], -1);
|
||||
|
||||
sigs = result[0];
|
||||
total = result[1];
|
||||
sigs = result[0];
|
||||
total = result[1];
|
||||
|
||||
for (i = 0; i < sigs.length; i++) {
|
||||
sig = sigs[i];
|
||||
input = tx.inputs[i];
|
||||
input.script = sig[0];
|
||||
input.witness = sig[1];
|
||||
}
|
||||
for (i = 0; i < sigs.length; i++) {
|
||||
sig = sigs[i];
|
||||
input = tx.inputs[i];
|
||||
input.script = sig[0];
|
||||
input.witness = sig[1];
|
||||
}
|
||||
|
||||
return total;
|
||||
}, this);
|
||||
};
|
||||
return total;
|
||||
});
|
||||
|
||||
/**
|
||||
* Execute the mining job (no timeout).
|
||||
|
||||
Loading…
Reference in New Issue
Block a user