muun-recovery/vendor/github.com/muun/libwallet/btcsuitew/txscriptw/script.go

150 lines
3.6 KiB
Go

package txscriptw
import (
"bytes"
"encoding/binary"
"fmt"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/muun/libwallet/btcsuitew/chainhashw"
)
// CalcTaprootSigHash crafts signature digest.
// It only supports SIGHASH_ALL without ANYONECANPAY, and no annex or script paths.
func CalcTaprootSigHash(
tx *wire.MsgTx,
sigHashes *TaprootSigHashes,
index int,
hashType txscript.SigHashType,
) ([]byte, error) {
if index >= len(tx.TxIn) {
return nil, fmt.Errorf("wanted index %d but found only %d inputs", index, len(tx.TxIn))
}
anyoneCanPay := hashType&txscript.SigHashAnyOneCanPay != 0
hashType = hashType & 0x1f
if hashType != txscript.SigHashAll {
return nil, fmt.Errorf("only SIGHASH_ALL is supported")
}
if anyoneCanPay {
return nil, fmt.Errorf("anyoneCanPay is not supported")
}
b := new(bytes.Buffer)
// Epoch [1] (not technically part of the message, but every use-case adds this prefix later)
b.WriteByte(0x00)
// SigHash type [1]
b.WriteByte(byte(hashType))
// nVersion [4]
b.Write(uInt32Le(uint32(tx.Version)))
// nLockTime [4]
b.Write(uInt32Le(tx.LockTime))
// input data [128 per input] always included since we failed for anyoneCanPay
if !anyoneCanPay {
b.Write(sigHashes.HashPrevOuts[:])
b.Write(sigHashes.HashAmounts[:])
b.Write(sigHashes.HashScriptPubKeys[:])
b.Write(sigHashes.HashSequence[:])
}
// output data [?] always included since we checked for SigHashAll
if hashType != txscript.SigHashNone && hashType != txscript.SigHashSingle {
b.Write(sigHashes.HashOutputs[:])
}
// Spend type [1] always 0x00 since we don't support annex or script path
b.WriteByte(0x00)
if anyoneCanPay {
// MISSING: commit to the spent output and sequence (never since we failed for anyoneCanPay)
} else {
// Input index [4]
b.Write(uInt32Le(uint32(index)))
}
// MISSING: do some more hashing and commit to the annex (not supported)
if hashType == txscript.SigHashSingle {
return nil, fmt.Errorf("SIGHASH_SINGLE is not supported")
}
// MISSING: encode extensions, such as the script path commitment from BIP-342 (not supported)
// As with the epoch byte above, not technically part of the message, but used in all cases
return chainhashw.TaggedHashB(chainhashw.TagTapSighash, b.Bytes()), nil
}
func uInt32Le(n uint32) []byte {
var nBytes [4]byte
binary.LittleEndian.PutUint32(nBytes[:], n)
return nBytes[:]
}
func uInt64Le(n uint64) []byte {
var nBytes [8]byte
binary.LittleEndian.PutUint64(nBytes[:], n)
return nBytes[:]
}
func calcHashPrevOuts(tx *wire.MsgTx) chainhash.Hash {
b := new(bytes.Buffer)
for _, txIn := range tx.TxIn {
b.Write(txIn.PreviousOutPoint.Hash[:])
b.Write(uInt32Le(txIn.PreviousOutPoint.Index))
}
return chainhash.HashH(b.Bytes())
}
func calcHashSequences(tx *wire.MsgTx) chainhash.Hash {
b := new(bytes.Buffer)
for _, txIn := range tx.TxIn {
b.Write(uInt32Le(txIn.Sequence))
}
return chainhash.HashH(b.Bytes())
}
func calcHashOutputs(tx *wire.MsgTx) chainhash.Hash {
b := new(bytes.Buffer)
for _, txOut := range tx.TxOut {
wire.WriteTxOut(b, 0, 0, txOut)
}
return chainhash.HashH(b.Bytes())
}
func calcHashScriptPubKeys(txOuts []*wire.TxOut) chainhash.Hash {
b := new(bytes.Buffer)
for _, txOut := range txOuts {
wire.WriteVarInt(b, 0, uint64(len(txOut.PkScript)))
b.Write(txOut.PkScript)
}
return chainhash.HashH(b.Bytes())
}
func calcHashAmounts(txOuts []*wire.TxOut) chainhash.Hash {
b := new(bytes.Buffer)
for _, txOut := range txOuts {
b.Write(uInt64Le(uint64(txOut.Value)))
}
return chainhash.HashH(b.Bytes())
}