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