lighstorm 0.0.2 → 0.0.4

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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/.env.example +5 -0
  3. data/.gitignore +1 -0
  4. data/.rspec +1 -0
  5. data/.rubocop.yml +10 -0
  6. data/Gemfile +5 -1
  7. data/Gemfile.lock +32 -8
  8. data/README.md +30 -341
  9. data/adapters/connections/channel_node/fee.rb +26 -0
  10. data/adapters/connections/channel_node.rb +52 -0
  11. data/adapters/connections/payment_channel.rb +28 -0
  12. data/adapters/edges/channel.rb +80 -0
  13. data/adapters/edges/forward.rb +41 -0
  14. data/adapters/edges/payment/purpose.rb +34 -0
  15. data/adapters/edges/payment.rb +53 -0
  16. data/adapters/invoice.rb +50 -0
  17. data/adapters/nodes/node.rb +77 -0
  18. data/adapters/payment_request.rb +77 -0
  19. data/components/cache.rb +3 -2
  20. data/components/lnd.rb +5 -1
  21. data/controllers/channel/actions/update_fee.rb +76 -0
  22. data/controllers/channel/all.rb +79 -0
  23. data/controllers/channel/find_by_id.rb +153 -0
  24. data/controllers/channel/mine.rb +114 -0
  25. data/controllers/channel.rb +23 -0
  26. data/controllers/forward/all.rb +244 -0
  27. data/controllers/forward/group_by_channel.rb +89 -0
  28. data/controllers/forward.rb +28 -0
  29. data/controllers/invoice/actions/create.rb +36 -0
  30. data/controllers/invoice/actions/pay.rb +28 -0
  31. data/controllers/invoice/actions/pay_through_route.rb +71 -0
  32. data/controllers/invoice/all.rb +70 -0
  33. data/controllers/invoice/find_by_secret_hash.rb +42 -0
  34. data/controllers/invoice.rb +36 -0
  35. data/controllers/node/all.rb +63 -0
  36. data/controllers/node/find_by_public_key.rb +49 -0
  37. data/controllers/node/myself.rb +34 -0
  38. data/controllers/node.rb +23 -0
  39. data/controllers/payment/all.rb +352 -0
  40. data/controllers/payment.rb +21 -0
  41. data/docs/.nojekyll +0 -0
  42. data/docs/README.md +655 -0
  43. data/docs/_coverpage.md +12 -0
  44. data/docs/index.html +26 -0
  45. data/docs/vendor/docsify/docsify@4.js +1 -0
  46. data/docs/vendor/docsify-themeable/theme-simple-dark.css +3 -0
  47. data/docs/vendor/docsify-themeable/theme-simple-dark.css.map +1 -0
  48. data/docs/vendor/prismjs/prism-bash.min.js +1 -0
  49. data/docs/vendor/prismjs/prism-ruby.min.js +1 -0
  50. data/docs/vendor/prismjs/prism-tomorrow.min.css +1 -0
  51. data/lighstorm.gemspec +4 -2
  52. data/models/connections/channel_node/accounting.rb +3 -12
  53. data/models/connections/channel_node/fee.rb +44 -56
  54. data/models/connections/channel_node/htlc.rb +52 -0
  55. data/models/connections/channel_node/policy.rb +11 -12
  56. data/models/connections/channel_node.rb +20 -19
  57. data/models/connections/forward_channel.rb +8 -43
  58. data/models/connections/payment_channel.rb +18 -39
  59. data/models/edges/channel/accounting.rb +42 -18
  60. data/models/edges/channel/hop.rb +65 -0
  61. data/models/edges/channel.rb +98 -120
  62. data/models/edges/forward.rb +9 -118
  63. data/models/edges/groups/{analysis.rb → channel_forwards/analysis.rb} +9 -9
  64. data/models/edges/groups/channel_forwards.rb +10 -40
  65. data/models/edges/payment.rb +29 -214
  66. data/models/errors.rb +29 -0
  67. data/models/invoice.rb +49 -0
  68. data/models/nodes/node/lightning.rb +5 -16
  69. data/models/nodes/node/platform.rb +7 -13
  70. data/models/nodes/node.rb +19 -56
  71. data/models/payment_request.rb +69 -0
  72. data/models/rate.rb +11 -1
  73. data/models/satoshis.rb +5 -6
  74. data/ports/dsl/lighstorm/errors.rb +5 -0
  75. data/ports/dsl/lighstorm.rb +12 -8
  76. data/ports/grpc.rb +62 -0
  77. data/static/cache.rb +12 -0
  78. data/static/spec.rb +3 -1
  79. metadata +58 -8
  80. data/models/connections/channel_node/constraints.rb +0 -24
