scale.rb 0.2.16 → 0.3.0

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.
Files changed (52) 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 +43 -35
  7. data/README.md +44 -1
  8. data/Rakefile +6 -0
  9. data/exe/scale +39 -79
  10. data/lib/address.rb +3 -0
  11. data/lib/common.rb +163 -0
  12. data/lib/helper.rb +25 -8
  13. data/lib/metadata/metadata.rb +28 -18
  14. data/lib/metadata/metadata_v0.rb +24 -20
  15. data/lib/metadata/metadata_v1.rb +13 -9
  16. data/lib/metadata/metadata_v10.rb +2 -2
  17. data/lib/metadata/metadata_v11.rb +2 -2
  18. data/lib/metadata/metadata_v12.rb +9 -8
  19. data/lib/metadata/metadata_v13.rb +161 -0
  20. data/lib/metadata/metadata_v2.rb +2 -2
  21. data/lib/metadata/metadata_v3.rb +2 -2
  22. data/lib/metadata/metadata_v4.rb +21 -11
  23. data/lib/metadata/metadata_v5.rb +21 -11
  24. data/lib/metadata/metadata_v6.rb +9 -9
  25. data/lib/metadata/metadata_v7.rb +26 -15
  26. data/lib/metadata/metadata_v8.rb +9 -9
  27. data/lib/metadata/metadata_v9.rb +2 -2
  28. data/lib/scale.rb +41 -341
  29. data/lib/scale/base.rb +177 -95
  30. data/lib/scale/block.rb +17 -13
  31. data/lib/scale/trie.rb +1 -1
  32. data/lib/scale/types.rb +139 -40
  33. data/lib/scale/version.rb +1 -1
  34. data/lib/scale_bytes.rb +63 -0
  35. data/lib/substrate_client.rb +31 -17
  36. data/lib/type_builder.rb +279 -0
  37. data/lib/type_registry.rb +91 -0
  38. data/lib/type_registry/crab.json +676 -595
  39. data/lib/type_registry/darwinia.json +730 -554
  40. data/lib/type_registry/default.json +3 -2
  41. data/lib/type_registry/pangolin.json +771 -0
  42. data/scale.gemspec +7 -5
  43. data/scripts/mmr_root_to_sign.rb +10 -0
  44. data/src/lib.rs +80 -25
  45. metadata +59 -30
  46. data/lib/type_registry/edgeware.json +0 -124
  47. data/lib/type_registry/joystream.json +0 -49
  48. data/lib/type_registry/kulupu.json +0 -15
  49. data/lib/type_registry/plasm.json +0 -89
  50. data/lib/type_registry/robonomics.json +0 -39
  51. data/lib/type_registry/westend.json +0 -63
  52. data/src/storage_key.rs +0 -41
@@ -1,7 +1,7 @@
1
1
  module Scale
2
2
  module Types
3
3
  class MetadataV8
4
- include SingleValue
4
+ include Base
5
5
  attr_accessor :call_index, :event_index
6
6
 
7
7
  def initialize(value)
@@ -11,7 +11,7 @@ module Scale
11
11
  end
12
12
 
13
13
  def self.decode(scale_bytes)
14
- modules = Scale::Types.type_of("Vec<MetadataV8Module>").decode(scale_bytes).value
14
+ modules = Scale::Types.get("Vec<MetadataV8Module>").decode(scale_bytes).value
15
15
 
