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,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../ports/grpc'
4
+ require_relative '../../adapters/edges/channel'
5
+ require_relative '../../adapters/nodes/node'
6
+ require_relative '../../adapters/connections/channel_node/fee'
7
+
8
+ require_relative '../node/find_by_public_key'
9
+
10
+ module Lighstorm
11
+ module Controllers
12
+ module Channel
13
+ module FindById
14
+ def self.fetch(id)
15
+ data = {
16
+ at: Time.now,
17
+ get_info: Ports::GRPC.lightning.get_info.to_h,
18
+ # Ensure that we are getting fresh up-date data about our own fees.
19
+ fee_report: Ports::GRPC.lightning.fee_report.to_h,
20
+ get_chan_info: Ports::GRPC.lightning.get_chan_info(chan_id: id.to_i).to_h,
21
+ get_node_info: {},
22
+ list_channels: []
23
+ }
24
+
25
+ data[:get_node_info][data[:get_chan_info][:node1_pub]] = Ports::GRPC.lightning.get_node_info(
26
+ pub_key: data[:get_chan_info][:node1_pub]
27
+ ).to_h
28
+
29
+ data[:get_node_info][data[:get_chan_info][:node2_pub]] = Ports::GRPC.lightning.get_node_info(
30
+ pub_key: data[:get_chan_info][:node2_pub]
31
+ ).to_h
32
+
33
+ partners = [
34
+ data[:get_chan_info][:node1_pub],
35
+ data[:get_chan_info][:node2_pub]
36
+ ]
37
+
38
+ is_mine = partners.include?(data[:get_info][:identity_pubkey])
39
+
40
+ if is_mine
41
+ partner = partners.find { |p| p != data[:get_info][:identity_pubkey] }
42
+
43
+ data[:list_channels] = Ports::GRPC.lightning.list_channels(
44
+ peer: [partner].pack('H*')
45
+ ).channels.map(&:to_h)
46
+ end
47
+
48
+ data
49
+ end
50
+
51
+ def self.adapt(raw)
52
+ adapted = {
53
+ get_info: Lighstorm::Adapter::Node.get_info(raw[:get_info]),
54
+ get_chan_info: Lighstorm::Adapter::Channel.get_chan_info(raw[:get_chan_info]),
55
+ fee_report: raw[:fee_report][:channel_fees].map do |raw_fee|
56
+ Lighstorm::Adapter::Fee.fee_report(raw_fee.to_h)
57
+ end,
58
+ list_channels: raw[:list_channels].map do |raw_channel|
59
+ Lighstorm::Adapter::Channel.list_channels(raw_channel.to_h, raw[:at])
60
+ end,
61
+ get_node_info: {}
62
+ }
63
+
64
+ raw[:get_node_info].each_key do |public_key|
65
+ adapted[:get_node_info][public_key] = Lighstorm::Adapter::Node.get_node_info(
66
+ raw[:get_node_info][public_key]
67
+ )
68
+ end
69
+
70
+ adapted
71
+ end
72
+
73
+ def self.transform(data, adapted)
74
+ [0, 1].each do |i|
75
+ adapted[:list_channels].each_index do |c|
76
+ if adapted[:list_channels][c][:partners][i][:node][:public_key].nil?
77
+ adapted[:list_channels][c][:partners][i][:node] = adapted[:get_info]
78
+ end
79
+ end
80
+ end
81
+
82
+ data[:known] = true
83
+ data[:mine] = false
84
+ data[:exposure] = 'public'
85
+
86
+ [0, 1].each do |i|
87
+ data[:partners][i][:node] = adapted[:get_info] if data[:partners][i][:node][:public_key].nil?
88
+
89
+ if data[:partners][i][:node][:public_key] == adapted[:get_info][:public_key]
90
+ data[:partners][i][:node] = adapted[:get_info]
91
+ data[:partners][i][:node][:myself] = true
92
+ data[:mine] = true
93
+ adapted[:fee_report].each do |channel|
94
+ next unless data[:id] == channel[:id]
95
+
96
+ data[:partners][i][:policy][:fee] = channel[:partner][:policy][:fee]
97
+ break
98
+ end
99
+ else
100
+ data[:partners][i][:node] = adapted[:get_node_info][data[:partners][i][:node][:public_key]]
101
+ data[:partners][i][:node][:platform] = {
102
+ blockchain: adapted[:get_info][:platform][:blockchain],
103
+ network: adapted[:get_info][:platform][:network]
104
+ }
105
+
106
+ data[:partners][i][:node][:myself] = false
107
+ end
108
+ end
109
+
110
+ adapted[:list_channels].each do |channel|
111
+ next unless channel[:id] == data[:id]
112
+
113
+ channel.each_key do |key|
114
+ next if data.key?(key)
115
+
116
+ data[key] = channel[key]
117
+ end
118
+
119
+ data[:accounting] = channel[:accounting]
120
+
121
+ channel[:partners].each do |partner|
122
+ data[:partners].each_index do |i|
123
+ next unless data[:partners][i][:node][:public_key] == partner[:node][:public_key]
124
+
125
+ partner.each_key do |key|
126
+ next if data[:partners][i].key?(key)
127
+
128
+ data[:partners][i][key] = partner[key]
129
+ end
130
+ end
131
+ end
132
+
133
+ break
134
+ end
135
+
136
+ data
137
+ end
138
+
139
+ def self.data(id, &vcr)
140
+ raw = vcr.nil? ? fetch(id) : vcr.call(-> { fetch(id) })
141
+
142
+ adapted = adapt(raw)
143
+
144
+ transform(adapted[:get_chan_info], adapted)
145
+ end
146
+
147
+ def self.model(data)
148
+ Lighstorm::Models::Channel.new(data)
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../ports/grpc'
4
+ require_relative '../../adapters/edges/channel'
5
+ require_relative '../../adapters/nodes/node'
6
+ require_relative '../../adapters/connections/channel_node/fee'
7
+
8
+ module Lighstorm
9
+ module Controllers
10
+ module Channel
11
+ module Mine
12
+ def self.fetch
13
+ data = {
14
+ at: Time.now,
15
+ get_info: Ports::GRPC.lightning.get_info.to_h,
16
+ # Ensure that we are getting fresh up-date data about our own fees.
17
+ fee_report: Ports::GRPC.lightning.fee_report.to_h,
18
+ list_channels: Ports::GRPC.lightning.list_channels.channels.map(&:to_h),
19
+ get_chan_info: {},
20
+ get_node_info: {}
21
+ }
22
+
23
+ data[:list_channels].each do |channel|
24
+ unless data[:get_chan_info][channel[:chan_id]]
25
+ data[:get_chan_info][channel[:chan_id]] = Ports::GRPC.lightning.get_chan_info(
26
+ chan_id: channel[:chan_id]
27
+ ).to_h
28
+ end
29
+
30
+ next if data[:get_node_info][channel[:remote_pubkey]]
31
+
32
+ data[:get_node_info][channel[:remote_pubkey]] = Ports::GRPC.lightning.get_node_info(
33
+ pub_key: channel[:remote_pubkey]
34
+ ).to_h
35
+ end
36
+
37
+ data
38
+ end
39
+
40
+ def self.adapt(raw)
41
+ adapted = {
42
+ get_info: Lighstorm::Adapter::Node.get_info(raw[:get_info]),
43
+ fee_report: raw[:fee_report][:channel_fees].map do |raw_fee|
44
+ Lighstorm::Adapter::Fee.fee_report(raw_fee.to_h)
45
+ end,
46
+ list_channels: raw[:list_channels].map do |raw_channel|
47
+ Lighstorm::Adapter::Channel.list_channels(raw_channel.to_h, raw[:at])
48
+ end,
49
+ get_chan_info: {},
50
+ get_node_info: {}
51
+ }
52
+
53
+ raw[:get_chan_info].each do |key, value|
54
+ adapted[:get_chan_info][key] = Lighstorm::Adapter::Channel.get_chan_info(value.to_h)
55
+ end
56
+
57
+ raw[:get_node_info].each do |key, value|
58
+ adapted[:get_node_info][key] = Lighstorm::Adapter::Node.get_node_info(value.to_h)
59
+ end
60
+
61
+ adapted
62
+ end
63
+
64
+ def self.transform(data, adapted)
65
+ [0, 1].each do |i|
66
+ data[:partners][i][:node] = adapted[:get_info] if data[:partners][i][:node][:public_key].nil?
67
+
68
+ adapted[:get_chan_info][data[:id].to_i][:partners].each do |partner|
69
+ if data[:partners][i][:node][:public_key] == partner[:node][:public_key]
70
+ data[:partners][i][:policy] = partner[:policy]
71
+ end
72
+ end
73
+
74
+ if data[:partners][i][:node][:public_key] == adapted[:get_info][:public_key]
75
+ data[:partners][i][:node][:platform] = adapted[:get_info][:platform]
76
+ data[:partners][i][:node][:myself] = true
77
+ data[:known] = true
78
+ data[:mine] = true
79
+ adapted[:fee_report].each do |channel|
80
+ next unless data[:id] == channel[:id]
81
+
82
+ data[:partners][i][:policy][:fee] = channel[:partner][:policy][:fee]
83
+ break
84
+ end
85
+ else
86
+ data[:partners][i][:node] = adapted[:get_node_info][data[:partners][i][:node][:public_key]]
87
+ data[:partners][i][:node][:platform] = {
88
+ blockchain: adapted[:get_info][:platform][:blockchain],
89
+ network: adapted[:get_info][:platform][:network]
90
+ }
91
+
92
+ data[:partners][i][:node][:myself] = false
93
+ end
94
+ end
95
+ data
96
+ end
97
+
98
+ def self.data(&vcr)
99
+ raw = vcr.nil? ? fetch : vcr.call(-> { fetch })
100
+
101
+ adapted = adapt(raw)
102
+
103
+ adapted[:list_channels].map { |data| transform(data, adapted) }
104
+ end
105
+
106
+ def self.model(data)
107
+ data.map do |node_data|
108
+ Lighstorm::Models::Channel.new(node_data)
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './channel/mine'
4
+ require_relative './channel/all'
5
+ require_relative './channel/find_by_id'
6
+
7
+ module Lighstorm
8
+ module Controllers
9
+ module Channel
10
+ def self.mine
11
+ Mine.model(Mine.data)
12
+ end
13
+
14
+ def self.all(limit: nil)
15
+ All.model(All.data(limit: limit))
16
+ end
17
+
18
+ def self.find_by_id(id)
19
+ FindById.model(FindById.data(id))
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,244 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest'
4
+
5
+ require_relative '../../ports/grpc'
6
+ require_relative '../../adapters/nodes/node'
7
+ require_relative '../../adapters/edges/channel'
8
+ require_relative '../../adapters/edges/forward'
9
+ require_relative '../../adapters/connections/channel_node/fee'
10
+
11
+ require_relative '../../models/edges/forward'
12
+
13
+ module Lighstorm
14
+ module Controllers
15
+ module Forward
16
+ module All
17
+ def self.fetch(limit: nil)
18
+ at = Time.now
19
+
20
+ last_offset = 0
21
+
22
+ forwards = []
23
+
24
+ loop do
25
+ response = Ports::GRPC.lightning.forwarding_history(index_offset: last_offset)
26
+
27
+ response.forwarding_events.each { |forward| forwards << forward.to_h }
28
+
29
+ # TODO: How to optimize this?
30
+ # break if !limit.nil? && forwards.size >= limit
31
+
32
+ break if last_offset == response.last_offset_index || last_offset > response.last_offset_index
33
+
34
+ last_offset = response.last_offset_index
35
+ end
36
+
37
+ forwards = forwards.sort_by { |raw_forward| -raw_forward[:timestamp_ns] }
38
+
39
+ forwards = forwards[0..limit - 1] unless limit.nil?
40
+
41
+ data = {
42
+ at: at,
43
+ get_info: Ports::GRPC.lightning.get_info.to_h,
44
+ fee_report: Ports::GRPC.lightning.fee_report.to_h,
45
+ forwarding_history: forwards,
46
+ list_channels: {},
47
+ get_chan_info: {},
48
+ get_node_info: {}
49
+ }
50
+
51
+ forwards.each do |forward|
52
+ unless data[:get_chan_info][forward[:chan_id_in]]
53
+ begin
54
+ data[:get_chan_info][forward[:chan_id_in]] = Ports::GRPC.lightning.get_chan_info(
55
+ chan_id: forward[:chan_id_in]
56
+ ).to_h
57
+ rescue GRPC::Unknown => e
58
+ data[:get_chan_info][forward[:chan_id_in]] = { _error: e }
59
+ end
60
+ end
61
+
62
+ next if data[:get_chan_info][forward[:chan_id_out]]
63
+
64
+ begin
65
+ data[:get_chan_info][forward[:chan_id_out]] = Ports::GRPC.lightning.get_chan_info(
66
+ chan_id: forward[:chan_id_out]
67
+ ).to_h
68
+ rescue GRPC::Unknown => e
69
+ data[:get_chan_info][forward[:chan_id_out]] = { _error: e }
70
+ end
71
+ end
72
+
73
+ list_channels_done = {}
74
+
75
+ data[:get_chan_info].each_value do |channel|
76
+ next if channel[:_error]
77
+
78
+ partners = [channel[:node1_pub], channel[:node2_pub]]
79
+
80
+ is_mine = partners.include?(data[:get_info][:identity_pubkey])
81
+
82
+ if is_mine
83
+ partner = partners.find { |p| p != data[:get_info][:identity_pubkey] }
84
+
85
+ unless list_channels_done[partner]
86
+ Ports::GRPC.lightning.list_channels(
87
+ peer: [partner].pack('H*')
88
+ ).channels.map(&:to_h).each do |list_channels|
89
+ data[:list_channels][list_channels[:chan_id]] = list_channels
90
+ end
91
+
92
+ list_channels_done[partner] = true
93
+ end
94
+ end
95
+
96
+ unless data[:get_node_info][channel[:node1_pub]]
97
+ data[:get_node_info][channel[:node1_pub]] = Ports::GRPC.lightning.get_node_info(
98
+ pub_key: channel[:node1_pub]
99
+ ).to_h
100
+ end
101
+
102
+ next if data[:get_node_info][channel[:node2_pub]]
103
+
104
+ data[:get_node_info][channel[:node2_pub]] = Ports::GRPC.lightning.get_node_info(
105
+ pub_key: channel[:node2_pub]
106
+ ).to_h
107
+ end
108
+
109
+ data[:list_channels].each_value do |channel|
110
+ next if data[:get_node_info][channel[:remote_pubkey]]
111
+
112
+ data[:get_node_info][channel[:remote_pubkey]] = Ports::GRPC.lightning.get_node_info(
113
+ pub_key: channel[:remote_pubkey]
114
+ ).to_h
115
+ end
116
+
117
+ data
118
+ end
119
+
120
+ def self.adapt(raw)
121
+ adapted = {
122
+ get_info: Lighstorm::Adapter::Node.get_info(raw[:get_info]),
123
+ forwarding_history: raw[:forwarding_history].map do |raw_forward|
124
+ Lighstorm::Adapter::Forward.forwarding_history(raw_forward)
125
+ end,
126
+ fee_report: raw[:fee_report][:channel_fees].map do |raw_fee|
127
+ Lighstorm::Adapter::Fee.fee_report(raw_fee.to_h)
128
+ end,
129
+ list_channels: {},
130
+ get_chan_info: {},
131
+ get_node_info: {}
132
+ }
133
+
134
+ raw[:get_chan_info].each_key do |key|
135
+ next if raw[:get_chan_info][key][:_error]
136
+
137
+ adapted[:get_chan_info][key] = Lighstorm::Adapter::Channel.get_chan_info(
138
+ raw[:get_chan_info][key]
139
+ )
140
+ end
141
+
142
+ raw[:list_channels].each_key do |key|
143
+ adapted[:list_channels][key] = Lighstorm::Adapter::Channel.list_channels(
144
+ raw[:list_channels][key], raw[:at]
145
+ )
146
+ end
147
+
148
+ raw[:get_node_info].each_key do |key|
149
+ adapted[:get_node_info][key] = Lighstorm::Adapter::Node.get_node_info(
150
+ raw[:get_node_info][key]
151
+ )
152
+ end
153
+
154
+ adapted
155
+ end
156
+
157
+ def self.transform(data, adapted)
158
+ unless adapted[:get_chan_info][data[:id].to_i]
159
+ data[:_key] = Digest::SHA256.hexdigest(data[:id])
160
+ return data
161
+ end
162
+
163
+ data = adapted[:get_chan_info][data[:id].to_i]
164
+ data[:known] = true
165
+
166
+ [0, 1].each do |i|
167
+ if data[:partners][i][:node][:public_key] == adapted[:get_info][:public_key]
168
+ data[:partners][i][:node] =
169
+ adapted[:get_info]
170
+ end
171
+
172
+ adapted[:get_chan_info][data[:id].to_i][:partners].each do |partner|
173
+ if data[:partners][i][:node][:public_key] == partner[:node][:public_key]
174
+ data[:partners][i][:policy] = partner[:policy]
175
+ end
176
+ end
177
+
178
+ if data[:partners][i][:node][:public_key] == adapted[:get_info][:public_key]
179
+ data[:partners][i][:node][:platform] = adapted[:get_info][:platform]
180
+ data[:partners][i][:node][:myself] = true
181
+ data[:mine] = true
182
+ adapted[:fee_report].each do |channel|
183
+ next unless data[:id] == channel[:id]
184
+
185
+ data[:partners][i][:policy][:fee] = channel[:partner][:policy][:fee]
186
+ break
187
+ end
188
+ else
189
+ data[:partners][i][:node] = adapted[:get_node_info][data[:partners][i][:node][:public_key]]
190
+ data[:partners][i][:node][:platform] = {
191
+ blockchain: adapted[:get_info][:platform][:blockchain],
192
+ network: adapted[:get_info][:platform][:network]
193
+ }
194
+
195
+ data[:partners][i][:node][:myself] = false
196
+ end
197
+ end
198
+
199
+ channel = adapted[:list_channels][data[:id].to_i]
200
+
201
+ return data unless channel
202
+
203
+ channel.each_key do |key|
204
+ next if data.key?(key)
205
+
206
+ data[key] = channel[key]
207
+ end
208
+
209
+ data[:accounting] = channel[:accounting]
210
+
211
+ channel[:partners].each do |partner|
212
+ data[:partners].each_index do |i|
213
+ partner.each_key do |key|
214
+ next if data[:partners][i].key?(key)
215
+
216
+ data[:partners][i][key] = partner[key]
217
+ end
218
+ end
219
+ end
220
+
221
+ data
222
+ end
223
+
224
+ def self.data(limit: nil, &vcr)
225
+ raw = vcr.nil? ? fetch(limit: limit) : vcr.call(-> { fetch(limit: limit) })
226
+
227
+ adapted = adapt(raw)
228
+
229
+ adapted[:forwarding_history].map do |data|
230
+ data[:in][:channel] = transform(data[:in][:channel], adapted)
231
+ data[:out][:channel] = transform(data[:out][:channel], adapted)
232
+ data
233
+ end
234
+ end
235
+
236
+ def self.model(data)
237
+ data.map do |node_data|
238
+ Lighstorm::Models::Forward.new(node_data)
239
+ end
240
+ end
241
+ end
242
+ end
243
+ end
244
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest'
4
+
5
+ require_relative 'all'
6
+
7
+ require_relative '../../models/edges/groups/channel_forwards'
8
+
9
+ module Lighstorm
10
+ module Controllers
11
+ module Forward
12
+ module GroupByChannel
13
+ def self.filter(forwards, hours_ago)
14
+ return forwards if hours_ago.nil?
15
+
16
+ forwards.filter do |forward|
17
+ forward_hours_ago = (Time.now - Time.parse(forward[:at].to_s)).to_f / 3600
18
+ forward_hours_ago <= hours_ago
19
+ end
20
+ end
21
+
22
+ def self.group(forwards, direction)
23
+ groups = {}
24
+
25
+ forwards.each do |forward|
26
+ key = forward[direction][:channel][:id]
27
+ groups[key] = [] unless groups.key?(key)
28
+ groups[key] << forward
29
+ end
30
+
31
+ groups.values
32
+ end
33
+
34
+ def self.analyze(forwards, direction)
35
+ group = {
36
+ last_at: nil,
37
+ analysis: {
38
+ count: 0,
39
+ sums: { amount: { milisatoshis: 0 }, fee: { milisatoshis: 0 } }
40
+ },
41
+ channel: nil
42
+ }
43
+
44
+ forwards.each do |forward|
45
+ group[:last_at] = forward[:at] if group[:last_at].nil? || forward[:at] > group[:last_at]
46
+
47
+ group[:channel] = forward[direction][:channel] if group[:channel].nil?
48
+
49
+ group[:analysis][:count] += 1
50
+ group[:analysis][:sums][:amount][:milisatoshis] += forward[:in][:amount][:milisatoshis]
51
+ group[:analysis][:sums][:fee][:milisatoshis] += forward[:fee][:milisatoshis]
52
+ end
53
+
54
+ group[:_key] = _key(group)
55
+
56
+ group
57
+ end
58
+
59
+ def self._key(group)
60
+ Digest::SHA256.hexdigest(
61
+ [group[:last_at], group[:analysis][:count], group[:channel][:id]].join('/')
62
+ )
63
+ end
64
+
65
+ def self.sort(groups)
66
+ groups.sort_by { |group| - Time.parse(group[:last_at].to_s).to_f }
67
+ .sort_by { |group| - group[:analysis][:count] }
68
+ end
69
+
70
+ def self.data(direction: :out, hours_ago: nil, limit: nil, &vcr)
71
+ data = All.data(&vcr)
72
+
73
+ filtered = filter(data, hours_ago)
74
+ groups = group(filtered, direction)
75
+ analyzed = groups.map { |group| analyze(group, direction) }
76
+ sorted = sort(analyzed)
77
+
78
+ sorted = sorted[0..limit - 1] unless limit.nil?
79
+
80
+ sorted
81
+ end
82
+
83
+ def self.model(data)
84
+ data.map { |group| Models::ChannelForwardsGroup.new(group) }
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './forward/all'
4
+ require_relative './forward/group_by_channel'
5
+
6
+ module Lighstorm
7
+ module Controllers
8
+ module Forward
9
+ def self.all(limit: nil)
10
+ All.model(All.data(limit: limit))
11
+ end
12
+
13
+ def self.first
14
+ All.model(All.data).first
15
+ end
16
+
17
+ def self.last
18
+ All.model(All.data).last
19
+ end
20
+
21
+ def self.group_by_channel(direction: :out, hours_ago: nil, limit: nil)
22
+ GroupByChannel.model(
23
+ GroupByChannel.data(direction: direction, hours_ago: hours_ago, limit: limit)
24
+ )
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../ports/grpc'
4
+ require_relative '../../../models/errors'
5
+
6
+ module Lighstorm
7
+ module Controllers
8
+ module Invoice
9
+ module Create
10
+ def self.perform(description: nil, milisatoshis: nil, preview: false, fake: false)
11
+ raise Errors::ToDoError, self
12
+
13
+ grpc_request = {
14
+ service: :lightning,
15
+ method: :add_invoice,
16
+ params: {
17
+ memo: description,
18
+ value_msat: milisatoshis
19
+ }
20
+ }
21
+
22
+ return grpc_request if preview
23
+
24
+ # expiry: Default is 86400 (24 hours).
25
+ response = LND.instance.middleware("lightning.#{grpc_request[:method]}") do
26
+ LND.instance.client.lightning.send(grpc_request[:method], grpc_request[:params])
27
+ end
28
+
29
+ # TODO
30
+ # find_by_secret_hash(invoice.r_hash.unpack1('H*'))
31
+ # response
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end