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

Create A Bitcoin Hardware Wallet With Golang And A Raspberry Pi Zero

TwitterFacebookRedditLinkedInHacker News

Over the past month or so, in my free time, I’ve been working towards creating an affordable hardware wallet for various cryptocurrencies such as Bitcoin. Right now many cryptocurrency enthusiasts are using the Ledger Nano S hardware wallet, but those are very expensive and rarely in supply.

I own several Raspberry Pi Zero and thought it would be a perfect opportunity to take what I know about Raspberry Pi and Golang to create a wallet for a fraction of the price as the industry leading wallets.

We’re going to see how to create a hardware wallet, which I’m calling the Open Ledger Micro, using Golang and a Raspberry Pi Zero.

Disclaimer: I am a developer and Bitcoin enthusiast and not a cryptocurrency or cryptography expert. There may be bugs in my logic or code. I advise you to use this tutorial at your own risk. If you’ve found a bug or think you can improve on something, share it in the comments.

The Project Goals

Before we get into the code, we should probably figure out what we’re going to build. We are going to build an application with Go and Angular to be installed on a Raspberry Pi Zero. It will look something like the following animated image.

Open Ledger Micro

The point of hardware wallets is that they hold encrypted private keys, never expose the private keys, and operate in an offline environment. We are using a Raspberry Pi Zero because it has neither WiFi or Bluetooth which makes it much more difficult to compromise.

As seen in my previous tutorial, Connect to a Raspberry Pi Zero with a USB Cable and SSH, I demonstrated how to emulate ethernet over USB. We’re going to do this to serve a RESTful API over USB so that only the host computer can access the data. The Go application will serve an API and the Angular application will be a nice front-end. We’ll never need to connect to the Linux OS in this example.

Creating a RESTful API with Go and a Multiplexer

The Go side of things will be doing all of the heavy lifting. We’ll be managing keys, encrypting our data, signing transactions, and doing this entirely through a RESTful API.

Defining the Project Files and Downloading the Dependencies

We need to create a fresh Go project somewhere within our $GOPATH. I’m going to be referencing open-ledger-micro as a project within my $GOPATH.

The project should have the following structure:

main.go
transaction.go
wallet.go
coin.go

You could create tests, but we’re going to skip them in this particular tutorial. There are a few package dependencies that we need to obtain to make our lives just a little easier.

From the command line, execute the following:

go get github.com/GeertJohan/go.rice
go get github.com/gorilla/handlers
go get github.com/gorilla/mux
go get github.com/btcsuite/btcutil
go get github.com/btcsuite/btcd

The gorilla/handlers package isn’t absolutely necessary, but it will help when testing locally because we can manage cross-origin resource sharing (CORS) with it. You can read more about CORS and Golang in a previous article I wrote since it will not be emphasized here.

The GeertJohan/go.rice package will allow us to bundle our Angular application and the gorilla/mux package will act as our multiplexer for serving our API. The btcsuite/btcutil and btcsuite/btcd packages will be used for everything Bitcoin and cryptocurrencies.

Developing the Private Key and Public Key Logic for Bitcoin

A wallet is kind of useless if we cannot create and import private keys and manage public addresses. This is a good starting point for us in this tutorial.

Open the project’s coin.go file and include the following:

package main

import (
	"errors"

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

type Coin struct {
	Name                string `json:"name"`
	Symbol              string `json:"symbol"`
	WIF                 string `json:"wif,omitempty"`
	UncompressedAddress string `json:"uncompressed_address"`
	CompressedAddress   string `json:"compressed_address"`
}

type Network struct {
	name        string
	symbol      string
	xpubkey     byte
	xprivatekey byte
	magic       wire.BitcoinNet
}

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

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

func (coin *Coin) Generate(network Network) error { }

func (coin *Coin) Import(network Network) error { }

For any given coin, we know exactly what information is necessary. We’re going to want to know the name of the coin, the symbol, the private key in WIF format, and the public address in both compressed and uncompressed format so that way we don’t have to continuously generate it. This information is summed up in the Coin data structure.

We won’t be able to generate or even manage coins without knowing the network information behind the coin. For this reason, we need to maintain some network parameters as defined in the Network data structure. The xpubkey value is the byte prefix for public keys and the xprivatekey value is the byte prefix for private keys. The magic value is the information about the actual network.

With the Network data structure, we can define some coin types:

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

If you’re wondering where I obtained each of the values, I pulled them from each of the respective cryptocurrency projects on GitHub.

With the network information available, it needs to be set before it can be used. This is where the GetNetworkParams function comes into play:

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

Essentially, in the GetNetworkParams function, we’re taking the information from the MainNetParams, which is the Bitcoin network, and overriding it to a particular coin network.

Let’s say we want to generate a new set of keys given a particular coin network:

func (coin *Coin) Generate(network Network) error {
	if network == (Network{}) {
		return errors.New("Unsupported cryptocurrency symbol provided")
	}
	secret, err := btcec.NewPrivateKey(btcec.S256())
	if err != nil {
		return err
	}
	wif, err := btcutil.NewWIF(secret, network.GetNetworkParams(), false)
	if err != nil {
		return err
	}
	coin.WIF = wif.String()
	uncompressedAddress, err := btcutil.NewAddressPubKey(wif.PrivKey.PubKey().SerializeUncompressed(), network.GetNetworkParams())
	if err != nil {
		return err
	}
	compressedAddress, err := btcutil.NewAddressPubKey(wif.PrivKey.PubKey().SerializeCompressed(), network.GetNetworkParams())
	if err != nil {
		return err
	}
	coin.UncompressedAddress = uncompressedAddress.EncodeAddress()
	coin.CompressedAddress = compressedAddress.EncodeAddress()
	coin.Name = network.name
	coin.Symbol = network.symbol
	return nil
}

Given a particular network, we can generate a new private key and store it in wallet import format (WIF). Using the WIF key, we can generate uncompressed and compressed public addresses. We’re storing both because we don’t know what a user might want to use.

Importing keys works in a similar fashion to generating keys.

func (coin *Coin) Import(network Network) error {
	if network == (Network{}) {
		return errors.New("Unsupported cryptocurrency symbol provided")
	}
	wif, err := btcutil.DecodeWIF(coin.WIF)
	if err != nil {
		return err
	}
	if !wif.IsForNet(network.GetNetworkParams()) {
		return errors.New("The WIF string is not valid for the `" + network.name + "` network")
	}
	uncompressedAddress, err := btcutil.NewAddressPubKey(wif.PrivKey.PubKey().SerializeUncompressed(), network.GetNetworkParams())
	if err != nil {
		return err
	}
	compressedAddress, err := btcutil.NewAddressPubKey(wif.PrivKey.PubKey().SerializeCompressed(), network.GetNetworkParams())
	if err != nil {
		return err
	}
	coin.WIF = wif.String()
	coin.UncompressedAddress = uncompressedAddress.EncodeAddress()
	coin.CompressedAddress = compressedAddress.EncodeAddress()
	coin.Name = network.name
	coin.Symbol = network.symbol
	return nil
}

Given a WIF key, we try to decode it. If the key is formatted incorrectly, we’ll throw an error. If the key is correctly formatted, we need to validate it for a given network. If the key is valid for a particular network, generate the uncompressed and compressed public addresses.

Just like that we can generate wallet information for coins. If you’d like to see more information on generating cryptocurrency keys, check out my previous tutorial titled, Generate Cryptocurrency Private Keys and Public Addresses with Golang.

Encrypting and Decrypting Wallet Data with AES Ciphers

Now that we can create coins, we need to be able to store them in a wallet as ciphertext. If the Raspberry Pi Zero is stolen, we don’t want to make it easy for people to retrieve your private keys.

The plan is to have a wallet with any number of coins. When interacting with the wallet, it will be stored on disk and encrypted with a passphrase or retrieved from disk and decrypted with a passphrase. For simplicity, we’re going to manage our wallet in a key-value fashion where encrypted JSON exists in a file rather than trying to implement some other database like SQLite.

Open the project’s wallet.go file and include the following code:

package main

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/md5"
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"os"
)

type Wallet struct {
	Coins []Coin `json:"coins"`
}

func (wallet Wallet) CreateHash(key string) string { }

func (wallet Wallet) Encrypt(passphrase string) ([]byte, error) { }

func (wallet *Wallet) Decrypt(data []byte, passphrase string) error { }

func (wallet Wallet) EncryptFile(passphrase string) error { }

func (wallet *Wallet) DecryptFile(passphrase string) error { }

There is more to the wallet code, but our focus for now is on the encryption and decryption side of things.

Notice that we have a Wallet data structure that maintains a slice of the Coin data structure that we had created previously. We’re just planning to keep track of our coin data, nothing fancy.

When it comes to ciphers in Golang, we can’t just use any passphrase as our key. The key must be of a certain byte length which is more trouble than it is worth to try to match in a passphrase. Instead, we can actually hash a passphrase using an algorithm that produces a hash of the appropriate length.

Take the CreateHash function:

func (wallet Wallet) CreateHash(key string) string {
	hasher := md5.New()
	hasher.Write([]byte(key))
	return hex.EncodeToString(hasher.Sum(nil))
}

Using a simple MD5 hash, we can convert our passphrase to a string of hexadecimal content.

Given a passphrase and a wallet that contains data, we can call the Encrypt function:

func (wallet Wallet) Encrypt(passphrase string) ([]byte, error) {
	block, err := aes.NewCipher([]byte(wallet.CreateHash(passphrase)))
	if err != nil {
		return nil, err
	}
	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return nil, err
	}
	nonce := make([]byte, gcm.NonceSize())
	io.ReadFull(rand.Reader, nonce)
	data, err := json.Marshal(wallet)
	if err != nil {
		return nil, err
	}
	ciphertext := gcm.Seal(nonce, nonce, data, nil)
	return ciphertext, nil
}

