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  ```