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