github.com/decred/dcrlnd@v0.7.6/docs/grpc/ruby.md (about) 1 # How to write a Ruby gRPC client for the Lightning Network Daemon 2 3 This section enumerates what you need to do to write a client that communicates 4 with `lnd` in Ruby. 5 6 ### Introduction 7 8 `lnd` uses the `gRPC` protocol for communication with clients like `lncli`. 9 10 `gRPC` is based on protocol buffers and as such, you will need to compile 11 the `lnd` proto file in Ruby before you can use it to communicate with `lnd`. 12 13 ### Setup 14 15 Install gRPC rubygems: 16 17 ``` 18 $ gem install grpc 19 $ gem install grpc-tools 20 ``` 21 22 Clone the Google APIs repository: 23 24 ``` 25 $ git clone https://github.com/googleapis/googleapis.git 26 ``` 27 28 Fetch the `lightning.proto` file (or copy it from your local source directory): 29 30 ```shell 31 ⛰ curl -o lightning.proto -s https://raw.githubusercontent.com/lightningnetwork/lnd/master/lnrpc/lightning.proto 32 ``` 33 34 Compile the proto file: 35 36 ```shell 37 ⛰ grpc_tools_ruby_protoc --proto_path googleapis:. --ruby_out=. --grpc_out=. lightning.proto 38 ``` 39 40 Two files will be generated in the current directory: 41 42 * `lightning_pb.rb` 43 * `lightning_services_pb.rb` 44 45 ### Examples 46 47 #### Simple client to display wallet balance 48 49 Every time you use the Ruby gRPC you need to require the `lightning_services_pb` file. 50 51 We assume that `lnd` runs on the default `localhost:10009`. 52 53 We further assume you run `lnd` with `--no-macaroons`. 54 55 Note that when an IP address is used to connect to the node (e.g. 192.168.1.21 instead of localhost) you need to add `--tlsextraip=192.168.1.21` to your `lnd` configuration and re-generate the certificate (delete tls.cert and tls.key and restart lnd). 56 57 ```ruby 58 #!/usr/bin/env ruby 59 60 $:.unshift(File.dirname(__FILE__)) 61 62 require 'grpc' 63 require 'lightning_services_pb' 64 65 # Due to updated ECDSA generated tls.cert we need to let gprc know that 66 # we need to use that cipher suite otherwise there will be a handhsake 67 # error when we communicate with the lnd rpc server. 68 ENV['GRPC_SSL_CIPHER_SUITES'] = "HIGH+ECDSA" 69 70 certificate = File.read(File.expand_path("~/.lnd/tls.cert")) 71 credentials = GRPC::Core::ChannelCredentials.new(certificate) 72 stub = Lnrpc::Lightning::Stub.new('127.0.0.1:10009', credentials) 73 74 response = stub.wallet_balance(Lnrpc::WalletBalanceRequest.new()) 75 puts "Total balance: #{response.total_balance}" 76 ``` 77 78 This will show the `total_balance` of the wallet. 79 80 #### Streaming client for invoice payment updates 81 82 ```ruby 83 #!/usr/bin/env ruby 84 85 $:.unshift(File.dirname(__FILE__)) 86 87 require 'grpc' 88 require 'lightning_services_pb' 89 90 ENV['GRPC_SSL_CIPHER_SUITES'] = "HIGH+ECDSA" 91 92 certificate = File.read(File.expand_path("~/.lnd/tls.cert")) 93 credentials = GRPC::Core::ChannelCredentials.new(certificate) 94 stub = Lnrpc::Lightning::Stub.new('127.0.0.1:10009', credentials) 95 96 stub.subscribe_invoices(Lnrpc::InvoiceSubscription.new) do |invoice| 97 puts invoice.inspect 98 end 99 ``` 100 101 Now, create an invoice on your node: 102 103 ```bash 104 $ lncli addinvoice --amt=590 105 { 106 "r_hash": <R_HASH>, 107 "pay_req": <PAY_REQ> 108 } 109 ``` 110 111 Next send a payment to it from another node: 112 113 ``` 114 $ lncli sendpayment --pay_req=<PAY_REQ> 115 ``` 116 117 You should now see the details of the settled invoice appear. 118 119 #### Using Macaroons 120 121 To authenticate using macaroons you need to include the macaroon in the metadata of the request. 122 123 ```ruby 124 # Lnd admin macaroon is at ~/.lnd/data/chain/bitcoin/simnet/admin.macaroon on Linux and 125 # ~/Library/Application Support/Lnd/data/chain/bitcoin/simnet/admin.macaroon on Mac 126 macaroon_binary = File.read(File.expand_path("~/.lnd/data/chain/bitcoin/simnet/admin.macaroon")) 127 macaroon = macaroon_binary.each_byte.map { |b| b.to_s(16).rjust(2,'0') }.join 128 ``` 129 130 The simplest approach to use the macaroon is to include the metadata in each request as shown below. 131 132 ```ruby 133 stub.get_info(Lnrpc::GetInfoRequest.new, metadata: {macaroon: macaroon}) 134 ``` 135 136 However, this can get tiresome to do for each request. We can use gRPC interceptors to add this metadata to each request automatically. Our interceptor class would look like this. 137 138 ```ruby 139 class MacaroonInterceptor < GRPC::ClientInterceptor 140 attr_reader :macaroon 141 142 def initialize(macaroon) 143 @macaroon = macaroon 144 super 145 end 146 147 def request_response(request:, call:, method:, metadata:) 148 metadata['macaroon'] = macaroon 149 yield 150 end 151 152 def server_streamer(request:, call:, method:, metadata:) 153 metadata['macaroon'] = macaroon 154 yield 155 end 156 end 157 ``` 158 159 And then we would include it when we create our stub like so. 160 161 ```ruby 162 certificate = File.read(File.expand_path("~/.lnd/tls.cert")) 163 credentials = GRPC::Core::ChannelCredentials.new(certificate) 164 macaroon_binary = File.read(File.expand_path("~/.lnd/data/chain/bitcoin/simnet/admin.macaroon")) 165 macaroon = macaroon_binary.each_byte.map { |b| b.to_s(16).rjust(2,'0') }.join 166 167 stub = Lnrpc::Lightning::Stub.new( 168 'localhost:10009', 169 credentials, 170 interceptors: [MacaroonInterceptor.new(macaroon)] 171 ) 172 173 # Now we don't need to pass the metadata on a request level 174 p stub.get_info(Lnrpc::GetInfoRequest.new) 175 ``` 176 177 #### Receive Large Responses 178 179 A GRPC::ResourceExhausted exception is raised when a server response is too large. In particular, this will happen with mainnet DescribeGraph calls. The solution is to raise the default limits by including a channel_args hash when creating our stub. 180 181 ```ruby 182 stub = Lnrpc::Lightning::Stub.new( 183 'localhost:10009', 184 credentials, 185 channel_args: {"grpc.max_receive_message_length" => 1024 * 1024 * 50} 186 ) 187 ```