scale.rb 0.2.16 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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