lighstorm 0.0.8 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.env.example +1 -0
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +1 -0
  5. data/Gemfile +5 -3
  6. data/Gemfile.lock +13 -8
  7. data/README.md +11 -3
  8. data/Rakefile +11 -0
  9. data/adapters/edges/payment/purpose.rb +22 -11
  10. data/adapters/edges/payment.rb +51 -12
  11. data/adapters/invoice.rb +133 -31
  12. data/components/cache.rb +7 -2
  13. data/controllers/forward/group_by_channel.rb +1 -1
  14. data/controllers/invoice/actions/create.rb +22 -6
  15. data/controllers/invoice/actions/pay.rb +79 -13
  16. data/controllers/invoice/all.rb +16 -6
  17. data/controllers/invoice/decode.rb +6 -6
  18. data/controllers/invoice/find_by_secret_hash.rb +7 -1
  19. data/controllers/invoice.rb +15 -6
  20. data/controllers/node/actions/pay.rb +114 -0
  21. data/controllers/payment/actions/pay.rb +104 -0
  22. data/controllers/payment/all.rb +79 -28
  23. data/controllers/transaction/all.rb +54 -0
  24. data/controllers/transaction.rb +13 -0
  25. data/deleted.sh +1 -0
  26. data/docs/README.md +307 -51
  27. data/docs/_coverpage.md +1 -1
  28. data/docs/index.html +1 -1
  29. data/helpers/time_expression.rb +33 -0
  30. data/models/connections/payment_channel.rb +13 -8
  31. data/models/edges/channel.rb +1 -1
  32. data/models/edges/payment.rb +51 -20
  33. data/models/errors.rb +29 -1
  34. data/models/invoice.rb +69 -11
  35. data/models/nodes/node.rb +35 -0
  36. data/models/satoshis.rb +11 -3
  37. data/models/secret.rb +37 -0
  38. data/models/transaction.rb +42 -0
  39. data/ports/dsl/lighstorm.rb +2 -0
  40. data/static/cache.rb +14 -13
  41. data/static/spec.rb +1 -1
  42. metadata +11 -4
  43. data/adapters/payment_request.rb +0 -87
  44. data/models/payment_request.rb +0 -72
@@ -2,25 +2,91 @@
2
2
 
3
3
  require_relative '../../../ports/grpc'
4
4
  require_relative '../../../models/errors'
5
+ require_relative '../../../models/edges/payment'
6
+ require_relative '../../../adapters/edges/payment'
7
+ require_relative '../../invoice'
8
+ require_relative '../../action'
9
+ require_relative '../../node/myself'
10
+
11
+ require_relative '../../payment/actions/pay'
5
12
 
6
13
  module Lighstorm
7
14
  module Controllers
8
15
  module Invoice
9
16
  module Pay
10
- def self.perform(_invoice, preview: false, fake: false)
11
- raise Errors::ToDoError, self
12
-
13
- LND.instance.middleware('router.send_payment_v2') do
14
- result = []
15
- LND.instance.client.router.send_payment_v2(
16
- payment_request: request,
17
- timeout_seconds: 5,
18
- allow_self_payment: true
19
- ) do |response|
20
- result << response
21
- end
22
- result
17
+ def self.dispatch(grpc_request, &vcr)
18
+ Payment::Pay.dispatch(grpc_request, &vcr)
19
+ end
20
+
21
+ def self.fetch(code, &vcr)
22
+ Payment::Pay.fetch(code, &vcr)
23
+ end
24
+
25
+ def self.adapt(data, node_get_info)
26
+ Payment::Pay.adapt(data, node_get_info)
27
+ end
28
+
29
+ def self.model(data)
30
+ Payment::Pay.model(data)
31
+ end
32
+
33
+ def self.prepare(code:, times_out_in:, amount: nil, fee: nil, message: nil)
34
+ request = {
35
+ service: :router,
36
+ method: :send_payment_v2,
37
+ params: {
38
+ payment_request: code,
39
+ timeout_seconds: Helpers::TimeExpression.seconds(times_out_in),
40
+ allow_self_payment: true,
41
+ dest_custom_records: {}
42
+ }
43
+ }
44
+
45
+ request[:params][:amt_msat] = amount[:millisatoshis] unless amount.nil?
46
+
47
+ unless fee.nil? || fee[:maximum].nil? || fee[:maximum][:millisatoshis].nil?
48
+ request[:params][:fee_limit_msat] = fee[:maximum][:millisatoshis]
49
+ end
50
+
51
+ if !message.nil? && !message.empty?
52
+ # https://github.com/satoshisstream/satoshis.stream/blob/main/TLV_registry.md
53
+ request[:params][:dest_custom_records][34_349_334] = message
23
54
  end
