github.com/decred/dcrlnd@v0.7.6/lnrpc/walletrpc/walletkit_util.go (about) 1 package walletrpc 2 3 import ( 4 "fmt" 5 "strconv" 6 "strings" 7 8 "github.com/decred/dcrlnd/lnrpc" 9 ) 10 11 // AccountsToWatchOnly converts the accounts returned by the walletkit's 12 // ListAccounts RPC into a struct that can be used to create a watch-only 13 // wallet. 14 func AccountsToWatchOnly(exported []*Account) ([]*lnrpc.WatchOnlyAccount, 15 error) { 16 17 result := make([]*lnrpc.WatchOnlyAccount, len(exported)) 18 for idx, acct := range exported { 19 parsedPath, err := parseDerivationPath(acct.DerivationPath) 20 if err != nil { 21 return nil, fmt.Errorf("error parsing derivation path "+ 22 "of account %d: %v", idx, err) 23 } 24 if len(parsedPath) < 3 { 25 return nil, fmt.Errorf("derivation path of account %d "+ 26 "has invalid derivation path, need at least "+ 27 "path of depth 3, instead has depth %d", idx, 28 len(parsedPath)) 29 } 30 31 result[idx] = &lnrpc.WatchOnlyAccount{ 32 Purpose: parsedPath[0], 33 CoinType: parsedPath[1], 34 Account: parsedPath[2], 35 Xpub: acct.ExtendedPublicKey, 36 } 37 } 38 39 return result, nil 40 } 41 42 // parseDerivationPath parses a path in the form of m/x'/y'/z'/a/b into a slice 43 // of [x, y, z, a, b], meaning that the apostrophe is ignored and 2^31 is _not_ 44 // added to the numbers. 45 func parseDerivationPath(path string) ([]uint32, error) { 46 path = strings.TrimSpace(path) 47 if len(path) == 0 { 48 return nil, fmt.Errorf("path cannot be empty") 49 } 50 if !strings.HasPrefix(path, "m/") { 51 return nil, fmt.Errorf("path must start with m/") 52 } 53 54 // Just the root key, no path was provided. This is valid but not useful 55 // in most cases. 56 rest := strings.ReplaceAll(path, "m/", "") 57 if rest == "" { 58 return []uint32{}, nil 59 } 60 61 parts := strings.Split(rest, "/") 62 indices := make([]uint32, len(parts)) 63 for i := 0; i < len(parts); i++ { 64 part := parts[i] 65 if strings.Contains(parts[i], "'") { 66 part = strings.TrimRight(parts[i], "'") 67 } 68 parsed, err := strconv.ParseInt(part, 10, 32) 69 if err != nil { 70 return nil, fmt.Errorf("could not parse part \"%s\": "+ 71 "%v", part, err) 72 } 73 indices[i] = uint32(parsed) 74 } 75 return indices, nil 76 }