Cannot broadcast bitcoin transaction Golang

Viewed 55

I've encouraged an error broadcasting my bitcoin transaction here.

Error validating transaction: Error running script for input 0 referencing 78b6df775e4132f747d8d15909cfe61834e6ff1d3b1e42219ace64aae7151e18 at 0: Script was NOT verified successfully.. 

I've tried to put decoded Asm PkScript instead of PubKey in TxIn. I've tried to find similar error on sof but their steps didn't work for me. Can anyone find out what is wrong with this tx? because my vm didn't find any troubles.

Hex transaction:

0100000001181e15e7aa64ce9a21421e3b1dffe63418e6cf0959d1d847f732415e77dfb678000000008a47304402200aa339f80c705a22180c8edd4acc4baa5bc76c9e8f64852eb3cb507f027aee9c02206da34700e76df568ef018fe346bb5b9c4484506ee84740916d6c3b454c2e1d3f0141042a1b4d9533ff37cd290b95016056cacbdbbf99b5975345dc5837a95f698f8a26d74f726e2a1e62552107953595502c8078a84311690c61ace3f22644ae33a0abffffffff02d0070000000000001976a91437383464376cccaf2ae4c8a121805f45bf544e4488acc8320000000000001976a914a404271e971d58240add530813e514b0ffe45f3588ac00000000

Decoded transaction:

{
    "addresses": [
        "mvUC64ETwYLbTjH38ZVigDwMBtCS9TbuT1",
        "mkYvnmm3KUBkvVqUAYsG6A6amt5Dva4jzX"
    ],
    "block_height": -1,
    "block_index": -1,
    "confirmations": 0,
    "double_spend": false,
    "fees": 5000,
    "hash": "410f8119e9c225fc197a21ede7f9e6902f85a36010a2776dd3fdc2663072a36c",
    "inputs": [
        {
            "addresses": [
                "mvUC64ETwYLbTjH38ZVigDwMBtCS9TbuT1"
            ],
            "age": 2345496,
            "output_index": 0,
            "output_value": 20000,
            "prev_hash": "78b6df775e4132f747d8d15909cfe61834e6ff1d3b1e42219ace64aae7151e18",
            "script": "47304402200aa339f80c705a22180c8edd4acc4baa5bc76c9e8f64852eb3cb507f027aee9c02206da34700e76df568ef018fe346bb5b9c4484506ee84740916d6c3b454c2e1d3f0141042a1b4d9533ff37cd290b95016056cacbdbbf99b5975345dc5837a95f698f8a26d74f726e2a1e62552107953595502c8078a84311690c61ace3f22644ae33a0ab",
            "script_type": "pay-to-pubkey-hash",
            "sequence": 4294967295
        }
    ],
    "outputs": [
        {
            "addresses": [
                "mkYvnmm3KUBkvVqUAYsG6A6amt5Dva4jzX"
            ],
            "script": "76a91437383464376cccaf2ae4c8a121805f45bf544e4488ac",
            "script_type": "pay-to-pubkey-hash",
            "value": 2000
        },
        {
            "addresses": [
                "mvUC64ETwYLbTjH38ZVigDwMBtCS9TbuT1"
            ],
            "script": "76a914a404271e971d58240add530813e514b0ffe45f3588ac",
            "script_type": "pay-to-pubkey-hash",
            "value": 13000
        }
    ],
    "preference": "low",
    "received": "2022-09-06T14:10:50.749940874Z",
    "relayed_by": "44.204.10.232",
    "size": 257,
    "total": 15000,
    "ver": 1,
    "vin_sz": 1,
    "vout_sz": 2,
    "vsize": 257
}
func main() {
    connCfg := &rpcclient.ConnConfig{ //smth here
    }
    client, err := rpcclient.New(connCfg, nil)
    if err != nil {
        log.Fatal(err)
    }
    defer client.Shutdown()
    utxoMap := make(map[string]string)
    utxoMap["mvUC64ETwYLbTjH38ZVigDwMBtCS9TbuT1"] = "78b6df775e4132f747d8d15909cfe61834e6ff1d3b1e42219ace64aae7151e18"
    rawTx, err := CreateRawTx("mvUC64ETwYLbTjH38ZVigDwMBtCS9TbuT1", "mkYvnmm3KUBkvVqUAYsG6A6amt5Dva4jzX", 2000, utxoMap, client)
    if err != nil {
        println(err.Error())
    }
    signedTx, err := Sign(rawTx, "private key here")
    if err != nil {
        println(err.Error())
    }
    fmt.Println(signedTx)
}

type UTXO struct {
    previousTxID string
    pubKeyScript string
    Asm          string
    Amount       decimal.Decimal
}

