lighstorm 0.0.9 → 0.0.11
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/Gemfile.lock +2 -2
- data/README.md +10 -3
- data/adapters/invoice.rb +2 -2
- data/controllers/invoice/actions/create.rb +4 -4
- data/controllers/invoice/actions/pay.rb +17 -9
- data/controllers/invoice/decode.rb +6 -6
- data/controllers/invoice/find_by_code.rb +70 -0
- data/controllers/invoice/find_by_secret_hash.rb +5 -3
- data/controllers/invoice.rb +11 -6
- data/controllers/node/actions/pay.rb +10 -5
- data/controllers/payment/actions/pay.rb +4 -4
- data/controllers/payment/all.rb +49 -16
- data/controllers/payment.rb +8 -0
- data/docs/README.md +82 -26
- data/docs/_coverpage.md +1 -1
- data/docs/index.html +1 -1
- data/models/errors.rb +16 -1
- data/models/invoice.rb +5 -3
- data/models/nodes/node.rb +7 -4
- data/models/secret.rb +15 -1
- data/static/spec.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6072337300f3fd850fbca3ba5c7c804842c275d5f35fa288dfb83747fa0b1a56
|
4
|
+
data.tar.gz: 8a16d83cb96f0ed911ebd55e12707b20f0aac00a0acac8d176e544a68abad647
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3ccb6cef3322fcd1c0efb1439ddaf756e631113fbc7544218d10d6f71d08ca2ad80cf08b029ae4145e6916dcefc9ee3683124567bb952b02bf70793cc794616b
|
7
|
+
data.tar.gz: 2755ec9d8c2bf041459be7292983103c880f032e5e1e315ce2da2cc97ff945bbfb9f4083259a0c88ec22df8cd0301282c8ea376d63371a438ad58677b7f3d9e6
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
lighstorm (0.0.
|
4
|
+
lighstorm (0.0.11)
|
5
5
|
dotenv (~> 2.8, >= 2.8.1)
|
6
6
|
lnd-client (~> 0.0.5)
|
7
7
|
zache (~> 0.12.0)
|
@@ -47,7 +47,7 @@ GEM
|
|
47
47
|
rspec-expectations (3.12.2)
|
48
48
|
diff-lcs (>= 1.2.0, < 2.0)
|
49
49
|
rspec-support (~> 3.12.0)
|
50
|
-
rspec-mocks (3.12.
|
50
|
+
rspec-mocks (3.12.4)
|
51
51
|
diff-lcs (>= 1.2.0, < 2.0)
|
52
52
|
rspec-support (~> 3.12.0)
|
53
53
|
rspec-support (3.12.0)
|
data/README.md
CHANGED
@@ -34,7 +34,7 @@ Although it tries to stay close to [Lightning's terminologies](https://docs.ligh
|
|
34
34
|
Add to your `Gemfile`:
|
35
35
|
|
36
36
|
```ruby
|
37
|
-
gem 'lighstorm', '~> 0.0.
|
37
|
+
gem 'lighstorm', '~> 0.0.11'
|
38
38
|
```
|
39
39
|
|
40
40
|
```ruby
|
@@ -46,15 +46,22 @@ Lighstorm.config!(
|
|
46
46
|
macaroon_path: '/lnd/data/chain/bitcoin/mainnet/admin.macaroon',
|
47
47
|
)
|
48
48
|
|
49
|
-
puts Lighstorm.version # => 0.0.
|
49
|
+
puts Lighstorm.version # => 0.0.11
|
50
50
|
|
51
51
|
Lighstorm::Node.myself.alias # => icebaker/old-stone
|
52
52
|
|
53
53
|
Lighstorm::Invoice.create(
|
54
|
-
description: 'Coffee',
|
54
|
+
description: 'Coffee',
|
55
|
+
amount: { millisatoshis: 1_000 },
|
55
56
|
payable: 'once'
|
56
57
|
)
|
57
58
|
|
59
|
+
Lighstorm::Invoice.decode('lnbc20m1pv...qqdhhwkj').pay
|
60
|
+
|
61
|
+
Lighstorm::Invoice.decode('lnbc20m1pv...qqdhhwkj').pay(
|
62
|
+
fee: { maximum: { millisatoshis: 1000 } }
|
63
|
+
)
|
64
|
+
|
58
65
|
Lighstorm::Satoshis.new(
|
59
66
|
millisatoshis: 75_621_650
|
60
67
|
).satoshis # => 75_621
|
data/adapters/invoice.rb
CHANGED
@@ -18,7 +18,7 @@ module Lighstorm
|
|
18
18
|
}
|
19
19
|
end
|
20
20
|
|
21
|
-
def self.decode_pay_req(grpc,
|
21
|
+
def self.decode_pay_req(grpc, code = nil)
|
22
22
|
adapted = {
|
23
23
|
_source: :decode_pay_req,
|
24
24
|
_key: Digest::SHA256.hexdigest(
|
@@ -43,7 +43,7 @@ module Lighstorm
|
|
43
43
|
}
|
44
44
|
}
|
45
45
|
|
46
|
-
adapted[:code] =
|
46
|
+
adapted[:code] = code unless code.nil?
|
47
47
|
|
48
48
|
if grpc[:features].key?(30) && grpc[:features][30][:is_required]
|
49
49
|
raise "unexpected feature[30] name #{grpc[:features][30][:name]}" if grpc[:features][30][:name] != 'amp'
|
@@ -16,7 +16,7 @@ module Lighstorm
|
|
16
16
|
).to_h
|
17
17
|
end
|
18
18
|
|
19
|
-
def self.prepare(payable:, expires_in:, description: nil,
|
19
|
+
def self.prepare(payable:, expires_in:, description: nil, amount: nil)
|
20
20
|
request = {
|
21
21
|
service: :lightning,
|
22
22
|
method: :add_invoice,
|
@@ -28,7 +28,7 @@ module Lighstorm
|
|
28
28
|
}
|
29
29
|
}
|
30
30
|
|
31
|
-
request[:params][:value_msat] = millisatoshis unless
|
31
|
+
request[:params][:value_msat] = amount[:millisatoshis] unless amount.nil?
|
32
32
|
|
33
33
|
if payable.to_sym == :indefinitely
|
34
34
|
request[:params][:is_amp] = true
|
@@ -55,10 +55,10 @@ module Lighstorm
|
|
55
55
|
FindBySecretHash.model(data)
|
56
56
|
end
|
57
57
|
|
58
|
-
def self.perform(payable:, expires_in:, description: nil,
|
58
|
+
def self.perform(payable:, expires_in:, description: nil, amount: nil, preview: false, &vcr)
|
59
59
|
grpc_request = prepare(
|
60
60
|
description: description,
|
61
|
-
|
61
|
+
amount: amount,
|
62
62
|
expires_in: expires_in,
|
63
63
|
payable: payable
|
64
64
|
)
|
@@ -18,8 +18,8 @@ module Lighstorm
|
|
18
18
|
Payment::Pay.dispatch(grpc_request, &vcr)
|
19
19
|
end
|
20
20
|
|
21
|
-
def self.fetch(
|
22
|
-
Payment::Pay.fetch(
|
21
|
+
def self.fetch(code, &vcr)
|
22
|
+
Payment::Pay.fetch(code, &vcr)
|
23
23
|
end
|
24
24
|
|
25
25
|
def self.adapt(data, node_get_info)
|
@@ -30,19 +30,23 @@ module Lighstorm
|
|
30
30
|
Payment::Pay.model(data)
|
31
31
|
end
|
32
32
|
|
33
|
-
def self.prepare(
|
33
|
+
def self.prepare(code:, times_out_in:, amount: nil, fee: nil, message: nil)
|
34
34
|
request = {
|
35
35
|
service: :router,
|
36
36
|
method: :send_payment_v2,
|
37
37
|
params: {
|
38
|
-
payment_request:
|
38
|
+
payment_request: code,
|
39
39
|
timeout_seconds: Helpers::TimeExpression.seconds(times_out_in),
|
40
40
|
allow_self_payment: true,
|
41
41
|
dest_custom_records: {}
|
42
42
|
}
|
43
43
|
}
|
44
44
|
|
45
|
-
request[:params][:amt_msat] = millisatoshis unless
|
45
|
+
request[:params][:amt_msat] = amount[:millisatoshis] unless amount.nil?
|
46
|
+
|
47
|
+
unless fee.nil? || fee[:maximum].nil? || fee[:maximum][:millisatoshis].nil?
|
48
|
+
request[:params][:fee_limit_msat] = fee[:maximum][:millisatoshis]
|
49
|
+
end
|
46
50
|
|
47
51
|
if !message.nil? && !message.empty?
|
48
52
|
# https://github.com/satoshisstream/satoshis.stream/blob/main/TLV_registry.md
|
@@ -55,11 +59,15 @@ module Lighstorm
|
|
55
59
|
end
|
56
60
|
|
57
61
|
def self.perform(
|
58
|
-
times_out_in:,
|
62
|
+
times_out_in:, code:,
|
63
|
+
amount: nil, fee: nil,
|
64
|
+
message: nil,
|
65
|
+
preview: false, &vcr
|
59
66
|
)
|
60
67
|
grpc_request = prepare(
|
61
|
-
|
62
|
-
|
68
|
+
code: code,
|
69
|
+
amount: amount,
|
70
|
+
fee: fee,
|
63
71
|
message: message,
|
64
72
|
times_out_in: times_out_in
|
65
73
|
)
|
@@ -70,7 +78,7 @@ module Lighstorm
|
|
70
78
|
|
71
79
|
Payment::Pay.raise_error_if_exists!(response)
|
72
80
|
|
73
|
-
data = fetch(
|
81
|
+
data = fetch(code, &vcr)
|
74
82
|
|
75
83
|
adapted = adapt(response, data)
|
76
84
|
|
@@ -8,17 +8,17 @@ module Lighstorm
|
|
8
8
|
module Controllers
|
9
9
|
module Invoice
|
10
10
|
module Decode
|
11
|
-
def self.fetch(
|
11
|
+
def self.fetch(code)
|
12
12
|
{
|
13
|
-
|
14
|
-
decode_pay_req: Ports::GRPC.lightning.decode_pay_req(pay_req:
|
13
|
+
_code: code,
|
14
|
+
decode_pay_req: Ports::GRPC.lightning.decode_pay_req(pay_req: code).to_h
|
15
15
|
}
|
16
16
|
end
|
17
17
|
|
18
18
|
def self.adapt(raw)
|
19
19
|
{
|
20
20
|
decode_pay_req: Lighstorm::Adapter::Invoice.decode_pay_req(
|
21
|
-
raw[:decode_pay_req], raw[:
|
21
|
+
raw[:decode_pay_req], raw[:_code]
|
22
22
|
)
|
23
23
|
}
|
24
24
|
end
|
@@ -27,8 +27,8 @@ module Lighstorm
|
|
27
27
|
adapted[:decode_pay_req]
|
28
28
|
end
|
29
29
|
|
30
|
-
def self.data(
|
31
|
-
raw = vcr.nil? ? fetch(
|
30
|
+
def self.data(code, &vcr)
|
31
|
+
raw = vcr.nil? ? fetch(code) : vcr.call(-> { fetch(code) })
|
32
32
|
|
33
33
|
adapted = adapt(raw)
|
34
34
|
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../ports/grpc'
|
4
|
+
require_relative '../../adapters/invoice'
|
5
|
+
require_relative '../../models/invoice'
|
6
|
+
|
7
|
+
module Lighstorm
|
8
|
+
module Controllers
|
9
|
+
module Invoice
|
10
|
+
module FindByCode
|
11
|
+
def self.fetch(code)
|
12
|
+
at = Time.now
|
13
|
+
|
14
|
+
decoded = Ports::GRPC.lightning.decode_pay_req(pay_req: code).to_h
|
15
|
+
|
16
|
+
{ response: {
|
17
|
+
at: at,
|
18
|
+
decode_pay_req: decoded,
|
19
|
+
lookup_invoice: Ports::GRPC.lightning.lookup_invoice(r_hash_str: decoded[:payment_hash]).to_h
|
20
|
+
}, exception: nil }
|
21
|
+
rescue StandardError => e
|
22
|
+
{ exception: e }
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.adapt(raw)
|
26
|
+
raise 'missing at' if raw[:at].nil?
|
27
|
+
|
28
|
+
{
|
29
|
+
lookup_invoice: Lighstorm::Adapter::Invoice.lookup_invoice(
|
30
|
+
raw[:lookup_invoice],
|
31
|
+
raw[:at]
|
32
|
+
)
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.transform(adapted)
|
37
|
+
adapted[:lookup_invoice][:known] = true
|
38
|
+
adapted[:lookup_invoice]
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.data(code, &vcr)
|
42
|
+
raw = vcr.nil? ? fetch(code) : vcr.call(-> { fetch(code) })
|
43
|
+
|
44
|
+
raise_error_if_exists!(raw)
|
45
|
+
|
46
|
+
adapted = adapt(raw[:response])
|
47
|
+
|
48
|
+
transform(adapted)
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.model(data)
|
52
|
+
Lighstorm::Models::Invoice.new(data)
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.raise_error_if_exists!(response)
|
56
|
+
return if response[:exception].nil?
|
57
|
+
|
58
|
+
if response[:exception].is_a?(GRPC::NotFound)
|
59
|
+
raise NoInvoiceFoundError.new(
|
60
|
+
"Invoice not found. Try using Invoice.decode if you don't own the invoice.",
|
61
|
+
grpc: response[:exception]
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
raise LighstormError.new(grpc: response[:exception])
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -9,10 +9,12 @@ module Lighstorm
|
|
9
9
|
module Invoice
|
10
10
|
module FindBySecretHash
|
11
11
|
def self.fetch(secret_hash)
|
12
|
-
{
|
12
|
+
{ response: {
|
13
13
|
at: Time.now,
|
14
14
|
lookup_invoice: Ports::GRPC.lightning.lookup_invoice(r_hash_str: secret_hash).to_h
|
15
|
-
}
|
15
|
+
}, exception: nil }
|
16
|
+
rescue StandardError => e
|
17
|
+
{ exception: e }
|
16
18
|
end
|
17
19
|
|
18
20
|
def self.adapt(raw)
|
@@ -34,7 +36,7 @@ module Lighstorm
|
|
34
36
|
def self.data(secret_hash, &vcr)
|
35
37
|
raw = vcr.nil? ? fetch(secret_hash) : vcr.call(-> { fetch(secret_hash) })
|
36
38
|
|
37
|
-
adapted = adapt(raw)
|
39
|
+
adapted = adapt(raw[:response])
|
38
40
|
|
39
41
|
transform(adapted)
|
40
42
|
end
|
data/controllers/invoice.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require_relative './invoice/all'
|
4
4
|
require_relative './invoice/decode'
|
5
5
|
require_relative './invoice/find_by_secret_hash'
|
6
|
+
require_relative './invoice/find_by_code'
|
6
7
|
require_relative './invoice/actions/create'
|
7
8
|
|
8
9
|
module Lighstorm
|
@@ -20,17 +21,21 @@ module Lighstorm
|
|
20
21
|
All.model(All.data).last
|
21
22
|
end
|
22
23
|
|
23
|
-
def self.find_by_secret_hash(secret_hash)
|
24
|
-
FindBySecretHash.model(FindBySecretHash.data(secret_hash))
|
24
|
+
def self.find_by_secret_hash(secret_hash, &vcr)
|
25
|
+
FindBySecretHash.model(FindBySecretHash.data(secret_hash, &vcr))
|
25
26
|
end
|
26
27
|
|
27
|
-
def self.
|
28
|
-
|
28
|
+
def self.find_by_code(code, &vcr)
|
29
|
+
FindByCode.model(FindByCode.data(code, &vcr))
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.decode(code, &vcr)
|
33
|
+
Decode.model(Decode.data(code, &vcr))
|
29
34
|
end
|
30
35
|
|
31
36
|
def self.create(
|
32
37
|
payable:,
|
33
|
-
description: nil,
|
38
|
+
description: nil, amount: nil,
|
34
39
|
# Lightning Invoice Expiration: UX Considerations
|
35
40
|
# https://d.elor.me/2022/01/lightning-invoice-expiration-ux-considerations/
|
36
41
|
expires_in: { hours: 24 },
|
@@ -39,7 +44,7 @@ module Lighstorm
|
|
39
44
|
Create.perform(
|
40
45
|
payable: payable,
|
41
46
|
description: description,
|
42
|
-
|
47
|
+
amount: amount,
|
43
48
|
expires_in: expires_in,
|
44
49
|
preview: preview,
|
45
50
|
&vcr
|
@@ -34,7 +34,7 @@ module Lighstorm
|
|
34
34
|
Payment::Pay.model(data)
|
35
35
|
end
|
36
36
|
|
37
|
-
def self.prepare(public_key:,
|
37
|
+
def self.prepare(public_key:, amount:, times_out_in:, secret:, through:, fee: nil, message: nil)
|
38
38
|
# Appreciation note for people that suffered in the past and shared
|
39
39
|
# their knowledge, so we don't have to struggle the same:
|
40
40
|
# - https://github.com/lightningnetwork/lnd/discussions/6357
|
@@ -46,7 +46,7 @@ module Lighstorm
|
|
46
46
|
method: :send_payment_v2,
|
47
47
|
params: {
|
48
48
|
dest: [public_key].pack('H*'),
|
49
|
-
amt_msat: millisatoshis,
|
49
|
+
amt_msat: amount[:millisatoshis],
|
50
50
|
timeout_seconds: Helpers::TimeExpression.seconds(times_out_in),
|
51
51
|
allow_self_payment: true,
|
52
52
|
dest_custom_records: {}
|
@@ -58,6 +58,10 @@ module Lighstorm
|
|
58
58
|
request[:params][:dest_custom_records][34_349_334] = message
|
59
59
|
end
|
60
60
|
|
61
|
+
unless fee.nil? || fee[:maximum].nil? || fee[:maximum][:millisatoshis].nil?
|
62
|
+
request[:params][:fee_limit_msat] = fee[:maximum][:millisatoshis]
|
63
|
+
end
|
64
|
+
|
61
65
|
if through.to_sym == :keysend
|
62
66
|
request[:params][:payment_hash] = [secret[:hash]].pack('H*')
|
63
67
|
request[:params][:dest_custom_records][5_482_373_484] = [secret[:preimage]].pack('H*')
|
@@ -71,8 +75,8 @@ module Lighstorm
|
|
71
75
|
end
|
72
76
|
|
73
77
|
def self.perform(
|
74
|
-
public_key:,
|
75
|
-
times_out_in:,
|
78
|
+
public_key:, amount:, through:,
|
79
|
+
times_out_in:, fee: nil,
|
76
80
|
message: nil, secret: nil,
|
77
81
|
preview: false, &vcr
|
78
82
|
)
|
@@ -80,7 +84,8 @@ module Lighstorm
|
|
80
84
|
|
81
85
|
grpc_request = prepare(
|
82
86
|
public_key: public_key,
|
83
|
-
|
87
|
+
amount: amount,
|
88
|
+
fee: fee,
|
84
89
|
through: through,
|
85
90
|
times_out_in: times_out_in,
|
86
91
|
secret: secret,
|
@@ -30,15 +30,15 @@ module Lighstorm
|
|
30
30
|
vcr.nil? ? call(grpc_request) : vcr.call(-> { call(grpc_request) }, :dispatch)
|
31
31
|
end
|
32
32
|
|
33
|
-
def self.fetch_all(
|
33
|
+
def self.fetch_all(code)
|
34
34
|
{
|
35
|
-
invoice_decode:
|
35
|
+
invoice_decode: code.nil? ? nil : Invoice::Decode.data(code),
|
36
36
|
node_myself: Node::Myself.data
|
37
37
|
}
|
38
38
|
end
|
39
39
|
|
40
|
-
def self.fetch(
|
41
|
-
raw = vcr.nil? ? fetch_all(
|
40
|
+
def self.fetch(code = nil, &vcr)
|
41
|
+
raw = vcr.nil? ? fetch_all(code) : vcr.call(-> { fetch_all(code) })
|
42
42
|
end
|
43
43
|
|
44
44
|
def self.adapt(grpc_data, fetch_data)
|
data/controllers/payment/all.rb
CHANGED
@@ -12,7 +12,7 @@ module Lighstorm
|
|
12
12
|
module Controllers
|
13
13
|
module Payment
|
14
14
|
module All
|
15
|
-
def self.fetch(purpose: nil, limit: nil, fetch: {})
|
15
|
+
def self.fetch(purpose: nil, invoice_code: nil, secret_hash: nil, limit: nil, fetch: {})
|
16
16
|
at = Time.now
|
17
17
|
|
18
18
|
grpc = Ports::GRPC.session
|
@@ -31,6 +31,10 @@ module Lighstorm
|
|
31
31
|
response.payments.each do |payment|
|
32
32
|
payment = payment.to_h
|
33
33
|
|
34
|
+
next unless invoice_code.nil? || payment[:payment_request] == invoice_code
|
35
|
+
|
36
|
+
next unless secret_hash.nil? || payment[:payment_hash] == secret_hash
|
37
|
+
|
34
38
|
payment_purpose = Adapter::Purpose.list_payments(payment, get_info)
|
35
39
|
|
36
40
|
case purpose
|
@@ -108,9 +112,13 @@ module Lighstorm
|
|
108
112
|
|
109
113
|
next if fetch[:get_node_info] == false || data[:get_node_info][hop[:pub_key]]
|
110
114
|
|
111
|
-
|
112
|
-
|
113
|
-
|
115
|
+
begin
|
116
|
+
data[:get_node_info][hop[:pub_key]] = grpc.lightning.get_node_info(
|
117
|
+
pub_key: hop[:pub_key]
|
118
|
+
).to_h
|
119
|
+
rescue StandardError => e
|
120
|
+
data[:get_node_info][hop[:pub_key]] = { _error: e }
|
121
|
+
end
|
114
122
|
end
|
115
123
|
end
|
116
124
|
end
|
@@ -151,24 +159,36 @@ module Lighstorm
|
|
151
159
|
end
|
152
160
|
|
153
161
|
unless fetch[:get_node_info] == false || data[:get_node_info][channel[:node1_pub]]
|
154
|
-
|
155
|
-
|
156
|
-
|
162
|
+
begin
|
163
|
+
data[:get_node_info][channel[:node1_pub]] = grpc.lightning.get_node_info(
|
164
|
+
pub_key: channel[:node1_pub]
|
165
|
+
).to_h
|
166
|
+
rescue StandardError => e
|
167
|
+
data[:get_node_info][channel[:node1_pub]] = { _error: e }
|
168
|
+
end
|
157
169
|
end
|
158
170
|
|
159
171
|
next if fetch[:get_node_info] == false || data[:get_node_info][channel[:node2_pub]]
|
160
172
|
|
161
|
-
|
162
|
-
|
163
|
-
|
173
|
+
begin
|
174
|
+
data[:get_node_info][channel[:node2_pub]] = grpc.lightning.get_node_info(
|
175
|
+
pub_key: channel[:node2_pub]
|
176
|
+
).to_h
|
177
|
+
rescue StandardError => e
|
178
|
+
data[:get_node_info][channel[:node2_pub]] = { _error: e }
|
179
|
+
end
|
164
180
|
end
|
165
181
|
|
166
182
|
data[:list_channels].each_value do |channel|
|
167
183
|
next if fetch[:get_node_info] == false || data[:get_node_info][channel[:remote_pubkey]]
|
168
184
|
|
169
|
-
|
170
|
-
|
171
|
-
|
185
|
+
begin
|
186
|
+
data[:get_node_info][channel[:remote_pubkey]] = grpc.lightning.get_node_info(
|
187
|
+
pub_key: channel[:remote_pubkey]
|
188
|
+
).to_h
|
189
|
+
rescue StandardError => e
|
190
|
+
data[:get_node_info][channel[:remote_pubkey]] = { _error: e }
|
191
|
+
end
|
172
192
|
end
|
173
193
|
|
174
194
|
data[:@meta] = { calls: grpc.calls }
|
@@ -223,6 +243,8 @@ module Lighstorm
|
|
223
243
|
end
|
224
244
|
|
225
245
|
raw[:get_node_info].each_key do |key|
|
246
|
+
next if raw[:get_node_info][key][:_error]
|
247
|
+
|
226
248
|
adapted[:get_node_info][key] = Lighstorm::Adapter::Node.get_node_info(
|
227
249
|
raw[:get_node_info][key]
|
228
250
|
)
|
@@ -373,11 +395,22 @@ module Lighstorm
|
|
373
395
|
list_payments
|
374
396
|
end
|
375
397
|
|
376
|
-
def self.data(
|
398
|
+
def self.data(
|
399
|
+
purpose: nil, invoice_code: nil, secret_hash: nil, limit: nil,
|
400
|
+
fetch: {}, &vcr
|
401
|
+
)
|
377
402
|
raw = if vcr.nil?
|
378
|
-
self.fetch(
|
403
|
+
self.fetch(
|
404
|
+
purpose: purpose, invoice_code: invoice_code, secret_hash: secret_hash,
|
405
|
+
limit: limit, fetch: fetch
|
406
|
+
)
|
379
407
|
else
|
380
|
-
vcr.call(
|
408
|
+
vcr.call(lambda {
|
409
|
+
self.fetch(
|
410
|
+
purpose: purpose, invoice_code: invoice_code, secret_hash: secret_hash,
|
411
|
+
limit: limit, fetch: fetch
|
412
|
+
)
|
413
|
+
})
|
381
414
|
end
|
382
415
|
|
383
416
|
adapted = adapt(raw)
|
data/controllers/payment.rb
CHANGED
@@ -16,6 +16,14 @@ module Lighstorm
|
|
16
16
|
def self.last(purpose: nil, fetch: {})
|
17
17
|
All.model(All.data(purpose: purpose, fetch: fetch)).last
|
18
18
|
end
|
19
|
+
|
20
|
+
def self.find_by_secret_hash(secret_hash, &vcr)
|
21
|
+
All.model(All.data(secret_hash: secret_hash, &vcr)).first
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.find_by_invoice_code(invoice_code, &vcr)
|
25
|
+
All.model(All.data(invoice_code: invoice_code, &vcr)).first
|
26
|
+
end
|
19
27
|
end
|
20
28
|
end
|
21
29
|
end
|
data/docs/README.md
CHANGED
@@ -27,7 +27,7 @@ Lighstorm::Channel.mine.first.myself.node.alias
|
|
27
27
|
Add to your `Gemfile`:
|
28
28
|
|
29
29
|
```ruby
|
30
|
-
gem 'lighstorm', '~> 0.0.
|
30
|
+
gem 'lighstorm', '~> 0.0.11'
|
31
31
|
```
|
32
32
|
|
33
33
|
Run `bundle install`.
|
@@ -60,23 +60,25 @@ Lighstorm.config!(
|
|
60
60
|
```ruby
|
61
61
|
require 'lighstorm'
|
62
62
|
|
63
|
-
puts Lighstorm.version # => 0.0.
|
63
|
+
puts Lighstorm.version # => 0.0.11
|
64
64
|
|
65
65
|
Lighstorm::Invoice.create(
|
66
|
-
description: 'Coffee', millisatoshis: 1000, payable: 'once'
|
66
|
+
description: 'Coffee', amount: { millisatoshis: 1000 }, payable: 'once'
|
67
67
|
)
|
68
68
|
|
69
|
-
Lighstorm::Invoice.decode(
|
70
|
-
|
71
|
-
).pay
|
69
|
+
Lighstorm::Invoice.decode('lnbc20m1pv...qqdhhwkj').pay
|
70
|
+
|
71
|
+
Lighstorm::Invoice.decode('lnbc20m1pv...qqdhhwkj').pay(
|
72
|
+
fee: { maximum: { millisatoshis: 1000 } }
|
73
|
+
)
|
72
74
|
|
73
75
|
Lighstorm::Node.find_by_public_key(
|
74
76
|
'02d3c80335a8ccb2ed364c06875f32240f36f7edb37d80f8dbe321b4c364b6e997'
|
75
|
-
).pay(millisatoshis: 1000)
|
77
|
+
).pay(amount: { millisatoshis: 1000 })
|
76
78
|
|
77
79
|
Lighstorm::Node.find_by_public_key(
|
78
80
|
'02d3c80335a8ccb2ed364c06875f32240f36f7edb37d80f8dbe321b4c364b6e997'
|
79
|
-
).send_message('Hello from Lighstorm!', millisatoshis: 1000)
|
81
|
+
).send_message('Hello from Lighstorm!', amount: { millisatoshis: 1000 })
|
80
82
|
|
81
83
|
Lighstorm::Node.myself.alias # => icebaker/old-stone
|
82
84
|
Lighstorm::Node.myself.public_key # => 02d3...e997
|
@@ -264,23 +266,25 @@ destination = Lighstorm::Node.find_by_public_key(
|
|
264
266
|
|
265
267
|
destination.alias # => 'icebaker/old-stone'
|
266
268
|
|
267
|
-
destination.pay(millisatoshis: 1000)
|
269
|
+
destination.pay(amount: { millisatoshis: 1000 })
|
268
270
|
|
269
271
|
destination.pay(
|
270
|
-
millisatoshis: 1500,
|
272
|
+
amount: { millisatoshis: 1500 },
|
273
|
+
fee: { maximum: { millisatoshis: 1000 } },
|
271
274
|
message: 'Hello from Lighstorm!',
|
272
275
|
through: 'amp',
|
273
276
|
times_out_in: { seconds: 5 }
|
274
277
|
)
|
275
278
|
|
276
279
|
destination.pay(
|
277
|
-
millisatoshis: 1200,
|
280
|
+
amount: { millisatoshis: 1200 },
|
281
|
+
fee: { maximum: { millisatoshis: 1000 } },
|
278
282
|
message: 'Hello from Lighstorm!',
|
279
283
|
through: 'keysend',
|
280
284
|
times_out_in: { seconds: 5 }
|
281
285
|
)
|
282
286
|
|
283
|
-
action = destination.pay(millisatoshis: 1000)
|
287
|
+
action = destination.pay(amount: { millisatoshis: 1000 })
|
284
288
|
action.result.fee.millisatoshis
|
285
289
|
```
|
286
290
|
|
@@ -295,23 +299,31 @@ destination = Lighstorm::Node.find_by_public_key(
|
|
295
299
|
|
296
300
|
destination.alias # => 'icebaker/old-stone'
|
297
301
|
|
298
|
-
destination.send_message(
|
302
|
+
destination.send_message(
|
303
|
+
'Hello from Lighstorm!',
|
304
|
+
amount: { millisatoshis: 1000 }
|
305
|
+
)
|
299
306
|
|
300
307
|
destination.send_message(
|
301
308
|
'Hello from Lighstorm!',
|
302
|
-
millisatoshis: 1000,
|
309
|
+
amount: { millisatoshis: 1000 },
|
310
|
+
fee: { maximum: { millisatoshis: 1000 } },
|
303
311
|
through: 'amp',
|
304
312
|
times_out_in: { seconds: 5 }
|
305
313
|
)
|
306
314
|
|
307
315
|
destination.send_message(
|
308
316
|
'Hello from Lighstorm!',
|
309
|
-
millisatoshis: 1000,
|
317
|
+
amount: { millisatoshis: 1000 },
|
318
|
+
fee: { maximum: { millisatoshis: 1000 } },
|
310
319
|
through: 'keysend',
|
311
320
|
times_out_in: { seconds: 5 }
|
312
321
|
)
|
313
322
|
|
314
|
-
action = destination.send_message(
|
323
|
+
action = destination.send_message(
|
324
|
+
'Hello from Lighstorm!',
|
325
|
+
amount: { millisatoshis: 1000 }
|
326
|
+
)
|
315
327
|
action.result.fee.millisatoshis
|
316
328
|
```
|
317
329
|
|
@@ -450,6 +462,12 @@ Lighstorm::Invoice.find_by_secret_hash(
|
|
450
462
|
'1d438b8100518c9fba0a607e3317d6b36f74ceef3a6591836eb2f679c6853501'
|
451
463
|
)
|
452
464
|
|
465
|
+
invoice = Lighstorm::Invoice.find_by_code('lnbc20n1pj...0eqps7h0k9')
|
466
|
+
|
467
|
+
invoice.secret.valid_proof?(
|
468
|
+
'c504f73f83e3772b802844b54021e44e071c03011eeda476b198f7a093bcb09e'
|
469
|
+
) # => true
|
470
|
+
|
453
471
|
# _key is helpful for reactive javascript frameworks.
|
454
472
|
# Please don't consider it as a unique identifier
|
455
473
|
# for the item. Instead, use it as a volatile key for
|
@@ -485,12 +503,12 @@ invoice.secret.hash
|
|
485
503
|
# 'preview' let you check the expected operation
|
486
504
|
# before actually performing it for debug purposes
|
487
505
|
preview = Lighstorm::Invoice.create(
|
488
|
-
description: 'Coffee', millisatoshis: 1000,
|
506
|
+
description: 'Coffee', amount: { millisatoshis: 1000 },
|
489
507
|
payable: 'once', preview: true
|
490
508
|
)
|
491
509
|
|
492
510
|
action = Lighstorm::Invoice.create(
|
493
|
-
description: 'Coffee', millisatoshis: 1000,
|
511
|
+
description: 'Coffee', amount: { millisatoshis: 1000 },
|
494
512
|
payable: 'once', expires_in: { minutes: 5 }
|
495
513
|
)
|
496
514
|
|
@@ -504,7 +522,7 @@ action = Lighstorm::Invoice.create(
|
|
504
522
|
)
|
505
523
|
|
506
524
|
action = Lighstorm::Invoice.create(
|
507
|
-
description: 'Concert Ticket', millisatoshis: 500000000,
|
525
|
+
description: 'Concert Ticket', amount: { millisatoshis: 500000000 },
|
508
526
|
payable: 'indefinitely', expires_in: { days: 5 }
|
509
527
|
)
|
510
528
|
|
@@ -514,6 +532,19 @@ action.response
|
|
514
532
|
invoice = action.result
|
515
533
|
```
|
516
534
|
|
535
|
+
### Proof of Payment
|
536
|
+
|
537
|
+
[Making Payments](https://docs.lightning.engineering/the-lightning-network/multihop-payments)
|
538
|
+
|
539
|
+
|
540
|
+
```ruby
|
541
|
+
invoice = Lighstorm::Invoice.find_by_code('lnbc20n1pj...0eqps7h0k9')
|
542
|
+
|
543
|
+
invoice.secret.valid_proof?(
|
544
|
+
'c504f73f83e3772b802844b54021e44e071c03011eeda476b198f7a093bcb09e'
|
545
|
+
) # => true
|
546
|
+
```
|
547
|
+
|
517
548
|
### Pay
|
518
549
|
|
519
550
|
[Understanding Lightning Invoices](https://docs.lightning.engineering/the-lightning-network/payment-lifecycle/understanding-lightning-invoices)
|
@@ -535,6 +566,8 @@ payment = action.result
|
|
535
566
|
payment.at
|
536
567
|
payment.state
|
537
568
|
|
569
|
+
payment.secret.proof
|
570
|
+
|
538
571
|
payment.amount.millisatoshis
|
539
572
|
payment.fee.millisatoshis
|
540
573
|
payment.fee.parts_per_million(
|
@@ -547,7 +580,8 @@ payment.hops.size
|
|
547
580
|
|
548
581
|
```ruby
|
549
582
|
invoice.pay(
|
550
|
-
millisatoshis: 1500,
|
583
|
+
amount: { millisatoshis: 1500 },
|
584
|
+
fee: { maximum: { millisatoshis: 1000 } },
|
551
585
|
message: 'here we go',
|
552
586
|
times_out_in: { seconds: 5 }
|
553
587
|
)
|
@@ -568,7 +602,7 @@ end
|
|
568
602
|
|
569
603
|
```ruby
|
570
604
|
begin
|
571
|
-
invoice.pay(millisatoshis: 1000)
|
605
|
+
invoice.pay(amount: { millisatoshis: 1000 })
|
572
606
|
rescue AmountForNonZeroError => error
|
573
607
|
error.message # 'Millisatoshis must not be specified...'
|
574
608
|
error.grpc.class # GRPC::Unknown
|
@@ -609,6 +643,7 @@ rescue PaymentError => error
|
|
609
643
|
error.result
|
610
644
|
end
|
611
645
|
```
|
646
|
+
|
612
647
|
## Payment
|
613
648
|
|
614
649
|
[](https://raw.githubusercontent.com/icebaker/assets/main/lighstorm/graph-payment.png)
|
@@ -629,6 +664,12 @@ Lighstorm::Payment.all(limit: 10, purpose: 'rebalance')
|
|
629
664
|
# 'self-payment', 'peer-to-peer',
|
630
665
|
# 'rebalance', 'payment'
|
631
666
|
|
667
|
+
Lighstorm::Payment.find_by_invoice_code('lnbc20n1pj...0eqps7h0k9')
|
668
|
+
|
669
|
+
Lighstorm::Payment.find_by_secret_hash(
|
670
|
+
'1d438b8100518c9fba0a607e3317d6b36f74ceef3a6591836eb2f679c6853501'
|
671
|
+
)
|
672
|
+
|
632
673
|
# _key is helpful for reactive javascript frameworks.
|
633
674
|
# Please don't consider it as a unique identifier
|
634
675
|
# for the item. Instead, use it as a volatile key for
|
@@ -658,6 +699,7 @@ payment.purpose
|
|
658
699
|
# https://docs.lightning.engineering/the-lightning-network/multihop-payments
|
659
700
|
payment.secret.preimage
|
660
701
|
payment.secret.hash
|
702
|
+
payment.secret.proof
|
661
703
|
|
662
704
|
payment.invoice.created_at
|
663
705
|
payment.invoice.expires_at
|
@@ -734,6 +776,18 @@ payment.hops[0].channel.entry.public_key
|
|
734
776
|
payment.hops[0].channel.entry.alias
|
735
777
|
payment.hops[0].channel.entry.color
|
736
778
|
```
|
779
|
+
|
780
|
+
### Proof of Payment
|
781
|
+
|
782
|
+
[Making Payments](https://docs.lightning.engineering/the-lightning-network/multihop-payments)
|
783
|
+
|
784
|
+
```ruby
|
785
|
+
payment = Lighstorm::Invoice.decode('lnbc20m1pv...qqdhhwkj').pay.result
|
786
|
+
|
787
|
+
payment.secret.proof
|
788
|
+
# => 'c504f73f83e3772b802844b54021e44e071c03011eeda476b198f7a093bcb09e'
|
789
|
+
```
|
790
|
+
|
737
791
|
### Performance
|
738
792
|
Avoid fetching data that you don't need:
|
739
793
|
```ruby
|
@@ -990,7 +1044,7 @@ gem 'lighstorm', path: '/home/user/lighstorm'
|
|
990
1044
|
# demo.rb
|
991
1045
|
require 'lighstorm'
|
992
1046
|
|
993
|
-
puts Lighstorm.version # => 0.0.
|
1047
|
+
puts Lighstorm.version # => 0.0.11
|
994
1048
|
```
|
995
1049
|
|
996
1050
|
```sh
|
@@ -1050,13 +1104,15 @@ The downside is that we can't [lazy-load](https://en.wikipedia.org/wiki/Lazy_loa
|
|
1050
1104
|
To perform an _action_, like creating an Invoice, you:
|
1051
1105
|
```ruby
|
1052
1106
|
Lighstorm::Invoice.create(
|
1053
|
-
description: 'Coffee', millisatoshis: 1000
|
1107
|
+
description: 'Coffee', amount: { millisatoshis: 1000 }
|
1054
1108
|
)
|
1055
1109
|
```
|
1056
1110
|
|
1057
1111
|
Internally, what's happening:
|
1058
1112
|
```ruby
|
1059
|
-
action = Lighstorm::Invoice.create(
|
1113
|
+
action = Lighstorm::Invoice.create(
|
1114
|
+
description: 'Coffee', amount: { millisatoshis: 1000 }
|
1115
|
+
)
|
1060
1116
|
|
1061
1117
|
request = Controllers::Invoice::Create.prepare(params) # pure
|
1062
1118
|
response = Controllers::Invoice::Create.dispatch(request) # side effect
|
@@ -1225,13 +1281,13 @@ gem build lighstorm.gemspec
|
|
1225
1281
|
|
1226
1282
|
gem signin
|
1227
1283
|
|
1228
|
-
gem push lighstorm-0.0.
|
1284
|
+
gem push lighstorm-0.0.11.gem
|
1229
1285
|
```
|
1230
1286
|
|
1231
1287
|
_________________
|
1232
1288
|
|
1233
1289
|
<center>
|
1234
|
-
lighstorm 0.0.
|
1290
|
+
lighstorm 0.0.11
|
1235
1291
|
|
|
1236
1292
|
<a href="https://github.com/icebaker/lighstorm" rel="noopener noreferrer" target="_blank">GitHub</a>
|
1237
1293
|
|
|
data/docs/_coverpage.md
CHANGED
data/docs/index.html
CHANGED
data/models/errors.rb
CHANGED
@@ -2,7 +2,21 @@
|
|
2
2
|
|
3
3
|
module Lighstorm
|
4
4
|
module Errors
|
5
|
-
class LighstormError < StandardError
|
5
|
+
class LighstormError < StandardError
|
6
|
+
attr_reader :grpc
|
7
|
+
|
8
|
+
def initialize(message = nil, grpc: nil)
|
9
|
+
super(message)
|
10
|
+
@grpc = grpc
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_h
|
14
|
+
output = { class: self.class, message: message }
|
15
|
+
output[:grpc] = grpc.message unless grpc.nil?
|
16
|
+
|
17
|
+
output
|
18
|
+
end
|
19
|
+
end
|
6
20
|
|
7
21
|
class ToDoError < LighstormError; end
|
8
22
|
|
@@ -19,6 +33,7 @@ module Lighstorm
|
|
19
33
|
class OperationNotAllowedError < LighstormError; end
|
20
34
|
class UnexpectedNumberOfHTLCsError < LighstormError; end
|
21
35
|
class UnknownChannelError < LighstormError; end
|
36
|
+
class NoInvoiceFoundError < LighstormError; end
|
22
37
|
|
23
38
|
class InvoiceMayHaveMultiplePaymentsError < LighstormError; end
|
24
39
|
|
data/models/invoice.rb
CHANGED
@@ -84,7 +84,8 @@ module Lighstorm
|
|
84
84
|
end
|
85
85
|
|
86
86
|
def pay(
|
87
|
-
|
87
|
+
amount: nil, message: nil, route: nil,
|
88
|
+
fee: nil,
|
88
89
|
times_out_in: { seconds: 5 },
|
89
90
|
preview: false
|
90
91
|
)
|
@@ -92,8 +93,9 @@ module Lighstorm
|
|
92
93
|
Controllers::Invoice::PayThroughRoute.perform(self, route: route, preview: preview)
|
93
94
|
else
|
94
95
|
Controllers::Invoice::Pay.perform(
|
95
|
-
|
96
|
-
|
96
|
+
code: code,
|
97
|
+
amount: amount,
|
98
|
+
fee: fee,
|
97
99
|
message: message,
|
98
100
|
times_out_in: times_out_in,
|
99
101
|
preview: preview
|
data/models/nodes/node.rb
CHANGED
@@ -100,13 +100,14 @@ module Lighstorm
|
|
100
100
|
end
|
101
101
|
|
102
102
|
def send_message(
|
103
|
-
message,
|
103
|
+
message, amount:, fee: nil, secret: nil,
|
104
104
|
times_out_in: { seconds: 5 }, through: 'amp',
|
105
105
|
preview: false
|
106
106
|
)
|
107
107
|
pay(
|
108
108
|
message: message,
|
109
|
-
|
109
|
+
amount: amount,
|
110
|
+
fee: fee,
|
110
111
|
secret: secret,
|
111
112
|
times_out_in: times_out_in,
|
112
113
|
through: through,
|
@@ -115,13 +116,15 @@ module Lighstorm
|
|
115
116
|
end
|
116
117
|
|
117
118
|
def pay(
|
118
|
-
|
119
|
+
amount:, message: nil, secret: nil,
|
120
|
+
fee: nil,
|
119
121
|
times_out_in: { seconds: 5 }, through: 'amp',
|
120
122
|
preview: false
|
121
123
|
)
|
122
124
|
Controllers::Node::Pay.perform(
|
123
125
|
public_key: public_key,
|
124
|
-
|
126
|
+
amount: amount,
|
127
|
+
fee: fee,
|
125
128
|
through: through,
|
126
129
|
secret: secret,
|
127
130
|
message: message,
|
data/models/secret.rb
CHANGED
@@ -7,9 +7,15 @@ module Lighstorm
|
|
7
7
|
class Secret
|
8
8
|
attr_reader :preimage, :hash
|
9
9
|
|
10
|
-
def self.
|
10
|
+
def self.generate
|
11
11
|
data = { preimage: SecureRandom.hex(32) }
|
12
12
|
data[:hash] = Digest::SHA256.hexdigest([data[:preimage]].pack('H*'))
|
13
|
+
data
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.create(&vcr)
|
17
|
+
data = vcr.nil? ? generate : vcr.call(-> { generate })
|
18
|
+
|
13
19
|
Secret.new(data)
|
14
20
|
end
|
15
21
|
|
@@ -20,6 +26,14 @@ module Lighstorm
|
|
20
26
|
@hash = data[:hash]
|
21
27
|
end
|
22
28
|
|
29
|
+
def proof
|
30
|
+
@preimage
|
31
|
+
end
|
32
|
+
|
33
|
+
def valid_proof?(candidate_preimage)
|
34
|
+
candidate_preimage == preimage
|
35
|
+
end
|
36
|
+
|
23
37
|
def to_h
|
24
38
|
{
|
25
39
|
preimage: preimage,
|
data/static/spec.rb
CHANGED
@@ -4,7 +4,7 @@ module Lighstorm
|
|
4
4
|
module Static
|
5
5
|
SPEC = {
|
6
6
|
name: 'lighstorm',
|
7
|
-
version: '0.0.
|
7
|
+
version: '0.0.11',
|
8
8
|
author: 'icebaker',
|
9
9
|
summary: 'API for interacting with a Lightning Node.',
|
10
10
|
description: 'Lighstorm is an opinionated abstraction layer on top of the lnd-client for interacting with a Lightning Node.',
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lighstorm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- icebaker
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-03-
|
11
|
+
date: 2023-03-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dotenv
|
@@ -103,6 +103,7 @@ files:
|
|
103
103
|
- controllers/invoice/actions/pay_through_route.rb
|
104
104
|
- controllers/invoice/all.rb
|
105
105
|
- controllers/invoice/decode.rb
|
106
|
+
- controllers/invoice/find_by_code.rb
|
106
107
|
- controllers/invoice/find_by_secret_hash.rb
|
107
108
|
- controllers/node.rb
|
108
109
|
- controllers/node/actions/apply_gossip.rb
|