bumi / lnrpc

a gRPC client for the Lightning Network Daemin (LND) packed as rubygem
MIT License
18 stars 6 forks source link

trying to use lnrpc with trusted certificates #3

Closed ryan-lingle closed 5 years ago

ryan-lingle commented 5 years ago

Trying to adapt gem to use with btc pay server which utilizes trusted certificates.

Changed GRPC::Core::ChannelCredentials.new(self.credentials) to GRPC::Core::ChannelCredentials.new

Following the pattern that Nicolas Dorier used with Alex Bosworth's ln-service: https://github.com/alexbosworth/ln-service/pull/71

Continue to get this error: rails aborted! GRPC::Unknown: 2:unmarshal v1: packet size too big

Wondering if you might have some insight as to why and if we can add this functionality to lnrpc? If I get it to work properly on my machine and can create a pr.

bumi commented 5 years ago

that's a very good point. I haven't looked into that yet. I currently don't have an lnd behind a "trusted" cert.

actually initializing the GRPC::Core::ChannelCredentials with no parameter/nil should do the trick to load the root certs from the environment/server.

Do you have the full trace of your error?

ryan-lingle commented 5 years ago

full trace:

/Users/Ryan/.rbenv/versions/2.4.4/lib/ruby/gems/2.4.0/gems/grpc-1.19.0-universal-darwin/src/ruby/lib/grpc/generic/active_call.rb:31:in `check_status': 2:unmarshal v1: packet size too big (GRPC::Unknown)
    from /Users/Ryan/.rbenv/versions/2.4.4/lib/ruby/gems/2.4.0/gems/grpc-1.19.0-universal-darwin/src/ruby/lib/grpc/generic/active_call.rb:181:in `attach_status_results_and_complete_call'
    from /Users/Ryan/.rbenv/versions/2.4.4/lib/ruby/gems/2.4.0/gems/grpc-1.19.0-universal-darwin/src/ruby/lib/grpc/generic/active_call.rb:377:in `request_response'
    from /Users/Ryan/.rbenv/versions/2.4.4/lib/ruby/gems/2.4.0/gems/grpc-1.19.0-universal-darwin/src/ruby/lib/grpc/generic/client_stub.rb:178:in `block in request_response'
    from /Users/Ryan/.rbenv/versions/2.4.4/lib/ruby/gems/2.4.0/gems/grpc-1.19.0-universal-darwin/src/ruby/lib/grpc/generic/interceptors.rb:181:in `block in intercept!'
    from /Users/Ryan/.rbenv/versions/2.4.4/lib/ruby/gems/2.4.0/gems/lnrpc-0.5.2/lib/lnrpc/macaroon_interceptor.rb:13:in `inject_macaroon_metadata'
    from /Users/Ryan/.rbenv/versions/2.4.4/lib/ruby/gems/2.4.0/gems/grpc-1.19.0-universal-darwin/src/ruby/lib/grpc/generic/interceptors.rb:175:in `intercept!'
    from /Users/Ryan/.rbenv/versions/2.4.4/lib/ruby/gems/2.4.0/gems/grpc-1.19.0-universal-darwin/src/ruby/lib/grpc/generic/client_stub.rb:177:in `request_response'
    from /Users/Ryan/.rbenv/versions/2.4.4/lib/ruby/gems/2.4.0/gems/grpc-1.19.0-universal-darwin/src/ruby/lib/grpc/generic/service.rb:170:in `block (3 levels) in rpc_stub_class'
    from /Users/Ryan/.rbenv/versions/2.4.4/lib/ruby/gems/2.4.0/gems/lnrpc-0.5.2/lib/lnrpc/client.rb:60:in `method_missing'
    from rpc.rb:6:in `<main>'
bumi commented 5 years ago

hmmm, that's strange... and how exactly does your code look like?

ryan-lingle commented 5 years ago
require 'lnrpc'

macaroon = "[my macaroon here]".each_byte.map { |b| b.to_s(16).rjust(2,'0') }.join

lnd = Lnrpc::Client.new({macaroon: macaroon, channel: 'btcpay399208.lndyn.com:443'})
lnd.get_info

with this adjustment to gem:

self.address = options[:address] || DEFAULT_ADDRESS

self.credentials = options[:credentials_path] ? ::File.read(::File.expand_path(options[:credentials_path] || DEFAULT_CREDENTIALS_PATH)) : options[:credentials]

options[:macaroon] ||= begin
        macaroon_binary = ::File.read(::File.expand_path(options[:macaroon_path] || DEFAULT_MACAROON_PATH))
        macaroon_binary.unpack("H*")
      end

self.macaroon = options[:macaroon]

channelcredentials = self.credentials ? GRPC::Core::ChannelCredentials.new(self.credentials) : GRPC::Core::ChannelCredentials.new

self.grpc_client = Lnrpc::Lightning::Stub.new(self.address,
                                                    channelcredentials,
                                                    interceptors: [Lnrpc::MacaroonInterceptor.new(self.macaroon)]
                                                   )
bumi commented 5 years ago

ok, "[my macaroon here]" is what you're loading from the macaroon file? (binary?)

and I think in the options the address should be passed as address not channel:

lnd = Lnrpc::Client.new({macaroon: macaroon, address: 'btcpay399208.lndyn.com:443'})
bumi commented 5 years ago

and is your lnd node really reachable through that address and port?

ryan-lingle commented 5 years ago

Good catch on the channel mistype (luckily I had the default address constant set to btcpay399208.lndyn.com:443).

The macaroon is the actual admin macaroon for the lnd instance as a string (just trying to get it up and running at this point).

The node should be reachable through that port. The node is a BTC Pay Server running on a lunanode VM. BTC Pay Server exposes an LND grpc service on that endpoint:

screen shot 2019-03-04 at 10 59 41 am
bumi commented 5 years ago

sadly I don't know btc pay server, how do you get the macaroon from btcpay server? if it is already a hex value (which I assume) then you should not need the .each_byte.map { |b| b.to_s(16).rjust(2,'0') }.join

ryan-lingle commented 5 years ago

Yep, that was the problem! Thanks a ton!

For the pr:

I can assume trusted cert when no credentials and credentials_path (instead of assuming DEFAULT_CREDENTIALS_PATH):

def initialize(options={})
      self.address = options[:address] || DEFAULT_ADDRESS

      self.credentials = options[:credentials_path] ? ::File.read(::File.expand_path(options[:credentials_path])) : options[:credentials]

      options[:macaroon] ||= begin
        macaroon_binary = ::File.read(::File.expand_path(options[:macaroon_path] || DEFAULT_MACAROON_PATH))
        macaroon_binary.unpack("H*")
      end

      self.macaroon = options[:macaroon]

      channel_credentials = self.credentials ? GRPC::Core::ChannelCredentials.new(self.credentials) : GRPC::Core::ChannelCredentials.new

      self.grpc_client = Lnrpc::Lightning::Stub.new(self.address,
                                                    channel_credentials,
                                                    interceptors: [Lnrpc::MacaroonInterceptor.new(self.macaroon)]
                                                   )
end

or continue to assume the DEFAULT_CREDENTIALS_PATH in that case and make user pass in additional argument (trusted_certificated: true) :

def initialize(options={})
      self.address = options[:address] || DEFAULT_ADDRESS

      options[:credentials] ||= ::File.read(::File.expand_path(options[:credentials_path] || DEFAULT_CREDENTIALS_PATH))
      self.credentials = options[:credentials]

      options[:macaroon] ||= begin
        macaroon_binary = ::File.read(::File.expand_path(options[:macaroon_path] || DEFAULT_MACAROON_PATH))
        macaroon_binary.unpack("H*")
      end
      self.macaroon = options[:macaroon]

      channel_credentials = options[:trusted_certification] ? GRPC::Core::ChannelCredentials.new : GRPC::Core::ChannelCredentials.new(self.credentials)

      self.grpc_client = Lnrpc::Lightning::Stub.new(self.address,
                                                    channel_credentials,
                                                    interceptors: [Lnrpc::MacaroonInterceptor.new(self.macaroon)]
                                                   )
 end
bumi commented 5 years ago

yay, great! happy to hear that.

I was just experimenting with something like this:

    def initialize(options={})
      self.address = options[:address] || DEFAULT_ADDRESS

      unless options.has_key?(:credentials)
        options[:credentials] = ::File.read(::File.expand_path(options[:credentials_path] || DEFAULT_CREDENTIALS_PATH))
      end
      self.credentials = options[:credentials]

      unless options.has_key?(:macaroon)
        options[:macaroon] = ::File.read(::File.expand_path(options[:macaroon_path] || DEFAULT_MACAROON_PATH)).unpack("H*")
      end
      self.macaroon = options[:macaroon]
     self.grpc_client = Lnrpc::Lightning::Stub.new(self.address,
                                                    GRPC::Core::ChannelCredentials.new(self.credentials),
                                                    interceptors: [Lnrpc::MacaroonInterceptor.new(self.macaroon)]
                                                  )
    end

so it could be used like:

lnd = Lnrpc::Client.new({credentials: nil, macaroon: macaroon, address: 'btcpay399208.lndyn.com:443'})

meaning if nil is passed as credentials then the default/trusted system certs are used?

what do you think?

ryan-lingle commented 5 years ago

I like it!

bumi commented 5 years ago

I've just pushed a branch PR #5 if you want to test it. let me know if it works for you. still in the testing phrase and I need to write some specs for it.

ryan-lingle commented 5 years ago

Works great!

bumi commented 5 years ago

ok, great!! as I don't have any btc pay server running... do you want to add a section to the readme or in some docs folder or wiki a HowTo connect to it? that could be super helpful for others.

ryan-lingle commented 5 years ago

Yes, I will do that!