diff --git a/lib/mining/template.js b/lib/mining/template.js index 57bd8033d..eaa475abc 100644 --- a/lib/mining/template.js +++ b/lib/mining/template.js @@ -654,7 +654,7 @@ class BlockEntry { item.fee = entry.getFee(); item.rate = entry.getDeltaRate(); item.priority = entry.getPriority(attempt.height); - item.free = entry.getDeltaFee() < policy.getMinFee(entry.size); + item.free = entry.descFee < policy.getMinFee(entry.descSize); item.sigops = entry.sigops; item.descRate = entry.getDescRate(); return item; diff --git a/test/miner-test.js b/test/miner-test.js index 40afc8aa0..d7e6a98e9 100644 --- a/test/miner-test.js +++ b/test/miner-test.js @@ -9,6 +9,7 @@ const Mempool = require('../lib/mempool/mempool'); const Miner = require('../lib/mining/miner'); const Address = require('../lib/primitives/address'); const MTX = require('../lib/primitives/mtx'); +const Coin = require('../lib/primitives/coin'); const MemWallet = require('./util/memwallet'); const {BufferSet} = require('buffer-map'); @@ -77,6 +78,7 @@ describe('Miner', function() { let walletAddr; const txids = new BufferSet(); + let coin, parentTX; it('should generate 20 blocks to wallet address', async () => { walletAddr = wallet.createReceive().getAddress(); @@ -110,7 +112,6 @@ describe('Miner', function() { } assert.strictEqual(mempool.map.size, 10); - const block = await miner.mineBlock(chain.tip, addr); await chain.add(block); @@ -180,4 +181,81 @@ describe('Miner', function() { assert(txids.has(block.txs[i].hash())); } }); + + it('should not include free transaction in a block', async () => { + // Miner does not have any room for free TXs + miner.options.minWeight = 0; + + const value = 1 * 1e6; + const fee = 0; + + // Get a change address + const change = wallet.createChange().getAddress(); + const mtx = new MTX(); + coin = wallet.getCoins()[0]; + mtx.addCoin(coin); + mtx.addOutput(addr, value); + mtx.addOutput(change, coin.value - value - fee); // no fee + wallet.sign(mtx); + parentTX = mtx.toTX(); + + await mempool.addTX(parentTX, -1); + assert.strictEqual(mempool.map.size, 1); + + const block = await miner.mineBlock(chain.tip, addr); + await chain.add(block); + + // TX is still in mempool, nothing in block except coinbase + assert.strictEqual(mempool.map.size, 1); + assert.strictEqual(block.txs.length, 1); + }); + + it('should fail to double spend the coin - duplicate tx in mempool', async () => { + const value = 1 * 1e6; + const fee = 1000; + const mtx = new MTX(); + + const change = wallet.createChange().getAddress(); + mtx.addCoin(coin); + mtx.addOutput(addr, value); + mtx.addOutput(change, coin.value - value - fee); + wallet.sign(mtx); + const tx = mtx.toTX(); + + assert.rejects( + async () => await mempool.addTX(tx, -1), + { + code: 'duplicate', + reason: 'bad-txns-inputs-spent' + } + ); + // orignal tx is still in mempool + assert.strictEqual(mempool.map.size, 1); + }); + + it('should include child transaction if child pays enough fee (CPFP)', async () => { + const fee = 1000; + + // Fee should be enough for both the first transation and second transaction + assert(fee > 140 + 108); + + const mtx = new MTX(); + const change = wallet.createChange().getAddress(); + const coin = Coin.fromTX(parentTX, 1, -1); + + mtx.addCoin(coin); + mtx.addOutput(change, coin.value - fee); + wallet.sign(mtx); + const tx = mtx.toTX(); + await mempool.addTX(tx, -1); + // Both transactions in mempool + assert.strictEqual(mempool.map.size, 2); + + const block = await miner.mineBlock(chain.tip, addr); + await chain.add(block); + + // Both transactions should get mined into the block + assert.strictEqual(mempool.map.size, 0); + assert.strictEqual(block.txs.length, 3); + }); });