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 }