lighstorm 0.0.8 → 0.0.9

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.
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.8'
30
+ gem 'lighstorm', '~> 0.0.9'
31
31
  ```
32
32
 
33
33
  Run `bundle install`.
@@ -60,12 +60,24 @@ Lighstorm.config!(
60
60
  ```ruby
61
61
  require 'lighstorm'
62
62
 
63
- puts Lighstorm.version # => 0.0.8
63
+ puts Lighstorm.version # => 0.0.9
64
64
 
65
65
  Lighstorm::Invoice.create(
66
- description: 'Coffee', millisatoshis: 1000
66
+ description: 'Coffee', millisatoshis: 1000, payable: 'once'
67
67
  )
68
68
 
69
+ Lighstorm::Invoice.decode(
70
+ 'lnbc20m1pv...qqdhhwkj'
71
+ ).pay
72
+
73
+ Lighstorm::Node.find_by_public_key(
74
+ '02d3c80335a8ccb2ed364c06875f32240f36f7edb37d80f8dbe321b4c364b6e997'
75
+ ).pay(millisatoshis: 1000)
76
+
77
+ Lighstorm::Node.find_by_public_key(
78
+ '02d3c80335a8ccb2ed364c06875f32240f36f7edb37d80f8dbe321b4c364b6e997'
79
+ ).send_message('Hello from Lighstorm!', millisatoshis: 1000)
80
+
69
81
  Lighstorm::Node.myself.alias # => icebaker/old-stone
70
82
  Lighstorm::Node.myself.public_key # => 02d3...e997
71
83
 
@@ -95,7 +107,7 @@ payment.hops.first.channel.partner.node.alias
95
107
 
96
108
  Lighstorm::Satoshis.new(
97
109
  millisatoshis: 75621650
98
- ).satoshis # => 75621
110
+ ).satoshis # => 75621.65
99
111
  ```
100
112
 
101
113
  # Data Modeling
@@ -146,7 +158,9 @@ forward = Lighstorm::Forward.last
146
158
  forward.at
147
159
 
148
160
  forward.fee.millisatoshis
149
- forward.fee.parts_per_million
161
+ forward.fee.parts_per_million(
162
+ forward.in.amount.millisatoshis
163
+ )
150
164
 
151
165
  forward.in.amount.millisatoshis
152
166
  forward.out.amount.millisatoshis
@@ -168,15 +182,15 @@ forward.out.channel.partner.node.alias
168
182
  ### Payment
169
183
 
170
184
  ```ruby
171
- payment = Payment.last
185
+ payment = Lighstorm::Payment.last
172
186
 
173
- payment.status
174
- payment.created_at
187
+ payment.at
188
+ payment.state
175
189
 
176
190
  # https://github.com/lightning/bolts/blob/master/11-payment-encoding.md
177
- payment.request.code # "lnbc20m1pv...qqdhhwkj"
191
+ payment.invoice.code # "lnbc20m1pv...qqdhhwkj"
178
192
 
179
- payment.request.amount.millisatoshis
193
+ payment.invoice.amount.millisatoshis
180
194
 
181
195
  payment.from.hop
182
196
  payment.from.amount.millisatoshis
@@ -239,6 +253,76 @@ node.platform.lightning.implementation
239
253
  node.platform.lightning.version
