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: Wallet #1166

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
12 changes: 12 additions & 0 deletions lib/wallet/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,18 @@
return super.setFilter(filter.toRaw());
}

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

async getBlockFromNode(hash) {
// return super.getBlockPeer(hash);
console.log('getBlockFromNode');

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

View check run for this annotation

Codecov / codecov/patch

lib/wallet/client.js#L83

Added line #L83 was not covered by tests
}

async rescan(start) {
if (Buffer.isBuffer(start))
start = util.revHex(start);
Expand Down
19 changes: 19 additions & 0 deletions lib/wallet/nodeclient.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@

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

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

View check run for this annotation

Codecov / codecov/patch

lib/wallet/nodeclient.js#L41

Added line #L41 was not covered by tests

await this.emitAsync('block connect', entry, block.txs);
});

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

Expand All @@ -50,6 +57,13 @@
await this.emitAsync('block disconnect', entry);
});

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

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

View check run for this annotation

Codecov / codecov/patch

lib/wallet/nodeclient.js#L61-L62

Added lines #L61 - L62 were not covered by tests

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

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

View check run for this annotation

Codecov / codecov/patch

lib/wallet/nodeclient.js#L64

Added line #L64 was not covered by tests
});

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

async getBlockFromNode(hash) {
// await this.node.chain.getBlockPeer(hash);
console.log('getBlockFromNode');

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

View check run for this annotation

Codecov / codecov/patch

lib/wallet/nodeclient.js#L153

Added line #L153 was not covered by tests
}

/**
* Send a transaction. Do not wait for promise.
* @param {TX} tx
Expand Down
11 changes: 11 additions & 0 deletions lib/wallet/nullclient.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,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) {
;
}

/**
* Esimate smart fee.
* @param {Number?} blocks
Expand Down
48 changes: 48 additions & 0 deletions lib/wallet/walletdb.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
const layouts = require('./layout');
const records = require('./records');
const NullClient = require('./nullclient');
const Script = require('../script/script');
const Address = require('../primitives/address');
const layout = layouts.wdb;
const tlayout = layouts.txdb;

Expand Down Expand Up @@ -65,10 +67,12 @@
this.state = new ChainState();
this.confirming = false;
this.height = 0;
this.filterHeight = 0;
this.wallets = new Map();
this.depth = 0;
this.rescanning = false;
this.filterSent = false;
this.isWitness = false;

// Wallet read lock.
this.readLock = new MapLock();
Expand Down Expand Up @@ -169,6 +173,14 @@
this.emit('error', e);
}
});

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

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

View check run for this annotation

Codecov / codecov/patch

lib/wallet/walletdb.js#L178-L179

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

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

View check run for this annotation

Codecov / codecov/patch

lib/wallet/walletdb.js#L181

Added line #L181 was not covered by tests
}
});
}

/**
Expand Down Expand Up @@ -201,6 +213,9 @@
id: 'primary'
});

const account = await wallet.getAccount(wallet.wid);
this.isWitness = account.witness;

const addr = await wallet.receiveAddress();

this.logger.info(
Expand Down Expand Up @@ -568,6 +583,39 @@
return this.client.resetFilter();
}

async checkFilter (blockHash, filter) {
let foundMatch = false;
this.filterHeight = this.filterHeight + 1;
const gcsKey = blockHash.slice(0, 16);

const piter = this.db.iterator({
gte: layout.p.min(),
lte: layout.p.max()
});

await piter.each(async (key) => {
const [data] = layout.p.decode(key);
let address = null;
if (data.length === 20) {
if (this.isWitness)
address = Address.fromWitnessPubkeyhash(data);
else
address = Address.fromPubkeyhash(data);
} else if (data.length === 32) {
address = Address.fromWitnessScripthash(data);

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

View check run for this annotation

Codecov / codecov/patch

lib/wallet/walletdb.js#L603-L605

Added lines #L603 - L605 were not covered by tests
}

const script = Script.fromAddress(address);
const match = filter.match(gcsKey, script.toRaw());
if (match) {
foundMatch = true;
return;
}
});

return foundMatch;
}

/**
* Backup the wallet db.
* @param {String} path
Expand Down
104 changes: 104 additions & 0 deletions test/wallet-neutrino-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
'use strict';

const assert = require('bsert');
const WalletDB = require('../lib/wallet/walletdb');
const { Network } = require('../lib/protocol');
const WorkerPool = require('../lib/workers/workerpool');
const Chain = require('../lib/blockchain/chain');
const BlockStore = require('../lib/blockstore/level');
const Miner = require('../lib/mining/miner');
const CoinView = require('../lib/coins/coinview');

const wdb = new WalletDB();

const network = Network.get('regtest');

const workers = new WorkerPool({
enabled: true,
size: 2
});

const blocks = new BlockStore({
memory: true,
network
});

const chain = new Chain({
memory: true,
blocks,
network,
workers
});

const miner = new Miner({
chain,
version: 4,
workers
});

let wallet = null;
const addresses = [];
const minedBlocks = [];
const filters = [];

describe('wallet-neutrino', function() {
before(async () => {
await wdb.open();
});

after(async () => {
await wdb.close();
});

it('should open wallet', async () => {
wallet = await wdb.create();
});

it('should create accounts', async () => {
await wallet.createAccount('foo');
});

it('should generate addresses', async () => {
for (let i = 0; i < 3; i++) {
const key = await wallet.createReceive(0);
const address = key.getAddress();
addresses.push(address);
}
});

it('should create 3 match blocks', async () => {
for (let i = 0; i < 3; i++) {
const addr = addresses[i];
const block = await miner.mineBlock(null, addr);
minedBlocks.push(block);
}
});

it('should create 2 non-match blocks', async () => {
for (let i = 0; i < 2; i++) {
const block = await miner.mineBlock(null, null);
minedBlocks.push(block);
}
});

it('should create filters', async () => {
for (let i = 0; i < 5; i++) {
const filter = minedBlocks[i].toBasicFilter(new CoinView());
filters.push(filter);
}
});

it('should match the filters', async () => {
for (let i = 0; i < 3; i++) {
const match = await wdb.checkFilter(minedBlocks[i].hash(), filters[i]);
assert(match);
}
});

it('should not match the filters', async () => {
for (let i = 3; i < 5; i++) {
const match = await wdb.checkFilter(minedBlocks[i].hash(), filters[i]);
assert(!match);
}
});
});