239 lines
7.6 KiB
JavaScript
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);
|
|
});
|
|
});
|