diff --git a/lib/wallet/txdb.js b/lib/wallet/txdb.js index 3c45aacc75..dd59e85cfc 100644 --- a/lib/wallet/txdb.js +++ b/lib/wallet/txdb.js @@ -2240,6 +2240,19 @@ class TXDB { }); } + /** + * Test whether an account owns a transaction. + * @param {Number} acct + * @param {Hash} hash + * @returns {Promise} - Returns Boolean. + */ + + acctOwnsTX(acct, hash) { + assert(typeof acct === 'number'); + + return this.bucket.has(layout.T.encode(acct, hash)); + } + /** * Get hashes of all transactions in the database. * @param {Number} acct diff --git a/lib/wallet/wallet.js b/lib/wallet/wallet.js index 1a780c896c..9d72b1893f 100644 --- a/lib/wallet/wallet.js +++ b/lib/wallet/wallet.js @@ -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.'); @@ -1781,6 +1786,9 @@ class Wallet extends EventEmitter { if (!coin) continue; + if (acct && !await this.txdb.acctOwnsTX(acct, hash)) + continue; + // Is local? if (coin.height < ns.height) continue; @@ -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); } diff --git a/test/double-reveal-test.js b/test/double-reveal-test.js new file mode 100644 index 0000000000..bf9479f79c --- /dev/null +++ b/test/double-reveal-test.js @@ -0,0 +1,90 @@ +/* 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', async () => { + await wallet.sendReveal(name, {account: 'alice'}); + }); +});