github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/accounts/hd.go (about)

     1  // This file is part of the go-sberex library. The go-sberex library is 
     2  // free software: you can redistribute it and/or modify it under the terms 
     3  // of the GNU Lesser General Public License as published by the Free 
     4  // Software Foundation, either version 3 of the License, or (at your option)
     5  // any later version.
     6  //
     7  // The go-sberex library is distributed in the hope that it will be useful, 
     8  // but WITHOUT ANY WARRANTY; without even the implied warranty of
     9  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 
    10  // General Public License <http://www.gnu.org/licenses/> for more details.
    11  
    12  package accounts
    13  
    14  import (
    15  	"errors"
    16  	"fmt"
    17  	"math"
    18  	"math/big"
    19  	"strings"
    20  )
    21  
    22  // DefaultRootDerivationPath is the root path to which custom derivation endpoints
    23  // are appended. As such, the first account will be at m/44'/60'/0'/0, the second
    24  // at m/44'/60'/0'/1, etc.
    25  var DefaultRootDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}
    26  
    27  // DefaultBaseDerivationPath is the base path from which custom derivation endpoints
    28  // are incremented. As such, the first account will be at m/44'/60'/0'/0, the second
    29  // at m/44'/60'/0'/1, etc.
    30  var DefaultBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0, 0}
    31  
    32  // DefaultLedgerBaseDerivationPath is the base path from which custom derivation endpoints
    33  // are incremented. As such, the first account will be at m/44'/60'/0'/0, the second
    34  // at m/44'/60'/0'/1, etc.
    35  var DefaultLedgerBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}
    36  
    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 Sberex.
    49  //
    50  // The root path for Sberex is m/44'/60'/0'/0, albeit it's not set in stone
    51  // yet whether accounts should increment the last component or the children of
    52  // that. We will go with the simpler approach of incrementing the last component.
    53  type DerivationPath []uint32
    54  
    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  
    64  	// Handle absolute or relative paths
    65  	components := strings.Split(path, "/")
    66  	switch {
    67  	case len(components) == 0:
    68  		return nil, errors.New("empty derivation path")
    69  
    70  	case strings.TrimSpace(components[0]) == "":
    71  		return nil, errors.New("ambiguous path: use 'm/' prefix for absolute paths, or no leading '/' for relative ones")
    72  
    73  	case strings.TrimSpace(components[0]) == "m":
    74  		components = components[1:]
    75  
    76  	default:
    77  		result = append(result, DefaultRootDerivationPath...)
    78  	}
    79  	// All remaining components are relative, append one by one
    80  	if len(components) == 0 {
    81  		return nil, errors.New("empty derivation path") // Empty relative paths
    82  	}
    83  	for _, component := range components {
    84  		// Ignore any user added whitespace
    85  		component = strings.TrimSpace(component)
    86  		var value uint32
    87  
    88  		// Handle hardened paths
    89  		if strings.HasSuffix(component, "'") {
    90  			value = 0x80000000
    91  			component = strings.TrimSpace(strings.TrimSuffix(component, "'"))
    92  		}
    93  		// Handle the non hardened component
    94  		bigval, ok := new(big.Int).SetString(component, 0)
    95  		if !ok {
    96  			return nil, fmt.Errorf("invalid component: %s", component)
    97  		}
    98  		max := math.MaxUint32 - value
    99  		if bigval.Sign() < 0 || bigval.Cmp(big.NewInt(int64(max))) > 0 {
   100  			if value == 0 {
   101  				return nil, fmt.Errorf("component %v out of allowed range [0, %d]", bigval, max)
   102  			}
   103  			return nil, fmt.Errorf("component %v out of allowed hardened range [0, %d]", bigval, max)
   104  		}
   105  		value += uint32(bigval.Uint64())
   106  
   107  		// Append and repeat
   108  		result = append(result, value)
   109  	}
   110  	return result, nil
   111  }
   112  
   113  // String implements the stringer interface, converting a binary derivation path
   114  // to its canonical representation.
   115  func (path DerivationPath) String() string {
   116  	result := "m"
   117  	for _, component := range path {
   118  		var hardened bool
   119  		if component >= 0x80000000 {
   120  			component -= 0x80000000
   121  			hardened = true
   122  		}
   123  		result = fmt.Sprintf("%s/%d", result, component)
   124  		if hardened {
   125  			result += "'"
   126  		}
   127  	}
   128  	return result
   129  }