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 #1155

Draft
wants to merge 21 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
7 changes: 5 additions & 2 deletions bin/bcoin
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,11 @@ for arg in "$@"; do
--daemon)
daemon=1
;;
--spv)
cmd='spvnode'
--neutrino)
cmd='neutrino'
;;
--spv)
cmd='spvnode'
;;
esac
done
Expand Down
48 changes: 48 additions & 0 deletions bin/neutrino
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/usr/bin/env node

'use strict';

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

// Doubt in db
const node = new Neutrino({
file: true,
argv: true,
env: true,
logFile: true,
logConsole: true,
logLevel: 'debug',
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);
}

masterchief164 marked this conversation as resolved.
Show resolved Hide resolved
(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
1 change: 1 addition & 0 deletions lib/bcoin.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +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');
Expand Down
90 changes: 79 additions & 11 deletions lib/blockchain/chain.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ class Chain extends AsyncEmitter {

this.orphanMap = new BufferMap();
this.orphanPrev = new BufferMap();

this.getPrunedMap = new BufferMap();
}

/**
Expand Down Expand Up @@ -583,7 +585,7 @@ class Chain extends AsyncEmitter {
// UASF is now enforced (bip148) (mainnet-only).
if (this.options.bip148 && this.network === Network.main) {
if (witness !== thresholdStates.LOCKED_IN
&& witness !== thresholdStates.ACTIVE) {
&& witness !== thresholdStates.ACTIVE) {
Copy link
Member

Choose a reason for hiding this comment

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

Is this change automatic from you linter? The original code doesn't fail the bcoin lint tests, lets leave it alone since it just makes reviewing your work a bit more confusing (there's several nits like this in the PR)

// The BIP148 MTP check is nonsensical in
// that it includes the _current_ entry's
// timestamp. This requires some hackery,
Expand Down Expand Up @@ -1368,7 +1370,17 @@ class Chain extends AsyncEmitter {
}

// Do we already have this block?
if (await this.hasEntry(hash)) {
const existingEntry = await this.getEntry(hash);

if (existingEntry && this.getPrunedMap.has(hash)) {
block = block.toBlock();
await this.db.updateNeutrinoSave();
await this.db.save(existingEntry, block, new CoinView());
await this.db.updateNeutrinoSave();
return existingEntry;
}

if (existingEntry) {
this.logger.debug('Already have block: %h.', block.hash());
throw new VerifyError(block, 'duplicate', 'duplicate', 0);
}
Expand Down Expand Up @@ -1791,6 +1803,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 @@ -1925,6 +1955,33 @@ class Chain extends AsyncEmitter {
return this.db.getBlock(hash);
}

async getBlockPeer(hash, filter) {
let block = await this.db.getBlock(hash);
if (block) {
const entry = await this.getEntry(hash);
assert(entry.hash.equals(hash));
return block;
} else {
this.logger.warning('Block not found, attempting to download');

// Ensure hash not height
hash = await this.db.getHash(hash);

const wait = new Promise((resolve, reject) => {
this.getPrunedMap.set(hash, resolve);
});

await this.emitAsync('getprunedblock', hash);
await wait;
block = await this.db.getBlock(hash);
const entry = await this.getEntry(hash);
assert(entry.hash.equals(hash));

this.emit('getblockpeer', entry, block);
return block;
}
}

/**
* Retrieve a block from the database (not filled with coins).
* @param {Hash} block
Expand Down Expand Up @@ -2007,15 +2064,20 @@ class Chain extends AsyncEmitter {
if (this.height < this.network.lastCheckpoint)
return;
}

if (this.tip.time < util.now() - this.network.block.maxTipAge)
if (this.options.neutrino && this.tip.time < 1686851917)
// TODO change this later
return;
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 @@ -2144,7 +2206,7 @@ class Chain extends AsyncEmitter {

assert(hash);

for (;;) {
for (; ;) {
const orphan = this.orphanMap.get(hash);

if (!orphan)
Expand Down Expand Up @@ -2221,8 +2283,8 @@ class Chain extends AsyncEmitter {
return pow.bits;

while (prev.height !== 0
&& prev.height % pow.retargetInterval !== 0
&& prev.bits === pow.bits) {
&& prev.height % pow.retargetInterval !== 0
&& prev.bits === pow.bits) {
const cache = this.getPrevCache(prev);

if (cache)
Expand Down Expand Up @@ -2455,7 +2517,7 @@ class Chain extends AsyncEmitter {
const state = await this.getState(prev, deployment);

if (state === thresholdStates.LOCKED_IN
|| state === thresholdStates.STARTED) {
|| state === thresholdStates.STARTED) {
version |= 1 << deployment.bit;
}
}
Expand Down Expand Up @@ -2616,6 +2678,7 @@ class ChainOptions {
this.compression = true;

this.spv = false;
this.neutrino = false;
Copy link
Member

Choose a reason for hiding this comment

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

I'm still feeling like we can get away with a chain in SPV mode but a pool in neutrino mode, which might mean we can leave chain.js mostly alone. But I'm just starting to dig in here so I defer to you

this.bip91 = false;
this.bip148 = false;
this.prune = false;
Expand All @@ -2639,7 +2702,7 @@ class ChainOptions {
fromOptions(options) {
if (!options.spv) {
assert(options.blocks && typeof options.blocks === 'object',
'Chain requires a blockstore.');
'Chain requires a blockstore.');
}

this.blocks = options.blocks;
Expand All @@ -2662,6 +2725,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
Loading