55
+
56
+ request[:params].delete(:dest_custom_records) if request[:params][:dest_custom_records].empty?
57
+
58
+ request
59
+ end
60
+
61
+ def self.perform(
62
+ times_out_in:, code:,
63
+ amount: nil, fee: nil,
64
+ message: nil,
65
+ preview: false, &vcr
66
+ )
67
+ grpc_request = prepare(
68
+ code: code,
69
+ amount: amount,
70
+ fee: fee,
71
+ message: message,
72
+ times_out_in: times_out_in
73
+ )
74
+
75
+ return grpc_request if preview
76
+
77
+ response = dispatch(grpc_request, &vcr)
78
+
79
+ Payment::Pay.raise_error_if_exists!(response)
80
+
81
+ data = fetch(code, &vcr)
82
+
83
+ adapted = adapt(response, data)
84
+
85
+ model = self.model(adapted)
86
+
87
+ Payment::Pay.raise_failure_if_exists!(model, response)
88
+
89
+ Action::Output.new({ response: response[:response], result: model })
24
90
  end
25
91
  end
26
92
  end
@@ -8,7 +8,9 @@ module Lighstorm
8
8
  module Controllers
9
9
  module Invoice
10
10
  module All
11
- def self.fetch(limit: nil)
11
+ def self.fetch(limit: nil, spontaneous: false)
12
+ at = Time.now
13
+
12
14
  last_offset = 0
13
15
 
14
16
  invoices = []
@@ -19,7 +21,9 @@ module Lighstorm
19
21
  num_max_invoices: 10
20
22
  )
21
23
 
22
- response.invoices.each { |invoice| invoices << invoice.to_h }
24
+ response.invoices.each do |invoice|
25
+ invoices << invoice.to_h if spontaneous || !invoice.payment_request.empty?
26
+ end
23
27
 
24
28
  # TODO: How to optimize this?
25
29
  # break if !limit.nil? && invoices.size >= limit
@@ -33,13 +37,15 @@ module Lighstorm
33
37
 
34
38
  invoices = invoices[0..limit - 1] unless limit.nil?
35
39
 
36
- { list_invoices: invoices }
40
+ { at: at, list_invoices: invoices }
37
41
  end
38
42
 
39
43
  def self.adapt(raw)
44
+ raise 'missing at' if raw[:at].nil?
45
+
40
46
  {
41
47
  list_invoices: raw[:list_invoices].map do |raw_invoice|
42
- Lighstorm::Adapter::Invoice.list_invoices(raw_invoice)
48
+ Lighstorm::Adapter::Invoice.list_invoices(raw_invoice, raw[:at])
43
49
  end
44
50
  }
45
51
  end
@@ -51,8 +57,12 @@ module Lighstorm
51
57
  end
52
58
  end
53
59
 
54
- def self.data(limit: nil, &vcr)
55
- raw = vcr.nil? ? fetch(limit: limit) : vcr.call(-> { fetch(limit: limit) })
60
+ def self.data(limit: nil, spontaneous: false, &vcr)
61
+ raw = if vcr.nil?
62
+ fetch(limit: limit, spontaneous: spontaneous)
63
+ else
64
+ vcr.call(-> { fetch(limit: limit, spontaneous: spontaneous) })
65
+ end
56
66
 
