lighstorm 0.0.15 → 0.0.16

Sign up to get free protection for your applications and to get access to all the features.
Files changed (156) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/README.md +7 -7
  4. data/adapters/bitcoin/address.rb +19 -0
  5. data/adapters/bitcoin/transaction.rb +34 -0
  6. data/adapters/lightning/connections/channel_node/fee.rb +28 -0
  7. data/adapters/lightning/connections/channel_node/policy.rb +63 -0
  8. data/adapters/lightning/connections/channel_node.rb +77 -0
  9. data/adapters/lightning/connections/payment_channel.rb +30 -0
  10. data/adapters/lightning/edges/channel.rb +98 -0
  11. data/adapters/lightning/edges/forward.rb +42 -0
  12. data/adapters/lightning/edges/payment/purpose.rb +45 -0
  13. data/adapters/lightning/edges/payment.rb +89 -0
  14. data/adapters/lightning/invoice.rb +186 -0
  15. data/adapters/lightning/nodes/node.rb +113 -0
  16. data/controllers/action.rb +1 -1
  17. data/controllers/bitcoin/address/actions/create.rb +91 -0
  18. data/controllers/bitcoin/address/actions/pay.rb +119 -0
  19. data/controllers/bitcoin/address.rb +24 -0
  20. data/controllers/bitcoin/transaction/all.rb +65 -0
  21. data/controllers/bitcoin/transaction.rb +25 -0
  22. data/controllers/concerns/impersonatable.rb +5 -3
  23. data/controllers/connection.rb +1 -1
  24. data/controllers/lightning/channel/actions/apply_gossip.rb +201 -0
  25. data/controllers/lightning/channel/actions/update_fee.rb +94 -0
  26. data/controllers/lightning/channel/all.rb +85 -0
  27. data/controllers/lightning/channel/find_by_id.rb +155 -0
  28. data/controllers/lightning/channel/mine.rb +116 -0
  29. data/controllers/lightning/channel.rb +43 -0
  30. data/controllers/lightning/forward/all.rb +250 -0
  31. data/controllers/lightning/forward/group_by_channel.rb +91 -0
  32. data/controllers/lightning/forward.rb +36 -0
  33. data/controllers/lightning/invoice/actions/create.rb +90 -0
  34. data/controllers/lightning/invoice/actions/pay.rb +97 -0
  35. data/controllers/lightning/invoice/actions/pay_through_route.rb +73 -0
  36. data/controllers/lightning/invoice/all.rb +82 -0
  37. data/controllers/lightning/invoice/decode.rb +50 -0
  38. data/controllers/lightning/invoice/find_by_code.rb +76 -0
  39. data/controllers/lightning/invoice/find_by_secret_hash.rb +56 -0
  40. data/controllers/lightning/invoice.rb +64 -0
  41. data/controllers/lightning/node/actions/apply_gossip.rb +114 -0
  42. data/controllers/lightning/node/actions/pay.rb +117 -0
  43. data/controllers/lightning/node/all.rb +69 -0
  44. data/controllers/lightning/node/find_by_public_key.rb +55 -0
  45. data/controllers/lightning/node/myself.rb +36 -0
  46. data/controllers/lightning/node.rb +35 -0
  47. data/controllers/lightning/payment/actions/pay.rb +110 -0
  48. data/controllers/lightning/payment/all.rb +439 -0
  49. data/controllers/lightning/payment.rb +37 -0
  50. data/controllers/lightning/secret/valid_proof.rb +60 -0
  51. data/controllers/lightning/secret.rb +16 -0
  52. data/controllers/wallet/activity/all.rb +196 -0
  53. data/controllers/wallet/activity.rb +28 -0
  54. data/controllers/wallet/balance.rb +3 -3
  55. data/controllers/wallet.rb +1 -1
  56. data/docs/README.md +229 -148
  57. data/docs/_coverpage.md +1 -1
  58. data/docs/index.html +1 -1
  59. data/models/bitcoin/address.rb +49 -0
  60. data/models/bitcoin/transaction.rb +41 -0
  61. data/models/concerns/protectable.rb +1 -1
  62. data/models/errors.rb +7 -8
  63. data/models/lightning/connections/channel_node/accounting.rb +27 -0
  64. data/models/lightning/connections/channel_node/fee.rb +82 -0
  65. data/models/lightning/connections/channel_node/htlc/blocks/delta.rb +41 -0
  66. data/models/lightning/connections/channel_node/htlc.rb +88 -0
  67. data/models/lightning/connections/channel_node/policy.rb +41 -0
  68. data/models/lightning/connections/channel_node.rb +86 -0
  69. data/models/lightning/connections/forward_channel.rb +31 -0
  70. data/models/lightning/connections/payment_channel.rb +58 -0
  71. data/models/lightning/edges/channel/accounting.rb +87 -0
  72. data/models/lightning/edges/channel/hop.rb +67 -0
  73. data/models/lightning/edges/channel.rb +197 -0
  74. data/models/lightning/edges/forward.rb +51 -0
  75. data/models/lightning/edges/groups/channel_forwards/analysis.rb +71 -0
  76. data/models/lightning/edges/groups/channel_forwards.rb +39 -0
  77. data/models/lightning/edges/payment.rb +104 -0
  78. data/models/lightning/invoice.rb +115 -0
  79. data/models/lightning/nodes/node/lightning.rb +33 -0
  80. data/models/lightning/nodes/node/platform.rb +50 -0
  81. data/models/lightning/nodes/node.rb +145 -0
  82. data/models/lightning/rate.rb +37 -0
  83. data/models/lightning/secret.rb +57 -0
  84. data/models/satoshis.rb +1 -1
  85. data/models/wallet/activity.rb +54 -0
  86. data/models/wallet/balance.rb +42 -0
  87. data/ports/dsl/lighstorm.rb +29 -16
  88. data/static/cache.rb +2 -0
  89. data/static/spec.rb +1 -1
  90. metadata +74 -68
  91. data/adapters/connections/channel_node/fee.rb +0 -26
  92. data/adapters/connections/channel_node/policy.rb +0 -61
  93. data/adapters/connections/channel_node.rb +0 -75
  94. data/adapters/connections/payment_channel.rb +0 -28
  95. data/adapters/edges/channel.rb +0 -96
  96. data/adapters/edges/forward.rb +0 -40
  97. data/adapters/edges/payment/purpose.rb +0 -43
  98. data/adapters/edges/payment.rb +0 -87
  99. data/adapters/invoice.rb +0 -184
  100. data/adapters/nodes/node.rb +0 -111
  101. data/adapters/transaction.rb +0 -23
  102. data/controllers/activity/all.rb +0 -194
  103. data/controllers/activity.rb +0 -26
  104. data/controllers/channel/actions/apply_gossip.rb +0 -193
  105. data/controllers/channel/actions/update_fee.rb +0 -91
  106. data/controllers/channel/all.rb +0 -83
  107. data/controllers/channel/find_by_id.rb +0 -153
  108. data/controllers/channel/mine.rb +0 -114
  109. data/controllers/channel.rb +0 -41
  110. data/controllers/forward/all.rb +0 -248
  111. data/controllers/forward/group_by_channel.rb +0 -89
  112. data/controllers/forward.rb +0 -34
  113. data/controllers/invoice/actions/create.rb +0 -88
  114. data/controllers/invoice/actions/pay.rb +0 -95
  115. data/controllers/invoice/actions/pay_through_route.rb +0 -71
  116. data/controllers/invoice/all.rb +0 -80
  117. data/controllers/invoice/decode.rb +0 -44
  118. data/controllers/invoice/find_by_code.rb +0 -70
  119. data/controllers/invoice/find_by_secret_hash.rb +0 -54
  120. data/controllers/invoice.rb +0 -62
  121. data/controllers/node/actions/apply_gossip.rb +0 -112
  122. data/controllers/node/actions/pay.rb +0 -115
  123. data/controllers/node/all.rb +0 -67
  124. data/controllers/node/find_by_public_key.rb +0 -53
  125. data/controllers/node/myself.rb +0 -34
  126. data/controllers/node.rb +0 -33
  127. data/controllers/payment/actions/pay.rb +0 -108
  128. data/controllers/payment/all.rb +0 -437
  129. data/controllers/payment.rb +0 -35
  130. data/controllers/secret/valid_proof.rb +0 -58
  131. data/controllers/transaction/all.rb +0 -61
  132. data/controllers/transaction.rb +0 -25
  133. data/models/activity.rb +0 -52
  134. data/models/connections/channel_node/accounting.rb +0 -25
  135. data/models/connections/channel_node/fee.rb +0 -80
  136. data/models/connections/channel_node/htlc/blocks/delta.rb +0 -39
  137. data/models/connections/channel_node/htlc.rb +0 -86
  138. data/models/connections/channel_node/policy.rb +0 -39
  139. data/models/connections/channel_node.rb +0 -84
  140. data/models/connections/forward_channel.rb +0 -29
  141. data/models/connections/payment_channel.rb +0 -56
  142. data/models/edges/channel/accounting.rb +0 -85
  143. data/models/edges/channel/hop.rb +0 -65
  144. data/models/edges/channel.rb +0 -195
  145. data/models/edges/forward.rb +0 -49
  146. data/models/edges/groups/channel_forwards/analysis.rb +0 -69
  147. data/models/edges/groups/channel_forwards.rb +0 -37
  148. data/models/edges/payment.rb +0 -102
  149. data/models/invoice.rb +0 -113
  150. data/models/nodes/node/lightning.rb +0 -31
  151. data/models/nodes/node/platform.rb +0 -48
  152. data/models/nodes/node.rb +0 -143
  153. data/models/rate.rb +0 -35
  154. data/models/secret.rb +0 -55
  155. data/models/transaction.rb +0 -39
  156. data/models/wallet.rb +0 -40
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../ports/grpc'
4
+ require_relative '../../../adapters/lightning/invoice'
5
+ require_relative '../../../models/lightning/invoice'
6
+
7
+ module Lighstorm
8
+ module Controller
9
+ module Lightning
10
+ module Invoice
11
+ module All
12
+ def self.fetch(components, limit: nil, spontaneous: false)
13
+ at = Time.now
14
+
15
+ last_offset = 0
16
+
17
+ invoices = []
18
+
19
+ loop do
20
+ response = components[:grpc].lightning.list_invoices(
21
+ index_offset: last_offset,
22
+ num_max_invoices: 10
23
+ )
24
+
25
+ response.invoices.each do |invoice|
26
+ invoices << invoice.to_h if spontaneous || !invoice.payment_request.empty?
27
+ end
28
+
29
+ # TODO: How to optimize this?
30
+ # break if !limit.nil? && invoices.size >= limit
31
+
32
+ break if last_offset == response.last_index_offset || last_offset > response.last_index_offset
33
+
34
+ last_offset = response.last_index_offset
35
+ end
36
+
37
+ invoices = invoices.sort_by { |raw_invoice| -raw_invoice[:creation_date] }
38
+
39
+ invoices = invoices[0..limit - 1] unless limit.nil?
40
+
41
+ { at: at, list_invoices: invoices }
42
+ end
43
+
44
+ def self.adapt(raw)
45
+ raise 'missing at' if raw[:at].nil?
46
+
47
+ {
48
+ list_invoices: raw[:list_invoices].map do |raw_invoice|
49
+ Lighstorm::Adapter::Lightning::Invoice.list_invoices(raw_invoice, raw[:at])
50
+ end
51
+ }
52
+ end
53
+
54
+ def self.transform(adapted)
55
+ adapted[:list_invoices].map do |invoice|
56
+ invoice[:known] = true
57
+ invoice
58
+ end
59
+ end
60
+
61
+ def self.data(components, limit: nil, spontaneous: false, &vcr)
62
+ raw = if vcr.nil?
63
+ fetch(components, limit: limit, spontaneous: spontaneous)
64
+ else
65
+ vcr.call(-> { fetch(components, limit: limit, spontaneous: spontaneous) })
66
+ end
67
+
68
+ adapted = adapt(raw)
69
+
70
+ transform(adapted)
71
+ end
72
+
73
+ def self.model(data, components)
74
+ data.map do |node_data|
75
+ Lighstorm::Model::Lightning::Invoice.new(node_data, components)
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../ports/grpc'
4
+ require_relative '../../../adapters/lightning/invoice'
5
+ require_relative '../../../models/lightning/invoice'
6
+
7
+ module Lighstorm
8
+ module Controller
9
+ module Lightning
10
+ module Invoice
11
+ module Decode
12
+ def self.fetch(components, code)
13
+ {
14
+ _code: code,
15
+ decode_pay_req: components[:grpc].lightning.decode_pay_req(pay_req: code).to_h
16
+ }
17
+ end
18
+
19
+ def self.adapt(raw)
20
+ {
21
+ decode_pay_req: Lighstorm::Adapter::Lightning::Invoice.decode_pay_req(
22
+ raw[:decode_pay_req], raw[:_code]
23
+ )
24
+ }
25
+ end
26
+
27
+ def self.transform(adapted)
28
+ adapted[:decode_pay_req]
29
+ end
30
+
31
+ def self.data(components, code, &vcr)
32
+ raw = if vcr.nil?
33
+ fetch(components, code.sub('lightning:', ''))
34
+ else
35
+ vcr.call(-> { fetch(components, code.sub('lightning:', '')) })
36
+ end
37
+
38
+ adapted = adapt(raw)
39
+
40
+ transform(adapted)
41
+ end
42
+
43
+ def self.model(data, components)
44
+ Lighstorm::Model::Lightning::Invoice.new(data, components)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../ports/grpc'
4
+ require_relative '../../../adapters/lightning/invoice'
5
+ require_relative '../../../models/lightning/invoice'
6
+
7
+ module Lighstorm
8
+ module Controller
9
+ module Lightning
10
+ module Invoice
11
+ module FindByCode
12
+ def self.fetch(components, code)
13
+ at = Time.now
14
+
15
+ decoded = components[:grpc].lightning.decode_pay_req(pay_req: code).to_h
16
+
17
+ { response: {
18
+ at: at,
19
+ decode_pay_req: decoded,
20
+ lookup_invoice: components[:grpc].lightning.lookup_invoice(r_hash_str: decoded[:payment_hash]).to_h
21
+ }, exception: nil }
22
+ rescue StandardError => e
23
+ { exception: e }
24
+ end
25
+
26
+ def self.adapt(raw)
27
+ raise 'missing at' if raw[:at].nil?
28
+
29
+ {
30
+ lookup_invoice: Lighstorm::Adapter::Lightning::Invoice.lookup_invoice(
31
+ raw[:lookup_invoice],
32
+ raw[:at]
33
+ )
34
+ }
35
+ end
36
+
37
+ def self.transform(adapted)
38
+ adapted[:lookup_invoice][:known] = true
39
+ adapted[:lookup_invoice]
40
+ end
41
+
42
+ def self.data(components, code, &vcr)
43
+ raw = if vcr.nil?
44
+ fetch(components, code.sub('lightning:', ''))
45
+ else
46
+ vcr.call(-> { fetch(components, code.sub('lightning:', '')) })
47
+ end
48
+
49
+ raise_error_if_exists!(raw)
50
+
51
+ adapted = adapt(raw[:response])
52
+
53
+ transform(adapted)
54
+ end
55
+
56
+ def self.model(data, components)
57
+ Lighstorm::Model::Lightning::Invoice.new(data, components)
58
+ end
59
+
60
+ def self.raise_error_if_exists!(response)
61
+ return if response[:exception].nil?
62
+
63
+ if response[:exception].is_a?(GRPC::NotFound)
64
+ raise NoInvoiceFoundError.new(
65
+ "Invoice not found. Try using Invoice.decode if you don't own the invoice.",
66
+ grpc: response[:exception]
67
+ )
68
+ end
69
+
70
+ raise LighstormError.new(grpc: response[:exception])
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../ports/grpc'
4
+ require_relative '../../../adapters/lightning/invoice'
5
+ require_relative '../../../models/lightning/invoice'
6
+
7
+ module Lighstorm
8
+ module Controller
9
+ module Lightning
10
+ module Invoice
11
+ module FindBySecretHash
12
+ def self.fetch(components, secret_hash)
13
+ { response: {
14
+ at: Time.now,
15
+ lookup_invoice: components[:grpc].lightning.lookup_invoice(r_hash_str: secret_hash).to_h
16
+ }, exception: nil }
17
+ rescue StandardError => e
18
+ { exception: e }
19
+ end
20
+
21
+ def self.adapt(raw)
22
+ raise 'missing at' if raw[:at].nil?
23
+
24
+ {
25
+ lookup_invoice: Lighstorm::Adapter::Lightning::Invoice.lookup_invoice(
26
+ raw[:lookup_invoice],
27
+ raw[:at]
28
+ )
29
+ }
30
+ end
31
+
32
+ def self.transform(adapted)
33
+ adapted[:lookup_invoice][:known] = true
34
+ adapted[:lookup_invoice]
35
+ end
36
+
37
+ def self.data(components, secret_hash, &vcr)
38
+ raw = if vcr.nil?
39
+ fetch(components, secret_hash)
40
+ else
41
+ vcr.call(-> { fetch(components, secret_hash) })
42
+ end
43
+
44
+ adapted = adapt(raw[:response])
45
+
46
+ transform(adapted)
47
+ end
48
+
49
+ def self.model(data, components)
50
+ Lighstorm::Model::Lightning::Invoice.new(data, components)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../concerns/impersonatable'
4
+
5
+ require_relative './invoice/all'
6
+ require_relative './invoice/decode'
7
+ require_relative './invoice/find_by_secret_hash'
8
+ require_relative './invoice/find_by_code'
9
+ require_relative './invoice/actions/create'
10
+
11
+ module Lighstorm
12
+ module Controller
13
+ module Lightning
14
+ module Invoice
15
+ extend Impersonatable
16
+
17
+ class DSL < Impersonatable::DSL
18
+ def all(limit: nil, spontaneous: false)
19
+ All.model(All.data(components, limit: limit, spontaneous: spontaneous), components)
20
+ end
21
+
22
+ def first
23
+ All.model(All.data(components), components).first
24
+ end
25
+
26
+ def last
27
+ All.model(All.data(components), components).last
28
+ end
29
+
30
+ def find_by_secret_hash(secret_hash, &vcr)
31
+ FindBySecretHash.model(FindBySecretHash.data(components, secret_hash, &vcr), components)
32
+ end
33
+
34
+ def find_by_code(code, &vcr)
35
+ FindByCode.model(FindByCode.data(components, code, &vcr), components)
36
+ end
37
+
38
+ def decode(code, &vcr)
39
+ Decode.model(Decode.data(components, code, &vcr), components)
40
+ end
41
+
42
+ def create(
43
+ payable:,
44
+ description: nil, amount: nil,
45
+ # Lightning Invoice Expiration: UX Considerations
46
+ # https://d.elor.me/2022/01/lightning-invoice-expiration-ux-considerations/
47
+ expires_in: { hours: 24 },
48
+ preview: false, &vcr
49
+ )
50
+ Create.perform(
51
+ components,
52
+ payable: payable,
53
+ description: description,
54
+ amount: amount,
55
+ expires_in: expires_in,
56
+ preview: preview,
57
+ &vcr
58
+ )
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ require_relative '../../../../ports/grpc'
6
+ require_relative '../../../../models/errors'
7
+ require_relative '../../../../models/lightning/nodes/node'
8
+ require_relative '../../../../adapters/lightning/nodes/node'
9
+
10
+ module Lighstorm
11
+ module Controller
12
+ module Lightning
13
+ module Node
14
+ module ApplyGossip
15
+ SKIPABLE = %w[
16
+ _source
17
+ _key
18
+ ].freeze
19
+
20
+ NOT_ALLOWED = [
21
+ 'public_key'
22
+ ].freeze
23
+
24
+ APPLICABLE = %w[
25
+ alias
26
+ color
27
+ ].freeze
28
+
29
+ def self.perform(actual, gossip)
30
+ updated = Model::Lightning::Node.new(Adapter::Lightning::Node.subscribe_channel_graph(gossip), nil)
31
+
32
+ actual_dump = actual.dump
33
+ updated_dump = updated.dump
34
+
35
+ diff = generate_diff(actual_dump, updated_dump)
36
+
37
+ diff.each do |change|
38
+ key = change[:path].join('/')
39
+ next unless NOT_ALLOWED.include?(key)
40
+
41
+ raise IncoherentGossipError, "Gossip doesn't belong to this Node"
42
+ end
43
+
44
+ diff.filter do |change|
45
+ key = change[:path].join('/')
46
+ if SKIPABLE.include?(key)
47
+ false
48
+ elsif APPLICABLE.include?(key)
49
+ apply!(actual, key, change)
50
+ true
51
+ else
52
+ raise Lighstorm::Errors::MissingGossipHandlerError, "don't know how to apply '#{key}'"
53
+ end
54
+ end
55
+ end
56
+
57
+ def self.apply!(actual, key, change)
58
+ case key
59
+ when 'alias'
60
+ token = SecureRandom.hex
61
+ actual.prepare_token!(token)
62
+ actual.alias = {
63
+ value: change[:to],
64
+ token: token
65
+ }
66
+ when 'color'
67
+ token = SecureRandom.hex
68
+ actual.prepare_token!(token)
69
+ actual.color = {
70
+ value: change[:to],
71
+ token: token
72
+ }
73
+ else
74
+ raise Lighstorm::Errors::MissingGossipHandlerError, "don't know how to apply '#{key}'"
75
+ end
76
+ end
77
+
78
+ def self.generate_diff(actual, node, path = [], diff = [])
79
+ case node
80
+ when Hash
81
+ result = {}
82
+ node.each_key do |key|
83
+ result[key] = generate_diff(actual, node[key], path.dup.push(key), diff)
84
+ end
85
+ when Array
86
+ result = []
87
+ node.each_with_index do |value, i|
88
+ result << generate_diff(actual, value, path.dup.push(i), diff)
89
+ end
90
+ else
91
+ new_value = node
92
+
93
+ unless new_value.nil?
94
+ actual_value = actual
95
+ path.each do |key|
96
+ if actual_value[key]
97
+ actual_value = actual_value[key]
98
+ else
99
+ actual_value = nil
100
+ break
101
+ end
102
+ end
103
+
104
+ diff << { path: path, from: actual_value, to: new_value } if actual_value != new_value
105
+ end
106
+ end
107
+
108
+ diff
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+ require 'digest'
5
+
6
+ require_relative '../../../../ports/grpc'
7
+ require_relative '../../../../models/lightning/secret'
8
+ require_relative '../../../../models/errors'
9
+ require_relative '../../../../models/lightning/edges/payment'
10
+ require_relative '../../../../adapters/lightning/edges/payment'
11
+ require_relative '../../invoice'
12
+ require_relative '../../../action'
13
+ require_relative '../../node/myself'
14
+
15
+ require_relative '../../payment/actions/pay'
16
+
17
+ module Lighstorm
18
+ module Controller
19
+ module Lightning
20
+ module Node
21
+ module Pay
22
+ def self.dispatch(components, grpc_request, &vcr)
23
+ Payment::Pay.dispatch(components, grpc_request, &vcr)
24
+ end
25
+
26
+ def self.fetch(components, &vcr)
27
+ Payment::Pay.fetch(components, &vcr)
28
+ end
29
+
30
+ def self.adapt(data, node_get_info)
31
+ Payment::Pay.adapt(data, node_get_info)
32
+ end
33
+
34
+ def self.model(data, components)
35
+ Payment::Pay.model(data, components)
36
+ end
37
+
38
+ def self.prepare(public_key:, amount:, times_out_in:, secret:, through:, fee: nil, message: nil)
39
+ # Appreciation note for people that suffered in the past and shared
40
+ # their knowledge, so we don't have to struggle the same:
41
+ # - https://github.com/lightningnetwork/lnd/discussions/6357
42
+ # - https://docs.lightning.engineering/lightning-network-tools/lnd/send-messages-with-keysend
43
+ # - https://peakd.com/@brianoflondon/lightning-keysend-is-strange-and-how-to-send-keysend-payment-in-lightning-with-the-lnd-rest-api-via-python
44
+ # We are standing on the shoulders of giants, thank you very much. :)
45
+ request = {
46
+ service: :router,
47
+ method: :send_payment_v2,
48
+ params: {
49
+ dest: [public_key].pack('H*'),
50
+ amt_msat: amount[:millisatoshis],
51
+ timeout_seconds: Helpers::TimeExpression.seconds(times_out_in),
52
+ allow_self_payment: true,
53
+ dest_custom_records: {}
54
+ }
55
+ }
56
+
57
+ if !message.nil? && !message.empty?
58
+ # https://github.com/satoshisstream/satoshis.stream/blob/main/TLV_registry.md
59
+ request[:params][:dest_custom_records][34_349_334] = message
60
+ end
61
+
62
+ unless fee.nil? || fee[:maximum].nil? || fee[:maximum][:millisatoshis].nil?
63
+ request[:params][:fee_limit_msat] = fee[:maximum][:millisatoshis]
64
+ end
65
+
66
+ if through.to_sym == :keysend
67
+ request[:params][:payment_hash] = [secret[:hash]].pack('H*')
68
+ request[:params][:dest_custom_records][5_482_373_484] = [secret[:proof]].pack('H*')
69
+ elsif through.to_sym == :amp
70
+ request[:params][:amp] = true
71
+ end
72
+
73
+ request[:params].delete(:dest_custom_records) if request[:params][:dest_custom_records].empty?
74
+
75
+ request
76
+ end
77
+
78
+ def self.perform(
79
+ components,
80
+ public_key:, amount:, through:,
81
+ times_out_in:, fee: nil,
82
+ message: nil, secret: nil,
83
+ preview: false, &vcr
84
+ )
85
+ secret = Model::Lightning::Secret.create.to_h if secret.nil? && through.to_sym == :keysend
86
+
87
+ grpc_request = prepare(
88
+ public_key: public_key,
89
+ amount: amount,
90
+ fee: fee,
91
+ through: through,
92
+ times_out_in: times_out_in,
93
+ secret: secret,
94
+ message: message
95
+ )
96
+
97
+ return grpc_request if preview
98
+
99
+ response = dispatch(components, grpc_request, &vcr)
100
+
101
+ Payment::Pay.raise_error_if_exists!(grpc_request, response)
102
+
103
+ data = fetch(components, &vcr)
104
+
105
+ adapted = adapt(response, data)
106
+
107
+ model = self.model(adapted, components)
108
+
109
+ Payment::Pay.raise_failure_if_exists!(model, grpc_request, response)
110
+
111
+ Action::Output.new({ request: grpc_request, response: response[:response], result: model })
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../ports/grpc'
4
+ require_relative '../../../adapters/lightning/nodes/node'
5
+
6
+ module Lighstorm
7
+ module Controller
8
+ module Lightning
9
+ module Node
10
+ module All
11
+ def self.fetch(components, limit: nil)
12
+ data = {
13
+ at: Time.now,
14
+ get_info: components[:grpc].lightning.get_info.to_h,
15
+ describe_graph: components[:grpc].lightning.describe_graph.nodes
16
+ }
17
+
18
+ data[:describe_graph] = data[:describe_graph][0..limit - 1] unless limit.nil?
19
+
20
+ data
21
+ end
22
+
23
+ def self.adapt(raw)
24
+ {
25
+ get_info: Lighstorm::Adapter::Lightning::Node.get_info(raw[:get_info]),
26
+ describe_graph: raw[:describe_graph].map do |raw|
27
+ Lighstorm::Adapter::Lightning::Node.describe_graph(raw.to_h)
28
+ end
29
+ }
30
+ end
31
+
32
+ def self.transform(describe_graph, get_info)
33
+ if get_info[:public_key] == describe_graph[:public_key]
34
+ get_info.merge(myself: true)
35
+ else
36
+ describe_graph.merge(
37
+ platform: {
38
+ blockchain: get_info[:platform][:blockchain],
39
+ network: get_info[:platform][:network]
40
+ },
41
+ myself: false
42
+ )
43
+ end
44
+ end
45
+
46
+ def self.data(components, limit: nil, &vcr)
47
+ raw = if vcr.nil?
48
+ fetch(components, limit: limit)
49
+ else
50
+ vcr.call(-> { fetch(components, limit: limit) })
51
+ end
52
+
53
+ adapted = adapt(raw)
54
+
55
+ adapted[:describe_graph].map do |data|
56
+ transform(data, adapted[:get_info])
57
+ end
58
+ end
59
+
60
+ def self.model(data, components)
61
+ data.map do |node_data|
62
+ Lighstorm::Model::Lightning::Node.new(node_data, components)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../ports/grpc'
4
+ require_relative '../../../adapters/lightning/nodes/node'
5
+
6
+ module Lighstorm
7
+ module Controller
8
+ module Lightning
9
+ module Node
10
+ module FindByPublicKey
11
+ def self.fetch(components, public_key)
12
+ {
13
+ at: Time.now,
14
+ get_info: components[:grpc].lightning.get_info.to_h,
15
+ get_node_info: components[:grpc].lightning.get_node_info(pub_key: public_key).to_h
16
+ }
17
+ end
18
+
19
+ def self.adapt(raw)
20
+ {
21
+ get_info: Lighstorm::Adapter::Lightning::Node.get_info(raw[:get_info]),
22
+ get_node_info: Lighstorm::Adapter::Lightning::Node.get_node_info(raw[:get_node_info])
23
+ }
24
+ end
25
+
26
+ def self.data(components, public_key, &vcr)
27
+ raw = if vcr.nil?
28
+ fetch(components, public_key)
29
+ else
30
+ vcr.call(-> { fetch(components, public_key) })
31
+ end
32
+
33
+ adapted = adapt(raw)
34
+
35
+ if adapted[:get_info][:public_key] == adapted[:get_node_info][:public_key]
36
+ adapted[:get_info].merge(myself: true)
37
+ else
38
+ adapted[:get_node_info].merge(
39
+ platform: {
40
+ blockchain: adapted[:get_info][:platform][:blockchain],
41
+ network: adapted[:get_info][:platform][:network]
42
+ },
43
+ myself: false
44
+ )
45
+ end
46
+ end
47
+
48
+ def self.model(data, components)
49
+ Lighstorm::Model::Lightning::Node.new(data, components)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end