radiator 0.3.7 → 0.3.8

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: a7dc50f2559e5378d12b9511cf95ea18c8fa182b
4
- data.tar.gz: d484fcd9e21336f1202552589d3c8b58311d48b2
3
+ metadata.gz: 2fc2844ca29378c9c7ec0e1bca017599a67a8790
4
+ data.tar.gz: 2637ed93438b046db2a0bb23ad8ff76d6a7dc468
5
5
  SHA512:
6
- metadata.gz: e54dc905d85d797f9079194f9ae4f7f8c09623238f0bfbd9ea7bd73ad8666f5b981e9221027cb5cea268f6b0e870e433227271a2d42b98a5d01659ad27004a83
7
- data.tar.gz: 2869c113566019bbd2c2ef67db6ab745abaf0e33fef3526a30a041b05ad524d1ec44ac6e2edf4a45d45662b7c8832c038e30fa90b0f501d254ecd46bb5b464e5
6
+ metadata.gz: c320fd85c60540483de9fc407af4561a3612b8b4eb0d08ee31ae71800532a093d2bbc2123716dd1aa7f00d940555d4c22d6db5caccf76ec2d7df692f373cd68d
7
+ data.tar.gz: fcb25d0fcff488149b5f880caedd459b898ff6dafcd828e4efb2455b9790692deb275f3461f018dbb2feabaa6c4017070ce458cfd4df83eae8dfe1da1aa442ab
data/Gemfile.lock CHANGED
@@ -1,14 +1,14 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- radiator (0.3.7)
4
+ radiator (0.3.8)
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)
8
8
  hashie (~> 3.5, >= 3.5.5)
9
9
  json (~> 2.0, >= 2.0.2)
10
10
  logging (~> 2.2, >= 2.2.0)
11
- net-http-persistent (>= 2.5.2)
11
+ net-http-persistent (~> 2.5, >= 2.5.2)
12
12
 
13
13
  GEM
14
14
  remote: https://rubygems.org/
@@ -17,7 +17,6 @@ GEM
17
17
  public_suffix (>= 2.0.2, < 4.0)
18
18
  awesome_print (1.8.0)
19
19
  bitcoin-ruby (0.0.12)
20
- connection_pool (2.2.1)
21
20
  crack (0.4.3)
22
21
  safe_yaml (~> 1.0.0)
23
22
  docile (1.1.5)
@@ -35,10 +34,9 @@ GEM
35
34
  minitest-proveit (1.0.0)
36
35
  minitest (> 5, < 7)
37
36
  multi_json (1.12.2)
38
- net-http-persistent (3.0.0)
39
- connection_pool (~> 2.2)
37
+ net-http-persistent (2.9.4)
40
38
  public_suffix (3.0.0)
41
- rake (12.1.0)
39
+ rake (12.2.1)
42
40
  safe_yaml (1.0.4)
43
41
  simplecov (0.15.1)
44
42
  docile (~> 1.1.0)
data/Rakefile CHANGED
@@ -2,6 +2,7 @@ require 'bundler/gem_tasks'
2
2
  require 'rake/testtask'
3
3
  require 'yard'
4
4
  require 'radiator'
5
+ require 'awesome_print'
5
6
 
6
7
  Rake::TestTask.new(:test) do |t|
7
8
  t.libs << 'test'
@@ -25,9 +26,35 @@ task :dump_vcr do |t|
25
26
  exec 'rm -v test/fixtures/vcr_cassettes/*.yml'
26
27
  end
27
28
 
