github.com/core-coin/go-core/v2@v2.1.9/accounts/hd.go (about) 1 // Copyright 2017 by the Authors 2 // This file is part of the go-core library. 3 // 4 // The go-core library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-core library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-core library. If not, see <http://www.gnu.org/licenses/>. 16 17 package accounts 18 19 import ( 20 "encoding/json" 21 "errors" 22 "fmt" 23 "math" 24 "math/big" 25 "strings" 26 ) 27 28 // DefaultRootDerivationPath is the root path to which custom derivation endpoints 29 // are appended. As such, the first account will be at m/44'/654'/0'/0, the second 30 // at m/44'/654'/0'/1, etc. 31 var DefaultRootDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 654, 0x80000000 + 0, 0} 32 33 // DefaultBaseDerivationPath is the base path from which custom derivation endpoints 34 // are incremented. As such, the first account will be at m/44'/654'/0'/0/0, the second 35 // at m/44'/654'/0'/0/1, etc. 36 var DefaultBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 654, 0x80000000 + 0, 0, 0} 37 38 // DerivationPath represents the computer friendly version of a hierarchical 39 // deterministic wallet account derivaion path. 40 // 41 // The BIP-32 spec https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki 42 // defines derivation paths to be of the form: 43 // 44 // m / purpose' / coin_type' / account' / change / address_index 45 // 46 // The BIP-44 spec https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki 47 // defines that the `purpose` be 44' (or 0x8000002C) for crypto currencies, and 48 // SLIP-44 https://github.com/satoshilabs/slips/blob/master/slip-0044.md assigns 49 // the `coin_type` 654' (or 0x8000028e) to Core. 50 // 51 // The root path for Core is m/44'/654'/0'/0 according to the specification 52 // from https://github.com/core-coin/CIPs/issues/84, albeit it's not set in stone 53 // yet whether accounts should increment the last component or the children of 54 // that. We will go with the simpler approach of incrementing the last component. 55 type DerivationPath []uint32 56 57 // ParseDerivationPath converts a user specified derivation path string to the 58 // internal binary representation. 59 // 60 // Full derivation paths need to start with the `m/` prefix, relative derivation 61 // paths (which will get appended to the default root path) must not have prefixes 62 // in front of the first element. Whitespace is ignored. 63 func ParseDerivationPath(path string) (DerivationPath, error) { 64 var result DerivationPath 65 66 // Handle absolute or relative paths 67 components := strings.Split(path, "/") 68 switch { 69 case len(components) == 0: 70 return nil, errors.New("empty derivation path") 71 72 case strings.TrimSpace(components[0]) == "": 73 return nil, errors.New("ambiguous path: use 'm/' prefix for absolute paths, or no leading '/' for relative ones") 74 75 case strings.TrimSpace(components[0]) == "m": 76 components = components[1:] 77 78 default: 79 result = append(result, DefaultRootDerivationPath...) 80 } 81 // All remaining components are relative, append one by one 82 if len(components) == 0 { 83 return nil, errors.New("empty derivation path") // Empty relative paths 84 } 85 for _, component := range components { 86 // Ignore any user added whitespace 87 component = strings.TrimSpace(component) 88 var value uint32 89 90 // Handle hardened paths 91 if strings.HasSuffix(component, "'") { 92 value = 0x80000000 93 component = strings.TrimSpace(strings.TrimSuffix(component, "'")) 94 } 95 // Handle the non hardened component 96 bigval, ok := new(big.Int).SetString(component, 0) 97 if !ok { 98 return nil, fmt.Errorf("invalid component: %s", component) 99 } 100 max := math.MaxUint32 - value 101 if bigval.Sign() < 0 || bigval.Cmp(big.NewInt(int64(max))) > 0 { 102 if value == 0 { 103 return nil, fmt.Errorf("component %v out of allowed range [0, %d]", bigval, max) 104 } 105 return nil, fmt.Errorf("component %v out of allowed hardened range [0, %d]", bigval, max) 106 } 107 value += uint32(bigval.Uint64()) 108 109 // Append and repeat 110 result = append(result, value) 111 } 112 return result, nil 113 } 114 115 // String implements the stringer interface, converting a binary derivation path 116 // to its canonical representation. 117 func (path DerivationPath) String() string { 118 result := "m" 119 for _, component := range path { 120 var hardened bool 121 if component >= 0x80000000 { 122 component -= 0x80000000 123 hardened = true 124 } 125 result = fmt.Sprintf("%s/%d", result, component) 126 if hardened { 127 result += "'" 128 } 129 } 130 return result 131 } 132 133 // MarshalJSON turns a derivation path into its json-serialized string 134 func (path DerivationPath) MarshalJSON() ([]byte, error) { 135 return json.Marshal(path.String()) 136 } 137 138 // UnmarshalJSON a json-serialized string back into a derivation path 139 func (path *DerivationPath) UnmarshalJSON(b []byte) error { 140 var dp string 141 var err error 142 if err = json.Unmarshal(b, &dp); err != nil { 143 return err 144 } 145 *path, err = ParseDerivationPath(dp) 146 return err 147 } 148 149 // DefaultIterator creates a BIP-32 path iterator, which progresses by increasing the last component: 150 // i.e. m/44'/654'/0'/0/0, m/44'/654'/0'/0/1, m/44'/654'/0'/0/2, ... m/44'/654'/0'/0/N. 151 func DefaultIterator(base DerivationPath) func() DerivationPath { 152 path := make(DerivationPath, len(base)) 153 copy(path[:], base[:]) 154 // Set it back by one, so the first call gives the first result 155 path[len(path)-1]-- 156 return func() DerivationPath { 157 path[len(path)-1]++ 158 return path 159 } 160 }