|
| 1 | +// Hint generation + reconstruction. |
| 2 | +// |
| 3 | +// generateHints: walk a contiguous range, build the UTXO set at the end, then for |
| 4 | +// each block list the block-local output indices that remain unspent. The index |
| 5 | +// counts ALL outputs in transaction order (so a verifier walking outputs aligns); |
| 6 | +// OP_RETURN outputs get an index but are never in the UTXO set, so never in a |
| 7 | +// hint. These per-block index lists are what hintsfile.js Elias-Fano encodes. |
| 8 | +// |
| 9 | +// reconstructUtxo: the assumevalid verifier side — rebuild the UTXO set purely |
| 10 | +// from the blocks + the hint bitmap (no input processing). With the matching |
| 11 | +// accumulator check (validate.js), this is SwiftSync: fast set construction from |
| 12 | +// hints, verified against a trusted commitment. |
| 13 | +// |
| 14 | +// Engine-agnostic: caller injects txidOf(tx). |
| 15 | + |
| 16 | +const NULL_TXID = '00'.repeat(32); |
| 17 | +const isOpReturn = (spk) => typeof spk === 'string' && spk.startsWith('6a'); |
| 18 | + |
| 19 | +export function generateHints(blocks, { txidOf }) { |
| 20 | + const utxo = new Set(); |
| 21 | + for (const block of blocks) for (const tx of block.transactions) { |
| 22 | + const txid = txidOf(tx); |
| 23 | + for (let v = 0; v < tx.outputs.length; v++) if (!isOpReturn(tx.outputs[v].scriptPubKey)) utxo.add(txid + ':' + v); |
| 24 | + for (const inp of tx.inputs) if (inp.prevout.txid !== NULL_TXID) utxo.delete(inp.prevout.txid + ':' + inp.prevout.vout); |
| 25 | + } |
| 26 | + const blockHints = []; |
| 27 | + for (const block of blocks) { |
| 28 | + const idx = []; let i = 0; |
| 29 | + for (const tx of block.transactions) { |
| 30 | + const txid = txidOf(tx); |
| 31 | + for (let v = 0; v < tx.outputs.length; v++) { if (utxo.has(txid + ':' + v)) idx.push(i); i++; } |
| 32 | + } |
| 33 | + blockHints.push(idx); |
| 34 | + } |
| 35 | + return { height: blocks.length, blockHints }; |
| 36 | +} |
| 37 | + |
| 38 | +export function reconstructUtxo(blocks, blockHints, { txidOf }) { |
| 39 | + const utxo = new Set(); |
| 40 | + for (let b = 0; b < blocks.length; b++) { |
| 41 | + const want = new Set(blockHints[b]); let i = 0; |
| 42 | + for (const tx of blocks[b].transactions) { |
| 43 | + const txid = txidOf(tx); |
| 44 | + for (let v = 0; v < tx.outputs.length; v++) { if (want.has(i)) utxo.add(txid + ':' + v); i++; } |
| 45 | + } |
| 46 | + } |
| 47 | + return utxo; |
| 48 | +} |
0 commit comments