lighstorm 0.0.3 → 0.0.5

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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/.env.example +5 -0
  3. data/.github/workflows/ruby-rspec-tests.yml +39 -0
  4. data/.gitignore +1 -0
  5. data/.rspec +1 -0
  6. data/.rubocop.yml +10 -0
  7. data/Gemfile +4 -0
  8. data/Gemfile.lock +26 -2
  9. data/README.md +30 -371
  10. data/adapters/connections/channel_node/fee.rb +26 -0
  11. data/adapters/connections/channel_node/policy.rb +61 -0
  12. data/adapters/connections/channel_node.rb +74 -0
  13. data/adapters/connections/payment_channel.rb +28 -0
  14. data/adapters/edges/channel.rb +96 -0
  15. data/adapters/edges/forward.rb +40 -0
  16. data/adapters/edges/payment/purpose.rb +32 -0
  17. data/adapters/edges/payment.rb +50 -0
  18. data/adapters/invoice.rb +49 -0
  19. data/adapters/nodes/node.rb +111 -0
  20. data/adapters/payment_request.rb +76 -0
  21. data/components/cache.rb +3 -2
  22. data/components/lnd.rb +5 -1
  23. data/controllers/channel/actions/apply_gossip.rb +194 -0
  24. data/controllers/channel/actions/update_fee.rb +76 -0
  25. data/controllers/channel/all.rb +79 -0
  26. data/controllers/channel/find_by_id.rb +153 -0
  27. data/controllers/channel/mine.rb +114 -0
  28. data/controllers/channel.rb +27 -0
  29. data/controllers/forward/all.rb +244 -0
  30. data/controllers/forward/group_by_channel.rb +89 -0
  31. data/controllers/forward.rb +28 -0
  32. data/controllers/invoice/actions/create.rb +36 -0
  33. data/controllers/invoice/actions/pay.rb +28 -0
  34. data/controllers/invoice/actions/pay_through_route.rb +71 -0
  35. data/controllers/invoice/all.rb +70 -0
  36. data/controllers/invoice/find_by_secret_hash.rb +42 -0
  37. data/controllers/invoice.rb +36 -0
  38. data/controllers/node/actions/apply_gossip.rb +112 -0
  39. data/controllers/node/all.rb +63 -0
  40. data/controllers/node/find_by_public_key.rb +49 -0
  41. data/controllers/node/myself.rb +34 -0
  42. data/controllers/node.rb +27 -0
  43. data/controllers/payment/all.rb +368 -0
  44. data/controllers/payment.rb +21 -0
  45. data/docs/.nojekyll +0 -0
  46. data/docs/README.md +731 -0
  47. data/docs/_coverpage.md +12 -0
  48. data/docs/index.html +26 -0
  49. data/docs/vendor/docsify/docsify@4.js +1 -0
  50. data/docs/vendor/docsify-themeable/theme-simple-dark.css +3 -0
  51. data/docs/vendor/docsify-themeable/theme-simple-dark.css.map +1 -0
  52. data/docs/vendor/prismjs/prism-bash.min.js +1 -0
  53. data/docs/vendor/prismjs/prism-ruby.min.js +1 -0
  54. data/docs/vendor/prismjs/prism-tomorrow.min.css +1 -0
  55. data/lighstorm.gemspec +3 -1
  56. data/models/concerns/protectable.rb +23 -0
  57. data/models/connections/channel_node/accounting.rb +7 -14
  58. data/models/connections/channel_node/fee.rb +55 -84
  59. data/models/connections/channel_node/htlc/blocks/delta.rb +39 -0
  60. data/models/connections/channel_node/htlc.rb +65 -11
  61. data/models/connections/channel_node/policy.rb +15 -18
  62. data/models/connections/channel_node.rb +48 -22
  63. data/models/connections/forward_channel.rb +8 -43
  64. data/models/connections/payment_channel.rb +18 -39
  65. data/models/edges/channel/accounting.rb +45 -20
  66. data/models/edges/channel/hop.rb +65 -0
  67. data/models/edges/channel.rb +111 -169
  68. data/models/edges/forward.rb +9 -119
  69. data/models/edges/groups/{analysis.rb → channel_forwards/analysis.rb} +9 -9
  70. data/models/edges/groups/channel_forwards.rb +10 -40
  71. data/models/edges/payment.rb +29 -215
  72. data/models/errors.rb +32 -0
  73. data/models/invoice.rb +49 -0
  74. data/models/nodes/node/lightning.rb +11 -16
  75. data/models/nodes/node/platform.rb +13 -13
  76. data/models/nodes/node.rb +59 -103
  77. data/models/payment_request.rb +72 -0
  78. data/models/rate.rb +11 -1
  79. data/models/satoshis.rb +5 -6
  80. data/ports/dsl/lighstorm/errors.rb +5 -0
  81. data/ports/dsl/lighstorm.rb +11 -9
  82. data/ports/grpc/session.rb +26 -0
  83. data/ports/grpc.rb +80 -0
  84. data/static/cache.rb +12 -0
  85. data/static/spec.rb +3 -1
  86. metadata +62 -6
  87. data/models/connections/channel_node/constraints.rb +0 -24
