lighstorm 0.0.3 → 0.0.5

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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/.env.example +5 -0
  3. data/.github/workflows/ruby-rspec-tests.yml +39 -0
  4. data/.gitignore +1 -0
  5. data/.rspec +1 -0
  6. data/.rubocop.yml +10 -0
  7. data/Gemfile +4 -0
  8. data/Gemfile.lock +26 -2
  9. data/README.md +30 -371
  10. data/adapters/connections/channel_node/fee.rb +26 -0
  11. data/adapters/connections/channel_node/policy.rb +61 -0
  12. data/adapters/connections/channel_node.rb +74 -0
  13. data/adapters/connections/payment_channel.rb +28 -0
  14. data/adapters/edges/channel.rb +96 -0
  15. data/adapters/edges/forward.rb +40 -0
  16. data/adapters/edges/payment/purpose.rb +32 -0
  17. data/adapters/edges/payment.rb +50 -0
  18. data/adapters/invoice.rb +49 -0
  19. data/adapters/nodes/node.rb +111 -0
  20. data/adapters/payment_request.rb +76 -0
  21. data/components/cache.rb +3 -2
  22. data/components/lnd.rb +5 -1
  23. data/controllers/channel/actions/apply_gossip.rb +194 -0
  24. data/controllers/channel/actions/update_fee.rb +76 -0
  25. data/controllers/channel/all.rb +79 -0
  26. data/controllers/channel/find_by_id.rb +153 -0
  27. data/controllers/channel/mine.rb +114 -0
  28. data/controllers/channel.rb +27 -0
  29. data/controllers/forward/all.rb +244 -0
  30. data/controllers/forward/group_by_channel.rb +89 -0
  31. data/controllers/forward.rb +28 -0
  32. data/controllers/invoice/actions/create.rb +36 -0
  33. data/controllers/invoice/actions/pay.rb +28 -0
  34. data/controllers/invoice/actions/pay_through_route.rb +71 -0
  35. data/controllers/invoice/all.rb +70 -0
  36. data/controllers/invoice/find_by_secret_hash.rb +42 -0
  37. data/controllers/invoice.rb +36 -0
  38. data/controllers/node/actions/apply_gossip.rb +112 -0
  39. data/controllers/node/all.rb +63 -0
  40. data/controllers/node/find_by_public_key.rb +49 -0
  41. data/controllers/node/myself.rb +34 -0
  42. data/controllers/node.rb +27 -0
  43. data/controllers/payment/all.rb +368 -0
  44. data/controllers/payment.rb +21 -0
  45. data/docs/.nojekyll +0 -0
  46. data/docs/README.md +731 -0
  47. data/docs/_coverpage.md +12 -0
  48. data/docs/index.html +26 -0
  49. data/docs/vendor/docsify/docsify@4.js +1 -0
  50. data/docs/vendor/docsify-themeable/theme-simple-dark.css +3 -0
  51. data/docs/vendor/docsify-themeable/theme-simple-dark.css.map +1 -0
  52. data/docs/vendor/prismjs/prism-bash.min.js +1 -0
  53. data/docs/vendor/prismjs/prism-ruby.min.js +1 -0
  54. data/docs/vendor/prismjs/prism-tomorrow.min.css +1 -0
  55. data/lighstorm.gemspec +3 -1
  56. data/models/concerns/protectable.rb +23 -0
  57. data/models/connections/channel_node/accounting.rb +7 -14
  58. data/models/connections/channel_node/fee.rb +55 -84
  59. data/models/connections/channel_node/htlc/blocks/delta.rb +39 -0
  60. data/models/connections/channel_node/htlc.rb +65 -11
  61. data/models/connections/channel_node/policy.rb +15 -18
  62. data/models/connections/channel_node.rb +48 -22
  63. data/models/connections/forward_channel.rb +8 -43
  64. data/models/connections/payment_channel.rb +18 -39
  65. data/models/edges/channel/accounting.rb +45 -20
  66. data/models/edges/channel/hop.rb +65 -0
  67. data/models/edges/channel.rb +111 -169
  68. data/models/edges/forward.rb +9 -119
  69. data/models/edges/groups/{analysis.rb → channel_forwards/analysis.rb} +9 -9
  70. data/models/edges/groups/channel_forwards.rb +10 -40
  71. data/models/edges/payment.rb +29 -215
  72. data/models/errors.rb +32 -0
  73. data/models/invoice.rb +49 -0
  74. data/models/nodes/node/lightning.rb +11 -16
  75. data/models/nodes/node/platform.rb +13 -13
  76. data/models/nodes/node.rb +59 -103
  77. data/models/payment_request.rb +72 -0
  78. data/models/rate.rb +11 -1
  79. data/models/satoshis.rb +5 -6
  80. data/ports/dsl/lighstorm/errors.rb +5 -0
  81. data/ports/dsl/lighstorm.rb +11 -9
  82. data/ports/grpc/session.rb +26 -0
  83. data/ports/grpc.rb +80 -0
  84. data/static/cache.rb +12 -0
  85. data/static/spec.rb +3 -1
  86. metadata +62 -6
  87. data/models/connections/channel_node/constraints.rb +0 -24