In the encryption function, we are creating a new AES cipher from our hashed passphrase. An AES cipher requires a nonce to be used and since we have no intention of storing it as a separate entity on our disk, we’re going to store it prefixed to our ciphertext data. The decryption process requires the same nonce used in encryption, so as long as we know the size of the nonce, we can extract it from the prefix.

Take the Decrypt function now that we have a slice of bytes that represent our ciphertext data:

func (wallet *Wallet) Decrypt(data []byte, passphrase string) error {
	block, err := aes.NewCipher([]byte(wallet.CreateHash(passphrase)))
	if err != nil {
		return err
	}
	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return err
	}
	nonceSize := gcm.NonceSize()
	nonce, ciphertext := data[:nonceSize], data[nonceSize:]
	plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
	if err != nil {
		return err
	}
	json.Unmarshal(plaintext, wallet)
	return nil
}

Notice that the setup of our decryption function is nearly the same as our encryption function. The difference is that instead of creating a nonce and prefixing it, we’re separating the nonce and ciphertext, then decrypting it with our hashed passphrase.

Instead of returning the plaintext data, we’re marshaling it into our wallet so that it can be used seamlessly throughout the project.

With the core encryption and decryption functionality implemented, we need to take it a step further and take it to disk.

Take for instance, the EncryptFile function:

func (wallet Wallet) EncryptFile(passphrase string) error {
	file, err := os.Create("wallet.dat")
	if err != nil {
		return err
	}
	defer file.Close()
	data, err := wallet.Encrypt(passphrase)
	if err != nil {
		return err
	}
	_, err = file.Write(data)
	return err
}

When we wish to encrypt our wallet, we are creating and opening a file called wallet.dat that exists on the disk of the Raspberry Pi Zero. The standard Encrypt function is called and we take the slice of byte that is returned and we write it to the opened file. When we’re done, we close the file and move on with our day.

Similarly, we do the following with the DecryptFile function:

func (wallet *Wallet) DecryptFile(passphrase string) error {
	ciphertext, err := ioutil.ReadFile("wallet.dat")
	if err != nil {
		return err
	}
	err = wallet.Decrypt(ciphertext, passphrase)
	if err != nil {
		return err
	}
	return nil
}

In the above function, we are reading from a wallet.dat file on disk and storing the data in a slice of byte. Our Decrypt function takes a slice of byte which we can decrypt to plaintext using a passphrase. The plaintext is marshaled into the wallet for later use.

The basis to our secure wallet storage is now complete. If you’d like to learn more about encryption and decryption in Go, I suggest you check out a previous article I wrote titled, Encrypt and Decrypt Data in a Golang Application with the Crypto Packages.

Managing Wallet and Coin Information in a Secure Fashion

Now that we have coins and encryption to work with, we probably want some semi-attractive helper functions to use every time we wish to do something with a coin or a wallet.

Open the project’s wallet.go file if it isn’t already open and include the following below the encryption code that we had previously added:

func (wallet Wallet) Create(key string) error { }

func (wallet Wallet) Destroy() error { }

func (wallet Wallet) Import(coin Coin, passphrase string) error { }

func (wallet *Wallet) Dump(passphrase string) error { }

func (wallet *Wallet) GetAddresses(passphrase string) error { }

func (wallet *Wallet) Authenticate(passphrase string) bool { }

To start things off, we’re going to want to create a new wallet on disk. We may or may not have anything to save in it, but that is fine. In the Create function, see the following:

func (wallet Wallet) Create(key string) error {
	err := wallet.EncryptFile(key)
	return err
}

We’re taking a wallet, which may be empty, and are saving it to disk with a given passphrase. We can do something equally as simple when it comes to the Destroy function:

func (wallet Wallet) Destroy() error {
	err := os.Remove("wallet.dat")
	return err
}

The Create and Destroy functions, while useful, won’t be used aggressively like the others.

Before we try to create or retrieve wallet data, it might be a good idea to check to see if the password we’re using is valid. It is more important in a frontend, but can still be valuable elsewhere.

func (wallet *Wallet) Authenticate(passphrase string) bool {
	err := wallet.DecryptFile(passphrase)
	if err != nil {
		return false
	}
	return true
}

Essentially, we’re trying to decrypt a wallet. If there are errors in the decryption process, either the wallet doesn’t exist, or the passphrase is incorrect. We can use this function when trying to sign in via the Angular application.

Assuming that we’ve decrypted our wallet somewhere, we can try to import a coin into it:

func (wallet Wallet) Import(coin Coin, passphrase string) error {
	if coin == (Coin{}) {
		return errors.New("The coin must be valid")
	}
	wallet.Coins = append(wallet.Coins, coin)
	wallet.EncryptFile(passphrase)
	return nil
}

Remember the content we added to the coin.go file? It doesn’t matter if this is a coin we’re importing or a coin we’re creating, it will be imported into the decrypted wallet and then the wallet will be encrypted and saved on disk.

If we want to get the addresses that exist in our wallet, we can call the GetAddresses function:

func (wallet *Wallet) GetAddresses(passphrase string) error {
	err := wallet.DecryptFile(passphrase)
	if err != nil {
		return errors.New(`{ "message": "The password is not correct" }`)
	}
	for index, _ := range wallet.Coins {
		wallet.Coins[index].WIF = ""
	}
	return nil
}

What we’re doing is decrypting our wallet, but removing all the WIF information. We’re not encrypting so our WIF information is safe, but we’re not returning it. Remember, if you want a truly secure hardware wallet, the WIF information should never be exposed or leave the Raspberry Pi.

To go everything I just said, if you really want to retrieve everything, you could create a Dump function:

func (wallet *Wallet) Dump(passphrase string) error {
	err := wallet.DecryptFile(passphrase)
	if err != nil {
		return errors.New(`{ "message": "The password is not correct" }`)
	}
	return nil
}

Use the Dump function with care if it exists. You wouldn’t want your private keys to be exposed by accident.

Create and Sign Transactions for Broadcasting on the Blockchain

Now that we can interact with our wallet and coins safely, we should probably worry about creating and signing transactions that can later be broadcasted to the blockchain. While not lengthy, transactions are probably the most complicated part of this tutorial.

I’ve written about this subject previously in a tutorial titled, Create and Sign Bitcoin Transactions with Golang, which will contain more depth than what follows. I suggest you check it out for further information.

Here is what we plan to do when creating a transaction:

  1. Take a WIF string from our wallet and load it as a private key.
  2. Generate a public key script for both the sender and the recipient.
  3. Create an origin transaction for the sender which represents UTXO data being sent.
  4. Create a redeem transaction that includes information from the origin transaction.
  5. Sign the redeem transaction so funds can truly be transferred.
  6. Validate the signed transaction for errors before it can be broadcasted.

We do not plan to broadcast the transaction from our hardware wallet. If you wish to broadcast the transaction, copy and paste the signed output and take it to a blockchain explorer to broadcast. For example, BitPay has a popular explorer for Bitcoin.

Open the project’s transaction.go file and include the following:

package main

import (
	"bytes"
	"encoding/hex"

	"github.com/btcsuite/btcd/chaincfg/chainhash"
	"github.com/btcsuite/btcd/txscript"
	"github.com/btcsuite/btcd/wire"
	"github.com/btcsuite/btcutil"
)

type Transaction struct {
	TxId               string `json:"txid"`
	SourceAddress      string `json:"source_address"`
	DestinationAddress string `json:"destination_address"`
	Amount             int64  `json:"amount"`
	UnsignedTx         string `json:"unsignedtx"`
	SignedTx           string `json:"signedtx"`
}

func CreateTransaction(network Network, secret string, destination string, amount int64, txHash string) (Transaction, error) { }

Not only are we planning to create a transaction, but we need something to store it in. This is useful for returning data back to the client in a formatted fashion, but it could also be useful for storing on the disk in the future. What is truly important to us is the SignedTx property in the Transaction data structure.

So let’s examine the process of creating a transaction.

wif, err := btcutil.DecodeWIF(secret)
if err != nil {
    return Transaction{}, err
}
addresspubkey, err := btcutil.NewAddressPubKey(wif.PrivKey.PubKey().SerializeUncompressed(), network.GetNetworkParams())
if err != nil {
    return Transaction{}, err
}

Given a WIF string, we can attempt to decode it and use it as a private key. We’re also calculating the public key for the given private key.

Since we’re creating a raw transaction offline, we won’t have access to all the unspent transaction output (UTXO) information, so we’ll be creating our own UTXO.

sourceTx := wire.NewMsgTx(wire.TxVersion)
sourceUtxoHash, err := chainhash.NewHashFromStr(txHash)
if err != nil {
    return Transaction{}, err
}
sourceUtxo := wire.NewOutPoint(sourceUtxoHash, 0)
sourceTxIn := wire.NewTxIn(sourceUtxo, nil, nil)

Given a previous transaction on the blockchain, we are hashing it and using it as our sender input.

destinationAddress, err := btcutil.DecodeAddress(destination, network.GetNetworkParams())
sourceAddress, err := btcutil.DecodeAddress(addresspubkey.EncodeAddress(), network.GetNetworkParams())
if err != nil {
    return Transaction{}, err
}
destinationPkScript, err := txscript.PayToAddrScript(destinationAddress)
if err != nil {
    return Transaction{}, err
}
sourcePkScript, err := txscript.PayToAddrScript(sourceAddress)
if err != nil {
    return Transaction{}, err
}
sourceTxOut := wire.NewTxOut(amount, sourcePkScript)
sourceTx.AddTxIn(sourceTxIn)
sourceTx.AddTxOut(sourceTxOut)
sourceTxHash := sourceTx.TxHash()

