'use strict'; const assert = require('assert'); const path = require('path'); const fs = require('../../lib/utils/fs'); const Block = require('../../lib/primitives/block'); const MerkleBlock = require('../../lib/primitives/merkleblock'); const Headers = require('../../lib/primitives/headers'); const {CompactBlock} = require('../../lib/net/bip152'); const TX = require('../../lib/primitives/tx'); const Output = require('../../lib/primitives/output'); const CoinView = require('../../lib/coins/coinview'); const BufferReader = require('../../lib/utils/reader'); const BufferWriter = require('../../lib/utils/writer'); const common = exports; common.readFile = function readFile(name, enc) { const file = path.resolve(__dirname, '..', 'data', name); return fs.readFileSync(file, enc); }; common.writeFile = function writeFile(name, data) { const file = path.resolve(__dirname, '..', 'data', name); return fs.writeFileSync(file, data); }; common.exists = function exists(name) { const file = path.resolve(__dirname, '..', 'data', name); return fs.existsSync(file); }; common.readBlock = function readBlock(name) { const raw = common.readFile(`${name}.raw`); if (!common.exists(`${name}-undo.raw`)) return new BlockContext(Block, raw); const undoRaw = common.readFile(`${name}-undo.raw`); return new BlockContext(Block, raw, undoRaw); }; common.readMerkle = function readMerkle(name) { const raw = common.readFile(`${name}.raw`); return new BlockContext(MerkleBlock, raw); }; common.readCompact = function readCompact(name) { const raw = common.readFile(`${name}.raw`); return new BlockContext(CompactBlock, raw); }; common.readTX = function readTX(name) { const raw = common.readFile(`${name}.raw`); if (!common.exists(`${name}-undo.raw`)) return new TXContext(raw); const undoRaw = common.readFile(`${name}-undo.raw`); return new TXContext(raw, undoRaw); }; common.writeBlock = function writeBlock(name, block, view) { common.writeFile(`${name}.raw`, block.toRaw()); if (!view) return; const undo = makeBlockUndo(block, view); const undoRaw = serializeUndo(undo); common.writeFile(`${name}-undo.raw`, undoRaw); }; common.writeTX = function writeTX(name, tx, view) { common.writeFile(`${name}.raw`, tx.toRaw()); if (!view) return; const undo = makeTXUndo(tx, view); const undoRaw = serializeUndo(undo); common.writeFile(`${name}-undo.raw`, undoRaw); }; function parseUndo(data) { const br = new BufferReader(data); const items = []; while (br.left()) { const output = Output.fromReader(br); items.push(output); } return items; } function serializeUndo(items) { const bw = new BufferWriter(); for (const item of items) { bw.writeI64(item.value); bw.writeVarBytes(item.script.toRaw()); } return bw.render(); } function applyBlockUndo(block, undo) { const view = new CoinView(); let i = 0; for (const tx of block.txs) { if (tx.isCoinbase()) continue; for (const {prevout} of tx.inputs) view.addOutput(prevout, undo[i++]); } assert(i === undo.length, 'Undo coins data inconsistency.'); return view; } function applyTXUndo(tx, undo) { const view = new CoinView(); let i = 0; for (const {prevout} of tx.inputs) view.addOutput(prevout, undo[i++]); assert(i === undo.length, 'Undo coins data inconsistency.'); return view; } function makeBlockUndo(block, view) { const items = []; for (const tx of block.txs) { if (tx.isCoinbase()) continue; for (const {prevout} of tx.inputs) { const coin = view.getOutput(prevout); assert(coin); items.push(coin); } } return items; } function makeTXUndo(tx, view) { const items = []; for (const {prevout} of tx.inputs) { const coin = view.getOutput(prevout); assert(coin); items.push(coin); } return items; } class BlockContext { constructor(ctor, raw, undoRaw) { this.ctor = ctor; this.raw = raw; this.undoRaw = undoRaw || null; } getRaw() { return this.raw; } getBlock() { const Block = this.ctor; const block = Block.fromRaw(this.raw); if (!this.undoRaw) { const view = new CoinView(); return [block, view]; } const undo = parseUndo(this.undoRaw); const view = applyBlockUndo(block, undo); return [block, view]; } getHeaders() { return Headers.fromHead(this.raw); } } class TXContext { constructor(raw, undoRaw) { this.raw = raw; this.undoRaw = undoRaw || null; } getRaw() { return this.raw; } getTX() { const tx = TX.fromRaw(this.raw); if (!this.undoRaw) { const view = new CoinView(); return [tx, view]; } const undo = parseUndo(this.undoRaw); const view = applyTXUndo(tx, undo); return [tx, view]; } }