func GetUTXO(utxoMap map[string]string, address string, client *rpcclient.Client) (utxo UTXO, err error) {
    // unmarshal it, and extract necessary data
    hash, _ := chainhash.NewHashFromStr(utxoMap[address])
    tx, err := client.GetRawTransactionVerbose(hash)
    if err != nil {
        return utxo, errors.Wrapf(err, "GetUTXO.GetRawTransactionVerbose")
    }
    pubKeyScript := tx.Vout[0].ScriptPubKey.Hex
    previousTxID := utxoMap[address]
    amount := decimal.NewFromFloat(tx.Vout[0].Value)

    asm := tx.Vout[0].ScriptPubKey.Hex
    return UTXO{
        previousTxID: previousTxID,
        pubKeyScript: pubKeyScript,
        Asm:          asm,
        Amount:       amount,
    }, nil
}

func GetPayToAddrScript(address string) []byte {
    rcvAddress, _ := btcutil.DecodeAddress(address, &chaincfg.TestNet3Params)
    rcvScript, _ := txscript.PayToAddrScript(rcvAddress)
    return rcvScript
}

type TXRef struct {
    TXHash    string
    TXOutputN int
}

// get private key
func getKeyAddressFromPrivateKey(privateKey string, conf *chaincfg.Params) (*btcec.PrivateKey, string, error) {
    newWif, err := btcutil.DecodeWIF(privateKey)
    if err != nil {
        return nil, "", err
    }

    publicKey := newWif.PrivKey.PubKey().SerializeUncompressed()
    address, err := btcutil.NewAddressPubKeyHash(btcutil.Hash160(publicKey), conf)
    if err != nil {
        return nil, "", err
    }
    return newWif.PrivKey, address.EncodeAddress(), nil
}

// debug signing function
func Sign(rawTx, privateKey string) (string, error) {
    var signTx wire.MsgTx
    err := json.Unmarshal([]byte(rawTx), &signTx)
    if err != nil {
        return "", err
    }

    myPrivateKey, address, err := getKeyAddressFromPrivateKey(privateKey, &chaincfg.TestNet3Params)

    fmt.Println("address ", address)
    if err != nil {
        return "", err
    }
    txScript := GetPayToAddrScript(address)
    for i := 0; i < len(signTx.TxIn); i++ {
        sig, err := txscript.SignatureScript(
            &signTx,             // The tx to be signed.
            i,                   // The index of the txin the signature is for.
            txScript,            // The other half of the script from the PubKeyHash.
            txscript.SigHashAll, // The signature flags that indicate what the sig covers.
            myPrivateKey,        // The key to generate the signature with.
            false) // The compress sig flag. This saves space on the blockchain.

        if err != nil {
            return "", err
        }

        //refer from this link for sign multiple inputs https://bitcoin.stackexchange.com/questions/41209/how-to-sign-a-transaction-with-multiple-inputs
        signTx.TxIn[i].SignatureScript = sig

        //Validate signature
        flags := txscript.StandardVerifyFlags
        vm, err := txscript.NewEngine(txScript, &signTx, i, flags, nil, nil, signTx.TxOut[0].Value, nil)
        if err != nil {
            return "", err
        }
        if err := vm.Execute(); err != nil {
            return "", err
        }
    }
    // return signed tx
    var signedTx bytes.Buffer
    signTx.Serialize(&signedTx)
    hexSignedTx := hex.EncodeToString(signedTx.Bytes())
    return hexSignedTx, nil
}

// CreateBtcRawTx create raw tx for sending btc
func CreateRawTx(from string, to string, amount int64, utxoMap map[string]string, client *rpcclient.Client) (string, error) {
    amountInt := amount
    var feeBitcoin int64 = 5000
    spendAmount := amountInt + feeBitcoin
    utxo, err := GetUTXO(utxoMap, from, client)
    changeAmount := utxo.Amount.Shift(8).IntPart() - spendAmount
    // create new empty transaction
    redemTx := wire.NewMsgTx(wire.TxVersion)
    // create multiple txIns
    hash, err := chainhash.NewHashFromStr(utxo.previousTxID)
    if err != nil {
        fmt.Printf("could not get hash from transaction ID: %v", err)
        return "", err
    }

    outPoint := wire.NewOutPoint(hash, 0)
    txIn := wire.NewTxIn(outPoint, nil, nil)

    redemTx.AddTxIn(txIn)

    // create TxOut
    rcvScript := GetPayToAddrScript(to)
    txOut := wire.NewTxOut(amountInt, rcvScript)
    redemTx.AddTxOut(txOut)
    // create TxOut for change address, in this case, change address is sender itself
    if changeAmount > 0 {
        // return change BTC to its own address
        rcvChangeAddressScript := GetPayToAddrScript(from)
        txOut := wire.NewTxOut(changeAmount, rcvChangeAddressScript)
        redemTx.AddTxOut(txOut)
    }

    encodedTx, err := json.Marshal(redemTx)
    if err != nil {
        return "", err
    }
    // return raw tx in hex format
    return string(encodedTx), nil
}

my bitcoin wallet: mvUC64ETwYLbTjH38ZVigDwMBtCS9TbuT1

last transaction on the wallet (hash): 78b6df775e4132f747d8d15909cfe61834e6ff1d3b1e42219ace64aae7151e18

0 Answers
Related