Using the source and destination public addresses we can create transaction output. The output for our sender transaction will use the public key script for the sender as well as the amount in satoshi that are available from the previous transaction hash. With the previous transaction hash, the amount, and the sender’s public key script, we can finalize our sending transaction which will be used in the redeem transaction.

redeemTx := wire.NewMsgTx(wire.TxVersion)
destinationUtxo := wire.NewOutPoint(&sourceTxHash, 0)
redeemTxIn := wire.NewTxIn(destinationUtxo, nil, nil)
redeemTx.AddTxIn(redeemTxIn)
redeemTxOut := wire.NewTxOut(amount, destinationPkScript)
redeemTx.AddTxOut(redeemTxOut)

The redeem transaction will use the origin transaction hash for the input and the output will be the amount of the origin transaction that we wish to send to the recipient.

Once we have the properly formatted redeem transaction, we can sign it using the sender’s private key:

sigScript, err := txscript.SignatureScript(redeemTx, 0, sourceTx.TxOut[0].PkScript, txscript.SigHashAll, wif.PrivKey, false)
if err != nil {
    return Transaction{}, err
}
redeemTx.TxIn[0].SignatureScript = sigScript

The sigScript value will contain the signature as well as the public key of the sender.

Assuming everything went well up to now, you should have a signed transaction. We can validate it to take the guess work out of things:

flags := txscript.StandardVerifyFlags
vm, err := txscript.NewEngine(sourceTx.TxOut[0].PkScript, redeemTx, 0, flags, nil, nil, amount)
if err != nil {
    return Transaction{}, err
}
if err := vm.Execute(); err != nil {
    return Transaction{}, err
}

If validation of our redeem transaction is successful, we can store the results and return them.

var unsignedTx bytes.Buffer
var signedTx bytes.Buffer
sourceTx.Serialize(&unsignedTx)
redeemTx.Serialize(&signedTx)
transaction.TxId = sourceTxHash.String()
transaction.UnsignedTx = hex.EncodeToString(unsignedTx.Bytes())
transaction.Amount = amount
transaction.SignedTx = hex.EncodeToString(signedTx.Bytes())
transaction.SourceAddress = sourceAddress.EncodeAddress()
transaction.DestinationAddress = destinationAddress.EncodeAddress()
return transaction, nil

A few more things to note about the transaction logic that we’ve implemented. First, we’re using a single input. This means that the value being sent must exist in full for a particular UTXO rather than chaining several UTXO inputs. Second, we’ve not implemented a change address output, meaning we must send everything in our input, otherwise the remainder is used for the mining fee. Third, our input amount and output amount are the same, meaning zero allocation for fees. You may want to add some logic to use around 667 satoshi as the fee.

The full CreateTransaction function can be seen below:

func CreateTransaction(network Network, secret string, destination string, amount int64, txHash string) (Transaction, error) {
	var transaction Transaction
	wif, err := btcutil.DecodeWIF(secret)
	if err != nil {
		return Transaction{}, err
	}
	addresspubkey, err := btcutil.NewAddressPubKey(wif.PrivKey.PubKey().SerializeUncompressed(), network.GetNetworkParams())
	if err != nil {
		return Transaction{}, err
	}
	sourceTx := wire.NewMsgTx(wire.TxVersion)
	sourceUtxoHash, err := chainhash.NewHashFromStr(txHash)
	if err != nil {
		return Transaction{}, err
	}
	sourceUtxo := wire.NewOutPoint(sourceUtxoHash, 0)
	sourceTxIn := wire.NewTxIn(sourceUtxo, nil, nil)
	destinationAddress, err := btcutil.DecodeAddress(destination, network.GetNetworkParams())
	sourceAddress, err := btcutil.DecodeAddress(addresspubkey.EncodeAddress(), network.GetNetworkParams())
	if err != nil {
		return Transaction{}, err
	}
	destinationPkScript, err := txscript.PayToAddrScript(destinationAddress)
	if err != nil {
		return Transaction{}, err
	}
	sourcePkScript, err := txscript.PayToAddrScript(sourceAddress)
	if err != nil {
		return Transaction{}, err
	}
	sourceTxOut := wire.NewTxOut(amount, sourcePkScript)
	sourceTx.AddTxIn(sourceTxIn)
	sourceTx.AddTxOut(sourceTxOut)
	sourceTxHash := sourceTx.TxHash()
	redeemTx := wire.NewMsgTx(wire.TxVersion)
	destinationUtxo := wire.NewOutPoint(&sourceTxHash, 0)
	redeemTxIn := wire.NewTxIn(destinationUtxo, nil, nil)
	redeemTx.AddTxIn(redeemTxIn)
	redeemTxOut := wire.NewTxOut(amount, destinationPkScript)
	redeemTx.AddTxOut(redeemTxOut)
	sigScript, err := txscript.SignatureScript(redeemTx, 0, sourceTx.TxOut[0].PkScript, txscript.SigHashAll, wif.PrivKey, false)
	if err != nil {
		return Transaction{}, err
	}
	redeemTx.TxIn[0].SignatureScript = sigScript
	flags := txscript.StandardVerifyFlags
	vm, err := txscript.NewEngine(sourceTx.TxOut[0].PkScript, redeemTx, 0, flags, nil, nil, amount)
	if err != nil {
		return Transaction{}, err
	}
	if err := vm.Execute(); err != nil {
		return Transaction{}, err
	}
	var unsignedTx bytes.Buffer
	var signedTx bytes.Buffer
	sourceTx.Serialize(&unsignedTx)
	redeemTx.Serialize(&signedTx)
	transaction.TxId = sourceTxHash.String()
	transaction.UnsignedTx = hex.EncodeToString(unsignedTx.Bytes())
	transaction.Amount = amount
	transaction.SignedTx = hex.EncodeToString(signedTx.Bytes())
	transaction.SourceAddress = sourceAddress.EncodeAddress()
	transaction.DestinationAddress = destinationAddress.EncodeAddress()
	return transaction, nil
}

Like I said previously, if you wanted more depth to how transactions work in Golang, check out my previous article on the subject. Transactions are tricky and it is in my opinion the most difficult part about cryptocurrencies.

Developing and Serving API Endpoints with Gorilla Mux

We now have all of our driving Golang logic. We only have to see it in action now, through the serving of RESTful API endpoints. We can create an API using gorilla/mux that will call each of the wallet functions that we had created.

Open your project’s main.go file and include the following:

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"
	"strings"
	"time"

	rice "github.com/GeertJohan/go.rice"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
)

func CreateWalletEndpoint(response http.ResponseWriter, request *http.Request) { }

func DestroyWalletEndpoint(response http.ResponseWriter, request *http.Request) { }

func DumpWalletEndpoint(response http.ResponseWriter, request *http.Request) { }

func ImportCoinEndpoint(response http.ResponseWriter, request *http.Request) { }

func CreateCoinEndpoint(response http.ResponseWriter, request *http.Request) { }

func AuthenticateWalletEndpoint(response http.ResponseWriter, request *http.Request) { }

func GetAddressesEndpoint(response http.ResponseWriter, request *http.Request) { }

func ExportWalletEndpoint(response http.ResponseWriter, request *http.Request) { }

func CreateTransactionEndpoint(response http.ResponseWriter, request *http.Request) { }