57
67
  adapted = adapt(raw)
58
68
 
@@ -8,17 +8,17 @@ module Lighstorm
8
8
  module Controllers
9
9
  module Invoice
10
10
  module Decode
11
- def self.fetch(request_code)
11
+ def self.fetch(code)
12
12
  {
13
- _request_code: request_code,
14
- decode_pay_req: Ports::GRPC.lightning.decode_pay_req(pay_req: request_code).to_h
13
+ _code: code,
14
+ decode_pay_req: Ports::GRPC.lightning.decode_pay_req(pay_req: code).to_h
15
15
  }
16
16
  end
17
17
 
18
18
  def self.adapt(raw)
19
19
  {
20
20
  decode_pay_req: Lighstorm::Adapter::Invoice.decode_pay_req(
21
- raw[:decode_pay_req], raw[:_request_code]
21
+ raw[:decode_pay_req], raw[:_code]
22
22
  )
23
23
  }
24
24
  end
@@ -27,8 +27,8 @@ module Lighstorm
27
27
  adapted[:decode_pay_req]
28
28
  end
29
29
 
30
- def self.data(request_code, &vcr)
31
- raw = vcr.nil? ? fetch(request_code) : vcr.call(-> { fetch(request_code) })
30
+ def self.data(code, &vcr)
31
+ raw = vcr.nil? ? fetch(code) : vcr.call(-> { fetch(code) })
32
32
 
33
33
  adapted = adapt(raw)
34
34
 
@@ -10,13 +10,19 @@ module Lighstorm
10
10
  module FindBySecretHash
11
11
  def self.fetch(secret_hash)
12
12
  {
13
+ at: Time.now,
13
14
  lookup_invoice: Ports::GRPC.lightning.lookup_invoice(r_hash_str: secret_hash).to_h
14
15
  }
15
16
  end
16
17
 
17
18
  def self.adapt(raw)
19
+ raise 'missing at' if raw[:at].nil?
20
+
18
21
  {
19
- lookup_invoice: Lighstorm::Adapter::Invoice.lookup_invoice(raw[:lookup_invoice])
22
+ lookup_invoice: Lighstorm::Adapter::Invoice.lookup_invoice(
23
+ raw[:lookup_invoice],
24
+ raw[:at]
25
+ )
20
26
  }
21
27
  end
22
28
 
@@ -8,8 +8,8 @@ require_relative './invoice/actions/create'
8
8
  module Lighstorm
9
9
  module Controllers
10
10
  module Invoice
11
- def self.all(limit: nil)
12
- All.model(All.data(limit: limit))
11
+ def self.all(limit: nil, spontaneous: false)
12
+ All.model(All.data(limit: limit, spontaneous: spontaneous))
13
13
  end
14
14
 
15
15
  def self.first
@@ -24,14 +24,23 @@ module Lighstorm
24
24
  FindBySecretHash.model(FindBySecretHash.data(secret_hash))
25
25
  end
26
26
 
27
- def self.decode(request_code, &vcr)
28
- Decode.model(Decode.data(request_code, &vcr))
27
+ def self.decode(code, &vcr)
28
+ Decode.model(Decode.data(code, &vcr))
29
29
  end
30
30
 