@@ -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
@@ -0,0 +1,28 @@
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 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
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,71 @@
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 PayThroughRoute
10
+ def self.perform(_invoice, route:, preview: false, fake: false)
11
+ raise Errors::ToDoError, self
12
+
13
+ channels = route.map do |channel_id|
14
+ Channel.find_by_id(channel_id)
15
+ end
16
+
17
+ outgoing_channel_id = channels.first.id
18
+
19
+ hops_public_keys = []
20
+
21
+ hops_public_keys << if channels.first.mine?
22
+ channels.first.partner.node.public_key
23
+ else
24
+ channels.first.partners.last.node.public_key
25
+ end
26
+
27
+ channels[1..channels.size - 2].each do |channel|
28
+ hops_public_keys << channel.partners.last.node.public_key
29
+ end
30
+
31
+ hops_public_keys << if channels.last.mine?
32
+ channels.last.myself.node.public_key
33
+ else
34
+ channels.last.partners.last.node.public_key
35
+ end
36
+ begin
37
+ route = LND.instance.middleware('router.build_route') do
38
+ LND.instance.client.router.build_route(
39
+ amt_msat: amount.milisatoshis,
40
+ outgoing_chan_id: channels.first.id.to_i,
41
+ hop_pubkeys: hops_public_keys.map { |hpk| [hpk].pack('H*') },
42
+ payment_addr: [address].pack('H*')
43
+ )
44
+ end
45
+
46
+ if preview
47
+ return {
48
+ method: :send_to_route_v2,
49
+ params: {
50
+ payment_hash: hash,
51
+ route: route.to_h[:route],
52
+ skip_temp_err: true
53
+ }
54
+ }
55
+ end
56
+
57
+ LND.instance.middleware('router.send_to_route_v2') do
58
+ LND.instance.client.router.send_to_route_v2(
59
+ payment_hash: [hash].pack('H*'),
60
+ route: route.to_h[:route],
61
+ skip_temp_err: true
62
+ )
63
+ end
64
+ rescue StandardError => e
65
+ e
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../ports/grpc'
4
+ require_relative '../../adapters/invoice'
5
+ require_relative '../../models/invoice'
6
+
7
+ module Lighstorm
8
+ module Controllers
9
+ module Invoice
10
+ module All
11
+ def self.fetch(limit: nil)
12
+ last_offset = 0
13
+
14
+ invoices = []
15
+
16
+ loop do
17
+ response = Ports::GRPC.lightning.list_invoices(
18
+ index_offset: last_offset,
19
+ num_max_invoices: 10
20
+ )
21
+
22
+ response.invoices.each { |invoice| invoices << invoice.to_h }
23
+
24
+ # TODO: How to optimize this?
25
+ # break if !limit.nil? && invoices.size >= limit
26
+
27
+ break if last_offset == response.last_index_offset || last_offset > response.last_index_offset
28
+
29
+ last_offset = response.last_index_offset
30
+ end
31
+
32
+ invoices = invoices.sort_by { |raw_invoice| -raw_invoice[:creation_date] }
33
+
34
+ invoices = invoices[0..limit - 1] unless limit.nil?
35
+
36
+ { list_invoices: invoices }
37
+ end
38
+
39
+ def self.adapt(raw)
40
+ {
41
+ list_invoices: raw[:list_invoices].map do |raw_invoice|
42
+ Lighstorm::Adapter::Invoice.list_invoices(raw_invoice)
43
+ end
44
+ }
45
+ end
46
+
47
+ def self.transform(adapted)
48
+ adapted[:list_invoices].map do |invoice|
49
+ invoice[:known] = true
50
+ invoice
51
+ end
52
+ end
53
+
54
+ def self.data(limit: nil, &vcr)
55
+ raw = vcr.nil? ? fetch(limit: limit) : vcr.call(-> { fetch(limit: limit) })
56
+
57
+ adapted = adapt(raw)
58
+
59
+ transform(adapted)
60
+ end
61
+
62
+ def self.model(data)
63
+ data.map do |node_data|
64
+ Lighstorm::Models::Invoice.new(node_data)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../ports/grpc'
4
+ require_relative '../../adapters/invoice'
5
+ require_relative '../../models/invoice'
6
+
7
+ module Lighstorm
8
+ module Controllers
9
+ module Invoice
10
+ module FindBySecretHash
11
+ def self.fetch(secret_hash)
12
+ {
13
+ lookup_invoice: Ports::GRPC.lightning.lookup_invoice(r_hash_str: secret_hash).to_h
14
+ }
15
+ end
16
+
17
+ def self.adapt(raw)
18
+ {
19
+ lookup_invoice: Lighstorm::Adapter::Invoice.lookup_invoice(raw[:lookup_invoice])
20
+ }
21
+ end
22
+
23
+ def self.transform(adapted)
24
+ adapted[:lookup_invoice][:known] = true
25
+ adapted[:lookup_invoice]
26
+ end
27
+
28
+ def self.data(secret_hash, &vcr)
29
+ raw = vcr.nil? ? fetch(secret_hash) : vcr.call(-> { fetch(secret_hash) })
30
+
31
+ adapted = adapt(raw)
32
+
33
+ transform(adapted)
34
+ end
35
+
36
+ def self.model(data)
37
+ Lighstorm::Models::Invoice.new(data)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './invoice/all'
4
+ require_relative './invoice/find_by_secret_hash'
5
+ require_relative './invoice/actions/create'
6
+
7
+ module Lighstorm
8
+ module Controllers
9
+ module Invoice
10
+ def self.all(limit: nil)
11
+ All.model(All.data(limit: limit))
12
+ end
13
+
14
+ def self.first
15
+ All.model(All.data).first
16
+ end
17
+
18
+ def self.last
19
+ All.model(All.data).last
20
+ end
21
+
22
+ def self.find_by_secret_hash(secret_hash)
23
+ FindBySecretHash.model(FindBySecretHash.data(secret_hash))
24
+ end
25
+
26
+ def self.create(description: nil, milisatoshis: nil, preview: false, fake: false)
27
+ Create.perform(
28
+ description: description,
29
+ milisatoshis: milisatoshis,
30
+ preview: preview,
31
+ fake: fake
32
+ )
33
+ end
34
+ end
35
+ end
36
+ end