radiator 0.3.7 → 0.3.8

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