31
- def self.create(description: nil, millisatoshis: nil, preview: false, &vcr)
31
+ def self.create(
32
+ payable:,
33
+ description: nil, amount: nil,
34
+ # Lightning Invoice Expiration: UX Considerations
35
+ # https://d.elor.me/2022/01/lightning-invoice-expiration-ux-considerations/
36
+ expires_in: { hours: 24 },
37
+ preview: false, &vcr
38
+ )
32
39
  Create.perform(
40
+ payable: payable,
33
41
  description: description,
34
- millisatoshis: millisatoshis,
42
+ amount: amount,
43
+ expires_in: expires_in,
35
44
  preview: preview,
36
45
  &vcr
37
46
  )
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+ require 'digest'
5
+
6
+ require_relative '../../../ports/grpc'
7
+ require_relative '../../../models/secret'
8
+ require_relative '../../../models/errors'
9
+ require_relative '../../../models/edges/payment'
10
+ require_relative '../../../adapters/edges/payment'
11
+ require_relative '../../invoice'
12
+ require_relative '../../action'
13
+ require_relative '../../node/myself'
14
+
15
+ require_relative '../../payment/actions/pay'
16
+
17
+ module Lighstorm
18
+ module Controllers
19
+ module Node
20
+ module Pay
21
+ def self.dispatch(grpc_request, &vcr)
22
+ Payment::Pay.dispatch(grpc_request, &vcr)
23
+ end
24
+
25
+ def self.fetch(&vcr)
26
+ Payment::Pay.fetch(&vcr)
27
+ end
28
+
29
+ def self.adapt(data, node_get_info)
30
+ Payment::Pay.adapt(data, node_get_info)
31
+ end
32
+
33
+ def self.model(data)
34
+ Payment::Pay.model(data)
35
+ end
36
+
37
+ def self.prepare(public_key:, amount:, times_out_in:, secret:, through:, fee: nil, message: nil)
38
+ # Appreciation note for people that suffered in the past and shared
39
+ # their knowledge, so we don't have to struggle the same:
40
+ # - https://github.com/lightningnetwork/lnd/discussions/6357
41
+ # - https://docs.lightning.engineering/lightning-network-tools/lnd/send-messages-with-keysend
42
+ # - https://peakd.com/@brianoflondon/lightning-keysend-is-strange-and-how-to-send-keysend-payment-in-lightning-with-the-lnd-rest-api-via-python
43
+ # We are standing on the shoulders of giants, thank you very much. :)
44
+ request = {
45
+ service: :router,
46
+ method: :send_payment_v2,
47
+ params: {
48
+ dest: [public_key].pack('H*'),
49
+ amt_msat: amount[:millisatoshis],
50
+ timeout_seconds: Helpers::TimeExpression.seconds(times_out_in),
51
+ allow_self_payment: true,
52
+ dest_custom_records: {}
53
+ }
54
+ }
55
+
56
+ if !message.nil? && !message.empty?
57
+ # https://github.com/satoshisstream/satoshis.stream/blob/main/TLV_registry.md
58
+ request[:params][:dest_custom_records][34_349_334] = message
59
+ end
60
+
61
+ unless fee.nil? || fee[:maximum].nil? || fee[:maximum][:millisatoshis].nil?
62
+ request[:params][:fee_limit_msat] = fee[:maximum][:millisatoshis]
63
+ end
64
+
65
+ if through.to_sym == :keysend
66
+ request[:params][:payment_hash] = [secret[:hash]].pack('H*')
67
+ request[:params][:dest_custom_records][5_482_373_484] = [secret[:preimage]].pack('H*')
68
+ elsif through.to_sym == :amp
69
+ request[:params][:amp] = true
70
+ end
71
+
72
+ request[:params].delete(:dest_custom_records) if request[:params][:dest_custom_records].empty?
73
+
74
+ request
75
+ end
76
+
77
+ def self.perform(
78
+ public_key:, amount:, through:,
79
+ times_out_in:, fee: nil,
80
+ message: nil, secret: nil,
81
+ preview: false, &vcr
82
+ )
83
+ secret = Models::Secret.create.to_h if secret.nil? && through.to_sym == :keysend
84
+
85
+ grpc_request = prepare(
86
+ public_key: public_key,
87
+ amount: amount,
88
+ fee: fee,
89
+ through: through,
90
+ times_out_in: times_out_in,
91
+ secret: secret,
92
+ message: message
93
+ )
94
+
95
+ return grpc_request if preview
96
+
97
+ response = dispatch(grpc_request, &vcr)
98
+
99
+ Payment::Pay.raise_error_if_exists!(response)
100
+
101
+ data = fetch(&vcr)
102
+
103
+ adapted = adapt(response, data)
104
+
105
+ model = self.model(adapted)
106
+
107
+ Payment::Pay.raise_failure_if_exists!(model, response)
108
+
109
+ Action::Output.new({ response: response[:response], result: model })
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+ require 'digest'
5
+
6
+ require_relative '../../../ports/grpc'
7
+ require_relative '../../../models/errors'
8
+ require_relative '../../../models/edges/payment'
9
+ require_relative '../../../adapters/edges/payment'
10
+ require_relative '../../node/myself'
11
+ require_relative '../../invoice/decode'
12
+
13
+ module Lighstorm
14
+ module Controllers
15
+ module Payment
16
+ module Pay
17
+ def self.call(grpc_request)
18
+ result = []
19
+ Lighstorm::Ports::GRPC.send(grpc_request[:service]).send(
20
+ grpc_request[:method], grpc_request[:params]
21
+ ) do |response|
22
+ result << response.to_h
23
+ end
24
+ { response: result, exception: nil }
25
+ rescue StandardError => e
26
+ { exception: e }
27
+ end
28
+
29
+ def self.dispatch(grpc_request, &vcr)
30
+ vcr.nil? ? call(grpc_request) : vcr.call(-> { call(grpc_request) }, :dispatch)
31
+ end
32
+
33
+ def self.fetch_all(code)
34
+ {
35
+ invoice_decode: code.nil? ? nil : Invoice::Decode.data(code),
36
+ node_myself: Node::Myself.data
37
+ }
38
+ end
39
+
40
+ def self.fetch(code = nil, &vcr)
41
+ raw = vcr.nil? ? fetch_all(code) : vcr.call(-> { fetch_all(code) })
42
+ end
43
+
44
+ def self.adapt(grpc_data, fetch_data)
45
+ Adapter::Payment.send_payment_v2(
46
+ grpc_data[:response].last,
47
+ fetch_data[:node_myself],
48
+ fetch_data[:invoice_decode]
49
+ )
50
+ end
51
+
52
+ def self.model(data)
53
+ Models::Payment.new(data)
54
+ end
55
+
56
+ def self.raise_error_if_exists!(response)
57
+ return if response[:exception].nil?
58
+
59
+ if response[:exception].is_a?(GRPC::AlreadyExists)
60
+ raise AlreadyPaidError.new(
61
+ 'The invoice is already paid.',
62
+ grpc: response[:exception]
63
+ )
64
+ end
65
+
66
+ if response[:exception].message =~ /amount must not be specified when paying a non-zero/
67
+ raise AmountForNonZeroError.new(
68
+ 'Millisatoshis must not be specified when paying a non-zero amount invoice.',
69
+ grpc: response[:exception]
70
+ )
71
+ end
72
+
73
+ if response[:exception].message =~ /amount must be specified when paying a zero amount/
74
+ raise MissingMillisatoshisError.new(
75
+ 'Millisatoshis must be specified when paying a zero amount invoice.',
76
+ grpc: response[:exception]
77
+ )
78
+ end
79
+
80
+ raise PaymentError.new(
81
+ response[:exception].message,
82
+ grpc: response[:exception]
83
+ )
84
+ end
85
+
86
+ def self.raise_failure_if_exists!(model, response)
87
+ return unless model.state == 'failed'
88
+
89
+ if response[:response].last[:failure_reason] == :FAILURE_REASON_NO_ROUTE
90
+ raise NoRouteFoundError.new(
91
+ response[:response].last[:failure_reason],
92
+ response: response[:response], result: model
93
+ )
94
+ else
95
+ raise PaymentError.new(
96
+ response[:response].last[:failure_reason],
97
+ response: response[:response], result: model
98
+ )
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -108,9 +108,13 @@ module Lighstorm
108
108
 
