lighstorm 0.0.4 → 0.0.6

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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby-rspec-tests.yml +39 -0
  3. data/Gemfile.lock +3 -3
  4. data/README.md +5 -5
  5. data/adapters/connections/channel_node/policy.rb +61 -0
  6. data/adapters/connections/channel_node.rb +40 -18
  7. data/adapters/edges/channel.rb +23 -7
  8. data/adapters/edges/forward.rb +1 -2
  9. data/adapters/edges/payment/purpose.rb +0 -2
  10. data/adapters/edges/payment.rb +2 -5
  11. data/adapters/invoice.rb +2 -3
  12. data/adapters/nodes/node.rb +39 -5
  13. data/adapters/payment_request.rb +0 -1
  14. data/controllers/channel/actions/apply_gossip.rb +194 -0
  15. data/controllers/channel/find_by_id.rb +1 -1
  16. data/controllers/channel/mine.rb +1 -1
  17. data/controllers/channel.rb +4 -0
  18. data/controllers/invoice/actions/create.rb +10 -9
  19. data/controllers/invoice.rb +1 -1
  20. data/controllers/node/actions/apply_gossip.rb +112 -0
  21. data/controllers/node.rb +4 -0
  22. data/controllers/payment/all.rb +52 -36
  23. data/controllers/payment.rb +6 -6
  24. data/docs/README.md +109 -13
  25. data/docs/_coverpage.md +1 -1
  26. data/docs/index.html +1 -1
  27. data/models/concerns/protectable.rb +23 -0
  28. data/models/connections/channel_node/accounting.rb +4 -0
  29. data/models/connections/channel_node/fee.rb +27 -18
  30. data/models/connections/channel_node/htlc/blocks/delta.rb +39 -0
  31. data/models/connections/channel_node/htlc.rb +48 -14
  32. data/models/connections/channel_node/policy.rb +11 -2
  33. data/models/connections/channel_node.rb +33 -1
  34. data/models/edges/channel/accounting.rb +17 -0
  35. data/models/edges/channel.rb +39 -5
  36. data/models/edges/forward.rb +0 -1
  37. data/models/edges/payment.rb +0 -1
  38. data/models/errors.rb +3 -0
  39. data/models/nodes/node/lightning.rb +6 -0
  40. data/models/nodes/node/platform.rb +6 -0
  41. data/models/nodes/node.rb +51 -0
  42. data/models/payment_request.rb +6 -3
  43. data/ports/grpc/session.rb +26 -0
  44. data/ports/grpc.rb +23 -5
  45. data/static/spec.rb +1 -1
  46. metadata +9 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 79b37e0a32c6d34ed5e4b05fc32eb894a808a08b9d2b6efea608be59644a94c9
4
- data.tar.gz: ba7f317ff7699cdae5f9645f533e1779ca5de988aec3d8320172f2ec6807d0d1
3
+ metadata.gz: bea078c4355c1f245ed7800a6d235763e325d3c4cf71e42f14589513eb69a429
4
+ data.tar.gz: 026c1c57b2dc8a6d06cebc2155f32189b7d998574d1e802b8e64f90ae63938ae
5
5
  SHA512:
6
- metadata.gz: b633147091290d526f184ca324964d9bc2fd13cb769a2009e7b732280ddd305fac82d63d2c9c735277ecc14f2fbdf4310d7b4d95611398753879746d170ed33b
7
- data.tar.gz: e979df8953dd05ab5efceb268cde4f305190bc07d3304e304155ddef3ebf876f0444612b59420e2c7f24572d20b758e6521a7a569e3788eaf5b76037ee8ded7e
6
+ metadata.gz: d69e4361ef42951fa5d316a476ce7f2ac63b9ee307198646a2b263601ec0e79d75792b99985767f61a38942401a4e3d24d8ff5b938b7e4fddcb8d427b566d2b4
7
+ data.tar.gz: 85de98976d458ba9279291f37ae4cda03c8aa3965a53751932360a546b406004915d5035666bf6b45f624e1eaa8c0e2d2b8b135632cdee3b8c622b83e94b6631
@@ -0,0 +1,39 @@
1
+ # This workflow uses actions that are not certified by GitHub.
2
+ # They are provided by a third-party and are governed by
3
+ # separate terms of service, privacy policy, and support
4
+ # documentation.
5
+
6
+ # GitHub recommends pinning actions to a commit SHA.
7
+ # To get a newer version, you will need to update the SHA.
8
+ # You can also reference a tag or branch, but the action may change without warning.
9
+
10
+ name: RSpec Tests
11
+
12
+ on:
13
+ push:
14
+ branches: [ main ]
15
+ pull_request:
16
+ branches: [ main ]
17
+
18
+ jobs:
19
+ rspec:
20
+
21
+ runs-on: ubuntu-latest
22
+
23
+ steps:
24
+ - uses: actions/checkout@v3
25
+ - uses: actions/cache@v3
26
+ with:
27
+ path: vendor/bundle
28
+ key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
29
+ restore-keys: |
30
+ ${{ runner.os }}-gems-
31
+ - name: Set up Ruby
32
+ uses: ruby/setup-ruby@359bebbc29cbe6c87da6bc9ea3bc930432750108
33
+ with:
34
+ ruby-version: '3.0.0'
35
+ bundler-cache: true
36
+ - name: Install Dependencies
37
+ run: bundle install
38
+ - name: Run RSpec Tests
39
+ run: bundle exec rspec
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- lighstorm (0.0.4)
4
+ lighstorm (0.0.6)
5
5
  dotenv (~> 2.8, >= 2.8.1)
