From 734ab735b94ea399446bb1febc89b7a2d9acd1d7 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 27 Jan 2023 15:30:41 +0100 Subject: [PATCH 1/6] psbt: export Bip32 encode/decode functions --- btcutil/psbt/bip32.go | 4 ++-- btcutil/psbt/partial_input.go | 8 +++++--- btcutil/psbt/partial_output.go | 8 +++++--- btcutil/psbt/taproot.go | 11 ++++++----- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/btcutil/psbt/bip32.go b/btcutil/psbt/bip32.go index 6b22dc06..2fe6afa2 100644 --- a/btcutil/psbt/bip32.go +++ b/btcutil/psbt/bip32.go @@ -36,10 +36,10 @@ func (s Bip32Sorter) Less(i, j int) bool { return bytes.Compare(s[i].PubKey, s[j].PubKey) < 0 } -// readBip32Derivation deserializes a byte slice containing chunks of 4 byte +// ReadBip32Derivation deserializes a byte slice containing chunks of 4 byte // little endian encodings of uint32 values, the first of which is the // masterkeyfingerprint and the remainder of which are the derivation path. -func readBip32Derivation(path []byte) (uint32, []uint32, error) { +func ReadBip32Derivation(path []byte) (uint32, []uint32, error) { // BIP-0174 defines the derivation path being encoded as // "<32-bit uint> <32-bit uint>*" // with the asterisk meaning 0 to n times. Which in turn means that an diff --git a/btcutil/psbt/partial_input.go b/btcutil/psbt/partial_input.go index 7686c451..5128f1fc 100644 --- a/btcutil/psbt/partial_input.go +++ b/btcutil/psbt/partial_input.go @@ -174,7 +174,9 @@ func (pi *PInput) deserialize(r io.Reader) error { if !validatePubkey(keydata) { return ErrInvalidPsbtFormat } - master, derivationPath, err := readBip32Derivation(value) + master, derivationPath, err := ReadBip32Derivation( + value, + ) if err != nil { return err } @@ -322,7 +324,7 @@ func (pi *PInput) deserialize(r io.Reader) error { return ErrInvalidKeydata } - taprootDerivation, err := readTaprootBip32Derivation( + taprootDerivation, err := ReadTaprootBip32Derivation( keydata, value, ) if err != nil { @@ -538,7 +540,7 @@ func (pi *PInput) serialize(w io.Writer) error { ) }) for _, derivation := range pi.TaprootBip32Derivation { - value, err := serializeTaprootBip32Derivation( + value, err := SerializeTaprootBip32Derivation( derivation, ) if err != nil { diff --git a/btcutil/psbt/partial_output.go b/btcutil/psbt/partial_output.go index 33b5ff99..f0e90406 100644 --- a/btcutil/psbt/partial_output.go +++ b/btcutil/psbt/partial_output.go @@ -74,7 +74,9 @@ func (po *POutput) deserialize(r io.Reader) error { if !validatePubkey(keydata) { return ErrInvalidKeydata } - master, derivationPath, err := readBip32Derivation(value) + master, derivationPath, err := ReadBip32Derivation( + value, + ) if err != nil { return err } @@ -123,7 +125,7 @@ func (po *POutput) deserialize(r io.Reader) error { return ErrInvalidKeydata } - taprootDerivation, err := readTaprootBip32Derivation( + taprootDerivation, err := ReadTaprootBip32Derivation( keydata, value, ) if err != nil { @@ -211,7 +213,7 @@ func (po *POutput) serialize(w io.Writer) error { ) }) for _, derivation := range po.TaprootBip32Derivation { - value, err := serializeTaprootBip32Derivation( + value, err := SerializeTaprootBip32Derivation( derivation, ) if err != nil { diff --git a/btcutil/psbt/taproot.go b/btcutil/psbt/taproot.go index 4d0619ec..b9df860c 100644 --- a/btcutil/psbt/taproot.go +++ b/btcutil/psbt/taproot.go @@ -2,6 +2,7 @@ package psbt import ( "bytes" + "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" @@ -92,10 +93,10 @@ func (s *TaprootBip32Derivation) SortBefore(other *TaprootBip32Derivation) bool return bytes.Compare(s.XOnlyPubKey, other.XOnlyPubKey) < 0 } -// readTaprootBip32Derivation deserializes a byte slice containing the Taproot +// ReadTaprootBip32Derivation deserializes a byte slice containing the Taproot // BIP32 derivation info that consists of a list of leaf hashes as well as the // normal BIP32 derivation info. -func readTaprootBip32Derivation(xOnlyPubKey, +func ReadTaprootBip32Derivation(xOnlyPubKey, value []byte) (*TaprootBip32Derivation, error) { // The taproot key BIP 32 derivation path is defined as: @@ -141,7 +142,7 @@ func readTaprootBip32Derivation(xOnlyPubKey, } // Read the BIP32 derivation info. - fingerprint, path, err := readBip32Derivation(leftoverBuf.Bytes()) + fingerprint, path, err := ReadBip32Derivation(leftoverBuf.Bytes()) if err != nil { return nil, err } @@ -152,9 +153,9 @@ func readTaprootBip32Derivation(xOnlyPubKey, return &derivation, nil } -// serializeTaprootBip32Derivation serializes a TaprootBip32Derivation to its +// SerializeTaprootBip32Derivation serializes a TaprootBip32Derivation to its // raw byte representation. -func serializeTaprootBip32Derivation(d *TaprootBip32Derivation) ([]byte, +func SerializeTaprootBip32Derivation(d *TaprootBip32Derivation) ([]byte, error) { var buf bytes.Buffer From 2dbc98bdf3dc3f72a749c246cd0171bdd7abafd2 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 27 Jan 2023 15:30:42 +0100 Subject: [PATCH 2/6] psbt: fix formatting and typos --- btcutil/psbt/bip32.go | 2 +- btcutil/psbt/extractor.go | 5 +- btcutil/psbt/finalizer.go | 5 +- btcutil/psbt/partial_input.go | 120 ++++++++++++++++----------------- btcutil/psbt/partial_output.go | 44 ++++++------ btcutil/psbt/psbt.go | 67 +++++++++--------- btcutil/psbt/signer.go | 24 ++++--- btcutil/psbt/utils.go | 2 +- 8 files changed, 134 insertions(+), 135 deletions(-) diff --git a/btcutil/psbt/bip32.go b/btcutil/psbt/bip32.go index 2fe6afa2..96a3f672 100644 --- a/btcutil/psbt/bip32.go +++ b/btcutil/psbt/bip32.go @@ -13,7 +13,7 @@ type Bip32Derivation struct { // PubKey is the raw pubkey serialized in compressed format. PubKey []byte - // MasterKeyFingerprint is the finger print of the master pubkey. + // MasterKeyFingerprint is the fingerprint of the master pubkey. MasterKeyFingerprint uint32 // Bip32Path is the BIP 32 path with child index as a distinct integer. diff --git a/btcutil/psbt/extractor.go b/btcutil/psbt/extractor.go index dc7f10fd..365e2f1b 100644 --- a/btcutil/psbt/extractor.go +++ b/btcutil/psbt/extractor.go @@ -61,13 +61,14 @@ func Extract(p *Packet) (*wire.MsgTx, error) { return nil, err } - // Now that we know how may inputs we'll need, we'll + // Now that we know how many inputs we'll need, we'll // construct a packing slice, then read out each input // (with a varint prefix) from the witnessReader. tin.Witness = make(wire.TxWitness, witCount) for j := uint64(0); j < witCount; j++ { wit, err := wire.ReadVarBytes( - witnessReader, 0, txscript.MaxScriptSize, "witness", + witnessReader, 0, + txscript.MaxScriptSize, "witness", ) if err != nil { return nil, err diff --git a/btcutil/psbt/finalizer.go b/btcutil/psbt/finalizer.go index 8c50a94b..a215aa59 100644 --- a/btcutil/psbt/finalizer.go +++ b/btcutil/psbt/finalizer.go @@ -14,6 +14,7 @@ package psbt import ( "bytes" "fmt" + "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" ) @@ -462,7 +463,9 @@ func finalizeWitnessInput(p *Packet, inIndex int) error { return ErrNotFinalizable } - serializedWitness, err = writePKHWitness(sigs[0], pubKeys[0]) + serializedWitness, err = writePKHWitness( + sigs[0], pubKeys[0], + ) if err != nil { return err } diff --git a/btcutil/psbt/partial_input.go b/btcutil/psbt/partial_input.go index 5128f1fc..73595d25 100644 --- a/btcutil/psbt/partial_input.go +++ b/btcutil/psbt/partial_input.go @@ -37,9 +37,7 @@ type PInput struct { // NOTE: Only one of the two arguments should be specified, with the other // being `nil`; otherwise the created PsbtInput object will fail IsSane() // checks and will not be usable. -func NewPsbtInput(nonWitnessUtxo *wire.MsgTx, - witnessUtxo *wire.TxOut) *PInput { - +func NewPsbtInput(nonWitnessUtxo *wire.MsgTx, witnessUtxo *wire.TxOut) *PInput { return &PInput{ NonWitnessUtxo: nonWitnessUtxo, WitnessUtxo: witnessUtxo, @@ -57,7 +55,6 @@ func NewPsbtInput(nonWitnessUtxo *wire.MsgTx, // IsSane returns true only if there are no conflicting values in the Psbt // PInput. For segwit v0 no checks are currently implemented. func (pi *PInput) IsSane() bool { - // TODO(guggero): Implement sanity checks for segwit v1. For segwit v0 // it is unsafe to only rely on the witness UTXO so we don't check that // only one is set anymore. @@ -69,12 +66,12 @@ func (pi *PInput) IsSane() bool { // deserialize attempts to deserialize a new PInput from the passed io.Reader. func (pi *PInput) deserialize(r io.Reader) error { for { - keyint, keydata, err := getKey(r) + keyCode, keyData, err := getKey(r) if err != nil { return err } - if keyint == -1 { - // Reached separator byte + if keyCode == -1 { + // Reached separator byte, this section is done. break } value, err := wire.ReadVarBytes( @@ -84,14 +81,14 @@ func (pi *PInput) deserialize(r io.Reader) error { return err } - switch InputType(keyint) { + switch InputType(keyCode) { case NonWitnessUtxoType: if pi.NonWitnessUtxo != nil { return ErrDuplicateKey } - if keydata != nil { - return ErrInvalidKeydata + if keyData != nil { + return ErrInvalidKeyData } tx := wire.NewMsgTx(2) @@ -105,8 +102,8 @@ func (pi *PInput) deserialize(r io.Reader) error { if pi.WitnessUtxo != nil { return ErrDuplicateKey } - if keydata != nil { - return ErrInvalidKeydata + if keyData != nil { + return ErrInvalidKeyData } txout, err := readTxOut(value) if err != nil { @@ -116,7 +113,7 @@ func (pi *PInput) deserialize(r io.Reader) error { case PartialSigType: newPartialSig := PartialSig{ - PubKey: keydata, + PubKey: keyData, Signature: value, } @@ -124,7 +121,7 @@ func (pi *PInput) deserialize(r io.Reader) error { return ErrInvalidPsbtFormat } - // Duplicate keys are not allowed + // Duplicate keys are not allowed. for _, x := range pi.PartialSigs { if bytes.Equal(x.PubKey, newPartialSig.PubKey) { return ErrDuplicateKey @@ -137,27 +134,27 @@ func (pi *PInput) deserialize(r io.Reader) error { if pi.SighashType != 0 { return ErrDuplicateKey } - if keydata != nil { - return ErrInvalidKeydata + if keyData != nil { + return ErrInvalidKeyData } - // Bounds check on value here since the sighash type must be a - // 32-bit unsigned integer. + // Bounds check on value here since the sighash type + // must be a 32-bit unsigned integer. if len(value) != 4 { - return ErrInvalidKeydata + return ErrInvalidKeyData } - shtype := txscript.SigHashType( + sighashType := txscript.SigHashType( binary.LittleEndian.Uint32(value), ) - pi.SighashType = shtype + pi.SighashType = sighashType case RedeemScriptInputType: if pi.RedeemScript != nil { return ErrDuplicateKey } - if keydata != nil { - return ErrInvalidKeydata + if keyData != nil { + return ErrInvalidKeyData } pi.RedeemScript = value @@ -165,13 +162,13 @@ func (pi *PInput) deserialize(r io.Reader) error { if pi.WitnessScript != nil { return ErrDuplicateKey } - if keydata != nil { - return ErrInvalidKeydata + if keyData != nil { + return ErrInvalidKeyData } pi.WitnessScript = value case Bip32DerivationInputType: - if !validatePubkey(keydata) { + if !validatePubkey(keyData) { return ErrInvalidPsbtFormat } master, derivationPath, err := ReadBip32Derivation( @@ -183,7 +180,7 @@ func (pi *PInput) deserialize(r io.Reader) error { // Duplicate keys are not allowed for _, x := range pi.Bip32Derivation { - if bytes.Equal(x.PubKey, keydata) { + if bytes.Equal(x.PubKey, keyData) { return ErrDuplicateKey } } @@ -191,7 +188,7 @@ func (pi *PInput) deserialize(r io.Reader) error { pi.Bip32Derivation = append( pi.Bip32Derivation, &Bip32Derivation{ - PubKey: keydata, + PubKey: keyData, MasterKeyFingerprint: master, Bip32Path: derivationPath, }, @@ -201,8 +198,8 @@ func (pi *PInput) deserialize(r io.Reader) error { if pi.FinalScriptSig != nil { return ErrDuplicateKey } - if keydata != nil { - return ErrInvalidKeydata + if keyData != nil { + return ErrInvalidKeyData } pi.FinalScriptSig = value @@ -211,8 +208,8 @@ func (pi *PInput) deserialize(r io.Reader) error { if pi.FinalScriptWitness != nil { return ErrDuplicateKey } - if keydata != nil { - return ErrInvalidKeydata + if keyData != nil { + return ErrInvalidKeyData } pi.FinalScriptWitness = value @@ -221,26 +218,26 @@ func (pi *PInput) deserialize(r io.Reader) error { if pi.TaprootKeySpendSig != nil { return ErrDuplicateKey } - if keydata != nil { - return ErrInvalidKeydata + if keyData != nil { + return ErrInvalidKeyData } // The signature can either be 64 or 65 bytes. switch { case len(value) == schnorrSigMinLength: if !validateSchnorrSignature(value) { - return ErrInvalidKeydata + return ErrInvalidKeyData } case len(value) == schnorrSigMaxLength: if !validateSchnorrSignature( value[0:schnorrSigMinLength], ) { - return ErrInvalidKeydata + return ErrInvalidKeyData } default: - return ErrInvalidKeydata + return ErrInvalidKeyData } pi.TaprootKeySpendSig = value @@ -248,13 +245,13 @@ func (pi *PInput) deserialize(r io.Reader) error { case TaprootScriptSpendSignatureType: // The key data for the script spend signature is: // - if len(keydata) != 32*2 { - return ErrInvalidKeydata + if len(keyData) != 32*2 { + return ErrInvalidKeyData } newPartialSig := TaprootScriptSpendSig{ - XOnlyPubKey: keydata[:32], - LeafHash: keydata[32:], + XOnlyPubKey: keyData[:32], + LeafHash: keyData[32:], } // The signature can either be 64 or 65 bytes. @@ -270,11 +267,11 @@ func (pi *PInput) deserialize(r io.Reader) error { ) default: - return ErrInvalidKeydata + return ErrInvalidKeyData } if !newPartialSig.checkValid() { - return ErrInvalidKeydata + return ErrInvalidKeyData } // Duplicate keys are not allowed. @@ -290,11 +287,11 @@ func (pi *PInput) deserialize(r io.Reader) error { case TaprootLeafScriptType: if len(value) < 1 { - return ErrInvalidKeydata + return ErrInvalidKeyData } newLeafScript := TaprootTapLeafScript{ - ControlBlock: keydata, + ControlBlock: keyData, Script: value[:len(value)-1], LeafVersion: txscript.TapscriptLeafVersion( value[len(value)-1], @@ -302,7 +299,7 @@ func (pi *PInput) deserialize(r io.Reader) error { } if !newLeafScript.checkValid() { - return ErrInvalidKeydata + return ErrInvalidKeyData } // Duplicate keys are not allowed. @@ -320,12 +317,12 @@ func (pi *PInput) deserialize(r io.Reader) error { ) case TaprootBip32DerivationInputType: - if !validateXOnlyPubkey(keydata) { - return ErrInvalidKeydata + if !validateXOnlyPubkey(keyData) { + return ErrInvalidKeyData } taprootDerivation, err := ReadTaprootBip32Derivation( - keydata, value, + keyData, value, ) if err != nil { return err @@ -333,7 +330,7 @@ func (pi *PInput) deserialize(r io.Reader) error { // Duplicate keys are not allowed. for _, x := range pi.TaprootBip32Derivation { - if bytes.Equal(x.XOnlyPubKey, keydata) { + if bytes.Equal(x.XOnlyPubKey, keyData) { return ErrDuplicateKey } } @@ -346,12 +343,12 @@ func (pi *PInput) deserialize(r io.Reader) error { if pi.TaprootInternalKey != nil { return ErrDuplicateKey } - if keydata != nil { - return ErrInvalidKeydata + if keyData != nil { + return ErrInvalidKeyData } if !validateXOnlyPubkey(value) { - return ErrInvalidKeydata + return ErrInvalidKeyData } pi.TaprootInternalKey = value @@ -360,25 +357,27 @@ func (pi *PInput) deserialize(r io.Reader) error { if pi.TaprootMerkleRoot != nil { return ErrDuplicateKey } - if keydata != nil { - return ErrInvalidKeydata + if keyData != nil { + return ErrInvalidKeyData } pi.TaprootMerkleRoot = value default: // A fall through case for any proprietary types. - keyintanddata := []byte{byte(keyint)} - keyintanddata = append(keyintanddata, keydata...) + keyCodeAndData := append( + []byte{byte(keyCode)}, keyData..., + ) newUnknown := &Unknown{ - Key: keyintanddata, + Key: keyCodeAndData, Value: value, } - // Duplicate key+keydata are not allowed + // Duplicate key+keyData are not allowed. for _, x := range pi.Unknowns { if bytes.Equal(x.Key, newUnknown.Key) && bytes.Equal(x.Value, newUnknown.Value) { + return ErrDuplicateKey } } @@ -392,7 +391,6 @@ func (pi *PInput) deserialize(r io.Reader) error { // serialize attempts to serialize the target PInput into the passed io.Writer. func (pi *PInput) serialize(w io.Writer) error { - if !pi.IsSane() { return ErrInvalidPsbtFormat } @@ -595,7 +593,7 @@ func (pi *PInput) serialize(w io.Writer) error { } // Unknown is a special case; we don't have a key type, only a key and - // a value field + // a value field. for _, kv := range pi.Unknowns { err := serializeKVpair(w, kv.Key, kv.Value) if err != nil { diff --git a/btcutil/psbt/partial_output.go b/btcutil/psbt/partial_output.go index f0e90406..9b4cd182 100644 --- a/btcutil/psbt/partial_output.go +++ b/btcutil/psbt/partial_output.go @@ -34,12 +34,12 @@ func NewPsbtOutput(redeemScript []byte, witnessScript []byte, // deserialize attempts to recode a new POutput from the passed io.Reader. func (po *POutput) deserialize(r io.Reader) error { for { - keyint, keydata, err := getKey(r) + keyCode, keyData, err := getKey(r) if err != nil { return err } - if keyint == -1 { - // Reached separator byte + if keyCode == -1 { + // Reached separator byte, this section is done. break } @@ -50,14 +50,14 @@ func (po *POutput) deserialize(r io.Reader) error { return err } - switch OutputType(keyint) { + switch OutputType(keyCode) { case RedeemScriptOutputType: if po.RedeemScript != nil { return ErrDuplicateKey } - if keydata != nil { - return ErrInvalidKeydata + if keyData != nil { + return ErrInvalidKeyData } po.RedeemScript = value @@ -65,14 +65,14 @@ func (po *POutput) deserialize(r io.Reader) error { if po.WitnessScript != nil { return ErrDuplicateKey } - if keydata != nil { - return ErrInvalidKeydata + if keyData != nil { + return ErrInvalidKeyData } po.WitnessScript = value case Bip32DerivationOutputType: - if !validatePubkey(keydata) { - return ErrInvalidKeydata + if !validatePubkey(keyData) { + return ErrInvalidKeyData } master, derivationPath, err := ReadBip32Derivation( value, @@ -81,16 +81,16 @@ func (po *POutput) deserialize(r io.Reader) error { return err } - // Duplicate keys are not allowed + // Duplicate keys are not allowed. for _, x := range po.Bip32Derivation { - if bytes.Equal(x.PubKey, keydata) { + if bytes.Equal(x.PubKey, keyData) { return ErrDuplicateKey } } po.Bip32Derivation = append(po.Bip32Derivation, &Bip32Derivation{ - PubKey: keydata, + PubKey: keyData, MasterKeyFingerprint: master, Bip32Path: derivationPath, }, @@ -100,12 +100,12 @@ func (po *POutput) deserialize(r io.Reader) error { if po.TaprootInternalKey != nil { return ErrDuplicateKey } - if keydata != nil { - return ErrInvalidKeydata + if keyData != nil { + return ErrInvalidKeyData } if !validateXOnlyPubkey(value) { - return ErrInvalidKeydata + return ErrInvalidKeyData } po.TaprootInternalKey = value @@ -114,19 +114,19 @@ func (po *POutput) deserialize(r io.Reader) error { if po.TaprootTapTree != nil { return ErrDuplicateKey } - if keydata != nil { - return ErrInvalidKeydata + if keyData != nil { + return ErrInvalidKeyData } po.TaprootTapTree = value case TaprootBip32DerivationOutputType: - if !validateXOnlyPubkey(keydata) { - return ErrInvalidKeydata + if !validateXOnlyPubkey(keyData) { + return ErrInvalidKeyData } taprootDerivation, err := ReadTaprootBip32Derivation( - keydata, value, + keyData, value, ) if err != nil { return err @@ -134,7 +134,7 @@ func (po *POutput) deserialize(r io.Reader) error { // Duplicate keys are not allowed. for _, x := range po.TaprootBip32Derivation { - if bytes.Equal(x.XOnlyPubKey, keydata) { + if bytes.Equal(x.XOnlyPubKey, keyData) { return ErrDuplicateKey } } diff --git a/btcutil/psbt/psbt.go b/btcutil/psbt/psbt.go index 7241bdb6..7b3b5800 100644 --- a/btcutil/psbt/psbt.go +++ b/btcutil/psbt/psbt.go @@ -22,7 +22,7 @@ import ( const psbtMagicLength = 5 var ( - // psbtMagic is the separator + // psbtMagic is the separator. psbtMagic = [psbtMagicLength]byte{0x70, 0x73, 0x62, 0x74, 0xff, // = "psbt" + 0xff sep } @@ -34,7 +34,7 @@ var ( const MaxPsbtValueLength = 4000000 // MaxPsbtKeyLength is the length of the largest key that we'll successfully -// deserialize from the wire. Anything more will return ErrInvalidKeydata. +// deserialize from the wire. Anything more will return ErrInvalidKeyData. const MaxPsbtKeyLength = 10000 var ( @@ -47,40 +47,41 @@ var ( // due to having the same key repeated in the same key-value pair. ErrDuplicateKey = errors.New("Invalid Psbt due to duplicate key") - // ErrInvalidKeydata indicates that a key-value pair in the PSBT + // ErrInvalidKeyData indicates that a key-value pair in the PSBT // serialization contains data in the key which is not valid. - ErrInvalidKeydata = errors.New("Invalid key data") + ErrInvalidKeyData = errors.New("Invalid key data") - // ErrInvalidMagicBytes indicates that a passed Psbt serialization is invalid - // due to having incorrect magic bytes. - ErrInvalidMagicBytes = errors.New("Invalid Psbt due to incorrect magic bytes") + // ErrInvalidMagicBytes indicates that a passed Psbt serialization is + // invalid due to having incorrect magic bytes. + ErrInvalidMagicBytes = errors.New("Invalid Psbt due to incorrect " + + "magic bytes") - // ErrInvalidRawTxSigned indicates that the raw serialized transaction in the - // global section of the passed Psbt serialization is invalid because it - // contains scriptSigs/witnesses (i.e. is fully or partially signed), which - // is not allowed by BIP174. - ErrInvalidRawTxSigned = errors.New("Invalid Psbt, raw transaction must " + - "be unsigned.") + // ErrInvalidRawTxSigned indicates that the raw serialized transaction + // in the global section of the passed Psbt serialization is invalid + // because it contains scriptSigs/witnesses (i.e. is fully or partially + // signed), which is not allowed by BIP174. + ErrInvalidRawTxSigned = errors.New("Invalid Psbt, raw transaction " + + "must be unsigned.") // ErrInvalidPrevOutNonWitnessTransaction indicates that the transaction // hash (i.e. SHA256^2) of the fully serialized previous transaction - // provided in the NonWitnessUtxo key-value field doesn't match the prevout - // hash in the UnsignedTx field in the PSBT itself. - ErrInvalidPrevOutNonWitnessTransaction = errors.New("Prevout hash does " + - "not match the provided non-witness utxo serialization") + // provided in the NonWitnessUtxo key-value field doesn't match the + // prevout hash in the UnsignedTx field in the PSBT itself. + ErrInvalidPrevOutNonWitnessTransaction = errors.New("Prevout hash " + + "does not match the provided non-witness utxo serialization") // ErrInvalidSignatureForInput indicates that the signature the user is // trying to append to the PSBT is invalid, either because it does // not correspond to the previous transaction hash, or redeem script, // or witness script. // NOTE this does not include ECDSA signature checking. - ErrInvalidSignatureForInput = errors.New("Signature does not correspond " + - "to this input") + ErrInvalidSignatureForInput = errors.New("Signature does not " + + "correspond to this input") - // ErrInputAlreadyFinalized indicates that the PSBT passed to a Finalizer - // already contains the finalized scriptSig or witness. - ErrInputAlreadyFinalized = errors.New("Cannot finalize PSBT, finalized " + - "scriptSig or scriptWitnes already exists") + // ErrInputAlreadyFinalized indicates that the PSBT passed to a + // Finalizer already contains the finalized scriptSig or witness. + ErrInputAlreadyFinalized = errors.New("Cannot finalize PSBT, " + + "finalized scriptSig or scriptWitnes already exists") // ErrIncompletePSBT indicates that the Extractor object // was unable to successfully extract the passed Psbt struct because @@ -99,8 +100,8 @@ var ( ErrInvalidSigHashFlags = errors.New("Invalid Sighash Flags") // ErrUnsupportedScriptType indicates that the redeem script or - // scriptwitness given is not supported by this codebase, or is otherwise - // not valid. + // script witness given is not supported by this codebase, or is + // otherwise not valid. ErrUnsupportedScriptType = errors.New("Unsupported script type") ) @@ -148,7 +149,6 @@ func validateUnsignedTX(tx *wire.MsgTx) bool { // NewFromUnsignedTx creates a new Psbt struct, without any signatures (i.e. // only the global section is non-empty) using the passed unsigned transaction. func NewFromUnsignedTx(tx *wire.MsgTx) (*Packet, error) { - if !validateUnsignedTX(tx) { return nil, ErrInvalidRawTxSigned } @@ -157,14 +157,12 @@ func NewFromUnsignedTx(tx *wire.MsgTx) (*Packet, error) { outSlice := make([]POutput, len(tx.TxOut)) unknownSlice := make([]Unknown, 0) - retPsbt := Packet{ + return &Packet{ UnsignedTx: tx, Inputs: inSlice, Outputs: outSlice, Unknowns: unknownSlice, - } - - return &retPsbt, nil + }, nil } // NewFromRawBytes returns a new instance of a Packet struct created by reading @@ -175,7 +173,6 @@ func NewFromUnsignedTx(tx *wire.MsgTx) (*Packet, error) { // NOTE: To create a Packet from one's own data, rather than reading in a // serialization from a counterparty, one should use a psbt.New. func NewFromRawBytes(r io.Reader, b64 bool) (*Packet, error) { - // If the PSBT is encoded in bas64, then we'll create a new wrapper // reader that'll allow us to incrementally decode the contents of the // io.Reader. @@ -197,11 +194,11 @@ func NewFromRawBytes(r io.Reader, b64 bool) (*Packet, error) { // Next we parse the GLOBAL section. There is currently only 1 known // key type, UnsignedTx. We insist this exists first; unknowns are // allowed, but only after. - keyint, keydata, err := getKey(r) + keyCode, keyData, err := getKey(r) if err != nil { return nil, err } - if GlobalType(keyint) != UnsignedTxType || keydata != nil { + if GlobalType(keyCode) != UnsignedTxType || keyData != nil { return nil, ErrInvalidPsbtFormat } @@ -278,7 +275,7 @@ func NewFromRawBytes(r io.Reader, b64 bool) (*Packet, error) { outSlice[i] = output } - // Populate the new Packet object + // Populate the new Packet object. newPsbt := Packet{ UnsignedTx: msgTx, Inputs: inSlice, @@ -298,7 +295,6 @@ func NewFromRawBytes(r io.Reader, b64 bool) (*Packet, error) { // Serialize creates a binary serialization of the referenced Packet struct // with lexicographical ordering (by key) of the subsections. func (p *Packet) Serialize(w io.Writer) error { - // First we write out the precise set of magic bytes that identify a // valid PSBT transaction. if _, err := w.Write(psbtMagic[:]); err != nil { @@ -382,7 +378,6 @@ func (p *Packet) IsComplete() bool { // SanityCheck checks conditions on a PSBT to ensure that it obeys the // rules of BIP174, and returns true if so, false if not. func (p *Packet) SanityCheck() error { - if !validateUnsignedTX(p.UnsignedTx) { return ErrInvalidRawTxSigned } diff --git a/btcutil/psbt/signer.go b/btcutil/psbt/signer.go index 58826531..dcbcf93f 100644 --- a/btcutil/psbt/signer.go +++ b/btcutil/psbt/signer.go @@ -22,12 +22,12 @@ const ( // attached. SignSuccesful = 0 - // SignFinalized indicates that this input is already finalized, so the provided - // signature was *not* attached + // SignFinalized indicates that this input is already finalized, so the + // provided signature was *not* attached SignFinalized = 1 - // SignInvalid indicates that the provided signature data was not valid. In this case - // an error will also be returned. + // SignInvalid indicates that the provided signature data was not valid. + // In this case an error will also be returned. SignInvalid = -1 ) @@ -73,9 +73,10 @@ func (u *Updater) Sign(inIndex int, sig []byte, pubKey []byte, // // Case 1: if witnessScript is present, it must be of type witness; // if not, signature insertion will of course fail. + pInput := u.Upsbt.Inputs[inIndex] switch { - case u.Upsbt.Inputs[inIndex].WitnessScript != nil: - if u.Upsbt.Inputs[inIndex].WitnessUtxo == nil { + case pInput.WitnessScript != nil: + if pInput.WitnessUtxo == nil { err := nonWitnessToWitness(u.Upsbt, inIndex) if err != nil { return SignInvalid, err @@ -89,12 +90,12 @@ func (u *Updater) Sign(inIndex int, sig []byte, pubKey []byte, // Case 2: no witness script, only redeem script; can be legacy p2sh or // p2sh-wrapped p2wkh. - case u.Upsbt.Inputs[inIndex].RedeemScript != nil: + case pInput.RedeemScript != nil: // We only need to decide if the input is witness, and we don't // rely on the witnessutxo/nonwitnessutxo in the PSBT, instead // we check the redeemScript content. if txscript.IsWitnessProgram(redeemScript) { - if u.Upsbt.Inputs[inIndex].WitnessUtxo == nil { + if pInput.WitnessUtxo == nil { err := nonWitnessToWitness(u.Upsbt, inIndex) if err != nil { return SignInvalid, err @@ -113,9 +114,10 @@ func (u *Updater) Sign(inIndex int, sig []byte, pubKey []byte, // non-p2sh. To check if it's segwit, check the scriptPubKey of the // output. default: - if u.Upsbt.Inputs[inIndex].WitnessUtxo == nil { - outIndex := u.Upsbt.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Index - script := u.Upsbt.Inputs[inIndex].NonWitnessUtxo.TxOut[outIndex].PkScript + if pInput.WitnessUtxo == nil { + txIn := u.Upsbt.UnsignedTx.TxIn[inIndex] + outIndex := txIn.PreviousOutPoint.Index + script := pInput.NonWitnessUtxo.TxOut[outIndex].PkScript if txscript.IsWitnessProgram(script) { err := nonWitnessToWitness(u.Upsbt, inIndex) diff --git a/btcutil/psbt/utils.go b/btcutil/psbt/utils.go index 94a5546a..65b597d5 100644 --- a/btcutil/psbt/utils.go +++ b/btcutil/psbt/utils.go @@ -245,7 +245,7 @@ func getKey(r io.Reader) (int, []byte, error) { // Check that we don't attempt to decode a dangerously large key. if count > MaxPsbtKeyLength { - return -1, nil, ErrInvalidKeydata + return -1, nil, ErrInvalidKeyData } // Next, we ready out the designated number of bytes, which may include From 19c7c3d853656fd0b47a6df0f3e7260e20fc85cf Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 27 Jan 2023 15:30:43 +0100 Subject: [PATCH 3/6] psbt: allow Unknowns in outputs --- btcutil/psbt/partial_output.go | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/btcutil/psbt/partial_output.go b/btcutil/psbt/partial_output.go index 9b4cd182..86e47645 100644 --- a/btcutil/psbt/partial_output.go +++ b/btcutil/psbt/partial_output.go @@ -17,6 +17,7 @@ type POutput struct { TaprootInternalKey []byte TaprootTapTree []byte TaprootBip32Derivation []*TaprootBip32Derivation + Unknowns []*Unknown } // NewPsbtOutput creates an instance of PsbtOutput; the three parameters @@ -144,8 +145,25 @@ func (po *POutput) deserialize(r io.Reader) error { ) default: - // Unknown type is allowed for inputs but not outputs. - return ErrInvalidPsbtFormat + // A fall through case for any proprietary types. + keyCodeAndData := append( + []byte{byte(keyCode)}, keyData..., + ) + newUnknown := &Unknown{ + Key: keyCodeAndData, + Value: value, + } + + // Duplicate key+keyData are not allowed. + for _, x := range po.Unknowns { + if bytes.Equal(x.Key, newUnknown.Key) && + bytes.Equal(x.Value, newUnknown.Value) { + + return ErrDuplicateKey + } + } + + po.Unknowns = append(po.Unknowns, newUnknown) } } @@ -228,5 +246,14 @@ func (po *POutput) serialize(w io.Writer) error { } } + // Unknown is a special case; we don't have a key type, only a key and + // a value field + for _, kv := range po.Unknowns { + err := serializeKVpair(w, kv.Key, kv.Value) + if err != nil { + return err + } + } + return nil } From 5ebbb1bb9163e5dce38d2f1441a316a989c9d7d1 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 27 Jan 2023 15:30:44 +0100 Subject: [PATCH 4/6] psbt: encode global unknowns --- btcutil/psbt/psbt.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/btcutil/psbt/psbt.go b/btcutil/psbt/psbt.go index 7b3b5800..00332d67 100644 --- a/btcutil/psbt/psbt.go +++ b/btcutil/psbt/psbt.go @@ -319,6 +319,15 @@ func (p *Packet) Serialize(w io.Writer) error { return err } + // Unknown is a special case; we don't have a key type, only a key and + // a value field + for _, kv := range p.Unknowns { + err := serializeKVpair(w, kv.Key, kv.Value) + if err != nil { + return err + } + } + // With that our global section is done, so we'll write out the // separator. separator := []byte{0x00} From 6c81c664bb25af53cb18c8449c32d2c1d7d1f64a Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 27 Jan 2023 15:30:45 +0100 Subject: [PATCH 5/6] psbt: use pointer slice for global unknowns To unify the way the unknown fields are handled, we change the global ones to a slice of pointers as well. This makes it easier to add generic handler code for unknown fields, if they are uniform across the levels (global, input, output). --- btcutil/psbt/psbt.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/btcutil/psbt/psbt.go b/btcutil/psbt/psbt.go index 00332d67..964061bd 100644 --- a/btcutil/psbt/psbt.go +++ b/btcutil/psbt/psbt.go @@ -113,7 +113,7 @@ type Unknown struct { Value []byte } -// Packet is the actual psbt repreesntation. It is a is a set of 1 + N + M +// Packet is the actual psbt representation. It is a set of 1 + N + M // key-value pair lists, 1 global, defining the unsigned transaction structure // with N inputs and M outputs. These key-value pairs can contain scripts, // signatures, key derivations and other transaction-defining data. @@ -130,7 +130,7 @@ type Packet struct { Outputs []POutput // Unknowns are the set of custom types (global only) within this PSBT. - Unknowns []Unknown + Unknowns []*Unknown } // validateUnsignedTx returns true if the transaction is unsigned. Note that @@ -155,7 +155,7 @@ func NewFromUnsignedTx(tx *wire.MsgTx) (*Packet, error) { inSlice := make([]PInput, len(tx.TxIn)) outSlice := make([]POutput, len(tx.TxOut)) - unknownSlice := make([]Unknown, 0) + unknownSlice := make([]*Unknown, 0) return &Packet{ UnsignedTx: tx, @@ -224,7 +224,7 @@ func NewFromRawBytes(r io.Reader, b64 bool) (*Packet, error) { // Next we parse any unknowns that may be present, making sure that we // break at the separator. - var unknownSlice []Unknown + var unknownSlice []*Unknown for { keyint, keydata, err := getKey(r) if err != nil { @@ -244,7 +244,7 @@ func NewFromRawBytes(r io.Reader, b64 bool) (*Packet, error) { keyintanddata := []byte{byte(keyint)} keyintanddata = append(keyintanddata, keydata...) - newUnknown := Unknown{ + newUnknown := &Unknown{ Key: keyintanddata, Value: value, } From bb0a2f3790eebcf9aad402df3bb3f53d9142f358 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 27 Jan 2023 15:30:46 +0100 Subject: [PATCH 6/6] psbt: add unit test for unknowns --- btcutil/psbt/psbt_test.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/btcutil/psbt/psbt_test.go b/btcutil/psbt/psbt_test.go index 1ce4780d..6ed154f8 100644 --- a/btcutil/psbt/psbt_test.go +++ b/btcutil/psbt/psbt_test.go @@ -1502,3 +1502,28 @@ func TestWitnessForNonWitnessUtxo(t *testing.T) { t.Fatalf("unable to extract funding TX: %v", err) } } + +// TestUnknowns tests that we can parse and serialize unknown fields on all +// three levels (global, input, output). +func TestUnknowns(t *testing.T) { + packetWithUnknowns := "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" + + packet, err := NewFromRawBytes( + strings.NewReader(packetWithUnknowns), true, + ) + require.NoError(t, err) + + require.Len(t, packet.Unknowns, 2) + + require.Len(t, packet.Inputs, 1) + require.Len(t, packet.Inputs[0].Unknowns, 10) + + require.Len(t, packet.Outputs, 2) + require.Len(t, packet.Outputs[0].Unknowns, 7) + + // Convert it to base64 again to make sure the fields are also + // serialized. + encoded, err := packet.B64Encode() + require.NoError(t, err) + require.Equal(t, packetWithUnknowns, encoded) +}