29
+ desc 'Tests the ability to broadcast live data. This task broadcasts a claim_reward_balance of 0.0000001 VESTS.'
30
+ task :test_live_broadcast, [:account, :wif, :chain] do |t, args|
31
+ account_name = args[:account] || 'social'
32
+ posting_wif = args[:wif] || '5JrvPrQeBBvCRdjv29iDvkwn3EQYZ9jqfAHzrCyUvfbEbRkrYFC'
33
+ chain = args[:chain] || 'steem'
34
+ options = {chain: chain, wif: posting_wif}
35
+ tx = Radiator::Transaction.new(options)
36
+ tx.operations << {
37
+ type: :claim_reward_balance,
38
+ account: account_name,
39
+ reward_steem: '0.000 STEEM',
40
+ reward_sbd: '0.000 SBD',
41
+ reward_vests: '0.000001 VESTS'
42
+ }
43
+
44
+ response = tx.process(true)
45
+ ap response
46
+
47
+ if !!response.result
48
+ result = response.result
49
+
50
+ puts "https://steemd.com/b/#{result[:block_num]}" if !!result[:block_num]
51
+ puts "https://steemd.com/tx/#{result[:id]}" if !!result[:id]
52
+ end
53
+ end
54
+
28
55
  desc 'Tests the ability to stream live data.'
29
56
  task :test_live_stream, :chain do |t, args|
30
- chain = (args[:chain] || 'steem').to_sym
57
+ chain = args[:chain] || 'steem'
31
58
  last_block_number = 0
32
59
  options = {chain: chain}
33
60
  api = Radiator::Api.new(options)
data/lib/radiator/api.rb CHANGED
@@ -400,14 +400,13 @@ module Radiator
400
400
  tries += 1
401
401
 
402
402
  begin
403
- if @recover_transactions_on_error && api_name == :network_broadcast_api
403
+ if tries > 1 && @recover_transactions_on_error && api_name == :network_broadcast_api
404
404
  signatures, exp = extract_signatures(options)
405
405
 
406
- if tries > 1 && !!signatures && signatures.any?
407
- offset = [(exp - timestamp).abs, 300].min
406
+ if !!signatures && signatures.any?
407
+ offset = [(exp - timestamp).abs, 30].min
408
408
 
409
409
  if !!(response = recover_transaction(signatures, current_rpc_id, timestamp - offset))
410
- warning 'Found recovered transaction after retry.', method_name, true
411
410
  response = Hashie::Mash.new(response)
412
411
  end
413
412
  end
@@ -440,7 +439,7 @@ module Radiator
440
439
  when '504' then warning 'Code 504: Gateway Timeout, retrying ...', method_name, true
441
440
  else
442
441
  warning "Unknown code #{response.code}, retrying ...", method_name, true
443
- ap response
442
+ warning response
444
443
  end
445
444
  end
446
445
  end
@@ -469,7 +468,7 @@ module Radiator
469
468
  warning "ApiError (#{e.message}), retrying ...", method_name, true
470
469
  # rescue => e
471
470
  # warning "Unknown exception from request, retrying ...", method_name, true
472
- # ap e if defined? ap
471
+ # warning e
473
472
  end
474
473
 
475
474
  if !!response
@@ -550,6 +549,10 @@ module Radiator
550
549
  end
551
550
 
552
551
  def recover_transaction(signatures, expected_rpc_id, after)
552
+ debug "Looking for signatures: #{signatures.map{|s| s[0..5]}} since: #{after}"
553
+
554
+ count = 0
555
+ start = Time.now.utc
553
556
  block_range = api.get_dynamic_global_properties do |properties|
554
557
  high = properties.head_block_number
555
558
  low = high - 100
@@ -562,6 +565,7 @@ module Radiator
562
565
  # but we also give up once the block time is before the `after` argument.
563
566
 
564
567
  api.get_blocks(block_range) do |block, block_num|
568
+ count += 1
565
569
  raise ApiError, "Race condition detected on remote node at: #{block_num}" if block.nil?
566
570
 
567
571
  timestamp = Time.parse(block.timestamp + 'Z')
@@ -570,8 +574,11 @@ module Radiator
570
574
  block.transactions.each_with_index do |tx, index|
571
575
  next unless ((tx['signatures'] || []) & signatures).any?
572
576
 
