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,194 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ require_relative '../../../ports/grpc'
6
+ require_relative '../../../models/errors'
7
+ require_relative '../../../models/edges/channel'
8
+ require_relative '../../../adapters/edges/channel'
9
+
10
+ module Lighstorm
11
+ module Controllers
12
+ module Channel
13
+ module ApplyGossip
14
+ SKIPABLE = [
15
+ '_key',
16
+ '_source',
17
+ 'partners/0/_source',
18
+ 'partners/0/policy/_source',
19
+ 'partners/1/_source',
20
+ 'partners/1/policy/_source'
21
+ ].freeze
22
+
23
+ NOT_ALLOWED = [
24
+ 'id'
25
+ ].freeze
26
+
27
+ APPLICABLE = [
28
+ 'accounting/capacity/milisatoshis',
29
+ 'partners/0/policy/fee/base/milisatoshis',
30
+ 'partners/0/state',
31
+ 'partners/1/policy/fee/base/milisatoshis',
32
+ 'partners/1/state',
33
+ 'partners/1/policy/fee/rate/parts_per_million',
34
+ 'partners/0/policy/fee/rate/parts_per_million',
35
+ 'partners/0/policy/htlc/minimum/milisatoshis',
36
+ 'partners/1/policy/htlc/minimum/milisatoshis',
37
+ 'partners/0/policy/htlc/maximum/milisatoshis',
38
+ 'partners/1/policy/htlc/maximum/milisatoshis',
39
+ 'partners/0/policy/htlc/blocks/delta/minimum',
40
+ 'partners/1/policy/htlc/blocks/delta/minimum'
41
+ ].freeze
42
+
43
+ def self.perform(actual, gossip)
44
+ updated = Models::Channel.new(Adapter::Channel.subscribe_channel_graph(gossip))
45
+
46
+ actual_dump = actual.dump
47
+ updated_dump = updated.dump
48
+
49
+ if actual.partners.first.node.public_key == updated.partners.last.node.public_key &&
50
+ actual.partners.last.node.public_key == updated.partners.first.node.public_key
51
+ a = updated_dump[:partners][0]
52
+ b = updated_dump[:partners][1]
53
+
54
+ updated_dump[:partners][0] = b
55
+ updated_dump[:partners][1] = a
56
+ end
57
+
58
+ diff = generate_diff(actual_dump, updated_dump)
59
+
60
+ diff.each do |change|
61
+ key = change[:path].join('/')
62
+ next unless NOT_ALLOWED.include?(key)
63
+
64
+ raise IncoherentGossipError, "Gossip doesn't belong to this Channel"
65
+ end
66
+
67
+ diff.filter do |change|
68
+ key = change[:path].join('/')
69
+ if SKIPABLE.include?(key)
70
+ false
71
+ elsif APPLICABLE.include?(key)
72
+ apply!(actual, key, change)
73
+ true
74
+ else
75
+ raise Lighstorm::Errors::MissingGossipHandlerError, "don't know how to apply '#{key}'"
76
+ end
77
+ end
78
+ end
79
+
80
+ def self.apply!(actual, key, change)
81
+ case key
82
+ when 'accounting/capacity/milisatoshis'
83
+ token = SecureRandom.hex
84
+ actual.accounting.prepare_token!(token)
85
+ actual.accounting.capacity = {
86
+ value: Models::Satoshis.new(milisatoshis: change[:to]),
87
+ token: token
88
+ }
89
+ when 'partners/0/policy/htlc/maximum/milisatoshis',
90
+ 'partners/1/policy/htlc/maximum/milisatoshis' then
91
+ policy = actual.partners[change[:path][1]].policy
92
+
93
+ token = SecureRandom.hex
94
+ policy.htlc.prepare_token!(token)
95
+ policy.htlc.maximum = {
96
+ value: Models::Satoshis.new(milisatoshis: change[:to]),
97
+ token: token
98
+ }
99
+ when 'partners/0/policy/htlc/minimum/milisatoshis',
100
+ 'partners/1/policy/htlc/minimum/milisatoshis' then
101
+ if actual.partners[change[:path][1]].policy.nil?
102
+ actual.partners[change[:path][1]].policy = Lighstorm::Models::Policy.new({})
103
+ end
104
+
105
+ policy = actual.partners[change[:path][1]].policy
106
+
107
+ token = SecureRandom.hex
108
+ policy.htlc.prepare_token!(token)
109
+ policy.htlc.minimum = {
110
+ value: Models::Satoshis.new(milisatoshis: change[:to]),
111
+ token: token
112
+ }
113
+ when 'partners/0/policy/htlc/blocks/delta/minimum',
114
+ 'partners/1/policy/htlc/blocks/delta/minimum' then
115
+ if actual.partners[change[:path][1]].policy.nil?
116
+ actual.partners[change[:path][1]].policy = Lighstorm::Models::Policy.new({})
117
+ end
118
+
119
+ policy = actual.partners[change[:path][1]].policy
120
+
121
+ token = SecureRandom.hex
122
+ policy.htlc.blocks.delta.prepare_token!(token)
123
+ policy.htlc.blocks.delta.minimum = {
124
+ value: change[:to],
125
+ token: token
126
+ }
127
+ when 'partners/0/policy/fee/rate/parts_per_million',
128
+ 'partners/1/policy/fee/rate/parts_per_million' then
129
+ policy = actual.partners[change[:path][1]].policy
130
+
131
+ token = SecureRandom.hex
132
+ policy.fee.prepare_token!(token)
133
+ policy.fee.rate = {
134
+ value: Models::Rate.new(parts_per_million: change[:to]),
135
+ token: token
136
+ }
137
+ when 'partners/0/policy/fee/base/milisatoshis',
138
+ 'partners/1/policy/fee/base/milisatoshis' then
139
+ policy = actual.partners[change[:path][1]].policy
140
+
141
+ token = SecureRandom.hex
142
+ policy.fee.prepare_token!(token)
143
+ policy.fee.base = {
144
+ value: Models::Satoshis.new(milisatoshis: change[:to]),
145
+ token: token
146
+ }
147
+ when 'partners/0/state',
148
+ 'partners/1/state' then
149
+ partner = actual.partners[change[:path][1]]
150
+
151
+ token = SecureRandom.hex
152
+ partner.prepare_token!(token)
153
+ partner.state = { value: change[:to], token: token }
154
+ else
155
+ raise Lighstorm::Errors::MissingGossipHandlerError, "don't know how to apply '#{key}'"
156
+ end
157
+ end
158
+
159
+ def self.generate_diff(actual, node, path = [], diff = [])
160
+ case node
161
+ when Hash
162
+ result = {}
163
+ node.each_key do |key|
164
+ result[key] = generate_diff(actual, node[key], path.dup.push(key), diff)
165
+ end
166
+ when Array
167
+ result = []
168
+ node.each_with_index do |value, i|
169
+ result << generate_diff(actual, value, path.dup.push(i), diff)
170
+ end
171
+ else
172
+ new_value = node
173
+
174
+ unless new_value.nil?
175
+ actual_value = actual
176
+ path.each do |key|
177
+ if actual_value[key]
178
+ actual_value = actual_value[key]
179
+ else
180
+ actual_value = nil
181
+ break
182
+ end
183
+ end
184
+
185
+ diff << { path: path, from: actual_value, to: new_value } if actual_value != new_value
186
+ end
187
+ end
188
+
189
+ diff
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../ports/grpc'
4
+ require_relative '../../../models/errors'
5
+ require_relative '../../../models/satoshis'
6
+ require_relative '../../../models/rate'
7
+
8
+ module Lighstorm
9
+ module Controllers
10
+ module Channel
11
+ module UpdateFee
12
+ def self.perform(policy, params, preview: false, fake: false)
13
+ grpc_request = {
14
+ service: :lightning,
15
+ method: :update_channel_policy,
16
+ params: {
17
+ chan_point: {
18
+ funding_txid_str: policy.transaction.funding.id,
19
+ output_index: policy.transaction.funding.index
20
+ },
21
+ base_fee_msat: policy.fee.base.milisatoshis,
22
+ fee_rate_ppm: policy.fee.rate.parts_per_million,
23
+ time_lock_delta: policy.htlc.blocks.delta.minimum,
24
+ max_htlc_msat: policy.htlc.maximum.milisatoshis,
25
+ min_htlc_msat: policy.htlc.minimum.milisatoshis
26
+ }
27
+ }
28
+
29
+ if params[:rate] && params[:rate][:parts_per_million]
30
+ if (params[:rate][:parts_per_million]).negative?
31
+ raise Errors::NegativeNotAllowedError, "fee rate can't be negative: #{params[:rate][:parts_per_million]}"
32
+ end
33
+
34
+ grpc_request[:params][:fee_rate_ppm] = params[:rate][:parts_per_million]
35
+ end
36
+
37
+ if params[:base] && params[:base][:milisatoshis]
38
+ if (params[:base][:milisatoshis]).negative?
39
+ raise Errors::NegativeNotAllowedError, "fee base can't be negative: #{params[:base][:milisatoshis]}"
40
+ end
41
+
42
+ grpc_request[:params][:base_fee_msat] = params[:base][:milisatoshis]
43
+ end
44
+
45
+ return grpc_request if preview
46
+
47
+ response = if fake
48
+ :fake
49
+ else
50
+ LND.instance.middleware("lightning.#{grpc_request[:method]}") do
51
+ LND.instance.client.lightning.send(grpc_request[:method], grpc_request[:params])
52
+ end
53
+ end
54
+
55
+ raise UpdateChannelPolicyError.new(nil, response) unless fake || response.failed_updates.empty?
56
+
57
+ token = SecureRandom.hex
58
+ policy.fee.prepare_token!(token)
59
+ policy.fee.base = {
60
+ value: Models::Satoshis.new(milisatoshis: grpc_request[:params][:base_fee_msat]),
61
+ token: token
62
+ }
63
+
64
+ token = SecureRandom.hex
65
+ policy.fee.prepare_token!(token)
66
+ policy.fee.rate = {
67
+ value: Models::Rate.new(parts_per_million: grpc_request[:params][:fee_rate_ppm]),
68
+ token: token
69
+ }
70
+
71
+ response
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'mine'
4
+
5
+ require_relative '../../ports/grpc'
6
+ require_relative '../../adapters/edges/channel'
7
+ require_relative '../../adapters/nodes/node'
8
+ require_relative '../../adapters/connections/channel_node/fee'
9
+
10
+ module Lighstorm
11
+ module Controllers
12
+ module Channel
13
+ module All
14
+ def self.fetch(limit: nil)
15
+ data = {
16
+ at: Time.now,
17
+ mine: Mine.fetch,
18
+ describe_graph: Ports::GRPC.lightning.describe_graph.edges
19
+ }
20
+
21
+ data[:describe_graph] = data[:describe_graph][0..limit - 1] unless limit.nil?
22
+
23
+ data
24
+ end
25
+
26
+ def self.adapt(raw)
27
+ mine_adapted = Mine.adapt(raw[:mine])
28
+
29
+ mine = mine_adapted[:list_channels].map do |data|
30
+ Mine.transform(data, mine_adapted)
31
+ end
32
+
33
+ adapted = {
34
+ mine: {},
35
+ describe_graph: raw[:describe_graph].map do |raw_channel|
36
+ Lighstorm::Adapter::Channel.describe_graph(raw_channel.to_h)
37
+ end
38
+ }
39
+
40
+ mine.each do |channel|
41
+ adapted[:mine][channel[:id]] = channel
42
+ end
43
+
44
+ adapted
45
+ end
46
+
47
+ def self.transform(data, adapted)
48
+ return adapted[:mine][data[:id]] if adapted[:mine][data[:id]]
49
+
50
+ data[:known] = true
51
+ data[:mine] = false
52
+
53
+ data[:partners].each do |partner|
54
+ partner[:node][:platform] = {
55
+ blockchain: adapted[:mine].first[1][:partners][0][:node][:platform][:blockchain],
56
+ network: adapted[:mine].first[1][:partners][0][:node][:platform][:network]
57
+ }
58
+ end
59
+
60
+ data
61
+ end
62
+
63
+ def self.data(limit: nil, &vcr)
64
+ raw = vcr.nil? ? fetch(limit: limit) : vcr.call(-> { fetch(limit: limit) })
65
+
66
+ adapted = adapt(raw)
67
+
68
+ adapted[:describe_graph].map { |data| transform(data, adapted) }
69
+ end
70
+
71
+ def self.model(data)
72
+ data.map do |node_data|
73
+ Lighstorm::Models::Channel.new(node_data)
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -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].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].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,27 @@
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
+
22
+ def self.adapt(dump: nil, gossip: nil)
23
+ Models::Channel.adapt(dump: dump, gossip: gossip)
24
+ end
25
+ end
26
+ end
27
+ end