240
254
  ```
241
255
 
256
+ ### Pay
257
+
258
+ Read more about [_Spontaneous Payments_](https://docs.lightning.engineering/lightning-network-tools/lnd/send-messages-with-keysend#send-a-spontaneous-payment).
259
+
260
+ ```ruby
261
+ destination = Lighstorm::Node.find_by_public_key(
262
+ '02d3c80335a8ccb2ed364c06875f32240f36f7edb37d80f8dbe321b4c364b6e997'
263
+ )
264
+
265
+ destination.alias # => 'icebaker/old-stone'
266
+
267
+ destination.pay(millisatoshis: 1000)
268
+
269
+ destination.pay(
270
+ millisatoshis: 1500,
271
+ message: 'Hello from Lighstorm!',
272
+ through: 'amp',
273
+ times_out_in: { seconds: 5 }
274
+ )
275
+
276
+ destination.pay(
277
+ millisatoshis: 1200,
278
+ message: 'Hello from Lighstorm!',
279
+ through: 'keysend',
280
+ times_out_in: { seconds: 5 }
281
+ )
282
+
283
+ action = destination.pay(millisatoshis: 1000)
284
+ action.result.fee.millisatoshis
285
+ ```
286
+
287
+ ### Send Messages
288
+
289
+ **Warning:** Sending messages through Lightning Network requires you to spend satoshis and potentially pay fees.
290
+
291
+ ```ruby
292
+ destination = Lighstorm::Node.find_by_public_key(
293
+ '02d3c80335a8ccb2ed364c06875f32240f36f7edb37d80f8dbe321b4c364b6e997'
294
+ )
295
+
296
+ destination.alias # => 'icebaker/old-stone'
297
+
298
+ destination.send_message('Hello from Lighstorm!', millisatoshis: 1000)
299
+
300
+ destination.send_message(
301
+ 'Hello from Lighstorm!',
302
+ millisatoshis: 1000,
303
+ through: 'amp',
304
+ times_out_in: { seconds: 5 }
305
+ )
306
+
307
+ destination.send_message(
308
+ 'Hello from Lighstorm!',
309
+ millisatoshis: 1000,
310
+ through: 'keysend',
311
+ times_out_in: { seconds: 5 }
312
+ )
313
+
314
+ action = destination.send_message('Hello from Lighstorm!', millisatoshis: 1000)
315
+ action.result.fee.millisatoshis
316
+ ```
317
+
318
+ Read more about sending messages:
319
+ - [_Send a message to other nodes_](https://docs.lightning.engineering/lightning-network-tools/lnd/send-messages-with-keysend#send-a-message-to-other-nodes)
320
+ - [_Does Private messaging over Bitcoin’s Lightning Network have potential?_](https://cryptopurview.com/private-messaging-over-bitcoins-lightning-network/)
321
+ - [_How Bitcoin's Lightning Can Be Used for Private Messaging_](https://www.coindesk.com/markets/2019/11/09/how-bitcoins-lightning-can-be-used-for-private-messaging/)
322
+
323
+ ### Error Handling
324
+ Same error handling used for [Invoices Payment Errors](?id=error-handling-1)
325
+
242
326
  ## Channel
243
327
 
244
328
  [![This is an image representing Channel as a graph.](https://raw.githubusercontent.com/icebaker/assets/main/lighstorm/graph-channel.png)](https://raw.githubusercontent.com/icebaker/assets/main/lighstorm/graph-channel.png)
@@ -373,23 +457,24 @@ Lighstorm::Invoice.find_by_secret_hash(
373
457
  invoice._key
374
458
 
375
459
  invoice.created_at
376
- invoice.settle_at
460
+ invoice.expires_at
461
+ invoice.settled_at
377
462
 
378
463
  invoice.state
379
464
 
380
465
  # https://github.com/lightning/bolts/blob/master/11-payment-encoding.md
381
- invoice.request.code # "lnbc20m1pv...qqdhhwkj"
466
+ invoice.code # "lnbc20m1pv...qqdhhwkj"
382
467
 
383
- invoice.request.amount.millisatoshis
468
+ invoice.amount.millisatoshis
384
469
 
385
- invoice.request.description.memo
386
- invoice.request.description.hash
470
+ invoice.payable # 'once' or 'indefinitely'
387
471
 
388
- # https://docs.lightning.engineering/the-lightning-network/multihop-payments
389
- invoice.request.secret.preimage
390
- invoice.request.secret.hash
472
+ invoice.description.memo
473
+ invoice.description.hash
391
474
 
392
- invoice.request.address
475
+ # https://docs.lightning.engineering/the-lightning-network/multihop-payments
476
+ invoice.secret.preimage
477
+ invoice.secret.hash
393
478
  ```
394
479
 
395
480
  ### Create
@@ -400,11 +485,27 @@ invoice.request.address
400
485
  # 'preview' let you check the expected operation
401
486
  # before actually performing it for debug purposes
402
487
  preview = Lighstorm::Invoice.create(
403
- description: 'Coffee', millisatoshis: 1000, preview: true
488
+ description: 'Coffee', millisatoshis: 1000,
489
+ payable: 'once', preview: true
490
+ )
491
+
492
+ action = Lighstorm::Invoice.create(
493
+ description: 'Coffee', millisatoshis: 1000,
494
+ payable: 'once', expires_in: { minutes: 5 }
404
495
  )
405
496
 
406
497
  action = Lighstorm::Invoice.create(
407
- description: 'Piña Colada', millisatoshis: 1000
498
+ description: 'Beer', payable: 'once'
499
+ )
500
+
501
+ action = Lighstorm::Invoice.create(
502
+ description: 'Donations', payable: 'indefinitely',
503
+ expires_in: { hours: 24 }
504
+ )
505
+
506
+ action = Lighstorm::Invoice.create(
507
+ description: 'Concert Ticket', millisatoshis: 500000000,
508
+ payable: 'indefinitely', expires_in: { days: 5 }
408
509
  )
