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

feat: added wallet side integration for neutrino #1159

Draft
wants to merge 2 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
42 changes: 41 additions & 1 deletion lib/blockchain/chain.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@

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

this.getPrunedMap = new BufferMap();
}

/**
Expand Down Expand Up @@ -1368,7 +1370,18 @@
}

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

// FOR EDUCATIONAL PURPOSES ONLY: save block without checking anything
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;

Check warning on line 1381 in lib/blockchain/chain.js

View check run for this annotation

Codecov / codecov/patch

lib/blockchain/chain.js#L1377-L1381

Added lines #L1377 - L1381 were not covered by tests
}

if (existingEntry) {
this.logger.debug('Already have block: %h.', block.hash());
throw new VerifyError(block, 'duplicate', 'duplicate', 0);
}
Expand Down Expand Up @@ -1925,6 +1938,33 @@
return this.db.getBlock(hash);
}

async getBlockPeer(hash, filter) {
let block = await this.db.getBlock(hash);
if (block) {
let entry = await this.getEntry(hash);

Check failure on line 1944 in lib/blockchain/chain.js

View workflow job for this annotation

GitHub Actions / Lint

'entry' is never reassigned. Use 'const' instead
assert(entry.hash.equals(hash));
return block;

Check warning on line 1946 in lib/blockchain/chain.js

View check run for this annotation

Codecov / codecov/patch

lib/blockchain/chain.js#L1942-L1946

Added lines #L1942 - L1946 were not covered by tests
} else {
this.logger.warning('Block not found, attempting to download');

Check warning on line 1948 in lib/blockchain/chain.js

View check run for this annotation

Codecov / codecov/patch

lib/blockchain/chain.js#L1948

Added line #L1948 was not covered by tests

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

Check warning on line 1951 in lib/blockchain/chain.js

View check run for this annotation

Codecov / codecov/patch

lib/blockchain/chain.js#L1951

Added line #L1951 was not covered by tests

// FOR EDUCATIONAL PURPOSES ONLY: flag block for re-downloading
const wait = new Promise((resolve, reject) => {
this.getPrunedMap.set(hash, resolve);

Check warning on line 1955 in lib/blockchain/chain.js

View check run for this annotation

Codecov / codecov/patch

lib/blockchain/chain.js#L1954-L1955

Added lines #L1954 - L1955 were not covered by tests
});

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

Check failure on line 1961 in lib/blockchain/chain.js

View workflow job for this annotation

GitHub Actions / Lint

'entry' is never reassigned. Use 'const' instead
assert(entry.hash.equals(hash));

Check warning on line 1962 in lib/blockchain/chain.js

View check run for this annotation

Codecov / codecov/patch

lib/blockchain/chain.js#L1958-L1962

Added lines #L1958 - L1962 were not covered by tests

return block;

Check warning on line 1964 in lib/blockchain/chain.js

View check run for this annotation

Codecov / codecov/patch

lib/blockchain/chain.js#L1964

Added line #L1964 was not covered by tests
}
}

/**
* Retrieve a block from the database (not filled with coins).
* @param {Hash} block
Expand Down
14 changes: 12 additions & 2 deletions lib/blockchain/chaindb.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
this.pending = null;
this.current = null;

this.neutrinoSave = false;

this.cacheHash = new LRU(this.options.entryCache, null, BufferMap);
this.cacheHeight = new LRU(this.options.entryCache);

Expand Down Expand Up @@ -1001,7 +1003,7 @@
*/

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 @@ -1150,6 +1152,14 @@
* @returns {Promise}
*/

async updateNeutrinoSave () {
if(this.neutrinoSave) {

Check failure on line 1156 in lib/blockchain/chaindb.js

View workflow job for this annotation

GitHub Actions / Lint

Expected space(s) after "if"
this.neutrinoSave = false;

Check warning on line 1157 in lib/blockchain/chaindb.js

View check run for this annotation

Codecov / codecov/patch

lib/blockchain/chaindb.js#L1156-L1157

Added lines #L1156 - L1157 were not covered by tests
} else {
this.neutrinoSave = true;

Check warning on line 1159 in lib/blockchain/chaindb.js

View check run for this annotation

Codecov / codecov/patch

lib/blockchain/chaindb.js#L1159

Added line #L1159 was not covered by tests
}
}