6
6
  lnd-client (~> 0.0.5)
7
7
  zache (~> 0.12.0)
@@ -50,14 +50,14 @@ GEM
50
50
  diff-lcs (>= 1.2.0, < 2.0)
51
51
  rspec-support (~> 3.12.0)
52
52
  rspec-support (3.12.0)
53
- rubocop (1.45.1)
53
+ rubocop (1.46.0)
54
54
  json (~> 2.3)
55
55
  parallel (~> 1.10)
56
56
  parser (>= 3.2.0.0)
57
57
  rainbow (>= 2.2.2, < 4.0)
58
58
  regexp_parser (>= 1.8, < 3.0)
59
59
  rexml (>= 3.2.5, < 4.0)
60
- rubocop-ast (>= 1.24.1, < 2.0)
60
+ rubocop-ast (>= 1.26.0, < 2.0)
61
61
  ruby-progressbar (~> 1.7)
62
62
  unicode-display_width (>= 2.4.0, < 3.0)
63
63
  rubocop-ast (1.26.0)
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Lighstorm
1
+ # Lighstorm [![Gem Version](https://badge.fury.io/rb/lighstorm.svg)](https://badge.fury.io/rb/lighstorm) ![RSpec Tests Status](https://github.com/icebaker/lighstorm/actions/workflows/ruby-rspec-tests.yml/badge.svg)
2
2
 
3
3
  > ⚠️ Warning: Early-stage, breaking changes are expected.
4
4
 
