substrate_client.rb 0.1.3 → 0.1.4
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/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)
|