@@ -6,36 +6,33 @@ require_relative 'htlc'
6
6
  module Lighstorm
7
7
  module Models
8
8
  class Policy
9
- def initialize(channel, node)
10
- @channel = channel
11
- @node = node
12
- end
13
-
14
- def data
15
- return @data if @data
16
-
17
- return if !@channel.data[:get_chan_info] && !@channel.data[:describe_graph]
18
-
19
- key = @channel.data[:get_chan_info] ? :get_chan_info : :describe_graph
9
+ attr_reader :transaction
20
10
 
21
- @data ||= if @channel.data[key].node1_pub == @node.public_key
22
- @channel.data[key].node1_policy
23
- else
24
- @channel.data[key].node2_policy
25
- end
11
+ def initialize(data, transaction)
12
+ @data = data
13
+ @transaction = transaction
26
14
  end
27
15
 
28
16
  def fee
29
- @fee ||= Fee.new(self, @channel, @node)
17
+ @fee ||= Fee.new(self, @data ? @data[:fee] : {})
30
18
  end
31
19
 
32
20
  def htlc
33
- @htlc ||= HTLC.new(self, @channel, @node)
21
+ @htlc ||= HTLC.new(@data ? @data[:htlc] : {})
34
22
  end
35
23
 
36
24
  def to_h
37
25
  { fee: fee.to_h, htlc: htlc.to_h }
38
26
  end
27
+
28
+ def dump
29
+ result = Marshal.load(Marshal.dump(@data))
30
+
31
+ result = result.merge({ fee: fee.dump }) if @data && @data[:fee]
32
+ result = result.merge({ htlc: htlc.dump }) if @data && @data[:htlc]
33
+
34
+ result
35
+ end
39
36
  end
40
37
  end
41
38
  end
@@ -1,47 +1,73 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'channel_node/constraints'
4
3
  require_relative 'channel_node/policy'
5
4
  require_relative 'channel_node/accounting'
6
5
 
6
+ require_relative '../nodes/node'
7
+ require_relative '../errors'
8
+
7
9
  module Lighstorm
8
10
  module Models
9
11
  class ChannelNode
10
- KIND = :connection
12
+ include Protectable
11
13
 
12
- attr_reader :node
14
+ attr_reader :state
13
15
 
14
- def initialize(channel, node)
15
- @channel = channel
16
- @node = node
16
+ def initialize(data, is_mine, transaction)
17
+ @data = data
18
+ @state = data[:state]
19
+ @is_mine = is_mine
20
+ @transaction = transaction
17
21
  end
18
22
 
19
- def policy
20
- @policy ||= Policy.new(@channel, @node)
23
+ def active?
24
+ state == 'active'
21
25
  end
22
26
 
23
- def constraints
24
- @constraints ||= Constraints.new(@channel, @node)
27
+ def node
28
+ @node ||= Node.new(@data[:node])
29
+ end
30
+
31
+ def policy
32
+ @policy ||= Policy.new(@data[:policy], @transaction)
25
33
  end
26
34
 
27
35
  def accounting
28
- @accounting ||= ChannelNodeAccounting.new(@channel, @node)
36
+ raise Errors::NotYourChannelError unless @is_mine
37
+
38
+ @accounting ||= @data[:accounting] ? ChannelNodeAccounting.new(@data[:accounting]) : nil
29
39
  end
30
40
 
31
41
  def to_h
32
- if @channel.data[:get_chan_info]
33
- {
34
- accounting: accounting.to_h,
35
- node: @node.to_h,
36
- policy: policy.to_h
37
- # constraints: constraints.to_h
38
- }
39
- else
42
+ restult = { state: state, node: node.to_h }
43
+
44
+ restult[:accounting] = accounting.to_h if @is_mine
45
+ restult[:policy] = policy.to_h if @data[:policy]
46
+
47
+ restult
48
+ end
49
+
50
+ def dump
51
+ result = Marshal.load(Marshal.dump(@data)).merge(
40
52
  {
41
- node: @node.to_h,
42
- policy: policy.to_h
53
+ node: node.dump,
54
+ policy: policy.dump
43
55
  }
44
- end
56
+ )
57
+
58
+ result[:accounting] = accounting.dump if @is_mine
59
+ result.delete(:policy) if result[:policy].nil?
60
+
61
+ result
62
+ end
63
+
64
+ def state=(value)
65
+ protect!(value)
66
+
67
+ @state = value[:value]
68
+ @data[:state] = @state
69
+
70
+ state
45
71
  end
