lighstorm 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby-rspec-tests.yml +39 -0
  3. data/Gemfile.lock +1 -1
  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/node/actions/apply_gossip.rb +112 -0
  19. data/controllers/node.rb +4 -0
  20. data/controllers/payment/all.rb +52 -36
  21. data/controllers/payment.rb +6 -6
  22. data/docs/README.md +89 -13
  23. data/docs/_coverpage.md +1 -1
  24. data/docs/index.html +1 -1
  25. data/models/concerns/protectable.rb +23 -0
  26. data/models/connections/channel_node/accounting.rb +4 -0
  27. data/models/connections/channel_node/fee.rb +27 -18
  28. data/models/connections/channel_node/htlc/blocks/delta.rb +39 -0
  29. data/models/connections/channel_node/htlc.rb +48 -14
  30. data/models/connections/channel_node/policy.rb +11 -2
  31. data/models/connections/channel_node.rb +33 -1
  32. data/models/edges/channel/accounting.rb +17 -0
  33. data/models/edges/channel.rb +39 -5
  34. data/models/edges/forward.rb +0 -1
  35. data/models/edges/payment.rb +0 -1
  36. data/models/errors.rb +3 -0
  37. data/models/nodes/node/lightning.rb +6 -0
  38. data/models/nodes/node/platform.rb +6 -0
  39. data/models/nodes/node.rb +51 -0
  40. data/models/payment_request.rb +6 -3
  41. data/ports/grpc/session.rb +26 -0
  42. data/ports/grpc.rb +23 -5
  43. data/static/spec.rb +1 -1
  44. metadata +9 -2
@@ -73,7 +73,7 @@ module Lighstorm
73
73
  def self.transform(data, adapted)
74
74
  [0, 1].each do |i|
75
75
  adapted[:list_channels].each_index do |c|
76
- if adapted[:list_channels][c][:partners][i][:node][:public_key].nil?
76
+ if adapted[:list_channels][c][:partners][i][:node].nil?
77
77
  adapted[:list_channels][c][:partners][i][:node] = adapted[:get_info]
78
78
  end
79
79
  end
@@ -63,7 +63,7 @@ module Lighstorm
63
63
 
64
64
  def self.transform(data, adapted)
65
65
  [0, 1].each do |i|
66
- data[:partners][i][:node] = adapted[:get_info] if data[:partners][i][:node][:public_key].nil?
66
+ data[:partners][i][:node] = adapted[:get_info] if data[:partners][i][:node].nil?
67
67
 
68
68
  adapted[:get_chan_info][data[:id].to_i][:partners].each do |partner|
69
69
  if data[:partners][i][:node][:public_key] == partner[:node][:public_key]
@@ -18,6 +18,10 @@ module Lighstorm
18
18
  def self.find_by_id(id)
19
19
  FindById.model(FindById.data(id))
20
20
  end
21
+
22
+ def self.adapt(dump: nil, gossip: nil)
23
+ Models::Channel.adapt(dump: dump, gossip: gossip)
24
+ end
21
25
  end
22
26
  end
23
27
  end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ require_relative '../../../ports/grpc'
