btcsuite / btcd

An alternative full node bitcoin implementation written in Go (golang)
https://github.com/btcsuite/btcd/blob/master/README.md
ISC License
6.26k stars 2.37k forks source link

Can not create P2WPKH transaction #1989

Closed phanquocky closed 1 year ago

phanquocky commented 1 year ago

I have a unspents transaction:

$btcctl --wallet listunspent
[
  {
    "txid": "81ca4185a877ff4aefa11b52a3b8865524ad044a8e3422580f2ecbd4cfd402a1",
    "vout": 1,
    "address": "tb1qen8dmh9e8fx7m0xl9gz3774w44f39wccq86xm6",
    "account": "default",
    "scriptPubKey": "0014cccedddcb93a4dedbcdf2a051f7aaead5312bb18",
    "amount": 0.005,
    "confirmations": 1903,
    "spendable": true
  },
  {
    "txid": "81ca4185a877ff4aefa11b52a3b8865524ad044a8e3422580f2ecbd4cfd402a1",
    "vout": 0,
    "address": "mgzwyoP3wwurRGKruLnVTRxkzNMJh53J1Y",
    "account": "default",
    "scriptPubKey": "76a9141043828f7c914a26a5bcb8a1a36fafcae41a550a88ac",
    "amount": 0.00575776,
    "confirmations": 1903,
    "spendable": true
  },
  {
    "txid": "62bc5ee6179a45445adc01f09ade947955fb31bbeda6583b722f5d4641efe85f",
    "vout": 1,
    "address": "moioPjJgk4JYnB83UivqxfVMjgrZK6dsAk",
    "account": "default",
    "scriptPubKey": "76a91459fe9112328ff6fdde80fb03fb5e1a0d2963b3d688ac",
    "amount": 0.00000229,
    "confirmations": 1994,
    "spendable": true
  }
]

I want to spend my first unspent transaction:

// code go
package main
import (
    "fmt"
    "io/ioutil"
    "log"
    "math"
    "path/filepath"
    "time"

    "github.com/btcsuite/btcd/btcjson"
    "github.com/btcsuite/btcd/btcutil"
    "github.com/btcsuite/btcd/chaincfg"
    "github.com/btcsuite/btcd/chaincfg/chainhash"
    "github.com/btcsuite/btcd/rpcclient"
)

