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  }