scale.rb 0.2.17 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Cargo.lock +8 -4
  4. data/Cargo.toml +2 -3
  5. data/Dockerfile +4 -1
  6. data/Gemfile.lock +23 -21
  7. data/README.md +44 -1
  8. data/Rakefile +6 -0
  9. data/exe/scale +49 -0
  10. data/lib/helper.rb +19 -4
  11. data/lib/metadata/metadata.rb +27 -17
  12. data/lib/metadata/metadata_v0.rb +24 -20
  13. data/lib/metadata/metadata_v1.rb +13 -9
  14. data/lib/metadata/metadata_v10.rb +2 -2
  15. data/lib/metadata/metadata_v11.rb +2 -2
  16. data/lib/metadata/metadata_v12.rb +9 -8
  17. data/lib/metadata/metadata_v13.rb +161 -0
  18. data/lib/metadata/metadata_v2.rb +2 -2
  19. data/lib/metadata/metadata_v3.rb +2 -2
  20. data/lib/metadata/metadata_v4.rb +21 -11
  21. data/lib/metadata/metadata_v5.rb +21 -11
  22. data/lib/metadata/metadata_v6.rb +9 -9
  23. data/lib/metadata/metadata_v7.rb +26 -15
  24. data/lib/metadata/metadata_v8.rb +9 -9
  25. data/lib/metadata/metadata_v9.rb +2 -2
  26. data/lib/scale.rb +40 -339
  27. data/lib/scale/base.rb +175 -93
  28. data/lib/scale/block.rb +10 -10
  29. data/lib/scale/trie.rb +1 -1
  30. data/lib/scale/types.rb +139 -40
  31. data/lib/scale/version.rb +1 -1
  32. data/lib/scale_bytes.rb +63 -0
  33. data/lib/substrate_client.rb +11 -8
  34. data/lib/type_builder.rb +280 -0
  35. data/lib/type_registry.rb +91 -0
  36. data/lib/type_registry/crab.json +676 -595
  37. data/lib/type_registry/darwinia.json +730 -554
  38. data/lib/type_registry/default.json +3 -2
  39. data/lib/type_registry/pangolin.json +771 -0
  40. data/scale.gemspec +1 -0
  41. data/scripts/mmr_root_to_sign.rb +10 -0
  42. data/src/lib.rs +80 -25
  43. metadata +25 -10
  44. data/lib/type_registry/edgeware.json +0 -124
  45. data/lib/type_registry/joystream.json +0 -49
  46. data/lib/type_registry/kulupu.json +0 -15
  47. data/lib/type_registry/plasm.json +0 -89
  48. data/lib/type_registry/robonomics.json +0 -39
  49. data/lib/type_registry/westend.json +0 -63
  50. data/src/storage_key.rs +0 -41
data/lib/scale/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Scale
2
- VERSION = "0.2.17".freeze
2
+ VERSION = "0.3.1".freeze
3
3
  end
@@ -0,0 +1,63 @@
1
+ module Scale
2
+ class Bytes
3
+ attr_reader :data, :bytes
4
+ attr_reader :offset
5
+
6
+ def initialize(data)
7
+ if (data.class == Array) && data.is_byte_array?
8
+ @bytes = data
9
+ elsif (data.class == String) && data.start_with?("0x") && (data.length % 2 == 0)
10
+ arr = data[2..].scan(/../).map(&:hex)
11
+ @bytes = arr
12
+ else
13
+ raise "Provided data is not valid"
14
+ end
15
+
16
+ @data = data
17
+ @offset = 0
18
+ end
19
+
20
+ def reset_offset
21
+ @offset = 0
22
+ end
23
+
24
+ def get_next_bytes(length)
25
+ result = @bytes[@offset...@offset + length]
26
+ if result.length < length
27
+ str = @data[(2 + @offset * 2)..]
28
+ str = str.length > 40 ? (str[0...40]).to_s + "..." : str
29
+ raise "No enough data: #{str}, expect length: #{length}, but #{result.length}"
30
+ end
31
+ @offset += length
32
+ result
33
+ rescue RangeError => ex
34
+ puts "length: #{length}"
35
+ puts ex.message
36
+ puts ex.backtrace
37
+ end
38
+
39
+ def get_remaining_bytes
40
+ @bytes[offset..]
41
+ end
42
+
43
+ def to_hex_string
44
+ @bytes.bytes_to_hex
45
+ end
46
+
47
+ def to_bin_string
48
+ @bytes.bytes_to_bin
49
+ end
50
+
51
+ def to_ascii
52
+ @bytes[0...offset].pack("C*") + "<================================>" + @bytes[offset..].pack("C*")
53
+ end
54
+
55
+ def ==(other)
56
+ bytes == other.bytes && offset == other.offset
57
+ end
58
+
59
+ def to_s
60
+ green(@bytes[0...offset].bytes_to_hex) + yellow(@bytes[offset..].bytes_to_hex[2..])
61
+ end
62
+ end
63
+ end
@@ -39,6 +39,7 @@ class SubstrateClient
39
39
  @url = url
