2017-08-03 04:53:53 +03:00
# How to write a Python gRPC client for the Lightning Network Daemon
2017-03-01 02:49:10 +03:00
2017-03-01 03:09:35 +03:00
This section enumerates what you need to do to write a client that communicates
2017-12-20 04:28:38 +03:00
with `lnd` in Python.
2017-03-01 02:49:10 +03:00
2017-08-03 04:53:53 +03:00
### Setup and Installation
2017-03-01 03:09:35 +03:00
Lnd uses the gRPC protocol for communication with clients like lncli. gRPC is
based on protocol buffers and as such, you will need to compile the lnd proto
2017-08-03 04:53:53 +03:00
file in Python before you can use it to communicate with lnd.
2017-03-01 02:49:10 +03:00
* Create a virtual environment for your project
```
$ virtualenv lnd
```
* Activate the virtual environment
```
$ source lnd/bin/activate
```
2017-03-01 03:09:35 +03:00
* Install dependencies (googleapis-common-protos is required due to the use of
google/api/annotations.proto)
2017-03-01 02:49:10 +03:00
```
(lnd)$ pip install grpcio grpcio-tools googleapis-common-protos
```
2017-03-01 03:09:35 +03:00
* Clone the google api's repository (required due to the use of
google/api/annotations.proto)
2017-03-01 02:49:10 +03:00
```
(lnd)$ git clone https://github.com/googleapis/googleapis.git
```
2017-08-03 04:53:53 +03:00
* Copy the lnd rpc.proto file (you'll find this at
[lnrpc/rpc.proto ](https://github.com/lightningnetwork/lnd/blob/master/lnrpc/rpc.proto ))
or just download it
2017-03-01 02:49:10 +03:00
```
(lnd)$ curl -o rpc.proto -s https://raw.githubusercontent.com/lightningnetwork/lnd/master/lnrpc/rpc.proto
```
* Compile the proto file
```
(lnd)$ python -m grpc_tools.protoc --proto_path=googleapis:. --python_out=. --grpc_python_out=. rpc.proto
```
2017-03-01 03:09:35 +03:00
After following these steps, two files `rpc_pb2.py` and `rpc_pb2_grpc.py` will
2017-08-03 04:53:53 +03:00
be generated. These files will be imported in your project anytime you use
Python gRPC.
2017-03-01 02:49:10 +03:00
2017-08-03 04:53:53 +03:00
#### Imports and Client
2017-03-01 02:49:10 +03:00
2018-02-07 06:11:11 +03:00
Every time you use Python gRPC, you will have to import the generated rpc modules
2017-08-03 04:53:53 +03:00
and set up a channel and stub to your connect to your `lnd` node:
2017-03-01 02:49:10 +03:00
2017-08-03 04:53:53 +03:00
```python
2017-03-01 02:49:10 +03:00
import rpc_pb2 as ln
import rpc_pb2_grpc as lnrpc
2017-08-03 04:53:53 +03:00
import grpc
2017-12-28 17:04:01 +03:00
import os
2017-03-01 02:49:10 +03:00
2018-03-21 03:54:19 +03:00
# Due to updated ECDSA generated tls.cert we need to let gprc know that
# we need to use that cipher suite otherwise there will be a handhsake
# error when we communicate with the lnd rpc server.
os.environ["GRPC_SSL_CIPHER_SUITES"] = 'HIGH+ECDSA'
2017-08-09 08:30:51 +03:00
# Lnd cert is at ~/.lnd/tls.cert on Linux and
# ~/Library/Application Support/Lnd/tls.cert on Mac
2018-03-20 23:42:53 +03:00
cert = open(os.path.expanduser('~/.lnd/tls.cert'), 'rb').read()
2017-08-09 08:30:51 +03:00
creds = grpc.ssl_channel_credentials(cert)
channel = grpc.secure_channel('localhost:10009', creds)
2017-03-01 02:49:10 +03:00
stub = lnrpc.LightningStub(channel)
2017-08-03 04:53:53 +03:00
```
2017-03-01 02:49:10 +03:00
2017-08-03 04:53:53 +03:00
### Examples
2017-08-09 08:30:51 +03:00
Let's walk through some examples of Python gRPC clients. These examples assume
that you have at least two `lnd` nodes running, the RPC location of one of which
is at the default `localhost:10009` , with an open channel between the two nodes.
2017-08-03 04:53:53 +03:00
#### Simple RPC
```python
# Retrieve and display the wallet balance
2017-03-01 02:49:10 +03:00
response = stub.WalletBalance(ln.WalletBalanceRequest(witness_only=True))
2018-03-20 23:42:53 +03:00
print(response.total_balance)
2017-03-01 02:49:10 +03:00
```
2017-03-01 03:09:35 +03:00
2017-08-03 04:53:53 +03:00
#### Response-streaming RPC
```python
request = ln.InvoiceSubscription()
2017-12-28 17:04:01 +03:00
for invoice in stub.SubscribeInvoices(request):
2018-03-20 23:42:53 +03:00
print(invoice)
2017-08-03 04:53:53 +03:00
```
2017-08-09 08:30:51 +03:00
Now, create an invoice for your node at `localhost:10009` and send a payment to
it from another node.
```bash
2018-01-18 21:32:24 +03:00
$ lncli addinvoice --amt=100
2017-08-09 08:30:51 +03:00
{
"r_hash": < R_HASH > ,
"pay_req": < PAY_REQ >
}
$ lncli sendpayment --pay_req=< PAY_REQ >
```
Your Python console should now display the details of the recently satisfied
2017-08-03 04:53:53 +03:00
invoice.
#### Bidirectional-streaming RPC
```python
from time import sleep
import codecs
def request_generator(dest, amt):
# Initialization code here
counter = 0
print("Starting up")
while True:
request = ln.SendRequest(
dest=dest,
amt=amt,
)
yield request
# Alter parameters here
counter += 1
sleep(2)
# Outputs from lncli are hex-encoded
dest_hex = < RECEIVER_ID_PUBKEY >
dest_bytes = codecs.decode(dest_hex, 'hex')
request_iterable = request_generator(dest=dest_bytes, amt=100)
for payment in stub.SendPayment(request_iterable):
2018-03-20 23:42:53 +03:00
print(payment)
2017-08-03 04:53:53 +03:00
```
This example will send a payment of 100 satoshis every 2 seconds.
2018-03-19 19:55:31 +03:00
#### Using Macaroons
To authenticate using macaroons you need to include the macaroon in the metadata of the request.
```python
2018-03-23 08:28:11 +03:00
import codecs
2018-03-19 19:55:31 +03:00
# Lnd admin macaroon is at ~/.lnd/admin.macaroon on Linux and
# ~/Library/Application Support/Lnd/admin.macaroon on Mac
2018-03-23 08:28:11 +03:00
with open(os.path.expanduser('~/.lnd/admin.macaroon'), 'rb') as f:
2018-03-19 19:55:31 +03:00
macaroon_bytes = f.read()
2018-03-23 08:28:11 +03:00
macaroon = codecs.encode(macaroon_bytes, 'hex')
2018-03-19 19:55:31 +03:00
```
The simplest approach to use the macaroon is to include the metadata in each request as shown below.
```python
stub.GetInfo(ln.GetInfoRequest(), metadata=[('macaroon', macaroon)])
```
However, this can get tiresome to do for each request, so to avoid explicitly including the macaroon we can update the credentials to include it automatically.
```python
def metadata_callback(context, callback):
# for more info see grpc docs
2018-03-23 08:28:11 +03:00
callback([('macaroon', macaroon)], None)
2018-03-19 19:55:31 +03:00
# build ssl credentials using the cert the same as before
cert_creds = grpc.ssl_channel_credentials(cert)
# now build meta data credentials
auth_creds = grpc.metadata_call_credentials(metadata_callback)
# combine the cert credentials and the macaroon auth credentials
# such that every call is properly encrypted and authenticated
combined_creds = grpc.composite_channel_credentials(cert_creds, auth_creds)
# finally pass in the combined credentials when creating a channel
channel = grpc.secure_channel('localhost:10009', combined_creds)
stub = lnrpc.LightningStub(channel)
# now every call will be made with the macaroon already included
stub.GetInfo(ln.GetInfoRequest())
```
2017-08-03 04:53:53 +03:00
### Conclusion
2017-03-01 03:09:35 +03:00
With the above, you should have all the `lnd` related `gRPC` dependencies
installed locally into your virtual environment. In order to get up to speed
with `protofbuf` usage from Python, see [this official `protobuf` tutorial for
Python](https://developers.google.com/protocol-buffers/docs/pythontutorial).
Additionally, [this official gRPC
2017-08-03 04:53:53 +03:00
resource](http://www.grpc.io/docs/tutorials/basic/python.html) provides more
details around how to drive `gRPC` from Python.