"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MockMiner = void 0;
exports.createCoinbaseTransaction = createCoinbaseTransaction;
const sdk_1 = require("@bsv/sdk");
const MockChainStorage_1 = require("./MockChainStorage");
const merkleTree_1 = require("./merkleTree");
const Services_1 = require("../services/Services");
const utilityHelpers_1 = require("../utility/utilityHelpers");
const utilityHelpers_noBuffer_1 = require("../utility/utilityHelpers.noBuffer");
/**
 * Creates a coinbase transaction for the given block height.
 * Uses OP_TRUE (0x51) as the output script so anyone can spend it.
 */
function createCoinbaseTransaction(height) {
    const tx = new sdk_1.Transaction();
    // BIP34: height in scriptSig
    // Encode height as a minimally-encoded script number pushed in the unlocking script
    const heightBytes = [];
    let h = height;
    if (h === 0) {
        heightBytes.push(0);
    }
    else {
        while (h > 0) {
            heightBytes.push(h & 0xff);
            h >>= 8;
        }
        // If the high bit is set, add a 0x00 byte to keep it positive
        if (heightBytes[heightBytes.length - 1] & 0x80) {
            heightBytes.push(0);
        }
    }
    const scriptSigBytes = [heightBytes.length, ...heightBytes];
    const unlockingScript = sdk_1.Script.fromBinary(scriptSigBytes);
    tx.addInput({
        sourceTXID: '00'.repeat(32),
        sourceOutputIndex: 0xffffffff,
        unlockingScript,
        sequence: 0xffffffff
    });
    tx.addOutput({
        satoshis: 5000000000,
        lockingScript: sdk_1.Script.fromHex('51') // OP_TRUE
    });
    return tx;
}
class MockMiner {
    /**
     * Mine a new block containing all unmined transactions.
     * Returns the new block header.
     */
    async mineBlock(storage) {
        const tip = await storage.getChainTip();
        const newHeight = tip ? tip.height + 1 : 0;
        const previousHash = tip ? tip.hash : '00'.repeat(32);
        const unminedTxs = await storage.getUnminedTransactions();
        const coinbaseTx = createCoinbaseTransaction(newHeight);
        const coinbaseTxid = coinbaseTx.id('hex');
        const coinbaseRawTx = Array.from(coinbaseTx.toBinary());
        const txids = [coinbaseTxid, ...unminedTxs.map(t => t.txid)];
        const merkleRoot = (0, merkleTree_1.computeMerkleRoot)(txids);
        const time = Math.floor(Date.now() / 1000);
        const bits = 0x207fffff;
        const nonce = Math.floor(Math.random() * 0xffffffff);
        const headerObj = {
            version: 1,
            previousHash,
            merkleRoot,
            time,
            bits,
            nonce
        };
        const headerBinary = (0, Services_1.toBinaryBaseBlockHeader)(headerObj);
        const hash = (0, utilityHelpers_noBuffer_1.asString)((0, utilityHelpers_1.doubleSha256BE)(headerBinary));
        // Compute script hash for the coinbase output (OP_TRUE = 0x51)
        const coinbaseOutputScript = [0x51];
        const coinbaseScriptHash = (0, utilityHelpers_noBuffer_1.asString)((0, utilityHelpers_1.sha256Hash)(coinbaseOutputScript));
        // Wrap in a knex transaction for atomicity
        await storage.knex.transaction(async (trx) => {
            const trxStorage = new MockChainStorage_1.MockChainStorage(trx);
            // Insert coinbase tx
            await trxStorage.knex('mockchain_transactions').insert({
                txid: coinbaseTxid,
                rawTx: Buffer.from(coinbaseRawTx),
                blockHeight: newHeight,
                blockIndex: 0
            });
            // Insert coinbase UTXO
            await trxStorage.knex('mockchain_utxos').insert({
                txid: coinbaseTxid,
                vout: 0,
                lockingScript: Buffer.from(coinbaseOutputScript),
                satoshis: 5000000000,
                scriptHash: coinbaseScriptHash,
                spentByTxid: null,
                isCoinbase: true,
                blockHeight: newHeight
            });
            // Update unmined txs with block height and sequential block index
            for (let i = 0; i < unminedTxs.length; i++) {
                await trxStorage
                    .knex('mockchain_transactions')
                    .where({ txid: unminedTxs[i].txid })
                    .update({ blockHeight: newHeight, blockIndex: i + 1 });
                // Update blockHeight on UTXOs belonging to these transactions
                await trxStorage.knex('mockchain_utxos').where({ txid: unminedTxs[i].txid }).update({ blockHeight: newHeight });
            }
            // Insert block header
            const headerRow = {
                height: newHeight,
                hash,
                previousHash,
                merkleRoot,
                version: 1,
                time,
                bits,
                nonce,
                coinbaseTxid
            };
            await trxStorage.knex('mockchain_block_headers').insert(headerRow);
        });
        return {
            height: newHeight,
            hash,
            previousHash,
            merkleRoot,
            version: 1,
            time,
            bits,
            nonce
        };
    }
}
exports.MockMiner = MockMiner;
//# sourceMappingURL=MockMiner.js.map