40
40
  @request_id = 1
41
41
  @metadata_cache = {}
42
+ init_types_and_metadata
42
43
  end
43
44
 
44
45
  def request(method, params)
@@ -121,8 +122,8 @@ class SubstrateClient
121
122
  block = self.chain_getBlock(block_hash)
122
123
  SubstrateClient::Helper.decode_block(block)
123
124
  rescue => ex
124
- puts ex.message
125
- puts ex.backtrace.join("\n\t")
125
+ Scale::Types.logger.error ex
126
+ Scale::Types.logger.error ex.backtrace.join("\n\t")
126
127
  end
127
128
 
128
129
  def get_block_events(block_hash=nil)
@@ -136,16 +137,18 @@ class SubstrateClient
136
137
  [events_data, decoded]
137
138
  end
138
139
 
139
- # Plain: client.get_storage("Sudo", "Key")
140
- # Plain: client.get_storage("Balances", "TotalIssuance")
141
- # Map: client.get_storage("System", "Account", ["0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"])
142
- # DoubleMap: client.get_storage("ImOnline", "AuthoredBlocks", [2818, "0x749ddc93a65dfec3af27cc7478212cb7d4b0c0357fef35a0163966ab5333b757"])
143
140
  def get_storage(module_name, storage_name, params = nil, block_hash = nil)
144
141
  self.init_types_and_metadata(block_hash)
145
142
 
146
- storage_key, return_type = SubstrateClient::Helper.generate_storage_key_from_metadata(@metadata, module_name, storage_name, params)
143
+ storage_key, return_type, storage_item = SubstrateClient::Helper.generate_storage_key_from_metadata(@metadata, module_name, storage_name, params)
144
+
147
145
  data = self.state_getStorage(storage_key, block_hash)
148
- return unless data
146
+
147
+ if data.nil?
148
+ return if storage_item[:modifier] == "Optional"
149
+
150
+ data = storage_item[:fallback]
151
+ end
149
152
 
150
153
  bytes = Scale::Bytes.new(data)
151
154
  type = Scale::Types.get(return_type)
