protocol: classify.
This commit is contained in:
parent
93fe6669bf
commit
9240d2f827
@ -14,11 +14,11 @@
|
||||
const assert = require('assert');
|
||||
|
||||
/**
|
||||
* Verify Error
|
||||
* An error thrown during verification. Can be either
|
||||
* a mempool transaction validation error or a blockchain
|
||||
* block verification error. Ultimately used to send
|
||||
* `reject` packets to peers.
|
||||
* @constructor
|
||||
* @extends Error
|
||||
* @param {Block|TX} msg
|
||||
* @param {String} code - Reject packet code.
|
||||
@ -26,38 +26,43 @@ const assert = require('assert');
|
||||
* @param {Number} score - Ban score increase
|
||||
* (can be -1 for no reject packet).
|
||||
* @param {Boolean} malleated
|
||||
* @property {String} code
|
||||
* @property {Buffer} hash
|
||||
* @property {Number} height (will be the coinbase height if not present).
|
||||
* @property {Number} score
|
||||
* @property {String} message
|
||||
* @property {Boolean} malleated
|
||||
*/
|
||||
|
||||
function VerifyError(msg, code, reason, score, malleated) {
|
||||
Error.call(this);
|
||||
class VerifyError extends Error {
|
||||
/**
|
||||
* Create a verify error.
|
||||
* @constructor
|
||||
* @param {Block|TX} msg
|
||||
* @param {String} code - Reject packet code.
|
||||
* @param {String} reason - Reject packet reason.
|
||||
* @param {Number} score - Ban score increase
|
||||
* (can be -1 for no reject packet).
|
||||
* @param {Boolean} malleated
|
||||
*/
|
||||
|
||||
assert(typeof code === 'string');
|
||||
assert(typeof reason === 'string');
|
||||
assert(score >= 0);
|
||||
constructor(msg, code, reason, score, malleated) {
|
||||
super();
|
||||
|
||||
this.type = 'VerifyError';
|
||||
this.message = '';
|
||||
this.code = code;
|
||||
this.reason = reason;
|
||||
this.score = score;
|
||||
this.hash = msg.hash('hex');
|
||||
this.malleated = malleated || false;
|
||||
assert(typeof code === 'string');
|
||||
assert(typeof reason === 'string');
|
||||
assert(score >= 0);
|
||||
|
||||
this.message = `Verification failure: ${reason}`
|
||||
+ ` (code=${code} score=${score} hash=${msg.rhash()})`;
|
||||
this.type = 'VerifyError';
|
||||
this.message = '';
|
||||
this.code = code;
|
||||
this.reason = reason;
|
||||
this.score = score;
|
||||
this.hash = msg.hash('hex');
|
||||
this.malleated = malleated || false;
|
||||
|
||||
if (Error.captureStackTrace)
|
||||
Error.captureStackTrace(this, VerifyError);
|
||||
this.message = `Verification failure: ${reason}`
|
||||
+ ` (code=${code} score=${score} hash=${msg.rhash()})`;
|
||||
|
||||
if (Error.captureStackTrace)
|
||||
Error.captureStackTrace(this, VerifyError);
|
||||
}
|
||||
}
|
||||
|
||||
Object.setPrototypeOf(VerifyError.prototype, Error.prototype);
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
@ -14,49 +14,341 @@ const consensus = require('./consensus');
|
||||
const TimeData = require('./timedata');
|
||||
|
||||
/**
|
||||
* Network
|
||||
* Represents a network.
|
||||
* @alias module:protocol.Network
|
||||
* @constructor
|
||||
* @param {Object|NetworkType} options - See {@link module:network}.
|
||||
*/
|
||||
|
||||
function Network(options) {
|
||||
if (!(this instanceof Network))
|
||||
return new Network(options);
|
||||
class Network {
|
||||
/**
|
||||
* Create a network.
|
||||
* @constructor
|
||||
* @param {Object} options
|
||||
*/
|
||||
|
||||
assert(!Network[options.type], 'Cannot create two networks.');
|
||||
constructor(options) {
|
||||
assert(!Network[options.type], 'Cannot create two networks.');
|
||||
|
||||
this.type = options.type;
|
||||
this.seeds = options.seeds;
|
||||
this.magic = options.magic;
|
||||
this.port = options.port;
|
||||
this.checkpointMap = options.checkpointMap;
|
||||
this.lastCheckpoint = options.lastCheckpoint;
|
||||
this.checkpoints = [];
|
||||
this.halvingInterval = options.halvingInterval;
|
||||
this.genesis = options.genesis;
|
||||
this.genesisBlock = options.genesisBlock;
|
||||
this.pow = options.pow;
|
||||
this.block = options.block;
|
||||
this.bip30 = options.bip30;
|
||||
this.activationThreshold = options.activationThreshold;
|
||||
this.minerWindow = options.minerWindow;
|
||||
this.deployments = options.deployments;
|
||||
this.deploys = options.deploys;
|
||||
this.unknownBits = ~consensus.VERSION_TOP_MASK;
|
||||
this.keyPrefix = options.keyPrefix;
|
||||
this.addressPrefix = options.addressPrefix;
|
||||
this.requireStandard = options.requireStandard;
|
||||
this.rpcPort = options.rpcPort;
|
||||
this.walletPort = options.walletPort;
|
||||
this.minRelay = options.minRelay;
|
||||
this.feeRate = options.feeRate;
|
||||
this.maxFeeRate = options.maxFeeRate;
|
||||
this.selfConnect = options.selfConnect;
|
||||
this.requestMempool = options.requestMempool;
|
||||
this.time = new TimeData();
|
||||
this.type = options.type;
|
||||
this.seeds = options.seeds;
|
||||
this.magic = options.magic;
|
||||
this.port = options.port;
|
||||
this.checkpointMap = options.checkpointMap;
|
||||
this.lastCheckpoint = options.lastCheckpoint;
|
||||
this.checkpoints = [];
|
||||
this.halvingInterval = options.halvingInterval;
|
||||
this.genesis = options.genesis;
|
||||
this.genesisBlock = options.genesisBlock;
|
||||
this.pow = options.pow;
|
||||
this.block = options.block;
|
||||
this.bip30 = options.bip30;
|
||||
this.activationThreshold = options.activationThreshold;
|
||||
this.minerWindow = options.minerWindow;
|
||||
this.deployments = options.deployments;
|
||||
this.deploys = options.deploys;
|
||||
this.unknownBits = ~consensus.VERSION_TOP_MASK;
|
||||
this.keyPrefix = options.keyPrefix;
|
||||
this.addressPrefix = options.addressPrefix;
|
||||
this.requireStandard = options.requireStandard;
|
||||
this.rpcPort = options.rpcPort;
|
||||
this.walletPort = options.walletPort;
|
||||
this.minRelay = options.minRelay;
|
||||
this.feeRate = options.feeRate;
|
||||
this.maxFeeRate = options.maxFeeRate;
|
||||
this.selfConnect = options.selfConnect;
|
||||
this.requestMempool = options.requestMempool;
|
||||
this.time = new TimeData();
|
||||
|
||||
this.init();
|
||||
this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a deployment by bit index.
|
||||
* @param {Number} bit
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
init() {
|
||||
let bits = 0;
|
||||
|
||||
for (const deployment of this.deploys)
|
||||
bits |= 1 << deployment.bit;
|
||||
|
||||
bits |= consensus.VERSION_TOP_MASK;
|
||||
|
||||
this.unknownBits = ~bits >>> 0;
|
||||
|
||||
for (const key of Object.keys(this.checkpointMap)) {
|
||||
const hash = this.checkpointMap[key];
|
||||
const height = Number(key);
|
||||
|
||||
this.checkpoints.push({ hash, height });
|
||||
}
|
||||
|
||||
this.checkpoints.sort(cmpNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a deployment by bit index.
|
||||
* @param {Number} bit
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
byBit(bit) {
|
||||
const index = binary.search(this.deploys, bit, cmpBit);
|
||||
|
||||
if (index === -1)
|
||||
return null;
|
||||
|
||||
return this.deploys[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get network adjusted time.
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
now() {
|
||||
return this.time.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get network adjusted time in milliseconds.
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
ms() {
|
||||
return this.time.ms();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a network. Get existing network if possible.
|
||||
* @param {NetworkType|Object} options
|
||||
* @returns {Network}
|
||||
*/
|
||||
|
||||
static create(options) {
|
||||
if (typeof options === 'string')
|
||||
options = networks[options];
|
||||
|
||||
assert(options, 'Unknown network.');
|
||||
|
||||
if (Network[options.type])
|
||||
return Network[options.type];
|
||||
|
||||
const network = new Network(options);
|
||||
|
||||
Network[network.type] = network;
|
||||
|
||||
if (!Network.primary)
|
||||
Network.primary = network;
|
||||
|
||||
return network;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default network. This network will be used
|
||||
* if nothing is passed as the `network` option for
|
||||
* certain objects.
|
||||
* @param {NetworkType} type - Network type.
|
||||
* @returns {Network}
|
||||
*/
|
||||
|
||||
static set(type) {
|
||||
assert(typeof type === 'string', 'Bad network.');
|
||||
Network.primary = Network.get(type);
|
||||
Network.type = type;
|
||||
return Network.primary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a network with a string or a Network object.
|
||||
* @param {NetworkType|Network} type - Network type.
|
||||
* @returns {Network}
|
||||
*/
|
||||
|
||||
static get(type) {
|
||||
if (!type) {
|
||||
assert(Network.primary, 'No default network.');
|
||||
return Network.primary;
|
||||
}
|
||||
|
||||
if (type instanceof Network)
|
||||
return type;
|
||||
|
||||
if (typeof type === 'string')
|
||||
return Network.create(type);
|
||||
|
||||
throw new Error('Unknown network.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a network with a string or a Network object.
|
||||
* @param {NetworkType|Network} type - Network type.
|
||||
* @returns {Network}
|
||||
*/
|
||||
|
||||
static ensure(type) {
|
||||
if (!type) {
|
||||
assert(Network.primary, 'No default network.');
|
||||
return Network.primary;
|
||||
}
|
||||
|
||||
if (type instanceof Network)
|
||||
return type;
|
||||
|
||||
if (typeof type === 'string') {
|
||||
if (networks[type])
|
||||
return Network.create(type);
|
||||
}
|
||||
|
||||
assert(Network.primary, 'No default network.');
|
||||
|
||||
return Network.primary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a network by an associated comparator.
|
||||
* @private
|
||||
* @param {Object} value
|
||||
* @param {Function} compare
|
||||
* @param {Network|null} network
|
||||
* @param {String} name
|
||||
* @returns {Network}
|
||||
*/
|
||||
|
||||
static by(value, compare, network, name) {
|
||||
if (network) {
|
||||
network = Network.get(network);
|
||||
if (compare(network, value))
|
||||
return network;
|
||||
throw new Error(`Network mismatch for ${name}.`);
|
||||
}
|
||||
|
||||
for (const type of networks.types) {
|
||||
network = networks[type];
|
||||
if (compare(network, value))
|
||||
return Network.get(type);
|
||||
}
|
||||
|
||||
throw new Error(`Network not found for ${name}.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a network by its magic number.
|
||||
* @param {Number} value
|
||||
* @param {Network?} network
|
||||
* @returns {Network}
|
||||
*/
|
||||
|
||||
static fromMagic(value, network) {
|
||||
return Network.by(value, cmpMagic, network, 'magic number');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a network by its WIF prefix.
|
||||
* @param {Number} value
|
||||
* @param {Network?} network
|
||||
* @returns {Network}
|
||||
*/
|
||||
|
||||
static fromWIF(prefix, network) {
|
||||
return Network.by(prefix, cmpWIF, network, 'WIF');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a network by its xpubkey prefix.
|
||||
* @param {Number} value
|
||||
* @param {Network?} network
|
||||
* @returns {Network}
|
||||
*/
|
||||
|
||||
static fromPublic(prefix, network) {
|
||||
return Network.by(prefix, cmpPub, network, 'xpubkey');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a network by its xprivkey prefix.
|
||||
* @param {Number} value
|
||||
* @param {Network?} network
|
||||
* @returns {Network}
|
||||
*/
|
||||
|
||||
static fromPrivate(prefix, network) {
|
||||
return Network.by(prefix, cmpPriv, network, 'xprivkey');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a network by its xpubkey base58 prefix.
|
||||
* @param {String} prefix
|
||||
* @param {Network?} network
|
||||
* @returns {Network}
|
||||
*/
|
||||
|
||||
static fromPublic58(prefix, network) {
|
||||
return Network.by(prefix, cmpPub58, network, 'xpubkey');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a network by its xprivkey base58 prefix.
|
||||
* @param {String} prefix
|
||||
* @param {Network?} network
|
||||
* @returns {Network}
|
||||
*/
|
||||
|
||||
static fromPrivate58(prefix, network) {
|
||||
return Network.by(prefix, cmpPriv58, network, 'xprivkey');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a network by its base58 address prefix.
|
||||
* @param {Number} value
|
||||
* @param {Network?} network
|
||||
* @returns {Network}
|
||||
*/
|
||||
|
||||
static fromAddress(prefix, network) {
|
||||
return Network.by(prefix, cmpAddress, network, 'base58 address');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a network by its bech32 address prefix.
|
||||
* @param {String} hrp
|
||||
* @param {Network?} network
|
||||
* @returns {Network}
|
||||
*/
|
||||
|
||||
static fromBech32(hrp, network) {
|
||||
return Network.by(hrp, cmpBech32, network, 'bech32 address');
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the network to a string.
|
||||
* @returns {String}
|
||||
*/
|
||||
|
||||
toString() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspect the network.
|
||||
* @returns {String}
|
||||
*/
|
||||
|
||||
inspect() {
|
||||
return `<Network: ${this.type}>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test an object to see if it is a Network.
|
||||
* @param {Object} obj
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
static isNetwork(obj) {
|
||||
return obj instanceof Network;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -83,294 +375,6 @@ Network.regtest = null;
|
||||
Network.segnet4 = null;
|
||||
Network.simnet = null;
|
||||
|
||||
/**
|
||||
* Get a deployment by bit index.
|
||||
* @param {Number} bit
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
Network.prototype.init = function init() {
|
||||
let bits = 0;
|
||||
|
||||
for (const deployment of this.deploys)
|
||||
bits |= 1 << deployment.bit;
|
||||
|
||||
bits |= consensus.VERSION_TOP_MASK;
|
||||
|
||||
this.unknownBits = ~bits >>> 0;
|
||||
|
||||
for (const key of Object.keys(this.checkpointMap)) {
|
||||
const hash = this.checkpointMap[key];
|
||||
const height = Number(key);
|
||||
|
||||
this.checkpoints.push({ hash, height });
|
||||
}
|
||||
|
||||
this.checkpoints.sort(cmpNode);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a deployment by bit index.
|
||||
* @param {Number} bit
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
Network.prototype.byBit = function byBit(bit) {
|
||||
const index = binary.search(this.deploys, bit, cmpBit);
|
||||
|
||||
if (index === -1)
|
||||
return null;
|
||||
|
||||
return this.deploys[index];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get network adjusted time.
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
Network.prototype.now = function now() {
|
||||
return this.time.now();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get network adjusted time in milliseconds.
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
Network.prototype.ms = function ms() {
|
||||
return this.time.ms();
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a network. Get existing network if possible.
|
||||
* @param {NetworkType|Object} options
|
||||
* @returns {Network}
|
||||
*/
|
||||
|
||||
Network.create = function create(options) {
|
||||
if (typeof options === 'string')
|
||||
options = networks[options];
|
||||
|
||||
assert(options, 'Unknown network.');
|
||||
|
||||
if (Network[options.type])
|
||||
return Network[options.type];
|
||||
|
||||
const network = new Network(options);
|
||||
|
||||
Network[network.type] = network;
|
||||
|
||||
if (!Network.primary)
|
||||
Network.primary = network;
|
||||
|
||||
return network;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the default network. This network will be used
|
||||
* if nothing is passed as the `network` option for
|
||||
* certain objects.
|
||||
* @param {NetworkType} type - Network type.
|
||||
* @returns {Network}
|
||||
*/
|
||||
|
||||
Network.set = function set(type) {
|
||||
assert(typeof type === 'string', 'Bad network.');
|
||||
Network.primary = Network.get(type);
|
||||
Network.type = type;
|
||||
return Network.primary;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a network with a string or a Network object.
|
||||
* @param {NetworkType|Network} type - Network type.
|
||||
* @returns {Network}
|
||||
*/
|
||||
|
||||
Network.get = function get(type) {
|
||||
if (!type) {
|
||||
assert(Network.primary, 'No default network.');
|
||||
return Network.primary;
|
||||
}
|
||||
|
||||
if (type instanceof Network)
|
||||
return type;
|
||||
|
||||
if (typeof type === 'string')
|
||||
return Network.create(type);
|
||||
|
||||
throw new Error('Unknown network.');
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a network with a string or a Network object.
|
||||
* @param {NetworkType|Network} type - Network type.
|
||||
* @returns {Network}
|
||||
*/
|
||||
|
||||
Network.ensure = function ensure(type) {
|
||||
if (!type) {
|
||||
assert(Network.primary, 'No default network.');
|
||||
return Network.primary;
|
||||
}
|
||||
|
||||
if (type instanceof Network)
|
||||
return type;
|
||||
|
||||
if (typeof type === 'string') {
|
||||
if (networks[type])
|
||||
return Network.create(type);
|
||||
}
|
||||
|
||||
assert(Network.primary, 'No default network.');
|
||||
|
||||
return Network.primary;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a network by an associated comparator.
|
||||
* @private
|
||||
* @param {Object} value
|
||||
* @param {Function} compare
|
||||
* @param {Network|null} network
|
||||
* @param {String} name
|
||||
* @returns {Network}
|
||||
*/
|
||||
|
||||
Network.by = function by(value, compare, network, name) {
|
||||
if (network) {
|
||||
network = Network.get(network);
|
||||
if (compare(network, value))
|
||||
return network;
|
||||
throw new Error(`Network mismatch for ${name}.`);
|
||||
}
|
||||
|
||||
for (const type of networks.types) {
|
||||
network = networks[type];
|
||||
if (compare(network, value))
|
||||
return Network.get(type);
|
||||
}
|
||||
|
||||
throw new Error(`Network not found for ${name}.`);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a network by its magic number.
|
||||
* @param {Number} value
|
||||
* @param {Network?} network
|
||||
* @returns {Network}
|
||||
*/
|
||||
|
||||
Network.fromMagic = function fromMagic(value, network) {
|
||||
return Network.by(value, cmpMagic, network, 'magic number');
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a network by its WIF prefix.
|
||||
* @param {Number} value
|
||||
* @param {Network?} network
|
||||
* @returns {Network}
|
||||
*/
|
||||
|
||||
Network.fromWIF = function fromWIF(prefix, network) {
|
||||
return Network.by(prefix, cmpWIF, network, 'WIF');
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a network by its xpubkey prefix.
|
||||
* @param {Number} value
|
||||
* @param {Network?} network
|
||||
* @returns {Network}
|
||||
*/
|
||||
|
||||
Network.fromPublic = function fromPublic(prefix, network) {
|
||||
return Network.by(prefix, cmpPub, network, 'xpubkey');
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a network by its xprivkey prefix.
|
||||
* @param {Number} value
|
||||
* @param {Network?} network
|
||||
* @returns {Network}
|
||||
*/
|
||||
|
||||
Network.fromPrivate = function fromPrivate(prefix, network) {
|
||||
return Network.by(prefix, cmpPriv, network, 'xprivkey');
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a network by its xpubkey base58 prefix.
|
||||
* @param {String} prefix
|
||||
* @param {Network?} network
|
||||
* @returns {Network}
|
||||
*/
|
||||
|
||||
Network.fromPublic58 = function fromPublic58(prefix, network) {
|
||||
return Network.by(prefix, cmpPub58, network, 'xpubkey');
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a network by its xprivkey base58 prefix.
|
||||
* @param {String} prefix
|
||||
* @param {Network?} network
|
||||
* @returns {Network}
|
||||
*/
|
||||
|
||||
Network.fromPrivate58 = function fromPrivate58(prefix, network) {
|
||||
return Network.by(prefix, cmpPriv58, network, 'xprivkey');
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a network by its base58 address prefix.
|
||||
* @param {Number} value
|
||||
* @param {Network?} network
|
||||
* @returns {Network}
|
||||
*/
|
||||
|
||||
Network.fromAddress = function fromAddress(prefix, network) {
|
||||
return Network.by(prefix, cmpAddress, network, 'base58 address');
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a network by its bech32 address prefix.
|
||||
* @param {String} hrp
|
||||
* @param {Network?} network
|
||||
* @returns {Network}
|
||||
*/
|
||||
|
||||
Network.fromBech32 = function fromBech32(hrp, network) {
|
||||
return Network.by(hrp, cmpBech32, network, 'bech32 address');
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert the network to a string.
|
||||
* @returns {String}
|
||||
*/
|
||||
|
||||
Network.prototype.toString = function toString() {
|
||||
return this.type;
|
||||
};
|
||||
|
||||
/**
|
||||
* Inspect the network.
|
||||
* @returns {String}
|
||||
*/
|
||||
|
||||
Network.prototype.inspect = function inspect() {
|
||||
return `<Network: ${this.type}>`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test an object to see if it is a Network.
|
||||
* @param {Object} obj
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Network.isNetwork = function isNetwork(obj) {
|
||||
return obj instanceof Network;
|
||||
};
|
||||
|
||||
/*
|
||||
* Set initial network.
|
||||
*/
|
||||
|
||||
@ -12,123 +12,126 @@ const util = require('../utils/util');
|
||||
const binary = require('../utils/binary');
|
||||
|
||||
/**
|
||||
* Time Data
|
||||
* An object which handles "adjusted time". This may not
|
||||
* look it, but this is actually a semi-consensus-critical
|
||||
* piece of code. It handles version packets from peers
|
||||
* and calculates what to offset our system clock's time by.
|
||||
* @alias module:protocol.TimeData
|
||||
* @constructor
|
||||
* @param {Number} [limit=200]
|
||||
* @extends EventEmitter
|
||||
* @property {Array} samples
|
||||
* @property {Object} known
|
||||
* @property {Number} limit
|
||||
* @property {Number} offset
|
||||
*/
|
||||
|
||||
function TimeData(limit) {
|
||||
if (!(this instanceof TimeData))
|
||||
return new TimeData(limit);
|
||||
class TimeData extends EventEmitter {
|
||||
/**
|
||||
* Create time data.
|
||||
* @constructor
|
||||
* @param {Number} [limit=200]
|
||||
*/
|
||||
|
||||
EventEmitter.call(this);
|
||||
constructor(limit) {
|
||||
super();
|
||||
|
||||
if (limit == null)
|
||||
limit = 200;
|
||||
if (limit == null)
|
||||
limit = 200;
|
||||
|
||||
this.samples = [];
|
||||
this.known = new Map();
|
||||
this.limit = limit;
|
||||
this.offset = 0;
|
||||
this.checked = false;
|
||||
}
|
||||
this.samples = [];
|
||||
this.known = new Map();
|
||||
this.limit = limit;
|
||||
this.offset = 0;
|
||||
this.checked = false;
|
||||
}
|
||||
|
||||
Object.setPrototypeOf(TimeData.prototype, EventEmitter.prototype);
|
||||
/**
|
||||
* Add time data.
|
||||
* @param {String} id
|
||||
* @param {Number} time
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add time data.
|
||||
* @param {String} id
|
||||
* @param {Number} time
|
||||
*/
|
||||
add(id, time) {
|
||||
if (this.samples.length >= this.limit)
|
||||
return;
|
||||
|
||||
TimeData.prototype.add = function add(id, time) {
|
||||
if (this.samples.length >= this.limit)
|
||||
return;
|
||||
if (this.known.has(id))
|
||||
return;
|
||||
|
||||
if (this.known.has(id))
|
||||
return;
|
||||
const sample = time - util.now();
|
||||
|
||||
const sample = time - util.now();
|
||||
this.known.set(id, sample);
|
||||
|
||||
this.known.set(id, sample);
|
||||
binary.insert(this.samples, sample, compare);
|
||||
|
||||
binary.insert(this.samples, sample, compare);
|
||||
this.emit('sample', sample, this.samples.length);
|
||||
|
||||
this.emit('sample', sample, this.samples.length);
|
||||
if (this.samples.length >= 5 && this.samples.length % 2 === 1) {
|
||||
let median = this.samples[this.samples.length >>> 1];
|
||||
|
||||
if (this.samples.length >= 5 && this.samples.length % 2 === 1) {
|
||||
let median = this.samples[this.samples.length >>> 1];
|
||||
if (Math.abs(median) >= 70 * 60) {
|
||||
if (!this.checked) {
|
||||
let match = false;
|
||||
|
||||
if (Math.abs(median) >= 70 * 60) {
|
||||
if (!this.checked) {
|
||||
let match = false;
|
||||
for (const offset of this.samples) {
|
||||
if (offset !== 0 && Math.abs(offset) < 5 * 60) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (const offset of this.samples) {
|
||||
if (offset !== 0 && Math.abs(offset) < 5 * 60) {
|
||||
match = true;
|
||||
break;
|
||||
if (!match) {
|
||||
this.checked = true;
|
||||
this.emit('mismatch');
|
||||
}
|
||||
}
|
||||
|
||||
if (!match) {
|
||||
this.checked = true;
|
||||
this.emit('mismatch');
|
||||
}
|
||||
median = 0;
|
||||
}
|
||||
|
||||
median = 0;
|
||||
this.offset = median;
|
||||
this.emit('offset', this.offset);
|
||||
}
|
||||
|
||||
this.offset = median;
|
||||
this.emit('offset', this.offset);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the current adjusted time.
|
||||
* @returns {Number} Adjusted Time.
|
||||
*/
|
||||
/**
|
||||
* Get the current adjusted time.
|
||||
* @returns {Number} Adjusted Time.
|
||||
*/
|
||||
|
||||
TimeData.prototype.now = function now() {
|
||||
return util.now() + this.offset;
|
||||
};
|
||||
now() {
|
||||
return util.now() + this.offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust a timestamp.
|
||||
* @param {Number} time
|
||||
* @returns {Number} Adjusted Time.
|
||||
*/
|
||||
/**
|
||||
* Adjust a timestamp.
|
||||
* @param {Number} time
|
||||
* @returns {Number} Adjusted Time.
|
||||
*/
|
||||
|
||||
TimeData.prototype.adjust = function adjust(time) {
|
||||
return time + this.offset;
|
||||
};
|
||||
adjust(time) {
|
||||
return time + this.offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unadjust a timestamp.
|
||||
* @param {Number} time
|
||||
* @returns {Number} Local Time.
|
||||
*/
|
||||
/**
|
||||
* Unadjust a timestamp.
|
||||
* @param {Number} time
|
||||
* @returns {Number} Local Time.
|
||||
*/
|
||||
|
||||
TimeData.prototype.local = function local(time) {
|
||||
return time - this.offset;
|
||||
};
|
||||
local(time) {
|
||||
return time - this.offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current adjusted time in milliseconds.
|
||||
* @returns {Number} Adjusted Time.
|
||||
*/
|
||||
/**
|
||||
* Get the current adjusted time in milliseconds.
|
||||
* @returns {Number} Adjusted Time.
|
||||
*/
|
||||
|
||||
TimeData.prototype.ms = function ms() {
|
||||
return Date.now() + this.offset * 1000;
|
||||
};
|
||||
ms() {
|
||||
return Date.now() + this.offset * 1000;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Helpers
|
||||
|
||||
Loading…
Reference in New Issue
Block a user