This commit is contained in:
Chris Kleeschulte 2017-07-19 15:50:47 -04:00
parent 861a80d678
commit de4c59f958
9 changed files with 247 additions and 28 deletions

View File

@ -96,16 +96,16 @@ Encoding.prototype.encodeUtxoIndexValue = function(height, satoshis, timestamp,
heightBuffer.writeUInt32BE(height); heightBuffer.writeUInt32BE(height);
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(4);
timestampBuffer.writeUInt32BE(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(8); var timestamp = buffer.readUInt32BE(12);
var scriptBuffer = buffer.slice(12); var scriptBuffer = buffer.slice(16);
return { return {
height: height, height: height,
satoshis: satoshis, satoshis: satoshis,

View File

@ -6,13 +6,13 @@ function Encoding(servicePrefix) {
} }
// ---- hash --> height // ---- hash --> header
Encoding.prototype.encodeHeaderKey = function(hash) { Encoding.prototype.encodeHeaderKey = function(hash) {
return Buffer.concat([ this._servicePrefix, new Buffer(hash, 'hex') ]); return Buffer.concat([ this._servicePrefix, new Buffer(hash, 'hex') ]);
}; };
Encoding.prototype.decodeHeaderKey = function(buffer) { Encoding.prototype.decodeHeaderKey = function(buffer) {
return buffer.slice(3).toString('hex'); return buffer.slice(2).toString('hex');
}; };
Encoding.prototype.encodeHeaderValue = function(header) { Encoding.prototype.encodeHeaderValue = function(header) {
@ -26,7 +26,9 @@ Encoding.prototype.encodeHeaderValue = function(header) {
bitsBuf.writeUInt32BE(header.bits); bitsBuf.writeUInt32BE(header.bits);
var nonceBuf = new Buffer(4); var nonceBuf = new Buffer(4);
nonceBuf.writeUInt32BE(header.nonce); nonceBuf.writeUInt32BE(header.nonce);
return Buffer.concat([ versionBuf, prevHash, merkleRoot, tsBuf, bitsBuf, nonceBuf ]); var heightBuf = new Buffer(4);
heightBuf.writeUInt32BE(header.height);
return Buffer.concat([ versionBuf, prevHash, merkleRoot, tsBuf, bitsBuf, nonceBuf, heightBuf ]);
}; };
Encoding.prototype.decodeHeaderValue = function(buffer) { Encoding.prototype.decodeHeaderValue = function(buffer) {
@ -36,13 +38,15 @@ Encoding.prototype.decodeHeaderValue = function(buffer) {
var ts = buffer.readUInt32BE(68); var ts = buffer.readUInt32BE(68);
var bits = buffer.readUInt32BE(72); var bits = buffer.readUInt32BE(72);
var nonce = buffer.readUInt32BE(76); var nonce = buffer.readUInt32BE(76);
var height = buffer.readUInt32BE(80);
return { return {
version: version, version: version,
prevHash: prevHash, prevHash: prevHash,
merkleRoot: merkleRoot, merkleRoot: merkleRoot,
timestakmp: ts, timestamp: ts,
bits: bits, bits: bits,
nonce: nonce nonce: nonce,
height: height
}; };
}; };

View File

@ -6,7 +6,6 @@ var Encoding = require('./encoding');
var index = require('../../'); var index = require('../../');
var log = index.log; var log = index.log;
var utils = require('../../utils'); var utils = require('../../utils');
var constants = require('../../constants');
var HeaderService = function(options) { var HeaderService = function(options) {
@ -26,7 +25,7 @@ HeaderService.dependencies = [ 'p2p', 'db' ];
HeaderService.prototype.getAPIMethods = function() { HeaderService.prototype.getAPIMethods = function() {
var methods = [ var methods = [
['getAllHeaders', this, this.getHeaders, 0] ['getAllHeaders', this, this.getAllHeaders, 0]
]; ];
return methods; return methods;
@ -112,7 +111,9 @@ HeaderService.prototype._onHeaders = function(headers) {
HeaderService.prototype._getHeaderOperations = function(headers) { HeaderService.prototype._getHeaderOperations = function(headers) {
var self = this; var self = this;
var runningHeight = this._tip.height;
return headers.map(function(header) { return headers.map(function(header) {
header.height = ++runningHeight;
return { return {
type: 'put', type: 'put',
key: self._encoding.encodeHeaderKey(header.hash), key: self._encoding.encodeHeaderKey(header.hash),
@ -153,18 +154,26 @@ HeaderService.prototype._sync = function() {
log.info('Headers download progress: ' + this._tip.height + '/' + log.info('Headers download progress: ' + this._tip.height + '/' +
this._numNeeded + ' (' + (this._tip.height / this._numNeeded*100).toFixed(2) + '%)'); this._numNeeded + ' (' + (this._tip.height / this._numNeeded*100).toFixed(2) + '%)');
this._p2p.getHeaders({ startHash: this._tip.hash }); this._p2p.getHeaders({ startHash: this._tip.hash });
return; return;
} }
}; };
HeaderService.prototype.getHeaders = function(callback) { HeaderService.prototype.getAllHeaders = function(callback) {
var self = this; var self = this;
var results = []; var results = [];
var start = self._encoding.encodeHeaderKey(0); var start = self._encoding.encodeHeaderKey(0);
var end = self._encoding.encodeHeaderKey(0xffffffff);
var criteria = {
gte: start,
lte: end
};
var stream = self._db.createReadStream(criteria); var stream = self._db.createReadStream(criteria);
var streamErr; var streamErr;
@ -173,10 +182,9 @@ HeaderService.prototype.getHeaders = function(callback) {
}); });
stream.on('data', function(data) { stream.on('data', function(data) {
results.push({ var res = {};
hash: self.__encoding.decodeHeaderKey(data.key), res[self._encoding.decodeHeaderKey(data.key)] = self._encoding.decodeHeaderValue(data.value);
header: self._encoding.decodeHeaderValue(data.value) results.push(res);
});
}); });
stream.on('end', function() { stream.on('end', function() {

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
var tx = require('bcoin').tx; var Tx = require('bcoin').tx;
function Encoding(servicePrefix) { function Encoding(servicePrefix) {
this.servicePrefix = servicePrefix; this.servicePrefix = servicePrefix;
@ -41,9 +41,11 @@ Encoding.prototype.decodeTransactionValue = function(buffer) {
var inputValuesLength = buffer.readUInt16BE(8); 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 + 10));
} }
var transaction = tx.fromRaw(buffer.slice(inputValues.length * 8 + 14));
var txBuf = buffer.slice(inputValues.length * 8 + 10);
var transaction = Tx.fromRaw(txBuf);
transaction.__height = height; transaction.__height = height;
transaction.__inputValues = inputValues; transaction.__inputValues = inputValues;

View File

@ -15,8 +15,8 @@ describe('Address service encoding', function() {
var prefix0 = new Buffer('00', 'hex'); var prefix0 = new Buffer('00', 'hex');
var prefix1 = new Buffer('01', 'hex'); var prefix1 = new Buffer('01', 'hex');
var ts = Math.floor(new Date('2017-02-28').getTime() / 1000); var ts = Math.floor(new Date('2017-02-28').getTime() / 1000);
var tsBuf = new Buffer(8); var tsBuf = new Buffer(4);
tsBuf.writeDoubleBE(ts); tsBuf.writeUInt32BE(ts);
addressSizeBuf.writeUInt8(address.length); addressSizeBuf.writeUInt8(address.length);
var addressIndexKeyBuf = Buffer.concat([ var addressIndexKeyBuf = Buffer.concat([
servicePrefix, servicePrefix,

View File

@ -0,0 +1,70 @@
'use strict';
var should = require('chai').should();
var Encoding = require('../../../lib/services/header/encoding');
describe('Header service encoding', function() {
var servicePrefix = new Buffer('0000', 'hex');
var encoding = new Encoding(servicePrefix);
var hash = '91b58f19b6eecba94ed0f6e463e8e334ec0bcda7880e2985c82a8f32e4d03add';
var header = {
prevHash: '91b58f19b6eecba94ed0f6e463e8e334ec0bcda7880e2985c82a8f32e4d03ade',
version: 0x2000012,
merkleRoot: '91b58f19b6eecba94ed0f6e463e8e334ec0bcda7880e2985c82a8f32e4d03adf',
timestamp: 1E9,
bits: 400000,
nonce: 123456,
height: 123
};
var versionBuf = new Buffer(4);
var prevHash = new Buffer(header.prevHash, 'hex');
var merkleRoot = new Buffer(header.merkleRoot, 'hex');
var tsBuf = new Buffer(4);
var bitsBuf = new Buffer(4);
var nonceBuf = new Buffer(4);
var heightBuf = new Buffer(4);
it('should encode header key' , function() {
var hashBuf = new Buffer(hash, 'hex');
encoding.encodeHeaderKey(hash).should.deep.equal(Buffer.concat([servicePrefix, hashBuf]));
});
it('should decode header key', function() {
var hashBuf = new Buffer(hash, 'hex');
encoding.decodeHeaderKey(Buffer.concat([servicePrefix, hashBuf]))
.should.equal(hash);
});
it('should encode header value', function() {
versionBuf.writeInt32BE(header.version); // signed
tsBuf.writeUInt32BE(header.timestamp);
bitsBuf.writeUInt32BE(header.bits);
nonceBuf.writeUInt32BE(header.nonce);
heightBuf.writeUInt32BE(header.height);
encoding.encodeHeaderValue(header).should.deep.equal(Buffer.concat([
versionBuf,
prevHash,
merkleRoot,
tsBuf,
bitsBuf,
nonceBuf,
heightBuf
]));
});
it('should decode header value', function() {
encoding.decodeHeaderValue(Buffer.concat([
versionBuf,
prevHash,
merkleRoot,
tsBuf,
bitsBuf,
nonceBuf,
heightBuf
])).should.deep.equal(header);
});
});

View File

@ -0,0 +1,137 @@
'use strict';
var sinon = require('sinon');
var HeaderService = require('../../../lib/services/header');
var Tx = require('bcoin').tx;
var expect = require('chai').expect;
var Encoding = require('../../../lib/services/header/encoding');
var utils = require('../../../lib/utils');
var EventEmitter = require('events').EventEmitter;
describe('Header Service', function() {
var headerService;
var sandbox;
beforeEach(function() {
sandbox = sinon.sandbox.create();
headerService = new HeaderService({
node: {
getNetworkName: function() { return 'regtest'; },
services: []
}
});
headerService._encoding = new Encoding(new Buffer('0000', 'hex'));
});
afterEach(function() {
sandbox.restore();
});
describe('#start', function() {
it('should get prefix for database', function(done) {
var getServiceTip = sandbox.stub().callsArgWith(1, null, { height: 123, hash: 'a' });
var startSubs = sandbox.stub(headerService, '_startSubscriptions');
var setListeners = sandbox.stub(headerService, '_setListeners');
var getPrefix = sandbox.stub().callsArgWith(1, null, new Buffer('ffee', 'hex'));
headerService._db = { getPrefix: getPrefix, getServiceTip: getServiceTip };
headerService.start(function() {
expect(startSubs.calledOnce).to.be.true;
expect(setListeners.calledOnce).to.be.true;
expect(headerService._tip).to.be.deep.equal({ height: 123, hash: 'a' });
expect(headerService._encoding).to.be.instanceOf(Encoding);
done();
});
});
});
describe('#stop', function() {
it('should stop the service', function(done) {
headerService.stop(function() {
done();
});
});
});
describe('#getAllHeaders', function() {
it('should get all the headers', function(done) {
var stream = new EventEmitter();
var createReadStream = sandbox.stub().returns(stream);
var hash = sandbox.stub().returns('a');
var header = sandbox.stub().returns({});
var hashKey = sandbox.stub();
var headerVal = sandbox.stub();
headerService._db = { createReadStream: createReadStream };
headerService._encoding = { decodeHeaderKey: hash, decodeHeaderValue: header, encodeHeaderKey: hashKey, encodeHeaderValue: headerVal };
headerService.getAllHeaders(function(err, headers) {
if (err) {
return callback(err);
}
expect(headers).to.be.deep.equal([ { a: {} } ]);
done();
});
stream.emit('data', { key: 'a', value: 'a' });
stream.emit('end');
});
});
describe('#_startSync', function() {
it('should start the sync process', function() {
headerService._bestHeight = 123;
headerService._tip = { height: 121, hash: 'a' };
var sync = sandbox.stub(headerService, '_sync');
headerService._startSync();
expect(sync.calledOnce).to.be.true;
expect(headerService._numNeeded).to.equal(2);
});
});
describe('#_sync', function() {
it('should sync header', function() {
headerService._p2pHeaderCallsNeeded = 10;
headerService._numNeeded = 1000;
headerService._tip = { height: 121, hash: 'a' };
var getHeaders = sandbox.stub();
headerService._p2p = { getHeaders: getHeaders };
headerService._sync();
expect(getHeaders.calledOnce).to.be.true;
expect(headerService._p2pHeaderCallsNeeded).to.equal(9);
});
});
describe('#_onHeaders', function() {
it('should handle new headers received', function() {
var headers = [ { hash: 'b' } ];
headerService._tip = { height: 123, hash: 'a' };
headerService._bestHeight = 123;
var getHeaderOps = sandbox.stub(headerService, '_getHeaderOperations').returns([]);
var encodeTip = sandbox.stub().returns({ key: 'b', value: 'b' });
var batch = sandbox.stub();
var sync = sandbox.stub(headerService, '_sync');
utils.encodeTip = encodeTip;
headerService._db = { batch: batch };
headerService._onHeaders(headers);
expect(getHeaderOps.calledOnce).to.be.true;
expect(encodeTip.calledOnce).to.be.true;
expect(batch.calledOnce).to.be.true;
expect(sync.calledOnce).to.be.false;
});
});
});

View File

@ -11,8 +11,8 @@ describe('Timestamp service encoding', function() {
var encoding = new Encoding(servicePrefix); var encoding = new Encoding(servicePrefix);
var blockhash = '00000000000000000115b92b1ff4377441049bff75c6c48b626eb99e8b744297'; var blockhash = '00000000000000000115b92b1ff4377441049bff75c6c48b626eb99e8b744297';
var timestamp = 5; var timestamp = 5;
var timestampBuf = new Buffer(8); var timestampBuf = new Buffer(4);
timestampBuf.writeDoubleBE(timestamp); timestampBuf.writeUInt32BE(timestamp);
it('should encode block timestamp key' , function() { it('should encode block timestamp key' , function() {
encoding.encodeBlockTimestampKey(blockhash).should.deep.equal(Buffer.concat([servicePrefix, blockPrefix, new Buffer(blockhash, 'hex')])); encoding.encodeBlockTimestampKey(blockhash).should.deep.equal(Buffer.concat([servicePrefix, blockPrefix, new Buffer(blockhash, 'hex')]));
@ -47,5 +47,3 @@ describe('Timestamp service encoding', function() {
encoding.decodeTimestampBlockValue(new Buffer(blockhash, 'hex')).should.equal(blockhash); encoding.decodeTimestampBlockValue(new Buffer(blockhash, 'hex')).should.equal(blockhash);
}); });
}); });

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
var should = require('chai').should(); var should = require('chai').should();
var bitcore = require('bitcore-lib'); var Tx = require('bcoin').tx;
var Encoding = require('../../../lib/services/transaction/encoding'); var Encoding = require('../../../lib/services/transaction/encoding');
@ -11,8 +11,8 @@ describe('Transaction service encoding', function() {
var encoding = new Encoding(servicePrefix); var encoding = new Encoding(servicePrefix);
var txid = '91b58f19b6eecba94ed0f6e463e8e334ec0bcda7880e2985c82a8f32e4d03add'; var txid = '91b58f19b6eecba94ed0f6e463e8e334ec0bcda7880e2985c82a8f32e4d03add';
var txHex = '0100000001cc3ffe0638792c8b39328bb490caaefe2cf418f2ce0144956e0c22515f29724d010000006a473044022030ce9fa68d1a32abf0cd4adecf90fb998375b64fe887c6987278452b068ae74c022036a7d00d1c8af19e298e04f14294c807ebda51a20389ad751b4ff3c032cf8990012103acfcb348abb526526a9f63214639d79183871311c05b2eebc727adfdd016514fffffffff02f6ae7d04000000001976a9144455183e407ee4d3423858c8a3275918aedcd18e88aca99b9b08010000001976a9140beceae2c29bfde08d2b6d80b33067451c5887be88ac00000000'; var txHex = '0100000001cc3ffe0638792c8b39328bb490caaefe2cf418f2ce0144956e0c22515f29724d010000006a473044022030ce9fa68d1a32abf0cd4adecf90fb998375b64fe887c6987278452b068ae74c022036a7d00d1c8af19e298e04f14294c807ebda51a20389ad751b4ff3c032cf8990012103acfcb348abb526526a9f63214639d79183871311c05b2eebc727adfdd016514fffffffff02f6ae7d04000000001976a9144455183e407ee4d3423858c8a3275918aedcd18e88aca99b9b08010000001976a9140beceae2c29bfde08d2b6d80b33067451c5887be88ac00000000';
var tx = new bitcore.Transaction(txHex); var tx = Tx.fromRaw(txHex, 'hex');
var txEncoded = Buffer.concat([new Buffer('00000002', 'hex'), new Buffer('3ff0000000000000', 'hex'), new Buffer('0002', 'hex'), new Buffer('40000000000000004008000000000000', 'hex'), tx.toBuffer()]); var txEncoded = Buffer.concat([new Buffer('00000002', 'hex'), new Buffer('00000001', 'hex'), new Buffer('0002', 'hex'), new Buffer('40000000000000004008000000000000', 'hex'), tx.toRaw()]);
it('should encode transaction key' , function() { it('should encode transaction key' , function() {
var txBuf = new Buffer(txid, 'hex'); var txBuf = new Buffer(txid, 'hex');