How to generate a signature preimage

The best library in JS for doing this is bsv2 which exposes an internal function which builds the preimage when signing a transaction, allowing you to output the result:

npm i bsv

node
const bsv = require('bsv')

const therawhex = '0100000001a772376502aef2738dca0089811044c3689896ac37053f24a9784a13399f764a010000008b483045022100b8a72a38b9630139a597f271c7016501656d717012d14f81ceee47ad7bce8bf702203bc16d1098ed3b81d04e836b4ea6ed5791a6b0863c9165529215b7be3616ec1a4141044ff33350fbb662de8f22d488319792b563dbd120b016a1bc55645465ee27edcb7e58fbaa4bb7ff1cd2620882a18132a3e51e0b0da6455d0ff08703db5b6234b2ffffffff02e8030000000000001976a91461079f5031a5b7e312d9fc5051fd7ce018fabc9288acb01d0000000000001976a9141f02307e6139effb4ec53283bcf6072e4796e10688ace9390b00'

tx.id()
// '9c60182f013eb70304b3dc6d63bc00e15b58a452040c29a0be11344e03760fd0'

Look that txid up here: https://whatsonchain.com/tx/9c60182f013eb70304b3dc6d63bc00e15b58a452040c29a0be11344e03760fd0

Click the JSON tab and grab the hex from the input the tx is spending, along with the value:

Then use them to calculate the Preimage:

var lockingScript = bsv.Script.fromHex('76a9141f02307e6139effb4ec53283bcf6072e4796e10688ac')
var satoshis = bsv.Bn(0.000088 * 100000000)
var sigHashType = bsv.Sig.SIGHASH_ALL | bsv.Sig.SIGHASH_FORKID
var flags = bsv.Tx.SCRIPT_ENABLE_SIGHASH_FORKID
var vIn = 0
var preimage = tx.sighashPreimage(sigHashType, vIn, lockingScript, satoshis, flags).toString('hex')
// 01000000822859cb9c3648de08a50feb15bb7f7c387478c43f55097cd1105800fa1778583bb13029ce7b1f559ef5e747fcac439f1455a2ec7c5f09b72290795e70665044a772376502aef2738dca0089811044c3689896ac37053f24a9784a13399f764a010000001976a9141f02307e6139effb4ec53283bcf6072e4796e10688ac6022000000000000fffffffff5d957fb805c72174d3fe34c0559632e1ee9432a4a714bab7b1bc9fbec15a1d7e9390b0041000000

We can then parse that out to something more visual:

const { VarInt } = bsv
const preimageParser = hex => {
  const toHex = str => str.toString('hex')
  const preimage = Buffer.from(hex, 'hex')
  const version = toHex(preimage.slice(0, 4)) // 4 bytes
  const hashPrevouts = toHex(preimage.slice(4, 36))// 32 bytes
  const hashSequence = toHex(preimage.slice(36, 68))// 32bytes
  const txid = toHex(preimage.slice(68, 100).reverse())//32 bytes txid
  const vout = toHex(preimage.slice(100, 104))// 4 bytes vout
  const l = VarInt.fromBuffer(preimage.slice(104)).toNumber()
  const varIntLength = VarInt.fromNumber(l).toBuffer().length
  const len = preimage.length
  const script = toHex(preimage.slice(104 + varIntLength, len - 52)) // bytes length of script
  const satoshis = toHex(preimage.slice(len - 52, len - 44))// amount 8 bytes
  const nSequence = toHex(preimage.slice(len - 44, len - 40))// 4 bytes
  const hashOutputs = toHex(preimage.slice(len - 40, len - 8))// 32 bytes
  const nLocktime = toHex(preimage.slice(len - 8, len - 4))// 4 bytes
  const nHashType = toHex(preimage.slice(len - 4, len))// 4 bytes
  return {
    version, hashPrevouts, hashSequence, txid, vout, l, script, satoshis, nSequence, hashOutputs, nLocktime, nHashType
  }
}

preimageParser(preimage)
// result below:

{
  version: '01000000',
  hashPrevouts: '822859cb9c3648de08a50feb15bb7f7c387478c43f55097cd1105800fa177858',
  hashSequence: '3bb13029ce7b1f559ef5e747fcac439f1455a2ec7c5f09b72290795e70665044',
  txid: '4a769f39134a78a9243f0537ac969868c34410818900ca8d73f2ae02653772a7',
  vout: '01000000',
  l: 25,
  script: '76a9141f02307e6139effb4ec53283bcf6072e4796e10688ac',
  satoshis: '6022000000000000',
  nSequence: 'ffffffff',
  hashOutputs: 'f5d957fb805c72174d3fe34c0559632e1ee9432a4a714bab7b1bc9fbec15a1d7',
  nLocktime: 'e9390b00',
  nHashType: '41000000'
}

1 Like