meeseeker 0.0.7 → 2.0.0

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.
@@ -0,0 +1,20 @@
1
+ module Meeseeker::HiveEngine
2
+
3
+ class Agent < Meeseeker::SteemEngine::Agent
4
+ def initialize(options = {})
5
+ super
6
+
7
+ self.user_agent = Meeseeker::AGENT_ID
8
+ self.max_history = 0
9
+ self.default_encoding = 'UTF-8'
10
+
11
+ @node_url = options[:url] || Meeseeker::hive_engine_node_url
12
+ end
13
+ end
14
+
15
+ class FollowerJob < Meeseeker::SteemEngine::FollowerJob
16
+ def initialize(options = {})
17
+ @chain_key_prefix = options[:chain_key_prefix] || Meeseeker::HIVE_ENGINE_CHAIN_KEY_PREFIX
18
+ end
19
+ end
20
+ end
@@ -26,30 +26,44 @@ module Meeseeker::SteemEngine
26
26
  end
27
27
 
28
28
  def latest_block_info
29
- request_body = {
30
- jsonrpc: "2.0",
31
- method: :getLatestBlockInfo,
32
- id: rpc_id
33
- }.to_json
29
+ 5.times do
30
+ request_body = {
31
+ jsonrpc: "2.0",
32
+ method: :getLatestBlockInfo,
33
+ id: rpc_id
34
+ }.to_json
35
+
36
+ response = request_with_entity :post, blockchain_uri, request_body, POST_HEADERS
37
+ latest_block_info = JSON[response.body]["result"]
38
+
39
+ return latest_block_info if !!latest_block_info
40
+
41
+ sleep 3
42
+ end
34
43
 
35
- response = request_with_entity :post, blockchain_uri, request_body, POST_HEADERS
36
-
37
- JSON[response.body]["result"]
44
+ return nil
38
45
  end
39
46
 
40
47
  def block(block_num)
41
- request_body = {
42
- jsonrpc: "2.0",
43
- method: :getBlockInfo,
44
- params: {
45
- blockNumber: block_num
46
- },
47
- id: rpc_id
48
- }.to_json
49
-
50
- response = request_with_entity :post, blockchain_uri, request_body, POST_HEADERS
48
+ 5.times do
49
+ request_body = {
50
+ jsonrpc: "2.0",
51
+ method: :getBlockInfo,
52
+ params: {
53
+ blockNumber: block_num.to_i
54
+ },
55
+ id: rpc_id
56
+ }.to_json
57
+
58
+ response = request_with_entity :post, blockchain_uri, request_body, POST_HEADERS
59
+ block = JSON[response.body]["result"]
60
+
61
+ return block if !!block
62
+
63
+ sleep 3
64
+ end
51
65
 
52
- JSON[response.body]["result"]
66
+ return nil
53
67
  end
54
68
  private
55
69
  def rpc_id
@@ -1,5 +1,15 @@
1
1
  module Meeseeker::SteemEngine
2
+ MAX_RETRY_INTERVAL = 18.0
3
+
2
4
  class FollowerJob
5
+ def initialize(options = {})
6
+ @chain_key_prefix = options[:chain_key_prefix] || Meeseeker::STEEM_ENGINE_CHAIN_KEY_PREFIX
7
+ end
8
+
9
+ def chain_name
10
+ @chain_key_prefix.split('_').map(&:capitalize).join(' ')
11
+ end
12
+
3
13
  def perform(options = {})
4
14
  redis = Meeseeker.redis
5
15
  last_key_prefix = nil
@@ -7,11 +17,14 @@ module Meeseeker::SteemEngine
7
17
  current_block_num = nil
8
18
  block_transactions = []
9
19
 
10
- stream_transactions(options) do |transaction, block|
20
+ stream_transactions(options) do |data, block|
21
+ transaction = data[:transaction]
22
+ virtual = !!data[:virtual]
23
+
11
24
  begin
12
- trx_id = transaction['transactionId'].split('-').first
25
+ trx_id = transaction['transactionId'].to_s.split('-').first
13
26
  block_num = block['blockNumber']
