Taproot Sighash ans Schnorr

I have done all possible checks: signhash and taproot calculation, no changes in BASE64 processes, empty symbols, Shnorr validity and that BASE64 is exactly 64 (not 64 + 1), but I get the same error every time.

Logic: 1. Receive psbt and decode -> Sign inputs (I don't finalise, the server does) using Default method (ALL the same result)-> encode in Base64 -> return partially signed PSBT to server.

I get the "Invalid Schnorr" error every time by server. You see, if you compare script PSBT and output psbt, they are 99% identical except for Shnorr (field unknown 13). I've attached a screenshot to show the difference.

  1. I thought the problem might be in the formats for the server, but also Bitcoin cli analyse returns an ‘updater’ status for my script output:
{
    { ‘result’: {
        { ‘inputs’: [
            {
                ‘has_utxo": true,
                ‘is_final": false,
                { ‘next’: { ‘updater’.
            },
            {
                ‘has_utxo": true,
                ‘is_final": false,
                { ‘next’: { ‘updater’.
            }
        ],
        ‘estimated_vsize": 286,
        ‘estimated_feerate": 6.0e-5,
        ‘fee": 1.716e-5,
        ‘next": { ‘updater’.
    },
    ‘error": null,
    ‘id": null
}
  1. Although if I sign via Unisat or any other extension wallet - it will return:
{
    ‘result": {
        { ‘inputs’: [
            {
                ‘has_utxo": true,
                ‘is_final": false,
                { ‘next’: { ‘updater’.
            },
            {
                ‘has_utxo": true,
                ‘is_final": false,
                { ‘next’: ‘finalizer’.
            }
        ],
        ‘fee": 1.716e-5,
        ‘next": { ‘updater’
    },
    ‘error": null,
    ‘id": null
}

Code:

func main() {
    wifKeyStr := "WIF"
    psbtBase64 := "cHNidP8BAPwCAAAAAgupxw+/SJhtowTzcr1wx9c/uanFKaJ3yU3J6UKjMTMzlAEAAAD/////Q9g6nnkQbdroR/sr2jeNqZ9nlZbVuqSNdjakxTxLgYADAAAAAP////8ETQEAAAAAAAAiUSCJwof7Xh35Js6aL/iDn8TPfRHPzAuFSuiewIWArB8uOmGDAAAAAAAAIlEgsxHYe7mlcSxOwOnapg4WHRT5UuB+vWF8uMz6Oxu5Bf9DAgAAAAAAABYAFAnkWBtaeW/v/BxPBmkgboAct6nrVkwAAAAAAAAiUSCJwof7Xh35Js6aL/iDn8TPfRHPzAuFSuiewIWArB8uOgAAAAAI/AJtZQNzaWdAvHgNmehOZ6jBZiu5ckCncLnVeViAr3PlKIgR2v8NPzhW/OXpu8oaK4b3bgRIL/qrMT5WlpgdybZGXVaAxSJVIwv8Am1lBnNpZ2V4cAhCeTbOW+ZAAAABAStNAQAAAAAAACJRIDCs9kAJfdTiQei7Cf+RSH3Cwr5oxFjNj27APixQD2L4ARcgc+G1jBTUGzdWX447VOZ6zEec2eeffMqg8KTrQ6plriIAAQErrtgAAAAAAAAiUSCJwof7Xh35Js6aL/iDn8TPfRHPzAuFSuiewIWArB8uOgEXIK0XtHBW7oxyV677kvzIL56kaRx0i5AADj3yumRxQD17AAAAAAA="
    cleanPSBTBase64 := cleanBase64(psbtBase64)

    psbtBytes, _ := base64.StdEncoding.DecodeString(cleanPSBTBase64)
    packet, _ := psbt.NewFromRawBytes(bytes.NewReader(psbtBytes), false)

    prevOutFetcher := txscript.NewMultiPrevOutFetcher(nil)
    for i, input := range packet.Inputs {
        if input.WitnessUtxo != nil {
            prevOutFetcher.AddPrevOut(packet.UnsignedTx.TxIn[i].PreviousOutPoint, &wire.TxOut{
                Value:    input.WitnessUtxo.Value,
                PkScript: input.WitnessUtxo.PkScript,
            })
        }
    }

    inputToSign := 1
    if len(packet.Inputs[inputToSign].TaprootInternalKey) > 0 {
        sigHashes := txscript.NewTxSigHashes(packet.UnsignedTx, prevOutFetcher)
        pkScript, _ := getInputScript(packet, inputToSign)
        amount, _ := getInputAmount(packet, inputToSign)

        taprootInternalKey := packet.Inputs[inputToSign].TaprootInternalKey
        wifKey, _ := btcutil.DecodeWIF(wifKeyStr)
        publicKeyFromWIF := schnorr.SerializePubKey(wifKey.PrivKey.PubKey())

        if bytes.Equal(taprootInternalKey, publicKeyFromWIF) {
            sighashType := txscript.SigHashDefault
            sighash, _ := txscript.CalcTaprootSignatureHash(sigHashes, sighashType, packet.UnsignedTx, inputToSign, txscript.NewCannedPrevOutputFetcher(pkScript, amount))

            signature, _ := schnorr.Sign(wifKey.PrivKey, sighash)
            serializedSig := signature.Serialize()

            packet.Inputs[inputToSign].TaprootKeySpendSig = serializedSig
        }
    }

    signedPsbtBase64, _ := packet.B64Encode()
    fmt.Printf("Signed PSBT (Base64): %s\n", signedPsbtBase64)
}

func cleanBase64(s string) string {
    return strings.Join(strings.Fields(s), "")
}

func getInputScript(packet *psbt.Packet, idx int) ([]byte, error) {
    input := packet.Inputs[idx]
    if input.WitnessUtxo != nil && len(input.WitnessUtxo.PkScript) > 0 {
        return input.WitnessUtxo.PkScript, nil
    }
    if input.NonWitnessUtxo != nil {
        prevTx := input.NonWitnessUtxo
        outPoint := packet.UnsignedTx.TxIn[idx].PreviousOutPoint
        if int(outPoint.Index) >= len(prevTx.TxOut) {
            return nil, fmt.Errorf("Invalid outpoint index %d for input %d", outPoint.Index, idx)
        }
        return prevTx.TxOut[outPoint.Index].PkScript, nil
    }
    return nil, fmt.Errorf("PkScript not found for input %d", idx)
}

func getInputAmount(packet *psbt.Packet, idx int) (int64, error) {
    input := packet.Inputs[idx]
    if input.WitnessUtxo != nil {
        return input.WitnessUtxo.Value, nil
    }
    if input.NonWitnessUtxo != nil {
        prevTx := input.NonWitnessUtxo
        outPoint := packet.UnsignedTx.TxIn[idx].PreviousOutPoint
        if int(outPoint.Index) >= len(prevTx.TxOut) {
            return 0, fmt.Errorf("Invalid outpoint index %d for input %d", outPoint.Index, idx)
        }
        return prevTx.TxOut[outPoint.Index].Value, nil
    }
    return 0, fmt.Errorf("WitnessUtxo or NonWitnessUtxo not found for input %d", idx)
}

OUTPUT:

cHNidP8BAPwCAAAAAgupxw+/SJhtowTzcr1wx9c/uanFKaJ3yU3J6UKjMTMzlAEAAAD/////Q9g6nnkQbdroR/sr2jeNqZ9nlZbVuqSNdjakxTxLgYADAAAAAP////8ETQEAAAAAAAAiUSCJwof7Xh35Js6aL/iDn8TPfRHPzAuFSuiewIWArB8uOmGDAAAAAAAAIlEgsxHYe7mlcSxOwOnapg4WHRT5UuB+vWF8uMz6Oxu5Bf9DAgAAAAAAABYAFAnkWBtaeW/v/BxPBmkgboAct6nrVkwAAAAAAAAiUSCJwof7Xh35Js6aL/iDn8TPfRHPzAuFSuiewIWArB8uOgAAAAAI/AJtZQNzaWdAvHgNmehOZ6jBZiu5ckCncLnVeViAr3PlKIgR2v8NPzhW/OXpu8oaK4b3bgRIL/qrMT5WlpgdybZGXVaAxSJVIwv8Am1lBnNpZ2V4cAhCeTbOW+ZAAAABAStNAQAAAAAAACJRIDCs9kAJfdTiQei7Cf+RSH3Cwr5oxFjNj27APixQD2L4ARcgc+G1jBTUGzdWX447VOZ6zEec2eeffMqg8KTrQ6plriIAAQErrtgAAAAAAAAiUSCJwof7Xh35Js6aL/iDn8TPfRHPzAuFSuiewIWArB8uOgEXIK0XtHBW7oxyV677kvzIL56kaRx0i5AADj3yumRxQD17AAAAAAA=
Calculating Taproot address...
Taproot Address: bc1p38pg0767rhujdn569lug887yea73rn7vpwz546y7czzcptql9caq6cafwt
TapKeyBytes: 89c287fb5e1df926ce9a2ff8839fc4cf7d11cfcc0b854ae89ec08580ac1f2e3a
2024/11/27 12:13:42 Taproot Address: bc1p38pg0767rhujdn569lug887yea73rn7vpwz546y7czzcptql9caq6cafwt
Public Key (Hex): ad17b47056ee8c7257aefb92fcc82f9ea4691c748b90000e3df2ba6471403d7b
Decoding PSBT...
PSBT successfully decoded.
Parsing PSBT...
PSBT successfully parsed.
Creating MultiPrevOutFetcher...
Adding previous output for input 0
Adding previous output for input 1
MultiPrevOutFetcher successfully created.
Signing input 1...
Input uses Taproot.
Creating TxSigHashes...
TxSigHashes successfully created.
PkScript: 512089c287fb5e1df926ce9a2ff8839fc4cf7d11cfcc0b854ae89ec08580ac1f2e3a
Amount: 55470
TaprootInternalKey of input 1: ad17b47056ee8c7257aefb92fcc82f9ea4691c748b90000e3df2ba6471403d7b
Public key from WIF: ad17b47056ee8c7257aefb92fcc82f9ea4691c748b90000e3df2ba6471403d7b
The signing key matches the TaprootInternalKey of the input.
Calculating sighash...
Sighash: 92aec029f002090d7621fffce4ebac7ff333d971e832db3980bb93112cb851ce
Creating signature...
Signature: 98dcfc29a85bc89ed35a86fa8073d5708ea93e095f4171b65c72ed12227d537a849b539ef6f9b845aeef61ff0bd4e94b664981e14a29e2443ddca98ecb9360f3
Signature for input 1 added.
Serializing signed PSBT...
Signed PSBT (Base64): cHNidP8BAPwCAAAAAgupxw+/SJhtowTzcr1wx9c/uanFKaJ3yU3J6UKjMTMzlAEAAAD/////Q9g6nnkQbdroR/sr2jeNqZ9nlZbVuqSNdjakxTxLgYADAAAAAP////8ETQEAAAAAAAAiUSCJwof7Xh35Js6aL/iDn8TPfRHPzAuFSuiewIWArB8uOmGDAAAAAAAAIlEgsxHYe7mlcSxOwOnapg4WHRT5UuB+vWF8uMz6Oxu5Bf9DAgAAAAAAABYAFAnkWBtaeW/v/BxPBmkgboAct6nrVkwAAAAAAAAiUSCJwof7Xh35Js6aL/iDn8TPfRHPzAuFSuiewIWArB8uOgAAAAAI/AJtZQNzaWdAvHgNmehOZ6jBZiu5ckCncLnVeViAr3PlKIgR2v8NPzhW/OXpu8oaK4b3bgRIL/qrMT5WlpgdybZGXVaAxSJVIwv8Am1lBnNpZ2V4cAhCeTbOW+ZAAAABAStNAQAAAAAAACJRIDCs9kAJfdTiQei7Cf+RSH3Cwr5oxFjNj27APixQD2L4ARcgc+G1jBTUGzdWX447VOZ6zEec2eeffMqg8KTrQ6plriIAAQErrtgAAAAAAAAiUSCJwof7Xh35Js6aL/iDn8TPfRHPzAuFSuiewIWArB8uOgETQJjc/CmoW8ie01qG+oBz1XCOqT4JX0Fxtlxy7RIifVN6hJtTnvb5uEWu72H/C9TpS2ZJgeFKKeJEPdypjsuTYPMBFyCtF7RwVu6Mcleu+5L8yC+epGkcdIuQAA498rpkcUA9ewAAAAAA

PSBT MY Script output:

cHNidP8BAPwCAAAAAgupxw+/SJhtowTzcr1wx9c/uanFKaJ3yU3J6UKjMTMzlAEAAAD/////Q9g6nnkQbdroR/sr2jeNqZ9nlZbVuqSNdjakxTxLgYADAAAAAP////8ETQEAAAAAAAAiUSCJwof7Xh35Js6aL/iDn8TPfRHPzAuFSuiewIWArB8uOmGDAAAAAAAAIlEgsxHYe7mlcSxOwOnapg4WHRT5UuB+vWF8uMz6Oxu5Bf9DAgAAAAAAABYAFAnkWBtaeW/v/BxPBmkgboAct6nrVkwAAAAAAAAiUSCJwof7Xh35Js6aL/iDn8TPfRHPzAuFSuiewIWArB8uOgAAAAAI/AJtZQNzaWdAvHgNmehOZ6jBZiu5ckCncLnVeViAr3PlKIgR2v8NPzhW/OXpu8oaK4b3bgRIL/qrMT5WlpgdybZGXVaAxSJVIwv8Am1lBnNpZ2V4cAhCeTbOW+ZAAAABAStNAQAAAAAAACJRIDCs9kAJfdTiQei7Cf+RSH3Cwr5oxFjNj27APixQD2L4ARcgc+G1jBTUGzdWX447VOZ6zEec2eeffMqg8KTrQ6plriIAAQErrtgAAAAAAAAiUSCJwof7Xh35Js6aL/iDn8TPfRHPzAuFSuiewIWArB8uOgETQJjc/CmoW8ie01qG+oBz1XCOqT4JX0Fxtlxy7RIifVN6hJtTnvb5uEWu72H/C9TpS2ZJgeFKKeJEPdypjsuTYPMBFyCtF7RwVu6Mcleu+5L8yC+epGkcdIuQAA498rpkcUA9ewAAAAAA

Wallet extension PSBT:

cHNidP8BAPwCAAAAAgupxw+/SJhtowTzcr1wx9c/uanFKaJ3yU3J6UKjMTMzlAEAAAD/////Q9g6nnkQbdroR/sr2jeNqZ9nlZbVuqSNdjakxTxLgYADAAAAAP////8ETQEAAAAAAAAiUSCJwof7Xh35Js6aL/iDn8TPfRHPzAuFSuiewIWArB8uOmGDAAAAAAAAIlEgsxHYe7mlcSxOwOnapg4WHRT5UuB+vWF8uMz6Oxu5Bf9DAgAAAAAAABYAFAnkWBtaeW/v/BxPBmkgboAct6nrVkwAAAAAAAAiUSCJwof7Xh35Js6aL/iDn8TPfRHPzAuFSuiewIWArB8uOgAAAAAI/AJtZQNzaWdAvHgNmehOZ6jBZiu5ckCncLnVeViAr3PlKIgR2v8NPzhW/OXpu8oaK4b3bgRIL/qrMT5WlpgdybZGXVaAxSJVIwv8Am1lBnNpZ2V4cAhCeTbOW+ZAAAABAStNAQAAAAAAACJRIDCs9kAJfdTiQei7Cf+RSH3Cwr5oxFjNj27APixQD2L4ARcgc+G1jBTUGzdWX447VOZ6zEec2eeffMqg8KTrQ6plriIAAQErrtgAAAAAAAAiUSCJwof7Xh35Js6aL/iDn8TPfRHPzAuFSuiewIWArB8uOgETQF/kABzHRRZdy9+33rcvJZxMBYhwywu6TY0f+XOEbwBdvcjHcy2tYjR2VM2YbAFCaS9xtHx+SgfAS6RmvTh4xh0BFyCtF7RwVu6Mcleu+5L8yC+epGkcdIuQAA498rpkcUA9ewAAAAAA

Raw PSBT (input psbt fot scirpt and extension):

cHNidP8BAPwCAAAAAgupxw+/SJhtowTzcr1wx9c/uanFKaJ3yU3J6UKjMTMzlAEAAAD/////Q9g6nnkQbdroR/sr2jeNqZ9nlZbVuqSNdjakxTxLgYADAAAAAP////8ETQEAAAAAAAAiUSCJwof7Xh35Js6aL/iDn8TPfRHPzAuFSuiewIWArB8uOmGDAAAAAAAAIlEgsxHYe7mlcSxOwOnapg4WHRT5UuB+vWF8uMz6Oxu5Bf9DAgAAAAAAABYAFAnkWBtaeW/v/BxPBmkgboAct6nrVkwAAAAAAAAiUSCJwof7Xh35Js6aL/iDn8TPfRHPzAuFSuiewIWArB8uOgAAAAAI/AJtZQNzaWdAvHgNmehOZ6jBZiu5ckCncLnVeViAr3PlKIgR2v8NPzhW/OXpu8oaK4b3bgRIL/qrMT5WlpgdybZGXVaAxSJVIwv8Am1lBnNpZ2V4cAhCeTbOW+ZAAAABAStNAQAAAAAAACJRIDCs9kAJfdTiQei7Cf+RSH3Cwr5oxFjNj27APixQD2L4ARcgc+G1jBTUGzdWX447VOZ6zEec2eeffMqg8KTrQ6plriIAAQErrtgAAAAAAAAiUSCJwof7Xh35Js6aL/iDn8TPfRHPzAuFSuiewIWArB8uOgEXIK0XtHBW7oxyV677kvzIL56kaRx0i5AADj3yumRxQD17AAAAAAA=

The big problem is that I don't understand why sighash is incorrect (and hence Schnorr's signature), but I don't think the problem is the formation of the schnorr signature. I think I'm considering something wrong to calculate the sighash OR not considering, although I can't figure out what exactly, since I don't get errors at any stage in my code other than checking the BTC CLI or POST to the server



from Recent Questions - Bitcoin Stack Exchange https://ift.tt/kD75lKi
via IFTTT

Popular posts from this blog

Bitcoin Mining Could Be Strengthening The Ruble, Russian Central Bank Says

Crypto Exec Warns Tokenization Is Moving Faster Than Expected

Bitwise Clients Pour $69M Into Solana as Bulls Fight to Reclaim $200 Resistance Zone