577
+ debug "Found transaction #{count} block(s) ago; took #{(Time.now.utc - start)} seconds to scan."
578
+
573
579
  return {
574
580
  id: expected_rpc_id,
581
+ recovered_by: http_id,
575
582
  result: {
576
583
  id: block.transaction_ids[index],
577
584
  block_num: block_num,
@@ -582,6 +589,8 @@ module Radiator
582
589
  end
583
590
  end
584
591
 
592
+ debug "Could not find transaction in #{count} block(s); took #{(Time.now.utc - start)} seconds to scan."
593
+
585
594
  return nil
586
595
  end
587
596
 
@@ -608,7 +617,7 @@ module Radiator
608
617
  end
609
618
 
610
619
  def flappy?
611
- !!@backoff_at && Time.now - @backoff_at < 300
620
+ !!@backoff_at && Time.now.utc - @backoff_at < 300
612
621
  end
613
622
 
614
623
  def drop_current_failover_url(prefix)
@@ -623,7 +632,7 @@ module Radiator
623
632
 
624
633
  def handle_error(response, request_options, method_name, tries)
625
634
  parser = ErrorParser.new(response)
626
- signatures, exp = extract_signatures(request_options)
635
+ _signatures, exp = extract_signatures(request_options)
627
636
 
628
637
  if (!!exp && exp < Time.now.utc) || tries > 2
629
638
  # Whatever the error was, it is already expired or tried too much. No
@@ -636,6 +645,34 @@ module Radiator
636
645
  return nil
637
646
  end
638
647
 
648
+ if !!parser.trx_id
649
+ # Turns out, the ErrorParser found a transaction id. It might come in
650
+ # handy, so let's append this to the result along with the error.
651
+
652
+ response[:result] = {
653
+ id: parser.trx_id,
654
+ block_num: -1,
655
+ trx_num: -1,
656
+ expired: false
657
+ }
658
+
659
+ if @recover_transactions_on_error
660
+ begin
661
+ # Node operators often disable this operation.
662
+ api.get_transaction(parser.trx_id) do |tx|
663
+ if !!tx
664
+ response[:result][:block_num] = tx.block_num
665
+ response[:result][:trx_num] = tx.transaction_num
666
+ response[:recovered_by] = http_id
667
+ response.delete('error') # no need for this, now
668
+ end
669
+ end
670
+ rescue
671
+ debug "Couldn't find block for trx_id: #{parser.trx_id}, giving up."
672
+ end
673
+ end
674
+ end
675
+
639
676
  Hashie::Mash.new(response)
640
677
  end
641
678
 
@@ -661,13 +698,13 @@ module Radiator
661
698
  def backoff
662
699
  shutdown
663
700
  bump_failover if flappy? || !healthy?(@url)
664
- @backoff_at ||= Time.now
701
+ @backoff_at ||= Time.now.utc
665
702
  @backoff_sleep ||= 0.01
666
703
 
667
704
  @backoff_sleep *= 2
668
705
  sleep @backoff_sleep
669
706
 
670
- if !!@backoff_at && Time.now - @backoff_at > 300
707
+ if !!@backoff_at && Time.now.utc - @backoff_at > 300
671
708
  @backoff_at = nil
672
709
  @backoff_sleep = nil
673
710
  end
@@ -1,6 +1,6 @@
1
1
  module Radiator
2
2
  module ChainConfig
3
- EXPIRE_IN_SECS = 15
3
+ EXPIRE_IN_SECS = 600
4
4
  EXPIRE_IN_SECS_PROPOSAL = 24 * 60 * 60
5
5
 
6
6
  NETWORKS_STEEM_CHAIN_ID = '0000000000000000000000000000000000000000000000000000000000000000'
@@ -4,18 +4,21 @@ module Radiator
4
4
 
5
5
  attr_reader :response, :error_code, :error_message,
6
6
  :api_name, :api_method, :api_params,
7
- :expiry, :can_retry, :can_reprepare, :debug
7
+ :expiry, :can_retry, :can_reprepare, :trx_id, :debug
8
8
 
9
9
  alias expiry? expiry
10
10
  alias can_retry? can_retry
11
11
  alias can_reprepare? can_reprepare
12
12
 
13
- REPREPARE = [
13
+ REPREPARE_WHITELIST = [
14
14
  'is_canonical( c ): signature is not canonical',
15
- 'now < trx.expiration: ',
16
- '(skip & skip_transaction_dupe_check) || trx_idx.indices().get<by_trx_id>().find(trx_id) == trx_idx.indices().get<by_trx_id>().end(): Duplicate transaction check failed'
15
+ 'now < trx.expiration: '
17
16
  ]
18
17
 
18
+ DUPECHECK = '(skip & skip_transaction_dupe_check) || trx_idx.indices().get<by_trx_id>().find(trx_id) == trx_idx.indices().get<by_trx_id>().end(): Duplicate transaction check failed'
19
+
20
+ REPREPARE_BLACKLIST = [DUPECHECK]
21
+
19
22
  def initialize(response)
20
23
  @response = response
21
24
 
@@ -28,6 +31,7 @@ module Radiator
28
31
  @expiry = nil
29
32
  @can_retry = nil
30
33
  @can_reprepare = nil
34
+ @trx_id = nil
31
35
  @debug = nil
32
36
 
33
37
  parse_error_response
@@ -48,12 +52,16 @@ module Radiator
48
52
  @api_name = data_call_method['call.params']
49
53
  end
50
54
 
55
+ # See if we can recover a transaction id out of this hot mess.
56
+ data_trx_ix = stack_datum.find { |data| !!data['trx_ix'] }
57
+ @trx_id = data_trx_ix['trx_ix'] if !!data_trx_ix
58
+
51
59
  case @error_code
52
60
  when 10
53
61
  @expiry = false
54
62
  @can_retry = false
55
63
  @can_reprepare = if @api_name == 'network_broadcast_api'
56
- (stack_formats & REPREPARE).any?
64
+ (stack_formats & REPREPARE_WHITELIST).any?
57
65
  else
58
66
  false
59
67
  end
@@ -286,7 +286,7 @@ module Radiator
286
286
  rescue StreamError; raise
287
287
  # rescue => e
288
288
  # warning "Unknown streaming error: #{e.inspect}, retrying ... "
289
- # ap e
289
+ # warning e
290
290
  # redo
291
291
  end; end
292
292
  end
@@ -408,8 +408,7 @@ module Radiator
408
408
  throwable = secondary[:throw]
409
409
 
410
410
  warning message
411
-
412
- ap error if !!error
411
+ warning error if !!error
413
412
  backoff_api.send :backoff if !!backoff_api
414
413
  throw throwable if !!throwable
415
414
  end
@@ -44,6 +44,9 @@ module Radiator
44
44
  @private_key = Bitcoin::Key.from_base58 wif
45
45
  end
46
46
 
47
+ @expiration ||= nil
48
+ @immutable_expiration = !!@expiration
49
+
47
50
  options = options.merge(url: @url, chain: @chain)
48
51
  @api = Api.new(options)
49
52
  @network_broadcast_api = NetworkBroadcastApi.new(options)
@@ -78,7 +81,7 @@ module Radiator
78
81
  parser = ErrorParser.new(response)
79
82
 
80
83
  if parser.can_reprepare?
81
- debug "Repreparing transaction ..."
84
+ debug "Error code: #{parser}, repreparing transaction ..."
82
85
  prepare
83
86
  redo
84
87
  end
@@ -105,7 +108,7 @@ module Radiator
105
108
  end
106
109
  private
107
110
  def payload
108
- {
111
+ @payload ||= {
109
112
  expiration: @expiration.strftime('%Y-%m-%dT%H:%M:%S'),
110
113
  ref_block_num: @ref_block_num,
111
114
  ref_block_prefix: @ref_block_prefix,
@@ -118,55 +121,57 @@ module Radiator
118
121
  def prepare
119
122
  raise TransactionError, "No wif or private key." unless !!@wif || !!@private_key
120
123
 
121
- @api.get_dynamic_global_properties do |properties, error|
122
- if !!error
123
- raise TransactionError, "Unable to prepare transaction.", error
124
+ @payload = nil
125
+
126
+ while @expiration.nil? && @ref_block_num.nil? && @ref_block_prefix.nil?
127
+ @api.get_dynamic_global_properties do |properties, error|
128
+ if !!error
129
+ raise TransactionError, "Unable to prepare transaction.", error
130
+ end
131
+
132
+ @properties = properties
124
133
  end
125
-
126
- @properties = properties
127
-
128
- case @chain
129
- when :steem, :test
130
- # You can actually go back as far as the TaPoS buffer will allow, which
131
- # is something like 50,000 blocks.
132
134
 
133
- block_number = @properties.last_irreversible_block_num
135
+ # You can actually go back as far as the TaPoS buffer will allow, which
136
+ # is something like 50,000 blocks.
134
137
 
135
- @api.get_block(block_number) do |block, error|
136
- if !!error
137
- raise TransactionError, "Unable to prepare transaction.", error
138
- end
138
+ block_number = @properties.last_irreversible_block_num
139
+
140
+ @api.get_block(block_number) do |block, error|
141
+ if !!error
142
+ raise TransactionError, "Unable to prepare transaction.", error
143
+ end
144
+
145
+ if !!block && !!block.previous
146
+ @ref_block_num = (block_number - 1) & 0xFFFF
147
+ @ref_block_prefix = unhexlify(block.previous[8..-1]).unpack('V*')[0]
148
+
149
+ # The expiration allows for transactions to expire if they are not
150
+ # included into a block by that time. Always update it to the current
151
+ # time + EXPIRE_IN_SECS.
152
+ #
153
+ # Note, as of #1215, expiration exactly 'now' will be rejected:
154
+ # https://github.com/steemit/steem/blob/57451b80d2cf480dcce9b399e48e56aa7af1d818/libraries/chain/database.cpp#L2870
155
+ # https://github.com/steemit/steem/issues/1215
156
+
157
+ block_time = Time.parse(@properties.time + 'Z')
158
+ @expiration ||= block_time + EXPIRE_IN_SECS
159
+ else
160
+ # Suspect this happens when there are microforks, but it should be
161
+ # rare, especially since we're asking for the last irreversible
162
+ # block.
139
163
 
140
164
  if block.nil?
141
- raise TransactionError, "Unable to prepare transaction, block missing."
165
+ warning "Block missing while trying to prepare transaction, retrying ..."
166
+ else
167
+ debug block if ENV['LOG'] == 'DEBUG'
168
+
169
+ warning "Block structure while trying to prepare transaction, retrying ..."
142
170
  end
143
171
 
144
- if block.block_id.nil?
145
- raise TransactionError, "Unable to prepare transaction, block.block_id missing."
146
- end
147
-
148
- @ref_block_num = block_number & 0xFFFF
149
- @ref_block_prefix = unhexlify(block.block_id[8..-1]).unpack('V*')[0]
172
+ @expiration = nil unless @immutable_expiration
150
173
  end
151
- when :golos
152
- # No support for block_id in get_block on golos (yet), so just use the
153
- # head block number.
154
-
155
- @ref_block_num = @properties.head_block_number & 0xFFFF
156
- @ref_block_prefix = unhexlify(@properties.head_block_id[8..-1]).unpack('V*')[0]
157
- else
158
- raise TransactionError, "Unable to prepare transaction, unsupported chain: #{@chain}"
159
174
  end
160
-
161
- # The expiration allows for transactions to expire if they are not
162
- # included into a block by that time. Always update it to the current
163
- # time + EXPIRE_IN_SECS.
164
- #
165
- # Note, as of #1215, expiration exactly 'now' will be rejected:
166
- # https://github.com/steemit/steem/blob/57451b80d2cf480dcce9b399e48e56aa7af1d818/libraries/chain/database.cpp#L2870
167
- # https://github.com/steemit/steem/issues/1215
168
-
169
- @expiration = Time.parse(@properties.time + 'Z') + EXPIRE_IN_SECS
170
175
  end
171
176
 
172
177
  self
@@ -198,7 +203,7 @@ module Radiator
198
203
  digest_hex = digest.freeze
199
204
 
200
205
  loop do
201
- @expiration += 1
206
+ @expiration += 1 unless @immutable_expiration
202
207
  sig = ec.sign_compact(digest_hex, @private_key.priv, public_key_hex)
203
208
 
204
209
  next if public_key_hex != ec.recover_compact(digest_hex, sig)
@@ -22,35 +22,44 @@ module Radiator
22
22
  [signatures, expirations.min]
23
23
  end
24
24
 
25
- def send_log(level, message, prefix = nil)
26
- log_message = if !!prefix
27
- "#{prefix} :: #{message}"
28
- else
29
- message
30
- end
31
-
32
- if !!@logger
33
- @logger.send level, log_message
25
+ def send_log(level, obj, prefix = nil)
26
+ log_message = case obj
27
+ when String
28
+ log_message = if !!prefix
29
+ "#{prefix} :: #{obj}"
30
+ else
31
+ obj
32
+ end
33
+
34
+ if !!@logger
35
+ @logger.send level, log_message
36
+ else
37
+ puts "#{level}: #{log_message}"
38
+ end
34
39
  else
35
- puts "#{level}: #{log_message}"
40
+ if !!prefix
41
+ @logger.ap log_level: level, prefix => obj
42
+ else
43
+ @logger.ap obj, level
44
+ end
36
45
  end
37
46
 
38
47
  nil
39
48
  end
40
49
 
41
- def error(message, prefix = nil)
42
- send_log(:error, message, prefix)
50
+ def error(obj, prefix = nil)
51
+ send_log(:error, obj, prefix)
43
52
  end
44
53
 
45
- def warning(message, prefix = nil, log_debug_node = false)
54
+ def warning(obj, prefix = nil, log_debug_node = false)
46
55
  debug("Current node: #{@url}", prefix) if !!log_debug_node && @url
47
56
 
48
- send_log(:warn, message, prefix)
57
+ send_log(:warn, obj, prefix)
49
58
  end
50
59
 
51
- def debug(message, prefix = nil)
60
+ def debug(obj, prefix = nil)
52
61
  if ENV['LOG'] == 'DEBUG'
53
- send_log(:debug, message, prefix)
62
+ send_log(:debug, obj, prefix)
54
63
  end
55
64
  end
56
65
 
@@ -1,3 +1,3 @@
1
1
  module Radiator
2
- VERSION = '0.3.7'
2
+ VERSION = '0.3.8'
3
3
  end
data/radiator.gemspec CHANGED
@@ -27,7 +27,7 @@ Gem::Specification.new do |spec|
27
27
  spec.add_development_dependency 'vcr', '~> 3.0', '>= 3.0.3'
28
28
  spec.add_development_dependency 'yard', '~> 0.9.9'
29
29
 
30
- spec.add_dependency('net-http-persistent', '>= 2.5.2')
30
+ spec.add_dependency('net-http-persistent', '~> 2.5', '>= 2.5.2')
31
31
  spec.add_dependency('json', '~> 2.0', '>= 2.0.2')
32
32
  spec.add_dependency('logging', '~> 2.2', '>= 2.2.0')
33
33
  spec.add_dependency('hashie', '~> 3.5', '>= 3.5.5')
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.3.7
4
+ version: 0.3.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anthony Martin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-10-25 00:00:00.000000000 Z
11
+ date: 2017-10-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -176,6 +176,9 @@ dependencies:
176
176
  name: net-http-persistent
177
177
  requirement: !ruby/object:Gem::Requirement
178
178
  requirements:
179
+ - - "~>"
180
+ - !ruby/object:Gem::Version
181
+ version: '2.5'
179
182
  - - ">="
180
183
  - !ruby/object:Gem::Version
181
184
  version: 2.5.2
@@ -183,6 +186,9 @@ dependencies:
183
186
  prerelease: false
184
187
  version_requirements: !ruby/object:Gem::Requirement
185
188
  requirements:
189
+ - - "~>"
190
+ - !ruby/object:Gem::Version
191
+ version: '2.5'
186
192
  - - ">="
187
193
  - !ruby/object:Gem::Version
188
194
  version: 2.5.2