github.com/valorbit/go-ethereum@v1.9.11-rc4/accounts/hd.go (about) 1 // Copyright 2017 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum 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-ethereum 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-ethereum 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 // This blockchain's SLIP-0044 coin type . TODO : Register it 29 var DefaultCoinType uint32 = 0x80000000 + 538 30 31 // DefaultRootDerivationPath is the root path to which custom derivation endpoints 32 // are appended. As such, the first account will be at m/44'/60'/0'/0, the second 33 // at m/44'/60'/0'/1, etc. 34 var DefaultRootDerivationPath = DerivationPath{0x80000000 + 44, DefaultCoinType, 0x80000000 + 0, 0} 35 36 // DefaultBaseDerivationPath is the base path from which custom derivation endpoints 37 // are incremented. As such, the first account will be at m/44'/60'/0'/0/0, the second 38 // at m/44'/60'/0'/0/1, etc. 39 var DefaultBaseDerivationPath = DerivationPath{0x80000000 + 44, DefaultCoinType, 0x80000000 + 0, 0, 0} 40 41 // LegacyLedgerBaseDerivationPath is the legacy base path from which custom derivation 42 // endpoints are incremented. As such, the first account will be at m/44'/60'/0'/0, the 43 // second at m/44'/60'/0'/1, etc. 44 // Legacy same as base 45 var LegacyLedgerBaseDerivationPath = DefaultBaseDerivationPath 46 47 // DerivationPath represents the computer friendly version of a hierarchical 48 // deterministic wallet account derivaion path. 49 // 50 // The BIP-32 spec https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki 51 // defines derivation paths to be of the form: 52 // 53 // m / purpose' / coin_type' / account' / change / address_index 54 // 55 // The BIP-44 spec https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki 56 // defines that the `purpose` be 44' (or 0x8000002C) for crypto currencies, and 57 // SLIP-44 https://github.com/satoshilabs/slips/blob/master/slip-0044.md assigns 58 // the `coin_type` 60' (or 0x8000003C) to Ethereum. 59 // 60 // The root path for Ethereum is m/44'/60'/0'/0 according to the specification 61 // from https://github.com/valorbit/EIPs/issues/84, albeit it's not set in stone 62 // yet whether accounts should increment the last component or the children of 63 // that. We will go with the simpler approach of incrementing the last component. 64 type DerivationPath []uint32 65 66 // ParseDerivationPath converts a user specified derivation path string to the 67 // internal binary representation. 68 // 69 // Full derivation paths need to start with the `m/` prefix, relative derivation 70 // paths (which will get appended to the default root path) must not have prefixes 71 // in front of the first element. Whitespace is ignored. 72 func ParseDerivationPath(path string) (DerivationPath, error) { 73 var result DerivationPath 74 75 // Handle absolute or relative paths 76 components := strings.Split(path, "/") 77 switch { 78 case len(components) == 0: 79 return nil, errors.New("empty derivation path") 80 81 case strings.TrimSpace(components[0]) == "": 82 return nil, errors.New("ambiguous path: use 'm/' prefix for absolute paths, or no leading '/' for relative ones") 83 84 case strings.TrimSpace(components[0]) == "m": 85 components = components[1:] 86 87 default: 88 result = append(result, DefaultRootDerivationPath...) 89 } 90 // All remaining components are relative, append one by one 91 if len(components) == 0 { 92 return nil, errors.New("empty derivation path") // Empty relative paths 93 } 94 for _, component := range components { 95 // Ignore any user added whitespace 96 component = strings.TrimSpace(component) 97 var value uint32 98 99 // Handle hardened paths 100 if strings.HasSuffix(component, "'") { 101 value = 0x80000000 102 component = strings.TrimSpace(strings.TrimSuffix(component, "'")) 103 } 104 // Handle the non hardened component 105 bigval, ok := new(big.Int).SetString(component, 0) 106 if !ok { 107 return nil, fmt.Errorf("invalid component: %s", component) 108 } 109 max := math.MaxUint32 - value 110 if bigval.Sign() < 0 || bigval.Cmp(big.NewInt(int64(max))) > 0 { 111 if value == 0 { 112 return nil, fmt.Errorf("component %v out of allowed range [0, %d]", bigval, max) 113 } 114 return nil, fmt.Errorf("component %v out of allowed hardened range [0, %d]", bigval, max) 115 } 116 value += uint32(bigval.Uint64()) 117 118 // Append and repeat 119 result = append(result, value) 120 } 121 return result, nil 122 } 123 124 // String implements the stringer interface, converting a binary derivation path 125 // to its canonical representation. 126 func (path DerivationPath) String() string { 127 result := "m" 128 for _, component := range path { 129 var hardened bool 130 if component >= 0x80000000 { 131 component -= 0x80000000 132 hardened = true 133 } 134 result = fmt.Sprintf("%s/%d", result, component) 135 if hardened { 136 result += "'" 137 } 138 } 139 return result 140 } 141 142 // MarshalJSON turns a derivation path into its json-serialized string 143 func (path DerivationPath) MarshalJSON() ([]byte, error) { 144 return json.Marshal(path.String()) 145 } 146 147 // UnmarshalJSON a json-serialized string back into a derivation path 148 func (path *DerivationPath) UnmarshalJSON(b []byte) error { 149 var dp string 150 var err error 151 if err = json.Unmarshal(b, &dp); err != nil { 152 return err 153 } 154 *path, err = ParseDerivationPath(dp) 155 return err 156 }