Skip to content

Commit bf95b5a

Browse files
committed
feat: save filters and added wallet test
1 parent 37b61f1 commit bf95b5a

File tree

8 files changed

+177
-61
lines changed

8 files changed

+177
-61
lines changed

bin/neutrino

-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
console.log('Starting bcoin');
66
process.title = 'bcoin';
77
const Neutrino = require('../lib/node/neutrino');
8-
const Outpoint = require('../lib/primitives/outpoint');
9-
const assert = require('assert');
108

119
// Doubt in db
1210
const node = new Neutrino({

lib/blockchain/layout.js

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ const layout = {
3535
R: bdb.key('R'),
3636
D: bdb.key('D'),
3737
N: bdb.key('N'),
38+
F: bdb.key('H', ['hash256']),
3839
e: bdb.key('e', ['hash256']),
3940
h: bdb.key('h', ['hash256']),
4041
H: bdb.key('H', ['uint32']),

lib/indexer/filterindexer.js

+43
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,49 @@ class FilterIndexer extends Indexer {
8585
this.put(layout.f.encode(hash), gcsFilter.hash());
8686
}
8787

88+
/**
89+
* save filter header
90+
* @param {Hash} blockHash
91+
* @param {Hash} filterHeader
92+
* @param {Hash} filterHash
93+
* @returns {Promise}
94+
*/
95+
96+
async saveFilterHeader(blockHash, filterHeader, filterHash) {
97+
assert(blockHash);
98+
assert(filterHeader);
99+
assert(filterHash);
100+
101+
const filter = new Filter();
102+
filter.header = filterHeader;
103+
104+
await this.blocks.writeFilter(blockHash, filter.toRaw(), this.filterType);
105+
// console.log(layout.f.encode(blockHash));
106+
this.put(layout.f.encode(blockHash), filterHash);
107+
}
108+
109+
/**
110+
* Save filter
111+
* @param {Hash} blockHash
112+
* @param {BasicFilter} basicFilter
113+
* @param {Hash} filterHeader
114+
* @returns {Promise}
115+
*/
116+
117+
async saveFilter(blockHash, basicFilter, filterHeader) {
118+
assert(blockHash);
119+
assert(basicFilter);
120+
assert(filterHeader);
121+
122+
const filter = new Filter();
123+
filter.filter = basicFilter.toRaw();
124+
filter.header = filterHeader;
125+
126+
await this.blocks.writeFilter(blockHash, filter.toRaw(), this.filterType);
127+
// console.log(layout.f.encode(blockHash));
128+
this.put(layout.f.encode(blockHash), basicFilter.hash());
129+
}
130+
88131
/**
89132
* Prune compact filters.
90133
* @private

lib/indexer/indexer.js

+5
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,11 @@ class Indexer extends EventEmitter {
292292
*/
293293

294294
async _syncBlock(meta, block, view) {
295+
if (this.chain.options.neutrino) {
296+
if (!this.batch)
297+
this.start();
298+
return true;
299+
}
295300
// In the case that the next block is being
296301
// connected or the current block disconnected
297302
// use the block and view being passed directly,

lib/net/pool.js

+43-21
Original file line numberDiff line numberDiff line change
@@ -769,7 +769,7 @@ class Pool extends EventEmitter {
769769
this.filterSyncing = true;
770770
const cFilterHeight = await this.chain.getCFilterHeight();
771771
const startHeight = cFilterHeight
772-
? cFilterHeight : 0;
772+
? cFilterHeight : 1;
773773
const chainHeight = await this.chain.height;
774774
const stopHeight = chainHeight > 1000 ? 1000 : chainHeight;
775775
const stopHash = await this.chain.getHash(stopHeight);
@@ -931,6 +931,8 @@ class Pool extends EventEmitter {
931931
peer.blockTime = Date.now();
932932
if (this.options.neutrino) {
933933
peer.sendGetHeaders(locator);
934+
if (!this.syncing)
935+
this.startFilterHeadersSync();
934936
return true;
935937
}
936938
if (this.checkpoints) {
@@ -1759,7 +1761,11 @@ class Pool extends EventEmitter {
17591761
return;
17601762

17611763
if (this.neutrino) {
1762-
this.startSync();
1764+
const locator = await this.chain.getLocator();
1765+
this.sendLocator(locator, peer);
1766+
if (!this.syncing)
1767+
this.startFilterHeadersSync();
1768+
return;
17631769
}
17641770

17651771
// Request headers instead.
@@ -2172,30 +2178,35 @@ class Pool extends EventEmitter {
21722178
}
21732179

21742180
const filterType = packet.filterType;
2175-
assert(filterType === this.getcfheadersFilterType);
2181+
2182+
if (filterType !== this.getcfheadersFilterType) {
2183+
peer.ban();
2184+
peer.destroy();
2185+
return;
2186+
}
2187+
21762188
const stopHash = packet.stopHash;
21772189
assert(stopHash.equals(this.getcfheadersStopHash));
21782190
let previousFilterHeader = packet.previousFilterHeader;
21792191
const filterHashes = packet.filterHashes;
2180-
assert(filterHashes.length <= 2000);
21812192
let blockHeight = await this.chain.getHeight(stopHash)
2182-
- filterHashes.length;
2193+
- filterHashes.length + 1;
21832194
const stopHeight = await this.chain.getHeight(stopHash);
21842195
for (const filterHash of filterHashes) {
2185-
assert(blockHeight < stopHeight);
2196+
assert(blockHeight <= stopHeight);
21862197
const basicFilter = new BasicFilter();
21872198
basicFilter._hash = filterHash;
21882199
const filterHeader = basicFilter.header(previousFilterHeader);
21892200
const lastFilterHeader = this.cfHeaderChain.tail;
21902201
const cfHeaderEntry = new CFHeaderEntry(
21912202
filterHash, lastFilterHeader.height + 1);
21922203
this.cfHeaderChain.push(cfHeaderEntry);
2193-
// todo: verify the filterHeader
2194-
// todo: save the filterHeader
2204+
const blockHash = await this.chain.getHash(blockHeight);
2205+
const indexer = this.getFilterIndexer(filtersByVal[filterType]);
2206+
await indexer.saveFilterHeader(blockHash, filterHeader, filterHash);
21952207
previousFilterHeader = filterHeader;
2196-
// todo: add a function for this in chain.js
2197-
blockHeight++;
21982208
await this.chain.saveCFHeaderHeight(blockHeight);
2209+
blockHeight++;
21992210
}
22002211
if (this.headerChain.tail.height <= stopHeight)
22012212
this.emit('cfheaders');
@@ -2220,20 +2231,31 @@ class Pool extends EventEmitter {
22202231
const filterType = packet.filterType;
22212232
const filter = packet.filterBytes;
22222233

2223-
// todo: verify the filter
2224-
assert(filterType === this.getcfheadersFilterType);
2234+
if (filterType !== this.getcfheadersFilterType) {
2235+
peer.ban();
2236+
peer.destroy();
2237+
return;
2238+
}
2239+
22252240
const blockHeight = await this.chain.getHeight(blockHash);
22262241
const stopHeight = await this.chain.getHeight(this.getcfiltersStopHash);
22272242

22282243
assert(blockHeight >= this.getcfiltersStartHeight
22292244
&& blockHeight <= stopHeight);
22302245

2231-
await this.chain.saveCFilterHeight(blockHeight);
2232-
const cFilterHeight = await this.chain.getCFilterHeight();
2233-
// todo: save the filter
2234-
const basicFilter = new BasicFilter();
2235-
const gcsFilter = basicFilter.fromNBytes(filter);
2236-
this.emit('cfilter', blockHash, gcsFilter);
2246+
const cFilterHeight = await this.chain.getCFilterHeight();
2247+
2248+
const indexer = this.getFilterIndexer(filtersByVal[filterType]);
2249+
2250+
const filterHeader = await indexer.getFilterHeader(blockHash);
2251+
2252+
const basicFilter = new BasicFilter();
2253+
const gcsFilter = basicFilter.fromNBytes(filter);
2254+
2255+
await indexer.saveFilter(blockHash, gcsFilter, filterHeader);
2256+
2257+
this.emit('cfilter', blockHash, gcsFilter);
2258+
await this.chain.saveCFilterHeight(blockHeight);
22372259
const startHeight = stopHeight + 1;
22382260
let nextStopHeight;
22392261
if (cFilterHeight === stopHeight
@@ -2436,7 +2458,7 @@ class Pool extends EventEmitter {
24362458
this.logger.warning(
24372459
'Peer sent a bad header chain (%s).',
24382460
peer.hostname());
2439-
peer.destroy();
2461+
peer.increaseBan(10);
24402462
return;
24412463
}
24422464

@@ -2459,7 +2481,7 @@ class Pool extends EventEmitter {
24592481

24602482
this.headerChain.push(node);
24612483
if (this.options.neutrino)
2462-
await this._addBlock(peer, header, chainCommon.flags.VERIFY_NONE);
2484+
await this._addBlock(peer, header, chainCommon.flags.VERIFY_POW);
24632485
}
24642486

24652487
this.logger.debug(
@@ -2472,7 +2494,7 @@ class Pool extends EventEmitter {
24722494
peer.blockTime = Date.now();
24732495

24742496
// Request the blocks we just added.
2475-
if (checkpoint) {
2497+
if (checkpoint && !this.options.neutrino) {
24762498
this.headerChain.shift();
24772499
this.resolveHeaders(peer);
24782500
return;

lib/node/neutrino.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ class Neutrino extends Node {
9191
chain: this.chain,
9292
prefix: this.config.prefix,
9393
checkpoints: true,
94+
filterIndexers: this.filterIndexers,
9495
proxy: this.config.str('proxy'),
9596
onion: this.config.bool('onion'),
9697
upnp: this.config.bool('upnp'),
@@ -168,12 +169,13 @@ class Neutrino extends Node {
168169
if (this.chain.height === 0)
169170
return;
170171
this.logger.info('Block Headers are fully synced');
171-
// this.pool.startFilterCheckPtSync(); // TODO: Maybe implement this later
172172
await this.pool.startFilterHeadersSync();
173173
});
174174

175175
this.pool.on('cfheaders', async () => {
176-
this.logger.info('CF Headers Synced');
176+
if (this.chain.height === 0)
177+
return;
178+
this.logger.info('Filter Headers are fully synced');
177179
await this.pool.startFilterSync();
178180
});
179181

@@ -200,6 +202,10 @@ class Neutrino extends Node {
200202
await this.http.open();
201203
await this.handleOpen();
202204

205+
for (const filterindex of this.filterIndexers.values()) {
206+
await filterindex.open();
207+
}
208+
203209
this.logger.info('Node is loaded.');
204210
}
205211

@@ -220,6 +226,10 @@ class Neutrino extends Node {
220226
await this.pool.close();
221227
await this.chain.close();
222228
await this.handleClose();
229+
230+
for (const filterindex of this.filterIndexers.values()) {
231+
await filterindex.close();
232+
}
223233
}
224234

225235
/**

lib/wallet/walletdb.js

+13-21
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const Outpoint = require('../primitives/outpoint');
2626
const layouts = require('./layout');
2727
const records = require('./records');
2828
const NullClient = require('./nullclient');
29+
const Script = require('../script/script');
2930
const layout = layouts.wdb;
3031
const tlayout = layouts.txdb;
3132

@@ -65,6 +66,7 @@ class WalletDB extends EventEmitter {
6566
this.state = new ChainState();
6667
this.confirming = false;
6768
this.height = 0;
69+
this.filterHeight = 0;
6870
this.wallets = new Map();
6971
this.depth = 0;
7072
this.rescanning = false;
@@ -577,7 +579,9 @@ class WalletDB extends EventEmitter {
577579
}
578580

579581
async checkFilter (blockHash, filter) {
580-
const gcsKey = blockHash.slice(0, 16);
582+
// script pub keys
583+
this.filterHeight = this.filterHeight + 1;
584+
const gcsKey = blockHash.reverse().slice(0, 16);
581585

582586
const piter = this.db.iterator({
583587
gte: layout.p.min(),
@@ -586,26 +590,14 @@ class WalletDB extends EventEmitter {
586590

587591
await piter.each(async (key) => {
588592
const [data] = layout.p.decode(key);
589-
const match = filter.match(gcsKey, data);
590-
if (match) {
591-
await this.client.getBlockFromNode(blockHash, filter);
592-
return;
593-
}
594-
});
595-
596-
const oiter = this.db.iterator({
597-
gte: layout.o.min(),
598-
lte: layout.o.max()
599-
});
600-
601-
await oiter.each(async (key) => {
602-
const [hash, index] = layout.o.decode(key);
603-
const outpoint = new Outpoint(hash, index);
604-
const data = outpoint.toRaw();
605-
const match = filter.match(gcsKey, data);
606-
if (match) {
607-
await this.client.getBlockFromNode(blockHash, filter);
608-
return;
593+
// address fromHash toScript
594+
if (data.length === 20) {
595+
const script = Script.fromPubkeyhash(data);
596+
const match = filter.match(gcsKey, script);
597+
if (match) {
598+
await this.client.getBlockFromNode(blockHash, filter);
599+
return;
600+
}
609601
}
610602
});
611603
}

0 commit comments

Comments
 (0)