radiator 0.4.0pre2 → 0.4.0pre3
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/Gemfile.lock +10 -4
- data/Rakefile +8 -5
- data/lib/radiator/account_history_api.rb +15 -0
- data/lib/radiator/api.rb +96 -19
- data/lib/radiator/chain.rb +2 -2
- data/lib/radiator/condenser_api.rb +89 -15
- data/lib/radiator/error_parser.rb +82 -25
- data/lib/radiator/operation.rb +1 -0
- data/lib/radiator/stream.rb +49 -17
- data/lib/radiator/tag_api.rb +1 -1
- data/lib/radiator/transaction.rb +1 -1
- data/lib/radiator/type/amount.rb +34 -12
- data/lib/radiator/utils.rb +2 -0
- data/lib/radiator/version.rb +1 -1
- data/lib/radiator.rb +1 -0
- data/radiator.gemspec +2 -1
- metadata +28 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b3f2fba4fb406179bfcb988e1d1669615b392569
|
4
|
+
data.tar.gz: b721efe6e8751fef45eb40a31e1799c3c695378f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 439ff52f2860e63571e6be0cdd53431f80ceaf4e8671c6754ab5db47a41a4cf87447a6b23ae8ce69209990e5064651619bdb897e27d75c507e9b107ae8123f98
|
7
|
+
data.tar.gz: c62becd00245e1478d287bd4a62b013608f0138078626969225947ab4f2322daec363ed54af443d1c5f7b652775e1833bf6a02871af36f269fb8bd3ef9089c54
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
radiator (0.4.
|
4
|
+
radiator (0.4.0pre3)
|
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)
|
@@ -17,11 +17,12 @@ GEM
|
|
17
17
|
public_suffix (>= 2.0.2, < 4.0)
|
18
18
|
awesome_print (1.8.0)
|
19
19
|
bitcoin-ruby (0.0.18)
|
20
|
+
coderay (1.1.2)
|
20
21
|
connection_pool (2.2.1)
|
21
22
|
crack (0.4.3)
|
22
23
|
safe_yaml (~> 1.0.0)
|
23
24
|
docile (1.1.5)
|
24
|
-
ffi (1.9.
|
25
|
+
ffi (1.9.23)
|
25
26
|
hashdiff (0.3.7)
|
26
27
|
hashie (3.5.7)
|
27
28
|
json (2.1.0)
|
@@ -29,6 +30,7 @@ GEM
|
|
29
30
|
logging (2.2.2)
|
30
31
|
little-plugger (~> 1.1)
|
31
32
|
multi_json (~> 1.10)
|
33
|
+
method_source (0.9.0)
|
32
34
|
minitest (5.11.3)
|
33
35
|
minitest-line (0.6.4)
|
34
36
|
minitest (~> 5.0)
|
@@ -37,8 +39,11 @@ GEM
|
|
37
39
|
multi_json (1.13.1)
|
38
40
|
net-http-persistent (3.0.0)
|
39
41
|
connection_pool (~> 2.2)
|
42
|
+
pry (0.11.3)
|
43
|
+
coderay (~> 1.1.0)
|
44
|
+
method_source (~> 0.9.0)
|
40
45
|
public_suffix (3.0.2)
|
41
|
-
rake (12.3.
|
46
|
+
rake (12.3.1)
|
42
47
|
safe_yaml (1.0.4)
|
43
48
|
simplecov (0.15.1)
|
44
49
|
docile (~> 1.1.0)
|
@@ -57,9 +62,10 @@ PLATFORMS
|
|
57
62
|
|
58
63
|
DEPENDENCIES
|
59
64
|
bundler (~> 1.15, >= 1.15.4)
|
60
|
-
minitest (~> 5.
|
65
|
+
minitest (~> 5.10, >= 5.10.3)
|
61
66
|
minitest-line (~> 0.6.3)
|
62
67
|
minitest-proveit (~> 1.0, >= 1.0.0)
|
68
|
+
pry (~> 0.11, >= 0.11.3)
|
63
69
|
radiator!
|
64
70
|
rake (~> 12.1, >= 12.1.0)
|
65
71
|
simplecov (~> 0.15.1)
|
data/Rakefile
CHANGED
@@ -3,6 +3,7 @@ require 'rake/testtask'
|
|
3
3
|
require 'yard'
|
4
4
|
require 'radiator'
|
5
5
|
require 'awesome_print'
|
6
|
+
require 'pry'
|
6
7
|
|
7
8
|
Rake::TestTask.new(:test) do |t|
|
8
9
|
t.libs << 'test'
|
@@ -21,9 +22,11 @@ end
|
|
21
22
|
|
22
23
|
task default: :test
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
25
|
+
namespace :clean do
|
26
|
+
desc 'Deletes test/fixtures/vcr_cassettes/*.yml so they can be rebuilt fresh.'
|
27
|
+
task :vcr do |t|
|
28
|
+
exec 'rm -v test/fixtures/vcr_cassettes/*.yml'
|
29
|
+
end
|
27
30
|
end
|
28
31
|
|
29
32
|
desc 'Tests the ability to broadcast live data. This task broadcasts a claim_reward_balance of 0.0000001 VESTS.'
|
@@ -57,7 +60,7 @@ task :test_live_stream, [:chain, :persist] do |t, args|
|
|
57
60
|
chain = args[:chain] || 'steem'
|
58
61
|
persist = (args[:persist] || 'true') == 'true'
|
59
62
|
last_block_number = 0
|
60
|
-
options = {chain: chain, persist: persist
|
63
|
+
options = {chain: chain, persist: persist}
|
61
64
|
total_ops = 0.0
|
62
65
|
total_vops = 0.0
|
63
66
|
elapsed = 0
|
@@ -101,7 +104,7 @@ task :test_live_stream, [:chain, :persist] do |t, args|
|
|
101
104
|
else
|
102
105
|
# This should not happen. If it does, there's likely a bug in Radiator.
|
103
106
|
|
104
|
-
puts "Error, last block
|
107
|
+
puts "Error, last block number was #{last_block_number}, did not expect #{n}."
|
105
108
|
end
|
106
109
|
|
107
110
|
last_block_number = n
|
data/lib/radiator/api.rb
CHANGED
@@ -141,11 +141,18 @@ module Radiator
|
|
141
141
|
DEFAULT_STEEM_FAILOVER_URLS = [
|
142
142
|
DEFAULT_STEEM_URL,
|
143
143
|
'https://api.steemitstage.com',
|
144
|
+
'https://appbasetest.timcliff.com',
|
144
145
|
'https://gtg.steem.house:8090',
|
146
|
+
'https://api.steem.house',
|
145
147
|
'https://seed.bitcoiner.me',
|
146
148
|
'https://steemd.minnowsupportproject.org',
|
147
149
|
'https://steemd.privex.io',
|
148
|
-
'https://rpc.steemliberator.com'
|
150
|
+
'https://rpc.steemliberator.com',
|
151
|
+
'https://rpc.curiesteem.com',
|
152
|
+
'https://rpc.buildteam.io',
|
153
|
+
'https://steemd.pevo.science',
|
154
|
+
'https://rpc.steemviz.com',
|
155
|
+
'https://steemd.steemgigs.org'
|
149
156
|
]
|
150
157
|
|
151
158
|
DEFAULT_GOLOS_FAILOVER_URLS = [
|
@@ -242,6 +249,7 @@ module Radiator
|
|
242
249
|
true
|
243
250
|
end
|
244
251
|
|
252
|
+
@persist_error_count = 0
|
245
253
|
@persist = if options.keys.include? :persist
|
246
254
|
options[:persist]
|
247
255
|
else
|
@@ -294,11 +302,11 @@ module Radiator
|
|
294
302
|
|
295
303
|
if !!block
|
296
304
|
block_number.each do |i|
|
297
|
-
yield block_api.get_block(i).result, i
|
305
|
+
yield block_api.get_block(block_num: i).result, i
|
298
306
|
end
|
299
307
|
else
|
300
308
|
block_number.map do |i|
|
301
|
-
block_api.get_block(i).result
|
309
|
+
block_api.get_block(block_num: i).result
|
302
310
|
end
|
303
311
|
end
|
304
312
|
end
|
@@ -340,7 +348,8 @@ module Radiator
|
|
340
348
|
# @private
|
341
349
|
def method_names
|
342
350
|
return @method_names if !!@method_names
|
343
|
-
|
351
|
+
return CondenserApi::METHOD_NAMES if api_name == :condenser_api
|
352
|
+
|
344
353
|
@method_names = Radiator::Api.methods(api_name).map do |e|
|
345
354
|
e['method'].to_sym
|
346
355
|
end
|
@@ -348,12 +357,12 @@ module Radiator
|
|
348
357
|
|
349
358
|
# @private
|
350
359
|
def api_name
|
351
|
-
:
|
360
|
+
:condenser_api
|
352
361
|
end
|
353
362
|
|
354
363
|
# @private
|
355
364
|
def respond_to_missing?(m, include_private = false)
|
356
|
-
method_names.include?(m.to_sym)
|
365
|
+
method_names.nil? ? false : method_names.include?(m.to_sym)
|
357
366
|
end
|
358
367
|
|
359
368
|
# @private
|
@@ -363,19 +372,25 @@ module Radiator
|
|
363
372
|
current_rpc_id = rpc_id
|
364
373
|
method_name = [api_name, m].join('.')
|
365
374
|
response = nil
|
366
|
-
options = if
|
375
|
+
options = if api_name == :condenser_api
|
367
376
|
{
|
368
377
|
jsonrpc: "2.0",
|
378
|
+
method: method_name,
|
369
379
|
params: args,
|
370
380
|
id: current_rpc_id,
|
371
|
-
method: m
|
372
381
|
}
|
373
382
|
else
|
383
|
+
rpc_args = if args.empty?
|
384
|
+
{}
|
385
|
+
else
|
386
|
+
args.first
|
387
|
+
end
|
388
|
+
|
374
389
|
{
|
375
390
|
jsonrpc: "2.0",
|
376
|
-
|
391
|
+
method: method_name,
|
392
|
+
params: rpc_args,
|
377
393
|
id: current_rpc_id,
|
378
|
-
method: "call"
|
379
394
|
}
|
380
395
|
end
|
381
396
|
|
@@ -414,7 +429,15 @@ module Radiator
|
|
414
429
|
response = JSON[body]
|
415
430
|
|
416
431
|
if response['id'] != options[:id]
|
417
|
-
|
432
|
+
if !!response['id']
|
433
|
+
warning "Unexpected rpc_id (expected: #{options[:id]}, got: #{response['id']}), retrying ...", method_name, true
|
434
|
+
else
|
435
|
+
# The node has broken the jsonrpc spec.
|
436
|
+
warning "Node did not provide jsonrpc id (expected: #{options[:id]}, got: nothing, retrying ...", method_name, true
|
437
|
+
end
|
438
|
+
if response.keys.include?('error')
|
439
|
+
handle_error(response, options, method_name, tries)
|
440
|
+
end
|
418
441
|
elsif response.keys.include?('error')
|
419
442
|
handle_error(response, options, method_name, tries)
|
420
443
|
else
|
@@ -433,6 +456,12 @@ module Radiator
|
|
433
456
|
end
|
434
457
|
rescue Net::HTTP::Persistent::Error => e
|
435
458
|
warning "Unable to perform request: #{e} :: #{!!e.cause ? "cause: #{e.cause.message}" : ''}, retrying ...", method_name, true
|
459
|
+
if e.cause.class == Net::HTTPMethodNotAllowed
|
460
|
+
warning 'Node upstream is misconfigured.'
|
461
|
+
drop_current_failover_url method_name
|
462
|
+
end
|
463
|
+
|
464
|
+
@persist_error_count += 1
|
436
465
|
rescue ConnectionPool::Error => e
|
437
466
|
warning "Connection Pool Error (#{e.message}), retrying ...", method_name, true
|
438
467
|
rescue Errno::ECONNREFUSED => e
|
@@ -457,6 +486,7 @@ module Radiator
|
|
457
486
|
warning "Socket Error (#{e.message}), retrying ...", method_name, true
|
458
487
|
rescue JSON::ParserError => e
|
459
488
|
warning "JSON Parse Error (#{e.message}), retrying ...", method_name, true
|
489
|
+
drop_current_failover_url method_name if tries > 5
|
460
490
|
response = nil
|
461
491
|
rescue ApiError => e
|
462
492
|
warning "ApiError (#{e.message}), retrying ...", method_name, true
|
@@ -466,8 +496,23 @@ module Radiator
|
|
466
496
|
end
|
467
497
|
|
468
498
|
if !!response
|
499
|
+
@persist_error_count = 0
|
500
|
+
|
469
501
|
if !!block
|
470
|
-
|
502
|
+
if api_name == :condenser_api
|
503
|
+
return yield(response.result, response.error, response.id)
|
504
|
+
else
|
505
|
+
if defined?(response.result.size) && response.result.size == 0
|
506
|
+
return yield(nil, response.error, response.id)
|
507
|
+
elsif (
|
508
|
+
defined?(response.result.size) && response.result.size == 1 &&
|
509
|
+
defined?(response.result.values)
|
510
|
+
)
|
511
|
+
return yield(response.result.values.first, response.error, response.id)
|
512
|
+
else
|
513
|
+
return yield(response.result, response.error, response.id)
|
514
|
+
end
|
515
|
+
end
|
471
516
|
else
|
472
517
|
return response
|
473
518
|
end
|
@@ -525,12 +570,16 @@ module Radiator
|
|
525
570
|
http
|
526
571
|
end
|
527
572
|
|
573
|
+
def api_options
|
574
|
+
@api_options.merge(failover_urls: @failover_urls, logger: @logger, hashie_logger: @hashie_logger)
|
575
|
+
end
|
576
|
+
|
528
577
|
def api
|
529
|
-
@api ||= self.class == Api ? self : Api.new(
|
578
|
+
@api ||= self.class == Api ? self : Api.new(api_options)
|
530
579
|
end
|
531
580
|
|
532
581
|
def block_api
|
533
|
-
@block_api ||= self.class == BlockApi ? self : BlockApi.new(
|
582
|
+
@block_api ||= self.class == BlockApi ? self : BlockApi.new(api_options)
|
534
583
|
end
|
535
584
|
|
536
585
|
def rpc_id
|
@@ -549,7 +598,7 @@ module Radiator
|
|
549
598
|
def http
|
550
599
|
return @http_memo[http_id] if @http_memo.keys.include? http_id
|
551
600
|
|
552
|
-
@http_memo[http_id] = if @persist
|
601
|
+
@http_memo[http_id] = if @persist && @persist_error_count < 10
|
553
602
|
idempotent = api_name != :network_broadcast_api
|
554
603
|
|
555
604
|
http = if defined? Net::HTTP::Persistent::DEFAULT_POOL_SIZE
|
@@ -683,13 +732,15 @@ module Radiator
|
|
683
732
|
!!@backoff_at && Time.now.utc - @backoff_at < 300
|
684
733
|
end
|
685
734
|
|
735
|
+
# Note, this methods only removes the uri.to_s if present but it does not
|
736
|
+
# call bump_failover, in order to avoid a race condition.
|
686
737
|
def drop_current_failover_url(prefix)
|
687
738
|
if @preferred_failover_urls.size == 1
|
688
739
|
warning "Node #{uri} appears to be misconfigured but no other node is available, retrying ...", prefix
|
689
740
|
else
|
690
741
|
warning "Removing misconfigured node from failover urls: #{uri}, retrying ...", prefix
|
691
|
-
@preferred_failover_urls.delete(uri)
|
692
|
-
@failover_urls.delete(uri)
|
742
|
+
@preferred_failover_urls.delete(uri.to_s)
|
743
|
+
@failover_urls.delete(uri.to_s)
|
693
744
|
end
|
694
745
|
end
|
695
746
|
|
@@ -697,13 +748,14 @@ module Radiator
|
|
697
748
|
parser = ErrorParser.new(response)
|
698
749
|
_signatures, exp = extract_signatures(request_options)
|
699
750
|
|
700
|
-
if (!!exp && exp < Time.now.utc) || tries > 2
|
751
|
+
if (!!exp && exp < Time.now.utc) || (tries > 2 && !parser.node_degraded?)
|
701
752
|
# Whatever the error was, it is already expired or tried too much. No
|
702
753
|
# need to try to recover.
|
703
754
|
|
704
755
|
debug "Error code #{parser} but transaction already expired or too many tries, giving up (attempt: #{tries})."
|
705
756
|
elsif parser.can_retry?
|
706
757
|
drop_current_failover_url method_name if !!exp && parser.expiry?
|
758
|
+
drop_current_failover_url method_name if parser.node_degraded?
|
707
759
|
debug "Error code #{parser} (attempt: #{tries}), retrying ..."
|
708
760
|
return nil
|
709
761
|
end
|
@@ -750,7 +802,32 @@ module Radiator
|
|
750
802
|
|
751
803
|
# Also note, this check is done **without** net-http-persistent.
|
752
804
|
|
753
|
-
|
805
|
+
response = open(url + HEALTH_URI)
|
806
|
+
response = JSON[response.read]
|
807
|
+
|
808
|
+
if !!response['error']
|
809
|
+
if !!response['error']['data']
|
810
|
+
if !!response['error']['data']['message']
|
811
|
+
error "#{url} error: #{response['error']['data']['message']}"
|
812
|
+
end
|
813
|
+
elsif !!response['error']['message']
|
814
|
+
error "#{url} error: #{response['error']['message']}"
|
815
|
+
else
|
816
|
+
error "#{url} error: #{response['error']}"
|
817
|
+
end
|
818
|
+
|
819
|
+
false
|
820
|
+
elsif response['status'] == 'OK'
|
821
|
+
true
|
822
|
+
else
|
823
|
+
error "#{url} status: #{response['status']}"
|
824
|
+
|
825
|
+
false
|
826
|
+
end
|
827
|
+
rescue JSON::ParserError
|
828
|
+
# No JSON, but also no HTTP error code, so we're OK.
|
829
|
+
|
830
|
+
true
|
754
831
|
rescue => e
|
755
832
|
error "Health check failure for #{url}: #{e.inspect}"
|
756
833
|
sleep 0.2
|
data/lib/radiator/chain.rb
CHANGED
@@ -177,7 +177,7 @@ module Radiator
|
|
177
177
|
|
178
178
|
until count == followers.size
|
179
179
|
count = followers.size
|
180
|
-
follow_api.get_followers(account_name, followers.last, 'blog', 1000) do |follows, err|
|
180
|
+
follow_api.get_followers(account: account_name, start: followers.last, type: 'blog', limit: 1000) do |follows, err|
|
181
181
|
raise ChainError, ErrorParser.new(err) if !!err
|
182
182
|
|
183
183
|
followers += follows.map(&:follower)
|
@@ -200,7 +200,7 @@ module Radiator
|
|
200
200
|
|
201
201
|
until count == following.size
|
202
202
|
count = following.size
|
203
|
-
follow_api.get_following(account_name, following.last, 'blog', 100) do |follows, err|
|
203
|
+
follow_api.get_following(account: account_name, start: following.last, type: 'blog', limit: 100) do |follows, err|
|
204
204
|
raise ChainError, ErrorParser.new(err) if !!err
|
205
205
|
|
206
206
|
following += follows.map(&:following)
|
@@ -1,21 +1,95 @@
|
|
1
1
|
module Radiator
|
2
2
|
class CondenserApi < Api
|
3
|
+
METHOD_NAMES = [
|
4
|
+
:broadcast_block,
|
5
|
+
:broadcast_transaction,
|
6
|
+
:broadcast_transaction_synchronous,
|
7
|
+
:get_account_bandwidth,
|
8
|
+
:get_account_count,
|
9
|
+
:get_account_history,
|
10
|
+
:get_account_references,
|
11
|
+
:get_account_reputations,
|
12
|
+
:get_account_votes,
|
13
|
+
:get_accounts,
|
14
|
+
:get_active_votes,
|
15
|
+
:get_active_witnesses,
|
16
|
+
:get_block,
|
17
|
+
:get_block_header,
|
18
|
+
:get_blog,
|
19
|
+
:get_blog_authors,
|
20
|
+
:get_blog_entries,
|
21
|
+
:get_chain_properties,
|
22
|
+
:get_comment_discussions_by_payout,
|
23
|
+
:get_config,
|
24
|
+
:get_content,
|
25
|
+
:get_content_replies,
|
26
|
+
:get_conversion_requests,
|
27
|
+
:get_current_median_history_price,
|
28
|
+
:get_discussions_by_active,
|
29
|
+
:get_discussions_by_author_before_date,
|
30
|
+
:get_discussions_by_blog,
|
31
|
+
:get_discussions_by_cashout,
|
32
|
+
:get_discussions_by_children,
|
33
|
+
:get_discussions_by_comments,
|
34
|
+
:get_discussions_by_created,
|
35
|
+
:get_discussions_by_feed,
|
36
|
+
:get_discussions_by_hot,
|
37
|
+
:get_discussions_by_promoted,
|
38
|
+
:get_discussions_by_trending,
|
39
|
+
:get_discussions_by_votes,
|
40
|
+
:get_dynamic_global_properties,
|
41
|
+
:get_escrow,
|
42
|
+
:get_expiring_vesting_delegations,
|
43
|
+
:get_feed,
|
44
|
+
:get_feed_entries,
|
45
|
+
:get_feed_history,
|
46
|
+
:get_follow_count,
|
47
|
+
:get_followers,
|
48
|
+
:get_following,
|
49
|
+
:get_hardfork_version,
|
50
|
+
:get_key_references,
|
51
|
+
:get_market_history,
|
52
|
+
:get_market_history_buckets,
|
53
|
+
:get_next_scheduled_hardfork,
|
54
|
+
:get_open_orders,
|
55
|
+
:get_ops_in_block,
|
56
|
+
:get_order_book,
|
57
|
+
:get_owner_history,
|
58
|
+
:get_post_discussions_by_payout,
|
59
|
+
:get_potential_signatures,
|
60
|
+
:get_reblogged_by,
|
61
|
+
:get_recent_trades,
|
62
|
+
:get_recovery_request,
|
63
|
+
:get_replies_by_last_update,
|
64
|
+
:get_required_signatures,
|
65
|
+
:get_reward_fund,
|
66
|
+
:get_savings_withdraw_from,
|
67
|
+
:get_savings_withdraw_to,
|
68
|
+
:get_state,
|
69
|
+
:get_tags_used_by_author,
|
70
|
+
:get_ticker,
|
71
|
+
:get_trade_history,
|
72
|
+
:get_transaction,
|
73
|
+
:get_transaction_hex,
|
74
|
+
:get_trending_tags,
|
75
|
+
:get_version,
|
76
|
+
:get_vesting_delegations,
|
77
|
+
:get_volume,
|
78
|
+
:get_withdraw_routes,
|
79
|
+
:get_witness_by_account,
|
80
|
+
:get_witness_count,
|
81
|
+
:get_witness_schedule,
|
82
|
+
:get_witnesses,
|
83
|
+
:get_witnesses_by_vote,
|
84
|
+
:lookup_account_names,
|
85
|
+
:lookup_accounts,
|
86
|
+
:lookup_witness_accounts,
|
87
|
+
:verify_account_authority,
|
88
|
+
:verify_authority
|
89
|
+
].freeze
|
90
|
+
|
3
91
|
def method_names
|
4
|
-
|
5
|
-
:get_state,
|
6
|
-
:get_next_scheduled_hardfork,
|
7
|
-
:get_reward_fund,
|
8
|
-
:get_accounts,
|
9
|
-
:lookup_account_names,
|
10
|
-
:lookup_accounts,
|
11
|
-
:get_account_count,
|
12
|
-
:get_savings_withdraw_to,
|
13
|
-
:get_witnesses,
|
14
|
-
:get_witness_count,
|
15
|
-
:get_open_orders,
|
16
|
-
:get_account_votes,
|
17
|
-
:lookup_witness_accounts
|
18
|
-
].freeze
|
92
|
+
METHOD_NAMES
|
19
93
|
end
|
20
94
|
|
21
95
|
def api_name
|
@@ -6,11 +6,12 @@ module Radiator
|
|
6
6
|
|
7
7
|
attr_reader :response, :error, :error_code, :error_message,
|
8
8
|
:api_name, :api_method, :api_params,
|
9
|
-
:expiry, :can_retry, :can_reprepare, :trx_id, :debug
|
9
|
+
:expiry, :can_retry, :can_reprepare, :node_degraded, :trx_id, :debug
|
10
10
|
|
11
11
|
alias expiry? expiry
|
12
12
|
alias can_retry? can_retry
|
13
13
|
alias can_reprepare? can_reprepare
|
14
|
+
alias node_degraded? node_degraded
|
14
15
|
|
15
16
|
REPREPARE_WHITELIST = [
|
16
17
|
'is_canonical( c ): signature is not canonical',
|
@@ -56,41 +57,62 @@ module Radiator
|
|
56
57
|
else
|
57
58
|
response
|
58
59
|
end
|
59
|
-
|
60
|
+
|
60
61
|
begin
|
61
|
-
if
|
62
|
+
if !!@error['data']
|
63
|
+
# These are, by far, the more interesting errors, so we try to pull
|
64
|
+
# them out first, if possible.
|
65
|
+
|
66
|
+
@error_code = @error['data']['code']
|
67
|
+
stacks = @error['data']['stack']
|
68
|
+
stack_formats = nil
|
69
|
+
|
70
|
+
@error_message = if !!stacks
|
71
|
+
stack_formats = stacks.map { |s| s['format'] }
|
72
|
+
stack_datum = stacks.map { |s| s['data'] }
|
73
|
+
data_call_method = stack_datum.find { |data| data['call.method'] == 'call' }
|
74
|
+
data_name = stack_datum.find { |data| !!data['name'] }
|
75
|
+
|
76
|
+
# See if we can recover a transaction id out of this hot mess.
|
77
|
+
data_trx_ix = stack_datum.find { |data| !!data['trx_ix'] }
|
78
|
+
@trx_id = data_trx_ix['trx_ix'] if !!data_trx_ix
|
79
|
+
|
80
|
+
stack_formats.reject(&:empty?).join('; ')
|
81
|
+
else
|
82
|
+
@error_code ||= @error['code']
|
83
|
+
@error['message']
|
84
|
+
end
|
85
|
+
|
86
|
+
@api_name, @api_method, @api_params = if !!data_call_method
|
87
|
+
@api_name = data_call_method['call.params']
|
88
|
+
end
|
89
|
+
else
|
90
|
+
@error_code = ['code']
|
91
|
+
@error_message = ['message']
|
62
92
|
@expiry = false
|
63
93
|
@can_retry = false
|
64
94
|
@can_reprepare = false
|
65
|
-
|
66
|
-
return
|
67
|
-
end
|
68
|
-
|
69
|
-
@error_code = @error['data']['code']
|
70
|
-
stacks = @error['data']['stack']
|
71
|
-
stack_formats = stacks.map { |s| s['format'] }
|
72
|
-
stack_datum = stacks.map { |s| s['data'] }
|
73
|
-
data_call_method = stack_datum.find { |data| data['call.method'] == 'call' }
|
74
|
-
|
75
|
-
@error_message = stack_formats.reject(&:empty?).join('; ')
|
76
|
-
|
77
|
-
@api_name, @api_method, @api_params = if !!data_call_method
|
78
|
-
@api_name = data_call_method['call.params']
|
79
95
|
end
|
80
96
|
|
81
|
-
# See if we can recover a transaction id out of this hot mess.
|
82
|
-
data_trx_ix = stack_datum.find { |data| !!data['trx_ix'] }
|
83
|
-
@trx_id = data_trx_ix['trx_ix'] if !!data_trx_ix
|
84
|
-
|
85
97
|
case @error_code
|
86
|
-
when
|
98
|
+
when -32003
|
99
|
+
if error_match?('Unable to acquire database lock')
|
100
|
+
@expiry = false
|
101
|
+
@can_retry = true
|
102
|
+
@can_reprepare = true
|
103
|
+
end
|
104
|
+
when -32000
|
87
105
|
@expiry = false
|
88
|
-
@can_retry =
|
106
|
+
@can_retry = coerce_backtrace
|
89
107
|
@can_reprepare = if @api_name == 'network_broadcast_api'
|
90
|
-
(
|
108
|
+
error_match(REPREPARE_WHITELIST)
|
91
109
|
else
|
92
110
|
false
|
93
111
|
end
|
112
|
+
when 10
|
113
|
+
@expiry = false
|
114
|
+
@can_retry = coerce_backtrace
|
115
|
+
@can_reprepare = !!stack_formats && (stack_formats & REPREPARE_WHITELIST).any?
|
94
116
|
when 13
|
95
117
|
@error_message = @error['data']['message']
|
96
118
|
@expiry = false
|
@@ -126,7 +148,7 @@ module Radiator
|
|
126
148
|
@can_reprepare = false
|
127
149
|
end
|
128
150
|
rescue => e
|
129
|
-
ap
|
151
|
+
ap error_parser_exception: e, original_response: response#, backtrace: e.backtrace
|
130
152
|
|
131
153
|
@expiry = false
|
132
154
|
@can_retry = false
|
@@ -134,6 +156,41 @@ module Radiator
|
|
134
156
|
end
|
135
157
|
end
|
136
158
|
|
159
|
+
def coerce_backtrace
|
160
|
+
can_retry = false
|
161
|
+
|
162
|
+
case @error['code']
|
163
|
+
when -32003
|
164
|
+
can_retry = error_match?('Unable to acquire database lock')
|
165
|
+
can_retry = if !can_retry && error_match?('Internal Error"')
|
166
|
+
can_retry = true
|
167
|
+
@node_degraded = true
|
168
|
+
else
|
169
|
+
@node_degraded = false
|
170
|
+
end
|
171
|
+
when -32002
|
172
|
+
can_retry = @node_degraded = error_match?('Could not find API')
|
173
|
+
when 1
|
174
|
+
can_retry = @node_degraded = error_match?('no method with name \'condenser_api')
|
175
|
+
end
|
176
|
+
|
177
|
+
can_retry
|
178
|
+
end
|
179
|
+
|
180
|
+
def error_match?(match)
|
181
|
+
case match
|
182
|
+
when String
|
183
|
+
@error['message'] && @error['message'].include?(match)
|
184
|
+
when Array
|
185
|
+
if @error['message']
|
186
|
+
match.map { |m| @error['message'].include?(m) }.include? true
|
187
|
+
else
|
188
|
+
false
|
189
|
+
end
|
190
|
+
else; false
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
137
194
|
def to_s
|
138
195
|
if !!error_message && !error_message.empty?
|
139
196
|
"#{error_code}: #{error_message}"
|
data/lib/radiator/operation.rb
CHANGED
data/lib/radiator/stream.rb
CHANGED
@@ -17,6 +17,12 @@ module Radiator
|
|
17
17
|
# @private
|
18
18
|
INITIAL_TIMEOUT = 0.0200
|
19
19
|
|
20
|
+
# Note, even though block production is advertised at 3 seconds, often
|
21
|
+
# blocks are available in 1.5 seconds. However, we still keep our
|
22
|
+
# expectations at 3 seconds.
|
23
|
+
# @private
|
24
|
+
BLOCK_PRODUCTION = 3.0
|
25
|
+
|
20
26
|
# @private
|
21
27
|
MAX_TIMEOUT = 80
|
22
28
|
|
@@ -277,9 +283,17 @@ module Radiator
|
|
277
283
|
end
|
278
284
|
|
279
285
|
if head_block == latest_block_number
|
280
|
-
# This can when there's a delay in block production.
|
281
|
-
|
282
|
-
|
286
|
+
# This can happen when there's a delay in block production.
|
287
|
+
|
288
|
+
if current_timeout > BLOCK_PRODUCTION * 6
|
289
|
+
standby "Stream has stalled severely ...", {
|
290
|
+
and: {backoff: api, throw: :sequence}
|
291
|
+
}
|
292
|
+
elsif current_timeout > BLOCK_PRODUCTION * 3
|
293
|
+
warning "Stream has stalled ..."
|
294
|
+
end
|
295
|
+
|
296
|
+
timeout and throw :sequence
|
283
297
|
elsif head_block < latest_block_number
|
284
298
|
# This can happen if a reverse proxy is acting up.
|
285
299
|
standby "Invalid block sequence at height: #{head_block}", {
|
@@ -287,6 +301,7 @@ module Radiator
|
|
287
301
|
}
|
288
302
|
end
|
289
303
|
|
304
|
+
reset_timeout
|
290
305
|
start ||= head_block
|
291
306
|
range = (start..head_block)
|
292
307
|
|
@@ -307,20 +322,28 @@ module Radiator
|
|
307
322
|
current_range = r[index..-1]
|
308
323
|
|
309
324
|
if current_range.size % RANGE_BEHIND_WARNING == 0
|
310
|
-
|
325
|
+
warning "Stream behind by #{current_range.size} blocks (about #{(current_range.size * 3) / 60.0} minutes)."
|
311
326
|
end
|
312
327
|
end
|
313
328
|
|
314
|
-
block_api.get_block(n) do |current_block, error|
|
315
|
-
if
|
329
|
+
block_api.get_block(block_num: n) do |current_block, error|
|
330
|
+
if !!error
|
331
|
+
if error.message == 'Unable to acquire database lock'
|
332
|
+
start = n
|
333
|
+
timeout
|
334
|
+
standby "Node was unable to acquire database lock, retrying ...", {
|
335
|
+
and: {throw: :sequence}
|
336
|
+
}
|
337
|
+
else
|
338
|
+
standby "Node responded with: #{error.message || 'unknown error'}, retrying ...", {
|
339
|
+
error: error,
|
340
|
+
and: {throw: :sequence}
|
341
|
+
}
|
342
|
+
end
|
343
|
+
elsif current_block.nil?
|
316
344
|
standby "Node responded with: empty block, retrying ...", {
|
317
345
|
and: {throw: :sequence}
|
318
346
|
}
|
319
|
-
elsif !!error
|
320
|
-
standby "Node responded with: #{error.message || 'unknown error'}, retrying ...", {
|
321
|
-
error: error,
|
322
|
-
and: {throw: :sequence}
|
323
|
-
}
|
324
347
|
end
|
325
348
|
|
326
349
|
latest_block_number = n
|
@@ -329,7 +352,7 @@ module Radiator
|
|
329
352
|
end
|
330
353
|
|
331
354
|
start = head_block + 1
|
332
|
-
sleep
|
355
|
+
sleep BLOCK_PRODUCTION / range.size
|
333
356
|
end
|
334
357
|
rescue StreamError; raise
|
335
358
|
# rescue => e
|
@@ -430,11 +453,11 @@ module Radiator
|
|
430
453
|
raise StreamError, JSON[response.error] if !!response.error
|
431
454
|
result = response.result
|
432
455
|
break if !!result
|
433
|
-
|
456
|
+
warning "#{key}: #{param} result missing, retrying with timeout: #{current_timeout} seconds"
|
434
457
|
reset_api
|
435
|
-
|
458
|
+
timeout
|
436
459
|
end
|
437
|
-
|
460
|
+
reset_timeout
|
438
461
|
result
|
439
462
|
else
|
440
463
|
key_value = api.get_dynamic_global_properties.result[key]
|
@@ -448,7 +471,7 @@ module Radiator
|
|
448
471
|
return value
|
449
472
|
end
|
450
473
|
end
|
451
|
-
sleep
|
474
|
+
sleep current_timeout
|
452
475
|
end
|
453
476
|
end
|
454
477
|
|
@@ -460,10 +483,19 @@ module Radiator
|
|
460
483
|
def timeout
|
461
484
|
@timeout ||= INITIAL_TIMEOUT
|
462
485
|
@timeout *= 2
|
463
|
-
|
486
|
+
reset_timeout if @timeout > MAX_TIMEOUT
|
487
|
+
sleep @timeout || INITIAL_TIMEOUT
|
464
488
|
@timeout
|
465
489
|
end
|
466
490
|
|
491
|
+
def current_timeout
|
492
|
+
@timeout || INITIAL_TIMEOUT
|
493
|
+
end
|
494
|
+
|
495
|
+
def reset_timeout
|
496
|
+
@timeout = nil
|
497
|
+
end
|
498
|
+
|
467
499
|
def virtual_op_type?(type)
|
468
500
|
type = [type].flatten.compact.map(&:to_sym)
|
469
501
|
|
data/lib/radiator/tag_api.rb
CHANGED
data/lib/radiator/transaction.rb
CHANGED
@@ -93,7 +93,7 @@ module Radiator
|
|
93
93
|
|
94
94
|
if broadcast
|
95
95
|
loop do
|
96
|
-
response = @network_broadcast_api.broadcast_transaction_synchronous(payload)
|
96
|
+
response = @network_broadcast_api.broadcast_transaction_synchronous(trx: payload)
|
97
97
|
|
98
98
|
if !!response.error
|
99
99
|
parser = ErrorParser.new(response)
|
data/lib/radiator/type/amount.rb
CHANGED
@@ -6,18 +6,31 @@ module Radiator
|
|
6
6
|
def initialize(value)
|
7
7
|
super(:amount, value)
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
else
|
9
|
+
case value
|
10
|
+
when Array
|
11
|
+
a, p, t = value
|
12
|
+
@asset = case t
|
13
|
+
when '@@000000013' then 'SBD'
|
14
|
+
when '@@000000021' then 'STEEM'
|
15
|
+
when '@@000000037' then 'VESTS'
|
16
|
+
else; raise TypeError, "Asset #{@asset} unknown."
|
17
|
+
end
|
18
|
+
@precision = p
|
19
|
+
@amount = "%.#{p}f" % (a.to_f / 10 ** p)
|
20
|
+
else
|
21
|
+
@amount, @asset = value.strip.split(' ')
|
22
|
+
@precision = case @asset
|
23
|
+
when 'STEEM' then 3
|
24
|
+
when 'VESTS' then 6
|
25
|
+
when 'SBD' then 3
|
26
|
+
when 'GOLOS' then 3
|
27
|
+
when 'GESTS' then 6
|
28
|
+
when 'GBG' then 3
|
29
|
+
when 'CORE' then 3
|
30
|
+
when 'CESTS' then 6
|
31
|
+
when 'TEST' then 3
|
32
|
+
else; raise TypeError, "Asset #{@asset} unknown."
|
33
|
+
end
|
21
34
|
end
|
22
35
|
end
|
23
36
|
|
@@ -30,6 +43,15 @@ module Radiator
|
|
30
43
|
asset
|
31
44
|
end
|
32
45
|
|
46
|
+
def to_a
|
47
|
+
case @asset
|
48
|
+
when 'STEEM' then [(@amount.to_f * 1000).to_i.to_s, 3, '@@000000021']
|
49
|
+
when 'VESTS' then [(@amount.to_f * 1000000).to_i.to_s, 6, '@@000000037']
|
50
|
+
when 'SBD' then [(@amount.to_f * 1000).to_i.to_s, 3, '@@000000013']
|
51
|
+
else; raise TypeError, "Asset #{@asset} unknown."
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
33
55
|
def to_s
|
34
56
|
"#{@amount} #{@asset}"
|
35
57
|
end
|
data/lib/radiator/utils.rb
CHANGED
data/lib/radiator/version.rb
CHANGED
data/lib/radiator.rb
CHANGED
@@ -23,6 +23,7 @@ module Radiator
|
|
23
23
|
require 'radiator/network_broadcast_api'
|
24
24
|
require 'radiator/chain_stats_api'
|
25
25
|
require 'radiator/account_by_key_api'
|
26
|
+
require 'radiator/account_history_api'
|
26
27
|
require 'radiator/condenser_api'
|
27
28
|
require 'radiator/block_api'
|
28
29
|
require 'radiator/stream'
|
data/radiator.gemspec
CHANGED
@@ -19,13 +19,14 @@ Gem::Specification.new do |spec|
|
|
19
19
|
|
20
20
|
spec.add_development_dependency 'bundler', '~> 1.15', '>= 1.15.4'
|
21
21
|
spec.add_development_dependency 'rake', '~> 12.1', '>= 12.1.0'
|
22
|
-
spec.add_development_dependency 'minitest', '~> 5.
|
22
|
+
spec.add_development_dependency 'minitest', '~> 5.10', '>= 5.10.3'
|
23
23
|
spec.add_development_dependency 'minitest-line', '~> 0.6.3'
|
24
24
|
spec.add_development_dependency 'minitest-proveit', '~> 1.0', '>= 1.0.0'
|
25
25
|
spec.add_development_dependency 'webmock', '~> 3.1', '>= 3.1.0'
|
26
26
|
spec.add_development_dependency 'simplecov', '~> 0.15.1'
|
27
27
|
spec.add_development_dependency 'vcr', '~> 3.0', '>= 3.0.3'
|
28
28
|
spec.add_development_dependency 'yard', '~> 0.9.9'
|
29
|
+
spec.add_development_dependency 'pry', '~> 0.11', '>= 0.11.3'
|
29
30
|
|
30
31
|
# net-http-persistent has an open-ended dependency because radiator directly
|
31
32
|
# supports net-http-persistent-3.0.0 as well as net-http-persistent-2.5.2.
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: radiator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.0pre3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Anthony Martin
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-04-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -56,20 +56,20 @@ dependencies:
|
|
56
56
|
requirements:
|
57
57
|
- - "~>"
|
58
58
|
- !ruby/object:Gem::Version
|
59
|
-
version: '5.
|
59
|
+
version: '5.10'
|
60
60
|
- - ">="
|
61
61
|
- !ruby/object:Gem::Version
|
62
|
-
version: 5.
|
62
|
+
version: 5.10.3
|
63
63
|
type: :development
|
64
64
|
prerelease: false
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
66
66
|
requirements:
|
67
67
|
- - "~>"
|
68
68
|
- !ruby/object:Gem::Version
|
69
|
-
version: '5.
|
69
|
+
version: '5.10'
|
70
70
|
- - ">="
|
71
71
|
- !ruby/object:Gem::Version
|
72
|
-
version: 5.
|
72
|
+
version: 5.10.3
|
73
73
|
- !ruby/object:Gem::Dependency
|
74
74
|
name: minitest-line
|
75
75
|
requirement: !ruby/object:Gem::Requirement
|
@@ -172,6 +172,26 @@ dependencies:
|
|
172
172
|
- - "~>"
|
173
173
|
- !ruby/object:Gem::Version
|
174
174
|
version: 0.9.9
|
175
|
+
- !ruby/object:Gem::Dependency
|
176
|
+
name: pry
|
177
|
+
requirement: !ruby/object:Gem::Requirement
|
178
|
+
requirements:
|
179
|
+
- - "~>"
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: '0.11'
|
182
|
+
- - ">="
|
183
|
+
- !ruby/object:Gem::Version
|
184
|
+
version: 0.11.3
|
185
|
+
type: :development
|
186
|
+
prerelease: false
|
187
|
+
version_requirements: !ruby/object:Gem::Requirement
|
188
|
+
requirements:
|
189
|
+
- - "~>"
|
190
|
+
- !ruby/object:Gem::Version
|
191
|
+
version: '0.11'
|
192
|
+
- - ">="
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: 0.11.3
|
175
195
|
- !ruby/object:Gem::Dependency
|
176
196
|
name: net-http-persistent
|
177
197
|
requirement: !ruby/object:Gem::Requirement
|
@@ -328,6 +348,7 @@ files:
|
|
328
348
|
- lib/golos.rb
|
329
349
|
- lib/radiator.rb
|
330
350
|
- lib/radiator/account_by_key_api.rb
|
351
|
+
- lib/radiator/account_history_api.rb
|
331
352
|
- lib/radiator/api.rb
|
332
353
|
- lib/radiator/base_error.rb
|
333
354
|
- lib/radiator/block_api.rb
|
@@ -386,7 +407,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
386
407
|
version: 1.3.1
|
387
408
|
requirements: []
|
388
409
|
rubyforge_project:
|
389
|
-
rubygems_version: 2.6.
|
410
|
+
rubygems_version: 2.6.14
|
390
411
|
signing_key:
|
391
412
|
specification_version: 4
|
392
413
|
summary: STEEM RPC Ruby Client
|