radiator 0.3.15 → 0.4.0pre1
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 +4 -4
- data/Gemfile.lock +9 -9
- data/README.md +2 -18
- data/Rakefile +11 -2
- data/lib/golos.rb +3 -0
- data/lib/radiator/api.rb +62 -123
- data/lib/radiator/chain.rb +184 -224
- data/lib/radiator/error_parser.rb +3 -5
- data/lib/radiator/logger.rb +4 -2
- data/lib/radiator/mixins/acts_as_poster.rb +124 -0
- data/lib/radiator/mixins/acts_as_voter.rb +50 -0
- data/lib/radiator/mixins/acts_as_wallet.rb +67 -0
- data/lib/radiator/transaction.rb +64 -51
- data/lib/radiator/type/permission.rb +2 -1
- data/lib/radiator/utils.rb +3 -11
- data/lib/radiator/version.rb +1 -1
- data/lib/radiator.rb +3 -1
- data/lib/steem.rb +3 -0
- metadata +8 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b0f7aaa5e12224ae48fdfe1535182583e298d97f
|
4
|
+
data.tar.gz: 5ec823c95e96ad1a44ceb0b77bc08b72aede7f8e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b7736813af94b8ef03e1b80e0f43aa593b194c48f71094e672798d3791eab2f0925ee313d1ea5b8b749cc8b753e0209f9298205038c5ff71b43015289eedc27
|
7
|
+
data.tar.gz: 55248de7d7142432b2d8b3a11a9e536c34915ba52bea098e38988e4c3c180ace704cfe852aa9a65914b6afc702f6bd94a6a831c2e5a1a7b6ee623b31c5e8aa53
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
radiator (0.
|
4
|
+
radiator (0.4.0pre1)
|
5
5
|
awesome_print (~> 1.7, >= 1.7.0)
|
6
6
|
bitcoin-ruby (~> 0.0, >= 0.0.11)
|
7
7
|
ffi (~> 1.9, >= 1.9.18)
|
@@ -16,28 +16,28 @@ GEM
|
|
16
16
|
addressable (2.5.2)
|
17
17
|
public_suffix (>= 2.0.2, < 4.0)
|
18
18
|
awesome_print (1.8.0)
|
19
|
-
bitcoin-ruby (0.0.
|
19
|
+
bitcoin-ruby (0.0.17)
|
20
20
|
connection_pool (2.2.1)
|
21
21
|
crack (0.4.3)
|
22
22
|
safe_yaml (~> 1.0.0)
|
23
23
|
docile (1.1.5)
|
24
|
-
ffi (1.9.
|
24
|
+
ffi (1.9.21)
|
25
25
|
hashdiff (0.3.7)
|
26
|
-
hashie (3.5.
|
26
|
+
hashie (3.5.7)
|
27
27
|
json (2.1.0)
|
28
28
|
little-plugger (1.1.4)
|
29
29
|
logging (2.2.2)
|
30
30
|
little-plugger (~> 1.1)
|
31
31
|
multi_json (~> 1.10)
|
32
|
-
minitest (5.
|
32
|
+
minitest (5.11.3)
|
33
33
|
minitest-line (0.6.4)
|
34
34
|
minitest (~> 5.0)
|
35
35
|
minitest-proveit (1.0.0)
|
36
36
|
minitest (> 5, < 7)
|
37
|
-
multi_json (1.
|
37
|
+
multi_json (1.13.1)
|
38
38
|
net-http-persistent (3.0.0)
|
39
39
|
connection_pool (~> 2.2)
|
40
|
-
public_suffix (3.0.
|
40
|
+
public_suffix (3.0.2)
|
41
41
|
rake (12.3.0)
|
42
42
|
safe_yaml (1.0.4)
|
43
43
|
simplecov (0.15.1)
|
@@ -46,7 +46,7 @@ GEM
|
|
46
46
|
simplecov-html (~> 0.10.0)
|
47
47
|
simplecov-html (0.10.2)
|
48
48
|
vcr (3.0.3)
|
49
|
-
webmock (3.
|
49
|
+
webmock (3.3.0)
|
50
50
|
addressable (>= 2.3.6)
|
51
51
|
crack (>= 0.3.2)
|
52
52
|
hashdiff
|
@@ -68,4 +68,4 @@ DEPENDENCIES
|
|
68
68
|
yard (~> 0.9.9)
|
69
69
|
|
70
70
|
BUNDLED WITH
|
71
|
-
1.16.
|
71
|
+
1.16.1
|
data/README.md
CHANGED
@@ -397,7 +397,7 @@ Radiator supports failover for situations where a node has, for example, become
|
|
397
397
|
|
398
398
|
```ruby
|
399
399
|
options = {
|
400
|
-
|
400
|
+
ur: 'https://api.steemit.com',
|
401
401
|
failover_urls: [
|
402
402
|
'https://api.steemitstage.com',
|
403
403
|
'https://gtg.steem.house:8090'
|
@@ -421,22 +421,6 @@ There is another rare scenario involving `::Transaction` broadcasts that's handl
|
|
421
421
|
tx = Radiator::Transaction.new(wif: wif, recover_transactions_on_error: false)
|
422
422
|
```
|
423
423
|
|
424
|
-
### Golos Failover Examples
|
425
|
-
|
426
|
-
Typically, you only need to pass `chain: :golos` to enable Golos. Failover is enabled by default. If you want to provide your own full nodes, use this format:
|
427
|
-
|
428
|
-
```ruby
|
429
|
-
options = {
|
430
|
-
chain: :golos,
|
431
|
-
url: 'https://ws.golos.io',
|
432
|
-
failover_urls: [
|
433
|
-
'https://api.golos.cf'
|
434
|
-
]
|
435
|
-
}
|
436
|
-
|
437
|
-
api = Radiator::Api.new(options)
|
438
|
-
```
|
439
|
-
|
440
424
|
## Debugging
|
441
425
|
|
442
426
|
To enable debugging, set environment `LOG=DEBUG` before launching your app. E.g.:
|
@@ -501,7 +485,7 @@ Verify your code is not doing too much between blocks.
|
|
501
485
|
<img src="http://www.steemimg.com/images/2016/08/19/RadiatorCoolingFan-54in-Webfdcb1.png" />
|
502
486
|
</center>
|
503
487
|
|
504
|
-
See my previous Ruby How To posts in: [/
|
488
|
+
See my previous Ruby How To posts in: [#radiator](https://steemit.com/created/radiator) [#ruby](https://steemit.com/created/ruby)
|
505
489
|
|
506
490
|
## Get in touch!
|
507
491
|
|
data/Rakefile
CHANGED
@@ -75,8 +75,17 @@ task :test_live_stream, [:chain, :persist] do |t, args|
|
|
75
75
|
o = t.map(&:operations)
|
76
76
|
op_size = o.map(&:size).reduce(0, :+)
|
77
77
|
total_ops += op_size
|
78
|
-
api.get_ops_in_block(n, true) do |vops|
|
79
|
-
|
78
|
+
api.get_ops_in_block(n, true) do |vops, error|
|
79
|
+
if !!error
|
80
|
+
puts "Error on get_ops_in_block for block #{n}"
|
81
|
+
ap error
|
82
|
+
end
|
83
|
+
|
84
|
+
vop_size = if vops.nil?
|
85
|
+
0
|
86
|
+
else
|
87
|
+
vops.size
|
88
|
+
end
|
80
89
|
total_vops += vop_size
|
81
90
|
|
82
91
|
vop_ratio = if total_vops > 0
|
data/lib/golos.rb
CHANGED
data/lib/radiator/api.rb
CHANGED
@@ -151,7 +151,8 @@ module Radiator
|
|
151
151
|
DEFAULT_GOLOS_FAILOVER_URLS = [
|
152
152
|
DEFAULT_GOLOS_URL,
|
153
153
|
'https://api.golos.cf',
|
154
|
-
# not recommended:
|
154
|
+
# not recommended, not all plug-ins enabled:
|
155
|
+
# 'https://ws.goldvoice.club',
|
155
156
|
# 'http://golos-seed.arcange.eu',
|
156
157
|
# not recommended, requires option ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE
|
157
158
|
# 'https://golos-seed.arcange.eu',
|
@@ -245,13 +246,14 @@ module Radiator
|
|
245
246
|
|
246
247
|
Hashie.logger = @hashie_logger
|
247
248
|
@method_names = nil
|
249
|
+
@uri = nil
|
250
|
+
@http_id = nil
|
248
251
|
@http_memo = {}
|
249
252
|
@api_options = options.dup.merge(chain: @chain)
|
250
253
|
@api = nil
|
251
254
|
@block_api = nil
|
252
255
|
@backoff_at = nil
|
253
|
-
|
254
|
-
ObjectSpace.define_finalizer(self, self.class.finalize(@logger, @hashie_logger))
|
256
|
+
@jussi_supported = []
|
255
257
|
end
|
256
258
|
|
257
259
|
# Get a specific block or range of blocks.
|
@@ -287,87 +289,6 @@ module Radiator
|
|
287
289
|
end
|
288
290
|
end
|
289
291
|
|
290
|
-
# Find a specific block.
|
291
|
-
#
|
292
|
-
# Example:
|
293
|
-
#
|
294
|
-
# api = Radiator::Api.new
|
295
|
-
# block = api.find_block(12345678)
|
296
|
-
# transactions = block.transactions
|
297
|
-
#
|
298
|
-
# ... or ...
|
299
|
-
#
|
300
|
-
# api = Radiator::Api.new
|
301
|
-
# transactions = api.find_block(12345678) do |block|
|
302
|
-
# block.transactions
|
303
|
-
# end
|
304
|
-
#
|
305
|
-
# @param block_number [Fixnum]
|
306
|
-
# @param block the block to execute for each result, optional.
|
307
|
-
# @return [Hash]
|
308
|
-
def find_block(block_number, &block)
|
309
|
-
if !!block
|
310
|
-
yield api.get_blocks(block_number).first
|
311
|
-
else
|
312
|
-
api.get_blocks(block_number).first
|
313
|
-
end
|
314
|
-
end
|
315
|
-
|
316
|
-
# Find a specific account.
|
317
|
-
#
|
318
|
-
# Example:
|
319
|
-
#
|
320
|
-
# api = Radiator::Api.new
|
321
|
-
# ned = api.find_account('ned')
|
322
|
-
# vesting_shares = ned.vesting_shares
|
323
|
-
#
|
324
|
-
# ... or ...
|
325
|
-
#
|
326
|
-
# api = Radiator::Api.new
|
327
|
-
# vesting_shares = api.find_account('ned') do |ned|
|
328
|
-
# ned.vesting_shares
|
329
|
-
# end
|
330
|
-
#
|
331
|
-
# @param id [String] Name of the account to find.
|
332
|
-
# @param block the block to execute for each result, optional.
|
333
|
-
# @return [Hash]
|
334
|
-
def find_account(id, &block)
|
335
|
-
if !!block
|
336
|
-
yield api.get_accounts([id]).result.first
|
337
|
-
else
|
338
|
-
api.get_accounts([id]).result.first
|
339
|
-
end
|
340
|
-
end
|
341
|
-
|
342
|
-
# Returns the current base (STEEM) price in the vest asset (VESTS).
|
343
|
-
#
|
344
|
-
def base_per_mvest
|
345
|
-
api.get_dynamic_global_properties do |properties|
|
346
|
-
total_vesting_fund_steem = properties.total_vesting_fund_steem.to_f
|
347
|
-
total_vesting_shares_mvest = properties.total_vesting_shares.to_f / 1e6
|
348
|
-
|
349
|
-
total_vesting_fund_steem / total_vesting_shares_mvest
|
350
|
-
end
|
351
|
-
end
|
352
|
-
|
353
|
-
alias steem_per_mvest base_per_mvest
|
354
|
-
|
355
|
-
# Returns the current base (STEEM) price in the debt asset (SBD).
|
356
|
-
#
|
357
|
-
def base_per_debt
|
358
|
-
get_feed_history do |feed_history|
|
359
|
-
current_median_history = feed_history.current_median_history
|
360
|
-
base = current_median_history.base
|
361
|
-
base = base.split(' ').first.to_f
|
362
|
-
quote = current_median_history.quote
|
363
|
-
quote = quote.split(' ').first.to_f
|
364
|
-
|
365
|
-
(base / quote) * steem_per_mvest
|
366
|
-
end
|
367
|
-
end
|
368
|
-
|
369
|
-
alias steem_per_usd base_per_debt
|
370
|
-
|
371
292
|
# Stops the persistant http connections.
|
372
293
|
#
|
373
294
|
def shutdown
|
@@ -384,6 +305,18 @@ module Radiator
|
|
384
305
|
@api = nil
|
385
306
|
@block_api.shutdown if !!@block_api && @block_api != self
|
386
307
|
@block_api = nil
|
308
|
+
|
309
|
+
if !!@logger && defined?(@logger.close)
|
310
|
+
if defined?(@logger.closed?)
|
311
|
+
@logger.close unless @logger.closed?
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
if !!@hashie_logger && defined?(@hashie_logger.close)
|
316
|
+
if defined?(@hashie_logger.closed?)
|
317
|
+
@hashie_logger.close unless @hashie_logger.closed?
|
318
|
+
end
|
319
|
+
end
|
387
320
|
end
|
388
321
|
|
389
322
|
# @private
|
@@ -412,12 +345,21 @@ module Radiator
|
|
412
345
|
current_rpc_id = rpc_id
|
413
346
|
method_name = [api_name, m].join('.')
|
414
347
|
response = nil
|
415
|
-
options =
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
348
|
+
options = if jussi_supported? && api_name == :database_api
|
349
|
+
{
|
350
|
+
jsonrpc: "2.0",
|
351
|
+
params: args,
|
352
|
+
id: current_rpc_id,
|
353
|
+
method: m
|
354
|
+
}
|
355
|
+
else
|
356
|
+
{
|
357
|
+
jsonrpc: "2.0",
|
358
|
+
params: [api_name, m, args],
|
359
|
+
id: current_rpc_id,
|
360
|
+
method: "call"
|
361
|
+
}
|
362
|
+
end
|
421
363
|
|
422
364
|
tries = 0
|
423
365
|
timestamp = Time.now.utc
|
@@ -425,10 +367,6 @@ module Radiator
|
|
425
367
|
loop do
|
426
368
|
tries += 1
|
427
369
|
|
428
|
-
if tries > 5 && flappy? && !check_file_open?
|
429
|
-
raise ApiError, 'PANIC: Out of file resources'
|
430
|
-
end
|
431
|
-
|
432
370
|
begin
|
433
371
|
if tries > 1 && @recover_transactions_on_error && api_name == :network_broadcast_api
|
434
372
|
signatures, exp = extract_signatures(options)
|
@@ -450,6 +388,8 @@ module Radiator
|
|
450
388
|
elsif !response.kind_of? Net::HTTPSuccess
|
451
389
|
warning "Unexpected response (code: #{response.code}): #{response.inspect}, retrying ...", method_name, true
|
452
390
|
else
|
391
|
+
detect_jussi(response)
|
392
|
+
|
453
393
|
case response.code
|
454
394
|
when '200'
|
455
395
|
body = response.body
|
@@ -475,12 +415,18 @@ module Radiator
|
|
475
415
|
end
|
476
416
|
rescue Net::HTTP::Persistent::Error => e
|
477
417
|
warning "Unable to perform request: #{e} :: #{!!e.cause ? "cause: #{e.cause.message}" : ''}, retrying ...", method_name, true
|
418
|
+
rescue ConnectionPool::Error => e
|
419
|
+
warning "Connection Pool Error (#{e.message}), retrying ...", method_name, true
|
478
420
|
rescue Errno::ECONNREFUSED => e
|
479
421
|
warning 'Connection refused, retrying ...', method_name, true
|
480
422
|
rescue Errno::EADDRNOTAVAIL => e
|
481
423
|
warning 'Node not available, retrying ...', method_name, true
|
482
424
|
rescue Errno::ECONNRESET => e
|
483
425
|
warning "Connection Reset (#{e.message}), retrying ...", method_name, true
|
426
|
+
rescue Errno::EBUSY => e
|
427
|
+
warning "Resource busy (#{e.message}), retrying ...", method_name, true
|
428
|
+
rescue Errno::ENETDOWN => e
|
429
|
+
warning "Network down (#{e.message}), retrying ...", method_name, true
|
484
430
|
rescue Net::ReadTimeout => e
|
485
431
|
warning 'Node read timeout, retrying ...', method_name, true
|
486
432
|
rescue Net::OpenTimeout => e
|
@@ -626,6 +572,21 @@ module Radiator
|
|
626
572
|
end
|
627
573
|
end
|
628
574
|
|
575
|
+
def jussi_supported?(url = @url)
|
576
|
+
@jussi_supported.include? url
|
577
|
+
end
|
578
|
+
|
579
|
+
def detect_jussi(response)
|
580
|
+
return if jussi_supported?(@url)
|
581
|
+
|
582
|
+
jussi_response_id = response['x-jussi-response-id']
|
583
|
+
|
584
|
+
if !!jussi_response_id
|
585
|
+
debug "Found a node that supports jussi: #{@url}"
|
586
|
+
@jussi_supported << @url
|
587
|
+
end
|
588
|
+
end
|
589
|
+
|
629
590
|
def recover_transaction(signatures, expected_rpc_id, after)
|
630
591
|
debug "Looking for signatures: #{signatures.map{|s| s[0..5]}} since: #{after}"
|
631
592
|
|
@@ -643,25 +604,21 @@ module Radiator
|
|
643
604
|
# but we also give up once the block time is before the `after` argument.
|
644
605
|
|
645
606
|
api.get_blocks(block_range) do |block, block_num|
|
646
|
-
count += 1
|
647
|
-
raise ApiError, "Race condition detected on remote node at: #{block_num}" if block.nil?
|
648
|
-
|
649
|
-
# TODO Some blockchains (like Golos) do not have transaction_ids. In
|
650
|
-
# the future, it would be better to decode the operation and signature
|
651
|
-
# into the transaction id.
|
652
|
-
# See: https://github.com/steemit/steem/issues/187
|
653
|
-
# See: https://github.com/GolosChain/golos/issues/281
|
654
607
|
unless defined? block.transaction_ids
|
655
|
-
|
656
|
-
|
608
|
+
# Happens on Golos, see: https://github.com/GolosChain/golos/issues/281
|
609
|
+
error "Blockchain does not provide transaction ids in blocks, giving up."
|
610
|
+
return nil
|
657
611
|
end
|
658
612
|
|
613
|
+
count += 1
|
614
|
+
raise ApiError, "Race condition detected on remote node at: #{block_num}" if block.nil?
|
615
|
+
|
659
616
|
timestamp = Time.parse(block.timestamp + 'Z')
|
660
617
|
break if timestamp < after
|
661
618
|
|
662
619
|
block.transactions.each_with_index do |tx, index|
|
663
620
|
next unless ((tx['signatures'] || []) & signatures).any?
|
664
|
-
|
621
|
+
|
665
622
|
debug "Found transaction #{count} block(s) ago; took #{(Time.now.utc - start)} seconds to scan."
|
666
623
|
|
667
624
|
return {
|
@@ -783,12 +740,6 @@ module Radiator
|
|
783
740
|
end
|
784
741
|
end
|
785
742
|
|
786
|
-
def check_file_open?
|
787
|
-
File.exists?('.')
|
788
|
-
rescue
|
789
|
-
false
|
790
|
-
end
|
791
|
-
|
792
743
|
def backoff
|
793
744
|
shutdown
|
794
745
|
bump_failover if flappy? || !healthy?(uri)
|
@@ -803,17 +754,5 @@ module Radiator
|
|
803
754
|
@backoff_sleep = nil
|
804
755
|
end
|
805
756
|
end
|
806
|
-
|
807
|
-
def self.finalize(logger, hashie_logger)
|
808
|
-
proc {
|
809
|
-
if !!logger && defined?(logger.close) && !logger.closed?
|
810
|
-
logger.close
|
811
|
-
end
|
812
|
-
|
813
|
-
if !!hashie_logger && defined?(hashie_logger.close) && !hashie_logger.closed?
|
814
|
-
hashie_logger.close
|
815
|
-
end
|
816
|
-
}
|
817
|
-
end
|
818
757
|
end
|
819
758
|
end
|