14
- current_key_prefix = "steem_engine:#{block_num}:#{trx_id}"
27
+ current_key_prefix = "#{@chain_key_prefix}:#{block_num}:#{trx_id}"
15
28
  contract = transaction['contract']
16
29
  action = transaction['action']
17
30
 
@@ -19,7 +32,7 @@ module Meeseeker::SteemEngine
19
32
  trx_index += 1
20
33
  else
21
34
  if !!last_key_prefix
22
- n, b, t = last_key_prefix.split(':')
35
+ _, b, t = last_key_prefix.split(':')
23
36
  transaction_payload = {
24
37
  block_num: b.to_i,
25
38
  transaction_id: t,
@@ -27,10 +40,17 @@ module Meeseeker::SteemEngine
27
40
  }
28
41
 
29
42
  block_transactions << trx_id
30
- redis.publish('steem_engine:transaction', transaction_payload.to_json)
43
+
44
+ trx_pub_key = if !!virtual
45
+ "#{@chain_key_prefix}:virtual_transaction"
46
+ else
47
+ "#{@chain_key_prefix}:transaction"
48
+ end
49
+
50
+ redis.publish(trx_pub_key, transaction_payload.to_json)
31
51
  end
32
52
 
33
- last_key_prefix = "steem_engine:#{block_num}:#{trx_id}"
53
+ last_key_prefix = "#{@chain_key_prefix}:#{block_num}:#{trx_id}"
34
54
  trx_index = 0
35
55
  end
36
56
 
@@ -38,6 +58,12 @@ module Meeseeker::SteemEngine
38
58
  puts key
39
59
  end
40
60
 
61
+ unless Meeseeker.max_keys == -1
62
+ while redis.keys("#{@chain_key_prefix}:*").size > Meeseeker.max_keys
63
+ sleep Meeseeker::BLOCK_INTERVAL
64
+ end
65
+ end
66
+
41
67
  redis.set(key, transaction.to_json)
42
68
  redis.expire(key, Meeseeker.expire_keys) unless Meeseeker.expire_keys == -1
43
69
 
@@ -47,27 +73,51 @@ module Meeseeker::SteemEngine
47
73
  block_num: block_num
48
74
  }
49
75
 
50
- redis.set(Meeseeker::LAST_STEEM_ENGINE_BLOCK_NUM_KEY, block_num)
51
- redis.publish('steem_engine:block', block_payload.to_json)
76
+ redis.set(@chain_key_prefix + Meeseeker::LAST_STEEM_ENGINE_BLOCK_NUM_KEY_SUFFIX, block_num)
77
+ redis.publish("#{@chain_key_prefix}:block", block_payload.to_json)
52
78
  current_block_num = block_num
53
79
  end
54
80
 
55
- redis.publish("steem_engine:#{contract}", {key: key}.to_json)
56
- redis.publish("steem_engine:#{contract}:#{action}", {key: key}.to_json)
81
+ redis.publish("#{@chain_key_prefix}:#{contract}", {key: key}.to_json)
82
+ redis.publish("#{@chain_key_prefix}:#{contract}:#{action}", {key: key}.to_json)
57
83
  end
58
84
  end
59
85
  private
86
+ def agent
87
+ @agent ||= case @chain_key_prefix
88
+ when 'steem_engine' then Agent.new
89
+ when 'hive_engine' then Meeseeker::HiveEngine::Agent.new
90
+ end
91
+ end
92
+
93
+ def agent_reset
94
+ return if @agent.nil?
95
+
96
+ @agent.shutdown
97
+ @agent = nil
98
+ end
99
+
100
+ def retry_interval
101
+ @retry_interval ||= 0.1
102
+ @retry_interval *= 2
103
+
104
+ [@retry_interval, MAX_RETRY_INTERVAL].min
105
+ end
106
+
107
+ def reset_retry_interval
108
+ @retry_interval = nil
109
+ end
110
+
60
111
  def stream_transactions(options = {}, &block)
61
112
  redis = Meeseeker.redis
62
113
  last_block_num = nil
63
- agent = Agent.new
64
114
  until_block_num = options[:until_block_num].to_i
65
115
 
66
116
  if !!options[:at_block_num]
67
117
  last_block_num = options[:at_block_num].to_i
68
118
  else
