description: Tutorial on how to generate Ethereum wallets with Go.

Generating New Wallets

To generate a new wallet first we need to import the go-ethereum crypto package that provides the GenerateKey method for generating a random private key.

  1. privateKey, err := crypto.GenerateKey()
  2. if err != nil {
  3. log.Fatal(err)
  4. }

Then we can convert it to bytes by importing the golang crypto/ecdsa package and using the FromECDSA method.

  1. privateKeyBytes := crypto.FromECDSA(privateKey)

We can now convert it to a hexadecimal string by using the go-ethereum hexutil package which provides the Encode method which takes a byte slice. Then we strip off the 0x after it’s hex encoded.

  1. fmt.Println(hexutil.Encode(privateKeyBytes)[2:]) // fad9c8855b740a0b7ed4c221dbad0f33a83a49cad6b3fe8d5817ac83d38b6a19

This is the private key which is used for signing transactions and is to be treated like a password and never be shared, since who ever is in possesion of it will have access to all your funds.

Since the public key is derived from the private key, go-ethereum’s crypto private key has a Public method that will return the public key.

  1. publicKey := privateKey.Public()

Converting it to hex is a similar process that we went through with the private key. We strip off the 0x and the first 2 characters 04 which is always the EC prefix and is not required.

  1. publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
  2. if !ok {
  3. log.Fatal("cannot assert type: publicKey is not of type *ecdsa.PublicKey")
  4. }
  5. publicKeyBytes := crypto.FromECDSAPub(publicKeyECDSA)
  6. fmt.Println(hexutil.Encode(publicKeyBytes)[4:]) // 9a7df67f79246283fdc93af76d4f8cdd62c4886e8cd870944e817dd0b97934fdd7719d0810951e03418205868a5c1b40b192451367f28e0088dd75e15de40c05

Now that we have the public key we can easily generate the public address which is what you’re used to seeing. In order to do that, the go-ethereum crypto package has a PubkeyToAddress method which accepts an ECDSA public key, and returns the public address.

  1. address := crypto.PubkeyToAddress(*publicKeyECDSA).Hex()
  2. fmt.Println(address) // 0x96216849c49358B10257cb55b28eA603c874b05E

The public address is simply the Keccak-256 hash of the public key, and then we take the last 40 characters (20 bytes) and prefix it with 0x. Here’s how you can do it manually using the crypto/sha3 keccak256 function.

  1. hash := sha3.NewLegacyKeccak256()
  2. hash.Write(publicKeyBytes[1:])
  3. fmt.Println(hexutil.Encode(hash.Sum(nil)[12:])) // 0x96216849c49358b10257cb55b28ea603c874b05e

Full code

wallet_generate.go

  1. package main
  2. import (
  3. "crypto/ecdsa"
  4. "fmt"
  5. "log"
  6. "github.com/ethereum/go-ethereum/common/hexutil"
  7. "github.com/ethereum/go-ethereum/crypto"
  8. "golang.org/x/crypto/sha3"
  9. )
  10. func main() {
  11. privateKey, err := crypto.GenerateKey()
  12. if err != nil {
  13. log.Fatal(err)
  14. }
  15. privateKeyBytes := crypto.FromECDSA(privateKey)
  16. fmt.Println(hexutil.Encode(privateKeyBytes)[2:]) // fad9c8855b740a0b7ed4c221dbad0f33a83a49cad6b3fe8d5817ac83d38b6a19
  17. publicKey := privateKey.Public()
  18. publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
  19. if !ok {
  20. log.Fatal("cannot assert type: publicKey is not of type *ecdsa.PublicKey")
  21. }
  22. publicKeyBytes := crypto.FromECDSAPub(publicKeyECDSA)
  23. fmt.Println(hexutil.Encode(publicKeyBytes)[4:]) // 9a7df67f79246283fdc93af76d4f8cdd62c4886e8cd870944e817dd0b97934fdd7719d0810951e03418205868a5c1b40b192451367f28e0088dd75e15de40c05
  24. address := crypto.PubkeyToAddress(*publicKeyECDSA).Hex()
  25. fmt.Println(address) // 0x96216849c49358B10257cb55b28eA603c874b05E
  26. hash := sha3.NewLegacyKeccak256()
  27. hash.Write(publicKeyBytes[1:])
  28. fmt.Println(hexutil.Encode(hash.Sum(nil)[12:])) // 0x96216849c49358b10257cb55b28ea603c874b05e
  29. }