mirror of
https://github.com/muun/recovery.git
synced 2025-02-23 11:32:33 -05:00
150 lines
3.6 KiB
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())
|
|
}
|