UNPKG

@bsv/wallet-toolbox

Version:

BRC100 conforming wallet, wallet storage and wallet signer components

136 lines 5.3 kB
"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