You may have experience writing web applications in Go but would you know how to write a decentralized app?

This article will guide you step-by-step through the full process of programming your first decentralized application, a custom CLI Wallet for Ethereum blockchain, in Go, from scratch!

Project name: GophersLand Treasury

Goal 1/3: Setup project skeleton

Create a new project in your $GOPATH workspace.

mkdir -p /home/enchanter/go/src/github.com/gophersland/treasury
cd /home/enchanter/go/src/github.com/gophersland/treasury

If you don’t have already dep, install it.

go get -u github.com/golang/dep/cmd/dep

Initialize Gopkg.toml, lock file and an empty vendor directory to manage our future dependencies.

dep init
ls
> Gopkg.toml Gopkg.lock vendor/

Create the following main.go file.

package main

import "fmt"

func main() {
    fmt.Println("Building GophersLand Treasury DApp")
}

Compile the project and run it to test everything is setup successfully.

go install && treasury

> Building GophersLand Treasury DApp

Goal 2/3: New Account

In blockchain world, wallets are responsible for managing accounts and securing them.

Account: Like the concept of a USER in traditional web development. Accounts are based on Private Keys

Wallet: An application handling accounts and their Private/Public keys. Can perform transactions, query blockchain state, check account balances etc

That being said, our first project feature will be… Account creation for Ethereum network!

If you are not familiar with Ethereum blockchain, take my course “Learning Ethereum Blockchain”.

learning_ethereum_blockchain_img

By taking the course you will:

  • Understand Ethereum history and presence
  • Learn the necessary Ethereum vocabulary
  • Setup your fully synced blockchain node locally
  • Manage Accounts, Wallets
  • Perform transactions
  • Program a smart contract
  • Get 6 months of my full-time blockchain job knowledge compressed to 34 lectures
  • Become a Night Elf you always wanted to be ;)

Creating a CLI wizard

Let’s create a simple interactive CLI wizard that will walk us through our decentralized application (DApp, from now on).

package main

import (
    "bufio"
    "fmt"
    "os"
    "strconv"
)

const choiceCreateAccount = 1

func main() {
    fmt.Println("Welcome to GophersLand Treasury!")

    fmt.Println("What action would you like to perform?")
    fmt.Printf("    Type: '%d' to Create new account.\n", choiceCreateAccount)

    scanner := bufio.NewScanner(os.Stdin)
    for scanner.Scan() {
        cmd, err := strconv.Atoi(scanner.Text())
        if err != nil {
            fmt.Println(err)
            os.Exit(0)
        }

        switch cmd {
        case choiceCreateAccount:

        }

        fmt.Println("Done.")
        os.Exit(0)
    }

    if err := scanner.Err(); err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
}

Compile, launch and type ‘1’:

choice_create_acc

Importing go-ethereum repository

In order to create a compatible, Ethereum account, we must generate the Private and Public key using the same cryptography algorithm as Ethereum blockchain is using and store it in the official “Keystore” directory of our blockchain Node.

How to set up an Ethereum blockchain node and initialize the Keystore directory?

Check out this FREE lecture of my course walking you through the full process step by step and then continue in this blog post.

Done? Bravo.

In the course, we configured the Keystore directory to be in the following path: /home/enchanter/.gophersland_ethereum_r1/keystore.

How do we find out how go-ethereum is creating new accounts?

Reverse engineering FTW!

Never be afraid of jumping into the source code of 3rd party libraries. You should know as a developer what your project literally depends on. Browsing through the official go-ethereum repository, we can see in the accouns/keystore package a method called NewAccount().

Let’s require this package using our dependency manager, Dep.

If you are confused but intrigued, I explain all the details about Ethereum Keystore and how to set up a full blockchain node in my previously mentioned course.

Require the go-ethereum’s keystore pkg dependency:

dep ensure -add github.com/ethereum/go-ethereum/accounts/keystore@1.8.20

Because Dep is still young and doesn’t handle well non-Go files such as C code. If you check your vendor directory, vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1 and compare it with the online Github source code, you will see this library was not properly fetched. ISSUE-2738 explains the problem.

Let’s solve it with a hack, we’ll copy the libsecp256k1 package manually into our vendor folder.

Clone the go-ethereum v1.8.20 into the project:

git clone -b 'v1.8.20' --single-branch https://github.com/ethereum/go-ethereum.git ./ethereum_hack

Copy the missing library:

cp -r ./ethereum_hack/crypto/secp256k1/libsecp256k1 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/

Remove the cloned hack:

rm -rf ./ethereum_hack

Uff, let’s continue.

GenerateAccount() function

We are in the most important part of the process. Let’s encapsulate the creation of a new account to a new function, GenerateAccount() inside a new package called accounty and a file called generate.go.

Why “accounty” and not “account”? According to GoLang package conventions, you should not use a possible variable name as a package name to avoid collisions. I recommend checking the package-names doc if you would like to explore the topic more.

The function signature:

func GenerateAccount(keystoreDir string, pwd string) (common.Address, error) {
}
Variable Meaning
keystoreDir is a path to our blockchain node Keystore directory
pwd is the passphrase used for encrypting the account’s private key
common.Address the return value is account address itself encoded in a HEX format, represent as common.Address{} struct

The function must first construct a new instance of Keystore struct.

ks := keystore.NewKeyStore(keystoreDir, keystore.StandardScryptN, keystore.StandardScryptP)

ScryptN is a CPU/memory cost parameter, which must be a power of two greater than 1. ScryptP must satisfy r * p < 2³⁰. Basically, how expensive/secure the encryption of our account’s Private Key should be.

keystore.StandardScryptN will use 256MB of memory and taking approximately 1s CPU time on a modern processor. StandardScryptN = 1 « 18.

On constructed Keystore struct, we can now finally call the NewAccount() method that will generate and encrypt our new Ethereum account with our passphrase.

acc, err := ks.NewAccount(pwd)
if err != nil {
    return common.Address{}, err
}

return acc.Address, nil

Full source code:

package accounty

import (
    "github.com/ethereum/go-ethereum/accounts/keystore"
    "github.com/ethereum/go-ethereum/common"
)

func GenerateAccount(keystoreDir string, pwd string) (common.Address, error) {
    ks := keystore.NewKeyStore(keystoreDir, keystore.StandardScryptN, keystore.StandardScryptP)
    acc, err := ks.NewAccount(pwd)
    if err != nil {
        return common.Address{}, err
    }

    return acc.Address, nil
}

Calling GenerateAccount()

Let’s go back to the main.go file and modify our switch case to call GenerateAccount() function.

switch cmd {
case choiceCreateAccount:
    acc, err := accounty.GenerateAccount(keystoreDir, passphrase)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    fmt.Printf("New account '%s' successfully generated and stored inside the '%s' directory.\n", acc.Hex(), keystoreDir)
}

As the keystoreDir and passhrase are undefined, we can further modify our CLI wizard to ask for these 2 parameters interactively.

CLI Wizard should ask for ‘keystoreDir’ and encryption ‘passphrase’

Create a new, basic scan method inside the main.go:

func scanCreateAccountArgs() (keystoreDir string, passphrase string, err error) {
    keystoreDir = ""
    passphrase = ""

    fmt.Println("Paste the keystoreDir path where your new account should be saved:")
    _, err = fmt.Scanf("%s",&keystoreDir)
    if err != nil {
        return
    }

    fmt.Println("Type the passphrase you want to encrypt your account with:")
    _, err = fmt.Scanf("%s",&passphrase)
    if err != nil {
        return
    }

    return
}

And call it before the GenerateAccount():

keystoreDir, passphrase, err := scanCreateAccountArgs()
if err != nil {
    fmt.Println(err)
    os.Exit(1)
}

acc, err := accounty.GenerateAccount(keystoreDir, passphrase)
...

Goal 3/3: Run our super-duper-cool CLI Treasury wizard

go install && treasury

Result:

New account ‘0x5Cc07c0c75BD1b54914DAf8062e89682Ae3dB5F4’ successfully generated and stored inside the ‘/home/enchanter/.gophersland_ethereum_r1/keystore’ directory.

AWESOME!

create_acc_wizard_res

We can double check the Private key was stored:

ls -la /home/enchanter/.gophersland_ethereum_r1/keystore

> -rw------- 1 enchanter enchanter  491 dic 16 15:49 UTC--2018-12-16T14-49-11.033201534Z--5cc07c0c75bd1b54914daf8062e89682ae3db5f4

Summary

Congratulation, You created your first DApp program in Go, a CLI Wallet with ability to generate new Ethereum-compatible blockchain accounts.

View full source code.

Next steps

  • Subscribe to the newsletter at the bottom of the blog post for more tutorials on how to program additional features
  • Enroll in the course “Learning Ethereum Blockchain” and transfer some Ether across a global decentralized test network to your newly created Ethereum account

learning_ethereum_blockchain_img