substrate_client.rb 0.1.3 → 0.1.4
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 -4
- data/README.md +172 -8
- data/exe/metadata +2 -1
- data/lib/substrate_client.rb +314 -91
- data/lib/substrate_client/version.rb +1 -1
- data/substrate_client.gemspec +1 -2
- metadata +4 -20
- data/exe/substrate_client +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5f0802284ca7abb1fd02f502506a00ba67ef2c0d436a1bede7012fc7746196c9
|
4
|
+
data.tar.gz: 2b76f455d30a57c0b36eceecc5cf19e720126e9f071e3899b054a151bb1b122c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 392f72952f9822de5dff8b6c5df89b7eab52e7bdf38c988f0ea4448c8f6439f74dd06e415dd1fc33c627ed4c74e494c8838c452d3975e1f92c89a8b86e4e68a9
|
7
|
+
data.tar.gz: eda7bacb9cb728d254c3582ef99696689fe03d387b14ad38ad4196b1c4a2ed10eb3f806a8c01c1da9c9143ffe00a15e1597b534c2df9ac1876d62fd63a1be230
|
data/Gemfile.lock
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
substrate_client.rb (0.1.
|
4
|
+
substrate_client.rb (0.1.4)
|
5
5
|
activesupport (~> 5.2.4)
|
6
6
|
eventmachine (~> 1.2.7)
|
7
7
|
faye-websocket (~> 0.10.9)
|
8
|
-
scale.rb (~> 0.2.
|
9
|
-
substrate_common.rb (~> 0.1.8)
|
8
|
+
scale.rb (~> 0.2.5)
|
10
9
|
|
11
10
|
GEM
|
12
11
|
remote: https://rubygems.org/
|
@@ -47,7 +46,7 @@ GEM
|
|
47
46
|
diff-lcs (>= 1.2.0, < 2.0)
|
48
47
|
rspec-support (~> 3.9.0)
|
49
48
|
rspec-support (3.9.2)
|
50
|
-
scale.rb (0.2.
|
49
|
+
scale.rb (0.2.5)
|
51
50
|
activesupport (>= 4.0.0)
|
52
51
|
json (~> 2.3.0)
|
53
52
|
substrate_common.rb (~> 0.1.8)
|
data/README.md
CHANGED
@@ -20,7 +20,9 @@ Or install it yourself as:
|
|
20
20
|
|
21
21
|
## Usage
|
22
22
|
|
23
|
-
###
|
23
|
+
### Supported rpc methods
|
24
|
+
|
25
|
+
#### rpc method list
|
24
26
|
|
25
27
|
```ruby
|
26
28
|
require "substrate_client"
|
@@ -28,15 +30,177 @@ require "substrate_client"
|
|
28
30
|
client = SubstrateClient.new("wss://kusama-rpc.polkadot.io/")
|
29
31
|
puts client.method_list
|
30
32
|
```
|
31
|
-
The rpc
|
33
|
+
The rpc methods can be dynamically called by its name, so the methods returned by this method can all be used.
|
34
|
+
|
35
|
+
#### hard-coded rpc method
|
36
|
+
|
37
|
+
But, in order to show the parameters more clearly, some important or frequently used methods are hard-coded:
|
38
|
+
|
39
|
+
- chain_get_finalised_head
|
40
|
+
|
41
|
+
Get hash of the last finalized block in the canon chain
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
- chain_get_head
|
46
|
+
|
47
|
+
- chain_get_header(block_hash = nil)
|
48
|
+
|
49
|
+
Retrieves the header for a specific block
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
- chain_get_block(block_hash = nil)
|
54
|
+
|
55
|
+
Get header and body of a relay chain block
|
56
|
+
|
57
|
+
|
58
|
+
|
59
|
+
- chain_get_block_hash(block_id)
|
60
|
+
|
61
|
+
Get the block hash for a specific block
|
62
|
+
|
63
|
+
|
64
|
+
|
65
|
+
- chain_get_runtime_version(block_hash = nil)
|
66
|
+
|
67
|
+
Get the runtime version for a specific block
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
- state_get_metadata(block_hash = nil)
|
72
|
+
|
73
|
+
Returns the runtime metadata by block
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
- state_get_storage(storage_key, block_hash = nil)
|
78
|
+
|
79
|
+
Retrieves the storage for a key
|
80
|
+
|
81
|
+
|
82
|
+
|
83
|
+
- system_name
|
84
|
+
|
85
|
+
- system_version
|
86
|
+
|
87
|
+
|
88
|
+
|
89
|
+
- chain_subscribe_all_heads(&callback)
|
90
|
+
|
91
|
+
Retrieves the newest header via subscription. This will return data continuously until you unsubscribe the subscription.
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
subscription = client.chain_subscribe_new_heads do |data|
|
95
|
+
p data
|
96
|
+
end
|
97
|
+
```
|
98
|
+
|
99
|
+
- chain_unsubscribe_all_heads(subscription)
|
100
|
+
|
101
|
+
Unsubscribe newest header subscription.
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
client.chain_unsubscribe_all_heads(subscription)
|
105
|
+
```
|
106
|
+
|
107
|
+
|
108
|
+
|
109
|
+
- chain_subscribe_new_heads(&callback)
|
110
|
+
|
111
|
+
Retrieves the best header via subscription. This will return data continuously until you unsubscribe the subscription.
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
subscription = client.chain_subscribe_new_heads do |data|
|
115
|
+
p data
|
116
|
+
end
|
117
|
+
```
|
118
|
+
|
119
|
+
- chain_unsubscribe_new_heads(subscription)
|
120
|
+
|
121
|
+
Unsubscribe the best header subscription.
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
client.chain_unsubscribe_new_heads(subscription)
|
125
|
+
```
|
126
|
+
|
127
|
+
|
128
|
+
|
129
|
+
- chain_subscribe_finalized_heads(&callback)
|
130
|
+
|
131
|
+
Retrieves the best finalized header via subscription. This will return data continuously until you unsubscribe the subscription.
|
132
|
+
|
133
|
+
```ruby
|
134
|
+
subscription = client.chain_subscribe_finalized_heads do |data|
|
135
|
+
p data
|
136
|
+
end
|
137
|
+
```
|
138
|
+
|
139
|
+
- chain_unsubscribe_finalized_heads(subscription)
|
140
|
+
|
141
|
+
Unsubscribe the best finalized header subscription.
|
142
|
+
|
143
|
+
```ruby
|
144
|
+
client.chain_unsubscribe_finalized_heads(subscription)
|
145
|
+
```
|
146
|
+
|
147
|
+
|
148
|
+
|
149
|
+
- state_subscribe_runtime_version(&callback)
|
150
|
+
|
151
|
+
Retrieves the runtime version via subscription.
|
152
|
+
|
153
|
+
- state_unsubscribe_runtime_version(subscription)
|
154
|
+
|
155
|
+
Unsubscribe the runtime version subscription.
|
156
|
+
|
157
|
+
|
158
|
+
|
159
|
+
- state_subscribe_storage(keys, &callback)
|
160
|
+
|
161
|
+
Subscribes to storage changes for the provided keys until unsubscribe.
|
162
|
+
|
163
|
+
```ruby
|
164
|
+
subscription = client.state_subscribe_storage ["0x26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"] do |data|
|
165
|
+
p data
|
166
|
+
end
|
167
|
+
```
|
168
|
+
|
169
|
+
- state_unsubscribe_storage(subscription)
|
170
|
+
|
171
|
+
Unsubscribe storage changes.
|
172
|
+
|
173
|
+
|
174
|
+
### Cutom methods based on rpc methods
|
175
|
+
|
176
|
+
These methods will encode the parameters and decode the returned data
|
177
|
+
|
178
|
+
- get_block_number(block_hash)
|
179
|
+
|
180
|
+
- get_metadata(block_hash)
|
181
|
+
|
182
|
+
- get_block(block_hash=nil)
|
183
|
+
|
184
|
+
- get_block_events(block_hash)
|
185
|
+
|
186
|
+
- subscribe_block_events(&callback)
|
187
|
+
|
188
|
+
- get_storage(module_name, storage_name, params = nil, block_hash = nil)
|
189
|
+
|
190
|
+
```ruby
|
191
|
+
client.get_storage("Sudo", "Key")
|
192
|
+
client.get_storage("Balances", "TotalIssuance")
|
193
|
+
client.get_storage("System", "Account", ["0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"])
|
194
|
+
client.get_storage("ImOnline", "AuthoredBlocks", [2818, "0x749ddc93a65dfec3af27cc7478212cb7d4b0c0357fef35a0163966ab5333b757"])
|
195
|
+
```
|
196
|
+
|
197
|
+
- compose_call(module_name, call_name, params, block_hash=nil)
|
198
|
+
|
199
|
+
```ruby
|
200
|
+
compose_call "Balances", "Transfer", { dest: "0x586cb27c291c813ce74e86a60dad270609abf2fc8bee107e44a80ac00225c409", value: 1_000_000_000_000 }
|
201
|
+
```
|
32
202
|
|
33
|
-
## TODO
|
34
203
|
|
35
|
-
- [x] ws wss request support
|
36
|
-
- [ ] http request support
|
37
|
-
- [x] generate storage key
|
38
|
-
- [x] call any api supported by substrate node with ruby's method missing function
|
39
|
-
- [ ] metadata caching
|
40
204
|
|
41
205
|
## Development
|
42
206
|
|
data/exe/metadata
CHANGED
data/lib/substrate_client.rb
CHANGED
@@ -1,109 +1,263 @@
|
|
1
1
|
require "substrate_client/version"
|
2
2
|
|
3
|
-
require "
|
4
|
-
require "scale"
|
5
|
-
|
3
|
+
require "scale.rb"
|
6
4
|
require "faye/websocket"
|
7
5
|
require "eventmachine"
|
8
6
|
require "json"
|
9
7
|
require "active_support"
|
10
8
|
require "active_support/core_ext/string"
|
11
9
|
|
12
|
-
def ws_request(url, payload)
|
13
|
-
result = nil
|
14
|
-
|
15
|
-
EM.run do
|
16
|
-
ws = Faye::WebSocket::Client.new(url)
|
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
|
36
|
-
end
|
37
|
-
|
38
|
-
result
|
39
|
-
end
|
40
|
-
|
41
10
|
class SubstrateClient
|
11
|
+
class RpcError < StandardError; end
|
12
|
+
|
42
13
|
attr_accessor :spec_name, :spec_version, :metadata
|
14
|
+
attr_reader :ws
|
43
15
|
|
44
|
-
def initialize(url)
|
16
|
+
def initialize(url: , spec_name: nil)
|
45
17
|
@url = url
|
46
18
|
@request_id = 1
|
19
|
+
@spec_name = spec_name
|
20
|
+
Scale::TypeRegistry.instance.load(spec_name)
|
21
|
+
|
22
|
+
init_ws
|
47
23
|
end
|
48
24
|
|
49
|
-
|
50
|
-
|
25
|
+
def request(method, params, subscription_callback=nil)
|
26
|
+
queue = Queue.new
|
27
|
+
|
51
28
|
payload = {
|
52
29
|
"jsonrpc" => "2.0",
|
53
30
|
"method" => method,
|
54
31
|
"params" => params,
|
55
32
|
"id" => @request_id
|
56
33
|
}
|
34
|
+
|
35
|
+
@callbacks[@request_id] = proc { |data| queue << data }
|
36
|
+
@ws.send(payload.to_json)
|
57
37
|
@request_id += 1
|
58
|
-
|
38
|
+
data = queue.pop
|
39
|
+
|
40
|
+
if not subscription_callback.nil? && data["result"]
|
41
|
+
@subscription_callbacks[data["result"]] = subscription_callback
|
42
|
+
end
|
43
|
+
|
44
|
+
if data["error"]
|
45
|
+
raise RpcError, data["error"]
|
46
|
+
else
|
47
|
+
data["result"]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def init_runtime(block_hash: nil, block_id: nil)
|
52
|
+
if block_hash.nil?
|
53
|
+
if not block_id.nil?
|
54
|
+
block_hash = self.chain_get_block_hash(block_id)
|
55
|
+
else
|
56
|
+
block_hash = self.chain_get_head
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# set current runtime spec version
|
61
|
+
runtime_version = self.state_get_runtime_version(block_hash)
|
62
|
+
@spec_version = runtime_version["specVersion"]
|
63
|
+
Scale::TypeRegistry.instance.spec_version = @spec_version
|
64
|
+
|
65
|
+
# set current metadata
|
66
|
+
@metadata = self.get_metadata(block_hash)
|
67
|
+
Scale::TypeRegistry.instance.metadata = @metadata.value
|
68
|
+
true
|
69
|
+
end
|
70
|
+
|
71
|
+
def invoke(method, *params)
|
72
|
+
# params.reject! { |param| param.nil? }
|
73
|
+
request(method, params)
|
59
74
|
end
|
60
75
|
|
61
|
-
|
62
|
-
|
63
|
-
|
76
|
+
def rpc_method(method_name)
|
77
|
+
SubstrateClient.real_method_name(method_name.to_s)
|
78
|
+
end
|
79
|
+
|
80
|
+
# ################################################
|
81
|
+
# origin rpc methods
|
82
|
+
# ################################################
|
64
83
|
def method_missing(method, *args)
|
65
|
-
|
66
|
-
|
84
|
+
rpc_method = SubstrateClient.real_method_name(method)
|
85
|
+
invoke rpc_method, *args
|
86
|
+
end
|
87
|
+
|
88
|
+
def rpc_methods
|
89
|
+
invoke rpc_method(__method__)
|
90
|
+
end
|
91
|
+
|
92
|
+
def chain_get_head
|
93
|
+
invoke rpc_method(__method__)
|
94
|
+
end
|
95
|
+
|
96
|
+
def chain_get_finalised_head
|
97
|
+
invoke rpc_method(__method__)
|
98
|
+
end
|
99
|
+
|
100
|
+
def chain_get_header(block_hash = nil)
|
101
|
+
invoke rpc_method(__method__), block_hash
|
102
|
+
end
|
103
|
+
|
104
|
+
def chain_get_block(block_hash = nil)
|
105
|
+
invoke rpc_method(__method__), block_hash
|
106
|
+
end
|
107
|
+
|
108
|
+
def chain_get_block_hash(block_id)
|
109
|
+
invoke rpc_method(__method__), block_id
|
110
|
+
end
|
111
|
+
|
112
|
+
def chain_get_runtime_version(block_hash = nil)
|
113
|
+
invoke rpc_method(__method__), block_hash
|
114
|
+
end
|
115
|
+
|
116
|
+
def state_get_metadata(block_hash = nil)
|
117
|
+
invoke rpc_method(__method__), block_hash
|
118
|
+
end
|
119
|
+
|
120
|
+
def state_get_storage(storage_key, block_hash = nil)
|
121
|
+
invoke rpc_method(__method__), storage_key, block_hash
|
122
|
+
end
|
123
|
+
|
124
|
+
def system_name
|
125
|
+
invoke rpc_method(__method__)
|
126
|
+
end
|
127
|
+
|
128
|
+
def system_version
|
129
|
+
invoke rpc_method(__method__)
|
130
|
+
end
|
131
|
+
|
132
|
+
def chain_subscribe_all_heads(&callback)
|
133
|
+
request rpc_method(__method__), [], callback
|
134
|
+
end
|
135
|
+
|
136
|
+
def chain_unsubscribe_all_heads(subscription)
|
137
|
+
invoke rpc_method(__method__), subscription
|
138
|
+
end
|
139
|
+
|
140
|
+
def chain_subscribe_new_heads(&callback)
|
141
|
+
request rpc_method(__method__), [], callback
|
142
|
+
end
|
143
|
+
|
144
|
+
def chain_unsubscribe_new_heads(subscription)
|
145
|
+
invoke rpc_method(__method__), subscription
|
146
|
+
end
|
147
|
+
|
148
|
+
def chain_subscribe_finalized_heads(&callback)
|
149
|
+
request rpc_method(__method__), [], callback
|
150
|
+
end
|
151
|
+
|
152
|
+
def chain_unsubscribe_finalized_heads(subscription)
|
153
|
+
invoke rpc_method(__method__), subscription
|
154
|
+
end
|
155
|
+
|
156
|
+
def state_subscribe_runtime_version(&callback)
|
157
|
+
request rpc_method(__method__), [], callback
|
158
|
+
end
|
159
|
+
|
160
|
+
def state_unsubscribe_runtime_version(subscription)
|
161
|
+
invoke rpc_method(__method__), subscription
|
162
|
+
end
|
163
|
+
|
164
|
+
def state_subscribe_storage(keys, &callback)
|
165
|
+
request rpc_method(__method__), [keys], callback
|
166
|
+
end
|
167
|
+
|
168
|
+
def state_unsubscribe_storage(subscription)
|
169
|
+
invoke rpc_method(__method__), subscription
|
67
170
|
end
|
68
171
|
|
69
172
|
# ################################################
|
70
|
-
# custom methods
|
173
|
+
# custom methods based on origin rpc methods
|
71
174
|
# ################################################
|
72
175
|
def method_list
|
73
|
-
|
74
|
-
methods << "method_list"
|
75
|
-
methods << "get_storage_at"
|
176
|
+
self.rpc_methods["methods"].map(&:underscore)
|
76
177
|
end
|
77
178
|
|
78
|
-
def
|
79
|
-
|
80
|
-
|
81
|
-
@spec_version = block_runtime_version["specVersion"]
|
82
|
-
|
83
|
-
Scale::TypeRegistry.instance.load(spec_name, spec_version)
|
84
|
-
@metadata = self.get_metadata(block_hash)
|
85
|
-
true
|
179
|
+
def get_block_number(block_hash)
|
180
|
+
header = self.chain_get_header(block_hash)
|
181
|
+
header["number"].to_i(16)
|
86
182
|
end
|
87
183
|
|
88
184
|
def get_metadata(block_hash)
|
89
185
|
hex = self.state_get_metadata(block_hash)
|
90
|
-
|
91
|
-
|
186
|
+
Scale::Types::Metadata.decode(Scale::Bytes.new(hex))
|
187
|
+
end
|
188
|
+
|
189
|
+
def get_block(block_hash=nil)
|
190
|
+
self.init_runtime(block_hash: block_hash)
|
191
|
+
block = self.chain_get_block(block_hash)
|
192
|
+
|
193
|
+
block["block"]["header"]["number"] = block["block"]["header"]["number"].to_i(16)
|
194
|
+
|
195
|
+
block["block"]["extrinsics"].each_with_index do |hex, i|
|
196
|
+
scale_bytes = Scale::Bytes.new(hex)
|
197
|
+
block["block"]["extrinsics"][i] = Scale::Types::Extrinsic.decode(scale_bytes).to_human
|
198
|
+
end
|
199
|
+
|
200
|
+
block['block']['header']["digest"]["logs"].each_with_index do |hex, i|
|
201
|
+
scale_bytes = Scale::Bytes.new(hex)
|
202
|
+
block['block']['header']["digest"]["logs"][i] = Scale::Types::LogDigest.decode(scale_bytes).to_human
|
203
|
+
end
|
204
|
+
|
205
|
+
block
|
206
|
+
end
|
207
|
+
|
208
|
+
def get_block_events(block_hash)
|
209
|
+
self.init_runtime(block_hash: block_hash)
|
210
|
+
|
211
|
+
storage_key = "0x26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"
|
212
|
+
events_data = state_get_storage storage_key, block_hash
|
213
|
+
|
214
|
+
scale_bytes = Scale::Bytes.new(events_data)
|
215
|
+
Scale::Types.get("Vec<EventRecord>").decode(scale_bytes).to_human
|
216
|
+
end
|
217
|
+
|
218
|
+
def subscribe_block_events(&callback)
|
219
|
+
self.chain_subscribe_finalised_heads do |data|
|
220
|
+
|
221
|
+
block_number = data["params"]["result"]["number"].to_i(16) - 1
|
222
|
+
block_hash = data["params"]["result"]["parentHash"]
|
223
|
+
|
224
|
+
EM.defer(
|
225
|
+
|
226
|
+
proc {
|
227
|
+
events = get_block_events block_hash
|
228
|
+
{ block_number: block_number, events: events }
|
229
|
+
},
|
230
|
+
|
231
|
+
proc { |result|
|
232
|
+
begin
|
233
|
+
callback.call result
|
234
|
+
rescue => ex
|
235
|
+
puts ex.message
|
236
|
+
puts ex.backtrace.join("\n")
|
237
|
+
end
|
238
|
+
},
|
239
|
+
|
240
|
+
proc { |e|
|
241
|
+
puts e
|
242
|
+
}
|
243
|
+
|
244
|
+
)
|
245
|
+
end
|
92
246
|
end
|
93
247
|
|
94
|
-
# client.
|
95
|
-
# Plain: client.
|
96
|
-
# Map: client.
|
97
|
-
# DoubleMap: client.
|
98
|
-
def
|
248
|
+
# Plain: client.get_storage("Sudo", "Key")
|
249
|
+
# Plain: client.get_storage("Balances", "TotalIssuance")
|
250
|
+
# Map: client.get_storage("System", "Account", ["0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"])
|
251
|
+
# DoubleMap: client.get_storage("ImOnline", "AuthoredBlocks", [2818, "0x749ddc93a65dfec3af27cc7478212cb7d4b0c0357fef35a0163966ab5333b757"])
|
252
|
+
def get_storage(module_name, storage_name, params = nil, block_hash = nil)
|
253
|
+
self.init_runtime(block_hash: block_hash)
|
99
254
|
|
100
|
-
# TODO: uninit raise a exception
|
101
255
|
# find the storage item from metadata
|
102
|
-
metadata_modules = metadata[:modules]
|
256
|
+
metadata_modules = metadata.value.value[:metadata][:modules]
|
103
257
|
metadata_module = metadata_modules.detect { |mm| mm[:name] == module_name }
|
104
258
|
raise "Module '#{module_name}' not exist" unless metadata_module
|
105
|
-
storage_item = metadata_module[:storage][:items].detect { |item| item[:name] ==
|
106
|
-
raise "Storage item '#{
|
259
|
+
storage_item = metadata_module[:storage][:items].detect { |item| item[:name] == storage_name }
|
260
|
+
raise "Storage item '#{storage_name}' not exist. \n#{metadata_module.inspect}" unless storage_item
|
107
261
|
|
108
262
|
if storage_item[:type][:Plain]
|
109
263
|
return_type = storage_item[:type][:Plain]
|
@@ -129,48 +283,66 @@ class SubstrateClient
|
|
129
283
|
|
130
284
|
storage_hash = SubstrateClient.generate_storage_hash(
|
131
285
|
module_name,
|
132
|
-
|
286
|
+
storage_name,
|
133
287
|
params,
|
134
288
|
hasher,
|
135
289
|
hasher2,
|
136
|
-
metadata[:version]
|
290
|
+
metadata.value.value[:metadata][:version]
|
137
291
|
)
|
138
292
|
|
139
|
-
|
140
|
-
|
141
|
-
result = self.state_get_storage_at(storage_hash, block_hash)
|
293
|
+
result = self.state_get_storage(storage_hash, block_hash)
|
142
294
|
return unless result
|
143
|
-
Scale::Types.get(return_type).decode(Scale::Bytes.new(result))
|
144
|
-
|
145
|
-
|
146
|
-
|
295
|
+
Scale::Types.get(return_type).decode(Scale::Bytes.new(result))
|
296
|
+
end
|
297
|
+
|
298
|
+
# compose_call "Balances", "Transfer", { dest: "0x586cb27c291c813ce74e86a60dad270609abf2fc8bee107e44a80ac00225c409", value: 1_000_000_000_000 }
|
299
|
+
def compose_call(module_name, call_name, params, block_hash=nil)
|
300
|
+
self.init_runtime(block_hash: block_hash)
|
301
|
+
|
302
|
+
call = metadata.get_module_call(module_name, call_name)
|
303
|
+
|
304
|
+
value = {
|
305
|
+
call_index: call[:lookup],
|
306
|
+
module_name: module_name,
|
307
|
+
call_name: call_name,
|
308
|
+
params: []
|
309
|
+
}
|
310
|
+
|
311
|
+
params.keys.each_with_index do |call_param_name, i|
|
312
|
+
param_value = params[call_param_name]
|
313
|
+
value[:params] << {
|
314
|
+
name: call_param_name.to_s,
|
315
|
+
type: call[:args][i][:type],
|
316
|
+
value: param_value
|
317
|
+
}
|
318
|
+
end
|
319
|
+
|
320
|
+
Scale::Types::Extrinsic.new(value).encode
|
147
321
|
end
|
148
322
|
|
149
323
|
class << self
|
150
|
-
def generate_storage_hash(
|
324
|
+
def generate_storage_hash(module_name, storage_name, params = nil, hasher = nil, hasher2 = nil, metadata_version = nil)
|
151
325
|
if metadata_version and metadata_version >= 9
|
152
|
-
storage_hash = Crypto.twox128(
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
raise "Unexpected third parameter for storage call"
|
162
|
-
end
|
163
|
-
|
164
|
-
param_key = param.hex_to_bytes
|
165
|
-
param_hasher = "Twox128" if param_hasher.nil?
|
166
|
-
storage_hash += Crypto.send param_hasher.underscore, param_key
|
326
|
+
storage_hash = Crypto.twox128(module_name) + Crypto.twox128(storage_name)
|
327
|
+
|
328
|
+
params&.each_with_index do |param, index|
|
329
|
+
if index == 0
|
330
|
+
param_hasher = hasher
|
331
|
+
elsif index == 1
|
332
|
+
param_hasher = hasher2
|
333
|
+
else
|
334
|
+
raise "Unexpected third parameter for storage call"
|
167
335
|
end
|
336
|
+
|
337
|
+
param_key = param.hex_to_bytes
|
338
|
+
param_hasher = "Twox128" if param_hasher.nil?
|
339
|
+
storage_hash += Crypto.send param_hasher.underscore, param_key
|
168
340
|
end
|
169
341
|
|
170
342
|
"0x#{storage_hash}"
|
171
343
|
else
|
172
344
|
# TODO: add test
|
173
|
-
storage_hash =
|
345
|
+
storage_hash = module_name + " " + storage_name
|
174
346
|
|
175
347
|
unless params.nil?
|
176
348
|
params = [params] if params.class != ::Array
|
@@ -193,6 +365,57 @@ class SubstrateClient
|
|
193
365
|
|
194
366
|
end
|
195
367
|
|
368
|
+
private
|
369
|
+
def init_ws
|
370
|
+
queue = Queue.new
|
196
371
|
|
197
|
-
|
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
|
198
386
|
|
387
|
+
sleep(3)
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
def start_connection
|
394
|
+
@callbacks = {}
|
395
|
+
@subscription_callbacks = {}
|
396
|
+
|
397
|
+
@ws = Faye::WebSocket::Client.new(@url)
|
398
|
+
@ws.on :message do |event|
|
399
|
+
# p [:message, event.data]
|
400
|
+
if event.data.include?("jsonrpc")
|
401
|
+
begin
|
402
|
+
data = JSON.parse event.data
|
403
|
+
|
404
|
+
if data["params"]
|
405
|
+
if @subscription_callbacks[data["params"]["subscription"]]
|
406
|
+
@subscription_callbacks[data["params"]["subscription"]].call data
|
407
|
+
end
|
408
|
+
else
|
409
|
+
@callbacks[data["id"]].call data
|
410
|
+
@callbacks.delete(data["id"])
|
411
|
+
end
|
412
|
+
|
413
|
+
rescue => ex
|
414
|
+
puts ex.message
|
415
|
+
puts ex.backtrace.join("\n")
|
416
|
+
end
|
417
|
+
end
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
end
|
data/substrate_client.gemspec
CHANGED
@@ -36,11 +36,10 @@ Gem::Specification.new do |spec|
|
|
36
36
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
37
37
|
spec.require_paths = ["lib"]
|
38
38
|
|
39
|
-
spec.add_dependency "substrate_common.rb", "~> 0.1.8"
|
40
39
|
spec.add_dependency "faye-websocket", "~> 0.10.9"
|
41
40
|
spec.add_dependency "eventmachine", "~> 1.2.7"
|
42
41
|
spec.add_dependency "activesupport", "~> 5.2.4"
|
43
|
-
spec.add_dependency "scale.rb", "~> 0.2.
|
42
|
+
spec.add_dependency "scale.rb", "~> 0.2.5"
|
44
43
|
|
45
44
|
spec.add_development_dependency "bundler", "~> 1.17"
|
46
45
|
spec.add_development_dependency "rake", ">= 12.3.3"
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
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.4
|
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-
|
11
|
+
date: 2020-05-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: substrate_common.rb
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: 0.1.8
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: 0.1.8
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: faye-websocket
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -72,14 +58,14 @@ dependencies:
|
|
72
58
|
requirements:
|
73
59
|
- - "~>"
|
74
60
|
- !ruby/object:Gem::Version
|
75
|
-
version: 0.2.
|
61
|
+
version: 0.2.5
|
76
62
|
type: :runtime
|
77
63
|
prerelease: false
|
78
64
|
version_requirements: !ruby/object:Gem::Requirement
|
79
65
|
requirements:
|
80
66
|
- - "~>"
|
81
67
|
- !ruby/object:Gem::Version
|
82
|
-
version: 0.2.
|
68
|
+
version: 0.2.5
|
83
69
|
- !ruby/object:Gem::Dependency
|
84
70
|
name: bundler
|
85
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -141,7 +127,6 @@ email:
|
|
141
127
|
- wuminzhe@gmail.com
|
142
128
|
executables:
|
143
129
|
- metadata
|
144
|
-
- substrate_client
|
145
130
|
extensions: []
|
146
131
|
extra_rdoc_files: []
|
147
132
|
files:
|
@@ -157,7 +142,6 @@ files:
|
|
157
142
|
- bin/console
|
158
143
|
- bin/setup
|
159
144
|
- exe/metadata
|
160
|
-
- exe/substrate_client
|
161
145
|
- lib/substrate_client.rb
|
162
146
|
- lib/substrate_client/version.rb
|
163
147
|
- substrate_client.gemspec
|
data/exe/substrate_client
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "substrate_client"
|
4
|
-
require "json"
|
5
|
-
|
6
|
-
client = SubstrateClient.new("wss://cc3-5.kusama.network/")
|
7
|
-
# puts client.system_name
|
8
|
-
# puts client.system_chain
|
9
|
-
# puts client.system_version
|
10
|
-
# puts client.system_peers
|
11
|
-
# puts client.state_getStorage "0x0b76934f4cc08dee01012d059e1b83ee5e0621c4869aa60c02be9adcc98a0d1d"
|
12
|
-
# puts client.state_getChildKeys "0x","0x",1,"0x"
|
13
|
-
# puts client.runtime_getState "System", "Events",[],"0xe0303fbe2bc8482e06fb649f934c41349d12a25bd618c92e3331a3347e434f7b"
|
14
|
-
# puts client.rpc_methods
|
15
|
-
|
16
|
-
client.init
|
17
|
-
metadata = client.metadata
|
18
|
-
puts JSON.pretty_generate(metadata)
|