Our website is made possible by displaying online advertisements to our visitors. Please consider supporting us by disabling your ad blocker.

Generate Cryptocurrency Private Keys And Public Addresses With Golang

TwitterFacebookRedditLinkedInHacker News

Over the past month or so I’ve demonstrated how to generate address information for a variety of cryptocurrency Altcoins using technologies like Node.js, Vue.js, and Angular. The thing about my previous tutorials are that they all used the JavaScript stack in some sense. What if we wanted to adventure into other technologies like Golang?

The process for generating key and address information for Bitcoin and popular Altcoins is pretty much the same. In reality, the difference is defined by the private key and public key prefix information, typically associated to a network.

We’re going to see how to generate and import private keys for a variety of cryptocurrency coins as well as their addresses using the Go programming language.

Getting the Go Project Dependencies

Rather than reinventing the wheel and developing our own cryptocurrency related algorithms, we’re going to leverage a very popular set of packages.

Assuming that Go is installed and the $GOPATH is configured, execute the following:

go get github.com/btcsuite/btcutil
go get github.com/btcsuite/btcd

We’ll be using the btcutil and btcd packages which are technically designed for Bitcoin, but we’re going to change some things to get most other Altcoins supported.

With the appropriate packages installed, we can proceed to figuring out the cryptocurrency network information.

Obtaining and Calculating Prefix Information for Private Keys and Public Keys

When it comes to all cryptocurrency coins, there are a diverse set of key prefixes. These prefixes are simply a byte that alters how the final key looks.

Take Bitcoin for example:

xpubkey: 0x00
xprivatekey: 0x80

Before we figure out what exactly the above values mean, you’ll probably be wondering how exactly I came up with them. The honest answer is that I dug around official coin source code repositories for every coin I was interested in.

If you look at the popular Bitcore repository, you’ll notice the following in the networks.js file:

{
    name: 'livenet',
    alias: 'mainnet',
    pubkeyhash: 0x00,
    privatekey: 0x80,
    scripthash: 0x05,
    xpubkey: 0x0488b21e,
    xprivkey: 0x0488ade4,
    networkMagic: 0xf9beb4d9,
    port: 8333,
    dnsSeeds: [
        'seed.bitcoin.sipa.be',
        'dnsseed.bluematt.me',
        'dnsseed.bitcoin.dashjr.org',
        'seed.bitcoinstats.com',
        'seed.bitnodes.io',
        'bitseed.xf2.org'
    ]
}

So yes, the Bitcore repository is a Node.js project, but it is only an example. You can easily find this information in other repositories that are not Node.js.

Now let’s look at an Altcoin. Let’s take a look at Reddcoin (RDD):

{
    name: 'livenet',
    magic: hex('fbc0b6db'),
    addressVersion: 61,
    privKeyVersion: 189,
    P2SHVersion: 5,
    hkeyPublicVersion: 0x0488b21e,
    hkeyPrivateVersion: 0x0488ade4,
    genesisBlock: {
        hash: hex('CCDEC174EBD4FA10314B3B9EF9CB8ADCF9AA87E57EC6AD0D0E3C3C5AD9E068B8'),
        merkle_root: hex('FF79AF16A9FFEB1B826DE1EA7F24539A2FE3702FE987912B09072BC41DBC02B5'),
        height: 0,
        nonce: 222583475,
        version: 1,
        prev_hash: buffertools.fill(new Buffer(32), 0),
        timestamp: 1390280400,
        bits: 504365040
    },
    dnsSeeds: [
        'seed.reddcoin.com'
    ],
    defaultClientPort: 45444,
    lastPoWBlock: 260799
}

The above block was taken from the Reddcore project’s networks.js file. As you can see the information is a bit different. We weren’t lucky enough to have the byte information drawn out for us.

Using the Bitcoin Wiki, we can learn about how prefixes are actually calculated. With that knowledge, we can find a decimal to hexadecimal calculator such as BinaryHex Converter, to take the addressVersion and privKeyVersion above and convert it into something we can use.

The Reddcoin values that we want are as follows:

xpubkey: 0x3d
xprivatekey: 0xbd

Depending on what you want to accomplish with this tutorial, do some digging around in official cryptocurrency coin repositories. With the prefix information, we’ll be able to accomplish quite a bit.

Developing the Application Logic for Generating Altcoin Keys

With the dependencies installed and the prefix information for our keys known, we can start developing an application. Create a main.go file somewhere in a new project start out with the following code:

package main

import (
	"encoding/hex"
	"errors"

	"github.com/btcsuite/btcd/btcec"
	"github.com/btcsuite/btcd/chaincfg"
	"github.com/btcsuite/btcutil"
)

