ruby-ethereum 0.9.6 → 0.10.0
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 +4 -4
- data/lib/ethereum/abi/contract_translator.rb +16 -5
- data/lib/ethereum/db/level_db.rb +5 -5
- data/lib/ethereum/db/refcount_db.rb +3 -3
- data/lib/ethereum/env.rb +4 -1
- data/lib/ethereum/external_call.rb +18 -6
- data/lib/ethereum/fast_vm.rb +7 -3
- data/lib/ethereum/opcodes.rb +7 -0
- data/lib/ethereum/tester/solidity_wrapper.rb +2 -0
- data/lib/ethereum/version.rb +1 -1
- data/lib/ethereum/vm.rb +68 -16
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 924077d29477e9f46eb1db5e877d50064b8763f8
|
4
|
+
data.tar.gz: 16e19c8bcda8230a4051e97fcd0953175bd103aa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 916783bdd8651f67a07897cb4fceb5f8cb75ef272aab5b432c3b17c318285861dbb6a369c8f99f4d3a42755166824e5e002cb870bb2e8a257181a1f1080e35c8
|
7
|
+
data.tar.gz: 751ab5b00e1e89c318ac77138714acc6df7bc09fad6ea355b2ddbd85da1679603e56c607a05f040b2924cd9f870f53ada872e427ac22ce439eb5db74cf9d7085
|
@@ -12,17 +12,22 @@ module Ethereum
|
|
12
12
|
end
|
13
13
|
|
14
14
|
@contract = {
|
15
|
+
fallback_data: nil,
|
15
16
|
constructor_data: nil,
|
16
17
|
function_data: {},
|
17
18
|
event_data: {}
|
18
19
|
}
|
19
20
|
|
20
21
|
contract_interface.each do |desc|
|
21
|
-
encode_types = desc['inputs'].map {|e| e['type'] }
|
22
|
-
signature = desc['inputs'].map {|e| [e['type'], e['name']] }
|
23
|
-
|
24
|
-
# type can be omitted, defaulting to function
|
25
22
|
type = desc['type'] || 'function'
|
23
|
+
encode_types = []
|
24
|
+
signature = []
|
25
|
+
|
26
|
+
if type != 'fallback' && desc.has_key?('inputs')
|
27
|
+
encode_types = desc['inputs'].map {|e| e['type'] }
|
28
|
+
signature = desc['inputs'].map {|e| [e['type'], e['name']] }
|
29
|
+
end
|
30
|
+
|
26
31
|
case type
|
27
32
|
when 'function'
|
28
33
|
name = basename desc['name']
|
@@ -32,7 +37,8 @@ module Ethereum
|
|
32
37
|
encode_types: encode_types,
|
33
38
|
decode_types: decode_types,
|
34
39
|
is_constant: desc.fetch('constant', false),
|
35
|
-
signature: signature
|
40
|
+
signature: signature,
|
41
|
+
payable: desc.fetch('payable', false)
|
36
42
|
}
|
37
43
|
when 'event'
|
38
44
|
name = basename desc['name']
|
@@ -51,6 +57,11 @@ module Ethereum
|
|
51
57
|
encode_types: encode_types,
|
52
58
|
signature: signature
|
53
59
|
}
|
60
|
+
when 'fallback'
|
61
|
+
raise ValueError, "Only one fallback function is supported." if @contract[:fallback_data]
|
62
|
+
@contract[:fallback_data] = {
|
63
|
+
payable: desc['payable']
|
64
|
+
}
|
54
65
|
else
|
55
66
|
raise ValueError, "Unknown interface type: #{type}"
|
56
67
|
end
|
data/lib/ethereum/db/level_db.rb
CHANGED
@@ -25,15 +25,15 @@ module Ethereum
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def get(k)
|
28
|
-
logger.
|
28
|
+
logger.debug 'getting entry', key: Utils.encode_hex(k)[0,8]
|
29
29
|
|
30
30
|
if @uncommited.has_key?(k)
|
31
31
|
raise KeyError, 'key not in db' unless @uncommited[k]
|
32
|
-
logger.
|
32
|
+
logger.debug "from uncommited"
|
33
33
|
return @uncommited[k]
|
34
34
|
end
|
35
35
|
|
36
|
-
logger.
|
36
|
+
logger.debug "from db"
|
37
37
|
raise KeyError, k.inspect unless @db.exists?(k)
|
38
38
|
v = @db.get(k)
|
39
39
|
o = decompress v
|
@@ -43,7 +43,7 @@ module Ethereum
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def put(k, v)
|
46
|
-
logger.
|
46
|
+
logger.debug 'putting entry', key: Utils.encode_hex(k)[0,8], size: v.size
|
47
47
|
@uncommited[k] = v
|
48
48
|
end
|
49
49
|
|
@@ -64,7 +64,7 @@ module Ethereum
|
|
64
64
|
end
|
65
65
|
|
66
66
|
def delete(k)
|
67
|
-
logger.
|
67
|
+
logger.debug 'deleting entry', key: key
|
68
68
|
@uncommited[k] = nil
|
69
69
|
end
|
70
70
|
|
@@ -34,14 +34,14 @@ module Ethereum
|
|
34
34
|
new_refcount = Utils.encode_int(refcount+1)
|
35
35
|
ref_put k, RLP.encode([new_refcount, v])
|
36
36
|
|
37
|
-
if
|
37
|
+
if logger.trace?
|
38
38
|
logger.trace "increasing #{Utils.encode_hex(k)}=#{v} to #{refcount+1}"
|
39
39
|
end
|
40
40
|
rescue
|
41
41
|
ref_put k, RLP.encode([ONE_ENCODED, v])
|
42
42
|
@journal.push [ZERO_ENCODED, k]
|
43
43
|
|
44
|
-
if
|
44
|
+
if logger.trace?
|
45
45
|
logger.trace "increasing #{Utils.encode_hex(k)}=#{v} to 1"
|
46
46
|
end
|
47
47
|
end
|
@@ -54,7 +54,7 @@ module Ethereum
|
|
54
54
|
node_object = RLP.decode ref_get(k)
|
55
55
|
refcount = Utils.decode_int node_object[0]
|
56
56
|
|
57
|
-
if
|
57
|
+
if logger.trace?
|
58
58
|
logger.trace "decreasing #{Utils.encode_hex(k)} to #{refcount-1}"
|
59
59
|
end
|
60
60
|
|
data/lib/ethereum/env.rb
CHANGED
@@ -61,7 +61,10 @@ module Ethereum
|
|
61
61
|
|
62
62
|
dao_fork_blknum: 1920000,
|
63
63
|
child_dao_list: Utils.child_dao_list.map {|addr| Utils.normalize_address addr },
|
64
|
-
dao_withdrawer: Utils.normalize_address('0xbf4ed7b27f1d666546e30d74d50d173d20bca754')
|
64
|
+
dao_withdrawer: Utils.normalize_address('0xbf4ed7b27f1d666546e30d74d50d173d20bca754'),
|
65
|
+
|
66
|
+
anti_dos_fork_blknum: 2457000,
|
67
|
+
clearing_fork_blknum: 2**100
|
65
68
|
}.freeze
|
66
69
|
|
67
70
|
attr :db, :config, :global_config
|
@@ -80,6 +80,10 @@ module Ethereum
|
|
80
80
|
@block.number >= @block.config[:homestead_fork_blknum]
|
81
81
|
end
|
82
82
|
|
83
|
+
def post_anti_dos_hardfork
|
84
|
+
@block.number >= @block.config[:anti_dos_fork_blknum]
|
85
|
+
end
|
86
|
+
|
83
87
|
def post_metropolis_hardfork
|
84
88
|
@block.number >= @block.config[:metropolis_fork_blknum]
|
85
89
|
end
|
@@ -147,9 +151,13 @@ module Ethereum
|
|
147
151
|
def apply_msg(msg, code=nil)
|
148
152
|
code ||= get_code msg.code_address
|
149
153
|
|
150
|
-
log_msg.
|
151
|
-
|
152
|
-
|
154
|
+
if log_msg.trace?
|
155
|
+
log_msg.debug "MSG APPLY", sender: Utils.encode_hex(msg.sender), to: Utils.encode_hex(msg.to), gas: msg.gas, value: msg.value, data: Utils.encode_hex(msg.data.extract_all)
|
156
|
+
if log_state.trace?
|
157
|
+
log_state.trace "MSG PRE STATE SENDER", account: Utils.encode_hex(msg.sender), balance: get_balance(msg.sender), state: log_storage(msg.sender)
|
158
|
+
log_state.trace "MSG PRE STATE RECIPIENT", account: Utils.encode_hex(msg.to), balance: get_balance(msg.to), state: log_storage(msg.to)
|
159
|
+
end
|
160
|
+
end
|
153
161
|
|
154
162
|
# snapshot before execution
|
155
163
|
snapshot = self.snapshot
|
@@ -169,9 +177,13 @@ module Ethereum
|
|
169
177
|
res, gas, dat = VM.execute self, msg, code
|
170
178
|
end
|
171
179
|
|
172
|
-
log_msg.trace
|
173
|
-
|
174
|
-
|
180
|
+
if log_msg.trace?
|
181
|
+
log_msg.trace "MSG APPLIED", gas_remained: gas, sender: msg.sender, to: msg.to, data: dat
|
182
|
+
if log_state.trace?
|
183
|
+
log_state.trace "MSG POST STATE SENDER", account: Utils.encode_hex(msg.sender), balance: get_balance(msg.sender), state: log_storage(msg.sender)
|
184
|
+
log_state.trace "MSG POST STATE RECIPIENT", account: Utils.encode_hex(msg.to), balance: get_balance(msg.to), state: log_storage(msg.to)
|
185
|
+
end
|
186
|
+
end
|
175
187
|
|
176
188
|
if res == 0
|
177
189
|
log_msg.debug 'REVERTING'
|
data/lib/ethereum/fast_vm.rb
CHANGED
@@ -57,7 +57,7 @@ module Ethereum
|
|
57
57
|
s.gas -= gas
|
58
58
|
|
59
59
|
ops.each do |op|
|
60
|
-
if
|
60
|
+
if log_vm_exit.trace?
|
61
61
|
trace_data = {
|
62
62
|
stack: s.stack.map(&:to_s),
|
63
63
|
inst: op,
|
@@ -573,12 +573,16 @@ module Ethereum
|
|
573
573
|
end
|
574
574
|
|
575
575
|
def vm_exception(error, **kwargs)
|
576
|
-
log_vm_exit.trace
|
576
|
+
if log_vm_exit.trace?
|
577
|
+
log_vm_exit.trace('EXCEPTION', cause: error, **kwargs)
|
578
|
+
end
|
577
579
|
return 0, 0, []
|
578
580
|
end
|
579
581
|
|
580
582
|
def peaceful_exit(cause, gas, data, **kwargs)
|
581
|
-
log_vm_exit.trace
|
583
|
+
if log_vm_exit.trace?
|
584
|
+
log_vm_exit.trace('EXIT', cause: cause, **kwargs)
|
585
|
+
end
|
582
586
|
return 1, gas, data
|
583
587
|
end
|
584
588
|
|
data/lib/ethereum/opcodes.rb
CHANGED
@@ -127,5 +127,12 @@ module Ethereum
|
|
127
127
|
GCALLNEWACCOUNT = 25000
|
128
128
|
GSUICIDEREFUND = 24000
|
129
129
|
|
130
|
+
SLOAD_SUPPLEMENTAL_GAS = 150
|
131
|
+
CALL_SUPPLEMENTAL_GAS = 660
|
132
|
+
EXTCODELOAD_SUPPLEMENTAL_GAS = 680
|
133
|
+
BALANCE_SUPPLEMENTAL_GAS = 380
|
134
|
+
CALL_CHILD_LIMIT_NUM = 63
|
135
|
+
CALL_CHILD_LIMIT_DENOM = 64
|
136
|
+
SUICIDE_SUPPLEMENTAL_GAS = 5000
|
130
137
|
end
|
131
138
|
end
|
data/lib/ethereum/version.rb
CHANGED
data/lib/ethereum/vm.rb
CHANGED
@@ -33,7 +33,6 @@ module Ethereum
|
|
33
33
|
steps = 0
|
34
34
|
_prevop = nil
|
35
35
|
|
36
|
-
timestamp = Time.now
|
37
36
|
loop do
|
38
37
|
return peaceful_exit('CODE OUT OF RANGE', s.gas, []) if s.pc >= processed_code.size
|
39
38
|
|
@@ -50,7 +49,7 @@ module Ethereum
|
|
50
49
|
# only to decide which features get logged in 'eth.vm.op', i.e.
|
51
50
|
# tracing can not be activated by activating a sub like
|
52
51
|
# 'eth.vm.op.stack'.
|
53
|
-
if
|
52
|
+
if log_vm_exit.trace?
|
54
53
|
trace_data = {
|
55
54
|
stack: s.stack.map(&:to_s),
|
56
55
|
gas: s.gas + fee,
|
@@ -211,6 +210,9 @@ module Ethereum
|
|
211
210
|
when :ADDRESS
|
212
211
|
stk.push Utils.coerce_to_int(msg.to)
|
213
212
|
when :BALANCE
|
213
|
+
if ext.post_anti_dos_hardfork
|
214
|
+
return vm_exception('OUT OF GAS') unless eat_gas(s, Opcodes::BALANCE_SUPPLEMENTAL_GAS)
|
215
|
+
end
|
214
216
|
s0 = stk.pop
|
215
217
|
addr = Utils.coerce_addr_to_hex(s0 % 2**160)
|
216
218
|
stk.push ext.get_balance(addr)
|
@@ -249,10 +251,16 @@ module Ethereum
|
|
249
251
|
when :GASPRICE
|
250
252
|
stk.push ext.tx_gasprice
|
251
253
|
when :EXTCODESIZE
|
254
|
+
if ext.post_anti_dos_hardfork
|
255
|
+
return vm_exception('OUT OF GAS') unless eat_gas(s, Opcodes::EXTCODELOAD_SUPPLEMENTAL_GAS)
|
256
|
+
end
|
252
257
|
addr = stk.pop
|
253
258
|
addr = Utils.coerce_addr_to_hex(addr % 2**160)
|
254
259
|
stk.push (ext.get_code(addr) || Constant::BYTE_EMPTY).size
|
255
260
|
when :EXTCODECOPY
|
261
|
+
if ext.post_anti_dos_hardfork
|
262
|
+
return vm_exception('OUT OF GAS') unless eat_gas(s, Opcodes::EXTCODELOAD_SUPPLEMENTAL_GAS)
|
263
|
+
end
|
256
264
|
addr, mstart, cstart, size = stk.pop, stk.pop, stk.pop, stk.pop
|
257
265
|
addr = Utils.coerce_addr_to_hex(addr % 2**160)
|
258
266
|
extcode = ext.get_code(addr) || Constant::BYTE_EMPTY
|
@@ -308,6 +316,9 @@ module Ethereum
|
|
308
316
|
return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, s0, 1)
|
309
317
|
mem[s0] = s1 % 256
|
310
318
|
when :SLOAD
|
319
|
+
if ext.post_anti_dos_hardfork
|
320
|
+
return vm_exception('OUT OF GAS') unless eat_gas(s, Opcodes::SLOAD_SUPPLEMENTAL_GAS)
|
321
|
+
end
|
311
322
|
s0 = stk.pop
|
312
323
|
stk.push ext.get_storage_data(msg.to, s0)
|
313
324
|
when :SSTORE
|
@@ -385,7 +396,9 @@ module Ethereum
|
|
385
396
|
|
386
397
|
data = mem.safe_slice(mstart, msz)
|
387
398
|
ext.log(msg.to, topics, Utils.int_array_to_bytes(data))
|
388
|
-
log_log.trace
|
399
|
+
if log_log.trace?
|
400
|
+
log_log.trace('LOG', to: msg.to, topics: topics, data: data)
|
401
|
+
end
|
389
402
|
elsif op == :CREATE
|
390
403
|
value, mstart, msz = stk.pop, stk.pop, stk.pop
|
391
404
|
|
@@ -393,15 +406,20 @@ module Ethereum
|
|
393
406
|
|
394
407
|
if ext.get_balance(msg.to) >= value && msg.depth < 1024
|
395
408
|
cd = CallData.new mem, mstart, msz
|
396
|
-
|
409
|
+
|
410
|
+
ingas = s.gas
|
411
|
+
if ext.post_anti_dos_hardfork
|
412
|
+
ingas = max_call_gas(ingas)
|
413
|
+
end
|
414
|
+
create_msg = Message.new(msg.to, Constant::BYTE_EMPTY, value, ingas, cd, depth: msg.depth+1)
|
397
415
|
|
398
416
|
o, gas, addr = ext.create create_msg
|
399
417
|
if o.true?
|
400
418
|
stk.push Utils.coerce_to_int(addr)
|
401
|
-
s.gas
|
419
|
+
s.gas -= (ingas - gas)
|
402
420
|
else
|
403
421
|
stk.push 0
|
404
|
-
s.gas
|
422
|
+
s.gas -= ingas
|
405
423
|
end
|
406
424
|
else
|
407
425
|
stk.push(0)
|
@@ -415,12 +433,17 @@ module Ethereum
|
|
415
433
|
|
416
434
|
to = Utils.zpad_int(to)[12..-1] # last 20 bytes
|
417
435
|
extra_gas = (ext.account_exists(to) ? 0 : 1) * Opcodes::GCALLNEWACCOUNT +
|
418
|
-
(value > 0 ? 1 : 0) * Opcodes::GCALLVALUETRANSFER
|
436
|
+
(value > 0 ? 1 : 0) * Opcodes::GCALLVALUETRANSFER +
|
437
|
+
(ext.post_anti_dos_hardfork ? 1 : 0) * Opcodes::CALL_SUPPLEMENTAL_GAS
|
438
|
+
if ext.post_anti_dos_hardfork
|
439
|
+
return vm_exception('OUT OF GAS', needed: extra_gas) if s.gas < extra_gas
|
440
|
+
gas = [gas, max_call_gas(s.gas-extra_gas)].min
|
441
|
+
else
|
442
|
+
return vm_exception('OUT OF GAS', needed: gas+extra_gas) if s.gas < gas+extra_gas
|
443
|
+
end
|
444
|
+
|
419
445
|
submsg_gas = gas + Opcodes::GSTIPEND * (value > 0 ? 1 : 0)
|
420
446
|
total_gas = gas + extra_gas
|
421
|
-
|
422
|
-
return vm_exception('OUT OF GAS', needed: total_gas) if s.gas < total_gas
|
423
|
-
|
424
447
|
if ext.get_balance(msg.to) >= value && msg.depth < 1024
|
425
448
|
s.gas -= total_gas
|
426
449
|
|
@@ -454,12 +477,17 @@ module Ethereum
|
|
454
477
|
return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, memin_start, memin_sz)
|
455
478
|
return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, memout_start, memout_sz)
|
456
479
|
|
457
|
-
extra_gas = (value > 0 ? 1 : 0) * Opcodes::GCALLVALUETRANSFER
|
480
|
+
extra_gas = (value > 0 ? 1 : 0) * Opcodes::GCALLVALUETRANSFER +
|
481
|
+
(ext.post_anti_dos_hardfork ? 1 : 0) * Opcodes::CALL_SUPPLEMENTAL_GAS
|
482
|
+
if ext.post_anti_dos_hardfork
|
483
|
+
return vm_exception('OUT OF GAS', needed: extra_gas) if s.gas < extra_gas
|
484
|
+
gas = [gas, max_call_gas(s.gas-extra_gas)].min
|
485
|
+
else
|
486
|
+
return vm_exception('OUT OF GAS', needed: gas+extra_gas) if s.gas < gas+extra_gas
|
487
|
+
end
|
488
|
+
|
458
489
|
submsg_gas = gas + Opcodes::GSTIPEND * (value > 0 ? 1 : 0)
|
459
490
|
total_gas = gas + extra_gas
|
460
|
-
|
461
|
-
return vm_exception('OUT OF GAS', needed: total_gas) if s.gas < total_gas
|
462
|
-
|
463
491
|
if ext.get_balance(msg.to) >= value && msg.depth < 1024
|
464
492
|
s.gas -= total_gas
|
465
493
|
|
@@ -496,6 +524,12 @@ module Ethereum
|
|
496
524
|
s0 = stk.pop
|
497
525
|
to = Utils.zpad_int(s0)[12..-1] # last 20 bytes
|
498
526
|
|
527
|
+
if ext.post_anti_dos_hardfork
|
528
|
+
extra_gas = Opcodes::SUICIDE_SUPPLEMENTAL_GAS +
|
529
|
+
(ext.account_exists(to) ? 0 : 1) * Opcodes::GCALLNEWACCOUNT
|
530
|
+
return vm_exception('OUT OF GAS') unless eat_gas(s, extra_gas)
|
531
|
+
end
|
532
|
+
|
499
533
|
xfer = ext.get_balance(msg.to)
|
500
534
|
ext.set_balance(to, ext.get_balance(to)+xfer)
|
501
535
|
ext.set_balance(msg.to, 0)
|
@@ -550,12 +584,16 @@ module Ethereum
|
|
550
584
|
end
|
551
585
|
|
552
586
|
def vm_exception(error, **kwargs)
|
553
|
-
log_vm_exit.trace
|
587
|
+
if log_vm_exit.trace?
|
588
|
+
log_vm_exit.trace('EXCEPTION', cause: error, **kwargs)
|
589
|
+
end
|
554
590
|
return 0, 0, []
|
555
591
|
end
|
556
592
|
|
557
593
|
def peaceful_exit(cause, gas, data, **kwargs)
|
558
|
-
log_vm_exit.trace
|
594
|
+
if log_vm_exit.trace?
|
595
|
+
log_vm_exit.trace('EXIT', cause: cause, **kwargs)
|
596
|
+
end
|
559
597
|
return 1, gas, data
|
560
598
|
end
|
561
599
|
|
@@ -602,5 +640,19 @@ module Ethereum
|
|
602
640
|
sz * Opcodes::GMEMORY + sz**2 / Opcodes::GQUADRATICMEMDENOM
|
603
641
|
end
|
604
642
|
|
643
|
+
def eat_gas(s, amount)
|
644
|
+
if s.gas < amount
|
645
|
+
s.gas = 0
|
646
|
+
false
|
647
|
+
else
|
648
|
+
s.gas -= amount
|
649
|
+
true
|
650
|
+
end
|
651
|
+
end
|
652
|
+
|
653
|
+
def max_call_gas(gas)
|
654
|
+
gas - (gas / Opcodes::CALL_CHILD_LIMIT_DENOM)
|
655
|
+
end
|
656
|
+
|
605
657
|
end
|
606
658
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-ethereum
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jan Xie
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-11-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rlp
|
@@ -98,16 +98,16 @@ dependencies:
|
|
98
98
|
name: block_logger
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- - "
|
101
|
+
- - ">="
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version:
|
103
|
+
version: 0.1.3
|
104
104
|
type: :runtime
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- - "
|
108
|
+
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version:
|
110
|
+
version: 0.1.3
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: leveldb
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|