scale.rb 0.1.0 → 0.2.0

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