type Network struct {
	name        string
	symbol      string
	xpubkey     byte
	xprivatekey byte
}

var network = map[string]Network{
	"rdd": {name: "reddcoin", symbol: "rdd", xpubkey: 0x3d, xprivatekey: 0xbd},
	"dgb": {name: "digibyte", symbol: "dgb", xpubkey: 0x1e, xprivatekey: 0x80},
	"btc": {name: "bitcoin", symbol: "btc", xpubkey: 0x00, xprivatekey: 0x80},
	"ltc": {name: "litecoin", symbol: "ltc", xpubkey: 0x30, xprivatekey: 0xb0},
}

func (network Network) GetNetworkParams() *chaincfg.Params { }

func (network Network) CreatePrivateKey() (*btcutil.WIF, error) { }

func (network Network) ImportPrivateKey(secretHex string) (*btcutil.WIF, error) { }

func (network Network) ImportWIF(wifStr string) (*btcutil.WIF, error) { }

func (network Network) GetAddress(wif *btcutil.WIF) (*btcutil.AddressPubKey, error) { }

func main() {
    fmt.Println("Starting the application...")
}

For now I’ve purposefully left the methods empty. We want to highlight that we’ve created a data structure called Network to hold our prefix information. We’re also defining our networks in a map so that we can easily use them.

Before we can use the network information to generate keys, we need to set it. Take the GetNetworkParams function for example:

func (network Network) GetNetworkParams() *chaincfg.Params {
	networkParams := &chaincfg.MainNetParams
	networkParams.PubKeyHashAddrID = network.xpubkey
	networkParams.PrivateKeyID = network.xprivatekey
	return networkParams
}

We can then use the GetNetworkParams function within any function that creates or imports keys. For example, if we wanted to create keys, we would use the following:

func (network Network) CreatePrivateKey() (*btcutil.WIF, error) {
	secret, err := btcec.NewPrivateKey(btcec.S256())
	if err != nil {
		return nil, err
	}
	return btcutil.NewWIF(secret, network.GetNetworkParams(), true)
}

The above code will generate a new private WIF key using the network parameters of the passed network. If we wanted to turn this around, we could accept a WIF key and validate it for a particular network:

func (network Network) ImportWIF(wifStr string) (*btcutil.WIF, error) {
	wif, err := btcutil.DecodeWIF(wifStr)
	if err != nil {
		return nil, err
	}
	if !wif.IsForNet(network.GetNetworkParams()) {
		return nil, errors.New("The WIF string is not valid for the `" + network.name + "` network")
	}
	return wif, nil
}

In the above code, we accept a WIF string. If the WIF string is malformed we’ll return an error. In this case, malformed does not mean incorrect for a particular network. After we confirm the WIF key is not malformed, we can validate that it is correct for a particular network and return it.

Since we’ve probably created or imported a WIF key by now, we should probably create a public address. After all, you don’t want to share your WIF key.

func (network Network) GetAddress(wif *btcutil.WIF) (*btcutil.AddressPubKey, error) {
	return btcutil.NewAddressPubKey(wif.PrivKey.PubKey().SerializeCompressed(), network.GetNetworkParams())
}

The above code will take a WIF key and use it along with the network information to create a public address. The WIF key and the public address are really all you need when it comes to creating a cryptocurrency wallet for Bitcoin or similar Altcoins.

To test our code, we can do the following:

func main() {
    fmt.Println("Starting the application...")
    wif, _ := network["btc"].CreatePrivateKey()
    address, _ := network["btc"].GetAddress(wif)
    fmt.Printf("%s - %s", wif.String(), address.EncodeAddress())
}

See how we made use of the map in the above code? We’re saying that we want to create a Bitcoin WIF key as well as a public address for Bitcoin and print out both. Had we wanted to use a different cryptocurrency, we would have defined it differently in our map variable.

Conclusion

You just saw how to generate WIF keys for Bitcoin and other cryptocurrency Altcoins. By determining the network information for the desired coin, you can generate, import, and validate quite a variety of coins.

Each of the btcutil and btcd packages do quite a bit when it comes to the blockchain, far beyond the examples used in this tutorial. If you need to create transactions, take a look at my tutorial titled, Create and Sign Bitcoin Transactions with Golang. If you’d like to create a fully functional hardware wallet with all the bells and whistles, check out my tutorial titled, Create a Bitcoin Hardware Wallet with Golang and a Raspberry Pi Zero.

A video version of this article can be seen below.

Nic Raboy

Nic Raboy

Nic Raboy is an advocate of modern web and mobile development technologies. He has experience in C#, JavaScript, Golang and a variety of frameworks such as Angular, NativeScript, and Unity. Nic writes about his development experiences related to making web and mobile development easier to understand.