hive-ruby 1.0.0.pre.1

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.
Files changed (80) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +54 -0
  3. data/CONTRIBUTING.md +79 -0
  4. data/Gemfile +3 -0
  5. data/Gemfile.lock +91 -0
  6. data/LICENSE +21 -0
  7. data/README.md +248 -0
  8. data/Rakefile +358 -0
  9. data/gource.sh +6 -0
  10. data/hive-ruby.gemspec +40 -0
  11. data/images/Anthony Martin.png +0 -0
  12. data/lib/hive.rb +89 -0
  13. data/lib/hive/api.rb +223 -0
  14. data/lib/hive/base_error.rb +218 -0
  15. data/lib/hive/block_api.rb +78 -0
  16. data/lib/hive/bridge.rb +12 -0
  17. data/lib/hive/broadcast.rb +1334 -0
  18. data/lib/hive/chain_config.rb +34 -0
  19. data/lib/hive/fallback.rb +287 -0
  20. data/lib/hive/formatter.rb +14 -0
  21. data/lib/hive/jsonrpc.rb +112 -0
  22. data/lib/hive/marshal.rb +231 -0
  23. data/lib/hive/mixins/jsonable.rb +37 -0
  24. data/lib/hive/mixins/retriable.rb +58 -0
  25. data/lib/hive/mixins/serializable.rb +45 -0
  26. data/lib/hive/operation.rb +141 -0
  27. data/lib/hive/operation/account_create.rb +10 -0
  28. data/lib/hive/operation/account_create_with_delegation.rb +12 -0
  29. data/lib/hive/operation/account_update.rb +8 -0
  30. data/lib/hive/operation/account_witness_proxy.rb +4 -0
  31. data/lib/hive/operation/account_witness_vote.rb +5 -0
  32. data/lib/hive/operation/cancel_transfer_from_savings.rb +4 -0
  33. data/lib/hive/operation/challenge_authority.rb +5 -0
  34. data/lib/hive/operation/change_recovery_account.rb +5 -0
  35. data/lib/hive/operation/claim_account.rb +5 -0
  36. data/lib/hive/operation/claim_reward_balance.rb +6 -0
  37. data/lib/hive/operation/comment.rb +9 -0
  38. data/lib/hive/operation/comment_options.rb +10 -0
  39. data/lib/hive/operation/convert.rb +5 -0
  40. data/lib/hive/operation/create_claimed_account.rb +10 -0
  41. data/lib/hive/operation/custom.rb +5 -0
  42. data/lib/hive/operation/custom_binary.rb +8 -0
  43. data/lib/hive/operation/custom_json.rb +6 -0
  44. data/lib/hive/operation/decline_voting_rights.rb +4 -0
  45. data/lib/hive/operation/delegate_vesting_shares.rb +5 -0
  46. data/lib/hive/operation/delete_comment.rb +4 -0
  47. data/lib/hive/operation/escrow_approve.rb +8 -0
  48. data/lib/hive/operation/escrow_dispute.rb +7 -0
  49. data/lib/hive/operation/escrow_release.rb +10 -0
  50. data/lib/hive/operation/escrow_transfer.rb +12 -0
  51. data/lib/hive/operation/feed_publish.rb +4 -0
  52. data/lib/hive/operation/limit_order_cancel.rb +4 -0
  53. data/lib/hive/operation/limit_order_create.rb +8 -0
  54. data/lib/hive/operation/limit_order_create2.rb +8 -0
  55. data/lib/hive/operation/prove_authority.rb +4 -0
  56. data/lib/hive/operation/recover_account.rb +6 -0
  57. data/lib/hive/operation/report_over_production.rb +5 -0
  58. data/lib/hive/operation/request_account_recovery.rb +6 -0
  59. data/lib/hive/operation/reset_account.rb +5 -0
  60. data/lib/hive/operation/set_reset_account.rb +5 -0
  61. data/lib/hive/operation/set_withdraw_vesting_route.rb +6 -0
  62. data/lib/hive/operation/transfer.rb +6 -0
  63. data/lib/hive/operation/transfer_from_savings.rb +7 -0
  64. data/lib/hive/operation/transfer_to_savings.rb +6 -0
  65. data/lib/hive/operation/transfer_to_vesting.rb +5 -0
  66. data/lib/hive/operation/vote.rb +6 -0
  67. data/lib/hive/operation/withdraw_vesting.rb +4 -0
  68. data/lib/hive/operation/witness_set_properties.rb +5 -0
  69. data/lib/hive/operation/witness_update.rb +7 -0
  70. data/lib/hive/rpc/base_client.rb +179 -0
  71. data/lib/hive/rpc/http_client.rb +143 -0
  72. data/lib/hive/rpc/thread_safe_http_client.rb +35 -0
  73. data/lib/hive/stream.rb +385 -0
  74. data/lib/hive/transaction.rb +115 -0
  75. data/lib/hive/transaction_builder.rb +406 -0
  76. data/lib/hive/type/amount.rb +126 -0
  77. data/lib/hive/type/base_type.rb +10 -0
  78. data/lib/hive/utils.rb +17 -0
  79. data/lib/hive/version.rb +4 -0
  80. metadata +502 -0