func main() {

    ntfnHandlers := rpcclient.NotificationHandlers{
        OnAccountBalance: func(account string, balance btcutil.Amount, confirmed bool) {
            log.Printf("New balance for account %s: %v", account,
                balance)
        },
    }

    // Connect to local btcwallet RPC server using websockets.
    certHomeDir := btcutil.AppDataDir("btcwallet", false)
    certs, err := ioutil.ReadFile(filepath.Join(certHomeDir, "rpc.cert"))
    if err != nil {
        log.Fatal(err)
    }
    connCfg := &rpcclient.ConnConfig{
        Host:         "localhost:18332",
        Endpoint:     "ws",
        User:         "eeyHzXbqKIxOrZquE5c9POR/8mI=",
        Pass:         "q7pfGKtXrHE95qpS3oFGdAiAAXM=",
        Certificates: certs,
        Params:       "testnet3",
    }
    client, err := rpcclient.New(connCfg, &ntfnHandlers)
    if err != nil {
        log.Fatal(err)
    }

    // want spend 0.004 btc
    btcAmount := btcutil.Amount(400000)

    unspents, err := client.ListUnspent()
    if err != nil {
        log.Println("Cannot listUnspent")
    }

    inputs := make([]btcjson.TransactionInput, 0)
    inputs = append(inputs, btcjson.TransactionInput{Txid: unspents[0].TxID, Vout: unspents[0].Vout})

    // send coin to this address
    destinationAddress, err := btcutil.DecodeAddress("mhzsEj6fpWmkyi2xwBnb1CeKzEWxLrLU6g", &chaincfg.TestNet3Params)
    if err != nil {
        log.Println("Your address is not valid")
    }

    amounts := make(map[btcutil.Address]btcutil.Amount)
    amounts[destinationAddress] = btcAmount

    var locktime int64 = 0
    log.Println("intputs", inputs)
    log.Println("amounts", amounts)
    rawTx, err := client.CreateRawTransaction(inputs, amounts, &locktime)
    if err != nil {
        log.Println("Can't create raw transaction")
    }
    log.Println("tx", rawTx.TxHash())

    err = client.WalletPassphrase("161002", 100)
    if err != nil {
        log.Println("Cannot unlock wallet with passphrase")
    }

    fetcher := txscript.NewCannedPrevOutputFetcher([]byte(unspents[0].ScriptPubKey), int64(unspents[0].Amount))
    sigHashes := txscript.NewTxSigHashes(rawTx, fetcher)

    address, err := btcutil.DecodeAddress(unspents[0].Address, &chaincfg.TestNet3Params)
    if err != nil {
        log.Println("Cannot address")
    }
    privKey, err := client.DumpPrivKey(address)
    if err != nil {
        log.Println("Cannot Privkey")
    }
    log.Println("Private Key:", privKey.PrivKey)

    witness, err := txscript.WitnessSignature(rawTx, sigHashes, 0, rawTx.TxOut[0].Value, []byte(unspents[0].ScriptPubKey), txscript.SigHashAll, privKey.PrivKey, true)
    if err != nil {
        log.Println("Cannot witness")
        log.Println(err)
    }
    log.Println("witness: ", witness)
    rawTx.TxIn[0].Witness = witness

    hashTx, err := client.SendRawTransaction(rawTx, true)
    if err != nil {
        log.Println(err)
    }
    log.Println("hashtx", hashTx)

    time.AfterFunc(time.Second*10, func() {
        log.Println("Client shutting down...")
        client.Shutdown()
        log.Println("Client shutdown complete.")
    })

    // Wait until the client either shuts down gracefully (or the user
    // terminates the process with Ctrl+C).
    client.WaitForShutdown()
}

But i got error but I don't find any information for this error. If you don't mind, you can help point out what error in this code

error: 
2023/05/31 11:46:41 intputs [{81ca4185a877ff4aefa11b52a3b8865524ad044a8e3422580f2ecbd4cfd402a1 1}]
2023/05/31 11:46:41 amounts map[mhzsEj6fpWmkyi2xwBnb1CeKzEWxLrLU6g:0.004 BTC]
2023/05/31 11:46:41 tx 1b3be042f83763b778a4813ce82ec99c01280ab49c2b2af23cc121bf4a184cb7
2023/05/31 11:46:42 Private Key: &{0525753a322636e0be1435d59b2a74b9ce746cb62c03327517136288a1f9f568}
2023/05/31 11:46:42 witness:  [[48 68 2 32 14 125 251 215 107 158 54 113 191 86 215 113 131 202 83 34 153 47 49 81 189 86 42 160 222 73 5 111 141 46 59 28 2 32 83 2 159 102 184 116 235 31 230 37 96 223 48 246 44 187 190 108 124 253 99 14 11 159 217 145 62 70 238 75 80 95 1] [3 184 138 186 135 21 69 153 124 186 25 37 5 225 65 86 255 143 222 187 163 102 34 77 202 196 123 184 113 155 220 119 130]]
2023/05/31 11:46:42 -25: TX rejected: failed to validate input 1b3be042f83763b778a4813ce82ec99c01280ab49c2b2af23cc121bf4a184cb7:0 which references output 81ca4185a877ff4aefa11b52a3b8865524ad044a8e3422580f2ecbd4cfd402a1:1 - signature not empty on failed checksig (input witness [304402200e7dfbd76b9e3671bf56d77183ca5322992f3151bd562aa0de49056f8d2e3b1c022053029f66b874eb1fe62560df30f62cbbbe6c7cfd630e0b9fd9913e46ee4b505f01 03b88aba871545997cba192505e14156ff8fdebba366224dcac47bb8719bdc7782], input script bytes , prev output script bytes 0014cccedddcb93a4dedbcdf2a051f7aaead5312bb18)
2023/05/31 11:46:42 hashtx <nil>
2023/05/31 11:46:52 Client shutting down...
2023/05/31 11:46:52 Client shutdown complete.
guggero commented 1 year ago

