Add FilterLoad, FilterAdd, and FilterClear Messages + BloomFilter

This commit is contained in:
William Wolf 2015-02-16 01:42:32 -08:00
parent e19734593e
commit abbe63666f
8 changed files with 168 additions and 3 deletions

47
lib/bloomfilter.js Normal file
View File

@ -0,0 +1,47 @@
'use strict';
var bitcore = require('bitcore');
var BloomFilter = require('bloom-filter');
var BufferReader = bitcore.encoding.BufferReader;
var BufferWriter = bitcore.encoding.BufferWriter;
var $ = bitcore.util.preconditions;
BloomFilter.fromBuffer = function fromBuffer(payload) {
var parser = new BufferReader(payload);
var data = parser.readVarLengthBuffer();
$.checkState(data.length <= BloomFilter.MAX_BLOOM_FILTER_SIZE,
'Filter data must be <= MAX_BLOOM_FILTER_SIZE bytes');
var nHashFuncs = parser.readUInt32LE();
$.checkState(nHashFuncs <= BloomFilter.MAX_HASH_FUNCS,
'Filter nHashFuncs must be <= MAX_HASH_FUNCS');
var nTweak = parser.readUInt32LE();
var nFlags = parser.readUInt8();
var vData = [];
var dataParser = new BufferReader(data);
for(var i = 0; i < data.length; i++) {
vData.push(dataParser.readUInt8());
}
return new BloomFilter({
vData: vData,
nHashFuncs: nHashFuncs,
nTweak: nTweak,
nFlags: nFlags
});
}
BloomFilter.prototype.toBuffer = function toBuffer() {
var bw = new BufferWriter();
bw.writeVarintNum(this.vData.length);
for(var i = 0; i < this.vData.length; i++) {
bw.writeUInt8(this.vData[i]);
}
bw.writeUInt32LE(this.nHashFuncs);
bw.writeUInt32LE(this.nTweak);
bw.writeUInt8(this.nFlags);
return bw.concat();
};
module.exports = BloomFilter;

View File

@ -4,5 +4,6 @@
module.exports = {
Messages: require('./messages'),
Peer: require('./peer'),
Pool: require('./pool')
Pool: require('./pool'),
BloomFilter: require('./bloomfilter')
};

View File