409
510
 
410
511
  action.to_h
@@ -413,6 +514,101 @@ action.response
413
514
  invoice = action.result
414
515
  ```
415
516
 
517
+ ### Pay
518
+
519
+ [Understanding Lightning Invoices](https://docs.lightning.engineering/the-lightning-network/payment-lifecycle/understanding-lightning-invoices)
520
+
521
+ ```ruby
522
+ invoice = Lighstorm::Invoice.decode('lnbc20m1pv...qqdhhwkj')
523
+
524
+ # 'preview' let you check the expected operation
525
+ # before actually performing it for debug purposes
526
+ invoice.pay(preview: true)
527
+
528
+ action = invoice.pay
529
+
530
+ action.to_h
531
+
532
+ action.response
533
+ payment = action.result
534
+
535
+ payment.at
536
+ payment.state
537
+
538
+ payment.amount.millisatoshis
539
+ payment.fee.millisatoshis
540
+ payment.fee.parts_per_million(
541
+ payment.amount.millisatoshis
542
+ )
543
+
544
+ payment.purpose
545
+ payment.hops.size
546
+ ```
547
+
548
+ ```ruby
549
+ invoice.pay(
550
+ millisatoshis: 1500,
551
+ message: 'here we go',
552
+ times_out_in: { seconds: 5 }
553
+ )
554
+ ```
555
+
556
+ #### Error Handling
557
+ Check [Error Handling](?id=error-handling-2)
558
+
559
+ ```ruby
560
+ begin
561
+ invoice.pay
562
+ rescue AlreadyPaidError => error
563
+ error.message # 'The invoice is already paid.'
564
+ error.grpc.class # GRPC::AlreadyExists
565
+ error.grpc.message # '6:invoice is already paid. debug_error_string:{UNKNOWN...'
566
+ end
567
+ ```
568
+
569
+ ```ruby
570
+ begin
571
+ invoice.pay(millisatoshis: 1000)
572
+ rescue AmountForNonZeroError => error
573
+ error.message # 'Millisatoshis must not be specified...'
574
+ error.grpc.class # GRPC::Unknown
575
+ error.grpc.message # '2:amount must not be specified when paying...'
576
+ end
577
+ ```
578
+
579
+ ```ruby
580
+ begin
581
+ invoice.pay
582
+ rescue MissingMillisatoshisError => error
583
+ error.message # 'Millisatoshis must be specified...'
584
+ error.grpc.class # GRPC::Unknown
585
+ error.grpc.message # '2:amount must be specified when paying a zero...'
586
+ end
587
+ ```
588
+
589
+ ```ruby
590
+ begin
591
+ invoice.pay
592
+ rescue NoRouteFoundError => error
593
+ error.message # 'FAILURE_REASON_NO_ROUTE'
594
+ e.response
595
+ e.response.last[:failure_reason] # => :FAILURE_REASON_NO_ROUTE
596
+ end
597
+ ```
598
+
599
+
600
+ ```ruby
601
+ begin
602
+ invoice.pay
603
+ rescue PaymentError => error
604
+ error.class
605
+ error.message
606
+
607
+ error.grpc
608
+ error.response
609
+ error.result
610
+ end
611
+ ```
416
612
  ## Payment
417
613
 
418
614
  [![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)
@@ -430,7 +626,8 @@ Lighstorm::Payment.last
430
626
  Lighstorm::Payment.all(limit: 10, purpose: 'rebalance')
431
627
 
432
628
  # Possible Purposes:
433
- ['self-payment', 'peer-to-peer', 'rebalance', 'payment']
629
+ # 'self-payment', 'peer-to-peer',
630
+ # 'rebalance', 'payment'
434
631
 
435
632
  # _key is helpful for reactive javascript frameworks.
436
633
  # Please don't consider it as a unique identifier
@@ -440,34 +637,53 @@ payment._key
440
637
 
441
638
  payment.to_h
442
639
 
443
- payment.status
444
- payment.created_at
445
- payment.settled_at
446
- payment.purpose
640
+ payment.at
641
+
642
+ payment.amount.millisatoshis
447
643
 
448
644
  payment.fee.millisatoshis
449
645
  payment.fee.parts_per_million(
450
- payment.request.amount.millisatoshis
646
+ payment.amount.millisatoshis
451
647
  )
452
648
 
453
- # https://github.com/lightning/bolts/blob/master/11-payment-encoding.md
454
- payment.request.code # "lnbc20m1pv...qqdhhwkj"
649
+ payment.state
650
+ payment.message
455
651
 
456
- payment.request.amount.millisatoshis
652
+ payment.how # 'spontaneously', 'with-invoice'
653
+ payment.through # 'keysend', 'amp', 'non-amp'
654
+ payment.purpose
655
+ # 'self-payment', 'peer-to-peer',
656
+ # 'rebalance', 'payment'
457
657
 
458
658
  # https://docs.lightning.engineering/the-lightning-network/multihop-payments
459
- payment.request.secret.preimage
460
- payment.request.secret.hash
659
+ payment.secret.preimage
660
+ payment.secret.hash
661
+
662
+ payment.invoice.created_at
663
+ payment.invoice.expires_at
664
+ payment.invoice.settled_at
461
665
 
462
- payment.request.address
666
+ payment.invoice.state
667
+
668
+ # https://github.com/lightning/bolts/blob/master/11-payment-encoding.md
669
+ payment.invoice.code # "lnbc20m1pv...qqdhhwkj"
670
+ payment.invoice.amount.millisatoshis
463
671
 
464
- payment.request.description.memo
465
- payment.request.description.hash
672
+ payment.invoice.payable # 'once', 'indefinitely'
673
+
674
+ payment.invoice.description.memo
675
+ payment.invoice.description.hash
676
+
677
+ # https://docs.lightning.engineering/the-lightning-network/multihop-payments
678
+ payment.invoice.secret.preimage
679
+ payment.invoice.secret.hash
466
680
 
467
681
  payment.from.hop
468
682
  payment.from.amount.millisatoshis
469
683
  payment.from.fee.millisatoshis
470
- payment.from.fee.parts_per_million(payment.from.amount.millisatoshis)
684
+ payment.from.fee.parts_per_million(
685
+ payment.from.amount.millisatoshis
686
+ )
471
687
 
472
688
  payment.from.channel.id
473
689
 
@@ -482,7 +698,9 @@ payment.from.channel.exit.color
482
698
  payment.to.hop
483
699
  payment.to.amount.millisatoshis
484
700
  payment.to.fee.millisatoshis
485
- payment.to.fee.parts_per_million(payment.to.amount.millisatoshis)
701
+ payment.to.fee.parts_per_million(
702
+ payment.to.amount.millisatoshis
703
+ )
486
704
 
487
705
  payment.to.channel.id
488
706
 
@@ -502,7 +720,9 @@ payment.hops[0].last?
502
720
  payment.hops[0].hop
503
721
  payment.hops[0].amount.millisatoshis
504
722
  payment.hops[0].fee.millisatoshis
505
- payment.hops[0].fee.parts_per_million(payment.hops[0].amount.millisatoshis)
723
+ payment.hops[0].fee.parts_per_million(
724
+ payment.hops[0].amount.millisatoshis
725
+ )
506
726
 
507
727
  payment.hops[0].channel.id
508
728
 
@@ -666,6 +886,7 @@ Lighstorm::Channel.adapt(dump: channel.dump)
666
886
 
667
887
  ```ruby
