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 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