class Bitcoin::SigHashGenerator::SchnorrSigHashGenerator

v1 witness sighash generator see: github.com/bitcoin/bips/blob/master/bip-0341.mediawiki

Public Instance Methods

generate(tx, input_index, hash_type, opts) click to toggle source

generate signature hash for taproot and tapscript @param [Hash] opts some data using signature. This class requires following key params:

  • sig_version: sig version. :taproot or :tapscript

  • prevouts: array of all prevout

  • annex: annex value with binary format if annex exist.

  • leaf_hash: leaf hash with binary format if sig_version is :tapscript, it required

  • last_code_separator_pos: the position of last code separator

@return [String] signature hash with binary format.

# File lib/bitcoin/sighash_generator.rb, line 107
def generate(tx, input_index, hash_type, opts)
  raise ArgumentError, 'Invalid sig_version was specified.' unless [:taproot, :tapscript].include?(opts[:sig_version])

  ext_flag = opts[:sig_version] == :taproot ? 0 : 1
  key_version = 0
  output_ype = hash_type == SIGHASH_TYPE[:default] ? SIGHASH_TYPE[:all] : (hash_type & 0x03)
  input_type = hash_type & 0x80
  epoc = '00'.htb

  buf = epoc # EPOC
  buf << [hash_type, tx.version, tx.lock_time].pack('CVV')
  unless input_type == SIGHASH_TYPE[:anyonecanpay]
    buf << Bitcoin.sha256(tx.in.map{|i|i.out_point.to_payload}.join) # sha_prevouts
    buf << Bitcoin.sha256(opts[:prevouts].map(&:value).pack('Q*'))# sha_amounts
    buf << Bitcoin.sha256(opts[:prevouts].map{|o|o.script_pubkey.to_payload(true)}.join) # sha_scriptpubkeys
    buf << Bitcoin.sha256(tx.in.map(&:sequence).pack('V*')) # sha_sequences
  end

  buf << Bitcoin.sha256(tx.out.map(&:to_payload).join) if output_ype == SIGHASH_TYPE[:all]

  spend_type = (ext_flag << 1) + (opts[:annex] ? 1 : 0)
  buf << [spend_type].pack('C')
  if input_type == SIGHASH_TYPE[:anyonecanpay]
    buf << tx.in[input_index].out_point.to_payload
    buf << opts[:prevouts][input_index].to_payload
    buf << [tx.in[input_index].sequence].pack('V')
  else
    buf << [input_index].pack('V')
  end

  buf << Bitcoin.sha256(Bitcoin.pack_var_string(opts[:annex])) if opts[:annex]

  if output_ype == SIGHASH_TYPE[:single]
    raise ArgumentError, "Tx does not have #{input_index} th output." if input_index >= tx.out.size
    buf << Bitcoin.sha256(tx.out[input_index].to_payload)
  end

  if opts[:sig_version] == :tapscript
    buf << opts[:leaf_hash]
    buf << [key_version, opts[:last_code_separator_pos]].pack("CV")
  end

  Bitcoin.tagged_hash('TapSighash', buf)
end