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..0d63c2d 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' @@ -34,3 +36,5 @@ def debug(key, value) # clients require 'scale_rb/client/http_client' require 'scale_rb/client/ws_client' + +require 'scale_rb/metadata/types_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_share.rb similarity index 96% rename from lib/scale_rb/client/client_ext.rb rename to lib/scale_rb/client/client_share.rb index 9dfac87..2589091 100644 --- a/lib/scale_rb/client/client_ext.rb +++ b/lib/scale_rb/client/client_share.rb @@ -1,11 +1,11 @@ module ScaleRb # This module is used to add extra methods to both the ScaleRb::WsClient ScaleRb::HttpClient - module ClientExt + module ClientShare # get decoded metadata at block_hash 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/client/http_client.rb b/lib/scale_rb/client/http_client.rb index 31dea89..fe96f08 100644 --- a/lib/scale_rb/client/http_client.rb +++ b/lib/scale_rb/client/http_client.rb @@ -4,11 +4,11 @@ require 'net/http' require 'json' -require_relative 'client_ext' +require_relative 'client_share' module ScaleRb class HttpClient - include ClientExt + include ClientShare attr_accessor :supported_methods def initialize(url) diff --git a/lib/scale_rb/client/ws_client.rb b/lib/scale_rb/client/ws_client.rb index 5692edb..4cc347a 100644 --- a/lib/scale_rb/client/ws_client.rb +++ b/lib/scale_rb/client/ws_client.rb @@ -2,7 +2,7 @@ require 'async/websocket/client' require 'async/http/endpoint' -require_relative 'client_ext' +require_relative 'client_share' module ScaleRb class WsClient @@ -52,7 +52,7 @@ def parse_message(message) module ScaleRb class WsClient - include ClientExt + include ClientShare attr_accessor :supported_methods def initialize(connection) 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..dfbe598 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,94 +11,102 @@ # 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, :registry + + def initialize(metadata_prefixed, types = nil) + @metadata_prefixed = metadata_prefixed + @magic_number = @metadata_prefixed[:magicNumber] + metadata = @metadata_prefixed[:metadata] + @version = metadata.keys.first + raise "Unsupported metadata version: #{@version}" unless [:V14, :V13, :V12, :V11, :V10, :V9].include?(@version) + + @metadata = metadata[@version] + if @version == :V14 + @registry = ScaleRb::PortableRegistry.new(@metadata.dig(:lookup, :types)) + else + @registry = ScaleRb::OldRegistry.new(types) + end 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) - - Metadata.const_get("Metadata#{version.upcase}").get_module_by_index(pallet_index, metadata_prefixed) + def to_json(*_args) + JSON.pretty_generate(@metadata_prefixed) 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 TYPES = { - Type: 'Str', - Bytes: 'Vec', - MetadataPrefixed: { - magicNumber: 'U32', - metadata: 'Metadata' + 'Type' => 'Str', + 'Bytes' => 'Vec', + 'MetadataPrefixed' => { + 'magicNumber' => 'U32', + 'metadata' => 'Metadata' }, - Placeholder: 'Null', - Metadata: { - _enum: { - V0: 'Placeholder', - V1: 'Placeholder', - V2: 'Placeholder', - V3: 'Placeholder', - V4: 'Placeholder', - V5: 'Placeholder', - V6: 'Placeholder', - V7: 'Placeholder', - V8: 'Placeholder', - V9: 'MetadataV9', - V10: 'MetadataV10', - V11: 'MetadataV11', - V12: 'MetadataV12', - V13: 'MetadataV13', - V14: 'MetadataV14' + 'Placeholder' => 'Null', + 'Metadata' => { + '_enum' => { + 'V0' => 'Placeholder', + 'V1' => 'Placeholder', + 'V2' => 'Placeholder', + 'V3' => 'Placeholder', + 'V4' => 'Placeholder', + 'V5' => 'Placeholder', + 'V6' => 'Placeholder', + 'V7' => 'Placeholder', + 'V8' => 'Placeholder', + 'V9' => 'MetadataV9', + 'V10' => 'MetadataV10', + 'V11' => 'MetadataV11', + 'V12' => 'MetadataV12', + 'V13' => 'MetadataV13', + 'V14' => 'MetadataV14' } } }.merge(MetadataV14::TYPES) diff --git a/lib/scale_rb/metadata/metadata_v10.rb b/lib/scale_rb/metadata/metadata_v10.rb index 435ce04..cc18c6b 100644 --- a/lib/scale_rb/metadata/metadata_v10.rb +++ b/lib/scale_rb/metadata/metadata_v10.rb @@ -4,54 +4,54 @@ module ScaleRb module Metadata module MetadataV10 TYPES = { - ErrorMetadataV10: 'ErrorMetadataV9', - EventMetadataV10: 'EventMetadataV9', - FunctionArgumentMetadataV10: 'FunctionArgumentMetadataV9', - FunctionMetadataV10: 'FunctionMetadataV9', - MetadataV10: { - modules: 'Vec' + 'ErrorMetadataV10' => 'ErrorMetadataV9', + 'EventMetadataV10' => 'EventMetadataV9', + 'FunctionArgumentMetadataV10' => 'FunctionArgumentMetadataV9', + 'FunctionMetadataV10' => 'FunctionMetadataV9', + 'MetadataV10' => { + 'modules' => 'Vec' }, - ModuleConstantMetadataV10: 'ModuleConstantMetadataV9', - ModuleMetadataV10: { - name: 'Text', - storage: 'Option', - calls: 'Option>', - events: 'Option>', - constants: 'Vec', - errors: 'Vec' + 'ModuleConstantMetadataV10' => 'ModuleConstantMetadataV9', + 'ModuleMetadataV10' => { + 'name' => 'Text', + 'storage' => 'Option', + 'calls' => 'Option>', + 'events' => 'Option>', + 'constants' => 'Vec', + 'errors' => 'Vec' }, - StorageEntryModifierV10: 'StorageEntryModifierV9', - StorageEntryMetadataV10: { - name: 'Text', - modifier: 'StorageEntryModifierV10', - type: 'StorageEntryTypeV10', - fallback: 'Bytes', - docs: 'Vec' + 'StorageEntryModifierV10' => 'StorageEntryModifierV9', + 'StorageEntryMetadataV10' => { + 'name' => 'Text', + 'modifier' => 'StorageEntryModifierV10', + 'type' => 'StorageEntryTypeV10', + 'fallback' => 'Bytes', + 'docs' => 'Vec' }, - StorageEntryTypeV10: { - _enum: { - Plain: 'Type', - Map: { - hasher: 'StorageHasherV10', - key: 'Type', - value: 'Type', - linked: 'bool' + 'StorageEntryTypeV10' => { + '_enum' => { + 'Plain' => 'Type', + 'Map' => { + 'hasher' => 'StorageHasherV10', + 'key' => 'Type', + 'value' => 'Type', + 'linked' => 'bool' }, - DoubleMap: { - hasher: 'StorageHasherV10', - key1: 'Type', - key2: 'Type', - value: 'Type', - key2Hasher: 'StorageHasherV10' + 'DoubleMap' => { + 'hasher' => 'StorageHasherV10', + 'key1' => 'Type', + 'key2' => 'Type', + 'value' => 'Type', + 'key2Hasher' => 'StorageHasherV10' } } }, - StorageMetadataV10: { - prefix: 'Text', - items: 'Vec' + 'StorageMetadataV10' => { + 'prefix' => 'Text', + 'items' => 'Vec' }, - StorageHasherV10: { - _enum: %w[ + 'StorageHasherV10' => { + '_enum' => %w[ Blake2_128 Blake2_256 Blake2_128Concat diff --git a/lib/scale_rb/metadata/metadata_v11.rb b/lib/scale_rb/metadata/metadata_v11.rb index c1c4e1e..ac39ba7 100644 --- a/lib/scale_rb/metadata/metadata_v11.rb +++ b/lib/scale_rb/metadata/metadata_v11.rb @@ -4,59 +4,59 @@ module ScaleRb module Metadata module MetadataV11 TYPES = { - ErrorMetadataV11: 'ErrorMetadataV10', - EventMetadataV11: 'EventMetadataV10', - ExtrinsicMetadataV11: { - version: 'u8', - signedExtensions: 'Vec' + 'ErrorMetadataV11' => 'ErrorMetadataV10', + 'EventMetadataV11' => 'EventMetadataV10', + 'ExtrinsicMetadataV11' => { + 'version' => 'u8', + 'signedExtensions' => 'Vec' }, - FunctionArgumentMetadataV11: 'FunctionArgumentMetadataV10', - FunctionMetadataV11: 'FunctionMetadataV10', - MetadataV11: { - modules: 'Vec', - extrinsic: 'ExtrinsicMetadataV11' + 'FunctionArgumentMetadataV11' => 'FunctionArgumentMetadataV10', + 'FunctionMetadataV11' => 'FunctionMetadataV10', + 'MetadataV11' => { + 'modules' => 'Vec', + 'extrinsic' => 'ExtrinsicMetadataV11' }, - ModuleConstantMetadataV11: 'ModuleConstantMetadataV10', - ModuleMetadataV11: { - name: 'Text', - storage: 'Option', - calls: 'Option>', - events: 'Option>', - constants: 'Vec', - errors: 'Vec' + 'ModuleConstantMetadataV11' => 'ModuleConstantMetadataV10', + 'ModuleMetadataV11' => { + 'name' => 'Text', + 'storage' => 'Option', + 'calls' => 'Option>', + 'events' => 'Option>', + 'constants' => 'Vec', + 'errors' => 'Vec' }, - StorageEntryModifierV11: 'StorageEntryModifierV10', - StorageEntryMetadataV11: { - name: 'Text', - modifier: 'StorageEntryModifierV11', - type: 'StorageEntryTypeV11', - fallback: 'Bytes', - docs: 'Vec' + 'StorageEntryModifierV11' => 'StorageEntryModifierV10', + 'StorageEntryMetadataV11' => { + 'name' => 'Text', + 'modifier' => 'StorageEntryModifierV11', + 'type' => 'StorageEntryTypeV11', + 'fallback' => 'Bytes', + 'docs' => 'Vec' }, - StorageEntryTypeV11: { - _enum: { - Plain: 'Type', - Map: { - hasher: 'StorageHasherV11', - key: 'Type', - value: 'Type', - linked: 'bool' + 'StorageEntryTypeV11' => { + '_enum' => { + 'Plain' => 'Type', + 'Map' => { + 'hasher' => 'StorageHasherV11', + 'key' => 'Type', + 'value' => 'Type', + 'linked' => 'bool' }, - DoubleMap: { - hasher: 'StorageHasherV11', - key1: 'Type', - key2: 'Type', - value: 'Type', - key2Hasher: 'StorageHasherV11' + 'DoubleMap' => { + 'hasher' => 'StorageHasherV11', + 'key1' => 'Type', + 'key2' => 'Type', + 'value' => 'Type', + 'key2Hasher' => 'StorageHasherV11' } } }, - StorageMetadataV11: { - prefix: 'Text', - items: 'Vec' + 'StorageMetadataV11' => { + 'prefix' => 'Text', + 'items' => 'Vec' }, - StorageHasherV11: { - _enum: %w[ + 'StorageHasherV11' => { + '_enum' => %w[ Blake2_128 Blake2_256 Blake2_128Concat diff --git a/lib/scale_rb/metadata/metadata_v12.rb b/lib/scale_rb/metadata/metadata_v12.rb index ccb9c77..1ab8b38 100644 --- a/lib/scale_rb/metadata/metadata_v12.rb +++ b/lib/scale_rb/metadata/metadata_v12.rb @@ -4,30 +4,30 @@ module ScaleRb module Metadata module MetadataV12 TYPES = { - ErrorMetadataV12: 'ErrorMetadataV11', - EventMetadataV12: 'EventMetadataV11', - ExtrinsicMetadataV12: 'ExtrinsicMetadataV11', - FunctionArgumentMetadataV12: 'FunctionArgumentMetadataV11', - FunctionMetadataV12: 'FunctionMetadataV11', - MetadataV12: { - modules: 'Vec', - extrinsic: 'ExtrinsicMetadataV12' + 'ErrorMetadataV12' => 'ErrorMetadataV11', + 'EventMetadataV12' => 'EventMetadataV11', + 'ExtrinsicMetadataV12' => 'ExtrinsicMetadataV11', + 'FunctionArgumentMetadataV12' => 'FunctionArgumentMetadataV11', + 'FunctionMetadataV12' => 'FunctionMetadataV11', + 'MetadataV12' => { + 'modules' => 'Vec', + 'extrinsic' => 'ExtrinsicMetadataV12' }, - ModuleConstantMetadataV12: 'ModuleConstantMetadataV11', - ModuleMetadataV12: { - name: 'Text', - storage: 'Option', - calls: 'Option>', - events: 'Option>', - constants: 'Vec', - errors: 'Vec', - index: 'u8' + 'ModuleConstantMetadataV12' => 'ModuleConstantMetadataV11', + 'ModuleMetadataV12' => { + 'name' => 'Text', + 'storage' => 'Option', + 'calls' => 'Option>', + 'events' => 'Option>', + 'constants' => 'Vec', + 'errors' => 'Vec', + 'index' => 'u8' }, - StorageEntryModifierV12: 'StorageEntryModifierV11', - StorageEntryMetadataV12: 'StorageEntryMetadataV11', - StorageEntryTypeV12: 'StorageEntryTypeV11', - StorageMetadataV12: 'StorageMetadataV11', - StorageHasherV12: 'StorageHasherV11' + 'StorageEntryModifierV12' => 'StorageEntryModifierV11', + 'StorageEntryMetadataV12' => 'StorageEntryMetadataV11', + 'StorageEntryTypeV12' => 'StorageEntryTypeV11', + 'StorageMetadataV12' => 'StorageMetadataV11', + 'StorageHasherV12' => 'StorageHasherV11' }.freeze end end diff --git a/lib/scale_rb/metadata/metadata_v13.rb b/lib/scale_rb/metadata/metadata_v13.rb index b34f88a..bf5b4a8 100644 --- a/lib/scale_rb/metadata/metadata_v13.rb +++ b/lib/scale_rb/metadata/metadata_v13.rb @@ -4,61 +4,61 @@ module ScaleRb module Metadata module MetadataV13 TYPES = { - MetadataV13: { - modules: 'Vec', - extrinsic: 'ExtrinsicMetadataV13' + 'MetadataV13' => { + 'modules' => 'Vec', + 'extrinsic' => 'ExtrinsicMetadataV13' }, - ModuleMetadataV13: { - name: 'Text', - storage: 'Option', - calls: 'Option>', - events: 'Option>', - constants: 'Vec', - errors: 'Vec', - index: 'u8' + 'ModuleMetadataV13' => { + 'name' => 'Text', + 'storage' => 'Option', + 'calls' => 'Option>', + 'events' => 'Option>', + 'constants' => 'Vec', + 'errors' => 'Vec', + 'index' => 'u8' }, - StorageMetadataV13: { - prefix: 'Text', - items: 'Vec' + 'StorageMetadataV13' => { + 'prefix' => 'Text', + 'items' => 'Vec' }, - StorageEntryMetadataV13: { - name: 'Text', - modifier: 'StorageEntryModifierV13', - type: 'StorageEntryTypeV13', - fallback: 'Bytes', - docs: 'Vec' + 'StorageEntryMetadataV13' => { + 'name' => 'Text', + 'modifier' => 'StorageEntryModifierV13', + 'type' => 'StorageEntryTypeV13', + 'fallback' => 'Bytes', + 'docs' => 'Vec' }, - StorageEntryModifierV13: 'StorageEntryModifierV12', - StorageEntryTypeV13: { - _enum: { - plain: 'Type', - map: { - hasher: 'StorageHasherV13', - key: 'Type', - value: 'Type', - linked: 'bool' + 'StorageEntryModifierV13' => 'StorageEntryModifierV12', + 'StorageEntryTypeV13' => { + '_enum' => { + 'plain' => 'Type', + 'map' => { + 'hasher' => 'StorageHasherV13', + 'key' => 'Type', + 'value' => 'Type', + 'linked' => 'bool' }, - doubleMap: { - hasher: 'StorageHasherV13', - key1: 'Type', - key2: 'Type', - value: 'Type', - key2Hasher: 'StorageHasherV13' + 'doubleMap' => { + 'hasher' => 'StorageHasherV13', + 'key1' => 'Type', + 'key2' => 'Type', + 'value' => 'Type', + 'key2Hasher' => 'StorageHasherV13' }, - nMap: { - keyVec: 'Vec', - hashers: 'Vec', - value: 'Type' + 'nMap' => { + 'keyVec' => 'Vec', + 'hashers' => 'Vec', + 'value' => 'Type' } } }, - StorageHasherV13: 'StorageHasherV12', - FunctionMetadataV13: 'FunctionMetadataV12', - EventMetadataV13: 'EventMetadataV12', - ModuleConstantMetadataV13: 'ModuleConstantMetadataV12', - ErrorMetadataV13: 'ErrorMetadataV12', - ExtrinsicMetadataV13: 'ExtrinsicMetadataV12' + 'StorageHasherV13' => 'StorageHasherV12', + 'FunctionMetadataV13' => 'FunctionMetadataV12', + 'EventMetadataV13' => 'EventMetadataV12', + 'ModuleConstantMetadataV13' => 'ModuleConstantMetadataV12', + 'ErrorMetadataV13' => 'ErrorMetadataV12', + 'ExtrinsicMetadataV13' => 'ExtrinsicMetadataV12' }.freeze end end diff --git a/lib/scale_rb/metadata/metadata_v14.rb b/lib/scale_rb/metadata/metadata_v14.rb index 0bca39f..c7dc2ca 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,157 +50,163 @@ 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 = { - MetadataV14: { - lookup: 'PortableRegistry', - pallets: 'Vec', - extrinsic: 'ExtrinsicMetadataV14', - type: 'SiLookupTypeId' + 'MetadataV14' => { + 'lookup' => 'PortableRegistry', + 'pallets' => 'Vec', + 'extrinsic' => 'ExtrinsicMetadataV14', + 'type' => 'SiLookupTypeId' }, # PortableRegistry begin - PortableRegistry: { - types: 'Vec' - }, - PortableTypeV14: { - id: 'Si1LookupTypeId', - type: 'Si1Type' - }, - Si1LookupTypeId: 'Compact', - Si1Type: { - path: 'Si1Path', - params: 'Vec', - def: 'Si1TypeDef', - docs: 'Vec' - }, - Si1Path: 'Vec', - Si1TypeParameter: { - name: 'Text', - type: 'Option' - }, - Si1TypeDef: { - _enum: { - composite: 'Si1TypeDefComposite', - variant: 'Si1TypeDefVariant', - sequence: 'Si1TypeDefSequence', - array: 'Si1TypeDefArray', - tuple: 'Si1TypeDefTuple', - primitive: 'Si1TypeDefPrimitive', - compact: 'Si1TypeDefCompact', - bitSequence: 'Si1TypeDefBitSequence', - historicMetaCompat: 'Text' # TODO: sanitize? + 'PortableRegistry' => { + 'types' => 'Vec' + }, + 'PortableTypeV14' => { + 'id' => 'Si1LookupTypeId', + 'type' => 'Si1Type' + }, + 'Si1LookupTypeId' => 'Compact', + 'Si1Type' => { + 'path' => 'Si1Path', + 'params' => 'Vec', + 'def' => 'Si1TypeDef', + 'docs' => 'Vec' + }, + 'Si1Path' => 'Vec', + 'Si1TypeParameter' => { + 'name' => 'Text', + 'type' => 'Option' + }, + 'Si1TypeDef' => { + '_enum' => { + 'composite' => 'Si1TypeDefComposite', + 'variant' => 'Si1TypeDefVariant', + 'sequence' => 'Si1TypeDefSequence', + 'array' => 'Si1TypeDefArray', + 'tuple' => 'Si1TypeDefTuple', + 'primitive' => 'Si1TypeDefPrimitive', + 'compact' => 'Si1TypeDefCompact', + 'bitSequence' => 'Si1TypeDefBitSequence', + 'historicMetaCompat' => 'Text' } }, - Si1TypeDefComposite: { - fields: 'Vec' + 'Si1TypeDefComposite' => { + 'fields' => 'Vec' }, - Si1Field: { - name: 'Option', - type: 'Si1LookupTypeId', - typeName: 'Option', - docs: 'Vec' + 'Si1Field' => { + 'name' => 'Option', + 'type' => 'Si1LookupTypeId', + 'typeName' => 'Option', + 'docs' => 'Vec' }, - Si1TypeDefVariant: { - variants: 'Vec' + 'Si1TypeDefVariant' => { + 'variants' => 'Vec' }, - Si1Variant: { - name: 'Text', - fields: 'Vec', - index: 'u8', - docs: 'Vec' + 'Si1Variant' => { + 'name' => 'Text', + 'fields' => 'Vec', + 'index' => 'u8', + 'docs' => 'Vec' }, - Si1TypeDefSequence: { - type: 'Si1LookupTypeId' + 'Si1TypeDefSequence' => { + 'type' => 'Si1LookupTypeId' }, - Si1TypeDefArray: { - len: 'u32', - type: 'Si1LookupTypeId' + 'Si1TypeDefArray' => { + 'len' => 'u32', + 'type' => 'Si1LookupTypeId' }, - Si1TypeDefTuple: 'Vec', - Si1TypeDefPrimitive: { - _enum: %w[ + 'Si1TypeDefTuple' => 'Vec', + 'Si1TypeDefPrimitive' => { + '_enum' => %w[ Bool Char Str U8 U16 U32 U64 U128 U256 I8 I16 I32 I64 I128 I256 ] }, - Si1TypeDefCompact: { - type: 'Si1LookupTypeId' + 'Si1TypeDefCompact' => { + 'type' => 'Si1LookupTypeId' }, - Si1TypeDefBitSequence: { - bitStoreType: 'Si1LookupTypeId', - bitOrderType: 'Si1LookupTypeId' + 'Si1TypeDefBitSequence' => { + 'bitStoreType' => 'Si1LookupTypeId', + 'bitOrderType' => 'Si1LookupTypeId' }, # PortableRegistry end # PalletMetadataV14 begin - PalletMetadataV14: { - name: 'Text', - storage: 'Option', - calls: 'Option', - events: 'Option', - constants: 'Vec', - errors: 'Option', - index: 'U8' - }, - PalletStorageMetadataV14: { - prefix: 'Text', - items: 'Vec' - }, - StorageEntryMetadataV14: { - name: 'Text', - modifier: 'StorageEntryModifierV14', - type: 'StorageEntryTypeV14', - fallback: 'Bytes', - docs: 'Vec' - }, - StorageEntryModifierV14: { - _enum: %w[Optional Default Required] - }, - StorageEntryTypeV14: { - _enum: { - plain: 'SiLookupTypeId', - map: { - hashers: 'Vec', - key: 'SiLookupTypeId', - value: 'SiLookupTypeId' + 'PalletMetadataV14' => { + 'name' => 'Text', + 'storage' => 'Option', + 'calls' => 'Option', + 'events' => 'Option', + 'constants' => 'Vec', + 'errors' => 'Option', + 'index' => 'U8' + }, + 'PalletStorageMetadataV14' => { + 'prefix' => 'Text', + 'items' => 'Vec' + }, + 'StorageEntryMetadataV14' => { + 'name' => 'Text', + 'modifier' => 'StorageEntryModifierV14', + 'type' => 'StorageEntryTypeV14', + 'fallback' => 'Bytes', + 'docs' => 'Vec' + }, + 'StorageEntryModifierV14' => { + '_enum' => %w[Optional Default Required] + }, + 'StorageEntryTypeV14' => { + '_enum' => { + 'plain' => 'SiLookupTypeId', + 'map' => { + 'hashers' => 'Vec', + 'key' => 'SiLookupTypeId', + 'value' => 'SiLookupTypeId' } } }, - StorageHasherV14: { - _enum: %w[Blake2128 Blake2256 Blake2128Concat Twox128 Twox256 Twox64Concat Identity] + 'StorageHasherV14' => { + '_enum' => %w[Blake2128 Blake2256 Blake2128Concat Twox128 Twox256 Twox64Concat Identity] }, - PalletCallMetadataV14: { - type: 'Si1LookupTypeId' + 'PalletCallMetadataV14' => { + 'type' => 'Si1LookupTypeId' }, - PalletEventMetadataV14: { - type: 'SiLookupTypeId' + 'PalletEventMetadataV14' => { + 'type' => 'SiLookupTypeId' }, - PalletConstantMetadataV14: { - name: 'Text', - type: 'SiLookupTypeId', - value: 'Bytes', - docs: 'Vec' + 'PalletConstantMetadataV14' => { + 'name' => 'Text', + 'type' => 'SiLookupTypeId', + 'value' => 'Bytes', + 'docs' => 'Vec' }, - PalletErrorMetadataV14: { - type: 'SiLookupTypeId' + 'PalletErrorMetadataV14' => { + 'type' => 'SiLookupTypeId' }, # PalletMetadataV14 end # ExtrinsicMetadataV14 begin - ExtrinsicMetadataV14: { - type: 'SiLookupTypeId', - version: 'u8', - signedExtensions: 'Vec' + 'ExtrinsicMetadataV14' => { + 'type' => 'SiLookupTypeId', + 'version' => 'u8', + 'signedExtensions' => 'Vec' }, - SignedExtensionMetadataV14: { - identifier: 'Text', - type: 'SiLookupTypeId', - additionalSigned: 'SiLookupTypeId' + 'SignedExtensionMetadataV14' => { + 'identifier' => 'Text', + 'type' => 'SiLookupTypeId', + 'additionalSigned' => 'SiLookupTypeId' }, # ExtrinsicMetadataV14 end - SiLookupTypeId: 'Compact' + 'SiLookupTypeId' => 'Compact' }.freeze end end diff --git a/lib/scale_rb/metadata/metadata_v9.rb b/lib/scale_rb/metadata/metadata_v9.rb index afe1d40..6487df3 100644 --- a/lib/scale_rb/metadata/metadata_v9.rb +++ b/lib/scale_rb/metadata/metadata_v9.rb @@ -1,74 +1,74 @@ # frozen_string_literal: true - +# https://github.com/polkadot-js/api/blob/master/packages/types/src/metadata/v9/toV10.ts module ScaleRb module Metadata module MetadataV9 TYPES = { - ErrorMetadataV9: { - name: 'Text', - docs: 'Vec' + 'ErrorMetadataV9' => { + 'name' => 'Text', + 'docs' => 'Vec' }, - EventMetadataV9: { - name: 'Text', - args: 'Vec', - docs: 'Vec' + 'EventMetadataV9' => { + 'name' => 'Text', + 'args' => 'Vec', + 'docs' => 'Vec' }, - FunctionArgumentMetadataV9: { - name: 'Text', - type: 'Type' + 'FunctionArgumentMetadataV9' => { + 'name' => 'Text', + 'type' => 'Type' }, - FunctionMetadataV9: { - name: 'Text', - args: 'Vec', - docs: 'Vec' + 'FunctionMetadataV9' => { + 'name' => 'Text', + 'args' => 'Vec', + 'docs' => 'Vec' }, - MetadataV9: { - modules: 'Vec' + 'MetadataV9' => { + 'modules' => 'Vec' }, - ModuleConstantMetadataV9: { - name: 'Text', - type: 'Type', - value: 'Bytes', - docs: 'Vec' + 'ModuleConstantMetadataV9' => { + 'name' => 'Text', + 'type' => 'Type', + 'value' => 'Bytes', + 'docs' => 'Vec' }, - ModuleMetadataV9: { - name: 'Text', - storage: 'Option', - calls: 'Option>', - events: 'Option>', - constants: 'Vec', - errors: 'Vec' + 'ModuleMetadataV9' => { + 'name' => 'Text', + 'storage' => 'Option', + 'calls' => 'Option>', + 'events' => 'Option>', + 'constants' => 'Vec', + 'errors' => 'Vec' }, - StorageEntryMetadataV9: { - name: 'Text', - modifier: 'StorageEntryModifierV9', - type: 'StorageEntryTypeV9', - fallback: 'Bytes', - docs: 'Vec' + 'StorageEntryMetadataV9' => { + 'name' => 'Text', + 'modifier' => 'StorageEntryModifierV9', + 'type' => 'StorageEntryTypeV9', + 'fallback' => 'Bytes', + 'docs' => 'Vec' }, - StorageEntryModifierV9: { - _enum: %w[Optional Default Required] + 'StorageEntryModifierV9' => { + '_enum' => %w[Optional Default Required] }, - StorageEntryTypeV9: { - _enum: { - Plain: 'Type', - Map: { - hasher: 'StorageHasherV9', - key: 'Type', - value: 'Type', - linked: 'bool' + 'StorageEntryTypeV9' => { + '_enum' => { + 'Plain' => 'Type', + 'Map' => { + 'hasher' => 'StorageHasherV9', + 'key' => 'Type', + 'value' => 'Type', + 'linked' => 'bool' }, - DoubleMap: { - hasher: 'StorageHasherV9', - key1: 'Type', - key2: 'Type', - value: 'Type', - key2Hasher: 'StorageHasherV9' + 'DoubleMap' => { + 'hasher' => 'StorageHasherV9', + 'key1' => 'Type', + 'key2' => 'Type', + 'value' => 'Type', + 'key2Hasher' => 'StorageHasherV9' } } }, - StorageHasherV9: { - _enum: %w[ + 'StorageHasherV9' => { + '_enum' => %w[ Blake2_128 Blake2_256 Twox128 @@ -76,9 +76,9 @@ module MetadataV9 Twox64Concat ] }, - StorageMetadataV9: { - prefix: 'Text', - items: 'Vec' + 'StorageMetadataV9' => { + 'prefix' => 'Text', + 'items' => 'Vec' } }.freeze end 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/metadata/types_helper.rb b/lib/scale_rb/metadata/types_helper.rb new file mode 100644 index 0000000..af021ab --- /dev/null +++ b/lib/scale_rb/metadata/types_helper.rb @@ -0,0 +1,63 @@ +require 'yaml' + +module ScaleRb + module Metadata + module TypesHelper + class << self + def build_types(spec_version, yaml_file) + yaml_content = YAML.load_file(yaml_file) + types = yaml_content['global']['types'].transform_values { |v| parse_type(v) } + + # Apply spec version specific overrides + yaml_content['forSpec']&.each do |spec_range| + range = spec_range['range'] + next unless in_range?(spec_version, range) + + spec_range['types']&.each do |type_name, type_def| + types[type_name] = parse_type(type_def) + end + end + + types + end + + private + + def in_range?(version, range) + min, max = range + return false if min && version < min + return false if max && version > max + true + end + + def parse_type(type_def) + case type_def + when String + type_def + when Hash + if type_def['_enum'] + parse_enum(type_def['_enum']) + else + type_def.transform_values { |v| parse_type(v) } + end + when Array + type_def.map { |v| parse_type(v) } + else + type_def + end + end + + def parse_enum(enum_def) + case enum_def + when Array + { '_enum' => enum_def } + when Hash + { '_enum' => enum_def.transform_values { |v| parse_type(v) } } + else + { '_enum' => enum_def } + end + 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..d147cc6 --- /dev/null +++ b/lib/scale_rb/old_registry.rb @@ -0,0 +1,552 @@ +# 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) + 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] + 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 + # { '_enum' => ['A', 'B', 'C'] } + 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.to_sym, 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.to_sym, + index:, + tuple: TupleType.new( + tuple: [use(variant_def)], + registry: self + ), + ) + when ::Array + TupleVariant.new( + name: variant_name.to_sym, + index:, + tuple: TupleType.new( + tuple: variant_def.map { |field_type| use(field_type) }, + registry: self + ) + ) + when ::Hash + StructVariant.new( + name: variant_name.to_sym, + 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..2404bfe --- /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, types = nil) + @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/assets/polkadot_types.yaml b/spec/assets/polkadot_types.yaml new file mode 100644 index 0000000..7839ccd --- /dev/null +++ b/spec/assets/polkadot_types.yaml @@ -0,0 +1,880 @@ +# This file provides all the type information necessary to decode historic blocks and storage entries on the +# Polkadot relay chain. See https://docs.rs/scale-info-legacy/0.2.0/scale_info_legacy/chain_types/struct.ChainTypeRegistry.html +# for more information, or check out the documentation for this crate to see example usage. +global: + types: + # Babe + EquivocationProof
: + offender: AuthorityId + slotNumber: u64 + firstHeader: Header + secondHeader: Header + BabeAuthorityWeight: u64 + schnorrkel::Randomness: Hash + Slot: u64 + BabeEpochConfiguration: + c: (u64, u64) + allowedSlots: AllowedSlots + AllowedSlots: + _enum: ['PrimarySlots', 'PrimaryAndSecondaryPlainSlots', 'PrimaryAndSecondaryVRFSlots'] + MaybeRandomness: Option + + # System + Perbill: u32 + ChangesTrieConfiguration: + digestInterval: u32 + digestLevels: u32 + KeyValue: '(StorageKey, StorageData)' + Key: Bytes + StorageKey: Bytes # Unsure about this: storage keys can vary in shape based on hashers etc. + StorageData: Bytes + RefCount: u8 + AccountInfo: + nonce: Index + refcount: RefCount + data: AccountData + T::Index: Index + T::AccountData: + free: Balance + reserved: Balance + miscFrozen: Balance + feeFrozen: Balance + LastRuntimeUpgradeInfo: + specVersion: Compact + specName: Text + Text: String + ExtrinsicsWeight: + normal: Weight + operational: Weight + EventRecord: + phase: Phase + event: Event + topics: Vec + Phase: + _enum: + ApplyExtrinsic: u32 + Finalization: [] + Initialization: [] + T::Event: builtin::Event + DispatchInfo: + weight: Weight + class: DispatchClass + paysFee: Pays + DispatchClass: + _enum: ['Normal', 'Operational', 'Mandatory'] + Pays: + _enum: ['Yes', 'No'] + DispatchResult: Result<(), DispatchError> + DispatchError: + _enum: + Other: [] + CannotLookup: [] + BadOrigin: [] + Module: DispatchErrorModule + ConsumerRemaining: [] + NoProviders: [] + TooManyConsumers: [] + Token: TokenError + Arithmetic: ArithmeticError + Transactional: TransactionalError + Exhausted: [] + Corruption: [] + Unavailable: [] + DispatchErrorModule: + index: u8 + error: u8 + TokenError: + _enum: + - NoFunds + - WouldDie + - BelowMinimum + - CannotCreate + - UnknownAsset + - Frozen + - Unsupported + # these are dropped, but still in older versions + # (if this adjusts, will need to take a re-look) + - Underflow + - Overflow + ArithmeticError: + _enum: ['Underflow', 'Overflow', 'DivisionByZero'] + TransactionError: + _enum: ['LimitReached', 'NoLayer'] + EventIndex: u32 + weights::ExtrinsicsWeight: + normal: Weight + operational: Weight + ConsumedWeight: + normal: Weight + operational: Weight + mandatory: Weight + DigestOf: Digest + + # Scheduler + T::BlockNumber: u32 + BlockNumber: u32 + schedule::Period: (BlockNumber, u32) + schedule::Priority: u8 + ::Call: builtin::Call + ::Call: builtin::Call + Scheduled: + maybeId: Option + priority: schedule::Priority + call: Call + maybePeriodic: Option> + # origin: T::PalletsOrigin + TaskAddress: (BlockNumber, u32) + Scheduled: + maybeId: Option + priority: schedule::Priority + call: Call + maybePeriodic: Option<(BlockNumber, u32)> + origin: PalletsOrigin + T::PalletsOrigin: + # This should be built from metadata in an ideal world. + _enum: + System: SystemOrigin + SystemOrigin: + _enum: + Root: [] + Signed: AccountId + None: [] + + # Timestamp + T::Moment: u64 + + # Indices + T::AccountIndex: u32 + T::AccountId: AccountId32 + AccountIndex: u32 + + # Balances + ::Source: AccountId + T::Balance: Balance + Releases: + _enum: ['V1', 'V2', 'V3', 'V4', 'V5', 'V6', 'V7', 'V8', 'V9', 'V10'] + BalanceLock: + id: LockIdentifier + amount: Balance + reasons: Reasons + LockIdentifier: '[u8; 8]' + Reasons: + _enum: ['Fee', 'Misc', 'All'] + Multiplier: u128 + + # Authorship + T::Header: + parentHash: Hash + number: Compact + stateRoot: Hash + extrinsicsRoot: Hash + digest: Digest + Digest: + logs: Vec + Hash: H256 + DigestItem: + _enum: + Other: Bytes + AuthoritiesChange: Vec + ChangesTrieRoot: Hash + SealV0: SealV0 + Consensus: Consensus + Seal: Seal + PreRuntime: PreRuntime + ChangesTrieSignal: ChangesTrieSignal + RuntimeEnvironmentUpdated: [] + AuthorityId: AccountId32 + SealV0: (u64, Signature) + Consensus: (ConsensusEngineId, Bytes) + Seal: Consensus + PreRuntime: Consensus + ChangesTrieSignal: + _enum: + NewConfiguration: Option + ChangesTrieConfiguration: + digestInterval: u32 + digestLevels: u32 + Signature: H512 + ConsensusEngineId: '[u8; 4]' + + # Staking + RewardDestination: + _enum: + Staked: [] + Stash: [] + Controller: [] + Account: AccountId + None: [] + RewardDestination: + _enum: + Staked: [] + Stash: [] + Controller: [] + Account: AccountId + None: [] + ValidatorPrefs: + commission: Compact + EraIndex: u32 + ValidatorIndex: u32 + CompactAssignments: + votes1: 'Vec<(NominatorIndex, [CompactScore; 0], ValidatorIndex)>' + votes2: 'Vec<(NominatorIndex, [CompactScore; 1], ValidatorIndex)>' + votes3: 'Vec<(NominatorIndex, [CompactScore; 2], ValidatorIndex)>' + votes4: 'Vec<(NominatorIndex, [CompactScore; 3], ValidatorIndex)>' + votes5: 'Vec<(NominatorIndex, [CompactScore; 4], ValidatorIndex)>' + votes6: 'Vec<(NominatorIndex, [CompactScore; 5], ValidatorIndex)>' + votes7: 'Vec<(NominatorIndex, [CompactScore; 6], ValidatorIndex)>' + votes8: 'Vec<(NominatorIndex, [CompactScore; 7], ValidatorIndex)>' + votes9: 'Vec<(NominatorIndex, [CompactScore; 8], ValidatorIndex)>' + votes10: 'Vec<(NominatorIndex, [CompactScore; 9], ValidatorIndex)>' + votes11: 'Vec<(NominatorIndex, [CompactScore; 10], ValidatorIndex)>' + votes12: 'Vec<(NominatorIndex, [CompactScore; 11], ValidatorIndex)>' + votes13: 'Vec<(NominatorIndex, [CompactScore; 12], ValidatorIndex)>' + votes14: 'Vec<(NominatorIndex, [CompactScore; 13], ValidatorIndex)>' + votes15: 'Vec<(NominatorIndex, [CompactScore; 14], ValidatorIndex)>' + votes16: 'Vec<(NominatorIndex, [CompactScore; 15], ValidatorIndex)>' + PhragmenScore: '[u128; 3]' + ElectionSize: + validators: Compact + nominators: Compact + NominatorIndex: u32 + CompactScore: (ValidatorIndex, OffchainAccuracy) + ValidatorIndex: u16 + OffchainAccuracy: PerU16 + PerU16: u16 + ElectionScore: '[u128; 3]' + Percent: u8 + ActiveEraInfo: + index: EraIndex + start: Option + Forcing: + _enum: ['NotForcing', 'ForceNew', 'ForceNone', 'ForceAlways'] + StakingLedger: + stash: AccountId + total: Compact + active: Compact + unlocking: Vec> + claimedRewards: Vec + UnlockChunk: + value: Compact + era: Compact + EraIndex: u32 + Nominations: + targets: Vec + submittedIn: EraIndex + suppressed: bool + Moment: u64 + EraRewardPoints: + total: u32 + individual: BTreeMap + ElectionResult: + electedStashes: Vec + exposures: Vec<(AccountId, Exposure)> + compute: ElectionCompute + ElectionCompute: + # in previous versions the last entry was "AuthorityId" + # (since no data attached, and it is via SCALE can rename) + _enum: ['OnChain', 'Signed', 'Unsigned'] + Exposure: + total: Compact + own: Compact + others: Vec> + IndividualExposure: + who: AccountId + value: Compact + ElectionStatus: + _enum: + Close: [] + Open: BlockNumber + slashing::SlashingSpans: + spanIndex: slashing::SpanIndex + lastStart: EraIndex + lastNonzeroSlash: EraIndex + prior: Vec + slashing::SpanIndex: u32 + UnappliedSlash: + validator: AccountId + own: Balance + others: Vec<(AccountId, Balance)> + reporters: Vec + payout: Balance + slashing::SlashingSpans: + spanIndex: slashing::SpanIndex + lastStart: EraIndex + lastNonzeroSlash: EraIndex + prior: Vec + slashing::SpanRecord: + slashed: Balance + paidOut: Balance + + # Offences + ReportIdOf: Hash + OffenceDetails: + offender: IdentificationTuple + reporters: Vec + T::IdentificationTuple: (ValidatorId, FullIdentification) + FullIdentification: + total: Compact + own: Compact + others: Vec> + Kind: '[u8; 16]' + OpaqueTimeSlot: Bytes + + # ElectionProviderMultiPhase + RawSolution: + compact: CompactAssignments + score: ElectionScore + round: u32 + SolutionOrSnapshotSize: + voters: Compact + targets: Compact + Phase: + _enum: + Off: [] + Signed: [] + Unsigned: (bool, BlockNumber) + Emergency: [] + ReadySolution: + supports: Vec<(AccountId, SolutionSupport)> + score: ElectionScore + compute: ElectionCompute + ElectionCompute: + # in previous versions the last entry was "AuthorityId" + # (since no data attached, and it is via SCALE can rename) + _enum: ['OnChain', 'Signed', 'Unsigned'] + SolutionSupport: + total: ExtendedBalance + voters: Vec<(AccountId, ExtendedBalance)> + ExtendedBalance: u128 + SubmissionIndicesOf: BTreeMap + SignedSubmissionOf: + who: AccountId + deposit: Balance + solution: RawSolution + reward: Balance + + # Session + T::Keys: (AccountId, AccountId, AccountId, AccountId, AccountId) + T::ValidatorId: AccountId + KeyTypeId: u32 + + # Grandpa + T::Hash: H256 + T::KeyOwnerProof: MembershipProof + MembershipProof: + session: SessionIndex + trieNodes: Vec + validatorCount: ValidatorCount + ValidatorCount: u32 + EquivocationProof: + setId: u64 + equivocation: GrandpaEquivocation + GrandpaEquivocation: + _enum: + Prevote: GrandpaEquivocationValue + Precommit: GrandpaEquivocationValue + GrandpaEquivocationValue: + roundNumber: u64 + identity: AuthorityId + first: '(GrandpaPrevote, AuthoritySignature)' + second: '(GrandpaPrevote, AuthoritySignature)' + GrandpaPrevote: + targetHash: Hash, + targetNumber: BlockNumber + SetId: u64 + + # ImOnline + Heartbeat: + blockNumber: BN + networkState: OpaqueNetworkState + sessionIndex: SessionIndex + authorityIndex: AuthIndex + validatorsLen: u32 + ::Signature: H512 + T::AuthorityId: AccountId + ValidatorId: AccountId + + + # Democracy + PropIndex: u32 + ReferendumIndex: u32 + AccountVote: + _enum: + Standard: + vote: Vote + balance: Balance + Split: + aye: Balance + nay: Balance + Vote: u8 # most significant bit for "aye", conviction 0001-0101 + Conviction: + _enum: + # 0.1x votes, unlocked. + - None + # 1x votes, locked for an enactment period following a successful vote. + - Locked1x + # 2x votes, locked for 2x enactment periods following a successful vote. + - Locked2x + # 3x votes, locked for 4x... + - Locked3x + # 4x votes, locked for 8x... + - Locked4x + # 5x votes, locked for 16x... + - Locked5x, + # 6x votes,locked for 32x... + - Locked6x + ReferendumInfo: + _enum: + Ongoing: ReferendumStatus + Finished: ReferendumInfoFinished + ReferendumStatus: + end: BlockNumber + proposalHash: Hash + threshold: VoteThreshold + delay: BlockNumber + tally: Tally + Tally: + ayes: Balance + nays: Balance + turnout: Balance + VoteThreshold: + _enum: [ + 'Super Majority Approve', + 'Super Majority Against', + 'Simple Majority' + ] + ReferendumInfoFinished: + approved: bool + end: BlockNumber + Voting: + _enum: + Direct: VotingDirect + Delegating: VotingDelegating + VotingDirect: + votes: Vec<(ReferendumIndex, AccountVote)> + delegations: Delegations + prior: (BlockNumber, Balance) + VotingDelegating: + balance: Balance + target: AccountId + conviction: Conviction + delegations: Delegations + prior: (BlockNumber, Balance) + Delegations: + votes: Balance + capital: Balance + PreimageStatus: + _enum: + Missing: BlockNumber + Available: PreimageStatusAvailable + PreimageStatusAvailable: + data: Bytes + provider: AccountId + deposit: Balance + since: BlockNumber + expiry: Option + + # Council + MemberCount: u32 + >::Proposal: builtin::Call + >::Proposal: builtin::Call + ProposalIndex: u32 + Weight: u64 # v1 = u32? but we seem to need u64 on block 29231. v2 = u64, v3 = { refTime: Compact, proofSize: Compact } + + # ElectionsPhragmen + DefunctVoter: + who: Source, + voteCount: Compact + candidateCount: Compact + ::Source: AccountId + Renouncing: + _enum: + Member: [] + RunnerUp: [] + Candidate: Compact + SeatHolder: + who: AccountId + stake: Balance + deposit: Balance + Voter: + votes: Vec + stake: Balance + deposit: Balance + + # Parachains + AttestedCandidate: + candidate: AbridgedCandidateReceipt + validityVotes: Vec + validatorIndices: BitVec + DoubleVoteReport: + identity: ValidatorId + first: (Statement, ValidatorSignature) + second: (Statement, ValidatorSignature) + proof: Proof + signingContext: SigningContext + >::Proof: MembershipProof + ValidatorId: AccountId + ValidatorSignature: Signature + Statement: + _enum: + Never: [] # index starts at 1 + Candidate: Hash + Valid: Hash + Invalid: Hash + SigningContext: + sessionIndex: SessionIndex + parentHash: Hash + + # Attestations + MoreAttestations: {} + + # Slots + LeasePeriodOf: T::BlockNumber + SubId: u32 + AuctionIndex: u32 + ParaId: u32 + HeadData: Bytes + ValidationCode: Bytes + + # Registrar + ParaInfo: + manager: AccountId + deposit: Balance + locked: bool + CollatorId: H256 + + # Claims + EcdsaSignature: '[u8; 65]' + EthereumAddress: '[u8; 20]' + StatementKind: + _enum: ['Regular', 'Saft'] + + # Vesting + VestingInfo: + locked: Balance + perBlock: Balance + startingBlock: BlockNumber + + # Utility + Timepoint: + height: BlockNumber + index: u32 + + # Identity + IdentityInfo: + additional: Vec + display: Data + legal: Data + web: Data + riot: Data + email: Data + pgpFingerprint: Option + image: Data + twitter: Data + IdentityInfo: IdentityInfo + IdentityInfoAdditional: (Data, Data) + RegistrarIndex: u32 + Judgement: + _enum: + Unknown: [] + FeePaid: Balance + Reasonable: [] + KnownGood: [] + OutOfDate: [] + LowQuality: [] + Erroneous: [] + IdentityFields: u64 # from least significant bit to most, 1 bit enabled for Display, Legal, Web, Riot, Email, PgpFingerprint, Image, Twitter + Registration: + judgements: Vec + deposit: Balance + info: IdentityInfo + Registration: Registration + RegistrationJudgement: (RegistrarIndex, IdentityJudgement) + IdentityJudgement: + _enum: + Unknown: [] + FeePaid: Balance + Reasonable: [] + KnownGood: [] + OutOfDate: [] + LowQuality: [] + Erroneous: [] + RegistrarIndex: u32 + RegistrarInfo: + account: AccountId + fee: Balance + fields: IdentityFields + + + # Poll + Approvals: "[bool; 4]" + Data: + _enum: + None: [] # 0 + # Custom encode/decode means that conceptually + # there is a variant for each size of raw data + # from 0-32 bytes: + Raw0: "[u8; 0]" # 1 + Raw1: "[u8; 1]" # 2 + Raw2: "[u8; 2]" # 3 + Raw3: "[u8; 3]" # 4 + Raw4: "[u8; 4]" # 5 + Raw5: "[u8; 5]" # 6 + Raw6: "[u8; 6]" # 7 + Raw7: "[u8; 7]" # 8 + Raw8: "[u8; 8]" # 9 + Raw9: "[u8; 9]" # 10 + Raw10: "[u8; 10]" # 11 + Raw11: "[u8; 11]" # 12 + Raw12: "[u8; 12]" # 13 + Raw13: "[u8; 13]" # 14 + Raw14: "[u8; 14]" # 15 + Raw15: "[u8; 15]" # 16 + Raw16: "[u8; 16]" # 17 + Raw17: "[u8; 17]" # 18 + Raw18: "[u8; 18]" # 19 + Raw19: "[u8; 19]" # 20 + Raw20: "[u8; 20]" # 21 + Raw21: "[u8; 21]" # 22 + Raw22: "[u8; 22]" # 23 + Raw23: "[u8; 23]" # 24 + Raw24: "[u8; 24]" # 25 + Raw25: "[u8; 25]" # 26 + Raw26: "[u8; 26]" # 27 + Raw27: "[u8; 27]" # 28 + Raw28: "[u8; 28]" # 29 + Raw29: "[u8; 29]" # 30 + Raw30: "[u8; 30]" # 31 + Raw31: "[u8; 31]" # 32 + Raw32: "[u8; 32]" # 33 + BlakeTwo256: H256 # 34 + Sha256: H256 # 35 + Keccak256: H256 # 36 + ShaThree256: H256 # 37 + + # Proxy + T::ProxyType: + _enum: + - Any + - NonTransfer + - Governance + - Staking + - UnusedSudoBalances + - IdentityJudgement + - CancelProxy + - Auction + AccountValidity: + _enum: + - Invalid + - Initiated + - Pending + - ValidLow + - ValidHigh + - Completed + Permill: u32 + CallHashOf: Hash + ProxyDefinition: + delegate: AccountId + proxyType: ProxyType + delay: BlockNumber + Announcement: + real: AccountId + callHash: CallHash + height: BlockNumber + + # MultiSig + OpaqueCall: Vec + Multisig: + when: Timepoint + deposit: Balance + depositor: AccountId + approvals: Vec + Timepoint: + height: BlockNumber + index: u32 + + # Treasury + BountyIndex: u32 + Proposal: + proposer: AccountId + value: Balance + beneficiary: AccountId + bond: Balance + OpenTip: + reason: Hash + who: AccountId + finder: AccountId + deposit: Balance + closes: Option + tips: Vec<(AccountId, Balance)> + findersFee: bool + Bounty: + proposer: AccountId + value: Balance + fee: Balance + curatorDeposit: Balance + bond: Balance + status: BountyStatus + BountyStatus: + _enum: + Proposed: [] + Approved: [] + Funded: [] + CuratorProposed: BountyStatusCuratorProposed + Active: BountyStatusActive + PendingPayout: BountyStatusPendingPayout + BountyStatusActive: + curator: AccountId + updateDue: BlockNumber + BountyStatusCuratorProposed: + curator: AccountId + BountyStatusPendingPayout: + curator: AccountId + beneficiary: AccountId + unlockAt: BlockNumber + + # Purchase + AccountStatus: + validity: AccountValidity + freeBalance: Balance + lockedBalance: Balance + signature: Vec + vat: Permill + AccountValidity: + _enum: ['Invalid', 'Initiated', 'Pending', 'ValidLow', 'ValidHigh', 'Completed'] + + # imOnline + OpaqueNetworkState: + peerId: OpaquePeerId + externalAddresses: Vec + OpaquePeerId: Bytes + OpaqueMultiaddr: Bytes + AuthIndex: u32 + AuthoritySignature: Signature + + # Manually defined types below: + + WeakBoundedVec: Vec + BoundedVec: Vec + + Balance: u128 + BalanceOf: Balance + BalanceOf: Balance + + Index: u32 + AccountId32: "[u8; 32]" + AccountId: AccountId32 + SessionIndex: u32 + Bytes: Vec + EcdsaSignature: '[u8; 65]' + Ed25519Signature: '[u8; 64]' + Sr25519Signature: '[u8; 64]' + BitVec: bitvec::vec::BitVec + + H32: '[u8; 4]' + H64: '[u8; 8]' + H128: '[u8; 16]' + H160: '[u8; 20]' + H256: '[u8; 32]' + H512: '[u8; 64]' + H1024: '[u8; 128]' + H2048: '[u8; 256]' + Hash: H256 + Era: + # This has a dumb representation because it's encoded in a custom way. + # This representation matches the TypeInfo generated for it in newer metadatas. + _enum: {"Immortal":[],"Mortal1":"u8","Mortal2":"u8","Mortal3":"u8","Mortal4":"u8","Mortal5":"u8","Mortal6":"u8","Mortal7":"u8","Mortal8":"u8","Mortal9":"u8","Mortal10":"u8","Mortal11":"u8","Mortal12":"u8","Mortal13":"u8","Mortal14":"u8","Mortal15":"u8","Mortal16":"u8","Mortal17":"u8","Mortal18":"u8","Mortal19":"u8","Mortal20":"u8","Mortal21":"u8","Mortal22":"u8","Mortal23":"u8","Mortal24":"u8","Mortal25":"u8","Mortal26":"u8","Mortal27":"u8","Mortal28":"u8","Mortal29":"u8","Mortal30":"u8","Mortal31":"u8","Mortal32":"u8","Mortal33":"u8","Mortal34":"u8","Mortal35":"u8","Mortal36":"u8","Mortal37":"u8","Mortal38":"u8","Mortal39":"u8","Mortal40":"u8","Mortal41":"u8","Mortal42":"u8","Mortal43":"u8","Mortal44":"u8","Mortal45":"u8","Mortal46":"u8","Mortal47":"u8","Mortal48":"u8","Mortal49":"u8","Mortal50":"u8","Mortal51":"u8","Mortal52":"u8","Mortal53":"u8","Mortal54":"u8","Mortal55":"u8","Mortal56":"u8","Mortal57":"u8","Mortal58":"u8","Mortal59":"u8","Mortal60":"u8","Mortal61":"u8","Mortal62":"u8","Mortal63":"u8","Mortal64":"u8","Mortal65":"u8","Mortal66":"u8","Mortal67":"u8","Mortal68":"u8","Mortal69":"u8","Mortal70":"u8","Mortal71":"u8","Mortal72":"u8","Mortal73":"u8","Mortal74":"u8","Mortal75":"u8","Mortal76":"u8","Mortal77":"u8","Mortal78":"u8","Mortal79":"u8","Mortal80":"u8","Mortal81":"u8","Mortal82":"u8","Mortal83":"u8","Mortal84":"u8","Mortal85":"u8","Mortal86":"u8","Mortal87":"u8","Mortal88":"u8","Mortal89":"u8","Mortal90":"u8","Mortal91":"u8","Mortal92":"u8","Mortal93":"u8","Mortal94":"u8","Mortal95":"u8","Mortal96":"u8","Mortal97":"u8","Mortal98":"u8","Mortal99":"u8","Mortal100":"u8","Mortal101":"u8","Mortal102":"u8","Mortal103":"u8","Mortal104":"u8","Mortal105":"u8","Mortal106":"u8","Mortal107":"u8","Mortal108":"u8","Mortal109":"u8","Mortal110":"u8","Mortal111":"u8","Mortal112":"u8","Mortal113":"u8","Mortal114":"u8","Mortal115":"u8","Mortal116":"u8","Mortal117":"u8","Mortal118":"u8","Mortal119":"u8","Mortal120":"u8","Mortal121":"u8","Mortal122":"u8","Mortal123":"u8","Mortal124":"u8","Mortal125":"u8","Mortal126":"u8","Mortal127":"u8","Mortal128":"u8","Mortal129":"u8","Mortal130":"u8","Mortal131":"u8","Mortal132":"u8","Mortal133":"u8","Mortal134":"u8","Mortal135":"u8","Mortal136":"u8","Mortal137":"u8","Mortal138":"u8","Mortal139":"u8","Mortal140":"u8","Mortal141":"u8","Mortal142":"u8","Mortal143":"u8","Mortal144":"u8","Mortal145":"u8","Mortal146":"u8","Mortal147":"u8","Mortal148":"u8","Mortal149":"u8","Mortal150":"u8","Mortal151":"u8","Mortal152":"u8","Mortal153":"u8","Mortal154":"u8","Mortal155":"u8","Mortal156":"u8","Mortal157":"u8","Mortal158":"u8","Mortal159":"u8","Mortal160":"u8","Mortal161":"u8","Mortal162":"u8","Mortal163":"u8","Mortal164":"u8","Mortal165":"u8","Mortal166":"u8","Mortal167":"u8","Mortal168":"u8","Mortal169":"u8","Mortal170":"u8","Mortal171":"u8","Mortal172":"u8","Mortal173":"u8","Mortal174":"u8","Mortal175":"u8","Mortal176":"u8","Mortal177":"u8","Mortal178":"u8","Mortal179":"u8","Mortal180":"u8","Mortal181":"u8","Mortal182":"u8","Mortal183":"u8","Mortal184":"u8","Mortal185":"u8","Mortal186":"u8","Mortal187":"u8","Mortal188":"u8","Mortal189":"u8","Mortal190":"u8","Mortal191":"u8","Mortal192":"u8","Mortal193":"u8","Mortal194":"u8","Mortal195":"u8","Mortal196":"u8","Mortal197":"u8","Mortal198":"u8","Mortal199":"u8","Mortal200":"u8","Mortal201":"u8","Mortal202":"u8","Mortal203":"u8","Mortal204":"u8","Mortal205":"u8","Mortal206":"u8","Mortal207":"u8","Mortal208":"u8","Mortal209":"u8","Mortal210":"u8","Mortal211":"u8","Mortal212":"u8","Mortal213":"u8","Mortal214":"u8","Mortal215":"u8","Mortal216":"u8","Mortal217":"u8","Mortal218":"u8","Mortal219":"u8","Mortal220":"u8","Mortal221":"u8","Mortal222":"u8","Mortal223":"u8","Mortal224":"u8","Mortal225":"u8","Mortal226":"u8","Mortal227":"u8","Mortal228":"u8","Mortal229":"u8","Mortal230":"u8","Mortal231":"u8","Mortal232":"u8","Mortal233":"u8","Mortal234":"u8","Mortal235":"u8","Mortal236":"u8","Mortal237":"u8","Mortal238":"u8","Mortal239":"u8","Mortal240":"u8","Mortal241":"u8","Mortal242":"u8","Mortal243":"u8","Mortal244":"u8","Mortal245":"u8","Mortal246":"u8","Mortal247":"u8","Mortal248":"u8","Mortal249":"u8","Mortal250":"u8","Mortal251":"u8","Mortal252":"u8","Mortal253":"u8","Mortal254":"u8","Mortal255":"u8"} + + MultiAddress: + _enum: + Id: AccountId + Index: AccountIndex + Raw: Bytes + Address32: H256 + Address20: H160 + + MultiSignature: + _enum: + Ed25519: Ed25519Signature + Sr25519: Sr25519Signature + Ecdsa: EcdsaSignature + + # Hardcoded types for old metadatas that don't know about them. + hardcoded::ExtrinsicAddress: AccountId32 + hardcoded::ExtrinsicSignature: MultiSignature + + # signed exts + ChargeAssetTxPayment: + tip: Compact + assetId: Option + ChargeTransactionPayment: + tip: Compact + CheckBlockGasLimit: [] + CheckEra: CheckMortality + CheckGenesis: [] + CheckMortality: + era: Era + CheckNonZeroSender: [] + CheckNonce: + nonce: Compact + CheckSpecVersion: [] + CheckTxVersion: [] + CheckVersion: [] + CheckWeight: [] + LockStakingStatus: [] + ValidateEquivocationReport: [] + TransactionCallFilter: [] + LimitParathreadCommits: [] + OnlyStakingAndClaims: [] + PrevalidateAttests: [] + RestrictFunctionality: [] + ValidateDoubleVoteReports: [] + DisallowSigned: [] + +forSpec: + - range: [23, null] + types: + CompactScoreCompact: (Compact, Compact) + CompactAssignments: + votes1: Vec<(Compact, Compact)> + votes2: Vec<(Compact, CompactScoreCompact, Compact)> + votes3: Vec<(Compact, [CompactScoreCompact; 2], Compact)> + votes4: Vec<(Compact, [CompactScoreCompact; 3], Compact)> + votes5: Vec<(Compact, [CompactScoreCompact; 4], Compact)> + votes6: Vec<(Compact, [CompactScoreCompact; 5], Compact)> + votes7: Vec<(Compact, [CompactScoreCompact; 6], Compact)> + votes8: Vec<(Compact, [CompactScoreCompact; 7], Compact)> + votes9: Vec<(Compact, [CompactScoreCompact; 8], Compact)> + votes10: Vec<(Compact, [CompactScoreCompact; 9], Compact)> + votes11: Vec<(Compact, [CompactScoreCompact; 10], Compact)> + votes12: Vec<(Compact, [CompactScoreCompact; 11], Compact)> + votes13: Vec<(Compact, [CompactScoreCompact; 12], Compact)> + votes14: Vec<(Compact, [CompactScoreCompact; 13], Compact)> + votes15: Vec<(Compact, [CompactScoreCompact; 14], Compact)> + votes16: Vec<(Compact, [CompactScoreCompact; 15], Compact)> + - range: [25, null] + types: + RefCount: u32 + - range: [28, null] + types: + hardcoded::ExtrinsicAddress: MultiAddress + ::Source: MultiAddress + T::Keys: (AccountId, AccountId, AccountId, AccountId, AccountId, AccountId) + ValidatorPrefs: + commission: Compact + blocked: bool + - range: [28, 29] + types: + AccountInfo: + nonce: Index + consumers: RefCount + providers: RefCount + data: AccountData + - range: [30, null] + types: + AccountInfo: + nonce: Index + consumers: RefCount + providers: RefCount + sufficients: RefCount + data: AccountData \ No newline at end of file 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/metadata_spec.rb b/spec/metadata/metadata_spec.rb new file mode 100644 index 0000000..79fd2fd --- /dev/null +++ b/spec/metadata/metadata_spec.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +require 'scale_rb' +require 'json' + +# ENABLE_TYPE_ENFORCEMENT=true TYPE_ENFORCEMENT_LEVEL=0 rspec ./spec/metadata_spec.rb +# Note: enable type checking will cause performance issue. + +# https://github.com/polkadot-js/api/tree/master/packages/types-support/src/metadata +module ScaleRb + RSpec.describe Metadata do + before(:all) do + hex = File.read('./spec/assets/substrate-metadata-v14-hex').strip + time = Benchmark.measure do + @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.magic_number).to eq(1_635_018_093) + end + + it 'can get storage item from metadata v14' do + storage_item = @metadata.storage('System', 'BlockHash') + expect(storage_item).to eql( + { + name: 'BlockHash', + modifier: :Default, + type: { map: { hashers: [:Twox64Concat], key: 4, value: 11 } }, + fallback: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0], + docs: [' Map of block numbers to block hashes.'] + } + ) + end + + it 'can get module by name' do + 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 }) + expect(system_module[:index]).to eql(0) + end + + it 'can get module by index' do + 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 call type' do + call_type = @metadata.call('System', 'remark') + expect(call_type).to eql( + { + name: 'remark', + fields: [{ name: 'remark', type: 12, typeName: 'Vec', docs: [] }], + index: 1, + docs: [ + 'Make some on-chain remark.', + '', + '# ', + '- `O(1)`', + '# ' + ] + } + ) + 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/metadata/types_helper_spec.rb b/spec/metadata/types_helper_spec.rb new file mode 100644 index 0000000..e0c3ee8 --- /dev/null +++ b/spec/metadata/types_helper_spec.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +require 'scale_rb' + +RSpec.describe ScaleRb::Metadata::TypesHelper do + let(:yaml_path) { './spec/assets/polkadot_types.yaml' } + + describe '.build_types' do + it 'loads basic types from global section' do + types = described_class.build_types(22, yaml_path) + + # Test a simple string type + expect(types['Balance']).to eq('u128') + + # Test a hash type + expect(types['AccountInfo']).to eq({ + 'nonce' => 'Index', + 'refcount' => 'RefCount', + 'data' => 'AccountData' + }) + + # Test an enum type + expect(types['Phase']).to eq({ + '_enum' => { + 'ApplyExtrinsic' => 'u32', + 'Finalization' => [], + 'Initialization' => [] + } + }) + end + + context 'with spec version specific overrides' do + it 'applies overrides for spec version 25' do + types = described_class.build_types(25, yaml_path) + expect(types['RefCount']).to eq('u32') + end + + it 'applies overrides for spec version 28' do + types = described_class.build_types(28, yaml_path) + + # Test that ValidatorPrefs was updated + expect(types['ValidatorPrefs']).to eq({ + 'commission' => 'Compact', + 'blocked' => 'bool' + }) + + # Test that AccountInfo was updated + expect(types['AccountInfo']).to eq({ + 'nonce' => 'Index', + 'consumers' => 'RefCount', + 'providers' => 'RefCount', + 'data' => 'AccountData' + }) + end + + it 'applies overrides for spec version 30' do + types = described_class.build_types(30, yaml_path) + + # Test that AccountInfo was updated again + expect(types['AccountInfo']).to eq({ + 'nonce' => 'Index', + 'consumers' => 'RefCount', + 'providers' => 'RefCount', + 'sufficients' => 'RefCount', + 'data' => 'AccountData' + }) + end + end + + context 'with complex type definitions' do + it 'handles nested types correctly' do + types = described_class.build_types(22, yaml_path) + + expect(types['EventRecord']).to eq({ + 'phase' => 'Phase', + 'event' => 'Event', + 'topics' => 'Vec' + }) + end + + it 'handles array types correctly' do + types = described_class.build_types(22, yaml_path) + + expect(types['LockIdentifier']).to eq('[u8; 8]') + end + end + end +end \ No newline at end of file diff --git a/spec/metadata_spec.rb b/spec/metadata_spec.rb deleted file mode 100644 index d4fed97..0000000 --- a/spec/metadata_spec.rb +++ /dev/null @@ -1,160 +0,0 @@ -# frozen_string_literal: true - -require 'scale_rb' -require 'json' - -# ENABLE_TYPE_ENFORCEMENT=true TYPE_ENFORCEMENT_LEVEL=0 rspec ./spec/metadata_spec.rb -# Note: enable type checking will cause performance issue. - -# https://github.com/polkadot-js/api/tree/master/packages/types-support/src/metadata -module ScaleRb - RSpec.describe Metadata do - before(:all) do - hex = File.read('./spec/assets/substrate-metadata-v14-hex').strip - time = Benchmark.measure do - @metadata = ScaleRb::Metadata.decode_metadata(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) - end - - it 'can get storage item from metadata v14' do - storage_item = ScaleRb::Metadata.get_storage_item('System', 'BlockHash', @metadata) - expect(storage_item).to eql( - { - name: 'BlockHash', - modifier: :Default, - type: { map: { hashers: [:Twox64Concat], key: 4, value: 11 } }, - fallback: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0], - docs: [' Map of block numbers to block hashes.'] - } - ) - end - - it 'can get module by name' do - system_module = ScaleRb::Metadata.get_module('System', @metadata) - 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 }) - expect(system_module[:index]).to eql(0) - end - - it 'can get module by index' do - system_module = ScaleRb::Metadata.get_module_by_index(0, @metadata) - 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) - expect(call_type).to eql( - { - name: 'remark', - fields: [{ name: 'remark', type: 12, typeName: 'Vec', docs: [] }], - index: 1, - docs: [ - 'Make some on-chain remark.', - '', - '# ', - '- `O(1)`', - '# ' - ] - } - ) - end - - it 'can build registry from its lookup' do - registry = ScaleRb::Metadata.build_registry(@metadata) - expect(registry.types.length).to eq(696) - 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 )