wip
This commit is contained in:
parent
aceddb12d9
commit
861a80d678
@ -31,8 +31,8 @@ Encoding.prototype.encodeAddressIndexKey = function(address, height, txid, index
|
||||
inputBuffer.writeUInt8(input || 0);
|
||||
buffers.push(inputBuffer);
|
||||
|
||||
var timestampBuffer = new Buffer(8);
|
||||
timestampBuffer.writeDoubleBE(timestamp || 0)
|
||||
var timestampBuffer = new Buffer(4);
|
||||
timestampBuffer.writeUInt32BE(timestamp || 0);
|
||||
buffers.push(timestampBuffer);
|
||||
|
||||
return Buffer.concat(buffers);
|
||||
@ -46,7 +46,7 @@ Encoding.prototype.decodeAddressIndexKey = function(buffer) {
|
||||
var txid = buffer.slice(addressSize + 8, addressSize + 40).toString('hex');
|
||||
var index = buffer.readUInt32BE(addressSize + 40);
|
||||
var input = buffer.readUInt8(addressSize + 44);
|
||||
var timestamp = buffer.readDoubleBE(addressSize + 45);
|
||||
var timestamp = buffer.readUInt32BE(addressSize + 45);
|
||||
return {
|
||||
address: address,
|
||||
height: height,
|
||||
@ -57,7 +57,7 @@ Encoding.prototype.decodeAddressIndexKey = function(buffer) {
|
||||
};
|
||||
};
|
||||
|
||||
Encoding.prototype.encodeUtxoIndexKey = function(address, txid, outputIndex, timestamp) {
|
||||
Encoding.prototype.encodeUtxoIndexKey = function(address, txid, outputIndex) {
|
||||
var prefix = new Buffer('01', 'hex');
|
||||
var buffers = [this.servicePrefix, prefix];
|
||||
|
||||
@ -97,15 +97,15 @@ Encoding.prototype.encodeUtxoIndexValue = function(height, satoshis, timestamp,
|
||||
var satoshisBuffer = new Buffer(8);
|
||||
satoshisBuffer.writeDoubleBE(satoshis);
|
||||
var timestampBuffer = new Buffer(8);
|
||||
timestampBuffer.writeDoubleBE(timestamp || 0)
|
||||
timestampBuffer.writeUInt32BE(timestamp || 0)
|
||||
return Buffer.concat([heightBuffer, satoshisBuffer, timestampBuffer, scriptBuffer]);
|
||||
};
|
||||
|
||||
Encoding.prototype.decodeUtxoIndexValue = function(buffer) {
|
||||
var height = buffer.readUInt32BE();
|
||||
var satoshis = buffer.readDoubleBE(4);
|
||||
var timestamp = buffer.readDoubleBE(12);
|
||||
var scriptBuffer = buffer.slice(20);
|
||||
var timestamp = buffer.readDoubleBE(8);
|
||||
var scriptBuffer = buffer.slice(12);
|
||||
return {
|
||||
height: height,
|
||||
satoshis: satoshis,
|
||||
|
||||
50
lib/services/header/encoding.js
Normal file
50
lib/services/header/encoding.js
Normal file
@ -0,0 +1,50 @@
|
||||
'use strict';
|
||||
|
||||
|
||||
function Encoding(servicePrefix) {
|
||||
this._servicePrefix = servicePrefix;
|
||||
}
|
||||
|
||||
|
||||
// ---- hash --> height
|
||||
Encoding.prototype.encodeHeaderKey = function(hash) {
|
||||
return Buffer.concat([ this._servicePrefix, new Buffer(hash, 'hex') ]);
|
||||
};
|
||||
|
||||
Encoding.prototype.decodeHeaderKey = function(buffer) {
|
||||
return buffer.slice(3).toString('hex');
|
||||
};
|
||||
|
||||
Encoding.prototype.encodeHeaderValue = function(header) {
|
||||
var versionBuf = new Buffer(4);
|
||||
versionBuf.writeInt32BE(header.version);
|
||||
var prevHash = new Buffer(header.prevHash, 'hex');
|
||||
var merkleRoot = new Buffer(header.merkleRoot, 'hex');
|
||||
var tsBuf = new Buffer(4);
|
||||
tsBuf.writeUInt32BE(header.timestamp);
|
||||
var bitsBuf = new Buffer(4);
|
||||
bitsBuf.writeUInt32BE(header.bits);
|
||||
var nonceBuf = new Buffer(4);
|
||||
nonceBuf.writeUInt32BE(header.nonce);
|
||||
return Buffer.concat([ versionBuf, prevHash, merkleRoot, tsBuf, bitsBuf, nonceBuf ]);
|
||||
};
|
||||
|
||||
Encoding.prototype.decodeHeaderValue = function(buffer) {
|
||||
var version = buffer.readInt32BE();
|
||||
var prevHash = buffer.slice(4, 36).toString('hex');
|
||||
var merkleRoot = buffer.slice(36, 68).toString('hex');
|
||||
var ts = buffer.readUInt32BE(68);
|
||||
var bits = buffer.readUInt32BE(72);
|
||||
var nonce = buffer.readUInt32BE(76);
|
||||
return {
|
||||
version: version,
|
||||
prevHash: prevHash,
|
||||
merkleRoot: merkleRoot,
|
||||
timestakmp: ts,
|
||||
bits: bits,
|
||||
nonce: nonce
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = Encoding;
|
||||
|
||||
192
lib/services/header/index.js
Normal file
192
lib/services/header/index.js
Normal file
@ -0,0 +1,192 @@
|
||||
'use strict';
|
||||
|
||||
var BaseService = require('../../service');
|
||||
var inherits = require('util').inherits;
|
||||
var Encoding = require('./encoding');
|
||||
var index = require('../../');
|
||||
var log = index.log;
|
||||
var utils = require('../../utils');
|
||||
var constants = require('../../constants');
|
||||
|
||||
var HeaderService = function(options) {
|
||||
|
||||
BaseService.call(this, options);
|
||||
|
||||
this._tip = null;
|
||||
this._p2p = this.node.services.p2p;
|
||||
this._db = this.node.services.db;
|
||||
};
|
||||
|
||||
inherits(HeaderService, BaseService);
|
||||
|
||||
HeaderService.dependencies = [ 'p2p', 'db' ];
|
||||
|
||||
|
||||
// --- public prototype functions
|
||||
HeaderService.prototype.getAPIMethods = function() {
|
||||
|
||||
var methods = [
|
||||
['getAllHeaders', this, this.getHeaders, 0]
|
||||
];
|
||||
|
||||
return methods;
|
||||
|
||||
};
|
||||
|
||||
HeaderService.prototype.start = function(callback) {
|
||||
|
||||
var self = this;
|
||||
|
||||
self._db.getPrefix(self.name, function(err, prefix) {
|
||||
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
self._db.getServiceTip(self.name, function(err, tip) {
|
||||
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
self._tip = tip;
|
||||
self._encoding = new Encoding(prefix);
|
||||
self._setListeners();
|
||||
self._startSubscriptions();
|
||||
callback();
|
||||
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
HeaderService.prototype.stop = function(callback) {
|
||||
callback();
|
||||
};
|
||||
|
||||
HeaderService.prototype._startSubscriptions = function() {
|
||||
|
||||
if (this._subscribed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._subscribed = true;
|
||||
if (!this._bus) {
|
||||
this._bus = this.node.openBus({remoteAddress: 'localhost'});
|
||||
}
|
||||
|
||||
this._bus.on('p2p/headers', this._onHeaders.bind(this));
|
||||
this._bus.subscribe('p2p/headers');
|
||||
};
|
||||
|
||||
HeaderService.prototype._onHeaders = function(headers) {
|
||||
|
||||
if (!headers || headers.length < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._tip.hash = headers[headers.length - 1].hash;
|
||||
this._tip.height = this._tip.height + headers.length;
|
||||
|
||||
var operations = this._getHeaderOperations(headers);
|
||||
|
||||
var tipOps = utils.encodeTip(this._tip, this.name);
|
||||
|
||||
operations.push({
|
||||
type: 'put',
|
||||
key: tipOps.key,
|
||||
value: tipOps.value
|
||||
});
|
||||
|
||||
this._db.batch(operations);
|
||||
|
||||
if (this._tip.height >= this._bestHeight) {
|
||||
log.info('Header download complete.');
|
||||
this.emit('headers');
|
||||
return;
|
||||
}
|
||||
|
||||
this._sync();
|
||||
|
||||
};
|
||||
|
||||
HeaderService.prototype._getHeaderOperations = function(headers) {
|
||||
|
||||
var self = this;
|
||||
return headers.map(function(header) {
|
||||
return {
|
||||
type: 'put',
|
||||
key: self._encoding.encodeHeaderKey(header.hash),
|
||||
value: self._encoding.encodeHeaderValue(header)
|
||||
};
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
HeaderService.prototype._setListeners = function() {
|
||||
|
||||
this._p2p.once('bestHeight', this._onBestHeight.bind(this));
|
||||
|
||||
};
|
||||
|
||||
HeaderService.prototype._onBestHeight = function(height) {
|
||||
this._bestHeight = height;
|
||||
this._startSync();
|
||||
};
|
||||
|
||||
HeaderService.prototype._startSync = function() {
|
||||
|
||||
this._numNeeded = this._bestHeight - this._tip.height;
|
||||
if (this._numNeeded <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
log.info('Gathering: ' + this._numNeeded + ' ' + 'header(s) from the peer-to-peer network.');
|
||||
|
||||
this._p2pHeaderCallsNeeded = Math.ceil(this._numNeeded / 500);
|
||||
this._sync();
|
||||
|
||||
};
|
||||
|
||||
HeaderService.prototype._sync = function() {
|
||||
|
||||
if (--this._p2pHeaderCallsNeeded > 0) {
|
||||
|
||||
log.info('Headers download progress: ' + this._tip.height + '/' +
|
||||
this._numNeeded + ' (' + (this._tip.height / this._numNeeded*100).toFixed(2) + '%)');
|
||||
this._p2p.getHeaders({ startHash: this._tip.hash });
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
HeaderService.prototype.getHeaders = function(callback) {
|
||||
|
||||
var self = this;
|
||||
var results = [];
|
||||
var start = self._encoding.encodeHeaderKey(0);
|
||||
var stream = self._db.createReadStream(criteria);
|
||||
|
||||
var streamErr;
|
||||
stream.on('error', function(error) {
|
||||
streamErr = error;
|
||||
});
|
||||
|
||||
stream.on('data', function(data) {
|
||||
results.push({
|
||||
hash: self.__encoding.decodeHeaderKey(data.key),
|
||||
header: self._encoding.decodeHeaderValue(data.value)
|
||||
});
|
||||
});
|
||||
|
||||
stream.on('end', function() {
|
||||
if (streamErr) {
|
||||
return streamErr;
|
||||
}
|
||||
callback(null, results);
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
module.exports = HeaderService;
|
||||
|
||||
@ -16,25 +16,25 @@ Encoding.prototype.decodeBlockTimestampKey = function(buffer) {
|
||||
};
|
||||
|
||||
Encoding.prototype.encodeBlockTimestampValue = function(timestamp) {
|
||||
var timestampBuffer = new Buffer(new Array(8));
|
||||
timestampBuffer.writeDoubleBE(timestamp);
|
||||
var timestampBuffer = new Buffer(4);
|
||||
timestampBuffer.writeUInt32BE(timestamp);
|
||||
return timestampBuffer;
|
||||
};
|
||||
|
||||
Encoding.prototype.decodeBlockTimestampValue = function(buffer) {
|
||||
return buffer.readDoubleBE();
|
||||
return buffer.readUInt32BE();
|
||||
};
|
||||
|
||||
|
||||
// ---- timestamp -> block hash
|
||||
Encoding.prototype.encodeTimestampBlockKey = function(timestamp) {
|
||||
var timestampBuffer = new Buffer(new Array(8));
|
||||
timestampBuffer.writeDoubleBE(timestamp);
|
||||
var timestampBuffer = new Buffer(4);
|
||||
timestampBuffer.writeUInt32BE(timestamp);
|
||||
return Buffer.concat([this._servicePrefix, this._timestampPrefix, timestampBuffer]);
|
||||
};
|
||||
|
||||
Encoding.prototype.decodeTimestampBlockKey = function(buffer) {
|
||||
return buffer.readDoubleBE(3);
|
||||
return buffer.readUInt32BE(3);
|
||||
};
|
||||
|
||||
Encoding.prototype.encodeTimestampBlockValue = function(hash) {
|
||||
|
||||
@ -18,8 +18,8 @@ Encoding.prototype.encodeTransactionValue = function(transaction) {
|
||||
var heightBuffer = new Buffer(4);
|
||||
heightBuffer.writeUInt32BE(transaction.__height);
|
||||
|
||||
var timestampBuffer = new Buffer(8);
|
||||
timestampBuffer.writeDoubleBE(transaction.__timestamp);
|
||||
var timestampBuffer = new Buffer(4);
|
||||
timestampBuffer.writeUInt32BE(transaction.__timestamp);
|
||||
|
||||
var inputValues = transaction.__inputValues;
|
||||
var inputValuesBuffer = new Buffer(8 * inputValues.length);
|
||||
@ -31,14 +31,14 @@ Encoding.prototype.encodeTransactionValue = function(transaction) {
|
||||
inputValuesLengthBuffer.writeUInt16BE(inputValues.length);
|
||||
|
||||
return new Buffer.concat([heightBuffer, timestampBuffer,
|
||||
inputValuesLengthBuffer, inputValuesBuffer, transaction.toBuffer()]);
|
||||
inputValuesLengthBuffer, inputValuesBuffer, transaction.toRaw()]);
|
||||
};
|
||||
|
||||
Encoding.prototype.decodeTransactionValue = function(buffer) {
|
||||
var height = buffer.readUInt32BE();
|
||||
var timestamp = buffer.readDoubleBE(4);
|
||||
var timestamp = buffer.readUInt32BE(4);
|
||||
|
||||
var inputValuesLength = buffer.readUInt16BE(12);
|
||||
var inputValuesLength = buffer.readUInt16BE(8);
|
||||
var inputValues = [];
|
||||
for(var i = 0; i < inputValuesLength; i++) {
|
||||
inputValues.push(buffer.readDoubleBE(i * 8 + 14));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user