lighstorm 0.0.4 → 0.0.6

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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby-rspec-tests.yml +39 -0
  3. data/Gemfile.lock +3 -3
  4. data/README.md +5 -5
  5. data/adapters/connections/channel_node/policy.rb +61 -0
  6. data/adapters/connections/channel_node.rb +40 -18
  7. data/adapters/edges/channel.rb +23 -7
  8. data/adapters/edges/forward.rb +1 -2
  9. data/adapters/edges/payment/purpose.rb +0 -2
  10. data/adapters/edges/payment.rb +2 -5
  11. data/adapters/invoice.rb +2 -3
  12. data/adapters/nodes/node.rb +39 -5
  13. data/adapters/payment_request.rb +0 -1
  14. data/controllers/channel/actions/apply_gossip.rb +194 -0
  15. data/controllers/channel/find_by_id.rb +1 -1
  16. data/controllers/channel/mine.rb +1 -1
  17. data/controllers/channel.rb +4 -0
  18. data/controllers/invoice/actions/create.rb +10 -9
  19. data/controllers/invoice.rb +1 -1
  20. data/controllers/node/actions/apply_gossip.rb +112 -0
  21. data/controllers/node.rb +4 -0
  22. data/controllers/payment/all.rb +52 -36
  23. data/controllers/payment.rb +6 -6
  24. data/docs/README.md +109 -13
  25. data/docs/_coverpage.md +1 -1
  26. data/docs/index.html +1 -1
  27. data/models/concerns/protectable.rb +23 -0
  28. data/models/connections/channel_node/accounting.rb +4 -0
  29. data/models/connections/channel_node/fee.rb +27 -18
  30. data/models/connections/channel_node/htlc/blocks/delta.rb +39 -0
  31. data/models/connections/channel_node/htlc.rb +48 -14
  32. data/models/connections/channel_node/policy.rb +11 -2
  33. data/models/connections/channel_node.rb +33 -1
  34. data/models/edges/channel/accounting.rb +17 -0
  35. data/models/edges/channel.rb +39 -5
  36. data/models/edges/forward.rb +0 -1
  37. data/models/edges/payment.rb +0 -1
  38. data/models/errors.rb +3 -0
  39. data/models/nodes/node/lightning.rb +6 -0
  40. data/models/nodes/node/platform.rb +6 -0
  41. data/models/nodes/node.rb +51 -0
  42. data/models/payment_request.rb +6 -3
  43. data/ports/grpc/session.rb +26 -0
  44. data/ports/grpc.rb +23 -5
  45. data/static/spec.rb +1 -1
  46. metadata +9 -2
data/docs/README.md CHANGED
@@ -23,7 +23,7 @@ Lighstorm::Channel.mine.first.myself.node.alias
23
23
  Add to your `Gemfile`:
24
24
 
25
25
  ```ruby
26
- gem 'lighstorm', '~> 0.0.4'
26
+ gem 'lighstorm', '~> 0.0.6'
27
27
  ```
28
28
 
29
29
  Run `bundle install`.
