mirror of
https://github.com/benjamin-wilson/public-pool.git
synced 2025-03-27 02:02:10 +01:00
segwit
This commit is contained in:
parent
88f56d68b1
commit
7b77bf8006
@ -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) {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Block, Transaction } from 'bitcoinjs-lib';
|
||||
import * as bitcoinjs from 'bitcoinjs-lib';
|
||||
import { plainToInstance } from 'class-transformer';
|
||||
import { validate, ValidatorOptions } from 'class-validator';
|
||||
import * as crypto from 'crypto';
|
||||
@ -271,18 +272,36 @@ export class StratumV1Client extends EasyUnsubscribe {
|
||||
private constructBlockAndBroadcast(job: MiningJob, submission: MiningSubmitMessage) {
|
||||
const block = new Block();
|
||||
|
||||
block.version = job.blockTemplate.version;
|
||||
const blockHeightScript = `03${job.blockTemplate.height.toString(16).padStart(8, '0')}${job.id}${submission.extraNonce2}`;
|
||||
|
||||
const inputScript = bitcoinjs.script.compile([bitcoinjs.opcodes.OP_RETURN, Buffer.from(blockHeightScript, 'hex')]);
|
||||
job.coinbaseTransaction.ins[0].script = inputScript;
|
||||
|
||||
|
||||
const versionMask = parseInt(submission.versionMask, 16);
|
||||
let version = job.version;
|
||||
if (versionMask !== undefined && versionMask != 0) {
|
||||
version = (version ^ versionMask);
|
||||
}
|
||||
|
||||
block.version = version;
|
||||
block.prevHash = Buffer.from(job.prevhash, 'hex');
|
||||
block.merkleRoot = Buffer.from(job.merkleRoot, 'hex');
|
||||
|
||||
block.timestamp = job.ntime;
|
||||
block.bits = job.nbits;
|
||||
block.nonce = parseInt(submission.nonce, 16);
|
||||
|
||||
block.transactions = job.blockTemplate.transactions.map(tx => {
|
||||
return Transaction.fromHex(tx.data);
|
||||
});
|
||||
block.transactions.unshift(job.coinbaseTransaction);
|
||||
|
||||
block.merkleRoot = bitcoinjs.Block.calculateMerkleRoot(block.transactions, false);
|
||||
block.witnessCommit = bitcoinjs.Block.calculateMerkleRoot(block.transactions, true);
|
||||
|
||||
// const test1 = block.getWitnessCommit();
|
||||
// const test2 = block.checkTxRoots();
|
||||
|
||||
const coinbaseTx = `${job.coinb1}${this.id}${submission.extraNonce2}${job.coinb2}`;
|
||||
block.transactions.unshift(Transaction.fromHex(coinbaseTx));
|
||||
|
||||
const blockHex = block.toHex(false);
|
||||
this.bitcoinRpcService.SUBMIT_BLOCK(blockHex);
|
||||
|
@ -74,6 +74,7 @@ export class MiningSubmitMessage extends StratumBaseMessage {
|
||||
|
||||
const coinbaseTx = `${job.coinb1}${extraNonce}${extraNonce2}${job.coinb2}`;
|
||||
|
||||
|
||||
const newRoot = this.calculateMerkleRootHash(coinbaseTx, job.merkle_branch)
|
||||
|
||||
const truediffone = Big('26959535291011309493156476344723991336010898738574164086137773096960');
|
||||
@ -94,6 +95,8 @@ export class MiningSubmitMessage extends StratumBaseMessage {
|
||||
header.writeBigUint64LE(BigInt(job.nbits), 72);
|
||||
header.writeUInt32LE(nonce, 76);
|
||||
|
||||
console.log(header.toString('hex'))
|
||||
|
||||
|
||||
const hashBuffer: Buffer = crypto.createHash('sha256').update(header).digest();
|
||||
const hashResult: Buffer = crypto.createHash('sha256').update(hashBuffer).digest();
|
||||
@ -142,7 +145,7 @@ export class MiningSubmitMessage extends StratumBaseMessage {
|
||||
bothMerkles.set(newRoot);
|
||||
}
|
||||
|
||||
return bothMerkles;
|
||||
return bothMerkles.subarray(0, 32)
|
||||
}
|
||||
|
||||
private sha256(data: Buffer) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user