109
109
  next if fetch[:get_node_info] == false || data[:get_node_info][hop[:pub_key]]
110
110
 
111
- data[:get_node_info][hop[:pub_key]] = grpc.lightning.get_node_info(
112
- pub_key: hop[:pub_key]
113
- ).to_h
111
+ begin
112
+ data[:get_node_info][hop[:pub_key]] = grpc.lightning.get_node_info(
113
+ pub_key: hop[:pub_key]
114
+ ).to_h
115
+ rescue StandardError => e
116
+ data[:get_node_info][hop[:pub_key]] = { _error: e }
117
+ end
114
118
  end
115
119
  end
116
120
  end
@@ -151,24 +155,36 @@ module Lighstorm
151
155
  end
152
156
 
153
157
  unless fetch[:get_node_info] == false || data[:get_node_info][channel[:node1_pub]]
154
- data[:get_node_info][channel[:node1_pub]] = grpc.lightning.get_node_info(
155
- pub_key: channel[:node1_pub]
156
- ).to_h
158
+ begin
159
+ data[:get_node_info][channel[:node1_pub]] = grpc.lightning.get_node_info(
160
+ pub_key: channel[:node1_pub]
161
+ ).to_h
162
+ rescue StandardError => e
163
+ data[:get_node_info][channel[:node1_pub]] = { _error: e }
164
+ end
157
165
  end
