This commit is contained in:
Ben Wilson
2023-06-23 13:55:18 -04:00
parent 88f56d68b1
commit 7b77bf8006
3 changed files with 77 additions and 43 deletions
+50 -38
View File
@@ -32,6 +32,7 @@ export class MiningJob {
public versionMask: string;
public tree: MerkleTree;
public coinbaseTransaction: bitcoinjs.Transaction;
constructor(id: string, payoutInformation: AddressObject[], public blockTemplate: IBlockTemplate, public networkDifficulty: number, public clean_jobs: boolean) {
@@ -45,76 +46,87 @@ export class MiningJob {
this.ntime = Math.floor(new Date().getTime() / 1000);
const transactionFees = blockTemplate.transactions.reduce((pre, cur, i, arr) => {
return pre + cur.fee;
}, 0);
const allTransactions = blockTemplate.transactions.map(t => bitcoinjs.Transaction.fromHex(t.data));
this.coinbaseTransaction = this.createCoinbaseTransaction(payoutInformation, this.blockTemplate.height, this.blockTemplate.coinbasevalue);
allTransactions.unshift(this.coinbaseTransaction);
const witnessRootHash = bitcoinjs.Block.calculateMerkleRoot(allTransactions, true);
const { coinbasePart1, coinbasePart2 } = this.createCoinbaseTransaction(payoutInformation, blockTemplate.height, blockTemplate.coinbasevalue);
this.coinb1 = coinbasePart1;
//The commitment is recorded in a scriptPubKey of the coinbase transaction. It must be at least 38 bytes, with the first 6-byte of 0x6a24aa21a9ed, that is:
// 1-byte - OP_RETURN (0x6a)
// 1-byte - Push the following 36 bytes (0x24)
// 4-byte - Commitment header (0xaa21a9ed)
const segwitMagicBits = Buffer.from('aa21a9ed', 'hex');
// 32-byte - Commitment hash: Double-SHA256(witness root hash|witness reserved value)
const merkleRoot = this.sha256(this.sha256(witnessRootHash));
// 39th byte onwards: Optional data with no consensus meaning
this.coinbaseTransaction.outs[0].script = bitcoinjs.script.compile([bitcoinjs.opcodes.OP_RETURN, Buffer.concat([segwitMagicBits, merkleRoot])]);
//@ts-ignore
const serializedTx = this.coinbaseTransaction.__toBuffer().toString('hex');
const blockHeightScript = `03${this.blockTemplate.height.toString(16).padStart(8, '0')}` + '00000000' + '00000000';
const partOneIndex = serializedTx.indexOf(blockHeightScript) + blockHeightScript.length;
const coinbasePart1 = serializedTx.slice(0, partOneIndex);
const coinbasePart2 = serializedTx.slice(partOneIndex);
this.coinb1 = coinbasePart1.slice(0, coinbasePart1.length - 16);
this.coinb2 = coinbasePart2;
const coinbaseHash = this.sha256(this.coinb1 + this.coinb2).toString('hex');
const coinbaseBuffer = Buffer.from(coinbaseHash, 'hex');
// Calculate merkle branch
const transactionBuffers = blockTemplate.transactions.map(tx => Buffer.from(tx.hash, 'hex'));
transactionBuffers.unshift(coinbaseBuffer);
const transactionBuffers = allTransactions.map(tx => tx.getHash(false));
this.tree = new MerkleTree(transactionBuffers, this.sha256, { isBitcoinTree: true });
const rootBuffer = this.tree.getRoot();
this.merkleRoot = rootBuffer.toString('hex');
this.merkle_branch = this.tree.getProof(coinbaseBuffer).map(p => p.data.toString('hex'));
this.merkle_branch = this.tree.getProof(this.coinbaseTransaction.getHash(false)).map(p => p.data.toString('hex'));
this.constructResponse();
}
private createCoinbaseTransaction(addresses: AddressObject[], blockHeight: number, reward: number): { coinbasePart1: string, coinbasePart2: string } {
private createCoinbaseTransaction(addresses: AddressObject[], blockHeight: number, reward: number): bitcoinjs.Transaction {
// Part 1
const blockHeightScript = `03${blockHeight.toString(16).padStart(8, '0')}`;
const outputIndex = 'ffffffff';
const tx = new bitcoinjs.Transaction();
// Set the version of the transaction
tx.version = 2;
const version = '01000000';
const inputCount = '01';
const fakeCoinbaseInput = '0000000000000000000000000000000000000000000000000000000000000000';
const blockHeightScript = `03${blockHeight.toString(16).padStart(8, '0')}` + '00000000' + '00000000';
// const inputScriptBytes = ((blockHeightScript.length + 16) / 2).toString(16).padStart(2, '0');
// const OP_RETURN = '6a';
// const inputScript = `${OP_RETURN}${inputScriptBytes}${blockHeightScript}`
const inputScriptBytes = ((blockHeightScript.length + 16) / 2).toString(16).padStart(2, '0');
const inputScript = bitcoinjs.script.compile([bitcoinjs.opcodes.OP_RETURN, Buffer.from(blockHeightScript, 'hex')])
// Add the coinbase input (input with no previous output)
tx.addInput(Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex'), 0xffffffff, 0xffffffff, inputScript);
const coinbasePart1 = version + inputCount + fakeCoinbaseInput + outputIndex + inputScriptBytes + blockHeightScript;
// Add an output
const recipientAddress = addresses[0].address;
const scriptPubKey = bitcoinjs.payments.p2wpkh({ address: recipientAddress, network: bitcoinjs.networks.testnet });
tx.addOutput(scriptPubKey.output, reward);
// Part 2
const outputs = addresses
.map((addressObj) => {
const percentage = addressObj.percent / 100;
const satoshis = Math.floor(reward * percentage);
const satoshiBuff = Buffer.alloc(4);
satoshiBuff.writeUInt32LE(satoshis);
const littleEndianSatoshis = satoshiBuff.toString('hex').padEnd(16, '0');
const script = bitcoinjs.payments.p2wpkh({ address: addressObj.address }).output.toString('hex') // Convert address to hex
const scriptBytes = (script.length / 2).toString(16).padStart(2, '0');
return littleEndianSatoshis + scriptBytes + script;
})
.join('');
const outputCountHex = addresses.length.toString(16).padStart(2, '0');
const segwitWitnessReservedValue = Buffer.alloc(32, 0);
const sequence = 'ffffffff';
const lockTime = '00000000';
//and the coinbase's input's witness must consist of a single 32-byte array for the witness reserved value
const coinbasePart2 = sequence + outputCountHex + outputs + lockTime;
tx.ins[0].witness = [segwitWitnessReservedValue];
return { coinbasePart1, coinbasePart2 };
return tx;
}
private sha256(data) {