46
72
  end
47
73
  end
@@ -5,58 +5,23 @@ require_relative '../edges/channel'
5
5
  module Lighstorm
6
6
  module Models
7
7
  class ForwardChannel
8
- KIND = :connection
9
-
10
- def initialize(direction, forward, respond_info: true)
11
- @respond_info = respond_info
12
- @direction = direction
13
- @forward = forward
8
+ def initialize(data)
9
+ @data = data
14
10
  end
15
11
 
16
- def channel
17
- @channel ||= Channel.find_by_id(channel_id)
12
+ def amount
13
+ @amount ||= Satoshis.new(milisatoshis: @data[:amount][:milisatoshis])
18
14
  end
19
15
 
20
- def amount
21
- @amount ||= if @direction == :in
22
- Satoshis.new(milisatoshis:
23
- @forward.data[:forwarding_history][:forwarding_events].first.amt_in_msat)
24
- else
25
- Satoshis.new(milisatoshis:
26
- @forward.data[:forwarding_history][:forwarding_events].first.amt_out_msat)
27
- end
16
+ def channel
17
+ @channel ||= Channel.new(@data[:channel])
28
18
  end
29
19
 
30
20
  def to_h
31
- response = {
21
+ {
32
22
  amount: amount.to_h,
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
45
- }
46
- }
23
+ channel: channel.to_h
47
24
  }
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
60
25
  end
61
26
  end
62
27
  end
@@ -1,71 +1,50 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../edges/channel'
3
+ require_relative '../edges/channel/hop'
4
4
  require_relative '../nodes/node'
5
5
 
6
6
  module Lighstorm
7
7
  module Models
8
8
  class PaymentChannel
9
- KIND = :connection
9
+ attr_reader :hop
10
+
11
+ def initialize(data, payment)
12
+ @data = data
10
13
 
11
- def initialize(raw_hop, hop_index, respond_info: true)
12
- @respond_info = respond_info
13
- @raw_hop = raw_hop
14
- @hop = hop_index
14
+ @hop = data[:hop]
15
+ @payment = payment
15
16
  end
16
17
 
17
- attr_reader :hop
18
+ def first?
19
+ @hop == 1
20
+ end
18
21
 
19
- def channel
20
- Channel.find_by_id(@raw_hop.chan_id)
22
+ def last?
23
+ @data[:is_last] == true
21
24
  end
22
25
 
23
26
  def amount
24
- @amount ||= Satoshis.new(milisatoshis: @raw_hop.amt_to_forward_msat)
27
+ @amount ||= Satoshis.new(milisatoshis: @data[:amount][:milisatoshis])
25
28
  end
26
29
 
27
30
  def fee
28
- @fee ||= Satoshis.new(milisatoshis: @raw_hop.fee_msat)
29
- end
30
-
31
- def raw
32
- @raw_hop
31
+ @fee ||= Satoshis.new(milisatoshis: @data[:fee][:milisatoshis])
33
32
  end
34
33
 
35
- def partner_node
36
- Node.find_by_public_key(@raw_hop.pub_key)
34
+ def channel
35
+ @channel ||= HopChannel.new(@data, @payment)
37
36
  end
38
37
 
39
38
  def to_h
40
- response = {
39
+ {
41
40
  hop: hop,
42
41
  amount: amount.to_h,
43
42
  fee: {
44
43
  milisatoshis: fee.milisatoshis,
45
44
  parts_per_million: fee.parts_per_million(amount.milisatoshis)
46
45
  },
47
- channel: {
48
- id: @raw_hop.chan_id.to_s,
49
- node: {
50
- public_key: @raw_hop.pub_key
51
- }
52
- }
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
- }
46
+ channel: channel.to_h
66
47
  }
67
-
68
- response
69
48
  end
70
49
  end
71
50
  end
@@ -1,48 +1,59 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../../satoshis'
4
+ require_relative '../../errors'
5
+ require_relative '../../concerns/protectable'
4
6
 
5
7
  module Lighstorm
6
8
  module Models
7
9
  class ChannelAccounting
8
- def initialize(channel)
9
- @channel = channel
10
+ include Protectable
11
+
12
+ def initialize(data, is_mine)
13
+ @data = data
14
+ @is_mine = is_mine
10
15
  end
11
16
 
12
17
  def capacity
