hd.go 6.5 KB

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