lighstorm 0.0.1 → 0.0.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dc2a22398ed869fa9709af719d701c1fa00f5475f8de245111b40d602211e84d
4
- data.tar.gz: c41b6c9d08350d0e4717242eb663b6e85aa9c9c714f46974e4969e340d13a033
3
+ metadata.gz: 5255fb12e5463a29335c433ca7ccc20cdc6154a686f25c1272d4bf608e71764b
4
+ data.tar.gz: 7865931e5f6ee408a810481f1f809edcdf3d5fabfcd31e76bf3e3cad1dfabe64
5
5
  SHA512:
6
- metadata.gz: f560d47006a324f3468122cc719a89595523cfb639b8ad6dc45cf4ee7bc4d3bb2befb0a5ec5b02f84d8810bd7364322ef78f224611097a407999aaab571fd8c6
7
- data.tar.gz: 1ae71460fae62e5022eeff6f3a91e15dadd23ef382162efb068b74d61cd7ae714b68182165a463b6d035eb6daa0fb5dde5ef0aa1e7c660f91e77a85f42f8f0f7
6
+ metadata.gz: 7c7793a2361ffe29f9e97b1be00500274054f58acea841bb025fac94ebac969117c09f69cc644219825e06883df93a400cebce0db1f72f1b72e75ab3296e37b6
7
+ data.tar.gz: 81ce8ce6fcc5d564ae5b0a51c99080d0f301ecbf9a5fb6da45bd8f633a4d35eea6adb1b77f1f0a2ecebb9bf4c8e11c0185aca35f688f21f2118ff73a0f5ac1ab
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- lighstorm (0.0.1)
4
+ lighstorm (0.0.2)
5
5
  dotenv (~> 2.8, >= 2.8.1)
6
6
  lnd-client (~> 0.0.4)
7
7
  zache (~> 0.12.0)
@@ -16,7 +16,7 @@ GEM
16
16
  google-protobuf (3.21.12)
17
17
  googleapis-common-protos-types (1.5.0)
18
18
  google-protobuf (~> 3.14)
19
- grpc (1.51.0)
19
+ grpc (1.52.0-x86_64-linux)
20
20
  google-protobuf (~> 3.21)
21
21
  googleapis-common-protos-types (~> 1.0)
22
22
  json (2.6.3)
@@ -24,7 +24,7 @@ GEM
24
24
  grpc (~> 1.51)
25
25
  method_source (1.0.0)
26
26
  parallel (1.22.1)
27
- parser (3.2.0.0)
27
+ parser (3.2.1.0)
28
28
  ast (~> 2.4.1)
29
29
  pry (0.14.2)
30
30
  coderay (~> 1.1)
@@ -33,9 +33,9 @@ GEM
33
33
  byebug (~> 11.0)
34
34
  pry (>= 0.13, < 0.15)
35
35
  rainbow (3.1.1)
36
- regexp_parser (2.6.2)
36
+ regexp_parser (2.7.0)
37
37
  rexml (3.2.5)
38
- rubocop (1.44.1)
38
+ rubocop (1.45.1)
39
39
  json (~> 2.3)
40
40
  parallel (~> 1.10)
41
41
  parser (>= 3.2.0.0)
