As Lightning Network adoption continues to surge and more prominent companies like Paypal hire teams to get into the field, you shouldn't be missing out and join too!
While the network is still maturing, now is the best time to learn its ins and outs and become a part of an incredible community of enthusiastic and passionate people who shares ideals of financial independence and freedom.

Making it a good network citizen is an involving topic, so today, I want to focus on the mere basic: setting up a dev environment and connecting to it from Elixir.

Setting up the environment

The best way to hit the ground running is to set up a request (private blockchain network) with Lightning Polar. There is a wonderfully detailed guide here https://docs.lightning.engineering/lapps/guides/polar-lapps/local-cluster-setup-with-polar

In short - we need a network with two bitcoin nodes and two lightning network nodes. Each lightning network node will talk to its bitcoin node and each other. Bitcoin nodes will communicate through the base layer, and lightning nodes will communicate directly with each other.

Creating local regtest network with Polar

After your network booted up (if not - stopping it and starting again as always helps), create a channel between lightning nodes.

Openning a balanced channel

I immediately paid almost half of the channel capacity to Bob, and liquidity became more evenly distributed - you can see how the color of a channel changed from green to green/blue. Channels are bidirectional and of a fixed size - you can use only as much sats as deposited in funding transactions.
And initially, all the funds were on Alice's side, so Bob could only receive them. Spreading liquidity between peers so both can use a channel to send and receive payments is called "balancing," In a testnet or mainnet, you can use  Loop to send yourself payment from lightning network to base layer. That provides you with incoming liquidity for the cost of the on-chain transaction fee.

Now our network is ready to use!

Building an RPC client

LND offers both REST and RPC with mostly the same feature set but sometimes new features were available in the RPC sooner than in the REST. At least in the past.

LND RPC features are broken into several files and for the simplest case we can compile definitions only for the most used features described in https://github.com/lightningnetwork/lnd/tree/master/lnrpc/rpc.proto

I have this small script in the app's bin folder

#!/bin/bash

OUTPUT_DIR=lib/grpc

mkdir -p .tmp/output
git clone https://github.com/googleapis/googleapis.git .tmp/googleapis


echo "===> Processing RPC Protobuf definition"
curl -s https://raw.githubusercontent.com/lightningnetwork/lnd/master/lnrpc/lightning.proto -o .tmp/lightning.proto
protoc --elixir_out=plugins=grpc:./.tmp/output .tmp/lightning.proto --proto_path=.tmp/googleapis:.tmp
cp .tmp/output/lightning.pb.ex ${OUTPUT_DIR}/lightning.pb.ex

echo "===> Processing Router Protobuf definition"
curl -o .tmp/router.proto -s https://raw.githubusercontent.com/lightningnetwork/lnd/master/lnrpc/routerrpc/router.proto
protoc --elixir_out=plugins=grpc:./.tmp/output .tmp/router.proto --proto_path=.tmp/googleapis:.tmp
cp .tmp/output/router.pb.ex ${OUTPUT_DIR}/router.pb.ex
rm -rf .tmp

Be sure to install the protobuf compiler before running it (brew install protobuf if you're using Brew).

This script downloads Router and RPC protobouf definitions along with their dependencies builds them and places results in lib/grpc

You should see  lightning.pb.ex  and lightning.pb.ex in it after running this script.

Connecting to a node

To be able to talk to a node, we will need the following pieces:

  • Node address and port - available at
  • Macaroon file that defines our permissions
  • TLS cert

You can get all those details on the "Connection" tab after clicking on a specific LND node in Polar.

node connection credentials

I, personally, have the following lines in config/dev.exs:

config :my_app,
  lnd_ssl_crt: System.user_home() <> "/.polar/networks/1/volumes/lnd/alice/tls.cert",
  lnd_admin_macaroon: System.user_home() <> "/.polar/networks/1/volumes/lnd/alice/data/chain/bitcoin/regtest/admin.macaroon"
LND credentials configuration

Those paths should be identical across the machines as long as those are your two first nodes in the created network since they're bound to node names and first two nodes are always named "alice" and "bob" ;)

Add gRPC dependecy to your mix.exs file:

def deps do
  [
    {:grpc, github: "elixir-grpc/grpc"},
    # 2.9.0 fixes some important bugs, so it's better to use ~> 2.9.0
    {:cowlib, "~> 2.9.0", override: true}
  ]
end

and we are ready for a launch!

Let's try out some of those juicy LND calls in our iex shell :

First we need to connecto to a node using cert

cacertfile = Application.get_env(:my_app, :lnd_ssl_crt)
creds = GRPC.Credential.new(ssl: [cacertfile: cacertfile])
{:ok, conn} = GRPC.Stub.connect(Application.get_env(:my_app, :lightning_node_address), cred: creds)

With a connection in our hands, we can start interacting with LND services. For example, we can take a look at our balance:

macaroon = Application.get_env(:my_app, :lnd_admin_macaroon)|> File.read!() |> Base.encode16()
{:ok, ballance} = Lnrpc.Lightning.Stub.wallet_balance(conn, Lnrpc.WalletBalanceRequest.new(), metadata: %{macaroon: macaroon})

and you should be seeing something like this:

{:ok,
 %Lnrpc.WalletBalanceResponse{
   account_balance: %{
     "default" => %Lnrpc.WalletAccountBalance{
       confirmed_balance: 31337,
       unconfirmed_balance: 0
     }
   },
   confirmed_balance: 31337,
   total_balance: 31337,
   unconfirmed_balance: 0
 }}

⚡ Congrats, you are ready for the lightning payments! ⚡

Visualization of LN channels a nodes, courtesy of @pymoment