witness, err := txscript.WitnessSignature(rawTx, sigHashes, 0, rawTx.TxOut[0].Value, []byte(unspents[0].ScriptPubKey), txscript.SigHashAll, privKey.PrivKey, true)

The rawTx.TxOut[0].Value is wrong. This needs to be the input value (so unspents[0].Value).

phanquocky commented 1 year ago

I have changed the code to: witness, err := txscript.WitnessSignature(rawTx, sigHashes, 0, int64(unspents[0].Amount), []byte(unspents[0].ScriptPubKey), txscript.SigHashAll, privKey.PrivKey, true) but I also get same error: 2023/05/31 14:41:07 intputs [{81ca4185a877ff4aefa11b52a3b8865524ad044a8e3422580f2ecbd4cfd402a1 1}] 2023/05/31 14:41:07 amounts map[mhzsEj6fpWmkyi2xwBnb1CeKzEWxLrLU6g:0.004 BTC] 2023/05/31 14:41:07 tx {1 [0xc0002a64e0] [0xc00002cf40] 0} 2023/05/31 14:41:07 Private Key: &{0525753a322636e0be1435d59b2a74b9ce746cb62c03327517136288a1f9f568} 2023/05/31 14:41:07 witness: [[48 69 2 33 0 174 192 136 224 161 227 210 73 250 200 186 136 103 243 33 57 182 93 170 236 138 74 162 41 126 65 5 100 240 181 100 169 2 32 41 99 4 243 148 45 49 56 62 49 177 102 149 112 179 7 9 47 172 118 79 210 10 22 194 60 161 212 43 61 142 233 1] [3 184 138 186 135 21 69 153 124 186 25 37 5 225 65 86 255 143 222 187 163 102 34 77 202 196 123 184 113 155 220 119 130]] 2023/05/31 14:41:07 -25: TX rejected: failed to validate input 1b3be042f83763b778a4813ce82ec99c01280ab49c2b2af23cc121bf4a184cb7:0 which references output 81ca4185a877ff4aefa11b52a3b8865524ad044a8e3422580f2ecbd4cfd402a1:1 - signature not empty on failed checksig (input witness [3045022100aec088e0a1e3d249fac8ba8867f32139b65daaec8a4aa2297e410564f0b564a90220296304f3942d31383e31b1669570b307092fac764fd20a16c23ca1d42b3d8ee901 03b88aba871545997cba192505e14156ff8fdebba366224dcac47bb8719bdc7782], input script bytes , prev output script bytes 0014cccedddcb93a4dedbcdf2a051f7aaead5312bb18) 2023/05/31 14:41:07 hashtx

guggero commented 1 year ago

What's the value of []byte(unspents[0].ScriptPubKey)? I think that might need to be hex decoded instead of just cast to a byte slice (everywhere it's used).

phanquocky commented 1 year ago

the value of unspents[0].ScriptPubKey is string: 0014cccedddcb93a4dedbcdf2a051f7aaead5312bb18 the value of []byte(unspents[0].ScriptPubKey) is : [48 48 49 52 99 99 99 101 100 100 100 99 98 57 51 97 52 100 101 100 98 99 100 102 50 97 48 53 49 102 55 97 97 101 97 100 53 51 49 50 98 98 49 56]

phanquocky commented 1 year ago

I have replaced all of the []byte(unspents[0].ScriptPubKey) to scriptPubkeyUnspent, _ := hex.DecodeString(unspents[0].ScriptPubKey). But I also got the same error

guggero commented 1 year ago

Can you post the full code as it looks now? Please use triple back ticks to format (or the UI elements of GitHub).