func main() {
	fmt.Println("Starting the application...")
	router := mux.NewRouter()
	api := router.PathPrefix("/api").Subrouter()
	api.HandleFunc("/wallet", CreateWalletEndpoint).Methods("POST")
	api.HandleFunc("/wallet", DestroyWalletEndpoint).Methods("DELETE")
	api.HandleFunc("/wallet", DumpWalletEndpoint).Methods("GET")
	api.HandleFunc("/addresses", GetAddressesEndpoint).Methods("GET")
	api.HandleFunc("/authenticate", AuthenticateWalletEndpoint).Methods("POST")
	api.HandleFunc("/import-coin", ImportCoinEndpoint).Methods("POST")
	api.HandleFunc("/create-coin", CreateCoinEndpoint).Methods("POST")
	api.HandleFunc("/backup", ExportWalletEndpoint).Methods("GET")
	api.HandleFunc("/transaction", CreateTransactionEndpoint).Methods("POST")
	router.PathPrefix("/").Handler(http.FileServer(rice.MustFindBox("ui/dist").HTTPBox()))
	fmt.Println("Listening at :12345...")
	log.Fatal(http.ListenAndServe(":12345", handlers.CORS(handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type", "Authorization"}), handlers.AllowedMethods([]string{"GET", "POST", "PUT", "HEAD", "OPTIONS"}), handlers.AllowedOrigins([]string{"*"}))(router)))
}

We’re going to have quite a few API endpoints powering our application. Before we jump into the driving logic of each endpoint, let’s figure out how routing works.

api := router.PathPrefix("/api").Subrouter()
api.HandleFunc("/wallet", CreateWalletEndpoint).Methods("POST")
api.HandleFunc("/wallet", DestroyWalletEndpoint).Methods("DELETE")
api.HandleFunc("/wallet", DumpWalletEndpoint).Methods("GET")
api.HandleFunc("/addresses", GetAddressesEndpoint).Methods("GET")
api.HandleFunc("/authenticate", AuthenticateWalletEndpoint).Methods("POST")
api.HandleFunc("/import-coin", ImportCoinEndpoint).Methods("POST")
api.HandleFunc("/create-coin", CreateCoinEndpoint).Methods("POST")
api.HandleFunc("/backup", ExportWalletEndpoint).Methods("GET")
api.HandleFunc("/transaction", CreateTransactionEndpoint).Methods("POST")
router.PathPrefix("/").Handler(http.FileServer(rice.MustFindBox("ui/dist").HTTPBox()))

We find ourself in a tricky position. We are going to have an API and a front-end all from the same application. This means that we need to use a sub-router to distinguish what is API and what is front-end.

Our sub-router will be the API and all endpoints will be prefixed with /api. The root endpoint will be our Angular application. We’ll be bundling it into the binary later in the tutorial, so don’t think too hard on what our commands are doing yet. The Angular application will have its own routing.

We’ll go through the flow of events a typical user might do with our application.

To start, we don’t have a wallet yet. It needs to be created, so we would probably hit the /api/wallet endpoint with a POST request:

func CreateWalletEndpoint(response http.ResponseWriter, request *http.Request) {
	response.Header().Set("Content-Type", "application/json")
	queryParams := request.URL.Query()
	var wallet Wallet
	json.NewDecoder(request.Body).Decode(&wallet)
	if queryParams.Get("key") == "" {
		response.WriteHeader(500)
		response.Write([]byte(`{ "message": "A password is required to create a wallet" }`))
		return
	}
	wallet.Create(queryParams.Get("key"))
	json.NewEncoder(response).Encode(wallet)
}

When the CreateWalletEndpoint function is called, we set the response type and we check the request for query parameters. A key must exist in the query parameters and it will be used to create a wallet using the previous Create method. The empty wallet will be encoded and sent in the response.

If we ever needed to destroy our wallet, we could do something similar:

func DestroyWalletEndpoint(response http.ResponseWriter, request *http.Request) {
	response.Header().Set("Content-Type", "application/json")
	var wallet Wallet
	err := wallet.Destroy()
	if err != nil {
		response.WriteHeader(500)
		response.Write([]byte(`{ "message": "` + err.Error() + `" }`))
		return
	}
	json.NewEncoder(response).Encode(wallet)
}

In your final application you may want to make it more difficult to destroy a wallet, or not at all. Destructive operations should be done with care.

In theory we have an empty wallet, so it would make sense to create or import a coin. For now, let’s say we want to generate a new coin rather than add a private key.

func CreateCoinEndpoint(response http.ResponseWriter, request *http.Request) {
	response.Header().Set("Content-Type", "application/json")
	var coin Coin
	json.NewDecoder(request.Body).Decode(&coin)
	queryParams := request.URL.Query()
	if queryParams.Get("key") == "" {
		response.WriteHeader(500)
		response.Write([]byte(`{ "message": "A password is required to use a wallet" }`))
		return
	}
	coin.Generate(network[strings.ToLower(coin.Symbol)])
	var wallet Wallet
	err := wallet.DecryptFile(queryParams.Get("key"))
	if err != nil {
		response.WriteHeader(500)
		response.Write([]byte(`{ "message": "The password is not correct" }`))
		return
	}
	err = wallet.Import(coin, queryParams.Get("key"))
	if err != nil {
		response.WriteHeader(500)
		response.Write([]byte(`{ "message": "` + err.Error() + `" }`))
		return
	}
	json.NewEncoder(response).Encode(coin)
}

The request requires that a key exists as a query parameter and that symbol exists in the request body. The symbol property tells us what kind of key pair we want to generate based on our list of networks. After the coin is generated, the wallet is decrypted, the key is imported, and the coin is returned in the response. For safety reasons, you may want to remove the WIF from the coin before encoding and returning it back to the user.

The ImportCoinEndpoint function behaves similar to the CreateCoinEndpoint function:

func ImportCoinEndpoint(response http.ResponseWriter, request *http.Request) {
	response.Header().Set("Content-Type", "application/json")
	queryParams := request.URL.Query()
	var coin Coin
	json.NewDecoder(request.Body).Decode(&coin)
	err := coin.Import(network[strings.ToLower(coin.Symbol)])
	if err != nil {
		response.WriteHeader(500)
		response.Write([]byte(`{ "message": "` + err.Error() + `" }`))
		return
	}
	var wallet Wallet
	err = wallet.DecryptFile(queryParams.Get("key"))
	if err != nil {
		response.WriteHeader(500)
		response.Write([]byte(`{ "message": "The password is not correct" }`))
		return
	}
	err = wallet.Import(coin, queryParams.Get("key"))
	if err != nil {
		response.WriteHeader(500)
		response.Write([]byte(`{ "message": "` + err.Error() + `" }`))
		return
	}
	json.NewEncoder(response).Encode(coin)
}

Not only are we expecting a symbol to be in the request body, but we’re expecting a wif property to be in the request body. With that information, we can decrypt the wallet, import the coin, and return the coin as a response. Remember, you probably want to remove the WIF string from the response.

We had previously created an Authenticate function, so we need to finish things off with a simple endpoint:

func AuthenticateWalletEndpoint(response http.ResponseWriter, request *http.Request) {
	response.Header().Set("Content-Type", "application/json")
	queryParams := request.URL.Query()
	if queryParams.Get("key") == "" {
		response.WriteHeader(500)
		response.Write([]byte(`{ "message": "A password is required to decrypt a wallet" }`))
		return
	}
	var wallet Wallet
	authenticated := wallet.Authenticate(queryParams.Get("key"))
	response.Write([]byte(`{ "authenticated": ` + strconv.FormatBool(authenticated) + ` }`))
}

Given a key in the query parameters, we check to see if it is valid and return a boolean response back to the user. If you’d like, you can harden this endpoint to restrict attempts and prevent brute force password attacks.

Let’s say that as a user, we’ve signed into the wallet and wish to see all of our addresses. We would call the GetAddressesEndpoint like so:

func GetAddressesEndpoint(response http.ResponseWriter, request *http.Request) {
	response.Header().Set("Content-Type", "application/json")
	queryParams := request.URL.Query()
	if queryParams.Get("key") == "" {
		response.WriteHeader(500)
		response.Write([]byte(`{ "message": "A password is required to decrypt a wallet" }`))
		return
	}
	var wallet Wallet
	err := wallet.GetAddresses(queryParams.Get("key"))
	if err != nil {
		response.WriteHeader(500)
		response.Write([]byte(`{ "message": "` + err.Error() + `" }`))
		return
	}
	json.NewEncoder(response).Encode(wallet)
}

Just like with the previous endpoints, we are looking for certain query parameters in order to decrypt our wallet and respond with the coin data, excluding the sensitive WIF keys.

Since we have access to our wallet information, we can start creating transactions to send cryptocurrencies to other wallets.

func CreateTransactionEndpoint(response http.ResponseWriter, request *http.Request) {
	response.Header().Set("Content-Type", "application/json")
	var transaction Transaction
	json.NewDecoder(request.Body).Decode(&transaction)
	queryParams := request.URL.Query()
	if queryParams.Get("key") == "" {
		response.WriteHeader(500)
		response.Write([]byte(`{ "message": "A password is required to decrypt a wallet" }`))
		return
	}
	var wallet Wallet
	err := wallet.Dump(queryParams.Get("key"))
	if err != nil {
		response.WriteHeader(500)
		response.Write([]byte(`{ "message": "` + err.Error() + `" }`))
		return
	}
	for _, coin := range wallet.Coins {
		if coin.UncompressedAddress == transaction.SourceAddress || coin.CompressedAddress == transaction.SourceAddress {
			tx, err := CreateTransaction(network[strings.ToLower(coin.Symbol)], coin.WIF, transaction.DestinationAddress, transaction.Amount, transaction.TxId)
			if err != nil {
				response.WriteHeader(500)
				response.Write([]byte(`{ "message": "` + err.Error() + `" }`))
				return
			}
			json.NewEncoder(response).Encode(tx)
			return
		}
	}
	response.WriteHeader(500)
	response.Write([]byte(`{ "message": "The source address does not exist in the wallet" }`))
}

Creating transactions in an endpoint is just as complicated as the underlying function code that we previously saw. In a request, we’re expecting a key in the query parameters as well as a txid, source_address, destination_address, and amount in the request body. Once we’ve decrypted the wallet we can search for the source address and obtain the corresponding private key. Remember, we don’t want to expose the private key to the user after it has been added to our wallet.

After we’ve created and signed a transaction, we can return it in a response.

Our last two endpoints are optional and should be used with care. Let’s say we wanted to dump all of our wallet data in plain text, we could create a DumpWalletEndpoint like so:

func DumpWalletEndpoint(response http.ResponseWriter, request *http.Request) {
	response.Header().Set("Content-Type", "application/json")
	queryParams := request.URL.Query()
	if queryParams.Get("key") == "" {
		response.WriteHeader(500)
		response.Write([]byte(`{ "message": "A password is required to decrypt a wallet" }`))
		return
	}
	var wallet Wallet
	err := wallet.Dump(queryParams.Get("key"))
	if err != nil {
		response.WriteHeader(500)
		response.Write([]byte(`{ "message": "` + err.Error() + `" }`))
		return
	}
	json.NewEncoder(response).Encode(wallet)
}

If the key is correct, the entire wallet, including WIF keys are returned to the client. Probably good for debugging, but bad for production. Similarly, if we ever wish to backup our encrypted wallet, we might have an ExportWalletEndpoint like so:

func ExportWalletEndpoint(response http.ResponseWriter, request *http.Request) {
	response.Header().Set("Content-Disposition", "attachment; filename=wallet.dat")
	file, err := ioutil.ReadFile("wallet.dat")
	if err != nil {
		response.WriteHeader(500)
		response.Write([]byte(err.Error()))
		return
	}
	response.Write(file)
}

The above endpoint behaves a bit different. Instead of returning JSON data, we are returning binary data that will be downloaded. The wallet will be encrypted, however, if someone has your encrypted wallet file, they could potentially break into it with the correct skills and willpower.

Technically, if you wanted to, you could test this Golang application on your host machine. Just comment the line that refers to the Angular front-end and execute:

go run *.go

Use cURL or a tool like Postman to hit each of the endpoints. If you want to learn more about creating RESTful APIs with Golang, check out my previous tutorial titled, Create a Simple RESTful API with Golang.

Creating an Attractive Front-End with Angular, TypeScript, and Bootstrap

While we could deploy the Go application to our Raspberry Pi and have a fully functional hardware wallet, it isn’t the most attractive of solutions. Instead it would be a good idea to create a nice front-end application to interact with the RESTful API that we had just created. It doesn’t really matter what JavaScript technology we use, but for this example we’ll be using Angular.

Create a New Angular Project with the Dependencies

To be successful with the Angular development, you’ll need the Angular CLI. With the Angular CLI installed, we can create a new project by executing the following command:

ng new ui

Make sure that the above command is executed within your Go project. As part of the build process we’ll be bundling it with Go.

If you plan to have an export or download feature within your hardware wallet, you’ll need an extra dependency that will allow us to save binary data as a file in Angular. Within the Angular project, using the CLI, execute:

npm install file-saver --save

If you don’t plan to download the wallet from the Raspberry Pi, you won’t need the dependency. To make our application attractive, we are going to be using Bootstrap and jQuery.

Download Bootstrap and extract the content to the project’s src/assets directory. Download jQuery and place the minified JavaScript file in the project’s src/assets/js directory. We’ll also need Font Awesome for a few icons. Download Font Awesome and place the font and CSS data in the project’s src/assets directory.

The downloaded files will need to be imported into our project’s src/index.html file. This file should look like the following:

<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>Open Ledger Micro</title>
        <base href="/">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="icon" type="image/x-icon" href="favicon.ico">
        <link href="/assets/css/fontawesome-all.min.css" rel="stylesheet">
        <link rel="stylesheet" href="/assets/css/bootstrap.min.css">
        <link rel="stylesheet" href="/assets/css/bootstrap-select.min.css">
    </head>
    <body>
        <app-root></app-root>
        <script src="/assets/js/jquery-3.2.1.min.js"></script>
        <script>
            window.jQuery = window.$ = require("jquery");
        </script>
        <script src="/assets/js/bootstrap.min.js"></script>
    </body>
</html>

We should be able to start development of our Angular project now that all of the CSS and JavaScript dependencies are in place.

Developing an Injectable Wallet Service for HTTP Integration

Most of our application will be driven by an Angular service. This allows us to consolidate our code in a singleton type instance to be used within every component.

Using the Angular CLI, execute the following command:

ng g service wallet

The above command will create a src/app/wallet.service.ts file in our project. Open it and include the following:

import { Injectable, EventEmitter } from '@angular/core';
import { Headers, RequestOptions, Http } from "@angular/http";
import { Router } from "@angular/router";
import { Observable } from "rxjs/Observable";
import "rxjs/add/operator/map";
import "rxjs/add/operator/do";

@Injectable()
export class WalletService {

    private host: string;
    private authenticated: EventEmitter<boolean> = new EventEmitter();
    public password: string;

    public constructor(private http: Http, private router: Router) {
        this.host = "http://localhost:12345/api";
        //this.host = "http://coin.local:12345/api";
        this.password = "";
    }

    public isAuthenticated() { }

    public authenticate(password: string) { }

    public disconnect() { }

    public create(password: string) { }

    public dump() { }

    public getAddresses() { }

    public import(symbol: string, secret: string) { }

    public backup() { }

    public createTransaction(sender: string, recipient: string, amount: number, txHash: string) { }

}

Before we jump into each of the functions, notice our constructor method. We’re injecting the Http service and the Router service because our WalletService will handle HTTP requests and navigation. Also notice the hosts that were initialized. If we wish to test on our host machine we’ll be using localhost, otherwise we’ll be using the hostname of our Raspberry Pi.

If we wish to authenticate, we will call the authenticate method:

public authenticate(password: string) {
	let headers = new Headers({ "content-type": "application/json" });
	let options = new RequestOptions({ headers: headers });
	return this.http.post(this.host + "/authenticate?key=" + password, null, options)
		.map(result => result.json())
		.do(result => {
			if(!result.authenticated) {
				throw new Error("The wallet does not exist or the password is incorrect");
			} else {
				this.password = password;
				this.authenticated.emit(true);
			}
		});
}

After a request is made to our Golang API, we make use of an EventEmitter for changing the authenticated status. Each of the pages will listen to this emitter and respond appropriately as things change.

We can subscribe to the emitter by calling the isAuthenticated function:

public isAuthenticated() {
    return this.authenticated;
}

If we ever need to sign out, we would call disconnect which contains the following:

public disconnect() {
	this.password = "";
	this.authenticated.emit(false);
}

Notice that the password is blanked out and the status is emitted to anything that is listening. This will eventually trigger us navigating back to the unlock screen.

When we want to create a wallet, we’ll call the create function:

public create(password: string) {
	let headers = new Headers({ "content-type": "application/json" });
	let options = new RequestOptions({ headers: headers });
	return this.http.post(this.host + "/wallet?key=" + password, null, options)
		.map(result => result.json());
}

The create function will issue a POST request where the results can be subscribed to. This request along with all other requests will contain a passphrase that is saved in our service after authenticating.

When we need to create or import a coin, we execute the import function:

public import(symbol: string, secret: string) {
	let headers = new Headers({ "content-type": "application/json" });
	let options = new RequestOptions({ headers: headers });
	if(secret != "") {
		return this.http.post(this.host + "/import-coin?key=" + this.password, JSON.stringify({ "symbol": symbol, "wif": secret }), options)
			.map(result => result.json());
	} else {
		return this.http.post(this.host + "/create-coin?key=" + this.password, JSON.stringify({ "symbol": symbol }), options)
			.map(result => result.json());
	}
}

If a WIF key is provided, we’ll send an HTTP request to our /api/import-coin endpoint, otherwise we’ll make a request to our /api/create-coin endpoint. At a minimum we require the symbol so we know which type of coin we’re working with.

When we have coins in our wallet, we’ll probably want to be able to view our public addresses. We can do that with the getAddresses function:

public getAddresses() {
	return this.http.get(this.host + "/addresses?key=" + this.password)
		.map(result => result.json());
}

Getting the idea yet? We’re just issuing a bunch of HTTP requests from Angular to our Go application.

Next if we wanted to create and sign a transaction, we could call the createTransaction function:

public createTransaction(sender: string, recipient: string, amount: number, txHash: string) {
	let headers = new Headers({ "content-type": "application/json" });
	let options = new RequestOptions({ headers: headers });
	return this.http.post(this.host + "/transaction?key=" + this.password, JSON.stringify({ "source_address": sender, "destination_address": recipient, "amount": amount, "txid": txHash }), options)
		.map(result => result.json());
}

Finally, and these are optional, if we wanted to dump or download our wallet data we could create two more functions. If we wish to backup our wallet, in other words download the encrypted file, we could create a function called backup like this:

public backup() {
	return this.http.get(this.host + "/backup")
		.map(result => result.text());
}

If we wanted to dump the wallet data in plaintext, we could create a dump function like this:

public dump() {
	return this.http.get(this.host + "/wallet?key=" + this.password)
		.map(result => result.json());
}

Again, these service functions are nothing more than a simple way to access our RESTful API that we had created with Go.

With the service out of the way and all the heavy lifting done, we can start working on each of our application pages.

Create and Navigate Between Components in the Application

Our Angular application will have five different screens, each responsible for doing something different. For example each of the screens will call something from our service.

Before we create these components, let’s add our CSS. Open the project’s src/styles.css and include the following:

body {
    padding-top: 50px;
    margin-bottom: 0px;
    background-color: #293036;
}

.navbar-default {
    background-color: #21262b;
    border-color: #FF8C00;
}

.navbar-default .navbar-brand {
    color: #FFFFFF;
}

.navbar-default .navbar-brand:focus, .navbar-default .navbar-brand:hover {
    color: #FFFFFF;
}

.navbar-default .navbar-nav>li>a {
    color: #FFFFFF;
}

.navbar-default .navbar-nav>li>a:focus, .navbar-default .navbar-nav>li>a:hover {
    color: #FFFFFF;
}

.navbar-fixed-top {
    border-width: 0 0 2px;
}

.form-group-lg .form-control {
    border-radius: 0px;
}

.list-group-item.active, .list-group-item.active:focus, .list-group-item.active:hover {
    background-color: #FF8C00;
}

.panel {
    border-radius: 0px;
    border: none;
}

thead {
    background-color: #FD8B25;
}

.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child, .panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child, .panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child, .panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child, .panel>.table:first-child>tbody:first-child>tr:first-child td:first-child, .panel>.table:first-child>tbody:first-child>tr:first-child th:first-child, .panel>.table:first-child>thead:first-child>tr:first-child td:first-child, .panel>.table:first-child>thead:first-child>tr:first-child th:first-child {
    border-radius: 0px;
}

.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child, .panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child, .panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child, .panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child, .panel>.table:first-child>tbody:first-child>tr:first-child td:last-child, .panel>.table:first-child>tbody:first-child>tr:first-child th:last-child, .panel>.table:first-child>thead:first-child>tr:first-child td:last-child, .panel>.table:first-child>thead:first-child>tr:first-child th:last-child {
    border-radius: 0px;
}

.alert-success {
    word-wrap: break-word;
    text-align: left;
    border-radius: 0px;
}

#footer {
    bottom: 0;
    width: 100%;
    position: absolute;
    height: 40px;
    background-color: #21262b;
    color: #FFFFFF;
}

.footer-block {
    margin: 10px 0;
}

#footer a {
    color: #FFFFFF;
}

#vertical-layout {
    display: flex;
    min-height: 100%;
    min-height: 100vh;
    align-items: center;
    margin-top: -50px;
}

