substrate_client.rb 0.1.2 → 0.1.7
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/Dockerfile +17 -0
- data/Gemfile.lock +5 -6
- data/README.md +240 -9
- data/exe/metadata +6 -6
- data/lib/helper.rb +129 -0
- data/lib/substrate_client.rb +274 -153
- data/lib/substrate_client/version.rb +1 -1
- data/lib/substrate_client_sync.rb +190 -0
- data/lib/timeout_queue.rb +34 -0
- data/lib/websocket.rb +79 -0
- data/substrate_client.gemspec +1 -2
- metadata +9 -20
- data/exe/substrate_client +0 -18
data/lib/substrate_client.rb
CHANGED
@@ -1,198 +1,319 @@
|
|
1
1
|
require "substrate_client/version"
|
2
2
|
|
3
|
-
require "
|
4
|
-
require "scale"
|
5
|
-
|
6
|
-
require "faye/websocket"
|
7
|
-
require "eventmachine"
|
3
|
+
require "logger"
|
4
|
+
require "scale.rb"
|
8
5
|
require "json"
|
9
6
|
require "active_support"
|
10
7
|
require "active_support/core_ext/string"
|
8
|
+
require "websocket"
|
9
|
+
require "helper"
|
10
|
+
require "timeout_queue"
|
11
|
+
require "substrate_client_sync"
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
ws.on :open do |event|
|
19
|
-
# p [:open]
|
20
|
-
ws.send(payload.to_json)
|
21
|
-
end
|
22
|
-
|
23
|
-
ws.on :message do |event|
|
24
|
-
# p [:message, event.data]
|
25
|
-
if event.data.include?("jsonrpc")
|
26
|
-
result = JSON.parse event.data
|
27
|
-
ws.close(3001, "data received")
|
28
|
-
EM.stop
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
ws.on :close do |event|
|
33
|
-
# p [:close, event.code, event.reason]
|
34
|
-
ws = nil
|
35
|
-
end
|
13
|
+
class SubstrateClient
|
14
|
+
class RpcError < StandardError; end
|
15
|
+
class RpcTimeout < StandardError; end
|
16
|
+
class << self
|
17
|
+
attr_accessor :logger
|
36
18
|
end
|
19
|
+
SubstrateClient.logger = Logger.new(STDOUT)
|
20
|
+
SubstrateClient.logger.level = Logger::INFO
|
37
21
|
|
38
|
-
result
|
39
|
-
end
|
40
|
-
|
41
|
-
class SubstrateClient
|
42
22
|
attr_accessor :spec_name, :spec_version, :metadata
|
23
|
+
attr_accessor :ws
|
43
24
|
|
44
|
-
def initialize(url)
|
25
|
+
def initialize(url, spec_name: nil, onopen: nil)
|
45
26
|
@url = url
|
46
27
|
@request_id = 1
|
28
|
+
@spec_name = spec_name
|
29
|
+
@onopen = onopen
|
30
|
+
Scale::TypeRegistry.instance.load(spec_name)
|
31
|
+
|
32
|
+
init_ws
|
33
|
+
|
34
|
+
at_exit { self.close }
|
47
35
|
end
|
48
36
|
|
49
|
-
|
50
|
-
|
37
|
+
def close
|
38
|
+
@ws.close
|
39
|
+
end
|
40
|
+
|
41
|
+
def request(method, params, callback: nil, subscription_callback: nil)
|
51
42
|
payload = {
|
52
43
|
"jsonrpc" => "2.0",
|
53
44
|
"method" => method,
|
54
45
|
"params" => params,
|
55
46
|
"id" => @request_id
|
56
47
|
}
|
48
|
+
|
49
|
+
while @callbacks.nil?
|
50
|
+
sleep(1)
|
51
|
+
end
|
52
|
+
|
53
|
+
@callbacks[@request_id] = proc do |data|
|
54
|
+
if not subscription_callback.nil? && data["result"]
|
55
|
+
@subscription_callbacks[data["result"]] = subscription_callback
|
56
|
+
end
|
57
|
+
|
58
|
+
callback.call data if callback
|
59
|
+
end
|
60
|
+
@ws.send(payload.to_json)
|
57
61
|
@request_id += 1
|
58
|
-
ws_request(@url, payload)
|
59
62
|
end
|
60
63
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
64
|
+
def init_runtime(block_hash=nil, &callback)
|
65
|
+
# set current runtime spec version
|
66
|
+
self.state_get_runtime_version(block_hash) do |runtime_version|
|
67
|
+
@spec_version = runtime_version["specVersion"]
|
68
|
+
Scale::TypeRegistry.instance.spec_version = @spec_version
|
69
|
+
|
70
|
+
# set current metadata
|
71
|
+
self.get_metadata(block_hash) do |metadata|
|
72
|
+
@metadata = metadata
|
73
|
+
Scale::TypeRegistry.instance.metadata = @metadata.value
|
74
|
+
callback.call
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def do_init_runtime(block_hash, &callback)
|
80
|
+
end
|
81
|
+
|
82
|
+
def invoke(method, params, callback)
|
83
|
+
request method, params, callback: proc { |data|
|
84
|
+
if data["error"]
|
85
|
+
Thread.main.raise RpcError, data["error"]
|
86
|
+
else
|
87
|
+
callback.call data["result"] unless callback.nil?
|
88
|
+
end
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
def rpc_method(method_name)
|
93
|
+
Helper.real_method_name(method_name.to_s)
|
67
94
|
end
|
68
95
|
|
69
96
|
# ################################################
|
70
|
-
#
|
97
|
+
# origin rpc methods
|
71
98
|
# ################################################
|
72
|
-
def
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
99
|
+
def method_missing(method, args, &callback)
|
100
|
+
rpc_method = Helper.real_method_name(method)
|
101
|
+
invoke rpc_method, args, callback
|
102
|
+
end
|
103
|
+
|
104
|
+
def state_get_runtime_version(block_hash=nil, &callback)
|
105
|
+
invoke rpc_method(__method__), [ block_hash ], callback
|
106
|
+
end
|
107
|
+
|
108
|
+
def rpc_methods(&callback)
|
109
|
+
invoke rpc_method(__method__), [], callback
|
110
|
+
end
|
111
|
+
|
112
|
+
def chain_get_head(&callback)
|
113
|
+
invoke rpc_method(__method__), [], callback
|
114
|
+
end
|
115
|
+
|
116
|
+
def chain_get_finalised_head(&callback)
|
117
|
+
invoke rpc_method(__method__), [], callback
|
118
|
+
end
|
119
|
+
|
120
|
+
def chain_get_header(block_hash = nil, &callback)
|
121
|
+
invoke rpc_method(__method__), [ block_hash ], callback
|
122
|
+
end
|
123
|
+
|
124
|
+
def chain_get_block(block_hash = nil, &callback)
|
125
|
+
invoke rpc_method(__method__), [ block_hash ], callback
|
126
|
+
end
|
127
|
+
|
128
|
+
def chain_get_block_hash(block_id, &callback)
|
129
|
+
invoke rpc_method(__method__), [ block_id ], callback
|
130
|
+
end
|
131
|
+
|
132
|
+
def chain_get_runtime_version(block_hash = nil, &callback)
|
133
|
+
invoke rpc_method(__method__), [ block_hash ], callback
|
134
|
+
end
|
135
|
+
|
136
|
+
def state_get_metadata(block_hash = nil, &callback)
|
137
|
+
invoke rpc_method(__method__), [ block_hash ], callback
|
138
|
+
end
|
139
|
+
|
140
|
+
def state_get_storage(storage_key, block_hash = nil, &callback)
|
141
|
+
invoke rpc_method(__method__), [ storage_key, block_hash ], callback
|
142
|
+
end
|
143
|
+
|
144
|
+
def system_name(&callback)
|
145
|
+
invoke rpc_method(__method__), [], callback
|
146
|
+
end
|
147
|
+
|
148
|
+
def system_version(&callback)
|
149
|
+
invoke rpc_method(__method__), [], callback
|
150
|
+
end
|
151
|
+
|
152
|
+
def chain_subscribe_all_heads(&callback)
|
153
|
+
request rpc_method(__method__), [], subscription_callback: callback
|
154
|
+
end
|
155
|
+
|
156
|
+
def chain_unsubscribe_all_heads(subscription)
|
157
|
+
invoke rpc_method(__method__), [ subscription ], nil
|
158
|
+
end
|
159
|
+
|
160
|
+
def chain_subscribe_new_heads(&callback)
|
161
|
+
request rpc_method(__method__), [], subscription_callback: callback
|
162
|
+
end
|
163
|
+
|
164
|
+
def chain_unsubscribe_new_heads(subscription)
|
165
|
+
invoke rpc_method(__method__), [ subscription ], nil
|
166
|
+
end
|
167
|
+
|
168
|
+
def chain_subscribe_finalized_heads(&callback)
|
169
|
+
request rpc_method(__method__), [], subscription_callback: callback
|
170
|
+
end
|
171
|
+
|
172
|
+
def chain_unsubscribe_finalized_heads(subscription)
|
173
|
+
invoke rpc_method(__method__), [ subscription ], nil
|
174
|
+
end
|
175
|
+
|
176
|
+
def state_subscribe_runtime_version(&callback)
|
177
|
+
request rpc_method(__method__), [], subscription_callback: callback
|
178
|
+
end
|
179
|
+
|
180
|
+
def state_unsubscribe_runtime_version(subscription)
|
181
|
+
invoke rpc_method(__method__), [ subscription ], nil
|
182
|
+
end
|
183
|
+
|
184
|
+
def state_subscribe_storage(keys, &callback)
|
185
|
+
request rpc_method(__method__), [keys], subscription_callback: callback
|
186
|
+
end
|
187
|
+
|
188
|
+
def state_unsubscribe_storage(subscription)
|
189
|
+
invoke rpc_method(__method__), [ subscription ], nil
|
190
|
+
end
|
191
|
+
|
192
|
+
# ################################################
|
193
|
+
# custom methods based on origin rpc methods
|
194
|
+
# ################################################
|
195
|
+
def method_list(&callback)
|
196
|
+
self.rpc_methods do |result|
|
197
|
+
callback.call result["methods"].map(&:underscore)
|
128
198
|
end
|
199
|
+
end
|
129
200
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
hasher2,
|
136
|
-
metadata[:version]
|
137
|
-
)
|
201
|
+
def get_block_number(block_hash, &callback)
|
202
|
+
self.chain_get_header(block_hash) do |header|
|
203
|
+
callback.call header["number"].to_i(16)
|
204
|
+
end
|
205
|
+
end
|
138
206
|
|
139
|
-
|
207
|
+
def get_metadata(block_hash=nil, &callback)
|
208
|
+
self.state_get_metadata(block_hash) do |hex|
|
209
|
+
callback.call Scale::Types::Metadata.decode(Scale::Bytes.new(hex))
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def get_block(block_hash=nil, &callback)
|
214
|
+
self.init_runtime block_hash do
|
215
|
+
self.chain_get_block(block_hash) do |block|
|
216
|
+
block = Helper.decode_block block
|
217
|
+
callback.call block
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
140
221
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
222
|
+
def get_block_events(block_hash=nil, &callback)
|
223
|
+
self.init_runtime(block_hash) do
|
224
|
+
storage_key = "0x26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"
|
225
|
+
self.state_get_storage storage_key, block_hash do |events_data|
|
226
|
+
scale_bytes = Scale::Bytes.new(events_data)
|
227
|
+
events = Scale::Types.get("Vec<EventRecord>").decode(scale_bytes).to_human
|
228
|
+
callback.call events
|
229
|
+
end
|
230
|
+
end
|
147
231
|
end
|
148
232
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
if params
|
155
|
-
params.each_with_index do |param, index|
|
156
|
-
if index == 0
|
157
|
-
param_hasher = hasher
|
158
|
-
elsif index == 1
|
159
|
-
param_hasher = hasher2
|
160
|
-
else
|
161
|
-
raise "Unexpected third parameter for storage call"
|
162
|
-
end
|
233
|
+
def subscribe_block_events(&callback)
|
234
|
+
self.chain_subscribe_finalized_heads do |data|
|
235
|
+
|
236
|
+
block_number = data["params"]["result"]["number"].to_i(16) - 1
|
237
|
+
block_hash = data["params"]["result"]["parentHash"]
|
163
238
|
|
164
|
-
|
165
|
-
|
166
|
-
|
239
|
+
EM.defer(
|
240
|
+
|
241
|
+
proc {
|
242
|
+
self.get_block_events block_hash do |events|
|
243
|
+
begin
|
244
|
+
result = { block_number: block_number, events: events }
|
245
|
+
callback.call result
|
246
|
+
rescue => ex
|
247
|
+
SubstrateClient.logger.error ex.message
|
248
|
+
SubstrateClient.logger.error ex.backtrace.join("\n")
|
249
|
+
end
|
167
250
|
end
|
168
|
-
|
251
|
+
},
|
169
252
|
|
170
|
-
|
171
|
-
|
172
|
-
# TODO: add test
|
173
|
-
storage_hash = storage_module_name + " " + storage_function_name
|
174
|
-
|
175
|
-
unless params.nil?
|
176
|
-
params = [params] if params.class != ::Array
|
177
|
-
params_key = params.join("")
|
178
|
-
hasher = "Twox128" if hasher.nil?
|
179
|
-
storage_hash += params_key.hex_to_bytes.bytes_to_utf8
|
180
|
-
end
|
253
|
+
proc { |result|
|
254
|
+
},
|
181
255
|
|
182
|
-
|
183
|
-
|
256
|
+
proc { |e|
|
257
|
+
SubstrateClient.logger.error e
|
258
|
+
}
|
259
|
+
|
260
|
+
)
|
184
261
|
end
|
262
|
+
end
|
185
263
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
264
|
+
def get_storage(module_name, storage_name, params = nil, block_hash = nil, &callback)
|
265
|
+
self.init_runtime(block_hash) do
|
266
|
+
storage_hash, return_type = Helper.generate_storage_hash_from_metadata(@metadata, module_name, storage_name, params)
|
267
|
+
self.state_get_storage(storage_hash, block_hash) do |result|
|
268
|
+
if result
|
269
|
+
storage = Scale::Types.get(return_type).decode(Scale::Bytes.new(result))
|
270
|
+
callback.call storage
|
271
|
+
else
|
272
|
+
callback.call nil
|
273
|
+
end
|
274
|
+
end
|
192
275
|
end
|
276
|
+
end
|
193
277
|
|
278
|
+
def compose_call(module_name, call_name, params, block_hash=nil, &callback)
|
279
|
+
self.init_runtime(block_hash) do
|
280
|
+
hex = Helper.compose_call_from_metadata(@metadata, module_name, call_name, params)
|
281
|
+
callback.call hex
|
282
|
+
end
|
194
283
|
end
|
195
284
|
|
285
|
+
private
|
286
|
+
def init_ws
|
287
|
+
@ws = Websocket.new(@url,
|
288
|
+
|
289
|
+
onopen: proc do |event|
|
290
|
+
@callbacks = {}
|
291
|
+
@subscription_callbacks = {}
|
292
|
+
@onopen.call event if not @onopen.nil?
|
293
|
+
end,
|
294
|
+
|
295
|
+
onmessage: proc do |event|
|
296
|
+
if event.data.include?("jsonrpc")
|
297
|
+
begin
|
298
|
+
data = JSON.parse event.data
|
299
|
+
|
300
|
+
if data["params"]
|
301
|
+
if @subscription_callbacks[data["params"]["subscription"]]
|
302
|
+
@subscription_callbacks[data["params"]["subscription"]].call data
|
303
|
+
end
|
304
|
+
else
|
305
|
+
@callbacks[data["id"]].call data
|
306
|
+
@callbacks.delete(data["id"])
|
307
|
+
end
|
196
308
|
|
197
|
-
|
309
|
+
rescue => ex
|
310
|
+
SubstrateClient.logger.error ex.message
|
311
|
+
SubstrateClient.logger.error ex.backtrace.join("\n")
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
198
315
|
|
316
|
+
)
|
317
|
+
end
|
318
|
+
|
319
|
+
end
|