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.
Files changed (68) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -1
  3. data/README.md +88 -15
  4. data/Rakefile +128 -31
  5. data/lib/steem.rb +49 -0
  6. data/lib/steem/api.rb +39 -37
  7. data/lib/steem/base_error.rb +80 -41
  8. data/lib/steem/block_api.rb +23 -3
  9. data/lib/steem/broadcast.rb +465 -29
  10. data/lib/steem/chain_config.rb +3 -3
  11. data/lib/steem/marshal.rb +231 -0
  12. data/lib/steem/mixins/jsonable.rb +37 -0
  13. data/lib/steem/mixins/retriable.rb +30 -24
  14. data/lib/steem/mixins/serializable.rb +45 -0
  15. data/lib/steem/operation.rb +141 -0
  16. data/lib/steem/operation/account_create.rb +10 -0
  17. data/lib/steem/operation/account_create_with_delegation.rb +12 -0
  18. data/lib/steem/operation/account_update.rb +8 -0
  19. data/lib/steem/operation/account_witness_proxy.rb +4 -0
  20. data/lib/steem/operation/account_witness_vote.rb +5 -0
  21. data/lib/steem/operation/cancel_transfer_from_savings.rb +4 -0
  22. data/lib/steem/operation/challenge_authority.rb +5 -0
  23. data/lib/steem/operation/change_recovery_account.rb +5 -0
  24. data/lib/steem/operation/claim_account.rb +5 -0
  25. data/lib/steem/operation/claim_reward_balance.rb +6 -0
  26. data/lib/steem/operation/comment.rb +9 -0
  27. data/lib/steem/operation/comment_options.rb +10 -0
  28. data/lib/steem/operation/convert.rb +5 -0
  29. data/lib/steem/operation/create_claimed_account.rb +10 -0
  30. data/lib/steem/operation/custom.rb +5 -0
  31. data/lib/steem/operation/custom_binary.rb +8 -0
  32. data/lib/steem/operation/custom_json.rb +6 -0
  33. data/lib/steem/operation/decline_voting_rights.rb +4 -0
  34. data/lib/steem/operation/delegate_vesting_shares.rb +5 -0
  35. data/lib/steem/operation/delete_comment.rb +4 -0
  36. data/lib/steem/operation/escrow_approve.rb +8 -0
  37. data/lib/steem/operation/escrow_dispute.rb +7 -0
  38. data/lib/steem/operation/escrow_release.rb +10 -0
  39. data/lib/steem/operation/escrow_transfer.rb +12 -0
  40. data/lib/steem/operation/feed_publish.rb +4 -0
  41. data/lib/steem/operation/limit_order_cancel.rb +4 -0
  42. data/lib/steem/operation/limit_order_create.rb +8 -0
  43. data/lib/steem/operation/limit_order_create2.rb +8 -0
  44. data/lib/steem/operation/prove_authority.rb +4 -0
  45. data/lib/steem/operation/recover_account.rb +6 -0
  46. data/lib/steem/operation/report_over_production.rb +5 -0
  47. data/lib/steem/operation/request_account_recovery.rb +6 -0
  48. data/lib/steem/operation/reset_account.rb +5 -0
  49. data/lib/steem/operation/set_reset_account.rb +5 -0
  50. data/lib/steem/operation/set_withdraw_vesting_route.rb +6 -0
  51. data/lib/steem/operation/transfer.rb +6 -0
  52. data/lib/steem/operation/transfer_from_savings.rb +7 -0
  53. data/lib/steem/operation/transfer_to_savings.rb +6 -0
  54. data/lib/steem/operation/transfer_to_vesting.rb +5 -0
  55. data/lib/steem/operation/vote.rb +6 -0
  56. data/lib/steem/operation/withdraw_vesting.rb +4 -0
  57. data/lib/steem/operation/witness_set_properties.rb +5 -0
  58. data/lib/steem/operation/witness_update.rb +7 -0
  59. data/lib/steem/rpc/base_client.rb +16 -4
  60. data/lib/steem/rpc/http_client.rb +18 -2
  61. data/lib/steem/stream.rb +385 -0
  62. data/lib/steem/transaction.rb +96 -0
  63. data/lib/steem/transaction_builder.rb +176 -103
  64. data/lib/steem/type/amount.rb +61 -9
  65. data/lib/steem/version.rb +1 -1
  66. data/steem-ruby.gemspec +9 -4
  67. metadata +203 -56
  68. 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,4 @@
