Skip to content

Commit

Permalink
feat: added neutrino mode client support
Browse files Browse the repository at this point in the history
  • Loading branch information
masterchief164 committed Jun 28, 2023
1 parent 093b18f commit 9084146
Show file tree
Hide file tree
Showing 12 changed files with 378 additions and 6 deletions.
3 changes: 3 additions & 0 deletions bin/bcoin
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ for arg in "$@"; do
--daemon)
daemon=1
;;
--neutrino)
cmd='neutrino'
;;
--spv)
cmd='spvnode'
;;
Expand Down
42 changes: 42 additions & 0 deletions bin/neutrino
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/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,
logLevel: 'debug',
db: 'leveldb',
memory: false,
workers: true,
loader: require
});

(async () => {
await node.ensure();
await node.open();
await node.connect();
node.startSync();

node.on('full', () => {
console.log('Full node');
});
})().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
2 changes: 1 addition & 1 deletion lib/bcoin.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,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');
bcoin.define('Address', './primitives/address');
Expand Down
2 changes: 2 additions & 0 deletions lib/blockchain/chain.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ const ChainEntry = require('./chainentry');
const CoinView = require('../coins/coinview');
const Script = require('../script/script');
const {VerifyError} = require('../protocol/errors');
const {filters} = require('../blockstore/common');
const {filtersByVal} = require('../net/common');
const thresholdStates = common.thresholdStates;

/**
Expand Down
51 changes: 51 additions & 0 deletions 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 @@ -1670,6 +1676,29 @@ 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);
}

/**
* 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 +1981,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.headersHeight = br.readU32();
state.filterHeight = br.readU32();
return state;
}
}

/*
* Expose
*/
Expand Down
4 changes: 4 additions & 0 deletions lib/blockchain/layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ const bdb = require('bdb');
* O -> chain options
* R -> tip hash
* D -> versionbits deployments
* N -> Neutrino Status
* F[hash] -> filterHeader
* e[hash] -> entry
* h[hash] -> height
* H[height] -> hash
Expand All @@ -33,6 +35,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
42 changes: 42 additions & 0 deletions lib/indexer/filterindexer.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,48 @@ 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
* @returns {Promise}
*/

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

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

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

/**
* Prune compact filters.
* @private
Expand Down
1 change: 1 addition & 0 deletions lib/indexer/indexer.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class Indexer extends EventEmitter {
*/

put(key, value) {
console.log('put', key, value.toString('hex'));
this.batch.put(key, value);
}

Expand Down
66 changes: 66 additions & 0 deletions lib/net/peer.js
Original file line number Diff line number Diff line change
Expand Up @@ -1009,6 +1009,12 @@ class Peer extends EventEmitter {
case packetTypes.GETHEADERS:
this.request(packetTypes.HEADERS, timeout * 2);
break;
case packetTypes.GETCFHEADERS:
this.request(packetTypes.CFHEADERS, timeout);
break;
case packetTypes.GETCFILTERS:
this.request(packetTypes.CFILTER, timeout);
break;
case packetTypes.GETDATA:
this.request(packetTypes.DATA, timeout * 2);
break;
Expand Down Expand Up @@ -1751,6 +1757,26 @@ class Peer extends EventEmitter {
this.send(packet);
}

/**
* @param {Number} filterType - `0` = basic
* @param {Number} startHeight - Height to start at.
* @param {Hash} stopHash - Hash to stop at.
* @returns {void}
* @description Send `getcfilters` to peer.
*/
sendGetCFilters(filterType, startHeight, stopHash) {
const packet = new packets.GetCFiltersPacket(
filterType,
startHeight,
stopHash);

this.logger.debug(
'Sending getcfilters (type=%d, startHeight=%d, stopHash=%h).',
filterType, startHeight, stopHash);

this.send(packet);
}

/**
* Send `cfheaders` to peer.
* @param {Number} filterType
Expand All @@ -1773,6 +1799,27 @@ class Peer extends EventEmitter {
this.send(packet);
}

/**
* @param {Number} filterType
* @param {Number} startHeight
* @param {Hash} stopHash
* @returns {void}
* @description Send `getcfheaders` to peer.
*/

sendGetCFHeaders(filterType, startHeight, stopHash) {
const packet = new packets.GetCFHeadersPacket(
filterType,
startHeight,
stopHash);

this.logger.debug(
'Sending getcfheaders (type=%d, start=%h, stop=%h).',
filterType, startHeight, stopHash);

this.send(packet);
}

/**
* send `cfcheckpt` to peer.
* @param {Number} filterType
Expand All @@ -1793,6 +1840,25 @@ class Peer extends EventEmitter {
this.send(packet);
}

/**
* Send `getcfcheckpt` to peer.
* @param {Number} filterType
* @param {Hash} stopHash
* @returns {void}
*/

sendGetCFCheckpt(filterType, stopHash) {
const packet = new packets.GetCFCheckptPacket(
filterType,
stopHash);

this.logger.debug(
'Sending getcfcheckpt (type=%d, stop=%h).',
filterType, stopHash);

this.send(packet);
}

/**
* Send `mempool` to peer.
*/
Expand Down
Loading

0 comments on commit 9084146

Please sign in to comment.