lighstorm 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby-rspec-tests.yml +39 -0
  3. data/Gemfile.lock +1 -1
  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/node/actions/apply_gossip.rb +112 -0
  19. data/controllers/node.rb +4 -0
  20. data/controllers/payment/all.rb +52 -36
  21. data/controllers/payment.rb +6 -6
  22. data/docs/README.md +89 -13
  23. data/docs/_coverpage.md +1 -1
  24. data/docs/index.html +1 -1
  25. data/models/concerns/protectable.rb +23 -0
  26. data/models/connections/channel_node/accounting.rb +4 -0
  27. data/models/connections/channel_node/fee.rb +27 -18
  28. data/models/connections/channel_node/htlc/blocks/delta.rb +39 -0
  29. data/models/connections/channel_node/htlc.rb +48 -14
  30. data/models/connections/channel_node/policy.rb +11 -2
  31. data/models/connections/channel_node.rb +33 -1
  32. data/models/edges/channel/accounting.rb +17 -0
  33. data/models/edges/channel.rb +39 -5
  34. data/models/edges/forward.rb +0 -1
  35. data/models/edges/payment.rb +0 -1
  36. data/models/errors.rb +3 -0
  37. data/models/nodes/node/lightning.rb +6 -0
  38. data/models/nodes/node/platform.rb +6 -0
  39. data/models/nodes/node.rb +51 -0
  40. data/models/payment_request.rb +6 -3
  41. data/ports/grpc/session.rb +26 -0
  42. data/ports/grpc.rb +23 -5
  43. data/static/spec.rb +1 -1
  44. 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: b9138a744ed51a4f15025f97f27e09ec5f3b100ef8def0a6d992895cb42f8b0c
4
+ data.tar.gz: 85435136bfbb78cc7c6baa74ea6a745e958d8eb3d3f08d42ef42d03a1d2230eb
5
5
  SHA512:
6
- metadata.gz: b633147091290d526f184ca324964d9bc2fd13cb769a2009e7b732280ddd305fac82d63d2c9c735277ecc14f2fbdf4310d7b4d95611398753879746d170ed33b
7
- data.tar.gz: e979df8953dd05ab5efceb268cde4f305190bc07d3304e304155ddef3ebf876f0444612b59420e2c7f24572d20b758e6521a7a569e3788eaf5b76037ee8ded7e
6
+ metadata.gz: e728481bdafde3c30dc23e85a8a48d0d3a5ac020410d14cbafe48b17d86661c2df86bab4f75a65099919adf09b3808ce19f410f915cb7287d2a7802884cc8f0a
7
+ data.tar.gz: 6efa2544e1d08c49966b322e10d8cc40f3d1de0238534665148bff8515686be8817abf039442308563d0cc6ee15d3cf71ef1486f55a946103dcdd663a735684b
@@ -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.5)
5
5
  dotenv (~> 2.8, >= 2.8.1)
6
6
  lnd-client (~> 0.0.5)
