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 +4 -4
- data/Gemfile.lock +5 -5
- data/README.md +63 -10
- data/components/cache.rb +10 -1
- data/models/connections/channel_node/fee.rb +46 -0
- data/models/connections/channel_node/policy.rb +3 -3
- data/models/connections/forward_channel.rb +28 -19
- data/models/connections/payment_channel.rb +26 -12
- data/models/edges/channel.rb +18 -12
- data/models/edges/forward.rb +65 -10
- data/models/edges/groups/analysis.rb +69 -0
- data/models/edges/groups/channel_forwards.rb +66 -0
- data/models/edges/payment.rb +14 -9
- data/models/nodes/node/lightning.rb +7 -1
- data/models/nodes/node/platform.rb +1 -1
- data/models/nodes/node.rb +12 -5
- data/models/rate.rb +2 -0
- data/static/cache.rb +65 -0
- data/static/spec.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5255fb12e5463a29335c433ca7ccc20cdc6154a686f25c1272d4bf608e71764b
|
4
|
+
data.tar.gz: 7865931e5f6ee408a810481f1f809edcdf3d5fabfcd31e76bf3e3cad1dfabe64
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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.
|
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.
|
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.
|
36
|
+
regexp_parser (2.7.0)
|
37
37
|
rexml (3.2.5)
|
38
|
-
rubocop (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.
|
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.
|
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.
|
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.
|
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:
|
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,
|
@@ -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 ||=
|
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
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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
|
-
@
|
14
|
+
@hop = hop_index
|
14
15
|
end
|
15
16
|
|
17
|
+
attr_reader :hop
|
18
|
+
|
16
19
|
def channel
|
17
|
-
|
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:
|
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:
|
46
|
-
|
47
|
-
|
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
|
data/models/edges/channel.rb
CHANGED
@@ -17,17 +17,17 @@ module Lighstorm
|
|
17
17
|
class Channel
|
18
18
|
KIND = :edge
|
19
19
|
|
20
|
-
attr_reader :
|
20
|
+
attr_reader :data
|
21
21
|
|
22
22
|
def self.all
|
23
|
-
response = Cache.for('lightning.list_channels'
|
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 =>
|
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'
|
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'
|
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
|
data/models/edges/forward.rb
CHANGED
@@ -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 =
|
25
|
-
|
26
|
-
|
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
|
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
|
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(
|
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
|
data/models/edges/payment.rb
CHANGED
@@ -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 =
|
25
|
-
LND.instance.
|
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:
|
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'
|
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'
|
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
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.
|
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.
|
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-
|
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:
|