diff --git a/bin/console b/bin/console old mode 100644 new mode 100755 diff --git a/exe/metadata b/exe/metadata index 3724024..242d768 100755 --- a/exe/metadata +++ b/exe/metadata @@ -35,5 +35,5 @@ else end metadata = client.get_metadata(block_hash) - puts JSON.pretty_generate(metadata) + puts metadata.to_json end diff --git a/lib/scale_rb.rb b/lib/scale_rb.rb index 0276459..93545d3 100644 --- a/lib/scale_rb.rb +++ b/lib/scale_rb.rb @@ -21,9 +21,11 @@ def debug(key, value) require 'scale_rb/types' require 'scale_rb/portable_registry' +require 'scale_rb/old_registry' require 'scale_rb/codec' require 'scale_rb/metadata/metadata' +require 'scale_rb/runtime_types' require 'scale_rb/hasher' require 'scale_rb/storage_helper' diff --git a/lib/scale_rb/call_helper.rb b/lib/scale_rb/call_helper.rb index 4b7211b..deeb83d 100644 --- a/lib/scale_rb/call_helper.rb +++ b/lib/scale_rb/call_helper.rb @@ -9,7 +9,7 @@ module CallHelper # "0x05000a1287977578f888bdc1c7627781af1cc000e6ab1300004c31b8d9a798"._to_bytes def self.decode_call(callbytes, metadata) pallet_index = callbytes[0] - pallet = Metadata.get_module_by_index(pallet_index, metadata) + pallet = metadata.pallet_by_index(pallet_index) # Remove the pallet_index # The callbytes we used below should not contain the pallet index. @@ -20,7 +20,7 @@ def self.decode_call(callbytes, metadata) decoded = Codec.decode( calls_type_id, callbytes_without_pallet_index, - Metadata.build_registry(metadata) + metadata.build_registry )&.first { @@ -34,9 +34,9 @@ def self.decode_call(callbytes, metadata) # {:pallet_name=>"Deposit", :call_name=>"claim", :call=>:claim]} # {:pallet_name=>"Balances", :call_name=>"transfer", :call=>{:transfer=>{:dest=>[10, 18, 135, 151, 117, 120, 248, 136, 189, 193, 199, 98, 119, 129, 175, 28, 192, 0, 230, 171], :value=>11000000000000000000}}]} def self.encode_call(call, metadata) - calls_type_id = Metadata.get_calls_type_id(call[:pallet_name], metadata) - pallet_index = Metadata.get_module(call[:pallet_name], metadata)._get(:index) - [pallet_index] + Codec.encode(calls_type_id, call[:call], Metadata.build_registry(metadata)) + calls_type_id = metadata.calls_type_id(call[:pallet_name]) + pallet_index = metadata.pallet(call[:pallet_name])[:index] + [pallet_index] + Codec.encode(calls_type_id, call[:call], metadata.build_registry) end end end diff --git a/lib/scale_rb/client/client_ext.rb b/lib/scale_rb/client/client_ext.rb index 9dfac87..98216ea 100644 --- a/lib/scale_rb/client/client_ext.rb +++ b/lib/scale_rb/client/client_ext.rb @@ -5,7 +5,7 @@ module ClientExt def get_metadata(block_hash = nil) block_hash ||= chain_getHead metadata_hex = state_getMetadata(block_hash) - Metadata.decode_metadata(metadata_hex) + Metadata::Metadata.from_hex(metadata_hex) end # Get decoded storage at block_hash @@ -134,10 +134,8 @@ def get_storage1(block_hash, pallet_name, item_name, key, value, registry) def get_storage2(block_hash, pallet_name, item_name, params, metadata) raise 'Metadata should not be nil' if metadata.nil? - registry = Metadata.build_registry(metadata) - item = Metadata.get_storage_item( - pallet_name, item_name, metadata - ) + registry = metadata.build_registry + item = metadata.storage(pallet_name, item_name) raise "No such storage item: `#{pallet_name}`.`#{item_name}`" if item.nil? modifier = item._get(:modifier) # Default | Optional diff --git a/lib/scale_rb/extrinsic_helper.rb b/lib/scale_rb/extrinsic_helper.rb new file mode 100644 index 0000000..0319ac7 --- /dev/null +++ b/lib/scale_rb/extrinsic_helper.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module ScaleRb + module ExtrinsicHelper + def decode_extrinsic(bytes, metadata_prefixed) + meta = bytes[0] + signed = (meta & 0x80) == 0x80 + version = (meta & 0x7f) + + raise "Unsupported version: #{version}" unless version == 4 + + nil unless signed + end + + def patch_types(registry, metadata_prefixed) + add_signed_extensions_type(metadata_prefixed.signed_extensions, registry) + end + + private + + def add_signed_extensions_type(signed_extensions, registry) + type = Types::StructType.new( + fields: signed_extensions.map do |signed_extension| + Types::Field.new( + name: Utils.camelize(signed_extension._get(:identifier)), + type: signed_extension._get(:type) + ) + end, + registry: + ) + registry.add_type(type) + end + end +end diff --git a/lib/scale_rb/metadata/metadata.rb b/lib/scale_rb/metadata/metadata.rb index 016ebc6..774e1bb 100644 --- a/lib/scale_rb/metadata/metadata.rb +++ b/lib/scale_rb/metadata/metadata.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require_relative './registry' - require_relative './metadata_v9' require_relative './metadata_v10' require_relative './metadata_v11' @@ -13,66 +11,74 @@ # https://github.com/paritytech/frame-metadata/blob/main/frame-metadata/src/lib.rs#L85 module ScaleRb module Metadata - class << self - def decode_metadata(hex) - bytes = ScaleRb::Utils.hex_to_u8a(hex) - - registry = ScaleRb::Metadata::Registry.new TYPES - metadata, = ScaleRb::Codec.decode('MetadataPrefixed', bytes, registry) - metadata + class Metadata + attr_reader :magic_number, :version, :metadata, :types + + def initialize(metadata_prefixed) + @metadata_prefixed = metadata_prefixed + @magic_number = @metadata_prefixed[:magicNumber] + metadata = @metadata_prefixed[:metadata] + @version = metadata.keys.first + raise "Unsupported metadata version: #{@version}" unless @version == :V14 + + @metadata = metadata[@version] + @types = @metadata.dig(:lookup, :types) end - def build_registry(metadata_prefixed) - types = ScaleRb::Utils.get(metadata_prefixed, :metadata, :V14, :lookup, :types) - ScaleRb::PortableRegistry.new(types) + def self.from_hex(hex) + metadata_prefixed, = ScaleRb::Codec.decode('MetadataPrefixed', Utils.hex_to_u8a(hex), OldRegistry.new(TYPES)) + Metadata.new(metadata_prefixed) end - def get_module(pallet_name, metadata_prefixed) - metadata = Utils.get(metadata_prefixed, :metadata) - version = metadata.keys.first - raise NotImplementedError, version unless %i[V14].include?(version) - - Metadata.const_get("Metadata#{version.upcase}").get_module(pallet_name, metadata_prefixed) + def self.from_json(str) + metadata_prefixed = JSON.parse(str, symbolize_names: true) + Metadata.new(metadata_prefixed) end - def get_module_by_index(pallet_index, metadata_prefixed) - metadata = Utils.get(metadata_prefixed, :metadata) - version = metadata.keys.first.to_sym - raise NotImplementedError, version unless %i[V14].include?(version) + def to_json(*_args) + JSON.pretty_generate(@metadata_prefixed) + end - Metadata.const_get("Metadata#{version.upcase}").get_module_by_index(pallet_index, metadata_prefixed) + def build_registry + ScaleRb::PortableRegistry.new(@metadata.dig(:lookup, :types)) end - def get_storage_item(pallet_name, item_name, metadata_prefixed) - metadata = Utils.get(metadata_prefixed, :metadata) - version = metadata.keys.first.to_sym - raise NotImplementedError, version unless %i[V14].include?(version) + def pallet(pallet_name) + @metadata[:pallets].find do |pallet| + pallet[:name] == pallet_name + end + end - Metadata.const_get("Metadata#{version.upcase}").get_storage_item(pallet_name, item_name, metadata_prefixed) + def pallet_by_index(pallet_index) + @metadata[:pallets].find do |pallet| + pallet[:index] == pallet_index + end end - def get_calls_type(pallet_name, metadata_prefixed) - metadata = Utils.get(metadata_prefixed, :metadata) - version = metadata.keys.first.to_sym - raise NotImplementedError, version unless %i[V14].include?(version) + def storage(pallet_name, item_name) + pallet = pallet(pallet_name) + raise "Pallet `#{pallet_name}` not found" if pallet.nil? - Metadata.const_get("Metadata#{version.upcase}").get_calls_type(pallet_name, metadata_prefixed) + pallet.dig(:storage, :items).find do |item| + item[:name] == item_name + end end - def get_calls_type_id(pallet_name, metadata_prefixed) - metadata = Utils.get(metadata_prefixed, :metadata) - version = metadata.keys.first.to_sym - raise NotImplementedError, version unless %i[V14].include?(version) + def calls_type_id(pallet_name) + pallet = pallet(pallet_name) + raise "Pallet `#{pallet_name}` not found" if pallet.nil? - Metadata.const_get("Metadata#{version.upcase}").get_calls_type_id(pallet_name, metadata_prefixed) + pallet.dig(:calls, :type) end - def get_call_type(pallet_name, call_name, metadata_prefixed) - metadata = Utils.get(metadata_prefixed, :metadata) - version = metadata.keys.first.to_sym - raise NotImplementedError, version unless %i[V14].include?(version) + def call(pallet_name, call_name) + calls_type_id = calls_type_id(pallet_name) + calls_type = @types[calls_type_id] + raise 'Calls type is not correct' if calls_type.nil? - Metadata.const_get("Metadata#{version.upcase}").get_call_type(pallet_name, call_name, metadata_prefixed) + calls_type.dig(:type, :def, :variant, :variants).find do |variant| + variant[:name].downcase == call_name.downcase + end end end diff --git a/lib/scale_rb/metadata/metadata_v14.rb b/lib/scale_rb/metadata/metadata_v14.rb index 0bca39f..790cfcd 100644 --- a/lib/scale_rb/metadata/metadata_v14.rb +++ b/lib/scale_rb/metadata/metadata_v14.rb @@ -4,9 +4,9 @@ module ScaleRb module Metadata module MetadataV14 class << self - def build_registry(metadata) - types = metadata._get(:lookup, :types) - ScaleRb.build_types(types) + def build_registry(metadata_prefixed) + types = ScaleRb::Utils.get(metadata_prefixed, :metadata, :V14, :lookup, :types) + ScaleRb::PortableRegistry.new(types) end def get_module(pallet_name, metadata_prefixed) @@ -50,6 +50,12 @@ def get_call_type(pallet_name, call_name, metadata_prefixed) variant._get(:name).downcase == call_name.downcase end end + + def signature_type(metadata_prefixed); end + + def signed_extensions(metadata_prefixed) + ScaleRb::Utils.get(metadata_prefixed, :metadata, :V14, :extrinsic, :signedExtensions) + end end TYPES = { diff --git a/lib/scale_rb/metadata/registry.rb b/lib/scale_rb/metadata/registry.rb deleted file mode 100644 index b8c6c59..0000000 --- a/lib/scale_rb/metadata/registry.rb +++ /dev/null @@ -1,269 +0,0 @@ -# frozen_string_literal: true - -require_relative './type_exp' -require_relative '../types' - -# rubocop:disable all -module ScaleRb - module Metadata - class Registry - include Types - - # Map name to index of type in `types` array - # % lookup :: String -> Integer - attr_reader :lookup - - # % keys :: Integer -> String - attr_reader :keys - - # % types :: Array - attr_reader :types - - attr_reader :old_types - - # % initialize :: Hash -> void - def initialize(old_types) - @old_types = old_types - @lookup = {} - @keys = {} - @types = [] - - build() - end - - def build() - @old_types.keys.each do |name| - use(name.to_s) - end - end - - def [](identifier) - if identifier.is_a?(::Integer) - @types[identifier] - elsif identifier.is_a?(::String) - @types[use(identifier)] - else - raise "Unknown identifier type: #{identifier.class}" - end - end - - def inspect - "registry(#{@types.length} types)" - end - - def to_s - @types.map.with_index do |type, index| - "#{@keys[index]} => #{type.to_s}" - end.join("\n") - end - - # % use :: String -> Integer - def use(old_type_exp) - raise "Empty old_type_exp: #{old_type_exp}" if old_type_exp.nil? || old_type_exp.strip == '' - - ast_type = TypeExp.parse(old_type_exp) - raise "No AST type for #{old_type_exp}" if ast_type.nil? - - key = ast_type.to_s - ti = lookup[key] - return ti if ti - - ti = @types.length - @types[ti] = "Placeholder" - @lookup[key] = ti - @keys[ti] = key - @types[ti] = build_portable_type(ast_type) - ti - end - - # % build_portable_type :: NamedType | ArrayType | TupleType -> PortableType - # __ :build_portable_type, { ast_type: TypedArray[TypeExp::ArrayType | TypeExp::TupleType | TypeExp::NamedType] } => PortableType - def build_portable_type(ast_type) - case ast_type - when TypeExp::ArrayType - ArrayType.new(use(ast_type.item), ast_type.len, registry: self) - when TypeExp::TupleType - TupleType.new(ast_type.params.map { |param| use(param) }) - when TypeExp::NamedType - build_portable_type_from_named_type(ast_type) - else - raise "Unknown type: #{ast_type.class}" - end - end - - # % build_portable_type_from_named_type :: NamedType -> PortableType - def build_portable_type_from_named_type(named_type) - name = named_type.name - params = named_type.params - - definition = @old_types[name.to_sym] - return build_from_definition(name, definition) if definition - - primitive = as_primitive(name) - return primitive if primitive - - case name - when 'Vec' - item_index = use(params[0].to_s) - SequenceType.new(type: item_index, registry: self) - when 'Option' - item_index = use(params[0].to_s) - VariantType.option(item_index, self) - when 'Result' - ok_index = use(params[0].to_s) - err_index = use(params[1].to_s) - VariantType.result(ok_index, err_index, self) - when 'Compact' - # item_index = use(params[0].to_s) - # CompactType.new(type: item_index, registry: self) - CompactType.new - when 'Null' - UnitType.new - else - raise "Unknown type: #{name}" - end - end - - # % as_primitive :: String -> PrimitiveType | nil - def as_primitive(name) - case name.downcase - when /^i\d+$/ - PrimitiveType.new(primitive: "I#{name[1..]}".to_sym) - when /^u\d+$/ - PrimitiveType.new(primitive: "U#{name[1..]}".to_sym) - when /^bool$/ - PrimitiveType.new(primitive: :Bool) - when /^str$/, /^text$/ - PrimitiveType.new(primitive: :Str) - else - nil - end - end - - # % build_from_definition :: String -> OldTypeDefinition -> PortableType | TypeAlias - # - # type OldTypeDefinition = String | OldEnumDefinition | OldStructDefinition - # type OldEnumDefinition = { - # _enum: String[] | Hash, - # } - # type OldStructDefinition = { - # _struct: Hash - # } - def build_from_definition(name, definition) # rubocop:disable Metrics/MethodLength - case definition - when String - # TypeAlias.new(name, use(definition)) - alias_type_id = use(definition) - # p "alias_type_id: #{alias_type_id}" - types[alias_type_id] - when Hash - if definition[:_enum] - _build_portable_type_from_enum_definition(definition) - elsif definition[:_set] - raise 'Sets are not supported' - else - _build_portable_type_from_struct_definition(definition) - end - end - end - - private - - def _indexed_enum?(definition) - definition[:_enum].is_a?(::Hash) && definition[:_enum].values.all? { |value| value.is_a?(::Integer) } - end - - # % _build_portable_type_from_enum_definition :: Hash -> VariantType - def _build_portable_type_from_enum_definition(definition) - variants = - if definition[:_enum].is_a?(::Array) - # Simple array enum: - # { - # _enum: ['A', 'B', 'C'] - # } - definition[:_enum].map.with_index do |variant_name, index| - SimpleVariant.new(name: variant_name.to_sym, index:) - end - elsif definition[:_enum].is_a?(::Hash) - if _indexed_enum?(definition) - # Indexed enum: - # { - # _enum: { - # Variant1: 0, - # Variant2: 1, - # Variant3: 2 - # } - # } - definition[:_enum].map do |variant_name, index| - SimpleVariant.new(name: variant_name, index:) - end - else - # Mixed enum: - # { - # _enum: { - # A: 'u32', - # B: {a: 'u32', b: 'u32'}, - # C: null, - # D: ['u32', 'u32'] - # } - # } - definition[:_enum].map.with_index do |(variant_name, variant_def), index| - case variant_def - when ::String - TupleVariant.new( - name: variant_name, - index:, - tuple: TupleType.new( - tuple: [use(variant_def)], - registry: self - ), - ) - when ::Array - TupleVariant.new( - name: variant_name, - index:, - tuple: TupleType.new( - tuple: variant_def.map { |field_type| use(field_type) }, - registry: self - ) - ) - when ::Hash - StructVariant.new( - name: variant_name, - index:, - struct: StructType.new( - fields: variant_def.map do |field_name, field_type| - Field.new(name: field_name.to_s, type: use(field_type)) - end, - registry: self - ) - ) - else - raise "Unknown variant type for #{variant_name}: #{variant_def.class}" - end - end - end - end - VariantType.new(variants:, registry: self) - end - - # % _build_portable_type_from_struct_definition :: Hash -> StructType - def _build_portable_type_from_struct_definition(definition) - fields = definition.map do |field_name, field_type| - Field.new(name: field_name.to_s, type: use(field_type)) - end - StructType.new(fields:, registry: self) - end - end - end -end - -# require_relative '../../metadata/metadata' - -# begin -# registry = ScaleRb::Metadata::Registry.new ScaleRb::Metadata::TYPES -# puts registry -# rescue StandardError => e -# puts e.message -# puts e.backtrace.join("\n") -# end diff --git a/lib/scale_rb/metadata/type_exp.rb b/lib/scale_rb/metadata/type_exp.rb deleted file mode 100644 index 1c8b500..0000000 --- a/lib/scale_rb/metadata/type_exp.rb +++ /dev/null @@ -1,286 +0,0 @@ -# frozen_string_literal: true - -module ScaleRb - module Metadata - module TypeExp - class Tokenizer - attr_reader :tokens, :index - - # % tokenize :: String -> [String] - def initialize(type_exp) - @tokens = tokenize(type_exp) - @index = 0 - end - - # % next_token :: -> String - def next_token - token = @tokens[@index] - @index += 1 - token - end - - # % peek_token :: -> String - def peek_token - @tokens[@index] - end - - # % eof? :: -> Bool - def eof? - @index >= @tokens.length - end - - private - - def tokenize(type_exp) - tokens = [] - current_token = '' - - type_exp.each_char do |char| - case char - when /[a-zA-Z0-9_]/ - current_token += char - when ':', '<', '>', '(', ')', '[', ']', ',', ';', '&', "'" - tokens << current_token unless current_token.empty? - if char == ':' && tokens.last == ':' - tokens[-1] = '::' - else - tokens << char - end - current_token = '' - when /\s/ - tokens << current_token unless current_token.empty? - current_token = '' - else - raise abort - end - end - - tokens << current_token unless current_token.empty? - tokens - end - end - - class NamedType - attr_reader :name, :params - - def initialize(name, params) - @name = name - @params = params - end - - def to_s - params.empty? ? name : "#{name}<#{params.map(&:to_s).join(', ')}>" - end - end - - class ArrayType - attr_reader :item, :len - - def initialize(item, len) - @item = item - @len = len - end - - def to_s - "[#{item}; #{len}]" - end - end - - class TupleType - attr_reader :params - - def initialize(params) - @params = params - end - - def to_s - "(#{params.map(&:to_s).join(', ')})" - end - end - - # % print :: NamedType | ArrayType | TupleType -> String - def self.print(type) - type.to_s - end - - # % parse :: String -> NamedType | ArrayType | TupleType - def self.parse(type_exp) - TypeExpParser.new(type_exp).parse - end - - class TypeExpParser - def initialize(type_exp) - @type_exp = type_exp - @tokenizer = Tokenizer.new(type_exp) - @current_token = @tokenizer.next_token - end - - def parse - build_type - end - - private - - # Consume and return the current token, or nil if it doesn't equal the expected token. - def expect(token) - return unless @current_token == token - - current_token = @current_token - @current_token = @tokenizer.next_token - current_token - end - - def expect!(token) - expect(token) || raise("Expected #{token}, got #{@current_token}") - end - - # Consume and return the current token if it matches the expected regex pattern. - def expect_regex(pattern) - return unless pattern.match?(@current_token) - - current_token = @current_token - @current_token = @tokenizer.next_token - current_token - end - - def expect_regex!(pattern) - expect_regex(pattern) || raise("Expected current token matching #{pattern.inspect}, got #{@current_token}") - end - - # Consume and return a natural number (integer) if the current token matches. - def expect_nat - expect_regex(/^\d+$/)&.to_i - end - - def expect_nat! - expect_nat || raise("Expected natural number, got #{@current_token}") - end - - def expect_name - expect_regex(/^[a-zA-Z]\w*$/) - end - - def expect_name! - expect_name || raise("Expected name, got #{@current_token}") - end - - def list(sep, &block) - result = [] - item = block.call - return result if item.nil? - - result << item - while expect(sep) - item = block.call - break if item.nil? # (A, B,) - - result << item - end - result - end - - def build_tuple_type - return nil unless expect('(') - - params = list(',') { build_type } - expect!(')') - - TupleType.new(params) - end - - # [u8; 16; H128] - # [u8; 16] - def build_array_type - return nil unless expect('[') - - item = build_type - raise "Expected array item, got #{@current_token}" if item.nil? - - expect!(';') - len = expect_nat! - - # [u8; 16; H128] - if expect(';') - expect_name! # Just consume the name - end - - expect!(']') - ArrayType.new(item, len) - end - - def build_named_type - name = nil - trait = nil - item = nil - - if expect('<') - # Handle trait syntax: ::Type - # name trait item - # '::Inherent' -> 'InherentOfflineReport' - # '' -> 'Compact' - # '>::Proposal' -> 'Proposal' - name = build_named_type.name - expect!('as') - trait = build_named_type.name - expect!('>') - else - name = expect_name - return if name.nil? - end - - # Consume the :: and get the next name - item = expect_name while expect('::') - - # Handle special cases - # From subsquid's code. But where are these coming from? - if name == 'InherentOfflineReport' && name == trait && item == 'Inherent' - # Do nothing - elsif name == 'exec' && item == 'StorageKey' - name = 'ContractStorageKey' - elsif name == 'Lookup' && item == 'Source' - name = 'LookupSource' - elsif name == 'Lookup' && item == 'Target' - name = 'LookupTarget' - elsif item - # '::Item' will raise error - raise "Expected item, got #{item}" if trait == 'HasCompact' - - name = item - elsif trait == 'HasCompact' # '' - return NamedType.new('Compact', [NamedType.new(name, [])]) - end - - NamedType.new(name, type_parameters) - end - - def type_parameters - if expect('<') - params = list(',') { expect_nat || build_type } - expect!('>') - else - params = [] - end - - params - end - - # &[u8] - # &'static [u8] - def build_pointer_bytes - return nil unless expect('&') # & - - expect("'") && expect!('static') - expect!('[') - expect!('u8') - expect!(']') - NamedType.new('Vec', [NamedType.new('u8', [])]) - end - - # % build_type :: TupleType | ArrayType | NamedType - def build_type - build_tuple_type || build_array_type || build_named_type || build_pointer_bytes - end - end - end - end -end diff --git a/lib/scale_rb/old_registry.rb b/lib/scale_rb/old_registry.rb new file mode 100644 index 0000000..39b3a73 --- /dev/null +++ b/lib/scale_rb/old_registry.rb @@ -0,0 +1,551 @@ +# frozen_string_literal: true + +require_relative './types' + +# rubocop:disable all +module ScaleRb + class OldRegistry + include Types + + # Map name to index of type in `types` array + # % lookup :: String -> Integer + attr_reader :lookup + + # % keys :: Integer -> String + attr_reader :keys + + # % types :: Array + attr_reader :types + + attr_reader :old_types + + # % initialize :: Hash -> void + def initialize(old_types) + @old_types = old_types + @lookup = {} + @keys = {} + @types = [] + + build() + end + + def build() + @old_types.keys.each do |name| + use(name.to_s) + end + end + + def [](identifier) + if identifier.is_a?(::Integer) + @types[identifier] + elsif identifier.is_a?(::String) + @types[use(identifier)] + else + raise "Unknown identifier type: #{identifier.class}" + end + end + + def inspect + "registry(#{@types.length} types)" + end + + def to_s + @types.map.with_index do |type, index| + "#{@keys[index]} => #{type.to_s}" + end.join("\n") + end + + # % use :: String -> Integer + def use(old_type_exp) + raise "Empty old_type_exp: #{old_type_exp}" if old_type_exp.nil? || old_type_exp.strip == '' + + ast_type = TypeExp.parse(old_type_exp) + raise "No AST type for #{old_type_exp}" if ast_type.nil? + + key = ast_type.to_s + ti = lookup[key] + return ti if ti + + ti = @types.length + @types[ti] = "Placeholder" + @lookup[key] = ti + @keys[ti] = key + @types[ti] = build_portable_type(ast_type) + ti + end + + # % build_portable_type :: NamedType | ArrayType | TupleType -> PortableType + # __ :build_portable_type, { ast_type: TypedArray[TypeExp::ArrayType | TypeExp::TupleType | TypeExp::NamedType] } => PortableType + def build_portable_type(ast_type) + case ast_type + when TypeExp::ArrayType + ArrayType.new(use(ast_type.item), ast_type.len, registry: self) + when TypeExp::TupleType + TupleType.new(ast_type.params.map { |param| use(param) }) + when TypeExp::NamedType + build_portable_type_from_named_type(ast_type) + else + raise "Unknown type: #{ast_type.class}" + end + end + + # % build_portable_type_from_named_type :: NamedType -> PortableType + def build_portable_type_from_named_type(named_type) + name = named_type.name + params = named_type.params + + definition = @old_types[name.to_sym] + return build_from_definition(name, definition) if definition + + primitive = as_primitive(name) + return primitive if primitive + + case name + when 'Vec' + item_index = use(params[0].to_s) + SequenceType.new(type: item_index, registry: self) + when 'Option' + item_index = use(params[0].to_s) + VariantType.option(item_index, self) + when 'Result' + ok_index = use(params[0].to_s) + err_index = use(params[1].to_s) + VariantType.result(ok_index, err_index, self) + when 'Compact' + # item_index = use(params[0].to_s) + # CompactType.new(type: item_index, registry: self) + CompactType.new + when 'Null' + UnitType.new + else + raise "Unknown type: #{name}" + end + end + + # % as_primitive :: String -> PrimitiveType | nil + def as_primitive(name) + case name.downcase + when /^i\d+$/ + PrimitiveType.new(primitive: "I#{name[1..]}".to_sym) + when /^u\d+$/ + PrimitiveType.new(primitive: "U#{name[1..]}".to_sym) + when /^bool$/ + PrimitiveType.new(primitive: :Bool) + when /^str$/, /^text$/ + PrimitiveType.new(primitive: :Str) + else + nil + end + end + + # % build_from_definition :: String -> OldTypeDefinition -> PortableType | TypeAlias + # + # type OldTypeDefinition = String | OldEnumDefinition | OldStructDefinition + # type OldEnumDefinition = { + # _enum: String[] | Hash, + # } + # type OldStructDefinition = { + # _struct: Hash + # } + def build_from_definition(name, definition) # rubocop:disable Metrics/MethodLength + case definition + when String + # TypeAlias.new(name, use(definition)) + alias_type_id = use(definition) + # p "alias_type_id: #{alias_type_id}" + types[alias_type_id] + when Hash + if definition[:_enum] + _build_portable_type_from_enum_definition(definition) + elsif definition[:_set] + raise 'Sets are not supported' + else + _build_portable_type_from_struct_definition(definition) + end + end + end + + private + + def _indexed_enum?(definition) + definition[:_enum].is_a?(::Hash) && definition[:_enum].values.all? { |value| value.is_a?(::Integer) } + end + + # % _build_portable_type_from_enum_definition :: Hash -> VariantType + def _build_portable_type_from_enum_definition(definition) + variants = + if definition[:_enum].is_a?(::Array) + # Simple array enum: + # { + # _enum: ['A', 'B', 'C'] + # } + definition[:_enum].map.with_index do |variant_name, index| + SimpleVariant.new(name: variant_name.to_sym, index:) + end + elsif definition[:_enum].is_a?(::Hash) + if _indexed_enum?(definition) + # Indexed enum: + # { + # _enum: { + # Variant1: 0, + # Variant2: 1, + # Variant3: 2 + # } + # } + definition[:_enum].map do |variant_name, index| + SimpleVariant.new(name: variant_name, index:) + end + else + # Mixed enum: + # { + # _enum: { + # A: 'u32', + # B: {a: 'u32', b: 'u32'}, + # C: null, + # D: ['u32', 'u32'] + # } + # } + definition[:_enum].map.with_index do |(variant_name, variant_def), index| + case variant_def + when ::String + TupleVariant.new( + name: variant_name, + index:, + tuple: TupleType.new( + tuple: [use(variant_def)], + registry: self + ), + ) + when ::Array + TupleVariant.new( + name: variant_name, + index:, + tuple: TupleType.new( + tuple: variant_def.map { |field_type| use(field_type) }, + registry: self + ) + ) + when ::Hash + StructVariant.new( + name: variant_name, + index:, + struct: StructType.new( + fields: variant_def.map do |field_name, field_type| + Field.new(name: field_name.to_s, type: use(field_type)) + end, + registry: self + ) + ) + else + raise "Unknown variant type for #{variant_name}: #{variant_def.class}" + end + end + end + end + VariantType.new(variants:, registry: self) + end + + # % _build_portable_type_from_struct_definition :: Hash -> StructType + def _build_portable_type_from_struct_definition(definition) + fields = definition.map do |field_name, field_type| + Field.new(name: field_name.to_s, type: use(field_type)) + end + StructType.new(fields:, registry: self) + end + end +end + +module ScaleRb + class OldRegistry + module TypeExp + class Tokenizer + attr_reader :tokens, :index + + # % tokenize :: String -> [String] + def initialize(type_exp) + @tokens = tokenize(type_exp) + @index = 0 + end + + # % next_token :: -> String + def next_token + token = @tokens[@index] + @index += 1 + token + end + + # % peek_token :: -> String + def peek_token + @tokens[@index] + end + + # % eof? :: -> Bool + def eof? + @index >= @tokens.length + end + + private + + def tokenize(type_exp) + tokens = [] + current_token = '' + + type_exp.each_char do |char| + case char + when /[a-zA-Z0-9_]/ + current_token += char + when ':', '<', '>', '(', ')', '[', ']', ',', ';', '&', "'" + tokens << current_token unless current_token.empty? + if char == ':' && tokens.last == ':' + tokens[-1] = '::' + else + tokens << char + end + current_token = '' + when /\s/ + tokens << current_token unless current_token.empty? + current_token = '' + else + raise abort + end + end + + tokens << current_token unless current_token.empty? + tokens + end + end + + class NamedType + attr_reader :name, :params + + def initialize(name, params) + @name = name + @params = params + end + + def to_s + params.empty? ? name : "#{name}<#{params.map(&:to_s).join(', ')}>" + end + end + + class ArrayType + attr_reader :item, :len + + def initialize(item, len) + @item = item + @len = len + end + + def to_s + "[#{item}; #{len}]" + end + end + + class TupleType + attr_reader :params + + def initialize(params) + @params = params + end + + def to_s + "(#{params.map(&:to_s).join(', ')})" + end + end + + # % print :: NamedType | ArrayType | TupleType -> String + def self.print(type) + type.to_s + end + + # % parse :: String -> NamedType | ArrayType | TupleType + def self.parse(type_exp) + TypeExpParser.new(type_exp).parse + end + + class TypeExpParser + def initialize(type_exp) + @type_exp = type_exp + @tokenizer = Tokenizer.new(type_exp) + @current_token = @tokenizer.next_token + end + + def parse + build_type + end + + private + + # Consume and return the current token, or nil if it doesn't equal the expected token. + def expect(token) + return unless @current_token == token + + current_token = @current_token + @current_token = @tokenizer.next_token + current_token + end + + def expect!(token) + expect(token) || raise("Expected #{token}, got #{@current_token}") + end + + # Consume and return the current token if it matches the expected regex pattern. + def expect_regex(pattern) + return unless pattern.match?(@current_token) + + current_token = @current_token + @current_token = @tokenizer.next_token + current_token + end + + def expect_regex!(pattern) + expect_regex(pattern) || raise("Expected current token matching #{pattern.inspect}, got #{@current_token}") + end + + # Consume and return a natural number (integer) if the current token matches. + def expect_nat + expect_regex(/^\d+$/)&.to_i + end + + def expect_nat! + expect_nat || raise("Expected natural number, got #{@current_token}") + end + + def expect_name + expect_regex(/^[a-zA-Z]\w*$/) + end + + def expect_name! + expect_name || raise("Expected name, got #{@current_token}") + end + + def list(sep, &block) + result = [] + item = block.call + return result if item.nil? + + result << item + while expect(sep) + item = block.call + break if item.nil? # (A, B,) + + result << item + end + result + end + + def build_tuple_type + return nil unless expect('(') + + params = list(',') { build_type } + expect!(')') + + TupleType.new(params) + end + + # [u8; 16; H128] + # [u8; 16] + def build_array_type + return nil unless expect('[') + + item = build_type + raise "Expected array item, got #{@current_token}" if item.nil? + + expect!(';') + len = expect_nat! + + # [u8; 16; H128] + if expect(';') + expect_name! # Just consume the name + end + + expect!(']') + ArrayType.new(item, len) + end + + def build_named_type + name = nil + trait = nil + item = nil + + if expect('<') + # Handle trait syntax: ::Type + # name trait item + # '::Inherent' -> 'InherentOfflineReport' + # '' -> 'Compact' + # '>::Proposal' -> 'Proposal' + name = build_named_type.name + expect!('as') + trait = build_named_type.name + expect!('>') + else + name = expect_name + return if name.nil? + end + + # Consume the :: and get the next name + item = expect_name while expect('::') + + # Handle special cases + # From subsquid's code. But where are these coming from? + if name == 'InherentOfflineReport' && name == trait && item == 'Inherent' + # Do nothing + elsif name == 'exec' && item == 'StorageKey' + name = 'ContractStorageKey' + elsif name == 'Lookup' && item == 'Source' + name = 'LookupSource' + elsif name == 'Lookup' && item == 'Target' + name = 'LookupTarget' + elsif item + # '::Item' will raise error + raise "Expected item, got #{item}" if trait == 'HasCompact' + + name = item + elsif trait == 'HasCompact' # '' + return NamedType.new('Compact', [NamedType.new(name, [])]) + end + + NamedType.new(name, type_parameters) + end + + def type_parameters + if expect('<') + params = list(',') { expect_nat || build_type } + expect!('>') + else + params = [] + end + + params + end + + # &[u8] + # &'static [u8] + def build_pointer_bytes + return nil unless expect('&') # & + + expect("'") && expect!('static') + expect!('[') + expect!('u8') + expect!(']') + NamedType.new('Vec', [NamedType.new('u8', [])]) + end + + # % build_type :: TupleType | ArrayType | NamedType + def build_type + build_tuple_type || build_array_type || build_named_type || build_pointer_bytes + end + end + end + end +end + +# require_relative '../../metadata/metadata' + +# begin +# registry = ScaleRb::Metadata::Registry.new ScaleRb::Metadata::TYPES +# puts registry +# rescue StandardError => e +# puts e.message +# puts e.backtrace.join("\n") +# end diff --git a/lib/scale_rb/portable_registry.rb b/lib/scale_rb/portable_registry.rb index bc5a075..33fa048 100644 --- a/lib/scale_rb/portable_registry.rb +++ b/lib/scale_rb/portable_registry.rb @@ -24,6 +24,14 @@ def inspect "a_portable_registry" end + def to_s + "a_portable_registry" + end + + def add_type(type) + @types << type + end + private __ :build_types, {} diff --git a/lib/scale_rb/runtime_types.rb b/lib/scale_rb/runtime_types.rb new file mode 100644 index 0000000..f80be77 --- /dev/null +++ b/lib/scale_rb/runtime_types.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +require_relative './metadata/metadata' + +module ScaleRb + class RuntimeTypes + attr_reader :metadata, :version + + def initialize(hex) + @metadata_prefixed, = ScaleRb::Codec.decode( + 'MetadataPrefixed', + ScaleRb::Utils.hex_to_u8a(hex), + ScaleRb::OldRegistry.new(Metadata::TYPES) + ) + metadata = @metadata_prefixed[:metadata] + @version = metadata.keys.first + @metadata = metadata[@version] + raise "Unsupported metadata version: #{@version}" unless @version == :V14 + end + + def registry + @registry ||= + ScaleRb::PortableRegistry.new(@metadata.dig(:lookup, :types)) + end + + def pallet(pallet_name) + @metadata[:pallets].find do |pallet| + pallet[:name] == pallet_name + end + end + + def pallet_by_index(pallet_index) + @metadata[:pallets].find do |pallet| + pallet[:index] == pallet_index + end + end + + def storage(pallet_name, item_name) + pallet = pallet(pallet_name) + raise "Pallet `#{pallet_name}` not found" if pallet.nil? + + pallet.dig(:storage, :items).find do |item| + item[:name] == item_name + end + end + + # example: + # #]>> + def call(pallet_name, call_name) + calls_type = registry[calls_type_id(pallet_name)] + raise 'Calls type is not correct' unless calls_type.is_a?(ScaleRb::Types::VariantType) + + calls_type.variants.find do |variant| + variant.name == call_name.to_sym + end + end + + private + + def calls_type_id(pallet_name) + pallet = pallet(pallet_name) + raise "Pallet `#{pallet_name}` not found" if pallet.nil? + + pallet.dig(:calls, :type) + end + end +end diff --git a/lib/scale_rb/storage_helper.rb b/lib/scale_rb/storage_helper.rb index 03bb628..d6ab5f7 100644 --- a/lib/scale_rb/storage_helper.rb +++ b/lib/scale_rb/storage_helper.rb @@ -69,8 +69,8 @@ def decode_storage2(data, storage_item, registry) end def decode_storage3(data, pallet_name, item_name, metadata) - registry = Metadata.build_registry(metadata) - storage_item = Metadata.get_storage_item(pallet_name, item_name, metadata) + registry = metadata.build_registry + storage_item = metadata.storage(pallet_name, item_name) decode_storage2(data, storage_item, registry) end end diff --git a/spec/call_spec.rb b/spec/call_spec.rb index effb310..ffe0659 100644 --- a/spec/call_spec.rb +++ b/spec/call_spec.rb @@ -6,10 +6,7 @@ module ScaleRb RSpec.describe CallHelper do it 'can decode call' do - metadata = JSON.parse( - File.read('./spec/assets/pangolin2.json'), - symbolize_names: true - ) + metadata = Metadata::Metadata.from_json(File.read('./spec/assets/pangolin2.json')) callbytes = Utils.hex_to_u8a('0x0901') decoded = CallHelper.decode_call(callbytes, metadata) @@ -57,10 +54,7 @@ module ScaleRb end it 'can encode call' do - metadata = JSON.parse( - File.read('./spec/assets/pangolin2.json'), - symbolize_names: true - ) + metadata = Metadata::Metadata.from_json(File.read('./spec/assets/pangolin2.json')) call = { pallet_name: 'Deposit', call_name: 'claim', call: :claim } encoded = CallHelper.encode_call(call, metadata) diff --git a/spec/metadata_spec.rb b/spec/metadata_spec.rb index d4fed97..79fd2fd 100644 --- a/spec/metadata_spec.rb +++ b/spec/metadata_spec.rb @@ -12,17 +12,17 @@ module ScaleRb before(:all) do hex = File.read('./spec/assets/substrate-metadata-v14-hex').strip time = Benchmark.measure do - @metadata = ScaleRb::Metadata.decode_metadata(hex) + @metadata = ScaleRb::Metadata::Metadata.from_hex(hex) end puts "Decoding metadata v14: #{time.real / 60} minutes" end it 'can decode metadata v14' do - expect(@metadata[:magicNumber]).to eq(1_635_018_093) + expect(@metadata.magic_number).to eq(1_635_018_093) end it 'can get storage item from metadata v14' do - storage_item = ScaleRb::Metadata.get_storage_item('System', 'BlockHash', @metadata) + storage_item = @metadata.storage('System', 'BlockHash') expect(storage_item).to eql( { name: 'BlockHash', @@ -36,7 +36,7 @@ module ScaleRb end it 'can get module by name' do - system_module = ScaleRb::Metadata.get_module('System', @metadata) + system_module = @metadata.pallet('System') expect(system_module.keys).to eql(%i[name storage calls events constants errors index]) expect(system_module[:name]).to eql('System') expect(system_module[:calls]).to eql({ type: 141 }) @@ -44,98 +44,12 @@ module ScaleRb end it 'can get module by index' do - system_module = ScaleRb::Metadata.get_module_by_index(0, @metadata) + system_module = @metadata.pallet_by_index(0) expect(system_module.keys).to eql(%i[name storage calls events constants errors index]) end - it 'can get calls type' do - calls_type = ScaleRb::Metadata.get_calls_type('System', @metadata) - expect(calls_type).to eql( - { - id: 141, - type: { - path: %w[frame_system pallet Call], - params: [{ name: 'T', type: nil }], - def: { - variant: { - variants: [ - { - name: 'fill_block', - fields: [{ name: 'ratio', type: 45, typeName: 'Perbill', docs: [] }], - index: 0, - docs: ['A dispatch that will fill the block weight up to the given ratio.'] - }, - { - name: 'remark', - fields: [{ name: 'remark', type: 12, typeName: 'Vec', docs: [] }], - index: 1, - docs: [ - 'Make some on-chain remark.', - '', - '# ', - '- `O(1)`', - '# ' - ] - }, - { - name: 'set_heap_pages', - fields: [{ name: 'pages', type: 10, typeName: 'u64', docs: [] }], - index: 2, - docs: ["Set the number of pages in the WebAssembly environment's heap."] - }, - { - name: 'set_code', - fields: [{ name: 'code', type: 12, typeName: 'Vec', docs: [] }], - index: 3, - docs: [ - 'Set the new runtime code.', - '', - '# ', - '- `O(C + S)` where `C` length of `code` and `S` complexity of `can_set_code`', - '- 1 call to `can_set_code`: `O(S)` (calls `sp_io::misc::runtime_version` which is', - ' expensive).', - '- 1 storage write (codec `O(C)`).', - '- 1 digest item.', - '- 1 event.', - 'The weight of this function is dependent on the runtime, but generally this is very', - 'expensive. We will treat this as a full block.', - '# ' - ] - }, - { - name: 'set_code_without_checks', - fields: [{ name: 'code', type: 12, typeName: 'Vec', docs: [] }], - index: 4, - docs: [ - 'Set the new runtime code without doing any checks of the given `code`.', - '', - '# ', - '- `O(C)` where `C` length of `code`', - '- 1 storage write (codec `O(C)`).', - '- 1 digest item.', - '- 1 event.', - 'The weight of this function is dependent on the runtime. We will treat this as a full', 'block. # ' - ] - }, - { name: 'set_storage', fields: [{ name: 'items', type: 142, typeName: 'Vec', docs: [] }], - index: 5, docs: ['Set some items of storage.'] }, - { name: 'kill_storage', fields: [{ name: 'keys', type: 144, typeName: 'Vec', docs: [] }], - index: 6, docs: ['Kill some items from storage.'] }, - { name: 'kill_prefix', - fields: [{ name: 'prefix', type: 12, typeName: 'Key', docs: [] }, { name: 'subkeys', type: 4, typeName: 'u32', docs: [] }], index: 7, docs: ['Kill all storage items with a key that starts with the given prefix.', '', '**NOTE:** We rely on the Root origin to provide us the number of subkeys under', 'the prefix we are removing to accurately calculate the weight of this function.'] }, - { name: 'remark_with_event', fields: [{ name: 'remark', type: 12, typeName: 'Vec', docs: [] }], - index: 8, docs: ['Make some on-chain remark and emit event.'] } - ] - } - }, - docs: ['Contains one variant per dispatchable that can be called by an extrinsic.'] - } - } - ) - end - it 'can get call type' do - call_type = ScaleRb::Metadata.get_call_type('System', 'remark', @metadata) + call_type = @metadata.call('System', 'remark') expect(call_type).to eql( { name: 'remark', @@ -152,9 +66,18 @@ module ScaleRb ) end - it 'can build registry from its lookup' do - registry = ScaleRb::Metadata.build_registry(@metadata) - expect(registry.types.length).to eq(696) - end + # it 'can get signature type' do + # expect = [ + # { identifier: 'CheckNonZeroSender', type: 686, additionalSigned: 31 }, + # { identifier: 'CheckSpecVersion', type: 687, additionalSigned: 4 }, + # { identifier: 'CheckTxVersion', type: 688, additionalSigned: 4 }, + # { identifier: 'CheckGenesis', type: 689, additionalSigned: 11 }, + # { identifier: 'CheckMortality', type: 690, additionalSigned: 11 }, + # { identifier: 'CheckNonce', type: 692, additionalSigned: 31 }, + # { identifier: 'CheckWeight', type: 693, additionalSigned: 31 }, + # { identifier: 'ChargeAssetTxPayment', type: 694, additionalSigned: 31 } + # ] + # expect(ScaleRb::Metadata.signed_extensions_type(@metadata)).to eq(expect) + # end end end diff --git a/spec/runtime_types_spec.rb b/spec/runtime_types_spec.rb new file mode 100644 index 0000000..fe1a902 --- /dev/null +++ b/spec/runtime_types_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'scale_rb' + +RSpec.describe ScaleRb::RuntimeTypes do + let(:hex) { File.read('spec/assets/substrate-metadata-v14-hex').strip } + let(:runtime_types) { ScaleRb::RuntimeTypes.new(hex) } + + it 'will raise error if metadata version is not v14' do + hex_v13 = File.read('spec/assets/substrate-metadata-v13-hex').strip + expect { ScaleRb::RuntimeTypes.new(hex_v13) }.to raise_error(RuntimeError) + end + + it 'have version' do + expect(runtime_types.version).to eq(:V14) + end + + it 'have portable registry' do + expect(runtime_types.registry.types.length).to eq(696) + end + + it 'can get a pallet' do + expect(runtime_types.pallet('System')).not_to be_nil + end + + it 'can get a pallet by index' do + expect(runtime_types.pallet_by_index(0)).not_to be_nil + end + + it 'can get a storage' do + puts runtime_types.storage('System', 'BlockHash') + expect(runtime_types.storage('System', 'BlockHash')).not_to be_nil + end + + it 'can get a call' do + expect(runtime_types.call_type_id('System', 'remark')).to eql(1) + end +end diff --git a/spec/storage_helper_spec.rb b/spec/storage_helper_spec.rb index 88957ef..9237ff5 100644 --- a/spec/storage_helper_spec.rb +++ b/spec/storage_helper_spec.rb @@ -68,7 +68,7 @@ module ScaleRb end it 'can decode system events' do - metadata = JSON.parse(File.open(File.join(__dir__, 'assets', 'darwinia-metadata.1243.json')).read) + metadata = Metadata::Metadata.from_json(File.read('./spec/assets/darwinia-metadata.1243.json')) data = '0x1800000000000000585f8f09000000000200000001000000040964766d3a000000000000002eabe5c6818731e282b80de1a03f8190426e0dd996404b4c00000000000000000000000000000001000000040764766d3a000000000000002eabe5c6818731e282b80de1a03f8190426e0dd996623116000000000000000000000000000000010000002f009c266c48f07121181d8424768f0ded0170cc63a6044c6030db06afe5c2251138fd7b0c3aef3876f9f60cecfae80a2e3b9cdd3b6d5d810200000000000000000000000000000000000000000000000000000000004b32200000000000000000000000000000000000000000000000000000000000051210db0ddcce0c5a3514e4396b69edac100b112deb966d7a6ee4ab8423edfc779b58f9ed9f96d0c7ba91d11970ea62f7648a7ba440ebacdcc1023c3ba310280cc7239edd250ff23d036d7d9ffc03377346814463d22f3e50fac3179f49a9c30e642c00000100000030002eabe5c6818731e282b80de1a03f8190426e0dd99c266c48f07121181d8424768f0ded0170cc63a6757a2695ae238d39120f2897d6e555b144c90f62ef457009bd83afc1dafc2e6b0000000001000000000080bf490521000000000000' storage = StorageHelper.decode_storage3(data, 'System', 'Events', metadata) expect = diff --git a/spec/tokenizer_spec.rb b/spec/tokenizer_spec.rb index 1bfac0f..e948982 100644 --- a/spec/tokenizer_spec.rb +++ b/spec/tokenizer_spec.rb @@ -3,7 +3,7 @@ require 'scale_rb' # rubocop:disable Metrics/BlockLength -RSpec.describe ScaleRb::Metadata::TypeExp::Tokenizer do +RSpec.describe ScaleRb::OldRegistry::TypeExp::Tokenizer do describe '.tokenize' do it 'tokenizes a simple type' do tokenizer = described_class.new('A') diff --git a/spec/type_exp_spec.rb b/spec/type_exp_spec.rb index 35f225c..7c3d047 100644 --- a/spec/type_exp_spec.rb +++ b/spec/type_exp_spec.rb @@ -2,7 +2,7 @@ require 'scale_rb' -RSpec.describe ScaleRb::Metadata::TypeExp do +RSpec.describe ScaleRb::OldRegistry::TypeExp do def self.ast(exp, type) it "AST: #{exp}" do parsed = described_class.parse(exp) @@ -19,33 +19,34 @@ def self.test(exp, result = nil) end describe 'AST parsing' do - ast('A', ScaleRb::Metadata::TypeExp::NamedType.new('A', [])) + ast('A', ScaleRb::OldRegistry::TypeExp::NamedType.new('A', [])) ast('Compact', - ScaleRb::Metadata::TypeExp::NamedType.new('Compact', [])) + ScaleRb::OldRegistry::TypeExp::NamedType.new('Compact', [])) ast('Compact', - ScaleRb::Metadata::TypeExp::NamedType.new('Compact', [ScaleRb::Metadata::TypeExp::NamedType.new('u8', [])])) + ScaleRb::OldRegistry::TypeExp::NamedType.new('Compact', + [ScaleRb::OldRegistry::TypeExp::NamedType.new('u8', [])])) ast('Vec', - ScaleRb::Metadata::TypeExp::NamedType.new('Vec', [ScaleRb::Metadata::TypeExp::NamedType.new('u8', [])])) + ScaleRb::OldRegistry::TypeExp::NamedType.new('Vec', [ScaleRb::OldRegistry::TypeExp::NamedType.new('u8', [])])) ast('[A; 10]', - ScaleRb::Metadata::TypeExp::ArrayType.new(ScaleRb::Metadata::TypeExp::NamedType.new('A', []), 10)) + ScaleRb::OldRegistry::TypeExp::ArrayType.new(ScaleRb::OldRegistry::TypeExp::NamedType.new('A', []), 10)) ast('[u8; 16; H128]', - ScaleRb::Metadata::TypeExp::ArrayType.new(ScaleRb::Metadata::TypeExp::NamedType.new('u8', []), 16)) + ScaleRb::OldRegistry::TypeExp::ArrayType.new(ScaleRb::OldRegistry::TypeExp::NamedType.new('u8', []), 16)) ast( '(A, B, [u8; 5])', - ScaleRb::Metadata::TypeExp::TupleType.new( + ScaleRb::OldRegistry::TypeExp::TupleType.new( [ - ScaleRb::Metadata::TypeExp::NamedType.new('A', []), - ScaleRb::Metadata::TypeExp::NamedType.new( + ScaleRb::OldRegistry::TypeExp::NamedType.new('A', []), + ScaleRb::OldRegistry::TypeExp::NamedType.new( 'B', [] ), - ScaleRb::Metadata::TypeExp::ArrayType.new( - ScaleRb::Metadata::TypeExp::NamedType.new( + ScaleRb::OldRegistry::TypeExp::ArrayType.new( + ScaleRb::OldRegistry::TypeExp::NamedType.new( 'u8', [] ), 5 )