radiator 0.4.0pre2 → 0.4.0pre3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ec41122ec4298e304c921f89355e437830c9fd78
4
- data.tar.gz: 5aec651358eea83415241b2c6295122951d833c0
3
+ metadata.gz: b3f2fba4fb406179bfcb988e1d1669615b392569
4
+ data.tar.gz: b721efe6e8751fef45eb40a31e1799c3c695378f
5
5
  SHA512:
6
- metadata.gz: 33e83e99337f5e9146a39e65057bcd88693f9d208fbcef91acba1f0b5d3741fed47a2d98e70110e3a326c3d771bf4b3ec68c5cd9dceb3271c35676d676f46b3c
7
- data.tar.gz: 170b5bec982a6f359cbdc64d0d89e7e695e13687c70a54867edd33424190ec216934502631e493e54494b42d395a22a1819b2ce63402df9fa8570f53c234b5e9
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.0pre2)
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.21)
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.0)
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.9, >= 5.9.0)
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
- desc 'Deletes test/fixtures/vcr_cassettes/*.yml so they can be rebuilt fresh.'
25
- task :dump_vcr do |t|
26
- exec 'rm -v test/fixtures/vcr_cassettes/*.yml'
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, url: 'https://api.steemitstage.com'}
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 nunber was #{last_block_number}, did not expect #{n}."
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
@@ -0,0 +1,15 @@
1
+ module Radiator
2
+ class AccountHistoryApi < Api
3
+ def method_names
4
+ @method_names ||= [
5
+ :get_account_history,
6
+ :get_ops_in_block,
7
+ :get_transaction
8
+ ].freeze
9
+ end
10
+
11
+ def api_name
12
+ :account_history_api
13
+ end
14
+ end
15
+ end
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
- :database_api
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 jussi_supported? && api_name == :database_api
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
- params: [api_name, m, args],
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
- warning "Unexpected rpc_id (expected: #{options[:id]}, got: #{response['id']}), retrying ...", method_name, true
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
- return yield(response.result, response.error, response.id)
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(@api_options)
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(@api_options)
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
- !!open(url + HEALTH_URI)
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
@@ -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
- @method_names ||= [
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 @error['data'].nil?
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 10
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 = false
106
+ @can_retry = coerce_backtrace
89
107
  @can_reprepare = if @api_name == 'network_broadcast_api'
90
- (stack_formats & REPREPARE_WHITELIST).any?
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 error_perser_exception: e, original_response: response
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}"
@@ -55,6 +55,7 @@ module Radiator
55
55
 
56
56
  params[p] = case v
57
57
  when Radiator::Type::Beneficiaries then [[0, v.to_h]]
58
+ when Radiator::Type::Amount then v.to_a
58
59
  else; v
59
60
  end
60
61
  end
@@ -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
- sleep 0.5
282
- throw :sequence
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
- standby "Stream behind by #{current_range.size} blocks (about #{(current_range.size * 3) / 60.0} minutes)."
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 current_block.nil?
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 3 / range.size
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
- warnning "#{key}: #{param} result missing, retrying with timeout: #{@timeout || INITIAL_TIMEOUT} seconds"
456
+ warning "#{key}: #{param} result missing, retrying with timeout: #{current_timeout} seconds"
434
457
  reset_api
435
- sleep timeout
458
+ timeout
436
459
  end
437
- @timeout = INITIAL_TIMEOUT
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 0.0200
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
- @timeout = INITIAL_TIMEOUT if @timeout > MAX_TIMEOUT
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
 
@@ -27,7 +27,7 @@ module Radiator
27
27
  end
28
28
 
29
29
  def api_name
30
- :tag_api
30
+ :tags_api
31
31
  end
32
32
  end
33
33
  end
@@ -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)
@@ -6,18 +6,31 @@ module Radiator
6
6
  def initialize(value)
7
7
  super(:amount, value)
8
8
 
9
- @amount, @asset = value.strip.split(' ')
10
- @precision = case @asset
11
- when 'STEEM' then 3
12
- when 'VESTS' then 6
13
- when 'SBD' then 3
14
- when 'GOLOS' then 3
15
- when 'GESTS' then 6
16
- when 'GBG' then 3
17
- when 'CORE' then 3
18
- when 'CESTS' then 6
19
- when 'TEST' then 3
20
- else; raise TypeError, "Asset #{@asset} unknown."
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
@@ -1,6 +1,8 @@
1
1
  module Radiator
2
2
  module Utils
3
3
  def extract_signatures(options)
4
+ return [] unless defined? options[:params].map
5
+
4
6
  params = options[:params]
5
7
 
6
8
  signatures = params.map do |param|
@@ -1,4 +1,4 @@
1
1
  module Radiator
2
- VERSION = '0.4.0pre2'
2
+ VERSION = '0.4.0pre3'
3
3
  AGENT_ID = "radiator/#{VERSION}"
4
4
  end
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.9', '>= 5.9.0'
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.0pre2
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-02-21 00:00:00.000000000 Z
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.9'
59
+ version: '5.10'
60
60
  - - ">="
61
61
  - !ruby/object:Gem::Version
62
- version: 5.9.0
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.9'
69
+ version: '5.10'
70
70
  - - ">="
71
71
  - !ruby/object:Gem::Version
72
- version: 5.9.0
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.11
410
+ rubygems_version: 2.6.14
390
411
  signing_key:
391
412
  specification_version: 4
392
413
  summary: STEEM RPC Ruby Client