@@ -56,7 +56,7 @@ Lighstorm.config!(
56
56
  ```ruby
57
57
  require 'lighstorm'
58
58
 
59
- puts Lighstorm.version # => 0.0.4
59
+ puts Lighstorm.version # => 0.0.6
60
60
 
61
61
  Lighstorm::Satoshis.new(
62
62
  milisatoshis: 75_621_650
@@ -251,16 +251,23 @@ end
251
251
  ```ruby
252
252
  LighstormError
253
253
 
254
+ IncoherentGossipError
255
+
256
+ TooManyArgumentsError
254
257
  MissingCredentialsError
258
+ MissingGossipHandlerError
255
259
  MissingMilisatoshisError
256
260
  MissingPartsPerMillionError
257
261
  MissingTTLError
262
+
258
263
  NegativeNotAllowedError
264
+
259
265
  NotYourChannelError
260
266
  NotYourNodeError
267
+ UnknownChannelError
268
+
261
269
  OperationNotAllowedError
262
270
  UnexpectedNumberOfHTLCsError
263
- UnknownChannelError
264
271
  UpdateChannelPolicyError
265
272
  ```
266
273
 
@@ -327,7 +334,8 @@ channel.mine?
327
334
  channel.id
328
335
  channel.opened_at
329
336
  channel.up_at
330
- channel.active
337
+ channel.state
338
+ channel.active?
331
339
  channel.exposure
332
340
 
333
341
  channel.accounting.capacity.milisatoshis
@@ -345,31 +353,39 @@ channel.partners[1]
345
353
  channel.partners[1].node.alias
346
354
 
347
355
  # Channels that belong to you:
348
- channel.myself
349
- channel.myself.node.alias
350
-
351
356
  channel.transaction.funding.id
352
357
  channel.transaction.funding.index
353
358
 
354
359
  channel.partner
355
- channel.partner.node.alias
360
+ channel.partner.state
361
+ channel.partner.active?
356
362
 
357
- channel.partner.accounting.balance.milisatoshis
358
- channel.partner.node.alias
359
363
  channel.partner.node.public_key
364
+ channel.partner.node.alias
360
365
  channel.partner.node.color
366
+
367
+ channel.partner.accounting.balance.milisatoshis
368
+
361
369
  channel.partner.policy.fee.base.milisatoshis
362
370
  channel.partner.policy.fee.rate.parts_per_million
371
+
363
372
  channel.partner.policy.htlc.minimum.milisatoshis
364
373
  channel.partner.policy.htlc.maximum.milisatoshis
365
374
  channel.partner.policy.htlc.blocks.delta.minimum
366
375
 
367
- channel.myself.accounting.balance.milisatoshis
368
- channel.myself.node.alias
376
+ channel.myself
377
+ channel.myself.state
378
+ channel.myself.active?
379
+
369
380
  channel.myself.node.public_key
381
+ channel.myself.node.alias
370
382
  channel.myself.node.color
383
+
384
+ channel.myself.accounting.balance.milisatoshis
385
+
371
386
  channel.myself.policy.fee.base.milisatoshis
372
387
  channel.myself.policy.fee.rate.parts_per_million
388
+
373
389
  channel.myself.policy.htlc.minimum.milisatoshis
374
390
  channel.myself.policy.htlc.maximum.milisatoshis
375
391
  channel.myself.policy.htlc.blocks.delta.minimum
@@ -380,6 +396,8 @@ channel.myself.policy.htlc.blocks.delta.minimum
380
396
  ```ruby
381
397
  channel = Lighstorm::Channel.mine.first
382
398
 
399
+ # 'preview' let you check the expected operation
400
+ # before actually performing it for debug purposes
383
401
  channel.myself.policy.fee.update(
384
402
  { rate: { parts_per_million: 25 } }, preview: true
385
403
  )
@@ -399,6 +417,8 @@ channel.myself.policy.fee.update(
399
417
 
400
418
  ## Invoice
401
419
 
420
+ [Understanding Lightning Invoices](https://docs.lightning.engineering/the-lightning-network/payment-lifecycle/understanding-lightning-invoices)
421
+
402
422
  ```ruby
403
423
  Lighstorm::Invoice
404
424
  Lighstorm::Invoice.all
@@ -436,6 +456,22 @@ invoice.request.secret.hash
436
456
  invoice.request.address
437
457
  ```
438
458
 
459
+ ### Operations
460
+
461
+ [Understanding Lightning Invoices](https://docs.lightning.engineering/the-lightning-network/payment-lifecycle/understanding-lightning-invoices)
462
+
463
+ ```ruby
464
+ # 'preview' let you check the expected operation
465
+ # before actually performing it for debug purposes
466
+ invoice = Lighstorm::Invoice.create(
467
+ milisatoshis: 1000, description: 'Coffee', preview: true
468
+ )
469
+
470
+ invoice = Lighstorm::Invoice.create(
471
+ milisatoshis: 1000, description: 'Piña Colada'
472
+ )
473
+ ```
474
+
439
475
  ## Payment
440
476
 
441
477
  [![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)
@@ -537,6 +573,17 @@ payment.hops[0].channel.entry.public_key
537
573
  payment.hops[0].channel.entry.alias
538
574
  payment.hops[0].channel.entry.color
539
575
  ```
576
+ ### Performance
577
+ Avoid fetching data that you don't need:
578
+ ```ruby
579
+ Lighstorm::Payment.all(
580
+ fetch: {
581
+ get_node_info: false,
582
+ lookup_invoice: false,
583
+ decode_pay_req: false,
584
+ get_chan_info: false }
585
+ )
586
+ ```
540
587
 
541
588
  ## Forward
542
589
 
@@ -625,6 +672,55 @@ group.channel.partner.node.public_key
625
672
  group.channel.partner.node.color
626
673
  ```
627
674
 
675
+ ## Gossip
676
+
677
+ [The Gossip Network](https://docs.lightning.engineering/the-lightning-network/the-gossip-network)
678
+
679
+ ### Node
680
+
681
+ ```ruby
682
+ gossip = {
683
+ 'identityKey' => '02d3c80335a8ccb2ed364c06875f32240f36f7edb37d80f8dbe321b4c364b6e997',
684
+ 'alias' => 'icebaker',
685
+ 'color' => '#eb34a4'
686
+ }
687
+
688
+ Lighstorm::Node.adapt(gossip: gossip)
689
+
690
+ node = Lighstorm::Node.find_by_public_key(
691
+ '02d3c80335a8ccb2ed364c06875f32240f36f7edb37d80f8dbe321b4c364b6e997'
692
+ )
693
+
694
+ diff = node.apply!(gossip: gossip)
695
+
696
+ Lighstorm::Node.adapt(dump: node.dump)
697
+ ```
698
+
699
+ ### Channel
700
+
701
+ ```ruby
702
+ gossip = {
703
+ 'chanId' => '850099509773795329',
704
+ 'capacity' => '5000000',
705
+ 'routingPolicy' => {
706
+ 'timeLockDelta' => 144,
707
+ 'minHtlc' => '1000',
708
+ 'feeBaseMsat' => '1000',
709
+ 'feeRateMilliMsat' => '300',
710
+ 'maxHtlcMsat' => '4950000000'
711
+ },
712
+ 'advertisingNode' => '02d3c80335a8ccb2ed364c06875f32240f36f7edb37d80f8dbe321b4c364b6e997'
713
+ }
714
+
715
+ Lighstorm::Channel.adapt(gossip: gossip)
716
+
717
+ channel = Lighstorm::Channel.find_by_id('850099509773795329')
718
+
719
+ diff = channel.apply!(gossip: gossip)
720
+
721
+ Lighstorm::Channel.adapt(dump: channel.dump)
722
+ ```
723
+
628
724
  ## Satoshis
629
725
 
630
726
  ```ruby
@@ -647,7 +743,7 @@ satoshis.parts_per_million(reference_in_milisatoshis)
647
743
  _________________
648
744
 
649
745
  <center>
650
- lighstorm 0.0.4
746
+ lighstorm 0.0.6
651
747
  |
652
748
  <a href="https://github.com/icebaker/lighstorm" rel="noopener noreferrer" target="_blank">GitHub</a>
653
749
  |
data/docs/_coverpage.md CHANGED
@@ -5,7 +5,7 @@
5
5
  - Make your Satoshis flow.
6
6
  - Unleash the power of Graphs.
7
7
 
8
- 0.0.4
8
+ 0.0.6
9
9
 
10
10
  [Documentation](README)
11
11
  [GitHub](https://github.com/icebaker/lighstorm)
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.4',
21
+ name: 'Lighstorm 0.0.6',
22
22
  repo: 'https://github.com/icebaker/lighstorm'
23
23
  }
24
24
  </script>
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lighstorm
4
+ module Models
5
+ module Protectable
6
+ def prepare_token!(token)
7
+ @token = token
8
+ end
9
+
10
+ def protect!(value)
11
+ validate_token!(value)
12
+ end
13
+
14
+ def validate_token!(value)
15
+ token = value.is_a?(Hash) ? value[:token] : nil
16
+
17
+ raise OperationNotAllowedError if token.nil? || @token.nil? || token != @token
18
+
19
+ @token = nil
20
+ end
21
+ end
22
+ end
23
+ end
@@ -16,6 +16,10 @@ module Lighstorm
16
16
  def to_h
17
17
  { balance: balance.to_h }
18
18
  end
19
+
20
+ def dump
21
+ Marshal.load(Marshal.dump(@data))
22
+ end
19
23
  end
20
24
  end
21
25
  end
@@ -4,6 +4,7 @@ require 'securerandom'
4
4
 
5
5
  require_relative '../../satoshis'
6
6
  require_relative '../../rate'
7
+ require_relative '../../concerns/protectable'
7
8
 
8
9
  require_relative '../../../components/lnd'
9
10
  require_relative '../../../controllers/channel/actions/update_fee'
@@ -11,24 +12,38 @@ require_relative '../../../controllers/channel/actions/update_fee'
11
12
  module Lighstorm
12
13
  module Models
13
14
  class Fee
15
+ include Protectable
16
+
14
17
  def initialize(policy, data)
15
18
  @policy = policy
16
19
  @data = data
17
20
  end
18
21
 
19
22
  def base
23
+ return nil unless @data[:base]
24
+
20
25
  @base ||= Satoshis.new(milisatoshis: @data[:base][:milisatoshis])
21
26
  end
22
27
 
23
28
  def rate
29
+ return nil unless @data[:rate]
30
+
24
31
  @rate ||= Rate.new(parts_per_million: @data[:rate][:parts_per_million])
25
32
  end
26
33
 
27
34
  def to_h
28
- {
29
- base: base.to_h,
30
- rate: rate.to_h
31
- }
35
+ result = {}
36
+
37
+ result[:base] = base.to_h if @data[:base]
38
+ result[:rate] = rate.to_h if @data[:rate]
39
+
40
+ return nil if result.empty?
41
+
42
+ result
43
+ end
44
+
45
+ def dump
46
+ Marshal.load(Marshal.dump(@data))
32
47
  end
33
48
 
34
49
  def update(params, preview: false, fake: false)
@@ -38,29 +53,23 @@ module Lighstorm
38
53
  end
39
54
 
40
55
  def base=(value)
41
- validate_token!(value)
56
+ protect!(value)
42
57
 
43
58
  @base = value[:value]
44
- end
45
59
 
46
- def rate=(value)
47
- validate_token!(value)
60
+ @data[:base] = { milisatoshis: @base.milisatoshis }
48
61
 
49
- @rate = value[:value]
62
+ base
50
63
  end
51
64
 
52
- def prepare_token!(token)
53
- @token = token
54
- end
55
-
56
- private
65
+ def rate=(value)
66
+ protect!(value)
57
67
 
58
- def validate_token!(value)
59
- token = value.is_a?(Hash) ? value[:token] : nil
68
+ @rate = value[:value]
60
69
 
61
- raise OperationNotAllowedError if token.nil? || @token.nil? || token != @token
70
+ @data[:rate] = { parts_per_million: @rate.parts_per_million }
62
71
 
63
- @token = nil
72
+ rate
64
73
  end
65
74
  end
66
75
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../../concerns/protectable'
4
+
5
+ module Lighstorm
6
+ module Models
7
+ class BlocksDelta
8
+ include Protectable
9
+
10
+ def initialize(data)
11
+ @data = data
12
+ end
13
+
14
+ def minimum
15
+ @minimum ||= @data[:minimum]
16
+ end
17
+
18
+ def to_h
19
+ {
20
+ minimum: minimum
21
+ }
22
+ end
23
+
24
+ def dump
25
+ Marshal.load(Marshal.dump(@data))
26
+ end
27
+
28
+ def minimum=(value)
29
+ protect!(value)
30
+
31
+ @minimum = value[:value]
32
+
33
+ @data[:minimum] = @minimum
34
+
35
+ minimum
36
+ end
37
+ end
38
+ end
39
+ end
@@ -2,42 +2,48 @@
2
2
 
3
3
  require_relative '../../satoshis'
4
4
  require_relative '../../rate'
5
-
6
- require_relative '../../../components/lnd'
5
+ require_relative '../../concerns/protectable'
6
+ require_relative 'htlc/blocks/delta'
7
7
 
8
8
  module Lighstorm
9
9
  module Models
10
10
  class HTLC
11
+ include Protectable
12
+
11
13
  def initialize(data)
12
14
  @data = data
13
15
  end
14
16
 
15
17
  def minimum
16
- @minimum ||= Satoshis.new(milisatoshis: @data[:minimum][:milisatoshis])
18
+ @minimum ||= if @data[:minimum]
19
+ Satoshis.new(
20
+ milisatoshis: @data[:minimum][:milisatoshis]
21
+ )
22
+ end
17
23
  end
18
24
 
19
25
  def maximum
20
- @maximum ||= Satoshis.new(milisatoshis: @data[:maximum][:milisatoshis])
26
+ @maximum ||= if @data[:maximum]
27
+ Satoshis.new(
28
+ milisatoshis: @data[:maximum][:milisatoshis]
29
+ )
30
+ end
21
31
  end
22
32
 
23
33
  def blocks
24
34
  @blocks ||= Struct.new(:data) do
25
35
  def delta
26
- Struct.new(:data) do
27
- def minimum
28
- data[:minimum]
29
- end
30
-
31
- def to_h
32
- { minimum: minimum }
33
- end
34
- end.new(data[:delta])
36
+ @delta ||= BlocksDelta.new(data[:delta] || {})
37
+ end
38
+
39
+ def dump
40
+ { delta: delta.dump }
35
41
  end
36
42
 
37
43
  def to_h
38
44
  { delta: delta.to_h }
39
45
  end
40
- end.new(@data[:blocks])
46
+ end.new(@data[:blocks] || {})
41
47
  end
42
48
 
43
49
  def to_h
@@ -47,6 +53,34 @@ module Lighstorm
47
53
  blocks: blocks.to_h
48
54
  }
49
55
  end
56
+
57
+ def dump
58
+ result = Marshal.load(Marshal.dump(@data))
59
+
60
+ result = result.merge({ blocks: blocks.dump }) if @data[:blocks]
61
+
62
+ result
63
+ end
64
+
65
+ def minimum=(value)
66
+ protect!(value)
67
+
68
+ @minimum = value[:value]
69
+
70
+ @data[:minimum] = { milisatoshis: @minimum.milisatoshis }
71
+
72
+ minimum
73
+ end
74
+
75
+ def maximum=(value)
76
+ protect!(value)
77
+
78
+ @maximum = value[:value]
79
+
80
+ @data[:maximum] = { milisatoshis: @maximum.milisatoshis }
81
+
82
+ maximum
83
+ end
50
84
  end
51
85
  end
52
86
  end
@@ -14,16 +14,25 @@ module Lighstorm
14
14
  end
15
15
 
16
16
  def fee
17
- @fee ||= @data ? Fee.new(self, @data[:fee]) : nil
17
+ @fee ||= Fee.new(self, @data ? @data[:fee] : {})
18
18
  end
19
19
 
20
20
  def htlc
21
- @htlc ||= @data ? HTLC.new(@data[:htlc]) : nil
21
+ @htlc ||= HTLC.new(@data ? @data[:htlc] : {})
22
22
  end
23
23
 
24
24
  def to_h
25
25
  { fee: fee.to_h, htlc: htlc.to_h }
26
26
  end
27
+
28
+ def dump
29
+ result = Marshal.load(Marshal.dump(@data))
30
+
31
+ result = result.merge({ fee: fee.dump }) if @data && @data[:fee]
32
+ result = result.merge({ htlc: htlc.dump }) if @data && @data[:htlc]
33
+
34
+ result
35
+ end
27
36
  end
28
37
  end
29
38
  end
@@ -9,12 +9,21 @@ require_relative '../errors'
9
9
  module Lighstorm
10
10
  module Models
11
11
  class ChannelNode
12
+ include Protectable
13
+
14
+ attr_reader :state
15
+
12
16
  def initialize(data, is_mine, transaction)
13
17
  @data = data
18
+ @state = data[:state]
14
19
  @is_mine = is_mine
15
20
  @transaction = transaction
16
21
  end
17
22
 
23
+ def active?
24
+ state == 'active'
25
+ end
26
+
18
27
  def node
19
28
  @node ||= Node.new(@data[:node])
20
29
  end
@@ -30,13 +39,36 @@ module Lighstorm
30
39
  end
31
40
 
32
41
  def to_h
33
- restult = { node: node.to_h }
42
+ restult = { state: state, node: node.to_h }
34
43
 
35
44
  restult[:accounting] = accounting.to_h if @is_mine
36
45
  restult[:policy] = policy.to_h if @data[:policy]
37
46
 
38
47
  restult
39
48
  end
49
+
50
+ def dump
51
+ result = Marshal.load(Marshal.dump(@data)).merge(
52
+ {
53
+ node: node.dump,
54
+ policy: policy.dump
55
+ }
56
+ )
57
+
58
+ result[:accounting] = accounting.dump if @is_mine
59
+ result.delete(:policy) if result[:policy].nil?
60
+
61
+ result
62
+ end
63
+
64
+ def state=(value)
65
+ protect!(value)
66
+
67
+ @state = value[:value]
68
+ @data[:state] = @state
69
+
70
+ state
71
+ end
40
72
  end
41
73
  end
42
74
  end
@@ -2,10 +2,13 @@
2
2
 
3
3
  require_relative '../../satoshis'
4
4
  require_relative '../../errors'
5
+ require_relative '../../concerns/protectable'
5
6
 
6
7
  module Lighstorm
7
8
  module Models
8
9
  class ChannelAccounting
10
+ include Protectable
11
+
9
12
  def initialize(data, is_mine)
10
13
  @data = data
11
14
  @is_mine = is_mine
@@ -63,6 +66,20 @@ module Lighstorm
63
66
  }
64
67
  end
65
68
  end
69
+
70
+ def dump
71
+ Marshal.load(Marshal.dump(@data))
72
+ end
73
+
74
+ def capacity=(value)
75
+ protect!(value)
76
+
77
+ @capacity = value[:value]
78
+
79
+ @data[:capacity][:milisatoshis] = @capacity.milisatoshis
80
+
81
+ capacity
82
+ end
66
83
  end
67
84
  end
68
85
  end