crea-ruby 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +55 -0
- data/CONTRIBUTING.md +79 -0
- data/Gemfile +3 -0
- data/LICENSE +22 -0
- data/README.md +234 -0
- data/Rakefile +332 -0
- data/crea-ruby.gemspec +39 -0
- data/gource.sh +6 -0
- data/lib/crea.rb +85 -0
- data/lib/crea/api.rb +208 -0
- data/lib/crea/base_error.rb +218 -0
- data/lib/crea/block_api.rb +78 -0
- data/lib/crea/broadcast.rb +1334 -0
- data/lib/crea/chain_config.rb +36 -0
- data/lib/crea/formatter.rb +14 -0
- data/lib/crea/jsonrpc.rb +108 -0
- data/lib/crea/marshal.rb +231 -0
- data/lib/crea/mixins/jsonable.rb +37 -0
- data/lib/crea/mixins/retriable.rb +58 -0
- data/lib/crea/mixins/serializable.rb +45 -0
- data/lib/crea/operation.rb +141 -0
- data/lib/crea/operation/account_create.rb +10 -0
- data/lib/crea/operation/account_create_with_delegation.rb +12 -0
- data/lib/crea/operation/account_update.rb +8 -0
- data/lib/crea/operation/account_witness_proxy.rb +4 -0
- data/lib/crea/operation/account_witness_vote.rb +5 -0
- data/lib/crea/operation/cancel_transfer_from_savings.rb +4 -0
- data/lib/crea/operation/challenge_authority.rb +5 -0
- data/lib/crea/operation/change_recovery_account.rb +5 -0
- data/lib/crea/operation/claim_account.rb +5 -0
- data/lib/crea/operation/claim_reward_balance.rb +6 -0
- data/lib/crea/operation/comment.rb +9 -0
- data/lib/crea/operation/comment_options.rb +10 -0
- data/lib/crea/operation/convert.rb +5 -0
- data/lib/crea/operation/create_claimed_account.rb +10 -0
- data/lib/crea/operation/custom.rb +5 -0
- data/lib/crea/operation/custom_binary.rb +8 -0
- data/lib/crea/operation/custom_json.rb +6 -0
- data/lib/crea/operation/decline_voting_rights.rb +4 -0
- data/lib/crea/operation/delegate_vesting_shares.rb +5 -0
- data/lib/crea/operation/delete_comment.rb +4 -0
- data/lib/crea/operation/escrow_approve.rb +8 -0
- data/lib/crea/operation/escrow_dispute.rb +7 -0
- data/lib/crea/operation/escrow_release.rb +10 -0
- data/lib/crea/operation/escrow_transfer.rb +12 -0
- data/lib/crea/operation/feed_publish.rb +4 -0
- data/lib/crea/operation/limit_order_cancel.rb +4 -0
- data/lib/crea/operation/limit_order_create.rb +8 -0
- data/lib/crea/operation/limit_order_create2.rb +8 -0
- data/lib/crea/operation/prove_authority.rb +4 -0
- data/lib/crea/operation/recover_account.rb +6 -0
- data/lib/crea/operation/report_over_production.rb +5 -0
- data/lib/crea/operation/request_account_recovery.rb +6 -0
- data/lib/crea/operation/reset_account.rb +5 -0
- data/lib/crea/operation/set_reset_account.rb +5 -0
- data/lib/crea/operation/set_withdraw_vesting_route.rb +6 -0
- data/lib/crea/operation/transfer.rb +6 -0
- data/lib/crea/operation/transfer_from_savings.rb +7 -0
- data/lib/crea/operation/transfer_to_savings.rb +6 -0
- data/lib/crea/operation/transfer_to_vesting.rb +5 -0
- data/lib/crea/operation/vote.rb +6 -0
- data/lib/crea/operation/withdraw_vesting.rb +4 -0
- data/lib/crea/operation/witness_set_properties.rb +5 -0
- data/lib/crea/operation/witness_update.rb +7 -0
- data/lib/crea/rpc/base_client.rb +179 -0
- data/lib/crea/rpc/http_client.rb +143 -0
- data/lib/crea/rpc/thread_safe_http_client.rb +35 -0
- data/lib/crea/stream.rb +385 -0
- data/lib/crea/transaction.rb +96 -0
- data/lib/crea/transaction_builder.rb +393 -0
- data/lib/crea/type/amount.rb +107 -0
- data/lib/crea/type/base_type.rb +10 -0
- data/lib/crea/utils.rb +17 -0
- data/lib/crea/version.rb +4 -0
- metadata +478 -0
data/lib/crea/stream.rb
ADDED
@@ -0,0 +1,385 @@
|
|
1
|
+
module Crea
|
2
|
+
# Crea::Stream allows a live view of the CREA blockchain.
|
3
|
+
#
|
4
|
+
# Example streaming blocks:
|
5
|
+
#
|
6
|
+
# stream = Crea::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 = Crea::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 = Crea::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 [Crea::DatabaseApi] :database_api
|
41
|
+
# @option options [Crea::BlockApi] :block_api
|
42
|
+
# @option options [Crea::AccountHistoryApi || Crea::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] || Crea::DatabaseApi.new(options)
|
50
|
+
@block_api = options[:block_api] || Crea::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 {Crea::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 = Crea::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 = Crea::Stream.new
|
160
|
+
# stream.operations(types: :vote_operation) do |vote|
|
161
|
+
# puts vote.to_json
|
162
|
+
# end
|
163
|
+
#
|
164
|
+
# ... Or ...
|
165
|
+
#
|
166
|
+
# stream = Crea::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 = Crea::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.cbd_payout, v.crea_payout, v.vesting_payout]}"
|
177
|
+
# end
|
178
|
+
#
|
179
|
+
# ... or multiple virtual operation types;
|
180
|
+
#
|
181
|
+
# stream = Crea::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 = Crea::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
|
+
Crea::AccountHistoryApi.new(@instance_options)
|
249
|
+
rescue Crea::UnknownApiError => e
|
250
|
+
warn "#{e.inspect}, falling back to Crea::CondenserApi." unless @no_warn
|
251
|
+
Crea::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 Crea::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 Crea::CondenserApi
|
334
|
+
[block_num, true]
|
335
|
+
when Crea::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 Crea::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 Crea::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
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Crea
|
2
|
+
class Transaction
|
3
|
+
include JSONable
|
4
|
+
include Utils
|
5
|
+
|
6
|
+
ATTRIBUTES = %i(id ref_block_num ref_block_prefix expiration operations
|
7
|
+
extensions signatures)
|
8
|
+
|
9
|
+
attr_accessor *ATTRIBUTES
|
10
|
+
|
11
|
+
def initialize(options = {})
|
12
|
+
if !!(hex = options.delete(:hex))
|
13
|
+
marshal = Marshal.new(hex: hex)
|
14
|
+
marshal.transaction(trx: self)
|
15
|
+
end
|
16
|
+
|
17
|
+
options.each do |k, v|
|
18
|
+
raise Crea::ArgumentError, "Invalid option specified: #{k}" unless ATTRIBUTES.include?(k.to_sym)
|
19
|
+
|
20
|
+
send("#{k}=", v)
|
21
|
+
end
|
22
|
+
|
23
|
+
self.operations ||= []
|
24
|
+
self.extensions ||= []
|
25
|
+
self.signatures ||= []
|
26
|
+
|
27
|
+
self.expiration = case @expiration
|
28
|
+
when String then Time.parse(@expiration + 'Z')
|
29
|
+
else; @expiration
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def inspect
|
34
|
+
properties = ATTRIBUTES.map do |prop|
|
35
|
+
unless (v = instance_variable_get("@#{prop}")).nil?
|
36
|
+
v = if v.respond_to? :strftime
|
37
|
+
v.strftime('%Y-%m-%dT%H:%M:%S')
|
38
|
+
else
|
39
|
+
v
|
40
|
+
end
|
41
|
+
|
42
|
+
"@#{prop}=#{v}"
|
43
|
+
end
|
44
|
+
end.compact.join(', ')
|
45
|
+
|
46
|
+
"#<#{self.class.name} [#{properties}]>"
|
47
|
+
end
|
48
|
+
|
49
|
+
def expiration
|
50
|
+
if @expiration.respond_to? :strftime
|
51
|
+
@expiration.strftime('%Y-%m-%dT%H:%M:%S')
|
52
|
+
else
|
53
|
+
@expiration
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def expired?
|
58
|
+
@expiration.nil? || @expiration < Time.now
|
59
|
+
end
|
60
|
+
|
61
|
+
def [](key)
|
62
|
+
key = key.to_sym
|
63
|
+
send(key) if self.class.attributes.include?(key)
|
64
|
+
end
|
65
|
+
|
66
|
+
def []=(key, value)
|
67
|
+
key = key.to_sym
|
68
|
+
send("#{key}=", value) if self.class.attributes.include?(key)
|
69
|
+
end
|
70
|
+
|
71
|
+
def ==(other_trx)
|
72
|
+
return true if self.equal? other_trx
|
73
|
+
return false unless self.class == other_trx.class
|
74
|
+
|
75
|
+
begin
|
76
|
+
return false if self[:ref_block_num].to_i != other_trx[:ref_block_num].to_i
|
77
|
+
return false if self[:ref_block_prefix].to_i != other_trx[:ref_block_prefix].to_i
|
78
|
+
return false if self[:expiration].to_i != other_trx[:expiration].to_i
|
79
|
+
return false if self[:operations].size != other_trx[:operations].size
|
80
|
+
|
81
|
+
op_values = self[:operations].map do |type, value|
|
82
|
+
[type.to_s, value.values.map{|v| v.to_s.gsub(/[^a-zA-Z0-9-]/, '')}]
|
83
|
+
end.flatten.sort
|
84
|
+
|
85
|
+
other_op_values = other_trx[:operations].map do |type, value|
|
86
|
+
[type.to_s, value.values.map{|v| v.to_s.gsub(/[^a-zA-Z0-9-]/, '')}]
|
87
|
+
end.flatten.sort
|
88
|
+
# binding.pry unless op_values == other_op_values
|
89
|
+
op_values == other_op_values
|
90
|
+
rescue => e
|
91
|
+
# binding.pry
|
92
|
+
false
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|