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);
var satoshisBuffer = new Buffer(8);
satoshisBuffer.writeDoubleBE(satoshis);
var timestampBuffer = new Buffer(8);
timestampBuffer.writeUInt32BE(timestamp || 0)
var timestampBuffer = new Buffer(4);
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(8);
var scriptBuffer = buffer.slice(12);
var timestamp = buffer.readUInt32BE(12);
var scriptBuffer = buffer.slice(16);
return {
height: height,
satoshis: satoshis,

View File

@ -6,13 +6,13 @@ function Encoding(servicePrefix) {
}
// ---- hash --> height
// ---- hash --> header
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');
return buffer.slice(2).toString('hex');
};
Encoding.prototype.encodeHeaderValue = function(header) {
@ -26,7 +26,9 @@ Encoding.prototype.encodeHeaderValue = function(header) {
bitsBuf.writeUInt32BE(header.bits);
var nonceBuf = new Buffer(4);
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) {
@ -36,13 +38,15 @@ Encoding.prototype.decodeHeaderValue = function(buffer) {
var ts = buffer.readUInt32BE(68);
var bits = buffer.readUInt32BE(72);
var nonce = buffer.readUInt32BE(76);
var height = buffer.readUInt32BE(80);
return {
version: version,
prevHash: prevHash,
merkleRoot: merkleRoot,
timestakmp: ts,
timestamp: ts,
bits: bits,
nonce: nonce
nonce: nonce,
height: height
};
};

View File

@ -6,7 +6,6 @@ var Encoding = require('./encoding');
var index = require('../../');
var log = index.log;
var utils = require('../../utils');
var constants = require('../../constants');
var HeaderService = function(options) {
@ -26,7 +25,7 @@ HeaderService.dependencies = [ 'p2p', 'db' ];
HeaderService.prototype.getAPIMethods = function() {
var methods = [
['getAllHeaders', this, this.getHeaders, 0]
['getAllHeaders', this, this.getAllHeaders, 0]
];
return methods;
@ -112,7 +111,9 @@ HeaderService.prototype._onHeaders = function(headers) {
HeaderService.prototype._getHeaderOperations = function(headers) {
var self = this;
var runningHeight = this._tip.height;
return headers.map(function(header) {
header.height = ++runningHeight;
return {
type: 'put',
key: self._encoding.encodeHeaderKey(header.hash),
@ -153,18 +154,26 @@ HeaderService.prototype._sync = function() {
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) {
HeaderService.prototype.getAllHeaders = function(callback) {
var self = this;
var results = [];
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 streamErr;
@ -173,10 +182,9 @@ HeaderService.prototype.getHeaders = function(callback) {
});
stream.on('data', function(data) {
results.push({
hash: self.__encoding.decodeHeaderKey(data.key),
header: self._encoding.decodeHeaderValue(data.value)
});
var res = {};
res[self._encoding.decodeHeaderKey(data.key)] = self._encoding.decodeHeaderValue(data.value);
results.push(res);
});
stream.on('end', function() {

View File

@ -1,6 +1,6 @@
'use strict';
var tx = require('bcoin').tx;
var Tx = require('bcoin').tx;
function Encoding(servicePrefix) {
this.servicePrefix = servicePrefix;
@ -41,9 +41,11 @@ Encoding.prototype.decodeTransactionValue = function(buffer) {
var inputValuesLength = buffer.readUInt16BE(8);
var inputValues = [];
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.__inputValues = inputValues;

View File

@ -15,8 +15,8 @@ describe('Address service encoding', function() {
var prefix0 = new Buffer('00', 'hex');
var prefix1 = new Buffer('01', 'hex');
var ts = Math.floor(new Date('2017-02-28').getTime() / 1000);
var tsBuf = new Buffer(8);
tsBuf.writeDoubleBE(ts);
var tsBuf = new Buffer(4);
tsBuf.writeUInt32BE(ts);
addressSizeBuf.writeUInt8(address.length);
var addressIndexKeyBuf = Buffer.concat([
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 blockhash = '00000000000000000115b92b1ff4377441049bff75c6c48b626eb99e8b744297';
var timestamp = 5;
var timestampBuf = new Buffer(8);
timestampBuf.writeDoubleBE(timestamp);
var timestampBuf = new Buffer(4);
timestampBuf.writeUInt32BE(timestamp);
it('should encode block timestamp key' , function() {
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);
});
});

View File

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