13
- if @channel.data[:get_chan_info]
14
- @capacity ||= Satoshis.new(milisatoshis: @channel.data[:get_chan_info].capacity * 1000)
15
- elsif @channel.data[:describe_graph]
16
- @capacity ||= Satoshis.new(milisatoshis: @channel.data[:describe_graph].capacity * 1000)
17
- end
18
+ @capacity ||= if @data[:capacity]
19
+ Satoshis.new(
20
+ milisatoshis: @data[:capacity][:milisatoshis]
21
+ )
22
+ end
18
23
  end
19
24
 
20
25
  def sent
21
- return nil unless @channel.data[:list_channels]
26
+ raise Errors::NotYourChannelError unless @is_mine
22
27
 
23
- @sent ||= Satoshis.new(milisatoshis: (
24
- @channel.data[:list_channels][:channels].first.total_satoshis_sent.to_f * 1000.0
25
- ))
28
+ @sent ||= if @data[:sent]
29
+ Satoshis.new(
30
+ milisatoshis: @data[:sent][:milisatoshis]
31
+ )
32
+ end
26
33
  end
27
34
 
28
35
  def received
29
- return nil unless @channel.data[:list_channels]
36
+ raise Errors::NotYourChannelError unless @is_mine
30
37
 
31
- @received ||= Satoshis.new(milisatoshis: (
32
- @channel.data[:list_channels][:channels].first.total_satoshis_received.to_f * 1000.0
33
- ))
38
+ @received ||= if @data[:received]
39
+ Satoshis.new(
40
+ milisatoshis: @data[:received][:milisatoshis]
41
+ )
42
+ end
34
43
  end
35
44
 
36
45
  def unsettled
37
- return nil unless @channel.data[:list_channels]
46
+ raise Errors::NotYourChannelError unless @is_mine
38
47
 
39
- @unsettled ||= Satoshis.new(milisatoshis: (
40
- @channel.data[:list_channels][:channels].first.unsettled_balance.to_f * 1000.0
41
- ))
48
+ @unsettled ||= if @data[:unsettled]
49
+ Satoshis.new(
50
+ milisatoshis: @data[:unsettled][:milisatoshis]
51
+ )
52
+ end
42
53
  end
43
54
 
44
55
  def to_h
45
- if @channel.data[:get_chan_info]
56
+ if @is_mine
46
57
  {
47
58
  capacity: capacity.to_h,
48
59
  sent: sent.to_h,
@@ -55,6 +66,20 @@ module Lighstorm
55
66
  }
56
67
  end
57
68
  end
69
+
70
+ def dump
71
+ Marshal.load(Marshal.dump(@data))
72
+ end
73
+
74
+ def capacity=(value)
75
+ protect!(value)
76
+
77
+ @capacity = value[:value]
78
+
79
+ @data[:capacity][:milisatoshis] = @capacity.milisatoshis
80
+
81
+ capacity
82
+ end
58
83
  end
59
84
  end
60
85
  end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../channel'
4
+
5
+ module Lighstorm
6
+ module Models
7
+ class HopChannel < Channel
8
+ def initialize(data, payment)
9
+ @hop_data = data
10
+ @payment = payment
11
+ super(data[:channel])
12
+ end
13
+
14
+ def target
15
+ partners.find do |partner|
16
+ partner.node.public_key == @hop_data[:channel][:target][:public_key]
17
+ end.node
18
+ end
19
+
20
+ def exit
21
+ @exit ||= if include_myself? && @hop_data[:hop] == 1
22
+ partners.reverse.find do |partner|
23
+ !partner.node.myself?
24
+ end.node
25
+ elsif @hop_data[:hop] == 1
26
+ target
27
+ end
28
+ end
29
+
30
+ def entry
31
+ return nil if @hop_data[:is_last] && @hop_data[:hop] == 1
32
+
33
+ @entry ||= if include_myself? && @hop_data[:is_last]
34
+ if partners.size > 1
35
+ partners.reverse.find do |partner|
36
+ !partner.node.myself?
37
+ end.node
38
+ else
39
+ @payment.hops[@payment.hops.size - 2].channel.target
40
+ end
41
+ end
42
+ end
43
+
44
+ def include_myself?
45
+ !partners.find { |partner| partner.node.myself? }.nil?
46
+ end
47
+
48
+ def to_h
49
+ if !known? && !partners.size.positive?
50
+ { _key: _key, id: id }
51
+ else
52
+ target_hash = target.to_h
53
+ target_hash.delete(:platform)
54
+
55
+ result = { _key: _key, id: id, target: target_hash }
56
+
57
+ result[:entry] = entry.to_h if entry
58
+ result[:exit] = self.exit.to_h if self.exit
59
+
60
+ result
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end