Skip to content

Commit

Permalink
wallet: check account ownership of bid TX when making reveal TX
Browse files Browse the repository at this point in the history
Adds new method hasCoinByAccount(acct, hash, index) to txdb which
returns true if the coin (hash, index) is owned by account (number).
Adds this check to wallet.makeReveal() to prevent BIDs from other
wallet accounts being included as inputs to a new REVEAL TX from a
specific account.
  • Loading branch information
pinheadmz committed Aug 1, 2019
1 parent 0d5865f commit e0362db
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 2 deletions.
14 changes: 14 additions & 0 deletions lib/wallet/txdb.js
Original file line number Diff line number Diff line change
Expand Up @@ -2240,6 +2240,20 @@ class TXDB {
});
}

/**
* Test whether an account owns a coin.
* @param {Number} acct
* @param {Hash} hash
* @param {Index} number
* @returns {Promise} - Returns Boolean.
*/

hasCoinByAccount(acct, hash, index) {
assert(typeof acct === 'number');

return this.bucket.has(layout.C.encode(acct, hash, index));
}

/**
* Get hashes of all transactions in the database.
* @param {Number} acct
Expand Down
13 changes: 11 additions & 2 deletions lib/wallet/wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -1742,9 +1742,14 @@ class Wallet extends EventEmitter {
* @returns {MTX}
*/

async makeReveal(name) {
async makeReveal(name, acct) {
assert(typeof name === 'string');

if (acct) {
assert((acct >>> 0) === acct || typeof acct === 'string');
acct = await this.getAccountIndex(acct);
}

if (!rules.verifyName(name))
throw new Error('Invalid name.');

Expand Down Expand Up @@ -1781,6 +1786,9 @@ class Wallet extends EventEmitter {
if (!coin)
continue;

if (acct && !await this.txdb.hasCoinByAccount(acct, hash, index))
continue;

// Is local?
if (coin.height < ns.height)
continue;
Expand Down Expand Up @@ -1820,7 +1828,8 @@ class Wallet extends EventEmitter {
*/

async _createReveal(name, options) {
const mtx = await this.makeReveal(name);
const acct = options ? options.account : null;
const mtx = await this.makeReveal(name, acct);
await this.fill(mtx, options);
return this.finalize(mtx, options);
}
Expand Down
103 changes: 103 additions & 0 deletions test/double-reveal-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/* eslint-env mocha */
/* eslint prefer-arrow-callback: "off" */

'use strict';

const assert = require('bsert');
const Network = require('../lib/protocol/network');
const FullNode = require('../lib/node/fullnode');
const Address = require('../lib/primitives/address');
const rules = require('../lib/covenants/rules');

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

const node = new FullNode({
memory: true,
network: 'regtest',
plugins: [require('../lib/wallet/plugin')]
});

const {wdb} = node.require('walletdb');
const name = rules.grindName(10, 20, network);

let wallet, alice, bob, aliceMiner, bobMiner;

async function mineBlocks(n, addr) {
addr = addr ? addr : new Address().toString('regtest');
for (let i = 0; i < n; i++) {
const block = await node.miner.mineBlock(null, addr);
await node.chain.add(block);
}
}

describe('One wallet, two accounts, one name', function() {
before(async () => {
await node.open();

wallet = await wdb.create();

alice = await wallet.createAccount({name: 'alice'});
bob = await wallet.createAccount({name: 'bob'});

aliceMiner = await alice.receiveAddress();
bobMiner = await bob.receiveAddress();
});

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

it('should fund both accounts', async () => {
await mineBlocks(10, aliceMiner);
await mineBlocks(10, bobMiner);

// Wallet rescan is an effective way to ensure that
// wallet and chain are synced before proceeding.
await wdb.rescan(0);

const aliceBal = await wallet.getBalance('alice');
const bobBal = await wallet.getBalance('bob');
assert(aliceBal.confirmed === 2000 * 10 * 1e6);
assert(bobBal.confirmed === 2000 * 10 * 1e6);
});

it('should open an auction and proceed to REVEAL phase', async () => {
await wallet.sendOpen(name, false, {account: 'alice'});
await mineBlocks(network.names.treeInterval + 2);
let ns = await node.chain.db.getNameStateByName(name);
assert(ns.isBidding(node.chain.height, network));

await wdb.rescan(0);

await wallet.sendBid(name, 100000, 200000, {account: 'alice'});
await wallet.sendBid(name, 50000, 200000, {account: 'bob'});
await mineBlocks(network.names.biddingPeriod);
ns = await node.chain.db.getNameStateByName(name);
assert(ns.isReveal(node.chain.height, network));

await wdb.rescan(0);

const walletBids = await wallet.getBidsByName(name);
assert.strictEqual(walletBids.length, 2);

for (const bid of walletBids)
assert(bid.own);
});

it('should send REVEAL from one account at a time', async () => {
const tx1 = await wallet.sendReveal(name, {account: 'alice'});
assert(tx1);

const tx2 = await wallet.sendReveal(name, {account: 'bob'});
assert(tx2);

// Reset for next test
await wallet.abandon(tx1.hash());
await wallet.abandon(tx2.hash());
});

it('should send REVEAL from all accounts', async () => {
const tx = await wallet.sendRevealAll();
assert(tx);
});
});

0 comments on commit e0362db

Please sign in to comment.