mirror of
https://github.com/benjamin-wilson/public-pool.git
synced 2026-09-29 17:15:03 -07:00
segwit
This commit is contained in:
+50
-38
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user