Skip to content

Commit

Permalink
feat<node>: Headers-Sync
Browse files Browse the repository at this point in the history
  • Loading branch information
manavdesai27 committed Jul 20, 2023
1 parent a4bf281 commit 0d84694
Show file tree
Hide file tree
Showing 11 changed files with 577 additions and 20 deletions.
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
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');
bcoin.Node = require('./node/node');
bcoin.FullNode = require('./node/fullnode');
bcoin.SPVNode = require('./node/spvnode');
bcoin.Neutrino = require('./node/neutrino');

// 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');
bcoin.define('Node', './node/node');
bcoin.define('FullNode', './node/fullnode');
bcoin.define('SPVNode', './node/spvnode');
bcoin.define('Neutrino', './node/neutrino');

// Primitives
bcoin.define('primitives', './primitives');
Expand Down
37 changes: 32 additions & 5 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,19 +2021,22 @@ class Chain extends AsyncEmitter {
if (this.synced)
return;

if (this.options.neutrino && this.getProgress() < 1)
return;
if (this.options.checkpoints) {
if (this.height < this.network.lastCheckpoint)
return;
}

if (this.tip.time < util.now() - this.network.block.maxTipAge)
} else if (!this.options.neutrino &&
this.tip.time < util.now() - this.network.block.maxTipAge)
return;

if (!this.hasChainwork())
return;

this.synced = true;
this.emit('full');
if (this.options.neutrino)
this.emit('headersFull');
else
this.emit('full');
}

/**
Expand Down Expand Up @@ -2616,6 +2637,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 +2684,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;

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)
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 {
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']),
e: bdb.key('e', ['hash256']),
h: bdb.key('h', ['hash256']),
H: bdb.key('H', ['uint32']),
Expand Down
12 changes: 12 additions & 0 deletions lib/net/peer.js
Original file line number Diff line number Diff line change
Expand Up @@ -1449,6 +1449,12 @@ class Peer extends EventEmitter {
if (!(this.services & services.NETWORK))
throw new Error('Peer does not support network services.');

if (this.options.neutrino) {
if (!(this.services & services.NODE_COMPACT_FILTERS)) {
throw new Error('Peer does not support Compact Filters.');
}
}

if (this.options.headers) {
if (this.version < common.HEADERS_VERSION)
throw new Error('Peer does not support getheaders.');
Expand Down Expand Up @@ -2080,6 +2086,7 @@ class PeerOptions {
this.agent = common.USER_AGENT;
this.noRelay = false;
this.spv = false;
this.neutrino = false;
this.compact = false;
this.headers = false;
this.banScore = common.BAN_SCORE;
Expand Down Expand Up @@ -2143,6 +2150,11 @@ class PeerOptions {
this.spv = options.spv;
}

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

if (options.compact != null) {
assert(typeof options.compact === 'boolean');
this.compact = options.compact;
Expand Down
Loading

0 comments on commit 0d84694

Please sign in to comment.