Sign Transaction

Complete examples for signing BSV transactions with various signature types, patterns, and advanced signing scenarios.

Overview

Transaction signing is the process of creating cryptographic signatures that prove ownership of UTXOs and authorize their spending. This guide covers basic signing, SIGHASH types, multi-signature transactions, and advanced signing patterns with proper error handling.

Related SDK Components:

Basic Transaction Signing

import { Transaction, PrivateKey, P2PKH, Script } from '@bsv/sdk'

/**
 * Basic Transaction Signer
 *
 * Simple signing of P2PKH transactions
 */
class BasicTransactionSigner {
  /**
   * Sign a transaction with a single private key
   */
  async signTransaction(
    tx: Transaction,
    privateKey: PrivateKey
  ): Promise<Transaction> {
    try {
      console.log('Signing transaction...')
      console.log('Transaction ID:', tx.id('hex'))
      console.log('Inputs:', tx.inputs.length)

      // Set unlocking script template for all inputs
      for (let i = 0; i < tx.inputs.length; i++) {
        tx.inputs[i].unlockingScriptTemplate = new P2PKH().unlock(privateKey)
      }

      // Sign the transaction
      await tx.sign()

      console.log('Transaction signed successfully')

      // Verify signatures
      const verified = this.verifySignatures(tx)
      console.log('Signatures verified:', verified)

      return tx
    } catch (error) {
      throw new Error(`Transaction signing failed: ${error.message}`)
    }
  }

  /**
   * Sign specific input
   */
  async signInput(
    tx: Transaction,
    inputIndex: number,
    privateKey: PrivateKey
  ): Promise<Transaction> {
    try {
      if (inputIndex < 0 || inputIndex >= tx.inputs.length) {
        throw new Error(`Invalid input index: ${inputIndex}`)
      }

      console.log(`Signing input ${inputIndex}`)

      // Set unlocking script template for specific input
      tx.inputs[inputIndex].unlockingScriptTemplate = new P2PKH().unlock(privateKey)

      // Sign the transaction
      await tx.sign()

      console.log(`Input ${inputIndex} signed successfully`)

      return tx
    } catch (error) {
      throw new Error(`Input signing failed: ${error.message}`)
    }
  }

  /**
   * Sign with multiple private keys (one per input)
   */
  async signWithMultipleKeys(
    tx: Transaction,
    privateKeys: PrivateKey[]
  ): Promise<Transaction> {
    try {
      if (privateKeys.length !== tx.inputs.length) {
        throw new Error('Number of private keys must match number of inputs')
      }

      console.log('Signing with multiple keys...')

      // Set unlocking script template for each input
      for (let i = 0; i < tx.inputs.length; i++) {
        tx.inputs[i].unlockingScriptTemplate = new P2PKH().unlock(privateKeys[i])
      }

      // Sign the transaction
      await tx.sign()

      console.log('All inputs signed successfully')

      return tx
    } catch (error) {
      throw new Error(`Multi-key signing failed: ${error.message}`)
    }
  }

  /**
   * Verify transaction signatures
   */
  private verifySignatures(tx: Transaction): boolean {
    try {
      for (let i = 0; i < tx.inputs.length; i++) {
        const input = tx.inputs[i]

        // Check if input has unlocking script
        if (!input.unlockingScript || input.unlockingScript.chunks.length === 0) {
          console.error(`Input ${i} has no unlocking script`)
          return false
        }
      }

      return true
    } catch (error) {
      console.error('Signature verification failed:', error.message)
      return false
    }
  }

  /**
   * Create and sign a simple transaction
   */
  async createAndSign(
    privateKey: PrivateKey,
    utxo: UTXO,
    toAddress: string,
    amount: number
  ): Promise<Transaction> {
    try {
      // Create transaction
      const tx = new Transaction()

      // Add input
      tx.addInput({
        sourceTXID: utxo.txid,
        sourceOutputIndex: utxo.vout,
        unlockingScriptTemplate: new P2PKH().unlock(privateKey),
        sequence: 0xffffffff
      })

      // Add payment output
      tx.addOutput({
        satoshis: amount,
        lockingScript: Script.fromAddress(toAddress)
      })

      // Calculate change
      const fee = 200
      const change = utxo.satoshis - amount - fee

      if (change > 546) {
        const changeAddress = privateKey.toPublicKey().toAddress()
        tx.addOutput({
          satoshis: change,
          lockingScript: Script.fromAddress(changeAddress)
        })
      }

      // Sign transaction
      await tx.sign()

      console.log('Transaction created and signed')
      console.log('TXID:', tx.id('hex'))

      return tx
    } catch (error) {
      throw new Error(`Create and sign failed: ${error.message}`)
    }
  }
}

interface UTXO {
  txid: string
  vout: number
  satoshis: number
  script?: Script
}

/**
 * Usage Example
 */
async function basicSigningExample() {
  const signer = new BasicTransactionSigner()
  const privateKey = PrivateKey.fromWif('your-wif-key')

  // Create UTXO
  const utxo: UTXO = {
    txid: 'previous-tx-id...',
    vout: 0,
    satoshis: 100000
  }

  // Create and sign transaction
  const tx = await signer.createAndSign(
    privateKey,
    utxo,
    '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa',
    50000
  )

  console.log('Signed transaction:', tx.toHex())
}

SIGHASH Types and Patterns

Multi-Signature Transactions

Advanced Signing Patterns

See Also

SDK Components:

Learning Paths:

Last updated