@@ -0,0 +1,280 @@
1
+ module Scale
2
+ module Types
3
+ class << self
4
+
5
+ # type_info: type_string or type_def
6
+ # type_string: hard coded type name, Compact, H128, Vec<Compact>, (U32, U128), ...
7
+ # type_def : struct, enum, set
8
+ #
9
+ # if type_string start_with Scale::Types::, it is treat as a hard coded type
10
+ def get(type_info)
11
+ if type_info.class == ::String
12
+ if type_info.start_with?('Scale::Types::')
13
+ return get_hard_coded_type(type_info)
14
+ end
15
+
16
+ # find the final type from registry
17
+ type_info = fix_name(type_info)
18
+ type_info = get_final_type_from_registry(type_info)
19
+ end
20
+
21
+ build_type(type_info)
22
+ end
23
+
24
+ private
25
+ def build_type(type_info)
26
+ # 1. hard coded types, 2. Vec<...>, 3. Option<...>, 4. [xx; x], 5. (x, y)
27
+ if type_info.class == ::String
28
+ type_string = fix_name(type_info)
29
+ if type_string =~ /\AVec<.+>\z/
30
+ build_vec(type_string)
31
+ elsif type_info =~ /\AOption<.+>\z/
32
+ build_option(type_string)
33
+ elsif type_info =~ /\A\[.+;\s*\d+\]\z/
34
+ build_array(type_string)
35
+ elsif type_info =~ /\A\(.+\)\z/
36
+ build_tuple(type_string)
37
+ else
38
+ get_hard_coded_type(type_string)
39
+ end
40
+
41
+ # 5. Struct, 6. Enum, 7. Set
42
+ else
43
+
44
+ type_info.transform_keys!(&:to_sym)
45
+ if type_info[:type] == "struct"
46
+ build_struct(type_info)
47
+ elsif type_info[:type] == "enum"
48
+ build_enum(type_info)
49
+ elsif type_info[:type] == "set"
50
+ build_set(type_info)
51
+ else
52
+ raise Scale::TypeBuildError.new("Failed to build a type from #{type_info}")
53
+ end
54
+
55
+ end
56
+ end
57
+
58
+ def get_final_type_from_registry(type_info)
59
+ type_registry = TypeRegistry.instance
60
+ if type_registry.types.nil?
61
+ raise TypeRegistryNotLoadYet
62
+ end
63
+ TypeRegistry.instance.get(type_info)
64
+ end
65
+
66
+ def get_hard_coded_type(type_name)
67
+ # type_name = rename(type_name)
68
+ type_name = (type_name.start_with?("Scale::Types::") ? type_name : "Scale::Types::#{type_name}")
69
+ type_name.constantize2
70
+ rescue => e
71
+ raise Scale::TypeBuildError.new("Failed to get the hard coded type named `#{type_name}`")
72
+ end
73
+
74
+ def build_vec(type_string)
75
+ inner_type_str = type_string.scan(/\AVec<(.+)>\z/).first.first
76
+ inner_type = get(inner_type_str)
77
+
78
+ type_name = "Vec_#{inner_type.name.gsub('Scale::Types::', '')}_"
79
+
80
+ if !Scale::Types.const_defined?(type_name)
81
+ klass = Class.new do
82
+ include Scale::Types::Vec
83
+ inner_type inner_type
84
+ end
85
+ Scale::Types.const_set type_name, klass
86
+ else
87
+ Scale::Types.const_get type_name
88
+ end
89
+ end
90
+
91
+ def build_option(type_string)
92
+ inner_type_str = type_string.scan(/\AOption<(.+)>\z/).first.first
93
+
94
+ # an exception
95
+ # https://substrate.dev/docs/en/knowledgebase/advanced/codec#options
96
+ return get("Scale::Types::OptionBool") if inner_type_str.camelize2 == "Bool"
97
+
98
+ inner_type = get(inner_type_str)
99
+
100
+ type_name = "Option_#{inner_type.name.gsub('Scale::Types::', '')}_"
101
+
102
+ if !Scale::Types.const_defined?(type_name)
103
+ klass = Class.new do
104
+ include Scale::Types::Option
105
+ inner_type inner_type
106
+ end
107
+ Scale::Types.const_set type_name, klass
108
+ else
109
+ Scale::Types.const_get type_name
110
+ end
111
+ end
112
+
113
+ def build_array(type_string)
114
+ scan_result = type_string.scan /\[(.+);\s*(\d+)\]/
115
+
116
+ #
117
+ inner_type_str = scan_result[0][0]
118
+ inner_type = get(inner_type_str)
119
+
120
+ #
121
+ len = scan_result[0][1].to_i
122
+
123
+ type_name = "Array_#{inner_type.name.gsub('Scale::Types::', '')}_#{len}_"
124
+
125
+ if !Scale::Types.const_defined?(type_name)
126
+ klass = Class.new do
127
+ include Scale::Types::Array
128
+ inner_type inner_type
129
+ length len
130
+ end
131
+ Scale::Types.const_set type_name, klass
132
+ else
133
+ Scale::Types.const_get type_name
134
+ end
135
+ end
136
+
137
+ def build_tuple(type_string)
138
+ scan_result = type_string.scan /\A\((.+)\)\z/
139
+ inner_types_str = scan_result[0][0]
140
+ inner_type_strs = inner_types_str.split(",").map do |inner_type_str|
141
+ inner_type_str.strip
142
+ end
143
+
144
+ inner_types = inner_type_strs.map do |inner_type_str|
145
+ get(inner_type_str)
146
+ end
147
+
148
+ type_name = "Tuple_#{inner_types.map {|inner_type| inner_type.name.gsub('Scale::Types::', '')}.join("_")}_"
149
+ if !Scale::Types.const_defined?(type_name)
150
+ klass = Class.new do
151
+ include Scale::Types::Tuple
152
+ inner_types(*inner_types)
153
+ end
154
+ Scale::Types.const_set type_name, klass
155
+ else
156
+ Scale::Types.const_get type_name
157
+ end
158
+ end
159
+
160
+ def build_struct(type_info)
161
+ # items: {"a" => Type}
162
+ items = type_info[:type_mapping].map do |item|
163
+ item_name = item[0]
164
+ item_type = get(item[1])
165
+ [item_name, item_type]
166
+ end.to_h
167
+
168
+ partials = []
169
+ items.each_pair do |item_name, item_type|
170
+ partials << item_name.camelize2 + 'In' + item_type.name.gsub('Scale::Types::', '')
171
+ end
172
+ type_name = "Struct_#{partials.join('_')}_"
173
+
174
+ if !Scale::Types.const_defined?(type_name)
175
+ klass = Class.new do
176
+ include Scale::Types::Struct
177
+ items(**items)
178
+ end
179
+ Scale::Types.const_set type_name, klass
180
+ else
181
+ Scale::Types.const_get type_name
182
+ end
183
+ end
184
+
185
+ # not implemented: ["Compact", "Hex"]
186
+ def build_enum(type_info)
187
+ # type_mapping: [["Item1", "Compact"], [["Item2", "Hex"]]
188
+ if type_info.has_key?(:type_mapping)
189
+ # items: {a: Type}
190
+ items = type_info[:type_mapping].map do |item|
191
+ item_name = item[0]
192
+ item_type = get(item[1])
193
+ [item_name.to_sym, item_type]
194
+ end.to_h
195
+
196
+ partials = []
197
+ items.each_pair do |item_name, item_type|
198
+ partials << item_name.to_s.camelize2 + 'In' + item_type.name.gsub('Scale::Types::', '')
199
+ end
200
+ type_name = "Enum_#{partials.join('_')}_"
201
+
202
+ if !Scale::Types.const_defined?(type_name)
203
+ klass = Class.new do
204
+ include Scale::Types::Enum
205
+ items(**items)
206
+ end
207
+
208
+ return Scale::Types.const_set type_name, klass
209
+ else
210
+ return Scale::Types.const_get type_name
211
+ end
212
+ end
213
+
214
+ # value_list: [1, "hello"]
215
+ if type_info.has_key?(:value_list)
216
+ type_name = "Enum#{type_info[:value_list].map {|value| value.to_s.camelize2}.join}"
217
+
218
+ if !Scale::Types.const_defined?(type_name)
219
+ klass = Class.new do
220
+ include Scale::Types::Enum
221
+ values *type_info[:value_list]
222
+ end
223
+ return Scale::Types.const_set type_name, klass
224
+ else
225
+ return Scale::Types.const_get type_name
226
+ end
227
+ end
228
+ end
229
+
230
+
231
+ # {
232
+ # value_type: u32,
233
+ # value_list: {
234
+ # "TransactionPayment" => 0b00000001,
235
+ # "Transfer" => 0b00000010,
236
+ # "Reserve" => 0b00000100,
237
+ # ...
238
+ # }
239
+ # }
240
+ def build_set(type_info)
241
+ type_name = "Set#{type_info[:value_list].keys.map(&:camelize2).join("")}"
242
+ if !Scale::Types.const_defined?(type_name)
243
+ bytes_length = type_info[:value_type][1..].to_i / 8
244
+ klass = Class.new do
245
+ include Scale::Types::Set
246
+ items type_info[:value_list], bytes_length
247
+ end
248
+ return Scale::Types.const_set type_name, klass
249
+ else
250
+ return Scale::Types.const_get type_name
251
+ end
252
+ Scale::Types.const_set fix(name), klass
253
+ end
254
+
255
+ def fix_name(type)
256
+ type = type.gsub("T::", "")
257
+ .gsub("<T>", "")
258
+ .gsub("<T as Trait>::", "")
259
+ .delete("\n")
260
+ .gsub(/(u)(\d+)/, 'U\2')
261
+ return "Bool" if type == "bool"
262
+ return "Null" if type == "()"
263
+ return "String" if type == "Vec<u8>"
264
+ return "Compact" if type == "Compact<u32>" || type == "Compact<U32>"
265
+ return "Address" if type == "<Lookup as StaticLookup>::Source"
266
+ return "Compact" if type == "<Balance as HasCompact>::Type"
267
+ return "Compact" if type == "<BlockNumber as HasCompact>::Type"
268
+ return "Compact" if type =~ /\ACompact<[a-zA-Z0-9\s]*>\z/
269
+ return "CompactMoment" if type == "<Moment as HasCompact>::Type"
270
+ return "CompactMoment" if type == "Compact<Moment>"
271
+ return "InherentOfflineReport" if type == "<InherentOfflineReport as InherentOfflineReport>::Inherent"
272
+ return "AccountData" if type == "AccountData<Balance>"
273
+ return "EventRecord" if type == "EventRecord<Event, Hash>"
274
+
275
+ type
276
+ end
277
+
278
+ end
279
+ end
280
+ end
@@ -0,0 +1,91 @@
1
+ module Scale
2
+
3
+ class TypeRegistry
4
+ include Singleton
5
+
6
+ # init by load, and will not change
7
+ attr_reader :spec_name, :types
8
+ attr_reader :versioning, :custom_types # optional
9
+
10
+ # will change by different spec version
11
+ attr_accessor :spec_version # optional
12
+ attr_accessor :metadata
13
+
14
+ def load(spec_name: nil, custom_types: nil)
15
+ @spec_name = nil
16
+ @types = nil
17
+ @versioning = nil
18
+ @custom_types = nil
19
+
20
+ default_types, _, _ = load_chain_spec_types("default")
21
+
22
+ if spec_name
23
+ begin
24
+ @spec_name = spec_name
25
+ spec_types, @versioning, @spec_version = load_chain_spec_types(spec_name)
26
+ @types = default_types.merge(spec_types)
27
+ rescue => ex
28
+ # TODO: check different errors
29
+ Scale::Types.logger.error "There is no types json file named #{spec_name}"
30
+ @types = default_types
31
+ end
32
+ else
33
+ @spec_name = "default"
34
+ @types = default_types
35
+ end
36
+
37
+ self.custom_types = custom_types
38
+ true
39
+ end
40
+
41
+ def get(type_name)
42
+ all_types = self.all_types
43
+ type_traverse(type_name, all_types)
44
+ end
45
+
46
+ def custom_types=(custom_types)
47
+ @custom_types = custom_types.stringify_keys if (not custom_types.nil?) && custom_types.class.name == "Hash"
48
+ end
49
+
50
+ def all_types
51
+ all_types = {}.merge(@types)
52
+
53
+ if @spec_version && @versioning
54
+ @versioning.each do |item|
55
+ if @spec_version >= item["runtime_range"][0] &&
56
+ ( item["runtime_range"][1].nil? || @spec_version <= item["runtime_range"][1] )
57
+ all_types.merge!(item["types"])
58
+ end
59
+ end
60
+ end
61
+
62
+ all_types.merge!(@custom_types) if @custom_types
63
+ all_types
64
+ end
65
+
66
+ private
67
+
68
+ def load_chain_spec_types(spec_name)
69
+ file = File.join File.expand_path("../..", __FILE__), "lib", "type_registry", "#{spec_name}.json"
70
+ json_string = File.open(file).read
71
+ json = JSON.parse(json_string)
72
+
73
+ runtime_id = json["runtime_id"]
74
+
75
+ [json["types"], json["versioning"], runtime_id]
76
+ end
77
+
78
+ def type_traverse(type, types)
79
+ if type.class == ::String
80
+ real_type = types[type]
81
+
82
+ return type if real_type.nil? || real_type == type
83
+
84
+ type_traverse(real_type, types)
85
+ else
86
+ type
87
+ end
88
+ end
89
+ end
90
+
91
+ end