decred.org/dcrdex@v1.0.5/dex/networks/btc/descriptors.go (about)

     1  // This code is available on the terms of the project LICENSE.md file,
     2  // also available online at https://blueoakcouncil.org/license/1.0.0.
     3  
     4  package btc
     5  
     6  import (
     7  	"encoding/hex"
     8  	"errors"
     9  	"fmt"
    10  	"regexp"
    11  	"strconv"
    12  	"strings"
    13  
    14  	"github.com/btcsuite/btcd/btcec/v2"
    15  	"github.com/btcsuite/btcd/btcutil"
    16  	"github.com/btcsuite/btcd/btcutil/hdkeychain"
    17  )
    18  
    19  var (
    20  	// ErrMalformedDescriptor is returned when a provided descriptor string does
    21  	// not match the expected format for a descriptor.
    22  	ErrMalformedDescriptor = errors.New("malformed descriptor")
    23  )
    24  
    25  var (
    26  	hardened uint32 = hdkeychain.HardenedKeyStart
    27  
    28  	// {function}([{fingerprint}/{path}]{keyScriptOrNested})#{checksum}
    29  	descRE = regexp.MustCompile(`([[:alnum:]]+)` + `\((?:\[([[:xdigit:]]{8})/?([\d'h/]*)\])?(\S*)\)` +
    30  		`(?:#([[:alnum:]]{8}))?`) // https://regex101.com/r/75FEc4/1
    31  
    32  	// {xkey}{path}{range} e.g. {tpubDCD...}{/1'/2}{/*}
    33  	extKeyRE = regexp.MustCompile(`([[:alnum:]]+)((?:/\d+['h]?)*)(/\*['h]?)?`) // https://regex101.com/r/HLNzFF/1
    34  )
    35  
    36  // KeyOrigin describes the optional part of a KEY expression that contains key
    37  // origin information in side functions like pkh(KEY). For example, in the
    38  // descriptor wpkh([b940190e/84'/1'/0'/0/0]0300034...) the key origin is
    39  // [b940190e/84'/1'/0'/0/0], where the first part must be 8 hexadecimal
    40  // characters for the fingerprint of the master key, and there are zero or more
    41  // derivation steps to reach the key. See
    42  // https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md#key-origin-identification
    43  // Note for Fingerprint: "software must be willing to deal with collisions".
    44  type KeyOrigin struct {
    45  	// Fingerprint is "Exactly 8 hex characters for the fingerprint of the key
    46  	// where the derivation starts".
    47  	Fingerprint string
    48  	// Path is "zero or more /NUM or /NUM' path elements to indicate unhardened
    49  	// or hardened derivation steps between the fingerprint and the key or
    50  	// xpub/xprv root that follows". ParseDescriptor will strip a leading "/".
    51  	Path string
    52  	// Steps is the parsed path. Hardened keys indexes are offset by 2^31.
    53  	Steps []uint32
    54  }
    55  
    56  // String creates the canonical string representation of the key origin, e.g.
    57  // [d34db33f/44'/0'/0] for a Key origin with a Fingerprint of d34db33f and a
    58  // Path of 44'/0'/0.
    59  func (ko *KeyOrigin) String() string {
    60  	if ko.Path == "" {
    61  		return fmt.Sprintf("[%s]", ko.Fingerprint)
    62  	}
    63  	return fmt.Sprintf("[%s/%s]", ko.Fingerprint, strings.TrimPrefix(ko.Path, "/"))
    64  }
    65  
    66  // KeyFmt specifies the encoding of the key within a KEY expression.
    67  type KeyFmt byte
    68  
    69  // Valid KeyFmt values are one of:
    70  //  1. KeyHexPub: hex-encoded public key, compressed or uncompressed
    71  //  2. KeyWIFPriv: WIF-encoded private key
    72  //  3. KeyExtended public or private extended (BIP32) public key, PLUS zero or
    73  //     more /NUM or /NUM' steps, optionally terminated with a /* or /' to
    74  //     indicate all direct children (a range).
    75  const (
    76  	KeyUnknown KeyFmt = iota
    77  	KeyWIFPriv
    78  	KeyHexPub
    79  	KeyExtended
    80  )
    81  
    82  // pubKeyBytes is a roundabout helper since ExtendedKey.pubKeyBytes() is not
    83  // exported. The extended key should be created with an hdkeychain constructor
    84  // so the pubkey is always valid.
    85  func pubKeyBytes(key *hdkeychain.ExtendedKey) []byte {
    86  	pk, _ := key.ECPubKey()
    87  	return pk.SerializeCompressed()
    88  }
    89  
    90  func keyFingerprint(key *hdkeychain.ExtendedKey) []byte {
    91  	return btcutil.Hash160(pubKeyBytes(key))[:4]
    92  }
    93  
    94  // ParseKeyExtended is used to decode a Descriptor.Key when KeyFmt is
    95  // KeyExtended, which is the standard BIP32 extended key encoding with optional
    96  // derivation steps and a range indicator appended. The fingerprint of the
    97  // extended key is returned as a convenience. Use ParsePath to get the
    98  // derivation steps.
    99  // e.g. "tpubDCo.../0/*" is an extended public key with a ranged path.
   100  func ParseKeyExtended(keyPart string) (key *hdkeychain.ExtendedKey, fingerprint, path string, isRange bool, err error) {
   101  	xkeyParts := extKeyRE.FindStringSubmatch(keyPart)
   102  	if len(xkeyParts) < 4 { // extKeyRE has 3 capture groups + the match itself
   103  		return nil, "", "", false, ErrMalformedDescriptor
   104  	}
   105  
   106  	key, err = hdkeychain.NewKeyFromString(xkeyParts[1])
   107  	if err != nil {
   108  		return nil, "", "", false, err
   109  	}
   110  	fingerprint = hex.EncodeToString(keyFingerprint(key))
   111  	path = strings.TrimPrefix(xkeyParts[2], "/") // extKeyRE captures the leading "/"
   112  	isRange = len(xkeyParts[3]) > 0
   113  	return
   114  }
   115  
   116  func checkDescriptorKey(key string) KeyFmt {
   117  	// KeyWIFPriv
   118  	_, err := btcutil.DecodeWIF(key)
   119  	if err == nil {
   120  		return KeyWIFPriv
   121  	}
   122  
   123  	// KeyHexPub
   124  	if pkBytes, err := hex.DecodeString(key); err == nil {
   125  		if _, err = btcec.ParsePubKey(pkBytes); err == nil {
   126  			return KeyHexPub
   127  		}
   128  	}
   129  
   130  	// KeyExtended
   131  	if _, _, _, _, err = ParseKeyExtended(key); err == nil {
   132  		return KeyExtended
   133  	}
   134  
   135  	return KeyUnknown
   136  }
   137  
   138  // Descriptor models the output description language used by Bitcoin Core
   139  // descriptor wallets. Descriptors are a textual representation of an output or
   140  // address that begins with a "function", which may take as an argument a KEY,
   141  // SCRIPT, or other data specific to the function. This Descriptor type is
   142  // provided to decode and represent the most common KEY descriptor types and
   143  // SCRIPT types that commonly wrap other KEY types.
   144  type Descriptor struct {
   145  	// Function is the name of the top level function that begins the
   146  	// descriptor. For example, "pk", "pkh", "wpkh", "sh", "wsh", etc.
   147  	Function string
   148  	// Key is set for the KEY type functions and certain SCRIPT functions with
   149  	// nested KEY functions. May include a suffixed derivation path.
   150  	Key string
   151  	// KeyFmt is the type of key encoding.
   152  	KeyFmt KeyFmt
   153  	// KeyOrigin is an optional part of a KEY descriptor that describes the
   154  	// derivation of the key.
   155  	KeyOrigin *KeyOrigin
   156  	// Nested will only be set for descriptors with SCRIPT expressions.
   157  	Nested *Descriptor
   158  	// Expression is the entirety of the arguments to Function. This may be a
   159  	// KEY, SCRIPT, TREE, or combination of arguments depending on the function.
   160  	Expression string
   161  	// Checksum is an optional 8-character alphanumeric checksum of the
   162  	// descriptor. It is not validated.
   163  	Checksum string
   164  }
   165  
   166  func parseDescriptor(desc string, parentFn string) (*Descriptor, error) {
   167  	parts := descRE.FindStringSubmatch(desc)
   168  	if len(parts) < 6 { // descRE has 5 capture groups + the match itself
   169  		return nil, ErrMalformedDescriptor
   170  	}
   171  	parts = parts[1:] // pop off the match itself, just check capture groups
   172  
   173  	// function([fingerprint/path]arg)#checksum
   174  	function, fingerprint, path := parts[0], parts[1], parts[2]
   175  	arg, checksum := parts[3], parts[4]
   176  
   177  	d := &Descriptor{
   178  		Function:   function,
   179  		Expression: arg,
   180  		Checksum:   checksum,
   181  	}
   182  
   183  	switch function {
   184  	case "pk", "pkh", "wpkh", "combo": // KEY functions
   185  		d.Key = arg
   186  		// Be forward compatible: key format may be KeyUnknown, but the
   187  		// descriptor format is otherwise valid.
   188  		d.KeyFmt = checkDescriptorKey(arg)
   189  
   190  		if fingerprint != "" {
   191  			steps, isRange, err := parsePath(path) // descRE discards the leading "/"
   192  			if err != nil {
   193  				return nil, err
   194  			}
   195  			if isRange {
   196  				return nil, errors.New("range in key origin")
   197  			}
   198  
   199  			d.KeyOrigin = &KeyOrigin{
   200  				Fingerprint: fingerprint,
   201  				Path:        path,
   202  				Steps:       steps,
   203  			}
   204  
   205  			// Rebuild the full argument to the key function.
   206  			// e.g.	"[b940190e/84'/1'/0'/0/0]" + "030003...""
   207  			d.Expression = d.KeyOrigin.String() + d.Key
   208  		}
   209  
   210  	// SCRIPT functions that may have nested KEY function
   211  	case "sh":
   212  		// sh is "top level only"
   213  		if parentFn != "" {
   214  			return nil, errors.New("invalid nested scripthash")
   215  		}
   216  
   217  		fallthrough
   218  	case "wsh":
   219  		// wsh is "top level or inside sh only"
   220  		switch parentFn {
   221  		case "", "sh":
   222  		default:
   223  			return nil, errors.New("invalid nested witness scripthash")
   224  		}
   225  
   226  		if fingerprint != "" {
   227  			return nil, errors.New("key origin found in SCRIPT function")
   228  		}
   229  
   230  		var err error
   231  		d.Nested, err = parseDescriptor(arg, function)
   232  		if err != nil {
   233  			return nil, err
   234  		}
   235  		// If child SCRIPT was a KEY function, pull it up.
   236  		if d.Nested.KeyFmt != KeyUnknown {
   237  			d.KeyOrigin = d.Nested.KeyOrigin
   238  			d.Key = d.Nested.Key
   239  			d.KeyFmt = d.Nested.KeyFmt
   240  		}
   241  
   242  	default: // not KEY, like multi, tr, etc.
   243  	}
   244  
   245  	return d, nil
   246  }
   247  
   248  // ParseDescriptor parses a descriptor string. See
   249  // https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md
   250  // If the descriptor string does not match the expected pattern, an
   251  // ErrMalformedDescriptor error is returned. See the Descriptor type
   252  // documentation for information.
   253  func ParseDescriptor(desc string) (*Descriptor, error) {
   254  	return parseDescriptor(desc, "")
   255  }
   256  
   257  func parsePathPieces(pieces []string) (path []uint32, isRange bool, err error) {
   258  	for i, p := range pieces {
   259  		// In descriptor paths, "Anywhere a ' suffix is permitted to denote
   260  		// hardened derivation, the suffix h can be used instead."
   261  		if p == "*" || p == "*'" || p == "*h" {
   262  			if i != len(pieces)-1 {
   263  				return nil, false, errors.New("range indicator before end of path")
   264  			}
   265  			isRange = true
   266  			break // end of path, this is all the addresses
   267  		}
   268  		pN := strings.TrimRight(p, "'h") // strip any hardened character
   269  		pi, err := strconv.ParseUint(pN, 10, 32)
   270  		if err != nil {
   271  			return nil, false, err
   272  		}
   273  		if pN != p { // we stripped a ' or h => hardened
   274  			pi += uint64(hardened)
   275  		}
   276  		path = append(path, uint32(pi))
   277  	}
   278  	return
   279  }
   280  
   281  func parsePath(p string) (path []uint32, isRange bool, err error) {
   282  	if len(p) == 0 {
   283  		return
   284  	}
   285  	pp := strings.Split(p, "/")
   286  	return parsePathPieces(pp)
   287  }
   288  
   289  // ParsePath splits a common path string such as 84'/0'/0'/0 into it's
   290  // components, returning the steps in the derivation as integers. Hardened key
   291  // derivation steps, which are indicated by a ' or h, are offset by 2^31.
   292  // Certain paths in descriptors may end with /* (or /*' or /*h) to indicate all
   293  // child keys (or hardened child keys), in which case isRange will be true. As a
   294  // special case, a prefix of "m/" or just "/" is allowed, but a master key
   295  // fingerprint is not permitted and must be stripped first.
   296  func ParsePath(p string) (path []uint32, isRange bool, err error) {
   297  	p = strings.TrimPrefix(strings.TrimPrefix(p, "m"), "/")
   298  	return parsePath(p)
   299  }
   300  
   301  // DeepChild derives a new extended key from the provided root extended key and
   302  // derivation path. This is useful given a Descriptor.Key of type KeyExtended
   303  // that may be decoded into and extended key and path with ParseKeyExtended.
   304  // Given an address referencing the extended key via its fingerprint (also
   305  // returned by ParseKeyExtended) and derivation path, the private key for that
   306  // address may be derived given the child index of the address.
   307  func DeepChild(root *hdkeychain.ExtendedKey, path []uint32) (*hdkeychain.ExtendedKey, error) {
   308  	genChild := func(parent *hdkeychain.ExtendedKey, childIdx uint32) (*hdkeychain.ExtendedKey, error) {
   309  		err := hdkeychain.ErrInvalidChild
   310  		for err == hdkeychain.ErrInvalidChild {
   311  			var kid *hdkeychain.ExtendedKey
   312  			kid, err = parent.Derive(childIdx)
   313  			if err == nil {
   314  				return kid, nil
   315  			}
   316  			fmt.Printf("Child derive skipped a key index %d -> %d", childIdx, childIdx+1) // < 1 in 2^127 chance
   317  			childIdx++
   318  		}
   319  		return nil, err
   320  	}
   321  
   322  	extKey := root
   323  	for i, childIdx := range path {
   324  		childExtKey, err := genChild(extKey, childIdx)
   325  		if i > 0 { // don't zero the input key, root
   326  			extKey.Zero()
   327  		}
   328  		extKey = childExtKey
   329  		if err != nil {
   330  			if i > 0 {
   331  				extKey.Zero()
   332  			}
   333  			return nil, err
   334  		}
   335  	}
   336  
   337  	return extKey, nil
   338  }