lighstorm 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
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