212 lines
5.1 KiB
JavaScript
212 lines
5.1 KiB
JavaScript
'use strict';
|
|
|
|
const assert = require('assert');
|
|
const fs = require('../../lib/utils/fs');
|
|
const Block = require('../../lib/primitives/block');
|
|
const MerkleBlock = require('../../lib/primitives/merkleblock');
|
|
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');
|
|
|
|
exports.parseTX = function parseTX(name) {
|
|
const data = fs.readFileSync(`${__dirname}/../data/${name}.hex`, 'utf8');
|
|
const parts = data.trim().split('\n');
|
|
const raw = Buffer.from(parts[0], 'hex');
|
|
const tx = TX.fromRaw(raw);
|
|
const view = new CoinView();
|
|
const txs = [tx];
|
|
|
|
for (let i = 1; i < parts.length; i++) {
|
|
const raw = Buffer.from(parts[i], 'hex');
|
|
const prev = TX.fromRaw(raw);
|
|
view.addTX(prev, -1);
|
|
txs.push(prev);
|
|
}
|
|
|
|
return {
|
|
tx: tx,
|
|
view: view,
|
|
txs: txs
|
|
};
|
|
};
|
|
|
|
exports.readBlock = function readBlock(name) {
|
|
const height = name.substring(5);
|
|
const blockFile = `${__dirname}/../data/block${height}.raw`;
|
|
|
|
if (!fs.existsSync(blockFile)) {
|
|
const raw = fs.readFileSync(`${__dirname}/../data/${name}.raw`);
|
|
const block = Block.fromRaw(raw);
|
|
const view = new CoinView();
|
|
return { raw, block, view };
|
|
}
|
|
|
|
const raw = fs.readFileSync(blockFile);
|
|
const block = Block.fromRaw(raw);
|
|
|
|
const undoFile = `${__dirname}/../data/undo${height}.raw`;
|
|
|
|
if (!fs.existsSync(undoFile)) {
|
|
const view = new CoinView();
|
|
return { raw, block, view };
|
|
}
|
|
|
|
const undoRaw = fs.readFileSync(undoFile);
|
|
const undo = exports.parseUndo(undoRaw);
|
|
const view = exports.applyBlockUndo(block, undo);
|
|
|
|
return { raw, block, view };
|
|
};
|
|
|
|
exports.readMerkle = function readMerkle(name) {
|
|
const raw = fs.readFileSync(`${__dirname}/../data/${name}.raw`);
|
|
const block = MerkleBlock.fromRaw(raw);
|
|
return { raw, block };
|
|
};
|
|
|
|
exports.readCompact = function readCompact(name) {
|
|
const raw = fs.readFileSync(`${__dirname}/../data/${name}.raw`);
|
|
const block = CompactBlock.fromRaw(raw);
|
|
return { raw, block };
|
|
};
|
|
|
|
exports.readTX = function readTX(name) {
|
|
const index = name.substring(2);
|
|
const txFile = `${__dirname}/../data/tx${index}.raw`;
|
|
|
|
if (!fs.existsSync(txFile)) {
|
|
const raw = fs.readFileSync(`${__dirname}/../data/${name}.raw`);
|
|
const tx = TX.fromRaw(raw);
|
|
const view = new CoinView();
|
|
return { raw, tx, view };
|
|
}
|
|
|
|
const raw = fs.readFileSync(txFile);
|
|
const tx = TX.fromRaw(raw);
|
|
|
|
const undoFile = `${__dirname}/../data/utx${index}.raw`;
|
|
|
|
if (!fs.existsSync(undoFile)) {
|
|
const view = new CoinView();
|
|
return { raw, tx, view };
|
|
}
|
|
|
|
const undoRaw = fs.readFileSync(undoFile);
|
|
const undo = exports.parseUndo(undoRaw);
|
|
const view = exports.applyTXUndo(tx, undo);
|
|
|
|
return { raw, tx, view };
|
|
};
|
|
|
|
exports.parseUndo = function parseUndo(data) {
|
|
const br = new BufferReader(data);
|
|
const items = [];
|
|
|
|
while (br.left()) {
|
|
const output = Output.fromReader(br);
|
|
items.push(output);
|
|
}
|
|
|
|
return items;
|
|
};
|
|
|
|
exports.applyBlockUndo = 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;
|
|
};
|
|
|
|
exports.applyTXUndo = 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;
|
|
};
|
|
|
|
exports.makeBlockUndo = 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;
|
|
};
|
|
|
|
exports.makeTXUndo = function makeTXUndo(tx, view) {
|
|
const items = [];
|
|
|
|
for (const {prevout} of tx.inputs) {
|
|
const coin = view.getOutput(prevout);
|
|
assert(coin);
|
|
items.push(coin);
|
|
}
|
|
|
|
return items;
|
|
};
|
|
|
|
exports.serializeUndo = function serializeUndo(items) {
|
|
const bw = new BufferWriter();
|
|
|
|
for (const item of items) {
|
|
bw.writeI64(item.value);
|
|
bw.writeVarBytes(item.script.toRaw());
|
|
}
|
|
|
|
return bw.render();
|
|
};
|
|
|
|
exports.writeBlock = function writeBlock(name, block, view) {
|
|
const height = name.substring(5);
|
|
|
|
fs.writeFileSync(`${__dirname}/../data/block${height}.raw`, block.toRaw());
|
|
|
|
if (!view)
|
|
return;
|
|
|
|
const undo = exports.makeBlockUndo(block, view);
|
|
const undoRaw = exports.serializeUndo(undo);
|
|
|
|
fs.writeFileSync(`${__dirname}/../data/undo${height}.raw`, undoRaw);
|
|
};
|
|
|
|
exports.writeTX = function writeTX(name, tx, view) {
|
|
const index = name.substring(2);
|
|
|
|
fs.writeFileSync(`${__dirname}/../data/tx${index}.raw`, tx.toRaw());
|
|
|
|
if (!view)
|
|
return;
|
|
|
|
const undo = exports.makeTXUndo(tx, view);
|
|
const undoRaw = exports.serializeUndo(undo);
|
|
|
|
fs.writeFileSync(`${__dirname}/../data/utx${index}.raw`, undoRaw);
|
|
};
|