lighstorm 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby-rspec-tests.yml +39 -0
- data/Gemfile.lock +1 -1
- data/README.md +5 -5
- data/adapters/connections/channel_node/policy.rb +61 -0
- data/adapters/connections/channel_node.rb +40 -18
- data/adapters/edges/channel.rb +23 -7
- data/adapters/edges/forward.rb +1 -2
- data/adapters/edges/payment/purpose.rb +0 -2
- data/adapters/edges/payment.rb +2 -5
- data/adapters/invoice.rb +2 -3
- data/adapters/nodes/node.rb +39 -5
- data/adapters/payment_request.rb +0 -1
- data/controllers/channel/actions/apply_gossip.rb +194 -0
- data/controllers/channel/find_by_id.rb +1 -1
- data/controllers/channel/mine.rb +1 -1
- data/controllers/channel.rb +4 -0
- data/controllers/node/actions/apply_gossip.rb +112 -0
- data/controllers/node.rb +4 -0
- data/controllers/payment/all.rb +52 -36
- data/controllers/payment.rb +6 -6
- data/docs/README.md +89 -13
- data/docs/_coverpage.md +1 -1
- data/docs/index.html +1 -1
- data/models/concerns/protectable.rb +23 -0
- data/models/connections/channel_node/accounting.rb +4 -0
- data/models/connections/channel_node/fee.rb +27 -18
- data/models/connections/channel_node/htlc/blocks/delta.rb +39 -0
- data/models/connections/channel_node/htlc.rb +48 -14
- data/models/connections/channel_node/policy.rb +11 -2
- data/models/connections/channel_node.rb +33 -1
- data/models/edges/channel/accounting.rb +17 -0
- data/models/edges/channel.rb +39 -5
- data/models/edges/forward.rb +0 -1
- data/models/edges/payment.rb +0 -1
- data/models/errors.rb +3 -0
- data/models/nodes/node/lightning.rb +6 -0
- data/models/nodes/node/platform.rb +6 -0
- data/models/nodes/node.rb +51 -0
- data/models/payment_request.rb +6 -3
- data/ports/grpc/session.rb +26 -0
- data/ports/grpc.rb +23 -5
- data/static/spec.rb +1 -1
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b9138a744ed51a4f15025f97f27e09ec5f3b100ef8def0a6d992895cb42f8b0c
|
4
|
+
data.tar.gz: 85435136bfbb78cc7c6baa74ea6a745e958d8eb3d3f08d42ef42d03a1d2230eb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
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.
|
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.
|
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.
|
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.
|
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[:
|
24
|
-
|
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
|
data/adapters/edges/channel.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 '../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,
|
10
|
+
def self._key(id, state)
|
12
11
|
Digest::SHA256.hexdigest(
|
13
|
-
[id,
|
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:
|
30
|
-
up_at:
|
31
|
-
|
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
|
data/adapters/edges/forward.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
|
module Lighstorm
|
@@ -10,7 +9,7 @@ module Lighstorm
|
|
10
9
|
{
|
11
10
|
_source: :forwarding_history,
|
12
11
|
_key: _key(grpc),
|
13
|
-
at:
|
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] },
|
data/adapters/edges/payment.rb
CHANGED
@@ -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:
|
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:
|
33
|
-
settle_at:
|
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
|
data/adapters/nodes/node.rb
CHANGED
@@ -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
|
data/adapters/payment_request.rb
CHANGED
@@ -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
|