substrate_client.rb 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +3 -3
- data/lib/substrate_client.rb +54 -53
- data/lib/substrate_client/version.rb +1 -1
- data/lib/timeout_queue.rb +34 -0
- data/lib/websocket.rb +79 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ffef7ed87e2488047c796e8a77cc3e0789f532891a69283f6339b9dc55e899d
|
4
|
+
data.tar.gz: '08806295aaf4ced6714042496be2a54408fc4c1ba8d5bebbfe352b431407ec02'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ede3bdec9f513b5586f8d9404c2f55e8ab70d53cb70e8f87920da8ecaab5f605542bf1f5fef4137d267088f11591abf1b1624530f9e97f01d779c60015c876ff
|
7
|
+
data.tar.gz: 967fa99d7887a30bade8a3914cfed8496a0a821f9ffc101405608b941fb406418fcacbd95be499ed6cd28960b0eadc9b1c98e733828783814e58f5ea44b18336
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
substrate_client.rb (0.1.
|
4
|
+
substrate_client.rb (0.1.5)
|
5
5
|
activesupport (~> 5.2.4)
|
6
6
|
eventmachine (~> 1.2.7)
|
7
7
|
faye-websocket (~> 0.10.9)
|
@@ -10,7 +10,7 @@ PATH
|
|
10
10
|
GEM
|
11
11
|
remote: https://rubygems.org/
|
12
12
|
specs:
|
13
|
-
activesupport (5.2.4.
|
13
|
+
activesupport (5.2.4.3)
|
14
14
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
15
15
|
i18n (>= 0.7, < 2)
|
16
16
|
minitest (~> 5.1)
|
@@ -28,7 +28,7 @@ GEM
|
|
28
28
|
concurrent-ruby (~> 1.0)
|
29
29
|
json (2.3.0)
|
30
30
|
method_source (0.9.2)
|
31
|
-
minitest (5.14.
|
31
|
+
minitest (5.14.1)
|
32
32
|
pry (0.12.2)
|
33
33
|
coderay (~> 1.1.0)
|
34
34
|
method_source (~> 0.9.0)
|
data/lib/substrate_client.rb
CHANGED
@@ -1,29 +1,41 @@
|
|
1
1
|
require "substrate_client/version"
|
2
2
|
|
3
|
+
require "logger"
|
3
4
|
require "scale.rb"
|
4
|
-
require "faye/websocket"
|
5
|
-
require "eventmachine"
|
6
5
|
require "json"
|
7
6
|
require "active_support"
|
8
7
|
require "active_support/core_ext/string"
|
8
|
+
require "websocket"
|
9
|
+
require "timeout_queue"
|
9
10
|
|
10
11
|
class SubstrateClient
|
11
12
|
class RpcError < StandardError; end
|
13
|
+
class RpcTimeout < StandardError; end
|
14
|
+
class << self
|
15
|
+
attr_accessor :logger
|
16
|
+
end
|
17
|
+
SubstrateClient.logger = Logger.new(STDOUT)
|
18
|
+
SubstrateClient.logger.level = Logger::INFO
|
12
19
|
|
13
20
|
attr_accessor :spec_name, :spec_version, :metadata
|
14
|
-
|
21
|
+
attr_accessor :ws
|
15
22
|
|
16
|
-
def initialize(url: ,
|
23
|
+
def initialize(url, spec_name: nil, onopen: nil)
|
17
24
|
@url = url
|
18
25
|
@request_id = 1
|
19
26
|
@spec_name = spec_name
|
27
|
+
@onopen = onopen
|
20
28
|
Scale::TypeRegistry.instance.load(spec_name)
|
21
29
|
|
22
30
|
init_ws
|
23
31
|
end
|
24
32
|
|
33
|
+
def close
|
34
|
+
@ws.close
|
35
|
+
end
|
36
|
+
|
25
37
|
def request(method, params, subscription_callback=nil)
|
26
|
-
queue =
|
38
|
+
queue = TimeoutQueue.new
|
27
39
|
|
28
40
|
payload = {
|
29
41
|
"jsonrpc" => "2.0",
|
@@ -35,7 +47,7 @@ class SubstrateClient
|
|
35
47
|
@callbacks[@request_id] = proc { |data| queue << data }
|
36
48
|
@ws.send(payload.to_json)
|
37
49
|
@request_id += 1
|
38
|
-
data = queue.pop
|
50
|
+
data = queue.pop(true, 5)
|
39
51
|
|
40
52
|
if not subscription_callback.nil? && data["result"]
|
41
53
|
@subscription_callbacks[data["result"]] = subscription_callback
|
@@ -46,6 +58,8 @@ class SubstrateClient
|
|
46
58
|
else
|
47
59
|
data["result"]
|
48
60
|
end
|
61
|
+
rescue ThreadError => ex
|
62
|
+
raise RpcTimeout
|
49
63
|
end
|
50
64
|
|
51
65
|
def init_runtime(block_hash: nil, block_id: nil)
|
@@ -232,13 +246,13 @@ class SubstrateClient
|
|
232
246
|
begin
|
233
247
|
callback.call result
|
234
248
|
rescue => ex
|
235
|
-
|
236
|
-
|
249
|
+
SubstrateClient.logger.error ex.message
|
250
|
+
SubstrateClient.logger.error ex.backtrace.join("\n")
|
237
251
|
end
|
238
252
|
},
|
239
253
|
|
240
254
|
proc { |e|
|
241
|
-
|
255
|
+
SubstrateClient.logger.error e
|
242
256
|
}
|
243
257
|
|
244
258
|
)
|
@@ -289,7 +303,13 @@ class SubstrateClient
|
|
289
303
|
hasher2,
|
290
304
|
metadata.value.value[:metadata][:version]
|
291
305
|
)
|
292
|
-
|
306
|
+
|
307
|
+
p module_name
|
308
|
+
p storage_name
|
309
|
+
p params
|
310
|
+
p hasher
|
311
|
+
p hasher2
|
312
|
+
p metadata.value.value[:metadata][:version]
|
293
313
|
result = self.state_get_storage(storage_hash, block_hash)
|
294
314
|
return unless result
|
295
315
|
Scale::Types.get(return_type).decode(Scale::Bytes.new(result))
|
@@ -367,55 +387,36 @@ class SubstrateClient
|
|
367
387
|
|
368
388
|
private
|
369
389
|
def init_ws
|
370
|
-
|
371
|
-
|
372
|
-
Thread.new do
|
373
|
-
EM.run do
|
374
|
-
start_connection
|
375
|
-
queue << "ok"
|
376
|
-
end
|
377
|
-
end
|
378
|
-
|
379
|
-
if queue.pop
|
380
|
-
Thread.new do
|
381
|
-
loop do
|
382
|
-
if @ws && @ws.ready_state == 3
|
383
|
-
puts "try to reconnect"
|
384
|
-
start_connection
|
385
|
-
end
|
386
|
-
|
387
|
-
sleep(3)
|
388
|
-
end
|
389
|
-
end
|
390
|
-
end
|
391
|
-
end
|
390
|
+
@ws = Websocket.new(@url,
|
392
391
|
|
393
|
-
|
394
|
-
|
395
|
-
|
392
|
+
onopen: proc do |event|
|
393
|
+
@callbacks = {}
|
394
|
+
@subscription_callbacks = {}
|
395
|
+
@onopen.call event if not @onopen.nil?
|
396
|
+
end,
|
396
397
|
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
398
|
+
onmessage: proc do |event|
|
399
|
+
if event.data.include?("jsonrpc")
|
400
|
+
begin
|
401
|
+
data = JSON.parse event.data
|
402
|
+
|
403
|
+
if data["params"]
|
404
|
+
if @subscription_callbacks[data["params"]["subscription"]]
|
405
|
+
@subscription_callbacks[data["params"]["subscription"]].call data
|
406
|
+
end
|
407
|
+
else
|
408
|
+
@callbacks[data["id"]].call data
|
409
|
+
@callbacks.delete(data["id"])
|
407
410
|
end
|
408
|
-
else
|
409
|
-
@callbacks[data["id"]].call data
|
410
|
-
@callbacks.delete(data["id"])
|
411
|
-
end
|
412
411
|
|
413
|
-
|
414
|
-
|
415
|
-
|
412
|
+
rescue => ex
|
413
|
+
SubstrateClient.logger.error ex.message
|
414
|
+
SubstrateClient.logger.error ex.backtrace.join("\n")
|
415
|
+
end
|
416
416
|
end
|
417
417
|
end
|
418
|
-
|
418
|
+
|
419
|
+
)
|
419
420
|
end
|
420
421
|
|
421
422
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# https://vaneyckt.io/posts/ruby_concurrency_building_a_timeout_queue/
|
2
|
+
class TimeoutQueue
|
3
|
+
def initialize
|
4
|
+
@elems = []
|
5
|
+
@mutex = Mutex.new
|
6
|
+
@cond_var = ConditionVariable.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def <<(elem)
|
10
|
+
@mutex.synchronize do
|
11
|
+
@elems << elem
|
12
|
+
@cond_var.signal
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def pop(blocking = true, timeout = nil)
|
17
|
+
@mutex.synchronize do
|
18
|
+
if blocking
|
19
|
+
if timeout.nil?
|
20
|
+
while @elems.empty?
|
21
|
+
@cond_var.wait(@mutex)
|
22
|
+
end
|
23
|
+
else
|
24
|
+
timeout_time = Time.now.to_f + timeout
|
25
|
+
while @elems.empty? && (remaining_time = timeout_time - Time.now.to_f) > 0
|
26
|
+
@cond_var.wait(@mutex, remaining_time)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
raise ThreadError, 'queue empty' if @elems.empty?
|
31
|
+
@elems.shift
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/websocket.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require "faye/websocket"
|
2
|
+
require "eventmachine"
|
3
|
+
|
4
|
+
class SubstrateClient::Websocket
|
5
|
+
HEARTBEAT_INTERVAL = 3
|
6
|
+
RECONNECT_INTERVAL = 3
|
7
|
+
|
8
|
+
def initialize(url, onopen: nil, onmessage: nil)
|
9
|
+
@url = url
|
10
|
+
@onopen = onopen || proc { p [:open] }
|
11
|
+
@onmessage = onmessage || proc { |event| p [:message, event.data] }
|
12
|
+
|
13
|
+
@thread = Thread.new do
|
14
|
+
EM.run do
|
15
|
+
start_connection
|
16
|
+
end
|
17
|
+
SubstrateClient.logger.info "Event loop stopped"
|
18
|
+
end
|
19
|
+
@heartbeat_thread = start_heartbeat
|
20
|
+
end
|
21
|
+
|
22
|
+
def start_connection
|
23
|
+
SubstrateClient.logger.info "Start to connect"
|
24
|
+
@close = false
|
25
|
+
@missed_heartbeats = 0
|
26
|
+
@ping_id = 0
|
27
|
+
@ws = Faye::WebSocket::Client.new(@url)
|
28
|
+
@ws.on :open do |event|
|
29
|
+
@do_heartbeat = true
|
30
|
+
@onopen.call event
|
31
|
+
end
|
32
|
+
|
33
|
+
@ws.on :message do |event|
|
34
|
+
@onmessage.call event
|
35
|
+
end
|
36
|
+
|
37
|
+
@ws.on :close do |event|
|
38
|
+
# p [:close, event.code, event.reason]
|
39
|
+
if @close == false
|
40
|
+
@do_heartbeat = false
|
41
|
+
sleep RECONNECT_INTERVAL
|
42
|
+
start_connection
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
def start_heartbeat
|
49
|
+
Thread.new do
|
50
|
+
loop do
|
51
|
+
send_heartbeat if @do_heartbeat
|
52
|
+
sleep HEARTBEAT_INTERVAL
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def send_heartbeat
|
58
|
+
if @missed_heartbeats < 2
|
59
|
+
# puts "ping_#{@ping_id}"
|
60
|
+
@ws.ping @ping_id.to_s do
|
61
|
+
# puts "pong"
|
62
|
+
@missed_heartbeats -= 1
|
63
|
+
end
|
64
|
+
@missed_heartbeats += 1
|
65
|
+
@ping_id += 1
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def send(message)
|
70
|
+
@ws.send message
|
71
|
+
end
|
72
|
+
|
73
|
+
def close
|
74
|
+
@close = true
|
75
|
+
Thread.kill @heartbeat_thread
|
76
|
+
Thread.kill @thread
|
77
|
+
@ws = nil
|
78
|
+
end
|
79
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: substrate_client.rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wu Minzhe
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-05-
|
11
|
+
date: 2020-05-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faye-websocket
|
@@ -144,6 +144,8 @@ files:
|
|
144
144
|
- exe/metadata
|
145
145
|
- lib/substrate_client.rb
|
146
146
|
- lib/substrate_client/version.rb
|
147
|
+
- lib/timeout_queue.rb
|
148
|
+
- lib/websocket.rb
|
147
149
|
- substrate_client.gemspec
|
148
150
|
homepage: https://github.com/itering/substrate_client.rb
|
149
151
|
licenses:
|