In this tutorial series, we are going to learn how to create an HD bitcoin wallet using Golang. The idea of the tutorial series is not only showing how to create the Wallet and getting balances but also how to implement a grpc server and a CLI tool and a web-app for consuming it. In this part we are going to see how to implement our grpc server. Requirements: go version go1.13.8 darwin/amd64 Also, I will assume you already have some knowledge about Golang and understand the main concepts of bitcoin, so, I will no go deep on how to install Golang or what bitcoin is. So, let’s get started by defining what our program will do for us. To keep this tutorial small, I will split it into two parts, in this one where will create our service and a CLI tool that will allow us to create bitcoin key pairs (private and public keys) and retrieving the balance of our Wallet. Please, notice that this tutorial is only for educational proposes, and this software is not ready for production or anything. I have decided to use for the blockchain requests, and a go package I found for generating addresses, Blockcypher allows you to create wallets, but I thought that would be good to use the API only for the Blockchain requests. Blockcypher As well you can find the entire code for this tutorial . here You will need to install grpc, to do so you can follow the official : (https://grpc.io/docs/languages/go/quickstart/) or do the following: instructions Open your terminal and install the protocol compiler plugin for Go (protoc-gen-go) using the following commands: $ GO111MODULE=on $ go get github.com/golang/protobuf/protoc-gen-go export # Enable module mode Once you have installed the protoc-gen-go, let’s create our proto service: 1. Create a folder called proto in the root of your project. 2. Create a folder called btchdwallet inside of your proto folder. 3. Create a file called wallet.proto inside your btchdwallet folder with the following content: syntax = ; .microservice.btchdwallet; option go_package = ; service Wallet { rpc CreateWallet(Request) returns (Response) {} rpc CreateChildWallet(Request) returns (Response) {} rpc GetWallet(Request) returns (Response) {} rpc GetBalance(Request) returns (Response) {} } message Request { Address = ; Mnemonic = ; } message Response { Address = ; PubKey = ; PrivKey = ; Mnemonic = ; Balance = ; TotalReceived = ; TotalSent = ; UnconfirmedBalance = ; } // proto-service "proto3" package go "github.com/LuisAcerv/btchdwallet" string 1 string 2 string 1 string 2 string 3 string 4 int64 5 int64 6 int64 7 int64 8 4. Now create a new file called Makefile with the following content: build_protoc: protoc --go_out=plugins=grpc:. --go_opt=paths=source_relative proto/btchdwallet/wallet.proto 5. From your terminal run the following command: $ make build_protoc This command will create a new file at . Now we can start to build our service. ./proto/btchdwallet/wallet.pb.go Now we have our grpc service ready to have functionality added. Before we start creating the main file, we need to create some other scripts that will contain the program functionality itself. 6. We will create a little package called crypt that will be in charge of generating a random 8-byte hash that we are going to use to generate our mnemonics. To do that, create a new folder called crypt in your project root and file with the same name and go extension inside of it with the following content: crypt ( ) { key := ([] , ) _, err := rand.Read(key) err != { fmt.Println(err) } str := hex.EncodeToString(key) str } package import "fmt" "crypto/rand" "encoding/hex" // CreateHash returns random 8 byte array as a hex string func CreateHash () string make byte 8 if nil // handle error here return Let me make a parenthesis here. You can use the architecture that fits best for you, but take in count that this tutorial is a part of a series, and in future chapters, this architecture will have more sense than may have now. Said that, let’s continue… 7. Now that we have our little crypt helper, we will create another folder called wallet and file with the same name and go extension inside of it. I have to make another parenthesis here. I am using this to generate the bitcoin key pairs. Remember that this tutorial is only to understand how we can implement a grpc server and client in the context of bitcoin. For production-level software, you may want to use another tool, such as the API or anything else. Anyway, when it comes to cryptocurrencies, you must be aware of the risk of managing assets on behalf of users. package Blockcypher First, we have the CreateWallet function that returns four strings, the bitcoin address, the public key, the private key, and the mnemonic phrase. Notice that we are not storing this data anywhere, so the user MUST write down and keep this information into a safe place since we cannot recover this data later. wallet ( pb ) { seed := crypt.CreateHash() mnemonic, _ := mnemonic.New([] (seed), mnemonic.English) masterprv := hdwallet.MasterKey([] (mnemonic.Sentence())) masterpub := masterprv.Pub() address := masterpub.Address() &pb.Response{Address: address, PubKey: masterpub.String(), PrivKey: masterprv.String(), Mnemonic: mnemonic.Sentence()} } package import "fmt" "github.com/LuisAcerv/btchdwallet/proto/btchdwallet" "github.com/LuisAcerv/btchdwallet/config" "github.com/LuisAcerv/btchdwallet/crypt" "github.com/blockcypher/gobcy" "github.com/brianium/mnemonic" "github.com/wemeetagain/go-hdwallet" // CreateWallet is in charge of creating a new root wallet * . func CreateWallet () pb Response // Generate a random 256 bit seed byte // Create a master private key byte // Convert a private key to public key // Get your address return We are going to use a couple of packages here, make sure you install them in your project using: $ go get <package url> Now we need to add our function to retrieve our wallet. { masterprv := hdwallet.MasterKey([] (mnemonic)) masterpub := masterprv.Pub() address := masterpub.Address() &pb.Response{Address: address, PubKey: masterpub.String(), PrivKey: masterprv.String()} } // DecodeWallet is in charge of decoding wallet from mnemonic * . func DecodeWallet (mnemonic ) string pb Response // Get private key from mnemonic byte // Convert a private key to public key // Get your address return The decode wallet function takes one argument, the mnemonic. Using the mnemonic we can decode our private and public keys. Finally we are going to add a function to get our address balance: { btc := gobcy.API{conf.Blockcypher.Token, , } addr, err := btc.GetAddrBal(address, ) err != { fmt.Println(err) } balance := addr.Balance totalReceived := addr.TotalReceived totalSent := addr.TotalSent unconfirmedBalance := addr.UnconfirmedBalance &pb.Response{Address: address, Balance: (balance), TotalReceived: (totalReceived), TotalSent: (totalSent), UnconfirmedBalance: (unconfirmedBalance)} } // GetBalance is in charge of returning the given address balance * . func GetBalance (address ) string pb Response "btc" "main" nil if nil return int64 int64 int64 int64 Putting all together: wallet ( pb ) conf = config.ParseConfig() { seed := crypt.CreateHash() mnemonic, _ := mnemonic.New([] (seed), mnemonic.English) masterprv := hdwallet.MasterKey([] (mnemonic.Sentence())) masterpub := masterprv.Pub() address := masterpub.Address() &pb.Response{Address: address, PubKey: masterpub.String(), PrivKey: masterprv.String(), Mnemonic: mnemonic.Sentence()} } { masterprv := hdwallet.MasterKey([] (mnemonic)) masterpub := masterprv.Pub() address := masterpub.Address() &pb.Response{Address: address, PubKey: masterpub.String(), PrivKey: masterprv.String()} } { btc := gobcy.API{conf.Blockcypher.Token, , } addr, err := btc.GetAddrBal(address, ) err != { fmt.Println(err) } balance := addr.Balance totalReceived := addr.TotalReceived totalSent := addr.TotalSent unconfirmedBalance := addr.UnconfirmedBalance &pb.Response{Address: address, Balance: (balance), TotalReceived: (totalReceived), TotalSent: (totalSent), UnconfirmedBalance: (unconfirmedBalance)} } package import "fmt" "github.com/LuisAcerv/btchdwallet/proto/btchdwallet" "github.com/LuisAcerv/btchdwallet/config" "github.com/LuisAcerv/btchdwallet/crypt" "github.com/blockcypher/gobcy" "github.com/brianium/mnemonic" "github.com/wemeetagain/go-hdwallet" var // CreateWallet is in charge of creating a new root wallet * . func CreateWallet () pb Response // Generate a random 256 bit seed byte // Create a master private key byte // Convert a private key to public key // Get your address return // DecodeWallet is in charge of decoding wallet from mnemonic * . func DecodeWallet (mnemonic ) string pb Response // Get private key from mnemonic byte // Convert a private key to public key // Get your address return // GetBalance is in charge of returning the given address balance * . func GetBalance (address ) string pb Response "btc" "main" nil if nil return int64 int64 int64 int64 8. Once we have our wallet script is time to create our main script to implement our grpc server. main ( pb ) ( port = ) server { pb.UnimplementedWalletServer } { fmt.Println() fmt.Println( ) wallet := wallet.CreateWallet() wallet, } { fmt.Println() fmt.Println( ) wallet := wallet.DecodeWallet(in.Mnemonic) wallet, } { fmt.Println() fmt.Println( ) balance := wallet.GetBalance(in.Address) balance, } { lis, err := net.Listen( , port) err != { log.Fatalf( , err) } s := grpc.NewServer() pb.RegisterWalletServer(s, &server{}) reflection.Register(s) fmt.Printf( , port) fmt.Println() err := s.Serve(lis); err != { log.Fatalf( , err) } } package import "context" "fmt" "log" "net" "github.com/LuisAcerv/btchdwallet/proto/btchdwallet" "github.com/LuisAcerv/btchdwallet/wallet" "google.golang.org/grpc" "google.golang.org/grpc/reflection" const ":50055" type struct func (s *server) CreateWallet (ctx context.Context, in *pb.Request) (*pb.Response, error) "\nCreating new wallet" return nil func (s *server) GetWallet (ctx context.Context, in *pb.Request) (*pb.Response, error) "\nGetting wallet data" return nil func (s *server) GetBalance (ctx context.Context, in *pb.Request) (*pb.Response, error) "\nGetting Balance data" return nil func main () "tcp" if nil "failed to listen: %v" "Service running at port: %s" if nil "failed to serve: %v" This script has three functions, the same we have written in our script. We also are calling our wallet methods for these functions. wallet.proto If you run this script with go run main.go and everything goes well, you should see the following in your terminal: Service running at port: :50055 In the next chapter we will see step by step how to implement a client for our grpc server. You can download the whole code , this already includes a small CLI . To use it just run it as follows: here Run the grpc server $ go run main.go In another terminal run: $ go run client/client.go -m=create-wallet You should see this: Output: New Wallet >> > Public Key: xpub661MyMwAqRbcG3fYrFtkZGesCkhTZWAwHDM2Q1DbeMH6CcQSkrL5qzYwnRkzwKKhrsjbngkC8EcNTBvQmBAJhMUVAXmU4qv8jzVFkhrqme1 > Private Key: xprv9s21ZrQH143K3Zb5kEMkC8i8eiryA3T5uzRRbcoz61k7Kp5JDK1qJCETw9vxGBCe88qu57EKUu2hX54zeivPiZhCNQ5dV6CfKdhsCwMqm5j > Mnemonic: coral light army glare basket boil school egg couple payment flee goose To get your wallet $ go run client/client.go -m=get-wallet -mne= "coral light army glare basket boil school egg couple payment flee goose" To get your balance $ go run client/client.go -m=get-balance -addr=1Go23sv8vR81YuV1hHGsUrdyjLcGVUpCDy In the following chapter we are going step by step on how to implement this CLI . Happy hacking! If you have any comments throw me a tweet ! @luis_acervantes