@@ -33,7 +33,7 @@ Although it tries to stay close to [Lightning's terminologies](https://docs.ligh
33
33
  Add to your `Gemfile`:
34
34
 
35
35
  ```ruby
36
- gem 'lighstorm', '~> 0.0.4'
36
+ gem 'lighstorm', '~> 0.0.6'
37
37
  ```
38
38
 
39
39
  ```ruby
@@ -45,7 +45,7 @@ Lighstorm.config!(
45
45
  macaroon_path: '/lnd/data/chain/bitcoin/mainnet/admin.macaroon',
46
46
  )
47
47
 
48
- puts Lighstorm.version # => 0.0.4
48
+ puts Lighstorm.version # => 0.0.6
49
49
 
50
50
  Lighstorm::Satoshis.new(
51
51
  milisatoshis: 75_621_650
@@ -67,7 +67,7 @@ gem 'lighstorm', path: '/home/user/lighstorm'
67
67
  # demo.rb
68
68
  require 'lighstorm'
69
69
 
70
- puts Lighstorm.version # => 0.0.4
70
+ puts Lighstorm.version # => 0.0.6
71
71
  ```
72
72
 
73
73
  ```sh
@@ -100,5 +100,5 @@ gem build lighstorm.gemspec
100
100
 
101
101
  gem signin
102
102
 
103
- gem push lighstorm-0.0.4.gem
103
+ gem push lighstorm-0.0.6.gem
104
104
  ```
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lighstorm
4
+ module Adapter
5
+ class Policy
6
+ def self.get_chan_info(grpc)
7
+ {
8
+ fee: {
9
+ base: { milisatoshis: grpc[:fee_base_msat] },
10
+ rate: { parts_per_million: grpc[:fee_rate_milli_msat] }
11
+ },
12
+ htlc: {
13
+ minimum: { milisatoshis: grpc[:min_htlc] },
14
+ maximum: { milisatoshis: grpc[:max_htlc_msat] },
15
+ # https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#cltv_expiry_delta-selection
16
+ blocks: {
17
+ delta: {
18
+ minimum: grpc[:time_lock_delta] # aka cltv_expiry_delta
19
+ }
20
+ }
21
+ }
22
+ }
23
+ end
24
+
25
+ def self.subscribe_channel_graph(json)
26
+ result = {
27
+ _source: :subscribe_channel_graph,
28
+ fee: {
29
+ base: { milisatoshis: json['routingPolicy']['feeBaseMsat'].to_i },
30
+ rate: { parts_per_million: json['routingPolicy']['feeRateMilliMsat'].to_i }
31
+ },
32
+ htlc: {
33
+ minimum: { milisatoshis: json['routingPolicy']['minHtlc'].to_i },
34
+ maximum: { milisatoshis: json['routingPolicy']['maxHtlcMsat'].to_i },
35
+ # https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#cltv_expiry_delta-selection
36
+ blocks: {
37
+ delta: {
38
+ minimum: json['routingPolicy']['timeLockDelta'].to_i # aka cltv_expiry_delta
39
+ }
40
+ }
41
+ }
42
+ }
43
+
44
+ policy = json['routingPolicy']
45
+
46
+ result[:fee].delete(:base) unless policy.key?('feeBaseMsat') && !policy['feeBaseMsat'].nil?
47
+ result[:fee].delete(:rate) unless policy.key?('feeRateMilliMsat') && !policy['feeRateMilliMsat'].nil?
48
+ result.delete(:fee) if result[:fee].empty?
49
+
50
+ result[:htlc].delete(:minimum) unless policy.key?('minHtlc') && !policy['minHtlc'].nil?
51
+ result[:htlc].delete(:maximum) unless policy.key?('maxHtlcMsat') && !policy['maxHtlcMsat'].nil?
52
+ result[:htlc].delete(:blocks) unless policy.key?('timeLockDelta') && !policy['timeLockDelta'].nil?
53
+ result.delete(:htlc) if result[:htlc].empty?
54
+
55
+ return nil unless result.key?(:fee) || result.key?(:htlc)
56
+
57
+ result
58
+ end
59
+ end
60
+ end
61
+ end
@@ -1,16 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../nodes/node'
4
+ require_relative 'channel_node/policy'
4
5
 
5
6
  module Lighstorm
6
7
  module Adapter
7
8
  class ChannelNode
8
9
  def self.list_channels(grpc, key)
9
- {
10
+ data = {
10
11
  _source: :list_channels,
12
+ state: grpc[:active] ? 'active' : 'inactive',
11
13
  accounting: { balance: { milisatoshis: grpc[:"#{key}_balance"] * 1000 } },
12
14
  node: Node.list_channels(grpc, key)
13
15
  }
16
+
17
+ data.delete(:node) if data[:node].nil?
18
+
19
+ data
14
20
  end
15
21
 
16
22
  def self.get_chan_info(grpc, index)
@@ -20,32 +26,48 @@ module Lighstorm
20
26
  }
21
27
 
22
28
  if grpc[:"node#{index}_policy"]
23
- data[:policy] = {
24
- fee: {
25
- base: { milisatoshis: grpc[:"node#{index}_policy"][:fee_base_msat] },
26
- rate: { parts_per_million: grpc[:"node#{index}_policy"][:fee_rate_milli_msat] }
27
- },
28
- htlc: {
29
- minimum: { milisatoshis: grpc[:"node#{index}_policy"][:min_htlc] },
30
- maximum: { milisatoshis: grpc[:"node#{index}_policy"][:max_htlc_msat] },
31
- # https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#cltv_expiry_delta-selection
32
- blocks: {
33
- delta: {
34
- minimum: grpc[:"node#{index}_policy"][:time_lock_delta] # aka cltv_expiry_delta
35
- }
36
- }
37
- }
38
- }
29
+ data[:state] = grpc[:"node#{index}_policy"][:disabled] ? 'inactive' : 'active'
30
+ data[:policy] = Policy.get_chan_info(grpc[:"node#{index}_policy"])
39
31
  end
40
32
 
33
+ data.delete(:node) if data[:node].nil?
34
+
41
35
  data
42
36
  end
43
37
 
44
38
  def self.describe_graph(grpc, index)
45
- {
39
+ data = {
46
40
  _source: :describe_graph,
47
41
  node: Node.describe_graph_from_channel(grpc, index)
48
42
  }
43
+
44
+ # TODO: No examples to validate the correctness of this scenario.
45
+ if grpc[:"node#{index}_policy"]
46
+ data[:state] = grpc[:"node#{index}_policy"][:disabled] ? 'inactive' : 'active'
47
+ data[:policy] = Policy.get_chan_info(grpc[:"node#{index}_policy"])
48
+ end
49
+
50
+ data.delete(:node) if data[:node].nil?
51
+
52
+ data
53
+ end
54
+
55
+ def self.subscribe_channel_graph(json)
56
+ data = {
57
+ _source: :subscribe_channel_graph,
58
+ node: {
59
+ public_key: json['advertisingNode']
60
+ },
61
+ policy: Policy.subscribe_channel_graph(json)
62
+ }
63
+
64
+ unless json['routingPolicy']['disabled'].nil?
65
+ data[:state] = json['routingPolicy']['disabled'] ? 'inactive' : 'active'
66
+ end
67
+
68
+ data.delete(:policy) if data[:policy].nil?
69
+
70
+ data
49
71
  end
50
72
  end
51
73
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'date'
4
3
  require 'digest'
5
4
 
6
5
  require_relative '../connections/channel_node'
@@ -8,16 +7,16 @@ require_relative '../connections/channel_node'
8
7
  module Lighstorm
9
8
  module Adapter
10
9
  class Channel
11
- def self._key(id, active)
10
+ def self._key(id, state)
12
11
  Digest::SHA256.hexdigest(
13
- [id, active].join('/')
12
+ [id, state].join('/')
14
13
  )
15
14
  end
16
15
 
17
16
  def self.list_channels(grpc, at)
18
17
  {
19
18
  _source: :list_channels,
20
- _key: _key(grpc[:chan_id], grpc[:active]),
19
+ _key: _key(grpc[:chan_id], grpc[:active] ? 'active' : 'inactive'),
21
20
  # Standard JSON don't support BigInt, so, a String is safer.
22
21
  id: grpc[:chan_id].to_s,
23
22
  transaction: {
@@ -26,9 +25,9 @@ module Lighstorm
26
25
  index: grpc[:channel_point].split(':').last.to_i
27
26
  }
28
27
  },
29
- opened_at: DateTime.parse((at - grpc[:lifetime]).to_s),
30
- up_at: DateTime.parse((at - grpc[:uptime]).to_s),
31
- active: grpc[:active],
28
+ opened_at: at - grpc[:lifetime],
29
+ up_at: at - grpc[:uptime],
30
+ state: grpc[:active] ? 'active' : 'inactive',
32
31
  exposure: grpc[:private] ? 'private' : 'public',
33
32
  accounting: {
34
33
  capacity: { milisatoshis: grpc[:capacity] * 1000 },
@@ -75,6 +74,23 @@ module Lighstorm
75
74
  ]
76
75
  }
77
76
  end
77
+
78
+ def self.subscribe_channel_graph(json)
79
+ {
80
+ _source: :subscribe_channel_graph,
81
+ _key: _key(json['chanId'], nil),
82
+ id: json['chanId'],
83
+ accounting: {
84
+ capacity: { milisatoshis: json['capacity'].to_i * 1000 }
85
+ },
86
+ partners: [
87
+ ChannelNode.subscribe_channel_graph(json),
88
+ { node: {
89
+ public_key: json['connectingNode']
90
+ } }
91
+ ]
92
+ }
93
+ end
78
94
  end
79
95
  end
80
96
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'date'
4
3
  require 'digest'
5
4
 
6
5
  module Lighstorm
@@ -10,7 +9,7 @@ module Lighstorm
10
9
  {
11
10
  _source: :forwarding_history,
12
11
  _key: _key(grpc),
13
- at: DateTime.parse(Time.at(grpc[:timestamp_ns] / 1e+9).to_s),
12
+ at: Time.at(grpc[:timestamp_ns] / 1e+9),
14
13
  fee: { milisatoshis: grpc[:fee_msat] },
15
14
  in: {
16
15
  amount: { milisatoshis: grpc[:amt_in_msat] },
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'date'
4
-
5
3
  module Lighstorm
6
4
  module Adapter
7
5
  class Purpose
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'digest'
4
- require 'date'
5
4
 
6
5
  require_relative '../connections/payment_channel'
7
6
  require_relative 'payment/purpose'
@@ -30,7 +29,7 @@ module Lighstorm
30
29
  data = {
31
30
  _source: :list_payments,
32
31
  _key: _key(grpc),
33
- created_at: DateTime.parse(Time.at(grpc[:creation_time_ns] / 1e+9).to_s),
32
+ created_at: Time.at(grpc[:creation_time_ns] / 1e+9),
34
33
  status: grpc[:status].to_s.downcase,
35
34
  fee: { milisatoshis: grpc[:fee_msat] },
36
35
  purpose: Purpose.list_payments(grpc, node_get_info),
@@ -42,9 +41,7 @@ module Lighstorm
42
41
  end
43
42
  }
44
43
 
45
- if grpc[:htlcs].first[:resolve_time_ns]
46
- data[:settled_at] = DateTime.parse(Time.at(grpc[:htlcs].first[:resolve_time_ns] / 1e+9).to_s)
47
- end
44
+ data[:settled_at] = Time.at(grpc[:htlcs].first[:resolve_time_ns] / 1e+9) if grpc[:htlcs].first[:resolve_time_ns]
48
45
 
49
46
  data
50
47
  end
data/adapters/invoice.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'date'
4
3
  require 'digest'
5
4
 
6
5
  require_relative 'payment_request'
@@ -29,8 +28,8 @@ module Lighstorm
29
28
  def self.list_or_lookup(grpc)
30
29
  {
31
30
  _key: _key(grpc),
32
- created_at: DateTime.parse(Time.at(grpc[:creation_date]).to_s),
33
- settle_at: DateTime.parse(Time.at(grpc[:settle_date]).to_s),
31
+ created_at: Time.at(grpc[:creation_date]),
32
+ settle_at: grpc[:settle_date].nil? || !grpc[:settle_date].positive? ? nil : Time.at(grpc[:settle_date]),
34
33
  state: grpc[:state].to_s.downcase
35
34
  }
36
35
  end
@@ -20,15 +20,19 @@ module Lighstorm
20
20
  end
21
21
 
22
22
  def self.list_channels(grpc, key)
23
- {
23
+ data = {
24
24
  _source: :list_channels,
25
25
  _key: _key(key == :remote ? grpc[:remote_pubkey] : nil),
26
26
  public_key: key == :remote ? grpc[:remote_pubkey] : nil
27
27
  }
28
+
29
+ return nil if data[:public_key].nil?
30
+
31
+ data
28
32
  end
29
33
 
30
34
  def self.get_info(grpc)
31
- {
35
+ data = {
32
36
  _source: :get_info,
33
37
  _key: _key(grpc[:identity_pubkey]),
34
38
  public_key: grpc[:identity_pubkey],
@@ -43,34 +47,64 @@ module Lighstorm
43
47
  }
44
48
  }
45
49
  }
50
+
51
+ return nil if data[:public_key].nil? && data[:alias].nil? && data[:color].nil?
52
+
53
+ data
46
54
  end
47
55
 
48
56
  def self.get_node_info(grpc)
49
- {
57
+ data = {
50
58
  _source: :get_node_info,
51
59
  _key: _key(grpc[:node][:pub_key]),
52
60
  public_key: grpc[:node][:pub_key],
53
61
  alias: grpc[:node][:alias],
54
62
  color: grpc[:node][:color]
55
63
  }
64
+
65
+ return nil if data[:public_key].nil? && data[:alias].nil? && data[:color].nil?
66
+
67
+ data
56
68
  end
57
69
 
58
70
  def self.describe_graph(grpc)
59
- {
71
+ data = {
60
72
  _source: :describe_graph,
61
73
  _key: _key(grpc[:pub_key]),
62
74
  public_key: grpc[:pub_key],
63
75
  alias: grpc[:alias],
64
76
  color: grpc[:color]
65
77
  }
78
+
79
+ return nil if data[:public_key].nil? && data[:alias].nil? && data[:color].nil?
80
+
81
+ data
66
82
  end
67
83
 
68
84
  def self.describe_graph_from_channel(grpc, index)
69
- {
85
+ data = {
70
86
  _source: :describe_graph,
71
87
  _key: _key(grpc[:"node#{index}_pub"]),
72
88
  public_key: grpc[:"node#{index}_pub"]
73
89
  }
90
+
91
+ return nil if data[:public_key].nil?
92
+
93
+ data
94
+ end
95
+
96
+ def self.subscribe_channel_graph(json)
97
+ data = {
98
+ _source: :subscribe_channel_graph,
99
+ _key: _key(json['identityKey']),
100
+ public_key: json['identityKey'],
101
+ alias: json['alias'],
102
+ color: json['color']
103
+ }
104
+
105
+ return nil if data[:public_key].nil? && data[:alias].nil? && data[:color].nil?
106
+
107
+ data
74
108
  end
75
109
  end
76
110
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'date'
4
3
  require 'digest'
5
4
 
6
5
  require_relative '../ports/dsl/lighstorm/errors'