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.
- checksums.yaml +4 -4
- data/.env.example +5 -0
- data/.github/workflows/ruby-rspec-tests.yml +39 -0
- data/.gitignore +1 -0
- data/.rspec +1 -0
- data/.rubocop.yml +10 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +26 -2
- data/README.md +30 -371
- data/adapters/connections/channel_node/fee.rb +26 -0
- data/adapters/connections/channel_node/policy.rb +61 -0
- data/adapters/connections/channel_node.rb +74 -0
- data/adapters/connections/payment_channel.rb +28 -0
- data/adapters/edges/channel.rb +96 -0
- data/adapters/edges/forward.rb +40 -0
- data/adapters/edges/payment/purpose.rb +32 -0
- data/adapters/edges/payment.rb +50 -0
- data/adapters/invoice.rb +49 -0
- data/adapters/nodes/node.rb +111 -0
- data/adapters/payment_request.rb +76 -0
- data/components/cache.rb +3 -2
- data/components/lnd.rb +5 -1
- data/controllers/channel/actions/apply_gossip.rb +194 -0
- data/controllers/channel/actions/update_fee.rb +76 -0
- data/controllers/channel/all.rb +79 -0
- data/controllers/channel/find_by_id.rb +153 -0
- data/controllers/channel/mine.rb +114 -0
- data/controllers/channel.rb +27 -0
- data/controllers/forward/all.rb +244 -0
- data/controllers/forward/group_by_channel.rb +89 -0
- data/controllers/forward.rb +28 -0
- data/controllers/invoice/actions/create.rb +36 -0
- data/controllers/invoice/actions/pay.rb +28 -0
- data/controllers/invoice/actions/pay_through_route.rb +71 -0
- data/controllers/invoice/all.rb +70 -0
- data/controllers/invoice/find_by_secret_hash.rb +42 -0
- data/controllers/invoice.rb +36 -0
- data/controllers/node/actions/apply_gossip.rb +112 -0
- data/controllers/node/all.rb +63 -0
- data/controllers/node/find_by_public_key.rb +49 -0
- data/controllers/node/myself.rb +34 -0
- data/controllers/node.rb +27 -0
- data/controllers/payment/all.rb +368 -0
- data/controllers/payment.rb +21 -0
- data/docs/.nojekyll +0 -0
- data/docs/README.md +731 -0
- data/docs/_coverpage.md +12 -0
- data/docs/index.html +26 -0
- data/docs/vendor/docsify/docsify@4.js +1 -0
- data/docs/vendor/docsify-themeable/theme-simple-dark.css +3 -0
- data/docs/vendor/docsify-themeable/theme-simple-dark.css.map +1 -0
- data/docs/vendor/prismjs/prism-bash.min.js +1 -0
- data/docs/vendor/prismjs/prism-ruby.min.js +1 -0
- data/docs/vendor/prismjs/prism-tomorrow.min.css +1 -0
- data/lighstorm.gemspec +3 -1
- data/models/concerns/protectable.rb +23 -0
- data/models/connections/channel_node/accounting.rb +7 -14
- data/models/connections/channel_node/fee.rb +55 -84
- data/models/connections/channel_node/htlc/blocks/delta.rb +39 -0
- data/models/connections/channel_node/htlc.rb +65 -11
- data/models/connections/channel_node/policy.rb +15 -18
- data/models/connections/channel_node.rb +48 -22
- data/models/connections/forward_channel.rb +8 -43
- data/models/connections/payment_channel.rb +18 -39
- data/models/edges/channel/accounting.rb +45 -20
- data/models/edges/channel/hop.rb +65 -0
- data/models/edges/channel.rb +111 -169
- data/models/edges/forward.rb +9 -119
- data/models/edges/groups/{analysis.rb → channel_forwards/analysis.rb} +9 -9
- data/models/edges/groups/channel_forwards.rb +10 -40
- data/models/edges/payment.rb +29 -215
- data/models/errors.rb +32 -0
- data/models/invoice.rb +49 -0
- data/models/nodes/node/lightning.rb +11 -16
- data/models/nodes/node/platform.rb +13 -13
- data/models/nodes/node.rb +59 -103
- data/models/payment_request.rb +72 -0
- data/models/rate.rb +11 -1
- data/models/satoshis.rb +5 -6
- data/ports/dsl/lighstorm/errors.rb +5 -0
- data/ports/dsl/lighstorm.rb +11 -9
- data/ports/grpc/session.rb +26 -0
- data/ports/grpc.rb +80 -0
- data/static/cache.rb +12 -0
- data/static/spec.rb +3 -1
- metadata +62 -6
- data/models/connections/channel_node/constraints.rb +0 -24
@@ -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
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../nodes/node'
|
4
|
+
require_relative 'channel_node/policy'
|
5
|
+
|
6
|
+
module Lighstorm
|
7
|
+
module Adapter
|
8
|
+
class ChannelNode
|
9
|
+
def self.list_channels(grpc, key)
|
10
|
+
data = {
|
11
|
+
_source: :list_channels,
|
12
|
+
state: grpc[:active] ? 'active' : 'inactive',
|
13
|
+
accounting: { balance: { milisatoshis: grpc[:"#{key}_balance"] * 1000 } },
|
14
|
+
node: Node.list_channels(grpc, key)
|
15
|
+
}
|
16
|
+
|
17
|
+
data.delete(:node) if data[:node].nil?
|
18
|
+
|
19
|
+
data
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.get_chan_info(grpc, index)
|
23
|
+
data = {
|
24
|
+
_source: :get_chan_info,
|
25
|
+
node: Node.get_chan_info(grpc, index)
|
26
|
+
}
|
27
|
+
|
28
|
+
if grpc[:"node#{index}_policy"]
|
29
|
+
data[:state] = grpc[:"node#{index}_policy"][:disabled] ? 'inactive' : 'active'
|
30
|
+
data[:policy] = Policy.get_chan_info(grpc[:"node#{index}_policy"])
|
31
|
+
end
|
32
|
+
|
33
|
+
data.delete(:node) if data[:node].nil?
|
34
|
+
|
35
|
+
data
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.describe_graph(grpc, index)
|
39
|
+
data = {
|
40
|
+
_source: :describe_graph,
|
41
|
+
node: Node.describe_graph_from_channel(grpc, index)
|
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
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest'
|
4
|
+
|
5
|
+
module Lighstorm
|
6
|
+
module Adapter
|
7
|
+
class PaymentChannel
|
8
|
+
def self.list_payments(grpc, index)
|
9
|
+
{
|
10
|
+
_source: :list_payments,
|
11
|
+
hop: index + 1,
|
12
|
+
amount: { milisatoshis: grpc[:amt_to_forward_msat] },
|
13
|
+
fee: { milisatoshis: grpc[:fee_msat] },
|
14
|
+
channel: {
|
15
|
+
_key: Digest::SHA256.hexdigest(grpc[:chan_id].to_s),
|
16
|
+
id: grpc[:chan_id].to_s,
|
17
|
+
partners: [
|
18
|
+
node: {
|
19
|
+
_key: Digest::SHA256.hexdigest(grpc[:pub_key].to_s),
|
20
|
+
public_key: grpc[:pub_key]
|
21
|
+
}
|
22
|
+
]
|
23
|
+
}
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest'
|
4
|
+
|
5
|
+
require_relative '../connections/channel_node'
|
6
|
+
|
7
|
+
module Lighstorm
|
8
|
+
module Adapter
|
9
|
+
class Channel
|
10
|
+
def self._key(id, state)
|
11
|
+
Digest::SHA256.hexdigest(
|
12
|
+
[id, state].join('/')
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.list_channels(grpc, at)
|
17
|
+
{
|
18
|
+
_source: :list_channels,
|
19
|
+
_key: _key(grpc[:chan_id], grpc[:active] ? 'active' : 'inactive'),
|
20
|
+
# Standard JSON don't support BigInt, so, a String is safer.
|
21
|
+
id: grpc[:chan_id].to_s,
|
22
|
+
transaction: {
|
23
|
+
funding: {
|
24
|
+
id: grpc[:channel_point].split(':').first,
|
25
|
+
index: grpc[:channel_point].split(':').last.to_i
|
26
|
+
}
|
27
|
+
},
|
28
|
+
opened_at: at - grpc[:lifetime],
|
29
|
+
up_at: at - grpc[:uptime],
|
30
|
+
state: grpc[:active] ? 'active' : 'inactive',
|
31
|
+
exposure: grpc[:private] ? 'private' : 'public',
|
32
|
+
accounting: {
|
33
|
+
capacity: { milisatoshis: grpc[:capacity] * 1000 },
|
34
|
+
sent: { milisatoshis: grpc[:total_satoshis_sent] * 1000 },
|
35
|
+
received: { milisatoshis: grpc[:total_satoshis_received] * 1000 },
|
36
|
+
unsettled: { milisatoshis: grpc[:unsettled_balance] * 1000 }
|
37
|
+
},
|
38
|
+
partners: [
|
39
|
+
ChannelNode.list_channels(grpc, :local),
|
40
|
+
ChannelNode.list_channels(grpc, :remote)
|
41
|
+
]
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.get_chan_info(grpc)
|
46
|
+
{
|
47
|
+
_source: :get_chan_info,
|
48
|
+
_key: _key(grpc[:channel_id], nil),
|
49
|
+
# Standard JSON don't support BigInt, so, a String is safer.
|
50
|
+
id: grpc[:channel_id].to_s,
|
51
|
+
accounting: {
|
52
|
+
capacity: { milisatoshis: grpc[:capacity] * 1000 }
|
53
|
+
},
|
54
|
+
partners: [
|
55
|
+
ChannelNode.get_chan_info(grpc, 1),
|
56
|
+
ChannelNode.get_chan_info(grpc, 2)
|
57
|
+
]
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.describe_graph(grpc)
|
62
|
+
{
|
63
|
+
_source: :describe_graph,
|
64
|
+
_key: _key(grpc[:channel_id], nil),
|
65
|
+
# Standard JSON don't support BigInt, so, a String is safer.
|
66
|
+
id: grpc[:channel_id].to_s,
|
67
|
+
exposure: 'public',
|
68
|
+
accounting: {
|
69
|
+
capacity: { milisatoshis: grpc[:capacity] * 1000 }
|
70
|
+
},
|
71
|
+
partners: [
|
72
|
+
ChannelNode.describe_graph(grpc, 1),
|
73
|
+
ChannelNode.describe_graph(grpc, 2)
|
74
|
+
]
|
75
|
+
}
|
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
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest'
|
4
|
+
|
5
|
+
module Lighstorm
|
6
|
+
module Adapter
|
7
|
+
class Forward
|
8
|
+
def self.forwarding_history(grpc)
|
9
|
+
{
|
10
|
+
_source: :forwarding_history,
|
11
|
+
_key: _key(grpc),
|
12
|
+
at: Time.at(grpc[:timestamp_ns] / 1e+9),
|
13
|
+
fee: { milisatoshis: grpc[:fee_msat] },
|
14
|
+
in: {
|
15
|
+
amount: { milisatoshis: grpc[:amt_in_msat] },
|
16
|
+
channel: {
|
17
|
+
id: grpc[:chan_id_in].to_s
|
18
|
+
}
|
19
|
+
},
|
20
|
+
out: {
|
21
|
+
amount: { milisatoshis: grpc[:amt_out_msat] },
|
22
|
+
channel: {
|
23
|
+
id: grpc[:chan_id_out].to_s
|
24
|
+
}
|
25
|
+
}
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
def self._key(grpc)
|
30
|
+
Digest::SHA256.hexdigest(
|
31
|
+
[
|
32
|
+
grpc[:timestamp_ns],
|
33
|
+
grpc[:chan_id_in], grpc[:chan_id_out],
|
34
|
+
grpc[:amt_in_msat], grpc[:fee_msat]
|
35
|
+
].join('/')
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lighstorm
|
4
|
+
module Adapter
|
5
|
+
class Purpose
|
6
|
+
def self.list_payments(grpc, node_get_info)
|
7
|
+
return 'self-payment' if self_payment?(grpc)
|
8
|
+
return 'peer-to-peer' if peer_to_peer?(grpc)
|
9
|
+
return 'rebalance' if rebalance?(grpc, node_get_info)
|
10
|
+
|
11
|
+
'payment'
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.self_payment?(grpc)
|
15
|
+
grpc[:htlcs].first[:route][:hops].size == 2 &&
|
16
|
+
grpc[:htlcs].first[:route][:hops][0][:chan_id] == grpc[:htlcs].first[:route][:hops][1][:chan_id]
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.peer_to_peer?(grpc)
|
20
|
+
grpc[:htlcs].first[:route][:hops].size == 1
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.rebalance?(grpc, node_get_info)
|
24
|
+
return false if grpc[:htlcs].first[:route][:hops].size <= 2
|
25
|
+
|
26
|
+
destination_public_key = grpc[:htlcs].first[:route][:hops].last[:pub_key]
|
27
|
+
|
28
|
+
node_get_info[:identity_pubkey] == destination_public_key
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest'
|
4
|
+
|
5
|
+
require_relative '../connections/payment_channel'
|
6
|
+
require_relative 'payment/purpose'
|
7
|
+
|
8
|
+
require_relative '../payment_request'
|
9
|
+
|
10
|
+
require_relative '../../ports/dsl/lighstorm/errors'
|
11
|
+
|
12
|
+
module Lighstorm
|
13
|
+
module Adapter
|
14
|
+
class Payment
|
15
|
+
def self._key(grpc)
|
16
|
+
Digest::SHA256.hexdigest(
|
17
|
+
[
|
18
|
+
grpc[:payment_request],
|
19
|
+
grpc[:creation_time_ns],
|
20
|
+
grpc[:resolve_time_ns],
|
21
|
+
grpc[:status]
|
22
|
+
].join('/')
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.list_payments(grpc, node_get_info)
|
27
|
+
raise UnexpectedNumberOfHTLCsError, "htlcs: #{grpc[:htlcs].size}" if grpc[:htlcs].size > 1
|
28
|
+
|
29
|
+
data = {
|
30
|
+
_source: :list_payments,
|
31
|
+
_key: _key(grpc),
|
32
|
+
created_at: Time.at(grpc[:creation_time_ns] / 1e+9),
|
33
|
+
status: grpc[:status].to_s.downcase,
|
34
|
+
fee: { milisatoshis: grpc[:fee_msat] },
|
35
|
+
purpose: Purpose.list_payments(grpc, node_get_info),
|
36
|
+
request: PaymentRequest.list_payments(grpc),
|
37
|
+
hops: grpc[:htlcs].first[:route][:hops].map.with_index do |raw_hop, i|
|
38
|
+
hop = PaymentChannel.list_payments(raw_hop, i)
|
39
|
+
hop[:channel][:target] = { public_key: raw_hop[:pub_key] }
|
40
|
+
hop
|
41
|
+
end
|
42
|
+
}
|
43
|
+
|
44
|
+
data[:settled_at] = Time.at(grpc[:htlcs].first[:resolve_time_ns] / 1e+9) if grpc[:htlcs].first[:resolve_time_ns]
|
45
|
+
|
46
|
+
data
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/adapters/invoice.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest'
|
4
|
+
|
5
|
+
require_relative 'payment_request'
|
6
|
+
|
7
|
+
module Lighstorm
|
8
|
+
module Adapter
|
9
|
+
class Invoice
|
10
|
+
def self.lookup_invoice(grpc)
|
11
|
+
adapted = list_or_lookup(grpc)
|
12
|
+
|
13
|
+
adapted[:_source] = :lookup_invoice
|
14
|
+
adapted[:request] = PaymentRequest.lookup_invoice(grpc)
|
15
|
+
|
16
|
+
adapted
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.list_invoices(grpc)
|
20
|
+
adapted = list_or_lookup(grpc)
|
21
|
+
|
22
|
+
adapted[:_source] = :list_invoices
|
23
|
+
adapted[:request] = PaymentRequest.list_invoices(grpc)
|
24
|
+
|
25
|
+
adapted
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.list_or_lookup(grpc)
|
29
|
+
{
|
30
|
+
_key: _key(grpc),
|
31
|
+
created_at: Time.at(grpc[:creation_date]),
|
32
|
+
settle_at: Time.at(grpc[:settle_date]),
|
33
|
+
state: grpc[:state].to_s.downcase
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
def self._key(grpc)
|
38
|
+
Digest::SHA256.hexdigest(
|
39
|
+
[
|
40
|
+
grpc[:creation_date],
|
41
|
+
grpc[:settle_date],
|
42
|
+
grpc[:payment_request],
|
43
|
+
grpc[:state]
|
44
|
+
].join('/')
|
45
|
+
)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest'
|
4
|
+
|
5
|
+
module Lighstorm
|
6
|
+
module Adapter
|
7
|
+
class Node
|
8
|
+
def self._key(public_key)
|
9
|
+
Digest::SHA256.hexdigest(
|
10
|
+
[public_key].join('/')
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.get_chan_info(grpc, index)
|
15
|
+
{
|
16
|
+
_source: :get_chan_info,
|
17
|
+
_key: _key(grpc[:"node#{index}_pub"]),
|
18
|
+
public_key: grpc[:"node#{index}_pub"]
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.list_channels(grpc, key)
|
23
|
+
data = {
|
24
|
+
_source: :list_channels,
|
25
|
+
_key: _key(key == :remote ? grpc[:remote_pubkey] : nil),
|
26
|
+
public_key: key == :remote ? grpc[:remote_pubkey] : nil
|
27
|
+
}
|
28
|
+
|
29
|
+
return nil if data[:public_key].nil?
|
30
|
+
|
31
|
+
data
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.get_info(grpc)
|
35
|
+
data = {
|
36
|
+
_source: :get_info,
|
37
|
+
_key: _key(grpc[:identity_pubkey]),
|
38
|
+
public_key: grpc[:identity_pubkey],
|
39
|
+
alias: grpc[:alias],
|
40
|
+
color: grpc[:color],
|
41
|
+
platform: {
|
42
|
+
blockchain: grpc[:chains][0][:chain],
|
43
|
+
network: grpc[:chains][0][:network],
|
44
|
+
lightning: {
|
45
|
+
implementation: 'lnd',
|
46
|
+
version: grpc[:version]
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
return nil if data[:public_key].nil? && data[:alias].nil? && data[:color].nil?
|
52
|
+
|
53
|
+
data
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.get_node_info(grpc)
|
57
|
+
data = {
|
58
|
+
_source: :get_node_info,
|
59
|
+
_key: _key(grpc[:node][:pub_key]),
|
60
|
+
public_key: grpc[:node][:pub_key],
|
61
|
+
alias: grpc[:node][:alias],
|
62
|
+
color: grpc[:node][:color]
|
63
|
+
}
|
64
|
+
|
65
|
+
return nil if data[:public_key].nil? && data[:alias].nil? && data[:color].nil?
|
66
|
+
|
67
|
+
data
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.describe_graph(grpc)
|
71
|
+
data = {
|
72
|
+
_source: :describe_graph,
|
73
|
+
_key: _key(grpc[:pub_key]),
|
74
|
+
public_key: grpc[:pub_key],
|
75
|
+
alias: grpc[:alias],
|
76
|
+
color: grpc[:color]
|
77
|
+
}
|
78
|
+
|
79
|
+
return nil if data[:public_key].nil? && data[:alias].nil? && data[:color].nil?
|
80
|
+
|
81
|
+
data
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.describe_graph_from_channel(grpc, index)
|
85
|
+
data = {
|
86
|
+
_source: :describe_graph,
|
87
|
+
_key: _key(grpc[:"node#{index}_pub"]),
|
88
|
+
public_key: grpc[:"node#{index}_pub"]
|
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
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest'
|
4
|
+
|
5
|
+
require_relative '../ports/dsl/lighstorm/errors'
|
6
|
+
|
7
|
+
module Lighstorm
|
8
|
+
module Adapter
|
9
|
+
class PaymentRequest
|
10
|
+
def self.decode_pay_req(grpc)
|
11
|
+
{
|
12
|
+
_source: :decode_pay_req,
|
13
|
+
amount: { milisatoshis: grpc[:num_msat] },
|
14
|
+
description: {
|
15
|
+
memo: grpc[:description],
|
16
|
+
hash: grpc[:description_hash] == '' ? nil : grpc[:description_hash]
|
17
|
+
},
|
18
|
+
address: grpc[:payment_addr].unpack1('H*'),
|
19
|
+
secret: {
|
20
|
+
hash: grpc[:payment_hash]
|
21
|
+
}
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.lookup_invoice(grpc)
|
26
|
+
adapted = list_or_lookup_invoice(grpc)
|
27
|
+
adapted[:_source] = :lookup_invoice
|
28
|
+
adapted
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.list_invoices(grpc)
|
32
|
+
adapted = list_or_lookup_invoice(grpc)
|
33
|
+
adapted[:_source] = :list_invoices
|
34
|
+
adapted
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.list_or_lookup_invoice(grpc)
|
38
|
+
{
|
39
|
+
code: grpc[:payment_request],
|
40
|
+
amount: { milisatoshis: grpc[:value_msat] },
|
41
|
+
description: {
|
42
|
+
memo: grpc[:memo],
|
43
|
+
hash: grpc[:description_hash] == '' ? nil : grpc[:description_hash]
|
44
|
+
},
|
45
|
+
address: grpc[:payment_addr].unpack1('H*'),
|
46
|
+
secret: {
|
47
|
+
preimage: grpc[:r_preimage].unpack1('H*'),
|
48
|
+
hash: grpc[:r_hash].unpack1('H*')
|
49
|
+
}
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.list_payments(grpc)
|
54
|
+
raise UnexpectedNumberOfHTLCsError, "htlcs: #{grpc[:htlcs].size}" if grpc[:htlcs].size > 1
|
55
|
+
|
56
|
+
data = {
|
57
|
+
_source: :list_payments,
|
58
|
+
code: grpc[:payment_request],
|
59
|
+
amount: { milisatoshis: grpc[:value_msat] },
|
60
|
+
secret: {
|
61
|
+
preimage: grpc[:payment_preimage],
|
62
|
+
hash: grpc[:payment_hash]
|
63
|
+
}
|
64
|
+
}
|
65
|
+
|
66
|
+
grpc[:htlcs].first[:route][:hops].map do |raw_hop|
|
67
|
+
if raw_hop[:mpp_record] && raw_hop[:mpp_record][:payment_addr]
|
68
|
+
data[:address] = raw_hop[:mpp_record][:payment_addr].unpack1('H*')
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
data
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/components/cache.rb
CHANGED
@@ -5,6 +5,7 @@ require 'singleton'
|
|
5
5
|
require 'zache'
|
6
6
|
|
7
7
|
require_relative '../static/cache'
|
8
|
+
require_relative '../ports/dsl/lighstorm/errors'
|
8
9
|
|
9
10
|
module Lighstorm
|
10
11
|
class Cache
|
@@ -23,7 +24,7 @@ module Lighstorm
|
|
23
24
|
def for(key, ttl: nil, params: {}, &block)
|
24
25
|
if ttl.nil?
|
25
26
|
ttl = Lighstorm::Static::CACHE[key.sub('lightning.', '').to_sym]
|
26
|
-
raise "missing ttl for #{key}" if ttl.nil?
|
27
|
+
raise MissingTTLError, "missing ttl for #{key} static/cache.rb" if ttl.nil?
|
27
28
|
|
28
29
|
ttl = ttl[:ttl]
|
29
30
|
end
|
@@ -36,7 +37,7 @@ module Lighstorm
|
|
36
37
|
end
|
37
38
|
|
38
39
|
def build_key_for(key, params)
|
39
|
-
return key unless params.size.positive?
|
40
|
+
return key unless !params.nil? && params.size.positive?
|
40
41
|
|
41
42
|
key_params = []
|
42
43
|
params.keys.sort.each do |param_key|
|
data/components/lnd.rb
CHANGED
@@ -4,6 +4,8 @@ require 'singleton'
|
|
4
4
|
|
5
5
|
require 'lnd-client'
|
6
6
|
|
7
|
+
require_relative '../ports/dsl/lighstorm/errors'
|
8
|
+
|
7
9
|
module Lighstorm
|
8
10
|
class LND
|
9
11
|
include Singleton
|
@@ -23,7 +25,9 @@ module Lighstorm
|
|
23
25
|
def client
|
24
26
|
return @client if @client
|
25
27
|
|
26
|
-
raise 'missing credentials' if @config.nil? && ENV.fetch(
|
28
|
+
raise MissingCredentialsError, 'missing credentials' if @config.nil? && ENV.fetch(
|
29
|
+
'LIGHSTORM_CERTIFICATE_PATH', nil
|
30
|
+
).nil?
|
27
31
|
|
28
32
|
@client = if @config
|
29
33
|
create_client_from_config
|