@ -7,6 +7,7 @@
var Buffers = require('buffers');
var Put = require('bufferput');
var util = require('util');
var BloomFilter = require('./bloomfilter');
var bitcore = require('bitcore');
var _ = bitcore.deps._;
@ -14,6 +15,7 @@ var _ = bitcore.deps._;
var BlockHeaderModel = bitcore.BlockHeader;
var BlockModel = bitcore.Block;
var BufferReader = bitcore.encoding.BufferReader;
var BufferWriter = bitcore.encoding.BufferWriter;
var BufferUtil = bitcore.util.buffer;
var $ = bitcore.util.preconditions;
var Hash = bitcore.crypto.Hash;
@ -826,6 +828,80 @@ function GetHeaders(starts, stop) {
util.inherits(GetHeaders, GetBlocks);
module.exports.GetHeaders = Message.COMMANDS.getheaders = GetHeaders;
/**
* Request peer to apply a bloom filter to 'inv' messages sent back
*
* @name P2P.Message.filterload
* @param{BloomFilter} filter - a BloomFilter object
*/
function FilterLoad(filter) {
this.command = 'filterload';
$.checkArgument(_.isUndefined() || filter instanceof BloomFilter,
'BloomFilter object or undefined required for FilterLoad');
this.filter = filter;
return this;
}
util.inherits(FilterLoad, Message);
FilterLoad.prototype.fromBuffer = function(payload) {
this.filter = BloomFilter.fromBuffer(payload);
return this;
};
FilterLoad.prototype.getPayload = function() {
if(this.filter) {
return this.filter.toBuffer()
} else {
return BufferUtil.EMPTY_BUFFER;
}
};
module.exports.FilterLoad = Message.COMMANDS.filterload = FilterLoad;
/**
* Request peer to add data to a bloom filter already set by 'filterload'
*
* @name P2P.Message.filteradd
* @param{Buffer} data - Array of bytes representing bloom filter data
*/
function FilterAdd(data) {
this.command = 'filteradd';
this.data = data || new Buffer(0,'hex');
return this;
}
util.inherits(FilterAdd, Message);
FilterAdd.prototype.fromBuffer = function(payload) {
$.checkArgument(payload);
var parser = new BufferReader(payload);
this.data = parser.readVarLengthBuffer();
$.checkState(this.data.length <= BloomFilter.MAX_BLOOM_FILTER_SIZE,
'FilterAdd data must be <= 520 bytes');
this._checkFinished(parser);
return this;
};
FilterAdd.prototype.getPayload = function() {
var bw = new BufferWriter();
bw.writeVarintNum(this.data.length);
bw.write(this.data);
return bw.concat();
};
module.exports.FilterAdd = Message.COMMANDS.filterload = FilterAdd;
/**
* Request peer to apply a bloom filter to 'inv' messages sent back
*
* @name P2P.Message.filterclear
*/
function FilterClear() {
this.command = 'filterclear';
}
util.inherits(FilterClear, Message);
module.exports.FilterClear = Message.COMMANDS.filterclear = FilterClear;
/**
* Request for transactions on the mempool
*

View File

@ -86,7 +86,8 @@ Pool.MaxConnectedPeers = 8;
Pool.RetrySeconds = 30;
Pool.PeerEvents = ['version', 'inv', 'getdata', 'ping', 'pong', 'addr',
'getaddr', 'verack', 'reject', 'alert', 'headers', 'block',
'tx', 'getblocks', 'getheaders', 'error'
'tx', 'getblocks', 'getheaders', 'error', 'filterload', 'filteradd',
'filterclear'
];

View File

@ -53,6 +53,7 @@
},
"dependencies": {
"bitcore": "^0.10.2",
"bloom-filter": "^0.1.1",
"bufferput": "^0.1.2",
"buffers": "^0.1.1",
"socks5-client": "^0.3.6"

17
test/bloomfilter.js Normal file
View File

@ -0,0 +1,17 @@
'use strict';
var chai = require('chai');
var Data = require('./data/messages');
var P2P = require('../');
var BloomFilter = P2P.BloomFilter;
describe('BloomFilter', function() {
it('BloomFilter#fromBuffer and toBuffer methods work', function() {
var testPayload = Data.FILTERLOAD.payload;
var filter = new BloomFilter.fromBuffer(new Buffer(testPayload, 'hex'));
filter.toBuffer().toString('hex').should.equal(testPayload);
});
});

View File

@ -62,5 +62,17 @@
"PONG": {
"message": "f9beb4d9706f6e67000000000000000008000000c6466f1e6b86480ae969867c",
"payload": "6b86480ae969867c"
},
"FILTERLOAD": {
"message": "f9beb4d966696c7465726c6f61640000210000002ef97a71170000000000000000000000000000000000000000000000060000000000000000",
"payload": "170000000000000000000000000000000000000000000000060000000000000000"
},
"FILTERADD": {
"message": "f9beb4d966696c7465726c6f61640000150000009727ea0a1499108ad8ed9bb6274d3980bab5a85c048f0950c8",
"payload": "1499108ad8ed9bb6274d3980bab5a85c048f0950c8"
},
"FILTERCLEAR": {
"message": "f9beb4d966696c7465726c6365617200000000005df6e0e2",
"payload": ""
}
}

View File

@ -8,6 +8,7 @@ var Buffers = require('buffers');
var bitcore = require('bitcore');
var Data = require('./data/messages');
var P2P = require('../');
var BloomFilter = P2P.BloomFilter;
var Messages = P2P.Messages;
var Networks = bitcore.Networks;
var BufferUtils = bitcore.util.buffer;
@ -26,6 +27,9 @@ describe('Messages', function() {
Alert: 'alert',
Reject: 'reject',
Block: 'block',
FilterLoad: 'filterload',
FilterAdd: 'filteradd',
FilterClear: 'filterclear',
GetBlocks: 'getblocks',
GetHeaders: 'getheaders',
GetData: 'getdata',
@ -34,7 +38,7 @@ describe('Messages', function() {
Transaction: 'tx',
NotFound: 'notfound'
};
// TODO: add data for these
// TODO: add data for these
var noPayload = ['Reject', 'GetBlocks', 'GetHeaders'];
var names = Object.keys(commands);
describe('named', function() {
@ -116,4 +120,10 @@ describe('Messages', function() {
});
it('FilterLoad#fromBuffer method works', function() {
var testPayload = Data.FILTERLOAD.payload;
var msg = new Messages.FilterLoad().fromBuffer(new Buffer(testPayload, 'hex'));
msg.getPayload().toString('hex').should.equal(testPayload);
});
});