lighstorm 0.0.14 → 0.0.16

Sign up to get free protection for your applications and to get access to all the features.
Files changed (158) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/Gemfile.lock +7 -7
  4. data/README.md +7 -7
  5. data/adapters/bitcoin/address.rb +19 -0
  6. data/adapters/bitcoin/transaction.rb +34 -0
  7. data/adapters/lightning/connections/channel_node/fee.rb +28 -0
  8. data/adapters/lightning/connections/channel_node/policy.rb +63 -0
  9. data/adapters/lightning/connections/channel_node.rb +77 -0
  10. data/adapters/lightning/connections/payment_channel.rb +30 -0
  11. data/adapters/lightning/edges/channel.rb +98 -0
  12. data/adapters/lightning/edges/forward.rb +42 -0
  13. data/adapters/lightning/edges/payment/purpose.rb +45 -0
  14. data/adapters/lightning/edges/payment.rb +89 -0
  15. data/adapters/lightning/invoice.rb +186 -0
  16. data/adapters/lightning/nodes/node.rb +113 -0
  17. data/controllers/action.rb +1 -1
  18. data/controllers/bitcoin/address/actions/create.rb +91 -0
  19. data/controllers/bitcoin/address/actions/pay.rb +119 -0
  20. data/controllers/bitcoin/address.rb +24 -0
  21. data/controllers/bitcoin/transaction/all.rb +65 -0
  22. data/controllers/bitcoin/transaction.rb +25 -0
  23. data/controllers/concerns/impersonatable.rb +5 -3
  24. data/controllers/connection.rb +1 -1
  25. data/controllers/lightning/channel/actions/apply_gossip.rb +201 -0
  26. data/controllers/lightning/channel/actions/update_fee.rb +94 -0
  27. data/controllers/lightning/channel/all.rb +85 -0
  28. data/controllers/lightning/channel/find_by_id.rb +155 -0
  29. data/controllers/lightning/channel/mine.rb +116 -0
  30. data/controllers/lightning/channel.rb +43 -0
  31. data/controllers/lightning/forward/all.rb +250 -0
  32. data/controllers/lightning/forward/group_by_channel.rb +91 -0
  33. data/controllers/lightning/forward.rb +36 -0
  34. data/controllers/lightning/invoice/actions/create.rb +90 -0
  35. data/controllers/lightning/invoice/actions/pay.rb +97 -0
  36. data/controllers/lightning/invoice/actions/pay_through_route.rb +73 -0
  37. data/controllers/lightning/invoice/all.rb +82 -0
  38. data/controllers/lightning/invoice/decode.rb +50 -0
  39. data/controllers/lightning/invoice/find_by_code.rb +76 -0
  40. data/controllers/lightning/invoice/find_by_secret_hash.rb +56 -0
  41. data/controllers/lightning/invoice.rb +64 -0
  42. data/controllers/lightning/node/actions/apply_gossip.rb +114 -0
  43. data/controllers/lightning/node/actions/pay.rb +117 -0
  44. data/controllers/lightning/node/all.rb +69 -0
  45. data/controllers/lightning/node/find_by_public_key.rb +55 -0
  46. data/controllers/lightning/node/myself.rb +36 -0
  47. data/controllers/lightning/node.rb +35 -0
  48. data/controllers/lightning/payment/actions/pay.rb +110 -0
  49. data/controllers/lightning/payment/all.rb +439 -0
  50. data/controllers/lightning/payment.rb +37 -0
  51. data/controllers/lightning/secret/valid_proof.rb +60 -0
  52. data/controllers/lightning/secret.rb +16 -0
  53. data/controllers/wallet/activity/all.rb +196 -0
  54. data/controllers/wallet/activity.rb +28 -0
  55. data/controllers/wallet/balance.rb +3 -3
  56. data/controllers/wallet.rb +1 -1
  57. data/docs/README.md +229 -148
  58. data/docs/_coverpage.md +1 -1
  59. data/docs/index.html +1 -1
  60. data/lighstorm.gemspec +1 -1
  61. data/models/bitcoin/address.rb +49 -0
  62. data/models/bitcoin/transaction.rb +41 -0
  63. data/models/concerns/protectable.rb +1 -1
  64. data/models/errors.rb +7 -8
  65. data/models/lightning/connections/channel_node/accounting.rb +27 -0
  66. data/models/lightning/connections/channel_node/fee.rb +82 -0
  67. data/models/lightning/connections/channel_node/htlc/blocks/delta.rb +41 -0
  68. data/models/lightning/connections/channel_node/htlc.rb +88 -0
  69. data/models/lightning/connections/channel_node/policy.rb +41 -0
  70. data/models/lightning/connections/channel_node.rb +86 -0
  71. data/models/lightning/connections/forward_channel.rb +31 -0
  72. data/models/lightning/connections/payment_channel.rb +58 -0
  73. data/models/lightning/edges/channel/accounting.rb +87 -0
  74. data/models/lightning/edges/channel/hop.rb +67 -0
  75. data/models/lightning/edges/channel.rb +197 -0
  76. data/models/lightning/edges/forward.rb +51 -0
  77. data/models/lightning/edges/groups/channel_forwards/analysis.rb +71 -0
  78. data/models/lightning/edges/groups/channel_forwards.rb +39 -0
  79. data/models/lightning/edges/payment.rb +104 -0
  80. data/models/lightning/invoice.rb +115 -0
  81. data/models/lightning/nodes/node/lightning.rb +33 -0
  82. data/models/lightning/nodes/node/platform.rb +50 -0
  83. data/models/lightning/nodes/node.rb +145 -0
  84. data/models/lightning/rate.rb +37 -0
  85. data/models/lightning/secret.rb +57 -0
  86. data/models/satoshis.rb +1 -1
  87. data/models/wallet/activity.rb +54 -0
  88. data/models/wallet/balance.rb +42 -0
  89. data/ports/dsl/lighstorm.rb +29 -16
  90. data/static/cache.rb +2 -0
  91. data/static/spec.rb +1 -1
  92. metadata +76 -70
  93. data/adapters/connections/channel_node/fee.rb +0 -26
  94. data/adapters/connections/channel_node/policy.rb +0 -61
  95. data/adapters/connections/channel_node.rb +0 -75
  96. data/adapters/connections/payment_channel.rb +0 -28
  97. data/adapters/edges/channel.rb +0 -96
  98. data/adapters/edges/forward.rb +0 -40
  99. data/adapters/edges/payment/purpose.rb +0 -43
  100. data/adapters/edges/payment.rb +0 -87
  101. data/adapters/invoice.rb +0 -184
  102. data/adapters/nodes/node.rb +0 -111
  103. data/adapters/transaction.rb +0 -23
  104. data/controllers/activity/all.rb +0 -194
  105. data/controllers/activity.rb +0 -26
  106. data/controllers/channel/actions/apply_gossip.rb +0 -193
  107. data/controllers/channel/actions/update_fee.rb +0 -91
  108. data/controllers/channel/all.rb +0 -83
  109. data/controllers/channel/find_by_id.rb +0 -153
  110. data/controllers/channel/mine.rb +0 -114
  111. data/controllers/channel.rb +0 -41
  112. data/controllers/forward/all.rb +0 -248
  113. data/controllers/forward/group_by_channel.rb +0 -89
  114. data/controllers/forward.rb +0 -34
  115. data/controllers/invoice/actions/create.rb +0 -88
  116. data/controllers/invoice/actions/pay.rb +0 -95
  117. data/controllers/invoice/actions/pay_through_route.rb +0 -71
  118. data/controllers/invoice/all.rb +0 -80
  119. data/controllers/invoice/decode.rb +0 -44
  120. data/controllers/invoice/find_by_code.rb +0 -70
  121. data/controllers/invoice/find_by_secret_hash.rb +0 -54
  122. data/controllers/invoice.rb +0 -62
  123. data/controllers/node/actions/apply_gossip.rb +0 -112
  124. data/controllers/node/actions/pay.rb +0 -115
  125. data/controllers/node/all.rb +0 -67
  126. data/controllers/node/find_by_public_key.rb +0 -53
  127. data/controllers/node/myself.rb +0 -34
  128. data/controllers/node.rb +0 -33
  129. data/controllers/payment/actions/pay.rb +0 -108
  130. data/controllers/payment/all.rb +0 -437
  131. data/controllers/payment.rb +0 -35
  132. data/controllers/secret/valid_proof.rb +0 -58
  133. data/controllers/transaction/all.rb +0 -61
  134. data/controllers/transaction.rb +0 -25
  135. data/models/activity.rb +0 -52
  136. data/models/connections/channel_node/accounting.rb +0 -25
  137. data/models/connections/channel_node/fee.rb +0 -80
  138. data/models/connections/channel_node/htlc/blocks/delta.rb +0 -39
  139. data/models/connections/channel_node/htlc.rb +0 -86
  140. data/models/connections/channel_node/policy.rb +0 -39
  141. data/models/connections/channel_node.rb +0 -84
  142. data/models/connections/forward_channel.rb +0 -29
  143. data/models/connections/payment_channel.rb +0 -56
  144. data/models/edges/channel/accounting.rb +0 -85
  145. data/models/edges/channel/hop.rb +0 -65
  146. data/models/edges/channel.rb +0 -195
  147. data/models/edges/forward.rb +0 -49
  148. data/models/edges/groups/channel_forwards/analysis.rb +0 -69
  149. data/models/edges/groups/channel_forwards.rb +0 -37
  150. data/models/edges/payment.rb +0 -102
  151. data/models/invoice.rb +0 -113
  152. data/models/nodes/node/lightning.rb +0 -31
  153. data/models/nodes/node/platform.rb +0 -48
  154. data/models/nodes/node.rb +0 -143
  155. data/models/rate.rb +0 -35
  156. data/models/secret.rb +0 -55
  157. data/models/transaction.rb +0 -39
  158. data/models/wallet.rb +0 -40
