Class: Lnurl
- Inherits:
-
Object
- Object
- Lnurl
- Defined in:
- lib/lnurl.rb
Constant Summary collapse
- VERSION =
'1.1.1'.freeze
- MAX_INTEGER =
Maximum integer size Useful for max_length when decoding
2**31 - 1
- InvoiceResponse =
Class.new(OpenStruct)
- LnurlResponse =
Class.new(OpenStruct) do # amount in msats def request_invoice(args) args.transform_keys!(&:to_s) callback_uri = URI(callback) if callback_uri.query args = Hash[URI.decode_www_form(callback_uri.query)].merge(args) # reverse merge end callback_uri.query = URI.encode_www_form(args) body = Lnurl.http_get(callback_uri) InvoiceResponse.new JSON.parse(body) end end
- HRP =
'lnurl'.freeze
Instance Attribute Summary collapse
-
#uri ⇒ Object
readonly
Returns the value of attribute uri.
Class Method Summary collapse
- .convert_bits(data, from, to, padding = true) ⇒ Object
- .decode(lnurl, max_length = MAX_INTEGER) ⇒ Object
- .decode_lightning_address(lightning_address) ⇒ Object
- .decode_raw(lnurl, max_length = MAX_INTEGER) ⇒ Object
- .from_lightning_address(lightning_address) ⇒ Object
-
.http_get(uri, limit = 10) ⇒ Object
Handles HTTP GET requests and follows redirects if necessary.
- .valid?(value) ⇒ Boolean
Instance Method Summary collapse
- #data ⇒ Object
-
#initialize(uri) ⇒ Lnurl
constructor
A new instance of Lnurl.
- #payment_request(amount:) ⇒ Object
- #request_invoice(amount:) ⇒ Object
- #response ⇒ Object
- #to_bech32 ⇒ Object (also: #encode)
Constructor Details
#initialize(uri) ⇒ Lnurl
Returns a new instance of Lnurl.
32 33 34 |
# File 'lib/lnurl.rb', line 32 def initialize(uri) @uri = URI(uri) end |
Instance Attribute Details
#uri ⇒ Object (readonly)
Returns the value of attribute uri.
30 31 32 |
# File 'lib/lnurl.rb', line 30 def uri @uri end |
Class Method Details
.convert_bits(data, from, to, padding = true) ⇒ Object
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/lnurl.rb', line 89 def self.convert_bits(data, from, to, padding=true) acc = 0 bits = 0 ret = [] maxv = (1 << to) - 1 max_acc = (1 << (from + to - 1)) - 1 data.each do |v| return nil if v < 0 || (v >> from) != 0 acc = ((acc << from) | v) & max_acc bits += from while bits >= to bits -= to ret << ((acc >> bits) & maxv) end end if padding ret << ((acc << (to - bits)) & maxv) unless bits == 0 elsif bits >= from || ((acc << (to - bits)) & maxv) != 0 return nil end ret end |
.decode(lnurl, max_length = MAX_INTEGER) ⇒ Object
68 69 70 |
# File 'lib/lnurl.rb', line 68 def self.decode(lnurl, max_length = MAX_INTEGER) Lnurl.new(decode_raw(lnurl, max_length)) end |
.decode_lightning_address(lightning_address) ⇒ Object
83 84 85 86 |
# File 'lib/lnurl.rb', line 83 def self.decode_lightning_address(lightning_address) username, domain = lightning_address.split('@') "https://#{domain}/.well-known/lnurlp/#{username}" end |
.decode_raw(lnurl, max_length = MAX_INTEGER) ⇒ Object
72 73 74 75 76 77 |
# File 'lib/lnurl.rb', line 72 def self.decode_raw(lnurl, max_length = MAX_INTEGER) lnurl = lnurl.gsub(/^lightning:/, '') hrp, data, sepc = Bech32.decode(lnurl, max_length) # raise 'no lnurl' if hrp != HRP convert_bits(data, 5, 8, false).pack('C*').force_encoding('utf-8') end |
.from_lightning_address(lightning_address) ⇒ Object
79 80 81 |
# File 'lib/lnurl.rb', line 79 def self.from_lightning_address(lightning_address) Lnurl.new(decode_lightning_address(lightning_address)) end |
.http_get(uri, limit = 10) ⇒ Object
Handles HTTP GET requests and follows redirects if necessary
113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/lnurl.rb', line 113 def self.http_get(uri, limit = 10) raise ArgumentError, 'too many HTTP redirects' if limit.zero? response = Net::HTTP.get_response(uri) case response when Net::HTTPRedirection location = response['location'] http_get(URI(location), limit - 1) else response.body end end |
.valid?(value) ⇒ Boolean
60 61 62 63 64 65 66 |
# File 'lib/lnurl.rb', line 60 def self.valid?(value) return false unless value.to_s.downcase.match?(Regexp.new("^#{HRP}", 'i')) # false if the HRP does not match decoded = decode_raw(value) rescue false # rescue any decoding errors return false unless decoded # false if it could not get decoded return decoded.match?(URI.regexp) # check if the URI is valid end |
Instance Method Details
#data ⇒ Object
41 42 43 |
# File 'lib/lnurl.rb', line 41 def data self.class.convert_bits(uri.to_s.codepoints, 8, 5, true) end |
#payment_request(amount:) ⇒ Object
56 57 58 |
# File 'lib/lnurl.rb', line 56 def payment_request(amount:) request_invoice(amount: amount).pr end |
#request_invoice(amount:) ⇒ Object
52 53 54 |
# File 'lib/lnurl.rb', line 52 def request_invoice(amount:) response.request_invoice(amount: amount) end |
#response ⇒ Object
45 46 47 48 49 50 |
# File 'lib/lnurl.rb', line 45 def response @response ||= begin body = self.class.http_get(uri) LnurlResponse.new JSON.parse(body) end end |
#to_bech32 ⇒ Object Also known as: encode
36 37 38 |
# File 'lib/lnurl.rb', line 36 def to_bech32 Bech32.encode(HRP, data, Bech32::Encoding::BECH32).upcase end |