phanquocky commented 1 year ago

Oh sorry about the format of the code:


package main

import (
    "encoding/hex"
    "fmt"
    "io/ioutil"
    "log"
    "math"
    "path/filepath"
    "time"

    "github.com/btcsuite/btcd/btcjson"
    "github.com/btcsuite/btcd/btcutil"
    "github.com/btcsuite/btcd/chaincfg"
    "github.com/btcsuite/btcd/chaincfg/chainhash"
    "github.com/btcsuite/btcd/rpcclient"
    "github.com/btcsuite/btcd/txscript"
)

func main() {

    ntfnHandlers := rpcclient.NotificationHandlers{
        OnAccountBalance: func(account string, balance btcutil.Amount, confirmed bool) {
            log.Printf("New balance for account %s: %v", account,
                balance)
        },
    }

    // Connect to local btcwallet RPC server using websockets.
    certHomeDir := btcutil.AppDataDir("btcwallet", false)
    certs, err := ioutil.ReadFile(filepath.Join(certHomeDir, "rpc.cert"))
    if err != nil {
        log.Fatal(err)
    }
    connCfg := &rpcclient.ConnConfig{
        Host:         "localhost:18332",
        Endpoint:     "ws",
        User:         "eeyHzXbqKIxOrZquE5c9POR/8mI=",
        Pass:         "q7pfGKtXrHE95qpS3oFGdAiAAXM=",
        Certificates: certs,
        Params:       "testnet3",
    }
    client, err := rpcclient.New(connCfg, &ntfnHandlers)
    if err != nil {
        log.Fatal(err)
    }

    // want spend 0.004 btc
    btcAmount := btcutil.Amount(400000)

    unspents, err := client.ListUnspent()
    if err != nil {
        log.Println("Cannot listUnspent")
    }

    inputs := make([]btcjson.TransactionInput, 0)
    inputs = append(inputs, btcjson.TransactionInput{Txid: unspents[0].TxID, Vout: unspents[0].Vout})

    // send coin to this address
    destinationAddress, err := btcutil.DecodeAddress("mhzsEj6fpWmkyi2xwBnb1CeKzEWxLrLU6g", &chaincfg.TestNet3Params)
    if err != nil {
        log.Println("Your address is not valid")
    }

    amounts := make(map[btcutil.Address]btcutil.Amount)
    amounts[destinationAddress] = btcAmount

    var locktime int64 = 0
    log.Println("intputs", inputs)
    log.Println("amounts", amounts)
    rawTx, err := client.CreateRawTransaction(inputs, amounts, &locktime)
    if err != nil {
        log.Println("Can't create raw transaction")
    }
    log.Println("tx", *rawTx)

    err = client.WalletPassphrase("161002", 100)
    if err != nil {
        log.Println("Cannot unlock wallet with passphrase")
    }

    scriptPubkeyUnspent, _ := hex.DecodeString(unspents[0].ScriptPubKey)
    fetcher := txscript.NewCannedPrevOutputFetcher(scriptPubkeyUnspent, int64(unspents[0].Amount))
    sigHashes := txscript.NewTxSigHashes(rawTx, fetcher)
    address, err := btcutil.DecodeAddress(unspents[0].Address, &chaincfg.TestNet3Params)
    if err != nil {
        log.Println("Cannot address")
    }
    privKey, err := client.DumpPrivKey(address)
    if err != nil {
        log.Println("Cannot Privkey")
    }
    log.Println("Private Key:", privKey.PrivKey)

    log.Println("unspents[0].ScriptPubKey", unspents[0].ScriptPubKey)
    witness, err := txscript.WitnessSignature(rawTx, sigHashes, 0, int64(unspents[0].Amount), scriptPubkeyUnspent, txscript.SigHashAll, privKey.PrivKey, true)
    if err != nil {
        log.Println("Cannot witness")
        log.Println(err)
    }
    log.Println("witness: ", witness)
    rawTx.TxIn[0].Witness = witness

    hashTx, err := client.SendRawTransaction(rawTx, true)
    if err != nil {
        log.Println(err)
    }
    log.Println("hashtx", hashTx)

    time.AfterFunc(time.Second*10, func() {
        log.Println("Client shutting down...")
        client.Shutdown()
        log.Println("Client shutdown complete.")
    })

    // Wait until the client either shuts down gracefully (or the user
    // terminates the process with Ctrl+C).
    client.WaitForShutdown()
}