668
888
  Lighstorm::Satoshis
889
+ Lighstorm::Satoshis.new(bitcoins: 0.005)
669
890
  Lighstorm::Satoshis.new(millisatoshis: 75621650)
670
891
 
671
892
  satoshis.to_h
@@ -736,24 +957,26 @@ end
736
957
  ```ruby
737
958
  LighstormError
738
959
 
960
+ ArgumentError
739
961
  IncoherentGossipError
740
-
741
- TooManyArgumentsError
742
962
  MissingCredentialsError
743
963
  MissingGossipHandlerError
744
- MissingMillisatoshisError
745
964
  MissingPartsPerMillionError
746
- MissingTTLError
747
-
748
965
  NegativeNotAllowedError
749
-
750
966
  NotYourChannelError
751
967
  NotYourNodeError
752
- UnknownChannelError
753
-
754
968
  OperationNotAllowedError
969
+ TooManyArgumentsError
755
970
  UnexpectedNumberOfHTLCsError
971
+ UnknownChannelError
756
972
  UpdateChannelPolicyError
973
+
974
+ PaymentError
975
+
976
+ AlreadyPaidError
977
+ AmountForNonZeroError
978
+ MissingMillisatoshisError
979
+ NoRouteFoundError
757
980
  ```
758
981
 
759
982
  # Development
@@ -767,7 +990,7 @@ gem 'lighstorm', path: '/home/user/lighstorm'
767
990
  # demo.rb
768
991
  require 'lighstorm'
769
992
 
770
- puts Lighstorm.version # => 0.0.8
993
+ puts Lighstorm.version # => 0.0.9
771
994
  ```
772
995
 
773
996
  ```sh
@@ -969,6 +1192,24 @@ expected: nil
969
1192
 