7
7
  zache (~> 0.12.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.5'
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.5
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.5
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.5.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: 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'
@@ -0,0 +1,194 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ require_relative '../../../ports/grpc'
6
+ require_relative '../../../models/errors'
7
+ require_relative '../../../models/edges/channel'
8
+ require_relative '../../../adapters/edges/channel'
9
+
10
+ module Lighstorm
11
+ module Controllers
12
+ module Channel
13
+ module ApplyGossip
14
+ SKIPABLE = [
15
+ '_key',
16
+ '_source',
17
+ 'partners/0/_source',
18
+ 'partners/0/policy/_source',
19
+ 'partners/1/_source',
20
+ 'partners/1/policy/_source'
21
+ ].freeze
22
+
23
+ NOT_ALLOWED = [
24
+ 'id'
25
+ ].freeze
26
+
27
+ APPLICABLE = [
28
+ 'accounting/capacity/milisatoshis',
29
+ 'partners/0/policy/fee/base/milisatoshis',
30
+ 'partners/0/state',
31
+ 'partners/1/policy/fee/base/milisatoshis',
32
+ 'partners/1/state',
33
+ 'partners/1/policy/fee/rate/parts_per_million',
34
+ 'partners/0/policy/fee/rate/parts_per_million',
35
+ 'partners/0/policy/htlc/minimum/milisatoshis',
36
+ 'partners/1/policy/htlc/minimum/milisatoshis',
37
+ 'partners/0/policy/htlc/maximum/milisatoshis',
38
+ 'partners/1/policy/htlc/maximum/milisatoshis',
39
+ 'partners/0/policy/htlc/blocks/delta/minimum',
40
+ 'partners/1/policy/htlc/blocks/delta/minimum'
41
+ ].freeze
42
+
43
+ def self.perform(actual, gossip)
44
+ updated = Models::Channel.new(Adapter::Channel.subscribe_channel_graph(gossip))
45
+
46
+ actual_dump = actual.dump
47
+ updated_dump = updated.dump
48
+
49
+ if actual.partners.first.node.public_key == updated.partners.last.node.public_key &&
50
+ actual.partners.last.node.public_key == updated.partners.first.node.public_key
51
+ a = updated_dump[:partners][0]
52
+ b = updated_dump[:partners][1]
53
+
54
+ updated_dump[:partners][0] = b
55
+ updated_dump[:partners][1] = a
56
+ end
57
+
58
+ diff = generate_diff(actual_dump, updated_dump)
59
+
60
+ diff.each do |change|
61
+ key = change[:path].join('/')
62
+ next unless NOT_ALLOWED.include?(key)
63
+
64
+ raise IncoherentGossipError, "Gossip doesn't belong to this Channel"
65
+ end
66
+
67
+ diff.filter do |change|
68
+ key = change[:path].join('/')
69
+ if SKIPABLE.include?(key)
70
+ false
71
+ elsif APPLICABLE.include?(key)
72
+ apply!(actual, key, change)
73
+ true
74
+ else
75
+ raise Lighstorm::Errors::MissingGossipHandlerError, "don't know how to apply '#{key}'"
76
+ end
77
+ end
78
+ end
79
+
80
+ def self.apply!(actual, key, change)
81
+ case key
82
+ when 'accounting/capacity/milisatoshis'
83
+ token = SecureRandom.hex
84
+ actual.accounting.prepare_token!(token)
85
+ actual.accounting.capacity = {
86
+ value: Models::Satoshis.new(milisatoshis: change[:to]),
87
+ token: token
88
+ }
89
+ when 'partners/0/policy/htlc/maximum/milisatoshis',
90
+ 'partners/1/policy/htlc/maximum/milisatoshis' then
91
+ policy = actual.partners[change[:path][1]].policy
92
+
93
+ token = SecureRandom.hex
94
+ policy.htlc.prepare_token!(token)
95
+ policy.htlc.maximum = {
96
+ value: Models::Satoshis.new(milisatoshis: change[:to]),
97
+ token: token
98
+ }
99
+ when 'partners/0/policy/htlc/minimum/milisatoshis',
100
+ 'partners/1/policy/htlc/minimum/milisatoshis' then
101
+ if actual.partners[change[:path][1]].policy.nil?
102
+ actual.partners[change[:path][1]].policy = Lighstorm::Models::Policy.new({})
103
+ end
104
+
105
+ policy = actual.partners[change[:path][1]].policy
106
+
107
+ token = SecureRandom.hex
108
+ policy.htlc.prepare_token!(token)
109
+ policy.htlc.minimum = {
110
+ value: Models::Satoshis.new(milisatoshis: change[:to]),
111
+ token: token
112
+ }
113
+ when 'partners/0/policy/htlc/blocks/delta/minimum',
114
+ 'partners/1/policy/htlc/blocks/delta/minimum' then
115
+ if actual.partners[change[:path][1]].policy.nil?
116
+ actual.partners[change[:path][1]].policy = Lighstorm::Models::Policy.new({})
117
+ end
118
+
119
+ policy = actual.partners[change[:path][1]].policy
120
+
121
+ token = SecureRandom.hex
122
+ policy.htlc.blocks.delta.prepare_token!(token)
123
+ policy.htlc.blocks.delta.minimum = {
124
+ value: change[:to],
125
+ token: token
126
+ }
127
+ when 'partners/0/policy/fee/rate/parts_per_million',
128
+ 'partners/1/policy/fee/rate/parts_per_million' then
129
+ policy = actual.partners[change[:path][1]].policy
130
+
131
+ token = SecureRandom.hex
132
+ policy.fee.prepare_token!(token)
133
+ policy.fee.rate = {
134
+ value: Models::Rate.new(parts_per_million: change[:to]),
135
+ token: token
136
+ }
137
+ when 'partners/0/policy/fee/base/milisatoshis',
138
+ 'partners/1/policy/fee/base/milisatoshis' then
139
+ policy = actual.partners[change[:path][1]].policy
140
+
141
+ token = SecureRandom.hex
142
+ policy.fee.prepare_token!(token)
143
+ policy.fee.base = {
144
+ value: Models::Satoshis.new(milisatoshis: change[:to]),
145
+ token: token
146
+ }
147
+ when 'partners/0/state',
148
+ 'partners/1/state' then
149
+ partner = actual.partners[change[:path][1]]
150
+
151
+ token = SecureRandom.hex
152
+ partner.prepare_token!(token)
153
+ partner.state = { value: change[:to], token: token }
154
+ else
155
+ raise Lighstorm::Errors::MissingGossipHandlerError, "don't know how to apply '#{key}'"
156
+ end
157
+ end
158
+
159
+ def self.generate_diff(actual, node, path = [], diff = [])
160
+ case node
161
+ when Hash
162
+ result = {}
163
+ node.each_key do |key|
164
+ result[key] = generate_diff(actual, node[key], path.dup.push(key), diff)
165
+ end
166
+ when Array
167
+ result = []
168
+ node.each_with_index do |value, i|
169
+ result << generate_diff(actual, value, path.dup.push(i), diff)
170
+ end
171
+ else
172
+ new_value = node
173
+
174
+ unless new_value.nil?
175
+ actual_value = actual
176
+ path.each do |key|
177
+ if actual_value[key]
178
+ actual_value = actual_value[key]
179
+ else
180
+ actual_value = nil
181
+ break
182
+ end
183
+ end
184
+
185
+ diff << { path: path, from: actual_value, to: new_value } if actual_value != new_value
186
+ end
187
+ end
188
+
189
+ diff
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end