itns-sidechain/test/txstart-test.js
2024-05-31 13:03:39 +04:00

239 lines
7.6 KiB
JavaScript

'use strict';
const assert = require('bsert');
const fs = require('fs');
const {resolve} = require('path');
const FullNode = require('../lib/node/fullnode');
const TX = require('../lib/primitives/tx');
const Input = require('../lib/primitives/input');
const Output = require('../lib/primitives/output');
const MemWallet = require('./util/memwallet');
const AirdropProof = require('../lib/primitives/airdropproof');
const Block = require('../lib/primitives/block');
const Address = require('../lib/primitives/address');
const Script = require('../lib/script/script');
const common = require('../lib/blockchain/common');
const {ownership} = require('../lib/covenants/ownership');
const {CachedStubResolver, STUB_SERVERS} = require('./util/stub');
const VERIFY_NONE = common.flags.VERIFY_NONE;
const node = new FullNode({
memory: true,
network: 'regtest'
});
const RESET_TXSTART = node.network.txStart;
// Ease up that mempool
node.mempool.options.minRelay = 0;
node.mempool.options.rejectAbsurdFees = false;
node.miner.options.minWeight = 1000000;
// Use a valid, working airdrop proof
const FAUCET_PROOF_FILE = resolve(__dirname, 'data', 'faucet-proof.base64');
const raw = Buffer.from(fs.readFileSync(FAUCET_PROOF_FILE, 'binary'), 'base64');
const proof = AirdropProof.decode(raw);
// We only need this for the fakeClaim
const wallet = new MemWallet({network: 'regtest'});
node.chain.on('connect', (entry, block) => {
wallet.addBlock(entry, block.txs);
});
wallet.getNameStatus = async (nameHash) => {
assert(Buffer.isBuffer(nameHash));
const height = node.chain.height + 1;
return node.chain.db.getNameStatus(nameHash, height);
};
describe('Disable TXs', function() {
this.timeout(20000);
let utxo, lastTX;
const originalResolver = ownership.Resolver;
const originalServers = ownership.servers;
before(async () => {
ownership.Resolver = CachedStubResolver;
ownership.servers = STUB_SERVERS;
node.network.txStart = 5;
await node.open();
// Start with one block for the fakeClaim
const block = await node.miner.mineBlock();
assert(await node.chain.add(block));
});
after(async () => {
node.network.txStart = RESET_TXSTART;
ownership.Resolver = originalResolver;
ownership.servers = originalServers;
await node.close();
});
it('should reject tx from mempool before txStart', async () => {
const tx = new TX({
inputs: [new Input()],
outputs: [new Output()]
});
tx.inputs[0].prevout.hash = Buffer.alloc(32, 0x01);
await assert.rejects(node.mempool.addTX(tx),
{reason: 'no-tx-allowed-yet'});
});
it('should reject claim from mempool before txStart', async () => {
this.timeout(10000);
const claim = await wallet.fakeClaim('cloudflare');
try {
ownership.ignore = true;
await assert.rejects(node.mempool.addClaim(claim),
{reason: 'no-tx-allowed-yet'});
} finally {
ownership.ignore = false;
}
});
it('should reject airdrop from mempool before txStart', async () => {
await assert.rejects(node.mempool.addAirdrop(proof),
{reason: 'no-tx-allowed-yet'});
});
it('should reject block with >1 coinbase output before txStart', async () => {
const tx1 = new TX({
inputs: [new Input()],
outputs: [new Output(), new Output()]
});
tx1.locktime = node.chain.height + 1;
const block = new Block();
block.txs.push(tx1);
block.prevBlock = node.chain.tip.hash;
block.time = node.chain.tip.time + 1;
block.bits = await node.chain.getTarget(block.time, node.chain.tip);
await assert.rejects(node.chain.add(block, VERIFY_NONE),
{reason: 'no-tx-allowed-yet'});
assert(node.chain.hasInvalid(block));
});
it('should reject non-empty block before txStart', async () => {
const tx1 = new TX({
inputs: [new Input()],
outputs: [new Output()]
});
tx1.locktime = node.chain.height + 1;
const tx2 = new TX({
inputs: [new Input()],
outputs: [new Output()]
});
const block = new Block();
block.txs.push(tx1);
block.txs.push(tx2);
block.prevBlock = node.chain.tip.hash;
block.time = node.chain.tip.time + 2;
block.bits = await node.chain.getTarget(block.time, node.chain.tip);
await assert.rejects(node.chain.add(block, VERIFY_NONE),
{reason: 'no-tx-allowed-yet'});
assert(node.chain.hasInvalid(block));
});
it('should accept empty block before txStart', async () => {
// Create an address that takes literally nothing to spend
const addr = Address.fromScript(new Script());
const tx1 = new TX({
inputs: [new Input()],
outputs: [new Output()]
});
tx1.outputs[0].address = addr;
tx1.locktime = node.chain.height + 1;
const block = new Block();
block.txs.push(tx1);
block.prevBlock = node.chain.tip.hash;
block.time = node.chain.tip.time + 3;
block.bits = await node.chain.getTarget(block.time, node.chain.tip);
assert(await node.chain.add(block, VERIFY_NONE));
// Spend this output later
utxo = block.txs[0].hash();
});
it('should add blocks until one block before txStart', async() => {
for (let i = node.chain.height; i < node.network.txStart - 1; i++) {
const block = await node.miner.mineBlock();
assert(await node.chain.add(block));
}
// The NEXT block is the txStart height
assert.strictEqual(node.chain.height + 1, node.network.txStart);
});
it('should allow tx in mempool one block before txStart', async () => {
lastTX = new TX({
inputs: [new Input()],
outputs: [new Output()]
});
lastTX.inputs[0].prevout.hash = utxo;
lastTX.inputs[0].prevout.index = 0;
lastTX.inputs[0].witness.items[0] = Buffer.from([1, 1]); // true
lastTX.inputs[0].witness.items[1] = Buffer.alloc(0); // empty script
await node.mempool.addTX(lastTX);
assert.strictEqual(node.mempool.map.size, 1);
assert(node.mempool.has(lastTX.hash()));
assert.strictEqual(node.chain.height + 1, node.network.txStart);
});
it('should allow claim in mempool one block before txStart', async () => {
this.timeout(10000);
const claim = await wallet.fakeClaim('cloudflare');
try {
ownership.ignore = true;
await node.mempool.addClaim(claim);
} finally {
ownership.ignore = false;
}
assert.strictEqual(node.mempool.claims.size, 1);
assert(node.mempool.hasClaim(claim.hash()));
assert.strictEqual(node.chain.height + 1, node.network.txStart);
});
it('should allow airdrop in mempool one block before txStart', async () => {
await node.mempool.addAirdrop(proof);
assert.strictEqual(node.mempool.airdrops.size, 1);
assert(node.mempool.hasAirdrop(proof.hash()));
assert.strictEqual(node.chain.height + 1, node.network.txStart);
});
it('should accept a block full of goodies at txStart', async() => {
const block = await node.miner.mineBlock();
try {
ownership.ignore = true;
assert(await node.chain.add(block));
} finally {
ownership.ignore = false;
}
// This is the first block where transactions are allowed.
assert.strictEqual(node.chain.height, node.network.txStart);
// The first output is the coinbase reward. The second output
// is the claim, and the third output is the airdrop redemption.
assert.strictEqual(block.txs.length, 2);
assert.bufferEqual(block.txs[1].hash(), lastTX.hash());
assert.strictEqual(block.txs[0].outputs.length, 3);
assert(block.txs[0].outputs[0].covenant.isNone());
assert(block.txs[0].outputs[1].covenant.isClaim());
assert(block.txs[0].outputs[2].covenant.isNone());
assert.strictEqual(block.txs[0].outputs[2].value, proof.getValue() - proof.fee);
});
});