6
+ require_relative '../../../models/errors'
7
+ require_relative '../../../models/nodes/node'
8
+ require_relative '../../../adapters/nodes/node'
9
+
10
+ module Lighstorm
11
+ module Controllers
12
+ module Node
13
+ module ApplyGossip
14
+ SKIPABLE = %w[
15
+ _source
16
+ _key
17
+ ].freeze
18
+
19
+ NOT_ALLOWED = [
20
+ 'public_key'
21
+ ].freeze
22
+
23
+ APPLICABLE = %w[
24
+ alias
25
+ color
26
+ ].freeze
27
+
28
+ def self.perform(actual, gossip)
29
+ updated = Models::Node.new(Adapter::Node.subscribe_channel_graph(gossip))
30
+
31
+ actual_dump = actual.dump
32
+ updated_dump = updated.dump
33
+
34
+ diff = generate_diff(actual_dump, updated_dump)
35
+
36
+ diff.each do |change|
37
+ key = change[:path].join('/')
38
+ next unless NOT_ALLOWED.include?(key)
39
+
40
+ raise IncoherentGossipError, "Gossip doesn't belong to this Node"
41
+ end
42
+
43
+ diff.filter do |change|
44
+ key = change[:path].join('/')
45
+ if SKIPABLE.include?(key)
46
+ false
47
+ elsif APPLICABLE.include?(key)
48
+ apply!(actual, key, change)
49
+ true
50
+ else
51
+ raise Lighstorm::Errors::MissingGossipHandlerError, "don't know how to apply '#{key}'"
52
+ end
53
+ end
54
+ end
55
+
56
+ def self.apply!(actual, key, change)
57
+ case key
58
+ when 'alias'
59
+ token = SecureRandom.hex
60
+ actual.prepare_token!(token)
61
+ actual.alias = {
62
+ value: change[:to],
63
+ token: token
64
+ }
65
+ when 'color'
66
+ token = SecureRandom.hex
67
+ actual.prepare_token!(token)
68
+ actual.color = {
69
+ value: change[:to],
70
+ token: token
71
+ }
72
+ else
73
+ raise Lighstorm::Errors::MissingGossipHandlerError, "don't know how to apply '#{key}'"
74
+ end
75
+ end
76
+
77
+ def self.generate_diff(actual, node, path = [], diff = [])
78
+ case node
79
+ when Hash
80
+ result = {}
81
+ node.each_key do |key|
82
+ result[key] = generate_diff(actual, node[key], path.dup.push(key), diff)
83
+ end
84
+ when Array
85
+ result = []
86
+ node.each_with_index do |value, i|
87
+ result << generate_diff(actual, value, path.dup.push(i), diff)
88
+ end
89
+ else
90
+ new_value = node
91
+
92
+ unless new_value.nil?
93
+ actual_value = actual
94
+ path.each do |key|
95
+ if actual_value[key]
96
+ actual_value = actual_value[key]
97
+ else
98
+ actual_value = nil
99
+ break
100
+ end
101
+ end
102
+
103
+ diff << { path: path, from: actual_value, to: new_value } if actual_value != new_value
104
+ end
105
+ end
106
+
107
+ diff
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
data/controllers/node.rb CHANGED
@@ -18,6 +18,10 @@ module Lighstorm
18
18
  def self.find_by_public_key(public_key)
19
19
  FindByPublicKey.model(FindByPublicKey.data(public_key))
20
20
  end
21
+
22
+ def self.adapt(dump: nil, gossip: nil)
23
+ Models::Node.adapt(dump: dump, gossip: gossip)
24
+ end
21
25
  end
22
26
  end
23
27
  end
@@ -12,16 +12,19 @@ module Lighstorm
12
12
  module Controllers
13
13
  module Payment
14
14
  module All
15
- def self.fetch(purpose: nil, limit: nil)
15
+ def self.fetch(purpose: nil, limit: nil, fetch: {})
16
16
  at = Time.now
17
- get_info = Ports::GRPC.lightning.get_info.to_h
17
+
18
+ grpc = Ports::GRPC.session
19
+
20
+ get_info = grpc.lightning.get_info.to_h
18
21
 
19
22
  last_offset = 0
20
23
 
21
24
  payments = []
22
25
 
23
26
  loop do