1
+ class Steem::Operation::AccountWitnessProxy < Steem::Operation
2
+ def_attr account: :string
3
+ def_attr proxy: :string
4
+ end
@@ -0,0 +1,5 @@
1
+ class Steem::Operation::AccountWitnessVote < Steem::Operation
2
+ def_attr account: :string
3
+ def_attr witness: :string
4
+ def_attr approve: :boolean
5
+ end
@@ -0,0 +1,4 @@
1
+ class Steem::Operation::CancelTransferFromSavings < Steem::Operation
2
+ def_attr from: :string
3
+ def_attr request_id: :uint32
4
+ end
@@ -0,0 +1,5 @@
1
+ class Steem::Operation::ChallengeAuthority < Steem::Operation
2
+ def_attr challenger: :string
3
+ def_attr challenged: :string
4
+ def_attr require_owner: :boolean
5
+ end
@@ -0,0 +1,5 @@
1
+ class Steem::Operation::ChangeRecoveryAccount < Steem::Operation
2
+ def_attr account_to_recover: :string
3
+ def_attr new_recovery_account: :string
4
+ def_attr extensions: :empty_array
5
+ end
@@ -0,0 +1,5 @@
1
+ class Steem::Operation::ClaimAccount < Steem::Operation
2
+ def_attr creator: :string
3
+ def_attr fee: :amount
4
+ def_attr extensions: :empty_array
5
+ end
@@ -0,0 +1,6 @@
1
+ class Steem::Operation::ClaimRewardBalance < Steem::Operation
2
+ def_attr account: :string
3
+ def_attr reward_steem: :amount
4
+ def_attr reward_sbd: :amount
5
+ def_attr reward_vests: :amount
6
+ 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,5 @@
1
+ class Steem::Operation::Convert < Steem::Operation
2
+ def_attr owner: :string
3
+ def_attr requestid: :uint32
4
+ def_attr amount: :amount
5
+ 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,5 @@
1
+ class Steem::Operation::Custom < Steem::Operation
2
+ def_attr required_auths: :required_auths
3
+ def_attr id: :uint32
4
+ def_attr data: :raw_bytes
5
+ 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,6 @@
1
+ class Steem::Operation::CustomJson < Steem::Operation
2
+ def_attr required_auths: :required_auths
3
+ def_attr required_posting_auths: :required_auths
4
+ def_attr id: :string
5
+ def_attr json: :string
6
+ end
@@ -0,0 +1,4 @@
1
+ class Steem::Operation::DeclineVotingRights < Steem::Operation
2
+ def_attr account: :string
3
+ def_attr decline: :boolean
4
+ end
@@ -0,0 +1,5 @@
1
+ class Steem::Operation::DelegateVestingShares < Steem::Operation
2
+ def_attr delegator: :string
3
+ def_attr delegatee: :string
4
+ def_attr vesting_shares: :amount
5
+ end
@@ -0,0 +1,4 @@
1
+ class Steem::Operation::DeleteComment < Steem::Operation
2
+ def_attr author: :string
3
+ def_attr permlink: :string
4
+ end
@@ -0,0 +1,8 @@
1
+ class Steem::Operation::EscrowApprove < 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 escrow_id: :uint32
7
+ def_attr approve: :boolean
8
+ end
@@ -0,0 +1,7 @@
1
+ class Steem::Operation::EscrowDispute < 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 escrow_id: :uint32
7
+ 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
@@ -0,0 +1,4 @@
1
+ class Steem::Operation::FeedPublish < Steem::Operation
2
+ def_attr publisher: :string
3
+ def_attr exchange_rate: :price
4
+ end
@@ -0,0 +1,4 @@
1
+ class Steem::Operation::LimitOrderCancel < Steem::Operation
2
+ def_attr owner: :string
3
+ def_attr orderid: :uint32
4
+ end
@@ -0,0 +1,8 @@
1
+ class Steem::Operation::LimitOrderCreate < Steem::Operation
2
+ def_attr owner: :string
3
+ def_attr orderid: :uint32
4
+ def_attr amount_to_sell: :amount
5
+ def_attr min_to_receive: :amount
6
+ def_attr fill_or_kill: :boolean
7
+ def_attr expiration: :point_in_time
8
+ end
@@ -0,0 +1,8 @@
1
+ class Steem::Operation::LimitOrderCreate2 < Steem::Operation
2
+ def_attr owner: :string
3
+ def_attr orderid: :uint32
4
+ def_attr amount_to_sell: :amount
5
+ def_attr fill_or_kill: :boolean
6
+ def_attr exchange_rate: :price
7
+ def_attr expiration: :point_in_time
8
+ end
@@ -0,0 +1,4 @@
1
+ class Steem::Operation::ProveAuthority < Steem::Operation
2
+ def_attr challenged: :string
3
+ def_attr require_owner: :boolean
4
+ end
@@ -0,0 +1,6 @@
1
+ class Steem::Operation::RecoverAccount < Steem::Operation
2
+ def_attr account_to_recover: :string
3
+ def_attr new_owner_authority: :authority
4
+ def_attr recent_owner_authority: :authority
5
+ def_attr extensions: :empty_array
6
+ end
@@ -0,0 +1,5 @@
1
+ class Steem::Operation::ReportOverProduction < Steem::Operation
2
+ def_attr reporter: :string
3
+ def_attr first_block: :string # FIXME signed_block_header
4
+ def_attr second_block: :string # FIXME signed_block_header
5
+ end
@@ -0,0 +1,6 @@
1
+ class Steem::Operation::RequestAccountRecovery < Steem::Operation
2
+ def_attr recovery_account: :string
3
+ def_attr account_to_recover: :string
4
+ def_attr new_owner_authority: :authority
5
+ def_attr extensions: :empty_array
6
+ end
@@ -0,0 +1,5 @@
1
+ class Steem::Operation::ResetAccount < Steem::Operation
2
+ def_attr reset_account: :string
3
+ def_attr account_to_reset: :string
4
+ def_attr new_owner_authority: :authority
5
+ end
@@ -0,0 +1,5 @@
1
+ class Steem::Operation::SetResetAccount < Steem::Operation
2
+ def_attr account: :string
3
+ def_attr current_reset_account: :string
4
+ def_attr reset_account: :string
5
+ end
@@ -0,0 +1,6 @@
1
+ class Steem::Operation::SetWithdrawVestingRoute < Steem::Operation
2
+ def_attr from: :string
3
+ def_attr to: :string
4
+ def_attr percent: :uint16
5
+ def_attr auto_vest: :boolean
6
+ end
@@ -0,0 +1,6 @@
1
+ class Steem::Operation::Transfer < Steem::Operation
2
+ def_attr from: :string
3
+ def_attr to: :string
4
+ def_attr amount: :amount
5
+ def_attr memo: :string
6
+ end
@@ -0,0 +1,7 @@
1
+ class Steem::Operation::TransferFromSavings < Steem::Operation
2
+ def_attr from: :string
3
+ def_attr request_id: :uint32
4
+ def_attr to: :string
5
+ def_attr amount: :amount
6
+ def_attr memo: :string
7
+ end
@@ -0,0 +1,6 @@
1
+ class Steem::Operation::TransferToSavings < Steem::Operation
2
+ def_attr from: :string
3
+ def_attr to: :string
4
+ def_attr amount: :amount
5
+ def_attr memo: :string
6
+ end
@@ -0,0 +1,5 @@
1
+ class Steem::Operation::TransferToVesting < Steem::Operation
2
+ def_attr from: :string
3
+ def_attr to: :string
4
+ def_attr amount: :amount
5
+ end
@@ -0,0 +1,6 @@
1
+ class Steem::Operation::Vote < Steem::Operation
2
+ def_attr voter: :string
3
+ def_attr author: :string
4
+ def_attr permlink: :string
5
+ def_attr weight: :int16
6
+ end
@@ -0,0 +1,4 @@
1
+ class Steem::Operation::WithdrawVesting < Steem::Operation
2
+ def_attr account: :string
3
+ def_attr vesting_shares: :amount
4
+ end
@@ -0,0 +1,5 @@
1
+ class Steem::Operation::WitnessSetProperties < Steem::Operation
2
+ def_attr owner: :string
3
+ def_attr props: :witness_properties
4
+ def_attr extensions: :empty_array
5
+ end
@@ -0,0 +1,7 @@
1
+ class Steem::Operation::WitnessUpdate < Steem::Operation
2
+ def_attr owner: :string
3
+ def_attr url: :string
4
+ def_attr block_signing_key: :public_key
5
+ def_attr props: :chain_properties
6
+ def_attr fee: :amount
7
+ 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, Errno::ECONNREFUSED,
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}", error.nil? ? nil : error.to_json
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: #{e}" if !!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::ECONNREFUSED, IOError]
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 = 2 # seconds
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)
@@ -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