#content-box {
    background-color: #FFFFFF;
    border: 1px solid #21262b;
    padding: 30px;
    margin-top: 10px;
}

#content-box-title {
    padding: 0;
    margin: 0;
}

.no-radius {
    border-radius: 0px;
}

.red-strong {
    color: red;
    font-weight: bold;
}

The application will also be powered with Bootstrap, but the above CSS offers some other attractive optimizations.

Let’s create the components in the order that we’d be using them, starting with the unlock screen. From the Angular CLI, execute the following:

ng g component unlock

Open the project’s src/app/unlock/unlock.component.ts file and include the following TypeScript logic:

import { Component, OnInit } from '@angular/core';
import { Router } from "@angular/router";
import { WalletService } from "../wallet.service";

@Component({
    selector: 'app-unlock',
    templateUrl: './unlock.component.html',
    styleUrls: ['./unlock.component.css']
})
export class UnlockComponent implements OnInit {

    public input: any;

    public constructor(private router: Router, private wallet: WalletService) {
        this.input = {
            password: ""
        };
    }

    public ngOnInit() { }

    public unlock() {
        if(this.input.password == "") {
            return console.log("A password is required");
        }
        this.wallet.authenticate(this.input.password)
            .subscribe(result => {
                this.router.navigate(["/dashboard"]);
            }, error => {
                console.error(error.message);
            });
    }

}

