123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136 |
- // Copyright 2017 The go-ethereum Authors
- // This file is part of the go-ethereum library.
- //
- // The go-ethereum library is free software: you can redistribute it and/or modify
- // it under the terms of the GNU Lesser General Public License as published by
- // the Free Software Foundation, either version 3 of the License, or
- // (at your option) any later version.
- //
- // The go-ethereum library is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU Lesser General Public License for more details.
- //
- // You should have received a copy of the GNU Lesser General Public License
- // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
- package accounts
- import (
- "errors"
- "fmt"
- "math"
- "math/big"
- "strings"
- )
- // DefaultRootDerivationPath is the root path to which custom derivation endpoints
- // are appended. As such, the first account will be at m/44'/60'/0'/0, the second
- // at m/44'/60'/0'/1, etc.
- var DefaultRootDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}
- // DefaultBaseDerivationPath is the base path from which custom derivation endpoints
- // are incremented. As such, the first account will be at m/44'/60'/0'/0, the second
- // at m/44'/60'/0'/1, etc.
- var DefaultBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0, 0}
- // DefaultLedgerBaseDerivationPath is the base path from which custom derivation endpoints
- // are incremented. As such, the first account will be at m/44'/60'/0'/0, the second
- // at m/44'/60'/0'/1, etc.
- var DefaultLedgerBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}
- // DerivationPath represents the computer friendly version of a hierarchical
- // deterministic wallet account derivaion path.
- //
- // The BIP-32 spec https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
- // defines derivation paths to be of the form:
- //
- // m / purpose' / coin_type' / account' / change / address_index
- //
- // The BIP-44 spec https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
- // defines that the `purpose` be 44' (or 0x8000002C) for crypto currencies, and
- // SLIP-44 https://github.com/satoshilabs/slips/blob/master/slip-0044.md assigns
- // the `coin_type` 60' (or 0x8000003C) to Ethereum.
- //
- // The root path for Ethereum is m/44'/60'/0'/0 according to the specification
- // from https://github.com/ethereum/EIPs/issues/84, albeit it's not set in stone
- // yet whether accounts should increment the last component or the children of
- // that. We will go with the simpler approach of incrementing the last component.
- type DerivationPath []uint32
- // ParseDerivationPath converts a user specified derivation path string to the
- // internal binary representation.
- //
- // Full derivation paths need to start with the `m/` prefix, relative derivation
- // paths (which will get appended to the default root path) must not have prefixes
- // in front of the first element. Whitespace is ignored.
- func ParseDerivationPath(path string) (DerivationPath, error) {
- var result DerivationPath
- // Handle absolute or relative paths
- components := strings.Split(path, "/")
- switch {
- case len(components) == 0:
- return nil, errors.New("empty derivation path")
- case strings.TrimSpace(components[0]) == "":
- return nil, errors.New("ambiguous path: use 'm/' prefix for absolute paths, or no leading '/' for relative ones")
- case strings.TrimSpace(components[0]) == "m":
- components = components[1:]
- default:
- result = append(result, DefaultRootDerivationPath...)
- }
- // All remaining components are relative, append one by one
- if len(components) == 0 {
- return nil, errors.New("empty derivation path") // Empty relative paths
- }
- for _, component := range components {
- // Ignore any user added whitespace
- component = strings.TrimSpace(component)
- var value uint32
- // Handle hardened paths
- if strings.HasSuffix(component, "'") {
- value = 0x80000000
- component = strings.TrimSpace(strings.TrimSuffix(component, "'"))
- }
- // Handle the non hardened component
- bigval, ok := new(big.Int).SetString(component, 0)
- if !ok {
- return nil, fmt.Errorf("invalid component: %s", component)
- }
- max := math.MaxUint32 - value
- if bigval.Sign() < 0 || bigval.Cmp(big.NewInt(int64(max))) > 0 {
- if value == 0 {
- return nil, fmt.Errorf("component %v out of allowed range [0, %d]", bigval, max)
- }
- return nil, fmt.Errorf("component %v out of allowed hardened range [0, %d]", bigval, max)
- }
- value += uint32(bigval.Uint64())
- // Append and repeat
- result = append(result, value)
- }
- return result, nil
- }
- // String implements the stringer interface, converting a binary derivation path
- // to its canonical representation.
- func (path DerivationPath) String() string {
- result := "m"
- for _, component := range path {
- var hardened bool
- if component >= 0x80000000 {
- component -= 0x80000000
- hardened = true
- }
- result = fmt.Sprintf("%s/%d", result, component)
- if hardened {
- result += "'"
- }
- }
- return result
- }
|