github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/lib/btc/taproot.go (about)

     1  package btc
     2  
     3  import (
     4  	"crypto/sha256"
     5  	"encoding/binary"
     6  )
     7  
     8  type ScriptExecutionData struct {
     9  	//! The tapleaf hash.
    10  	M_tapleaf_hash []byte
    11  
    12  	//! Whether m_codeseparator_pos is initialized.
    13  	M_codeseparator_pos_init bool
    14  	//! Opcode position of the last executed OP_CODESEPARATOR (or 0xFFFFFFFF if none executed).
    15  	M_codeseparator_pos uint32
    16  
    17  	//! Hash of the annex data.
    18  	M_annex_hash []byte
    19  
    20  	//! Whether m_validation_weight_left is initialized.
    21  	M_validation_weight_left_init bool
    22  	//! How much validation weight is left (decremented for every successful non-empty signature check).
    23  	M_validation_weight_left int64
    24  }
    25  
    26  // TaprootSigHash implements taproot's sighash algorithm
    27  // script - if true uses TAPSCRIPT mode (not TAPROOT)
    28  func (tx *Tx) TaprootSigHash(execdata *ScriptExecutionData, in_pos int, hash_type byte, script bool) []byte {
    29  	var ext_flag, key_version byte
    30  
    31  	if script {
    32  		ext_flag = 1
    33  	}
    34  
    35  	tx.hash_lock.Lock()
    36  	defer tx.hash_lock.Unlock()
    37  
    38  	sha := Hasher(HASHER_TAPSIGHASH)
    39  
    40  	sha.Write([]byte{0}) // EPOCH
    41  
    42  	// Hash type
    43  	var output_type byte
    44  	if hash_type == SIGHASH_DEFAULT {
    45  		output_type = SIGHASH_ALL
    46  	} else {
    47  		output_type = (hash_type & SIGHASH_OUTPUT_MASK) // Default (no sighash byte) is equivalent to SIGHASH_ALL
    48  	}
    49  
    50  	input_type := hash_type & SIGHASH_INPUT_MASK
    51  	if !(hash_type <= 0x03 || (hash_type >= 0x81 && hash_type <= 0x83)) {
    52  		return make([]byte, 32)
    53  	}
    54  	sha.Write([]byte{hash_type})
    55  
    56  	// Transaction level data
    57  	binary.Write(sha, binary.LittleEndian, tx.Version)
    58  	binary.Write(sha, binary.LittleEndian, tx.Lock_time)
    59  
    60  	if input_type != SIGHASH_ANYONECANPAY {
    61  		if tx.m_prevouts_single_hash == nil {
    62  			sh := sha256.New()
    63  			for _, ti := range tx.TxIn {
    64  				sh.Write(ti.Input.Hash[:])
    65  				binary.Write(sh, binary.LittleEndian, ti.Input.Vout)
    66  			}
    67  			tx.m_prevouts_single_hash = sh.Sum(nil)
    68  
    69  			sh.Reset()
    70  			for i := range tx.TxIn {
    71  				binary.Write(sh, binary.LittleEndian, tx.Spent_outputs[i].Value)
    72  			}
    73  			tx.m_spent_amounts_single_hash = sh.Sum(nil)
    74  
    75  			sh.Reset()
    76  			for i := range tx.TxIn {
    77  				WriteVlen(sh, uint64(len(tx.Spent_outputs[i].Pk_script)))
    78  				sh.Write(tx.Spent_outputs[i].Pk_script)
    79  			}
    80  			tx.m_spent_scripts_single_hash = sh.Sum(nil)
    81  
    82  			sh.Reset()
    83  			for _, vin := range tx.TxIn {
    84  				binary.Write(sh, binary.LittleEndian, vin.Sequence)
    85  			}
    86  			tx.m_sequences_single_hash = sh.Sum(nil)
    87  		}
    88  
    89  		sha.Write(tx.m_prevouts_single_hash)
    90  		sha.Write(tx.m_spent_amounts_single_hash)
    91  		sha.Write(tx.m_spent_scripts_single_hash)
    92  		sha.Write(tx.m_sequences_single_hash)
    93  	}
    94  
    95  	if output_type == SIGHASH_ALL {
    96  		if tx.m_outputs_single_hash == nil {
    97  			sh := sha256.New()
    98  			for _, vout := range tx.TxOut {
    99  				binary.Write(sh, binary.LittleEndian, vout.Value)
   100  				WriteVlen(sh, uint64(len(vout.Pk_script)))
   101  				sh.Write(vout.Pk_script)
   102  			}
   103  			tx.m_outputs_single_hash = sh.Sum(nil)
   104  		}
   105  
   106  		sha.Write(tx.m_outputs_single_hash)
   107  	}
   108  
   109  	// Data about the input/prevout being spent
   110  	have_annex := execdata.M_annex_hash != nil
   111  	spend_type := (ext_flag << 1)
   112  	if have_annex {
   113  		spend_type++
   114  	}
   115  	sha.Write([]byte{spend_type})
   116  
   117  	if input_type == SIGHASH_ANYONECANPAY {
   118  		sha.Write(tx.TxIn[in_pos].Input.Hash[:])
   119  		binary.Write(sha, binary.LittleEndian, tx.TxIn[in_pos].Input.Vout)
   120  		binary.Write(sha, binary.LittleEndian, tx.Spent_outputs[in_pos].Value)
   121  		WriteVlen(sha, uint64(len(tx.Spent_outputs[in_pos].Pk_script)))
   122  		sha.Write(tx.Spent_outputs[in_pos].Pk_script)
   123  		binary.Write(sha, binary.LittleEndian, tx.TxIn[in_pos].Sequence)
   124  	} else {
   125  		binary.Write(sha, binary.LittleEndian, uint32(in_pos))
   126  	}
   127  	if have_annex {
   128  		sha.Write(execdata.M_annex_hash)
   129  	}
   130  
   131  	// Data about the output (if only one).
   132  	if output_type == SIGHASH_SINGLE {
   133  		if in_pos >= len(tx.TxOut) {
   134  			return make([]byte, 32)
   135  		}
   136  		sh := sha256.New()
   137  		binary.Write(sh, binary.LittleEndian, tx.TxOut[in_pos].Value)
   138  		WriteVlen(sh, uint64(len(tx.TxOut[in_pos].Pk_script)))
   139  		sh.Write(tx.TxOut[in_pos].Pk_script)
   140  		sha.Write(sh.Sum(nil))
   141  	}
   142  
   143  	// Additional data for BIP 342 signatures
   144  	if script {
   145  		sha.Write(execdata.M_tapleaf_hash)
   146  		sha.Write([]byte{key_version})
   147  		binary.Write(sha, binary.LittleEndian, execdata.M_codeseparator_pos)
   148  	}
   149  
   150  	return sha.Sum(nil)
   151  }