meeseeker 0.0.6 → 2.0.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,59 +73,114 @@ 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}:#{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)
56
83
  end
57
84
  end
58
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
+
59
111
  def stream_transactions(options = {}, &block)
60
112
  redis = Meeseeker.redis
61
113
  last_block_num = nil
62
- agent = Agent.new
114
+ until_block_num = options[:until_block_num].to_i
63
115
 
64
116
  if !!options[:at_block_num]
65
117
  last_block_num = options[:at_block_num].to_i
66
118
  else
67
- last_block_num = redis.get(Meeseeker::LAST_STEEM_ENGINE_BLOCK_NUM_KEY).to_i + 1
119
+ new_sync = false
120
+ last_block_num = redis.get(@chain_key_prefix + Meeseeker::LAST_STEEM_ENGINE_BLOCK_NUM_KEY_SUFFIX)
68
121
  block_info = agent.latest_block_info
69
122
  block_num = block_info['blockNumber']
70
-
71
- last_block = agent.block(last_block_num)
123
+ last_block = agent.block(block_num)
72
124
  last_block_timestamp = Time.parse(last_block['timestamp'] + 'Z')
125
+
126
+ if last_block_num.nil?
127
+ new_sync = true
128
+ last_block_num = block_num
129
+ else
130
+ last_block_num = last_block_num.to_i + 1
131
+ end
73
132
 
74
133
  if Meeseeker.expire_keys == -1
75
134
  last_block_num = [last_block_num, block_num].max
76
135
 
77
- puts "Sync Steem Engine from: #{last_block_num}"
78
- elsif Time.now.utc - last_block_timestamp > Meeseeker.expire_keys
136
+ puts "Sync #{chain_name} from: #{last_block_num}"
137
+ elsif new_sync || (Time.now.utc - last_block_timestamp > Meeseeker.expire_keys)
79
138
  last_block_num = block_num + 1
80
139
 
81
- puts 'Starting new Steem Engine sync.'
140
+ puts "Starting new #{chain_name} sync."
82
141
  else
83
- puts "Resuming from Steem Engine block #{last_block_num} ..."
142
+ puts "Resuming from #{chain_name} block #{last_block_num} ..."
84
143
  end
85
144
  end
86
145
 
87
146
  block_num = last_block_num
88
147
 
89
148
  loop do
90
- 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
91
158
 
92
159
  if block.nil?
93
- sleep 3 # sleep for one mainnet block interval
160
+ sleep Meeseeker::BLOCK_INTERVAL
94
161
  redo
95
162
  end
96
163
 
97
164
  transactions = block['transactions']
98
165
 
99
166
  transactions.each do |transaction|
100
- 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)
101
180
  end
102
181
 
182
+ break if until_block_num != 0 && block_num > until_block_num
183
+
103
184
  block_num = block_num + 1
104
185
  end
105
186
  end
@@ -1,4 +1,4 @@
1
1
  module Meeseeker
2
- VERSION = '0.0.6'
2
+ VERSION = '2.0.0-1'
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.pre-1', '>= 1.0.0.pre-1'
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