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,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../satoshis'
4
+ require_relative '../../../errors'
5
+ require_relative '../../../concerns/protectable'
6
+
7
+ module Lighstorm
8
+ module Model
9
+ module Lightning
10
+ class ChannelAccounting
11
+ include Protectable
12
+
13
+ def initialize(data, is_mine)
14
+ @data = data
15
+ @is_mine = is_mine
16
+ end
17
+
18
+ def capacity
19
+ @capacity ||= if @data[:capacity]
20
+ Satoshis.new(
21
+ millisatoshis: @data[:capacity][:millisatoshis]
22
+ )
23
+ end
24
+ end
25
+
26
+ def sent
27
+ raise Errors::NotYourChannelError unless @is_mine
28
+
29
+ @sent ||= if @data[:sent]
30
+ Satoshis.new(
31
+ millisatoshis: @data[:sent][:millisatoshis]
32
+ )
33
+ end
34
+ end
35
+
36
+ def received
37
+ raise Errors::NotYourChannelError unless @is_mine
38
+
39
+ @received ||= if @data[:received]
40
+ Satoshis.new(
41
+ millisatoshis: @data[:received][:millisatoshis]
42
+ )
43
+ end
44
+ end
45
+
46
+ def unsettled
47
+ raise Errors::NotYourChannelError unless @is_mine
48
+
49
+ @unsettled ||= if @data[:unsettled]
50
+ Satoshis.new(
51
+ millisatoshis: @data[:unsettled][:millisatoshis]
52
+ )
53
+ end
54
+ end
55
+
56
+ def to_h
57
+ if @is_mine
58
+ {
59
+ capacity: capacity.to_h,
60
+ sent: sent.to_h,
61
+ received: received.to_h,
62
+ unsettled: unsettled.to_h
63
+ }
64
+ else
65
+ {
66
+ capacity: capacity.to_h
67
+ }
68
+ end
69
+ end
70
+
71
+ def dump
72
+ Marshal.load(Marshal.dump(@data))
73
+ end
74
+
75
+ def capacity=(value)
76
+ protect!(value)
77
+
78
+ @capacity = value[:value]
79
+
80
+ @data[:capacity][:millisatoshis] = @capacity.millisatoshis
81
+
82
+ capacity
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../channel'
4
+
5
+ module Lighstorm
6
+ module Model
7
+ module Lightning
8
+ class HopChannel < Channel
9
+ def initialize(data, payment)
10
+ @hop_data = data
11
+ @payment = payment
12
+ super(data[:channel], nil)
13
+ end
14
+
15
+ def target
16
+ partners.find do |partner|
17
+ partner.node.public_key == @hop_data[:channel][:target][:public_key]
18
+ end.node
19
+ end
20
+
21
+ def exit
22
+ @exit ||= if include_myself? && @hop_data[:hop] == 1
23
+ partners.reverse.find do |partner|
24
+ !partner.node.myself?
25
+ end.node
26
+ elsif @hop_data[:hop] == 1
27
+ target
28
+ end
29
+ end
30
+
31
+ def entry
32
+ return nil if @hop_data[:is_last] && @hop_data[:hop] == 1
33
+
34
+ @entry ||= if include_myself? && @hop_data[:is_last]
35
+ if partners.size > 1
36
+ partners.reverse.find do |partner|
37
+ !partner.node.myself?
38
+ end.node
39
+ else
40
+ @payment.hops[@payment.hops.size - 2].channel.target
41
+ end
42
+ end
43
+ end
44
+
45
+ def include_myself?
46
+ !partners.find { |partner| partner.node.myself? }.nil?
47
+ end
48
+
49
+ def to_h
50
+ if !known? && !partners.size.positive?
51
+ { _key: _key, id: id }
52
+ else
53
+ target_hash = target.to_h
54
+ target_hash.delete(:platform)
55
+
56
+ result = { _key: _key, id: id, target: target_hash }
57
+
58
+ result[:entry] = entry.to_h if entry
59
+ result[:exit] = self.exit.to_h if self.exit
60
+
61
+ result
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,197 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'time'
4
+
5
+ require_relative '../../../ports/grpc'
6
+ require_relative '../../../adapters/lightning/edges/channel'
7
+
8
+ require_relative '../nodes/node'
9
+ require_relative './channel/accounting'
10
+
11
+ require_relative '../../../controllers/lightning/channel/actions/apply_gossip'
12
+ require_relative '../connections/channel_node'
13
+ require_relative '../../satoshis'
14
+
15
+ require_relative '../../errors'
16
+
17
+ module Lighstorm
18
+ module Model
19
+ module Lightning
20
+ class Channel
21
+ attr_reader :data, :_key, :id
22
+
23
+ def initialize(data, components)
24
+ @data = data
25
+ @components = components
26
+
27
+ @_key = data[:_key]
28
+ @id = data[:id]
29
+ end
30
+
31
+ def known?
32
+ @data[:known] == true
33
+ end
34
+
35
+ def mine?
36
+ ensure_known!
37
+
38
+ @data[:mine]
39
+ end
40
+
41
+ def exposure
42
+ ensure_known!
43
+
44
+ @data[:exposure]
45
+ end
46
+
47
+ def opened_at
48
+ ensure_mine!
49
+
50
+ @data[:opened_at]
51
+ end
52
+
53
+ def up_at
54
+ ensure_mine!
55
+
56
+ @data[:up_at]
57
+ end
58
+
59
+ def active?
60
+ ensure_mine!
61
+
62
+ @data[:state] == 'active'
63
+ end
64
+
65
+ def state
66
+ ensure_mine!
67
+
68
+ @data[:state]
69
+ end
70
+
71
+ def accounting
72
+ ensure_known!
73
+
74
+ @accounting ||= @data[:accounting] ? ChannelAccounting.new(@data[:accounting], mine?) : nil
75
+ end
76
+
77
+ def partners
78
+ @partners ||= if @data[:partners]
79
+ @data[:partners].map do |data|
80
+ ChannelNode.new(data, @components, known? ? mine? : nil, transaction)
81
+ end
82
+ else
83
+ []
84
+ end
85
+ end
86
+
87
+ def myself
88
+ ensure_mine!
89
+
90
+ @myself ||= partners.find { |partner| partner.node.myself? }
91
+ end
92
+
93
+ def partner
94
+ ensure_mine!
95
+
96
+ @partner ||= partners.find { |partner| !partner.node.myself? }
97
+ end
98
+
99
+ def transaction
100
+ Struct.new(:data) do
101
+ def funding
102
+ Struct.new(:data) do
103
+ def id
104
+ data[:id]
105
+ end
106
+
107
+ def index
108
+ data[:index]
109
+ end
110
+
111
+ def to_h
112
+ { id: id, index: index }
113
+ end
114
+ end.new(data[:funding])
115
+ end
116
+
117
+ def to_h
118
+ { funding: funding.to_h }
119
+ end
120
+ end.new(@data[:transaction])
121
+ end
122
+
123
+ def to_h
124
+ if !known? && partners.size.positive?
125
+ { _key: _key, id: id, partners: partners.map(&:to_h) }
126
+ elsif !known?
127
+ { _key: _key, id: id }
128
+ elsif mine?
129
+ {
130
+ _key: _key,
131
+ id: id,
132
+ opened_at: opened_at,
133
+ up_at: up_at,
134
+ state: state,
135
+ exposure: exposure,
136
+ accounting: accounting.to_h,
137
+ partner: partner.to_h,
138
+ myself: myself.to_h
139
+ }
140
+ elsif @data[:accounting]
141
+ {
142
+ _key: _key,
143
+ id: id,
144
+ accounting: accounting.to_h,
145
+ partners: partners.map(&:to_h)
146
+ }
147
+ else
148
+ {
149
+ _key: _key,
150
+ id: id,
151
+ partners: partners.map(&:to_h)
152
+ }
153
+ end
154
+ end
155
+
156
+ def dump
157
+ result = Marshal.load(Marshal.dump(@data)).merge(
158
+ { partners: partners.map(&:dump) }
159
+ )
160
+
161
+ result[:accounting] = accounting.dump if known?
162
+
163
+ result
164
+ end
165
+
166
+ def self.adapt(gossip: nil, dump: nil)
167
+ raise TooManyArgumentsError, 'you need to pass gossip: or dump:, not both' if !gossip.nil? && !dump.nil?
168
+
169
+ raise ArgumentError, 'missing gossip: or dump:' if gossip.nil? && dump.nil?
170
+
171
+ if !gossip.nil?
172
+ new(Adapter::Lightning::Channel.subscribe_channel_graph(gossip), nil)
173
+ elsif !dump.nil?
174
+ new(dump, nil)
175
+ end
176
+ end
177
+
178
+ def apply!(gossip:)
179
+ Controller::Lightning::Channel::ApplyGossip.perform(
180
+ self, gossip
181
+ )
182
+ end
183
+
184
+ private
185
+
186
+ def ensure_known!
187
+ raise Errors::UnknownChannelError unless known?
188
+ end
189
+
190
+ def ensure_mine!
191
+ ensure_known!
192
+ raise Errors::NotYourChannelError if @data[:mine] == false
193
+ end
194
+ end
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'time'
4
+
5
+ require_relative '../../satoshis'
6
+
7
+ require_relative '../connections/forward_channel'
8
+ require_relative 'groups/channel_forwards'
9
+
10
+ module Lighstorm
11
+ module Model
12
+ module Lightning
13
+ class Forward
14
+ attr_reader :_key, :at
15
+
16
+ def initialize(data, components)
17
+ @data = data
18
+ @components = components
19
+
20
+ @_key = data[:_key]
21
+ @at = data[:at]
22
+ end
23
+
24
+ def fee
25
+ @fee ||= Satoshis.new(millisatoshis: @data[:fee][:millisatoshis])
26
+ end
27
+
28
+ def in
29
+ @in ||= ForwardChannel.new(@data[:in], @components)
30
+ end
31
+
32
+ def out
33
+ @out ||= ForwardChannel.new(@data[:out], @components)
34
+ end
35
+
36
+ def to_h
37
+ {
38
+ _key: _key,
39
+ at: at,
40
+ fee: {
41
+ millisatoshis: fee.millisatoshis,
42
+ parts_per_million: fee.parts_per_million(self.in.amount.millisatoshis)
43
+ },
44
+ in: self.in.to_h,
45
+ out: out.to_h
46
+ }
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lighstorm
4
+ module Model
5
+ module Lightning
6
+ class ChannelForwardsGroup
7
+ Analysis = Struct.new(:data) do
8
+ def count
9
+ data[:count]
10
+ end
11
+
12
+ def sums
13
+ Struct.new(:sums) do
14
+ def amount
15
+ Satoshis.new(millisatoshis: sums[:amount][:millisatoshis])
16
+ end
17
+
18
+ def fee
19
+ Satoshis.new(millisatoshis: sums[:fee][:millisatoshis])
20
+ end
21
+
22
+ def to_h
23
+ {
24
+ amount: amount.to_h,
25
+ fee: {
26
+ millisatoshis: fee.millisatoshis,
27
+ parts_per_million: fee.parts_per_million(amount.millisatoshis)
28
+ }
29
+ }
30
+ end
31
+ end.new(data[:sums])
32
+ end
33
+
34
+ def averages
35
+ Struct.new(:data) do
36
+ def amount
37
+ Satoshis.new(
38
+ millisatoshis: data[:sums][:amount][:millisatoshis].to_f / data[:count]
39
+ )
40
+ end
41
+
42
+ def fee
43
+ Satoshis.new(
44
+ millisatoshis: data[:sums][:fee][:millisatoshis].to_f / data[:count]
45
+ )
46
+ end
47
+
48
+ def to_h
49
+ {
50
+ amount: amount.to_h,
51
+ fee: {
52
+ millisatoshis: fee.millisatoshis,
53
+ parts_per_million: fee.parts_per_million(amount.millisatoshis)
54
+ }
55
+ }
56
+ end
57
+ end.new(data)
58
+ end
59
+
60
+ def to_h
61
+ {
62
+ count: count,
63
+ sums: sums.to_h,
64
+ averages: averages.to_h
65
+ }
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../satoshis'
4
+ require_relative 'channel_forwards/analysis'
5
+
6
+ module Lighstorm
7
+ module Model
8
+ module Lightning
9
+ class ChannelForwardsGroup
10
+ attr_reader :_key, :last_at
11
+
12
+ def initialize(data, components)
13
+ @data = data
14
+ @components = components
15
+
16
+ @_key = data[:_key]
17
+ @last_at = data[:last_at]
18
+ end
19
+
20
+ def analysis
21
+ Analysis.new(@data[:analysis])
22
+ end
23
+
24
+ def channel
25
+ @channel ||= Channel.new(@data[:channel], @components)
26
+ end
27
+
28
+ def to_h
29
+ {
30
+ _key: _key,
31
+ last_at: last_at,
32
+ analysis: analysis.to_h,
33
+ channel: channel.to_h
34
+ }
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'time'
4
+
5
+ require_relative '../../satoshis'
6
+
7
+ require_relative '../connections/payment_channel'
8
+ require_relative '../nodes/node'
9
+ require_relative '../invoice'
10
+ require_relative '../secret'
11
+
12
+ module Lighstorm
13
+ module Model
14
+ module Lightning
15
+ class Payment
16
+ attr_reader :_key, :at, :state, :secret, :purpose, :through, :message
17
+
18
+ def initialize(data, components)
19
+ @data = data
20
+ @components = components
21
+
22
+ @_key = data[:_key]
23
+ @at = data[:at]
24
+ @state = data[:state]
25
+ @purpose = data[:purpose]
26
+ @through = data[:through]
27
+ @message = data[:message]
28
+ end
29
+
30
+ def how
31
+ @how ||= spontaneous? ? 'spontaneously' : 'with-invoice'
32
+ end
33
+
34
+ def invoice
35
+ @invoice ||= !spontaneous? && @data[:invoice] ? Invoice.new(@data[:invoice], @components) : nil
36
+ end
37
+
38
+ def amount
39
+ @amount ||= @data[:amount] ? Satoshis.new(millisatoshis: @data[:amount][:millisatoshis]) : nil
40
+ end
41
+
42
+ def fee
43
+ @fee ||= @data[:fee] ? Satoshis.new(millisatoshis: @data[:fee][:millisatoshis]) : nil
44
+ end
45
+
46
+ def secret
47
+ @secret ||= @data[:secret] ? Secret.new(@data[:secret], @components) : nil
48
+ end
49
+
50
+ def hops
51
+ return @hops if @hops
52
+ return nil if @data[:hops].nil?
53
+
54
+ @data[:hops].last[:is_last] = true
55
+ @hops = @data[:hops].map do |hop|
56
+ PaymentChannel.new(hop, self)
57
+ end
58
+ end
59
+
60
+ def from
61
+ @from ||= @data[:hops].nil? ? nil : hops.first
62
+ end
63
+
64
+ def to
65
+ @to ||= @data[:hops].nil? ? nil : hops.last
66
+ end
67
+
68
+ def to_h
69
+ response = {
70
+ _key: _key,
71
+ at: at,
72
+ state: state,
73
+ through: through,
74
+ purpose: purpose,
75
+ how: how,
76
+ message: message,
77
+ invoice: invoice&.to_h,
78
+ from: from.to_h,
79
+ to: to.to_h
80
+ }
81
+
82
+ response[:secret] = secret.to_h if secret
83
+ response[:amount] = amount.to_h if amount
84
+ if fee
85
+ response[:fee] = {
86
+ millisatoshis: fee.millisatoshis,
87
+ parts_per_million: fee.parts_per_million(amount.millisatoshis)
88
+ }
89
+ end
90
+
91
+ response[:hops] = hops.map(&:to_h) unless hops.nil?
92
+
93
+ response
94
+ end
95
+
96
+ private
97
+
98
+ def spontaneous?
99
+ !@data[:invoice] || @data[:invoice][:code].nil?
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end