Notice that we’re injecting the WalletService and Router in the constructor method. We’ll be using both in the unlock method. When the user chooses to unlock the application, we check to see if the password exists and we try to authenticate. If authentication is successful, we navigate to the dashboard.

The HTML that pairs with the UnlockComponent TypeScript is found in the project’s src/app/unlock/unlock.component.html and it looks like the following:

<div id="vertical-layout">
    <div class="container">
        <div class="row">
            <div class="col-md-12">
                <div id="content-box">
                    <div class="text-center form-group"><h1 id="content-box-title">Welcome to Open Ledger Micro</h1></div>
                    <hr />
                    <div class="text-center form-group">
                        <p>
                            Your wallet and all your private keys and secret keys are encrypted.
                            To continue, it must be unlocked.
                        </p>
                    </div>
                    <form>
                        <div class="form-group form-group-lg">
                            <input type="password" [(ngModel)]="input.password" class="form-control" name="password" placeholder="Password">
                        </div>
                        <div class="text-center form-group">
                            <button type="button" (click)="unlock()" class="btn btn-default btn-lg no-radius">Unlock</button>
                        </div>
                        <div class="text-center">
                            <p>Don't have a wallet yet? <a [routerLink]="['/generate']">Create a new wallet</a>.</p>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>

Most of the above HTML is related to Bootstrap. What we care about is the form that is bound to our input variable and the button that calls the unlock method.

If we don’t yet have a wallet, we’re going to need to create one. This brings us to the next component.

From the Angular CLI, execute the following command:

ng g component generate

Of the freshly created files, open the project’s src/app/generate/generate.component.ts file and include the following:

import { Component, OnInit } from '@angular/core';
import { Headers, RequestOptions, Http } from "@angular/http";
import { Router } from "@angular/router";
import { WalletService } from "../wallet.service";

@Component({
    selector: 'app-generate',
    templateUrl: './generate.component.html',
    styleUrls: ['./generate.component.css']
})
export class GenerateComponent implements OnInit {

    public input: any;

    public constructor(private http: Http, private router: Router, private wallet: WalletService) {
        this.input = {
            "password": "",
            "confirmPassword": ""
        };
    }

    public ngOnInit() { }

    public create() {
        if(this.input.password != "" && this.input.confirmPassword != "") {
            if(this.input.password == this.input.confirmPassword) {
                this.wallet.create(this.input.password)
                    .subscribe(result => {
                        this.router.navigate(["/unlock"]);
                    });
            } else {
                console.error("The passwords to not match");
            }
        } else {
            console.error("All fields must be filled");
        }
    }

}

The behavior of the GenerateComponent class is similar to the UnlockComponent class. We’re looking at the passwords entered by the user and creating a wallet using our service. If successful, we’ll navigate back to the unlock screen.

The HTML that pairs to this TypeScript is found in the src/app/generate/generate.component.html file and it looks like the following:

<div id="vertical-layout">
    <div class="container">
        <div class="row">
            <div class="col-md-12">
                <div id="content-box">
                    <div class="text-center form-group"><h1 id="content-box-title">Create a Wallet</h1></div>
                    <hr />
                    <div class="text-center form-group">
                        <p>
                            Your wallet contains all your secret keys and private keys. It will be encrypted, but should
                            be stored in a safe place. Losing your wallet will result in your coins being lost forever.
                        </p>
                        <p class="red-strong">Backup your wallet and store it in a safe place.</p>
                    </div>
                    <form>
                        <div class="form-group form-group-lg">
                            <input type="password" [(ngModel)]="input.password" class="form-control" name="password" placeholder="Password">
                        </div>
                        <div class="form-group form-group-lg">
                            <input type="password" [(ngModel)]="input.confirmPassword" class="form-control" name="confirmPassword" placeholder="Confirm Password">
                        </div>
                        <div class="text-center form-group">
                            <button type="button" (click)="create()" class="btn btn-default btn-lg no-radius">Create</button>
                        </div>
                        <div class="text-center">
                            <p>Already have a wallet? <a [routerLink]="['/unlock']">Unlock existing wallet</a>.</p>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>

Again, we’ve not introduced anything different than the previous unlock screen. Essentially a different screen that does more or less the same thing.

Assuming we’ve unlocked our application, we’re brought to the dashboard which displays our addresses. From the Angular CLI, execute the following command:

ng g component dashboard

After you’ve executed the above command, open the project’s src/app/dashboard/dashboard.component.ts file and include the following:

import { Component, OnInit } from '@angular/core';
import { Http } from "@angular/http";
import { Observable } from "rxjs/Observable";
import { WalletService } from "../wallet.service";

@Component({
    selector: 'app-dashboard',
    templateUrl: './dashboard.component.html',
    styleUrls: ['./dashboard.component.css']
})
export class DashboardComponent implements OnInit {

    public coins: any;

    public constructor(private http: Http, private wallet: WalletService) {
        this.coins = [];
    }

    public ngOnInit() {
        this.wallet.getAddresses()
            .subscribe(result => {
                this.coins = result.coins;
            }, error => {
                console.error(error);
            });
    }

}

When the page initializes, the ngOnInit method is called which makes a request to get our addresses. The result is added to our public array to be rendered onto the screen. Not much in terms of logic is happening here.

Now open the project’s src/app/dashboard/dashboard.component.html file and include the following:

<div class="container" style="margin-top: 30px">
    <div class="row" style="margin-bottom: 30px">
        <div class="col-md-12">
            <button [routerLink]="['/import']" class="btn btn-primary no-radius">Import</button>
        </div>
    </div>
    <div class="row">
        <div class="col-md-12">
            <div class="panel panel-default">
                <table class="table table-striped">
                    <thead>
                        <tr>
                            <th>Coin</th>
                            <th>Symbol</th>
                            <th>Uncompressed Address</th>
                            <th>Compressed Address</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr *ngFor="let coin of coins">
                            <td>{{ coin.name }}</td>
                            <td>{{ coin.symbol }}</td>
                            <td><a [routerLink]="['/transaction', coin.uncompressed_address]">{{ coin.uncompressed_address }}</a></td>
                            <td><a [routerLink]="['/transaction', coin.compressed_address]">{{ coin.compressed_address }}</a></td>
                        </tr>
                    </tbody>
                </table>
            </div>
        </div>
    </div>
</div>

In the core of our HTML, we have a loop that loops through our public array of coins. Each coin is rendered on the screen. Remember, we are not receiving any sensitive data nor are we trying to display it on the screen. If the import button is pressed we are navigated to the screen for importing coins and if either of the addresses is clicked, we are navigated to the transaction screen. Neither of those components are created yet, but we’ll get there.

Since we don’t have any coins yet, we need to create our component for importing coins. From the Angular CLI, execute the following:

ng g component import

Of the new files, open the project’s src/app/import/import.component.ts TypeScript file and include the following:

import { Component, OnInit } from '@angular/core';
import { Router } from "@angular/router";
import { WalletService } from "../wallet.service";

@Component({
    selector: 'app-import',
    templateUrl: './import.component.html',
    styleUrls: ['./import.component.css']
})
export class ImportComponent implements OnInit {


    public input: any;
    public types: Array<any>;

    public constructor(private router: Router, private wallet: WalletService) {
        this.input = {
            "type": "",
            "secret": ""
        }
    }

    public ngOnInit() { }

    public import() {
        if(this.input.type != "") {
            this.wallet.import(this.input.type, this.input.secret)
                .subscribe(result => {
                    this.router.navigate(["/dashboard"]);
                }, error => {
                    console.error(error);
                });
        }
    }

}

After the user has entered a symbol, a WIF string, or both, we’ll call the function for creating or importing coins into our wallet file. After the coin has been added, we navigate back to the dashboard.

The HTML found in src/app/import/import.component.html will look like the following:

<div id="vertical-layout">
    <div class="container">
        <div class="row">
            <div class="col-md-12">
                <div id="content-box">
                    <div class="text-center form-group"><h1 id="content-box-title">Import Coins</h1></div>
                    <hr />
                    <div class="text-center form-group">
                        <p>
                            Import private and secret keys into your encrypted wallet.
                        </p>
                    </div>
                    <form>
                        <div class="form-group form-group-lg">
                            <input type="text" [(ngModel)]="input.type" class="form-control" name="symbol" placeholder="Symbol (BTC, LTC, RDD, etc.)">
                        </div>
                        <div class="form-group form-group-lg">
                            <input type="text" [(ngModel)]="input.secret" class="form-control" name="secret" placeholder="Private Key (Leave Blank for New)">
                        </div>
                        <div class="text-center form-group">
                            <button type="button" (click)="import()" class="btn btn-default btn-lg no-radius">Import</button>
                        </div>
                        <div class="text-center">
                            <p><a [routerLink]="['/dashboard']">Cancel</a></p>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>

Just to reiterate, most of the HTML is related to Bootstrap. All we really care about is the form bindings to our TypeScript and the functions that exist on our button. Remember, our frontend isn’t doing anything beyond collecting and displaying data. The Go application does all the heavy lifting.

This brings us to our final component. We want to be able to create and sign transactions to be broadcasted on the blockchain network.

From the Angular CLI, execute the following:

ng g component transaction

Open the project’s src/app/transaction/transaction.component.ts file and include the following TypeScript logic:

import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from "@angular/router";
import { WalletService } from "../wallet.service";

@Component({
    selector: 'app-transaction',
    templateUrl: './transaction.component.html',
    styleUrls: ['./transaction.component.css']
})
export class TransactionComponent implements OnInit {

    public input: any;
    public signedTx: string;

    public constructor(private router: Router, private route: ActivatedRoute, private wallet: WalletService) {
        this.input = {
            "sourceAddress": "",
            "destinationAddress": "",
            "amount": "1000",
            "txId": ""
        }
        this.signedTx = "";
    }

    public ngOnInit() {
        this.route.params.subscribe(params => {
            this.input.sourceAddress = params["source"];
        });
    }

    public create() {
        if(this.input.sourceAddress != "" && this.input.destinationAddress != "" && this.input.txId != "" && this.input.amount != "") {
            this.wallet.createTransaction(this.input.sourceAddress, this.input.destinationAddress, parseInt(this.input.amount), this.input.txId)
                .subscribe(result => {
                    this.signedTx = result.signedtx;
                });
        } else {
            console.log("All fields are required");
        }
    }

}

Notice the ngOnInit method in the above code. We’re subscribing to the route parameters because we need to obtain the address selected on the previous screen. It is convenient and prevents our user from having to fill all form fields.

If all the fields are filled and the user executes the create function, the data is sent to our API and the transaction is returned.

The HTML for this component can be found in the src/app/transaction/transaction.component.html file and it contains:

<div id="vertical-layout">
    <div class="container">
        <div class="row">
            <div class="col-md-12">
                <div id="content-box">
                    <div class="text-center form-group"><h1 id="content-box-title">Create Transaction</h1></div>
                    <hr />
                    <div class="text-center form-group">
                        <p>
                            Create a signed transaction to be broadcasted on a blockchain network.
                        </p>
                    </div>
                    <form>
                        <div class="form-group form-group-lg">
                            <input type="text" [(ngModel)]="input.txId" class="form-control" name="txId" placeholder="Previous Transaction Hash">
                        </div>
                        <div class="form-group form-group-lg">
                            <input type="text" [(ngModel)]="input.sourceAddress" class="form-control" name="sourceAddress" placeholder="Sender Address">
                        </div>
                        <div class="form-group form-group-lg">
                            <input type="text" [(ngModel)]="input.destinationAddress" class="form-control" name="destinationAddress" placeholder="Recipient Address">
                        </div>
                        <div class="form-group form-group-lg">
                            <input type="text" [(ngModel)]="input.amount" class="form-control" name="amount" placeholder="Amount (Satoshi)">
                        </div>
                        <div class="text-center form-group">
                            <div *ngIf="signedTx != ''" class="alert alert-success" role="alert">
                                {{ signedTx }}
                            </div>
                        </div>
                        <div class="text-center form-group">
                            <button type="button" (click)="create()" class="btn btn-default btn-lg no-radius">Create</button>
                        </div>
                        <div class="text-center">
                            <p><a [routerLink]="['/dashboard']">Cancel</a></p>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>

Most of the above HTML is related to the form and the field bindings to the TypeScript. However, there is the following statement:

<div class="text-center form-group">
	<div *ngIf="signedTx != ''" class="alert alert-success" role="alert">
		{{ signedTx }}
	</div>
</div>

The signed transaction will not display until it exists. It is just some fancy UX stuff, but it makes a difference in the attractiveness of our application.

While all of our components are done, we aren’t entirely finished. We have a parent component that controls things at a higher level.

Open the project’s src/app/app.component.ts file and include the following TypeScript:

import { Component, OnInit } from '@angular/core';
import { Router } from "@angular/router";
import { saveAs } from "file-saver/FileSaver";
import { WalletService } from "./wallet.service";

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {

    public authenticated: boolean;

    public constructor(private router: Router, private wallet: WalletService) {
        this.authenticated = false;
    }

    public ngOnInit() {
        if(!this.authenticated) {
            this.router.navigate(["/unlock"], { replaceUrl: true });
        }
        this.wallet.isAuthenticated().subscribe(authenticated => {
            this.authenticated = authenticated;
            if(!authenticated) {
                this.router.navigate(["/unlock"], { replaceUrl: true });
            }
        });
    }

    public backup() {
        this.wallet.backup()
            .subscribe(result => {
                var blob = new Blob([result], { type: "text/plain" });
                saveAs(blob, "wallet.dat")
            })
    }

    public disconnect() {
        this.wallet.disconnect();
        this.router.navigate(["/unlock"]);
    }

}

Remember our authentication logic? We never actually set it up to secure the other components we created. That is because our parent component will take care of it for us.

In the ngOnInit method we check to see if we’re authenticated and if we’re not, navigate to the unlock screen. We’re also subscribing to the emitter from our service and if we are no longer authenticated navigate as well. In the parent component we have a few buttons that will allow us to backup our wallet or disconnect fro our session.

The HTML for this component is found in the src/app/app.component.html file and it looks like the following:

<nav class="navbar navbar-default navbar-fixed-top">
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand">Open Ledger Micro</a>
        </div>
        <div *ngIf="authenticated" id="navbar" class="navbar-collapse collapse">
            <ul class="nav navbar-nav navbar-right">
                <li><a [routerLink]="['/dashboard']"><i class="fas fa-tachometer-alt fa-lg"></i></a></li>
                <li><a style="cursor: pointer" (click)="backup()"><i class="fas fa-clone fa-lg"></i></a></li>
                <li><a style="cursor:pointer" (click)="disconnect()"><i class="fas fa-sign-out-alt fa-lg"></i></a></li>
            </ul>
        </div>
    </div>
</nav>
<router-outlet></router-outlet>
<div id="footer">
    <div class="container">
        <p class="footer-block">2018 &copy; <a href="https://www.thepolyglotdeveloper.com">The Polyglot Developer</a></p>
    </div>
</div>

Most of the above is related to Bootstrap and making our application look nice. However, notice the following:

<router-outlet></router-outlet>

The outlet allows our other components to pass through it. This means the parent component has HTML for both the parent component and the child components.

We have one more thing to do when it comes to Angular. We need to define our routes and import any remaining Angular dependencies.

Configure the Angular Module with Angular Dependencies

There are many ways to set up routing, but the easiest is just to add everything to our project’s src/app/app.module.ts file. Open it and include the following TypeScript code:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { RouterModule } from "@angular/router";
import { FormsModule } from "@angular/forms";
import { HttpModule } from "@angular/http";

import { AppComponent } from './app.component';
import { UnlockComponent } from './unlock/unlock.component';
import { DashboardComponent } from './dashboard/dashboard.component';
import { GenerateComponent } from './generate/generate.component';
import { WalletService } from "./wallet.service";
import { ImportComponent } from './import/import.component';
import { TransactionComponent } from './transaction/transaction.component';

const routes = [
    { path: "", redirectTo: "/unlock", pathMatch: "full" },
    { path: "unlock", component: UnlockComponent },
    { path: "generate", component: GenerateComponent },
    { path: "dashboard", component: DashboardComponent },
    { path: "import", component: ImportComponent },
    { path: "transaction/:source", component: TransactionComponent },
];

@NgModule({
  declarations: [
    AppComponent,
    UnlockComponent,
    DashboardComponent,
    GenerateComponent,
    ImportComponent,
    TransactionComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    RouterModule,
    RouterModule.forRoot(routes, { useHash: true })
  ],
  providers: [WalletService],
  bootstrap: [AppComponent]
})
export class AppModule { }

To start, we’re importing each of our components as well as a few modules related to forms, HTTP, and routing. What is very important to us is the routes array:

const routes = [
    { path: "", redirectTo: "/unlock", pathMatch: "full" },
    { path: "unlock", component: UnlockComponent },
    { path: "generate", component: GenerateComponent },
    { path: "dashboard", component: DashboardComponent },
    { path: "import", component: ImportComponent },
    { path: "transaction/:source", component: TransactionComponent },
];

The initial route is our unlock screen, but each of the other components have a path as well. You’ll notice that we used these paths throughout the HTML and TypeScript files in our other components.

We also need to import each of our components and each of our modules into the @NgModule block:

@NgModule({
  declarations: [
    AppComponent,
    UnlockComponent,
    DashboardComponent,
    GenerateComponent,
    ImportComponent,
    TransactionComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    RouterModule,
    RouterModule.forRoot(routes, { useHash: true })
  ],
  providers: [WalletService],
  bootstrap: [AppComponent]
})