69
119
  new_sync = false
70
- last_block_num = redis.get(Meeseeker::LAST_STEEM_ENGINE_BLOCK_NUM_KEY)
120
+ last_block_num = redis.get(@chain_key_prefix + Meeseeker::LAST_STEEM_ENGINE_BLOCK_NUM_KEY_SUFFIX)
71
121
  block_info = agent.latest_block_info
72
122
  block_num = block_info['blockNumber']
73
123
  last_block = agent.block(block_num)
@@ -83,30 +133,50 @@ module Meeseeker::SteemEngine
83
133
  if Meeseeker.expire_keys == -1
84
134
  last_block_num = [last_block_num, block_num].max
85
135
 
86
- puts "Sync Steem Engine from: #{last_block_num}"
136
+ puts "Sync #{chain_name} from: #{last_block_num}"
87
137
  elsif new_sync || (Time.now.utc - last_block_timestamp > Meeseeker.expire_keys)
88
138
  last_block_num = block_num + 1
89
139
 
90
- puts 'Starting new Steem Engine sync.'
140
+ puts "Starting new #{chain_name} sync."
91
141
  else
92
- puts "Resuming from Steem Engine block #{last_block_num} ..."
142
+ puts "Resuming from #{chain_name} block #{last_block_num} ..."
93
143
  end
94
144
  end
95
145
 
96
146
  block_num = last_block_num
97
147
 
98
148
  loop do
99
- block = agent.block(block_num)
149
+ begin
150
+ block = agent.block(block_num)
151
+ reset_retry_interval
152
+ rescue Net::HTTP::Persistent::Error => e
153
+ puts "Retrying: #{e}"
154
+ agent_reset
155
+ sleep retry_interval
156
+ redo
157
+ end
100
158
 
101
159
  if block.nil?
102
- sleep 3 # sleep for one mainnet block interval
160
+ sleep Meeseeker::BLOCK_INTERVAL
103
161
  redo
104
162
  end
105
163
 
106
164
  transactions = block['transactions']
107
165
 
108
166
  transactions.each do |transaction|
109
- yield transaction.merge(timestamp: block['timestamp']), block
167
+ yield({transaction: transaction.merge(timestamp: block['timestamp'])}, block)
168
+ end
169
+
170
+ virtual_transactions = block['virtualTransactions']
171
+
172
+ virtual_transactions.each do |virtual_transaction|
173
+ _, vtrx_in_block = virtual_transaction['transactionId'].split('-')
174
+ virtual_transaction = virtual_transaction.merge(
175
+ timestamp: block['timestamp'],
176
+ 'transactionId' => "#{Meeseeker::VIRTUAL_TRX_ID}-#{vtrx_in_block}"
177
+ )
178
+
179
+ yield({transaction: virtual_transaction, virtual: true}, block)
110
180
  end
111
181
 
112
182
  break if until_block_num != 0 && block_num > until_block_num
@@ -1,4 +1,4 @@
1
1
  module Meeseeker
2
- VERSION = '0.0.7'
2
+ VERSION = '2.0.0'
3
3
  AGENT_ID = "meeseeker/#{VERSION}"
4
4
  end
@@ -1,7 +1,11 @@
1
1
  module Meeseeker
2
2
  class WitnessScheduleJob
3
3
  def perform(options = {})
4
- database_api = Steem::DatabaseApi.new(url: Meeseeker.node_url)
4
+ chain = (options[:chain] || 'hive').to_sym
5
+ chain_key_prefix = chain.to_s if !!options[:chain]
6
+ chain_key_prefix ||= Meeseeker.default_chain_key_prefix
7
+ url = Meeseeker.default_url(chain_key_prefix)
8
+ database_api = Meeseeker.database_api_class(chain_key_prefix).new(url: url)
5
9
  redis = Meeseeker.redis
6
10
  mode = options.delete(:mode) || Meeseeker.stream_mode
7
11
  schedule = nil
@@ -53,7 +57,7 @@ module Meeseeker
53
57
 
54
58
  if next_shuffle_block_num != last_shuffle_block_num
55
59
  puts "next_shuffle_block_num: #{next_shuffle_block_num}; current_shuffled_witnesses: #{schedule.current_shuffled_witnesses.join(', ')}"
