psbt: move PSBT keys/type into new file

This commit is contained in:
Olaoluwa Osuntokun 2020-01-15 17:35:45 -08:00
parent e17c9730c4
commit 7611eb65d8
No known key found for this signature in database
GPG key ID: BC13F65E2DC84465
2 changed files with 155 additions and 210 deletions

View file

@ -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

149
psbt/types.go Normal file
View file

@ -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}|<prefix>|{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}|<prefix>|{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
)