steem-ruby 0.9.0 → 0.9.5
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 +5 -5
- data/.gitignore +1 -1
- data/README.md +88 -15
- data/Rakefile +128 -31
- data/lib/steem.rb +49 -0
- data/lib/steem/api.rb +39 -37
- data/lib/steem/base_error.rb +80 -41
- data/lib/steem/block_api.rb +23 -3
- data/lib/steem/broadcast.rb +465 -29
- data/lib/steem/chain_config.rb +3 -3
- data/lib/steem/marshal.rb +231 -0
- data/lib/steem/mixins/jsonable.rb +37 -0
- data/lib/steem/mixins/retriable.rb +30 -24
- data/lib/steem/mixins/serializable.rb +45 -0
- data/lib/steem/operation.rb +141 -0
- data/lib/steem/operation/account_create.rb +10 -0
- data/lib/steem/operation/account_create_with_delegation.rb +12 -0
- data/lib/steem/operation/account_update.rb +8 -0
- data/lib/steem/operation/account_witness_proxy.rb +4 -0
- data/lib/steem/operation/account_witness_vote.rb +5 -0
- data/lib/steem/operation/cancel_transfer_from_savings.rb +4 -0
- data/lib/steem/operation/challenge_authority.rb +5 -0
- data/lib/steem/operation/change_recovery_account.rb +5 -0
- data/lib/steem/operation/claim_account.rb +5 -0
- data/lib/steem/operation/claim_reward_balance.rb +6 -0
- data/lib/steem/operation/comment.rb +9 -0
- data/lib/steem/operation/comment_options.rb +10 -0
- data/lib/steem/operation/convert.rb +5 -0
- data/lib/steem/operation/create_claimed_account.rb +10 -0
- data/lib/steem/operation/custom.rb +5 -0
- data/lib/steem/operation/custom_binary.rb +8 -0
- data/lib/steem/operation/custom_json.rb +6 -0
- data/lib/steem/operation/decline_voting_rights.rb +4 -0
- data/lib/steem/operation/delegate_vesting_shares.rb +5 -0
- data/lib/steem/operation/delete_comment.rb +4 -0
- data/lib/steem/operation/escrow_approve.rb +8 -0
- data/lib/steem/operation/escrow_dispute.rb +7 -0
- data/lib/steem/operation/escrow_release.rb +10 -0
- data/lib/steem/operation/escrow_transfer.rb +12 -0
- data/lib/steem/operation/feed_publish.rb +4 -0
- data/lib/steem/operation/limit_order_cancel.rb +4 -0
- data/lib/steem/operation/limit_order_create.rb +8 -0
- data/lib/steem/operation/limit_order_create2.rb +8 -0
- data/lib/steem/operation/prove_authority.rb +4 -0
- data/lib/steem/operation/recover_account.rb +6 -0
- data/lib/steem/operation/report_over_production.rb +5 -0
- data/lib/steem/operation/request_account_recovery.rb +6 -0
- data/lib/steem/operation/reset_account.rb +5 -0
- data/lib/steem/operation/set_reset_account.rb +5 -0
- data/lib/steem/operation/set_withdraw_vesting_route.rb +6 -0
- data/lib/steem/operation/transfer.rb +6 -0
- data/lib/steem/operation/transfer_from_savings.rb +7 -0
- data/lib/steem/operation/transfer_to_savings.rb +6 -0
- data/lib/steem/operation/transfer_to_vesting.rb +5 -0
- data/lib/steem/operation/vote.rb +6 -0
- data/lib/steem/operation/withdraw_vesting.rb +4 -0
- data/lib/steem/operation/witness_set_properties.rb +5 -0
- data/lib/steem/operation/witness_update.rb +7 -0
- data/lib/steem/rpc/base_client.rb +16 -4
- data/lib/steem/rpc/http_client.rb +18 -2
- data/lib/steem/stream.rb +385 -0
- data/lib/steem/transaction.rb +96 -0
- data/lib/steem/transaction_builder.rb +176 -103
- data/lib/steem/type/amount.rb +61 -9
- data/lib/steem/version.rb +1 -1
- data/steem-ruby.gemspec +9 -4
- metadata +203 -56
- data/Gemfile.lock +0 -73
@@ -0,0 +1,10 @@
|
|
1
|
+
class Steem::Operation::AccountCreate < Steem::Operation
|
2
|
+
def_attr fee: :amount
|
3
|
+
def_attr creator: :string
|
4
|
+
def_attr new_account_name: :string
|
5
|
+
def_attr owner: :authority
|
6
|
+
def_attr active: :authority
|
7
|
+
def_attr posting: :authority
|
8
|
+
def_attr memo_key: :public_key
|
9
|
+
def_attr json_metadata: :string
|
10
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class Steem::Operation::AccountCreateWithDelegation < Steem::Operation
|
2
|
+
def_attr fee: :amount
|
3
|
+
def_attr delegation: :amount
|
4
|
+
def_attr creator: :string
|
5
|
+
def_attr new_account_name: :string
|
6
|
+
def_attr owner: :authority
|
7
|
+
def_attr active: :authority
|
8
|
+
def_attr posting: :authority
|
9
|
+
def_attr memo_key: :public_key
|
10
|
+
def_attr json_metadata: :string
|
11
|
+
def_attr extensions: :empty_array
|
12
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
class Steem::Operation::AccountUpdate < Steem::Operation
|
2
|
+
def_attr account: :string
|
3
|
+
def_attr owner: :optional_authority
|
4
|
+
def_attr active: :optional_authority
|
5
|
+
def_attr posting: :optional_authority
|
6
|
+
def_attr memo_key: :public_key
|
7
|
+
def_attr json_metadata: :string
|
8
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
class Steem::Operation::Comment < Steem::Operation
|
2
|
+
def_attr parent_author: :string
|
3
|
+
def_attr parent_permlink: :string
|
4
|
+
def_attr author: :string
|
5
|
+
def_attr permlink: :string
|
6
|
+
def_attr title: :string
|
7
|
+
def_attr body: :string
|
8
|
+
def_attr json_metadata: :string
|
9
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class Steem::Operation::CommentOptions < Steem::Operation
|
2
|
+
def_attr author: :string
|
3
|
+
def_attr permlink: :string
|
4
|
+
def_attr max_accepted_payout: :amount
|
5
|
+
def_attr percent_steem_dollars: :uint32
|
6
|
+
# def_attr allow_replies: :boolean
|
7
|
+
def_attr allow_votes: :boolean
|
8
|
+
def_attr allow_curation_rewards: :boolean
|
9
|
+
def_attr extensions: :comment_options_extensions
|
10
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class Steem::Operation::CreateClaimedAccount < Steem::Operation
|
2
|
+
def_attr creator: :string
|
3
|
+
def_attr new_account_name: :string
|
4
|
+
def_attr owner: :authority
|
5
|
+
def_attr active: :authority
|
6
|
+
def_attr posting: :authority
|
7
|
+
def_attr memo_key: :public_key
|
8
|
+
def_attr json_metadata: :string
|
9
|
+
def_attr extensions: :empty_array
|
10
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
class Steem::Operation::CustomBinary < Steem::Operation
|
2
|
+
def_attr required_owner_auths: :required_auths
|
3
|
+
def_attr required_active_auths: :required_auths
|
4
|
+
def_attr required_posting_auths: :required_auths
|
5
|
+
def_attr required_auths: :required_auths
|
6
|
+
def_attr id: :string
|
7
|
+
def_attr data: :raw_bytes
|
8
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class Steem::Operation::EscrowRelease < Steem::Operation
|
2
|
+
def_attr from: :string
|
3
|
+
def_attr to: :string
|
4
|
+
def_attr agent: :string
|
5
|
+
def_attr who: :string
|
6
|
+
def_attr receiver: :string
|
7
|
+
def_attr escrow_id: :uint32
|
8
|
+
def_attr sbd_amount: :amount
|
9
|
+
def_attr steem_amount: :amount
|
10
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class Steem::Operation::EscrowTransfer < Steem::Operation
|
2
|
+
def_attr from: :string
|
3
|
+
def_attr to: :string
|
4
|
+
def_attr sbd_amount: :amount
|
5
|
+
def_attr steem_amount: :amount
|
6
|
+
def_attr escrow_id: :uint32
|
7
|
+
def_attr agent: :string
|
8
|
+
def_attr fee: :amount
|
9
|
+
def_attr json_metadata: :string
|
10
|
+
def_attr ratification_deadline: :point_in_time
|
11
|
+
def_attr escrow_expiration: :point_in_time
|
12
|
+
end
|
@@ -12,8 +12,7 @@ module Steem
|
|
12
12
|
MAX_TIMEOUT_BACKOFF = 30
|
13
13
|
|
14
14
|
# @private
|
15
|
-
TIMEOUT_ERRORS = [Net::ReadTimeout, Errno::EBADF,
|
16
|
-
IOError]
|
15
|
+
TIMEOUT_ERRORS = [Net::ReadTimeout, Errno::EBADF, IOError]
|
17
16
|
|
18
17
|
def initialize(options = {})
|
19
18
|
@chain = options[:chain] || :steem
|
@@ -118,7 +117,7 @@ module Steem
|
|
118
117
|
error = response['error'].to_json if !!response['error']
|
119
118
|
|
120
119
|
if req_id != res_id
|
121
|
-
raise IncorrectResponseIdError, "#{method}: The json-rpc id did not match. Request was: #{req_id}, got: #{res_id.inspect}",
|
120
|
+
raise IncorrectResponseIdError, "#{method}: The json-rpc id did not match. Request was: #{req_id}, got: #{res_id.inspect}", BaseError.send(:build_backtrace, error)
|
122
121
|
end
|
123
122
|
end
|
124
123
|
|
@@ -143,7 +142,7 @@ module Steem
|
|
143
142
|
raise TooManyTimeoutsError.new("Too many timeouts for: #{context}", cause)
|
144
143
|
elsif @timeout_retry_count % 10 == 0
|
145
144
|
msg = "#{@timeout_retry_count} retry attempts for: #{context}"
|
146
|
-
msg += "; cause: #{
|
145
|
+
msg += "; cause: #{cause}" if !!cause
|
147
146
|
error_pipe.puts msg
|
148
147
|
end
|
149
148
|
|
@@ -162,6 +161,19 @@ module Steem
|
|
162
161
|
|
163
162
|
sleep @backoff
|
164
163
|
end
|
164
|
+
|
165
|
+
# @private
|
166
|
+
def raise_error_response(rpc_method_name, rpc_args, response)
|
167
|
+
raise UnknownError, "#{rpc_method_name}: #{response}" if response.error.nil?
|
168
|
+
|
169
|
+
error = response.error
|
170
|
+
|
171
|
+
if error.message == 'Invalid Request'
|
172
|
+
raise Steem::ArgumentError, "Unexpected arguments: #{rpc_args.inspect}. Expected: #{rpc_method_name} (#{args_keys_to_s(rpc_method_name)})"
|
173
|
+
end
|
174
|
+
|
175
|
+
BaseError.build_error(error, rpc_method_name)
|
176
|
+
end
|
165
177
|
end
|
166
178
|
end
|
167
179
|
end
|
@@ -17,7 +17,7 @@ module Steem
|
|
17
17
|
#
|
18
18
|
# @private
|
19
19
|
TIMEOUT_ERRORS = [Net::OpenTimeout, JSON::ParserError, Net::ReadTimeout,
|
20
|
-
Errno::EBADF, Errno::
|
20
|
+
Errno::EBADF, IOError, Errno::ENETDOWN, Steem::RemoteDatabaseLockError]
|
21
21
|
|
22
22
|
# @private
|
23
23
|
POST_HEADERS = {
|
@@ -30,7 +30,7 @@ module Steem
|
|
30
30
|
def http
|
31
31
|
@http ||= Net::HTTP.new(uri.host, uri.port).tap do |http|
|
32
32
|
http.use_ssl = true if uri.to_s =~ /^https/i
|
33
|
-
http.keep_alive_timeout =
|
33
|
+
http.keep_alive_timeout = 150 # seconds
|
34
34
|
|
35
35
|
# WARNING This method opens a serious security hole. Never use this
|
36
36
|
# method in production code.
|
@@ -108,6 +108,22 @@ module Steem
|
|
108
108
|
else; response
|
109
109
|
end
|
110
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 Steem::ArgumentError, r.error.inspect
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
111
127
|
yield_response response, &block
|
112
128
|
when '504' # Gateway Timeout
|
113
129
|
throw retry_timeout(:tota_cera_pila, response.body)
|
data/lib/steem/stream.rb
ADDED
@@ -0,0 +1,385 @@
|
|
1
|
+
module Steem
|
2
|
+
# Steem::Stream allows a live view of the STEEM blockchain.
|
3
|
+
#
|
4
|
+
# Example streaming blocks:
|
5
|
+
#
|
6
|
+
# stream = Steem::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 = Steem::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 = Steem::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 [Steem::DatabaseApi] :database_api
|
41
|
+
# @option options [Steem::BlockApi] :block_api
|
42
|
+
# @option options [Steem::AccountHistoryApi || Steem::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] || Steem::DatabaseApi.new(options)
|
50
|
+
@block_api = options[:block_api] || Steem::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 {Steem::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 = Steem::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 = Steem::Stream.new
|
160
|
+
# stream.operations(types: :vote_operation) do |vote|
|
161
|
+
# puts vote.to_json
|
162
|
+
# end
|
163
|
+
#
|
164
|
+
# ... Or ...
|
165
|
+
#
|
166
|
+
# stream = Steem::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 = Steem::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 = Steem::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 = Steem::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
|
+
Steem::AccountHistoryApi.new(@instance_options)
|
249
|
+
rescue Steem::UnknownApiError => e
|
250
|
+
warn "#{e.inspect}, falling back to Steem::CondenserApi." unless @no_warn
|
251
|
+
Steem::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 Steem::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 Steem::CondenserApi
|
334
|
+
[block_num, true]
|
335
|
+
when Steem::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 Steem::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 Steem::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
|