radiator 0.3.0dev3 → 0.3.0dev4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/lib/radiator/api.rb +19 -10
- data/lib/radiator/stream.rb +166 -64
- data/lib/radiator/transaction.rb +5 -0
- data/lib/radiator/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4b709aac2407041d491d0d08abb766ce404dc0cf
|
4
|
+
data.tar.gz: d007d6f2d7d20b096450f05c196790da6374b76e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 073339bb35cbffff34c4adedfef58534b0827aac9757f903f94f7a2ae56adb27ef8468dc85271895a725a70eb425015faa3609424fc266d88581e81f0acd023e
|
7
|
+
data.tar.gz: a7cae3da19c410b1fceb01b717de6b4199dea2f7ebbd8018b7f1ef21c07e6da58439c0016508d60c7d1b9c6f8bd9f488b66179519018f7c40afc50a6e2a5f581
|
data/Gemfile.lock
CHANGED
data/lib/radiator/api.rb
CHANGED
@@ -161,6 +161,7 @@ module Radiator
|
|
161
161
|
# @option options [Array<String>] :failover_urls An array that contains one or more full nodes to fall back on. Default from DEFAULT_FAILOVER_URLS.
|
162
162
|
# @option options [Logger] :logger An instance of `Logger` to send debug messages to.
|
163
163
|
# @option options [Boolean] :recover_transactions_on_error Have Radiator try to recover transactions that are accepted but could not be confirmed due to an error like network timeout. Default: `true`
|
164
|
+
# @option options [Integer] :max_requests Maximum number of requests on a connection before it is considered expired and automatically closed.
|
164
165
|
def initialize(options = {})
|
165
166
|
@user = options[:user]
|
166
167
|
@password = options[:password]
|
@@ -171,6 +172,7 @@ module Radiator
|
|
171
172
|
@debug = !!options[:debug]
|
172
173
|
@logger = options[:logger] || Radiator.logger
|
173
174
|
@hashie_logger = options[:hashie_logger] || Logger.new(nil)
|
175
|
+
@max_requests = options[:max_requests] || 30
|
174
176
|
|
175
177
|
unless @hashie_logger.respond_to? :warn
|
176
178
|
@hashie_logger = Logger.new(@hashie_logger)
|
@@ -304,8 +306,11 @@ module Radiator
|
|
304
306
|
# Stops the persistant http connections.
|
305
307
|
#
|
306
308
|
def shutdown
|
309
|
+
@http_id = nil
|
307
310
|
@http.shutdown if !!@http && defined?(@http.shutdown)
|
308
311
|
@http = nil
|
312
|
+
@api.shutdown if !!@api && @api != self
|
313
|
+
@api = nil
|
309
314
|
end
|
310
315
|
|
311
316
|
# @private
|
@@ -403,6 +408,9 @@ module Radiator
|
|
403
408
|
@logger.warn "Socket Error (#{e.message}), retrying ..."
|
404
409
|
rescue JSON::ParserError => e
|
405
410
|
@logger.warn "JSON Parse Error (#{e.message}), retrying ..."
|
411
|
+
response = nil
|
412
|
+
rescue ApiError => e
|
413
|
+
@logger.warn "ApiError (#{e.message}), retrying ..."
|
406
414
|
rescue => e
|
407
415
|
@logger.warn "Unknown exception from request, retrying ..."
|
408
416
|
ap e if defined? ap
|
@@ -444,13 +452,19 @@ module Radiator
|
|
444
452
|
@uri ||= URI.parse(@url)
|
445
453
|
end
|
446
454
|
|
455
|
+
def http_id
|
456
|
+
@http_id ||= "radiator-#{Radiator::VERSION}-#{api_name}-#{SecureRandom.uuid}"
|
457
|
+
end
|
458
|
+
|
447
459
|
def http
|
448
|
-
@
|
449
|
-
|
450
|
-
http.
|
451
|
-
http.max_requests = 30
|
460
|
+
@http ||= Net::HTTP::Persistent.new(http_id).tap do |http|
|
461
|
+
idempotent = api_name != :network_broadcast_api
|
462
|
+
http.keep_alive = 30
|
452
463
|
http.read_timeout = 10
|
453
464
|
http.open_timeout = 10
|
465
|
+
http.idle_timeout = idempotent ? 10 : nil
|
466
|
+
http.max_requests = @max_requests
|
467
|
+
http.retry_change_requests = idempotent
|
454
468
|
end
|
455
469
|
end
|
456
470
|
|
@@ -488,7 +502,7 @@ module Radiator
|
|
488
502
|
# but we also give up once the block time is before the `after` argument.
|
489
503
|
|
490
504
|
api.get_blocks(block_range) do |block, block_num|
|
491
|
-
raise "Race condition detected at: #{block_num}" if block.nil?
|
505
|
+
raise ApiError, "Race condition detected on remote node at: #{block_num}" if block.nil?
|
492
506
|
|
493
507
|
timestamp = Time.parse(block.timestamp + 'Z')
|
494
508
|
break if timestamp < after
|
@@ -496,9 +510,6 @@ module Radiator
|
|
496
510
|
block.transactions.each_with_index do |tx, index|
|
497
511
|
next unless ((tx['signatures'] || []) & signatures).any?
|
498
512
|
|
499
|
-
puts "Found matching signatures in #{(Time.now.utc - now)} seconds: #{signatures}"
|
500
|
-
ap tx
|
501
|
-
|
502
513
|
return {
|
503
514
|
id: rpc_id,
|
504
515
|
result: {
|
@@ -510,8 +521,6 @@ module Radiator
|
|
510
521
|
}
|
511
522
|
end
|
512
523
|
end
|
513
|
-
|
514
|
-
puts "Took #{(Time.now.utc - now)} seconds to scan for signatures."
|
515
524
|
end
|
516
525
|
|
517
526
|
def reset_failover
|
data/lib/radiator/stream.rb
CHANGED
@@ -38,33 +38,42 @@ module Radiator
|
|
38
38
|
# If symbol are passed, then only that operation is returned. Expected
|
39
39
|
# symbols are:
|
40
40
|
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
# author_reward
|
47
|
-
# curation_reward
|
48
|
-
# transfer_to_savings
|
49
|
-
# transfer_from_savings
|
41
|
+
# account_create
|
42
|
+
# account_create_with_delegation
|
43
|
+
# account_update
|
44
|
+
# account_witness_proxy
|
45
|
+
# account_witness_vote
|
50
46
|
# cancel_transfer_from_savings
|
51
|
-
#
|
47
|
+
# change_recovery_account
|
48
|
+
# claim_reward_balance
|
49
|
+
# comment
|
50
|
+
# comment_options
|
51
|
+
# convert
|
52
|
+
# custom
|
53
|
+
# custom_json
|
54
|
+
# decline_voting_rights
|
55
|
+
# delegate_vesting_shares
|
56
|
+
# delete_comment
|
52
57
|
# escrow_approve
|
53
58
|
# escrow_dispute
|
54
59
|
# escrow_release
|
55
|
-
#
|
56
|
-
#
|
60
|
+
# escrow_transfer
|
61
|
+
# feed_publish
|
57
62
|
# limit_order_cancel
|
58
|
-
#
|
59
|
-
#
|
63
|
+
# limit_order_create
|
64
|
+
# limit_order_create2
|
65
|
+
# pow
|
66
|
+
# pow2
|
67
|
+
# recover_account
|
68
|
+
# request_account_recovery
|
69
|
+
# set_withdraw_vesting_route
|
70
|
+
# transfer
|
71
|
+
# transfer_from_savings
|
72
|
+
# transfer_to_savings
|
73
|
+
# transfer_to_vesting
|
60
74
|
# vote
|
61
|
-
#
|
62
|
-
# account_witness_proxy
|
63
|
-
# account_create
|
64
|
-
# account_update
|
75
|
+
# withdraw_vesting
|
65
76
|
# witness_update
|
66
|
-
# pow
|
67
|
-
# custom
|
68
77
|
#
|
69
78
|
# For example, to stream only votes:
|
70
79
|
#
|
@@ -73,30 +82,93 @@ module Radiator
|
|
73
82
|
# puts vote.to_json
|
74
83
|
# end
|
75
84
|
#
|
85
|
+
# You can also stream virtual operations:
|
86
|
+
#
|
87
|
+
# stream = Radiator::Stream.new
|
88
|
+
# stream.operations(:author_reward) do |vop|
|
89
|
+
# puts "#{vop.author} got paid for #{vop.permlink}: #{[vop.sbd_payout, vop.steem_payout, vop.vesting_payout]}"
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
# ... or multiple virtual operation types;
|
93
|
+
#
|
94
|
+
# stream = Radiator::Stream.new
|
95
|
+
# stream.operations([:produer_reward, :author_reward]) do |vop|
|
96
|
+
# puts vop
|
97
|
+
# end
|
98
|
+
#
|
99
|
+
# ... or all types, inluding virtual operation types;
|
100
|
+
#
|
101
|
+
# stream = Radiator::Stream.new
|
102
|
+
# stream.operations(nil, nil, :head, include_virtual: true) do |vop|
|
103
|
+
# puts vop
|
104
|
+
# end
|
105
|
+
#
|
106
|
+
# Expected virtual operation types:
|
107
|
+
#
|
108
|
+
# author_reward
|
109
|
+
# curation_reward
|
110
|
+
# fill_convert_request
|
111
|
+
# fill_order
|
112
|
+
# fill_vesting_withdraw
|
113
|
+
# interest
|
114
|
+
# shutdown_witness
|
115
|
+
#
|
76
116
|
# @param type [symbol || Array<symbol>] the type(s) of operation, optional.
|
77
117
|
# @param start starting block
|
78
118
|
# @param mode we have the choice between
|
79
119
|
# * :head the last block
|
80
120
|
# * :irreversible the block that is confirmed by 2/3 of all block producers and is thus irreversible!
|
81
121
|
# @param block the block to execute for each result, optional.
|
122
|
+
# @param options [Hash] additional options
|
123
|
+
# @option options [Boollean] :include_virtual Also stream virtual options. Setting this true will impact performance. Default: false.
|
82
124
|
# @return [Hash]
|
83
|
-
def operations(type = nil, start = nil, mode = :irreversible, &block)
|
84
|
-
|
125
|
+
def operations(type = nil, start = nil, mode = :irreversible, options = {include_virtual: false}, &block)
|
126
|
+
type = [type].flatten.compact.map(&:to_sym)
|
127
|
+
include_virtual = !!options[:include_virtual]
|
128
|
+
|
129
|
+
if virtual_op_type?(type)
|
130
|
+
include_virtual = true
|
131
|
+
end
|
132
|
+
|
133
|
+
latest_block_number = -1
|
134
|
+
|
135
|
+
transactions(start, mode) do |transaction, trx_id, block_number|
|
136
|
+
virtual_ops_collected = latest_block_number == block_number
|
137
|
+
latest_block_number = block_number
|
138
|
+
|
85
139
|
ops = transaction.operations.map do |t, op|
|
86
140
|
t = t.to_sym
|
87
|
-
if type == t
|
141
|
+
if type.size == 1 && type.first == t
|
88
142
|
op
|
89
|
-
elsif type.
|
143
|
+
elsif type.none? || type.include?(t)
|
90
144
|
{t => op}
|
91
145
|
end
|
92
146
|
end.compact
|
93
147
|
|
148
|
+
if include_virtual && !virtual_ops_collected
|
149
|
+
api.get_ops_in_block(block_number, true) do |vops|
|
150
|
+
vops.each do |vtx|
|
151
|
+
next unless defined? vtx.op
|
152
|
+
|
153
|
+
t = vtx.op.first.to_sym
|
154
|
+
op = vtx.op.last
|
155
|
+
if type.size == 1 && type.first == t
|
156
|
+
ops << op
|
157
|
+
elsif type.none? || type.include?(t)
|
158
|
+
ops << {t => op}
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
virtual_ops_collected = true
|
164
|
+
end
|
165
|
+
|
94
166
|
next if ops.none?
|
95
167
|
|
96
168
|
return ops unless !!block
|
97
169
|
|
98
170
|
ops.each do |op|
|
99
|
-
yield op
|
171
|
+
yield op, trx_id, block_number
|
100
172
|
end
|
101
173
|
end
|
102
174
|
end
|
@@ -115,7 +187,7 @@ module Radiator
|
|
115
187
|
# @param block the block to execute for each result, optional.
|
116
188
|
# @return [Hash]
|
117
189
|
def transactions(start = nil, mode = :irreversible, &block)
|
118
|
-
blocks(start, mode) do |b|
|
190
|
+
blocks(start, mode) do |b, block_number|
|
119
191
|
next if (_transactions = b.transactions).nil?
|
120
192
|
return _transactions unless !!block
|
121
193
|
|
@@ -124,7 +196,7 @@ module Radiator
|
|
124
196
|
b['transaction_ids'][index]
|
125
197
|
end
|
126
198
|
|
127
|
-
yield transaction, trx_id
|
199
|
+
yield transaction, trx_id, block_number
|
128
200
|
end
|
129
201
|
end
|
130
202
|
end
|
@@ -145,44 +217,72 @@ module Radiator
|
|
145
217
|
# @return [Hash]
|
146
218
|
def blocks(start = nil, mode = :irreversible, max_blocks_per_node = MAX_BLOCKS_PER_NODE, &block)
|
147
219
|
counter = 0
|
148
|
-
|
149
|
-
|
150
|
-
properties = api.get_dynamic_global_properties.result
|
151
|
-
start = case mode.to_sym
|
152
|
-
when :head then properties.head_block_number
|
153
|
-
when :irreversible then properties.last_irreversible_block_num
|
154
|
-
else; raise StreamError, '"mode" has to be "head" or "irreversible"'
|
155
|
-
end
|
156
|
-
end
|
220
|
+
latest_block_number = -1
|
221
|
+
@api_options[:max_requests] = [max_blocks_per_node * 2, @api_options[:max_requests].to_i].max
|
157
222
|
|
158
223
|
loop do
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
224
|
+
catch :sequence do; begin
|
225
|
+
head_block = api.get_dynamic_global_properties do |properties|
|
226
|
+
if properties.head_block_number.nil?
|
227
|
+
# This can happen if a reverse proxy is acting up.
|
228
|
+
@logger.warn "Bad block sequence after height: #{latest_block_number}"
|
229
|
+
throw :sequence
|
230
|
+
end
|
231
|
+
|
232
|
+
case mode.to_sym
|
233
|
+
when :head then properties.head_block_number
|
234
|
+
when :irreversible then properties.last_irreversible_block_num
|
235
|
+
else; raise StreamError, '"mode" has to be "head" or "irreversible"'
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
if head_block == latest_block_number
|
240
|
+
# This can when there's a delay in block production.
|
241
|
+
sleep 0.5
|
242
|
+
throw :sequence
|
243
|
+
elsif head_block < latest_block_number
|
244
|
+
# This can happen if a reverse proxy is acting up.
|
245
|
+
@logger.warn "Invalid block sequence at height: #{head_block}"
|
246
|
+
sleep 0.5
|
247
|
+
throw :sequence
|
171
248
|
end
|
249
|
+
|
250
|
+
start ||= head_block
|
251
|
+
range = (start..head_block)
|
252
|
+
|
253
|
+
if range.size > 400
|
254
|
+
# When the range is 400 blocks, the stream will be behind by about
|
255
|
+
# 20 minutes. Time to warn.
|
256
|
+
@logger.warn "Stream behind by #{range.size} blocks (about #{(range.size * 3) / 60.0} minutes)."
|
257
|
+
end
|
258
|
+
|
259
|
+
[*range].each do |n|
|
260
|
+
if (counter += 1) > max_blocks_per_node
|
261
|
+
shutdown
|
262
|
+
counter = 0
|
263
|
+
end
|
172
264
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
265
|
+
api.get_block(n) do |current_block, error|
|
266
|
+
if !!error
|
267
|
+
@logger.warn "Node responded with: #{error.message || 'unknown error'}"
|
268
|
+
ap error
|
269
|
+
throw :sequence
|
270
|
+
end
|
271
|
+
|
272
|
+
latest_block_number = n
|
273
|
+
return current_block, n if block.nil?
|
274
|
+
yield current_block, n
|
275
|
+
end
|
276
|
+
|
277
|
+
start = head_block + 1
|
278
|
+
sleep 3 / range.size
|
181
279
|
end
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
280
|
+
rescue StreamError; raise
|
281
|
+
rescue => e
|
282
|
+
@logger.warn "Unknown streaming error: #{e.inspect}, retrying ... "
|
283
|
+
ap e
|
284
|
+
redo
|
285
|
+
end; end
|
186
286
|
end
|
187
287
|
end
|
188
288
|
|
@@ -241,10 +341,6 @@ module Radiator
|
|
241
341
|
end
|
242
342
|
end
|
243
343
|
private
|
244
|
-
def api
|
245
|
-
@api ||= Api.new(@api_options)
|
246
|
-
end
|
247
|
-
|
248
344
|
def method_missing(m, *args, &block)
|
249
345
|
super unless respond_to_missing?(m)
|
250
346
|
|
@@ -292,5 +388,11 @@ module Radiator
|
|
292
388
|
@timeout = INITIAL_TIMEOUT if @timeout > MAX_TIMEOUT
|
293
389
|
@timeout
|
294
390
|
end
|
391
|
+
|
392
|
+
def virtual_op_type?(type)
|
393
|
+
type = [type].flatten.compact.map(&:to_sym)
|
394
|
+
|
395
|
+
(Radiator::OperationTypes::TYPES.keys && type).any?
|
396
|
+
end
|
295
397
|
end
|
296
398
|
end
|
data/lib/radiator/transaction.rb
CHANGED
@@ -106,6 +106,11 @@ module Radiator
|
|
106
106
|
# The expiration allows for transactions to expire if they are not
|
107
107
|
# included into a block by that time. Always update it to the current
|
108
108
|
# time + EXPIRE_IN_SECS.
|
109
|
+
#
|
110
|
+
# Note, as of #1215, expiration exactly 'now' will be rejected:
|
111
|
+
# https://github.com/steemit/steem/blob/57451b80d2cf480dcce9b399e48e56aa7af1d818/libraries/chain/database.cpp#L2870
|
112
|
+
# https://github.com/steemit/steem/issues/1215
|
113
|
+
|
109
114
|
@expiration = Time.parse(@properties.time + 'Z') + EXPIRE_IN_SECS
|
110
115
|
|
111
116
|
self
|
data/lib/radiator/version.rb
CHANGED
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.
|
4
|
+
version: 0.3.0dev4
|
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-
|
11
|
+
date: 2017-10-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -410,7 +410,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
410
410
|
version: 1.3.1
|
411
411
|
requirements: []
|
412
412
|
rubyforge_project:
|
413
|
-
rubygems_version: 2.6.
|
413
|
+
rubygems_version: 2.6.12
|
414
414
|
signing_key:
|
415
415
|
specification_version: 4
|
416
416
|
summary: STEEM RPC Ruby Client
|