56
- redis.publish('steem:witness:schedule', schedule.to_json)
60
+ redis.publish("#{chain_key_prefix}:witness:schedule", schedule.to_json)
57
61
  last_shuffle_block_num = next_shuffle_block_num
58
62
  end
59
63
 
@@ -27,8 +27,11 @@ Gem::Specification.new do |s|
27
27
  s.add_development_dependency 'simplecov', '~> 0.15', '>= 0.15.1'
28
28
  s.add_development_dependency 'pry', '~> 0.11', '>= 0.11.3'
29
29
  s.add_development_dependency 'irb', '~> 1.0', '>= 1.0.0'
30
+ s.add_development_dependency 'mock_redis', '~> 0.22', '>= 0.22.0'
30
31
 
31
32
  s.add_dependency 'redis', '~> 4.1', '>= 4.1.0'
32
- s.add_dependency 'steem-mechanize', '~> 0.0', '>= 0.0.5'
33
+ s.add_dependency 'steem-ruby', '~> 0.9', '>= 0.9.4'
34
+ s.add_dependency 'hive-ruby', '~> 1.0.0', '>= 1.0.0'
35
+ s.add_dependency 'mechanize', '~> 2.7', '>= 2.7.6'
33
36
  s.add_dependency 'rb-readline', '~> 0.5', '>= 0.5.5'
34
37
  end
@@ -4,28 +4,265 @@ require 'rake'
4
4
  module Meeseeker
5
5
  class MeeseekerTest < Meeseeker::Test
6
6
  def setup
7
- gem_dir = File.expand_path("..", File.dirname(__FILE__))
8
- $LOAD_PATH.unshift gem_dir
9
-
10
- pwd = Dir.pwd
11
- Dir.chdir(gem_dir)
12
- Rake.application.init
13
- Rake.application.load_rakefile
14
- Dir.chdir(pwd)
7
+ @max_blocks = 30 # must be at least 15 to get past irreversible
8
+ end
9
+
10
+ def test_verify_hive_jobs
11
+ chain = 'hive'
12
+ check_keys(chain)
13
+ keys = []
14
+
15
+ begin
16
+ Rake::Task['verify:block_org'].reenable
17
+ assert Rake::Task['verify:block_org'].invoke('hive', @max_blocks)
18
+ rescue SystemExit => e
19
+ puts 'Exited.'
20
+ rescue Redis::TimeoutError => e
21
+ skip 'Timed out.'
22
+ end
23
+
24
+ begin
25
+ Rake::Task['verify:witness:schedule'].reenable
26
+ assert Rake::Task['verify:witness:schedule'].invoke('hive', @max_blocks)
27
+ rescue SystemExit => e
28
+ puts 'Exited.'
29
+ rescue Redis::TimeoutError => e
30
+ skip 'Timed out.'
31
+ end
32
+
33
+ block_api = Hive::BlockApi.new(url: 'http://anyx.io')
34
+ keys = Meeseeker.redis.keys('hive:*')
35
+ data = keys.map do |key|
36
+ next if key == 'hive:meeseeker:last_block_num'
37
+
38
+ n, b, t, i, o = key.split(':')
39
+
40
+ assert_equal 'hive', n, "expected hive key, got: #{key}"
41
+
42
+ [b, t]
43
+ end.compact.sample(10).to_h
15
44
 
16
- if !!Meeseeker.redis.get(Meeseeker::LAST_BLOCK_NUM_KEY)
17
- fail "Found existing keys. Please use 'rake reset' to enable this test."
45
+ assert data.any?, 'expect hive data'
46
+
47
+ data.each do |b, t|
48
+ block_api.get_block(block_num: b) do |result|
49
+ block = result.block
50
+
51
+ refute_nil block, "did not expect nil block (#{b})"
52
+
53
+ if !!block.transaction_ids
54
+ assert block.transaction_ids.include?(t), "Could not find hive trx_id (#{t}) in block (#{b})."
55
+ else
56
+ puts "Skipped check for hive trx_id (#{t}) in block (#{b}) because API does not support lookup."
57
+ end
58
+ end
59
+ end
60
+
61
+ if keys.any?
62
+ dropped = Meeseeker.redis.del(*keys)
63
+ puts "Dropped #{dropped} keys."
64
+ else
65
+ fail 'No keys.'
18
66
  end
