substrate_client.rb 0.1.7 → 0.1.8
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 +16 -18
- data/README.md +91 -189
- data/exe/metadata +3 -3
- data/lib/helper.rb +12 -22
- data/lib/substrate_client.rb +79 -253
- data/lib/substrate_client/version.rb +1 -1
- data/substrate_client.gemspec +3 -3
- metadata +14 -17
- data/lib/substrate_client_sync.rb +0 -190
- data/lib/timeout_queue.rb +0 -34
- data/lib/websocket.rb +0 -79
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bb233970cdc1b51057d436d0ffc2ae411b99ce3c2e5c51ba3dfb5dcdae0fc960
|
4
|
+
data.tar.gz: 35750c213d2fbf7ae223cdd7b9f7a8bc40c7c7b8fab6a09603a0604db6533562
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5731212fc4ad5791bb607f132e660b89f7ff652937029e986040aeee85335939d5a5edf9dd9a21a91f346c2b88711657586747a01727f35eae8318b9b944a62f
|
7
|
+
data.tar.gz: 69514b6b9b3eb085f7fefc672dff0b3a85cdfb64babfad28f6ab788f98ee950e2a2e9fcf2e89ab448b7e8c7137da20ba6ec0e68d375ece1b9c41e5f77d3d6964
|
data/Gemfile.lock
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
substrate_client.rb (0.1.
|
4
|
+
substrate_client.rb (0.1.8)
|
5
5
|
activesupport (~> 5.2.4)
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
kontena-websocket-client (~> 0.1.1)
|
7
|
+
scale.rb (~> 0.2.11)
|
8
|
+
substrate_common.rb (~> 0.1.9)
|
9
9
|
|
10
10
|
GEM
|
11
11
|
remote: https://rubygems.org/
|
12
12
|
specs:
|
13
|
-
activesupport (5.2.4.
|
13
|
+
activesupport (5.2.4.4)
|
14
14
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
15
15
|
i18n (>= 0.7, < 2)
|
16
16
|
minitest (~> 5.1)
|
@@ -18,17 +18,15 @@ GEM
|
|
18
18
|
base58 (0.2.3)
|
19
19
|
blake2b (0.10.0)
|
20
20
|
coderay (1.1.2)
|
21
|
-
concurrent-ruby (1.1.
|
21
|
+
concurrent-ruby (1.1.7)
|
22
22
|
diff-lcs (1.3)
|
23
|
-
|
24
|
-
faye-websocket (0.10.9)
|
25
|
-
eventmachine (>= 0.12.0)
|
26
|
-
websocket-driver (>= 0.5.1)
|
27
|
-
i18n (1.8.2)
|
23
|
+
i18n (1.8.5)
|
28
24
|
concurrent-ruby (~> 1.0)
|
29
|
-
json (2.3.
|
25
|
+
json (2.3.1)
|
26
|
+
kontena-websocket-client (0.1.1)
|
27
|
+
websocket-driver (~> 0.6.5)
|
30
28
|
method_source (0.9.2)
|
31
|
-
minitest (5.14.
|
29
|
+
minitest (5.14.2)
|
32
30
|
pry (0.12.2)
|
33
31
|
coderay (~> 1.1.0)
|
34
32
|
method_source (~> 0.9.0)
|
@@ -46,12 +44,12 @@ GEM
|
|
46
44
|
diff-lcs (>= 1.2.0, < 2.0)
|
47
45
|
rspec-support (~> 3.9.0)
|
48
46
|
rspec-support (3.9.2)
|
49
|
-
scale.rb (0.2.
|
47
|
+
scale.rb (0.2.11)
|
50
48
|
activesupport (>= 4.0.0)
|
51
49
|
json (~> 2.3.0)
|
52
|
-
substrate_common.rb (~> 0.1.
|
50
|
+
substrate_common.rb (~> 0.1.9)
|
53
51
|
thor (~> 0.19.0)
|
54
|
-
substrate_common.rb (0.1.
|
52
|
+
substrate_common.rb (0.1.9)
|
55
53
|
base58
|
56
54
|
blake2b
|
57
55
|
xxhash
|
@@ -59,9 +57,9 @@ GEM
|
|
59
57
|
thread_safe (0.3.6)
|
60
58
|
tzinfo (1.2.7)
|
61
59
|
thread_safe (~> 0.1)
|
62
|
-
websocket-driver (0.
|
60
|
+
websocket-driver (0.6.5)
|
63
61
|
websocket-extensions (>= 0.1.0)
|
64
|
-
websocket-extensions (0.1.
|
62
|
+
websocket-extensions (0.1.5)
|
65
63
|
xxhash (0.4.0)
|
66
64
|
|
67
65
|
PLATFORMS
|
data/README.md
CHANGED
@@ -22,205 +22,116 @@ Or install it yourself as:
|
|
22
22
|
|
23
23
|
### Supported rpc methods
|
24
24
|
|
25
|
-
#### rpc method list
|
26
|
-
|
27
25
|
```ruby
|
28
26
|
require "substrate_client"
|
29
27
|
|
30
28
|
client = SubstrateClient.new("wss://kusama-rpc.polkadot.io/")
|
31
|
-
client.
|
32
|
-
|
33
|
-
|
29
|
+
client.methods
|
30
|
+
```
|
31
|
+
returns like:
|
32
|
+
```shell
|
33
|
+
[
|
34
|
+
"account_nextIndex",
|
35
|
+
"author_hasKey",
|
36
|
+
...
|
37
|
+
"chain_getBlock",
|
38
|
+
"chain_getBlockHash",
|
39
|
+
...
|
40
|
+
]
|
34
41
|
```
|
35
|
-
The rpc methods can be dynamically called by its name, so the methods returned by this method can all be used.
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
#### rpc methods
|
40
|
-
|
41
|
-
But, in order to show the parameters more clearly, some important or frequently used methods are hard-coded:
|
42
|
-
|
43
|
-
- chain_get_finalised_head(&callback)
|
44
|
-
|
45
|
-
Get hash of the last finalized block in the canon chain
|
46
|
-
|
47
|
-
```ruby
|
48
|
-
client.chain_get_finalised_head do |head|
|
49
|
-
p head
|
50
|
-
end
|
51
|
-
```
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
- chain_get_head(&callback)
|
56
|
-
|
57
|
-
Retrieves the header
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
- chain_get_header(block_hash = nil, &callback)
|
62
|
-
|
63
|
-
Retrieves the header for a specific block
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
- chain_get_block(block_hash = nil, &callback)
|
68
|
-
|
69
|
-
Get header and body of a relay chain block
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
- chain_get_block_hash(block_id, &callback)
|
74
|
-
|
75
|
-
Get the block hash for a specific block
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
- chain_get_runtime_version(block_hash = nil, &callback)
|
80
|
-
|
81
|
-
Get the runtime version for a specific block
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
- state_get_metadata(block_hash = nil, &callback)
|
86
|
-
|
87
|
-
Returns the runtime metadata by block
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
- state_get_storage(storage_key, block_hash = nil, &callback)
|
92
|
-
|
93
|
-
Retrieves the storage for a key
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
- system_name(&callback)
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
- system_version(&callback)
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
- chain_subscribe_all_heads(&callback)
|
106
|
-
|
107
|
-
Retrieves the newest header via subscription. This will return data continuously until you unsubscribe the subscription.
|
108
|
-
|
109
|
-
```ruby
|
110
|
-
subscription = client.chain_subscribe_all_heads do |data|
|
111
|
-
p data
|
112
|
-
end
|
113
|
-
```
|
114
|
-
|
115
|
-
- chain_unsubscribe_all_heads(subscription)
|
116
|
-
|
117
|
-
Unsubscribe newest header subscription.
|
118
|
-
|
119
|
-
```ruby
|
120
|
-
client.chain_unsubscribe_all_heads(subscription)
|
121
|
-
```
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
- chain_subscribe_new_heads(&callback)
|
126
|
-
|
127
|
-
Retrieves the best header via subscription. This will return data continuously until you unsubscribe the subscription.
|
128
|
-
|
129
|
-
- chain_unsubscribe_new_heads(subscription)
|
130
|
-
|
131
|
-
Unsubscribe the best header subscription.
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
- chain_subscribe_finalized_heads(&callback)
|
137
|
-
|
138
|
-
Retrieves the best finalized header via subscription. This will return data continuously until you unsubscribe the subscription.
|
139
|
-
|
140
|
-
- chain_unsubscribe_finalized_heads(subscription)
|
141
|
-
|
142
|
-
Unsubscribe the best finalized header subscription.
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
- state_subscribe_runtime_version(&callback)
|
147
|
-
|
148
|
-
Retrieves the runtime version via subscription.
|
149
|
-
|
150
|
-
- state_unsubscribe_runtime_version(subscription)
|
151
|
-
|
152
|
-
Unsubscribe the runtime version subscription.
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
- state_subscribe_storage(keys, &callback)
|
157
|
-
|
158
|
-
Subscribes to storage changes for the provided keys until unsubscribe.
|
159
42
|
|
160
|
-
|
161
|
-
subscription = client.state_subscribe_storage ["0x26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"] do |data|
|
162
|
-
p data
|
163
|
-
end
|
164
|
-
```
|
43
|
+
The rpc methods can be dynamically called by its name, so you can call it like:
|
165
44
|
|
166
|
-
|
45
|
+
```ruby
|
46
|
+
client.chain_getBlockHash(1024)
|
47
|
+
```
|
167
48
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
49
|
+
### Origin rpc methods
|
50
|
+
|
51
|
+
- `account_nextIndex`
|
52
|
+
* `author_hasKey`
|
53
|
+
* `author_hasSessionKeys`
|
54
|
+
* `author_insertKey`
|
55
|
+
* `author_pendingExtrinsics`
|
56
|
+
* `author_removeExtrinsic`
|
57
|
+
* `author_rotateKeys`
|
58
|
+
* `author_submitExtrinsic`
|
59
|
+
* `babe_epochAuthorship`
|
60
|
+
* `chain_getBlock(block_hash = nil)`
|
61
|
+
* `chain_getBlockHash(block_id)`
|
62
|
+
* `chain_getFinalisedHead`
|
63
|
+
* `chain_getFinalizedHead`
|
64
|
+
* `chain_getHead`
|
65
|
+
* `chain_getHeader(block_hash = nil)`
|
66
|
+
* `chain_getRuntimeVersion(block_hash = nil)`
|
67
|
+
* `childstate_getKeys`
|
68
|
+
* `childstate_getStorage`
|
69
|
+
* `childstate_getStorageHash`
|
70
|
+
* `childstate_getStorageSize`
|
71
|
+
* `grandpa_roundState`
|
72
|
+
* `offchain_localStorageGet`
|
73
|
+
* `offchain_localStorageSet`
|
74
|
+
* `payment_queryInfo`
|
75
|
+
* `state_call`
|
76
|
+
* `state_callAt`
|
77
|
+
* `state_getKeys`
|
78
|
+
* `state_getKeysPaged`
|
79
|
+
* `state_getKeysPagedAt`
|
80
|
+
* `state_getMetadata(block_hash = nil)`
|
81
|
+
* `state_getPairs`
|
82
|
+
* `state_getReadProof`
|
83
|
+
* `state_getRuntimeVersion`
|
84
|
+
* `state_getStorage(storage_key, block_hash = nil)`
|
85
|
+
* `state_getStorageAt`
|
86
|
+
* `state_getStorageHash`
|
87
|
+
* `state_getStorageHashAt`
|
88
|
+
* `state_getStorageSize`
|
89
|
+
* `state_getStorageSizeAt`
|
90
|
+
* `state_queryStorage`
|
91
|
+
* `state_queryStorageAt`
|
92
|
+
* `system_accountNextIndex`
|
93
|
+
* `system_addReservedPeer`
|
94
|
+
* `system_chain`
|
95
|
+
* `system_chainType`
|
96
|
+
* `system_dryRun`
|
97
|
+
* `system_dryRunAt`
|
98
|
+
* `system_health`
|
99
|
+
* `system_localListenAddresses`
|
100
|
+
* `system_localPeerId`
|
101
|
+
* `system_name`
|
102
|
+
* `system_networkState`
|
103
|
+
* `system_nodeRoles`
|
104
|
+
* `system_peers`
|
105
|
+
* `system_properties`
|
106
|
+
* `system_removeReservedPeer`
|
107
|
+
- `system_version`
|
108
|
+
|
109
|
+
### Wrap methods
|
172
110
|
|
173
111
|
These methods will encode the parameters and decode the returned data
|
174
112
|
|
175
|
-
- get_block_number(block_hash
|
176
|
-
|
177
|
-
- get_metadata(block_hash, &callback)
|
113
|
+
- `get_block_number(block_hash)`
|
178
114
|
|
179
|
-
-
|
115
|
+
- `get_metadata(block_hash)`
|
180
116
|
|
181
|
-
-
|
117
|
+
- `get_block(block_hash=nil)`
|
182
118
|
|
183
|
-
-
|
119
|
+
- `get_block_events(block_hash)`
|
184
120
|
|
185
|
-
- get_storage(module_name, storage_name, params = nil, block_hash = nil
|
121
|
+
- `get_storage(module_name, storage_name, params = nil, block_hash = nil)`
|
186
122
|
|
187
123
|
```ruby
|
188
|
-
client.get_storage("Balances", "TotalIssuance", nil, nil)
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
client.get_storage("System", "Account", ["0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"], nil) do |storage|
|
193
|
-
p storage
|
194
|
-
end
|
195
|
-
|
196
|
-
client.get_storage("ImOnline", "AuthoredBlocks", [2818, "0x749ddc93a65dfec3af27cc7478212cb7d4b0c0357fef35a0163966ab5333b757"], nil) do |storage|
|
197
|
-
p storage
|
198
|
-
end
|
124
|
+
client.get_storage("Balances", "TotalIssuance", nil, nil)
|
125
|
+
client.get_storage("System", "Account", ["0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"], nil)
|
126
|
+
client.get_storage("ImOnline", "AuthoredBlocks", [2818, "0x749ddc93a65dfec3af27cc7478212cb7d4b0c0357fef35a0163966ab5333b757"], nil)
|
199
127
|
```
|
200
|
-
|
201
|
-
- compose_call(module_name, call_name, params, block_hash=nil
|
128
|
+
|
129
|
+
- `compose_call(module_name, call_name, params, block_hash=nil)`
|
202
130
|
|
203
131
|
```ruby
|
204
|
-
compose_call "Balances", "Transfer", { dest: "0x586cb27c291c813ce74e86a60dad270609abf2fc8bee107e44a80ac00225c409", value: 1_000_000_000_000 }, nil
|
205
|
-
p hex
|
206
|
-
end
|
132
|
+
client.compose_call "Balances", "Transfer", { dest: "0x586cb27c291c813ce74e86a60dad270609abf2fc8bee107e44a80ac00225c409", value: 1_000_000_000_000 }, nil
|
207
133
|
```
|
208
134
|
|
209
|
-
|
210
|
-
|
211
|
-
## Synchronized client
|
212
|
-
|
213
|
-
There is also a synchronized version of the client, which is disconnected every time the data is retrieved. It is very convenient in some situations. But this version cannot use the subscription interface.
|
214
|
-
|
215
|
-
```ruby
|
216
|
-
client = SubstrateClientSync.new "wss://kusama-rpc.polkadot.io/"
|
217
|
-
p client.method_list
|
218
|
-
p client.chain_get_head
|
219
|
-
p client.chain_get_finalised_head
|
220
|
-
p client.chain_get_header(block_hash = nil)
|
221
|
-
...
|
222
|
-
```
|
223
|
-
|
224
135
|
## Docker
|
225
136
|
|
226
137
|
1. update to latest image
|
@@ -253,22 +164,13 @@ p client.chain_get_header(block_hash = nil)
|
|
253
164
|
/usr/src/app # ./bin/console
|
254
165
|
[1] pry(main)> client = SubstrateClient.new("wss://kusama-rpc.polkadot.io/")
|
255
166
|
=> #<SubstrateClient:0x000055a78f124f58 ...
|
256
|
-
[2] pry(main)> client.
|
167
|
+
[2] pry(main)> client.methods
|
257
168
|
=> ...
|
258
|
-
[3] pry(main)>
|
259
|
-
...
|
260
|
-
```
|
261
|
-
|
262
|
-
5. Or, SubstrateClientSync:
|
263
|
-
|
264
|
-
```shell
|
265
|
-
/usr/src/app # ./bin/console
|
266
|
-
[1] pry(main)> client = SubstrateClientSync.new("wss://kusama-rpc.polkadot.io/", spec_name: "kusama")
|
267
|
-
=> #<SubstrateClientSync:0x000055a78edfd6e0 @request_id=1, @spec_name="kusama", @url="wss://kusama-rpc.polkadot.io/">
|
268
|
-
[2] pry(main)> client.chain_get_head
|
169
|
+
[3] pry(main)> client.chain_getHead
|
269
170
|
=> "0xb3c3a220d4639b7c62f179f534b3a66336a115ebc18f13db053f0c57437c45fc"
|
270
171
|
```
|
271
172
|
|
173
|
+
|
272
174
|
## Development
|
273
175
|
|
274
176
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/exe/metadata
CHANGED
@@ -3,10 +3,10 @@
|
|
3
3
|
require "substrate_client"
|
4
4
|
require "json"
|
5
5
|
|
6
|
-
url = ARGV[0] || "wss://kusama-rpc.polkadot.io
|
7
|
-
client =
|
6
|
+
url = ARGV[0] || "wss://kusama-rpc.polkadot.io"
|
7
|
+
client = SubstrateClient.new(url)
|
8
8
|
|
9
|
-
block_hash = ARGV[1] || client.
|
9
|
+
block_hash = ARGV[1] || client.chain_getFinalisedHead
|
10
10
|
|
11
11
|
metadata = client.get_metadata(block_hash)
|
12
12
|
puts JSON.pretty_generate(metadata.value.to_human)
|
data/lib/helper.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
|
2
2
|
class SubstrateClient::Helper
|
3
3
|
class << self
|
4
|
-
def
|
4
|
+
def generate_storage_key_from_metadata(metadata, module_name, storage_name, params = nil)
|
5
5
|
# find the storage item from metadata
|
6
6
|
metadata_modules = metadata.value.value[:metadata][:modules]
|
7
7
|
metadata_module = metadata_modules.detect { |mm| mm[:name] == module_name }
|
@@ -18,7 +18,8 @@ class SubstrateClient::Helper
|
|
18
18
|
return_type = map[:value]
|
19
19
|
# TODO: decode to account id if param is address
|
20
20
|
# params[0] = decode(params[0]) if map[:key] == "AccountId"
|
21
|
-
|
21
|
+
type = Scale::Types.get(map[:key])
|
22
|
+
params[0] = type.new(params[0]).encode
|
22
23
|
elsif map = storage_item[:type][:DoubleMap]
|
23
24
|
raise "Storage call of type \"DoubleMapType\" requires 2 parameters" if params.nil? || params.length != 2
|
24
25
|
|
@@ -31,7 +32,7 @@ class SubstrateClient::Helper
|
|
31
32
|
raise NotImplementedError
|
32
33
|
end
|
33
34
|
|
34
|
-
|
35
|
+
storage_key = generate_storage_key(
|
35
36
|
module_name,
|
36
37
|
storage_name,
|
37
38
|
params,
|
@@ -39,12 +40,12 @@ class SubstrateClient::Helper
|
|
39
40
|
hasher2,
|
40
41
|
metadata.value.value[:metadata][:version]
|
41
42
|
)
|
42
|
-
[
|
43
|
+
[storage_key, return_type]
|
43
44
|
end
|
44
45
|
|
45
|
-
def
|
46
|
+
def generate_storage_key(module_name, storage_name, params = nil, hasher = nil, hasher2 = nil, metadata_version = nil)
|
46
47
|
if metadata_version and metadata_version >= 9
|
47
|
-
|
48
|
+
storage_key = Crypto.twox128(module_name) + Crypto.twox128(storage_name)
|
48
49
|
|
49
50
|
params&.each_with_index do |param, index|
|
50
51
|
if index == 0
|
@@ -57,22 +58,22 @@ class SubstrateClient::Helper
|
|
57
58
|
|
58
59
|
param_key = param.hex_to_bytes
|
59
60
|
param_hasher = "Twox128" if param_hasher.nil?
|
60
|
-
|
61
|
+
storage_key += Crypto.send param_hasher.underscore, param_key
|
61
62
|
end
|
62
63
|
|
63
|
-
"0x#{
|
64
|
+
"0x#{storage_key}"
|
64
65
|
else
|
65
66
|
# TODO: add test
|
66
|
-
|
67
|
+
storage_key = module_name + " " + storage_name
|
67
68
|
|
68
69
|
unless params.nil?
|
69
70
|
params = [params] if params.class != ::Array
|
70
71
|
params_key = params.join("")
|
71
72
|
hasher = "Twox128" if hasher.nil?
|
72
|
-
|
73
|
+
storage_key += params_key.hex_to_bytes.bytes_to_utf8
|
73
74
|
end
|
74
75
|
|
75
|
-
"0x#{Crypto.send( hasher.underscore,
|
76
|
+
"0x#{Crypto.send( hasher.underscore, storage_key )}"
|
76
77
|
end
|
77
78
|
end
|
78
79
|
|
@@ -114,16 +115,5 @@ class SubstrateClient::Helper
|
|
114
115
|
block
|
115
116
|
end
|
116
117
|
|
117
|
-
# chain_unsubscribe_runtime_version
|
118
|
-
# =>
|
119
|
-
# chain_unsubscribeRuntimeVersion
|
120
|
-
def real_method_name(method_name)
|
121
|
-
segments = method_name.to_s.split("_")
|
122
|
-
if segments.length == 1
|
123
|
-
segments[0]
|
124
|
-
else
|
125
|
-
segments[0] + "_" + segments[1] + segments[2..].map(&:capitalize).join
|
126
|
-
end
|
127
|
-
end
|
128
118
|
end
|
129
119
|
end
|
data/lib/substrate_client.rb
CHANGED
@@ -5,40 +5,42 @@ require "scale.rb"
|
|
5
5
|
require "json"
|
6
6
|
require "active_support"
|
7
7
|
require "active_support/core_ext/string"
|
8
|
-
require "websocket"
|
9
8
|
require "helper"
|
10
|
-
require
|
11
|
-
|
9
|
+
require 'kontena-websocket-client'
|
10
|
+
|
11
|
+
def ws_request(url, payload)
|
12
|
+
result = nil
|
13
|
+
Kontena::Websocket::Client.connect(url, {}) do |client|
|
14
|
+
client.send(payload.to_json)
|
15
|
+
|
16
|
+
client.read do |message|
|
17
|
+
result = JSON.parse message
|
18
|
+
client.close(1000)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
return result
|
23
|
+
rescue Kontena::Websocket::CloseError => e
|
24
|
+
raise SubstrateClient::WebsocketError, e.reason
|
25
|
+
rescue Kontena::Websocket::Error => e
|
26
|
+
raise SubstrateClient::WebsocketError, e.reason
|
27
|
+
end
|
12
28
|
|
13
29
|
class SubstrateClient
|
30
|
+
class WebsocketError < StandardError; end
|
14
31
|
class RpcError < StandardError; end
|
15
32
|
class RpcTimeout < StandardError; end
|
16
|
-
class << self
|
17
|
-
attr_accessor :logger
|
18
|
-
end
|
19
|
-
SubstrateClient.logger = Logger.new(STDOUT)
|
20
|
-
SubstrateClient.logger.level = Logger::INFO
|
21
33
|
|
22
34
|
attr_accessor :spec_name, :spec_version, :metadata
|
23
|
-
attr_accessor :ws
|
24
35
|
|
25
|
-
def initialize(url, spec_name: nil
|
36
|
+
def initialize(url, spec_name: nil)
|
26
37
|
@url = url
|
27
38
|
@request_id = 1
|
28
39
|
@spec_name = spec_name
|
29
|
-
|
30
|
-
Scale::TypeRegistry.instance.load(spec_name)
|
31
|
-
|
32
|
-
init_ws
|
33
|
-
|
34
|
-
at_exit { self.close }
|
35
|
-
end
|
36
|
-
|
37
|
-
def close
|
38
|
-
@ws.close
|
40
|
+
Scale::TypeRegistry.instance.load(spec_name: spec_name)
|
39
41
|
end
|
40
42
|
|
41
|
-
def request(method, params
|
43
|
+
def request(method, params)
|
42
44
|
payload = {
|
43
45
|
"jsonrpc" => "2.0",
|
44
46
|
"method" => method,
|
@@ -46,274 +48,98 @@ class SubstrateClient
|
|
46
48
|
"id" => @request_id
|
47
49
|
}
|
48
50
|
|
49
|
-
|
50
|
-
|
51
|
+
data = ws_request(@url, payload)
|
52
|
+
if data["error"]
|
53
|
+
raise RpcError, data["error"]
|
54
|
+
else
|
55
|
+
data["result"]
|
51
56
|
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)
|
61
|
-
@request_id += 1
|
62
57
|
end
|
63
58
|
|
64
|
-
def init_runtime(block_hash=nil
|
59
|
+
def init_runtime(block_hash=nil)
|
65
60
|
# set current runtime spec version
|
66
|
-
self.
|
67
|
-
|
68
|
-
|
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
|
61
|
+
runtime_version = self.state_getRuntimeVersion(block_hash)
|
62
|
+
@spec_version = runtime_version["specVersion"]
|
63
|
+
Scale::TypeRegistry.instance.spec_version = @spec_version
|
78
64
|
|
79
|
-
|
65
|
+
# set current metadata
|
66
|
+
@metadata = self.get_metadata(block_hash)
|
67
|
+
Scale::TypeRegistry.instance.metadata = @metadata.value
|
68
|
+
true
|
80
69
|
end
|
81
70
|
|
82
|
-
def invoke(method, params
|
83
|
-
|
84
|
-
|
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)
|
71
|
+
def invoke(method, *params)
|
72
|
+
# params.reject! { |param| param.nil? }
|
73
|
+
request(method, params)
|
94
74
|
end
|
95
75
|
|
96
76
|
# ################################################
|
97
77
|
# origin rpc methods
|
98
78
|
# ################################################
|
99
|
-
def method_missing(method, args
|
100
|
-
|
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
|
79
|
+
def method_missing(method, *args)
|
80
|
+
invoke method, *args
|
190
81
|
end
|
191
82
|
|
192
83
|
# ################################################
|
193
84
|
# custom methods based on origin rpc methods
|
194
85
|
# ################################################
|
195
|
-
def
|
196
|
-
|
197
|
-
callback.call result["methods"].map(&:underscore)
|
198
|
-
end
|
199
|
-
end
|
200
|
-
|
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
|
86
|
+
def methods
|
87
|
+
invoke("rpc_methods")["methods"]
|
205
88
|
end
|
206
89
|
|
207
|
-
def
|
208
|
-
self.
|
209
|
-
|
210
|
-
end
|
90
|
+
def get_block_number(block_hash)
|
91
|
+
header = self.chain_getHeader(block_hash)
|
92
|
+
header["number"].to_i(16)
|
211
93
|
end
|
212
94
|
|
213
|
-
def
|
214
|
-
self.
|
215
|
-
|
216
|
-
block = Helper.decode_block block
|
217
|
-
callback.call block
|
218
|
-
end
|
219
|
-
end
|
95
|
+
def get_metadata(block_hash=nil)
|
96
|
+
hex = self.state_getMetadata(block_hash)
|
97
|
+
Scale::Types::Metadata.decode(Scale::Bytes.new(hex))
|
220
98
|
end
|
221
99
|
|
222
|
-
def
|
223
|
-
self.init_runtime(block_hash)
|
224
|
-
|
225
|
-
|
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
|
100
|
+
def get_block(block_hash=nil)
|
101
|
+
self.init_runtime(block_hash)
|
102
|
+
block = self.chain_getBlock(block_hash)
|
103
|
+
SubstrateClient::Helper.decode_block(block)
|
231
104
|
end
|
232
105
|
|
233
|
-
def
|
234
|
-
self.
|
106
|
+
def get_block_events(block_hash=nil)
|
107
|
+
self.init_runtime(block_hash)
|
235
108
|
|
236
|
-
|
237
|
-
|
109
|
+
storage_key = "0x26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"
|
110
|
+
events_data = state_getStorage storage_key, block_hash
|
238
111
|
|
239
|
-
|
240
|
-
|
241
|
-
|
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
|
250
|
-
end
|
251
|
-
},
|
112
|
+
scale_bytes = Scale::Bytes.new(events_data)
|
113
|
+
Scale::Types.get("Vec<EventRecord>").decode(scale_bytes).to_human
|
114
|
+
end
|
252
115
|
|
253
|
-
|
254
|
-
|
116
|
+
# Plain: client.get_storage("Sudo", "Key")
|
117
|
+
# Plain: client.get_storage("Balances", "TotalIssuance")
|
118
|
+
# Map: client.get_storage("System", "Account", ["0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"])
|
119
|
+
# DoubleMap: client.get_storage("ImOnline", "AuthoredBlocks", [2818, "0x749ddc93a65dfec3af27cc7478212cb7d4b0c0357fef35a0163966ab5333b757"])
|
120
|
+
def get_storage(module_name, storage_name, params = nil, block_hash = nil)
|
121
|
+
self.init_runtime(block_hash)
|
255
122
|
|
256
|
-
|
257
|
-
SubstrateClient.logger.error e
|
258
|
-
}
|
123
|
+
storage_key, return_type = SubstrateClient::Helper.generate_storage_key_from_metadata(@metadata, module_name, storage_name, params)
|
259
124
|
|
260
|
-
|
261
|
-
|
125
|
+
result = self.state_getStorage(storage_key, block_hash)
|
126
|
+
return unless result
|
127
|
+
Scale::Types.get(return_type).decode(Scale::Bytes.new(result))
|
262
128
|
end
|
263
129
|
|
264
|
-
def
|
265
|
-
self.init_runtime(block_hash)
|
266
|
-
|
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
|
275
|
-
end
|
130
|
+
def generate_storage_key(module_name, storage_name, params = nil, block_hash = nil)
|
131
|
+
self.init_runtime(block_hash)
|
132
|
+
SubstrateClient::Helper.generate_storage_key_from_metadata(@metadata, module_name, storage_name, params)
|
276
133
|
end
|
277
134
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
end
|
135
|
+
# compose_call "Balances", "Transfer", { dest: "0x586cb27c291c813ce74e86a60dad270609abf2fc8bee107e44a80ac00225c409", value: 1_000_000_000_000 }
|
136
|
+
def compose_call(module_name, call_name, params, block_hash=nil)
|
137
|
+
self.init_runtime(block_hash)
|
138
|
+
SubstrateClient::Helper.compose_call_from_metadata(@metadata, module_name, call_name, params)
|
283
139
|
end
|
284
140
|
|
285
|
-
|
286
|
-
|
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
|
308
|
-
|
309
|
-
rescue => ex
|
310
|
-
SubstrateClient.logger.error ex.message
|
311
|
-
SubstrateClient.logger.error ex.backtrace.join("\n")
|
312
|
-
end
|
313
|
-
end
|
314
|
-
end
|
315
|
-
|
316
|
-
)
|
141
|
+
def generate_storage_hash_from_data(storage_hex_data)
|
142
|
+
"0x" + Crypto.blake2_256(Scale::Bytes.new(storage_hex_data).bytes)
|
317
143
|
end
|
318
144
|
|
319
145
|
end
|
data/substrate_client.gemspec
CHANGED
@@ -36,10 +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 "faye-websocket", "~> 0.10.9"
|
40
|
-
spec.add_dependency "eventmachine", "~> 1.2.7"
|
41
39
|
spec.add_dependency "activesupport", "~> 5.2.4"
|
42
|
-
spec.add_dependency "scale.rb", "~> 0.2.
|
40
|
+
spec.add_dependency "scale.rb", "~> 0.2.11"
|
41
|
+
spec.add_dependency "kontena-websocket-client", "~> 0.1.1"
|
42
|
+
spec.add_dependency "substrate_common.rb", "~> 0.1.9"
|
43
43
|
|
44
44
|
spec.add_development_dependency "bundler", "~> 1.17"
|
45
45
|
spec.add_development_dependency "rake", ">= 12.3.3"
|
metadata
CHANGED
@@ -1,71 +1,71 @@
|
|
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.8
|
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-11-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: activesupport
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 5.2.4
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 5.2.4
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: scale.rb
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 0.2.11
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 0.2.11
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: kontena-websocket-client
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 0.1.1
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 0.1.1
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: substrate_common.rb
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 0.
|
61
|
+
version: 0.1.9
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 0.
|
68
|
+
version: 0.1.9
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: bundler
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -146,9 +146,6 @@ files:
|
|
146
146
|
- lib/helper.rb
|
147
147
|
- lib/substrate_client.rb
|
148
148
|
- lib/substrate_client/version.rb
|
149
|
-
- lib/substrate_client_sync.rb
|
150
|
-
- lib/timeout_queue.rb
|
151
|
-
- lib/websocket.rb
|
152
149
|
- substrate_client.gemspec
|
153
150
|
homepage: https://github.com/itering/substrate_client.rb
|
154
151
|
licenses:
|
@@ -1,190 +0,0 @@
|
|
1
|
-
def ws_request(url, payload)
|
2
|
-
queue = TimeoutQueue.new
|
3
|
-
|
4
|
-
thread = Thread.new do
|
5
|
-
EM.run do
|
6
|
-
ws = Faye::WebSocket::Client.new(url)
|
7
|
-
|
8
|
-
ws.on :open do |event|
|
9
|
-
ws.send(payload.to_json)
|
10
|
-
end
|
11
|
-
|
12
|
-
ws.on :message do |event|
|
13
|
-
if event.data.include?("jsonrpc")
|
14
|
-
queue << JSON.parse(event.data)
|
15
|
-
ws.close(3001, "data received")
|
16
|
-
Thread.kill thread
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
ws.on :close do |event|
|
21
|
-
ws = nil
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
queue.pop true, 10
|
27
|
-
rescue ThreadError => ex
|
28
|
-
raise SubstrateClientSync::RpcTimeout
|
29
|
-
end
|
30
|
-
|
31
|
-
class SubstrateClientSync
|
32
|
-
class RpcError < StandardError; end
|
33
|
-
class RpcTimeout < StandardError; end
|
34
|
-
|
35
|
-
attr_accessor :spec_name, :spec_version, :metadata
|
36
|
-
|
37
|
-
def initialize(url, spec_name: nil)
|
38
|
-
@url = url
|
39
|
-
@request_id = 1
|
40
|
-
@spec_name = spec_name
|
41
|
-
Scale::TypeRegistry.instance.load(spec_name)
|
42
|
-
end
|
43
|
-
|
44
|
-
def request(method, params)
|
45
|
-
payload = {
|
46
|
-
"jsonrpc" => "2.0",
|
47
|
-
"method" => method,
|
48
|
-
"params" => params,
|
49
|
-
"id" => @request_id
|
50
|
-
}
|
51
|
-
|
52
|
-
data = ws_request(@url, payload)
|
53
|
-
if data["error"]
|
54
|
-
raise RpcError, data["error"]
|
55
|
-
else
|
56
|
-
data["result"]
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def init_runtime(block_hash=nil)
|
61
|
-
# set current runtime spec version
|
62
|
-
runtime_version = self.state_get_runtime_version(block_hash)
|
63
|
-
@spec_version = runtime_version["specVersion"]
|
64
|
-
Scale::TypeRegistry.instance.spec_version = @spec_version
|
65
|
-
|
66
|
-
# set current metadata
|
67
|
-
@metadata = self.get_metadata(block_hash)
|
68
|
-
Scale::TypeRegistry.instance.metadata = @metadata.value
|
69
|
-
true
|
70
|
-
end
|
71
|
-
|
72
|
-
def invoke(method, *params)
|
73
|
-
# params.reject! { |param| param.nil? }
|
74
|
-
request(method, params)
|
75
|
-
end
|
76
|
-
|
77
|
-
def rpc_method(method_name)
|
78
|
-
SubstrateClient::Helper.real_method_name(method_name.to_s)
|
79
|
-
end
|
80
|
-
|
81
|
-
# ################################################
|
82
|
-
# origin rpc methods
|
83
|
-
# ################################################
|
84
|
-
def method_missing(method, *args)
|
85
|
-
rpc_method = SubstrateClient::Helper.real_method_name(method)
|
86
|
-
invoke rpc_method, *args
|
87
|
-
end
|
88
|
-
|
89
|
-
def state_get_runtime_version(block_hash=nil)
|
90
|
-
invoke rpc_method(__method__), block_hash
|
91
|
-
end
|
92
|
-
|
93
|
-
def rpc_methods
|
94
|
-
invoke rpc_method(__method__)
|
95
|
-
end
|
96
|
-
|
97
|
-
def chain_get_head
|
98
|
-
invoke rpc_method(__method__)
|
99
|
-
end
|
100
|
-
|
101
|
-
def chain_get_finalised_head
|
102
|
-
invoke rpc_method(__method__)
|
103
|
-
end
|
104
|
-
|
105
|
-
def chain_get_header(block_hash = nil)
|
106
|
-
invoke rpc_method(__method__), block_hash
|
107
|
-
end
|
108
|
-
|
109
|
-
def chain_get_block(block_hash = nil)
|
110
|
-
invoke rpc_method(__method__), block_hash
|
111
|
-
end
|
112
|
-
|
113
|
-
def chain_get_block_hash(block_id)
|
114
|
-
invoke rpc_method(__method__), block_id
|
115
|
-
end
|
116
|
-
|
117
|
-
def chain_get_runtime_version(block_hash = nil)
|
118
|
-
invoke rpc_method(__method__), block_hash
|
119
|
-
end
|
120
|
-
|
121
|
-
def state_get_metadata(block_hash = nil)
|
122
|
-
invoke rpc_method(__method__), block_hash
|
123
|
-
end
|
124
|
-
|
125
|
-
def state_get_storage(storage_key, block_hash = nil)
|
126
|
-
invoke rpc_method(__method__), storage_key, block_hash
|
127
|
-
end
|
128
|
-
|
129
|
-
def system_name
|
130
|
-
invoke rpc_method(__method__)
|
131
|
-
end
|
132
|
-
|
133
|
-
def system_version
|
134
|
-
invoke rpc_method(__method__)
|
135
|
-
end
|
136
|
-
|
137
|
-
# ################################################
|
138
|
-
# custom methods based on origin rpc methods
|
139
|
-
# ################################################
|
140
|
-
def method_list
|
141
|
-
self.rpc_methods["methods"].map(&:underscore)
|
142
|
-
end
|
143
|
-
|
144
|
-
def get_block_number(block_hash)
|
145
|
-
header = self.chain_get_header(block_hash)
|
146
|
-
header["number"].to_i(16)
|
147
|
-
end
|
148
|
-
|
149
|
-
def get_metadata(block_hash=nil)
|
150
|
-
hex = self.state_get_metadata(block_hash)
|
151
|
-
Scale::Types::Metadata.decode(Scale::Bytes.new(hex))
|
152
|
-
end
|
153
|
-
|
154
|
-
def get_block(block_hash=nil)
|
155
|
-
self.init_runtime(block_hash)
|
156
|
-
block = self.chain_get_block(block_hash)
|
157
|
-
SubstrateClient::Helper.decode_block(block)
|
158
|
-
end
|
159
|
-
|
160
|
-
def get_block_events(block_hash=nil)
|
161
|
-
self.init_runtime(block_hash)
|
162
|
-
|
163
|
-
storage_key = "0x26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"
|
164
|
-
events_data = state_get_storage storage_key, block_hash
|
165
|
-
|
166
|
-
scale_bytes = Scale::Bytes.new(events_data)
|
167
|
-
Scale::Types.get("Vec<EventRecord>").decode(scale_bytes).to_human
|
168
|
-
end
|
169
|
-
|
170
|
-
# Plain: client.get_storage("Sudo", "Key")
|
171
|
-
# Plain: client.get_storage("Balances", "TotalIssuance")
|
172
|
-
# Map: client.get_storage("System", "Account", ["0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"])
|
173
|
-
# DoubleMap: client.get_storage("ImOnline", "AuthoredBlocks", [2818, "0x749ddc93a65dfec3af27cc7478212cb7d4b0c0357fef35a0163966ab5333b757"])
|
174
|
-
def get_storage(module_name, storage_name, params = nil, block_hash = nil)
|
175
|
-
self.init_runtime(block_hash)
|
176
|
-
|
177
|
-
storage_hash, return_type = SubstrateClient::Helper.generate_storage_hash_from_metadata(@metadata, module_name, storage_name, params)
|
178
|
-
|
179
|
-
result = self.state_get_storage(storage_hash, block_hash)
|
180
|
-
return unless result
|
181
|
-
Scale::Types.get(return_type).decode(Scale::Bytes.new(result))
|
182
|
-
end
|
183
|
-
|
184
|
-
# compose_call "Balances", "Transfer", { dest: "0x586cb27c291c813ce74e86a60dad270609abf2fc8bee107e44a80ac00225c409", value: 1_000_000_000_000 }
|
185
|
-
def compose_call(module_name, call_name, params, block_hash=nil)
|
186
|
-
self.init_runtime(block_hash)
|
187
|
-
SubstrateClient::Helper.compose_call_from_metadata(@metadata, module_name, call_name, params)
|
188
|
-
end
|
189
|
-
|
190
|
-
end
|
data/lib/timeout_queue.rb
DELETED
@@ -1,34 +0,0 @@
|
|
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
DELETED
@@ -1,79 +0,0 @@
|
|
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
|