@@ -0,0 +1,352 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../ports/grpc'
4
+ require_relative '../../adapters/nodes/node'
5
+ require_relative '../../adapters/edges/payment'
6
+ require_relative '../../adapters/invoice'
7
+ require_relative '../../adapters/edges/payment/purpose'
8
+
9
+ require_relative '../../models/edges/payment'
10
+
11
+ module Lighstorm
12
+ module Controllers
13
+ module Payment
14
+ module All
15
+ def self.fetch(purpose: nil, limit: nil)
16
+ at = Time.now
17
+ get_info = Ports::GRPC.lightning.get_info.to_h
18
+
19
+ last_offset = 0
20
+
21
+ payments = []
22
+
23
+ loop do
24
+ response = Ports::GRPC.lightning.list_payments(
25
+ index_offset: last_offset
26
+ )
27
+
28
+ response.payments.each do |payment|
29
+ payment = payment.to_h
30
+
31
+ payment_purpose = Adapter::Purpose.list_payments(payment, get_info)
32
+
33
+ case purpose
34
+ when 'self-payment', 'self'
35
+ payments << payment if payment_purpose == 'self-payment'
36
+ when 'peer-to-peer', 'p2p'
37
+ payments << payment if payment_purpose == 'peer-to-peer'
38
+ when '!peer-to-peer', '!p2p'
39
+ payments << payment unless payment_purpose == 'peer-to-peer'
40
+ when 'rebalance'
41
+ payments << payment if payment_purpose == 'rebalance'
42
+ when '!rebalance'
43
+ payments << payment unless payment_purpose == 'rebalance'
44
+ when '!payment'
45
+ payments << payment unless payment_purpose == 'payment'
46
+ when 'payment'
47
+ payments << payment if payment_purpose == 'payment'
48
+ else
49
+ payments << payment
50
+ end
51
+ end
52
+
53
+ # TODO: How to optimize this?
54
+ # break if !limit.nil? && payments.size >= limit
55
+
56
+ break if last_offset == response.last_index_offset || last_offset > response.last_index_offset
57
+
58
+ last_offset = response.last_index_offset
59
+ end
60
+
61
+ payments = payments.sort_by { |raw_payment| -raw_payment[:creation_time_ns] }
62
+
63
+ payments = payments[0..limit - 1] unless limit.nil?
64
+
65
+ data = {
66
+ at: at,
67
+ get_info: Ports::GRPC.lightning.get_info.to_h,
68
+ fee_report: Ports::GRPC.lightning.fee_report.to_h,
69
+ list_payments: payments,
70
+ list_channels: {},
71
+ get_chan_info: {},
72
+ get_node_info: {},
73
+ lookup_invoice: {},
74
+ decode_pay_req: {}
75
+ }
76
+
77
+ payments.each do |payment|
78
+ unless payment[:payment_request] == '' || data[:decode_pay_req][payment[:payment_request]]
79
+ data[:decode_pay_req][payment[:payment_request]] = Ports::GRPC.lightning.decode_pay_req(
80
+ pay_req: payment[:payment_request]
81
+ ).to_h
82
+ end
83
+
84
+ unless data[:lookup_invoice][payment[:payment_hash]]
85
+ begin
86
+ data[:lookup_invoice][payment[:payment_hash]] = Ports::GRPC.lightning.lookup_invoice(
87
+ r_hash_str: payment[:payment_hash]
88
+ ).to_h
89
+ rescue StandardError => e
90
+ data[:lookup_invoice][payment[:payment_hash]] = { _error: e }
91
+ end
92
+ end
93
+
94
+ payment[:htlcs].each do |htlc|
95
+ htlc[:route][:hops].each do |hop|
96
+ unless data[:get_chan_info][hop[:chan_id]]
97
+ begin
98
+ data[:get_chan_info][hop[:chan_id]] = Ports::GRPC.lightning.get_chan_info(
99
+ chan_id: hop[:chan_id]
100
+ ).to_h
101
+ rescue GRPC::Unknown => e
102
+ data[:get_chan_info][hop[:chan_id]] = { _error: e }
103
+ end
104
+ end
105
+
106
+ next if data[:get_node_info][hop[:pub_key]]
107
+
108
+ data[:get_node_info][hop[:pub_key]] = Ports::GRPC.lightning.get_node_info(
109
+ pub_key: hop[:pub_key]
110
+ ).to_h
111
+ end
112
+ end
113
+ end
114
+
115
+ data[:lookup_invoice].each_value do |invoice|
116
+ if invoice[:_error] || invoice[:payment_request] == '' || data[:decode_pay_req][invoice[:payment_request]]
117
+ next
118
+ end
119
+
120
+ data[:decode_pay_req][invoice[:payment_request]] = Ports::GRPC.lightning.decode_pay_req(
121
+ pay_req: invoice[:payment_request]
122
+ ).to_h
123
+ end
124
+
125
+ list_channels_done = {}
126
+
127
+ data[:get_chan_info].each_value do |channel|
128
+ next if channel[:_error]
129
+
130
+ partners = [channel[:node1_pub], channel[:node2_pub]]
131
+
132
+ is_mine = partners.include?(data[:get_info][:identity_pubkey])
133
+
134
+ if is_mine
135
+ partner = partners.find { |p| p != data[:get_info][:identity_pubkey] }
136
+
137
+ unless list_channels_done[partner]
138
+ Ports::GRPC.lightning.list_channels(
139
+ peer: [partner].pack('H*')
140
+ ).channels.map(&:to_h).each do |list_channels|
141
+ data[:list_channels][list_channels[:chan_id]] = list_channels
142
+ end
143
+
144
+ list_channels_done[partner] = true
145
+ end
146
+ end
147
+
148
+ unless data[:get_node_info][channel[:node1_pub]]
149
+ data[:get_node_info][channel[:node1_pub]] = Ports::GRPC.lightning.get_node_info(
150
+ pub_key: channel[:node1_pub]
151
+ ).to_h
152
+ end
153
+
154
+ next if data[:get_node_info][channel[:node2_pub]]
155
+
156
+ data[:get_node_info][channel[:node2_pub]] = Ports::GRPC.lightning.get_node_info(
157
+ pub_key: channel[:node2_pub]
158
+ ).to_h
159
+ end
160
+
161
+ data[:list_channels].each_value do |channel|
162
+ next if data[:get_node_info][channel[:remote_pubkey]]
163
+
164
+ data[:get_node_info][channel[:remote_pubkey]] = Ports::GRPC.lightning.get_node_info(
165
+ pub_key: channel[:remote_pubkey]
166
+ ).to_h
167
+ end
168
+
169
+ data
170
+ end
171
+
172
+ def self.adapt(raw)
173
+ adapted = {
174
+ get_info: Lighstorm::Adapter::Node.get_info(raw[:get_info]),
175
+ list_payments: raw[:list_payments].map do |raw_payment|
176
+ Lighstorm::Adapter::Payment.list_payments(raw_payment, raw[:get_info])
177
+ end,
178
+ fee_report: raw[:fee_report][:channel_fees].map do |raw_fee|
179
+ Lighstorm::Adapter::Fee.fee_report(raw_fee.to_h)
180
+ end,
181
+ lookup_invoice: {},
182
+ list_channels: {},
183
+ get_chan_info: {},
184
+ get_node_info: {},
185
+ decode_pay_req: {}
186
+ }
187
+
188
+ raw[:decode_pay_req].each_key do |key|
189
+ next if raw[:decode_pay_req][key][:_error]
190
+
191
+ adapted[:decode_pay_req][key] = Lighstorm::Adapter::PaymentRequest.decode_pay_req(
192
+ raw[:decode_pay_req][key]
193
+ )
194
+ end
195
+
196
+ raw[:lookup_invoice].each_key do |key|
197
+ next if raw[:lookup_invoice][key][:_error]
198
+
199
+ adapted[:lookup_invoice][key] = Lighstorm::Adapter::Invoice.lookup_invoice(
200
+ raw[:lookup_invoice][key]
201
+ )
202
+ end
203
+
204
+ raw[:get_chan_info].each_key do |key|
205
+ next if raw[:get_chan_info][key][:_error]
206
+
207
+ adapted[:get_chan_info][key] = Lighstorm::Adapter::Channel.get_chan_info(
208
+ raw[:get_chan_info][key]
209
+ )
210
+ end
211
+
212
+ raw[:list_channels].each_key do |key|
213
+ adapted[:list_channels][key] = Lighstorm::Adapter::Channel.list_channels(
214
+ raw[:list_channels][key], raw[:at]
215
+ )
216
+ end
217
+
218
+ raw[:get_node_info].each_key do |key|
219
+ adapted[:get_node_info][key] = Lighstorm::Adapter::Node.get_node_info(
220
+ raw[:get_node_info][key]
221
+ )
222
+ end
223
+
224
+ adapted
225
+ end
226
+
227
+ def self.transform_channel(data, adapted)
228
+ if adapted[:get_chan_info][data[:id].to_i]
229
+ target = data[:target]
230
+ data = adapted[:get_chan_info][data[:id].to_i]
231
+ data[:target] = target
232
+ data[:known] = true
233
+ else
234
+ data[:_key] = Digest::SHA256.hexdigest(data[:id])
235
+ end
236
+
237
+ data[:mine] = true if data[:partners].size == 1
238
+
239
+ [0, 1].each do |i|
240
+ next unless data[:partners] && data[:partners][i]
241
+
242
+ if data[:partners][i][:node][:public_key] == adapted[:get_info][:public_key]
243
+ data[:partners][i][:node] = adapted[:get_info]
244
+ end
245
+
246
+ data[:partners].each do |partner|
247
+ if data[:partners][i][:node][:public_key] == partner[:node][:public_key]
248
+ data[:partners][i][:policy] = partner[:policy]
249
+ end
250
+ end
251
+
252
+ if data[:partners][i][:node][:public_key] == adapted[:get_info][:public_key]
253
+ data[:partners][i][:node][:platform] = adapted[:get_info][:platform]
254
+ data[:partners][i][:node][:myself] = true
255
+ data[:mine] = true
256
+ adapted[:fee_report].each do |channel|
257
+ next unless data[:id] == channel[:id]
258
+
259
+ data[:partners][i][:policy][:fee] = channel[:partner][:policy][:fee]
260
+ break
261
+ end
262
+ else
263
+ data[:partners][i][:node] = adapted[:get_node_info][data[:partners][i][:node][:public_key]]
264
+ data[:partners][i][:node][:platform] = {
265
+ blockchain: adapted[:get_info][:platform][:blockchain],
266
+ network: adapted[:get_info][:platform][:network]
267
+ }
268
+
269
+ data[:partners][i][:node][:myself] = false
270
+ end
271
+ end
272
+
273
+ channel = adapted[:list_channels][data[:id].to_i]
274
+
275
+ return data unless channel
276
+
277
+ channel.each_key do |key|
278
+ next if data.key?(key)
279
+
280
+ data[key] = channel[key]
281
+ end
282
+
283
+ data[:accounting] = channel[:accounting]
284
+
285
+ channel[:partners].each do |partner|
286
+ data[:partners].each_index do |i|
287
+ partner.each_key do |key|
288
+ next if data[:partners][i].key?(key)
289
+
290
+ data[:partners][i][key] = partner[key]
291
+ end
292
+ end
293
+ end
294
+
295
+ data
296
+ end
297
+
298
+ def self.transform(list_payments, adapted)
299
+ if adapted[:lookup_invoice][list_payments[:request][:secret][:hash]] &&
300
+ !adapted[:lookup_invoice][list_payments[:request][:secret][:hash]][:_error]
301
+
302
+ list_payments[:request] = adapted[:lookup_invoice][list_payments[:request][:secret][:hash]][:request]
303
+ else
304
+ list_payments[:request][:_key] = Digest::SHA256.hexdigest(
305
+ list_payments[:request][:code]
306
+ )
307
+ end
308
+ list_payments[:hops].each do |hop|
309
+ hop[:channel] = transform_channel(hop[:channel], adapted)
310
+ end
311
+
312
+ if adapted[:decode_pay_req][list_payments[:request][:code]]
313
+ decoded = adapted[:decode_pay_req][list_payments[:request][:code]]
314
+ request = list_payments[:request]
315
+
316
+ decoded.each_key do |key|
317
+ request[key] = decoded[key] unless request.key?(key)
318
+
319
+ next unless decoded[key].is_a?(Hash)
320
+
321
+ decoded[key].each_key do |sub_key|
322
+ request[key][sub_key] = decoded[key][sub_key] unless request[key].key?(sub_key)
323
+ end
324
+ end
325
+ end
326
+
327
+ list_payments
328
+ end
329
+
330
+ def self.data(purpose: nil, limit: nil, &vcr)
331
+ raw = if vcr.nil?
332
+ fetch(purpose: purpose, limit: limit)
333
+ else
334
+ vcr.call(-> { fetch(purpose: purpose, limit: limit) })
335
+ end
336
+
337
+ adapted = adapt(raw)
338
+
339
+ adapted[:list_payments].map do |data|
340
+ transform(data, adapted)
341
+ end
342
+ end
343
+
344
+ def self.model(data)
345
+ data.map do |node_data|
346
+ Lighstorm::Models::Payment.new(node_data)
347
+ end
348
+ end
349
+ end
350
+ end
351
+ end
352
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './payment/all'
4
+
5
+ module Lighstorm
6
+ module Controllers
7
+ module Payment
8
+ def self.all(purpose: nil, limit: nil)
9
+ All.model(All.data(purpose: purpose, limit: limit))
10
+ end
11
+
12
+ def self.first(purpose: nil)
13
+ All.model(All.data(purpose: purpose)).first
14
+ end
15
+
16
+ def self.last(purpose: nil)
17
+ All.model(All.data(purpose: purpose)).last
18
+ end
19
+ end
20
+ end
21
+ end
data/docs/.nojekyll ADDED
File without changes