19
67
  end
20
68
 
21
- def test_verify_all_jobs
22
- max_blocks = 30 # must be at least 15 to get past irreversible
69
+ def test_verify_steem_jobs
70
+ chain = 'steem'
71
+ check_keys(chain)
72
+ keys = []
73
+
74
+ begin
75
+ Rake::Task['verify:block_org'].reenable
76
+ assert Rake::Task['verify:block_org'].invoke('steem', @max_blocks)
77
+ rescue SystemExit => e
78
+ puts 'Exited.'
79
+ rescue Redis::TimeoutError => e
80
+ skip 'Timed out.'
81
+ end
82
+
83
+ begin
84
+ Rake::Task['verify:witness:schedule'].reenable
85
+ assert Rake::Task['verify:witness:schedule'].invoke('steem', @max_blocks)
86
+ rescue SystemExit => e
87
+ puts 'Exited.'
88
+ rescue Redis::TimeoutError => e
89
+ skip 'Timed out.'
90
+ end
91
+
92
+ block_api = Steem::BlockApi.new
93
+ keys = Meeseeker.redis.keys('steem:*')
94
+ data = keys.map do |key|
95
+ next if key == 'steem:meeseeker:last_block_num'
96
+
97
+ n, b, t, i, o = key.split(':')
98
+
99
+ assert_equal 'steem', n, "expected steem key, got: #{key}"
100
+
101
+ [b, t]
102
+ end.compact.sample(10).to_h
103
+
104
+ assert data.any?, 'expect steem data'
105
+
106
+ data.each do |b, t|
107
+ block_api.get_block(block_num: b) do |result|
108
+ block = result.block
109
+
110
+ refute_nil block, "did not expect nil block (#{b})"
111
+
112
+ if !!block.transaction_ids
113
+ assert block.transaction_ids.include?(t), "Could not find steem trx_id (#{t}) in block (#{b})."
114
+ else
115
+ puts "Skipped check for steem trx_id (#{t}) in block (#{b}) because API does not support lookup."
116
+ end
117
+ end
118
+ end
119
+
120
+ if keys.any?
121
+ dropped = Meeseeker.redis.del(*keys)
122
+ puts "Dropped #{dropped} keys."
123
+ else
124
+ fail 'No keys.'
125
+ end
126
+ end
23
127
 
24
- assert Rake::Task['verify:block_org'].invoke(max_blocks)
25
- assert Rake::Task['verify:steem_engine_block_org'].invoke(max_blocks)
26
- assert Rake::Task['verify:witness:schedule'].invoke(max_blocks)
128
+ def test_verify_steem_engine_jobs
129
+ chain = 'steem_engine'
130
+ check_keys(chain)
131
+ keys = []
132
+
133
+ begin
134
+ Rake::Task['verify:steem_engine_block_org'].reenable
135
+ Rake::Task['verify:engine_block_org'].reenable
136
+ assert Rake::Task['verify:steem_engine_block_org'].invoke(@max_blocks)
137
+ rescue SystemExit => e
138
+ puts 'Exited.'
139
+ rescue Redis::TimeoutError => e
140
+ skip 'Timed out.'
141
+ end
142
+
143
+ begin
144
+ Rake::Task['verify:steem_engine_ref_blocks'].reenable
145
+ Rake::Task['verify:engine_ref_blocks'].reenable
146
+ assert Rake::Task['verify:steem_engine_ref_blocks'].invoke(@max_blocks)
147
+ rescue SystemExit => e
148
+ puts 'Exited.'
149
+ rescue Redis::TimeoutError => e
150
+ skip 'Timed out.'
151
+ end
27
152
 
