1

I have a PSBT (in Base64 format) which I need to sign using GoLang, but I can't figure out which steps I need to do that. I found this but I'm not really confident with Bitcoin and Python, so I didn't fully understood the Signer Algorithm. What should I do?

Rockietto
  • 13
  • 3
  • I haven't used this particular feature, but `btcsuite/btcutil` (written in Go) has a `psbt` package that should allow you to do that: https://godoc.org/github.com/btcsuite/btcutil/psbt – Bjarne Magnussen Jun 03 '20 at 10:23
  • I saw it, but no example and I'm not enough confident with BTC to fully understand what I have to do – Rockietto Jun 03 '20 at 13:43

1 Answers1

2

As I commented, with the psbt package from btcsuite/btcutil it is possible.

I have created an example of how to use it. I don't know if this is the best/most efficient way of doing it. But I hope this gives you a template and example to work with. But it is relatively low-level and without some deeper technical understanding of Bitcoin it may be difficult to follow.

It spends the second (0-indexed) output from this transaction on Testnet.

package main

import (
    "bytes"
    "encoding/hex"
    "fmt"

    "github.com/btcsuite/btcd/chaincfg"
    "github.com/btcsuite/btcd/chaincfg/chainhash"
    "github.com/btcsuite/btcd/txscript"
    "github.com/btcsuite/btcd/wire"
    "github.com/btcsuite/btcutil"
    "github.com/btcsuite/btcutil/hdkeychain"
    "github.com/btcsuite/btcutil/psbt"
)

const (
    // Provided PSBT with one P2PKH input. Created in part 1 below.
    psbtBase64 = "cHNidP8BAFIBAAAAAX7Mj1abZbFtfUYFYBIFRKxCOxs8wfMRu3NhVBHTbnFfAQAAAAAAAAAAAayEAQAAAAAAFgAU8yFPpRndMpAJ7aO+RtRN4Nh3mbYAAAAAAAEBH6CGAQAAAAAAFgAUJw/E3xSMUU+8VVGHXk7Dc4vGNjkiBgOoVLKI2rj2DMlswSDkSf0KR7jQHULLPOWS/x3q8b3MqQyj8e/WAAAAAAMAAAAAAA=="

    // Extended Private Key.
    extPrivateKeyStr = "vprv9GpFMh8VMAXKgdZVFGXFvsexjr25MHKvtCY7vKHbRr8A6xCm4bQYbQrJmLz6h3F9MGf5edwxQuZR7DCLFVvjMxgfJ9so9mcN2SHGR3QFr3S"
)

// Part 1: Creating the PSBT (will be returned as base64)
func part1() string {
    // The extended private key was generated with Electrum and we derive the
    // specific key used.
    // `keyC` contains the private key that _contains_ the coins to be spend.
    keyC, _ := hdkeychain.NewKeyFromString(extPrivateKeyStr)
    fingerprint := keyC.ParentFingerprint()
    keyC, _ = keyC.Child(0)
    keyC, _ = keyC.Child(3) // Electrum derivation of the 4th address.
    pubKeyC, _ := keyC.ECPubKey()

    // Create a brand new PSBT spending the following outpoint.
    txhash, _ := chainhash.NewHashFromStr("5f716ed311546173bb11f3c13c1b3b42ac4405126005467d6db1659b568fcc7e")
    outpoint := wire.NewOutPoint(txhash, 1)
    inputs := []*wire.OutPoint{outpoint}
    pubkeyScript, _ := hex.DecodeString("0014270fc4df148c514fbc5551875e4ec3738bc63639")
    spendTxOut := wire.NewTxOut(100000, pubkeyScript)

    // The address and amount to send funds to.
    outAddress, err := btcutil.DecodeAddress("tb1q7vs5lfgem5efqz0d5wlyd4zdurv80xdkfkf3cn", &chaincfg.TestNet3Params)
    // The witnessScript consists of a `0x00`, followed by the size of the
    // pubkey-hash `0x20` and the pubkey-hash itself.
    witnessScript := append([]byte{0, 20}, outAddress.ScriptAddress()...)
    outputs := []*wire.TxOut{wire.NewTxOut(99500, witnessScript)}

    // Create the psbt instance.
    pC, err := psbt.New(inputs, outputs, wire.TxVersion, 0, []uint32{0}) // Note: typically the sequence is `0xffffffff`!
    if err != nil {
        panic(err)
    }

    // Use the Updater to add information to the input.
    u, err := psbt.NewUpdater(pC)
    if err != nil {
        panic(err)
    }

    u.AddInBip32Derivation(fingerprint, []uint32{0, 3}, pubKeyC.SerializeCompressed(), 0)
    u.AddInWitnessUtxo(spendTxOut, 0)
    u.AddInSighashType(0, 0)

    // Show the base64 encoding of the unsigned PSBT.
    b64, _ := pC.B64Encode()
    return b64

}

// Part 2: Sign the PSBT in base64 encoding and return it.
func part2(psbtBase64 string) string {
    // Reader for the PSBT.
    psbtBytes := []byte(psbtBase64)
    r := bytes.NewReader(psbtBytes)

    // Create instance of a PSBT.
    p, err := psbt.NewFromRawBytes(r, true)
    if err != nil {
        panic(err)
    }

    // Load the extended private key.
    bip32Key, err := hdkeychain.NewKeyFromString(extPrivateKeyStr)
    if err != nil {
        panic(err)
    }

    // Derivation path should be read from PSBT.
    // Note: We ignore checking the fingerprint, etc.
    path := p.Inputs[0].Bip32Derivation[0]
    for _, d := range path.Bip32Path {
        bip32Key, _ = bip32Key.Child(d)
    }

    pubKey, err := bip32Key.ECPubKey()
    if err != nil {
        panic(err)
    }

    privKey, err := bip32Key.ECPrivKey()
    if err != nil {
        panic(err)
    }

    // Manually creating the signature.
    sigHashes := txscript.NewTxSigHashes(p.UnsignedTx)
    sig, err := txscript.RawTxInWitnessSignature(p.UnsignedTx, sigHashes, 0,
        p.Inputs[0].WitnessUtxo.Value, p.Inputs[0].WitnessUtxo.PkScript,
        txscript.SigHashAll, privKey)

    // Use the Updater to add the signature to the input.
    u, err := psbt.NewUpdater(p)
    if err != nil {
        panic(err)
    }
    sucess, err := u.Sign(0, sig, pubKey.SerializeCompressed(), nil, nil)
    if err != nil {
        panic(err)
    }
    if sucess != psbt.SignSuccesful {
        panic("could not sucessfully sign for some reason")
    }

    // Finalize PSBT.
    err = psbt.Finalize(p, 0)
    tx, err := psbt.Extract(p)

    var buf bytes.Buffer
    tx.Serialize(&buf)
    return hex.EncodeToString(buf.Bytes())
}

func main() {
    fmt.Println("Base64 PSBT: ", part1())

    fmt.Println("Signed tx: ", part2(psbtBase64))
}
Bjarne Magnussen
  • 937
  • 5
  • 16
  • I understood you code, but I found out it seems to be working only for transactions with only one input. What about multiple input transactions? – Rockietto Jul 10 '20 at 22:54
  • The `Inputs` field of the `psbt` structure is a slice of inputs that can be indexed, as is done with the example code by simply using index `0`. – Bjarne Magnussen Jul 12 '20 at 07:55