github.com/0xsequence/ethkit@v1.25.0/ethwallet/hdnode.go (about)

     1  package ethwallet
     2  
     3  import (
     4  	"crypto/ecdsa"
     5  	"crypto/rand"
     6  	"fmt"
     7  
     8  	"github.com/0xsequence/ethkit/go-ethereum/accounts"
     9  	"github.com/0xsequence/ethkit/go-ethereum/common"
    10  	"github.com/0xsequence/ethkit/go-ethereum/crypto"
    11  	"github.com/btcsuite/btcd/btcutil/hdkeychain"
    12  	"github.com/btcsuite/btcd/chaincfg"
    13  	"github.com/tyler-smith/go-bip39"
    14  )
    15  
    16  // DefaultBaseDerivationPath is the base path from which custom derivation endpoints
    17  // are incremented. As such, the first account will be at m/44'/60'/0'/0/0, the second
    18  // at m/44'/60'/0'/0/1, etc.
    19  var DefaultBaseDerivationPath = accounts.DefaultBaseDerivationPath
    20  
    21  // Entropy bit size constants for 12 and 24 word mnemonics
    22  const (
    23  	EntropyBitSize12WordMnemonic = 128
    24  	EntropyBitSize24WordMnemonic = 256
    25  )
    26  
    27  type HDNode struct {
    28  	masterKey  *hdkeychain.ExtendedKey
    29  	privateKey *ecdsa.PrivateKey
    30  	publicKey  *ecdsa.PublicKey
    31  
    32  	entropy        []byte
    33  	mnemonic       string
    34  	derivationPath accounts.DerivationPath
    35  
    36  	address common.Address
    37  }
    38  
    39  func NewHDNodeFromPrivateKey(privateKey string) (*HDNode, error) {
    40  	key, err := crypto.HexToECDSA(privateKey)
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  
    45  	return &HDNode{
    46  		privateKey: key,
    47  		publicKey:  &key.PublicKey,
    48  		address:    crypto.PubkeyToAddress(key.PublicKey),
    49  	}, nil
    50  }
    51  
    52  func NewHDNodeFromMnemonic(mnemonic string, path *accounts.DerivationPath) (*HDNode, error) {
    53  	entropy, err := MnemonicToEntropy(mnemonic)
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  
    58  	hdnode, err := NewHDNodeFromEntropy(entropy, path)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	hdnode.mnemonic = mnemonic
    63  	return hdnode, nil
    64  }
    65  
    66  func NewHDNodeFromEntropy(entropy []byte, path *accounts.DerivationPath) (*HDNode, error) {
    67  	mnemonic, err := EntropyToMnemonic(entropy)
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  
    72  	seed, err := NewSeedFromMnemonic(mnemonic)
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  
    77  	masterKey, err := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams)
    78  	if err != nil {
    79  		return nil, err
    80  	}
    81  
    82  	var derivationPath accounts.DerivationPath
    83  	if path == nil {
    84  		derivationPath = DefaultBaseDerivationPath
    85  	} else {
    86  		derivationPath = *path
    87  	}
    88  
    89  	privateKey, err := derivePrivateKey(masterKey, derivationPath)
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  	publicKey, err := derivePublicKey(masterKey, derivationPath)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	address, err := deriveAddress(masterKey, derivationPath)
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  
   102  	return &HDNode{
   103  		masterKey:      masterKey,
   104  		privateKey:     privateKey,
   105  		publicKey:      publicKey,
   106  		entropy:        entropy,
   107  		mnemonic:       mnemonic,
   108  		derivationPath: derivationPath,
   109  		address:        address,
   110  	}, nil
   111  }
   112  
   113  func NewHDNodeFromRandomEntropy(bitSize int, path *accounts.DerivationPath) (*HDNode, error) {
   114  	entropy, err := RandomEntropy(bitSize)
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  	return NewHDNodeFromEntropy(entropy, path)
   119  }
   120  
   121  // NewSeedFromMnemonic returns a BIP-39 seed based on a BIP-39 mnemonic.
   122  func NewSeedFromMnemonic(mnemonic string) ([]byte, error) {
   123  	if mnemonic == "" {
   124  		return nil, fmt.Errorf("mnemonic is required")
   125  	}
   126  	return bip39.NewSeedWithErrorChecking(mnemonic, "")
   127  }
   128  
   129  func MnemonicToEntropy(mnemonic string) ([]byte, error) {
   130  	return bip39.MnemonicToByteArray(mnemonic, true)
   131  }
   132  
   133  func EntropyToMnemonic(entropy []byte) (string, error) {
   134  	return bip39.NewMnemonic(entropy)
   135  }
   136  
   137  func RandomEntropy(bitSize ...int) ([]byte, error) {
   138  	if len(bitSize) > 0 {
   139  		return bip39.NewEntropy(bitSize[0])
   140  	} else {
   141  		return bip39.NewEntropy(EntropyBitSize12WordMnemonic) // default
   142  	}
   143  }
   144  
   145  // RandomSeed returns a randomly generated BIP-39 seed.
   146  func RandomSeed() ([]byte, error) {
   147  	b := make([]byte, 64)
   148  	_, err := rand.Read(b)
   149  	return b, err
   150  }
   151  
   152  func IsValidMnemonic(mnemonic string) bool {
   153  	return bip39.IsMnemonicValid(mnemonic)
   154  }
   155  
   156  // ParseDerivationPath parses the derivation path in string format into []uint32
   157  func ParseDerivationPath(path string) (accounts.DerivationPath, error) {
   158  	return accounts.ParseDerivationPath(path)
   159  }
   160  
   161  func (h *HDNode) Mnemonic() string {
   162  	return h.mnemonic
   163  }
   164  
   165  func (h *HDNode) Entropy() []byte {
   166  	return h.entropy
   167  }
   168  
   169  func (h *HDNode) DerivationPath() accounts.DerivationPath {
   170  	return h.derivationPath
   171  }
   172  
   173  func (h *HDNode) Address() common.Address {
   174  	return h.address
   175  }
   176  
   177  func (h *HDNode) PrivateKey() *ecdsa.PrivateKey {
   178  	return h.privateKey
   179  }
   180  
   181  func (h *HDNode) PublicKey() *ecdsa.PublicKey {
   182  	return h.publicKey
   183  }
   184  
   185  func (h *HDNode) DerivePathFromString(path string) error {
   186  	derivationPath, err := ParseDerivationPath(path)
   187  	if err != nil {
   188  		return err
   189  	}
   190  	return h.DerivePath(derivationPath)
   191  }
   192  
   193  func (h *HDNode) DerivePath(derivationPath accounts.DerivationPath) error {
   194  	privateKey, err := derivePrivateKey(h.masterKey, derivationPath)
   195  	if err != nil {
   196  		return err
   197  	}
   198  	publicKey, err := derivePublicKey(h.masterKey, derivationPath)
   199  	if err != nil {
   200  		return err
   201  	}
   202  	address, err := deriveAddress(h.masterKey, derivationPath)
   203  	if err != nil {
   204  		return err
   205  	}
   206  
   207  	h.derivationPath = derivationPath
   208  	h.privateKey = privateKey
   209  	h.publicKey = publicKey
   210  	h.address = address
   211  
   212  	return nil
   213  }
   214  
   215  func (h *HDNode) DeriveAccountIndex(accountIndex uint32) error {
   216  	x := len(h.derivationPath)
   217  	if x < 4 {
   218  		return fmt.Errorf("invalid account derivation path")
   219  	}
   220  
   221  	// copy + update
   222  	updatedPath := make(accounts.DerivationPath, len(h.derivationPath))
   223  	copy(updatedPath, h.derivationPath)
   224  	updatedPath[x-1] = accountIndex
   225  
   226  	return h.DerivePath(updatedPath)
   227  }
   228  
   229  func (h *HDNode) Clone() (*HDNode, error) {
   230  	derivationPath := make(accounts.DerivationPath, len(h.derivationPath))
   231  	copy(derivationPath, h.derivationPath)
   232  	return NewHDNodeFromMnemonic(h.Mnemonic(), &derivationPath)
   233  }
   234  
   235  // DerivePrivateKey derives the private key of the derivation path.
   236  func derivePrivateKey(masterKey *hdkeychain.ExtendedKey, path accounts.DerivationPath) (*ecdsa.PrivateKey, error) {
   237  	var err error
   238  	key := masterKey
   239  	for _, n := range path {
   240  		key, err = key.Derive(n)
   241  		if err != nil {
   242  			return nil, err
   243  		}
   244  	}
   245  
   246  	privateKey, err := key.ECPrivKey()
   247  	if err != nil {
   248  		return nil, err
   249  	}
   250  
   251  	privateKeyECDSA := privateKey.ToECDSA()
   252  	return privateKeyECDSA, nil
   253  }
   254  
   255  // DerivePublicKey derives the public key of the derivation path.
   256  func derivePublicKey(masterKey *hdkeychain.ExtendedKey, path accounts.DerivationPath) (*ecdsa.PublicKey, error) {
   257  	privateKeyECDSA, err := derivePrivateKey(masterKey, path)
   258  	if err != nil {
   259  		return nil, err
   260  	}
   261  
   262  	publicKey := privateKeyECDSA.Public()
   263  	publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
   264  	if !ok {
   265  		return nil, fmt.Errorf("failed to get public key")
   266  	}
   267  
   268  	return publicKeyECDSA, nil
   269  }
   270  
   271  // DeriveAddress derives the account address of the derivation path.
   272  func deriveAddress(masterKey *hdkeychain.ExtendedKey, path accounts.DerivationPath) (common.Address, error) {
   273  	publicKeyECDSA, err := derivePublicKey(masterKey, path)
   274  	if err != nil {
   275  		return common.Address{}, err
   276  	}
   277  
   278  	address := crypto.PubkeyToAddress(*publicKeyECDSA)
   279  	return address, nil
   280  }