Skip to content

Commit

Permalink
mempool: invalidate claims when the claim period ends.
Browse files Browse the repository at this point in the history
  • Loading branch information
nodech committed May 24, 2023
1 parent d77707f commit 7f0357b
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 0 deletions.
7 changes: 7 additions & 0 deletions lib/mempool/mempool.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ class Mempool extends EventEmitter {
*/

async _addBlock(block, txs, view) {
const nextHeight = block.height + 1;
const entries = [];
const cb = txs[0];

Expand Down Expand Up @@ -284,6 +285,12 @@ class Mempool extends EventEmitter {
this.untrackClaim(entry);
}

// At the chain.tip.height of claimPeriod - 1, we no longer
// accept new claims as they will be invalid in the next block.
// We also need to clean them up from the mempool.
if (nextHeight === this.network.names.claimPeriod)
this.dropClaims();

// Remove all GooSig based airdrops from the mempool
// when the block one before the height that disables
// GooSig is added to the mempool to prevent the
Expand Down
165 changes: 165 additions & 0 deletions test/mempool-invalidation-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
'use strict';

const assert = require('bsert');
const {BufferMap} = require('buffer-map');
const Network = require('../lib/protocol/network');
const FullNode = require('../lib/node/fullnode');
const ownership = require('../lib/covenants/ownership');
const rules = require('../lib/covenants/rules');
const {forEvent} = require('./util/common');

const network = Network.get('regtest');
const {
treeInterval,
claimPeriod
} = network.names;

const ACTUAL_CLAIM_PERIOD = claimPeriod;

describe('Mempool Invalidation', function() {
const NAMES = [
// roots
'nl',

// top 100
'paypal',

// custom
'cloudflare',

// other
'steamdb'
];

describe('Claim Invalidation (Integration)', function() {
this.timeout(50000);

let node, wallet;

// copy names
const TEST_CLAIMS = NAMES.slice();

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

await node.ensure();
await node.open();

// Ignore claim validation
ownership.ignore = true;

const walletPlugin = node.require('walletdb');
const wdb = walletPlugin.wdb;
wallet = await wdb.get('primary');

const addr = await wallet.receiveAddress('default');
node.miner.addAddress(addr.toString());

// first interval maturity
// second interval mine claim
network.names.claimPeriod = treeInterval * 3;

// third interval last block should invalidate.
});

after(async () => {
network.names.claimPeriod = ACTUAL_CLAIM_PERIOD;

await node.close();
});

it('should mine an interval', async () => {
for (let i = 0; i < treeInterval; i++)
await mineBlock(node);
});

it('should mine claims before claimPeriod timeout', async () => {
const name = TEST_CLAIMS.shift();

const claim = await wallet.makeFakeClaim(name);
let block;

await node.mempool.insertClaim(claim);
assert.strictEqual(node.mempool.claims.size, 1);

// retain claim in mempool.
block = await mineBlock(node, { ignoreClaims: true });
assert.strictEqual(node.mempool.claims.size, 1);
assert.strictEqual(block.txs[0].outputs.length, 1);

// Now we can mine it.
block = await mineBlock(node);
assert.strictEqual(node.mempool.claims.size, 0);
assert.strictEqual(block.txs[0].outputs.length, 2);
assert.strictEqual(block.txs[0].outputs[1].covenant.type, rules.types.CLAIM);
});

it('should invalidate claim after claimPeriod timeout', async () => {
const name = TEST_CLAIMS.shift();
const claim = await wallet.makeFakeClaim(name);

let block = null;

// Mempool treats txs in it as if they were mined in the next block,
// so we need next block to still be valid.
while (node.chain.tip.height < network.names.claimPeriod - 2)
await mineBlock(node);

await node.mempool.insertClaim(claim);
block = await mineBlock(node, { ignoreClaims: true });

// Should invalidate the claim, because next block can't have claims.
assert.strictEqual(node.mempool.claims.size, 0);
assert.strictEqual(block.txs[0].outputs.length, 1);

// Should fail to insert claim, as they can't be mined.
let err;
try {
err = await node.mempool.insertClaim(claim);
} catch (e) {
err = e;
}

assert(err);
assert.strictEqual(err.type, 'VerifyError');
assert.strictEqual(err.reason, 'invalid-covenant');

block = await mineBlock(node);
assert.strictEqual(node.mempool.claims.size, 0);
assert.strictEqual(block.txs[0].outputs.length, 1);
});
});
});

async function mineBlock(node, opts = {}) {
assert(node);
const chain = node.chain;
const miner = node.miner;

const ignoreClaims = opts.ignoreClaims || false;

const forBlock = forEvent(node, 'block', 1, 2000);

let backupClaims = null;

if (ignoreClaims) {
backupClaims = node.mempool.claims;
node.mempool.claims = new BufferMap();
}
const job = await miner.cpu.createJob(chain.tip);

job.refresh();

if (ignoreClaims)
node.mempool.claims = backupClaims;

const block = await job.mineAsync();
await chain.add(block);
await forBlock;

return block;
}

0 comments on commit 7f0357b

Please sign in to comment.