scale_rb 0.4.2 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.devcontainer/devcontainer.json +21 -0
- data/Dockerfile +16 -0
- data/Gemfile +4 -4
- data/README.md +19 -7
- data/bin/console +0 -0
- data/bin/setup +0 -0
- data/examples/http_client_2.rb +0 -2
- data/exe/metadata +9 -11
- data/lib/address.rb +1 -1
- data/lib/custom_assign.rb +92 -0
- data/lib/scale_rb/call_helper.rb +42 -0
- data/lib/{client → scale_rb/client}/client_ext.rb +12 -13
- data/lib/{client → scale_rb/client}/http_client.rb +1 -1
- data/lib/{client → scale_rb/client}/ws_client.rb +11 -15
- data/lib/scale_rb/codec.rb +25 -0
- data/lib/scale_rb/codec_utils.rb +128 -0
- data/lib/scale_rb/decode.rb +164 -0
- data/lib/scale_rb/encode.rb +150 -0
- data/lib/{hasher.rb → scale_rb/hasher.rb} +10 -8
- data/lib/scale_rb/metadata/metadata.rb +114 -0
- data/lib/{metadata → scale_rb/metadata}/metadata_v10.rb +0 -17
- data/lib/{metadata → scale_rb/metadata}/metadata_v11.rb +0 -17
- data/lib/{metadata → scale_rb/metadata}/metadata_v12.rb +0 -17
- data/lib/{metadata → scale_rb/metadata}/metadata_v13.rb +0 -17
- data/lib/{metadata → scale_rb/metadata}/metadata_v14.rb +18 -18
- data/lib/{metadata → scale_rb/metadata}/metadata_v9.rb +0 -17
- data/lib/scale_rb/metadata/registry.rb +263 -0
- data/lib/scale_rb/metadata/type_exp.rb +286 -0
- data/lib/scale_rb/portable_registry.rb +133 -0
- data/lib/{storage_helper.rb → scale_rb/storage_helper.rb} +16 -4
- data/lib/scale_rb/types.rb +233 -0
- data/lib/scale_rb/utils.rb +125 -0
- data/lib/scale_rb/version.rb +1 -1
- data/lib/scale_rb.rb +20 -26
- data/lib/type_enforcer.rb +170 -0
- data/scale_rb.gemspec +3 -0
- metadata +57 -19
- data/lib/codec.rb +0 -450
- data/lib/metadata/metadata.rb +0 -137
- data/lib/monkey_patching.rb +0 -115
- data/lib/portable_codec.rb +0 -285
- data/lib/registry.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cb19be98106d9f8331a9be9812f6252c23d6913cbd98cedcae34bf1a464e7b90
|
4
|
+
data.tar.gz: cff1e034d85f130ed7d149628647f020adb9ffc532d9646bceda054007d6bf14
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: de658214901aa13071c6b8d0c1aa94c8db0b2cee3c6c1df3207eb0881399c12215ce6c05a7d89c7da115e279a60fbe4688710e5f3fba827d5bd729afb7275e59
|
7
|
+
data.tar.gz: 999f5311c1a66933281798c40bbb3b7956e528b3164c264f2a7694fa48c142683e49ff47a185c1e174501b2e8c3b083d2bd513d1e6c37cffff9002d9186908e4
|
@@ -0,0 +1,21 @@
|
|
1
|
+
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
2
|
+
// README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-dockerfile
|
3
|
+
{
|
4
|
+
"name": "scale_rb",
|
5
|
+
"build": {
|
6
|
+
// Sets the run context to one level up instead of the .devcontainer folder.
|
7
|
+
"context": "..",
|
8
|
+
// Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename.
|
9
|
+
"dockerfile": "../Dockerfile"
|
10
|
+
}
|
11
|
+
// Features to add to the dev container. More info: https://containers.dev/features.
|
12
|
+
// "features": {},
|
13
|
+
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
14
|
+
// "forwardPorts": [],
|
15
|
+
// Uncomment the next line to run commands after the container is created.
|
16
|
+
// "postCreateCommand": "cat /etc/os-release",
|
17
|
+
// Configure tool-specific properties.
|
18
|
+
// "customizations": {},
|
19
|
+
// Uncomment to connect as an existing user other than the container default. More info: https://aka.ms/dev-containers-non-root.
|
20
|
+
// "remoteUser": "devcontainer"
|
21
|
+
}
|
data/Dockerfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
FROM ruby:3.1
|
2
|
+
|
3
|
+
# Dependencies
|
4
|
+
RUN apt-get update -qq && apt-get install -y build-essential
|
5
|
+
RUN curl https://sh.rustup.rs -sSf | bash -s -- -y
|
6
|
+
ENV PATH="/root/.cargo/bin:${PATH}"
|
7
|
+
|
8
|
+
# Set the working directory
|
9
|
+
WORKDIR /scale_rb
|
10
|
+
COPY . /scale_rb
|
11
|
+
|
12
|
+
# Intall gems
|
13
|
+
RUN bundle install
|
14
|
+
|
15
|
+
# default command is to run a shell
|
16
|
+
CMD ["bash"]
|
data/Gemfile
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
source
|
1
|
+
source 'https://rubygems.org'
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in scale_rb.gemspec
|
4
4
|
gemspec
|
5
5
|
|
6
|
-
gem
|
7
|
-
gem
|
8
|
-
gem 'rubocop', group: 'development', require: false
|
6
|
+
gem 'rake', '~> 12.0'
|
7
|
+
gem 'rspec', '~> 3.0'
|
8
|
+
gem 'rubocop', group: 'development', require: false
|
data/README.md
CHANGED
@@ -1,13 +1,11 @@
|
|
1
1
|
# ScaleRb
|
2
2
|
|
3
|
-
It is still under heavy development. Use the latest version.
|
4
|
-
|
5
3
|
## Installation
|
6
4
|
|
7
5
|
Add this line to your application's Gemfile:
|
8
6
|
|
9
7
|
```ruby
|
10
|
-
gem
|
8
|
+
gem "scale_rb"
|
11
9
|
```
|
12
10
|
|
13
11
|
And then execute:
|
@@ -25,20 +23,34 @@ git clone https://github.com/wuminzhe/scale_rb.git
|
|
25
23
|
cd scale_rb
|
26
24
|
bundle install
|
27
25
|
bundle exec ruby examples/http_client_1.rb
|
28
|
-
# CONSOLE_LEVEL=debug bundle exec ruby examples/http_client_1.rb
|
29
26
|
```
|
30
27
|
|
31
28
|
## Development
|
32
29
|
|
33
|
-
|
30
|
+
### Run devcontainer
|
31
|
+
|
32
|
+
Open the project in vscode, then in the command palette, type `Reopen in Container` to open the project in a devcontainer.
|
33
|
+
|
34
|
+
![image](https://github.com/user-attachments/assets/39af785c-5570-46df-9e6e-bf816e7f7b68)
|
35
|
+
|
34
36
|
|
35
|
-
|
37
|
+
After the devcontainer is opened, you can run the following commands:
|
38
|
+
|
39
|
+
1. Tests:
|
40
|
+
|
41
|
+
```bash
|
42
|
+
bundle exec rspec
|
43
|
+
```
|
44
|
+
|
45
|
+
2. Examples:
|
46
|
+
```bash
|
47
|
+
bundle exec ruby examples/http_client_1.rb
|
48
|
+
```
|
36
49
|
|
37
50
|
## Contributing
|
38
51
|
|
39
52
|
Bug reports and pull requests are welcome on GitHub at https://github.com/wuminzhe/scale_rb. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/wuminzhe/scale_rb/blob/master/CODE_OF_CONDUCT.md).
|
40
53
|
|
41
|
-
|
42
54
|
## License
|
43
55
|
|
44
56
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/bin/console
CHANGED
File without changes
|
data/bin/setup
CHANGED
File without changes
|
data/examples/http_client_2.rb
CHANGED
@@ -14,7 +14,5 @@ def fetch_some_storages(client, block_number)
|
|
14
14
|
puts "Time taken: #{end_time - start_time} seconds"
|
15
15
|
end
|
16
16
|
|
17
|
-
ScaleRb.logger.level = Logger::DEBUG
|
18
|
-
|
19
17
|
client = ScaleRb::HttpClient.new('https://polkadot-rpc.dwellir.com')
|
20
18
|
fetch_some_storages(client, 21711742)
|
data/exe/metadata
CHANGED
@@ -5,13 +5,9 @@ require 'scale_rb'
|
|
5
5
|
require 'json'
|
6
6
|
require 'optparse'
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
# ./bin/metadata http://g2.dev.darwinia.network:2234 -b 0x23ebddd6519aaf1b7fc916c3709af13d7a4010943fb53038406581171000a58e
|
13
|
-
# ./bin/metadata https://rpc.darwinia.network -b 10511703
|
14
|
-
# ./bin/metadata https://pangoro-rpc.darwinia.network
|
8
|
+
# ./exe/metadata https://polkadot-rpc.dwellir.com -b 0xfc2d86c5a2cc92b7770dcc14e74116912c9f848000b5f4ceca6fe1475588692c
|
9
|
+
# ./exe/metadata https://polkadot-rpc.dwellir.com -b 22931689
|
10
|
+
# ./exe/metadata https://polkadot-rpc.dwellir.com
|
15
11
|
@options = {}
|
16
12
|
parser = OptionParser.new do |opts|
|
17
13
|
opts.banner = 'Usage: metadata [url] [options]'
|
@@ -26,16 +22,18 @@ else
|
|
26
22
|
parser.parse!
|
27
23
|
url = ARGV[0]
|
28
24
|
|
25
|
+
client = ScaleRb::HttpClient.new(url)
|
26
|
+
|
27
|
+
block_hash = nil
|
29
28
|
if @options[:block]
|
30
29
|
if @options[:block].length == 66
|
31
30
|
block_hash = @options[:block]
|
32
31
|
else
|
33
32
|
block_number = @options[:block].to_i
|
34
|
-
block_hash =
|
33
|
+
block_hash = client.chain_getBlockHash(block_number)
|
35
34
|
end
|
36
|
-
else
|
37
|
-
block_hash = ScaleRb::HttpClient.chain_getBlockHash url
|
38
35
|
end
|
39
36
|
|
40
|
-
|
37
|
+
metadata = client.get_metadata(block_hash)
|
38
|
+
puts JSON.pretty_generate(metadata)
|
41
39
|
end
|
data/lib/address.rb
CHANGED
@@ -79,7 +79,7 @@ module ScaleRb
|
|
79
79
|
end
|
80
80
|
|
81
81
|
input_bytes = ss58_format_bytes.bytes + pubkey_bytes
|
82
|
-
checksum = Blake2b.hex(SS58_PREFIX.bytes + input_bytes, 64)
|
82
|
+
checksum = Utils.hex_to_u8a(Blake2b.hex(SS58_PREFIX.bytes + input_bytes, 64))
|
83
83
|
|
84
84
|
Base58.binary_to_base58((input_bytes + checksum[0...checksum_length]).pack('C*'), :bitcoin)
|
85
85
|
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
def custom_assign(positional_params, keyword_params, args, kwargs = {}, defaults = {})
|
2
|
+
assigned = {}
|
3
|
+
|
4
|
+
# Handle positional arguments (with optional defaults)
|
5
|
+
raise ArgumentError, 'Too many positional arguments' if args.length > positional_params.length
|
6
|
+
|
7
|
+
positional_params.each_with_index do |param, index|
|
8
|
+
if args[index]
|
9
|
+
assigned[param] = args[index] # Assign from args if available
|
10
|
+
elsif args.length == positional_params.length
|
11
|
+
assigned[param] = nil
|
12
|
+
elsif defaults.key?(param)
|
13
|
+
assigned[param] = defaults[param] # Assign default if no argument is provided
|
14
|
+
else
|
15
|
+
raise ArgumentError, "Missing required positional argument: #{param}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Handle keyword arguments (with optional defaults)
|
20
|
+
kwargs.each_key do |key|
|
21
|
+
raise ArgumentError, "Unknown keyword argument: #{key}" unless keyword_params.include?(key)
|
22
|
+
end
|
23
|
+
|
24
|
+
keyword_params.each do |key|
|
25
|
+
if kwargs.key?(key)
|
26
|
+
assigned[key] = kwargs[key] # Assign from kwargs if available
|
27
|
+
elsif defaults.key?(key)
|
28
|
+
assigned[key] = defaults[key] # Assign default if not provided
|
29
|
+
else
|
30
|
+
raise ArgumentError, "Missing required keyword argument: #{key}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# assert_equal(positional_params.length + keyword_params.length, assigned.length)
|
35
|
+
assigned
|
36
|
+
end
|
37
|
+
|
38
|
+
def get_method_params(method)
|
39
|
+
params = method.parameters
|
40
|
+
|
41
|
+
positional_params = []
|
42
|
+
keyword_params = []
|
43
|
+
|
44
|
+
params.each do |type, name|
|
45
|
+
case type
|
46
|
+
when :req, :opt # Required, optional positional arguments
|
47
|
+
positional_params << name
|
48
|
+
when :key, :keyreq # Keyword arguments
|
49
|
+
keyword_params << name
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
[positional_params, keyword_params]
|
54
|
+
end
|
55
|
+
|
56
|
+
def assert_equal(expected, actual)
|
57
|
+
raise "Expected #{expected}, but got #{actual}" unless expected === actual
|
58
|
+
end
|
59
|
+
|
60
|
+
def build_assigned_params(method, defaults, args, kwargs)
|
61
|
+
positional_params, keyword_params = get_method_params(method)
|
62
|
+
custom_assign(positional_params, keyword_params, args, kwargs, defaults)
|
63
|
+
end
|
64
|
+
|
65
|
+
# def my_method(a, b = 2, c:, d: 4); end
|
66
|
+
|
67
|
+
# defaults = { b: 2, d: 4 }
|
68
|
+
|
69
|
+
# # my_method(5, c: 3)
|
70
|
+
# p build_assigned_params(:my_method, defaults, 5, c: 3)
|
71
|
+
# # => {:a=>5, :b=>2, :c=>3, :d=>4}
|
72
|
+
|
73
|
+
# # my_method(5, 6, c: 3)
|
74
|
+
# p build_assigned_params(:my_method, defaults, 5, 6, c: 3)
|
75
|
+
# # => {:a=>5, :b=>6, :c=>3, :d=>4}
|
76
|
+
|
77
|
+
# # my_method(5, 6, c: 3, d: 10)
|
78
|
+
# p build_assigned_params(:my_method, defaults, 5, 6, c: 3, d: 10)
|
79
|
+
# # => {:a=>5, :b=>6, :c=>3, :d=>10}
|
80
|
+
|
81
|
+
# # my_method(5, 6, c: 3, d: 10, e: 11)
|
82
|
+
# begin
|
83
|
+
# build_assigned_params(:my_method, defaults, 5, 6, c: 3, d: 10, e: 11)
|
84
|
+
# rescue ArgumentError => e
|
85
|
+
# puts e.message # => "Unknown keyword argument: e"
|
86
|
+
# end
|
87
|
+
|
88
|
+
# begin
|
89
|
+
# build_assigned_params(:my_method, defaults, 5, 6, 7, c: 3, d: 10)
|
90
|
+
# rescue ArgumentError => e
|
91
|
+
# puts e.message # => "Too many positional arguments"
|
92
|
+
# end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ScaleRb
|
4
|
+
module CallHelper
|
5
|
+
# callbytes's structure is: pallet_index + call_index + argsbytes
|
6
|
+
#
|
7
|
+
# callbytes examples:
|
8
|
+
# "0x0901"._to_bytes
|
9
|
+
# "0x05000a1287977578f888bdc1c7627781af1cc000e6ab1300004c31b8d9a798"._to_bytes
|
10
|
+
def self.decode_call(callbytes, metadata)
|
11
|
+
pallet_index = callbytes[0]
|
12
|
+
pallet = Metadata.get_module_by_index(pallet_index, metadata)
|
13
|
+
|
14
|
+
# Remove the pallet_index
|
15
|
+
# The callbytes we used below should not contain the pallet index.
|
16
|
+
# This is because the pallet index is not part of the call type.
|
17
|
+
# Its structure is: call_index + call_args
|
18
|
+
callbytes_without_pallet_index = callbytes[1..]
|
19
|
+
calls_type_id = pallet._get(:calls, :type)
|
20
|
+
decoded = Codec.decode(
|
21
|
+
calls_type_id,
|
22
|
+
callbytes_without_pallet_index,
|
23
|
+
Metadata.build_registry(metadata)
|
24
|
+
)&.first
|
25
|
+
|
26
|
+
{
|
27
|
+
pallet_name: pallet._get(:name),
|
28
|
+
call_name: decoded.is_a?(::Hash) ? decoded.keys.first.to_s : decoded.to_s,
|
29
|
+
call: decoded
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
# call examples:
|
34
|
+
# {:pallet_name=>"Deposit", :call_name=>"claim", :call=>:claim]}
|
35
|
+
# {:pallet_name=>"Balances", :call_name=>"transfer", :call=>{:transfer=>{:dest=>[10, 18, 135, 151, 117, 120, 248, 136, 189, 193, 199, 98, 119, 129, 175, 28, 192, 0, 230, 171], :value=>11000000000000000000}}]}
|
36
|
+
def self.encode_call(call, metadata)
|
37
|
+
calls_type_id = Metadata.get_calls_type_id(call[:pallet_name], metadata)
|
38
|
+
pallet_index = Metadata.get_module(call[:pallet_name], metadata)._get(:index)
|
39
|
+
[pallet_index] + Codec.encode(calls_type_id, call[:call], Metadata.build_registry(metadata))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -1,12 +1,11 @@
|
|
1
1
|
module ScaleRb
|
2
|
-
|
3
2
|
# This module is used to add extra methods to both the ScaleRb::WsClient ScaleRb::HttpClient
|
4
3
|
module ClientExt
|
5
4
|
# get decoded metadata at block_hash
|
6
5
|
def get_metadata(block_hash = nil)
|
7
6
|
block_hash ||= chain_getHead
|
8
7
|
metadata_hex = state_getMetadata(block_hash)
|
9
|
-
|
8
|
+
Metadata.decode_metadata(metadata_hex)
|
10
9
|
end
|
11
10
|
|
12
11
|
# Get decoded storage at block_hash
|
@@ -39,8 +38,8 @@ module ScaleRb
|
|
39
38
|
item[:changes].map do |change|
|
40
39
|
storage_key = change[0]
|
41
40
|
data = change[1] || default
|
42
|
-
storage = data.nil? ? nil :
|
43
|
-
{ storage_key
|
41
|
+
storage = data.nil? ? nil : Codec.decode(type_id, Utils.hex_to_u8a(data), registry)[0]
|
42
|
+
{ storage_key:, storage: }
|
44
43
|
end
|
45
44
|
end.flatten
|
46
45
|
end
|
@@ -83,7 +82,7 @@ module ScaleRb
|
|
83
82
|
# 'System',
|
84
83
|
# 'Account',
|
85
84
|
# key = {
|
86
|
-
# value: [['0x724d50824542b56f422588421643c4a162b90b5416ef063f2266a1eae6651641'
|
85
|
+
# value: [[Utils.hex_to_u8a('0x724d50824542b56f422588421643c4a162b90b5416ef063f2266a1eae6651641')]], # [AccountId]
|
87
86
|
# type: 0,
|
88
87
|
# hashers: ['Blake2128Concat']
|
89
88
|
# },
|
@@ -101,7 +100,7 @@ module ScaleRb
|
|
101
100
|
if key
|
102
101
|
if key[:value].nil? || key[:value].empty?
|
103
102
|
# map, but no key's value provided. get all storages under the partial storage key
|
104
|
-
partial_storage_key = StorageHelper.encode_storage_key(pallet_name, item_name)
|
103
|
+
partial_storage_key = Utils.u8a_to_hex(StorageHelper.encode_storage_key(pallet_name, item_name))
|
105
104
|
get_storages_by_partial_key(
|
106
105
|
block_hash,
|
107
106
|
partial_storage_key,
|
@@ -111,7 +110,8 @@ module ScaleRb
|
|
111
110
|
)
|
112
111
|
elsif key[:value].length != key[:hashers].length
|
113
112
|
# map with multi parts, but not have all values
|
114
|
-
partial_storage_key = StorageHelper.encode_storage_key(pallet_name, item_name, key,
|
113
|
+
partial_storage_key = Utils.u8a_to_hex(StorageHelper.encode_storage_key(pallet_name, item_name, key,
|
114
|
+
registry))
|
115
115
|
get_storages_by_partial_key(
|
116
116
|
block_hash,
|
117
117
|
partial_storage_key,
|
@@ -120,12 +120,12 @@ module ScaleRb
|
|
120
120
|
registry
|
121
121
|
)
|
122
122
|
else
|
123
|
-
storage_key = StorageHelper.encode_storage_key(pallet_name, item_name, key, registry)
|
123
|
+
storage_key = Utils.u8a_to_hex(StorageHelper.encode_storage_key(pallet_name, item_name, key, registry))
|
124
124
|
data = state_getStorage(storage_key, block_hash)
|
125
125
|
StorageHelper.decode_storage(data, value[:type], value[:modifier] == 'Optional', value[:fallback], registry)
|
126
126
|
end
|
127
127
|
else
|
128
|
-
storage_key = StorageHelper.encode_storage_key(pallet_name, item_name)
|
128
|
+
storage_key = Utils.u8a_to_hex(StorageHelper.encode_storage_key(pallet_name, item_name))
|
129
129
|
data = state_getStorage(storage_key, block_hash)
|
130
130
|
StorageHelper.decode_storage(data, value[:type], value[:modifier] == 'Optional', value[:fallback], registry)
|
131
131
|
end
|
@@ -152,12 +152,12 @@ module ScaleRb
|
|
152
152
|
if plain
|
153
153
|
[
|
154
154
|
nil,
|
155
|
-
{ type: plain, modifier
|
155
|
+
{ type: plain, modifier:, fallback: }
|
156
156
|
]
|
157
157
|
elsif map
|
158
158
|
[
|
159
159
|
{ value: params, type: map._get(:key), hashers: map._get(:hashers) },
|
160
|
-
{ type: map._get(:value), modifier
|
160
|
+
{ type: map._get(:value), modifier:, fallback: }
|
161
161
|
]
|
162
162
|
else
|
163
163
|
raise 'NoSuchStorageType'
|
@@ -175,11 +175,10 @@ module ScaleRb
|
|
175
175
|
if key.is_a?(Integer)
|
176
176
|
key.to_i
|
177
177
|
elsif key.is_a?(String) && key.start_with?('0x')
|
178
|
-
key
|
178
|
+
Utils.hex_to_u8a(key)
|
179
179
|
else
|
180
180
|
key
|
181
181
|
end
|
182
182
|
end
|
183
|
-
|
184
183
|
end
|
185
184
|
end
|
@@ -30,7 +30,7 @@ module ScaleRb
|
|
30
30
|
http.use_ssl = @uri.scheme == 'https'
|
31
31
|
|
32
32
|
request = Net::HTTP::Post.new(@uri, 'Content-Type' => 'application/json')
|
33
|
-
request.body = { jsonrpc: '2.0', method
|
33
|
+
request.body = { jsonrpc: '2.0', method:, params:, id: Time.now.to_i }.to_json
|
34
34
|
ScaleRb.logger.debug "—→ #{request.body}"
|
35
35
|
|
36
36
|
# https://docs.ruby-lang.org/en/master/Net/HTTPResponse.html
|
@@ -6,7 +6,6 @@ require_relative 'client_ext'
|
|
6
6
|
|
7
7
|
module ScaleRb
|
8
8
|
class WsClient
|
9
|
-
|
10
9
|
class << self
|
11
10
|
# @param [string] url
|
12
11
|
def start(url)
|
@@ -29,7 +28,7 @@ module ScaleRb
|
|
29
28
|
end
|
30
29
|
end
|
31
30
|
|
32
|
-
client.supported_methods = client.rpc_methods
|
31
|
+
client.supported_methods = client.rpc_methods[:methods]
|
33
32
|
yield client
|
34
33
|
|
35
34
|
recv_task.wait
|
@@ -37,7 +36,6 @@ module ScaleRb
|
|
37
36
|
recv_task&.stop
|
38
37
|
end
|
39
38
|
end
|
40
|
-
|
41
39
|
end
|
42
40
|
|
43
41
|
private
|
@@ -45,11 +43,10 @@ module ScaleRb
|
|
45
43
|
def parse_message(message)
|
46
44
|
message.parse
|
47
45
|
rescue StandardError => e
|
48
|
-
Console::Event::Failure.for(e).emit(self,
|
46
|
+
Console::Event::Failure.for(e).emit(self, 'Parse message failed!')
|
49
47
|
nil
|
50
48
|
end
|
51
49
|
end
|
52
|
-
|
53
50
|
end
|
54
51
|
end
|
55
52
|
|
@@ -102,9 +99,9 @@ module ScaleRb
|
|
102
99
|
def unsubscribe(method, subscription_id)
|
103
100
|
return unless method.include?('unsubscribe')
|
104
101
|
|
105
|
-
|
106
|
-
|
107
|
-
|
102
|
+
return unless @subscription_handler.unsubscribe(subscription_id)
|
103
|
+
|
104
|
+
request(method, [subscription_id])
|
108
105
|
end
|
109
106
|
|
110
107
|
def handle_response(response)
|
@@ -116,14 +113,14 @@ module ScaleRb
|
|
116
113
|
ScaleRb.logger.info "Received an unknown response: #{response}"
|
117
114
|
end
|
118
115
|
rescue StandardError => e
|
119
|
-
Console::Event::Failure.for(e).emit(self,
|
116
|
+
Console::Event::Failure.for(e).emit(self, 'Handle response failed!')
|
120
117
|
end
|
121
118
|
|
122
119
|
def read_message
|
123
120
|
loop do
|
124
121
|
return @connection.read
|
125
122
|
rescue StandardError => e
|
126
|
-
Console::Event::Failure.for(e).emit(self,
|
123
|
+
Console::Event::Failure.for(e).emit(self, 'Read message from connection failed!')
|
127
124
|
sleep 1
|
128
125
|
retry
|
129
126
|
end
|
@@ -138,7 +135,7 @@ module ScaleRb
|
|
138
135
|
response_future.resolve(response[:result])
|
139
136
|
})
|
140
137
|
|
141
|
-
request = { jsonrpc: '2.0', id: @request_id, method
|
138
|
+
request = { jsonrpc: '2.0', id: @request_id, method:, params: }
|
142
139
|
ScaleRb.logger.debug "—→ #{request}"
|
143
140
|
@connection.write(request.to_json)
|
144
141
|
|
@@ -186,10 +183,9 @@ module ScaleRb
|
|
186
183
|
subscription_id = notification.dig(:params, :subscription)
|
187
184
|
return if subscription_id.nil?
|
188
185
|
|
189
|
-
|
190
|
-
|
191
|
-
|
186
|
+
return unless @callbacks.key?(subscription_id)
|
187
|
+
|
188
|
+
@callbacks[subscription_id].call(notification)
|
192
189
|
end
|
193
190
|
end
|
194
|
-
|
195
191
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'decode'
|
4
|
+
require_relative 'encode'
|
5
|
+
|
6
|
+
module ScaleRb
|
7
|
+
module Codec
|
8
|
+
class Error < StandardError; end
|
9
|
+
class TypeNotFound < Error; end
|
10
|
+
class TypeNotImplemented < Error; end
|
11
|
+
class CompositeInvalidValue < Error; end
|
12
|
+
class ArrayLengthNotEqual < Error; end
|
13
|
+
class VariantItemNotFound < Error; end
|
14
|
+
class VariantIndexOutOfRange < Error; end
|
15
|
+
class VariantInvalidValue < Error; end
|
16
|
+
class VariantFieldsLengthNotMatch < Error; end
|
17
|
+
class LengthNotEqualErr < Error; end
|
18
|
+
class NotEnoughBytesError < Error; end
|
19
|
+
class Unreachable < Error; end
|
20
|
+
class InvalidBytesError < Error; end
|
21
|
+
|
22
|
+
extend Decode
|
23
|
+
extend Encode
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ScaleRb
|
4
|
+
module CodecUtils
|
5
|
+
module InternalDecodeUtils
|
6
|
+
extend TypeEnforcer
|
7
|
+
include Types
|
8
|
+
|
9
|
+
__ :decode_uint,
|
10
|
+
{ type: String.constrained(format: /\A[Uu]\d+\z/), bytes: U8Array },
|
11
|
+
DecodeResult[UnsignedInteger]
|
12
|
+
def decode_uint(type, bytes)
|
13
|
+
bit_length = type[1..].to_i
|
14
|
+
byte_length = bit_length / 8
|
15
|
+
raise Codec::NotEnoughBytesError, "type: #{type}" if bytes.length < byte_length
|
16
|
+
|
17
|
+
value = Utils.u8a_to_uint(bytes[0...byte_length].reverse)
|
18
|
+
# debug 'value', value
|
19
|
+
[
|
20
|
+
value,
|
21
|
+
bytes[byte_length..]
|
22
|
+
]
|
23
|
+
end
|
24
|
+
|
25
|
+
__ :decode_int, { type: String.constrained(format: /\A[Ii]\d+\z/), bytes: U8Array }, DecodeResult[Integer]
|
26
|
+
def decode_int(type, bytes)
|
27
|
+
bit_length = type[1..].to_i
|
28
|
+
byte_length = bit_length / 8
|
29
|
+
raise Codec::NotEnoughBytesError, "type: #{type}" if bytes.length < byte_length
|
30
|
+
|
31
|
+
value = Utils.u8a_to_int(bytes[0...byte_length].reverse, bit_length)
|
32
|
+
# debug 'value', value
|
33
|
+
[
|
34
|
+
value,
|
35
|
+
bytes[byte_length..]
|
36
|
+
]
|
37
|
+
end
|
38
|
+
|
39
|
+
__ :decode_str, { bytes: U8Array }, DecodeResult[String]
|
40
|
+
def decode_str(bytes)
|
41
|
+
length, remaining_bytes = _do_decode_compact(bytes)
|
42
|
+
raise Codec::NotEnoughBytesError, 'type: String' if remaining_bytes.length < length
|
43
|
+
|
44
|
+
[Utils.u8a_to_utf8(remaining_bytes[0...length]), remaining_bytes[length..]]
|
45
|
+
end
|
46
|
+
|
47
|
+
__ :decode_boolean, { bytes: U8Array }, DecodeResult[Bool]
|
48
|
+
def decode_boolean(bytes)
|
49
|
+
value = case bytes[0]
|
50
|
+
when 0x00 then false
|
51
|
+
when 0x01 then true
|
52
|
+
else raise Codec::InvalidBytesError, 'type: Boolean'
|
53
|
+
end
|
54
|
+
[value, bytes[1..]]
|
55
|
+
end
|
56
|
+
|
57
|
+
# TODO: inner type decoding
|
58
|
+
__ :decode_compact, { bytes: U8Array }, DecodeResult[UnsignedInteger]
|
59
|
+
def decode_compact(bytes)
|
60
|
+
_do_decode_compact(bytes)
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def _do_decode_compact(bytes)
|
66
|
+
case bytes[0] & 3
|
67
|
+
when 0 then [bytes[0] >> 2, bytes[1..]]
|
68
|
+
when 1 then [Utils.u8a_to_uint(bytes[0..1].reverse) >> 2, bytes[2..]]
|
69
|
+
when 2 then [Utils.u8a_to_uint(bytes[0..3].reverse) >> 2, bytes[4..]]
|
70
|
+
when 3
|
71
|
+
length = 4 + (bytes[0] >> 2)
|
72
|
+
[Utils.u8a_to_uint(bytes[1..length].reverse), bytes[length + 1..]]
|
73
|
+
else
|
74
|
+
raise Codec::Unreachable, 'type: Compact'
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
module InternalEncodeUtils
|
80
|
+
extend TypeEnforcer
|
81
|
+
include Types
|
82
|
+
|
83
|
+
__ :encode_uint, { type: String.constrained(format: /\A[Uu]\d+\z/), value: Integer }, U8Array
|
84
|
+
def encode_uint(type, value)
|
85
|
+
raise InvalidValueError, "type: #{type}, value: #{value.inspect}" unless value.is_a?(::Integer)
|
86
|
+
|
87
|
+
bit_length = type[1..].to_i
|
88
|
+
Utils.int_to_u8a(value, bit_length).reverse
|
89
|
+
end
|
90
|
+
|
91
|
+
__ :encode_int, { type: String.constrained(format: /\A[Ii]\d+\z/), value: Integer }, U8Array
|
92
|
+
def encode_int(_type, _value)
|
93
|
+
raise NotImplemented, 'encode_int'
|
94
|
+
# raise InvalidValueError, "type: #{type}, value: #{value.inspect}" unless value.is_a?(Integer)
|
95
|
+
#
|
96
|
+
# bit_length = type[1..].to_i
|
97
|
+
# Utils.int_to_u8a(value, bit_length).reverse
|
98
|
+
end
|
99
|
+
|
100
|
+
__ :encode_str, { string: String }, U8Array
|
101
|
+
def encode_str(string)
|
102
|
+
body = string.unpack('C*')
|
103
|
+
encode_compact(body.length) + body
|
104
|
+
end
|
105
|
+
|
106
|
+
__ :encode_boolean, { value: Bool }, U8Array
|
107
|
+
def encode_boolean(value)
|
108
|
+
return [0x00] if value == false
|
109
|
+
return [0x01] if value == true
|
110
|
+
|
111
|
+
raise InvalidValueError, "type: Bool, value: #{value.inspect}"
|
112
|
+
end
|
113
|
+
|
114
|
+
__ :encode_compact, { value: Integer }, U8Array
|
115
|
+
def encode_compact(value)
|
116
|
+
return [value << 2] if value.between?(0, 63)
|
117
|
+
return Utils.int_to_u8a(((value << 2) + 1)).reverse if value < 2**14
|
118
|
+
return Utils.int_to_u8a(((value << 2) + 2)).reverse if value < 2**30
|
119
|
+
|
120
|
+
bytes = Utils.int_to_u8a(value).reverse
|
121
|
+
[(((bytes.length - 4) << 2) + 3)] + bytes
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
extend InternalDecodeUtils
|
126
|
+
extend InternalEncodeUtils
|
127
|
+
end
|
128
|
+
end
|