@@ -0,0 +1,250 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest'
4
+
5
+ require_relative '../../../ports/grpc'
6
+ require_relative '../../../adapters/lightning/nodes/node'
7
+ require_relative '../../../adapters/lightning/edges/channel'
8
+ require_relative '../../../adapters/lightning/edges/forward'
9
+ require_relative '../../../adapters/lightning/connections/channel_node/fee'
10
+
11
+ require_relative '../../../models/lightning/edges/forward'
12
+
13
+ module Lighstorm
14
+ module Controller
15
+ module Lightning
16
+ module Forward
17
+ module All
18
+ def self.fetch(components, limit: nil)
19
+ at = Time.now
20
+
21
+ last_offset = 0
22
+
23
+ forwards = []
24
+
25
+ loop do
26
+ response = components[:grpc].lightning.forwarding_history(index_offset: last_offset)
27
+
28
+ response.forwarding_events.each { |forward| forwards << forward.to_h }
29
+
30
+ # TODO: How to optimize this?
31
+ # break if !limit.nil? && forwards.size >= limit
32
+
33
+ break if last_offset == response.last_offset_index || last_offset > response.last_offset_index
34
+
35
+ last_offset = response.last_offset_index
36
+ end
37
+
38
+ forwards = forwards.sort_by { |raw_forward| -raw_forward[:timestamp_ns] }
39
+
40
+ forwards = forwards[0..limit - 1] unless limit.nil?
41
+
42
+ data = {
43
+ at: at,
44
+ get_info: components[:grpc].lightning.get_info.to_h,
45
+ fee_report: components[:grpc].lightning.fee_report.to_h,
46
+ forwarding_history: forwards,
47
+ list_channels: {},
48
+ get_chan_info: {},
49
+ get_node_info: {}
50
+ }
51
+
52
+ forwards.each do |forward|
53
+ unless data[:get_chan_info][forward[:chan_id_in]]
54
+ begin
55
+ data[:get_chan_info][forward[:chan_id_in]] = components[:grpc].lightning.get_chan_info(
56
+ chan_id: forward[:chan_id_in]
57
+ ).to_h
58
+ rescue GRPC::Unknown => e
59
+ data[:get_chan_info][forward[:chan_id_in]] = { _error: e }
60
+ end
61
+ end
62
+
63
+ next if data[:get_chan_info][forward[:chan_id_out]]
64
+
65
+ begin
66
+ data[:get_chan_info][forward[:chan_id_out]] = components[:grpc].lightning.get_chan_info(
67
+ chan_id: forward[:chan_id_out]
68
+ ).to_h
69
+ rescue GRPC::Unknown => e
70
+ data[:get_chan_info][forward[:chan_id_out]] = { _error: e }
71
+ end
72
+ end
73
+
74
+ list_channels_done = {}
75
+
76
+ data[:get_chan_info].each_value do |channel|
77
+ next if channel[:_error]
78
+
79
+ partners = [channel[:node1_pub], channel[:node2_pub]]
80
+
81
+ is_mine = partners.include?(data[:get_info][:identity_pubkey])
82
+
83
+ if is_mine
84
+ partner = partners.find { |p| p != data[:get_info][:identity_pubkey] }
85
+
86
+ unless list_channels_done[partner]
87
+ components[:grpc].lightning.list_channels(
88
+ peer: [partner].pack('H*')
89
+ ).channels.map(&:to_h).each do |list_channels|
90
+ data[:list_channels][list_channels[:chan_id]] = list_channels
91
+ end
92
+
93
+ list_channels_done[partner] = true
94
+ end
95
+ end
96
+
97
+ unless data[:get_node_info][channel[:node1_pub]]
98
+ data[:get_node_info][channel[:node1_pub]] = components[:grpc].lightning.get_node_info(
99
+ pub_key: channel[:node1_pub]
100
+ ).to_h
101
+ end
102
+
103
+ next if data[:get_node_info][channel[:node2_pub]]
104
+
105
+ data[:get_node_info][channel[:node2_pub]] = components[:grpc].lightning.get_node_info(
106
+ pub_key: channel[:node2_pub]
107
+ ).to_h
108
+ end
109
+
110
+ data[:list_channels].each_value do |channel|
111
+ next if data[:get_node_info][channel[:remote_pubkey]]
112
+
113
+ data[:get_node_info][channel[:remote_pubkey]] = components[:grpc].lightning.get_node_info(
114
+ pub_key: channel[:remote_pubkey]
115
+ ).to_h
116
+ end
117
+
118
+ data
119
+ end
120
+
121
+ def self.adapt(raw)
122
+ adapted = {
123
+ get_info: Lighstorm::Adapter::Lightning::Node.get_info(raw[:get_info]),
124
+ forwarding_history: raw[:forwarding_history].map do |raw_forward|
125
+ Lighstorm::Adapter::Lightning::Forward.forwarding_history(raw_forward)
126
+ end,
127
+ fee_report: raw[:fee_report][:channel_fees].map do |raw_fee|
128
+ Lighstorm::Adapter::Lightning::Fee.fee_report(raw_fee.to_h)
129
+ end,
130
+ list_channels: {},
131
+ get_chan_info: {},
132
+ get_node_info: {}
133
+ }
134
+
135
+ raw[:get_chan_info].each_key do |key|
136
+ next if raw[:get_chan_info][key][:_error]
137
+
138
+ adapted[:get_chan_info][key] = Lighstorm::Adapter::Lightning::Channel.get_chan_info(
139
+ raw[:get_chan_info][key]
140
+ )
141
+ end
142
+
143
+ raw[:list_channels].each_key do |key|
144
+ adapted[:list_channels][key] = Lighstorm::Adapter::Lightning::Channel.list_channels(
145
+ raw[:list_channels][key], raw[:at]
146
+ )
147
+ end
148
+
149
+ raw[:get_node_info].each_key do |key|
150
+ adapted[:get_node_info][key] = Lighstorm::Adapter::Lightning::Node.get_node_info(
151
+ raw[:get_node_info][key]
152
+ )
153
+ end
154
+
155
+ adapted
156
+ end
157
+
158
+ def self.transform(data, adapted)
159
+ unless adapted[:get_chan_info][data[:id].to_i]
160
+ data[:_key] = Digest::SHA256.hexdigest(data[:id])
161
+ return data
162
+ end
163
+
164
+ data = adapted[:get_chan_info][data[:id].to_i]
165
+ data[:known] = true
166
+
167
+ [0, 1].each do |i|
168
+ if data[:partners][i][:node][:public_key] == adapted[:get_info][:public_key]
169
+ data[:partners][i][:node] =
170
+ adapted[:get_info]
171
+ end
172
+
173
+ adapted[:get_chan_info][data[:id].to_i][:partners].each do |partner|
174
+ if data[:partners][i][:node][:public_key] == partner[:node][:public_key]
175
+ data[:partners][i][:policy] = partner[:policy]
176
+ end
177
+ end
178
+
179
+ if data[:partners][i][:node][:public_key] == adapted[:get_info][:public_key]
180
+ data[:partners][i][:node][:platform] = adapted[:get_info][:platform]
181
+ data[:partners][i][:node][:myself] = true
182
+ data[:mine] = true
183
+ adapted[:fee_report].each do |channel|
184
+ next unless data[:id] == channel[:id]
185
+
186
+ data[:partners][i][:policy][:fee] = channel[:partner][:policy][:fee]
187
+ break
188
+ end
189
+ else
190
+ data[:partners][i][:node] = adapted[:get_node_info][data[:partners][i][:node][:public_key]]
191
+ data[:partners][i][:node][:platform] = {
192
+ blockchain: adapted[:get_info][:platform][:blockchain],
193
+ network: adapted[:get_info][:platform][:network]
194
+ }
195
+
196
+ data[:partners][i][:node][:myself] = false
197
+ end
198
+ end
199
+
200
+ channel = adapted[:list_channels][data[:id].to_i]
201
+
202
+ return data unless channel
203
+
204
+ channel.each_key do |key|
205
+ next if data.key?(key)
206
+
207
+ data[key] = channel[key]
208
+ end
209
+
210
+ data[:accounting] = channel[:accounting]
211
+
212
+ channel[:partners].each do |partner|
213
+ data[:partners].each_index do |i|
214
+ partner.each_key do |key|
215
+ next if data[:partners][i].key?(key)
216
+
217
+ data[:partners][i][key] = partner[key]
218
+ end
219
+ end
220
+ end
221
+
222
+ data
223
+ end
224
+
225
+ def self.data(components, limit: nil, &vcr)
226
+ raw = if vcr.nil?
227
+ fetch(components, limit: limit)
228
+ else
229
+ vcr.call(-> { fetch(components, limit: limit) })
230
+ end
231
+
232
+ adapted = adapt(raw)
233
+
234
+ adapted[:forwarding_history].map do |data|
235
+ data[:in][:channel] = transform(data[:in][:channel], adapted)
236
+ data[:out][:channel] = transform(data[:out][:channel], adapted)
237
+ data
238
+ end
239
+ end
240
+
241
+ def self.model(data)
242
+ data.map do |node_data|
243
+ Lighstorm::Model::Lightning::Forward.new(node_data)
244
+ end
245
+ end
246
+ end
247
+ end
248
+ end
249
+ end
250
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest'
4
+
5
+ require_relative './all'
6
+
7
+ require_relative '../../../models/lightning/edges/groups/channel_forwards'
8
+
9
+ module Lighstorm
10
+ module Controller
11
+ module Lightning
12
+ module Forward
13
+ module GroupByChannel
14
+ def self.filter(forwards, hours_ago)
15
+ return forwards if hours_ago.nil?
16
+
17
+ forwards.filter do |forward|
18
+ forward_hours_ago = (Time.now - Time.parse(forward[:at].to_s)).to_f / 3600
19
+ forward_hours_ago <= hours_ago
20
+ end
21
+ end
22
+
23
+ def self.group(forwards, direction)
24
+ groups = {}
25
+
26
+ forwards.each do |forward|
27
+ key = forward[direction][:channel][:id]
28
+ groups[key] = [] unless groups.key?(key)
29
+ groups[key] << forward
30
+ end
31
+
32
+ groups.values
33
+ end
34
+
35
+ def self.analyze(forwards, direction)
36
+ group = {
37
+ last_at: nil,
38
+ analysis: {
39
+ count: 0,
40
+ sums: { amount: { millisatoshis: 0 }, fee: { millisatoshis: 0 } }
41
+ },
42
+ channel: nil
43
+ }
44
+
45
+ forwards.each do |forward|
46
+ group[:last_at] = forward[:at] if group[:last_at].nil? || forward[:at] > group[:last_at]
47
+
48
+ group[:channel] = forward[direction][:channel] if group[:channel].nil?
49
+
50
+ group[:analysis][:count] += 1
51
+ group[:analysis][:sums][:amount][:millisatoshis] += forward[:in][:amount][:millisatoshis]
52
+ group[:analysis][:sums][:fee][:millisatoshis] += forward[:fee][:millisatoshis]
53
+ end
54
+
55
+ group[:_key] = _key(group)
56
+
57
+ group
58
+ end
59
+
60
+ def self._key(group)
61
+ Digest::SHA256.hexdigest(
62
+ [group[:last_at], group[:analysis][:count], group[:channel][:id]].join('/')
63
+ )
64
+ end
65
+
66
+ def self.sort(groups)
67
+ groups.sort_by { |group| - Time.parse(group[:last_at].to_s).to_f }
68
+ .sort_by { |group| - group[:analysis][:count] }
69
+ end
70
+
71
+ def self.data(components, direction: :out, hours_ago: nil, limit: nil, &vcr)
72
+ data = All.data(components, &vcr)
73
+
74
+ filtered = filter(data, hours_ago)
75
+ groups = group(filtered, direction)
76
+ analyzed = groups.map { |group| analyze(group, direction) }
77
+ sorted = sort(analyzed)
78
+
79
+ sorted = sorted[0..limit - 1] unless limit.nil?
80
+
81
+ sorted
82
+ end
83
+
84
+ def self.model(data)
85
+ data.map { |group| Model::Lightning::ChannelForwardsGroup.new(group) }
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../concerns/impersonatable'
4
+
5
+ require_relative './forward/all'
6
+ require_relative './forward/group_by_channel'
7
+
8
+ module Lighstorm
9
+ module Controller
10
+ module Lightning
11
+ module Forward
12
+ extend Impersonatable
13
+
14
+ class DSL < Impersonatable::DSL
15
+ def all(limit: nil)
16
+ All.model(All.data(components, limit: limit))
17
+ end
18
+
19
+ def first
20
+ All.model(All.data(components)).first
21
+ end
22
+
23
+ def last
24
+ All.model(All.data(components)).last
25
+ end
26
+
27
+ def group_by_channel(direction: :out, hours_ago: nil, limit: nil)
28
+ GroupByChannel.model(
29
+ GroupByChannel.data(components, direction: direction, hours_ago: hours_ago, limit: limit)
30
+ )
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../../ports/grpc'
4
+ require_relative '../../../../models/errors'
5
+ require_relative '../../../../helpers/time_expression'
6
+ require_relative '../../invoice'
7
+ require_relative '../../../action'
8
+
9
+ module Lighstorm
10
+ module Controller
11
+ module Lightning
12
+ module Invoice
13
+ module Create
14
+ def self.call(components, grpc_request)
15
+ components[:grpc].send(grpc_request[:service]).send(
16
+ grpc_request[:method], grpc_request[:params]
17
+ ).to_h
18
+ end
19
+
20
+ def self.prepare(payable:, expires_in:, description: nil, amount: nil)
21
+ request = {
22
+ service: :lightning,
23
+ method: :add_invoice,
24
+ params: {
25
+ memo: description,
26
+ # Lightning Invoice Expiration: UX Considerations
27
+ # https://d.elor.me/2022/01/lightning-invoice-expiration-ux-considerations/
28
+ expiry: Helpers::TimeExpression.seconds(expires_in)
29
+ }
30
+ }
31
+
32
+ request[:params][:value_msat] = amount[:millisatoshis] unless amount.nil?
33
+
34
+ if payable.to_sym == :indefinitely
35
+ request[:params][:is_amp] = true
36
+ elsif payable.to_sym != :once
37
+ raise Errors::ArgumentError, "payable: accepts 'indefinitely' or 'once', '#{payable}' is not valid."
38
+ end
39
+
40
+ request
41
+ end
42
+
43
+ def self.dispatch(components, grpc_request, &vcr)
44
+ if vcr.nil?
45
+ call(components, grpc_request)
46
+ else
47
+ vcr.call(-> { call(components, grpc_request) }, :dispatch)
48
+ end
49
+ end
50
+
51
+ def self.adapt(response)
52
+ Lighstorm::Adapter::Lightning::Invoice.add_invoice(response)
53
+ end
54
+
55
+ def self.fetch(components, adapted, &vcr)
56
+ FindBySecretHash.data(components, adapted[:secret][:hash], &vcr)
57
+ end
58
+
59
+ def self.model(data, components)
60
+ FindBySecretHash.model(data, components)
61
+ end
62
+
63
+ def self.perform(
64
+ components,
65
+ payable:, expires_in:, description: nil, amount: nil,
66
+ preview: false, &vcr
67
+ )
68
+ grpc_request = prepare(
69
+ description: description,
70
+ amount: amount,
71
+ expires_in: expires_in,
72
+ payable: payable
73
+ )
74
+
75
+ return grpc_request if preview
76
+
77
+ response = dispatch(components, grpc_request, &vcr)
78
+
79
+ adapted = adapt(response)
80
+
81
+ data = fetch(components, adapted, &vcr)
82
+ model = self.model(data, components)
83
+
84
+ Action::Output.new({ request: grpc_request, response: response, result: model })
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../../ports/grpc'
4
+ require_relative '../../../../models/errors'
5
+ require_relative '../../../../models/lightning/edges/payment'
6
+ require_relative '../../../../adapters/lightning/edges/payment'
7
+ require_relative '../../invoice'
8
+ require_relative '../../../action'
9
+ require_relative '../../node/myself'
10
+
11
+ require_relative '../../payment/actions/pay'
12
+
13
+ module Lighstorm
14
+ module Controller
15
+ module Lightning
16
+ module Invoice
17
+ module Pay
18
+ def self.dispatch(components, grpc_request, &vcr)
19
+ Payment::Pay.dispatch(components, grpc_request, &vcr)
20
+ end
21
+
22
+ def self.fetch(components, code, &vcr)
23
+ Payment::Pay.fetch(components, code, &vcr)
24
+ end
25
+
26
+ def self.adapt(data, node_get_info)
27
+ Payment::Pay.adapt(data, node_get_info)
28
+ end
29
+
30
+ def self.model(data, components)
31
+ Payment::Pay.model(data, components)
32
+ end
33
+
34
+ def self.prepare(code:, times_out_in:, amount: nil, fee: nil, message: nil)
35
+ request = {
36
+ service: :router,
37
+ method: :send_payment_v2,
38
+ params: {
39
+ payment_request: code,
40
+ timeout_seconds: Helpers::TimeExpression.seconds(times_out_in),
41
+ allow_self_payment: true,
42
+ dest_custom_records: {}
43
+ }
44
+ }
45
+
46
+ request[:params][:amt_msat] = amount[:millisatoshis] unless amount.nil?
47
+
48
+ unless fee.nil? || fee[:maximum].nil? || fee[:maximum][:millisatoshis].nil?
49
+ request[:params][:fee_limit_msat] = fee[:maximum][:millisatoshis]
50
+ end
51
+
52
+ if !message.nil? && !message.empty?
53
+ # https://github.com/satoshisstream/satoshis.stream/blob/main/TLV_registry.md
54
+ request[:params][:dest_custom_records][34_349_334] = message
55
+ end
56
+
57
+ request[:params].delete(:dest_custom_records) if request[:params][:dest_custom_records].empty?
58
+
59
+ request
60
+ end
61
+
62
+ def self.perform(
63
+ components,
64
+ times_out_in:, code:,
65
+ amount: nil, fee: nil,
66
+ message: nil,
67
+ preview: false, &vcr
68
+ )
69
+ grpc_request = prepare(
70
+ code: code,
71
+ amount: amount,
72
+ fee: fee,
73
+ message: message,
74
+ times_out_in: times_out_in
75
+ )
76
+
77
+ return grpc_request if preview
78
+
79
+ response = dispatch(components, grpc_request, &vcr)
80
+
81
+ Payment::Pay.raise_error_if_exists!(grpc_request, response)
82
+
83
+ data = fetch(components, code, &vcr)
84
+
85
+ adapted = adapt(response, data)
86
+
87
+ model = self.model(adapted, components)
88
+
89
+ Payment::Pay.raise_failure_if_exists!(model, grpc_request, response)
90
+
91
+ Action::Output.new({ request: grpc_request, response: response[:response], result: model })
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../../ports/grpc'
4
+ require_relative '../../../../models/errors'
5
+
6
+ module Lighstorm
7
+ module Controller
8
+ module Lightning
9
+ module Invoice
10
+ module PayThroughRoute
11
+ def self.perform(components, _invoice, route:, preview: false, fake: false)
12
+ raise Errors::ToDoError, self
13
+
14
+ channels = route.map do |channel_id|
15
+ Channel.find_by_id(channel_id, components)
16
+ end
17
+
18
+ outgoing_channel_id = channels.first.id
19
+
20
+ hops_public_keys = []
21
+
22
+ hops_public_keys << if channels.first.mine?
23
+ channels.first.partner.node.public_key
24
+ else
25
+ channels.first.partners.last.node.public_key
26
+ end
27
+
28
+ channels[1..channels.size - 2].each do |channel|
29
+ hops_public_keys << channel.partners.last.node.public_key
30
+ end
31
+
32
+ hops_public_keys << if channels.last.mine?
33
+ channels.last.myself.node.public_key
34
+ else
35
+ channels.last.partners.last.node.public_key
36
+ end
37
+ begin
38
+ route = LND.instance.middleware('router.build_route') do
39
+ LND.instance.client.router.build_route(
40
+ amt_msat: amount.millisatoshis,
41
+ outgoing_chan_id: channels.first.id.to_i,
42
+ hop_pubkeys: hops_public_keys.map { |hpk| [hpk].pack('H*') },
43
+ payment_addr: [address].pack('H*')
44
+ )
45
+ end
46
+
47
+ if preview
48
+ return {
49
+ method: :send_to_route_v2,
50
+ params: {
51
+ payment_hash: hash,
52
+ route: route.to_h[:route],
53
+ skip_temp_err: true
54
+ }
55
+ }
56
+ end
57
+
58
+ LND.instance.middleware('router.send_to_route_v2') do
59
+ LND.instance.client.router.send_to_route_v2(
60
+ payment_hash: [hash].pack('H*'),
61
+ route: route.to_h[:route],
62
+ skip_temp_err: true
63
+ )
64
+ end
65
+ rescue StandardError => e
66
+ e
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end