158
166
 
159
167
  next if fetch[:get_node_info] == false || data[:get_node_info][channel[:node2_pub]]
160
168
 
161
- data[:get_node_info][channel[:node2_pub]] = grpc.lightning.get_node_info(
162
- pub_key: channel[:node2_pub]
163
- ).to_h
169
+ begin
170
+ data[:get_node_info][channel[:node2_pub]] = grpc.lightning.get_node_info(
171
+ pub_key: channel[:node2_pub]
172
+ ).to_h
173
+ rescue StandardError => e
174
+ data[:get_node_info][channel[:node2_pub]] = { _error: e }
175
+ end
164
176
  end
165
177
 
166
178
  data[:list_channels].each_value do |channel|
167
179
  next if fetch[:get_node_info] == false || data[:get_node_info][channel[:remote_pubkey]]
168
180
 
169
- data[:get_node_info][channel[:remote_pubkey]] = grpc.lightning.get_node_info(
170
- pub_key: channel[:remote_pubkey]
171
- ).to_h
181
+ begin
182
+ data[:get_node_info][channel[:remote_pubkey]] = grpc.lightning.get_node_info(
183
+ pub_key: channel[:remote_pubkey]
184
+ ).to_h
185
+ rescue StandardError => e
186
+ data[:get_node_info][channel[:remote_pubkey]] = { _error: e }
187
+ end
172
188
  end
173
189
 
174
190
  data[:@meta] = { calls: grpc.calls }
@@ -195,7 +211,7 @@ module Lighstorm
195
211
  raw[:decode_pay_req].each_key do |key|
196
212
  next if raw[:decode_pay_req][key][:_error]
197
213
 
