Untitled

 avatar
unknown
javascript
2 months ago
5.0 kB
5
Indexable
const crypto = require('crypto');
const bs58 = require('bs58');
const fs = require('fs');

// Utility functions
const hexToBuffer = (hex) => Buffer.from(hex, 'hex');
const doubleSha256 = (data) => crypto.createHash('sha256').update(crypto.createHash('sha256').update(data).digest()).digest();
const encodeVarint = (n) => n < 0xfd ? [n] : (n <= 0xffff ? [0xfd, n & 0xFF, (n >> 8) & 0xFF] : (n <= 0xffffffff ? [0xfe, n & 0xFF, (n >> 8) & 0xFF, (n >> 16) & 0xFF, (n >> 24) & 0xFF] : [0xff, ...Array.from({length: 8}, (_, i) => (n >> (i * 8)) & 0xFF)]));
const intToLittleEndian = (val, size) => {
  let buf = Buffer.alloc(size);
  for (let i = 0; i < size; i++) buf[i] = (val >> (i * 8)) & 0xFF;
  return buf;
};

// Base58 address to script conversion (P2PKH or P2SH)
const addressToScript = (address) => {
  const decoded = bs58.decode(address);
  const hash160 = decoded.slice(1, 21);
  return Buffer.concat([Buffer.from([0xa9, 0x14]), hash160, Buffer.from([0x87])]);
};

// Transaction serialization
const serializeOutpoint = (hash, index) => {
  const hashBuf = Buffer.from(hash, 'hex').reverse();
  const indexBuf = Buffer.alloc(4);
  indexBuf.writeUInt32LE(index);
  return Buffer.concat([hashBuf, indexBuf]);
};

const serializeInput = (prevHash, prevIndex, script, sequence) => {
  const outpoint = serializeOutpoint(prevHash, prevIndex);
  const scriptLen = encodeVarint(script.length);
  const sequenceBuf = Buffer.alloc(4);
  sequenceBuf.writeUInt32LE(sequence);
  return Buffer.concat([outpoint, scriptLen, script, sequenceBuf]);
};

const serializeOutput = (value, script) => {
  const valueBuf = Buffer.alloc(8);
  valueBuf.writeBigUInt64LE(BigInt(value));
  const scriptLen = encodeVarint(script.length);
  return Buffer.concat([valueBuf, scriptLen, script]);
};

// P2WSH Witness creation
const getP2WSHWitness = (signatures, witnessScript) => {
  const witness = [0x04, 0x00]; // OP_0 due to CHECKMULTISIG bug
  signatures.forEach(sig => {
    witness.push(sig.length, ...sig);
  });
  witness.push(witnessScript.length, ...witnessScript);
  return Buffer.from(witness);
};

// Transaction creation
const createTransaction = () => {
  const pubKey1 = hexToBuffer('032ff8c5df0bc00fe1ac2319c3b8070d6d1e04cfbf4fedda499ae7b775185ad53b');
  const pubKey2 = hexToBuffer('039bbc8d24f89e5bc44c5b0d1980d6658316a6b2440023117c3c03a4975b04dd56');

  const redeemScript = Buffer.concat([Buffer.from([0x52, 0x21]), pubKey1, Buffer.from([0x21]), pubKey2, Buffer.from([0x52, 0xae])]);
  const witnessScript = redeemScript;
  const witnessProgram = doubleSha256(witnessScript);
  const p2wshScript = Buffer.concat([Buffer.from([0x00, 0x20]), witnessProgram]);

  const p2shRedeemScript = p2wshScript;
  const p2shInputScript = Buffer.concat([Buffer.from([p2shRedeemScript.length]), p2shRedeemScript]);

  const version = Buffer.from([0x02, 0x00, 0x00, 0x00]);
  const marker = Buffer.from([0x00]);
  const flag = Buffer.from([0x01]);

  // Inputs and Outputs
  const nInputs = encodeVarint(1);
  const txInput = serializeInput('0'.repeat(64), 0, p2shInputScript, 0xffffffff);
  const nOutputs = encodeVarint(1);
  const outputScript = addressToScript('325UUecEQuyrTd28Xs2hvAxdAjHM7XzqVF');
  const txOutput = serializeOutput(100000, outputScript);

  // Outpoints, sequences, outputs for final tx
  const outpoints = [serializeOutpoint('0'.repeat(64), 0)];
  const hashPrevouts = doubleSha256(Buffer.concat(outpoints));
  const sequences = [Buffer.from([0xff, 0xff, 0xff, 0xff])];
  const hashSequence = doubleSha256(Buffer.concat(sequences));
  const hashOutputs = doubleSha256(txOutput);

  // Preimage for sighash
  const preimage = Buffer.concat([
    version, hashPrevouts, hashSequence, outpoints[0], 
    encodeVarint(witnessScript.length), witnessScript,
    Buffer.from([100000]), sequences[0], hashOutputs,
    Buffer.from([0x00, 0x00, 0x00, 0x00]), Buffer.from([0x01, 0x00, 0x00, 0x00])
  ]);
  const sighash = doubleSha256(preimage);

  // Signatures
  const signature1 = Buffer.from('304402202335473bc09007ca8d4b738e67ac56a0a75b4f945d8d2fd0452437c15d4e2041022048c08ad5b8f09b304eb36e6ed0c6087d8fccd15d4110d114e856a9580844594701', 'hex');
  const signature2 = Buffer.from('30450221009aa7fc343e0d4d1d2298cb80b1b0453dcb0e38fe0af2f707118cb402a87fb0480220658b120004b1a43986b0ccdca723f66b9601c565152dee7631853b139c9746e701', 'hex');
  const signatures = [signature1, signature2];

  // Generate witness
  const witness = getP2WSHWitness(signatures, witnessScript);

  // Locktime
  const locktime = intToLittleEndian(0x00, 4);

  // Final transaction
  const finalTx = Buffer.concat([version, marker, flag, nInputs, txInput, nOutputs, txOutput, witness, locktime]);
  const txidNew = finalTx.toString('hex');

  // Output and save result
  console.log(txidNew);
  fs.writeFileSync('out.txt', txidNew);
};

// Call the function to create the transaction
createTransaction();
Editor is loading...
Leave a Comment