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,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lighstorm
4
+ module Adapter
5
+ class Policy
6
+ def self.get_chan_info(grpc)
7
+ {
8
+ fee: {
9
+ base: { milisatoshis: grpc[:fee_base_msat] },
10
+ rate: { parts_per_million: grpc[:fee_rate_milli_msat] }
11
+ },
12
+ htlc: {
13
+ minimum: { milisatoshis: grpc[:min_htlc] },
14
+ maximum: { milisatoshis: grpc[:max_htlc_msat] },
15
+ # https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#cltv_expiry_delta-selection
16
+ blocks: {
17
+ delta: {
18
+ minimum: grpc[:time_lock_delta] # aka cltv_expiry_delta
19
+ }
20
+ }
21
+ }
22
+ }
23
+ end
24
+
25
+ def self.subscribe_channel_graph(json)
26
+ result = {
27
+ _source: :subscribe_channel_graph,
28
+ fee: {
29
+ base: { milisatoshis: json['routingPolicy']['feeBaseMsat'].to_i },
30
+ rate: { parts_per_million: json['routingPolicy']['feeRateMilliMsat'].to_i }
31
+ },
32
+ htlc: {
33
+ minimum: { milisatoshis: json['routingPolicy']['minHtlc'].to_i },
34
+ maximum: { milisatoshis: json['routingPolicy']['maxHtlcMsat'].to_i },
35
+ # https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#cltv_expiry_delta-selection
36
+ blocks: {
37
+ delta: {
38
+ minimum: json['routingPolicy']['timeLockDelta'].to_i # aka cltv_expiry_delta
39
+ }
40
+ }
41
+ }
42
+ }
43
+
44
+ policy = json['routingPolicy']
45
+
46
+ result[:fee].delete(:base) unless policy.key?('feeBaseMsat') && !policy['feeBaseMsat'].nil?
47
+ result[:fee].delete(:rate) unless policy.key?('feeRateMilliMsat') && !policy['feeRateMilliMsat'].nil?
48
+ result.delete(:fee) if result[:fee].empty?
49
+
50
+ result[:htlc].delete(:minimum) unless policy.key?('minHtlc') && !policy['minHtlc'].nil?
51
+ result[:htlc].delete(:maximum) unless policy.key?('maxHtlcMsat') && !policy['maxHtlcMsat'].nil?
52
+ result[:htlc].delete(:blocks) unless policy.key?('timeLockDelta') && !policy['timeLockDelta'].nil?
53
+ result.delete(:htlc) if result[:htlc].empty?
54
+
55
+ return nil unless result.key?(:fee) || result.key?(:htlc)
56
+
57
+ result
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../nodes/node'
4
+ require_relative 'channel_node/policy'
5
+
6
+ module Lighstorm
7
+ module Adapter
8
+ class ChannelNode
9
+ def self.list_channels(grpc, key)
10
+ data = {
11
+ _source: :list_channels,
12
+ state: grpc[:active] ? 'active' : 'inactive',
13
+ accounting: { balance: { milisatoshis: grpc[:"#{key}_balance"] * 1000 } },
14
+ node: Node.list_channels(grpc, key)
15
+ }
16
+
17
+ data.delete(:node) if data[:node].nil?
18
+
19
+ data
20
+ end
21
+
22
+ def self.get_chan_info(grpc, index)
23
+ data = {
24
+ _source: :get_chan_info,
25
+ node: Node.get_chan_info(grpc, index)
26
+ }
27
+
28
+ if grpc[:"node#{index}_policy"]
29
+ data[:state] = grpc[:"node#{index}_policy"][:disabled] ? 'inactive' : 'active'
30
+ data[:policy] = Policy.get_chan_info(grpc[:"node#{index}_policy"])
31
+ end
32
+
33
+ data.delete(:node) if data[:node].nil?
34
+
35
+ data
36
+ end
37
+
38
+ def self.describe_graph(grpc, index)
39
+ data = {
40
+ _source: :describe_graph,
41
+ node: Node.describe_graph_from_channel(grpc, index)
42
+ }
43
+
44
+ # TODO: No examples to validate the correctness of this scenario.
45
+ if grpc[:"node#{index}_policy"]
46
+ data[:state] = grpc[:"node#{index}_policy"][:disabled] ? 'inactive' : 'active'
47
+ data[:policy] = Policy.get_chan_info(grpc[:"node#{index}_policy"])
48
+ end
49
+
50
+ data.delete(:node) if data[:node].nil?
51
+
52
+ data
53
+ end
54
+
55
+ def self.subscribe_channel_graph(json)
56
+ data = {
57
+ _source: :subscribe_channel_graph,
58
+ node: {
59
+ public_key: json['advertisingNode']
60
+ },
61
+ policy: Policy.subscribe_channel_graph(json)
62
+ }
63
+
64
+ unless json['routingPolicy']['disabled'].nil?
65
+ data[:state] = json['routingPolicy']['disabled'] ? 'inactive' : 'active'
66
+ end
67
+
68
+ data.delete(:policy) if data[:policy].nil?
69
+
70
+ data
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest'
4
+
5
+ module Lighstorm
6
+ module Adapter
7
+ class PaymentChannel
8
+ def self.list_payments(grpc, index)
9
+ {
10
+ _source: :list_payments,
11
+ hop: index + 1,
12
+ amount: { milisatoshis: grpc[:amt_to_forward_msat] },
13
+ fee: { milisatoshis: grpc[:fee_msat] },
14
+ channel: {
15
+ _key: Digest::SHA256.hexdigest(grpc[:chan_id].to_s),
16
+ id: grpc[:chan_id].to_s,
17
+ partners: [
18
+ node: {
19
+ _key: Digest::SHA256.hexdigest(grpc[:pub_key].to_s),
20
+ public_key: grpc[:pub_key]
21
+ }
22
+ ]
23
+ }
24
+ }
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest'
4
+
5
+ require_relative '../connections/channel_node'
6
+
7
+ module Lighstorm
8
+ module Adapter
9
+ class Channel
10
+ def self._key(id, state)
11
+ Digest::SHA256.hexdigest(
12
+ [id, state].join('/')
13
+ )
14
+ end
15
+
16
+ def self.list_channels(grpc, at)
17
+ {
18
+ _source: :list_channels,
19
+ _key: _key(grpc[:chan_id], grpc[:active] ? 'active' : 'inactive'),
20
+ # Standard JSON don't support BigInt, so, a String is safer.
21
+ id: grpc[:chan_id].to_s,
22
+ transaction: {
23
+ funding: {
24
+ id: grpc[:channel_point].split(':').first,
25
+ index: grpc[:channel_point].split(':').last.to_i
26
+ }
27
+ },
28
+ opened_at: at - grpc[:lifetime],
29
+ up_at: at - grpc[:uptime],
30
+ state: grpc[:active] ? 'active' : 'inactive',
31
+ exposure: grpc[:private] ? 'private' : 'public',
32
+ accounting: {
33
+ capacity: { milisatoshis: grpc[:capacity] * 1000 },
34
+ sent: { milisatoshis: grpc[:total_satoshis_sent] * 1000 },
35
+ received: { milisatoshis: grpc[:total_satoshis_received] * 1000 },
36
+ unsettled: { milisatoshis: grpc[:unsettled_balance] * 1000 }
37
+ },
38
+ partners: [
39
+ ChannelNode.list_channels(grpc, :local),
40
+ ChannelNode.list_channels(grpc, :remote)
41
+ ]
42
+ }
43
+ end
44
+
45
+ def self.get_chan_info(grpc)
46
+ {
47
+ _source: :get_chan_info,
48
+ _key: _key(grpc[:channel_id], nil),
49
+ # Standard JSON don't support BigInt, so, a String is safer.
50
+ id: grpc[:channel_id].to_s,
51
+ accounting: {
52
+ capacity: { milisatoshis: grpc[:capacity] * 1000 }
53
+ },
54
+ partners: [
55
+ ChannelNode.get_chan_info(grpc, 1),
56
+ ChannelNode.get_chan_info(grpc, 2)
57
+ ]
58
+ }
59
+ end
60
+
61
+ def self.describe_graph(grpc)
62
+ {
63
+ _source: :describe_graph,
64
+ _key: _key(grpc[:channel_id], nil),
65
+ # Standard JSON don't support BigInt, so, a String is safer.
66
+ id: grpc[:channel_id].to_s,
67
+ exposure: 'public',
68
+ accounting: {
69
+ capacity: { milisatoshis: grpc[:capacity] * 1000 }
70
+ },
71
+ partners: [
72
+ ChannelNode.describe_graph(grpc, 1),
73
+ ChannelNode.describe_graph(grpc, 2)
74
+ ]
75
+ }
76
+ end
77
+
78
+ def self.subscribe_channel_graph(json)
79
+ {
80
+ _source: :subscribe_channel_graph,
81
+ _key: _key(json['chanId'], nil),
82
+ id: json['chanId'],
83
+ accounting: {
84
+ capacity: { milisatoshis: json['capacity'].to_i * 1000 }
85
+ },
86
+ partners: [
87
+ ChannelNode.subscribe_channel_graph(json),
88
+ { node: {
89
+ public_key: json['connectingNode']
90
+ } }
91
+ ]
92
+ }
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest'
4
+
5
+ module Lighstorm
6
+ module Adapter
7
+ class Forward
8
+ def self.forwarding_history(grpc)
9
+ {
10
+ _source: :forwarding_history,
11
+ _key: _key(grpc),
12
+ at: Time.at(grpc[:timestamp_ns] / 1e+9),
13
+ fee: { milisatoshis: grpc[:fee_msat] },
14
+ in: {
15
+ amount: { milisatoshis: grpc[:amt_in_msat] },
16
+ channel: {
17
+ id: grpc[:chan_id_in].to_s
18
+ }
19
+ },
20
+ out: {
21
+ amount: { milisatoshis: grpc[:amt_out_msat] },
22
+ channel: {
23
+ id: grpc[:chan_id_out].to_s
24
+ }
25
+ }
26
+ }
27
+ end
28
+
29
+ def self._key(grpc)
30
+ Digest::SHA256.hexdigest(
31
+ [
32
+ grpc[:timestamp_ns],
33
+ grpc[:chan_id_in], grpc[:chan_id_out],
34
+ grpc[:amt_in_msat], grpc[:fee_msat]
35
+ ].join('/')
36
+ )
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lighstorm
4
+ module Adapter
5
+ class Purpose
6
+ def self.list_payments(grpc, node_get_info)
7
+ return 'self-payment' if self_payment?(grpc)
8
+ return 'peer-to-peer' if peer_to_peer?(grpc)
9
+ return 'rebalance' if rebalance?(grpc, node_get_info)
10
+
11
+ 'payment'
12
+ end
13
+
14
+ def self.self_payment?(grpc)
15
+ grpc[:htlcs].first[:route][:hops].size == 2 &&
16
+ grpc[:htlcs].first[:route][:hops][0][:chan_id] == grpc[:htlcs].first[:route][:hops][1][:chan_id]
17
+ end
18
+
19
+ def self.peer_to_peer?(grpc)
20
+ grpc[:htlcs].first[:route][:hops].size == 1
21
+ end
22
+
23
+ def self.rebalance?(grpc, node_get_info)
24
+ return false if grpc[:htlcs].first[:route][:hops].size <= 2
25
+
26
+ destination_public_key = grpc[:htlcs].first[:route][:hops].last[:pub_key]
27
+
28
+ node_get_info[:identity_pubkey] == destination_public_key
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest'
4
+
5
+ require_relative '../connections/payment_channel'
6
+ require_relative 'payment/purpose'
7
+
8
+ require_relative '../payment_request'
9
+
10
+ require_relative '../../ports/dsl/lighstorm/errors'
11
+
12
+ module Lighstorm
13
+ module Adapter
14
+ class Payment
15
+ def self._key(grpc)
16
+ Digest::SHA256.hexdigest(
17
+ [
18
+ grpc[:payment_request],
19
+ grpc[:creation_time_ns],
20
+ grpc[:resolve_time_ns],
21
+ grpc[:status]
22
+ ].join('/')
23
+ )
24
+ end
25
+
26
+ def self.list_payments(grpc, node_get_info)
27
+ raise UnexpectedNumberOfHTLCsError, "htlcs: #{grpc[:htlcs].size}" if grpc[:htlcs].size > 1
28
+
29
+ data = {
30
+ _source: :list_payments,
31
+ _key: _key(grpc),
32
+ created_at: Time.at(grpc[:creation_time_ns] / 1e+9),
33
+ status: grpc[:status].to_s.downcase,
34
+ fee: { milisatoshis: grpc[:fee_msat] },
35
+ purpose: Purpose.list_payments(grpc, node_get_info),
36
+ request: PaymentRequest.list_payments(grpc),
37
+ hops: grpc[:htlcs].first[:route][:hops].map.with_index do |raw_hop, i|
38
+ hop = PaymentChannel.list_payments(raw_hop, i)
39
+ hop[:channel][:target] = { public_key: raw_hop[:pub_key] }
40
+ hop
41
+ end
42
+ }
43
+
44
+ data[:settled_at] = Time.at(grpc[:htlcs].first[:resolve_time_ns] / 1e+9) if grpc[:htlcs].first[:resolve_time_ns]
45
+
46
+ data
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest'
4
+
5
+ require_relative 'payment_request'
6
+
7
+ module Lighstorm
8
+ module Adapter
9
+ class Invoice
10
+ def self.lookup_invoice(grpc)
11
+ adapted = list_or_lookup(grpc)
12
+
13
+ adapted[:_source] = :lookup_invoice
14
+ adapted[:request] = PaymentRequest.lookup_invoice(grpc)
15
+
16
+ adapted
17
+ end
18
+
19
+ def self.list_invoices(grpc)
20
+ adapted = list_or_lookup(grpc)
21
+
22
+ adapted[:_source] = :list_invoices
23
+ adapted[:request] = PaymentRequest.list_invoices(grpc)
24
+
25
+ adapted
26
+ end
27
+
28
+ def self.list_or_lookup(grpc)
29
+ {
30
+ _key: _key(grpc),
31
+ created_at: Time.at(grpc[:creation_date]),
32
+ settle_at: Time.at(grpc[:settle_date]),
33
+ state: grpc[:state].to_s.downcase
34
+ }
35
+ end
36
+
37
+ def self._key(grpc)
38
+ Digest::SHA256.hexdigest(
39
+ [
40
+ grpc[:creation_date],
41
+ grpc[:settle_date],
42
+ grpc[:payment_request],
43
+ grpc[:state]
44
+ ].join('/')
45
+ )
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest'
4
+
5
+ module Lighstorm
6
+ module Adapter
7
+ class Node
8
+ def self._key(public_key)
9
+ Digest::SHA256.hexdigest(
10
+ [public_key].join('/')
11
+ )
12
+ end
13
+
14
+ def self.get_chan_info(grpc, index)
15
+ {
16
+ _source: :get_chan_info,
17
+ _key: _key(grpc[:"node#{index}_pub"]),
18
+ public_key: grpc[:"node#{index}_pub"]
19
+ }
20
+ end
21
+
22
+ def self.list_channels(grpc, key)
23
+ data = {
24
+ _source: :list_channels,
25
+ _key: _key(key == :remote ? grpc[:remote_pubkey] : nil),
26
+ public_key: key == :remote ? grpc[:remote_pubkey] : nil
27
+ }
28
+
29
+ return nil if data[:public_key].nil?
30
+
31
+ data
32
+ end
33
+
34
+ def self.get_info(grpc)
35
+ data = {
36
+ _source: :get_info,
37
+ _key: _key(grpc[:identity_pubkey]),
38
+ public_key: grpc[:identity_pubkey],
39
+ alias: grpc[:alias],
40
+ color: grpc[:color],
41
+ platform: {
42
+ blockchain: grpc[:chains][0][:chain],
43
+ network: grpc[:chains][0][:network],
44
+ lightning: {
45
+ implementation: 'lnd',
46
+ version: grpc[:version]
47
+ }
48
+ }
49
+ }
50
+
51
+ return nil if data[:public_key].nil? && data[:alias].nil? && data[:color].nil?
52
+
53
+ data
54
+ end
55
+
56
+ def self.get_node_info(grpc)
57
+ data = {
58
+ _source: :get_node_info,
59
+ _key: _key(grpc[:node][:pub_key]),
60
+ public_key: grpc[:node][:pub_key],
61
+ alias: grpc[:node][:alias],
62
+ color: grpc[:node][:color]
63
+ }
64
+
65
+ return nil if data[:public_key].nil? && data[:alias].nil? && data[:color].nil?
66
+
67
+ data
68
+ end
69
+
70
+ def self.describe_graph(grpc)
71
+ data = {
72
+ _source: :describe_graph,
73
+ _key: _key(grpc[:pub_key]),
74
+ public_key: grpc[:pub_key],
75
+ alias: grpc[:alias],
76
+ color: grpc[:color]
77
+ }
78
+
79
+ return nil if data[:public_key].nil? && data[:alias].nil? && data[:color].nil?
80
+
81
+ data
82
+ end
83
+
84
+ def self.describe_graph_from_channel(grpc, index)
85
+ data = {
86
+ _source: :describe_graph,
87
+ _key: _key(grpc[:"node#{index}_pub"]),
88
+ public_key: grpc[:"node#{index}_pub"]
89
+ }
90
+
91
+ return nil if data[:public_key].nil?
92
+
93
+ data
94
+ end
95
+
96
+ def self.subscribe_channel_graph(json)
97
+ data = {
98
+ _source: :subscribe_channel_graph,
99
+ _key: _key(json['identityKey']),
100
+ public_key: json['identityKey'],
101
+ alias: json['alias'],
102
+ color: json['color']
103
+ }
104
+
105
+ return nil if data[:public_key].nil? && data[:alias].nil? && data[:color].nil?
106
+
107
+ data
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest'
4
+
5
+ require_relative '../ports/dsl/lighstorm/errors'
6
+
7
+ module Lighstorm
8
+ module Adapter
9
+ class PaymentRequest
10
+ def self.decode_pay_req(grpc)
11
+ {
12
+ _source: :decode_pay_req,
13
+ amount: { milisatoshis: grpc[:num_msat] },
14
+ description: {
15
+ memo: grpc[:description],
16
+ hash: grpc[:description_hash] == '' ? nil : grpc[:description_hash]
17
+ },
18
+ address: grpc[:payment_addr].unpack1('H*'),
19
+ secret: {
20
+ hash: grpc[:payment_hash]
21
+ }
22
+ }
23
+ end
24
+
25
+ def self.lookup_invoice(grpc)
26
+ adapted = list_or_lookup_invoice(grpc)
27
+ adapted[:_source] = :lookup_invoice
28
+ adapted
29
+ end
30
+
31
+ def self.list_invoices(grpc)
32
+ adapted = list_or_lookup_invoice(grpc)
33
+ adapted[:_source] = :list_invoices
34
+ adapted
35
+ end
36
+
37
+ def self.list_or_lookup_invoice(grpc)
38
+ {
39
+ code: grpc[:payment_request],
40
+ amount: { milisatoshis: grpc[:value_msat] },
41
+ description: {
42
+ memo: grpc[:memo],
43
+ hash: grpc[:description_hash] == '' ? nil : grpc[:description_hash]
44
+ },
45
+ address: grpc[:payment_addr].unpack1('H*'),
46
+ secret: {
47
+ preimage: grpc[:r_preimage].unpack1('H*'),
48
+ hash: grpc[:r_hash].unpack1('H*')
49
+ }
50
+ }
51
+ end
52
+
53
+ def self.list_payments(grpc)
54
+ raise UnexpectedNumberOfHTLCsError, "htlcs: #{grpc[:htlcs].size}" if grpc[:htlcs].size > 1
55
+
56
+ data = {
57
+ _source: :list_payments,
58
+ code: grpc[:payment_request],
59
+ amount: { milisatoshis: grpc[:value_msat] },
60
+ secret: {
61
+ preimage: grpc[:payment_preimage],
62
+ hash: grpc[:payment_hash]
63
+ }
64
+ }
65
+
66
+ grpc[:htlcs].first[:route][:hops].map do |raw_hop|
67
+ if raw_hop[:mpp_record] && raw_hop[:mpp_record][:payment_addr]
68
+ data[:address] = raw_hop[:mpp_record][:payment_addr].unpack1('H*')
69
+ end
70
+ end
71
+
72
+ data
73
+ end
74
+ end
75
+ end
76
+ end
data/components/cache.rb CHANGED
@@ -5,6 +5,7 @@ require 'singleton'
5
5
  require 'zache'
