radiator 0.3.15 → 0.4.0pre1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +9 -9
- data/README.md +2 -18
- data/Rakefile +11 -2
- data/lib/golos.rb +3 -0
- data/lib/radiator/api.rb +62 -123
- data/lib/radiator/chain.rb +184 -224
- data/lib/radiator/error_parser.rb +3 -5
- data/lib/radiator/logger.rb +4 -2
- data/lib/radiator/mixins/acts_as_poster.rb +124 -0
- data/lib/radiator/mixins/acts_as_voter.rb +50 -0
- data/lib/radiator/mixins/acts_as_wallet.rb +67 -0
- data/lib/radiator/transaction.rb +64 -51
- data/lib/radiator/type/permission.rb +2 -1
- data/lib/radiator/utils.rb +3 -11
- data/lib/radiator/version.rb +1 -1
- data/lib/radiator.rb +3 -1
- data/lib/steem.rb +3 -0
- metadata +8 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b0f7aaa5e12224ae48fdfe1535182583e298d97f
|
4
|
+
data.tar.gz: 5ec823c95e96ad1a44ceb0b77bc08b72aede7f8e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b7736813af94b8ef03e1b80e0f43aa593b194c48f71094e672798d3791eab2f0925ee313d1ea5b8b749cc8b753e0209f9298205038c5ff71b43015289eedc27
|
7
|
+
data.tar.gz: 55248de7d7142432b2d8b3a11a9e536c34915ba52bea098e38988e4c3c180ace704cfe852aa9a65914b6afc702f6bd94a6a831c2e5a1a7b6ee623b31c5e8aa53
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
radiator (0.
|
4
|
+
radiator (0.4.0pre1)
|
5
5
|
awesome_print (~> 1.7, >= 1.7.0)
|
6
6
|
bitcoin-ruby (~> 0.0, >= 0.0.11)
|
7
7
|
ffi (~> 1.9, >= 1.9.18)
|
@@ -16,28 +16,28 @@ GEM
|
|
16
16
|
addressable (2.5.2)
|
17
17
|
public_suffix (>= 2.0.2, < 4.0)
|
18
18
|
awesome_print (1.8.0)
|
19
|
-
bitcoin-ruby (0.0.
|
19
|
+
bitcoin-ruby (0.0.17)
|
20
20
|
connection_pool (2.2.1)
|
21
21
|
crack (0.4.3)
|
22
22
|
safe_yaml (~> 1.0.0)
|
23
23
|
docile (1.1.5)
|
24
|
-
ffi (1.9.
|
24
|
+
ffi (1.9.21)
|
25
25
|
hashdiff (0.3.7)
|
26
|
-
hashie (3.5.
|
26
|
+
hashie (3.5.7)
|
27
27
|
json (2.1.0)
|
28
28
|
little-plugger (1.1.4)
|
29
29
|
logging (2.2.2)
|
30
30
|
little-plugger (~> 1.1)
|
31
31
|
multi_json (~> 1.10)
|
32
|
-
minitest (5.
|
32
|
+
minitest (5.11.3)
|
33
33
|
minitest-line (0.6.4)
|
34
34
|
minitest (~> 5.0)
|
35
35
|
minitest-proveit (1.0.0)
|
36
36
|
minitest (> 5, < 7)
|
37
|
-
multi_json (1.
|
37
|
+
multi_json (1.13.1)
|
38
38
|
net-http-persistent (3.0.0)
|
39
39
|
connection_pool (~> 2.2)
|
40
|
-
public_suffix (3.0.
|
40
|
+
public_suffix (3.0.2)
|
41
41
|
rake (12.3.0)
|
42
42
|
safe_yaml (1.0.4)
|
43
43
|
simplecov (0.15.1)
|
@@ -46,7 +46,7 @@ GEM
|
|
46
46
|
simplecov-html (~> 0.10.0)
|
47
47
|
simplecov-html (0.10.2)
|
48
48
|
vcr (3.0.3)
|
49
|
-
webmock (3.
|
49
|
+
webmock (3.3.0)
|
50
50
|
addressable (>= 2.3.6)
|
51
51
|
crack (>= 0.3.2)
|
52
52
|
hashdiff
|
@@ -68,4 +68,4 @@ DEPENDENCIES
|
|
68
68
|
yard (~> 0.9.9)
|
69
69
|
|
70
70
|
BUNDLED WITH
|
71
|
-
1.16.
|
71
|
+
1.16.1
|
data/README.md
CHANGED
@@ -397,7 +397,7 @@ Radiator supports failover for situations where a node has, for example, become
|
|
397
397
|
|
398
398
|
```ruby
|
399
399
|
options = {
|
400
|
-
|
400
|
+
ur: 'https://api.steemit.com',
|
401
401
|
failover_urls: [
|
402
402
|
'https://api.steemitstage.com',
|
403
403
|
'https://gtg.steem.house:8090'
|
@@ -421,22 +421,6 @@ There is another rare scenario involving `::Transaction` broadcasts that's handl
|
|
421
421
|
tx = Radiator::Transaction.new(wif: wif, recover_transactions_on_error: false)
|
422
422
|
```
|
423
423
|
|
424
|
-
### Golos Failover Examples
|
425
|
-
|
426
|
-
Typically, you only need to pass `chain: :golos` to enable Golos. Failover is enabled by default. If you want to provide your own full nodes, use this format:
|
427
|
-
|
428
|
-
```ruby
|
429
|
-
options = {
|
430
|
-
chain: :golos,
|
431
|
-
url: 'https://ws.golos.io',
|
432
|
-
failover_urls: [
|
433
|
-
'https://api.golos.cf'
|
434
|
-
]
|
435
|
-
}
|
436
|
-
|
437
|
-
api = Radiator::Api.new(options)
|
438
|
-
```
|
439
|
-
|
440
424
|
## Debugging
|
441
425
|
|
442
426
|
To enable debugging, set environment `LOG=DEBUG` before launching your app. E.g.:
|
@@ -501,7 +485,7 @@ Verify your code is not doing too much between blocks.
|
|
501
485
|
<img src="http://www.steemimg.com/images/2016/08/19/RadiatorCoolingFan-54in-Webfdcb1.png" />
|
502
486
|
</center>
|
503
487
|
|
504
|
-
See my previous Ruby How To posts in: [/
|
488
|
+
See my previous Ruby How To posts in: [#radiator](https://steemit.com/created/radiator) [#ruby](https://steemit.com/created/ruby)
|
505
489
|
|
506
490
|
## Get in touch!
|
507
491
|
|
data/Rakefile
CHANGED
@@ -75,8 +75,17 @@ task :test_live_stream, [:chain, :persist] do |t, args|
|
|
75
75
|
o = t.map(&:operations)
|
76
76
|
op_size = o.map(&:size).reduce(0, :+)
|
77
77
|
total_ops += op_size
|
78
|
-
api.get_ops_in_block(n, true) do |vops|
|
79
|
-
|
78
|
+
api.get_ops_in_block(n, true) do |vops, error|
|
79
|
+
if !!error
|
80
|
+
puts "Error on get_ops_in_block for block #{n}"
|
81
|
+
ap error
|
82
|
+
end
|
83
|
+
|
84
|
+
vop_size = if vops.nil?
|
85
|
+
0
|
86
|
+
else
|
87
|
+
vops.size
|
88
|
+
end
|
80
89
|
total_vops += vop_size
|
81
90
|
|
82
91
|
vop_ratio = if total_vops > 0
|
data/lib/golos.rb
CHANGED
data/lib/radiator/api.rb
CHANGED
@@ -151,7 +151,8 @@ module Radiator
|
|
151
151
|
DEFAULT_GOLOS_FAILOVER_URLS = [
|
152
152
|
DEFAULT_GOLOS_URL,
|
153
153
|
'https://api.golos.cf',
|
154
|
-
# not recommended:
|
154
|
+
# not recommended, not all plug-ins enabled:
|
155
|
+
# 'https://ws.goldvoice.club',
|
155
156
|
# 'http://golos-seed.arcange.eu',
|
156
157
|
# not recommended, requires option ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE
|
157
158
|
# 'https://golos-seed.arcange.eu',
|
@@ -245,13 +246,14 @@ module Radiator
|
|
245
246
|
|
246
247
|
Hashie.logger = @hashie_logger
|
247
248
|
@method_names = nil
|
249
|
+
@uri = nil
|
250
|
+
@http_id = nil
|
248
251
|
@http_memo = {}
|
249
252
|
@api_options = options.dup.merge(chain: @chain)
|
250
253
|
@api = nil
|
251
254
|
@block_api = nil
|
252
255
|
@backoff_at = nil
|
253
|
-
|
254
|
-
ObjectSpace.define_finalizer(self, self.class.finalize(@logger, @hashie_logger))
|
256
|
+
@jussi_supported = []
|
255
257
|
end
|
256
258
|
|
257
259
|
# Get a specific block or range of blocks.
|
@@ -287,87 +289,6 @@ module Radiator
|
|
287
289
|
end
|
288
290
|
end
|
289
291
|
|
290
|
-
# Find a specific block.
|
291
|
-
#
|
292
|
-
# Example:
|
293
|
-
#
|
294
|
-
# api = Radiator::Api.new
|
295
|
-
# block = api.find_block(12345678)
|
296
|
-
# transactions = block.transactions
|
297
|
-
#
|
298
|
-
# ... or ...
|
299
|
-
#
|
300
|
-
# api = Radiator::Api.new
|
301
|
-
# transactions = api.find_block(12345678) do |block|
|
302
|
-
# block.transactions
|
303
|
-
# end
|
304
|
-
#
|
305
|
-
# @param block_number [Fixnum]
|
306
|
-
# @param block the block to execute for each result, optional.
|
307
|
-
# @return [Hash]
|
308
|
-
def find_block(block_number, &block)
|
309
|
-
if !!block
|
310
|
-
yield api.get_blocks(block_number).first
|
311
|
-
else
|
312
|
-
api.get_blocks(block_number).first
|
313
|
-
end
|
314
|
-
end
|
315
|
-
|
316
|
-
# Find a specific account.
|
317
|
-
#
|
318
|
-
# Example:
|
319
|
-
#
|
320
|
-
# api = Radiator::Api.new
|
321
|
-
# ned = api.find_account('ned')
|
322
|
-
# vesting_shares = ned.vesting_shares
|
323
|
-
#
|
324
|
-
# ... or ...
|
325
|
-
#
|
326
|
-
# api = Radiator::Api.new
|
327
|
-
# vesting_shares = api.find_account('ned') do |ned|
|
328
|
-
# ned.vesting_shares
|
329
|
-
# end
|
330
|
-
#
|
331
|
-
# @param id [String] Name of the account to find.
|
332
|
-
# @param block the block to execute for each result, optional.
|
333
|
-
# @return [Hash]
|
334
|
-
def find_account(id, &block)
|
335
|
-
if !!block
|
336
|
-
yield api.get_accounts([id]).result.first
|
337
|
-
else
|
338
|
-
api.get_accounts([id]).result.first
|
339
|
-
end
|
340
|
-
end
|
341
|
-
|
342
|
-
# Returns the current base (STEEM) price in the vest asset (VESTS).
|
343
|
-
#
|
344
|
-
def base_per_mvest
|
345
|
-
api.get_dynamic_global_properties do |properties|
|
346
|
-
total_vesting_fund_steem = properties.total_vesting_fund_steem.to_f
|
347
|
-
total_vesting_shares_mvest = properties.total_vesting_shares.to_f / 1e6
|
348
|
-
|
349
|
-
total_vesting_fund_steem / total_vesting_shares_mvest
|
350
|
-
end
|
351
|
-
end
|
352
|
-
|
353
|
-
alias steem_per_mvest base_per_mvest
|
354
|
-
|
355
|
-
# Returns the current base (STEEM) price in the debt asset (SBD).
|
356
|
-
#
|
357
|
-
def base_per_debt
|
358
|
-
get_feed_history do |feed_history|
|
359
|
-
current_median_history = feed_history.current_median_history
|
360
|
-
base = current_median_history.base
|
361
|
-
base = base.split(' ').first.to_f
|
362
|
-
quote = current_median_history.quote
|
363
|
-
quote = quote.split(' ').first.to_f
|
364
|
-
|
365
|
-
(base / quote) * steem_per_mvest
|
366
|
-
end
|
367
|
-
end
|
368
|
-
|
369
|
-
alias steem_per_usd base_per_debt
|
370
|
-
|
371
292
|
# Stops the persistant http connections.
|
372
293
|
#
|
373
294
|
def shutdown
|
@@ -384,6 +305,18 @@ module Radiator
|
|
384
305
|
@api = nil
|
385
306
|
@block_api.shutdown if !!@block_api && @block_api != self
|
386
307
|
@block_api = nil
|
308
|
+
|
309
|
+
if !!@logger && defined?(@logger.close)
|
310
|
+
if defined?(@logger.closed?)
|
311
|
+
@logger.close unless @logger.closed?
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
if !!@hashie_logger && defined?(@hashie_logger.close)
|
316
|
+
if defined?(@hashie_logger.closed?)
|
317
|
+
@hashie_logger.close unless @hashie_logger.closed?
|
318
|
+
end
|
319
|
+
end
|
387
320
|
end
|
388
321
|
|
389
322
|
# @private
|
@@ -412,12 +345,21 @@ module Radiator
|
|
412
345
|
current_rpc_id = rpc_id
|
413
346
|
method_name = [api_name, m].join('.')
|
414
347
|
response = nil
|
415
|
-
options =
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
348
|
+
options = if jussi_supported? && api_name == :database_api
|
349
|
+
{
|
350
|
+
jsonrpc: "2.0",
|
351
|
+
params: args,
|
352
|
+
id: current_rpc_id,
|
353
|
+
method: m
|
354
|
+
}
|
355
|
+
else
|
356
|
+
{
|
357
|
+
jsonrpc: "2.0",
|
358
|
+
params: [api_name, m, args],
|
359
|
+
id: current_rpc_id,
|
360
|
+
method: "call"
|
361
|
+
}
|
362
|
+
end
|
421
363
|
|
422
364
|
tries = 0
|
423
365
|
timestamp = Time.now.utc
|
@@ -425,10 +367,6 @@ module Radiator
|
|
425
367
|
loop do
|
426
368
|
tries += 1
|
427
369
|
|
428
|
-
if tries > 5 && flappy? && !check_file_open?
|
429
|
-
raise ApiError, 'PANIC: Out of file resources'
|
430
|
-
end
|
431
|
-
|
432
370
|
begin
|
433
371
|
if tries > 1 && @recover_transactions_on_error && api_name == :network_broadcast_api
|
434
372
|
signatures, exp = extract_signatures(options)
|
@@ -450,6 +388,8 @@ module Radiator
|
|
450
388
|
elsif !response.kind_of? Net::HTTPSuccess
|
451
389
|
warning "Unexpected response (code: #{response.code}): #{response.inspect}, retrying ...", method_name, true
|
452
390
|
else
|
391
|
+
detect_jussi(response)
|
392
|
+
|
453
393
|
case response.code
|
454
394
|
when '200'
|
455
395
|
body = response.body
|
@@ -475,12 +415,18 @@ module Radiator
|
|
475
415
|
end
|
476
416
|
rescue Net::HTTP::Persistent::Error => e
|
477
417
|
warning "Unable to perform request: #{e} :: #{!!e.cause ? "cause: #{e.cause.message}" : ''}, retrying ...", method_name, true
|
418
|
+
rescue ConnectionPool::Error => e
|
419
|
+
warning "Connection Pool Error (#{e.message}), retrying ...", method_name, true
|
478
420
|
rescue Errno::ECONNREFUSED => e
|
479
421
|
warning 'Connection refused, retrying ...', method_name, true
|
480
422
|
rescue Errno::EADDRNOTAVAIL => e
|
481
423
|
warning 'Node not available, retrying ...', method_name, true
|
482
424
|
rescue Errno::ECONNRESET => e
|
483
425
|
warning "Connection Reset (#{e.message}), retrying ...", method_name, true
|
426
|
+
rescue Errno::EBUSY => e
|
427
|
+
warning "Resource busy (#{e.message}), retrying ...", method_name, true
|
428
|
+
rescue Errno::ENETDOWN => e
|
429
|
+
warning "Network down (#{e.message}), retrying ...", method_name, true
|
484
430
|
rescue Net::ReadTimeout => e
|
485
431
|
warning 'Node read timeout, retrying ...', method_name, true
|
486
432
|
rescue Net::OpenTimeout => e
|
@@ -626,6 +572,21 @@ module Radiator
|
|
626
572
|
end
|
627
573
|
end
|
628
574
|
|
575
|
+
def jussi_supported?(url = @url)
|
576
|
+
@jussi_supported.include? url
|
577
|
+
end
|
578
|
+
|
579
|
+
def detect_jussi(response)
|
580
|
+
return if jussi_supported?(@url)
|
581
|
+
|
582
|
+
jussi_response_id = response['x-jussi-response-id']
|
583
|
+
|
584
|
+
if !!jussi_response_id
|
585
|
+
debug "Found a node that supports jussi: #{@url}"
|
586
|
+
@jussi_supported << @url
|
587
|
+
end
|
588
|
+
end
|
589
|
+
|
629
590
|
def recover_transaction(signatures, expected_rpc_id, after)
|
630
591
|
debug "Looking for signatures: #{signatures.map{|s| s[0..5]}} since: #{after}"
|
631
592
|
|
@@ -643,25 +604,21 @@ module Radiator
|
|
643
604
|
# but we also give up once the block time is before the `after` argument.
|
644
605
|
|
645
606
|
api.get_blocks(block_range) do |block, block_num|
|
646
|
-
count += 1
|
647
|
-
raise ApiError, "Race condition detected on remote node at: #{block_num}" if block.nil?
|
648
|
-
|
649
|
-
# TODO Some blockchains (like Golos) do not have transaction_ids. In
|
650
|
-
# the future, it would be better to decode the operation and signature
|
651
|
-
# into the transaction id.
|
652
|
-
# See: https://github.com/steemit/steem/issues/187
|
653
|
-
# See: https://github.com/GolosChain/golos/issues/281
|
654
607
|
unless defined? block.transaction_ids
|
655
|
-
|
656
|
-
|
608
|
+
# Happens on Golos, see: https://github.com/GolosChain/golos/issues/281
|
609
|
+
error "Blockchain does not provide transaction ids in blocks, giving up."
|
610
|
+
return nil
|
657
611
|
end
|
658
612
|
|
613
|
+
count += 1
|
614
|
+
raise ApiError, "Race condition detected on remote node at: #{block_num}" if block.nil?
|
615
|
+
|
659
616
|
timestamp = Time.parse(block.timestamp + 'Z')
|
660
617
|
break if timestamp < after
|
661
618
|
|
662
619
|
block.transactions.each_with_index do |tx, index|
|
663
620
|
next unless ((tx['signatures'] || []) & signatures).any?
|
664
|
-
|
621
|
+
|
665
622
|
debug "Found transaction #{count} block(s) ago; took #{(Time.now.utc - start)} seconds to scan."
|
666
623
|
|
667
624
|
return {
|
@@ -783,12 +740,6 @@ module Radiator
|
|
783
740
|
end
|
784
741
|
end
|
785
742
|
|
786
|
-
def check_file_open?
|
787
|
-
File.exists?('.')
|
788
|
-
rescue
|
789
|
-
false
|
790
|
-
end
|
791
|
-
|
792
743
|
def backoff
|
793
744
|
shutdown
|
794
745
|
bump_failover if flappy? || !healthy?(uri)
|
@@ -803,17 +754,5 @@ module Radiator
|
|
803
754
|
@backoff_sleep = nil
|
804
755
|
end
|
805
756
|
end
|
806
|
-
|
807
|
-
def self.finalize(logger, hashie_logger)
|
808
|
-
proc {
|
809
|
-
if !!logger && defined?(logger.close) && !logger.closed?
|
810
|
-
logger.close
|
811
|
-
end
|
812
|
-
|
813
|
-
if !!hashie_logger && defined?(hashie_logger.close) && !hashie_logger.closed?
|
814
|
-
hashie_logger.close
|
815
|
-
end
|
816
|
-
}
|
817
|
-
end
|
818
757
|
end
|
819
758
|
end
|