Take particular notice of the useHash property in our router. Remember our multiplexer for Go? We are using a sub-router for our API and loading our Angular application for the root route. By default each Angular route is handled by simple slashes. The problem with this is that Go is a nightmare when it comes to playing nice with the Angular router. The trick is to change how the Angular router behaves by using a pound symbol. Here are what two of the same routes might look like:

http://localhost:12345/dashboard
http://localhost:12345/#/dashboard

That simple trick saves us a lot of pain.

If we wanted to, we could run our application by executing the following command:

ng serve

Our Go application is potentially serving at http://localhost:12345 and our Angular application is potentially serving at http://localhost:4200. Remember the CORS that we set up? This is where it is valuable because we’re using two different ports for now.

More on CORS in Go can be found in my previous article titled, Handling CORS in a Golang Web Application.

Bundling and Building the Golang and Angular Application

Ideally we’re not going to want to serve the Angular application on our Raspberry Pi Zero because that adds a Node.js dependency and possibly other things. Instead we want to bundle the Angular application, cross-compile the binary, and enjoy a single application file with no dependencies.

Package Web Resources to be Included in the Binary

When bundling HTML, CSS, and JavaScript files, we want to encode them and add them to a Go file. There are many tools to do this, but I’ve had most success with GeertJohan/go.rice. Before we can do this, we need to build the Angular project.

Within the ui directory and using the Angular CLI, execute the following:

npm run build

The above command will create a dist directory with Webpack optimized HTML, CSS, and JavaScript files that can be run without any kind of server. Think back to what we had in our main.go file:

router.PathPrefix("/").Handler(http.FileServer(rice.MustFindBox("ui/dist").HTTPBox()))

The GeertJohan/go.rice tool will look at our project and automatically bundle our files listed in the MustFindBox function. Run the following command to make it happen:

rice embed-go

The above command will create a rice.go file in our project which represents our front-end bundle. If the command fails, you may need to add the following to your path:

export PATH=$PATH:$GOPATH/bin

With the Angular project built and bundled as a rice.go file, we can move on to compiling our application. However, if you want to learn more about GeertJohan/go.rice, check out my previous tutorial on the topic titled, Bundle HTML, CSS, and JavaScript to be Served in a Golang Application.

Cross-Compiling the Go Application for ARM Architectures

If you’re doing all your development directly on the Raspberry Pi Zero, I tip my hat to you, but if you’re like most people, you’re developing from a host machine. To cross-compile for the Raspberry Pi, we can do so without ever breaking a sweat.

From the command line, execute the following:

env GOOS=linux GOARCH=arm GOARM=5 go build

Just like that, we have a binary that contains our API, our front-end, and is for the Raspberry Pi Zero. For more information on cross-compiling Go applications, check out my previous tutorial titled, Cross Compiling Golang Applications for use on a Raspberry Pi.

Simplifying the Build Process

When you need to build your project, instead of running each of these commands by hand, you might want to create a script to get the job done. Here is the script I use:

cd ui
npm install
npm run build
cd ..
export PATH=$PATH:$GOPATH/bin
rice embed-go
env GOOS=linux GOARCH=arm GOARM=5 go build

Just add it to a build.sh file or similar and it will take care of building the Angular project, bundling it, and cross-compiling it.

Configuring a Raspberry Pi Zero for Emulating Ethernet with USB

One of the major points of this tutorial is that we’re using a Raspberry Pi Zero with no WiFi and no Bluetooth as our hardware wallet. This leaves us with little opportunity for communicating with the Raspberry Pi Zero at all. By enabling ethernet emulation over USB, not only could we SSH to the device from the host computer, but we can also listen for things coming from the other direction, hence our API and Angular application. Only the host computer can access the application, not other computers on the network.

Installing Raspbian Lite on the Raspberry Pi Zero

Raspbian Linux is the only Linux distribution that I’ve ever used when it comes to the Raspberry Pi Zero, so it is a requirement for this tutorial. Other distributions might work, but I have no idea how to enable ethernet emulation.

Download the latest Raspbian Lite image from the Raspberry Pi website.

If you’re using a Mac, mount an SD card and execute the following command:

diskutil list

Figure out the correct drive from the list and unmount it using something like this:

diskutil unmount /dev/disk4s1

Remember to choose the correct disk, do not just copy and paste my commands otherwise you might destroy something important.

Now you can install Raspbian Linux to your unmounted drive:

sudo dd bs=1 if=/path/to/raspbian.img of=/dev/rdisk4

In the above command you’re going to want to use the correct image file and path as well as the correct disk for your SD card. By prefixing ‘r’ in the disk, the command will complete a lot faster. However, either will work.

If you’re on Windows or Linux the commands will be a bit different. Unfortunately, I’m using a Mac and don’t have a solution for the other operating systems.

If you’d like to learn more about installing Raspbian Linux on your Raspberry Pi, check out my previous tutorial titled, Use Your Raspberry Pi as a Headless System without a Monitor.

Emulate Ethernet over USB

Leave your formatted SD card connected and mounted to your host computer. We need to change some settings before we place it in the Raspberry Pi Zero. If you’re on a Mac, navigate to /Volumes/boot.

The first thing to do is place an ssh file in this path. The file should have no extension and no data. By placing this file we’ll be able to SSH into the Pi Zero. To emulate ethernet over USB, we need to alter two files.

Open the /Volumes/boot/config.txt file and include the following line at the bottom:

dtoverlay=dwc2

Next, open the /Volumes/boot/cmdline.txt file and include modules-load=dwc2,g_ether after the rootwait parameter. This cmdline.txt file is space delimited and formatting is very important.

If you’d like to read more about emulating ethernet over USB, check out my previous tutorial titled, Connect to a Raspberry Pi Zero with a USB Cable and SSH.

At this point you can connect your SD card to your Raspberry Pi Zero.

Securing Connections to the Raspberry Pi Zero

With a fresh installation of Raspbian, you’re left with a default user account with pi as the username and raspberry as the password. For a sensitive project like this, it probably isn’t a good idea to keep the defaults. Instead we’re going to want to use SSH keys.

Connect the Raspberry Pi Zero to a computer via the USB port, not the power port.

Assuming you’re using Mac or Linux which has Bonjour installed, execute the following:

ssh pi@raspberrypi.local

If you’re on Windows, make sure you install Bonjour-like software to be able to discover the Pi Zero by its hostname. After you connect, execute the following command:

sudo raspi-config

Change the hostname and any other settings you feel to be appropriate. My hostname is coin.local as seen in my Angular code.

Without going into detail on how to add a public key on Linux, add your public key to ~/.ssh/authorized_keys so that way we don’t need a plaintext password to connect.

With a public key added, open the /etc/ssh/sshd_config file on the Raspberry Pi and set PasswordAuthentication to no. Don’t do this until you’ve confirmed that you can connect with your SSH key.

After you’re good, you need to restart the SSH service:

sudo service sshd restart

The Raspberry Pi Zero is now a little more hardened than when it started. Could you do better? Absolutely, but you might want to do some Linux research. The more locked down your Raspberry Pi, the better for your hardware wallet.

Deploying the Application and Configuring a Linux Service on the Raspberry Pi

We’re approaching the conclusion of our Raspberry Pi Zero hardware wallet tutorial. We just need to deploy what we’ve done so it can be used in production.

Create Systemd Scripts for an Auto-Starting Linux Service

It is a hassle to have to sign into the Raspberry Pi via SSH every time we plug it in to be able to access our wallet. Instead, we should create an auto-starting service for our application.

Create a /lib/systemd/system/open-ledger.service on the Raspberry Pi and include the following:

[Unit]
Description=Open Ledger Micro for Cryptocurrency

[Service]
Type=simple
ExecStart=/home/pi/open-ledger-micro
Restart=always

[Install]
WantedBy=multi-user.target

The service will run our binary file at the /home/pi/open-ledger-micro path. We haven’t uploaded our binary yet, but after we enable the service, it will run at startup.

After we’ve uploaded the binary, we will want to execute the following:

sudo systemctl enable open-ledger.service

With a little luck, that should work without issue.

Sending the Binary with SCP

The last thing we need to do is upload our cross-compiled binary file. From your host machine, with the Raspberry Pi Zero connected, execute the following command:

scp /path/to/open-ledger-micro pi@coin.local:~/

The above command assumes that your binary is called open-ledger-micro and that you’re using coin.local as your Raspberry Pi hostname.

Restart your Raspberry Pi Zero and enter http://coin.local:12345 in your web browser. If everything went smooth, you’ll have a fancy little hardware wallet with encryption at a fraction of the price of the Ledger Nano S and similar.

Conclusion

You just learned how to create a cryptocurrency hardware wallet using Golang, Angular, and a Raspberry Pi Zero. If you completed the tutorial, I applaud you as it was quite lengthy. Most of the tutorial took bits and pieces from previous tutorials that I wrote and used them in a realistic scenario. Most, if not all, of the previous tutorials include videos in case you have trouble getting things set up.

Some closing thoughts on the project:

  • Again, I am an enthusiast, not an expert when it comes to cryptocurrencies and cryptography. Use anything I created at your own risk, but definitely report back if you’ve found a bug.
  • Make sure you’re using the data port on the Raspberry Pi Zero and you’re using a charge and sync cable, not a charge cable. I prefer Anker brand USB cables.
  • We chose Go for this project because there wouldn’t be any further dependencies for the Raspberry Pi Zero which would potentially open it to further risk.
  • The hostname coin.local or whatever you chose will be accessible if you have Bonjour or similar on your host computer. Anything that can detect network devices by their hostname.

This project can be found on GitHub. I encourage you to contribute to it to make it better.

This article includes affiliate links to the Raspberry Pi Zero and accessories. Please use them to support me if you plan on buying anything used in this particular project.

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.