970
1193
  Remember to undo it afterward, replacing `expect!` with `expect`.
971
1194
 
1195
+ ### Extra Tips for Testing
1196
+
1197
+ To auto-fix contracts:
1198
+
1199
+ ```sh
1200
+ rspec --format json | bundle exec rake contracts:fix
1201
+ ```
1202
+
1203
+ To delete unused test data files, update the `.env` file:
1204
+ ```sh
1205
+ LIGHSTORM_DELETE_UNUSED_TEST_DATA=true
1206
+ ```
1207
+
1208
+ Deletion will only occur if you run all tests and no failures are found:
1209
+ ```ruby
1210
+ bundle exec rspec
1211
+ ```
1212
+
972
1213
  ## Generating Documentation
973
1214
 
974
1215
  ```sh
@@ -984,13 +1225,13 @@ gem build lighstorm.gemspec
984
1225
 
985
1226
  gem signin
986
1227
 
987
- gem push lighstorm-0.0.8.gem
1228
+ gem push lighstorm-0.0.9.gem
988
1229
  ```
989
1230
 
990
1231
  _________________
991
1232
 
992
1233
  <center>
993
- lighstorm 0.0.8
1234
+ lighstorm 0.0.9
994
1235
  |
995
1236
  <a href="https://github.com/icebaker/lighstorm" rel="noopener noreferrer" target="_blank">GitHub</a>
996
1237
  |
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.8
11
+ 0.0.9
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.8',
21
+ name: 'Lighstorm 0.0.9',
22
22
  repo: 'https://github.com/icebaker/lighstorm'
23
23
  }
24
24
  </script>
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../models/errors'
4
+
5
+ module Lighstorm
6
+ module Helpers
7
+ module TimeExpression
8
+ def self.seconds(expression)
9
+ raise Errors::ArgumentError, 'missing keywords for time expression' unless expression.is_a?(Hash)
10
+
11
+ duration = 0.0
12
+ expression.each_key do |key|
13
+ case key
14
+ when :seconds
15
+ duration += expression[key].to_f
16
+ when :minutes
17
+ duration += (expression[key].to_f * 60.0)
18
+ when :hours
19
+ duration += (expression[key].to_f * 60.0 * 60.0)
20
+ when :days
21
+ duration += (expression[key].to_f * 24.0 * 60.0 * 60.0)
22
+ else
23
+ raise Errors::ArgumentError, "unexpected keyword :#{key} for time expression #{expression}"
24
+ end
25
+ end
26
+
27
+ raise Errors::ArgumentError, 'missing keywords for time expression' if expression.keys.empty?
28
+
29
+ duration.to_i
30
+ end
31
+ end
32
+ end
33
+ end
@@ -24,11 +24,11 @@ module Lighstorm
24
24
  end
25
25
 
26
26
  def amount
27
- @amount ||= Satoshis.new(millisatoshis: @data[:amount][:millisatoshis])
27
+ @amount ||= @data[:amount] ? Satoshis.new(millisatoshis: @data[:amount][:millisatoshis]) : nil
28
28
  end
29
29
 
30
30
  def fee
31
- @fee ||= Satoshis.new(millisatoshis: @data[:fee][:millisatoshis])
31
+ @fee ||= @data[:fee] ? Satoshis.new(millisatoshis: @data[:fee][:millisatoshis]) : nil
32
32
  end
33
33
 
34
34
  def channel
@@ -36,15 +36,20 @@ module Lighstorm
36
36
  end
37
37
 
38
38
  def to_h
39
- {
39
+ result = {
40
40
  hop: hop,
41
- amount: amount.to_h,
42
- fee: {
43
- millisatoshis: fee.millisatoshis,
44
- parts_per_million: fee.parts_per_million(amount.millisatoshis)
45
- },
46
41
  channel: channel.to_h
47
42
  }
43
+
44
+ result[:amount] = amount.to_h if amount
45
+ if fee
46
+ result[:fee] = {
47
+ millisatoshis: fee.millisatoshis,
48
+ parts_per_million: fee.parts_per_million(amount.millisatoshis)
49
+ }
50
+ end
51
+
52
+ result
48
53
  end
49
54
  end
50
55
  end
@@ -9,7 +9,7 @@ require_relative '../../components/lnd'
9
9
  require_relative '../../components/cache'
10
10
 
11
11
  require_relative '../nodes/node'
12
- require_relative 'channel/accounting'
12
+ require_relative './channel/accounting'
13
13
 
14
14
  require_relative '../../controllers/channel/actions/apply_gossip'
15
15
  require_relative '../connections/channel_node'