24
- response = Ports::GRPC.lightning.list_payments(
27
+ response = grpc.lightning.list_payments(
25
28
  index_offset: last_offset
26
29
  )
27
30
 
@@ -64,8 +67,8 @@ module Lighstorm
64
67
 
65
68
  data = {
66
69
  at: at,
67
- get_info: Ports::GRPC.lightning.get_info.to_h,
68
- fee_report: Ports::GRPC.lightning.fee_report.to_h,
70
+ get_info: get_info,
71
+ fee_report: grpc.lightning.fee_report.to_h,
69
72
  list_payments: payments,
70
73
  list_channels: {},
71
74
  get_chan_info: {},
@@ -75,15 +78,15 @@ module Lighstorm
75
78
  }
76
79
 
77
80
  payments.each do |payment|
78
- unless payment[:payment_request] == '' || data[:decode_pay_req][payment[:payment_request]]
79
- data[:decode_pay_req][payment[:payment_request]] = Ports::GRPC.lightning.decode_pay_req(
81
+ unless fetch[:decode_pay_req] == false || payment[:payment_request] == '' || data[:decode_pay_req][payment[:payment_request]]
82
+ data[:decode_pay_req][payment[:payment_request]] = grpc.lightning.decode_pay_req(
80
83
  pay_req: payment[:payment_request]
81
84
  ).to_h
82
85
  end
83
86
 
84
- unless data[:lookup_invoice][payment[:payment_hash]]
87
+ unless fetch[:lookup_invoice] == false || data[:lookup_invoice][payment[:payment_hash]]
85
88
  begin
86
- data[:lookup_invoice][payment[:payment_hash]] = Ports::GRPC.lightning.lookup_invoice(
89
+ data[:lookup_invoice][payment[:payment_hash]] = grpc.lightning.lookup_invoice(
87
90
  r_hash_str: payment[:payment_hash]
88
91
  ).to_h
89
92
  rescue StandardError => e
@@ -93,9 +96,9 @@ module Lighstorm
93
96
 
94
97
  payment[:htlcs].each do |htlc|
95
98
  htlc[:route][:hops].each do |hop|
96
- unless data[:get_chan_info][hop[:chan_id]]
99
+ unless fetch[:get_chan_info] == false || data[:get_chan_info][hop[:chan_id]]
97
100
  begin
98
- data[:get_chan_info][hop[:chan_id]] = Ports::GRPC.lightning.get_chan_info(
101
+ data[:get_chan_info][hop[:chan_id]] = grpc.lightning.get_chan_info(
99
102
  chan_id: hop[:chan_id]
100
103
  ).to_h
101
104
  rescue GRPC::Unknown => e
@@ -103,23 +106,25 @@ module Lighstorm
103
106
  end
104
107
  end
105
108
 
106
- next if data[:get_node_info][hop[:pub_key]]
109
+ next if fetch[:get_node_info] == false || data[:get_node_info][hop[:pub_key]]
107
110
 
108
- data[:get_node_info][hop[:pub_key]] = Ports::GRPC.lightning.get_node_info(
111
+ data[:get_node_info][hop[:pub_key]] = grpc.lightning.get_node_info(
109
112
  pub_key: hop[:pub_key]
110
113
  ).to_h
111
114
  end
112
115
  end
113
116
  end
114
117
 
115
- data[:lookup_invoice].each_value do |invoice|
116
- if invoice[:_error] || invoice[:payment_request] == '' || data[:decode_pay_req][invoice[:payment_request]]
117
- next
118
- end
118
+ if fetch[:lookup_invoice] != false
119
+ data[:lookup_invoice].each_value do |invoice|
120
+ if fetch[:decode_pay_req] == false || invoice[:_error] || invoice[:payment_request] == '' || data[:decode_pay_req][invoice[:payment_request]]
121
+ next
122
+ end
119
123
 
120
- data[:decode_pay_req][invoice[:payment_request]] = Ports::GRPC.lightning.decode_pay_req(
121
- pay_req: invoice[:payment_request]
122
- ).to_h
124
+ data[:decode_pay_req][invoice[:payment_request]] = grpc.lightning.decode_pay_req(
125
+ pay_req: invoice[:payment_request]
126
+ ).to_h
127
+ end
123
128
  end
124
129
 
125
130
  list_channels_done = {}
@@ -135,7 +140,7 @@ module Lighstorm
135
140
  partner = partners.find { |p| p != data[:get_info][:identity_pubkey] }
136
141
 
137
142
  unless list_channels_done[partner]
138
- Ports::GRPC.lightning.list_channels(
143
+ grpc.lightning.list_channels(
139
144
  peer: [partner].pack('H*')
140
145
  ).channels.map(&:to_h).each do |list_channels|
141
146
  data[:list_channels][list_channels[:chan_id]] = list_channels
@@ -145,27 +150,29 @@ module Lighstorm
145
150
  end
146
151
  end
147
152
 
148
- unless data[:get_node_info][channel[:node1_pub]]
149
- data[:get_node_info][channel[:node1_pub]] = Ports::GRPC.lightning.get_node_info(
153
+ 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(
150
155
  pub_key: channel[:node1_pub]
151
156
  ).to_h
152
157
  end
153
158
 
154
- next if data[:get_node_info][channel[:node2_pub]]
159
+ next if fetch[:get_node_info] == false || data[:get_node_info][channel[:node2_pub]]
155
160
 
156
- data[:get_node_info][channel[:node2_pub]] = Ports::GRPC.lightning.get_node_info(
161
+ data[:get_node_info][channel[:node2_pub]] = grpc.lightning.get_node_info(
157
162
  pub_key: channel[:node2_pub]
158
163
  ).to_h
159
164
  end
160
165
 
161
166
  data[:list_channels].each_value do |channel|
162
- next if data[:get_node_info][channel[:remote_pubkey]]
167
+ next if fetch[:get_node_info] == false || data[:get_node_info][channel[:remote_pubkey]]
163
168
 
164
- data[:get_node_info][channel[:remote_pubkey]] = Ports::GRPC.lightning.get_node_info(
169
+ data[:get_node_info][channel[:remote_pubkey]] = grpc.lightning.get_node_info(
165
170
  pub_key: channel[:remote_pubkey]
166
171
  ).to_h
167
172
  end
168
173
 
174
+ data[:@meta] = { calls: grpc.calls }
175
+
169
176
  data
170
177
  end
171
178
 
@@ -256,11 +263,17 @@ module Lighstorm
256
263
  adapted[:fee_report].each do |channel|
257
264
  next unless data[:id] == channel[:id]
258
265
 
259
- data[:partners][i][:policy][:fee] = channel[:partner][:policy][:fee]
266
+ if data[:partners][i][:policy].nil?
267
+ data[:partners][i][:policy] = channel[:partner][:policy]
268
+ else
269
+ data[:partners][i][:policy][:fee] = channel[:partner][:policy][:fee]
270
+ end
260
271
  break
261
272
  end
262
273
  else
263
- data[:partners][i][:node] = adapted[:get_node_info][data[:partners][i][:node][:public_key]]
274
+ if adapted[:get_node_info][data[:partners][i][:node][:public_key]]
275
+ data[:partners][i][:node] = adapted[:get_node_info][data[:partners][i][:node][:public_key]]
276
+ end
264
277
  data[:partners][i][:node][:platform] = {
265
278
  blockchain: adapted[:get_info][:platform][:blockchain],
266
279
  network: adapted[:get_info][:platform][:network]
@@ -327,22 +340,25 @@ module Lighstorm
327
340
  list_payments
328
341
  end
329
342
 
330
- def self.data(purpose: nil, limit: nil, &vcr)
343
+ def self.data(purpose: nil, limit: nil, fetch: {}, &vcr)
331
344
  raw = if vcr.nil?
332
- fetch(purpose: purpose, limit: limit)
345
+ self.fetch(purpose: purpose, limit: limit, fetch: fetch)
333
346
  else
334
- vcr.call(-> { fetch(purpose: purpose, limit: limit) })
347
+ vcr.call(-> { self.fetch(purpose: purpose, limit: limit, fetch: fetch) })
335
348
  end
336
349
 
337
350
  adapted = adapt(raw)
338
351
 
339
- adapted[:list_payments].map do |data|
340
- transform(data, adapted)
341
- end
352
+ {
353
+ data: adapted[:list_payments].map do |data|
354
+ transform(data, adapted)
355
+ end,
356
+ meta: raw[:@meta]
357
+ }
342
358
  end
343
359
 
344
360
  def self.model(data)
345
- data.map do |node_data|
361
+ data[:data].map do |node_data|
346
362
  Lighstorm::Models::Payment.new(node_data)
347
363
  end
348
364
  end
@@ -5,16 +5,16 @@ require_relative './payment/all'
5
5
  module Lighstorm
6
6
  module Controllers
7
7
  module Payment
8
- def self.all(purpose: nil, limit: nil)
9
- All.model(All.data(purpose: purpose, limit: limit))
8
+ def self.all(purpose: nil, limit: nil, fetch: {})
9
+ All.model(All.data(purpose: purpose, limit: limit, fetch: fetch))
10
10
  end
11
11
 
12
- def self.first(purpose: nil)
13
- All.model(All.data(purpose: purpose)).first
12
+ def self.first(purpose: nil, fetch: {})
13
+ All.model(All.data(purpose: purpose, fetch: fetch)).first
14
14
  end
15
15
 
16
- def self.last(purpose: nil)
17
- All.model(All.data(purpose: purpose)).last
16
+ def self.last(purpose: nil, fetch: {})
17
+ All.model(All.data(purpose: purpose, fetch: fetch)).last
18
18
  end
19
19
  end
20
20
  end
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.5'
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.5
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
@@ -537,6 +553,17 @@ payment.hops[0].channel.entry.public_key
537
553
  payment.hops[0].channel.entry.alias
538
554
  payment.hops[0].channel.entry.color
539
555
  ```
556
+ ### Performance
557
+ Avoid fetching data that you don't need:
558
+ ```ruby
559
+ Lighstorm::Payment.all(
560
+ fetch: {
561
+ get_node_info: false,
562
+ lookup_invoice: false,
563
+ decode_pay_req: false,
564
+ get_chan_info: false }
565
+ )
566
+ ```
540
567
 
541
568
  ## Forward
542
569
 
@@ -625,6 +652,55 @@ group.channel.partner.node.public_key
625
652
  group.channel.partner.node.color
626
653
  ```
627
654
 
655
+ ## Gossip
656
+
657
+ [The Gossip Network](https://docs.lightning.engineering/the-lightning-network/the-gossip-network)
658
+
659
+ ### Node
660
+
661
+ ```ruby
662
+ gossip = {
663
+ 'identityKey' => '02d3c80335a8ccb2ed364c06875f32240f36f7edb37d80f8dbe321b4c364b6e997',
664
+ 'alias' => 'icebaker',
665
+ 'color' => '#eb34a4'
666
+ }
667
+
668
+ Lighstorm::Node.adapt(gossip: gossip)
669
+
670
+ node = Lighstorm::Node.find_by_public_key(
671
+ '02d3c80335a8ccb2ed364c06875f32240f36f7edb37d80f8dbe321b4c364b6e997'
672
+ )
673
+
674
+ diff = node.apply!(gossip: gossip)
675
+
676
+ Lighstorm::Node.adapt(dump: node.dump)
677
+ ```
678
+
679
+ ### Channel
680
+
681
+ ```ruby
682
+ gossip = {
683
+ 'chanId' => '850099509773795329',
684
+ 'capacity' => '5000000',
685
+ 'routingPolicy' => {
686
+ 'timeLockDelta' => 144,
687
+ 'minHtlc' => '1000',
688
+ 'feeBaseMsat' => '1000',
689
+ 'feeRateMilliMsat' => '300',
690
+ 'maxHtlcMsat' => '4950000000'
691
+ },
692
+ 'advertisingNode' => '02d3c80335a8ccb2ed364c06875f32240f36f7edb37d80f8dbe321b4c364b6e997'
693
+ }
694
+
695
+ Lighstorm::Channel.adapt(gossip: gossip)
696
+
697
+ channel = Lighstorm::Channel.find_by_id('850099509773795329')
698
+
699
+ diff = channel.apply!(gossip: gossip)
700
+
701
+ Lighstorm::Channel.adapt(dump: channel.dump)
702
+ ```
703
+
628
704
  ## Satoshis
629
705
 
630
706
  ```ruby
@@ -647,7 +723,7 @@ satoshis.parts_per_million(reference_in_milisatoshis)
647
723
  _________________
648
724
 
649
725
  <center>
650
- lighstorm 0.0.4
726
+ lighstorm 0.0.5
651
727
  |
652
728
  <a href="https://github.com/icebaker/lighstorm" rel="noopener noreferrer" target="_blank">GitHub</a>
653
729
  |
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.5
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.5',
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