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);
|
inputBuffer.writeUInt8(input || 0);
|
||||||
buffers.push(inputBuffer);
|
buffers.push(inputBuffer);
|
||||||
|
|
||||||
var timestampBuffer = new Buffer(8);
|
var timestampBuffer = new Buffer(4);
|
||||||
timestampBuffer.writeDoubleBE(timestamp || 0)
|
timestampBuffer.writeUInt32BE(timestamp || 0);
|
||||||
buffers.push(timestampBuffer);
|
buffers.push(timestampBuffer);
|
||||||
|
|
||||||
return Buffer.concat(buffers);
|
return Buffer.concat(buffers);
|
||||||
@ -46,7 +46,7 @@ Encoding.prototype.decodeAddressIndexKey = function(buffer) {
|
|||||||
var txid = buffer.slice(addressSize + 8, addressSize + 40).toString('hex');
|
var txid = buffer.slice(addressSize + 8, addressSize + 40).toString('hex');
|
||||||
var index = buffer.readUInt32BE(addressSize + 40);
|
var index = buffer.readUInt32BE(addressSize + 40);
|
||||||
var input = buffer.readUInt8(addressSize + 44);
|
var input = buffer.readUInt8(addressSize + 44);
|
||||||
var timestamp = buffer.readDoubleBE(addressSize + 45);
|
var timestamp = buffer.readUInt32BE(addressSize + 45);
|
||||||
return {
|
return {
|
||||||
address: address,
|
address: address,
|
||||||
height: height,
|
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 prefix = new Buffer('01', 'hex');
|
||||||
var buffers = [this.servicePrefix, prefix];
|
var buffers = [this.servicePrefix, prefix];
|
||||||
|
|
||||||
@ -97,15 +97,15 @@ Encoding.prototype.encodeUtxoIndexValue = function(height, satoshis, timestamp,
|
|||||||
var satoshisBuffer = new Buffer(8);
|
var satoshisBuffer = new Buffer(8);
|
||||||
satoshisBuffer.writeDoubleBE(satoshis);
|
satoshisBuffer.writeDoubleBE(satoshis);
|
||||||
var timestampBuffer = new Buffer(8);
|
var timestampBuffer = new Buffer(8);
|
||||||
timestampBuffer.writeDoubleBE(timestamp || 0)
|
timestampBuffer.writeUInt32BE(timestamp || 0)
|
||||||
return Buffer.concat([heightBuffer, satoshisBuffer, timestampBuffer, scriptBuffer]);
|
return Buffer.concat([heightBuffer, satoshisBuffer, timestampBuffer, scriptBuffer]);
|
||||||
};
|
};
|
||||||
|
|
||||||
Encoding.prototype.decodeUtxoIndexValue = function(buffer) {
|
Encoding.prototype.decodeUtxoIndexValue = function(buffer) {
|
||||||
var height = buffer.readUInt32BE();
|
var height = buffer.readUInt32BE();
|
||||||
var satoshis = buffer.readDoubleBE(4);
|
var satoshis = buffer.readDoubleBE(4);
|
||||||
var timestamp = buffer.readDoubleBE(12);
|
var timestamp = buffer.readDoubleBE(8);
|
||||||
var scriptBuffer = buffer.slice(20);
|
var scriptBuffer = buffer.slice(12);
|
||||||
return {
|
return {
|
||||||
height: height,
|
height: height,
|
||||||
satoshis: satoshis,
|
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) {
|
Encoding.prototype.encodeBlockTimestampValue = function(timestamp) {
|
||||||
var timestampBuffer = new Buffer(new Array(8));
|
var timestampBuffer = new Buffer(4);
|
||||||
timestampBuffer.writeDoubleBE(timestamp);
|
timestampBuffer.writeUInt32BE(timestamp);
|
||||||
return timestampBuffer;
|
return timestampBuffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
Encoding.prototype.decodeBlockTimestampValue = function(buffer) {
|
Encoding.prototype.decodeBlockTimestampValue = function(buffer) {
|
||||||
return buffer.readDoubleBE();
|
return buffer.readUInt32BE();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// ---- timestamp -> block hash
|
// ---- timestamp -> block hash
|
||||||
Encoding.prototype.encodeTimestampBlockKey = function(timestamp) {
|
Encoding.prototype.encodeTimestampBlockKey = function(timestamp) {
|
||||||
var timestampBuffer = new Buffer(new Array(8));
|
var timestampBuffer = new Buffer(4);
|
||||||
timestampBuffer.writeDoubleBE(timestamp);
|
timestampBuffer.writeUInt32BE(timestamp);
|
||||||
return Buffer.concat([this._servicePrefix, this._timestampPrefix, timestampBuffer]);
|
return Buffer.concat([this._servicePrefix, this._timestampPrefix, timestampBuffer]);
|
||||||
};
|
};
|
||||||
|
|
||||||
Encoding.prototype.decodeTimestampBlockKey = function(buffer) {
|
Encoding.prototype.decodeTimestampBlockKey = function(buffer) {
|
||||||
return buffer.readDoubleBE(3);
|
return buffer.readUInt32BE(3);
|
||||||
};
|
};
|
||||||
|
|
||||||
Encoding.prototype.encodeTimestampBlockValue = function(hash) {
|
Encoding.prototype.encodeTimestampBlockValue = function(hash) {
|
||||||
|
|||||||
@ -18,8 +18,8 @@ Encoding.prototype.encodeTransactionValue = function(transaction) {
|
|||||||
var heightBuffer = new Buffer(4);
|
var heightBuffer = new Buffer(4);
|
||||||
heightBuffer.writeUInt32BE(transaction.__height);
|
heightBuffer.writeUInt32BE(transaction.__height);
|
||||||
|
|
||||||
var timestampBuffer = new Buffer(8);
|
var timestampBuffer = new Buffer(4);
|
||||||
timestampBuffer.writeDoubleBE(transaction.__timestamp);
|
timestampBuffer.writeUInt32BE(transaction.__timestamp);
|
||||||
|
|
||||||
var inputValues = transaction.__inputValues;
|
var inputValues = transaction.__inputValues;
|
||||||
var inputValuesBuffer = new Buffer(8 * inputValues.length);
|
var inputValuesBuffer = new Buffer(8 * inputValues.length);
|
||||||
@ -31,14 +31,14 @@ Encoding.prototype.encodeTransactionValue = function(transaction) {
|
|||||||
inputValuesLengthBuffer.writeUInt16BE(inputValues.length);
|
inputValuesLengthBuffer.writeUInt16BE(inputValues.length);
|
||||||
|
|
||||||
return new Buffer.concat([heightBuffer, timestampBuffer,
|
return new Buffer.concat([heightBuffer, timestampBuffer,
|
||||||
inputValuesLengthBuffer, inputValuesBuffer, transaction.toBuffer()]);
|
inputValuesLengthBuffer, inputValuesBuffer, transaction.toRaw()]);
|
||||||
};
|
};
|
||||||
|
|
||||||
Encoding.prototype.decodeTransactionValue = function(buffer) {
|
Encoding.prototype.decodeTransactionValue = function(buffer) {
|
||||||
var height = buffer.readUInt32BE();
|
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 = [];
|
var inputValues = [];
|
||||||
for(var i = 0; i < inputValuesLength; i++) {
|
for(var i = 0; i < inputValuesLength; i++) {
|
||||||
inputValues.push(buffer.readDoubleBE(i * 8 + 14));
|
inputValues.push(buffer.readDoubleBE(i * 8 + 14));
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user