16
16
  value = {
17
17
  magicNumber: 1_635_018_093,
@@ -49,7 +49,7 @@ module Scale
49
49
  end
50
50
 
51
51
  class MetadataV8Module
52
- include SingleValue
52
+ include Base
53
53
  def self.decode(scale_bytes)
54
54
  name = String.decode(scale_bytes).value
55
55
 
@@ -66,29 +66,29 @@ module Scale
66
66
 
67
67
  has_calls = Bool.decode(scale_bytes).value
68
68
  if has_calls
69
- calls = Scale::Types.type_of("Vec<MetadataModuleCall>").decode(scale_bytes).value
69
+ calls = Scale::Types.get("Vec<MetadataModuleCall>").decode(scale_bytes).value
70
70
  result[:calls] = calls.map(&:value)
71
71
  end
72
72
 
73
73
  has_events = Bool.decode(scale_bytes).value
74
74
  if has_events
75
- events = Scale::Types.type_of("Vec<MetadataModuleEvent>").decode(scale_bytes).value
75
+ events = Scale::Types.get("Vec<MetadataModuleEvent>").decode(scale_bytes).value
76
76
  result[:events] = events.map(&:value)
77
77
  end
78
78
 
79
- result[:constants] = Scale::Types.type_of("Vec<MetadataV7ModuleConstants>").decode(scale_bytes).value.map(&:value)
80
- result[:errors] = Scale::Types.type_of("Vec<MetadataModuleError>").decode(scale_bytes).value.map(&:value)
79
+ result[:constants] = Scale::Types.get("Vec<MetadataV7ModuleConstants>").decode(scale_bytes).value.map(&:value)
80
+ result[:errors] = Scale::Types.get("Vec<MetadataModuleError>").decode(scale_bytes).value.map(&:value)
81
81
 
82
82
  MetadataV8Module.new(result)
83
83
  end
84
84
  end
85
85
 
86
86
  class MetadataModuleError
87
- include SingleValue
87
+ include Base
88
88
  def self.decode(scale_bytes)
89
89
  result = {
90
90
  name: String.decode(scale_bytes).value,
91
- documentation: Scale::Types.type_of("Vec<String>").decode(scale_bytes).value.map(&:value)
91
+ documentation: Scale::Types.get("Vec<String>").decode(scale_bytes).value.map(&:value)
92
92
  }
93
93
 
94
94
  MetadataModuleError.new(result)
@@ -1,7 +1,7 @@
1
1
  module Scale
2
2
  module Types
3
3
  class MetadataV9
4
- include SingleValue
4
+ include Base
5
5
  attr_accessor :call_index, :event_index
6
6
 
7
7
  def initialize(value)
@@ -11,7 +11,7 @@ module Scale
11
11
  end
12
12
 
13
13
  def self.decode(scale_bytes)
14
- modules = Scale::Types.type_of("Vec<MetadataV8Module>").decode(scale_bytes).value
14
+ modules = Scale::Types.get("Vec<MetadataV8Module>").decode(scale_bytes).value
15
15
 
16
16
  value = {
17
17
  magicNumber: 1_635_018_093,
data/lib/scale.rb CHANGED
@@ -1,9 +1,15 @@
1
1
  require "scale/version"
2
2
 
3
- require "substrate_common"
3
+ require "common"
4
+
4
5
  require "json"
5
6
  require "singleton"
6
7
 
8
+ require "scale_bytes"
9
+
10
+ require "type_registry"
11
+ require "type_builder"
12
+
7
13
  require "scale/base"
8
14
  require "scale/types"
9
15
  require "scale/block"
@@ -23,362 +29,36 @@ require "metadata/metadata_v9"
23
29
  require "metadata/metadata_v10"
24
30
  require "metadata/metadata_v11"
25
31
  require "metadata/metadata_v12"
32
+ require "metadata/metadata_v13"
26
33
 
27
34
  require "substrate_client"
28
35
  require "logger"
29
36
  require "helper"
30
- require 'kontena-websocket-client'
31
-
32
- class String
33
- def upcase_first
34
- self.sub(/\S/, &:upcase)
35
- end
36
-
37
- def camelize
38
- self.split('_').collect(&:upcase_first).join
39
- end
40
-
41
- def underscore
42
- self.gsub(/::/, '/').
43
- gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
44
- gsub(/([a-z\d])([A-Z])/,'\1_\2').
45
- tr("-", "_").
46
- downcase
47
- end
48
- end
49
37
 
50
38
  module Scale
51
- class Error < StandardError; end
52
-
53
- class TypeRegistry
54
- include Singleton
39
+ class ScaleError < StandardError; end
40
+ class TypeBuildError < ScaleError; end
41
+ class BadDataError < ScaleError; end
42
+ class TypeRegistryNotLoadYet < ScaleError; end
43
+ class StorageInputTypeError < ScaleError; end
55
44
 
56
- # init by load, and will not change
57
- attr_reader :spec_name, :types
58
- attr_reader :versioning, :custom_types # optional
59
-
60
- # will change by different spec version
61
- attr_accessor :spec_version # optional
62
- attr_accessor :metadata
63
-
64
- def load(spec_name: nil, custom_types: nil)
65
- @spec_name = nil
66
- @types = nil
67
- @versioning = nil
68
- @custom_types = nil
69
-
70
- default_types, _, _ = load_chain_spec_types("default")
45
+ module Types
46
+ class << self
47
+ attr_accessor :debug
48
+ end
71
49
 
72
- if spec_name
50
+ def self.check_types
51
+ TypeRegistry.instance.all_types.keys.each do |key|
73
52
  begin
74
- @spec_name = spec_name
75
- spec_types, @versioning, @spec_version = load_chain_spec_types(spec_name)
76
- @types = default_types.merge(spec_types)
53
+ type = self.get(key)
77
54
  rescue => ex
78
- puts "There is no types json file named #{spec_name}"
79
- @types = default_types
55
+ puts "[[ERROR]] #{key}: #{ex}"
80
56
  end
81
- else
82
- @spec_name = "default"
83
- @types = default_types
84
57
  end
85
-
86
- self.custom_types = custom_types
87
58
  true
88
59
  end
89
60
 
90
- def get(type_name)
91
- raise "Types not loaded" if @types.nil?
92
-
93
- all_types = {}.merge(@types)
94
-
95
- if @spec_version && @versioning
96
- @versioning.each do |item|
97
- if @spec_version >= item["runtime_range"][0] &&
98
- ( item["runtime_range"][1].nil? || @spec_version <= item["runtime_range"][1] )
99
- all_types.merge!(item["types"])
100
- end
101
- end
102
- end
103
-
104
- all_types.merge!(@custom_types) if @custom_types
105
-
106
- type = type_traverse(type_name, all_types)
107
-
108
- Scale::Types.constantize(type)
109
- end
110
-
111
- def custom_types=(custom_types)
112
- @custom_types = custom_types.stringify_keys if (not custom_types.nil?) && custom_types.class.name == "Hash"
113
- end
114
-
115
- private
116
-
117
- def load_chain_spec_types(spec_name)
118
- file = File.join File.expand_path("../..", __FILE__), "lib", "type_registry", "#{spec_name}.json"
119
- json_string = File.open(file).read
120
- json = JSON.parse(json_string)
121
-
122
- runtime_id = json["runtime_id"]
123
-
124
- [json["types"], json["versioning"], runtime_id]
125
- end
126
-
127
- def type_traverse(type, types)
128
- type = rename(type) if type.class == ::String
129
- if types.has_key?(type) && types[type] != type
130
- type_traverse(types[type], types)
131
- else
132
- type
133
- end
134
- end
135
- end
136
-
137
- # TODO: == implement
138
-
139
- class Bytes
140
- attr_reader :data, :bytes
141
- attr_reader :offset
142
-
143
- def initialize(data)
144
- if (data.class == Array) && data.is_byte_array?
145
- @bytes = data
146
- elsif (data.class == String) && data.start_with?("0x") && (data.length % 2 == 0)
147
- arr = data[2..].scan(/../).map(&:hex)
148
- @bytes = arr
149
- else
150
- raise "Provided data is not valid"
151
- end
152
-
153
- @data = data
154
- @offset = 0
155
- end
156
-
157
- def reset_offset
158
- @offset = 0
159
- end
160
-
161
- def get_next_bytes(length)
162
- result = @bytes[@offset...@offset + length]
163
- if result.length < length
164
- str = @data[(2 + @offset * 2)..]
165
- str = str.length > 40 ? (str[0...40]).to_s + "..." : str
166
- raise "No enough data: #{str}, expect length: #{length}, but #{result.length}"
167
- end
168
- @offset += length
169
- result
170
- rescue RangeError => ex
171
- puts "length: #{length}"
172
- puts ex.message
173
- puts ex.backtrace
174
- end
175
-
176
- def get_remaining_bytes
177
- @bytes[offset..]
178
- end
179
-
180
- def to_hex_string
181
- @bytes.bytes_to_hex
182
- end
183
-
184
- def to_bin_string
185
- @bytes.bytes_to_bin
186
- end
187
-
188
- def to_ascii
189
- @bytes[0...offset].pack("C*") + "<================================>" + @bytes[offset..].pack("C*")
190
- end
191
-
192
- def ==(other)
193
- bytes == other.bytes && offset == other.offset
194
- end
195
-
196
- def to_s
197
- green(@bytes[0...offset].bytes_to_hex) + yellow(@bytes[offset..].bytes_to_hex[2..])
198
- end
199
- end
200
-
201
- class TypesLoader
202
- def self.load(filename)
203
- path = File.join File.dirname(__FILE__), "types", filename
204
- content = File.open(path).read
205
- result = JSON.parse content
206
-
207
- types = result["default"]
208
- types.each do |name, body|
209
- if body.class == String
210
- target_type = "Scale::Types::#{body}"
211
- klass = Class.new(target_type.constantize) do
212
- end
213
- elsif body.class == Hash
214
- if body["type"] == "struct"
215
- struct_params = {}
216
- body["type_mapping"].each do |mapping|
217
- struct_params[mapping[0].to_sym] = mapping[1]
218
- end
219
- klass = Class.new do
220
- end
221
- klass.send(:include, Scale::Types::Struct)
222
- klass.send(:items, struct_params)
223
- Scale::Types.const_set name, klass
224
- elsif body["type"] = "enum"
225
- klass = Class.new do
226
- end
227
- klass.send(:include, Scale::Types::Enum)
228
- if body["type_mapping"]
229
- struct_params = {}
230
- body["type_mapping"].each do |mapping|
231
- struct_params[mapping[0].to_sym] = mapping[1]
232
- end
233
- klass.send(:items, struct_params)
234
- else
235
- klass.send(:values, body["value_list"])
236
- end
237
- Scale::Types.const_set name, klass
238
- end
239
- end
240
- end
241
- end
242
61
  end
243
-
244
- module Types
245
- def self.list
246
- TypeRegistry.instance.types
247
- end
248
-
249
- def self.get(type_name)
250
- TypeRegistry.instance.get(type_name)
251
- end
252
-
253
- def self.constantize(type)
254
- if type.class == ::String
255
- type_of(type.strip)
256
- else
257
- if type["type"] == "enum" && type.has_key?("type_mapping")
258
- type_of("Enum", type["type_mapping"].to_h)
259
- elsif type["type"] == "enum" && type.has_key?("value_list")
260
- type_of("Enum", type["value_list"])
261
- elsif type["type"] == "struct"
262
- type_of("Struct", type["type_mapping"].to_h)
263
- elsif type["type"] == "set"
264
- type_of("Set", type["value_list"])
265
- end
266
- end
267
- end
268
-
269
- def self.type_of(type_string, values = nil)
270
- if type_string.end_with?(">")
271
- type_strs = type_string.scan(/^([^<]*)<(.+)>$/).first
272
- type_str = type_strs.first
273
- inner_type_str = type_strs.last
274
-
275
- if type_str == "Vec" || type_str == "Option"
276
- klass = Class.new do
277
- include Scale::Types.type_of(type_str)
278
- inner_type inner_type_str
279
- end
280
- name = "#{type_str}<#{inner_type_str.camelize}>_#{klass.object_id}"
281
- Scale::Types.const_set fix(name), klass
282
- else
283
- raise "#{type_str} not support inner type: #{type_string}"
284
- end
285
- elsif type_string.start_with?("(") && type_string.end_with?(")") # tuple
286
- # TODO: add nested tuple support
287
- types_with_inner_type = type_string[1...-1].scan(/([A-Za-z]+<[^>]*>)/).first
288
-
289
- types_with_inner_type&.each do |type_str|
290
- new_type_str = type_str.tr(",", ";")
291
- type_string = type_string.gsub(type_str, new_type_str)
292
- end
293
-
294
- type_strs = type_string[1...-1].split(",").map do |type_str|
295
- type_str.strip.tr(";", ",")
296
- end
297
-
298
- klass = Class.new do
299
- include Scale::Types::Tuple
300
- inner_types *type_strs
301
- end
302
- name = "Tuple_Of_#{type_strs.map(&:camelize).join("_")}_#{klass.object_id}"
303
- Scale::Types.const_set fix(name), klass
304
- else
305
- if type_string == "Enum"
306
- # TODO: combine values to items
307
- klass = Class.new do
308
- include Scale::Types::Enum
309
- if values.class == ::Hash
310
- items values
311
- else
312
- values(*values)
313
- end
314
- end
315
- name = values.class == ::Hash ? values.values.map(&:camelize).join("_") : values.map(&:camelize).join("_")
316
- name = "Enum_Of_#{name}_#{klass.object_id}"
317
- Scale::Types.const_set fix(name), klass
318
- elsif type_string == "Struct"
319
- klass = Class.new do
320
- include Scale::Types::Struct
321
- items values
322
- end
323
- name = "Struct_Of_#{values.values.map(&:camelize).join("_")}_#{klass.object_id}"
324
- Scale::Types.const_set fix(name), klass
325
- elsif type_string == "Set"
326
- klass = Class.new do
327
- include Scale::Types::Set
328
- items values, 1
329
- end
330
- name = "Set_Of_#{values.keys.map(&:camelize).join("_")}_#{klass.object_id}"
331
- Scale::Types.const_set fix(name), klass
332
- else
333
- type_name = (type_string.start_with?("Scale::Types::") ? type_string : "Scale::Types::#{type_string}")
334
- begin
335
- type_name.constantize
336
- rescue NameError => e
337
- puts "#{type_string} is not defined"
338
- end
339
- end
340
- end
341
- end
342
- end
343
-
344
- end
345
-
346
- def fix(name)
347
- name
348
- .gsub("<", "˂").gsub(">", "˃")
349
- .gsub("(", "⁽").gsub(")", "⁾")
350
- .gsub(" ", "").gsub(",", "‚")
351
- .gsub(":", "։")
352
- end
353
-
354
- def rename(type)
355
- type = type.gsub("T::", "")
356
- .gsub("<T>", "")
357
- .gsub("<T as Trait>::", "")
358
- .delete("\n")
359
- .gsub("EventRecord<Event, Hash>", "EventRecord")
360
- .gsub(/(u)(\d+)/, 'U\2')
361
- return "Bool" if type == "bool"
362
- return "Null" if type == "()"
363
- return "String" if type == "Vec<u8>"
364
- return "Compact" if type == "Compact<u32>" || type == "Compact<U32>"
365
- return "Address" if type == "<Lookup as StaticLookup>::Source"
366
- return "Vec<Address>" if type == "Vec<<Lookup as StaticLookup>::Source>"
367
- return "Compact" if type == "<Balance as HasCompact>::Type"
368
- return "Compact" if type == "<BlockNumber as HasCompact>::Type"
369
- return "Compact" if type == "Compact<Balance>"
370
- return "Compact" if type == "Compact<BlockNumber>"
371
- return "CompactMoment" if type == "<Moment as HasCompact>::Type"
372
- return "CompactMoment" if type == "Compact<Moment>"
373
- return "InherentOfflineReport" if type == "<InherentOfflineReport as InherentOfflineReport>::Inherent"
374
- return "AccountData" if type == "AccountData<Balance>"
375
-
376
- if type =~ /\[U\d+; \d+\]/
377
- byte_length = type.scan(/\[U\d+; (\d+)\]/).first.first.to_i
378
- return "VecU8Length#{byte_length}"
379
- end
380
-
381
- type
382
62
  end
383
63
 
384
64
  def green(text)
@@ -389,6 +69,24 @@ def yellow(text)
389
69
  "\033[33m#{text}\033[0m"
390
70
  end
391
71
 
72
+ class String
73
+ def upcase_first
74
+ self.sub(/\S/, &:upcase)
75
+ end
76
+
77
+ def camelize2
78
+ self.split('_').collect(&:upcase_first).join
79
+ end
80
+
81
+ def underscore2
82
+ self.gsub(/::/, '/').
83
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
84
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
85
+ tr("-", "_").
86
+ downcase
87
+ end
88
+ end
89
+
392
90
  # https://www.ruby-forum.com/t/question-about-hex-signed-int/125510/4
393
91
  # machine bit length:
394
92
  # machine_byte_length = ['foo'].pack('p').size
@@ -426,3 +124,5 @@ class ::Hash
426
124
  Hash[h]
427
125
  end
428
126
  end
127
+
128
+ Scale::Types.debug = false