rubybear 0.0.2
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 +7 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +77 -0
- data/LICENSE +41 -0
- data/README.md +559 -0
- data/Rakefile +140 -0
- data/gource.sh +8 -0
- data/images/Anthony Martin.png +0 -0
- data/images/Marvin Hofmann.jpg +0 -0
- data/images/Marvin Hofmann.png +0 -0
- data/lib/bears.rb +17 -0
- data/lib/rubybear.rb +45 -0
- data/lib/rubybear/account_by_key_api.rb +7 -0
- data/lib/rubybear/account_history_api.rb +15 -0
- data/lib/rubybear/api.rb +907 -0
- data/lib/rubybear/base_error.rb +23 -0
- data/lib/rubybear/block_api.rb +14 -0
- data/lib/rubybear/broadcast_operations.json +500 -0
- data/lib/rubybear/chain.rb +299 -0
- data/lib/rubybear/chain_config.rb +22 -0
- data/lib/rubybear/chain_stats_api.rb +15 -0
- data/lib/rubybear/condenser_api.rb +99 -0
- data/lib/rubybear/database_api.rb +5 -0
- data/lib/rubybear/error_parser.rb +228 -0
- data/lib/rubybear/follow_api.rb +7 -0
- data/lib/rubybear/logger.rb +20 -0
- data/lib/rubybear/market_history_api.rb +19 -0
- data/lib/rubybear/methods.json +498 -0
- data/lib/rubybear/mixins/acts_as_poster.rb +124 -0
- data/lib/rubybear/mixins/acts_as_voter.rb +50 -0
- data/lib/rubybear/mixins/acts_as_wallet.rb +67 -0
- data/lib/rubybear/network_broadcast_api.rb +7 -0
- data/lib/rubybear/operation.rb +101 -0
- data/lib/rubybear/operation_ids.rb +98 -0
- data/lib/rubybear/operation_types.rb +139 -0
- data/lib/rubybear/stream.rb +527 -0
- data/lib/rubybear/tag_api.rb +33 -0
- data/lib/rubybear/transaction.rb +306 -0
- data/lib/rubybear/type/amount.rb +57 -0
- data/lib/rubybear/type/array.rb +17 -0
- data/lib/rubybear/type/beneficiaries.rb +29 -0
- data/lib/rubybear/type/future.rb +18 -0
- data/lib/rubybear/type/hash.rb +17 -0
- data/lib/rubybear/type/permission.rb +19 -0
- data/lib/rubybear/type/point_in_time.rb +19 -0
- data/lib/rubybear/type/price.rb +25 -0
- data/lib/rubybear/type/public_key.rb +18 -0
- data/lib/rubybear/type/serializer.rb +12 -0
- data/lib/rubybear/type/u_int16.rb +19 -0
- data/lib/rubybear/type/u_int32.rb +19 -0
- data/lib/rubybear/utils.rb +170 -0
- data/lib/rubybear/version.rb +4 -0
- data/rubybear.gemspec +40 -0
- metadata +412 -0
@@ -0,0 +1,139 @@
|
|
1
|
+
module Rubybear
|
2
|
+
|
3
|
+
# See: https://github.com/bearshares/bears-js/blob/766746adb5ded86380be982c844f4c269f7800ae/src/auth/serializer/src/operations.js
|
4
|
+
module OperationTypes
|
5
|
+
TYPES = {
|
6
|
+
transfer: {
|
7
|
+
amount: Type::Amount
|
8
|
+
},
|
9
|
+
transfer_to_coining: {
|
10
|
+
amount: Type::Amount
|
11
|
+
},
|
12
|
+
withdraw_coining: {
|
13
|
+
coining_shares: Type::Amount
|
14
|
+
},
|
15
|
+
limit_order_create: {
|
16
|
+
orderid: Type::Uint32,
|
17
|
+
amount_to_sell: Type::Amount,
|
18
|
+
min_to_receive: Type::Amount,
|
19
|
+
expiration: Type::PointInTime
|
20
|
+
},
|
21
|
+
limit_order_cancel: {
|
22
|
+
orderid: Type::Uint32
|
23
|
+
},
|
24
|
+
feed_publish: {
|
25
|
+
exchange_rate: Type::Price
|
26
|
+
},
|
27
|
+
convert: {
|
28
|
+
requestid: Type::Uint32,
|
29
|
+
amount: Type::Amount
|
30
|
+
},
|
31
|
+
account_create: {
|
32
|
+
fee: Type::Amount,
|
33
|
+
owner: Type::Permission,
|
34
|
+
active: Type::Permission,
|
35
|
+
posting: Type::Permission,
|
36
|
+
memo: Type::PublicKey
|
37
|
+
},
|
38
|
+
create_claimed_account: {
|
39
|
+
owner: Type::Permission,
|
40
|
+
active: Type::Permission,
|
41
|
+
posting: Type::Permission,
|
42
|
+
memo: Type::PublicKey
|
43
|
+
},
|
44
|
+
account_update: {
|
45
|
+
owner: Type::Permission,
|
46
|
+
active: Type::Permission,
|
47
|
+
posting: Type::Permission,
|
48
|
+
memo: Type::PublicKey
|
49
|
+
},
|
50
|
+
custom: {
|
51
|
+
id: Type::Uint16
|
52
|
+
},
|
53
|
+
comment_options: {
|
54
|
+
max_accepted_payout: Type::Amount,
|
55
|
+
allow_replies: Type::Future
|
56
|
+
},
|
57
|
+
set_withdraw_coining_route: {
|
58
|
+
percent: Type::Uint16
|
59
|
+
},
|
60
|
+
limit_order_create2: {
|
61
|
+
orderid: Type::Uint32,
|
62
|
+
amount_to_sell: Type::Amount,
|
63
|
+
exchange_rate: Type::Price,
|
64
|
+
expiration: Type::PointInTime
|
65
|
+
},
|
66
|
+
request_account_recovery: {
|
67
|
+
new_owner_Permission: Type::Permission
|
68
|
+
},
|
69
|
+
recover_account: {
|
70
|
+
new_owner_Permission: Type::Permission,
|
71
|
+
recent_owner_Permission: Type::Permission
|
72
|
+
},
|
73
|
+
escrow_transfer: {
|
74
|
+
bsd_amount: Type::Amount,
|
75
|
+
bears_amount: Type::Amount,
|
76
|
+
escrow_id: Type::Uint32,
|
77
|
+
fee: Type::Amount,
|
78
|
+
ratification_deadline: Type::PointInTime,
|
79
|
+
escrow_expiration: Type::PointInTime
|
80
|
+
},
|
81
|
+
escrow_dispute: {
|
82
|
+
escrow_id: Type::Uint32
|
83
|
+
},
|
84
|
+
escrow_release: {
|
85
|
+
escrow_id: Type::Uint32,
|
86
|
+
bsd_amount: Type::Amount,
|
87
|
+
bears_amount: Type::Amount
|
88
|
+
},
|
89
|
+
escrow_approve: {
|
90
|
+
escrow_id: Type::Uint32
|
91
|
+
},
|
92
|
+
transfer_to_savings: {
|
93
|
+
amount: Type::Amount
|
94
|
+
},
|
95
|
+
transfer_from_savings: {
|
96
|
+
request_id: Type::Uint32,
|
97
|
+
amount: Type::Amount
|
98
|
+
},
|
99
|
+
cancel_transfer_from_savings: {
|
100
|
+
request_id: Type::Uint32
|
101
|
+
},
|
102
|
+
reset_account: {
|
103
|
+
new_owner_permission: Type::Amount
|
104
|
+
},
|
105
|
+
set_reset_account: {
|
106
|
+
reward_bears: Type::Amount,
|
107
|
+
reward_bsd: Type::Amount,
|
108
|
+
reward_coins: Type::Amount
|
109
|
+
},
|
110
|
+
claim_reward_balance: {
|
111
|
+
reward_bears: Type::Amount,
|
112
|
+
reward_bsd: Type::Amount,
|
113
|
+
reward_coins: Type::Amount
|
114
|
+
},
|
115
|
+
delegate_coining_shares: {
|
116
|
+
coining_shares: Type::Amount
|
117
|
+
},
|
118
|
+
claim_account: {
|
119
|
+
fee: Type::Amount
|
120
|
+
},
|
121
|
+
witness_update: {
|
122
|
+
block_signing_key: Type::PublicKey,
|
123
|
+
props: Type::Array
|
124
|
+
},
|
125
|
+
witness_set_properties: {
|
126
|
+
props: Type::Hash
|
127
|
+
}
|
128
|
+
}
|
129
|
+
|
130
|
+
def type(key, param, value)
|
131
|
+
return if value.nil?
|
132
|
+
|
133
|
+
t = TYPES[key] or return value
|
134
|
+
p = t[param] or return value
|
135
|
+
|
136
|
+
p.new(value)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,527 @@
|
|
1
|
+
module Rubybear
|
2
|
+
# Rubybear::Stream allows a live view of the BEARS blockchain.
|
3
|
+
#
|
4
|
+
# All values returned by `get_dynamic_global_properties` can be streamed.
|
5
|
+
#
|
6
|
+
# For example, if you want to know which witness is currently signing blocks,
|
7
|
+
# use the following:
|
8
|
+
#
|
9
|
+
# stream = Rubybear::Stream.new
|
10
|
+
# stream.current_witness do |witness|
|
11
|
+
# puts witness
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# More importantly, full blocks, transactions, and operations can be streamed.
|
15
|
+
class Stream < Api
|
16
|
+
|
17
|
+
# @private
|
18
|
+
INITIAL_TIMEOUT = 0.0200
|
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
|
+
|
26
|
+
# @private
|
27
|
+
MAX_TIMEOUT = 80
|
28
|
+
|
29
|
+
# @private
|
30
|
+
MAX_BLOCKS_PER_NODE = 10000
|
31
|
+
|
32
|
+
RANGE_BEHIND_WARNING = 400
|
33
|
+
|
34
|
+
def initialize(options = {})
|
35
|
+
super
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns the latest operations from the blockchain.
|
39
|
+
#
|
40
|
+
# stream = Rubybear::Stream.new
|
41
|
+
# stream.operations do |op|
|
42
|
+
# puts op.to_json
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# If symbol are passed, then only that operation is returned. Expected
|
46
|
+
# symbols are:
|
47
|
+
#
|
48
|
+
# account_create
|
49
|
+
# account_create_with_delegation
|
50
|
+
# account_update
|
51
|
+
# account_witness_proxy
|
52
|
+
# account_witness_vote
|
53
|
+
# cancel_transfer_from_savings
|
54
|
+
# change_recovery_account
|
55
|
+
# claim_reward_balance
|
56
|
+
# comment
|
57
|
+
# comment_options
|
58
|
+
# convert
|
59
|
+
# custom
|
60
|
+
# custom_json
|
61
|
+
# decline_voting_rights
|
62
|
+
# delegate_coining_shares
|
63
|
+
# delete_comment
|
64
|
+
# escrow_approve
|
65
|
+
# escrow_dispute
|
66
|
+
# escrow_release
|
67
|
+
# escrow_transfer
|
68
|
+
# feed_publish
|
69
|
+
# limit_order_cancel
|
70
|
+
# limit_order_create
|
71
|
+
# limit_order_create2
|
72
|
+
# pow
|
73
|
+
# pow2
|
74
|
+
# recover_account
|
75
|
+
# request_account_recovery
|
76
|
+
# set_withdraw_coining_route
|
77
|
+
# transfer
|
78
|
+
# transfer_from_savings
|
79
|
+
# transfer_to_savings
|
80
|
+
# transfer_to_coining
|
81
|
+
# vote
|
82
|
+
# withdraw_coining
|
83
|
+
# witness_update
|
84
|
+
#
|
85
|
+
# For example, to stream only votes:
|
86
|
+
#
|
87
|
+
# stream = Rubybear::Stream.new
|
88
|
+
# stream.operations(:vote) do |vote|
|
89
|
+
# puts vote.to_json
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
# You can also stream virtual operations:
|
93
|
+
#
|
94
|
+
# stream = Rubybear::Stream.new
|
95
|
+
# stream.operations(:author_reward) do |vop|
|
96
|
+
# puts "#{vop.author} got paid for #{vop.permlink}: #{[vop.bsd_payout, vop.bears_payout, vop.coining_payout]}"
|
97
|
+
# end
|
98
|
+
#
|
99
|
+
# ... or multiple virtual operation types;
|
100
|
+
#
|
101
|
+
# stream = Rubybear::Stream.new
|
102
|
+
# stream.operations([:producer_reward, :author_reward]) do |vop|
|
103
|
+
# puts vop
|
104
|
+
# end
|
105
|
+
#
|
106
|
+
# ... or all types, inluding virtual operation types;
|
107
|
+
#
|
108
|
+
# stream = Rubybear::Stream.new
|
109
|
+
# stream.operations(nil, nil, :head, include_virtual: true) do |vop|
|
110
|
+
# puts vop
|
111
|
+
# end
|
112
|
+
#
|
113
|
+
# Expected virtual operation types:
|
114
|
+
#
|
115
|
+
# producer_reward
|
116
|
+
# author_reward
|
117
|
+
# curation_reward
|
118
|
+
# fill_convert_request
|
119
|
+
# fill_order
|
120
|
+
# fill_coining_withdraw
|
121
|
+
# interest
|
122
|
+
# shutdown_witness
|
123
|
+
#
|
124
|
+
# @param type [symbol || ::Array<symbol>] the type(s) of operation, optional.
|
125
|
+
# @param start starting block
|
126
|
+
# @param mode we have the choice between
|
127
|
+
# * :head the last block
|
128
|
+
# * :irreversible the block that is confirmed by 2/3 of all block producers and is thus irreversible!
|
129
|
+
# @param block the block to execute for each result, optional. Yields: |op, trx_id, block_num, api|
|
130
|
+
# @param options [::Hash] additional options
|
131
|
+
# @option options [Boollean] :include_virtual Also stream virtual options. Setting this true will impact performance. Default: false.
|
132
|
+
# @return [::Hash]
|
133
|
+
def operations(type = nil, start = nil, mode = :irreversible, options = {include_virtual: false}, &block)
|
134
|
+
type = [type].flatten.compact.map(&:to_sym)
|
135
|
+
include_virtual = !!options[:include_virtual]
|
136
|
+
|
137
|
+
if virtual_op_type?(type)
|
138
|
+
include_virtual = true
|
139
|
+
end
|
140
|
+
|
141
|
+
latest_block_number = -1
|
142
|
+
|
143
|
+
transactions(start, mode) do |transaction, trx_id, block_number|
|
144
|
+
virtual_ops_collected = latest_block_number == block_number
|
145
|
+
latest_block_number = block_number
|
146
|
+
|
147
|
+
ops = transaction.operations.map do |t, op|
|
148
|
+
t = t.to_sym
|
149
|
+
if type.size == 1 && type.first == t
|
150
|
+
op
|
151
|
+
elsif type.none? || type.include?(t)
|
152
|
+
{t => op}
|
153
|
+
end
|
154
|
+
end.compact
|
155
|
+
|
156
|
+
if include_virtual && !virtual_ops_collected
|
157
|
+
catch :pop_vops do; begin
|
158
|
+
api.get_ops_in_block(block_number, true) do |vops, error|
|
159
|
+
if !!error
|
160
|
+
standby "Node responded with: #{error.message || 'unknown error'}, retrying ...", {
|
161
|
+
error: error,
|
162
|
+
and: {throw: :pop_vops}
|
163
|
+
}
|
164
|
+
end
|
165
|
+
|
166
|
+
vops.each do |vtx|
|
167
|
+
next unless defined? vtx.op
|
168
|
+
|
169
|
+
t = vtx.op.first.to_sym
|
170
|
+
op = vtx.op.last
|
171
|
+
if type.size == 1 && type.first == t
|
172
|
+
ops << op
|
173
|
+
elsif type.none? || type.include?(t)
|
174
|
+
ops << {t => op}
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end; end
|
179
|
+
|
180
|
+
virtual_ops_collected = true
|
181
|
+
end
|
182
|
+
|
183
|
+
next if ops.none?
|
184
|
+
|
185
|
+
return ops unless !!block
|
186
|
+
|
187
|
+
ops.each do |op|
|
188
|
+
yield op, trx_id, block_number, api
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# Returns the latest transactions from the blockchain.
|
194
|
+
#
|
195
|
+
# stream = Rubybear::Stream.new
|
196
|
+
# stream.transactions do |tx, trx_id|
|
197
|
+
# puts "[#{trx_id}] #{tx.to_json}"
|
198
|
+
# end
|
199
|
+
#
|
200
|
+
# @param start starting block
|
201
|
+
# @param mode we have the choice between
|
202
|
+
# * :head the last block
|
203
|
+
# * :irreversible the block that is confirmed by 2/3 of all block producers and is thus irreversible!
|
204
|
+
# @param block the block to execute for each result, optional. Yields: |tx, trx_id, api|
|
205
|
+
# @return [::Hash]
|
206
|
+
def transactions(start = nil, mode = :irreversible, &block)
|
207
|
+
blocks(start, mode) do |b, block_number|
|
208
|
+
next if (_transactions = b.transactions).nil?
|
209
|
+
return _transactions unless !!block
|
210
|
+
|
211
|
+
_transactions.each_with_index do |transaction, index|
|
212
|
+
trx_id = if !!b['transaction_ids']
|
213
|
+
b['transaction_ids'][index]
|
214
|
+
end
|
215
|
+
|
216
|
+
yield transaction, trx_id, block_number, api
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
# Returns the latest blocks from the blockchain.
|
222
|
+
#
|
223
|
+
# stream = Rubybear::Stream.new
|
224
|
+
# stream.blocks do |bk, num|
|
225
|
+
# puts "[#{num}] #{bk.to_json}"
|
226
|
+
# end
|
227
|
+
#
|
228
|
+
# For convenience and memory management, the api used to poll the current
|
229
|
+
# block data is also available inside the block, e.g.:
|
230
|
+
#
|
231
|
+
# stream = Rubybear::Stream.new
|
232
|
+
# stream.blocks do |bk, num, api|
|
233
|
+
# puts "[#{num}] #{bk.to_json}"
|
234
|
+
#
|
235
|
+
# api.get_ops_in_block(num, true) do |vops, error|
|
236
|
+
# puts vops
|
237
|
+
# end
|
238
|
+
# end
|
239
|
+
#
|
240
|
+
# This idiom is useful for very long running scripts.
|
241
|
+
#
|
242
|
+
# @param start starting block
|
243
|
+
# @param mode we have the choice between
|
244
|
+
# * :head the last block
|
245
|
+
# * :irreversible the block that is confirmed by 2/3 of all block producers and is thus irreversible!
|
246
|
+
# @param max_blocks_per_node the number of blocks to read before trying a new node
|
247
|
+
# @param block the block to execute for each result, optional. Yields: |bk, num, api|
|
248
|
+
# @return [::Hash]
|
249
|
+
def blocks(start = nil, mode = :irreversible, max_blocks_per_node = MAX_BLOCKS_PER_NODE, &block)
|
250
|
+
reset_api
|
251
|
+
|
252
|
+
replay = !!start
|
253
|
+
counter = 0
|
254
|
+
latest_block_number = -1
|
255
|
+
@api_options[:max_requests] = [max_blocks_per_node * 2, @api_options[:max_requests].to_i].max
|
256
|
+
|
257
|
+
loop do
|
258
|
+
break if stop?
|
259
|
+
|
260
|
+
catch :sequence do; begin
|
261
|
+
head_block = api.get_dynamic_global_properties do |properties, error|
|
262
|
+
if !!error
|
263
|
+
standby "Node responded with: #{error.message || 'unknown error'}, retrying ...", {
|
264
|
+
error: error,
|
265
|
+
and: {throw: :sequence}
|
266
|
+
}
|
267
|
+
end
|
268
|
+
|
269
|
+
break if stop?
|
270
|
+
|
271
|
+
if properties.head_block_number.nil?
|
272
|
+
# This can happen if a reverse proxy is acting up.
|
273
|
+
standby "Bad block sequence after height: #{latest_block_number}", {
|
274
|
+
and: {throw: :sequence}
|
275
|
+
}
|
276
|
+
end
|
277
|
+
|
278
|
+
case mode.to_sym
|
279
|
+
when :head then properties.head_block_number
|
280
|
+
when :irreversible then properties.last_irreversible_block_num
|
281
|
+
else; raise StreamError, '"mode" has to be "head" or "irreversible"'
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
if head_block == latest_block_number
|
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
|
297
|
+
elsif head_block < latest_block_number
|
298
|
+
# This can happen if a reverse proxy is acting up.
|
299
|
+
standby "Invalid block sequence at height: #{head_block}", {
|
300
|
+
and: {backoff: api, throw: :sequence}
|
301
|
+
}
|
302
|
+
end
|
303
|
+
|
304
|
+
reset_timeout
|
305
|
+
start ||= head_block
|
306
|
+
range = (start..head_block)
|
307
|
+
|
308
|
+
for n in range
|
309
|
+
break if stop?
|
310
|
+
|
311
|
+
if (counter += 1) > max_blocks_per_node
|
312
|
+
reset_api
|
313
|
+
counter = 0
|
314
|
+
end
|
315
|
+
|
316
|
+
if !replay && range.size > RANGE_BEHIND_WARNING
|
317
|
+
# When the range is above RANGE_BEHIND_WARNING blocks, it's time
|
318
|
+
# to warn, unless we're replaying.
|
319
|
+
|
320
|
+
r = [*range]
|
321
|
+
index = r.index(n)
|
322
|
+
current_range = r[index..-1]
|
323
|
+
|
324
|
+
if current_range.size % RANGE_BEHIND_WARNING == 0
|
325
|
+
warning "Stream behind by #{current_range.size} blocks (about #{(current_range.size * 3) / 60.0} minutes)."
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
scoped_api, block_options = if use_condenser_namespace?
|
330
|
+
[api, n]
|
331
|
+
else
|
332
|
+
[block_api, {block_num: n}]
|
333
|
+
end
|
334
|
+
|
335
|
+
scoped_api.get_block(n) do |current_block, error|
|
336
|
+
if !!error
|
337
|
+
if error.message == 'Unable to acquire database lock'
|
338
|
+
start = n
|
339
|
+
timeout
|
340
|
+
standby "Node was unable to acquire database lock, retrying ...", {
|
341
|
+
and: {throw: :sequence}
|
342
|
+
}
|
343
|
+
else
|
344
|
+
standby "Node responded with: #{error.message || 'unknown error'}, retrying ...", {
|
345
|
+
error: error,
|
346
|
+
and: {throw: :sequence}
|
347
|
+
}
|
348
|
+
end
|
349
|
+
elsif current_block.nil?
|
350
|
+
standby "Node responded with: empty block, retrying ...", {
|
351
|
+
and: {throw: :sequence}
|
352
|
+
}
|
353
|
+
end
|
354
|
+
|
355
|
+
latest_block_number = n
|
356
|
+
return current_block, n if block.nil?
|
357
|
+
yield current_block, n, api
|
358
|
+
end
|
359
|
+
|
360
|
+
start = head_block + 1
|
361
|
+
sleep BLOCK_PRODUCTION / range.size
|
362
|
+
end
|
363
|
+
rescue StreamError; raise
|
364
|
+
# rescue => e
|
365
|
+
# warning "Unknown streaming error: #{e.inspect}, retrying ... "
|
366
|
+
# warning e
|
367
|
+
# redo
|
368
|
+
end; end
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
# Stops the persistant http connections.
|
373
|
+
#
|
374
|
+
def shutdown
|
375
|
+
flappy = false
|
376
|
+
|
377
|
+
begin
|
378
|
+
unless @api.nil?
|
379
|
+
flappy = @api.send(:flappy?)
|
380
|
+
@api.shutdown
|
381
|
+
end
|
382
|
+
|
383
|
+
unless @block_api.nil?
|
384
|
+
flappy = @block_api.send(:flappy?) unless flappy
|
385
|
+
@block_api.shutdown
|
386
|
+
end
|
387
|
+
rescue => e
|
388
|
+
warning("Unable to shut down: #{e}")
|
389
|
+
end
|
390
|
+
|
391
|
+
@api = nil
|
392
|
+
@block_api = nil
|
393
|
+
GC.start
|
394
|
+
end
|
395
|
+
|
396
|
+
# @private
|
397
|
+
def method_names
|
398
|
+
@method_names ||= [
|
399
|
+
:head_block_number,
|
400
|
+
:head_block_id,
|
401
|
+
:time,
|
402
|
+
:current_witness,
|
403
|
+
:total_pow,
|
404
|
+
:num_pow_witnesses,
|
405
|
+
:virtual_supply,
|
406
|
+
:current_supply,
|
407
|
+
:confidential_supply,
|
408
|
+
:current_bsd_supply,
|
409
|
+
:confidential_bsd_supply,
|
410
|
+
:total_coining_fund_bears,
|
411
|
+
:total_coining_shares,
|
412
|
+
:total_reward_fund_bears,
|
413
|
+
:total_reward_shares2,
|
414
|
+
:total_activity_fund_bears,
|
415
|
+
:total_activity_fund_shares,
|
416
|
+
:bsd_interest_rate,
|
417
|
+
:average_block_size,
|
418
|
+
:maximum_block_size,
|
419
|
+
:current_aslot,
|
420
|
+
:recent_slots_filled,
|
421
|
+
:participation_count,
|
422
|
+
:last_irreversible_block_num,
|
423
|
+
:max_virtual_bandwidth,
|
424
|
+
:current_reserve_ratio,
|
425
|
+
:block_numbers,
|
426
|
+
:blocks
|
427
|
+
].freeze
|
428
|
+
end
|
429
|
+
|
430
|
+
# @private
|
431
|
+
def method_params(method)
|
432
|
+
case method
|
433
|
+
when :block_numbers then {head_block_number: nil}
|
434
|
+
when :blocks then {get_block: :head_block_number}
|
435
|
+
else; nil
|
436
|
+
end
|
437
|
+
end
|
438
|
+
private
|
439
|
+
def method_missing(m, *args, &block)
|
440
|
+
super unless respond_to_missing?(m)
|
441
|
+
|
442
|
+
@latest_values ||= []
|
443
|
+
@latest_values.shift(5) if @latest_values.size > 20
|
444
|
+
loop do
|
445
|
+
break if stop?
|
446
|
+
|
447
|
+
value = if (n = method_params(m)).nil?
|
448
|
+
key_value = api.get_dynamic_global_properties.result[m]
|
449
|
+
else
|
450
|
+
key = n.keys.first
|
451
|
+
if !!n[key]
|
452
|
+
r = api.get_dynamic_global_properties.result
|
453
|
+
key_value = param = r[n[key]]
|
454
|
+
result = nil
|
455
|
+
loop do
|
456
|
+
break if stop?
|
457
|
+
|
458
|
+
response = api.send(key, param)
|
459
|
+
raise StreamError, JSON[response.error] if !!response.error
|
460
|
+
result = response.result
|
461
|
+
break if !!result
|
462
|
+
warning "#{key}: #{param} result missing, retrying with timeout: #{current_timeout} seconds"
|
463
|
+
reset_api
|
464
|
+
timeout
|
465
|
+
end
|
466
|
+
reset_timeout
|
467
|
+
result
|
468
|
+
else
|
469
|
+
key_value = api.get_dynamic_global_properties.result[key]
|
470
|
+
end
|
471
|
+
end
|
472
|
+
unless @latest_values.include? key_value
|
473
|
+
@latest_values << key_value
|
474
|
+
if !!block
|
475
|
+
yield value
|
476
|
+
else
|
477
|
+
return value
|
478
|
+
end
|
479
|
+
end
|
480
|
+
sleep current_timeout
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
def reset_api
|
485
|
+
shutdown
|
486
|
+
!!api && !!block_api
|
487
|
+
end
|
488
|
+
|
489
|
+
def timeout
|
490
|
+
@timeout ||= INITIAL_TIMEOUT
|
491
|
+
@timeout *= 2
|
492
|
+
reset_timeout if @timeout > MAX_TIMEOUT
|
493
|
+
sleep @timeout || INITIAL_TIMEOUT
|
494
|
+
@timeout
|
495
|
+
end
|
496
|
+
|
497
|
+
def current_timeout
|
498
|
+
@timeout || INITIAL_TIMEOUT
|
499
|
+
end
|
500
|
+
|
501
|
+
def reset_timeout
|
502
|
+
@timeout = nil
|
503
|
+
end
|
504
|
+
|
505
|
+
def virtual_op_type?(type)
|
506
|
+
type = [type].flatten.compact.map(&:to_sym)
|
507
|
+
|
508
|
+
(Rubybear::OperationTypes::TYPES.keys && type).any?
|
509
|
+
end
|
510
|
+
|
511
|
+
def stop?
|
512
|
+
@api.nil? || @block_api.nil?
|
513
|
+
end
|
514
|
+
|
515
|
+
def standby(message, options = {})
|
516
|
+
error = options[:error]
|
517
|
+
secondary = options[:and] || {}
|
518
|
+
backoff_api = secondary[:backoff]
|
519
|
+
throwable = secondary[:throw]
|
520
|
+
|
521
|
+
warning message
|
522
|
+
warning error if !!error
|
523
|
+
backoff_api.send :backoff if !!backoff_api
|
524
|
+
throw throwable if !!throwable
|
525
|
+
end
|
526
|
+
end
|
527
|
+
end
|