@bsv/wallet-toolbox
Version:
BRC100 conforming wallet, wallet storage and wallet signer components
136 lines • 5.3 kB
JavaScript
"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