async save(entry, block, view) {
this.start();
try {
Expand Down Expand Up @@ -1478,7 +1488,7 @@
async saveBlock(entry, block, view) {
const hash = block.hash();

if (this.options.spv)
if (this.options.spv && !this.neutrinoSave)
return;

// Write actual block data.
Expand Down
4 changes: 4 additions & 0 deletions lib/client/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@
return this.get(`/filter/${filter}`);
}

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

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

View check run for this annotation

Codecov / codecov/patch

lib/client/node.js#L173

Added line #L173 was not covered by tests
}

/**
* Add a transaction to the mempool and broadcast it.
* @param {TX} tx
Expand Down
8 changes: 8 additions & 0 deletions lib/net/pool.js
Original file line number Diff line number Diff line change
Expand Up @@ -2316,6 +2316,14 @@
}

// Block was orphaned.

const resolve = this.chain.getPrunedMap.get(hash);
if (resolve) {
this.logger.warning('Received pruned block by special request');
this.chain.getPrunedMap.delete(hash);
resolve();

Check warning on line 2324 in lib/net/pool.js

View check run for this annotation

Codecov / codecov/patch

lib/net/pool.js#L2322-L2324

Added lines #L2322 - L2324 were not covered by tests
}

Check failure on line 2326 in lib/net/pool.js

View workflow job for this annotation

GitHub Actions / Lint

Trailing spaces not allowed
if (!entry) {
if (this.checkpoints) {
this.logger.warning(
Expand Down
7 changes: 7 additions & 0 deletions lib/node/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,13 @@
return null;
});

socket.hook('get block peer', (...args) => {
const valid = new Validator(args);
const hash = valid.hash(0);
const filter = valid.buf(1);
return this.pool.getBlockPeer(hash, filter);

Check warning on line 505 in lib/node/http.js

View check run for this annotation

Codecov / codecov/patch

lib/node/http.js#L502-L505

Added lines #L502 - L505 were not covered by tests
})

Check failure on line 506 in lib/node/http.js

View workflow job for this annotation

GitHub Actions / Lint

Missing semicolon

socket.hook('estimate fee', (...args) => {
const valid = new Validator(args);
const blocks = valid.u32(0);
Expand Down
13 changes: 13 additions & 0 deletions lib/wallet/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
const util = require('../utils/util');
const TX = require('../primitives/tx');
const hash256 = require('bcrypto/lib/hash256');
const WalletKey = require('./walletkey');

Check failure on line 15 in lib/wallet/client.js

View workflow job for this annotation

GitHub Actions / Lint

'WalletKey' is assigned a value but never used
const Filter = require('../primitives/filter');

Check failure on line 16 in lib/wallet/client.js

View workflow job for this annotation

GitHub Actions / Lint

'Filter' is assigned a value but never used

Check warning on line 16 in lib/wallet/client.js

View check run for this annotation

Codecov / codecov/patch

lib/wallet/client.js#L15-L16

Added lines #L15 - L16 were not covered by tests

const parsers = {
'block connect': (entry, txs) => parseBlock(entry, txs),
Expand Down Expand Up @@ -71,6 +73,17 @@
return super.setFilter(filter.toRaw());
}

/**
* Check filter against wallet key ring
* @param {WalletKey} ring
* @param {Filter} filter
* @returns {Promise}
*/

async getBlockFromNode(hash, filter) {
return super.getBlockPeer(hash, filter);

Check warning on line 84 in lib/wallet/client.js

View check run for this annotation

Codecov / codecov/patch

lib/wallet/client.js#L84

Added line #L84 was not covered by tests
}

async rescan(start) {
if (Buffer.isBuffer(start))
start = util.revHex(start);
Expand Down
21 changes: 20 additions & 1 deletion lib/wallet/nodeclient.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

const assert = require('bsert');
const AsyncEmitter = require('bevent');
const WalletKey = require('./walletkey');

Check failure on line 11 in lib/wallet/nodeclient.js

View workflow job for this annotation

GitHub Actions / Lint

'WalletKey' is assigned a value but never used
const Filter = require('../primitives/filter');

Check failure on line 12 in lib/wallet/nodeclient.js

View workflow job for this annotation

GitHub Actions / Lint

'Filter' is assigned a value but never used

/**
* Node Client
Expand Down Expand Up @@ -37,7 +39,7 @@

init() {
this.node.chain.on('connect', async (entry, block) => {
if (!this.opened)
if (!this.opened || this.node.neutrino)
return;

await this.emitAsync('block connect', entry, block.txs);
Expand All @@ -50,6 +52,12 @@
await this.emitAsync('block disconnect', entry);
});

this.node.pool.on('cfilter', async (blockHeight, filter) => {
if(!this.opened) return;

Check failure on line 56 in lib/wallet/nodeclient.js

View workflow job for this annotation

GitHub Actions / Lint

Expected space(s) after "if"

Check warning on line 56 in lib/wallet/nodeclient.js

View check run for this annotation

Codecov / codecov/patch

lib/wallet/nodeclient.js#L56

Added line #L56 was not covered by tests

await this.emitAsync('cfilter', blockHeight, filter);

Check warning on line 58 in lib/wallet/nodeclient.js

View check run for this annotation

Codecov / codecov/patch

lib/wallet/nodeclient.js#L58

Added line #L58 was not covered by tests
})

this.node.on('tx', (tx) => {
if (!this.opened)
return;
Expand Down Expand Up @@ -134,6 +142,10 @@
return entry;
}

async getBlockFromNode(hash, filter) {
await this.node.chain.getBlockPeer(hash, filter);

Check warning on line 146 in lib/wallet/nodeclient.js

View check run for this annotation

Codecov / codecov/patch

lib/wallet/nodeclient.js#L146

Added line #L146 was not covered by tests
}

/**
* Send a transaction. Do not wait for promise.
* @param {TX} tx
Expand Down Expand Up @@ -174,6 +186,13 @@
this.node.pool.queueFilterLoad();
}

/**
* Check filter against wallet key ring
* @param {WalletKey} ring
* @param {Filter} filter
* @returns {Promise}
*/

/**
* Estimate smart fee.
* @param {Number?} blocks
Expand Down
13 changes: 13 additions & 0 deletions lib/wallet/nullclient.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

const assert = require('bsert');
const EventEmitter = require('events');
const WalletKey = require('./walletkey');
const Filter = require('../primitives/filter');

/**
* Null Client
Expand Down Expand Up @@ -130,6 +132,17 @@ class NullClient extends EventEmitter {
this.wdb.emit('reset filter');
}

/**
* Check filter against wallet key ring
* @param {WalletKey} ring
* @param {Filter} filter
* @returns {Promise}
*/

async getBlockFromNode(hash, filter) {
;
}

/**
* Esimate smart fee.
* @param {Number?} blocks
Expand Down
2 changes: 2 additions & 0 deletions lib/wallet/wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ class Wallet extends EventEmitter {
this.writeLock = new Lock();
this.fundLock = new Lock();

this.neutrino = false;

this.wid = 0;
this.id = null;
this.watchOnly = false;
Expand Down
39 changes: 39 additions & 0 deletions lib/wallet/walletdb.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,14 @@
this.emit('error', e);
}
});

this.client.bind('cfilter', async (blockHeight, filter) => {
try {
await this.checkFilter(blockHeight, filter);

Check warning on line 175 in lib/wallet/walletdb.js

View check run for this annotation

Codecov / codecov/patch

lib/wallet/walletdb.js#L174-L175

Added lines #L174 - L175 were not covered by tests
} catch (e) {
this.emit('error', e);

Check warning on line 177 in lib/wallet/walletdb.js

View check run for this annotation

Codecov / codecov/patch

lib/wallet/walletdb.js#L177

Added line #L177 was not covered by tests
}
})
}

/**
Expand Down Expand Up @@ -568,6 +576,37 @@
return this.client.resetFilter();
}

async checkFilter (blockHash, filter) {
const gcsKey = blockHash.slice(0, 16);

Check warning on line 580 in lib/wallet/walletdb.js

View check run for this annotation

Codecov / codecov/patch

lib/wallet/walletdb.js#L580

Added line #L580 was not covered by tests

const piter = this.db.iterator({

Check warning on line 582 in lib/wallet/walletdb.js

View check run for this annotation

Codecov / codecov/patch

lib/wallet/walletdb.js#L582

Added line #L582 was not covered by tests
gte: layout.p.min(),
lte: layout.p.max()
});

await piter.each(async (key) => {
const [data] = layout.p.decode(key);

Check warning on line 588 in lib/wallet/walletdb.js

View check run for this annotation

Codecov / codecov/patch

lib/wallet/walletdb.js#L587-L588

Added lines #L587 - L588 were not covered by tests
// todo: check filter
let match = filter.match(gcsKey, data);
if (match)
await this.client.getBlockFromNode(blockHash, filter);

Check warning on line 592 in lib/wallet/walletdb.js

View check run for this annotation

Codecov / codecov/patch

lib/wallet/walletdb.js#L590-L592

Added lines #L590 - L592 were not covered by tests
});

const oiter = this.db.iterator({

Check warning on line 595 in lib/wallet/walletdb.js

View check run for this annotation

Codecov / codecov/patch

lib/wallet/walletdb.js#L595

Added line #L595 was not covered by tests
gte: layout.o.min(),
lte: layout.o.max()
});

await oiter.each(async (key) => {
const [hash, index] = layout.o.decode(key);
const outpoint = new Outpoint(hash, index);
const data = outpoint.toRaw();
let match = filter.match(gcsKey, data);
if (match)
await this.client.getBlockFromNode(blockHash, filter);

Check warning on line 606 in lib/wallet/walletdb.js

View check run for this annotation

Codecov / codecov/patch

lib/wallet/walletdb.js#L600-L606

Added lines #L600 - L606 were not covered by tests
});
}

/**
* Backup the wallet db.
* @param {String} path
Expand Down