6
6
 
7
7
  require_relative '../static/cache'
8
+ require_relative '../ports/dsl/lighstorm/errors'
8
9
 
9
10
  module Lighstorm
10
11
  class Cache
@@ -23,7 +24,7 @@ module Lighstorm
23
24
  def for(key, ttl: nil, params: {}, &block)
24
25
  if ttl.nil?
25
26
  ttl = Lighstorm::Static::CACHE[key.sub('lightning.', '').to_sym]
26
- raise "missing ttl for #{key}" if ttl.nil?
27
+ raise MissingTTLError, "missing ttl for #{key} static/cache.rb" if ttl.nil?
27
28
 
28
29
  ttl = ttl[:ttl]
29
30
  end
@@ -36,7 +37,7 @@ module Lighstorm
36
37
  end
37
38
 
38
39
  def build_key_for(key, params)
39
- return key unless params.size.positive?
40
+ return key unless !params.nil? && params.size.positive?
40
41
 
41
42
  key_params = []
42
43
  params.keys.sort.each do |param_key|
data/components/lnd.rb CHANGED
@@ -4,6 +4,8 @@ require 'singleton'
4
4
 
5
5
  require 'lnd-client'
6
6
 
7
+ require_relative '../ports/dsl/lighstorm/errors'
8
+
7
9
  module Lighstorm
8
10
  class LND
9
11
  include Singleton
@@ -23,7 +25,9 @@ module Lighstorm
23
25
  def client
24
26
  return @client if @client
25
27
 
26
- raise 'missing credentials' if @config.nil? && ENV.fetch('LIGHSTORM_CERTIFICATE_PATH', nil).nil?
28
+ raise MissingCredentialsError, 'missing credentials' if @config.nil? && ENV.fetch(
29
+ 'LIGHSTORM_CERTIFICATE_PATH', nil
30
+ ).nil?
27
31
 
28
32
  @client = if @config
29
33
  create_client_from_config