The content in the console:

2023/05/31 15:47:11 intputs [{81ca4185a877ff4aefa11b52a3b8865524ad044a8e3422580f2ecbd4cfd402a1 1}]
2023/05/31 15:47:11 amounts map[mhzsEj6fpWmkyi2xwBnb1CeKzEWxLrLU6g:0.004 BTC]
2023/05/31 15:47:11 tx {1 [0xc0000d2060] [0xc000098120] 0}
2023/05/31 15:47:12 Private Key: &{0525753a322636e0be1435d59b2a74b9ce746cb62c03327517136288a1f9f568}
2023/05/31 15:47:12 unspents[0].ScriptPubKey 0014cccedddcb93a4dedbcdf2a051f7aaead5312bb18
2023/05/31 15:47:12 witness:  [[48 68 2 32 94 147 219 202 124 167 136 33 12 228 172 255 164 63 59 96 210 250 137 77 13 8 109 9 50 128 86 43 170 214 106 196 2 32 16 120 26 84 172 188 74 56 253 93 121 93 91 251 183 48 88 133 9 205 213 202 188 164 131 139 93 96 66 229 14 87 1] [3 184 138 186 135 21 69 153 124 186 25 37 5 225 65 86 255 143 222 187 163 102 34 77 202 196 123 184 113 155 220 119 130]]
2023/05/31 15:47:12 -25: TX rejected: failed to validate input 1b3be042f83763b778a4813ce82ec99c01280ab49c2b2af23cc121bf4a184cb7:0 which references output 81ca4185a877ff4aefa11b52a3b8865524ad044a8e3422580f2ecbd4cfd402a1:1 - signature not empty on failed checksig (input witness [304402205e93dbca7ca788210ce4acffa43f3b60d2fa894d0d086d093280562baad66ac4022010781a54acbc4a38fd5d795d5bfbb730588509cdd5cabca4838b5d6042e50e5701 03b88aba871545997cba192505e14156ff8fdebba366224dcac47bb8719bdc7782], input script bytes , prev output script bytes 0014cccedddcb93a4dedbcdf2a051f7aaead5312bb18)
2023/05/31 15:47:12 hashtx <nil>
2023/05/31 15:47:22 Client shutting down...
2023/05/31 15:47:22 Client shutdown complete.
phanquocky commented 1 year ago

And the list of the unspent I have is:

[
  {
    "txid": "81ca4185a877ff4aefa11b52a3b8865524ad044a8e3422580f2ecbd4cfd402a1",
    "vout": 1,
    "address": "tb1qen8dmh9e8fx7m0xl9gz3774w44f39wccq86xm6",
    "account": "default",
    "scriptPubKey": "0014cccedddcb93a4dedbcdf2a051f7aaead5312bb18",
    "amount": 0.005,
    "confirmations": 1903,
    "spendable": true
  },
  {
    "txid": "81ca4185a877ff4aefa11b52a3b8865524ad044a8e3422580f2ecbd4cfd402a1",
    "vout": 0,
    "address": "mgzwyoP3wwurRGKruLnVTRxkzNMJh53J1Y",
    "account": "default",
    "scriptPubKey": "76a9141043828f7c914a26a5bcb8a1a36fafcae41a550a88ac",
    "amount": 0.00575776,
    "confirmations": 1903,
    "spendable": true
  },
  {
    "txid": "62bc5ee6179a45445adc01f09ade947955fb31bbeda6583b722f5d4641efe85f",
    "vout": 1,
    "address": "moioPjJgk4JYnB83UivqxfVMjgrZK6dsAk",
    "account": "default",
    "scriptPubKey": "76a91459fe9112328ff6fdde80fb03fb5e1a0d2963b3d688ac",
    "amount": 0.00000229,
    "confirmations": 1994,
    "spendable": true
  }
]
guggero commented 1 year ago

