lighstorm 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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: