wallet/net: ensure low FPR of bloom filters. see #118.
This commit is contained in:
parent
f02f5ee5d2
commit
4cba97a0a8
@ -126,6 +126,7 @@ function Environment() {
|
|||||||
// Utils
|
// Utils
|
||||||
this.require('utils', './utils');
|
this.require('utils', './utils');
|
||||||
this.require('base58', './utils/base58');
|
this.require('base58', './utils/base58');
|
||||||
|
this.require('bloom', './utils/bloom');
|
||||||
this.require('co', './utils/co');
|
this.require('co', './utils/co');
|
||||||
this.require('encoding', './utils/encoding');
|
this.require('encoding', './utils/encoding');
|
||||||
this.require('lock', './utils/lock');
|
this.require('lock', './utils/lock');
|
||||||
|
|||||||
@ -122,7 +122,7 @@ function Pool(options) {
|
|||||||
this.hosts.address = this.address;
|
this.hosts.address = this.address;
|
||||||
|
|
||||||
if (this.options.spv)
|
if (this.options.spv)
|
||||||
this.spvFilter = Bloom.fromRate(10000, 0.001, Bloom.flags.ALL);
|
this.spvFilter = Bloom.fromRate(20000, 0.001, Bloom.flags.ALL);
|
||||||
|
|
||||||
if (!this.options.mempool)
|
if (!this.options.mempool)
|
||||||
this.txFilter = new Bloom.Rolling(50000, 0.000001);
|
this.txFilter = new Bloom.Rolling(50000, 0.000001);
|
||||||
|
|||||||
@ -163,9 +163,14 @@ MerkleBlock.prototype.verifyPartial = function verifyPartial() {
|
|||||||
if (this._validPartial != null)
|
if (this._validPartial != null)
|
||||||
return this._validPartial;
|
return this._validPartial;
|
||||||
|
|
||||||
tree = this.extractTree();
|
try {
|
||||||
|
tree = this.extractTree();
|
||||||
|
} catch (e) {
|
||||||
|
this._validPartial = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!tree || tree.root !== this.merkleRoot) {
|
if (tree.root !== this.merkleRoot) {
|
||||||
this._validPartial = false;
|
this._validPartial = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -246,16 +251,16 @@ MerkleBlock.prototype.extractTree = function extractTree() {
|
|||||||
hashes.push(this.hashes[p]);
|
hashes.push(this.hashes[p]);
|
||||||
|
|
||||||
if (totalTX === 0)
|
if (totalTX === 0)
|
||||||
return;
|
throw new Error('Zero transactions.');
|
||||||
|
|
||||||
if (totalTX > consensus.MAX_BLOCK_SIZE / 60)
|
if (totalTX > consensus.MAX_BLOCK_SIZE / 60)
|
||||||
return;
|
throw new Error('Too many transactions.');
|
||||||
|
|
||||||
if (hashes.length > totalTX)
|
if (hashes.length > totalTX)
|
||||||
return;
|
throw new Error('Too many hashes.');
|
||||||
|
|
||||||
if (flags.length * 8 < hashes.length)
|
if (flags.length * 8 < hashes.length)
|
||||||
return;
|
throw new Error('Flags too small.');
|
||||||
|
|
||||||
while (width(height) > 1)
|
while (width(height) > 1)
|
||||||
height++;
|
height++;
|
||||||
@ -266,13 +271,13 @@ MerkleBlock.prototype.extractTree = function extractTree() {
|
|||||||
root = traverse(height, 0);
|
root = traverse(height, 0);
|
||||||
|
|
||||||
if (failed)
|
if (failed)
|
||||||
return;
|
throw new Error('Mutated merkle tree.');
|
||||||
|
|
||||||
if (((bitsUsed + 7) / 8 | 0) !== flags.length)
|
if (((bitsUsed + 7) / 8 | 0) !== flags.length)
|
||||||
return;
|
throw new Error('Too many flag bits.');
|
||||||
|
|
||||||
if (hashUsed !== hashes.length)
|
if (hashUsed !== hashes.length)
|
||||||
return;
|
throw new Error('Incorrect number of hashes.');
|
||||||
|
|
||||||
return new PartialTree(root, matches, indexes, map);
|
return new PartialTree(root, matches, indexes, map);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -274,13 +274,17 @@ Bloom.fromRate = function fromRate(items, rate, update) {
|
|||||||
size = (-1 / LN2SQUARED * items * Math.log(rate)) | 0;
|
size = (-1 / LN2SQUARED * items * Math.log(rate)) | 0;
|
||||||
size = Math.max(8, size);
|
size = Math.max(8, size);
|
||||||
|
|
||||||
if (update !== -1)
|
if (update !== -1) {
|
||||||
size = Math.min(size, Bloom.MAX_BLOOM_FILTER_SIZE * 8);
|
assert(size <= Bloom.MAX_BLOOM_FILTER_SIZE * 8,
|
||||||
|
'Bloom filter size violates policy limits!');
|
||||||
|
}
|
||||||
|
|
||||||
n = Math.max(1, (size / items * LN2) | 0);
|
n = Math.max(1, (size / items * LN2) | 0);
|
||||||
|
|
||||||
if (update !== -1)
|
if (update !== -1) {
|
||||||
n = Math.min(n, Bloom.MAX_HASH_FUNCS);
|
assert(n <= Bloom.MAX_HASH_FUNCS,
|
||||||
|
'Bloom filter size violates policy limits!');
|
||||||
|
}
|
||||||
|
|
||||||
return new Bloom(size, n, -1, update);
|
return new Bloom(size, n, -1, update);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -76,7 +76,9 @@ function WalletDB(options) {
|
|||||||
this.widCache = new LRU(10000);
|
this.widCache = new LRU(10000);
|
||||||
this.pathMapCache = new LRU(100000);
|
this.pathMapCache = new LRU(100000);
|
||||||
|
|
||||||
this.filter = Bloom.fromRate(1000000, 0.001, this.options.spv ? 1 : -1);
|
this.filter = new Bloom();
|
||||||
|
|
||||||
|
this._init();
|
||||||
}
|
}
|
||||||
|
|
||||||
util.inherits(WalletDB, AsyncObject);
|
util.inherits(WalletDB, AsyncObject);
|
||||||
@ -88,6 +90,28 @@ util.inherits(WalletDB, AsyncObject);
|
|||||||
|
|
||||||
WalletDB.layout = layout;
|
WalletDB.layout = layout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize walletdb.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
WalletDB.prototype._init = function _init() {
|
||||||
|
var items = 1000000;
|
||||||
|
var flag = -1;
|
||||||
|
|
||||||
|
// Highest number of items with an
|
||||||
|
// FPR of 0.001. We have to do this
|
||||||
|
// by hand because Bloom.fromRate's
|
||||||
|
// policy limit enforcing is fairly
|
||||||
|
// naive.
|
||||||
|
if (this.options.spv) {
|
||||||
|
items = 20000;
|
||||||
|
flag = Bloom.flags.ALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.filter = Bloom.fromRate(items, 0.001, flag);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open the walletdb, wait for the database to load.
|
* Open the walletdb, wait for the database to load.
|
||||||
* @alias WalletDB#open
|
* @alias WalletDB#open
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user