diff --git a/lib/mempool/mempool.js b/lib/mempool/mempool.js index 141b974014..ecdc0f68e8 100644 --- a/lib/mempool/mempool.js +++ b/lib/mempool/mempool.js @@ -78,20 +78,29 @@ class Mempool extends EventEmitter { this.tip = this.network.genesis.hash; this.nextState = this.chain.state; - this.waiting = new BufferMap(); - this.orphans = new BufferMap(); - this.map = new BufferMap(); - this.spents = new BufferMap(); - this.claims = new BufferMap(); - this.airdrops = new BufferMap(); - this.airdropIndex = new Map(); - this.claimNames = new BufferMap(); + // "The" mempool + this.map = new BufferMap(); // hash -> MempoolEntry + this.claims = new BufferMap(); // hash -> ClaimEntry + this.airdrops = new BufferMap(); // hash -> AirdropEntry + + // Orphans and missing parents + this.waiting = new BufferMap(); // parent hash -> BufferSet[spender hashes] + this.orphans = new BufferMap(); // orphan tx hash -> Orphan + + // Prevent double-spends + this.spents = new BufferMap(); // prevout key -> MempoolEntry of spender + this.claimNames = new BufferMap(); // namehash -> ClaimEntry + this.airdropIndex = new Map(); // airdrop position -> AirdropEntry + + // Track namestates to validate incoming covenants + this.contracts = new ContractState(this.network); + + // Recently rejected txs by hash this.rejects = new RollingFilter(120000, 0.000001); + // Extensions of blockchain indexes by tx hash for API this.coinIndex = new CoinIndex(); this.txIndex = new TXIndex(); - - this.contracts = new ContractState(this.network); } /** @@ -193,13 +202,6 @@ class Mempool extends EventEmitter { */ async _addBlock(block, txs, view) { - if (this.map.size === 0 - && this.claims.size === 0 - && this.airdrops.size === 0) { - this.tip = block.hash; - return; - } - const entries = []; const cb = txs[0]; @@ -382,6 +384,17 @@ class Mempool extends EventEmitter { if (this.hasEntry(hash)) continue; + // Some covenants can only be used once per name per block. + // If the TX we want to re-insert into the mempool conflicts + // with another TX already in the mempool because of this rule, + // the solution is to evict the NEWER TX (the TX already in the + // mempool) and then insert the OLDER TX (from the disconnected block). + // Since the newer TX spends the output of the older TX, evicting the + // older TX but keeping the newer TX would leave the mempool in an + // invalid state, and the miner would produce invalid blocks. + if (this.contracts.hasNames(tx)) + this.removeNamestateUpdates(tx); + try { await this.insertTX(tx, -1); total += 1; @@ -777,6 +790,28 @@ class Mempool extends EventEmitter { return Coin.fromTX(entry.tx, index, -1); } + /** + * Check whether coin is still unspent. + * @param {Hash} hash + * @param {Number} index + * @returns {boolean} + */ + + hasCoin(hash, index) { + const entry = this.map.get(hash); + + if (!entry) + return false; + + if (this.isSpent(hash, index)) + return false; + + if (index >= entry.tx.outputs.length) + return false; + + return true; + } + /** * Check to see if a coin has been spent. This differs from * {@link ChainDB#isSpent} in that it actually maintains a @@ -1829,6 +1864,37 @@ class Mempool extends EventEmitter { } } + /** + * Recursively remove child transactions where linked names are updated + * @private + * @param {TX} tx + */ + + removeNamestateUpdates(tx) { + const names = new BufferSet(); + rules.addNames(tx, names); + const hash = tx.hash(); + + for (let i = 0; i < tx.outputs.length; i++) { + const spender = this.getSpent(hash, i); + + // No child in mempool spending this output + if (!spender) + continue; + + // Child is not linked by name. + // Covenant rules don't prevent it from + // staying in the mempool. + if (!rules.hasNames(spender.tx, names)) + continue; + + // Bye-bye, try again later! + // (After parent is re-confirmed) + this.removeSpenders(spender); + this.removeEntry(spender); + } + } + /** * Count the highest number of * ancestors a transaction may have. @@ -2360,6 +2426,8 @@ class Mempool extends EventEmitter { /** * Get coin viewpoint (lock). + * Note: this does not return the historical + * view of coins from the indexers * @method * @param {TX} tx * @returns {Promise} - Returns {@link CoinView}. @@ -2368,12 +2436,40 @@ class Mempool extends EventEmitter { async getSpentView(tx) { const unlock = await this.locker.lock(); try { - return await this.getCoinView(tx); + return await this._getSpentView(tx); } finally { unlock(); } } + /** + * Get coin viewpoint + * @param {TX} tx + * @returns {Promise} - Returns {@link CoinView} + */ + + async _getSpentView(tx) { + const view = new CoinView(); + + for (const {prevout} of tx.inputs) { + const {hash, index} = prevout; + const tx = this.getTX(hash); + + if (tx) { + if (index < tx.outputs.length) + view.addIndex(tx, index, -1); + continue; + } + + const coin = await this.chain.readCoin(prevout); + + if (coin) + view.addEntry(prevout, coin); + } + + return view; + } + /** * Get coin viewpoint (no lock). * @method @@ -2389,7 +2485,7 @@ class Mempool extends EventEmitter { const tx = this.getTX(hash); if (tx) { - if (index < tx.outputs.length) + if (this.hasCoin(hash, index)) view.addIndex(tx, index, -1); continue; } @@ -3046,7 +3142,7 @@ class Orphan { * Create an orphan. * @constructor * @param {TX} tx - * @param {Hash[]} missing + * @param {Number} missing * @param {Number} id */ diff --git a/lib/protocol/networks.js b/lib/protocol/networks.js index 08a75a729c..50a50cf098 100644 --- a/lib/protocol/networks.js +++ b/lib/protocol/networks.js @@ -785,18 +785,19 @@ regtest.txStart = 0; regtest.names = { auctionStart: 0, - rolloutInterval: 1000, + rolloutInterval: 2, lockupPeriod: 2, - renewalWindow: 400, - renewalPeriod: 500, - renewalMaturity: 1, + renewalWindow: 5000, + renewalPeriod: 2500, + renewalMaturity: 50, claimPeriod: 250000, - biddingPeriod: 50, - revealPeriod: 50, - treeInterval: 50, - transferLockup: 50, - revocationDelay: 50, - auctionMaturity: 50 + 50 + 50, + alexaLockupPeriod: 500000, + claimFrequency: 0, + biddingPeriod: 5, + revealPeriod: 10, + treeInterval: 5, + transferLockup: 10, + auctionMaturity: 5 + 10 + 50, noRollout: false, noReserved: false }; diff --git a/lib/wallet/walletdb.js b/lib/wallet/walletdb.js index 88b8f6f550..89250dd733 100644 --- a/lib/wallet/walletdb.js +++ b/lib/wallet/walletdb.js @@ -121,6 +121,7 @@ class WalletDB extends EventEmitter { this.client.on('connect', async () => { try { await this.syncNode(); + this.emit('sync done'); } catch (e) { this.emit('error', e); } diff --git a/test/mempool-reorg-test.js b/test/mempool-reorg-test.js new file mode 100644 index 0000000000..f2782dd9b6 --- /dev/null +++ b/test/mempool-reorg-test.js @@ -0,0 +1,241 @@ +'use strict'; + +const assert = require('bsert'); +const Network = require('../lib/protocol/network'); +const {Resource} = require('../lib/dns/resource'); +const FullNode = require('../lib/node/fullnode'); +const plugin = require('../lib/wallet/plugin'); +const Coin = require('../lib/primitives/coin'); +const Address = require('../lib/primitives/address'); +const MTX = require('../lib/primitives/mtx'); +const {forEvent} = require('./util/common'); + +const network = Network.get('regtest'); +const { + treeInterval, + biddingPeriod, + revealPeriod +} = network.names; + +describe('Mempool Covenant Reorg', function () { + const node = new FullNode({ + network: 'regtest' + }); + node.use(plugin); + + let wallet, name; + + before(async () => { + const wdb = node.require('walletdb').wdb; + const syncDone = forEvent(wdb, 'sync done'); + + await node.open(); + wallet = wdb.primary; + + await syncDone; + }); + + after(async () => { + await node.close(); + }); + + let counter = 0; + function makeResource() { + return Resource.fromJSON({ + records: [{type: 'TXT', txt: [`${counter++}`]}] + }); + } + + it('should fund wallet and win name', async () => { + await node.rpc.generate([10]); + name = await node.rpc.grindName([3]); + await wallet.sendOpen(name, true); + await node.rpc.generate([treeInterval + 1]); + await wallet.sendBid(name, 10000, 20000); + await node.rpc.generate([biddingPeriod]); + await wallet.sendReveal(name); + await node.rpc.generate([revealPeriod]); + await node.rpc.generate([1]); + await wallet.sendUpdate(name, makeResource()); + await node.rpc.generate([1]); + + const check = await node.rpc.getNameResource([name]); + assert.deepStrictEqual( + check, + {records: [{type: 'TXT', txt: ['0']}]} + ); + }); + + it('should generate UPDATE chain', async () => { + for (let i = 0; i < 10; i++) { + await wallet.sendUpdate(name, makeResource()); + await node.rpc.generate([1]); + } + + const check = await node.rpc.getNameResource([name]); + assert.deepStrictEqual( + check, + {records: [{type: 'TXT', txt: ['10']}]} + ); + }); + + it('should shallow reorg chain', async () => { + // Initial state + const res1 = await node.rpc.getNameResource([name]); + assert.strictEqual(res1.records[0].txt[0], '10'); + + // Mempool is empty + assert.strictEqual(node.mempool.map.size, 0); + + // Do not reorg beyond tree interval + assert(node.chain.height % treeInterval === 3); + + // Reorganize + const waiter = new Promise((resolve) => { + node.once('reorganize', () => { + resolve(); + }); + }); + + const depth = 3; + let entry = await node.chain.getEntryByHeight(node.chain.height - depth); + for (let i = 0; i <= depth; i++) { + const block = await node.miner.cpu.mineBlock(entry); + entry = await node.chain.add(block); + } + await waiter; + + // State after reorg + const res2 = await node.rpc.getNameResource([name]); + assert.strictEqual(res2.records[0].txt[0], '7'); + + // Mempool is NOT empty, "next" tx is waiting + assert.strictEqual(node.mempool.map.size, 1); + const tx = Array.from(node.mempool.map.values())[0].tx; + const res3 = Resource.decode(tx.outputs[0].covenant.items[2]); + assert.strictEqual(res3.records[0].txt[0], '8'); + + // This next block would be invalid in our own chain + // if mempool was corrupted with the wrong tx from the reorg. + await node.rpc.generate([1]); + + // State after new block + const res4 = await node.rpc.getNameResource([name]); + assert.strictEqual(res4.records[0].txt[0], '8'); + }); + + it('should deep reorg chain', async () => { + // Initial state + const res1 = await node.rpc.getNameResource([name]); + assert.strictEqual(res1.records[0].txt[0], '8'); + + // Mempool is empty + assert.strictEqual(node.mempool.map.size, 0); + + // Reorganize beyond tree interval + const waiter = new Promise((resolve) => { + node.once('reorganize', () => { + resolve(); + }); + }); + + const depth = 5; + let entry = await node.chain.getEntryByHeight(node.chain.height - depth); + // Intentionally forking from historical tree interval requires dirty hack + const {treeRoot} = await node.chain.getEntryByHeight(node.chain.height - depth + 1); + await node.chain.db.tree.inject(treeRoot); + for (let i = 0; i <= depth; i++) { + const block = await node.miner.cpu.mineBlock(entry); + entry = await node.chain.add(block); + } + await waiter; + + // State after reorg + const res2 = await node.rpc.getNameResource([name]); + assert.strictEqual(res2.records[0].txt[0], '7'); + + // Mempool is NOT empty, "next" tx is waiting + assert.strictEqual(node.mempool.map.size, 1); + const tx = Array.from(node.mempool.map.values())[0].tx; + const res3 = Resource.decode(tx.outputs[0].covenant.items[2]); + assert.strictEqual(res3.records[0].txt[0], '8'); + + // This next block would be invalid in our own chain + // if mempool was corrupted with the wrong tx from the reorg. + await node.rpc.generate([1]); + + // State after new block + const res4 = await node.rpc.getNameResource([name]); + assert.strictEqual(res4.records[0].txt[0], '8'); + }); + + it('should not remove child with no name covenants', async () => { + // Clear + await wallet.zap(0, 0); + + // Find the change output of last UPDATE + const {owner} = await node.chain.db.getNameStateByName(name); + const {height, tx} = await wallet.getTX(owner.hash); + assert.strictEqual(tx.outputs[0].covenant.type, 7); // UPDATE + assert.strictEqual(tx.outputs[1].covenant.type, 0); // NONE + + // We will send 2 TXs, wait for both to enter mempool + const waiter = new Promise((resolve) => { + let count = 0; + + node.mempool.on('tx', () => { + if (++count === 2) + resolve(); + }); + }); + + // Spend the change without covenant + const coin = Coin.fromTX(tx, 1, height); + const addr = new Address({ + version: 0, + hash: Buffer.alloc(20, 0xfe) + }); + const childNone = new MTX(); + childNone.addCoin(coin); + childNone.addOutput(addr, coin.value - 10000); + await wallet.sendMTX(childNone); + + // Also send another UPDATE + const childUpdate = await wallet.sendUpdate(name, makeResource()); + + // Both should be in mempool + await waiter; + assert.strictEqual(node.mempool.map.size, 2); + assert(node.mempool.getTX(childNone.hash())); + assert(node.mempool.getTX(childUpdate.hash())); + + // Reorg, from some other miner with an empty mempool + node.miner.mempool = null; + const depth = 1; + let entry = await node.chain.getEntryByHeight(node.chain.height - depth); + for (let i = 0; i <= depth; i++) { + const block = await node.miner.cpu.mineBlock(entry); + entry = await node.chain.add(block); + } + + // State after reorg + const res1 = await node.rpc.getNameResource([name]); + assert.strictEqual(res1.records[0].txt[0], '7'); + assert.strictEqual(node.mempool.map.size, 2); + + // The last confirmed name update is now back in mempool + assert(node.mempool.getTX(tx.hash())); + // The new change-spend is fine even though it's parent is also in mempool + assert(node.mempool.getTX(childNone.hash())); + // The new update TX is completely gone + assert(!node.mempool.getTX(childUpdate.hash())); + + // Everything should be valid in next block + node.miner.mempool = node.mempool; + await node.rpc.generate([1]); + assert.strictEqual(node.mempool.map.size, 0); + // State after new block + const res2 = await node.rpc.getNameResource([name]); + assert.strictEqual(res2.records[0].txt[0], '8'); + }); +}); diff --git a/test/mempool-test.js b/test/mempool-test.js index 28bdcf1e20..291eba66a4 100644 --- a/test/mempool-test.js +++ b/test/mempool-test.js @@ -5,6 +5,7 @@ const assert = require('bsert'); const random = require('bcrypto/lib/random'); +const Network = require('../lib/protocol/network'); const MempoolEntry = require('../lib/mempool/mempoolentry'); const Mempool = require('../lib/mempool/mempool'); const WorkerPool = require('../lib/workers/workerpool'); @@ -36,13 +37,14 @@ const ownership = require('../lib/covenants/ownership'); const ONE_HASH = Buffer.alloc(32, 0x00); ONE_HASH[0] = 0x01; +const network = Network.get('regtest'); const workers = new WorkerPool({ enabled: true }); const chain = new Chain({ - network: 'regtest', memory: true, + network, workers }); @@ -52,7 +54,7 @@ const mempool = new Mempool({ workers }); -const wallet = new MemWallet(); +const wallet = new MemWallet({ network }); let cachedTX = null; @@ -225,6 +227,62 @@ describe('Mempool', function() { })); }); + it('should get spent coins and reflect in coinview', async () => { + const wallet = new MemWallet({ network }); + const addr = wallet.getAddress(); + + const dummyCoin = dummyInput(addr, random.randomBytes(32)); + + const mtx1 = new MTX(); + mtx1.addOutput(wallet.getAddress(), 50000); + mtx1.addCoin(dummyCoin); + + wallet.sign(mtx1); + + const tx1 = mtx1.toTX(); + const coin1 = Coin.fromTX(tx1, 0, -1); + + const mtx2 = new MTX(); + mtx2.addOutput(wallet.getAddress(), 10000); + mtx2.addOutput(wallet.getAddress(), 30000); // 10k fee + mtx2.addCoin(coin1); + + wallet.sign(mtx2); + + const tx2 = mtx2.toTX(); + + await mempool.addTX(tx1); + + { + const view = await mempool.getCoinView(tx2); + const sview = await mempool.getSpentView(tx2); + assert(view.hasEntry(coin1)); + assert(sview.hasEntry(coin1)); + assert.strictEqual(mempool.hasCoin(coin1.hash, coin1.index), true); + assert.strictEqual(mempool.isSpent(coin1.hash, coin1.index), false); + } + + await mempool.addTX(tx2); + + { + const view = await mempool.getCoinView(tx1); + const sview = await mempool.getSpentView(tx1); + assert(!view.hasEntry(dummyCoin)); + assert(sview.hasEntry(dummyCoin)); + assert.strictEqual(mempool.hasCoin(coin1.hash, coin1.index), false); + assert.strictEqual(mempool.isSpent(coin1.hash, coin1.index), true); + } + + { + const view = await mempool.getCoinView(tx2); + const sview = await mempool.getSpentView(tx2); + assert(!view.hasEntry(coin1)); + assert(sview.hasEntry(coin1)); + assert.strictEqual(mempool.hasCoin(coin1.hash, coin1.index), false); + assert.strictEqual(mempool.isSpent(coin1.hash, coin1.index), true); + } + }); + it('should handle locktime', async () => { const key = KeyRing.generate(); const addr = key.getAddress(); @@ -416,7 +474,7 @@ describe('Mempool', function() { const chain = new Chain({ memory: true, workers, - network: 'regtest' + network }); const mempool = new Mempool({ @@ -425,6 +483,8 @@ describe('Mempool', function() { memory: true }); + const wallet = new MemWallet({ network }); + const COINBASE_MATURITY = mempool.network.coinbaseMaturity; const TREE_INTERVAL = mempool.network.names.treeInterval; mempool.network.names.auctionStart = 0; @@ -444,9 +504,7 @@ describe('Mempool', function() { // Number of coins available in // chaincoins (100k satoshi per coin). const N = 100; - const chaincoins = new MemWallet({ - network: 'regtest' - }); + const chaincoins = new MemWallet({ network }); chain.on('block', (block, entry) => { chaincoins.addBlock(entry, block.txs); @@ -508,18 +566,18 @@ describe('Mempool', function() { } const cb = mtx.toTX(); - const [block] = await getMockBlock(chain, [cb], false); + const [block, view] = await getMockBlock(chain, [cb], false); const entry = await chain.add(block, VERIFY_BODY); - await mempool._addBlock(entry, block.txs); + await mempool.addBlock(entry, block.txs, view); // Add 100 blocks so we don't get // premature spend of coinbase. for (let i = 0; i < 100; i++) { - const [block] = await getMockBlock(chain); + const [block, view] = await getMockBlock(chain); const entry = await chain.add(block, VERIFY_BODY); - await mempool._addBlock(entry, block.txs); + await mempool.addBlock(entry, block.txs, view); } chaincoins.addTX(cb); @@ -580,8 +638,66 @@ describe('Mempool', function() { // Ensure mempool contents are valid in next block const [newBlock, newView] = await getMockBlock(chain, [tx1, tx2]); const newEntry = await chain.add(newBlock, VERIFY_BODY); + await mempool.addBlock(newEntry, newBlock.txs, newView); + assert.strictEqual(mempool.map.size, 0); + }); + + it('should insert resolved orphan tx after parent confirmed', async () => { + await mempool.reset(); + // Mempool is empty + assert.strictEqual(mempool.map.size, 0); + // No orphans either + assert.strictEqual(mempool.waiting.size, 0); + assert.strictEqual(mempool.orphans.size, 0); + + // Create first TX + const coin1 = chaincoins.getCoins()[0]; + const addr = wallet.createReceive().getAddress(); + const mtx1 = new MTX(); + mtx1.addCoin(coin1); + mtx1.addOutput(addr, 90000); + chaincoins.sign(mtx1); + const tx1 = mtx1.toTX(); + chaincoins.addTX(tx1); + wallet.addTX(tx1); + + // Create second TX, spending output of first + const mtx2 = new MTX(); + mtx2.addTX(tx1, 0); + mtx2.addOutput(addr, 80000); + wallet.sign(mtx2); + const tx2 = mtx2.toTX(); + chaincoins.addTX(tx2); + wallet.addTX(tx2); + + // Attempt to add second TX to mempool + await mempool.addTX(tx2); + + // tx2 is orphan waiting on tx1 + assert.strictEqual(mempool.map.size, 0); + assert.strictEqual(mempool.waiting.size, 1); + assert.strictEqual(mempool.orphans.size, 1); + assert(mempool.waiting.has(tx1.hash())); + assert(mempool.orphans.has(tx2.hash())); + + // Confirm tx1 in a block + const [block, view] = await getMockBlock(chain, [tx1], true); + const entry = await chain.add(block, VERIFY_BODY); + await mempool.addBlock(entry, block.txs, view); + + // tx2 has been resolved back in to mempool + assert.strictEqual(mempool.map.size, 1); + assert.strictEqual(mempool.waiting.size, 0); + assert.strictEqual(mempool.orphans.size, 0); + assert(mempool.map.has(tx2.hash())); + + // Ensure mempool contents are valid in next block + const [newBlock, newView] = await getMockBlock(chain, [tx2]); + const newEntry = await chain.add(newBlock, VERIFY_BODY); await mempool._addBlock(newEntry, newBlock.txs, newView); assert.strictEqual(mempool.map.size, 0); + assert.strictEqual(mempool.waiting.size, 0); + assert.strictEqual(mempool.orphans.size, 0); }); it('should handle reorg: coinbase spends', async () => { @@ -600,7 +716,7 @@ describe('Mempool', function() { // Add it to block and mempool const [block1, view1] = await getMockBlock(chain, [cb], false); const entry1 = await chain.add(block1, VERIFY_BODY); - await mempool._addBlock(entry1, block1.txs, view1); + await mempool.addBlock(entry1, block1.txs, view1); // The coinbase output is a valid UTXO in the chain assert(await chain.getCoin(cb.hash(), 0)); @@ -631,7 +747,7 @@ describe('Mempool', function() { [block2, view2] = await getMockBlock(chain); entry2 = await chain.add(block2, VERIFY_BODY); - await mempool._addBlock(entry2, block2.txs, view2); + await mempool.addBlock(entry2, block2.txs, view2); } // Try again @@ -644,7 +760,7 @@ describe('Mempool', function() { // Confirm coinbase spend in a block const [block3, view3] = await getMockBlock(chain, [spend]); const entry3 = await chain.add(block3, VERIFY_BODY); - await mempool._addBlock(entry3, block3.txs, view3); + await mempool.addBlock(entry3, block3.txs, view3); // Coinbase spend has been removed from the mempool assert.strictEqual(mempool.map.size, 0); @@ -694,7 +810,7 @@ describe('Mempool', function() { // Add it to block and mempool const [block1, view1] = await getMockBlock(chain, [fund]); const entry1 = await chain.add(block1, VERIFY_BODY); - await mempool._addBlock(entry1, block1.txs, view1); + await mempool.addBlock(entry1, block1.txs, view1); // The fund TX output is a valid UTXO in the chain const spendCoin = await chain.getCoin(fund.hash(), 0); @@ -721,7 +837,7 @@ describe('Mempool', function() { // Confirm spend into block const [block2, view2] = await getMockBlock(chain, [spend]); const entry2 = await chain.add(block2, VERIFY_BODY); - await mempool._addBlock(entry2, block2.txs, view2); + await mempool.addBlock(entry2, block2.txs, view2); // Spend has been removed from the mempool assert.strictEqual(mempool.map.size, 0); @@ -759,7 +875,7 @@ describe('Mempool', function() { // Ensure mempool contents are valid in next block const [newBlock, newView] = await getMockBlock(chain, [fund]); const newEntry = await chain.add(newBlock, VERIFY_BODY); - await mempool._addBlock(newEntry, newBlock.txs, newView); + await mempool.addBlock(newEntry, newBlock.txs, newView); assert.strictEqual(mempool.map.size, 0); }); @@ -789,7 +905,7 @@ describe('Mempool', function() { // Add it to block and mempool const [block1, view1] = await getMockBlock(chain, [open]); const entry1 = await chain.add(block1, VERIFY_BODY); - await mempool._addBlock(entry1, block1.txs, view1); + await mempool.addBlock(entry1, block1.txs, view1); // The open TX output is a valid UTXO in the chain assert(await chain.getCoin(open.hash(), 0)); @@ -836,7 +952,7 @@ describe('Mempool', function() { [block2, view2] = await getMockBlock(chain); entry2 = await chain.add(block2, VERIFY_BODY); - await mempool._addBlock(entry2, block2.txs, view2); + await mempool.addBlock(entry2, block2.txs, view2); } // BIDDING is activated in the next block @@ -857,7 +973,7 @@ describe('Mempool', function() { // Confirm bid into block const [block3, view3] = await getMockBlock(chain, [bid]); const entry3 = await chain.add(block3, VERIFY_BODY); - await mempool._addBlock(entry3, block3.txs, view3); + await mempool.addBlock(entry3, block3.txs, view3); // Bid has been removed from the mempool assert.strictEqual(mempool.map.size, 0); @@ -957,7 +1073,7 @@ describe('Mempool', function() { try { ownership.ignore = true; entry2 = await chain.add(block2, VERIFY_BODY); - await mempool._addBlock(entry2, block2.txs, view2); + await mempool.addBlock(entry2, block2.txs, view2); } finally { ownership.ignore = false; } @@ -1022,7 +1138,7 @@ describe('Mempool', function() { [block2, view2] = await getMockBlock(chain); entry2 = await chain.add(block2, VERIFY_BODY); - await mempool._addBlock(entry2, block2.txs, view2); + await mempool.addBlock(entry2, block2.txs, view2); } // Update the claim with a *very recent* block commitment @@ -1063,7 +1179,7 @@ describe('Mempool', function() { try { ownership.ignore = true; entry3 = await chain.add(block3, VERIFY_BODY); - await mempool._addBlock(entry3, block3.txs, view3); + await mempool.addBlock(entry3, block3.txs, view3); } finally { ownership.ignore = false; } @@ -1122,7 +1238,7 @@ describe('Mempool', function() { const chain = new Chain({ memory: true, workers, - network: 'regtest' + network }); const mempool = new Mempool({ @@ -1148,8 +1264,8 @@ describe('Mempool', function() { // Number of coins available in // chaincoins (100k satoshi per coin). const N = 100; - const chaincoins = new MemWallet(); - const wallet = new MemWallet(); + const chaincoins = new MemWallet({ network }); + const wallet = new MemWallet({ network }); async function getMockBlock(chain, txs = [], cb = true) { if (cb) { @@ -1195,18 +1311,18 @@ describe('Mempool', function() { } const cb = mtx.toTX(); - const [block] = await getMockBlock(chain, [cb], false); + const [block, view] = await getMockBlock(chain, [cb], false); const entry = await chain.add(block, VERIFY_BODY); - await mempool._addBlock(entry, block.txs); + await mempool.addBlock(entry, block.txs, view); // Add 100 blocks so we don't get // premature spend of coinbase. for (let i = 0; i < 100; i++) { - const [block] = await getMockBlock(chain); + const [block, view] = await getMockBlock(chain); const entry = await chain.add(block, VERIFY_BODY); - await mempool._addBlock(entry, block.txs); + await mempool.addBlock(entry, block.txs, view); } chaincoins.addTX(cb); diff --git a/test/wallet-auction-test.js b/test/wallet-auction-test.js index 8f1b11b4df..47a19d1da5 100644 --- a/test/wallet-auction-test.js +++ b/test/wallet-auction-test.js @@ -64,6 +64,10 @@ describe('Wallet Auction', function() { await wdb.removeBlock(entry); }); + wdb.getNameStatus = async (nameHash) => { + return chain.db.getNameStatus(nameHash, chain.height + 1); + }; + // Generate blocks to roll out name and fund wallet let winnerAddr = await wallet.createReceive(); winnerAddr = winnerAddr.getAddress().toString(network);