Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Neutrino: Sync #1168

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion bin/bcoin
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ for arg in "$@"; do
--daemon)
daemon=1
;;
--spv)
--neutrino)
cmd='neutrino'
;;
--spv)
cmd='spvnode'
;;
esac
Expand Down
20 changes: 20 additions & 0 deletions bin/bcoin-cli
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,22 @@ class CLI {
this.log(filter);
}

async getFilterHeader() {
let hash = this.config.str(0, '');

if (hash.length !== 64)
hash = parseInt(hash, 10);

const filterHeader = await this.client.getFilterHeader(hash);

if (!filterHeader) {
this.log('Filter header not found.');
return;
}

this.log(filterHeader);
}

async estimateFee() {
const blocks = this.config.uint(0, 1);

Expand Down Expand Up @@ -246,6 +262,9 @@ class CLI {
case 'filter':
await this.getFilter();
break;
case 'filterheader':
await this.getFilterHeader();
break;
case 'fee':
await this.estimateFee();
break;
Expand All @@ -263,6 +282,7 @@ class CLI {
this.log(' $ coin [hash+index/address]: View coins.');
this.log(' $ fee [target]: Estimate smart fee.');
this.log(' $ filter [hash/height]: View filter.');
this.log(' $ filterheader [hash/height]: View filter header.');
this.log(' $ header [hash/height]: View block header.');
this.log(' $ info: Get server info.');
this.log(' $ mempool: Get mempool snapshot.');
Expand Down
43 changes: 43 additions & 0 deletions bin/neutrino
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env node

'use strict';

console.log('Starting bcoin');
process.title = 'bcoin';
const Neutrino = require('../lib/node/neutrino');

const node = new Neutrino({
file: true,
argv: true,
env: true,
logFile: true,
logConsole: true, // todo: remove
logLevel: 'debug', // todo: remove
db: 'leveldb',
memory: false,
workers: true,
loader: require
});

if (!node.config.bool('no-wallet') && !node.has('walletdb')) {
const plugin = require('../lib/wallet/plugin');
node.use(plugin);
}

(async () => {
await node.ensure();
await node.open();
await node.connect();
node.startSync();
})().catch((err) => {
console.error(err.stack);
process.exit(1);
});

process.on('unhandledRejection', (err, promise) => {
throw err;
});

process.on('SIGINT', async () => {
await node.close();
});
1 change: 1 addition & 0 deletions lib/bcoin-browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
bcoin.Node = require('./node/node');
bcoin.FullNode = require('./node/fullnode');
bcoin.SPVNode = require('./node/spvnode');
bcoin.Neutrino = require('./node/neutrino');

Check warning on line 92 in lib/bcoin-browser.js

View check run for this annotation

Codecov / codecov/patch

lib/bcoin-browser.js#L92

Added line #L92 was not covered by tests
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@masterchief164 Do we need to add neutrino to bcoin-browser? AFAIK, bcoin-browser is obselete.


// Primitives
bcoin.primitives = require('./primitives');
Expand Down
1 change: 1 addition & 0 deletions lib/bcoin.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@
bcoin.define('Node', './node/node');
bcoin.define('FullNode', './node/fullnode');
bcoin.define('SPVNode', './node/spvnode');
bcoin.define('Neutrino', './node/neutrino');

Check warning on line 126 in lib/bcoin.js

View check run for this annotation

Codecov / codecov/patch

lib/bcoin.js#L126

Added line #L126 was not covered by tests

// Primitives
bcoin.define('primitives', './primitives');
Expand Down
31 changes: 25 additions & 6 deletions lib/blockchain/chain.js
Original file line number Diff line number Diff line change
Expand Up @@ -1791,6 +1791,24 @@ class Chain extends AsyncEmitter {
return this.hasEntry(hash);
}

async getCFHeaderHeight() {
return await this.db.getCFHeaderHeight();
}

async saveCFHeaderHeight(height) {
this.db.neutrinoState.headerHeight = height;
await this.db.saveNeutrinoState();
}

async getCFilterHeight() {
return await this.db.getCFilterHeight();
}

async saveCFilterHeight(height) {
this.db.neutrinoState.filterHeight = height;
await this.db.saveNeutrinoState();
}

/**
* Find the corresponding block entry by hash or height.
* @param {Hash|Number} hash/height
Expand Down Expand Up @@ -2003,17 +2021,12 @@ class Chain extends AsyncEmitter {
if (this.synced)
return;

if (this.options.checkpoints) {
if (this.options.checkpoints)
if (this.height < this.network.lastCheckpoint)
return;
}

if (this.tip.time < util.now() - this.network.block.maxTipAge)
return;
manavdesai27 marked this conversation as resolved.
Show resolved Hide resolved

if (!this.hasChainwork())
return;

this.synced = true;
this.emit('full');
}
Expand Down Expand Up @@ -2616,6 +2629,7 @@ class ChainOptions {
this.compression = true;

this.spv = false;
this.neutrino = false;
this.bip91 = false;
this.bip148 = false;
this.prune = false;
Expand Down Expand Up @@ -2662,6 +2676,11 @@ class ChainOptions {
this.spv = options.spv;
}

if (options.neutrino != null) {
assert(typeof options.neutrino === 'boolean');
this.neutrino = options.neutrino;
}

if (options.prefix != null) {
assert(typeof options.prefix === 'string');
this.prefix = options.prefix;
Expand Down
63 changes: 62 additions & 1 deletion lib/blockchain/chaindb.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class ChainDB {
this.state = new ChainState();
this.pending = null;
this.current = null;
this.neutrinoState = null;
manavdesai27 marked this conversation as resolved.
Show resolved Hide resolved

this.cacheHash = new LRU(this.options.entryCache, null, BufferMap);
this.cacheHeight = new LRU(this.options.entryCache);
Expand Down Expand Up @@ -90,6 +91,11 @@ class ChainDB {
this.logger.info('ChainDB successfully initialized.');
}

if (this.options.neutrino) {
if (!this.neutrinoState)
this.neutrinoState = await this.getNeutrinoState();
}

this.logger.info(
'Chain State: hash=%h tx=%d coin=%d value=%s.',
this.state.tip,
Expand Down Expand Up @@ -1001,7 +1007,7 @@ class ChainDB {
*/

async getRawBlock(block) {
if (this.options.spv)
if (this.options.spv && !this.options.neutrino)
manavdesai27 marked this conversation as resolved.
Show resolved Hide resolved
return null;

const hash = await this.getHash(block);
Expand Down Expand Up @@ -1670,6 +1676,39 @@ class ChainDB {
b.put(layout.O.encode(), flags.toRaw());
return b.write();
}

/**
* Get Neutrino State
* @returns {Promise<NeutrinoState>} - Returns neutrino state
*/

async getNeutrinoState() {
const data = await this.db.get(layout.N.encode());
if (!data)
return new NeutrinoState();
return NeutrinoState.fromRaw(data);
}

async getCFHeaderHeight() {
const state = await this.getNeutrinoState();
return state.headerHeight;
}

async getCFilterHeight() {
const state = await this.getNeutrinoState();
return state.filterHeight;
}

/**
* Save Neutrino State
* @returns {void}
*/
async saveNeutrinoState() {
const state = this.neutrinoState.toRaw();
const b = this.db.batch();
b.put(layout.N.encode(), state);
return b.write();
}
}

/**
Expand Down Expand Up @@ -1952,6 +1991,28 @@ function fromU32(num) {
return data;
}

class NeutrinoState {
manavdesai27 marked this conversation as resolved.
Show resolved Hide resolved
constructor() { // TODO: do we add support for multiple filters?
this.headerHeight = 0;
this.filterHeight = 0;
}

toRaw() {
const bw = bio.write(8);
bw.writeU32(this.headerHeight);
bw.writeU32(this.filterHeight);
return bw.render();
}

static fromRaw(data) {
const state = new NeutrinoState();
const br = bio.read(data);
state.headerHeight = br.readU32();
state.filterHeight = br.readU32();
return state;
}
}

/*
* Expose
*/
Expand Down
3 changes: 3 additions & 0 deletions lib/blockchain/layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const bdb = require('bdb');
* O -> chain options
* R -> tip hash
* D -> versionbits deployments
* N -> neutrino state
* e[hash] -> entry
* h[hash] -> height
* H[height] -> hash
Expand All @@ -33,6 +34,8 @@ const layout = {
O: bdb.key('O'),
R: bdb.key('R'),
D: bdb.key('D'),
N: bdb.key('N'),
F: bdb.key('H', ['hash256']),
manavdesai27 marked this conversation as resolved.
Show resolved Hide resolved
e: bdb.key('e', ['hash256']),
h: bdb.key('h', ['hash256']),
H: bdb.key('H', ['uint32']),
Expand Down
15 changes: 12 additions & 3 deletions lib/client/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,18 @@
* @returns {Promise}
*/

getFilter(filter) {
assert(typeof filter === 'string' || typeof filter === 'number');
return this.get(`/filter/${filter}`);
getFilter(block) {
assert(typeof block === 'string' || typeof block === 'number');
return this.get(`/filter/${block}`);

Check warning on line 169 in lib/client/node.js

View check run for this annotation

Codecov / codecov/patch

lib/client/node.js#L168-L169

Added lines #L168 - L169 were not covered by tests
}

getFilterHeader(block) {
assert(typeof block === 'string' || typeof block === 'number');
return this.get(`/filterheader/${block}`);

Check warning on line 174 in lib/client/node.js

View check run for this annotation

Codecov / codecov/patch

lib/client/node.js#L173-L174

Added lines #L173 - L174 were not covered by tests
}

getBlockPeer(hash) {
return this.call('get block peer', hash);

Check warning on line 178 in lib/client/node.js

View check run for this annotation

Codecov / codecov/patch

lib/client/node.js#L178

Added line #L178 was not covered by tests
}

/**
Expand Down
43 changes: 43 additions & 0 deletions lib/indexer/filterindexer.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,49 @@ class FilterIndexer extends Indexer {
this.put(layout.f.encode(hash), gcsFilter.hash());
}

/**
* save filter header
* @param {Hash} blockHash
* @param {Hash} filterHeader
* @param {Hash} filterHash
* @returns {Promise}
*/

async saveFilterHeader(blockHash, filterHeader, filterHash) {
assert(blockHash);
assert(filterHeader);
assert(filterHash);

const filter = new Filter();
filter.header = filterHeader;

await this.blocks.writeFilter(blockHash, filter.toRaw(), this.filterType);
// console.log(layout.f.encode(blockHash));
this.put(layout.f.encode(blockHash), filterHash);
}

/**
* Save filter
* @param {Hash} blockHash
* @param {BasicFilter} basicFilter
* @param {Hash} filterHeader
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: add blockheight

* @returns {Promise}
*/

async saveFilter(blockHash, basicFilter, filterHeader) {
assert(blockHash);
assert(basicFilter);
assert(filterHeader);

const filter = new Filter();
filter.filter = basicFilter.toRaw();
filter.header = filterHeader;
Comment on lines +102 to +104
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe I'm being too picky about code styles but I'd prefer if you pass an options object instead of assigning values to class variables

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that many functions in filterindexer.js is doing this. We should make this change everywhere since we are touching this file anyway.


await this.blocks.writeFilter(blockHash, filter.toRaw(), this.filterType);
// console.log(layout.f.encode(blockHash));
this.put(layout.f.encode(blockHash), basicFilter.hash());
}

/**
* Prune compact filters.
* @private
Expand Down
Loading