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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 551ffb5be38f96d6c0bb4f7b8fbde5763c623f0d100f0a1b34acb90a6074fb23
4
- data.tar.gz: 59451d54f2ad6d45c95c7649ab0a048a4adeb7a15f1af08f133d8eaae107d9bc
3
+ metadata.gz: 6072337300f3fd850fbca3ba5c7c804842c275d5f35fa288dfb83747fa0b1a56
4
+ data.tar.gz: 8a16d83cb96f0ed911ebd55e12707b20f0aac00a0acac8d176e544a68abad647
5
5
  SHA512:
6
- metadata.gz: 54e1e6929a1f5dc67f319461d6fc3799dd5cd5c349224c19198a6bffd203bd3e6431fb6bf571976c2a1c0d3d1d07ceb9cb272206f06794c52595c4ec8d5d57d3
7
- data.tar.gz: a903325535c6c2e98b95c4e54cb51f7bbd7123b40dfb2b9143fc9d7a392290230f1bb026c7bd30a669c8f981286ef56acfe0a2bf3751ea9cce5a4d663e160d61
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.9)
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.3)
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.9'
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.9
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', millisatoshis: 1_000,
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, request_code = nil)
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] = request_code unless request_code.nil?
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, millisatoshis: 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 millisatoshis.nil?
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, millisatoshis: nil, preview: false, &vcr)
58
+ def self.perform(payable:, expires_in:, description: nil, amount: nil, preview: false, &vcr)
59
59
  grpc_request = prepare(
60
60
  description: description,
61
- millisatoshis: millisatoshis,
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(request_code, &vcr)
22
- Payment::Pay.fetch(request_code, &vcr)
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(request_code:, times_out_in:, millisatoshis: nil, message: nil)
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: request_code,
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 millisatoshis.nil?
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:, request_code:, millisatoshis: nil, message: nil, preview: false, &vcr
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
- request_code: request_code,
62
- millisatoshis: millisatoshis,
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(request_code, &vcr)
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(request_code)
11
+ def self.fetch(code)
12
12
  {
13
- _request_code: request_code,
14
- decode_pay_req: Ports::GRPC.lightning.decode_pay_req(pay_req: request_code).to_h
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[:_request_code]
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(request_code, &vcr)
31
- raw = vcr.nil? ? fetch(request_code) : vcr.call(-> { fetch(request_code) })
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
@@ -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.decode(request_code, &vcr)
28
- Decode.model(Decode.data(request_code, &vcr))
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, millisatoshis: 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
- millisatoshis: millisatoshis,
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:, millisatoshis:, times_out_in:, secret:, through:, message: nil)
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:, millisatoshis:, through:,
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
- millisatoshis: millisatoshis,
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(request_code)
33
+ def self.fetch_all(code)
34
34
  {
35
- invoice_decode: request_code.nil? ? nil : Invoice::Decode.data(request_code),
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(request_code = nil, &vcr)
41
- raw = vcr.nil? ? fetch_all(request_code) : vcr.call(-> { fetch_all(request_code) })
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)
@@ -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
- data[:get_node_info][hop[:pub_key]] = grpc.lightning.get_node_info(
112
- pub_key: hop[:pub_key]
113
- ).to_h
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
- data[:get_node_info][channel[:node1_pub]] = grpc.lightning.get_node_info(
155
- pub_key: channel[:node1_pub]
156
- ).to_h
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
- data[:get_node_info][channel[:node2_pub]] = grpc.lightning.get_node_info(
162
- pub_key: channel[:node2_pub]
163
- ).to_h
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
- data[:get_node_info][channel[:remote_pubkey]] = grpc.lightning.get_node_info(
170
- pub_key: channel[:remote_pubkey]
171
- ).to_h
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(purpose: nil, limit: nil, fetch: {}, &vcr)
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(purpose: purpose, limit: limit, fetch: 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(-> { self.fetch(purpose: purpose, limit: limit, fetch: fetch) })
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)
@@ -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.9'
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.9
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
- 'lnbc20m1pv...qqdhhwkj'
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('Hello from Lighstorm!', millisatoshis: 1000)
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('Hello from Lighstorm!', millisatoshis: 1000)
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
  [![This is an image representing Payment as a graph.](https://raw.githubusercontent.com/icebaker/assets/main/lighstorm/graph-payment.png)](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.9
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(description: 'Coffee', millisatoshis: 1000)
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.9.gem
1284
+ gem push lighstorm-0.0.11.gem
1229
1285
  ```
1230
1286
 
1231
1287
  _________________
1232
1288
 
1233
1289
  <center>
1234
- lighstorm 0.0.9
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
@@ -8,7 +8,7 @@
8
8
  - Built for maximum **reliability**.
9
9
  - Optimized for programmer **happiness**.
10
10
 
11
- 0.0.9
11
+ 0.0.11
12
12
 
13
13
  ⚠️ _Warning: Early-stage, breaking changes are expected._
14
14
 
data/docs/index.html CHANGED
@@ -18,7 +18,7 @@
18
18
  <script>
19
19
  window.$docsify = {
20
20
  coverpage: true,
21
- name: 'Lighstorm 0.0.9',
21
+ name: 'Lighstorm 0.0.11',
22
22
  repo: 'https://github.com/icebaker/lighstorm'
23
23
  }
24
24
  </script>
data/models/errors.rb CHANGED
@@ -2,7 +2,21 @@
2
2
 
3
3
  module Lighstorm
4
4
  module Errors
5
- class LighstormError < StandardError; end
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
- millisatoshis: nil, message: nil, route: nil,
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
- request_code: code,
96
- millisatoshis: millisatoshis,
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, millisatoshis:, secret: nil,
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
- millisatoshis: millisatoshis,
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
- millisatoshis:, message: nil, secret: nil,
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
- millisatoshis: millisatoshis,
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.create
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.9',
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.9
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 00:00:00.000000000 Z
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