198
- adapted[:decode_pay_req][key] = Lighstorm::Adapter::PaymentRequest.decode_pay_req(
214
+ adapted[:decode_pay_req][key] = Lighstorm::Adapter::Invoice.decode_pay_req(
199
215
  raw[:decode_pay_req][key]
200
216
  )
201
217
  end
@@ -204,7 +220,7 @@ module Lighstorm
204
220
  next if raw[:lookup_invoice][key][:_error]
205
221
 
206
222
  adapted[:lookup_invoice][key] = Lighstorm::Adapter::Invoice.lookup_invoice(
207
- raw[:lookup_invoice][key]
223
+ raw[:lookup_invoice][key], raw[:at]
208
224
  )
209
225
  end
210
226
 
@@ -223,6 +239,8 @@ module Lighstorm
223
239
  end
224
240
 
225
241
  raw[:get_node_info].each_key do |key|
242
+ next if raw[:get_node_info][key][:_error]
243
+
226
244
  adapted[:get_node_info][key] = Lighstorm::Adapter::Node.get_node_info(
227
245
  raw[:get_node_info][key]
228
246
  )
@@ -309,34 +327,67 @@ module Lighstorm
309
327
  end
310
328
 
311
329
  def self.transform(list_payments, adapted)
312
- if adapted[:lookup_invoice][list_payments[:request][:secret][:hash]] &&
313
- !adapted[:lookup_invoice][list_payments[:request][:secret][:hash]][:_error]
330
+ if adapted[:lookup_invoice][list_payments[:secret][:hash]] &&
331
+ !adapted[:lookup_invoice][list_payments[:secret][:hash]][:_error]
314
332
 
315
- list_payments[:request] = adapted[:lookup_invoice][list_payments[:request][:secret][:hash]][:request]
316
- else
317
- list_payments[:request][:_key] = Digest::SHA256.hexdigest(
318
- list_payments[:request][:code]
319
- )
333
+ if list_payments[:invoice]
334
+ lookup = adapted[:lookup_invoice][list_payments[:secret][:hash]]
335
+
336
+ list_payments[:invoice][:description] = lookup[:description]
337
+
338
+ lookup.each_key do |key|
339
+ if lookup[key].is_a?(Hash)
340
+ unless list_payments[:invoice].key?(key) && !list_payments[:invoice][key].nil?
341
+ list_payments[:invoice][key] = lookup[:key]
342
+ end
343
+
344
+ next
345
+ end
346
+
347
+ unless list_payments[:invoice].key?(key) && !list_payments[:invoice][key].nil? &&
348
+ (!list_payments[:invoice][key].is_a?(String) || !list_payments[:invoice][key].empty?)
349
+ list_payments[:invoice][key] = lookup[key]
350
+ end
351
+ end
352
+ else
353
+ list_payments[:invoice] = adapted[:lookup_invoice][list_payments[:secret][:hash]]
354
+ end
320
355
  end
356
+
321
357
  list_payments[:hops].each do |hop|
322
358
  hop[:channel] = transform_channel(hop[:channel], adapted)
323
359
  end
324
360
 
325
- if adapted[:decode_pay_req][list_payments[:request][:code]]
326
- decoded = adapted[:decode_pay_req][list_payments[:request][:code]]
327
- request = list_payments[:request]
361
+ if adapted[:decode_pay_req][list_payments[:invoice][:code]]
362
+ decoded = adapted[:decode_pay_req][list_payments[:invoice][:code]]
363
+ invoice = list_payments[:invoice]
328
364
 
329
365
  decoded.each_key do |key|
330
- request[key] = decoded[key] unless request.key?(key)
366
+ if !decoded[key].is_a?(Hash)
367
+ invoice[key] = decoded[key]
368
+ elsif decoded[key].is_a?(Hash)
369
+ invoice[key] = {} unless invoice.key?(key)
331
370
 
332
- next unless decoded[key].is_a?(Hash)
371
+ next if key == :secret
333
372
 
334
- decoded[key].each_key do |sub_key|
335
- request[key][sub_key] = decoded[key][sub_key] unless request[key].key?(sub_key)
373
+ decoded[key].each_key do |sub_key|
374
+ next if decoded[key][sub_key].nil? ||
375
+ (decoded[key][sub_key].is_a?(String) && decoded[key][sub_key].empty?)
376
+
377
+ invoice[key][sub_key] = decoded[key][sub_key]
378
+ end
336
379
  end
337
380
  end
338
381
  end
339
382
 
383
+ if list_payments[:invoice][:code]
384
+ if list_payments[:invoice][:payable] == 'once'
385
+ list_payments[:through] = 'non-amp'
386
+ elsif list_payments[:invoice][:payable] == 'indefinitely'
387
+ list_payments[:through] = 'amp'
388
+ end
389
+ end
390
+
340
391
  list_payments
341
392
  end
342
393