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