crea-ruby 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|