Hmm, it looks correct. So might be something small now. Can you add this before client.SendRawTransaction(rawTx, true) and paste the output here?

var buf bytes.Buffer
_ = rawTx.Serialize(&buf)
fmt.Printf("Raw tx: %x\n", buf.Bytes())

Untested code written from memory, might need slight adjustments, but you get the idea I hope.

phanquocky commented 1 year ago

sorry for the late reply. I just insert your code to my code i got the hex of the transaction

01000000000101a102d4cfd4cb2e0f5822348e4a04ad245586b8a3521ba1ef4aff77a88541ca810100000000ffffffff01801a0600000000001976a9141b37ad4401250185aeb05249ccd90f84caccab0888ac0247304402205e93dbca7ca788210ce4acffa43f3b60d2fa894d0d086d093280562baad66ac4022010781a54acbc4a38fd5d795d5bfbb730588509cdd5cabca4838b5d6042e50e57012103b88aba871545997cba192505e14156ff8fdebba366224dcac47bb8719bdc778200000000

And I use the command decoderawtransaction

{
  "txid": "1b3be042f83763b778a4813ce82ec99c01280ab49c2b2af23cc121bf4a184cb7",
  "version": 1,
  "locktime": 0,
  "vin": [
    {
      "txid": "81ca4185a877ff4aefa11b52a3b8865524ad044a8e3422580f2ecbd4cfd402a1",
      "vout": 1,
      "scriptSig": {
        "asm": "",
        "hex": ""
      },
      "txinwitness": [
        "304402205e93dbca7ca788210ce4acffa43f3b60d2fa894d0d086d093280562baad66ac4022010781a54acbc4a38fd5d795d5bfbb730588509cdd5cabca4838b5d6042e50e5701",
        "03b88aba871545997cba192505e14156ff8fdebba366224dcac47bb8719bdc7782"
      ],
      "sequence": 4294967295
    }
  ],
  "vout": [
    {
      "value": 0.004,
      "n": 0,
      "scriptPubKey": {
        "asm": "OP_DUP OP_HASH160 1b37ad4401250185aeb05249ccd90f84caccab08 OP_EQUALVERIFY OP_CHECKSIG",
        "hex": "76a9141b37ad4401250185aeb05249ccd90f84caccab0888ac",
        "reqSigs": 1,
        "type": "pubkeyhash",
        "addresses": [
          "mhzsEj6fpWmkyi2xwBnb1CeKzEWxLrLU6g"
        ]
      }
    }
  ]
}
guggero commented 1 year ago

I think you might need to set the version of the rawTx to 2 (before signing), as version 1 is deprecated (and therefore probably not allowed for witness spends).

phanquocky commented 1 year ago

I have set the version of the rawTx to 2 but it doesn't work. But I found the code show error:

in txscript/opcode.go: in function opcodeCheckSig

case !valid && vm.hasFlag(ScriptVerifyNullFail) && len(fullSigBytes) > 0:
        str := "signature not empty on failed checksig"
        return scriptError(ErrNullFail, str)
    }
phanquocky commented 1 year ago

full function of opcodeChecksig

