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