@@ -0,0 +1,143 @@
1
+ module Hive
2
+ module RPC
3
+ # {HttpClient} is intended for single-threaded applications. For
4
+ # multi-threaded apps, use {ThreadSafeHttpClient}.
5
+ class HttpClient < BaseClient
6
+ # Timeouts are lower level errors, related in that retrying them is
7
+ # trivial, unlike, for example TransactionExpiredError, that *requires*
8
+ # the client to do something before retrying.
9
+ #
10
+ # These situations are hopefully momentary interruptions or rate limiting
11
+ # but they might indicate a bigger problem with the node, so they are not
12
+ # retried forever, only up to MAX_TIMEOUT_RETRY_COUNT and then we give up.
13
+ #
14
+ # *Note:* {JSON::ParserError} is included in this list because under
15
+ # certain timeout conditions, a web server may respond with a generic
16
+ # http status code of 200 and HTML page.
17
+ #
18
+ # @private
19
+ TIMEOUT_ERRORS = [Net::OpenTimeout, JSON::ParserError, Net::ReadTimeout,
20
+ Errno::EBADF, IOError, Errno::ENETDOWN, Hive::RemoteDatabaseLockError]
21
+
22
+ # @private
23
+ POST_HEADERS = {
24
+ 'Content-Type' => 'application/json; charset=utf-8',
25
+ 'User-Agent' => Hive::AGENT_ID
26
+ }
27
+
28
+ JSON_RPC_BATCH_SIZE_MAXIMUM = 50
29
+
30
+ def http
31
+ @http ||= Net::HTTP.new(uri.host, uri.port).tap do |http|
32
+ http.use_ssl = true if uri.to_s =~ /^https/i
33
+ http.keep_alive_timeout = 150 # seconds
34
+
35
+ # WARNING This method opens a serious security hole. Never use this
36
+ # method in production code.
37
+ # http.set_debug_output(STDOUT) if !!ENV['DEBUG']
38
+ end
39
+ end
40
+
41
+ def http_post
42
+ @http_post ||= Net::HTTP::Post.new(uri.request_uri, POST_HEADERS)
43
+ end
44
+
45
+ def http_request(request)
46
+ http.request(request)
47
+ end
48
+
49
+ # This is the main method used by API instances to actually fetch data
50
+ # from the remote node. It abstracts the api namespace, method name, and
51
+ # parameters so that the API instance can be decoupled from the protocol.
52
+ #
53
+ # @param api_name [String] API namespace of the method being called.
54
+ # @param api_method [String] API method name being called.
55
+ # @param options [Hash] options
56
+ # @option options [Object] :request_object Hash or Array to become json in request body.
57
+ def rpc_execute(api_name = @api_name, api_method = nil, options = {}, &block)
58
+ reset_timeout
59
+
60
+ catch :tota_cera_pila do; begin
61
+ request = http_post
62
+
63
+ request_object = if !!api_name && !!api_method
64
+ put(api_name, api_method, options)
65
+ elsif !!options && defined?(options.delete)
66
+ options.delete(:request_object)
67
+ end
68
+
69
+ if request_object.size > JSON_RPC_BATCH_SIZE_MAXIMUM
70
+ raise JsonRpcBatchMaximumSizeExceededError, "Maximum json-rpc-batch is #{JSON_RPC_BATCH_SIZE_MAXIMUM} elements."
71
+ end
72
+
73
+ request.body = if request_object.class == Hash
74
+ request_object
75
+ elsif request_object.size == 1
76
+ request_object.first
77
+ else
78
+ request_object
79
+ end.to_json
80
+
81
+ response = catch :http_request do; begin; http_request(request)
82
+ rescue *TIMEOUT_ERRORS => e
83
+ throw retry_timeout(:http_request, e)
84
+ end; end
85
+
86
+ if response.nil?
87
+ throw retry_timeout(:tota_cera_pila, 'response was nil')
88
+ end
89
+
90
+ case response.code
91
+ when '200'
92
+ response = catch :parse_json do; begin; JSON[response.body]
93
+ rescue *TIMEOUT_ERRORS => e
94
+ throw retry_timeout(:parse_json, e)
95
+ end; end
96
+
97
+ response = case response
98
+ when Hash
99
+ Hashie::Mash.new(response).tap do |r|
100
+ evaluate_id(request: request_object.first, response: r, api_method: api_method)
101
+ end
102
+ when Array
103
+ Hashie::Array.new(response).tap do |r|
104
+ request_object.each_with_index do |req, index|
105
+ evaluate_id(request: req, response: r[index], api_method: api_method)
106
+ end
107
+ end
108
+ else; response
109
+ end
110
+
111
+ [response].flatten.each_with_index do |r, i|
112
+ if defined?(r.error) && !!r.error
113
+ if !!r.error.message
114
+ begin
115
+ rpc_method_name = "#{api_name}.#{api_method}"
116
+ rpc_args = [request_object].flatten[i]
117
+ raise_error_response rpc_method_name, rpc_args, r
118
+ rescue *TIMEOUT_ERRORS => e
119
+ throw retry_timeout(:tota_cera_pila, e)
120
+ end
121
+ else
122
+ raise Hive::ArgumentError, r.error.inspect
123
+ end
124
+ end
125
+ end
126
+
127
+ yield_response response, &block
128
+ when '504' # Gateway Timeout
129
+ throw retry_timeout(:tota_cera_pila, response.body)
130
+ when '502' # Bad Gateway
131
+ throw retry_timeout(:tota_cera_pila, response.body)
132
+ else
133
+ raise UnknownError, "#{api_name}.#{api_method}: #{response.body}"
134
+ end
135
+ end; end
136
+ end
137
+
138
+ def rpc_batch_execute(options = {}, &block)
139
+ yield_response rpc_execute(nil, nil, options), &block
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,35 @@
1
+ module Hive
2
+ module RPC
3
+ # {ThreadSafeHttpClient} is the default RPC Client used by `hive-ruby.`
4
+ # It's perfect for simple requests. But for higher performance, it's better
5
+ # to override {HttpClient} and implement something other than {Net::HTTP}.
6
+ #
7
+ # It performs http requests in a {Mutex} critical section because {Net::HTTP}
8
+ # is not thread safe. This is the very minimum level thread safety
9
+ # available.
10
+ class ThreadSafeHttpClient < HttpClient
11
+ SEMAPHORE = Mutex.new.freeze
12
+
13
+ # Same as #{HttpClient#http_post}, but scoped to each thread so it is
14
+ # thread safe.
15
+ def http_post
16
+ thread = Thread.current
17
+ http_post = thread.thread_variable_get(:http_post)
18
+ http_post ||= Net::HTTP::Post.new(uri.request_uri, POST_HEADERS)
19
+ thread.thread_variable_set(:http_post, http_post)
20
+ end
21
+
22
+ def http_request(request); SEMAPHORE.synchronize{super}; end
23
+
24
+ # Same as #{BaseClient#rpc_id}, auto-increment, but scoped to each thread
25
+ # so it is thread safe.
26
+ def rpc_id
27
+ thread = Thread.current
28
+ rpc_id = thread.thread_variable_get(:rpc_id)
29
+ rpc_id ||= 0
30
+ rpc_id += 1
31
+ thread.thread_variable_set(:rpc_id, rpc_id)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,385 @@
1
+ module Hive
2
+ # Hive::Stream allows a live view of the HIVE blockchain.
3
+ #
4
+ # Example streaming blocks:
5
+ #
6
+ # stream = Hive::Stream.new
7
+ #
8
+ # stream.blocks do |block, block_num|
9
+ # puts "#{block_num} :: #{block.witness}"
10
+ # end
11
+ #
12
+ # Example streaming transactions:
13
+ #
14
+ # stream = Hive::Stream.new
15
+ #
16
+ # stream.transactions do |trx, trx_id, block_num|
17
+ # puts "#{block_num} :: #{trx_id} :: operations: #{trx.operations.size}"
18
+ # end
19
+ #
20
+ # Example streaming operations:
21
+ #
22
+ # stream = Hive::Stream.new
23
+ #
24
+ # stream.operations do |op, trx_id, block_num|
25
+ # puts "#{block_num} :: #{trx_id} :: #{op.type}: #{op.value.to_json}"
26
+ # end
27
+ #
28
+ # Allows streaming of block headers, full blocks, transactions, operations and
29
+ # virtual operations.
30
+ class Stream
31
+ attr_reader :database_api, :block_api, :account_history_api, :mode
32
+
33
+ BLOCK_INTERVAL = 3
34
+ MAX_BACKOFF_BLOCK_INTERVAL = 30
35
+ MAX_RETRY_COUNT = 10
36
+
37
+ VOP_TRX_ID = ('0' * 40).freeze
38
+
39
+ # @param options [Hash] additional options
40
+ # @option options [Hive::DatabaseApi] :database_api
41
+ # @option options [Hive::BlockApi] :block_api
42
+ # @option options [Hive::AccountHistoryApi || Hive::CondenserApi] :account_history_api
43
+ # @option options [Symbol] :mode we have the choice between
44
+ # * :head the last block
45
+ # * :irreversible the block that is confirmed by 2/3 of all block producers and is thus irreversible!
46
+ # @option options [Boolean] :no_warn do not generate warnings
47
+ def initialize(options = {mode: :irreversible})
48
+ @instance_options = options
49
+ @database_api = options[:database_api] || Hive::DatabaseApi.new(options)
50
+ @block_api = options[:block_api] || Hive::BlockApi.new(options)
51
+ @account_history_api = options[:account_history_api]
52
+ @mode = options[:mode] || :irreversible
53
+ @no_warn = !!options[:no_warn]
54
+ end
55
+
56
+ # Use this method to stream block numbers. This is significantly faster
57
+ # than requesting full blocks and even block headers. Basically, the only
58
+ # thing this method does is call {Hive::Database#get_dynamic_global_properties} at 3 second
59
+ # intervals.
60
+ #
61
+ # @param options [Hash] additional options
62
+ # @option options [Integer] :at_block_num Starts the stream at the given block number. Default: nil.
63
+ # @option options [Integer] :until_block_num Ends the stream at the given block number. Default: nil.
64
+ def block_numbers(options = {}, &block)
65
+ block_objects(options.merge(object: :block_numbers), block)
66
+ end
67
+
68
+ # Use this method to stream block headers. This is quite a bit faster than
69
+ # requesting full blocks.
70
+ #
71
+ # @param options [Hash] additional options
72
+ # @option options [Integer] :at_block_num Starts the stream at the given block number. Default: nil.
73
+ # @option options [Integer] :until_block_num Ends the stream at the given block number. Default: nil.
74
+ def block_headers(options = {}, &block)
75
+ block_objects(options.merge(object: :block_headers), block)
76
+ end
77
+
78
+ # Use this method to stream full blocks.
79
+ #
80
+ # @param options [Hash] additional options
81
+ # @option options [Integer] :at_block_num Starts the stream at the given block number. Default: nil.
82
+ # @option options [Integer] :until_block_num Ends the stream at the given block number. Default: nil.
83
+ def blocks(options = {}, &block)
84
+ block_objects(options.merge(object: :blocks), block)
85
+ end
86
+
87
+ # Use this method to stream each transaction.
88
+ #
89
+ # @param options [Hash] additional options
90
+ # @option options [Integer] :at_block_num Starts the stream at the given block number. Default: nil.
91
+ # @option options [Integer] :until_block_num Ends the stream at the given block number. Default: nil.
92
+ def transactions(options = {}, &block)
93
+ blocks(options) do |block, block_num|
94
+ if block.nil?
95
+ warn "Batch missing block_num: #{block_num}, retrying ..."
96
+
97
+ block = block_api.get_block(block_num: block_num) do |result|
98
+ result.block
99
+ end
100
+ end
101
+
102
+ block.transactions.each_with_index do |transaction, index|
103
+ trx_id = block.transaction_ids[index]
104
+
105
+ yield transaction, trx_id, block_num
106
+ end
107
+ end
108
+ end
109
+
110
+ # Returns the latest operations from the blockchain.
111
+ #
112
+ # stream = Hive::Stream.new
113
+ # stream.operations do |op|
114
+ # puts op.to_json
115
+ # end
116
+ #
117
+ # If symbol are passed to `types` option, then only that operation is
118
+ # returned. Expected symbols are:
119
+ #
120
+ # account_create_operation
121
+ # account_create_with_delegation_operation
122
+ # account_update_operation
123
+ # account_witness_proxy_operation
124
+ # account_witness_vote_operation
125
+ # cancel_transfer_from_savings_operation
126
+ # change_recovery_account_operation
127
+ # claim_reward_balance_operation
128
+ # comment_operation
129
+ # comment_options_operation
130
+ # convert_operation
131
+ # custom_operation
132
+ # custom_json_operation
133
+ # decline_voting_rights_operation
134
+ # delegate_vesting_shares_operation
135
+ # delete_comment_operation
136
+ # escrow_approve_operation
137
+ # escrow_dispute_operation
138
+ # escrow_release_operation
139
+ # escrow_transfer_operation
140
+ # feed_publish_operation
141
+ # limit_order_cancel_operation
142
+ # limit_order_create_operation
143
+ # limit_order_create2_operation
144
+ # pow_operation
145
+ # pow2_operation
146
+ # recover_account_operation
147
+ # request_account_recovery_operation
148
+ # set_withdraw_vesting_route_operation
149
+ # transfer_operation
150
+ # transfer_from_savings_operation
151
+ # transfer_to_savings_operation
152
+ # transfer_to_vesting_operation
153
+ # vote_operation
154
+ # withdraw_vesting_operation
155
+ # witness_update_operation
156
+ #
157
+ # For example, to stream only votes:
158
+ #
159
+ # stream = Hive::Stream.new
160
+ # stream.operations(types: :vote_operation) do |vote|
161
+ # puts vote.to_json
162
+ # end
163
+ #
164
+ # ... Or ...
165
+ #
166
+ # stream = Hive::Stream.new
167
+ # stream.operations(:vote_operation) do |vote|
168
+ # puts vote.to_json
169
+ # end
170
+ #
171
+ # You can also stream virtual operations:
172
+ #
173
+ # stream = Hive::Stream.new
174
+ # stream.operations(types: :author_reward_operation, only_virtual: true) do |vop|
175
+ # v = vop.value
176
+ # puts "#{v.author} got paid for #{v.permlink}: #{[v.sbd_payout, v.steem_payout, v.vesting_payout]}"
177
+ # end
178
+ #
179
+ # ... or multiple virtual operation types;
180
+ #
181
+ # stream = Hive::Stream.new
182
+ # stream.operations(types: [:producer_reward_operation, :author_reward_operation], only_virtual: true) do |vop|
183
+ # puts vop.to_json
184
+ # end
185
+ #
186
+ # ... or all types, including virtual operation types from the head block number:
187
+ #
188
+ # stream = Hive::Stream.new(mode: :head)
189
+ # stream.operations(include_virtual: true) do |op|
190
+ # puts op.to_json
191
+ # end
192
+ #
193
+ # Expected virtual operation types:
194
+ #
195
+ # producer_reward_operation
196
+ # author_reward_operation
197
+ # curation_reward_operation
198
+ # fill_convert_request_operation
199
+ # fill_order_operation
200
+ # fill_vesting_withdraw_operation
201
+ # interest_operation
202
+ # shutdown_witness_operation
203
+ #
204
+ # @param args [Symbol || Array<Symbol> || Hash] the type(s) of operation or hash of expanded options, optional.
205
+ # @option args [Integer] :at_block_num Starts the stream at the given block number. Default: nil.
206
+ # @option args [Integer] :until_block_num Ends the stream at the given block number. Default: nil.
207
+ # @option args [Symbol || Array<Symbol>] :types the type(s) of operation, optional.
208
+ # @option args [Boolean] :only_virtual Only stream virtual options. Setting this true will improve performance because the stream only needs block numbers to then retrieve virtual operations. Default: false.
209
+ # @option args [Boolean] :include_virtual Also stream virtual options. Setting this true will impact performance. Default: false.
210
+ # @param block the block to execute for each result. Yields: |op, trx_id, block_num|
211
+ def operations(*args, &block)
212
+ options = {}
213
+ types = []
214
+ only_virtual = false
215
+ include_virtual = false
216
+ last_block_num = nil
217
+
218
+ case args.first
219
+ when Hash
220
+ options = args.first
221
+ types = transform_types(options[:types])
222
+ only_virtual = !!options[:only_virtual] || false
223
+ include_virtual = !!options[:include_virtual] || only_virtual || false
224
+ when Symbol, Array then types = transform_types(args)
225
+ end
226
+
227
+ if only_virtual
228
+ block_numbers(options) do |block_num|
229
+ get_virtual_ops(types, block_num, block)
230
+ end
231
+ else
232
+ transactions(options) do |transaction, trx_id, block_num|
233
+ transaction.operations.each do |op|
234
+ yield op, trx_id, block_num if types.none? || types.include?(op.type)
235
+
236
+ next unless last_block_num != block_num
237
+
238
+ last_block_num = block_num
239
+
240
+ get_virtual_ops(types, block_num, block) if include_virtual
241
+ end
242
+ end
243
+ end
244
+ end
245
+
246
+ def account_history_api
247
+ @account_history_api ||= begin
248
+ Hive::AccountHistoryApi.new(@instance_options)
249
+ rescue Hive::UnknownApiError => e
250
+ warn "#{e.inspect}, falling back to Hive::CondenserApi." unless @no_warn
251
+ Hive::CondenserApi.new(@instance_options)
252
+ end
253
+ end
254
+ private
255
+ # @private
256
+ def block_objects(options = {}, block)
257
+ object = options[:object]
258
+ object_method = "get_#{object}".to_sym
259
+ block_interval = BLOCK_INTERVAL
260
+
261
+ at_block_num, until_block_num = if !!block_range = options[:block_range]
262
+ [block_range.first, block_range.last]
263
+ else
264
+ [options[:at_block_num], options[:until_block_num]]
265
+ end
266
+
267
+ loop do
268
+ break if !!until_block_num && !!at_block_num && until_block_num < at_block_num
269
+
270
+ database_api.get_dynamic_global_properties do |properties|
271
+ current_block_num = find_block_number(properties)
272
+ current_block_num = [current_block_num, until_block_num].compact.min
273
+ at_block_num ||= current_block_num
274
+
275
+ if current_block_num >= at_block_num
276
+ range = at_block_num..current_block_num
277
+
278
+ if object == :block_numbers
279
+ range.each do |n|
280
+ block.call n
281
+ block_interval = BLOCK_INTERVAL
282
+ end
283
+ else
284
+ block_api.send(object_method, block_range: range) do |b, n|
285
+ block.call b, n
286
+ block_interval = BLOCK_INTERVAL
287
+ end
288
+ end
289
+
290
+ at_block_num = range.max + 1
291
+ else
292
+ # The stream has stalled, so let's back off and let the node sync
293
+ # up. We'll catch up with a bigger batch in the next cycle.
294
+ block_interval = [block_interval * 2, MAX_BACKOFF_BLOCK_INTERVAL].min
295
+ end
296
+ end
297
+
298
+ sleep block_interval
299
+ end
300
+ end
301
+
302
+ # @private
303
+ def find_block_number(properties)
304
+ block_num = case mode
305
+ when :head then properties.head_block_number
306
+ when :irreversible then properties.last_irreversible_block_num
307
+ else; raise Hive::ArgumentError, "Unknown mode: #{mode}"
308
+ end
309
+
310
+ block_num
311
+ end
312
+
313
+ # @private
314
+ def transform_types(types)
315
+ [types].compact.flatten.map do |type|
316
+ type = type.to_s
317
+
318
+ unless type.end_with? '_operation'
319
+ warn "Op type #{type} is deprecated. Use #{type}_operation instead." unless @no_warn
320
+ type += '_operation'
321
+ end
322
+
323
+ type
324
+ end
325
+ end
326
+
327
+ # @private
328
+ def get_virtual_ops(types, block_num, block)
329
+ retries = 0
330
+
331
+ loop do
332
+ get_ops_in_block_options = case account_history_api
333
+ when Hive::CondenserApi
334
+ [block_num, true]
335
+ when Hive::AccountHistoryApi
336
+ {
337
+ block_num: block_num,
338
+ only_virtual: true
339
+ }
340
+ end
341
+
342
+ response = account_history_api.get_ops_in_block(*get_ops_in_block_options)
343
+
344
+ if response.nil? || (result = response.result).nil?
345
+ if retries < MAX_RETRY_COUNT
346
+ warn "Retrying get_ops_in_block on block #{block_num}" unless @no_warn
347
+ retries = retries + 1
348
+ sleep 9
349
+ redo
350
+ else
351
+ raise TooManyRetriesError, "unable to get valid result while finding virtual operations for block: #{block_num}"
352
+ end
353
+ end
354
+
355
+ ops = case account_history_api
356
+ when Hive::CondenserApi
357
+ result.map do |trx|
358
+ op = {type: trx.op[0] + '_operation', value: trx.op[1]}
359
+ op = Hashie::Mash.new(op)
360
+ end
361
+ when Hive::AccountHistoryApi then result.ops.map { |trx| trx.op }
362
+ end
363
+
364
+ if ops.empty?
365
+ if retries < MAX_RETRY_COUNT
366
+ sleep 3
367
+ retries = retries + 1
368
+ redo
369
+ else
370
+ warn "unable to find virtual operations for block: #{block_num}"
371
+ # raise TooManyRetriesError, "unable to find virtual operations for block: #{block_num}"
372
+ end
373
+ end
374
+
375
+ ops.each do |op|
376
+ next if types.any? && !types.include?(op.type)
377
+
378
+ block.call op, VOP_TRX_ID, block_num
379
+ end
380
+
381
+ break
382
+ end
383
+ end
384
+ end
385
+ end