From 7611eb65d8b975cf9cc906f2314a6ec33a5829c3 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 15 Jan 2020 17:35:45 -0800 Subject: [PATCH 01/13] psbt: move PSBT keys/type into new file --- psbt/psbt.go | 216 ++------------------------------------------------ psbt/types.go | 149 ++++++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+), 210 deletions(-) create mode 100644 psbt/types.go diff --git a/psbt/psbt.go b/psbt/psbt.go index f9d8ff5..7b4100a 100644 --- a/psbt/psbt.go +++ b/psbt/psbt.go @@ -21,14 +21,12 @@ import ( "github.com/btcsuite/btcd/wire" ) -// BIP-174 aka PSBT defined values - -// Key types are currently encoded with single bytes -type psbtKeyType = uint8 - +// psbtMagicLength is the length of the magic bytes used to signal the start of +// a serialized PSBT packet. const psbtMagicLength = 5 var ( + // psbtMagic is the separator psbtMagic = [psbtMagicLength]byte{0x70, 0x73, 0x62, 0x74, 0xff, // = "psbt" + 0xff sep } @@ -39,30 +37,6 @@ var ( //less than 4M. const MaxPsbtValueLength = 4000000 -// The below are the known key types as per the BIP. -// Unknown types may be accepted but will not be processed. -const ( - - // Global known key types - PsbtGlobalUnsignedTx psbtKeyType = 0 - - // TxIn section known key types - PsbtInNonWitnessUtxo psbtKeyType = 0 - PsbtInWitnessUtxo psbtKeyType = 1 - PsbtInPartialSig psbtKeyType = 2 - PsbtInSighashType psbtKeyType = 3 - PsbtInRedeemScript psbtKeyType = 4 - PsbtInWitnessScript psbtKeyType = 5 - PsbtInBip32Derivation psbtKeyType = 6 - PsbtInFinalScriptSig psbtKeyType = 7 - PsbtInFinalScriptWitness psbtKeyType = 8 - - // TxOut section known key types - PsbtOutRedeemScript psbtKeyType = 0 - PsbtOutWitnessScript psbtKeyType = 1 - PsbtOutBip32Derivation psbtKeyType = 2 -) - var ( // ErrInvalidPsbtFormat is a generic error for any situation in which a @@ -130,187 +104,9 @@ var ( ErrUnsupportedScriptType = errors.New("Unsupported script type") ) -func serializeKVpair(w io.Writer, key []byte, value []byte) error { - err := wire.WriteVarBytes(w, 0, key) - if err != nil { - return err - } - err = wire.WriteVarBytes(w, 0, value) - if err != nil { - return err - } - return nil -} - -func serializeKVPairWithType(w io.Writer, kt psbtKeyType, keydata []byte, - value []byte) error { - if keydata == nil { - keydata = []byte{} - } - serializedKey := append([]byte{byte(kt)}, keydata...) - return serializeKVpair(w, serializedKey, value) -} - -// getKey retrieves a single key - both the key type and the keydata -// (if present) from the stream and returns the key type as an integer, -// or -1 if the key was of zero length, which is used to indicate the -// presence of a separator byte which indicates the end of a given key- -// value pair list, and the keydata as a byte slice or nil if none is -// present. -func getKey(r io.Reader) (int, []byte, error) { - - // For the key, we read the varint separately, instead of - // using the available ReadVarBytes, because we have a specific - // treatment of 0x00 here: - count, err := wire.ReadVarInt(r, 0) - if err != nil { - return -1, nil, ErrInvalidPsbtFormat - } - if count == 0 { - // separator indicates end of key-value pair list - return -1, nil, nil - } - - // read count bytes, this is the key (including type and any data) - var keyintanddata = make([]byte, count) - if _, err := io.ReadFull(r, keyintanddata[:]); err != nil { - return -1, nil, err - } - - keyType := int(string(keyintanddata)[0]) - // Note that the second return value will usually be empty, - // since most keys contain no more than the key type byte. - if len(keyintanddata) == 1 { - return keyType, nil, nil - } - return keyType, keyintanddata[1:], nil - -} - -// readTxOut is a limited version of wire.readTxOut, because -// the latter is not exported. -func readTxOut(txout []byte) (*wire.TxOut, error) { - if len(txout) < 10 { - return nil, ErrInvalidPsbtFormat - } - valueSer := binary.LittleEndian.Uint64(txout[:8]) - scriptPubKey := txout[9:] - return wire.NewTxOut(int64(valueSer), scriptPubKey), nil -} - -// PartialSig encapsulate a (BTC public key, ECDSA signature) -// pair, note that the fields are stored as byte slices, not -// btcec.PublicKey or btcec.Signature (because manipulations will -// be with the former not the latter, here); compliance with consensus -// serialization is enforced with .checkValid() -type PartialSig struct { - PubKey []byte - Signature []byte -} - -// PartialSigSorter implements sort.Interface. -type PartialSigSorter []*PartialSig - -func (s PartialSigSorter) Len() int { return len(s) } - -func (s PartialSigSorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] } - -func (s PartialSigSorter) Less(i, j int) bool { - return bytes.Compare(s[i].PubKey, s[j].PubKey) < 0 -} - -// validatePubkey checks if pubKey is *any* valid -// pubKey serialization in a Bitcoin context (compressed/uncomp. OK) -func validatePubkey(pubKey []byte) bool { - _, err := btcec.ParsePubKey(pubKey, btcec.S256()) - if err != nil { - return false - } - return true -} - -// validateSignature checks that the passed byte slice is a valid -// DER-encoded ECDSA signature, including the sighash flag. -// It does *not* of course validate the signature against any message -// or public key. -func validateSignature(sig []byte) bool { - _, err := btcec.ParseDERSignature(sig, btcec.S256()) - if err != nil { - return false - } - return true -} - -// See above notes (PartialSig, validatePubkey, validateSignature). -// NOTE update for Schnorr will be needed here if/when that activates. -func (ps *PartialSig) checkValid() bool { - return validatePubkey(ps.PubKey) && validateSignature(ps.Signature) -} - -// Bip32Derivation encapsulates the data for the input and output -// Bip32Derivation key-value fields. -type Bip32Derivation struct { - PubKey []byte - MasterKeyFingerprint uint32 - Bip32Path []uint32 -} - -// checkValid ensures that the PubKey in the Bip32Derivation -// struct is valid. -func (pb *Bip32Derivation) checkValid() bool { - return validatePubkey(pb.PubKey) -} - -// Bip32Sorter implements sort.Interface. -type Bip32Sorter []*Bip32Derivation - -func (s Bip32Sorter) Len() int { return len(s) } - -func (s Bip32Sorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] } - -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 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) { - - if len(path)%4 != 0 || len(path)/4-1 < 1 { - return 0, nil, ErrInvalidPsbtFormat - } - masterKeyInt := binary.LittleEndian.Uint32(path[:4]) - paths := make([]uint32, 0) - for i := 4; i < len(path); i += 4 { - paths = append(paths, binary.LittleEndian.Uint32(path[i:i+4])) - } - return masterKeyInt, paths, nil -} - -// SerializeBIP32Derivation takes a master key fingerprint -// as defined in BIP32, along with a path specified as a list -// of uint32 values, and returns a bytestring specifying the derivation -// in the format required by BIP174: -// // master key fingerprint (4) || child index (4) || child index (4) || ... -func SerializeBIP32Derivation(masterKeyFingerprint uint32, - bip32Path []uint32) []byte { - derivationPath := make([]byte, 0, 4+4*len(bip32Path)) - var masterKeyBytes [4]byte - binary.LittleEndian.PutUint32(masterKeyBytes[:], masterKeyFingerprint) - derivationPath = append(derivationPath, masterKeyBytes[:]...) - for _, path := range bip32Path { - var pathbytes [4]byte - binary.LittleEndian.PutUint32(pathbytes[:], path) - derivationPath = append(derivationPath, pathbytes[:]...) - } - return derivationPath -} - -// Unknown is a struct encapsulating a key-value pair for which -// the key type is unknown by this package; these fields are allowed -// in both the 'Global' and the 'Input' section of a PSBT. +// Unknown is a struct encapsulating a key-value pair for which the key type is +// unknown by this package; these fields are allowed in both the 'Global' and +// the 'Input' section of a PSBT. type Unknown struct { Key []byte Value []byte diff --git a/psbt/types.go b/psbt/types.go new file mode 100644 index 0000000..1b4a26a --- /dev/null +++ b/psbt/types.go @@ -0,0 +1,149 @@ +package psbt + +// GlobalType is the set of types that are used at the global scope level +// within the PSBT. +type GlobalType uint8 + +const ( + // UnsignedTxType is the global scope key that houses the unsigned + // transaction of the PSBT. The value is a transaction in network + // serialization. The scriptSigs and witnesses for each input must be + // empty. The transaction must be in the old serialization format + // (without witnesses). A PSBT must have a transaction, otherwise it is + // invalid. + UnsignedTxType GlobalType = 0 + + // XpubType houses a global xpub for the entire PSBT packet. + // + // The key ({0x01}|{xpub}) is he 78 byte serialized extended public key + // as defined by BIP 32. Extended public keys are those that can be + // used to derive public keys used in the inputs and outputs of this + // transaction. It should be the public key at the highest hardened + // derivation index so that + // the unhardened child keys used in the transaction can be derived. + // + // The value is the master key fingerprint as defined by BIP 32 + // concatenated with the derivation path of the public key. The + // derivation path is represented as 32-bit little endian unsigned + // integer indexes concatenated with each other. The number of 32 bit + // unsigned integer indexes must match the depth provided in the + // extended public key. + XpubType GlobalType = 1 + + // VersionType houses the global version number of this PSBT. There is + // no key (only contains the byte type), then the value if omitted, is + // assumed to be zero. + VersionType GlobalType = 0xFB + + // ProprietaryGlobalType is used to house any proper chary global-scope + // keys within the PSBT. + // + // The key is ({0xFC}||{subtype}|{key data}) a variable length + // identifier prefix, followed by a subtype, followed by the key data + // itself. + // + // The value is any data as defined by the proprietary type user. + ProprietaryGlobalType = 0xFC +) + +// InputType is the set of types that are defined for each input included +// within the PSBT. +type InputType uint32 + +const ( + // NonWitnessUtxoType has no key ({0x00}) and houses the transaction in + // network serialization format the current input spends from. This + // should only be present for inputs which spend non-segwit outputs. + // However, if it is unknown whether an input spends a segwit output, + // this type should be used. The entire input transaction is needed in + // order to be able to verify the values of the input (pre-segwit they + // aren't in the signature digest). + NonWitnessUtxoType InputType = 0 + + // WitnessUtxoType has no key ({0x01}), and houses the entire + // transaction output in network serialization which the current input + // spends from. This should only be present for inputs which spend + // segwit outputs, including P2SH embedded ones (value || script). + WitnessUtxoType InputType = 1 + + // PartialSigType is used to include a partial signature with key + // ({0x02}|{public key}). + // + // The value is the signature as would be pushed to the stack from a + // scriptSig or witness.. + PartialSigType InputType = 2 + + // SighashType is an empty key ({0x03}). + // + // The value contains the 32-bit unsigned integer specifying the + // sighash type to be used for this input. Signatures for this input + // must use the sighash type, finalizers must fail to finalize inputs + // which have signatures that do not match the specified sighash type. + // Signers who cannot produce signatures with the sighash type must not + // provide a signature. + SighashType InputType = 3 + + // RedeemScriptInputType is an empty key ({0x40}). + // + // The value is the redeem script of the input if present. + RedeemScriptInputType InputType = 4 + + // WitnessScriptInputType is an empty key ({0x05}). + // + // The value is the witness script of this input, if it has one. + WitnessScriptInputType InputType = 5 + + // Bip32DerivationInputType is a type that carries the pubkey along + // with the key ({0x06}|{public key}). + // + // The value is master key fingerprint as defined by BIP 32 + // concatenated with the derivation path of the public key. The + // derivation path is represented as 32 bit unsigned integer indexes + // concatenated with each other. Public keys are those that will be + // needed to sign this input. + Bip32DerivationInputType InputType = 6 + + // FinalScriptSigType is an empty key ({0x07}). + // + // The value contains a fully constructed scriptSig with signatures and + // any other scripts necessary for the input to pass validation. + FinalScriptSigType InputType = 7 + + // FinalScriptWitnessType is an empty key ({0x08}). The value is a + // fully constructed scriptWitness with signatures and any other + // scripts necessary for the input to pass validation. + FinalScriptWitnessType InputType = 8 + + // ProprietaryInputType is a custom type for use by devs. + // + // The key ({0xFC}||{subtype}|{key data}), is a Variable length + // identifier prefix, followed by a subtype, followed by the key data + // itself. + // + // The value is any value data as defined by the proprietary type user. + ProprietaryInputType InputType = 0xFC +) + +// OutputType is the set of types defined per output within the PSBT. +type OutputType uint32 + +const ( + // RedeemScriptOutputType is an empty key ({0x00}> + // + // The value is the redeemScript for this output if it has one. + RedeemScriptOutputType OutputType = 0 + + // WitnessScriptOutputType is an empty key ({0x01}). + // + // The value is the witness script of this input, if it has one. + WitnessScriptOutputType OutputType = 1 + + j // Bip32DerivationOutputType is used to communicate derivation information + // needed to spend this output. The key is ({0x02}|{public key}). + // + // The value is master key fingerprint concatenated with the derivation + // path of the public key. The derivation path is represented as 32-bit + // little endian unsigned integer indexes concatenated with each other. + // Public keys are those needed to spend this output. + Bip32DerivationOutputType OutputType = 2 +) From 9411189e0eade97e8980a3a57fafd5418888616c Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 15 Jan 2020 17:36:21 -0800 Subject: [PATCH 02/13] psbt: move input into new file --- psbt/partial_input.go | 361 ++++++++++++++++++++++++++++++++++++++++++ psbt/psbt.go | 305 ----------------------------------- 2 files changed, 361 insertions(+), 305 deletions(-) create mode 100644 psbt/partial_input.go diff --git a/psbt/partial_input.go b/psbt/partial_input.go new file mode 100644 index 0000000..3b4d123 --- /dev/null +++ b/psbt/partial_input.go @@ -0,0 +1,361 @@ +package psbt + +import ( + "bytes" + "encoding/binary" + "io" + "sort" + + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" +) + +// PInput is a struct encapsulating all the data that can be attached to any +// specific input of the PSBT. +type PInput struct { + NonWitnessUtxo *wire.MsgTx + WitnessUtxo *wire.TxOut + PartialSigs []*PartialSig + SighashType txscript.SigHashType + RedeemScript []byte + WitnessScript []byte + Bip32Derivation []*Bip32Derivation + FinalScriptSig []byte + FinalScriptWitness []byte + Unknowns []*Unknown +} + +// NewPsbtInput creates an instance of PsbtInput given either a nonWitnessUtxo +// or a witnessUtxo. +// +// 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 { + + return &PInput{ + NonWitnessUtxo: nonWitnessUtxo, + WitnessUtxo: witnessUtxo, + PartialSigs: []*PartialSig{}, + SighashType: 0, + RedeemScript: nil, + WitnessScript: nil, + Bip32Derivation: []*Bip32Derivation{}, + FinalScriptSig: nil, + FinalScriptWitness: nil, + Unknowns: nil, + } +} + +// IsSane returns true only if there are no conflicting values in the Psbt +// PInput. It checks that witness and non-witness utxo entries do not both +// exist, and that witnessScript entries are only added to witness inputs. +func (pi *PInput) IsSane() bool { + + if pi.NonWitnessUtxo != nil && pi.WitnessUtxo != nil { + return false + } + if pi.WitnessUtxo == nil && pi.WitnessScript != nil { + return false + } + if pi.WitnessUtxo == nil && pi.FinalScriptWitness != nil { + return false + } + + return true +} + +// 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) + if err != nil { + return err + } + if keyint == -1 { + // Reached separator byte + break + } + value, err := wire.ReadVarBytes( + r, 0, MaxPsbtValueLength, "PSBT value", + ) + if err != nil { + return err + } + + switch InputType(keyint) { + + case NonWitnessUtxoType: + if pi.NonWitnessUtxo != nil { + return ErrDuplicateKey + } + if keydata != nil { + return ErrInvalidKeydata + } + tx := wire.NewMsgTx(2) + + err := tx.Deserialize(bytes.NewReader(value)) + if err != nil { + return err + } + pi.NonWitnessUtxo = tx + + case WitnessUtxoType: + if pi.WitnessUtxo != nil { + return ErrDuplicateKey + } + if keydata != nil { + return ErrInvalidKeydata + } + txout, err := readTxOut(value) + if err != nil { + return err + } + pi.WitnessUtxo = txout + + case PartialSigType: + newPartialSig := PartialSig{ + PubKey: keydata, + Signature: value, + } + + if !newPartialSig.checkValid() { + return ErrInvalidPsbtFormat + } + + // Duplicate keys are not allowed + for _, x := range pi.PartialSigs { + if bytes.Equal(x.PubKey, newPartialSig.PubKey) { + return ErrDuplicateKey + } + } + + pi.PartialSigs = append(pi.PartialSigs, &newPartialSig) + + case SighashType: + if pi.SighashType != 0 { + return ErrDuplicateKey + } + if keydata != nil { + return ErrInvalidKeydata + } + + shtype := txscript.SigHashType( + binary.LittleEndian.Uint32(value), + ) + pi.SighashType = shtype + + case RedeemScriptInputType: + if pi.RedeemScript != nil { + return ErrDuplicateKey + } + if keydata != nil { + return ErrInvalidKeydata + } + pi.RedeemScript = value + + case WitnessScriptInputType: + if pi.WitnessScript != nil { + return ErrDuplicateKey + } + if keydata != nil { + return ErrInvalidKeydata + } + pi.WitnessScript = value + + case Bip32DerivationInputType: + if !validatePubkey(keydata) { + return ErrInvalidPsbtFormat + } + master, derivationPath, err := readBip32Derivation(value) + if err != nil { + return err + } + + // Duplicate keys are not allowed + for _, x := range pi.Bip32Derivation { + if bytes.Equal(x.PubKey, keydata) { + return ErrDuplicateKey + } + } + + pi.Bip32Derivation = append( + pi.Bip32Derivation, + &Bip32Derivation{ + PubKey: keydata, + MasterKeyFingerprint: master, + Bip32Path: derivationPath, + }, + ) + + case FinalScriptSigType: + if pi.FinalScriptSig != nil { + return ErrDuplicateKey + } + if keydata != nil { + return ErrInvalidKeydata + } + + pi.FinalScriptSig = value + + case FinalScriptWitnessType: + if pi.FinalScriptWitness != nil { + return ErrDuplicateKey + } + if keydata != nil { + return ErrInvalidKeydata + } + + pi.FinalScriptWitness = value + + default: + // A fall through case for any proprietary types. + keyintanddata := []byte{byte(keyint)} + keyintanddata = append(keyintanddata, keydata...) + newUnknown := &Unknown{ + Key: keyintanddata, + Value: value, + } + + // 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 + } + } + + pi.Unknowns = append(pi.Unknowns, newUnknown) + } + } + + return nil +} + +// 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 + } + + if pi.NonWitnessUtxo != nil { + var buf bytes.Buffer + err := pi.NonWitnessUtxo.Serialize(&buf) + if err != nil { + return err + } + + err = serializeKVPairWithType( + w, uint8(NonWitnessUtxoType), nil, buf.Bytes(), + ) + if err != nil { + return err + } + } + if pi.WitnessUtxo != nil { + var buf bytes.Buffer + err := wire.WriteTxOut(&buf, 0, 0, pi.WitnessUtxo) + if err != nil { + return err + } + + err = serializeKVPairWithType( + w, uint8(WitnessUtxoType), nil, buf.Bytes(), + ) + if err != nil { + return err + } + } + + if pi.FinalScriptSig == nil && pi.FinalScriptWitness == nil { + sort.Sort(PartialSigSorter(pi.PartialSigs)) + for _, ps := range pi.PartialSigs { + err := serializeKVPairWithType( + w, uint8(PartialSigType), ps.PubKey, + ps.Signature, + ) + if err != nil { + return err + } + } + + if pi.SighashType != 0 { + var shtBytes [4]byte + binary.LittleEndian.PutUint32( + shtBytes[:], uint32(pi.SighashType), + ) + + err := serializeKVPairWithType( + w, uint8(SighashType), nil, shtBytes[:], + ) + if err != nil { + return err + } + } + + if pi.RedeemScript != nil { + err := serializeKVPairWithType( + w, uint8(RedeemScriptInputType), nil, + pi.RedeemScript, + ) + if err != nil { + return err + } + } + + if pi.WitnessScript != nil { + err := serializeKVPairWithType( + w, uint8(WitnessScriptInputType), nil, + pi.WitnessScript, + ) + if err != nil { + return err + } + } + + sort.Sort(Bip32Sorter(pi.Bip32Derivation)) + for _, kd := range pi.Bip32Derivation { + err := serializeKVPairWithType( + w, + uint8(Bip32DerivationInputType), kd.PubKey, + SerializeBIP32Derivation( + kd.MasterKeyFingerprint, kd.Bip32Path, + ), + ) + if err != nil { + return err + } + } + } + + if pi.FinalScriptSig != nil { + err := serializeKVPairWithType( + w, uint8(FinalScriptSigType), nil, pi.FinalScriptSig, + ) + if err != nil { + return err + } + } + + if pi.FinalScriptWitness != nil { + err := serializeKVPairWithType( + w, uint8(FinalScriptWitnessType), nil, pi.FinalScriptWitness, + ) + if err != nil { + return err + } + } + + // Unknown is a special case; we don't have a key type, only a key and + // a value field + for _, kv := range pi.Unknowns { + err := serializeKVpair(w, kv.Key, kv.Value) + if err != nil { + return err + } + } + + return nil +} diff --git a/psbt/psbt.go b/psbt/psbt.go index 7b4100a..aaebbd5 100644 --- a/psbt/psbt.go +++ b/psbt/psbt.go @@ -112,311 +112,6 @@ type Unknown struct { Value []byte } -// PInput is a struct encapsulating all the data that can be attached -// to any specific input of the PSBT. -type PInput struct { - NonWitnessUtxo *wire.MsgTx - WitnessUtxo *wire.TxOut - PartialSigs []*PartialSig - SighashType txscript.SigHashType - RedeemScript []byte - WitnessScript []byte - Bip32Derivation []*Bip32Derivation - FinalScriptSig []byte - FinalScriptWitness []byte - Unknowns []*Unknown -} - -// NewPsbtInput creates an instance of PsbtInput given either a -// nonWitnessUtxo or a witnessUtxo. -// 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 { - return &PInput{ - NonWitnessUtxo: nonWitnessUtxo, - WitnessUtxo: witnessUtxo, - PartialSigs: []*PartialSig{}, - SighashType: 0, - RedeemScript: nil, - WitnessScript: nil, - Bip32Derivation: []*Bip32Derivation{}, - FinalScriptSig: nil, - FinalScriptWitness: nil, - Unknowns: nil, - } -} - -// IsSane returns true only if there are no conflicting -// values in the Psbt PInput. It checks that witness and non-witness -// utxo entries do not both exist, and that witnessScript entries are only -// added to witness inputs. -func (pi *PInput) IsSane() bool { - - if pi.NonWitnessUtxo != nil && pi.WitnessUtxo != nil { - return false - } - if pi.WitnessUtxo == nil && pi.WitnessScript != nil { - return false - } - if pi.WitnessUtxo == nil && pi.FinalScriptWitness != nil { - return false - } - - return true -} - -func (pi *PInput) deserialize(r io.Reader) error { - for { - keyint, keydata, err := getKey(r) - if err != nil { - return err - } - if keyint == -1 { - // Reached separator byte - break - } - value, err := wire.ReadVarBytes(r, 0, MaxPsbtValueLength, - "PSBT value") - if err != nil { - return err - } - - switch uint8(keyint) { - - case PsbtInNonWitnessUtxo: - if pi.NonWitnessUtxo != nil { - return ErrDuplicateKey - } - if keydata != nil { - return ErrInvalidKeydata - } - tx := wire.NewMsgTx(2) - if err := tx.Deserialize(bytes.NewReader(value)); err != nil { - return err - } - pi.NonWitnessUtxo = tx - - case PsbtInWitnessUtxo: - if pi.WitnessUtxo != nil { - return ErrDuplicateKey - } - if keydata != nil { - return ErrInvalidKeydata - } - txout, err := readTxOut(value) - if err != nil { - return err - } - pi.WitnessUtxo = txout - - case PsbtInPartialSig: - newPartialSig := PartialSig{PubKey: keydata, - Signature: value} - if !newPartialSig.checkValid() { - return ErrInvalidPsbtFormat - } - // Duplicate keys are not allowed - for _, x := range pi.PartialSigs { - if bytes.Equal(x.PubKey, newPartialSig.PubKey) { - return ErrDuplicateKey - } - } - pi.PartialSigs = append(pi.PartialSigs, &newPartialSig) - - case PsbtInSighashType: - if pi.SighashType != 0 { - return ErrDuplicateKey - } - if keydata != nil { - return ErrInvalidKeydata - } - shtype := txscript.SigHashType(binary.LittleEndian.Uint32(value)) - pi.SighashType = shtype - - case PsbtInRedeemScript: - if pi.RedeemScript != nil { - return ErrDuplicateKey - } - if keydata != nil { - return ErrInvalidKeydata - } - pi.RedeemScript = value - - case PsbtInWitnessScript: - if pi.WitnessScript != nil { - return ErrDuplicateKey - } - if keydata != nil { - return ErrInvalidKeydata - } - pi.WitnessScript = value - - case PsbtInBip32Derivation: - if !validatePubkey(keydata) { - return ErrInvalidPsbtFormat - } - master, derivationPath, err := readBip32Derivation(value) - if err != nil { - return err - } - // Duplicate keys are not allowed - for _, x := range pi.Bip32Derivation { - if bytes.Equal(x.PubKey, keydata) { - return ErrDuplicateKey - } - } - pi.Bip32Derivation = append(pi.Bip32Derivation, - &Bip32Derivation{ - PubKey: keydata, - MasterKeyFingerprint: master, - Bip32Path: derivationPath, - }) - - case PsbtInFinalScriptSig: - if pi.FinalScriptSig != nil { - return ErrDuplicateKey - } - if keydata != nil { - return ErrInvalidKeydata - } - pi.FinalScriptSig = value - - case PsbtInFinalScriptWitness: - if pi.FinalScriptWitness != nil { - return ErrDuplicateKey - } - if keydata != nil { - return ErrInvalidKeydata - } - pi.FinalScriptWitness = value - - default: - keyintanddata := []byte{byte(keyint)} - keyintanddata = append(keyintanddata, keydata...) - newUnknown := &Unknown{ - Key: keyintanddata, - Value: value, - } - // 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 - } - } - pi.Unknowns = append(pi.Unknowns, newUnknown) - } - } - return nil -} - -func (pi *PInput) serialize(w io.Writer) error { - - if !pi.IsSane() { - return ErrInvalidPsbtFormat - } - - if pi.NonWitnessUtxo != nil { - var buf bytes.Buffer - err := pi.NonWitnessUtxo.Serialize(&buf) - if err != nil { - return err - } - err = serializeKVPairWithType(w, PsbtInNonWitnessUtxo, nil, - buf.Bytes()) - if err != nil { - return err - } - } - if pi.WitnessUtxo != nil { - - var buf bytes.Buffer - err := wire.WriteTxOut(&buf, 0, 0, pi.WitnessUtxo) - if err != nil { - return err - } - err = serializeKVPairWithType(w, PsbtInWitnessUtxo, nil, buf.Bytes()) - if err != nil { - return err - } - } - if pi.FinalScriptSig == nil && pi.FinalScriptWitness == nil { - - sort.Sort(PartialSigSorter(pi.PartialSigs)) - for _, ps := range pi.PartialSigs { - err := serializeKVPairWithType(w, PsbtInPartialSig, ps.PubKey, - ps.Signature) - if err != nil { - return err - } - } - - if pi.SighashType != 0 { - var shtBytes [4]byte - binary.LittleEndian.PutUint32(shtBytes[:], - uint32(pi.SighashType)) - err := serializeKVPairWithType(w, PsbtInSighashType, nil, - shtBytes[:]) - if err != nil { - return err - } - } - if pi.RedeemScript != nil { - err := serializeKVPairWithType(w, PsbtInRedeemScript, nil, - pi.RedeemScript) - if err != nil { - return err - } - } - if pi.WitnessScript != nil { - err := serializeKVPairWithType(w, PsbtInWitnessScript, nil, - pi.WitnessScript) - if err != nil { - return err - } - } - - sort.Sort(Bip32Sorter(pi.Bip32Derivation)) - for _, kd := range pi.Bip32Derivation { - err := serializeKVPairWithType(w, PsbtInBip32Derivation, - kd.PubKey, - SerializeBIP32Derivation(kd.MasterKeyFingerprint, - kd.Bip32Path)) - if err != nil { - return err - } - } - } - - if pi.FinalScriptSig != nil { - err := serializeKVPairWithType(w, PsbtInFinalScriptSig, nil, - pi.FinalScriptSig) - if err != nil { - return err - } - } - - if pi.FinalScriptWitness != nil { - err := serializeKVPairWithType(w, PsbtInFinalScriptWitness, nil, - pi.FinalScriptWitness) - if err != nil { - return err - } - } - - // Unknown is a special case; we don't have a key type, only - // a key and a value field - for _, kv := range pi.Unknowns { - err := serializeKVpair(w, kv.Key, kv.Value) - if err != nil { - return err - } - } - - return nil -} // POutput is a struct encapsulating all the data that can be attached // to any specific output of the PSBT. From 33335d05df6302dde79dc18f64eaeec357dfd8f9 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 15 Jan 2020 17:36:48 -0800 Subject: [PATCH 03/13] psbt: move output into new file --- psbt/partial_output.go | 139 +++++++++++++++++++++++++++++++++++++++++ psbt/psbt.go | 109 -------------------------------- 2 files changed, 139 insertions(+), 109 deletions(-) create mode 100644 psbt/partial_output.go diff --git a/psbt/partial_output.go b/psbt/partial_output.go new file mode 100644 index 0000000..64d1bd4 --- /dev/null +++ b/psbt/partial_output.go @@ -0,0 +1,139 @@ +package psbt + +import ( + "bytes" + "io" + "sort" + + "github.com/btcsuite/btcd/wire" +) + +// POutput is a struct encapsulating all the data that can be attached +// to any specific output of the PSBT. +type POutput struct { + RedeemScript []byte + WitnessScript []byte + Bip32Derivation []*Bip32Derivation +} + +// NewPsbtOutput creates an instance of PsbtOutput; the three parameters +// redeemScript, witnessScript and Bip32Derivation are all allowed to be +// `nil`. +func NewPsbtOutput(redeemScript []byte, witnessScript []byte, + bip32Derivation []*Bip32Derivation) *POutput { + return &POutput{ + RedeemScript: redeemScript, + WitnessScript: witnessScript, + Bip32Derivation: bip32Derivation, + } +} + +// 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) + if err != nil { + return err + } + if keyint == -1 { + // Reached separator byte + break + } + + value, err := wire.ReadVarBytes( + r, 0, MaxPsbtValueLength, "PSBT value", + ) + if err != nil { + return err + } + + switch OutputType(keyint) { + + case RedeemScriptOutputType: + if po.RedeemScript != nil { + return ErrDuplicateKey + } + if keydata != nil { + return ErrInvalidKeydata + } + po.RedeemScript = value + + case WitnessScriptOutputType: + if po.WitnessScript != nil { + return ErrDuplicateKey + } + if keydata != nil { + return ErrInvalidKeydata + } + po.WitnessScript = value + + case Bip32DerivationOutputType: + if !validatePubkey(keydata) { + return ErrInvalidKeydata + } + master, derivationPath, err := readBip32Derivation(value) + if err != nil { + return err + } + + // Duplicate keys are not allowed + for _, x := range po.Bip32Derivation { + if bytes.Equal(x.PubKey, keydata) { + return ErrDuplicateKey + } + } + + po.Bip32Derivation = append(po.Bip32Derivation, + &Bip32Derivation{ + PubKey: keydata, + MasterKeyFingerprint: master, + Bip32Path: derivationPath, + }, + ) + + default: + // Unknown type is allowed for inputs but not outputs. + return ErrInvalidPsbtFormat + } + } + + return nil +} + +// serialize attempts to write out the target POutput into the passed +// io.Writer. +func (po *POutput) serialize(w io.Writer) error { + if po.RedeemScript != nil { + err := serializeKVPairWithType( + w, uint8(RedeemScriptOutputType), nil, po.RedeemScript, + ) + if err != nil { + return err + } + } + if po.WitnessScript != nil { + err := serializeKVPairWithType( + w, uint8(WitnessScriptOutputType), nil, po.WitnessScript, + ) + if err != nil { + return err + } + } + + sort.Sort(Bip32Sorter(po.Bip32Derivation)) + for _, kd := range po.Bip32Derivation { + err := serializeKVPairWithType(w, + uint8(Bip32DerivationOutputType), + kd.PubKey, + SerializeBIP32Derivation( + kd.MasterKeyFingerprint, + kd.Bip32Path, + ), + ) + if err != nil { + return err + } + } + + return nil +} diff --git a/psbt/psbt.go b/psbt/psbt.go index aaebbd5..89325a7 100644 --- a/psbt/psbt.go +++ b/psbt/psbt.go @@ -113,117 +113,8 @@ type Unknown struct { } -// POutput is a struct encapsulating all the data that can be attached -// to any specific output of the PSBT. -type POutput struct { - RedeemScript []byte - WitnessScript []byte - Bip32Derivation []*Bip32Derivation -} -// NewPsbtOutput creates an instance of PsbtOutput; the three parameters -// redeemScript, witnessScript and Bip32Derivation are all allowed to be -// `nil`. -func NewPsbtOutput(redeemScript []byte, witnessScript []byte, - bip32Derivation []*Bip32Derivation) *POutput { - return &POutput{ - RedeemScript: redeemScript, - WitnessScript: witnessScript, - Bip32Derivation: bip32Derivation, - } -} -func (po *POutput) deserialize(r io.Reader) error { - for { - keyint, keydata, err := getKey(r) - if err != nil { - return err - } - if keyint == -1 { - // Reached separator byte - break - } - value, err := wire.ReadVarBytes(r, 0, MaxPsbtValueLength, - "PSBT value") - if err != nil { - return err - } - - switch uint8(keyint) { - - case PsbtOutRedeemScript: - if po.RedeemScript != nil { - return ErrDuplicateKey - } - if keydata != nil { - return ErrInvalidKeydata - } - po.RedeemScript = value - - case PsbtOutWitnessScript: - if po.WitnessScript != nil { - return ErrDuplicateKey - } - if keydata != nil { - return ErrInvalidKeydata - } - po.WitnessScript = value - - case PsbtOutBip32Derivation: - if !validatePubkey(keydata) { - return ErrInvalidKeydata - } - master, derivationPath, err := readBip32Derivation(value) - if err != nil { - return err - } - // Duplicate keys are not allowed - for _, x := range po.Bip32Derivation { - if bytes.Equal(x.PubKey, keydata) { - return ErrDuplicateKey - } - } - po.Bip32Derivation = append(po.Bip32Derivation, - &Bip32Derivation{ - PubKey: keydata, - MasterKeyFingerprint: master, - Bip32Path: derivationPath, - }) - - default: - // unknown type is allowed for inputs but not outputs - return ErrInvalidPsbtFormat - } - } - return nil -} - -func (po *POutput) serialize(w io.Writer) error { - if po.RedeemScript != nil { - err := serializeKVPairWithType(w, PsbtOutRedeemScript, nil, - po.RedeemScript) - if err != nil { - return err - } - } - if po.WitnessScript != nil { - err := serializeKVPairWithType(w, PsbtOutWitnessScript, nil, - po.WitnessScript) - if err != nil { - return err - } - } - sort.Sort(Bip32Sorter(po.Bip32Derivation)) - for _, kd := range po.Bip32Derivation { - err := serializeKVPairWithType(w, PsbtOutBip32Derivation, - kd.PubKey, - SerializeBIP32Derivation(kd.MasterKeyFingerprint, - kd.Bip32Path)) - if err != nil { - return err - } - } - return nil } // Psbt is a set of 1 + N + M key-value pair lists, 1 global, From a94de55e850e8ff43925922fe105ce551ee6f5ee Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 15 Jan 2020 17:39:15 -0800 Subject: [PATCH 04/13] psbt: move bip32 structs into new file --- psbt/bip32.go | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 psbt/bip32.go diff --git a/psbt/bip32.go b/psbt/bip32.go new file mode 100644 index 0000000..9fbfc73 --- /dev/null +++ b/psbt/bip32.go @@ -0,0 +1,77 @@ +package psbt + +import ( + "bytes" + "encoding/binary" +) + +// Bip32Derivation encapsulates the data for the input and output +// Bip32Derivation key-value fields. +// +// TODO(roasbeef): use hdkeychain here instead? +type Bip32Derivation struct { + // PubKey is the raw pubkey serialized in compressed format. + PubKey []byte + + // MasterKeyFingerprint is the finger print of the master pubkey. + MasterKeyFingerprint uint32 + + // Bip32Path is the BIP 32 path with child index as a distinct integer. + Bip32Path []uint32 +} + +// checkValid ensures that the PubKey in the Bip32Derivation struct is valid. +func (pb *Bip32Derivation) checkValid() bool { + return validatePubkey(pb.PubKey) +} + +// Bip32Sorter implements sort.Interface for the Bip32Derivation struct. +type Bip32Sorter []*Bip32Derivation + +func (s Bip32Sorter) Len() int { return len(s) } + +func (s Bip32Sorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +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 +// 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) { + + if len(path)%4 != 0 || len(path)/4-1 < 1 { + return 0, nil, ErrInvalidPsbtFormat + } + + masterKeyInt := binary.LittleEndian.Uint32(path[:4]) + + var paths []uint32 + for i := 4; i < len(path); i += 4 { + paths = append(paths, binary.LittleEndian.Uint32(path[i:i+4])) + } + + return masterKeyInt, paths, nil +} + +// SerializeBIP32Derivation takes a master key fingerprint as defined in BIP32, +// along with a path specified as a list of uint32 values, and returns a +// bytestring specifying the derivation in the format required by BIP174: // +// master key fingerprint (4) || child index (4) || child index (4) || .... +func SerializeBIP32Derivation(masterKeyFingerprint uint32, + bip32Path []uint32) []byte { + + var masterKeyBytes [4]byte + binary.LittleEndian.PutUint32(masterKeyBytes[:], masterKeyFingerprint) + + derivationPath := make([]byte, 0, 4+4*len(bip32Path)) + derivationPath = append(derivationPath, masterKeyBytes[:]...) + for _, path := range bip32Path { + var pathbytes [4]byte + binary.LittleEndian.PutUint32(pathbytes[:], path) + derivationPath = append(derivationPath, pathbytes[:]...) + } + + return derivationPath +} From 57a65433945a857a55fc072fd57eff0f76a01387 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 15 Jan 2020 17:40:21 -0800 Subject: [PATCH 05/13] psbt: remove Creator struct, create New function as entry point into package The creator struct really didn't do anything before, as a result in this commit we move to get rid of it, and create a `New` method as customarily used in go packages. --- psbt/creator.go | 65 ++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 36 deletions(-) diff --git a/psbt/creator.go b/psbt/creator.go index 9258a27..a5f832e 100644 --- a/psbt/creator.go +++ b/psbt/creator.go @@ -4,44 +4,38 @@ package psbt -// The Creator has only one function: -// takes a list of inputs and outputs and converts them to -// the simplest Psbt, i.e. one with no data appended to inputs or outputs, -// but only the raw unsigned transaction in the global section. - import ( "github.com/btcsuite/btcd/wire" ) -// Creator holds a reference to a created Psbt struct. -type Creator struct { - Cpsbt *Psbt -} +// MinTxVersion is the lowest transaction version that we'll permit. +const MinTxVersion = 1 -// CreatePsbt , on provision of an input and output 'skeleton' for -// the transaction, returns a Creator struct. -// Note that we require OutPoints and not TxIn structs, as we will -// only populate the txid:n information, *not* any scriptSig/witness -// information. The values of nLockTime, nSequence (per input) and -// transaction version (must be 1 of 2) must be specified here. Note -// that the default nSequence value is wire.MaxTxInSequenceNum. -func (c *Creator) CreatePsbt(inputs []*wire.OutPoint, - outputs []*wire.TxOut, Version int32, nLockTime uint32, - nSequences []uint32) error { - // Create the new struct; the input and output lists will be empty, - // the unsignedTx object must be constructed and serialized, - // and that serialization should be entered as the only entry for - // the globalKVPairs list. +// New on provision of an input and output 'skeleton' for the transaction, a +// new partially populated PBST packet. The populated packet will include the +// unsigned transaction, and the set of known inputs and outputs contained +// within the unsigned transaction. The values of nLockTime, nSequence (per +// input) and transaction version (must be 1 of 2) must be specified here. Note +// that the default nSequence value is wire.MaxTxInSequenceNum. Referencing +// the PSBT BIP, this function serves the roles of teh Creator. +func New(inputs []*wire.OutPoint, + outputs []*wire.TxOut, version int32, nLockTime uint32, + nSequences []uint32) (*Packet, error) { - // Check the version is a valid Bitcoin tx version; the nLockTime - // can be any valid uint32. There must be one sequence number per - // input. - if !(Version == 1 || Version == 2) || len(nSequences) != len(inputs) { - return ErrInvalidPsbtFormat + // Create the new struct; the input and output lists will be empty, the + // unsignedTx object must be constructed and serialized, and that + // serialization should be entered as the only entry for the + // globalKVPairs list. + // + // Ensure that the version of the transaction is greater then our + // minimum allowed transaction version. There must be one sequence + // number per input. + if version < MinTxVersion || len(nSequences) != len(inputs) { + return nil, ErrInvalidPsbtFormat } - unsignedTx := wire.NewMsgTx(Version) - unsignedTx.LockTime = nLockTime + unsignedTx := wire.NewMsgTx(version) + unsignedTx.LockTime = nLockTime for i, in := range inputs { unsignedTx.AddTxIn(&wire.TxIn{ PreviousOutPoint: *in, @@ -57,14 +51,13 @@ func (c *Creator) CreatePsbt(inputs []*wire.OutPoint, // transaction; the unknown list can be nil. pInputs := make([]PInput, len(unsignedTx.TxIn)) pOutputs := make([]POutput, len(unsignedTx.TxOut)) - c.Cpsbt = &Psbt{ + + // This new Psbt is "raw" and contains no key-value fields, so sanity + // checking with c.Cpsbt.SanityCheck() is not required. + return &Packet{ UnsignedTx: unsignedTx, Inputs: pInputs, Outputs: pOutputs, Unknowns: nil, - } - - // This new Psbt is "raw" and contains no key-value fields, - // so sanity checking with c.Cpsbt.SanityCheck() is not required. - return nil + }, nil } From 41cb8d70da1f1544ef9d5268f108b3fce5bd8f74 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 15 Jan 2020 17:41:17 -0800 Subject: [PATCH 06/13] psbt: modify Extract method to return the transaction directly In this commit, we modify the Extract method to return the transaction directly as in many cases a user will likely want to write the transaction to disk, or perform additional validation rather than obtain the raw bytes directly. --- psbt/extractor.go | 71 ++++++++++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/psbt/extractor.go b/psbt/extractor.go index de80fec..dc7f10f 100644 --- a/psbt/extractor.go +++ b/psbt/extractor.go @@ -12,44 +12,63 @@ package psbt import ( "bytes" + "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" ) -// Extract takes a finalized psbt and outputs a network serialization -func Extract(p *Psbt) ([]byte, error) { +// Extract takes a finalized psbt.Packet and outputs a finalized transaction +// instance. Note that if the PSBT is in-complete, then an error +// ErrIncompletePSBT will be returned. As the extracted transaction has been +// fully finalized, it will be ready for network broadcast once returned. +func Extract(p *Packet) (*wire.MsgTx, error) { + // If the packet isn't complete, then we'll return an error as it + // doesn't have all the required witness data. if !p.IsComplete() { return nil, ErrIncompletePSBT } - var err error - // We take the existing UnsignedTx field and append SignatureScript - // and Witness as appropriate, then allow MsgTx to do the serialization - // for us. - newTx := p.UnsignedTx.Copy() - for i, tin := range newTx.TxIn { + + // First, we'll make a copy of the underlying unsigned transaction (the + // initial template) so we don't mutate it during our activates below. + finalTx := p.UnsignedTx.Copy() + + // For each input, we'll now populate any relevant witness and + // sigScript data. + for i, tin := range finalTx.TxIn { + // We'll grab the corresponding internal packet input which + // matches this materialized transaction input and emplace that + // final sigScript (if present). pInput := p.Inputs[i] if pInput.FinalScriptSig != nil { tin.SignatureScript = pInput.FinalScriptSig } + + // Similarly, if there's a final witness, then we'll also need + // to extract that as well, parsing the lower-level transaction + // encoding. if pInput.FinalScriptWitness != nil { - // to set the witness, need to re-deserialize the field - // For each input, the witness is encoded as a stack - // with one or more items. Therefore, we first read a - // varint which encodes the number of stack items. - r := bytes.NewReader(pInput.FinalScriptWitness) - witCount, err := wire.ReadVarInt(r, 0) + // In order to set the witness, need to re-deserialize + // the field as encoded within the PSBT packet. For + // each input, the witness is encoded as a stack with + // one or more items. + witnessReader := bytes.NewReader( + pInput.FinalScriptWitness, + ) + + // First we extract the number of witness elements + // encoded in the above witnessReader. + witCount, err := wire.ReadVarInt(witnessReader, 0) if err != nil { return nil, err } - // Then for witCount number of stack items, each item - // has a varint length prefix, followed by the witness - // item itself. - tin.Witness = make([][]byte, witCount) + // Now that we know how may 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++ { - // the 10000 size limit is as per BIP141 for witness script; - // TODO this constant should be somewhere else in the lib, - // perhaps btcd/wire/common.go ? - wit, err := wire.ReadVarBytes(r, 0, 10000, "witness") + wit, err := wire.ReadVarBytes( + witnessReader, 0, txscript.MaxScriptSize, "witness", + ) if err != nil { return nil, err } @@ -57,10 +76,6 @@ func Extract(p *Psbt) ([]byte, error) { } } } - var networkSerializedTx bytes.Buffer - err = newTx.Serialize(&networkSerializedTx) - if err != nil { - return nil, err - } - return networkSerializedTx.Bytes(), nil + + return finalTx, nil } From 6d70b190b0347c4fc9fd4aad926b808ce8b63343 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 15 Jan 2020 17:42:03 -0800 Subject: [PATCH 07/13] psbt: create new utils file, refactor finalizer for consistent code style --- psbt/finalizer.go | 599 ++++++++++++++++++++++----------------------- psbt/partialsig.go | 58 +++++ psbt/utils.go | 272 ++++++++++++++++++++ 3 files changed, 618 insertions(+), 311 deletions(-) create mode 100644 psbt/partialsig.go create mode 100644 psbt/utils.go diff --git a/psbt/finalizer.go b/psbt/finalizer.go index 5fa3f9e..0bf9dbb 100644 --- a/psbt/finalizer.go +++ b/psbt/finalizer.go @@ -6,480 +6,457 @@ package psbt // The Finalizer requires provision of a single PSBT input // in which all necessary signatures are encoded, and -// uses it to construct valid final scriptSig and scriptWitness +// uses it to construct valid final sigScript and scriptWitness // fields. // NOTE that p2sh (legacy) and p2wsh currently support only // multisig and no other custom script. import ( - "bytes" - "errors" - - "io" - "sort" - "github.com/btcsuite/btcd/txscript" - "github.com/btcsuite/btcd/wire" ) -// A utility function due to non-exported witness serialization -// (writeTxWitness encodes the bitcoin protocol encoding for a transaction -// input's witness into w). -func writeTxWitness(w io.Writer, wit [][]byte) error { - err := wire.WriteVarInt(w, 0, uint64(len(wit))) - if err != nil { - return err - } - for _, item := range wit { - err = wire.WriteVarBytes(w, 0, item) - if err != nil { - return err - } - } - return nil -} - -// writePKHWitness writes a witness for a p2wkh spending input -func writePKHWitness(sig []byte, pub []byte) ([]byte, error) { - var buf bytes.Buffer - var witnessItems = [][]byte{sig, pub} - err := writeTxWitness(&buf, witnessItems) - if err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -// checkIsMultisigScript is a utility function to check wheter -// a given redeemscript fits the standard multisig template used -// in all p2sh based multisig, given a set of pubkeys for redemption. -func checkIsMultiSigScript(pubKeys [][]byte, sigs [][]byte, - script []byte) bool { - - // First insist that the script type is multisig - if txscript.GetScriptClass(script) != txscript.MultiSigTy { - return false - } - - // Inspect the script to ensure that the number of sigs and - // pubkeys is correct - numSigs, numPubKeys, err := txscript.CalcMultiSigStats(script) - - if err != nil { - return false - } - - if numPubKeys != len(pubKeys) || numSigs != len(sigs) { - return false - } - - return true -} - -// extractKeyOrderFromScript is a utility function -// to extract an ordered list of signatures, given -// a serialized script (redeemscript or witness script), -// a list of pubkeys and the signatures corresponding to those -// pubkeys, so that the signatures will be embedded in the final -// scriptSig or scriptWitness in the correct order. -func extractKeyOrderFromScript(script []byte, expectedPubkeys [][]byte, - sigs [][]byte) ([][]byte, error) { - - if !checkIsMultiSigScript(expectedPubkeys, sigs, script) { - return nil, ErrUnsupportedScriptType - } - // Arrange the pubkeys and sigs into a slice of format: - // [[pub,sig], [pub,sig],..] - pubsSigs := [][][]byte{} - for i, pub := range expectedPubkeys { - tmp := [][]byte{pub, sigs[i]} - pubsSigs = append(pubsSigs, tmp) - } - type kv struct { - Key int - Value [][]byte - } - var positionMap []kv - for _, p := range pubsSigs { - pos := bytes.Index(script, p[0]) - if pos < 0 { - return nil, errors.New("Script does not contain pubkeys") - } - positionMap = append(positionMap, kv{Key: pos, Value: p}) - } - - sort.Slice(positionMap, func(i, j int) bool { - return positionMap[i].Key < positionMap[j].Key - }) - // Build the return array of signatures - sigsNew := [][]byte{} - for _, x := range positionMap { - sigsNew = append(sigsNew, x.Value[1]) - } - return sigsNew, nil -} - -// getMultisigScriptWitness creates a full Witness field for the transaction, -// given the public keys and signatures to be appended, after checking -// that the witnessScript is of type M of N multisig. This -// is used for both p2wsh and nested p2wsh multisig cases. -func getMultisigScriptWitness(witnessScript []byte, pubKeys [][]byte, - sigs [][]byte) ([]byte, error) { - - orderedSigs, err := extractKeyOrderFromScript(witnessScript, pubKeys, sigs) - if err != nil { - return nil, err - } - - var buf bytes.Buffer - var witnessItems = [][]byte{ - nil} - for _, os := range orderedSigs { - witnessItems = append(witnessItems, os) - } - witnessItems = append(witnessItems, witnessScript) - err = writeTxWitness(&buf, witnessItems) - if err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -// checkSigHashFlags compares the sighash flag byte on a signature with the -// value expected according to any PsbtInSighashType field in this section -// of the PSBT, and returns true if they match, false otherwise. -// If no SighashType field exists, it is assumed to be SIGHASH_ALL. -// TODO sighash type not restricted to one byte in future? -func checkSigHashFlags(sig []byte, input *PInput) bool { - expectedSighashType := txscript.SigHashAll - if input.SighashType != 0 { - expectedSighashType = input.SighashType - } - - return expectedSighashType == txscript.SigHashType(sig[len(sig)-1]) -} - -// isFinalized considers this input finalized if it contains -// at least one of the FinalScriptSig or FinalScriptWitness -// are filled (which only occurs in a successful call to Finalize*) -func isFinalized(p *Psbt, inIndex int) bool { +// isFinalized considers this input finalized if it contains at least one of +// the FinalScriptSig or FinalScriptWitness are filled (which only occurs in a +// successful call to Finalize*). +func isFinalized(p *Packet, inIndex int) bool { input := p.Inputs[inIndex] return input.FinalScriptSig != nil || input.FinalScriptWitness != nil } -// isFinalizable checks whether the structure of the entry -// for the input of the Psbt p at index inIndex contains sufficient -// information to finalize this input. Deduce the template -// from the contents. -func isFinalizable(p *Psbt, inIndex int) bool { - pInput := p.Inputs[inIndex] +// isFinalizableWitnessInput returns true if the target input is a witness UTXO +// that can be finalized. +func isFinalizableWitnessInput(pInput *PInput) bool { + pkScript := pInput.WitnessUtxo.PkScript - // Cannot be finalizable without any signatures - if pInput.PartialSigs == nil { - return false - } - - if pInput.WitnessUtxo != nil { - if txscript.IsWitnessProgram(pInput.WitnessUtxo.PkScript) { - if txscript.IsPayToWitnessScriptHash(pInput.WitnessUtxo.PkScript) { - if pInput.WitnessScript == nil || pInput.RedeemScript != nil { - return false - } - } else { - // if it's p2wkh there should be no redeemScript or witnessScript - if pInput.WitnessScript != nil || pInput.RedeemScript != nil { - return false - } - } - } else if txscript.IsPayToScriptHash(pInput.WitnessUtxo.PkScript) { - if pInput.RedeemScript == nil { - return false - } - // if it's nested, and it's p2wsh, it must have WitnessScript; - // if p2wkh, it must not. - if txscript.IsPayToWitnessScriptHash(pInput.RedeemScript) { - if pInput.WitnessScript == nil { - return false - } - } else if txscript.IsPayToWitnessPubKeyHash(pInput.RedeemScript) { - if pInput.WitnessScript != nil { - return false - } - } else { - // unrecognized type - return false - } - } - } else if pInput.NonWitnessUtxo != nil { - if pInput.WitnessScript != nil { - return false - } - outIndex := p.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Index - if txscript.IsPayToScriptHash(pInput.NonWitnessUtxo.TxOut[outIndex].PkScript) { - if pInput.RedeemScript == nil { + switch { + // If this is a native witness output, then we require both + // the witness script, but not a redeem script. + case txscript.IsWitnessProgram(pkScript): + if txscript.IsPayToWitnessScriptHash(pkScript) { + if pInput.WitnessScript == nil || + pInput.RedeemScript != nil { return false } } else { - if pInput.RedeemScript != nil { + // A P2WKH output on the other hand doesn't need + // neither a witnessScript or redeemScript. + if pInput.WitnessScript != nil || + pInput.RedeemScript != nil { return false } } - } else { - // one of witness and nonwitness utxo must be present + + // For nested P2SH inputs, we verify that a witness script is known. + case txscript.IsPayToScriptHash(pkScript): + if pInput.RedeemScript == nil { + return false + } + + // If this is a nested P2SH input, then it must also have a + // witness script, while we don't need one for P2WKH. + if txscript.IsPayToWitnessScriptHash(pInput.RedeemScript) { + if pInput.WitnessScript == nil { + return false + } + } else if txscript.IsPayToWitnessPubKeyHash(pInput.RedeemScript) { + if pInput.WitnessScript != nil { + return false + } + } else { + // unrecognized type + return false + } + + // If this isn't a nested nested P2SH output or a native witness + // output, then we can't finalize this input as we don't understand it. + default: return false } return true } -// MaybeFinalize attempts to finalize the input at index inIndex -// in the PSBT p, returning true with no error if it succeeds, OR -// if the input has already been finalized. -func MaybeFinalize(p *Psbt, inIndex int) (bool, error) { +// isFinalizableLegacyInput returns true of the passed input a legacy input +// (non-witness) that can be finalized. +func isFinalizableLegacyInput(p *Packet, pInput *PInput, inIndex int) bool { + // If the input has a witness, then it's invalid. + if pInput.WitnessScript != nil { + return false + } + + // Otherwise, we'll verify that we only have a RedeemScript if the prev + // output script is P2SH. + outIndex := p.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Index + if txscript.IsPayToScriptHash(pInput.NonWitnessUtxo.TxOut[outIndex].PkScript) { + if pInput.RedeemScript == nil { + return false + } + } else { + if pInput.RedeemScript != nil { + return false + } + } + + return true +} + +// isFinalizable checks whether the structure of the entry for the input of the +// psbt.Packet at index inIndex contains sufficient information to finalize +// this input. +func isFinalizable(p *Packet, inIndex int) bool { + pInput := p.Inputs[inIndex] + + // The input cannot be finalized without any signatures + if pInput.PartialSigs == nil { + return false + } + + // For an input to be finalized, we'll one of two possible top-level + // UTXOs present. Each UTXO type has a distinct set of requirements to + // be considered finalized. + switch { + + // A witness input must be either native P2WSH or nested P2SH with all + // relevant sigScript or witness data populated. + case pInput.WitnessUtxo != nil: + if !isFinalizableWitnessInput(&pInput) { + return false + } + + case pInput.NonWitnessUtxo != nil: + if !isFinalizableLegacyInput(p, &pInput, inIndex) { + return false + } + + // If neither a known UTXO type isn't present at all, then we'll + // return false as we need one of them. + default: + return false + } + + return true +} + +// MaybeFinalize attempts to finalize the input at index inIndex in the PSBT p, +// returning true with no error if it succeeds, OR if the input has already +// been finalized. +func MaybeFinalize(p *Packet, inIndex int) (bool, error) { if isFinalized(p, inIndex) { return true, nil } + if !isFinalizable(p, inIndex) { return false, ErrNotFinalizable } - err := Finalize(p, inIndex) - if err != nil { + + if err := Finalize(p, inIndex); err != nil { return false, err } + return true, nil } -// MaybeFinalizeAll attempts to finalize all inputs of the Psbt that -// are not already finalized, and returns an error if it fails to do so. -func MaybeFinalizeAll(p *Psbt) error { +// MaybeFinalizeAll attempts to finalize all inputs of the psbt.Packet that are +// not already finalized, and returns an error if it fails to do so. +func MaybeFinalizeAll(p *Packet) error { + for i := range p.UnsignedTx.TxIn { success, err := MaybeFinalize(p, i) if err != nil || !success { return err } } + return nil } -// Finalize assumes that the provided Psbt struct -// has all partial signatures and redeem scripts/witness scripts -// already prepared for the specified input, and so removes all temporary -// data and replaces them with completed scriptSig and witness -// fields, which are stored in key-types 07 and 08. The witness/ -// non-witness utxo fields in the inputs (key-types 00 and 01) are -// left intact as they may be needed for validation (?). -// If there is any invalid or incomplete data, an error is -// returned. -func Finalize(p *Psbt, inIndex int) error { - var err error +// Finalize assumes that the provided psbt.Packet struct has all partial +// signatures and redeem scripts/witness scripts already prepared for the +// specified input, and so removes all temporary data and replaces them with +// completed sigScript and witness fields, which are stored in key-types 07 and +// 08. The witness/non-witness utxo fields in the inputs (key-types 00 and 01) +// are left intact as they may be needed for validation (?). If there is any +// invalid or incomplete data, an error is returned. +func Finalize(p *Packet, inIndex int) error { pInput := p.Inputs[inIndex] - if pInput.WitnessUtxo != nil { - err = FinalizeWitness(p, inIndex) - if err != nil { + + // Depending on the UTXO type, we either attempt to finalize it as a + // witness or legacy UTXO. + switch { + case pInput.WitnessUtxo != nil: + if err := finalizeWitnessInput(p, inIndex); err != nil { return err } - } else if pInput.NonWitnessUtxo != nil { - err = FinalizeNonWitness(p, inIndex) - if err != nil { + + case pInput.NonWitnessUtxo != nil: + if err := finalizeNonWitnessInput(p, inIndex); err != nil { return err } - } else { + + default: return ErrInvalidPsbtFormat } - if err = p.SanityCheck(); err != nil { + // Before returning we sanity check the PSBT to ensure we don't extract + // an invalid transaction or produce an invalid intermediate state. + if err := p.SanityCheck(); err != nil { return err } + return nil } -// checkFinalScriptSigWitness checks whether a given input in the -// Psbt struct already has the fields 07 (FinalInScriptSig) or 08 -// (FinalInWitness). If so, it returns true. It does not modify the -// Psbt. -func checkFinalScriptSigWitness(p *Psbt, inIndex int) bool { +// checkFinalScriptSigWitness checks whether a given input in the psbt.Packet +// struct already has the fields 07 (FinalInScriptSig) or 08 (FinalInWitness). +// If so, it returns true. It does not modify the Psbt. +func checkFinalScriptSigWitness(p *Packet, inIndex int) bool { pInput := p.Inputs[inIndex] + if pInput.FinalScriptSig != nil { return true } + if pInput.FinalScriptWitness != nil { return true } + return false } -// FinalizeNonWitness attempts to create PsbtInFinalScriptSig field -// for input at index inIndex, and removes all other fields except -// for the utxo field, for an input of type non-witness, or returns -// an error. -func FinalizeNonWitness(p *Psbt, inIndex int) error { +// finalizeNonWitnessInput attempts to create a PsbtInFinalScriptSig field for +// the input at index inIndex, and removes all other fields except for the UTXO +// field, for an input of type non-witness, or returns an error. +func finalizeNonWitnessInput(p *Packet, inIndex int) error { + // If this input has already been finalized, then we'll return an error + // as we can't proceed. if checkFinalScriptSigWitness(p, inIndex) { return ErrInputAlreadyFinalized } - // Construct a scriptSig given the pubkey, signature (keytype 02), - // of which there might be multiple, and the redeem script - // field (keytype 04) if present (note, it is not present + + // Our goal here is to construct a sigScript given the pubkey, + // signature (keytype 02), of which there might be multiple, and the + // redeem script field (keytype 04) if present (note, it is not present // for p2pkh type inputs). - var scriptSig []byte - var err error + var sigScript []byte + pInput := p.Inputs[inIndex] containsRedeemScript := pInput.RedeemScript != nil - var pubKeys [][]byte - var sigs [][]byte + + var ( + pubKeys [][]byte + sigs [][]byte + ) for _, ps := range pInput.PartialSigs { pubKeys = append(pubKeys, ps.PubKey) + sigOK := checkSigHashFlags(ps.Signature, &pInput) if !sigOK { return ErrInvalidSigHashFlags } + sigs = append(sigs, ps.Signature) } + // We have failed to identify at least 1 (sig, pub) pair in the PSBT, + // which indicates it was not ready to be finalized. As a result, we + // can't proceed. if len(sigs) < 1 || len(pubKeys) < 1 { - // We have failed to identify at least 1 (sig, pub) pair - // in the PSBT, which indicates it was not ready to be finalized. return ErrNotFinalizable } + // If this input doesn't need a redeem script (P2PKH), then we'll + // construct a simple sigScript that's just the signature then the + // pubkey (OP_CHECKSIG). + var err error if !containsRedeemScript { - // p2pkh - insist on one sig/pub and build scriptSig + // At this point, we should only have a single signature and + // pubkey. if len(sigs) != 1 || len(pubKeys) != 1 { return ErrNotFinalizable } + + // In this case, our sigScript is just: . builder := txscript.NewScriptBuilder() builder.AddData(sigs[0]).AddData(pubKeys[0]) - scriptSig, err = builder.Script() + sigScript, err = builder.Script() if err != nil { return err } } else { - // This is assumed p2sh multisig - // Given redeemScript and pubKeys we can decide in what order - // signatures must be appended. - orderedSigs, err := extractKeyOrderFromScript(pInput.RedeemScript, - pubKeys, sigs) + // This is assumed p2sh multisig Given redeemScript and pubKeys + // we can decide in what order signatures must be appended. + orderedSigs, err := extractKeyOrderFromScript( + pInput.RedeemScript, pubKeys, sigs, + ) if err != nil { return err } - // TODO the below is specific to the multisig case. + + // At this point, we assume that this is a mult-sig input, so + // we construct our sigScript which looks something like this + // (mind the extra element for the extra multi-sig pop): + // * + // + // TODO(waxwing): the below is specific to the multisig case. builder := txscript.NewScriptBuilder() builder.AddOp(txscript.OP_FALSE) for _, os := range orderedSigs { builder.AddData(os) } builder.AddData(pInput.RedeemScript) - scriptSig, err = builder.Script() + sigScript, err = builder.Script() if err != nil { return err } } - // At this point, a scriptSig has been constructed. - // Remove all fields other than non-witness utxo (00) - // and finaliscriptsig (07) + + // At this point, a sigScript has been constructed. Remove all fields + // other than non-witness utxo (00) and finaliscriptsig (07) newInput := NewPsbtInput(pInput.NonWitnessUtxo, nil) - newInput.FinalScriptSig = scriptSig - // overwrite the entry in the input list at the correct index - // Note that this removes all the other entries in the list for - // this input index. + newInput.FinalScriptSig = sigScript + + // Overwrite the entry in the input list at the correct index. Note + // that this removes all the other entries in the list for this input + // index. p.Inputs[inIndex] = *newInput + return nil } -// FinalizeWitness attempts to create PsbtInFinalScriptSig field -// and PsbtInFinalScriptWitness field for input at index inIndex, -// and removes all other fields except for the utxo field, for an -// input of type witness, or returns an error. -func FinalizeWitness(p *Psbt, inIndex int) error { +// finalizeWitnessInput attempts to create PsbtInFinalScriptSig field and +// PsbtInFinalScriptWitness field for input at index inIndex, and removes all +// other fields except for the utxo field, for an input of type witness, or +// returns an error. +func finalizeWitnessInput(p *Packet, inIndex int) error { + // If this input has already been finalized, then we'll return an error + // as we can't proceed. if checkFinalScriptSigWitness(p, inIndex) { return ErrInputAlreadyFinalized } - // Construct a scriptSig given the redeem script - // field (keytype 04) if present (if not present it's empty - // as per bip141). - // Fill this in in field FinalScriptSig (keytype 07). - // And/or construct a FinalScriptWitness field (keytype 08), - // assuming either p2wkh or p2wsh multisig. - var scriptSig []byte - var witness []byte - var err error + + // Depending on the actual output type, we'll either populate a + // serializedWitness or a witness as well asa sigScript. + var ( + sigScript []byte + serializedWitness []byte + ) + pInput := p.Inputs[inIndex] - containsRedeemScript := pInput.RedeemScript != nil - cointainsWitnessScript := pInput.WitnessScript != nil - var pubKeys [][]byte - var sigs [][]byte + + // First we'll validate and collect the pubkey+sig pairs from the set + // of partial signatures. + var ( + pubKeys [][]byte + sigs [][]byte + ) for _, ps := range pInput.PartialSigs { pubKeys = append(pubKeys, ps.PubKey) + sigOK := checkSigHashFlags(ps.Signature, &pInput) if !sigOK { return ErrInvalidSigHashFlags + } + sigs = append(sigs, ps.Signature) } + + // If at this point, we don't have any pubkey+sig pairs, then we bail + // as we can't proceed. if len(sigs) == 0 || len(pubKeys) == 0 { return ErrNotFinalizable } + + containsRedeemScript := pInput.RedeemScript != nil + cointainsWitnessScript := pInput.WitnessScript != nil + + // If there's no redeem script, then we assume that this is native + // segwit input. + var err error if !containsRedeemScript { - if len(pubKeys) == 1 && len(sigs) == 1 && !cointainsWitnessScript { - // p2wkh case - witness, err = writePKHWitness(sigs[0], pubKeys[0]) + // If we have only a sigley pubkey+sig pair, and no witness + // script, then we assume this is a P2WKH input. + if len(pubKeys) == 1 && len(sigs) == 1 && + !cointainsWitnessScript { + + serializedWitness, err = writePKHWitness( + sigs[0], pubKeys[0], + ) if err != nil { return err } } else { - // Otherwise, we must have a witnessScript field, - // to fulfil the requirements of p2wsh - // NOTE (we tacitly assume multisig) + // Otherwise, we must have a witnessScript field, so + // we'll generate a valid multi-sig witness. + // + // NOTE: We tacitly assume multisig. + // + // TODO(roasbeef): need to add custom finalize for + // non-multisig P2WSH outputs (HTLCs, delay outputs, + // etc). if !cointainsWitnessScript { return ErrNotFinalizable } - witness, err = getMultisigScriptWitness(pInput.WitnessScript, - pubKeys, sigs) + + serializedWitness, err = getMultisigScriptWitness( + pInput.WitnessScript, pubKeys, sigs, + ) if err != nil { return err } } - } else { - // This is currently assumed p2wsh, multisig, nested in p2sh, - // or p2wkh, nested in p2sh. - // The scriptSig is taken from the redeemscript field, but embedded - // in a push + // Otherwise, we assume that this is a p2wsh multi-sig output, + // which is nested in a p2sh, or a p2wkh nested in a p2sh. + // + // In this case, we'll take the redeem script (the witness + // program in this case), and push it on the stack within the + // sigScript. builder := txscript.NewScriptBuilder() builder.AddData(pInput.RedeemScript) - scriptSig, err = builder.Script() + sigScript, err = builder.Script() if err != nil { return err } + + // If don't have a witness script, then we assume this is a + // nested p2wkh output. if !cointainsWitnessScript { - // Assumed p2sh-p2wkh - // Here the witness is just (sig, pub) as for p2pkh case + // Assumed p2sh-p2wkh Here the witness is just (sig, + // pub) as for p2pkh case if len(sigs) != 1 || len(pubKeys) != 1 { return ErrNotFinalizable } - witness, err = writePKHWitness(sigs[0], pubKeys[0]) + + serializedWitness, err = writePKHWitness(sigs[0], pubKeys[0]) if err != nil { return err } + } else { - // Assumed p2sh-p2wsh with multisig. - // To build the witness, we do exactly as for the native p2wsh case. - witness, err = getMultisigScriptWitness(pInput.WitnessScript, - pubKeys, sigs) + // Otherwise, we assume that this is a p2wsh multi-sig, + // so we generate the proper witness. + serializedWitness, err = getMultisigScriptWitness( + pInput.WitnessScript, pubKeys, sigs, + ) if err != nil { return err } } } - // At this point, a witness has been constructed, - // and a scriptSig (if nested; else it's []). - // Remove all fields other than witness utxo (01) - // and finalscriptsig (07), finalscriptwitness (08) + + // At this point, a witness has been constructed, and a sigScript (if + // nested; else it's []). Remove all fields other than witness utxo + // (01) and finalscriptsig (07), finalscriptwitness (08). newInput := NewPsbtInput(nil, pInput.WitnessUtxo) - if len(scriptSig) > 0 { - newInput.FinalScriptSig = scriptSig + if len(sigScript) > 0 { + newInput.FinalScriptSig = sigScript } - newInput.FinalScriptWitness = witness - // overwrite the entry in the input list at the correct index + + newInput.FinalScriptWitness = serializedWitness + + // Finally, we overwrite the entry in the input list at the correct + // index. p.Inputs[inIndex] = *newInput return nil } diff --git a/psbt/partialsig.go b/psbt/partialsig.go new file mode 100644 index 0000000..7211d25 --- /dev/null +++ b/psbt/partialsig.go @@ -0,0 +1,58 @@ +package psbt + +import ( + "bytes" + + "github.com/btcsuite/btcd/btcec" +) + +// PartialSig encapsulate a (BTC public key, ECDSA signature) +// pair, note that the fields are stored as byte slices, not +// btcec.PublicKey or btcec.Signature (because manipulations will +// be with the former not the latter, here); compliance with consensus +// serialization is enforced with .checkValid() +type PartialSig struct { + PubKey []byte + Signature []byte +} + +// PartialSigSorter implements sort.Interface for PartialSig. +type PartialSigSorter []*PartialSig + +func (s PartialSigSorter) Len() int { return len(s) } + +func (s PartialSigSorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +func (s PartialSigSorter) Less(i, j int) bool { + return bytes.Compare(s[i].PubKey, s[j].PubKey) < 0 +} + +// validatePubkey checks if pubKey is *any* valid pubKey serialization in a +// Bitcoin context (compressed/uncomp. OK). +func validatePubkey(pubKey []byte) bool { + _, err := btcec.ParsePubKey(pubKey, btcec.S256()) + if err != nil { + return false + } + return true +} + +// validateSignature checks that the passed byte slice is a valid DER-encoded +// ECDSA signature, including the sighash flag. It does *not* of course +// validate the signature against any message or public key. +func validateSignature(sig []byte) bool { + _, err := btcec.ParseDERSignature(sig, btcec.S256()) + if err != nil { + return false + } + return true +} + +// checkValid checks that both the pbukey and sig are valid. See the methods +// (PartialSig, validatePubkey, validateSignature) for more details. +// +// TODO(waxwing): update for Schnorr will be needed here if/when that +// activates. +func (ps *PartialSig) checkValid() bool { + return validatePubkey(ps.PubKey) && validateSignature(ps.Signature) +} diff --git a/psbt/utils.go b/psbt/utils.go new file mode 100644 index 0000000..5af0b14 --- /dev/null +++ b/psbt/utils.go @@ -0,0 +1,272 @@ +// Copyright (c) 2018 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package psbt + +import ( + "bytes" + "encoding/binary" + "errors" + "io" + "sort" + + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" +) + +// writeTxWitness is a A utility function due to non-exported witness +// serialization (writeTxWitness encodes the bitcoin protocol encoding for a +// transaction input's witness into w). +func writeTxWitness(w io.Writer, wit [][]byte) error { + if err := wire.WriteVarInt(w, 0, uint64(len(wit))); err != nil { + return err + } + + for _, item := range wit { + err := wire.WriteVarBytes(w, 0, item) + if err != nil { + return err + } + } + return nil +} + +// writePKHWitness writes a witness for a p2wkh spending input +func writePKHWitness(sig []byte, pub []byte) ([]byte, error) { + var ( + buf bytes.Buffer + witnessItems = [][]byte{sig, pub} + ) + + if err := writeTxWitness(&buf, witnessItems); err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +// checkIsMultisigScript is a utility function to check whether a given +// redeemscript fits the standard multisig template used in all P2SH based +// multisig, given a set of pubkeys for redemption. +func checkIsMultiSigScript(pubKeys [][]byte, sigs [][]byte, + script []byte) bool { + + // First insist that the script type is multisig. + if txscript.GetScriptClass(script) != txscript.MultiSigTy { + return false + } + + // Inspect the script to ensure that the number of sigs and pubkeys is + // correct + numSigs, numPubKeys, err := txscript.CalcMultiSigStats(script) + if err != nil { + return false + } + + // If the number of sigs provided, doesn't match the number of required + // pubkeys, then we can't proceed as we're not yet final. + if numPubKeys != len(pubKeys) || numSigs != len(sigs) { + return false + } + + return true +} + +// extractKeyOrderFromScript is a utility function to extract an ordered list +// of signatures, given a serialized script (redeemscript or witness script), a +// list of pubkeys and the signatures corresponding to those pubkeys. This +// function is used to ensure that the signatures will be embedded in the final +// scriptSig or scriptWitness in the correct order. +func extractKeyOrderFromScript(script []byte, expectedPubkeys [][]byte, + sigs [][]byte) ([][]byte, error) { + + // If this isn't a proper finalized multi-sig script, then we can't + // proceed. + if !checkIsMultiSigScript(expectedPubkeys, sigs, script) { + return nil, ErrUnsupportedScriptType + } + + // Arrange the pubkeys and sigs into a slice of format: + // * [[pub,sig], [pub,sig],..] + type sigWithPub struct { + pubKey []byte + sig []byte + } + var pubsSigs []sigWithPub + for i, pub := range expectedPubkeys { + pubsSigs = append(pubsSigs, sigWithPub{ + pubKey: pub, + sig: sigs[i], + }) + } + + // Now that we have the set of (pubkey, sig) pairs, we'll construct a + // position map that we can use to swap the order in the slice above to + // match how things are laid out in the script. + type positionEntry struct { + index int + value sigWithPub + } + var positionMap []positionEntry + + // For each pubkey in our pubsSigs slice, we'll now construct a proper + // positionMap entry, based on _where_ in the script the pubkey first + // appears. + for _, p := range pubsSigs { + pos := bytes.Index(script, p.pubKey) + if pos < 0 { + return nil, errors.New("script does not contain pubkeys") + } + + positionMap = append(positionMap, positionEntry{ + index: pos, + value: p, + }) + } + + // Now that we have the position map full populated, we'll use the + // index data to properly sort the entries in the map based on where + // they appear in the script. + sort.Slice(positionMap, func(i, j int) bool { + return positionMap[i].index < positionMap[j].index + }) + + // Finally, we can simply iterate through the position map in order to + // extract the proper signature ordering. + sortedSigs := make([][]byte, 0, len(positionMap)) + for _, x := range positionMap { + sortedSigs = append(sortedSigs, x.value.sig) + } + + return sortedSigs, nil +} + +// getMultisigScriptWitness creates a full psbt serialized Witness field for +// the transaction, given the public keys and signatures to be appended. This +// function will only accept witnessScripts of the type M of N multisig. This +// is used for both p2wsh and nested p2wsh multisig cases. +func getMultisigScriptWitness(witnessScript []byte, pubKeys [][]byte, + sigs [][]byte) ([]byte, error) { + + // First using the script as a guide, we'll properly order the sigs + // according to how their corresponding pubkeys appear in the + // witnessScript. + orderedSigs, err := extractKeyOrderFromScript( + witnessScript, pubKeys, sigs, + ) + if err != nil { + return nil, err + } + + // Now that we know the proper order, we'll append each of the + // signatures into a new witness stack, then top it off with the + // witness script at the end, prepending the nil as we need the extra + // pop.. + witnessElements := make(wire.TxWitness, 0, len(sigs)+2) + witnessElements = append(witnessElements, nil) + for _, os := range orderedSigs { + witnessElements = append(witnessElements, os) + } + witnessElements = append(witnessElements, witnessScript) + + // Now that we have the full witness stack, we'll serialize it in the + // expected format, and return the final bytes. + var buf bytes.Buffer + if err = writeTxWitness(&buf, witnessElements); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// checkSigHashFlags compares the sighash flag byte on a signature with the +// value expected according to any PsbtInSighashType field in this section of +// the PSBT, and returns true if they match, false otherwise. +// If no SighashType field exists, it is assumed to be SIGHASH_ALL. +// +// TODO(waxwing): sighash type not restricted to one byte in future? +func checkSigHashFlags(sig []byte, input *PInput) bool { + expectedSighashType := txscript.SigHashAll + if input.SighashType != 0 { + expectedSighashType = input.SighashType + } + + return expectedSighashType == txscript.SigHashType(sig[len(sig)-1]) +} + +// serializeKVpair writes out a kv pair using a varbyte prefix for each. +func serializeKVpair(w io.Writer, key []byte, value []byte) error { + if err := wire.WriteVarBytes(w, 0, key); err != nil { + return err + } + + return wire.WriteVarBytes(w, 0, value) +} + +// serializeKVPairWithType writes out to the passed writer a type coupled with +// a key. +func serializeKVPairWithType(w io.Writer, kt uint8, keydata []byte, + value []byte) error { + + // If the key has no data, then we write a blank slice. + if keydata == nil { + keydata = []byte{} + } + + // The final key to be written is: {type} || {keyData} + serializedKey := append([]byte{byte(kt)}, keydata...) + return serializeKVpair(w, serializedKey, value) +} + +// getKey retrieves a single key - both the key type and the keydata (if +// present) from the stream and returns the key type as an integer, or -1 if +// the key was of zero length. This integer is is used to indicate the presence +// of a separator byte which indicates the end of a given key-value pair list, +// and the keydata as a byte slice or nil if none is present. +func getKey(r io.Reader) (int, []byte, error) { + + // For the key, we read the varint separately, instead of using the + // available ReadVarBytes, because we have a specific treatment of 0x00 + // here: + count, err := wire.ReadVarInt(r, 0) + if err != nil { + return -1, nil, ErrInvalidPsbtFormat + } + if count == 0 { + // A separator indicates end of key-value pair list. + return -1, nil, nil + } + + // Next, we ready out the designated number of bytes, which may include + // a type, key, and optional data. + keyTypeAndData := make([]byte, count) + if _, err := io.ReadFull(r, keyTypeAndData[:]); err != nil { + return -1, nil, err + } + + keyType := int(string(keyTypeAndData)[0]) + + // Note that the second return value will usually be empty, since most + // keys contain no more than the key type byte. + if len(keyTypeAndData) == 1 { + return keyType, nil, nil + } + + // Otherwise, we return the key, along with any data that it may + // contain. + return keyType, keyTypeAndData[1:], nil + +} + +// readTxOut is a limited version of wire.ReadTxOut, because the latter is not +// exported. +func readTxOut(txout []byte) (*wire.TxOut, error) { + if len(txout) < 10 { + return nil, ErrInvalidPsbtFormat + } + + valueSer := binary.LittleEndian.Uint64(txout[:8]) + scriptPubKey := txout[9:] + + return wire.NewTxOut(int64(valueSer), scriptPubKey), nil +} From ef5f31d2e386c02e06ffc97d9c7f0e02bc28b0e7 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 15 Jan 2020 17:43:16 -0800 Subject: [PATCH 08/13] psbt: rename psbt.Psbt to psbt.Packet, rename existing constructors --- psbt/psbt.go | 217 ++++++++++++++++++++++++++++----------------------- 1 file changed, 119 insertions(+), 98 deletions(-) diff --git a/psbt/psbt.go b/psbt/psbt.go index 89325a7..2e593e0 100644 --- a/psbt/psbt.go +++ b/psbt/psbt.go @@ -10,14 +10,10 @@ package psbt import ( "bytes" "encoding/base64" - "encoding/binary" "errors" "io" - "sort" - "github.com/btcsuite/btcd/btcec" - "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" ) @@ -112,38 +108,42 @@ type Unknown struct { Value []byte } - - - -} - -// Psbt 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. -type Psbt struct { +// Packet is the actual psbt repreesntation. It is a 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. +type Packet struct { + // UnsignedTx is the decoded unsigned transaction for this PSBT. UnsignedTx *wire.MsgTx // Deserialization of unsigned tx - Inputs []PInput - Outputs []POutput - Unknowns []Unknown // Data of unknown type at global scope + + // Inputs contains all the information needed to properly sign this + // target input within the above transaction. + Inputs []PInput + + // Outputs contains all information required to spend any outputs + // produced by this PSBT. + Outputs []POutput + + // Unknowns are the set of custom types (global only) within this PSBT. + Unknowns []Unknown } -// validateUnsignedTx returns true if the transaction is unsigned. -// Note that more basic sanity requirements, -// such as the presence of inputs and outputs, is implicitly -// checked in the call to MsgTx.Deserialize() +// validateUnsignedTx returns true if the transaction is unsigned. Note that +// more basic sanity requirements, such as the presence of inputs and outputs, +// is implicitly checked in the call to MsgTx.Deserialize(). func validateUnsignedTX(tx *wire.MsgTx) bool { for _, tin := range tx.TxIn { if len(tin.SignatureScript) != 0 || len(tin.Witness) != 0 { return false } } + return true } -// NewPsbtFromUnsignedTx creates a new Psbt struct, without -// any signatures (i.e. only the global section is non-empty). -func NewPsbtFromUnsignedTx(tx *wire.MsgTx) (*Psbt, error) { +// 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 @@ -153,7 +153,7 @@ func NewPsbtFromUnsignedTx(tx *wire.MsgTx) (*Psbt, error) { outSlice := make([]POutput, len(tx.TxOut)) unknownSlice := make([]Unknown, 0) - retPsbt := Psbt{ + retPsbt := Packet{ UnsignedTx: tx, Inputs: inSlice, Outputs: outSlice, @@ -163,52 +163,52 @@ func NewPsbtFromUnsignedTx(tx *wire.MsgTx) (*Psbt, error) { return &retPsbt, nil } -// NewPsbt returns a new instance of a Psbt struct created -// by reading from a byte slice. If the format is invalid, an error -// is returned. If the argument b64 is true, the passed byte slice -// is decoded from base64 encoding before processing. -// NOTE To create a Psbt from one's own data, rather than reading -// in a serialization from a counterparty, one should use a psbt.Creator. -func NewPsbt(psbtBytes []byte, b64 bool) (*Psbt, error) { - var err error +// NewFromRawBytes returns a new instance of a Packet struct created by reading +// from a byte slice. If the format is invalid, an error is returned. If the +// argument b64 is true, the passed byte slice is decoded from base64 encoding +// before processing. +// +// 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. if b64 { - decoded := make([]byte, base64.StdEncoding.DecodedLen(len(psbtBytes))) - _, err = base64.StdEncoding.Decode(decoded, psbtBytes) - if err != nil { - return nil, err - } - psbtBytes = decoded + based64EncodedReader := r + r = base64.NewDecoder(base64.StdEncoding, based64EncodedReader) } - r := bytes.NewReader(psbtBytes) - // The Psbt struct does not store the fixed magic bytes, - // but they must be present or the serialization must - // be explicitly rejected. + + // The Packet struct does not store the fixed magic bytes, but they + // must be present or the serialization must be explicitly rejected. var magic [5]byte - if _, err = io.ReadFull(r, magic[:]); err != nil { + if _, err := io.ReadFull(r, magic[:]); err != nil { return nil, err } if magic != psbtMagic { return nil, ErrInvalidMagicBytes } - // 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. + // 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) if err != nil { return nil, err } - if uint8(keyint) != PsbtGlobalUnsignedTx || keydata != nil { + if GlobalType(keyint) != UnsignedTxType || keydata != nil { return nil, ErrInvalidPsbtFormat } - value, err := wire.ReadVarBytes(r, 0, MaxPsbtValueLength, - "PSBT value") + + // Now that we've verified the global type is present, we'll decode it + // into a proper unsigned transaction, and validate it. + value, err := wire.ReadVarBytes( + r, 0, MaxPsbtValueLength, "PSBT value", + ) if err != nil { return nil, err } - - // Attempt to deserialize the unsigned transaction. msgTx := wire.NewMsgTx(2) err = msgTx.Deserialize(bytes.NewReader(value)) if err != nil { @@ -218,8 +218,9 @@ func NewPsbt(psbtBytes []byte, b64 bool) (*Psbt, error) { return nil, ErrInvalidRawTxSigned } - // parse any unknowns that may be present, break at separator - unknownSlice := make([]Unknown, 0) + // Next we parse any unknowns that may be present, making sure that we + // break at the separator. + var unknownSlice []Unknown for { keyint, keydata, err := getKey(r) if err != nil { @@ -228,13 +229,17 @@ func NewPsbt(psbtBytes []byte, b64 bool) (*Psbt, error) { if keyint == -1 { break } - value, err := wire.ReadVarBytes(r, 0, MaxPsbtValueLength, - "PSBT value") + + value, err := wire.ReadVarBytes( + r, 0, MaxPsbtValueLength, "PSBT value", + ) if err != nil { return nil, err } + keyintanddata := []byte{byte(keyint)} keyintanddata = append(keyintanddata, keydata...) + newUnknown := Unknown{ Key: keyintanddata, Value: value, @@ -242,40 +247,40 @@ func NewPsbt(psbtBytes []byte, b64 bool) (*Psbt, error) { unknownSlice = append(unknownSlice, newUnknown) } - // Next we parse the INPUT section + // Next we parse the INPUT section. inSlice := make([]PInput, len(msgTx.TxIn)) - for i := range msgTx.TxIn { input := PInput{} err = input.deserialize(r) if err != nil { return nil, err } + inSlice[i] = input } - //Next we parse the OUTPUT section + // Next we parse the OUTPUT section. outSlice := make([]POutput, len(msgTx.TxOut)) - for i := range msgTx.TxOut { output := POutput{} err = output.deserialize(r) if err != nil { return nil, err } + outSlice[i] = output } - // Populate the new Psbt object - newPsbt := Psbt{ + // Populate the new Packet object + newPsbt := Packet{ UnsignedTx: msgTx, Inputs: inSlice, Outputs: outSlice, Unknowns: unknownSlice, } - // Extended sanity checking is applied here - // to make sure the externally-passed Psbt follows - // all the rules. + + // Extended sanity checking is applied here to make sure the + // externally-passed Packet follows all the rules. if err = newPsbt.SanityCheck(); err != nil { return nil, err } @@ -283,66 +288,82 @@ func NewPsbt(psbtBytes []byte, b64 bool) (*Psbt, error) { return &newPsbt, nil } -// Serialize creates a binary serialization of the referenced -// Psbt struct with lexicographical ordering (by key) of the subsections -func (p *Psbt) Serialize() ([]byte, 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 { - serPsbt := []byte{} - serPsbt = append(serPsbt, psbtMagic[:]...) + // First we write out the precise set of magic bytes that identify a + // valid PSBT transaction. + if _, err := w.Write(psbtMagic[:]); err != nil { + return err + } - // Create serialization of unsignedtx - serializedTx := bytes.NewBuffer(make([]byte, 0, - p.UnsignedTx.SerializeSize())) + // Next we prep to write out the unsigned transaction by first + // serializing it into an intermediate buffer. + serializedTx := bytes.NewBuffer( + make([]byte, 0, p.UnsignedTx.SerializeSize()), + ) if err := p.UnsignedTx.Serialize(serializedTx); err != nil { - return nil, err + return err } - var buf bytes.Buffer - err := serializeKVPairWithType(&buf, PsbtGlobalUnsignedTx, - nil, serializedTx.Bytes()) + + // Now that we have the serialized transaction, we'll write it out to + // the proper global type. + err := serializeKVPairWithType( + w, uint8(UnsignedTxType), nil, serializedTx.Bytes(), + ) if err != nil { - return nil, err + return err + } + + // With that our global section is done, so we'll write out the + // separator. + separator := []byte{0x00} + if _, err := w.Write(separator); err != nil { + return err } - serPsbt = append(serPsbt, buf.Bytes()...) - serPsbt = append(serPsbt, 0x00) for _, pInput := range p.Inputs { - var buf bytes.Buffer - err := pInput.serialize(&buf) + err := pInput.serialize(w) if err != nil { - return nil, err + return err + } + + if _, err := w.Write(separator); err != nil { + return err } - serPsbt = append(serPsbt, buf.Bytes()...) - serPsbt = append(serPsbt, 0x00) } for _, pOutput := range p.Outputs { - var buf bytes.Buffer - err := pOutput.serialize(&buf) + err := pOutput.serialize(w) if err != nil { - return nil, err + return err + } + + if _, err := w.Write(separator); err != nil { + return err } - serPsbt = append(serPsbt, buf.Bytes()...) - serPsbt = append(serPsbt, 0x00) } - return serPsbt, nil + return nil } // B64Encode returns the base64 encoding of the serialization of // the current PSBT, or an error if the encoding fails. -func (p *Psbt) B64Encode() (string, error) { - raw, err := p.Serialize() - if err != nil { +func (p *Packet) B64Encode() (string, error) { + var b bytes.Buffer + if err := p.Serialize(&b); err != nil { return "", err } - return base64.StdEncoding.EncodeToString(raw), nil + + return base64.StdEncoding.EncodeToString(b.Bytes()), nil } // IsComplete returns true only if all of the inputs are // finalized; this is particularly important in that it decides // whether the final extraction to a network serialized signed // transaction will be possible. -func (p *Psbt) IsComplete() bool { +func (p *Packet) IsComplete() bool { for i := 0; i < len(p.UnsignedTx.TxIn); i++ { if !isFinalized(p, i) { return false @@ -353,7 +374,7 @@ func (p *Psbt) 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 *Psbt) SanityCheck() error { +func (p *Packet) SanityCheck() error { if !validateUnsignedTX(p.UnsignedTx) { return ErrInvalidRawTxSigned From 2a3238c694ecb600cd444a508dc153f6737fc122 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 15 Jan 2020 17:43:29 -0800 Subject: [PATCH 09/13] psbt: create new enum type for return values of Sign method --- psbt/signer.go | 142 ++++++++++++++++++++++++++++--------------------- 1 file changed, 81 insertions(+), 61 deletions(-) diff --git a/psbt/signer.go b/psbt/signer.go index f1b1df5..9680c90 100644 --- a/psbt/signer.go +++ b/psbt/signer.go @@ -4,129 +4,149 @@ package psbt -// signer encapsulates the role 'Signer' -// as specified in BIP174; it controls the insertion of signatures; -// the Sign() function will attempt to insert signatures using -// Updater.addPartialSignature, after first ensuring the Psbt is in the -// correct state. +// signer encapsulates the role 'Signer' as specified in BIP174; it controls +// the insertion of signatures; the Sign() function will attempt to insert +// signatures using Updater.addPartialSignature, after first ensuring the Psbt +// is in the correct state. import ( "github.com/btcsuite/btcd/txscript" ) +// SignOutcome is a enum-like value that expresses the outcome of a call to the +// Sign method. +type SignOutcome int + +const ( + // SignSuccesful indicates that the partial signature was successfully + // attached. + SignSuccesful = 0 + + // 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 = -1 +) + // Sign allows the caller to sign a PSBT at a particular input; they // must provide a signature and a pubkey, both as byte slices; they can also -// optionally provide both witnessScript and/or redeemScript, otherwise -// these arguments must be set as nil (and in that case, they must already -// be present in the PSBT if required for signing to succeed). +// optionally provide both witnessScript and/or redeemScript, otherwise these +// arguments must be set as nil (and in that case, they must already be present +// in the PSBT if required for signing to succeed). // -// Return value: -// 0 indicates that the partial signature was successfully attached. -// 1 indicates that this input is already finalized, so the provided -// signature was *not* attached -// -1 indicates that the provided signature data was not valid. In this -// case an error will also be returned. -// -// This serves as a wrapper around Updater.addPartialSignature; -// it ensures that the redeemScript and witnessScript are updated as needed -// (note that the Updater is allowed to add redeemScripts and witnessScripts -// independently, before signing), and ensures that the right form of utxo -// field (NonWitnessUtxo or WitnessUtxo) is included in the input so that -// signature insertion (and then finalization) can take place. +// This serves as a wrapper around Updater.addPartialSignature; it ensures that +// the redeemScript and witnessScript are updated as needed (note that the +// Updater is allowed to add redeemScripts and witnessScripts independently, +// before signing), and ensures that the right form of utxo field +// (NonWitnessUtxo or WitnessUtxo) is included in the input so that signature +// insertion (and then finalization) can take place. func (u *Updater) Sign(inIndex int, sig []byte, pubKey []byte, - redeemScript []byte, witnessScript []byte) (int, error) { + redeemScript []byte, witnessScript []byte) (SignOutcome, error) { if isFinalized(u.Upsbt, inIndex) { - return 1, nil + return SignFinalized, nil } + // Add the witnessScript to the PSBT in preparation. If it already + // exists, it will be overwritten. if witnessScript != nil { - // Add the witnessScript to the PSBT in preparation. - // If it already exists, it will be overwritten. err := u.AddInWitnessScript(witnessScript, inIndex) if err != nil { - return -1, err + return SignInvalid, err } } + // Add the redeemScript to the PSBT in preparation. If it already + // exists, it will be overwritten. if redeemScript != nil { - // Add the redeemScript to the PSBT in preparation. - // If it already exists, it will be overwritten. err := u.AddInRedeemScript(redeemScript, inIndex) if err != nil { - return -1, err + return SignInvalid, err } } - // At this point, the PSBT must have the requisite - // witnessScript or redeemScript fields for signing to succeed. - - // case 1: if witnessScript is present, it must be of type witness; + // At this point, the PSBT must have the requisite witnessScript or + // redeemScript fields for signing to succeed. + // + // Case 1: if witnessScript is present, it must be of type witness; // if not, signature insertion will of course fail. - if u.Upsbt.Inputs[inIndex].WitnessScript != nil { + switch { + case u.Upsbt.Inputs[inIndex].WitnessScript != nil: if u.Upsbt.Inputs[inIndex].WitnessUtxo == nil { err := nonWitnessToWitness(u.Upsbt, inIndex) if err != nil { - return -1, err + return SignInvalid, err } } + err := u.addPartialSignature(inIndex, sig, pubKey) if err != nil { - return -1, err + return SignInvalid, err } - } else if u.Upsbt.Inputs[inIndex].RedeemScript != nil { - // case 2: no witness script, only redeem script; can be legacy - // p2sh or p2sh-wrapped p2wkh + + // Case 2: no witness script, only redeem script; can be legacy p2sh or + // p2sh-wrapped p2wkh. + case u.Upsbt.Inputs[inIndex].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: + // we check the redeemScript content. if txscript.IsWitnessProgram(redeemScript) { if u.Upsbt.Inputs[inIndex].WitnessUtxo == nil { err := nonWitnessToWitness(u.Upsbt, inIndex) if err != nil { - return -1, err + return SignInvalid, err } } } - // If it is not a valid witness program, we here assume - // that the provided WitnessUtxo/NonWitnessUtxo field was correct. + + // If it is not a valid witness program, we here assume that + // the provided WitnessUtxo/NonWitnessUtxo field was correct. err := u.addPartialSignature(inIndex, sig, pubKey) if err != nil { - return -1, err + return SignInvalid, err } - } else { - // case 3: Neither provided only works for native p2wkh, or - // non-segwit non-p2sh. To check if it's segwit, check - // the scriptPubKey of the output. + + // Case 3: Neither provided only works for native p2wkh, or non-segwit + // 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 + outIndex := u.Upsbt.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Index + script := u.Upsbt.Inputs[inIndex].NonWitnessUtxo.TxOut[outIndex].PkScript + if txscript.IsWitnessProgram(script) { err := nonWitnessToWitness(u.Upsbt, inIndex) if err != nil { - return -1, err + return SignInvalid, err } } } + err := u.addPartialSignature(inIndex, sig, pubKey) if err != nil { - return -1, err + return SignInvalid, err } } - return 0, nil + + return SignSuccesful, nil } -// nonWitnessToWitness extracts the TxOut from the existing -// NonWitnessUtxo field in the given PSBT input and sets it as type -// witness by replacing the NonWitnessUtxo field with a WitnessUtxo -// field. See https://github.com/bitcoin/bitcoin/pull/14197 -func nonWitnessToWitness(p *Psbt, inIndex int) error { +// nonWitnessToWitness extracts the TxOut from the existing NonWitnessUtxo +// field in the given PSBT input and sets it as type witness by replacing the +// NonWitnessUtxo field with a WitnessUtxo field. See +// https://github.com/bitcoin/bitcoin/pull/14197. +func nonWitnessToWitness(p *Packet, inIndex int) error { outIndex := p.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Index txout := p.Inputs[inIndex].NonWitnessUtxo.TxOut[outIndex] + // Remove the non-witness first, else sanity check will not pass: p.Inputs[inIndex].NonWitnessUtxo = nil - u := Updater{Upsbt: p} + u := Updater{ + Upsbt: p, + } + return u.AddInWitnessUtxo(txout, inIndex) } From 6bd3b8034f71ea3700931761458bc5d85aea3e15 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 15 Jan 2020 17:43:44 -0800 Subject: [PATCH 10/13] psbt: refactor updater.go for consistent code style --- psbt/updater.go | 235 +++++++++++++++++++++++++++++------------------- 1 file changed, 145 insertions(+), 90 deletions(-) diff --git a/psbt/updater.go b/psbt/updater.go index 434e05b..b8c2350 100644 --- a/psbt/updater.go +++ b/psbt/updater.go @@ -4,12 +4,11 @@ package psbt -// The Updater requires provision of a single PSBT and is able -// to add data to both input and output sections. -// It can be called repeatedly to add more data. -// It also allows addition of signatures via the addPartialSignature -// function; this is called internally to the package in the Sign() -// function of Updater, located in signer.go +// The Updater requires provision of a single PSBT and is able to add data to +// both input and output sections. It can be called repeatedly to add more +// data. It also allows addition of signatures via the addPartialSignature +// function; this is called internally to the package in the Sign() function of +// Updater, located in signer.go import ( "bytes" @@ -20,217 +19,259 @@ import ( "github.com/btcsuite/btcutil" ) -// Updater encapsulates the role 'Updater' -// as specified in BIP174; it accepts Psbt structs -// and has methods to add fields to the inputs and outputs. +// Updater encapsulates the role 'Updater' as specified in BIP174; it accepts +// Psbt structs and has methods to add fields to the inputs and outputs. type Updater struct { - Upsbt *Psbt + Upsbt *Packet } -// NewUpdater returns a new instance of Updater, -// if the passed Psbt struct is in a valid form, -// else an error. -func NewUpdater(p *Psbt) (*Updater, error) { +// NewUpdater returns a new instance of Updater, if the passed Psbt struct is +// in a valid form, else an error. +func NewUpdater(p *Packet) (*Updater, error) { if err := p.SanityCheck(); err != nil { return nil, err } + return &Updater{Upsbt: p}, nil } -// AddInNonWitnessUtxo adds the utxo information for an input which -// is non-witness. This requires provision of a full transaction -// (which is the source of the corresponding prevOut), and the input -// index. If addition of this key-value pair to the Psbt fails, an -// error is returned. +// AddInNonWitnessUtxo adds the utxo information for an input which is +// non-witness. This requires provision of a full transaction (which is the +// source of the corresponding prevOut), and the input index. If addition of +// this key-value pair to the Psbt fails, an error is returned. func (p *Updater) AddInNonWitnessUtxo(tx *wire.MsgTx, inIndex int) error { if inIndex > len(p.Upsbt.Inputs)-1 { return ErrInvalidPrevOutNonWitnessTransaction } + p.Upsbt.Inputs[inIndex].NonWitnessUtxo = tx + if err := p.Upsbt.SanityCheck(); err != nil { return ErrInvalidPsbtFormat } + return nil } -// AddInWitnessUtxo adds the utxo information for an input which -// is witness. This requires provision of a full transaction *output* -// (which is the source of the corresponding prevOut); not the full -// transaction because BIP143 means the output information is sufficient, -// and the input index. If addition of this key-value pair to the Psbt fails, -// an error is returned. +// AddInWitnessUtxo adds the utxo information for an input which is witness. +// This requires provision of a full transaction *output* (which is the source +// of the corresponding prevOut); not the full transaction because BIP143 means +// the output information is sufficient, and the input index. If addition of +// this key-value pair to the Psbt fails, an error is returned. func (p *Updater) AddInWitnessUtxo(txout *wire.TxOut, inIndex int) error { if inIndex > len(p.Upsbt.Inputs)-1 { return ErrInvalidPsbtFormat } + p.Upsbt.Inputs[inIndex].WitnessUtxo = txout + if err := p.Upsbt.SanityCheck(); err != nil { return ErrInvalidPsbtFormat } + return nil } -// addPartialSignature allows the Updater role to insert fields -// of type partial signature into a Psbt, consisting of both -// the pubkey (as keydata) and the ECDSA signature (as value). -// Note that the Signer role is encapsulated in this function; -// signatures are only allowed to be added that follow the sanity-check -// on signing rules explained in the BIP under `Signer`; if the rules are not -// satisfied, an ErrInvalidSignatureForInput is returned. -// NOTE this function does *not* validate the ECDSA signature itself. +// addPartialSignature allows the Updater role to insert fields of type partial +// signature into a Psbt, consisting of both the pubkey (as keydata) and the +// ECDSA signature (as value). Note that the Signer role is encapsulated in +// this function; signatures are only allowed to be added that follow the +// sanity-check on signing rules explained in the BIP under `Signer`; if the +// rules are not satisfied, an ErrInvalidSignatureForInput is returned. +// +// NOTE: This function does *not* validate the ECDSA signature itself. func (p *Updater) addPartialSignature(inIndex int, sig []byte, pubkey []byte) error { - partialSig := PartialSig{PubKey: pubkey, Signature: sig} - //First validate the passed (sig, pub): + partialSig := PartialSig{ + PubKey: pubkey, Signature: sig, + } + + // First validate the passed (sig, pub). if !partialSig.checkValid() { return ErrInvalidPsbtFormat } pInput := p.Upsbt.Inputs[inIndex] - // First check; don't add duplicates + // First check; don't add duplicates. for _, x := range pInput.PartialSigs { if bytes.Equal(x.PubKey, partialSig.PubKey) { return ErrDuplicateKey } } - // Sanity checks + // Next, we perform a series of additional sanity checks. if pInput.NonWitnessUtxo != nil { if len(p.Upsbt.UnsignedTx.TxIn) < inIndex+1 { return ErrInvalidPrevOutNonWitnessTransaction } + if pInput.NonWitnessUtxo.TxHash() != p.Upsbt.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Hash { return ErrInvalidSignatureForInput } + + // To validate that the redeem script matches, we must pull out + // the scriptPubKey of the corresponding output and compare + // that with the P2SH scriptPubKey that is generated by + // redeemScript. if pInput.RedeemScript != nil { - // To validate that the redeem script matches, we must pull out the - // scriptPubKey of the corresponding output and compare that with - // the P2SH scriptPubKey that is generated by redeemScript: outIndex := p.Upsbt.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Index scriptPubKey := pInput.NonWitnessUtxo.TxOut[outIndex].PkScript scriptHash := btcutil.Hash160(pInput.RedeemScript) - scriptHashScript, err := txscript.NewScriptBuilder().AddOp( - txscript.OP_HASH160).AddData(scriptHash).AddOp( - txscript.OP_EQUAL).Script() + + scriptHashScript, err := txscript.NewScriptBuilder(). + AddOp(txscript.OP_HASH160). + AddData(scriptHash). + AddOp(txscript.OP_EQUAL). + Script() if err != nil { return err } + if !bytes.Equal(scriptHashScript, scriptPubKey) { return ErrInvalidSignatureForInput } } + } else if pInput.WitnessUtxo != nil { scriptPubKey := pInput.WitnessUtxo.PkScript + var script []byte if pInput.RedeemScript != nil { scriptHash := btcutil.Hash160(pInput.RedeemScript) - scriptHashScript, err := txscript.NewScriptBuilder().AddOp( - txscript.OP_HASH160).AddData(scriptHash).AddOp( - txscript.OP_EQUAL).Script() + scriptHashScript, err := txscript.NewScriptBuilder(). + AddOp(txscript.OP_HASH160). + AddData(scriptHash). + AddOp(txscript.OP_EQUAL). + Script() if err != nil { return err } + if !bytes.Equal(scriptHashScript, scriptPubKey) { return ErrInvalidSignatureForInput } + script = pInput.RedeemScript } else { script = scriptPubKey } + // If a witnessScript field is present, this is a P2WSH, // whether nested or not (that is handled by the assignment to - // `script` above); in that case, sanity check that `script` - // is the p2wsh of witnessScript. Contrariwise, if no witnessScript - // field is present, this will be signed as p2wkh. + // `script` above); in that case, sanity check that `script` is + // the p2wsh of witnessScript. Contrariwise, if no + // witnessScript field is present, this will be signed as + // p2wkh. if pInput.WitnessScript != nil { witnessScriptHash := sha256.Sum256(pInput.WitnessScript) - witnessScriptHashScript, err := txscript.NewScriptBuilder().AddOp( - txscript.OP_0).AddData(witnessScriptHash[:]).Script() + witnessScriptHashScript, err := txscript.NewScriptBuilder(). + AddOp(txscript.OP_0). + AddData(witnessScriptHash[:]). + Script() if err != nil { return err } + if !bytes.Equal(script, witnessScriptHashScript[:]) { return ErrInvalidSignatureForInput } - } else { // p2wkh + } else { + // Otherwise, this is a p2wkh input. pubkeyHash := btcutil.Hash160(pubkey) - pubkeyHashScript, err := txscript.NewScriptBuilder().AddOp( - txscript.OP_0).AddData(pubkeyHash).Script() + pubkeyHashScript, err := txscript.NewScriptBuilder(). + AddOp(txscript.OP_0). + AddData(pubkeyHash). + Script() if err != nil { return err } + + // Validate that we're able to properly reconstruct the + // witness program. if !bytes.Equal(pubkeyHashScript, script) { return ErrInvalidSignatureForInput } } } else { - // attaching signature without utxo field is not allowed + + // Attaching signature without utxo field is not allowed. return ErrInvalidPsbtFormat } p.Upsbt.Inputs[inIndex].PartialSigs = append( - p.Upsbt.Inputs[inIndex].PartialSigs, &partialSig) + p.Upsbt.Inputs[inIndex].PartialSigs, &partialSig, + ) if err := p.Upsbt.SanityCheck(); err != nil { return err } - // Addition of a non-duplicate-key partial signature - // cannot violate sanity-check rules. + + // Addition of a non-duplicate-key partial signature cannot violate + // sanity-check rules. return nil } -// AddInSighashType adds the sighash type information for an input. -// The sighash type is passed as a 32 bit unsigned integer, along with the -// index for the input. An error is returned if addition of this key-value pair -// to the Psbt fails. +// AddInSighashType adds the sighash type information for an input. The +// sighash type is passed as a 32 bit unsigned integer, along with the index +// for the input. An error is returned if addition of this key-value pair to +// the Psbt fails. func (p *Updater) AddInSighashType(sighashType txscript.SigHashType, inIndex int) error { + p.Upsbt.Inputs[inIndex].SighashType = sighashType + if err := p.Upsbt.SanityCheck(); err != nil { return err } return nil } -// AddInRedeemScript adds the redeem script information for an input. -// The redeem script is passed serialized, as a byte slice, along with the -// index of the input. An error is returned if addition of this key-value pair -// to the Psbt fails. +// AddInRedeemScript adds the redeem script information for an input. The +// redeem script is passed serialized, as a byte slice, along with the index of +// the input. An error is returned if addition of this key-value pair to the +// Psbt fails. func (p *Updater) AddInRedeemScript(redeemScript []byte, inIndex int) error { + p.Upsbt.Inputs[inIndex].RedeemScript = redeemScript + if err := p.Upsbt.SanityCheck(); err != nil { return ErrInvalidPsbtFormat } + return nil } -// AddInWitnessScript adds the witness script information for an input. -// The witness script is passed serialized, as a byte slice, along with the -// index of the input. An error is returned if addition of this key-value pair -// to the Psbt fails. +// AddInWitnessScript adds the witness script information for an input. The +// witness script is passed serialized, as a byte slice, along with the index +// of the input. An error is returned if addition of this key-value pair to the +// Psbt fails. func (p *Updater) AddInWitnessScript(witnessScript []byte, inIndex int) error { + p.Upsbt.Inputs[inIndex].WitnessScript = witnessScript + if err := p.Upsbt.SanityCheck(); err != nil { return err } + return nil } -// AddInBip32Derivation takes a master key fingerprint -// as defined in BIP32, a BIP32 path as a slice of uint32 values, -// and a serialized pubkey as a byte slice, along with the -// integer index of the input, and inserts this data into that input. -// NOTE that this can be called multiple times for the same input. -// An error is returned if addition of this key-value pair -// to the Psbt fails. +// AddInBip32Derivation takes a master key fingerprint as defined in BIP32, a +// BIP32 path as a slice of uint32 values, and a serialized pubkey as a byte +// slice, along with the integer index of the input, and inserts this data into +// that input. +// +// NOTE: This can be called multiple times for the same input. An error is +// returned if addition of this key-value pair to the Psbt fails. func (p *Updater) AddInBip32Derivation(masterKeyFingerprint uint32, bip32Path []uint32, pubKeyData []byte, inIndex int) error { + bip32Derivation := Bip32Derivation{ PubKey: pubKeyData, MasterKeyFingerprint: masterKeyFingerprint, @@ -249,27 +290,32 @@ func (p *Updater) AddInBip32Derivation(masterKeyFingerprint uint32, } p.Upsbt.Inputs[inIndex].Bip32Derivation = append( - p.Upsbt.Inputs[inIndex].Bip32Derivation, &bip32Derivation) + p.Upsbt.Inputs[inIndex].Bip32Derivation, &bip32Derivation, + ) + if err := p.Upsbt.SanityCheck(); err != nil { return err } + return nil } -// AddOutBip32Derivation takes a master key fingerprint -// as defined in BIP32, a BIP32 path as a slice of uint32 values, -// and a serialized pubkey as a byte slice, along with the -// integer index of the output, and inserts this data into that output. -// NOTE that this can be called multiple times for the same output. -// An error is returned if addition of this key-value pair -// to the Psbt fails. +// AddOutBip32Derivation takes a master key fingerprint as defined in BIP32, a +// BIP32 path as a slice of uint32 values, and a serialized pubkey as a byte +// slice, along with the integer index of the output, and inserts this data +// into that output. +// +// NOTE: That this can be called multiple times for the same output. An error +// is returned if addition of this key-value pair to the Psbt fails. func (p *Updater) AddOutBip32Derivation(masterKeyFingerprint uint32, bip32Path []uint32, pubKeyData []byte, outIndex int) error { + bip32Derivation := Bip32Derivation{ PubKey: pubKeyData, MasterKeyFingerprint: masterKeyFingerprint, Bip32Path: bip32Path, } + if !bip32Derivation.checkValid() { return ErrInvalidPsbtFormat } @@ -282,31 +328,40 @@ func (p *Updater) AddOutBip32Derivation(masterKeyFingerprint uint32, } p.Upsbt.Outputs[outIndex].Bip32Derivation = append( - p.Upsbt.Outputs[outIndex].Bip32Derivation, &bip32Derivation) + p.Upsbt.Outputs[outIndex].Bip32Derivation, &bip32Derivation, + ) + if err := p.Upsbt.SanityCheck(); err != nil { return err } + return nil } -// AddOutRedeemScript takes a redeem script as a byte slice -// and appends it to the output at index outIndex. +// AddOutRedeemScript takes a redeem script as a byte slice and appends it to +// the output at index outIndex. func (p *Updater) AddOutRedeemScript(redeemScript []byte, outIndex int) error { + p.Upsbt.Outputs[outIndex].RedeemScript = redeemScript + if err := p.Upsbt.SanityCheck(); err != nil { return ErrInvalidPsbtFormat } + return nil } -// AddOutWitnessScript takes a witness script as a byte slice -// and appends it to the output at index outIndex. +// AddOutWitnessScript takes a witness script as a byte slice and appends it to +// the output at index outIndex. func (p *Updater) AddOutWitnessScript(witnessScript []byte, outIndex int) error { + p.Upsbt.Outputs[outIndex].WitnessScript = witnessScript + if err := p.Upsbt.SanityCheck(); err != nil { return err } + return nil } From 959fe939adad884a3a527efd11b0a79a392058cc Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 15 Jan 2020 17:44:00 -0800 Subject: [PATCH 11/13] psbt: update tests to match new API changes --- psbt/psbt_test.go | 186 +++++++++++++++++++++++++++++++--------------- 1 file changed, 127 insertions(+), 59 deletions(-) diff --git a/psbt/psbt_test.go b/psbt/psbt_test.go index 23b1a9d..96632d5 100644 --- a/psbt/psbt_test.go +++ b/psbt/psbt_test.go @@ -25,7 +25,8 @@ import ( // extracting it. Returned are: an unsigned transaction serialization, a list // of scriptSigs, one per input, and a list of witnesses, one per input. func createPsbtFromSignedTx(serializedSignedTx []byte) ( - *Psbt, [][]byte, []wire.TxWitness, error) { + *Packet, [][]byte, []wire.TxWitness, error) { + tx := wire.NewMsgTx(2) err := tx.Deserialize(bytes.NewReader(serializedSignedTx)) if err != nil { @@ -34,6 +35,7 @@ func createPsbtFromSignedTx(serializedSignedTx []byte) ( scriptSigs := make([][]byte, 0, len(tx.TxIn)) witnesses := make([]wire.TxWitness, 0, len(tx.TxIn)) tx2 := tx.Copy() + // Blank out signature info in inputs for i, tin := range tx2.TxIn { tin.SignatureScript = nil @@ -42,10 +44,10 @@ func createPsbtFromSignedTx(serializedSignedTx []byte) ( witnesses = append(witnesses, tx.TxIn[i].Witness) } - // Outputs always contain (value, scriptPubkey so don't need amending) - // Now tx2 is tx with all signing data stripped out - unsignedPsbt, err := NewPsbtFromUnsignedTx(tx2) + // Outputs always contain: (value, scriptPubkey) so don't need + // amending. Now tx2 is tx with all signing data stripped out + unsignedPsbt, err := NewFromUnsignedTx(tx2) if err != nil { return nil, nil, nil, err } @@ -116,16 +118,24 @@ func TestReadValidPsbtAndReserialize(t *testing.T) { if err != nil { t.Fatalf("Unable to decode hex: %v", err) } - testPsbt, err := NewPsbt(PsbtBytes, false) + + testPsbt, err := NewFromRawBytes( + bytes.NewReader(PsbtBytes), false, + ) if err != nil { t.Fatalf("unable to parse psbt: %v", err) } + t.Logf("Successfully parsed test, got transaction: %v", spew.Sdump(testPsbt.UnsignedTx)) - raw, err := testPsbt.Serialize() + + var b bytes.Buffer + err = testPsbt.Serialize(&b) if err != nil { t.Fatalf("Unable to serialize created Psbt: %v", err) } + + raw := b.Bytes() if !bytes.Equal(raw, PsbtBytes) { t.Fatalf("Serialized PSBT didn't match: %v", hex.EncodeToString(raw)) @@ -139,11 +149,13 @@ func TestReadInvalidPsbt(t *testing.T) { if err != nil { t.Fatalf("Unable to decode hex: %v", err) } - _, err = NewPsbt(PsbtBytes, false) + + _, err = NewFromRawBytes(bytes.NewReader(PsbtBytes), false) if err == nil { t.Fatalf("Incorrectly validated psbt: %v", hex.EncodeToString(PsbtBytes)) } + t.Logf("Correctly got error: %v", err) } } @@ -165,14 +177,16 @@ func TestSanityCheck(t *testing.T) { if err != nil { t.Fatalf("Unable to decode hex: %v", err) } - psbt1, err := NewPsbt(psbtraw1, false) + psbt1, err := NewFromRawBytes(bytes.NewReader(psbtraw1), false) if err != nil { t.Fatalf("Unable to create Psbt struct: %v", err) } + // Add a non-witness utxo field to input2 using raw insertion function, - // so that it becomes invalid, then NewUpdater should fail: + // so that it becomes invalid, then NewUpdater should fail. nonWitnessUtxoRaw, err := hex.DecodeString( - CUTestHexData["NonWitnessUtxo"]) + CUTestHexData["NonWitnessUtxo"], + ) if err != nil { t.Fatalf("Unable to decode hex: %v", err) } @@ -183,22 +197,26 @@ func TestSanityCheck(t *testing.T) { } inputs1 := &psbt1.Inputs[1] inputs1.NonWitnessUtxo = nonWitnessUtxo - // The PSBT is now in an inconsistent state; Updater creation should fail + + // The PSBT is now in an inconsistent state; Updater creation should + // fail. updater, err := NewUpdater(psbt1) if err == nil { t.Fatalf("Failed to identify invalid PSBT state ( " + "witness, non-witness fields)") } + // Overwrite back with the correct psbt psbtraw1, err = hex.DecodeString(validPsbtHex[1]) if err != nil { t.Fatalf("Unable to decode hex: %v", err) } - psbt1, err = NewPsbt(psbtraw1, false) + psbt1, err = NewFromRawBytes(bytes.NewReader(psbtraw1), false) updater, err = NewUpdater(psbt1) if err != nil { t.Fatalf("Unable to create Updater: %v", err) } + // Create a fake non-witness utxo field to overlap with // the existing witness input at index 1. tx := wire.NewMsgTx(2) @@ -211,10 +229,13 @@ func TestSanityCheck(t *testing.T) { t.Fatalf("Incorrectly accepted Psbt with conflicting witness " + "and non-witness utxo entries in the same input.") } + // Now we try again; this time we try to add a witnessScript // key-value pair to an input which is non-witness, which should // also be rejected. - psbt2, err := NewPsbt(psbtraw1, false) + psbt2, err := NewFromRawBytes( + bytes.NewReader(psbtraw1), false, + ) if err != nil { t.Fatalf("Unable to create Psbt struct: %v", err) } @@ -232,7 +253,6 @@ func TestSanityCheck(t *testing.T) { t.Fatalf("Incorrectly accepted adding witness script field " + "to non-witness utxo") } - } // Data for creation and updating tests @@ -319,33 +339,37 @@ func TestPsbtCreator(t *testing.T) { } prevOut2 := wire.NewOutPoint(hash2, uint32(1)) inputs := []*wire.OutPoint{prevOut1, prevOut2} - creator := Creator{} + // Check creation fails with invalid sequences: nSequences := []uint32{wire.MaxTxInSequenceNum} - err = creator.CreatePsbt(inputs, outputs, int32(3), uint32(0), nSequences) + _, err = New(inputs, outputs, int32(3), uint32(0), nSequences) if err == nil { t.Fatalf("Did not error when creating transaction with " + "invalid nSequences") } nSequences = append(nSequences, wire.MaxTxInSequenceNum) + // Check creation fails with invalid version - err = creator.CreatePsbt(inputs, outputs, int32(3), uint32(0), nSequences) + _, err = New(inputs, outputs, int32(0), uint32(0), nSequences) if err == nil { t.Fatalf("Did not error when creating transaction with " + "invalid version (3)") } + // Use valid data to create: - creator.CreatePsbt(inputs, outputs, int32(2), uint32(0), nSequences) - rawCreated, err := creator.Cpsbt.Serialize() + cPsbt, err := New(inputs, outputs, int32(2), uint32(0), nSequences) + var b bytes.Buffer + err = cPsbt.Serialize(&b) if err != nil { t.Fatalf("Unable to serialize created Psbt: %v", err) } - if CUTestHexData["COPsbtHex"] != hex.EncodeToString(rawCreated) { + if CUTestHexData["COPsbtHex"] != hex.EncodeToString(b.Bytes()) { t.Fatalf("Failed to create expected psbt, instead got: %v", - hex.EncodeToString(rawCreated)) + hex.EncodeToString(b.Bytes())) } + // Now simulate passing the created PSBT to an Updater - updater, err := NewUpdater(creator.Cpsbt) + updater, err := NewUpdater(cPsbt) if err != nil { t.Fatalf("Unable to create Updater object") } @@ -375,11 +399,13 @@ func TestPsbtCreator(t *testing.T) { t.Fatalf("Unable to add Witness Utxo to inputs: %v", err) } - rawUpdated, err := updater.Upsbt.Serialize() + b.Reset() + + err = updater.Upsbt.Serialize(&b) if err != nil { t.Fatalf("Unable to serialize updated Psbt: %v", err) } - if CUTestHexData["UOPsbtHex"] != hex.EncodeToString(rawUpdated) { + if CUTestHexData["UOPsbtHex"] != hex.EncodeToString(b.Bytes()) { t.Fatal("Failed to create valid updated PSBT after utxos") } input1RedeemScript, err := hex.DecodeString(CUTestHexData["Input1RedeemScript"]) @@ -406,11 +432,13 @@ func TestPsbtCreator(t *testing.T) { if err != nil { t.Fatalf("Unable to add witness script: %v", err) } - rawUpdated, err = updater.Upsbt.Serialize() + + b.Reset() + err = updater.Upsbt.Serialize(&b) if err != nil { t.Fatalf("Unable to serialize updated Psbt: %v", err) } - if CUTestHexData["UOPsbtHex2"] != hex.EncodeToString(rawUpdated) { + if CUTestHexData["UOPsbtHex2"] != hex.EncodeToString(b.Bytes()) { t.Fatal("Failed to create valid updated PSBT after redeem scripts") } masterKey, err := hex.DecodeString(CUMasterKeyFingerPrint) @@ -490,11 +518,13 @@ func TestPsbtCreator(t *testing.T) { if err != nil { t.Fatal("Failed to add key to second output") } - rawUpdated, err = updater.Upsbt.Serialize() + + b.Reset() + err = updater.Upsbt.Serialize(&b) if err != nil { t.Fatalf("Unable to serialize updated Psbt: %v", err) } - if CUTestHexData["UOPsbtHex3"] != hex.EncodeToString(rawUpdated) { + if CUTestHexData["UOPsbtHex3"] != hex.EncodeToString(b.Bytes()) { t.Fatal("Failed to create valid updated PSBT after BIP32 derivations") } err = updater.AddInSighashType(txscript.SigHashType(1), 0) @@ -505,11 +535,13 @@ func TestPsbtCreator(t *testing.T) { if err != nil { t.Fatal("Failed to add sighash type to second input") } - rawUpdated, err = updater.Upsbt.Serialize() + + b.Reset() + err = updater.Upsbt.Serialize(&b) if err != nil { t.Fatalf("Unable to serialize updated Psbt: %v", err) } - if CUTestHexData["UOPsbtHex4"] != hex.EncodeToString(rawUpdated) { + if CUTestHexData["UOPsbtHex4"] != hex.EncodeToString(b.Bytes()) { t.Fatal("Failed to create valid updated PSBT after sighash types") } b644, err := updater.Upsbt.B64Encode() @@ -536,7 +568,10 @@ var signerPsbtData = map[string]string{ } func TestPsbtSigner(t *testing.T) { - psbt1, err := NewPsbt([]byte(signerPsbtData["signer1PsbtB64"]), true) + psbt1, err := NewFromRawBytes( + bytes.NewReader([]byte(signerPsbtData["signer1PsbtB64"])), + true, + ) if err != nil { t.Fatalf("Failed to parse PSBT: %v", err) } @@ -559,11 +594,13 @@ func TestPsbtSigner(t *testing.T) { if err != nil { t.Fatalf("Unable to decode hex: %v", err) } - rawUpdated, err := psbtUpdater1.Upsbt.Serialize() + + var b bytes.Buffer + err = psbtUpdater1.Upsbt.Serialize(&b) if err != nil { t.Fatalf("Unable to serialize updated Psbt: %v", err) } - if !bytes.Equal(rawUpdated, signer1Result) { + if !bytes.Equal(b.Bytes(), signer1Result) { t.Fatalf("Failed to add signatures correctly") } } @@ -580,12 +617,15 @@ var finalizerPsbtData = map[string]string{ func TestPsbtExtractor(t *testing.T) { rawToFinalize, err := base64.StdEncoding.DecodeString( - finalizerPsbtData["finalizeb64"]) + finalizerPsbtData["finalizeb64"], + ) if err != nil { t.Fatalf("Error decoding b64: %v", err) } - psbt1, err := NewPsbt(rawToFinalize, false) + psbt1, err := NewFromRawBytes( + bytes.NewReader(rawToFinalize), false, + ) if err != nil { t.Fatalf("Failed to parse PSBT: %v", err) } @@ -598,7 +638,8 @@ func TestPsbtExtractor(t *testing.T) { } finalizer1Result, err := base64.StdEncoding.DecodeString( - finalizerPsbtData["resultb64"]) + finalizerPsbtData["resultb64"], + ) if err != nil { t.Fatalf("Unable to decode b64: %v", err) } @@ -606,19 +647,26 @@ func TestPsbtExtractor(t *testing.T) { if err != nil { t.Fatalf("Unable to decode hex: %v", err) } - resultToNetwork, err := Extract(psbt1) + tx, err := Extract(psbt1) if err != nil { t.Fatalf("Failed to extract: %v", err) } - rawPsbt1, err := psbt1.Serialize() + var resultToNetwork bytes.Buffer + if err := tx.Serialize(&resultToNetwork); err != nil { + t.Fatalf("unable to serialize: %v", err) + } + + var b bytes.Buffer + err = psbt1.Serialize(&b) if err != nil { t.Fatalf("Unable to serialize updated Psbt: %v", err) } - if !bytes.Equal(rawPsbt1, finalizer1Result) { - t.Fatalf("Failed to finalize transaction %x", rawPsbt1) + if !bytes.Equal(b.Bytes(), finalizer1Result) { + t.Fatalf("Failed to finalize transaction: expected %x, "+ + "got %x", finalizer1Result, b.Bytes()) } - if !bytes.Equal(finalToNetworkExpected, resultToNetwork) { - t.Fatalf("Failed to network serialize transaction: %x", resultToNetwork) + if !bytes.Equal(finalToNetworkExpected, resultToNetwork.Bytes()) { + t.Fatalf("Failed to network serialize transaction: %x", b.Bytes()) } } @@ -629,7 +677,7 @@ func TestImportFromCore1(t *testing.T) { // separately, then finalize and extract, and compare with the network // serialized tx output from Core. imported := "cHNidP8BAJwCAAAAAjaoF6eKeGsPiDQxxqqhFDfHWjBtZzRqmaZmvyCVWZ5JAQAAAAD/////RhypNiFfnQSMNpo0SGsgIvDOyMQFAYEHZXD5jp4kCrUAAAAAAP////8CgCcSjAAAAAAXqRQFWy8ScSkkhlGMwfOnx15YwRzApofwX5MDAAAAABepFAt4TyLfGnL9QY6GLYHbpSQj+QclhwAAAAAAAAAAAA==" - psbt1, err := NewPsbt([]byte(imported), true) + psbt1, err := NewFromRawBytes(bytes.NewReader([]byte(imported)), true) if err != nil { t.Fatalf("Failed to parse PSBT: %v", err) } @@ -709,7 +757,7 @@ func TestImportFromCore1(t *testing.T) { // modifications to the input data and check it fails sanity checks. // First an invalid tx: - psbtBorkedInput2, _ := NewPsbt([]byte(imported), true) + psbtBorkedInput2, _ := NewFromRawBytes(bytes.NewReader([]byte(imported)), true) borkedUpdater, err := NewUpdater(psbtBorkedInput2) if err != nil { t.Fatalf("NewUpdater failed while trying to create borked "+ @@ -759,15 +807,23 @@ func TestImportFromCore1(t *testing.T) { t.Fatalf("Failed to finalize second input, %v", err) } - networkSerializedTx, err := Extract(psbt1) + tx, err := Extract(psbt1) + if err != nil { + t.Fatalf("unable to extract tx: %v", err) + } + var networkSerializedTx bytes.Buffer + if err := tx.Serialize(&networkSerializedTx); err != nil { + t.Fatalf("unable to encode tx: %v", err) + } + expectedTx := "0200000000010236a817a78a786b0f883431c6aaa11437c75a306d67346a99a666bf2095599e490100000000ffffffff461ca936215f9d048c369a34486b2022f0cec8c4050181076570f98e9e240ab5000000006a473044022014eb9c4858f71c9f280bc68402aa742a5187f54c56c8eb07c902eb1eb5804e5502203d66656de8386b9b044346d5605f5ae2b200328fb30476f6ac993fc0dbb04559012103b4c79acdf4e7d978bef4019c421e4c6c67044ed49d27322dc90e808d8080e862ffffffff028027128c0000000017a914055b2f1271292486518cc1f3a7c75e58c11cc0a687f05f93030000000017a9140b784f22df1a72fd418e862d81dba52423f90725870247304402200da03ac9890f5d724c42c83c2a62844c08425a274f1a5bca50dcde4126eb20dd02205278897b65cb8e390a0868c9582133c7157b2ad3e81c1c70d8fbd65f51a5658b0121024d6b24f372dd4551277c8df4ecc0655101e11c22894c8e05a3468409c865a72c0000000000" expectedTxBytes, err := hex.DecodeString(expectedTx) if err != nil { t.Fatalf("Unable to decode hex: %v", err) } - if !bytes.Equal(expectedTxBytes, networkSerializedTx) { + if !bytes.Equal(expectedTxBytes, networkSerializedTx.Bytes()) { t.Fatalf("The produced network transaction did not match the expected: %x \n %x \n", - networkSerializedTx, expectedTxBytes) + networkSerializedTx.Bytes(), expectedTxBytes) } } @@ -787,7 +843,7 @@ func TestImportFromCore2(t *testing.T) { // the previous example, we cannot here compare with a Core produced // network serialized final transaction, because of the fake input. imported := "cHNidP8BAJsCAAAAAkxTQ+rig5QNnUS5nMc+Pccow4IcOJeQRcNNw+7p5ZA5AQAAAAD/////qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqoNAAAAAP////8CAIYOcAAAAAAWABQ1l7nn13RubTwqRQU2BnVV5WlXBWAxMbUAAAAAF6kUkiuXUjfWFgTp6nl/gf9+8zIWR6KHAAAAAAAAAAAA" - psbt1, err := NewPsbt([]byte(imported), true) + psbt1, err := NewFromRawBytes(bytes.NewReader([]byte(imported)), true) if err != nil { t.Fatalf("Failed to parse PSBT: %v", err) } @@ -877,7 +933,7 @@ func TestImportFromCore2(t *testing.T) { fakevalSerialized := binary.LittleEndian.Uint64(fakeTxOutSerialized[:8]) fakeScriptPubKey := fakeTxOutSerialized[9:] txFund2Out := wire.NewTxOut(int64(fakevalSerialized), fakeScriptPubKey) - psbt2, err := NewPsbt([]byte(expectedPsbtPartialB64), true) + psbt2, err := NewFromRawBytes(bytes.NewReader([]byte(expectedPsbtPartialB64)), true) if err != nil { t.Fatalf("Failed to load partial PSBT: %v", err) } @@ -984,14 +1040,22 @@ func TestImportFromCore2(t *testing.T) { if uoutput2.WitnessScript == nil { t.Fatalf("PSBT should contain outwitnessscript but it does not.") } + var tx bytes.Buffer networkSerializedTx, err := Extract(psbt2) + if err != nil { + t.Fatalf("unable to extract tx: %v", err) + } + if err := networkSerializedTx.Serialize(&tx); err != nil { + t.Fatalf("unable to encode tx: %v", err) + } expectedSerializedTx, err := hex.DecodeString("020000000001024c5343eae283940d9d44b99cc73e3dc728c3821c38979045c34dc3eee9e5903901000000171600147aed39420a8b7ab98a83791327ccb70819d1fbe2ffffffffaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0d000000232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903ffffffff0200860e70000000001600143597b9e7d7746e6d3c2a450536067555e5695705603131b50000000017a914922b975237d61604e9ea797f81ff7ef3321647a287024730440220546d182d00e45ef659c329dce6197dc19e0abc795e2c9279873f5a887998b273022044143113fc3475d04fc8d5113e0bbcb42d80514a9f1a2247e9b2a7878e20d449012102bb3ce35af26f4c826eab3e5fc263ef56871b26686a8a995599b7ee65766131040400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00000000") if err != nil { t.Fatalf("Failed to decode hex: %v", err) } - if !bytes.Equal(expectedSerializedTx, networkSerializedTx) { - t.Fatalf("Failed to create correct network serialized transaction: "+ - "%x\n", networkSerializedTx) + if !bytes.Equal(expectedSerializedTx, tx.Bytes()) { + t.Fatalf("Failed to create correct network serialized "+ + "transaction: expected %x, got %x", + expectedSerializedTx, tx.Bytes()) } } @@ -999,7 +1063,7 @@ func TestMaybeFinalizeAll(t *testing.T) { // The following data is from a 3rd transaction from Core, // using 3 inputs, all p2wkh. imported := "cHNidP8BAKQCAAAAAzJyXH13IqBFvvZ7y1VSgUgkMvMoPgP5CfFNqsjQexKQAQAAAAD/////fMdLydu5bsoiHN9cFSaBL0Qnq2KLSKx0RA4b938CAgQAAAAAAP/////yKNgfsDAHr/zFz8R9k8EFI26allfg9DdE8Gzj6tGlegEAAAAA/////wHw9E0OAAAAABYAFDnPCRduiEWmmSc1j30SJ8k9u7PHAAAAAAAAAAAA" - psbt1, err := NewPsbt([]byte(imported), true) + psbt1, err := NewFromRawBytes(bytes.NewReader([]byte(imported)), true) if err != nil { t.Fatalf("Failed to parse PSBT: %v", err) } @@ -1091,7 +1155,7 @@ func TestFromUnsigned(t *testing.T) { if err != nil { t.Fatalf("Error: %v", err) } - psbt1, err := NewPsbtFromUnsignedTx(tx) + psbt1, err := NewFromUnsignedTx(tx) if err != nil { t.Fatalf("Error: %v", err) } @@ -1105,7 +1169,7 @@ func TestFromUnsigned(t *testing.T) { if encoded != fromCoreB64 { t.Fatalf("Got incorrect b64: %v", encoded) } - _, err = NewPsbt([]byte(fromCoreB64), true) + _, err = NewFromRawBytes(bytes.NewReader([]byte(fromCoreB64)), true) if err != nil { t.Fatalf("Error: %v", err) } @@ -1144,7 +1208,7 @@ func TestNonWitnessToWitness(t *testing.T) { } // import the PSBT - psbt1, err := NewPsbt([]byte(psbt1B64), true) + psbt1, err := NewFromRawBytes(bytes.NewReader([]byte(psbt1B64)), true) if err != nil { t.Fatalf("Failed to create PSBT: %v", err) } @@ -1212,11 +1276,15 @@ func TestNonWitnessToWitness(t *testing.T) { } expectedNetworkSer, _ := hex.DecodeString("020000000001047b4131763e497f79c627665893faec24d0d3614f5296cd848c4da6501d96f93e0100000017160014b3773ea5d2c881d62aa9b86a1e66f5eadcff73a8ffffffff1b69ade1b5fbfac562d6375bb816b943fc6c25d83314281d1c7499ab77b02ba600000000171600142412be29368c0260cb841eecd9b59d7e01174aa1ffffffffcdadb65bec6cdf020e243aa1560086d769bab8700bed546bfe79e70822fd4a820100000000ffffffffbdecf627e9ae012b95a38d1f5ae0a4db3797afadfa6cf65c64c7b1355609e9bf010000006a4730440220290abcaacbd759c4f989762a9ee3468a9231788aab8f50bf65955d8597d8dd3602204d7e394f4419dc5392c6edba6945837458dd750a030ac67a746231903a8eb7db01210388025f50bb51c0469421ed13381f22f9d46a070ec2837e055c49c5876f0d0968ffffffff01f02672530000000017a914430b040b99f36dd63999e38dd99436b6199e1603870247304402201fb93318eda2b247c2bd7d1d8240eecdfa89eed02eeca51939eca44e4275498902201bf841164ad612f3e390f482cf8bcfddce138b37ee590f544f9295766d6b3584012102de369fd7d30c7deac19f4067e65c572463aaa2a99f4af9c772542f079809de710247304402205676877e6162ce40a49ee5a74443cdc1e7915637c42da7b872c2ec2298fd371b02203c1d4a05b1e2a7a588d9ec9b8d4892d2cd59bebe0e777483477a0ec692ebbe6d012102534f23cb88a048b649672967263bd7570312d5d31d066fa7b303970010a77b2b02473044022065d0a349709b8d8043cfd644cf6c196c1f601a22e1b3fdfbf8c0cc2a80fe2f1702207c87d36b666a8862e81ec5df288707f517d2f35ea1548feb82019de2c8de90f701210257d88eaf1e79b72ea0a33ae89b57dae95ea68499bdc6770257e010ab899f0abb0000000000") - serializedtx, err := Extract(psbt1) + tx, err := Extract(psbt1) if err != nil { t.Fatalf("Failed to extract: %v", err) } - if !bytes.Equal(expectedNetworkSer, serializedtx) { - t.Fatalf("Expected serialized transaction was not produced: %x", serializedtx) + var b bytes.Buffer + if err := tx.Serialize(&b); err != nil { + t.Fatalf("unable to encode tx: %v", err) + } + if !bytes.Equal(expectedNetworkSer, b.Bytes()) { + t.Fatalf("Expected serialized transaction was not produced: %x", b.Bytes()) } } From f1575b5bfd8e608713e21fabd425927b1ed9514b Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 15 Jan 2020 17:49:07 -0800 Subject: [PATCH 12/13] build: add top-level go mod file --- go.mod | 11 +++++++++++ go.sum | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 go.mod create mode 100644 go.sum diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..d455377 --- /dev/null +++ b/go.mod @@ -0,0 +1,11 @@ +module github.com/btcsuite/btcutil + +go 1.13 + +require ( + github.com/aead/siphash v1.0.1 + github.com/btcsuite/btcd v0.20.1-beta + github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495 + github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 + golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..088c1a3 --- /dev/null +++ b/go.sum @@ -0,0 +1,54 @@ +github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg= +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd h1:qdGvebPBDuYDPGi1WCPjy1tGyMpmDK8IEapSsszn7HE= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723 h1:ZA/jbKoGcVAnER6pCHPEkGdZOV7U1oLUedErBHCUMs0= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495 h1:6IyqGr3fnd0tM3YxipK27TUskaOVUjU2nG45yzwcQKY= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 h1:FOOIBWrEkLgmlgGfMuZT83xIwfPDxEI2OHu6xUmJMFE= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d h1:2+ZP7EfsZV7Vvmx3TIqSlSzATMkTAKqM14YGFPoSKjI= +golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From 8aa4d06cc274fb570dee7ab8c00539522e602a03 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 15 Jan 2020 17:52:12 -0800 Subject: [PATCH 13/13] psbt: create sub-module --- psbt/go.mod | 9 +++++++++ psbt/go.sum | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 psbt/go.mod create mode 100644 psbt/go.sum diff --git a/psbt/go.mod b/psbt/go.mod new file mode 100644 index 0000000..d47c286 --- /dev/null +++ b/psbt/go.mod @@ -0,0 +1,9 @@ +module github.com/btcsuite/btcutil/psbt + +go 1.13 + +require ( + github.com/btcsuite/btcd v0.20.1-beta + github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d + github.com/davecgh/go-spew v1.1.1 +) diff --git a/psbt/go.sum b/psbt/go.sum new file mode 100644 index 0000000..4267a6f --- /dev/null +++ b/psbt/go.sum @@ -0,0 +1,36 @@ +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6ApMkVy8cUxV0XrxdP9rVf6D87/Mng= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcutil v0.0.0-20191219182022-e17c9730c422 h1:EqnrgSSg0SFWRlEZLExgjtuUR/IPnuQ6qw6nwRda4Uk= +github.com/btcsuite/btcutil v0.0.0-20191219182022-e17c9730c422/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44 h1:9lP3x0pW80sDI6t1UMSLA4to18W7R7imwAI/sWS9S8Q= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=