From 7611eb65d8b975cf9cc906f2314a6ec33a5829c3 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 15 Jan 2020 17:35:45 -0800 Subject: [PATCH] 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 +)