func opcodeCheckSig(op *opcode, data []byte, vm *Engine) error {
    pkBytes, err := vm.dstack.PopByteArray()
    if err != nil {
        return err
    }

    fullSigBytes, err := vm.dstack.PopByteArray()
    if err != nil {
        return err
    }

    // The signature actually needs needs to be longer than this, but at
    // least 1 byte is needed for the hash type below.  The full length is
    // checked depending on the script flags and upon parsing the signature.
    //
    // This only applies if tapscript verification isn't active, as this
    // check is done within the sighash itself.
    if vm.taprootCtx == nil && len(fullSigBytes) < 1 {
        vm.dstack.PushBool(false)
        return nil
    }

    var sigVerifier signatureVerifier
    switch {
    // If no witness program is active, then we're verifying under the
    // base consensus rules.
    case vm.witnessProgram == nil:
        sigVerifier, err = newBaseSigVerifier(
            pkBytes, fullSigBytes, vm,
        )
        if err != nil {
            var scriptErr Error
            if errors.As(err, &scriptErr) {
                return err
            }

            vm.dstack.PushBool(false)
            return nil
        }

    // If the base segwit version is active, then we'll create the verifier
    // that factors in those new consensus rules.
    case vm.isWitnessVersionActive(BaseSegwitWitnessVersion):
        sigVerifier, err = newBaseSegwitSigVerifier(
            pkBytes, fullSigBytes, vm,
        )
        if err != nil {
            var scriptErr Error
            if errors.As(err, &scriptErr) {
                return err
            }

            vm.dstack.PushBool(false)
            return nil
        }

    // Otherwise, this is routine tapscript execution.
    case vm.taprootCtx != nil:
        // Account for changes in the sig ops budget after this
        // execution, but only for non-empty signatures.
        if len(fullSigBytes) > 0 {
            if err := vm.taprootCtx.tallysigOp(); err != nil {
                return err
            }
        }

        // Empty public keys immediately cause execution to fail.
        if len(pkBytes) == 0 {
            return scriptError(ErrTaprootPubkeyIsEmpty, "")
        }

        // If this is tapscript execution, and the signature was
        // actually an empty vector, then we push on an empty vector
        // and continue execution from there, but only if the pubkey
        // isn't empty.
        if len(fullSigBytes) == 0 {
            vm.dstack.PushByteArray([]byte{})
            return nil
        }

        // If the constructor fails immediately, then it's because
        // the public key size is zero, so we'll fail all script
        // execution.
        sigVerifier, err = newBaseTapscriptSigVerifier(
            pkBytes, fullSigBytes, vm,
        )
        if err != nil {
            return err
        }

    default:
        // We skip segwit v1 in isolation here, as the v1 rules aren't
        // used in script execution (for sig verification) and are only
        // part of the top-level key-spend verification which we
        // already skipped.
        //
        // In other words, this path shouldn't ever be reached
        //
        // TODO(roasbeef): return an error?
    }

    valid := sigVerifier.Verify()

    switch {
    // For tapscript, and prior execution with null fail active, if the
    // signature is invalid, then this MUST be an empty signature.
    case !valid && vm.taprootCtx != nil && len(fullSigBytes) != 0:
        fallthrough
    case !valid && vm.hasFlag(ScriptVerifyNullFail) && len(fullSigBytes) > 0:
        str := "signature not empty on failed checksig"
        return scriptError(ErrNullFail, str)
    }

    vm.dstack.PushBool(valid)
    return nil
}
guggero commented 1 year ago

I'm not sure what's wrong... I got it working locally with this (not sure what client.CreateRawTransaction() actually does):

    txid, err := chainhash.NewHashFromStr(unspents[0].TxID)
    if err != nil {
        log.Printf("error parsing hash")
    }
    utxo := &wire.TxOut{
        Value:    int64(unspents[0].Amount),
        PkScript: scriptPubkeyUnspent,
    }
    rawTx := &wire.MsgTx{
        Version: 2,
        TxIn: []*wire.TxIn{{
            PreviousOutPoint: wire.OutPoint{
                Hash:  *txid,
                Index: unspents[0].Vout,
            },
        }},
        TxOut: []*wire.TxOut{{
            Value:    int64(btcAmount),
            PkScript: destinationAddress.ScriptAddress(),
        }},
        LockTime: 0,
    }

    fetcher := txscript.NewCannedPrevOutputFetcher(utxo.PkScript, utxo.Value)
    sigHashes := txscript.NewTxSigHashes(rawTx, fetcher)