data/README.md CHANGED
@@ -33,7 +33,7 @@ _Lighstorm_ is an opinionated abstraction layer on top of the [lnd-client](https
33
33
 
34
34
  It brings an [object-oriented](https://en.wikipedia.org/wiki/Object-oriented_programming) approach for interacting with a [Lightning Node](https://github.com/lightningnetwork/lnd), influenced by the [Active Record Pattern](https://www.martinfowler.com/eaaCatalog/activeRecord.html) and [Active Record Models](https://guides.rubyonrails.org/active_record_basics.html) conventions.
35
35
 
36
- Although it tries to stay close to [Lightning's terminologies](https://docs.lightning.engineering/lightning-network-tools/lnd), it brings its own vocabulary and data modeling, optimizing for [programmer happiness](https://rubyonrails.org/doctrine).
36
+ Although it tries to stay close to [Lightning's terminologies](https://docs.lightning.engineering/lightning-network-tools/lnd), it brings its own vocabulary and [data modeling](#data-modeling), optimizing for [programmer happiness](https://rubyonrails.org/doctrine).
37
37
 
38
38
  ## Usage
39
39
 
@@ -42,7 +42,7 @@ Although it tries to stay close to [Lightning's terminologies](https://docs.ligh
42
42
  Add to your `Gemfile`:
43
43
 
44
44
  ```ruby
45
- gem 'lighstorm', '~> 0.0.1'
45
+ gem 'lighstorm', '~> 0.0.2'
46
46
  ```
47
47
 
48
48
  Run `bundle install`.
@@ -90,6 +90,7 @@ node.channels
90
90
  node.alias
91
91
  node.public_key
92
92
  node.color
93
+ node.myself?
93
94
 
94
95
  node.platform.blockchain
95
96
  node.platform.network
@@ -129,6 +130,14 @@ channel.myself.node.color
129
130
  channel.myself.policy.fee.base.milisatoshis
130
131
  channel.myself.policy.fee.rate.parts_per_million
131
132
 
133
+ channel.myself.policy.fee.update(
134
+ { rate: { parts_per_million: 25 } }, preview: true
135
+ )
136
+
137
+ channel.myself.policy.fee.update(
138
+ { rate: { parts_per_million: 25 } }
139
+ )
140
+
132
141
  Lighstorm::Forward
133
142
  Lighstorm::Forward.all
134
143
  Lighstorm::Forward.first
@@ -141,7 +150,9 @@ forward.id
141
150
  forward.at
142
151
 
143
152
  forward.fee.milisatoshis
144
- forward.fee.parts_per_million
153
+ forward.fee.parts_per_million(
154
+ forward.in.amount.milisatoshis
155
+ )
145
156
 
146
157
  forward.in.amount.milisatoshis
147
158
 
@@ -155,6 +166,37 @@ forward.out.channel.partner.node.alias
155
166
  forward.out.channel.partner.node.public_key
156
167
  forward.out.channel.partner.node.color
157
168
 
169
+ Lighstorm::Forward.group_by_channel(direction: :in, hours_ago: 24, limit: 5)
170
+
171
+ group.to_h
172
+
173
+ group.last_at
174
+ group.analysis.count
175
+ group.analysis.sums.amount.milisatoshis
176
+ group.analysis.sums.fee.milisatoshis
177
+ group.analysis.averages.amount.milisatoshis
178
+ group.analysis.averages.fee.milisatoshis
179
+ group.analysis.averages.fee.parts_per_million(
180
+ group.analysis.averages.amount.milisatoshis
181
+ )
182
+
183
+ group.in.id
184
+ group.in.partner.node.alias
185
+ group.in.partner.node.public_key
186
+ group.in.partner.node.color
187
+
188
+ Lighstorm::Forward.group_by_channel(direction: :out)
189
+
190
+ group.to_h
191
+
192
+ group.last_at
193
+ group.analysis.count
194
+
195
+ group.out.id
196
+ group.out.partner.node.alias
197
+ group.out.partner.node.public_key
198
+ group.out.partner.node.color
199
+
158
200
  Lighstorm::Payment
159
201
  Lighstorm::Payment.all
160
202
  Lighstorm::Payment.first
@@ -168,12 +210,17 @@ payment.purpose
168
210
  payment.status
169
211
  payment.amount.milisatoshis
170
212
  payment.fee.milisatoshis
171
- payment.fee.parts_per_million
213
+ payment.fee.parts_per_million(
214
+ payment.amount.milisatoshis
215
+ )
172
216
 
173
217
  payment.from.hop
174
218
  payment.from.amount.milisatoshis
175
219
  payment.from.fee.milisatoshis
176
- payment.from.fee.parts_per_million
220
+ payment.from.fee.parts_per_million(
221
+ payment.from.amount.milisatoshis
222
+ )
223
+
177
224
  payment.from.channel.id
178
225
  payment.from.channel.partner.node.alias
179
226
  payment.from.channel.partner.node.public_key
@@ -182,7 +229,10 @@ payment.from.channel.partner.node.color
182
229
  payment.to.hop
183
230
  payment.to.amount.milisatoshis
184
231
  payment.to.fee.milisatoshis
185
- payment.to.fee.parts_per_million
232
+ payment.to.fee.parts_per_million(
233
+ payment.to.amount.milisatoshis
234
+ )
235
+
186
236
  payment.to.channel.id
187
237
  payment.to.channel.partner.node.alias
188
238
  payment.to.channel.partner.node.public_key
@@ -193,7 +243,10 @@ payment.hops.size
193
243
  payment.hops[0].hop
194
244
  payment.hops[0].amount.milisatoshis
195
245
  payment.hops[0].fee.milisatoshis
196
- payment.hops[0].fee.parts_per_million
246
+ payment.hops[0].fee.parts_per_million(
247
+ payment.hops[0].amount.milisatoshis
248
+ )
249
+
197
250
  payment.hops[0].channel.id
198
251
  payment.hops[0].channel.partner.node.alias
199
252
  payment.hops[0].channel.partner.node.public_key
@@ -221,7 +274,7 @@ satoshis.parts_per_million(reference_in_milisatoshis)
221
274
  ```ruby
222
275
  require 'lighstorm'
223
276
 
224
- puts Lighstorm.version # => 0.0.1
277
+ puts Lighstorm.version # => 0.0.2
225
278
 
226
279
  Lighstorm::Satoshis.new(
227
280
  milisatoshis: 75621650
@@ -343,7 +396,7 @@ gem 'lighstorm', path: '/home/user/lighstorm'
343
396
  # demo.rb
344
397
  require 'lighstorm'
345
398
 
346
- puts Lighstorm.version # => 0.0.1
399
+ puts Lighstorm.version # => 0.0.2
347
400
  ```
348
401
 
349
402
  ```sh
@@ -358,5 +411,5 @@ gem build lighstorm.gemspec
358
411
 
359
412
  gem signin
360
413
 
361
- gem push lighstorm-0.0.1.gem
414
+ gem push lighstorm-0.0.2.gem
362
415
  ```
data/components/cache.rb CHANGED
@@ -4,6 +4,8 @@ require 'singleton'
4
4
 
5
5
  require 'zache'
6
6
 
7
+ require_relative '../static/cache'
8
+
7
9
  module Lighstorm
8
10
  class Cache
9
11
  include Singleton
@@ -18,7 +20,14 @@ module Lighstorm
18
20
  @client = Zache.new
19
21
  end
20
22
 
21
- def for(key, ttl: 1, params: {}, &block)
23
+ def for(key, ttl: nil, params: {}, &block)
24
+ if ttl.nil?
25
+ ttl = Lighstorm::Static::CACHE[key.sub('lightning.', '').to_sym]
26
+ raise "missing ttl for #{key}" if ttl.nil?
27
+
28
+ ttl = ttl[:ttl]
29
+ end
30
+
22
31
  key = build_key_for(key, params)
23
32
 
24
33
  @client.get(key, lifetime: ttl) do
@@ -3,10 +3,16 @@
3
3
  require_relative '../../satoshis'
4
4
  require_relative '../../rate'
5
5
 
6
+ require_relative '../../../components/lnd'
7
+
6
8
  module Lighstorm
7
9
  module Models
8
10
  class Fee
11
+ attr_reader :rate, :base
12
+
9
13
  def initialize(policy, channel, node)
14
+ @channel = channel
15
+ @policy = policy
10
16
  if node.myself?
11
17
  @base = Satoshis.new(
12
18
  milisatoshis: channel.data[:fee_report][:channel_fees].first.base_fee_msat
@@ -22,6 +28,46 @@ module Lighstorm
22
28
  end
23
29
  end
24
30
 
31
+ def update(params, preview: false)
32
+ chan_point = @channel.data[:get_chan_info].chan_point.split(':')
33
+
34
+ # add_message "lnrpc.PolicyUpdateRequest" do
35
+ # optional :base_fee_msat, :int64, 3
36
+ # optional :fee_rate, :double, 4
37
+ # optional :fee_rate_ppm, :uint32, 9
38
+ # optional :time_lock_delta, :uint32, 5
39
+ # optional :max_htlc_msat, :uint64, 6
40
+ # optional :min_htlc_msat, :uint64, 7
41
+ # optional :min_htlc_msat_specified, :bool, 8
42
+ # oneof :scope do
43
+ # optional :global, :bool, 1
44
+ # optional :chan_point, :message, 2, "lnrpc.ChannelPoint"
45
+ # end
46
+ # end
47
+
48
+ grpc_request = {
49
+ method: :update_channel_policy,
50
+ params: {
51
+ chan_point: {
52
+ funding_txid_str: chan_point[0],
53
+ output_index: chan_point[1].to_i
54
+ },
55
+ fee_rate_ppm: @policy.data.fee_rate_milli_msat,
56
+ base_fee_msat: @policy.data.fee_base_msat,
57
+ time_lock_delta: @policy.data.time_lock_delta,
58
+ max_htlc_msat: @policy.data.max_htlc_msat
59
+ }
60
+ }
61
+
62
+ grpc_request[:params][:fee_rate_ppm] = params[:rate][:parts_per_million]
63
+
64
+ return grpc_request if preview
65
+
66
+ LND.instance.middleware("lightning.#{grpc_request[:method]}") do
67
+ LND.instance.client.lightning.send(grpc_request[:method], grpc_request[:params])
68
+ end
69
+ end
70
+
25
71
  def to_h
26
72
  {
27
73
  base: @base.to_h,
@@ -18,12 +18,12 @@ module Lighstorm
18
18
  end
19
19
  end
20
20
 
21
- def fees
22
- @fees ||= Fee.new(self, @channel, @node)
21
+ def fee
22
+ @fee ||= Fee.new(self, @channel, @node)
23
23
  end
24
24
 
25
25
  def to_h
26
- { fee: fees.to_h }
26
+ { fee: fee.to_h }
27
27
  end
28
28
  end
29
29
  end
@@ -7,21 +7,14 @@ module Lighstorm
7
7
  class ForwardChannel
8
8
  KIND = :connection
9
9
 
10
- def initialize(direction, forward)
10
+ def initialize(direction, forward, respond_info: true)
11
+ @respond_info = respond_info
11
12
  @direction = direction
12
13
  @forward = forward
13
14
  end
14
15
 
15
16
  def channel
16
- @channel ||= if @direction == :in
17
- Channel.find_by_id(
18
- @forward.data[:forwarding_history][:forwarding_events].first.chan_id_in
19
- )
20
- else
21
- Channel.find_by_id(
22
- @forward.data[:forwarding_history][:forwarding_events].first.chan_id_out
23
- )
24
- end
17
+ @channel ||= Channel.find_by_id(channel_id)
25
18
  end
26
19
 
27
20
  def amount
@@ -35,19 +28,35 @@ module Lighstorm
35
28
  end
36
29
 
37
30
  def to_h
38
- {
31
+ response = {
39
32
  amount: amount.to_h,
40
- channel: {
41
- id: channel.id,
42
- partner: {
43
- node: {
44
- alias: channel.partner&.node&.alias,
45
- public_key: channel.partner&.node&.public_key,
46
- color: channel.partner&.node&.color
47
- }
33
+ channel: { id: channel_id }
34
+ }
35
+
36
+ return response unless @respond_info
37
+
38
+ response[:channel] = {
39
+ id: channel.id,
40
+ partner: {
41
+ node: {
42
+ alias: channel.partner&.node&.alias,
43
+ public_key: channel.partner&.node&.public_key,
44
+ color: channel.partner&.node&.color
48
45
  }
49
46
  }
50
47
  }
48
+
49
+ response
50
+ end
51
+
52
+ private
53
+
54
+ def channel_id
55
+ if @direction == :in
56
+ @forward.data[:forwarding_history][:forwarding_events].first.chan_id_in.to_s
57
+ else
58
+ @forward.data[:forwarding_history][:forwarding_events].first.chan_id_out.to_s
59
+ end
51
60
  end
52
61
  end
53
62
  end
@@ -8,13 +8,16 @@ module Lighstorm
8
8
  class PaymentChannel
9
9
  KIND = :connection
10
10
 
11
- def initialize(raw_hop, hop_index)
11
+ def initialize(raw_hop, hop_index, respond_info: true)
12
+ @respond_info = respond_info
12
13
  @raw_hop = raw_hop
13
- @index = hop_index
14
+ @hop = hop_index
14
15
  end
15
16
 
17
+ attr_reader :hop
18
+
16
19
  def channel
17
- @channel ||= Channel.find_by_id(@raw_hop.chan_id)
20
+ Channel.find_by_id(@raw_hop.chan_id)
18
21
  end
19
22
 
20
23
  def amount
@@ -34,24 +37,35 @@ module Lighstorm
34
37
  end
35
38
 
36
39
  def to_h
37
- {
38
- hop: @index,
40
+ response = {
41
+ hop: hop,
39
42
  amount: amount.to_h,
40
43
  fee: {
41
44
  milisatoshis: fee.milisatoshis,
42
45
  parts_per_million: fee.parts_per_million(amount.milisatoshis)
43
46
  },
44
47
  channel: {
45
- id: channel.id,
46
- partner: {
47
- node: {
48
- alias: partner_node&.alias,
49
- public_key: partner_node&.public_key,
50
- color: partner_node&.color
51
- }
48
+ id: @raw_hop.chan_id.to_s,
49
+ node: {
50
+ public_key: @raw_hop.pub_key
52
51
  }
53
52
  }
54
53
  }
54
+
55
+ return response unless @respond_info
56
+
57
+ response[:channel] = {
58
+ id: channel.id,
59
+ partner: {
60
+ node: {
61
+ alias: partner_node&.alias,
62
+ public_key: partner_node&.public_key,
63
+ color: partner_node&.color
64
+ }
65
+ }
66
+ }
67
+
68
+ response
55
69
  end
56
70
  end
57
71
  end
@@ -17,17 +17,17 @@ module Lighstorm
17
17
  class Channel
18
18
  KIND = :edge
19
19
 
20
- attr_reader :id, :data
20
+ attr_reader :data
21
21
 
22
22
  def self.all
23
- response = Cache.for('lightning.list_channels', ttl: 1) do
23
+ response = Cache.for('lightning.list_channels') do
24
24
  LND.instance.middleware('lightning.list_channels') do
25
25
  LND.instance.client.lightning.list_channels
26
26
  end
27
27
  end
28
28
 
29
29
  response.channels.map do |channel|
30
- Channel.find_by_id(channel.chan_id)
30
+ Channel.find_by_id(channel.chan_id.to_s)
31
31
  end
32
32
  end
33
33
 
@@ -43,21 +43,23 @@ module Lighstorm
43
43
  Channel.new({ id: id })
44
44
  end
45
45
 
46
+ def id
47
+ # Standard JSON don't support BigInt, so, a String is safer.
48
+ @id.to_s
49
+ end
50
+
46
51
  def initialize(params)
47
52
  begin
48
- response = Cache.for(
49
- 'lightning.get_chan_info',
50
- ttl: 1, params: { chan_id: params[:id] }
51
- ) do
53
+ response = Cache.for('lightning.get_chan_info', params: { chan_id: params[:id].to_i }) do
52
54
  LND.instance.middleware('lightning.get_chan_info') do
53
- LND.instance.client.lightning.get_chan_info(chan_id: params[:id])
55
+ LND.instance.client.lightning.get_chan_info(chan_id: params[:id].to_i)
54
56
  end
55
57
  end
56
58
 
57
59
  @data = { get_chan_info: response }
58
60
  @id = @data[:get_chan_info].channel_id
59
- rescue StandardError => _e
60
- @data = { get_chan_info: nil }
61
+ rescue StandardError => e
62
+ @data = { get_chan_info: nil, error: e }
61
63
  @id = params[:id]
62
64
  end
63
65
 
@@ -67,6 +69,10 @@ module Lighstorm
67
69
  calculate_times_after_list_channels!
68
70
  end
69
71
 
72
+ def error
73
+ @data[:error]
74
+ end
75
+
70
76
  def active
71
77
  @data[:list_channels] ? @data[:list_channels][:channels].first.active : nil
72
78
  end
@@ -141,7 +147,7 @@ module Lighstorm
141
147
 
142
148
  # Ensure that we are getting fresh up-date data about our own fees.
143
149
  def fetch_from_fee_report!
144
- response = Cache.for('lightning.fee_report', ttl: 1) do
150
+ response = Cache.for('lightning.fee_report') do
145
151
  LND.instance.middleware('lightning.fee_report') do
146
152
  LND.instance.client.lightning.fee_report
147
153
  end
@@ -156,7 +162,7 @@ module Lighstorm
156
162
  end
157
163
 
158
164
  def fetch_from_list_channels!
159
- response = Cache.for('lightning.list_channels', ttl: 1) do
165
+ response = Cache.for('lightning.list_channels') do
160
166
  LND.instance.middleware('lightning.list_channels') do
161
167
  LND.instance.client.lightning.list_channels
162
168
  end
@@ -7,6 +7,7 @@ require 'date'
7
7
  require_relative '../satoshis'
8
8
 
9
9
  require_relative '../connections/forward_channel'
10
+ require_relative 'groups/channel_forwards'
10
11
 
11
12
  module Lighstorm
12
13
  module Models
@@ -15,16 +16,21 @@ module Lighstorm
15
16
 
16
17
  attr_reader :data
17
18
 
18
- def self.all(limit: nil)
19
+ def self.all(limit: nil, raw: false, info: true)
19
20
  last_offset = 0
20
21
 
21
22
  forwards = []
22
23
 
23
24
  loop do
24
- response = LND.instance.middleware('lightning.forwarding_history') do
25
- LND.instance.client.lightning.forwarding_history(
26
- peer_alias_lookup: true, index_offset: last_offset
27
- )
25
+ response = Cache.for(
26
+ 'lightning.forwarding_history',
27
+ params: { peer_alias_lookup: true, index_offset: last_offset }
28
+ ) do
29
+ LND.instance.middleware('lightning.forwarding_history') do
30
+ LND.instance.client.lightning.forwarding_history(
31
+ peer_alias_lookup: true, index_offset: last_offset
32
+ )
33
+ end
28
34
  end
29
35
 
30
36
  response.forwarding_events.each { |raw_forward| forwards << raw_forward }
@@ -41,7 +47,9 @@ module Lighstorm
41
47
 
42
48
  forwards = forwards[0..limit - 1] unless limit.nil?
43
49
 
44
- forwards.map { |raw_forward| Forward.new(raw_forward) }
50
+ return forwards if raw
51
+
52
+ forwards.map { |raw_forward| Forward.new(raw_forward, respond_info: info) }
45
53
  end
46
54
 
47
55
  def self.first
@@ -52,7 +60,52 @@ module Lighstorm
52
60
  all.last
53
61
  end
54
62
 
55
- def initialize(raw)
63
+ def self.group_by_channel(direction: :out, hours_ago: nil, limit: nil, info: true)
64
+ raw_forwards = all(raw: true)
65
+
66
+ direction = direction.to_sym
67
+
68
+ groups = {}
69
+
70
+ raw_forwards.each do |raw_forward|
71
+ channel_id = direction == :in ? raw_forward.chan_id_in : raw_forward.chan_id_out
72
+
73
+ if hours_ago
74
+ forward_hours_ago = (
75
+ Time.now - Time.at(raw_forward.timestamp_ns / 1e+9)
76
+ ).to_f / 3600
77
+
78
+ next if forward_hours_ago > hours_ago
79
+ end
80
+
81
+ unless groups[channel_id]
82
+ groups[channel_id] = {
83
+ last_at: nil,
84
+ analysis: { count: 0, sums: { amount: 0, fee: 0 } },
85
+ direction => { id: channel_id }
86
+ }
87
+ end
88
+
89
+ groups[channel_id][:analysis][:count] += 1
90
+ groups[channel_id][:analysis][:sums][:amount] += raw_forward.amt_in_msat
91
+ groups[channel_id][:analysis][:sums][:fee] += raw_forward.fee_msat
92
+
93
+ if groups[channel_id][:last_at].nil? || raw_forward.timestamp_ns > groups[channel_id][:last_at]
94
+ groups[channel_id][:last_at] = raw_forward.timestamp_ns
95
+ groups[channel_id][:sample] = raw_forward
96
+ end
97
+ end
98
+
99
+ groups = groups.values.sort_by { |group| - group[:last_at] }
100
+ .sort_by { |group| - group[:analysis][:count] }
101
+
102
+ groups = groups[0..limit - 1] unless limit.nil?
103
+
104
+ groups.map { |raw_group| ChannelForwardsGroup.new(direction, raw_group) }
105
+ end
106
+
107
+ def initialize(raw, respond_info: true)
108
+ @respond_info = respond_info
56
109
  @data = { forwarding_history: { forwarding_events: [raw] } }
57
110
  end
58
111
 
@@ -63,7 +116,9 @@ module Lighstorm
63
116
  end
64
117
 
65
118
  def at
66
- DateTime.parse(Time.at(@data[:forwarding_history][:forwarding_events].first.timestamp).to_s)
119
+ DateTime.parse(Time.at(
120
+ @data[:forwarding_history][:forwarding_events].first.timestamp_ns / 1e+9
121
+ ).to_s)
67
122
  end
68
123
 
69
124
  def fee
@@ -71,11 +126,11 @@ module Lighstorm
71
126
  end
72
127
 
73
128
  def in
74
- @in ||= ForwardChannel.new(:in, self)
129
+ @in ||= ForwardChannel.new(:in, self, respond_info: @respond_info)
75
130
  end
76
131
 
77
132
  def out
78
- @out ||= ForwardChannel.new(:out, self)
133
+ @out ||= ForwardChannel.new(:out, self, respond_info: @respond_info)
79
134
  end
80
135
 
81
136
  def to_h
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lighstorm
4
+ module Models
5
+ class ChannelForwardsGroup
6
+ Analysis = Struct.new(:analysis) do
7
+ def count
8
+ analysis[:count]
9
+ end
10
+
11
+ def sums
12
+ Struct.new(:sums) do
13
+ def amount
14
+ Satoshis.new(milisatoshis: sums[:amount])
15
+ end
16
+
17
+ def fee
18
+ Satoshis.new(milisatoshis: sums[:fee])
19
+ end
20
+
21
+ def to_h
22
+ {
23
+ amount: amount.to_h,
24
+ fee: {
25
+ milisatoshis: fee.milisatoshis,
26
+ parts_per_million: fee.parts_per_million(amount.milisatoshis)
27
+ }
28
+ }
29
+ end
30
+ end.new(analysis[:sums])
31
+ end
32
+
33
+ def averages
34
+ Struct.new(:analysis) do
35
+ def amount
36
+ Satoshis.new(
37
+ milisatoshis: analysis[:sums][:amount].to_f / analysis[:count]
38
+ )
39
+ end
40
+
41
+ def fee
42
+ Satoshis.new(
43
+ milisatoshis: analysis[:sums][:fee].to_f / analysis[:count]
44
+ )
45
+ end
46
+
47
+ def to_h
48
+ {
49
+ amount: amount.to_h,
50
+ fee: {
51
+ milisatoshis: fee.milisatoshis,
52
+ parts_per_million: fee.parts_per_million(amount.milisatoshis)
53
+ }
54
+ }
55
+ end
56
+ end.new(analysis)
57
+ end
58
+
59
+ def to_h
60
+ {
61
+ count: count,
62
+ sums: sums.to_h,
63
+ averages: averages.to_h
64
+ }
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../satoshis'
4
+ require_relative 'analysis'
5
+
6
+ module Lighstorm
7
+ module Models
8
+ class ChannelForwardsGroup
9
+ def initialize(direction, data)
10
+ @direction = direction
11
+ @data = data
12
+ end
13
+
14
+ def last_at
15
+ @last_at ||= DateTime.parse(Time.at(@data[:last_at].to_f / 1e+9).to_s)
16
+ end
17
+
18
+ def analysis
19
+ Analysis.new(@data[:analysis])
20
+ end
21
+
22
+ def in
23
+ return @in if @in
24
+
25
+ raise raise ArgumentError, "Method `in` doesn't exist." unless @direction == :in
26
+
27
+ @in = Channel.new({ id: @data[:in][:id] })
28
+ end
29
+
30
+ def out
31
+ return @out if @out
32
+
33
+ raise raise ArgumentError, "Method `out` doesn't exist." unless @direction == :out
34
+
35
+ @out = Channel.new({ id: @data[:out][:id] })
36
+ end
37
+
38
+ # def capacity
39
+ # @capacity ||= Satoshis.new(milisatoshis: @channel.data[:get_chan_info].capacity * 1000)
40
+ # end
41
+
42
+ def to_h
43
+ {
44
+ last_at: last_at,
45
+ analysis: analysis.to_h,
46
+ @direction => {
47
+ id: channel.id,
48
+ partner: {
49
+ node: {
50
+ alias: channel&.partner&.node&.alias,
51
+ public_key: channel&.partner&.node&.public_key,
52
+ color: channel&.partner&.node&.color
53
+ }
54
+ }
55
+ }
56
+ }
57
+ end
58
+
59
+ private
60
+
61
+ def channel
62
+ @channel ||= @direction == :in ? self.in : out
63
+ end
64
+ end
65
+ end
66
+ end
@@ -15,14 +15,16 @@ module Lighstorm
15
15
 
16
16
  attr_reader :data
17
17
 
18
- def self.all(limit: nil, purpose: nil, hops: true)
18
+ def self.all(limit: nil, purpose: nil, hops: true, info: true)
19
19
  last_offset = 0
20
20
 
21
21
  payments = []
22
22
 
23
23
  loop do
24
- response = LND.instance.middleware('lightning.list_payments') do
25
- LND.instance.client.lightning.list_payments(index_offset: last_offset)
24
+ response = Cache.for('lightning.list_payments', params: { index_offset: last_offset }) do
25
+ LND.instance.middleware('lightning.list_payments') do
26
+ LND.instance.client.lightning.list_payments(index_offset: last_offset)
27
+ end
26
28
  end
27
29
 
28
30
  response.payments.each do |raw_payment|
@@ -57,7 +59,7 @@ module Lighstorm
57
59
  payments = payments[0..limit - 1] unless limit.nil?
58
60
 
59
61
  payments.map do |raw_payment|
60
- Payment.new(raw_payment, respond_hops: hops)
62
+ Payment.new(raw_payment, respond_hops: hops, respond_info: info)
61
63
  end
62
64
  end
63
65
 
@@ -69,8 +71,9 @@ module Lighstorm
69
71
  all.last
70
72
  end
71
73
 
72
- def initialize(raw, respond_hops: true)
74
+ def initialize(raw, respond_hops: true, respond_info: true)
73
75
  @respond_hops = respond_hops
76
+ @respond_info = respond_info
74
77
  @data = { list_payments: { payments: [raw] } }
75
78
  end
76
79
 
@@ -163,7 +166,7 @@ module Lighstorm
163
166
 
164
167
  @from = PaymentChannel.new(
165
168
  @data[:list_payments][:payments].first.htlcs.first.route.hops.first,
166
- 1
169
+ 1, respond_info: @respond_info
167
170
  )
168
171
 
169
172
  @from
@@ -185,12 +188,14 @@ module Lighstorm
185
188
  @data[:list_payments][:payments].first.htlcs.first.route.hops[
186
189
  @data[:list_payments][:payments].first.htlcs.first.route.hops.size - 2
187
190
  ],
188
- @data[:list_payments][:payments].first.htlcs.first.route.hops.size - 1
191
+ @data[:list_payments][:payments].first.htlcs.first.route.hops.size - 1,
192
+ respond_info: @respond_info
189
193
  )
190
194
  else
191
195
  PaymentChannel.new(
192
196
  @data[:list_payments][:payments].first.htlcs.first.route.hops.last,
193
- @data[:list_payments][:payments].first.htlcs.first.route.hops.size
197
+ @data[:list_payments][:payments].first.htlcs.first.route.hops.size,
198
+ respond_info: @respond_info
194
199
  )
195
200
  end
196
201
 
@@ -203,7 +208,7 @@ module Lighstorm
203
208
  validated_htlcs_number!
204
209
 
205
210
  @hops = @data[:list_payments][:payments].first.htlcs.first.route.hops.map.with_index do |raw_hop, i|
206
- PaymentChannel.new(raw_hop, i + 1)
211
+ PaymentChannel.new(raw_hop, i + 1, respond_info: @respond_info)
207
212
  end
208
213
  end
209
214
 
@@ -5,6 +5,8 @@ require_relative '../../../components/lnd'
5
5
  module Lighstorm
6
6
  module Models
7
7
  class Lightning
8
+ IMPLEMENTATION = 'lnd'
9
+
8
10
  def initialize(platform, node)
9
11
  raise 'cannot provide platform details for a node that is not yours' unless node.myself?
10
12
 
@@ -19,9 +21,13 @@ module Lighstorm
19
21
  { get_info: @data[:get_info].to_h }
20
22
  end
21
23
 
24
+ def implementation
25
+ @implementation ||= IMPLEMENTATION
26
+ end
27
+
22
28
  def to_h
23
29
  {
24
- implementation: 'lnd',
30
+ implementation: implementation,
25
31
  version: version
26
32
  }
27
33
  end
@@ -12,7 +12,7 @@ module Lighstorm
12
12
  def initialize(node)
13
13
  @node = node
14
14
 
15
- response = Cache.for('lightning.get_info', ttl: 1) do
15
+ response = Cache.for('lightning.get_info') do
16
16
  LND.instance.middleware('lightning.get_info') do
17
17
  LND.instance.client.lightning.get_info
18
18
  end
data/models/nodes/node.rb CHANGED
@@ -14,7 +14,7 @@ module Lighstorm
14
14
  attr_reader :alias, :public_key, :color
15
15
 
16
16
  def self.myself
17
- response = Cache.for('lightning.get_info', ttl: 1) do
17
+ response = Cache.for('lightning.get_info') do
18
18
  LND.instance.middleware('lightning.get_info') do
19
19
  LND.instance.client.lightning.get_info
20
20
  end
@@ -59,15 +59,22 @@ module Lighstorm
59
59
  private
60
60
 
61
61
  def initialize(params, myself: false)
62
- response = Cache.for(
63
- 'lightning.get_node_info',
64
- ttl: 5 * 60, params: { pub_key: params[:public_key] }
65
- ) do
62
+ response = Cache.for('lightning.get_node_info', params: { pub_key: params[:public_key] }) do
66
63
  LND.instance.middleware('lightning.get_node_info') do
67
64
  LND.instance.client.lightning.get_node_info(pub_key: params[:public_key])
68
65
  end
69
66
  end
70
67
 
68
+ unless myself
69
+ response_get_info = Cache.for('lightning.get_info') do
70
+ LND.instance.middleware('lightning.get_info') do
71
+ LND.instance.client.lightning.get_info
72
+ end
73
+ end
74
+
75
+ myself = true if params[:public_key] == response_get_info.identity_pubkey
76
+ end
77
+
71
78
  @data = { get_node_info: response }
72
79
 
73
80
  @myself = myself
data/models/rate.rb CHANGED
@@ -3,6 +3,8 @@
3
3
  module Lighstorm
4
4
  module Models
5
5
  class Rate
6
+ attr_reader :parts_per_million
7
+
6
8
  # https://en.wikipedia.org/wiki/Parts-per_notation
7
9
  def initialize(parts_per_million: nil)
8
10
  raise 'missing parts_per_million' if parts_per_million.nil?
data/static/cache.rb ADDED
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lighstorm
4
+ module Static
5
+ CACHE = {
6
+ fee_report: {
7
+ ttl: 0.01,
8
+ properties: %i[
9
+ base_fee_msat fee_per_mil
10
+ ]
11
+ },
12
+ forwarding_history: {
13
+ ttl: 1,
14
+ properties: %i[
15
+ amt_in_msat
16
+ amt_out_msat
17
+ chan_id_in
18
+ chan_id_out
19
+ fee_msat
20
+ timestamp_ns
21
+ ]
22
+ },
23
+ get_chan_info: {
24
+ ttl: 5 * 60,
25
+ properties: %i[
26
+ channel_id
27
+ chan_point
28
+ node1_pub
29
+ node1_policy
30
+ node2_pub
31
+ node2_policy
32
+ capacity
33
+ ]
34
+ },
35
+ get_info: {
36
+ ttl: 5 * 60,
37
+ properties: %i[identity_pubkey version chains chain network]
38
+ },
39
+ get_node_info: {
40
+ ttl: 5 * 60,
41
+ properties: %i[alias pub_key color]
42
+ },
43
+ list_channels: {
44
+ ttl: 1,
45
+ properties: %i[
46
+ local_balance remote_balance unsettled_balance
47
+ local_constraints remote_constraints
48
+ active private
49
+ lifetime uptime
50
+ total_satoshis_sent total_satoshis_received
51
+ ]
52
+ },
53
+ list_payments: {
54
+ ttl: 1,
55
+ properties: %i[
56
+ creation_date payment_hash status
57
+ value_msat fee_msat
58
+ htlcs route hops
59
+ chan_id pub_key
60
+ amt_to_forward_msat fee_msat
61
+ ]
62
+ }
63
+ }.freeze
64
+ end
65
+ end
data/static/spec.rb CHANGED
@@ -4,7 +4,7 @@ module Lighstorm
4
4
  module Static
5
5
  SPEC = {
6
6
  name: 'lighstorm',
7
- version: '0.0.1',
7
+ version: '0.0.2',
8
8
  author: 'icebaker',
9
9
  summary: 'API for interacting with a Lightning Node.',
10
10
  description: 'Lighstorm is an opinionated abstraction layer on top of the lnd-client for interacting with a Lightning Node.',
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lighstorm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - icebaker
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-02-05 00:00:00.000000000 Z
11
+ date: 2023-02-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dotenv
@@ -84,6 +84,8 @@ files:
84
84
  - models/edges/channel.rb
85
85
  - models/edges/channel/accounting.rb
86
86
  - models/edges/forward.rb
87
+ - models/edges/groups/analysis.rb
88
+ - models/edges/groups/channel_forwards.rb
87
89
  - models/edges/payment.rb
88
90
  - models/nodes/node.rb
89
91
  - models/nodes/node/lightning.rb
@@ -91,6 +93,7 @@ files:
91
93
  - models/rate.rb
92
94
  - models/satoshis.rb
93
95
  - ports/dsl/lighstorm.rb
96
+ - static/cache.rb
94
97
  - static/spec.rb
95
98
  homepage: https://github.com/icebaker/lighstorm
96
99
  licenses: