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  }