28
- Rake::Task['reset'].invoke
153
+ agent = Meeseeker::SteemEngine::Agent.new
154
+ keys = Meeseeker.redis.keys('steem_engine:*')
155
+ data = keys.map do |key|
156
+ n, b, t, i, o = key.split(':')
157
+
158
+ assert_equal chain, n, "expected steem_engine key, got: #{key}"
159
+
160
+ next if t == Meeseeker::VIRTUAL_TRX_ID
161
+
162
+ [b, t]
163
+ end.compact.sample(10).to_h
164
+
165
+ assert data.any?, 'expect steem_engine data'
166
+
167
+ data.each do |b, t|
168
+ block = agent.block(b)
169
+ refute_nil block, "did not expect nil block (#{b})"
170
+
171
+ count = block['transactions'].select do |trx|
172
+ trx['transactionId'].include? t
173
+ end.size
174
+
175
+ assert count > 0, "Could not find steem_engine trx_id (#{t}) in block (#{b})."
176
+ end
177
+
178
+ agent.shutdown
179
+
180
+ if keys.any?
181
+ dropped = Meeseeker.redis.del(*keys)
182
+ puts "Dropped #{dropped} keys."
183
+ else
184
+ fail 'No keys.'
185
+ end
186
+ end
187
+
188
+ def test_verify_hive_engine_jobs
189
+ chain = 'hive_engine'
190
+ check_keys(chain)
191
+ keys = []
192
+
193
+ begin
194
+ Rake::Task['verify:hive_engine_block_org'].reenable
195
+ Rake::Task['verify:engine_block_org'].reenable
196
+ assert Rake::Task['verify:hive_engine_block_org'].invoke(@max_blocks)
197
+ rescue SystemExit => e
198
+ puts 'Exited.'
199
+ rescue Redis::TimeoutError => e
200
+ skip 'Timed out.'
201
+ end
202
+
203
+ begin
204
+ Rake::Task['verify:hive_engine_ref_blocks'].reenable
205
+ Rake::Task['verify:engine_ref_blocks'].reenable
206
+ assert Rake::Task['verify:hive_engine_ref_blocks'].invoke(@max_blocks)
207
+ rescue SystemExit => e
208
+ puts 'Exited.'
209
+ rescue Redis::TimeoutError => e
210
+ skip 'Timed out.'
211
+ end
212
+
213
+ agent = Meeseeker::HiveEngine::Agent.new
214
+ keys = Meeseeker.redis.keys('hive_engine:*')
215
+ data = keys.map do |key|
216
+ n, b, t, i, o = key.split(':')
217
+
218
+ assert_equal chain, n, "expected hive_engine key, got: #{key}"
219
+
220
+ next if t == Meeseeker::VIRTUAL_TRX_ID
221
+
222
+ [b, t]
223
+ end.compact.sample(10).to_h
224
+
225
+ assert data.any?, 'expect hive_engine data'
226
+
227
+ data.each do |b, t|
228
+ block = agent.block(b)
229
+ refute_nil block, "did not expect nil block (#{b})"
230
+
231
+ count = block['transactions'].select do |trx|
232
+ trx['transactionId'].include? t
233
+ end.size
234
+
235
+ assert count > 0, "Could not find hive_engine trx_id (#{t}) in block (#{b})."
236
+ end
237
+
238
+ agent.shutdown
239
+
240
+ if keys.any?
241
+ dropped = Meeseeker.redis.del(*keys)
242
+ puts "Dropped #{dropped} keys."
243
+ else
244
+ fail 'No keys.'
245
+ end
246
+ end
247
+ private
248
+ def check_keys(chain)
249
+ chain = chain_key_prefix = chain.to_s
250
+ Meeseeker.node_url = case chain.to_sym
251
+ when :hive_engine then Meeseeker.shuffle_node_url('hive')
252
+ when :steem_engine then Meeseeker.shuffle_node_url('steem')
253
+ else
254
+ Meeseeker.shuffle_node_url(chain.to_s)
255
+ end
256
+
257
+ begin
258
+ if !!Meeseeker.redis.get(chain_key_prefix + Meeseeker::LAST_BLOCK_NUM_KEY_SUFFIX)
259
+ fail "Found existing keys. Please use 'rake reset' to enable this test."
260
+ end
261
+ rescue Redis::CannotConnectError => e
262
+ warn "Cannot connect to redis, using MockRedis instead."
263
+
264
+ Meeseeker.redis = MockRedis.new
265
+ end
29
266
  end
30
267
  end
31
268
  end