From be4f43d3305b139e8e77559280791021eeefcfa1 Mon Sep 17 00:00:00 2001 From: Yehor Popovych Date: Mon, 4 Sep 2023 00:54:33 +0100 Subject: [PATCH] added identifiable. validatable refactoring --- Sources/Substrate/Api/ConstantsApi.swift | 24 +- Sources/Substrate/Block/Event.swift | 54 +-- Sources/Substrate/Block/Events.swift | 30 +- Sources/Substrate/Config/Config.swift | 28 +- Sources/Substrate/Config/Frame.swift | 89 ++++ Sources/Substrate/Config/Pallet.swift | 99 ----- Sources/Substrate/Extrinsic/AccountId.swift | 20 +- Sources/Substrate/Extrinsic/Address.swift | 2 +- Sources/Substrate/Extrinsic/Call.swift | 59 ++- .../DynamicExtensionsProvider.swift | 38 +- .../Extensions/ExtensionsProvider.swift | 2 +- .../Extensions/SigningParameters.swift | 4 +- .../Extensions/StaticExtensionsProvider.swift | 14 +- Sources/Substrate/Extrinsic/Extrinsic.swift | 67 +-- Sources/Substrate/Extrinsic/ExtrinsicV4.swift | 4 +- Sources/Substrate/Extrinsic/Signature.swift | 2 +- .../Transaction/ExtrinsicEvents.swift | 14 +- Sources/Substrate/Metadata/Metadata.swift | 16 +- Sources/Substrate/Metadata/NetworkType.swift | 13 + .../Substrate/Metadata/v14/MetadataV14.swift | 31 +- .../Metadata/v14/NetworkMetadataV14.swift | 14 +- .../Substrate/Metadata/v15/MetadataV15.swift | 35 +- .../Metadata/v15/NetworkMetadataV15.swift | 2 + .../Substrate/Runtime/ExtendedRuntime.swift | 2 +- Sources/Substrate/Runtime/Runtime.swift | 26 +- Sources/Substrate/Runtime/RuntimeCall.swift | 70 +++- .../Substrate/Runtime/RuntimeCodable.swift | 31 -- .../Runtime/RuntimeValidatable.swift | 394 ------------------ .../Storage/StorageKey+StaticHelpers.swift | 56 ++- Sources/Substrate/Storage/StorageKey.swift | 115 +++-- .../Substrate/Storage/TupleStorageKey.swift | 30 +- Sources/Substrate/Types/BitSequence.swift | 17 +- .../Substrate/Types/Dynamic/AnyAddress.swift | 37 +- Sources/Substrate/Types/Dynamic/AnyCall.swift | 52 ++- .../Types/Dynamic/AnyCallErrors.swift | 31 +- .../Substrate/Types/Dynamic/AnyEvent.swift | 28 +- .../Types/Dynamic/AnyEventRecord.swift | 88 ++-- .../Dynamic/AnyExtrinsicFailureEvent.swift | 2 +- .../Types/Dynamic/AnyFixedHasher.swift | 20 +- Sources/Substrate/Types/Dynamic/AnyHash.swift | 18 +- .../Types/Dynamic/AnySignature.swift | 42 +- .../Dynamic/DynamicExtrinsicExtensions.swift | 36 +- Sources/Substrate/Types/FrameType.swift | 220 ++++++++++ Sources/Substrate/Types/Hash.swift | 28 +- Sources/Substrate/Types/Hasher.swift | 20 +- .../Substrate/Types/IdentifiableType.swift | 342 +++++++++++++++ .../Substrate/Types/Static/AccountId32.swift | 2 +- .../Substrate/Types/Static/BatchCalls.swift | 15 +- .../Substrate/Types/Static/BlockEvents.swift | 23 +- .../Types/Static/DispatchError.swift | 45 +- .../Substrate/Types/Static/EventRecord.swift | 21 +- .../Types/Static/EventsStorageKey.swift | 4 +- .../Substrate/Types/Static/ExtrinsicEra.swift | 35 +- .../Types/Static/ExtrinsicFailureEvent.swift | 6 +- Sources/Substrate/Types/Static/Hashers.swift | 21 +- .../Types/Static/MetadataRuntimeCalls.swift | 12 +- .../Substrate/Types/Static/MultiAddress.swift | 21 +- .../Types/Static/MultiSignature.swift | 24 +- .../Substrate/Types/Static/Signatures.swift | 59 +-- .../Types/Static/SubstrateFeeInfo.swift | 51 ++- .../Static/SubstrateSigningParameters.swift | 4 +- .../TransactionQueryInfoRuntimeCalls.swift | 14 +- .../Static/TransactionValidityError.swift | 36 +- .../Tuples+StaticExtrinsicExtensions.swift | 4 +- .../Types/Static/Tuples+TupleStorageKey.swift | 262 ++++++++---- .../Substrate/Types/Tuples+Identifiable.swift | 220 ++++++++++ .../Substrate/Types/Tuples+Validatable.swift | 164 ++++++++ .../Substrate/Types/Types+Identifiable.swift | 121 ++++++ .../Substrate/Types/Types+Validatable.swift | 116 ++++++ Sources/Substrate/Types/ValidatableType.swift | 207 +++++++++ .../Types/Value/Value+Representable.swift | 140 ++----- Sources/Substrate/Utils/Nothing.swift | 16 +- Sources/Substrate/Utils/SS58.swift | 2 +- Tests/SubstrateTests/StorageKeysTests.swift | 26 +- 74 files changed, 2634 insertions(+), 1403 deletions(-) create mode 100644 Sources/Substrate/Config/Frame.swift delete mode 100644 Sources/Substrate/Config/Pallet.swift delete mode 100644 Sources/Substrate/Runtime/RuntimeValidatable.swift create mode 100644 Sources/Substrate/Types/FrameType.swift create mode 100644 Sources/Substrate/Types/IdentifiableType.swift create mode 100644 Sources/Substrate/Types/Tuples+Identifiable.swift create mode 100644 Sources/Substrate/Types/Tuples+Validatable.swift create mode 100644 Sources/Substrate/Types/Types+Identifiable.swift create mode 100644 Sources/Substrate/Types/Types+Validatable.swift create mode 100644 Sources/Substrate/Types/ValidatableType.swift diff --git a/Sources/Substrate/Api/ConstantsApi.swift b/Sources/Substrate/Api/ConstantsApi.swift index 1b1f47a..c09cb45 100644 --- a/Sources/Substrate/Api/ConstantsApi.swift +++ b/Sources/Substrate/Api/ConstantsApi.swift @@ -76,24 +76,36 @@ public extension ConstantsApiRegistry { } } -public protocol StaticConstant: PalletType { +public protocol StaticConstant: FrameType { associatedtype TValue + static var pallet: String { get } static func decode(valueFrom decoder: inout D, runtime: any Runtime) throws -> TValue } +public extension StaticConstant { + @inlinable static var frame: String { pallet } +} + public extension StaticConstant where TValue: RuntimeDecodable { static func decode(valueFrom decoder: inout D, runtime: any Runtime) throws -> TValue { try TValue(from: &decoder, runtime: runtime) } } -public extension StaticConstant where Self: RuntimeValidatable, TValue: RuntimeDynamicValidatable { - static func validate(runtime: any Runtime) -> Result { +public extension StaticConstant where TValue: ValidatableType { + static func validate(runtime: any Runtime) -> Result { guard let info = runtime.resolve(constant: name, pallet: pallet) else { - return .failure(.infoNotFound(for: Self.self)) + return .failure(.typeInfoNotFound(for: Self.self)) } - return TValue.validate(runtime: runtime, type: info.type.id).mapError { - .childError(for: Self.self, error: $0) + return TValue.validate(runtime: runtime, type: info.type).mapError { + .childError(for: Self.self, index: -1, error: $0) } } } + +public extension StaticConstant where TValue: IdentifiableType { + @inlinable + static var definition: FrameTypeDefinition { + .constant(Self.self, type: TValue.definition) + } +} diff --git a/Sources/Substrate/Block/Event.swift b/Sources/Substrate/Block/Event.swift index 71ee6d4..4fd29d2 100644 --- a/Sources/Substrate/Block/Event.swift +++ b/Sources/Substrate/Block/Event.swift @@ -13,9 +13,32 @@ public protocol Event: RuntimeDynamicDecodable { var name: String { get } } -public protocol IdentifiableEvent: Event, PalletType {} +public protocol PalletEvent: Event, FrameType { + static var pallet: String { get } +} + +public extension PalletEvent { + @inlinable var pallet: String { Self.pallet } + @inlinable var frame: String { pallet } + @inlinable static var frame: String { pallet } + @inlinable static var frameTypeName: String { "Event" } +} + +public typealias EventTypeInfo = [(field: NetworkType.Field, type: NetworkType)] +public typealias EventChildTypes = [ValidatableType.Type] + +public extension PalletEvent where + Self: ComplexFrameType, TypeInfo == EventTypeInfo +{ + static func typeInfo(runtime: any Runtime) -> Result { + guard let info = runtime.resolve(eventParams: name, pallet: pallet) else { + return .failure(.typeInfoNotFound(for: Self.self)) + } + return .success(info) + } +} -public protocol StaticEvent: IdentifiableEvent, RuntimeDecodable { +public protocol StaticEvent: PalletEvent, RuntimeDecodable { init(paramsFrom decoder: inout D, runtime: Runtime) throws } @@ -24,35 +47,18 @@ public extension StaticEvent { let modIndex = try decoder.decode(UInt8.self) let evIndex = try decoder.decode(UInt8.self) guard let info = runtime.resolve(eventName: evIndex, pallet: modIndex) else { - throw EventDecodingError.eventNotFound(index: evIndex, pallet: modIndex) + throw FrameTypeError.typeInfoNotFound(for: Self.self, index: evIndex, frame: modIndex) } - guard Self.pallet == info.pallet && Self.name == info.name else { - throw EventDecodingError.foundWrongEvent(found: (name: info.name, pallet: info.pallet), - expected: (name: Self.name, pallet: Self.pallet)) + guard Self.frame == info.pallet && Self.name == info.name else { + throw FrameTypeError.foundWrongType(for: Self.self, name: info.name, frame: info.pallet) } try self.init(paramsFrom: &decoder, runtime: runtime) } } -public extension IdentifiableEvent where Self: RuntimeValidatableComposite { - static func validatableFieldIds(runtime: any Runtime) -> Result<[NetworkType.Id], ValidationError> { - guard let info = runtime.resolve(eventParams: name, pallet: pallet) else { - return .failure(.infoNotFound(for: Self.self)) - } - return .success(info.map{$0.type}) - } -} - -public protocol SomeEventRecord: RuntimeDynamicDecodable, RuntimeDynamicValidatable { +public protocol SomeEventRecord: RuntimeDynamicDecodable, ValidatableType { var extrinsicIndex: UInt32? { get } var header: (name: String, pallet: String) { get } var any: AnyEvent { get throws } - func typed(_ type: E.Type) throws -> E -} - -public enum EventDecodingError: Error { - case eventNotFound(index: UInt8, pallet: UInt8) - case foundWrongEvent(found: (name: String, pallet: String), expected: (name: String, pallet: String)) - case decodedNonVariantValue(Value) - case tooManyFieldsInVariant(variant: Value, expected: Int) + func typed(_ type: E.Type) throws -> E } diff --git a/Sources/Substrate/Block/Events.swift b/Sources/Substrate/Block/Events.swift index 40678fb..4188f35 100644 --- a/Sources/Substrate/Block/Events.swift +++ b/Sources/Substrate/Block/Events.swift @@ -8,7 +8,7 @@ import Foundation import ScaleCodec -public protocol SomeBlockEvents: RuntimeDynamicDecodable, RuntimeDynamicValidatable, Default { +public protocol SomeBlockEvents: RuntimeDynamicDecodable, ValidatableType, Default { associatedtype ER: SomeEventRecord var events: [ER] { get } func events(extrinsic index: UInt32) -> [ER] @@ -27,11 +27,11 @@ public extension SomeBlockEvents { } != nil } - func has(_ type: E.Type) -> Bool { + func has(_ type: E.Type) -> Bool { has(event: E.name, pallet: E.pallet) } - func has(_ type: E.Type, extrinsic index: UInt32) -> Bool { + func has(_ type: E.Type, extrinsic index: UInt32) -> Bool { has(event: E.name, pallet: E.pallet, extrinsic: index) } @@ -47,11 +47,11 @@ public extension SomeBlockEvents { } } - func all(records type: E.Type) -> [ER] { + func all(records type: E.Type) -> [ER] { all(records: E.name, pallet: E.pallet) } - func all(records type: E.Type, extrinsic index: UInt32) -> [ER] { + func all(records type: E.Type, extrinsic index: UInt32) -> [ER] { all(records: E.name, pallet: E.pallet, extrinsic: index) } @@ -63,11 +63,11 @@ public extension SomeBlockEvents { try all(records: event, pallet: pallet, extrinsic: index).map { try $0.any } } - func all(events type: E.Type) throws -> [E] { + func all(events type: E.Type) throws -> [E] { try all(records: E.name, pallet: E.pallet).map { try $0.typed(type) } } - func all(events type: E.Type, extrinsic index: UInt32) throws -> [E] { + func all(events type: E.Type, extrinsic index: UInt32) throws -> [E] { try all(records: E.name, pallet: E.pallet, extrinsic: index).map { try $0.typed(type) } } @@ -83,11 +83,11 @@ public extension SomeBlockEvents { } } - func first(record type: E.Type) -> ER? { + func first(record type: E.Type) -> ER? { first(record: E.name, pallet: E.pallet) } - func first(record type: E.Type, extrinsic index: UInt32) -> ER? { + func first(record type: E.Type, extrinsic index: UInt32) -> ER? { first(record: E.name, pallet: E.pallet, extrinsic: index) } @@ -99,11 +99,11 @@ public extension SomeBlockEvents { try first(record: name, pallet: pallet, extrinsic: index).map{try $0.any} } - func first(event type: E.Type) throws -> E? { + func first(event type: E.Type) throws -> E? { try first(record: type).map{try $0.typed(type)} } - func first(event type: E.Type, extrinsic index: UInt32) throws -> E? { + func first(event type: E.Type, extrinsic index: UInt32) throws -> E? { try first(record: type, extrinsic: index).map{try $0.typed(type)} } @@ -119,11 +119,11 @@ public extension SomeBlockEvents { } } - func last(record type: E.Type) -> ER? { + func last(record type: E.Type) -> ER? { last(record: E.name, pallet: E.pallet) } - func last(record type: E.Type, extrinsic index: UInt32) -> ER? { + func last(record type: E.Type, extrinsic index: UInt32) -> ER? { last(record: E.name, pallet: E.pallet, extrinsic: index) } @@ -135,11 +135,11 @@ public extension SomeBlockEvents { try last(record: name, pallet: pallet, extrinsic: index).map{try $0.any} } - func last(event type: E.Type) throws -> E? { + func last(event type: E.Type) throws -> E? { try last(record: type).map{try $0.typed(type)} } - func last(event type: E.Type, extrinsic index: UInt32) throws -> E? { + func last(event type: E.Type, extrinsic index: UInt32) throws -> E? { try last(record: type, extrinsic: index).map{try $0.typed(type)} } diff --git a/Sources/Substrate/Config/Config.swift b/Sources/Substrate/Config/Config.swift index b6c8dd2..04ed1c8 100644 --- a/Sources/Substrate/Config/Config.swift +++ b/Sources/Substrate/Config/Config.swift @@ -9,7 +9,7 @@ import Foundation import ScaleCodec public typealias ConfigUnsignedInteger = UnsignedInteger & ValueRepresentable & DataConvertible - & CompactCodable & Swift.Codable & RuntimeCodable & RuntimeDynamicValidatable + & CompactCodable & Swift.Codable & RuntimeCodable & IdentifiableType // Config split to avoid recursive types public protocol BasicConfig { @@ -19,11 +19,11 @@ public protocol BasicConfig { associatedtype TAddress: Address associatedtype TSignature: Signature associatedtype TExtrinsicEra: SomeExtrinsicEra - associatedtype TExtrinsicPayment: ValueRepresentable & RuntimeDynamicValidatable + associatedtype TExtrinsicPayment: ValueRepresentable & ValidatableType associatedtype TSystemProperties: SystemProperties associatedtype TRuntimeVersion: RuntimeVersion - associatedtype TRuntimeDispatchInfo: RuntimeDynamicDecodable & RuntimeDynamicValidatable - associatedtype TFeeDetails: RuntimeDynamicDecodable & RuntimeDynamicValidatable + associatedtype TRuntimeDispatchInfo: RuntimeDynamicDecodable & ValidatableType + associatedtype TFeeDetails: RuntimeDynamicDecodable & ValidatableType } public protocol Config { @@ -62,8 +62,8 @@ public protocol Config { func extrinsicManager() throws -> TExtrinsicManager func customCoders() throws -> [RuntimeCustomDynamicCoder] // Return Config pallets list for validation - func pallets(runtime: any Runtime) throws -> [Pallet] - func runtimeCalls(runtime: any Runtime) throws -> [any (StaticRuntimeCall & RuntimeValidatable).Type] + func frames(runtime: any Runtime) throws -> [Frame] + func runtimeCalls(runtime: any Runtime) throws -> [any StaticRuntimeCall.Type] // If you want your own Scale Codec coders func encoder() -> ScaleCodec.Encoder func encoder(reservedCapacity count: Int) -> ScaleCodec.Encoder @@ -76,7 +76,7 @@ public protocol BatchSupportedConfig: Config { associatedtype TBatchAllCall: SomeBatchCall func isBatchSupported(metadata: any Metadata) -> Bool - func batchCalls(runtime: any Runtime) throws -> [SomeBatchCall.Type] + func batchCalls(runtime: any Runtime) throws -> [any SomeBatchCall.Type] } @frozen public struct SBT { @@ -152,7 +152,7 @@ public extension BatchSupportedConfig { metadata.resolve(pallet: TBatchAllCall.pallet)?.callIndex(name: TBatchAllCall.name) != nil } - func batchCalls(runtime: any Runtime) throws -> [SomeBatchCall.Type] { + func batchCalls(runtime: any Runtime) throws -> [any SomeBatchCall.Type] { [TBatchCall.self, TBatchAllCall.self] } } @@ -166,14 +166,14 @@ public extension BatchSupportedConfig { } @inlinable - static func defaultPallets(runtime: any Runtime, config: C) throws -> [Pallet] { - try [BaseSystemPallet(runtime: runtime, config: config)] + static func defaultFrames(runtime: any Runtime, config: C) throws -> [Frame] { + try [BaseSystemFrame(runtime: runtime, config: config)] } @inlinable static func defaultRuntimeCalls( runtime: any Runtime, config: C - ) -> [any (StaticRuntimeCall & RuntimeValidatable).Type] { + ) -> [any StaticRuntimeCall.Type] { [TransactionQueryInfoRuntimeCall.RuntimeDispatchInfo>.self, TransactionQueryFeeDetailsRuntimeCall.FeeDetails>.self] } @@ -225,12 +225,12 @@ public extension Config { } @inlinable - func pallets(runtime: any Runtime) throws -> [Pallet] { - try Configs.defaultPallets(runtime: runtime, config: self) + func frames(runtime: any Runtime) throws -> [Frame] { + try Configs.defaultFrames(runtime: runtime, config: self) } @inlinable - func runtimeCalls(runtime: any Runtime) throws -> [any (StaticRuntimeCall & RuntimeValidatable).Type] { + func runtimeCalls(runtime: any Runtime) throws -> [any StaticRuntimeCall.Type] { Configs.defaultRuntimeCalls(runtime: runtime, config: self) } } diff --git a/Sources/Substrate/Config/Frame.swift b/Sources/Substrate/Config/Frame.swift new file mode 100644 index 0000000..97b2bbf --- /dev/null +++ b/Sources/Substrate/Config/Frame.swift @@ -0,0 +1,89 @@ +// +// Frame.swift +// +// +// Created by Yehor Popovych on 31/08/2023. +// + +import Foundation + +public protocol Frame { + static var name: String { get } + + var calls: [PalletCall.Type] { get } + var events: [PalletEvent.Type] { get } + var storageKeys: [any PalletStorageKey.Type] { get } + var constants: [any StaticConstant.Type] { get } + + func validate(runtime: any Runtime) -> Result +} + +public extension Frame { + func validate(runtime: any Runtime) -> Result { + calls.voidErrorMap { $0.validate(runtime: runtime) } + .flatMap { events.voidErrorMap { $0.validate(runtime: runtime) } } + .flatMap { storageKeys.voidErrorMap { $0.validate(runtime: runtime) } } + .flatMap { constants.voidErrorMap { $0.validate(runtime: runtime) } } + } +} + +public protocol FrameCall: PalletCall { + associatedtype TFrame: Frame +} + +public extension FrameCall { + static var pallet: String { TFrame.name } +} + +public protocol FrameEvent: Event, FrameType { + associatedtype TFrame: Frame +} + +public extension FrameEvent { + static var pallet: String { TFrame.name } +} + +public protocol FrameStorageKey: StorageKey, FrameType { + associatedtype TFrame: Frame +} + +public extension FrameStorageKey { + static var pallet: String { TFrame.name } +} + +public protocol FrameConstant: StaticConstant { + associatedtype TFrame: Frame +} + +public extension FrameConstant { + static var pallet: String { TFrame.name } +} + +public extension Configs { + struct BaseSystemFrame: Frame { + @inlinable + public static var name: String { "System" } + @inlinable + public var events: [PalletEvent.Type] { + [ST.ExtrinsicFailureEvent.self] + } + @inlinable + public var storageKeys: [any PalletStorageKey.Type] { + [EventsStorageKey.BlockEvents>.self] + } + @inlinable + public var constants: [any StaticConstant.Type] { [] } + + public let calls: [PalletCall.Type] + + public init(runtime: any Runtime, config: C) throws { + if let batch = config as? any BatchSupportedConfig, + batch.isBatchSupported(metadata: runtime.metadata) + { + self.calls = try batch.batchCalls(runtime: runtime) + } else { + self.calls = [] + } + } + } +} diff --git a/Sources/Substrate/Config/Pallet.swift b/Sources/Substrate/Config/Pallet.swift deleted file mode 100644 index d0ef72d..0000000 --- a/Sources/Substrate/Config/Pallet.swift +++ /dev/null @@ -1,99 +0,0 @@ -// -// PalletType.swift -// -// -// Created by Yehor Popovych on 31/08/2023. -// - -import Foundation - -public protocol PalletType { - static var pallet: String { get } - static var name: String { get } -} - -public extension PalletType { - var name: String { Self.name } - var pallet: String { Self.pallet } -} - -public protocol Pallet { - static var name: String { get } - - var calls: [(Call & PalletType & RuntimeValidatable).Type] { get } - var events: [(Event & PalletType & RuntimeValidatable).Type] { get } - var storageKeys: [any (StorageKey & PalletType & RuntimeValidatable).Type] { get } - var constants: [any (StaticConstant & PalletType & RuntimeValidatable).Type] { get } - - func validate(runtime: any Runtime) -> Result -} - -public extension Pallet { - func validate(runtime: any Runtime) -> Result { - calls.voidErrorMap { $0.validate(runtime: runtime) } - .flatMap { events.voidErrorMap { $0.validate(runtime: runtime) } } - .flatMap { storageKeys.voidErrorMap { $0.validate(runtime: runtime) } } - .flatMap { constants.voidErrorMap { $0.validate(runtime: runtime) } } - } -} - -public protocol PalletCall: Call, PalletType, RuntimeValidatable { - associatedtype TPallet: Pallet -} - -public extension PalletCall { - static var pallet: String { TPallet.name } -} - -public protocol PalletEvent: Event, PalletType, RuntimeValidatable { - associatedtype TPallet: Pallet -} - -public extension PalletEvent { - static var pallet: String { TPallet.name } -} - -public protocol PalletStorageKey: StorageKey, PalletType, RuntimeValidatable { - associatedtype TPallet: Pallet -} - -public extension PalletStorageKey { - static var pallet: String { TPallet.name } -} - -public protocol PalletConstant: StaticConstant, PalletType, RuntimeValidatable { - associatedtype TPallet: Pallet -} - -public extension PalletConstant { - static var pallet: String { TPallet.name } -} - -public extension Configs { - struct BaseSystemPallet: Pallet { - @inlinable - public static var name: String { "System" } - @inlinable - public var events: [(Event & PalletType & RuntimeValidatable).Type] { - [ST.ExtrinsicFailureEvent.self] - } - @inlinable - public var storageKeys: [any (PalletType & RuntimeValidatable & StorageKey).Type] { - [EventsStorageKey.BlockEvents>.self] - } - @inlinable - public var constants: [any (RuntimeValidatable & StaticConstant).Type] { [] } - - public let calls: [(Call & PalletType & RuntimeValidatable).Type] - - public init(runtime: any Runtime, config: C) throws { - if let batch = config as? any BatchSupportedConfig, - batch.isBatchSupported(metadata: runtime.metadata) - { - self.calls = try batch.batchCalls(runtime: runtime) - } else { - self.calls = [] - } - } - } -} diff --git a/Sources/Substrate/Extrinsic/AccountId.swift b/Sources/Substrate/Extrinsic/AccountId.swift index 675e478..362d109 100644 --- a/Sources/Substrate/Extrinsic/AccountId.swift +++ b/Sources/Substrate/Extrinsic/AccountId.swift @@ -9,7 +9,7 @@ import Foundation import ScaleCodec public protocol AccountId: RuntimeDynamicCodable, RuntimeDynamicSwiftCodable, - ValueRepresentable, RuntimeDynamicValidatable + ValueRepresentable, ValidatableType { init(from string: String, runtime: any Runtime, id: NetworkType.LazyId) throws @@ -43,7 +43,7 @@ public extension AccountId { } } -public protocol StaticAccountId: AccountId, RuntimeCodable, RuntimeSwiftCodable { +public protocol StaticAccountId: AccountId, IdentifiableType, RuntimeCodable, RuntimeSwiftCodable { init(checked raw: Data, runtime: any Runtime) throws init(from string: String, runtime: any Runtime) throws @@ -109,18 +109,6 @@ public extension StaticAccountId { try container.encode(self.string) } - static func validate(runtime: any Runtime, - type id: NetworkType.Id) -> Result { - guard let info = runtime.resolve(type: id) else { - return .failure(.typeNotFound(id)) - } - guard let count = info.asBytes(runtime) else { - return .failure(.wrongType(got: info, for: String(describing: Self.self))) - } - guard byteCount == count else { - return .failure(.wrongValuesCount(in: info, expected: byteCount, - for: String(describing: Self.self))) - } - return .success(()) - } + @inlinable + static var definition: TypeDefinition { .data(count: UInt32(byteCount)) } } diff --git a/Sources/Substrate/Extrinsic/Address.swift b/Sources/Substrate/Extrinsic/Address.swift index 37fb8d3..4cd0774 100644 --- a/Sources/Substrate/Extrinsic/Address.swift +++ b/Sources/Substrate/Extrinsic/Address.swift @@ -8,7 +8,7 @@ import Foundation import ScaleCodec -public protocol Address: RuntimeDynamicCodable, ValueRepresentable, RuntimeDynamicValidatable { +public protocol Address: RuntimeDynamicCodable, ValueRepresentable, ValidatableType { associatedtype TAccountId: AccountId init(accountId: TAccountId, diff --git a/Sources/Substrate/Extrinsic/Call.swift b/Sources/Substrate/Extrinsic/Call.swift index 8a0c439..19b5d0d 100644 --- a/Sources/Substrate/Extrinsic/Call.swift +++ b/Sources/Substrate/Extrinsic/Call.swift @@ -13,19 +13,38 @@ public protocol Call: RuntimeDynamicEncodable { var name: String { get } } -public extension Call { - @inlinable static var palletTypeName: String { "Call" } +public protocol PalletCall: Call, FrameType { + static var pallet: String { get } } -public protocol IdentifiableCall: Call, PalletType {} +public extension PalletCall { + @inlinable var pallet: String { Self.pallet } + @inlinable var frame: String { pallet } + @inlinable static var frameTypeName: String { "Call" } + @inlinable static var frame: String { pallet } +} + +public typealias CallTypeInfo = [(field: NetworkType.Field, type: NetworkType)] +public typealias CallChildTypes = [ValidatableType.Type] + +public extension PalletCall where + Self: ComplexFrameType, TypeInfo == CallTypeInfo +{ + static func typeInfo(runtime: any Runtime) -> Result { + guard let info = runtime.resolve(callParams: name, pallet: pallet) else { + return .failure(.typeInfoNotFound(for: Self.self)) + } + return .success(info) + } +} -public protocol SomeBatchCall: IdentifiableCall, RuntimeValidatable { +public protocol SomeBatchCall: PalletCall { var calls: [any Call] { get } init(calls: [any Call]) func add(_ call: any Call) -> Self } -public protocol StaticCall: IdentifiableCall, RuntimeEncodable, RuntimeDecodable { +public protocol StaticCall: PalletCall, RuntimeEncodable, RuntimeDecodable { init(decodingParams decoder: inout D, runtime: Runtime) throws func encodeParams(in encoder: inout E, runtime: Runtime) throws } @@ -35,18 +54,17 @@ public extension StaticCall { let modIndex = try decoder.decode(.enumCaseId) let callIndex = try decoder.decode(.enumCaseId) guard let info = runtime.resolve(callName: callIndex, pallet: modIndex) else { - throw CallCodingError.callNotFound(index: callIndex, pallet: modIndex) + throw FrameTypeError.typeInfoNotFound(for: Self.self, index: callIndex, frame: modIndex) } guard Self.pallet == info.pallet && Self.name == info.name else { - throw CallCodingError.foundWrongCall(found: (name: info.name, pallet: info.pallet), - expected: (name: Self.name, pallet: Self.pallet)) + throw FrameTypeError.foundWrongType(for: Self.self, name: info.name, frame: info.pallet) } try self.init(decodingParams: &decoder, runtime: runtime) } func encode(in encoder: inout E, runtime: Runtime) throws { guard let info = runtime.resolve(callIndex: name, pallet: pallet) else { - throw CallCodingError.callNotFound(name: name, pallet: pallet) + throw FrameTypeError.typeInfoNotFound(for: Self.self) } try encoder.encode(info.pallet, .enumCaseId) try encoder.encode(info.index, .enumCaseId) @@ -54,32 +72,13 @@ public extension StaticCall { } } -public extension IdentifiableCall where Self: RuntimeValidatableComposite { - static func validatableFieldIds(runtime: any Runtime) -> Result<[NetworkType.Id], ValidationError> { - guard let info = runtime.resolve(callParams: name, pallet: pallet) else { - return .failure(.infoNotFound(for: Self.self)) - } - return .success(info.map{$0.type}) - } -} - public protocol CallHolder { associatedtype TCall: Call var call: TCall { get } } -public enum CallCodingError: Error { - case callNotFound(index: UInt8, pallet: UInt8) - case callNotFound(name: String, pallet: String) - case foundWrongCall(found: (name: String, pallet: String), expected: (name: String, pallet: String)) - case valueNotFound(key: String) - case wrongFieldCountInVariant(variant: Value, expected: Int) - case wrongParametersCount(in: AnyCall, expected: Int) - case decodedNonVariantValue(Value) -} - -public protocol CallError: Error, RuntimeDynamicValidatable, RuntimeDynamicDecodable, +public protocol CallError: Error, ValidatableType, RuntimeDynamicDecodable, RuntimeDynamicSwiftDecodable {} -public protocol StaticCallError: CallError, RuntimeDecodable, RuntimeSwiftDecodable {} +public protocol StaticCallError: CallError, RuntimeDecodable, RuntimeSwiftDecodable, IdentifiableType {} diff --git a/Sources/Substrate/Extrinsic/Extensions/DynamicExtensionsProvider.swift b/Sources/Substrate/Extrinsic/Extensions/DynamicExtensionsProvider.swift index a2468b8..1752882 100644 --- a/Sources/Substrate/Extrinsic/Extensions/DynamicExtensionsProvider.swift +++ b/Sources/Substrate/Extrinsic/Extensions/DynamicExtensionsProvider.swift @@ -23,8 +23,26 @@ public protocol DynamicExtrinsicExtension: ExtrinsicSignedExtension { func validate( config: C.Type, runtime: any Runtime, - extra: NetworkType.Id, additionalSigned: NetworkType.Id - ) -> Result + extra: NetworkType.Info, additionalSigned: NetworkType.Info + ) -> Result +} + +public extension DynamicExtrinsicExtension { + @inlinable + func validate( + config: C.Type, runtime: any Runtime, + extra eId: NetworkType.Id, + additionalSigned aId: NetworkType.Id + ) -> Result { + guard let eType = runtime.resolve(type: eId) else { + return .failure(.typeNotFound(for: Self.self, id: eId)) + } + guard let aType = runtime.resolve(type: aId) else { + return .failure(.typeNotFound(for: Self.self, id: aId)) + } + return validate(config: config, runtime: runtime, + extra: eId.i(eType), additionalSigned: aId.i(aType)) + } } public class DynamicSignedExtensionsProvider: SignedExtensionsProvider { @@ -108,18 +126,16 @@ public class DynamicSignedExtensionsProvider: SignedExtensionsP public func validate( runtime: any Runtime - ) -> Result> { + ) -> Result> { _activeExtensions(runtime: runtime) .mapError {.left($0)} .flatMap { exts in - exts.reduce(.success(())) { p, ext in - p.flatMap { - ext.ext.validate( - config: BC.self, runtime: runtime, - extra: ext.extId, additionalSigned: ext.addId - ).mapError{.right($0)} - } - } + exts.voidErrorMap { ext in + ext.ext.validate( + config: BC.self, runtime: runtime, + extra: ext.extId, additionalSigned: ext.addId + ) + }.mapError {.right($0)} } } diff --git a/Sources/Substrate/Extrinsic/Extensions/ExtensionsProvider.swift b/Sources/Substrate/Extrinsic/Extensions/ExtensionsProvider.swift index 50ce4c8..97252be 100644 --- a/Sources/Substrate/Extrinsic/Extensions/ExtensionsProvider.swift +++ b/Sources/Substrate/Extrinsic/Extensions/ExtensionsProvider.swift @@ -43,7 +43,7 @@ public protocol SignedExtensionsProvider { func validate( runtime: any Runtime - ) -> Result> + ) -> Result> } public protocol ExtrinsicSignedExtension { diff --git a/Sources/Substrate/Extrinsic/Extensions/SigningParameters.swift b/Sources/Substrate/Extrinsic/Extensions/SigningParameters.swift index f41ca37..1960d28 100644 --- a/Sources/Substrate/Extrinsic/Extensions/SigningParameters.swift +++ b/Sources/Substrate/Extrinsic/Extensions/SigningParameters.swift @@ -13,7 +13,7 @@ public protocol AnyAccountPartialSigningParameter: Default { } public protocol NoncePartialSigningParameter: AnyAccountPartialSigningParameter { - associatedtype TNonce: UnsignedInteger & ValueRepresentable & RuntimeDynamicValidatable + associatedtype TNonce: UnsignedInteger & ValueRepresentable & ValidatableType associatedtype TAccountId: AccountId var account: TAccountId? { get set } @@ -106,7 +106,7 @@ public extension EraSigningParameters { } public protocol PaymentPartialSigningParameter: Default { - associatedtype TPayment: ValueRepresentable & RuntimeDynamicValidatable + associatedtype TPayment: ValueRepresentable & ValidatableType var tip: TPayment? { get set } func tip(_ tip: TPayment) -> Self diff --git a/Sources/Substrate/Extrinsic/Extensions/StaticExtensionsProvider.swift b/Sources/Substrate/Extrinsic/Extensions/StaticExtensionsProvider.swift index 5375082..a67905e 100644 --- a/Sources/Substrate/Extrinsic/Extensions/StaticExtensionsProvider.swift +++ b/Sources/Substrate/Extrinsic/Extensions/StaticExtensionsProvider.swift @@ -39,7 +39,7 @@ public protocol StaticExtrinsicExtension: StaticExtrinsicExtensionBase, Extrinsi runtime: any Runtime, extra: NetworkType.Id, additionalSigned: NetworkType.Id - ) -> Result + ) -> Result } public extension StaticExtrinsicExtension { @@ -47,13 +47,13 @@ public extension StaticExtrinsicExtension { } public extension StaticExtrinsicExtension where - TExtra: RuntimeDynamicValidatable, TAdditionalSigned: RuntimeDynamicValidatable + TExtra: ValidatableType, TAdditionalSigned: ValidatableType { func validate(runtime: Runtime, extra: NetworkType.Id, - additionalSigned: NetworkType.Id) -> Result + additionalSigned: NetworkType.Id) -> Result { - TExtra.validate(runtime: runtime, type: extra).flatMap { - TAdditionalSigned.validate(runtime: runtime, type: additionalSigned) + TExtra.validate(runtime: runtime, type: extra).flatMap { _ in + TAdditionalSigned.validate(runtime: runtime, type: additionalSigned).map {_ in} } } } @@ -66,7 +66,7 @@ public protocol StaticExtrinsicExtensions: StaticExtrinsicExtensionBase func validate( runtime: any Runtime, types: [ExtrinsicExtensionId: (extId: NetworkType.Id, addId: NetworkType.Id)] - ) -> Result> + ) -> Result> } public class StaticSignedExtensionsProvider: SignedExtensionsProvider { @@ -125,7 +125,7 @@ public class StaticSignedExtensionsProvider: Sig public func validate( runtime: any Runtime - ) -> Result> { + ) -> Result> { guard runtime.metadata.extrinsic.version == version else { return .failure(.left(.badExtrinsicVersion( supported: version, diff --git a/Sources/Substrate/Extrinsic/Extrinsic.swift b/Sources/Substrate/Extrinsic/Extrinsic.swift index 00d7962..8b013e5 100644 --- a/Sources/Substrate/Extrinsic/Extrinsic.swift +++ b/Sources/Substrate/Extrinsic/Extrinsic.swift @@ -75,7 +75,7 @@ public protocol OpaqueExtrinsic: RuntimeSwi } -public protocol SomeExtrinsicEra: RuntimeDynamicCodable, ValueRepresentable, RuntimeDynamicValidatable, Default { +public protocol SomeExtrinsicEra: RuntimeDynamicCodable, ValueRepresentable, ValidatableType, Default { var isImmortal: Bool { get } func blockHash(api: R) async throws -> R.RC.TBlock.THeader.THasher.THash @@ -95,7 +95,7 @@ public enum ExtrinsicCodingError: Error { case unknownExtension(identifier: ExtrinsicExtensionId) } -public protocol SomeExtrinsicFailureEvent: IdentifiableEvent, RuntimeValidatable { +public protocol SomeExtrinsicFailureEvent: PalletEvent { associatedtype Err: Error var error: Err { get } } @@ -107,55 +107,66 @@ public protocol SomeDispatchError: CallError { } public struct ModuleError: Error { - public enum DecodingError: Error { - case dispatchErrorIsNotModule(description: String) - case badModuleVariant(Value.Variant) - case palletNotFound(index: UInt8) - case badPalletError(type: NetworkType.Info?) - case errorNotFound(index: UInt8) - } - public let pallet: PalletMetadata public let error: NetworkType.Variant public init(variant: Value.Variant, runtime: any Runtime) throws { let fields = variant.values - guard fields.count == 2, - let index = fields[0].uint.flatMap({UInt8(exactly: $0)}), - let bytes = fields[1].bytes, bytes.count > 0 else - { - throw DecodingError.badModuleVariant(variant) + guard fields.count == 2 else { + throw FrameTypeError.wrongFieldsCount(for: "Error", expected: 2, + got: fields.count) + } + guard let index = fields[0].uint.flatMap({UInt8(exactly: $0)}) else { + throw FrameTypeError.paramMismatch(for: "Error", index: 0, + expected: "UInt8", got: fields[0].description) + } + guard let bytes = fields[1].bytes, bytes.count > 0 else { + throw FrameTypeError.paramMismatch(for: "Error", index: 1, + expected: "Data", got: fields[1].description) } try self.init(pallet: index, error: bytes[0], metadata: runtime.metadata) } public init(pallet: UInt8, error: UInt8, metadata: any Metadata) throws { guard let pallet = metadata.resolve(pallet: pallet) else { - throw DecodingError.palletNotFound(index: pallet) + throw FrameTypeError.typeInfoNotFound(for: "Error", index: error, frame: pallet) + } + guard let palletError = pallet.error else { + throw FrameTypeError.paramMismatch(for: "\(pallet.name).error", + index: -1, expected: "NetworkType.Info", + got: "nil") } - guard case .variant(variants: let variants) = pallet.error?.type.definition else { - throw DecodingError.badPalletError(type: pallet.error) + guard case .variant(variants: let variants) = palletError.type.definition else { + throw FrameTypeError.paramMismatch(for: "\(pallet.name).error", + index: -1, expected: "Variant", + got: palletError.type.description) } guard let error = variants.first(where: { $0.index == error }) else { - throw DecodingError.errorNotFound(index: error) + throw FrameTypeError.typeInfoNotFound(for: "Error", index: error, frame: pallet.index) } self.pallet = pallet self.error = error } - public static func validate(variant: NetworkType.Variant, - runtime: any Runtime) -> Bool + public static func validate(info: (index: UInt8, name: String, + fields: [(name: String?, type: NetworkType.Info)]), + type: NetworkType, + runtime: any Runtime) -> Result { - guard variant.fields.count == 2 else { - return false + guard info.fields.count == 2 else { + return .failure(.wrongVariantFieldsCount(for: Self.self, + variant: info.name, + expected: 2, in: type)) } - guard runtime.resolve(type: variant.fields[0].type)?.asPrimitive(runtime)?.isUInt == 8 else { - return false + guard info.fields[0].type.type.asPrimitive(runtime)?.isUInt == 8 else { + return .failure(.wrongType(for: Self.self, got: type, + reason: "field[0] is not UInt8")) } - guard runtime.resolve(type: variant.fields[0].type)?.asBytes(runtime) ?? 0 > 0 else { - return false + guard info.fields[1].type.type.asBytes(runtime) ?? 0 > 0 else { + return .failure(.wrongType(for: Self.self, got: type, + reason: "field[1] is not byte array")) } - return true + return .success(()) } } diff --git a/Sources/Substrate/Extrinsic/ExtrinsicV4.swift b/Sources/Substrate/Extrinsic/ExtrinsicV4.swift index 8825a2e..3717654 100644 --- a/Sources/Substrate/Extrinsic/ExtrinsicV4.swift +++ b/Sources/Substrate/Extrinsic/ExtrinsicV4.swift @@ -144,8 +144,8 @@ public class ExtrinsicV4Manager: ExtrinsicManager got: runtime.metadata.extrinsic.version ) } - try TAddress.validate(runtime: runtime, type: runtime.types.address.id).get() - try TSignature.validate(runtime: runtime, type: runtime.types.signature.id).get() + let _ = try TAddress.validate(runtime: runtime, type: runtime.types.address.id).get() + let _ = try TSignature.validate(runtime: runtime, type: runtime.types.signature.id).get() try extensions.validate(runtime: runtime).get() } diff --git a/Sources/Substrate/Extrinsic/Signature.swift b/Sources/Substrate/Extrinsic/Signature.swift index b0ed6ad..fec6d29 100644 --- a/Sources/Substrate/Extrinsic/Signature.swift +++ b/Sources/Substrate/Extrinsic/Signature.swift @@ -8,7 +8,7 @@ import Foundation import ScaleCodec -public protocol Signature: RuntimeDynamicCodable, ValueRepresentable, RuntimeDynamicValidatable { +public protocol Signature: RuntimeDynamicCodable, ValueRepresentable, ValidatableType { var raw: Data { get } var algorithm: CryptoTypeId { get } diff --git a/Sources/Substrate/Extrinsic/Transaction/ExtrinsicEvents.swift b/Sources/Substrate/Extrinsic/Transaction/ExtrinsicEvents.swift index 26e0bfd..0270adb 100644 --- a/Sources/Substrate/Extrinsic/Transaction/ExtrinsicEvents.swift +++ b/Sources/Substrate/Extrinsic/Transaction/ExtrinsicEvents.swift @@ -56,7 +56,7 @@ public extension ExtrinsicEvents { _events.has(event: event, pallet: pallet, extrinsic: index) } - func has(_ type: E.Type) -> Bool { + func has(_ type: E.Type) -> Bool { _events.has(type, extrinsic: index) } @@ -72,11 +72,11 @@ public extension ExtrinsicEvents { try _events.all(events: event, pallet: pallet, extrinsic: index) } - func all(records type: E.Type) -> [BE.ER] { + func all(records type: E.Type) -> [BE.ER] { _events.all(records: type) } - func all(events type: E.Type) throws -> [E] { + func all(events type: E.Type) throws -> [E] { try _events.all(events: type, extrinsic: index) } @@ -88,11 +88,11 @@ public extension ExtrinsicEvents { try _events.first(event: name, pallet: pallet, extrinsic: index) } - func first(record type: E.Type) -> BE.ER? { + func first(record type: E.Type) -> BE.ER? { _events.first(record: type, extrinsic: index) } - func first(event type: E.Type) throws -> E? { + func first(event type: E.Type) throws -> E? { try _events.first(event: type, extrinsic: index) } @@ -104,11 +104,11 @@ public extension ExtrinsicEvents { try _events.last(event: name, pallet: pallet, extrinsic: index) } - func last(record type: E.Type) -> BE.ER? { + func last(record type: E.Type) -> BE.ER? { _events.last(record: type, extrinsic: index) } - func last(event type: E.Type) throws -> E? { + func last(event type: E.Type) throws -> E? { try _events.last(event: type, extrinsic: index) } } diff --git a/Sources/Substrate/Metadata/Metadata.swift b/Sources/Substrate/Metadata/Metadata.swift index 90cd5b2..7b26838 100644 --- a/Sources/Substrate/Metadata/Metadata.swift +++ b/Sources/Substrate/Metadata/Metadata.swift @@ -8,6 +8,8 @@ import Foundation import ScaleCodec +public typealias LastMetadata = MetadataV15 + public protocol Metadata { var extrinsic: ExtrinsicMetadata { get } @@ -38,10 +40,10 @@ public protocol PalletMetadata { func callName(index: UInt8) -> String? func callIndex(name: String) -> UInt8? - func callParams(name: String) -> [NetworkType.Field]? + func callParams(name: String) -> [(field: NetworkType.Field, type: NetworkType)]? func eventName(index: UInt8) -> String? func eventIndex(name: String) -> UInt8? - func eventParams(name: String) -> [NetworkType.Field]? + func eventParams(name: String) -> [(field: NetworkType.Field, type: NetworkType)]? func storage(name: String) -> StorageMetadata? func constant(name: String) -> ConstantMetadata? @@ -49,8 +51,8 @@ public protocol PalletMetadata { public protocol StorageMetadata { var name: String { get } - var modifier: MetadataV14.StorageEntryModifier { get } - var types: (keys: [(MetadataV14.StorageHasher, NetworkType.Info)], + var modifier: LastMetadata.StorageEntryModifier { get } + var types: (keys: [(hasher: LastMetadata.StorageHasher, type: NetworkType.Info)], value: NetworkType.Info) { get } var defaultValue: Data { get } var documentation: [String] { get } @@ -86,7 +88,7 @@ public protocol RuntimeApiMetadata { func resolve( method name: String - ) -> (params: [(String, NetworkType.Info)], result: NetworkType.Info)? + ) -> (params: [(name: String, type: NetworkType.Info)], result: NetworkType.Info)? } public protocol OuterEnumsMetadata { @@ -100,7 +102,7 @@ public enum MetadataError: Error { case storageNonCompositeKey(name: String, pallet: String, type: NetworkType.Info) } -public struct OpaqueMetadata: ScaleCodec.Codable, RuntimeDecodable { +public struct OpaqueMetadata: ScaleCodec.Codable, RuntimeDecodable, IdentifiableType { public let raw: Data public init(from decoder: inout D) throws { @@ -115,4 +117,6 @@ public struct OpaqueMetadata: ScaleCodec.Codable, RuntimeDecodable { var decoder = config.decoder(data: raw) return try decoder.decode(VersionedNetworkMetadata.self).metadata.asMetadata() } + + public static var definition: TypeDefinition { .data } } diff --git a/Sources/Substrate/Metadata/NetworkType.swift b/Sources/Substrate/Metadata/NetworkType.swift index de168a7..f5f7580 100644 --- a/Sources/Substrate/Metadata/NetworkType.swift +++ b/Sources/Substrate/Metadata/NetworkType.swift @@ -40,10 +40,16 @@ public struct NetworkType: ScaleCodec.Codable, Hashable, Equatable, CustomString try encoder.encode(docs) } + @inlinable var name: String? { !path.isEmpty ? path.joined(separator: ".") : nil } + @inlinable + public func i(_ id: Id) -> Info { + Info(id: id, type: self) + } + public var description: String { if let name = self.name { if parameters.isEmpty { @@ -66,6 +72,7 @@ public extension NetworkType { public let id: UInt32 + @inlinable public var rawValue: UInt32 { id } public init(id: UInt32) { @@ -88,6 +95,12 @@ public extension NetworkType { try encoder.encode(id, .compact) } + @inlinable + public func i(_ type: NetworkType) -> Info { + Info(id: self, type: type) + } + + @inlinable public var description: String { String(id) } } } diff --git a/Sources/Substrate/Metadata/v14/MetadataV14.swift b/Sources/Substrate/Metadata/v14/MetadataV14.swift index 4ecfede..452b970 100644 --- a/Sources/Substrate/Metadata/v14/MetadataV14.swift +++ b/Sources/Substrate/Metadata/v14/MetadataV14.swift @@ -60,6 +60,9 @@ public final class MetadataV14: Metadata { public extension MetadataV14 { final class Pallet: PalletMetadata { + public typealias FieldInfo = (field: NetworkType.Field, type: NetworkType) + public typealias VariantInfo = (index: UInt8, fields: [FieldInfo]) + public let name: String public let index: UInt8 public let call: NetworkType.Info? @@ -67,10 +70,10 @@ public extension MetadataV14 { public let error: NetworkType.Info? public let callNameByIdx: [UInt8: String]? - public let callByName: [String: NetworkType.Variant]? + public let callByName: [String: VariantInfo]? public let eventNameByIdx: [UInt8: String]? - public let eventByName: [String: NetworkType.Variant]? + public let eventByName: [String: VariantInfo]? public let storageByName: [String: Storage]? public var storage: [String] { @@ -89,19 +92,19 @@ public extension MetadataV14 { self.call = network.call.map { NetworkType.Info(id: $0, type: types[$0]!) } self.event = network.event.map { NetworkType.Info(id: $0, type: types[$0]!) } self.error = network.error.map { NetworkType.Info(id: $0, type: types[$0]!) } - let calls = self.call.flatMap { Self.variants(for: $0.type.definition) } + let calls = self.call.flatMap { Self.variants(for: $0.type.definition, types: types) } self.callNameByIdx = calls.map { Dictionary(uniqueKeysWithValues: $0.map { ($0.index, $0.name) }) } self.callByName = calls.map { - Dictionary(uniqueKeysWithValues: $0.map { ($0.name, $0) }) + Dictionary(uniqueKeysWithValues: $0.map { ($0.name, ($0.index, $0.fields)) }) } - let events = self.event.flatMap { Self.variants(for:$0.type.definition) } + let events = self.event.flatMap { Self.variants(for:$0.type.definition, types: types) } self.eventNameByIdx = events.map { Dictionary(uniqueKeysWithValues: $0.map { ($0.index, $0.name) }) } self.eventByName = events.map { - Dictionary(uniqueKeysWithValues: $0.map { ($0.name, $0) }) + Dictionary(uniqueKeysWithValues: $0.map { ($0.name, ($0.index, $0.fields)) }) } self.storageByName = try network.storage .flatMap { @@ -123,7 +126,7 @@ public extension MetadataV14 { public func callIndex(name: String) -> UInt8? { callByName?[name]?.index } @inlinable - public func callParams(name: String) -> [NetworkType.Field]? { callByName?[name]?.fields } + public func callParams(name: String) -> [FieldInfo]? { callByName?[name]?.fields } @inlinable public func eventName(index: UInt8) -> String? { eventNameByIdx?[index] } @@ -132,7 +135,7 @@ public extension MetadataV14 { public func eventIndex(name: String) -> UInt8? { eventByName?[name]?.index } @inlinable - public func eventParams(name: String) -> [NetworkType.Field]? { eventByName?[name]?.fields } + public func eventParams(name: String) -> [FieldInfo]? { eventByName?[name]?.fields } @inlinable public func constant(name: String) -> ConstantMetadata? { constantByName[name] } @@ -141,10 +144,14 @@ public extension MetadataV14 { public func storage(name: String) -> StorageMetadata? { storageByName?[name] } private static func variants( - for def: NetworkType.Definition - ) -> [NetworkType.Variant]? { + for def: NetworkType.Definition, types: [NetworkType.Id: NetworkType] + ) -> [(index: UInt8, name: String, fields: [FieldInfo])]? { switch def { - case .variant(variants: let vars): return vars + case .variant(variants: let vars): + return vars.map { + (index: $0.index, name: $0.name, + fields: $0.fields.map{($0, types[$0.type]!)}) + } default: return nil } } @@ -158,7 +165,7 @@ public extension MetadataV14 { struct Storage: StorageMetadata { public let name: String public let modifier: StorageEntryModifier - public let types: (keys: [(StorageHasher, NetworkType.Info)], + public let types: (keys: [(hasher: StorageHasher, type: NetworkType.Info)], value: NetworkType.Info) public let defaultValue: Data public let documentation: [String] diff --git a/Sources/Substrate/Metadata/v14/NetworkMetadataV14.swift b/Sources/Substrate/Metadata/v14/NetworkMetadataV14.swift index 28351ea..44684b2 100644 --- a/Sources/Substrate/Metadata/v14/NetworkMetadataV14.swift +++ b/Sources/Substrate/Metadata/v14/NetworkMetadataV14.swift @@ -167,8 +167,20 @@ public extension MetadataV14.Network { } } + public var name: String { + switch self { + case .blake2b128: return "Blake2_128" + case .blake2b256: return "Blake2_256" + case .blake2b128concat: return "Blake2_128Concat" + case .xx128: return "Twox128" + case .xx256: return "Twox256" + case .xx64concat: return "Twox64Concat" + case .identity: return "Identity" + } + } + @inlinable - public var description: String { hasher.name } + public var description: String { name } } enum StorageEntryModifier: CaseIterable, ScaleCodec.Codable, CustomStringConvertible { diff --git a/Sources/Substrate/Metadata/v15/MetadataV15.swift b/Sources/Substrate/Metadata/v15/MetadataV15.swift index 54d83ee..30206b0 100644 --- a/Sources/Substrate/Metadata/v15/MetadataV15.swift +++ b/Sources/Substrate/Metadata/v15/MetadataV15.swift @@ -67,8 +67,13 @@ public extension MetadataV15 { typealias Constant = MetadataV14.Constant typealias Storage = MetadataV14.Storage typealias Extrinsic = MetadataV14.Extrinsic + typealias StorageEntryModifier = Network.StorageEntryModifier + typealias StorageHasher = Network.StorageHasher final class Pallet: PalletMetadata { + public typealias FieldInfo = (field: NetworkType.Field, type: NetworkType) + public typealias VariantInfo = (index: UInt8, fields: [FieldInfo]) + public let name: String public let index: UInt8 public let call: NetworkType.Info? @@ -76,10 +81,10 @@ public extension MetadataV15 { public let error: NetworkType.Info? public let callNameByIdx: [UInt8: String]? - public let callByName: [String: NetworkType.Variant]? + public let callByName: [String: VariantInfo]? public let eventNameByIdx: [UInt8: String]? - public let eventByName: [String: NetworkType.Variant]? + public let eventByName: [String: VariantInfo]? public let storageByName: [String: Storage]? public var storage: [String] { @@ -97,16 +102,16 @@ public extension MetadataV15 { self.call = network.call.map { NetworkType.Info(id: $0, type: types[$0]!) } self.event = network.event.map { NetworkType.Info(id: $0, type: types[$0]!) } self.error = network.error.map { NetworkType.Info(id: $0, type: types[$0]!) } - let calls = self.call.flatMap { Self.variants(for: $0.type.definition) } + let calls = self.call.flatMap { Self.variants(for: $0.type.definition, types: types) } self.callByName = calls.map { - Dictionary(uniqueKeysWithValues: $0.map { ($0.name, $0) }) + Dictionary(uniqueKeysWithValues: $0.map { ($0.name, ($0.index, $0.fields)) }) } self.callNameByIdx = calls.map { Dictionary(uniqueKeysWithValues: $0.map { ($0.index, $0.name) }) } - let events = self.event.flatMap { Self.variants(for:$0.type.definition) } + let events = self.event.flatMap { Self.variants(for:$0.type.definition, types: types) } self.eventByName = events.map { - Dictionary(uniqueKeysWithValues: $0.map { ($0.name, $0) }) + Dictionary(uniqueKeysWithValues: $0.map { ($0.name, ($0.index, $0.fields)) }) } self.eventNameByIdx = events.map { Dictionary(uniqueKeysWithValues: $0.map { ($0.index, $0.name) }) @@ -131,7 +136,7 @@ public extension MetadataV15 { public func callIndex(name: String) -> UInt8? { callByName?[name]?.index } @inlinable - public func callParams(name: String) -> [NetworkType.Field]? { callByName?[name]?.fields } + public func callParams(name: String) -> [FieldInfo]? { callByName?[name]?.fields } @inlinable public func eventName(index: UInt8) -> String? { eventNameByIdx?[index] } @@ -140,7 +145,7 @@ public extension MetadataV15 { public func eventIndex(name: String) -> UInt8? { eventByName?[name]?.index } @inlinable - public func eventParams(name: String) -> [NetworkType.Field]? { eventByName?[name]?.fields } + public func eventParams(name: String) -> [FieldInfo]? { eventByName?[name]?.fields } @inlinable public func constant(name: String) -> ConstantMetadata? { constantByName[name] } @@ -149,10 +154,14 @@ public extension MetadataV15 { public func storage(name: String) -> StorageMetadata? { storageByName?[name] } private static func variants( - for def: NetworkType.Definition - ) -> [NetworkType.Variant]? { + for def: NetworkType.Definition, types: [NetworkType.Id: NetworkType] + ) -> [(index: UInt8, name: String, fields: [FieldInfo])]? { switch def { - case .variant(variants: let vars): return vars + case .variant(variants: let vars): + return vars.map { + (index: $0.index, name: $0.name, + fields: $0.fields.map{($0, types[$0.type]!)}) + } default: return nil } } @@ -165,7 +174,7 @@ public extension MetadataV15 { public var methods: [String] { Array(methodsByName.keys) } public let methodsByName: - [String: (params: [(String, NetworkType.Info)], result: NetworkType.Info)] + [String: (params: [(name: String, type: NetworkType.Info)], result: NetworkType.Info)] public init(network: Network.RuntimeApi, types: [NetworkType.Id: NetworkType]) { self.name = network.name @@ -182,7 +191,7 @@ public extension MetadataV15 { public func resolve( method name: String - ) -> (params: [(String, NetworkType.Info)], result: NetworkType.Info)? { + ) -> (params: [(name: String, type: NetworkType.Info)], result: NetworkType.Info)? { methodsByName[name] } } diff --git a/Sources/Substrate/Metadata/v15/NetworkMetadataV15.swift b/Sources/Substrate/Metadata/v15/NetworkMetadataV15.swift index f802d6a..1242687 100644 --- a/Sources/Substrate/Metadata/v15/NetworkMetadataV15.swift +++ b/Sources/Substrate/Metadata/v15/NetworkMetadataV15.swift @@ -61,6 +61,8 @@ public extension MetadataV15.Network { typealias Extrinsic = MetadataV14.Network.Extrinsic typealias PalletStorage = MetadataV14.Network.PalletStorage typealias PalletConstant = MetadataV14.Network.PalletConstant + typealias StorageHasher = MetadataV14.Network.StorageHasher + typealias StorageEntryModifier = MetadataV14.Network.StorageEntryModifier struct Pallet: ScaleCodec.Codable { public let name: String diff --git a/Sources/Substrate/Runtime/ExtendedRuntime.swift b/Sources/Substrate/Runtime/ExtendedRuntime.swift index 4416d70..57c6b33 100644 --- a/Sources/Substrate/Runtime/ExtendedRuntime.swift +++ b/Sources/Substrate/Runtime/ExtendedRuntime.swift @@ -108,7 +108,7 @@ open class ExtendedRuntime: Runtime { open func validate() throws { try extrinsicManager.validate(runtime: self) - try config.pallets(runtime: self).voidErrorMap { $0.validate(runtime: self) }.get() + try config.frames(runtime: self).voidErrorMap { $0.validate(runtime: self) }.get() try config.runtimeCalls(runtime: self).voidErrorMap { $0.validate(runtime: self) }.get() } } diff --git a/Sources/Substrate/Runtime/Runtime.swift b/Sources/Substrate/Runtime/Runtime.swift index c9fe911..8372777 100644 --- a/Sources/Substrate/Runtime/Runtime.swift +++ b/Sources/Substrate/Runtime/Runtime.swift @@ -30,7 +30,8 @@ public protocol Runtime: AnyObject { // Calls func resolve(callName index: UInt8, pallet: UInt8) -> (pallet: String, name: String)? func resolve(callIndex name: String, pallet: String) -> (pallet: UInt8, index: UInt8)? - func resolve(callParams name: String, pallet: String) -> [NetworkType.Field]? + func resolve(callParams name: String, pallet: String) -> [(field: NetworkType.Field, + type: NetworkType)]? // Runtime Calls func resolve( @@ -40,7 +41,8 @@ public protocol Runtime: AnyObject { // Events func resolve(eventName index: UInt8, pallet: UInt8) -> (pallet: String, name: String)? func resolve(eventIndex name: String, pallet: String) -> (pallet: UInt8, index: UInt8)? - func resolve(eventParams name: String, pallet: String) -> [NetworkType.Field]? + func resolve(eventParams name: String, pallet: String) -> [(field: NetworkType.Field, + type: NetworkType)]? //Constants func resolve( @@ -50,7 +52,9 @@ public protocol Runtime: AnyObject { // Storage func resolve( storage name: String, pallet: String - ) -> (keys: [(MetadataV14.StorageHasher, NetworkType.Info)], value: NetworkType.Info, `default`: Data)? + ) -> (keys: [(hasher: LastMetadata.StorageHasher, type: NetworkType.Info)], + value: NetworkType.Info, + `default`: Data)? } public extension Runtime { @@ -79,7 +83,9 @@ public extension Runtime { } @inlinable - func resolve(callParams name: String, pallet: String) -> [NetworkType.Field]? { + func resolve(callParams name: String, pallet: String) -> [(field: NetworkType.Field, + type: NetworkType)]? + { metadata.resolve(pallet: pallet)?.callParams(name: name) } @@ -105,7 +111,9 @@ public extension Runtime { } @inlinable - func resolve(eventParams name: String, pallet: String) -> [NetworkType.Field]? { + func resolve(eventParams name: String, pallet: String) -> [(field: NetworkType.Field, + type: NetworkType)]? + { metadata.resolve(pallet: pallet)?.eventParams(name: name) } @@ -132,10 +140,12 @@ public extension Runtime { @inlinable func resolve( storage name: String, pallet: String - ) -> (keys: [(MetadataV14.StorageHasher, NetworkType.Info)], value: NetworkType.Info, `default`: Data)? { + ) -> (keys: [(hasher: LastMetadata.StorageHasher, type: NetworkType.Info)], + value: NetworkType.Info, + `default`: Data)? + { metadata.resolve(pallet: pallet)?.storage(name: name).flatMap { - let (keys, value) = $0.types - return (keys.map { ($0.0, $0.1) }, value, $0.defaultValue) + ($0.types.keys, $0.types.value, $0.defaultValue) } } } diff --git a/Sources/Substrate/Runtime/RuntimeCall.swift b/Sources/Substrate/Runtime/RuntimeCall.swift index 23e7523..16fd1cb 100644 --- a/Sources/Substrate/Runtime/RuntimeCall.swift +++ b/Sources/Substrate/Runtime/RuntimeCall.swift @@ -22,14 +22,61 @@ public extension RuntimeCall { var fullName: String { "\(api)_\(method)" } } -public protocol StaticRuntimeCall: RuntimeCall { +public protocol StaticRuntimeCall: RuntimeCall, FrameType { static var api: String { get } static var method: String { get } } public extension StaticRuntimeCall { - var api: String { Self.api } - var method: String { Self.method } + @inlinable var method: String { Self.method } + @inlinable var api: String { Self.api } + @inlinable var frame: String { api } + @inlinable static var frame: String { api } + @inlinable var name: String { method } + @inlinable static var name: String { method } + @inlinable static var frameTypeName: String { "RuntimeCall" } +} + +public typealias RuntimeCallTypeInfo = (params: [(name: String, type: NetworkType.Info)], + result: NetworkType.Info) +public typealias RuntimeCallChildTypes = (params: [ValidatableType.Type], + result: ValidatableType.Type) + +public extension StaticRuntimeCall where + Self: ComplexFrameType, TypeInfo == RuntimeCallTypeInfo +{ + @inlinable + static func typeInfo(runtime: any Runtime) -> Result { + guard let info = runtime.resolve(runtimeCall: method, api: api) else { + return .failure(.typeInfoNotFound(for: Self.self)) + } + return .success(info) + } +} + +public extension ComplexStaticFrameType where + TypeInfo == RuntimeCallTypeInfo, + ChildTypes == RuntimeCallChildTypes +{ + static func validate(info: TypeInfo, + runtime: any Runtime) -> Result + { + let ourTypes = childTypes + guard ourTypes.params.count == info.params.count else { + return .failure(.wrongFieldsCount(for: Self.self, expected: ourTypes.params.count, + got: info.params.count)) + } + return zip(ourTypes.params, info.params).enumerated().voidErrorMap { index, zip in + let (our, info) = zip + return our.validate(runtime: runtime, type: info.type).mapError { + .childError(for: Self.self, index: index, error: $0) + } + }.flatMap { + ourTypes.result.validate(runtime: runtime, type: info.result).mapError { + .childError(for: Self.self, index: -1, error: $0) + } + } + } } public extension StaticRuntimeCall where TReturn: RuntimeDecodable { @@ -38,7 +85,9 @@ public extension StaticRuntimeCall where TReturn: RuntimeDecodable { } } -public protocol StaticCodableRuntimeCall: StaticRuntimeCall where TReturn: ScaleCodec.Decodable { +public protocol StaticCodableRuntimeCall: StaticRuntimeCall + where TReturn: ScaleCodec.Decodable +{ func encodeParams(in encoder: inout E) throws func decode(returnFrom decoder: inout D) throws -> TReturn } @@ -63,16 +112,3 @@ public enum RuntimeCallCodingError: Error { expected: [(String, NetworkType.Info)]) case parameterNotFound(name: String, inParams: [String: any ValueRepresentable]) } - -public extension StaticRuntimeCall where - Self: RuntimeValidatableComposite, TReturn: RuntimeDynamicValidatable -{ - static func validatableFieldIds(runtime: any Runtime) -> Result<[NetworkType.Id], ValidationError> { - guard let info = runtime.resolve(runtimeCall: method, api: api) else { - return .failure(.infoNotFound(for: Self.self)) - } - return TReturn.validate(runtime: runtime, type: info.result.id) - .mapError { .childError(for: Self.self, error: $0) } - .map { info.params.map{$0.type.id} } - } -} diff --git a/Sources/Substrate/Runtime/RuntimeCodable.swift b/Sources/Substrate/Runtime/RuntimeCodable.swift index cc89d63..cb9c21e 100644 --- a/Sources/Substrate/Runtime/RuntimeCodable.swift +++ b/Sources/Substrate/Runtime/RuntimeCodable.swift @@ -251,34 +251,3 @@ public extension RuntimeCustomDynamicCoder { try Value(from: &container, as: id, runtime: runtime, custom: false) } } - -public enum DynamicCodableError: Error { - case typeNotFound(NetworkType.Id) - case wrongType(got: NetworkType, for: String) - case typeIdMismatch(got: NetworkType.Id, has: NetworkType.Id) - case wrongValuesCount(in: NetworkType, expected: Int, for: String) - case variantNotFound(name: String, in: NetworkType) - case fieldNotFound(name: String, in: NetworkType) - case runtimeTypeLookupFailed(name: String, reason: Error) - case badDecodedValue(value: Any, forType: NetworkType.Id) - - public init(_ validation: DynamicValidationError) { - switch validation { - case .typeNotFound(let id): self = .typeNotFound(id) - case .wrongType(got: let t, for: let f): self = .wrongType(got: t, for: f) - case .wrongValuesCount(in: let t, expected: let e, for: let f): - self = .wrongValuesCount(in: t, expected: e, for: f) - case .variantNotFound(name: let n, in: let t): self = .variantNotFound(name: n, in: t) - case .typeIdMismatch(got: let got, has: let has): self = .typeIdMismatch(got: got, has: has) - case .fieldNotFound(name: let n, in: let t): self = .fieldNotFound(name: n, in: t) - case .runtimeTypeLookupFailed(name: let n, reason: let e): - self = .runtimeTypeLookupFailed(name: n, reason: e) - } - } -} - -public extension Result where Failure == DynamicValidationError { - @inlinable func getCodableError() throws -> Success { - try mapError { DynamicCodableError($0) }.get() - } -} diff --git a/Sources/Substrate/Runtime/RuntimeValidatable.swift b/Sources/Substrate/Runtime/RuntimeValidatable.swift deleted file mode 100644 index f02c271..0000000 --- a/Sources/Substrate/Runtime/RuntimeValidatable.swift +++ /dev/null @@ -1,394 +0,0 @@ -// -// RuntimeValidatable.swift -// -// -// Created by Yehor Popovych on 21/08/2023. -// - -import Foundation -import ScaleCodec -import Numberick - -public protocol RuntimeValidatable { - static func validate(runtime: any Runtime) -> Result -} - -public protocol RuntimeDynamicValidatable { - static func validate(runtime: any Runtime, - type id: NetworkType.Id) -> Result -} - -public protocol RuntimeValidatableComposite: RuntimeValidatable { - static func validatableFieldIds(runtime: any Runtime) -> Result<[NetworkType.Id], ValidationError> - static func validate(fields ids: [NetworkType.Id], - runtime: any Runtime) -> Result -} - -public extension RuntimeValidatableComposite { - static func validate(runtime: any Runtime) -> Result { - validatableFieldIds(runtime: runtime).flatMap { - validate(fields: $0, runtime: runtime) - } - } -} - -public protocol RuntimeValidatableStaticComposite: RuntimeValidatableComposite { - static var validatableFields: [any RuntimeDynamicValidatable.Type] { get } -} - -public extension RuntimeValidatableStaticComposite { - static func validate(fields ids: [NetworkType.Id], - runtime: any Runtime) -> Result - { - let ourFields = validatableFields - guard ourFields.count == ids.count else { - return .failure(.wrongFieldsCount(for: Self.self, - expected: ourFields.count, - got: ids.count)) - } - return zip(ourFields, ids).voidErrorMap { field, id in - field.validate(runtime: runtime, type: id).mapError { - .childError(for: Self.self, error: $0) - } - } - } -} - -public protocol RuntimeDynamicValidatablePrimitive: RuntimeDynamicValidatable { - static func validate(self: NetworkType, name: String, - primitive: NetworkType.Primitive) -> Result -} - -public extension RuntimeDynamicValidatablePrimitive { - static func _validate(runtime: Runtime, - type id: NetworkType.Id) -> Result - { - guard let info = runtime.resolve(type: id) else { - return .failure(.typeNotFound(id)) - } - guard let primitive = info.asPrimitive(runtime) else { - return .failure(.wrongType(got: info, for: String(describing: Self.self))) - } - return validate(self: info, name: String(describing: Self.self), - primitive: primitive).map { info } - } - - static func validate(runtime: any Runtime, - type id: NetworkType.Id) -> Result - { - _validate(runtime: runtime, type: id).map {_ in} - } -} - -public protocol RuntimeDynamicValidatableComposite: RuntimeDynamicValidatable { - static func validate(self: NetworkType, name: String, - fields: [(name: String?, type: NetworkType.Id)], - runtime: any Runtime) -> Result -} - -public extension RuntimeDynamicValidatableComposite { - static func _validate(runtime: Runtime, - type id: NetworkType.Id) -> Result - { - guard let info = runtime.resolve(type: id)?.flatten(runtime) else { - return .failure(.typeNotFound(id)) - } - let fields: [(name: String?, type: NetworkType.Id)] - switch info.definition { - case .composite(fields: let fs): - fields = fs.map { ($0.name, $0.type) } - case .tuple(components: let ids): - fields = ids.map { (nil, $0) } - default: - return .failure(.wrongType(got: info, for: String(describing: Self.self))) - } - return validate(self: info, name: String(describing: Self.self), - fields: fields, runtime: runtime).map { info } - } - - static func validate(runtime: any Runtime, - type id: NetworkType.Id) -> Result - { - _validate(runtime: runtime, type: id).map {_ in} - } -} - -public protocol RuntimeDynamicValidatableStaticComposite: RuntimeDynamicValidatableComposite { - static var validatableFields: [any RuntimeDynamicValidatable.Type] { get } -} - -public extension RuntimeDynamicValidatableStaticComposite { - static func validate(self: NetworkType, name: String, - fields: [(name: String?, type: NetworkType.Id)], - runtime: any Runtime) -> Result - { - let ourFields = validatableFields - guard ourFields.count == fields.count else { - return .failure(.wrongValuesCount(in: self, - expected: ourFields.count, - for: name)) - } - return zip(ourFields, fields).voidErrorMap { field, info in - field.validate(runtime: runtime, type: info.type) - } - } -} - -public protocol RuntimeDynamicValidatableVariant: RuntimeDynamicValidatable { - static func validate(self: NetworkType, name: String, - variants: [NetworkType.Variant], - runtime: any Runtime) -> Result -} - -public extension RuntimeDynamicValidatableVariant { - static func _validate(runtime: Runtime, - type id: NetworkType.Id) -> Result - { - guard let info = runtime.resolve(type: id)?.flatten(runtime) else { - return .failure(.typeNotFound(id)) - } - guard case .variant(variants: let variants) = info.definition else { - return .failure(.wrongType(got: info, for: String(describing: Self.self))) - } - return validate(self: info, name: String(describing: Self.self), - variants: variants, runtime: runtime).map { info } - } - - static func validate(runtime: any Runtime, - type id: NetworkType.Id) -> Result - { - _validate(runtime: runtime, type: id).map {_ in} - } -} - -public typealias ValidatableStaticVariant = (index: UInt8, name: String, - fields: [any RuntimeDynamicValidatable.Type]) - -public protocol RuntimeDynamicValidatableStaticVariant: RuntimeDynamicValidatableVariant { - static var validatableVariants: [ValidatableStaticVariant] { get } -} - -public extension RuntimeDynamicValidatableStaticVariant { - static func validate(self: NetworkType, name: String, - variants: [NetworkType.Variant], - runtime: any Runtime) -> Result - { - let ourVariants = validatableVariants - guard ourVariants.count == variants.count else { - return .failure(.wrongValuesCount(in: self, - expected: ourVariants.count, - for: name)) - } - let variantsDict = Dictionary(uniqueKeysWithValues: variants.map { ($0.name, $0) }) - return ourVariants.voidErrorMap { variant in - guard let inVariant = variantsDict[variant.name] else { - return .failure(.variantNotFound(name: variant.name, in: self)) - } - guard variant.index == inVariant.index else { - return .failure(.wrongType(got: self, for: "\(name).\(variant.name)")) - } - guard variant.fields.count == inVariant.fields.count else { - return .failure(.wrongValuesCount(in: self, - expected: variant.fields.count, - for: "\(name).\(variant.name)")) - } - return zip(variant.fields, inVariant.fields).voidErrorMap { field, info in - field.validate(runtime: runtime, type: info.type) - } - } - } -} - -public enum ValidationError: Error { - case childError(for: RuntimeValidatable.Type, error: DynamicValidationError) - case infoNotFound(for: RuntimeValidatable.Type) - case wrongFieldsCount(for: RuntimeValidatable.Type, expected: Int, got: Int) - case paramMismatch(for: RuntimeValidatable.Type, expected: String, got: String) -} - -public enum DynamicValidationError: Error { - case typeNotFound(NetworkType.Id) - case runtimeTypeLookupFailed(name: String, reason: Error) - case wrongType(got: NetworkType, for: String) - case wrongValuesCount(in: NetworkType, expected: Int, for: String) - case fieldNotFound(name: String, in: NetworkType) - case variantNotFound(name: String, in: NetworkType) - case typeIdMismatch(got: NetworkType.Id, has: NetworkType.Id) -} - -public extension CaseIterable where Self: RuntimeDynamicValidatableStaticVariant { - static var validatableVariants: [ValidatableStaticVariant] { - allCases.enumerated().map { - (idx, cs) in (UInt8(idx), String(describing: cs).uppercasedFirst, []) - } - } -} - -public extension FixedWidthInteger where Self: RuntimeDynamicValidatablePrimitive { - @inlinable - static func validate(self: NetworkType, name: String, - primitive: NetworkType.Primitive) -> Result - { - guard let int = primitive.isAnyInt else { - return .failure(.wrongType(got: self, for: name)) - } - guard int.signed == Self.isSigned, int.bits == Self.bitWidth else { - return .failure(.wrongType(got: self, for: name)) - } - return .success(()) - } -} - -extension UInt8: RuntimeDynamicValidatablePrimitive {} -extension UInt16: RuntimeDynamicValidatablePrimitive {} -extension UInt32: RuntimeDynamicValidatablePrimitive {} -extension UInt64: RuntimeDynamicValidatablePrimitive {} -extension UInt: RuntimeDynamicValidatablePrimitive {} -extension NBKDoubleWidth: RuntimeDynamicValidatablePrimitive {} -extension Int8: RuntimeDynamicValidatablePrimitive {} -extension Int16: RuntimeDynamicValidatablePrimitive {} -extension Int32: RuntimeDynamicValidatablePrimitive {} -extension Int64: RuntimeDynamicValidatablePrimitive {} -extension Int: RuntimeDynamicValidatablePrimitive {} - -extension Value: RuntimeDynamicValidatable { - @inlinable - public static func validate(runtime: any Runtime, - type id: NetworkType.Id) -> Result - { - .success(()) - } -} - -extension Bool: RuntimeDynamicValidatablePrimitive { - @inlinable - public static func validate( - self: NetworkType, name: String, primitive: NetworkType.Primitive - ) -> Result { - primitive.isBool ? .success(()) : .failure(.wrongType(got: self, for: name)) - } -} - -extension String: RuntimeDynamicValidatablePrimitive { - @inlinable - public static func validate( - self: NetworkType, name: String, primitive: NetworkType.Primitive - ) -> Result { - primitive.isString ? .success(()) : .failure(.wrongType(got: self, for: name)) - } -} - -extension Data: RuntimeDynamicValidatable { - public static func validate(runtime: Runtime, - type id: NetworkType.Id) -> Result - { - guard let info = runtime.resolve(type: id) else { - return .failure(.typeNotFound(id)) - } - guard info.asBytes(runtime) != nil else { - return .failure(.wrongType(got: info, for: String(describing: Self.self))) - } - return .success(()) - } -} - -extension Compact: RuntimeDynamicValidatable { - public static func validate(runtime: any Runtime, - type id: NetworkType.Id) -> Result - { - guard let info = runtime.resolve(type: id) else { - return .failure(.typeNotFound(id)) - } - guard let compact = info.asCompact(runtime) else { - return .failure(.wrongType(got: info, for: String(describing: Self.self))) - } - if let primitive = compact.asPrimitive(runtime) { // Compact - guard let bits = primitive.isUInt else { - return .failure(.wrongType(got: info, for: String(describing: Self.self))) - } - guard bits <= T.compactBitWidth else { - return .failure(.wrongType(got: info, for: String(describing: Self.self))) - } - } else if compact.isEmpty(runtime) { // Compact<()> - guard T.compactBitWidth == 0 else { - return .failure(.wrongType(got: info, for: String(describing: Self.self))) - } - } else { // Unknown Compact - return .failure(.wrongType(got: info, for: String(describing: Self.self))) - } - - return .success(()) - } -} - -extension Character: RuntimeDynamicValidatablePrimitive { - @inlinable - public static func validate( - self: NetworkType, name: String, primitive: NetworkType.Primitive - ) -> Result { - primitive.isChar ? .success(()) : .failure(.wrongType(got: self, for: name)) - } -} - -extension Array: RuntimeDynamicValidatable where Element: RuntimeDynamicValidatable { - public static func validate(runtime: Runtime, - type id: NetworkType.Id) -> Result - { - guard let info = runtime.resolve(type: id) else { - return .failure(.typeNotFound(id)) - } - let flat = info.flatten(runtime) - switch flat.definition { - case .array(count: _, of: let eType), .sequence(of: let eType): - return Element.validate(runtime: runtime, type: eType) - case .composite(fields: let fields): - for field in fields { - switch Element.validate(runtime: runtime, type: field.type) { - case .success(_): continue - case .failure(let err): return .failure(err) - } - } - return .success(()) - case .tuple(components: let ids): - for id in ids { - switch Element.validate(runtime: runtime, type: id) { - case .success(_): continue - case .failure(let err): return .failure(err) - } - } - return .success(()) - default: - return .failure(.wrongType(got: info, for: String(describing: Self.self))) - } - } -} - -extension Optional: RuntimeDynamicValidatable where Wrapped: RuntimeDynamicValidatable { - public static func validate(runtime: Runtime, - type id: NetworkType.Id) -> Result - { - guard let info = runtime.resolve(type: id) else { - return .failure(.typeNotFound(id)) - } - guard let field = info.asOptional(runtime) else { - return .failure(.wrongType(got: info, for: String(describing: Self.self))) - } - return Wrapped.validate(runtime: runtime, type: field.type) - } -} - -extension Either: RuntimeDynamicValidatable where Left: RuntimeDynamicValidatable, Right: RuntimeDynamicValidatable { - public static func validate(runtime: Runtime, - type id: NetworkType.Id) -> Result - { - guard let info = runtime.resolve(type: id) else { - return .failure(.typeNotFound(id)) - } - guard let result = info.asResult(runtime) else { - return .failure(.wrongType(got: info, for: String(describing: Self.self))) - } - return Left.validate(runtime: runtime, type: result.err.type).flatMap { - Right.validate(runtime: runtime, type: result.ok.type) - } - } -} diff --git a/Sources/Substrate/Storage/StorageKey+StaticHelpers.swift b/Sources/Substrate/Storage/StorageKey+StaticHelpers.swift index d30c2f6..e0f6db0 100644 --- a/Sources/Substrate/Storage/StorageKey+StaticHelpers.swift +++ b/Sources/Substrate/Storage/StorageKey+StaticHelpers.swift @@ -20,8 +20,21 @@ public extension PlainStorageKey { self.init() } var pathHash: Data { Data() } +} + +public extension PlainStorageKey where + Self: ComplexStaticFrameType, TValue: ValidatableType, + ChildTypes == StorageKeyChildTypes +{ @inlinable - static var keyPath: [(any RuntimeDynamicValidatable.Type, any StaticHasher.Type)] { [] } + static var childTypes: ChildTypes { (keys: [], value: TValue.self) } +} + +public extension PlainStorageKey where TValue: IdentifiableType { + @inlinable + static var definition: FrameTypeDefinition { + .storage(Self.self, keys: [], value: TValue.definition) + } } public protocol MapStorageKey: StaticStorageKey, IterableStorageKey where @@ -48,15 +61,27 @@ public extension MapStorageKey { } } -public extension MapStorageKey where TKH.TKey: RuntimeDynamicValidatable { +public extension MapStorageKey where + Self: ComplexStaticFrameType, TKH.TKey: ValidatableType, + TValue: ValidatableType, ChildTypes == StorageKeyChildTypes +{ + @inlinable + static var childTypes: ChildTypes { + (keys: [(TKH.THasher.self, TKH.TKey.self)], value: TValue.self) + } +} + +public extension MapStorageKey where TKH.TKey: IdentifiableType, TValue: IdentifiableType { @inlinable - static var keyPath: [(any RuntimeDynamicValidatable.Type, any StaticHasher.Type)] { - [(TKH.TKey.self, TKH.THasher.self)] + static var definition: FrameTypeDefinition { + .storage(Self.self, keys: [(key: TKH.TKey.definition, hasher: TKH.THasher.hasherType)], + value: TValue.definition) } } public protocol DoubleMapStorageKey: StaticStorageKey, IterableStorageKey where - TParams == (TKH1.TKey, TKH2.TKey), TIterator == MapStorageKeyIterator + TParams == (TKH1.TKey, TKH2.TKey), TIterator == MapStorageKeyIterator, + TKH1.TKey: IdentifiableType, TKH2.TKey: IdentifiableType { associatedtype TKH1: TupleStorageKeyHasherPair associatedtype TKH2: TupleStorageKeyHasherPair @@ -88,11 +113,26 @@ public extension DoubleMapStorageKey { } public extension DoubleMapStorageKey where - TKH1.TKey: RuntimeDynamicValidatable, TKH2.TKey: RuntimeDynamicValidatable + Self: ComplexStaticFrameType, TKH1.TKey: ValidatableType, + TKH2.TKey: ValidatableType, TValue: ValidatableType, + ChildTypes == StorageKeyChildTypes +{ + @inlinable + static var childTypes: ChildTypes { + (keys: [(TKH1.THasher.self, TKH1.TKey.self), (TKH2.THasher.self, TKH2.TKey.self)], + value: TValue.self) + } +} + +public extension DoubleMapStorageKey where + TKH1.TKey: IdentifiableType, TKH2.TKey: IdentifiableType, + TValue: IdentifiableType { @inlinable - static var keyPath: [(any RuntimeDynamicValidatable.Type, any StaticHasher.Type)] { - [(TKH1.TKey.self, TKH1.THasher.self), (TKH2.TKey.self, TKH2.THasher.self)] + static var definition: FrameTypeDefinition { + .storage(Self.self, keys: [(key: TKH1.TKey.definition, hasher: TKH1.THasher.hasherType), + (key: TKH2.TKey.definition, hasher: TKH2.THasher.hasherType)], + value: TValue.definition) } } diff --git a/Sources/Substrate/Storage/StorageKey.swift b/Sources/Substrate/Storage/StorageKey.swift index 615670b..6da6d86 100644 --- a/Sources/Substrate/Storage/StorageKey.swift +++ b/Sources/Substrate/Storage/StorageKey.swift @@ -58,7 +58,37 @@ public protocol IterableStorageKey: StorageKey { associatedtype TIterator: StorageKeyRootIterator } -public protocol StaticStorageKey: StorageKey, PalletType, RuntimeDecodable where TBaseParams == Void { +public protocol PalletStorageKey: StorageKey, FrameType + where TBaseParams == Void +{ + static var pallet: String { get } +} + +public extension PalletStorageKey { + @inlinable var pallet: String { Self.pallet } + @inlinable var frame: String { pallet } + @inlinable static var frame: String { pallet } + @inlinable static var frameTypeName: String { "StorageKey" } +} + +public typealias StorageKeyTypeInfo = (keys: [(hasher: LastMetadata.StorageHasher, type: NetworkType.Info)], + value: NetworkType.Info) +public typealias StorageKeyChildTypes = (keys: [(hasher: StaticHasher.Type, type: ValidatableType.Type)], + value: ValidatableType.Type) + +public extension PalletStorageKey where + Self: ComplexFrameType, TypeInfo == StorageKeyTypeInfo +{ + @inlinable + static func typeInfo(runtime: any Runtime) -> Result { + guard let info = runtime.resolve(storage: name, pallet: pallet) else { + return .failure(.typeInfoNotFound(for: Self.self)) + } + return .success((info.keys, info.value)) + } +} + +public protocol StaticStorageKey: PalletStorageKey, RuntimeDecodable { init(_ params: TParams, runtime: any Runtime) throws init(decodingPath decoder: inout D, runtime: any Runtime) throws var pathHash: Data { get } @@ -87,14 +117,14 @@ public extension StaticStorageKey { } static func validate(base: TBaseParams, runtime: any Runtime) throws { - if runtime.resolve(storage: Self.name, pallet: Self.pallet) == nil { - throw StorageKeyCodingError.storageNotFound(name: Self.name, pallet: Self.pallet) + if runtime.resolve(storage: name, pallet: pallet) == nil { + throw FrameTypeError.typeInfoNotFound(for: Self.self) } } static func defaultValue(base: TBaseParams, runtime: any Runtime) throws -> TValue { - guard let (_, _, data) = runtime.resolve(storage: Self.name, pallet: Self.pallet) else { - throw StorageKeyCodingError.storageNotFound(name: Self.name, pallet: Self.pallet) + guard let (_, _, data) = runtime.resolve(storage: name, pallet: pallet) else { + throw StorageKeyCodingError.storageNotFound(name: name, pallet: pallet) } var decoder = runtime.decoder(with: data) return try decode(valueFrom: &decoder, runtime: runtime) @@ -111,8 +141,11 @@ public extension StaticStorageKey where TValue: RuntimeDecodable { } } -public extension StorageKeyRootIterator where TKey: StaticStorageKey { +public extension StorageKeyRootIterator where TKey: PalletStorageKey { @inlinable init() { self.init(base: ()) } +} + +public extension StorageKeyRootIterator where TKey: StaticStorageKey { @inlinable var hash: Data { Self.hash } @inlinable static var hash: Data { TKey.prefix } } @@ -123,6 +156,37 @@ public extension StorageKeyIterator where TKey: StaticStorageKey { } } +public extension ComplexStaticFrameType + where TypeInfo == StorageKeyTypeInfo, ChildTypes == StorageKeyChildTypes +{ + static func validate(info: TypeInfo, + runtime: any Runtime) -> Result + { + let ourTypes = childTypes + guard ourTypes.keys.count == info.keys.count else { + return .failure(.wrongFieldsCount(for: Self.self, expected: ourTypes.keys.count, + got: info.keys.count)) + } + return zip(ourTypes.keys, info.keys).enumerated().voidErrorMap { index, zip in + let (our, info) = zip + guard our.hasher.hasherType == info.hasher else { + return .failure( + .paramMismatch(for: Self.self, index: index, + expected: our.hasher.hasherType.name, + got: info.hasher.name) + ) + } + return our.type.validate(runtime: runtime, type: info.type).mapError { + .childError(for: Self.self, index: index, error: $0) + } + }.flatMap { + ourTypes.value.validate(runtime: runtime, type: info.value) .mapError { + .childError(for: Self.self, index: -1, error: $0) + } + } + } +} + public protocol DynamicStorageKey: IterableStorageKey where TParams == [ValueRepresentable], TBaseParams == (name: String, pallet: String), @@ -142,45 +206,6 @@ public protocol SomeStorageChangeSet: RuntimeSwiftDecodable { var changes: [(key: Data, value: Data?)] { get } } -public protocol ValidatableStorageKey: StaticStorageKey, RuntimeValidatable - where TValue: RuntimeDynamicValidatable -{ - static var keyPath: [(any RuntimeDynamicValidatable.Type, any StaticHasher.Type)] { get } -} - -public extension ValidatableStorageKey { - static func validate(path: [(NetworkType.Id, MetadataV14.StorageHasher)], - runtime: any Runtime) -> Result - { - let ownKp = keyPath - guard path.count == ownKp.count else { - return .failure(.wrongFieldsCount(for: Self.self, - expected: ownKp.count, - got: path.count)) - } - return zip(ownKp, path).voidErrorMap { key, info in - guard key.1.name == info.1.hasher.name else { - return .failure(.paramMismatch(for: Self.self, expected: key.1.name, - got: info.1.hasher.name)) - } - return key.0.validate(runtime: runtime, type: info.0).mapError { - .childError(for: Self.self, error: $0) - } - } - } - - static func validate(runtime: any Runtime) -> Result { - guard let info = runtime.resolve(storage: name, pallet: pallet) else { - return .failure(.infoNotFound(for: Self.self)) - } - return validate(path: info.keys.map {($0.1.id, $0.0)}, runtime: runtime).flatMap { - TValue.validate(runtime: runtime, type: info.value.id).mapError { - .childError(for: Self.self, error: $0) - } - } - } -} - public enum StorageKeyCodingError: Error { case storageNotFound(name: String, pallet: String) case badCountOfPathComponents(has: Int, expected: Int) diff --git a/Sources/Substrate/Storage/TupleStorageKey.swift b/Sources/Substrate/Storage/TupleStorageKey.swift index d76126e..ef3381a 100644 --- a/Sources/Substrate/Storage/TupleStorageKey.swift +++ b/Sources/Substrate/Storage/TupleStorageKey.swift @@ -42,9 +42,17 @@ public protocol TupleStorageKeyPath: ListTuple } public protocol TupleStorageValidatableKeyPath: TupleStorageKeyPath where - First.TKey: RuntimeDynamicValidatable, Last.TKey: RuntimeDynamicValidatable + First.TKey: ValidatableType, Last.TKey: ValidatableType { - static var path: [(any RuntimeDynamicValidatable.Type, any StaticHasher.Type)] { get } + static var validatablePath: [(hasher: any StaticHasher.Type, + type: any ValidatableType.Type)] { get } +} + +public protocol TupleStorageIdentifiableKeyPath: TupleStorageKeyPath where + First.TKey: IdentifiableType, Last.TKey: IdentifiableType +{ + static var identifiablePath: [(key: TypeDefinition, + hasher: LastMetadata.StorageHasher)] { get } } public protocol TupleStorageNKeyPath: TupleStorageKeyPath where DroppedFirst: TupleStorageKeyPath {} @@ -73,10 +81,22 @@ public extension TupleStorageKeyBase { } } -public extension TupleStorageKeyBase where TPath: TupleStorageValidatableKeyPath { +public extension TupleStorageKeyBase where + Self: ComplexStaticFrameType, TPath: TupleStorageValidatableKeyPath, + ChildTypes == StorageKeyChildTypes, TValue: ValidatableType +{ + @inlinable + static var childTypes: ChildTypes { + (keys: TPath.validatablePath, value: TValue.self) + } +} + +public extension TupleStorageKeyBase where + TPath: TupleStorageIdentifiableKeyPath, TValue: IdentifiableType +{ @inlinable - static var keyPath: [(any RuntimeDynamicValidatable.Type, any StaticHasher.Type)] { - TPath.path + static var definition: FrameTypeDefinition { + .storage(Self.self, keys: TPath.identifiablePath, value: TValue.definition) } } diff --git a/Sources/Substrate/Types/BitSequence.swift b/Sources/Substrate/Types/BitSequence.swift index fef8ee8..8aa0777 100644 --- a/Sources/Substrate/Types/BitSequence.swift +++ b/Sources/Substrate/Types/BitSequence.swift @@ -278,21 +278,12 @@ extension CustomDecoderFactory where T == BitSequence { extension BitSequence: ValueRepresentable { public func asValue(runtime: Runtime, type: NetworkType.Id) throws -> Value { - try Self.validate(runtime: runtime, type: type).getValueError() + let _ = try Self.validate(runtime: runtime, type: type).get() return .bits(self, type) } } -extension BitSequence: RuntimeDynamicValidatable { - public static func validate(runtime: Runtime, - type id: NetworkType.Id) -> Result - { - guard let info = runtime.resolve(type: id) else { - return .failure(.typeNotFound(id)) - } - guard info.isBitSequence(runtime) else { - return .failure(.wrongType(got: info, for: "BitSequence")) - } - return .success(()) - } +extension BitSequence: IdentifiableType { + @inlinable + public static var definition: TypeDefinition { .bitsequence } } diff --git a/Sources/Substrate/Types/Dynamic/AnyAddress.swift b/Sources/Substrate/Types/Dynamic/AnyAddress.swift index 6900f50..9e2d02d 100644 --- a/Sources/Substrate/Types/Dynamic/AnyAddress.swift +++ b/Sources/Substrate/Types/Dynamic/AnyAddress.swift @@ -18,11 +18,11 @@ public enum AnyAddress: Address, CustomStringConvertible { from decoder: inout D, as type: NetworkType.Id, runtime: any Runtime ) throws { guard let info = runtime.resolve(type: type) else { - throw Value.DecodingError.typeNotFound(type) + throw TypeError.typeNotFound(for: Self.self, id: type) } switch info.flatten(runtime).definition { case .variant(variants: let vars): - let idVar = try Self.findIdVariant(in: vars, type: info).getValueError() + let idVar = try Self.findIdVariant(in: vars, type: info).get() if try decoder.peek() == idVar.index { let _ = try decoder.decode(.enumCaseId) self = try .id(Id(from: &decoder, as: idVar.fields.first!.type, runtime: runtime)) @@ -63,13 +63,13 @@ public enum AnyAddress: Address, CustomStringConvertible { public func asValue(runtime: Runtime, type: NetworkType.Id) throws -> Value { guard let info = runtime.resolve(type: type) else { - throw ValueRepresentableError.typeNotFound(type) + throw TypeError.typeNotFound(for: Self.self, id: type) } switch info.flatten(runtime).definition { case .variant(variants: let vars): switch self { case .id(let id): - let idVar = try Self.findIdVariant(in: vars, type: info).getValueError() + let idVar = try Self.findIdVariant(in: vars, type: info).get() return try .variant( name: idVar.name, values: [id.asValue(runtime: runtime, type: idVar.fields.first!.type)], @@ -77,12 +77,11 @@ public enum AnyAddress: Address, CustomStringConvertible { ) case .other(name: let n, values: let params): guard let item = vars.first(where: { $0.name == n }) else { - throw ValueRepresentableError.variantNotFound(name: n, in: info) + throw TypeError.variantNotFound(for: Self.self, variant: n, in: info) } guard item.fields.count == params.count else { - throw ValueRepresentableError.wrongValuesCount(in: info, - expected: params.count, - for: n) + throw TypeError.wrongVariantFieldsCount(for: Self.self, variant: n, + expected: params.count, in: info) } let mapped = try zip(item.fields, params).map { try $1.asValue(runtime: runtime, type: $0.type) @@ -91,7 +90,8 @@ public enum AnyAddress: Address, CustomStringConvertible { } default: guard case .id(let id) = self else { - throw ValueRepresentableError.wrongType(got: info, for: "AnyAddress") + throw TypeError.wrongType(for: Self.self, got: info, + reason: "primitive type but self is not Id") } return try id.asValue(runtime: runtime, type: type) } @@ -99,7 +99,7 @@ public enum AnyAddress: Address, CustomStringConvertible { public static func findIdVariant( in vars: [NetworkType.Variant], type: NetworkType - ) -> Result { + ) -> Result { for item in vars { if item.fields.count != 1 { continue } if item.name.lowercased().contains("id") { return .success(item) } @@ -107,22 +107,19 @@ public enum AnyAddress: Address, CustomStringConvertible { return .success(item) } } - return .failure(.variantNotFound(name: "Id", in: type)) + return .failure(.variantNotFound(for: Self.self, variant: "Id", in: type)) } - public static func validate(runtime: Runtime, - type id: NetworkType.Id) -> Result + public static func validate(runtime: any Runtime, + type: NetworkType.Info) -> Result { - guard let info = runtime.resolve(type: id) else { - return .failure(.typeNotFound(id)) - } - switch info.flatten(runtime).definition { + switch type.type.flatten(runtime).definition { case .variant(variants: let vars): - return findIdVariant(in: vars, type: info).flatMap { variant in - TAccountId.validate(runtime: runtime, type: variant.fields.first!.type) + return findIdVariant(in: vars, type: type.type).flatMap { variant in + TAccountId.validate(runtime: runtime, type: variant.fields.first!.type).map{_ in} } default: - return TAccountId.validate(runtime: runtime, type: id) + return TAccountId.validate(runtime: runtime, type: type) } } diff --git a/Sources/Substrate/Types/Dynamic/AnyCall.swift b/Sources/Substrate/Types/Dynamic/AnyCall.swift index c5ec0c2..fbad398 100644 --- a/Sources/Substrate/Types/Dynamic/AnyCall.swift +++ b/Sources/Substrate/Types/Dynamic/AnyCall.swift @@ -25,32 +25,34 @@ public struct AnyCall: Call { runtime: Runtime) throws { guard let callParams = runtime.resolve(callParams: name, pallet: pallet) else { - throw CallCodingError.callNotFound(name: name, pallet: pallet) + throw FrameTypeError.typeInfoNotFound(for: errorTypeName) } guard callParams.count == _params.count else { - throw CallCodingError.wrongParametersCount(in: asVoid(), expected: callParams.count) + throw FrameTypeError.wrongFieldsCount(for: errorTypeName, + expected: callParams.count, + got: _params.count) } let variant: Value - if callParams.first?.name != nil { // Map + if callParams.first?.field.name != nil { // Map let pairs = try callParams.enumerated().map { (idx, el) in let value: any ValueRepresentable - if let val = _params[el.name!] { + if let val = _params[el.field.name!] { value = val } else if let val = _params[String(idx)] { value = val } else { - throw CallCodingError.valueNotFound(key: el.name!) + throw FrameTypeError.valueNotFound(for: errorTypeName, key:el.field.name!) } - return try (el.name!, value.asValue(runtime: runtime, type: el.type)) + return try (el.field.name!, value.asValue(runtime: runtime, type: el.field.type)) } let map = Dictionary(uniqueKeysWithValues: pairs) variant = Value(value: .variant(.map(name: name, fields: map)), context: type) } else { // Sequence let values = try callParams.enumerated().map { (idx, el) in guard let value = _params[String(idx)] else { - throw CallCodingError.valueNotFound(key: String(idx)) + throw FrameTypeError.valueNotFound(for: errorTypeName, key: String(idx)) } - return try value.asValue(runtime: runtime, type: el.type) + return try value.asValue(runtime: runtime, type: el.field.type) } variant = Value(value: .variant(.sequence(name: name, values: values)), context: type) } @@ -61,6 +63,10 @@ public struct AnyCall: Call { public func asVoid() -> AnyCall { AnyCall(name: name, pallet: pallet, _params: _params) } + + public var errorTypeName: String { + "Call: \(pallet).\(name)" + } } public extension AnyCall where C == Void { @@ -84,13 +90,17 @@ public extension AnyCall where C == Void { } } -extension AnyCall: RuntimeDynamicValidatable { +extension AnyCall: ValidatableType { public static func validate(runtime: Runtime, - type id: NetworkType.Id) -> Result + type: NetworkType.Info) -> Result { return Result { try runtime.types.call } - .mapError { .runtimeTypeLookupFailed(name: "call", reason: $0) } - .flatMap { $0.id == id ? .success(()) : .failure(.typeIdMismatch(got: id, has: $0.id)) } + .mapError { .runtimeTypeLookupFailed(for: Self.self, type: "call", reason: $0) } + .flatMap { + $0.type == type.type ? .success(()) : + .failure(.wrongType(for: Self.self, got: type.type, + reason: "call types is different")) + } } } @@ -128,17 +138,22 @@ public extension AnyCall where C == NetworkType.Id { switch value.value { case .variant(.sequence(name: let name, values: let values)): guard values.count == 1 else { - throw CallCodingError.wrongFieldCountInVariant(variant: value, expected: 1) + throw FrameTypeError.wrongFieldsCount(for: "AnyCall", + expected: 1, got: values.count) } pallet = name value = values.first! case .variant(.map(name: let name, fields: let fields)): guard fields.count == 1 else { - throw CallCodingError.wrongFieldCountInVariant(variant: value, expected: 1) + throw FrameTypeError.wrongFieldsCount(for: "AnyCall", + expected: 1, got: fields.count) } pallet = name - value = fields.values.first! - default: throw CallCodingError.decodedNonVariantValue(value) + value = fields.first!.value + default: throw FrameTypeError.paramMismatch(for: "AnyCall", + index: 0, + expected: "Value.Variant", + got: call.description) } try self.init(pallet: pallet, call: value) } @@ -154,7 +169,10 @@ public extension AnyCall where C == NetworkType.Id { self.init(name: name, pallet: pallet, params: fields) - default: throw CallCodingError.decodedNonVariantValue(call) + default: throw FrameTypeError.paramMismatch(for: "AnyCall: \(pallet)", + index: 0, + expected: "Value.Variant", + got: call.description) } } } diff --git a/Sources/Substrate/Types/Dynamic/AnyCallErrors.swift b/Sources/Substrate/Types/Dynamic/AnyCallErrors.swift index 9ccde54..0dcc9b2 100644 --- a/Sources/Substrate/Types/Dynamic/AnyCallErrors.swift +++ b/Sources/Substrate/Types/Dynamic/AnyCallErrors.swift @@ -27,8 +27,8 @@ public struct AnyTransactionValidityError: CallError, CustomStringConvertible { self.init(value: value) } - public static func validate(runtime: Runtime, - type id: NetworkType.Id) -> Result { + public static func validate(runtime: any Runtime, + type: NetworkType.Info) -> Result { .success(()) } @@ -37,7 +37,7 @@ public struct AnyTransactionValidityError: CallError, CustomStringConvertible { } } -public struct AnyDispatchError: SomeDispatchError, CustomStringConvertible { +public struct AnyDispatchError: SomeDispatchError, VariantValidatableType, CustomStringConvertible { public typealias TModuleError = ModuleError public typealias DecodingContext = RuntimeDynamicSwiftCodableContext @@ -48,11 +48,7 @@ public struct AnyDispatchError: SomeDispatchError, CustomStringConvertible { public var isModuleError: Bool { value.variant?.name.contains("Module") ?? false } public var moduleError: TModuleError { get throws { - let variant = value.variant! - guard variant.name.contains("Module") else { - throw ModuleError.DecodingError.dispatchErrorIsNotModule(description: description) - } - return try ModuleError(variant: variant, runtime: _runtime) + try ModuleError(variant: value.variant!, runtime: _runtime) }} public init(from decoder: inout D, as type: NetworkType.Id, runtime: Runtime) throws { @@ -80,22 +76,11 @@ public struct AnyDispatchError: SomeDispatchError, CustomStringConvertible { self._runtime = runtime } - public static func validate(runtime: Runtime, - type id: NetworkType.Id) -> Result - { - guard let info = runtime.resolve(type: id)?.flatten(runtime) else { - return .failure(.typeNotFound(id)) - } - guard case .variant(variants: let vars) = info.definition else { - return .failure(.wrongType(got: info, for: "DispatchError")) - } - guard let module = vars.first(where: { $0.name.contains("Module") }) else { - return .failure(.variantNotFound(name: "Module", in: info)) - } - guard TModuleError.validate(variant: module, runtime: runtime) else { - return .failure(.wrongType(got: info, for: "DispatchError.Module")) + public static func validate(info: TypeInfo, type: NetworkType.Info, runtime: Runtime) -> Result { + guard let module = info.first(where: { $0.name.contains("Module") }) else { + return .failure(.variantNotFound(for: Self.self, variant: "*Module*", in: type.type)) } - return .success(()) + return TModuleError.validate(info: module, type: type.type, runtime: runtime) } public var description: String { diff --git a/Sources/Substrate/Types/Dynamic/AnyEvent.swift b/Sources/Substrate/Types/Dynamic/AnyEvent.swift index 2004eb6..113471b 100644 --- a/Sources/Substrate/Types/Dynamic/AnyEvent.swift +++ b/Sources/Substrate/Types/Dynamic/AnyEvent.swift @@ -8,7 +8,7 @@ import Foundation import ScaleCodec -public struct AnyEvent: Event, RuntimeDynamicValidatable, CustomStringConvertible { +public struct AnyEvent: Event, ValidatableType, CustomStringConvertible { public let pallet: String public let name: String @@ -29,17 +29,22 @@ public struct AnyEvent: Event, RuntimeDynamicValidatable, CustomStringConvertibl switch value.value { case .variant(.sequence(name: let name, values: let values)): guard values.count == 1 else { - throw EventDecodingError.tooManyFieldsInVariant(variant: value, expected: 1) + throw FrameTypeError.wrongFieldsCount(for: "AnyEvent", expected: 1, + got: values.count) } pallet = name value = values.first! case .variant(.map(name: let name, fields: let fields)): guard fields.count == 1 else { - throw EventDecodingError.tooManyFieldsInVariant(variant: value, expected: 1) + throw FrameTypeError.wrongFieldsCount(for: "AnyEvent", expected: 1, + got: fields.count) } pallet = name value = fields.values.first! - default: throw EventDecodingError.decodedNonVariantValue(value) + default: throw FrameTypeError.paramMismatch(for: "AnyEvent", + index: 0, + expected: "Value.Variant", + got: value.description) } switch value.value { case .variant(.sequence(name: let name, values: let values)): @@ -50,7 +55,10 @@ public struct AnyEvent: Event, RuntimeDynamicValidatable, CustomStringConvertibl self.init(name: name, pallet: pallet, params: Value(value: .map(fields), context: value.context)) - default: throw EventDecodingError.decodedNonVariantValue(value) + default: throw FrameTypeError.paramMismatch(for: "AnyEvent: \(pallet)", + index: 0, + expected: "Value.Variant", + got: value.description) } } @@ -60,17 +68,19 @@ public struct AnyEvent: Event, RuntimeDynamicValidatable, CustomStringConvertibl let size = try Value.calculateSize(in: decoder, for: type, runtime: runtime) let hBytes = try decoder.peek(count: 2) guard let header = runtime.resolve(eventName: hBytes[1], pallet: hBytes[0]) else { - throw EventDecodingError.eventNotFound(index: hBytes[1], pallet: hBytes[0]) + throw FrameTypeError.typeInfoNotFound(for: "Event", index: hBytes[1], frame: hBytes[0]) } return try (name: header.name, pallet: header.pallet, data: decoder.read(count: size)) } public static func validate(runtime: Runtime, - type id: NetworkType.Id) -> Result + type: NetworkType.Info) -> Result { return Result { try runtime.types.event } - .mapError { .runtimeTypeLookupFailed(name: "event", reason: $0) } - .flatMap { $0.id == id ? .success(()) : .failure(.typeIdMismatch(got: id, has: $0.id)) } + .mapError { .runtimeTypeLookupFailed(for: Self.self, type: "event", reason: $0) } + .flatMap { $0.type == type.type ? .success(()) : + .failure(.wrongType(for: Self.self, got: type.type, + reason: "event types is different")) } } public var description: String { diff --git a/Sources/Substrate/Types/Dynamic/AnyEventRecord.swift b/Sources/Substrate/Types/Dynamic/AnyEventRecord.swift index c7ee730..2b986b2 100644 --- a/Sources/Substrate/Types/Dynamic/AnyEventRecord.swift +++ b/Sources/Substrate/Types/Dynamic/AnyEventRecord.swift @@ -28,7 +28,7 @@ public struct AnyEventRecord: SomeEventRecord, CustomStringConvertible { try _runtime.decode(from: data) { _ in _eventTypeId } } } - public func typed(_ type: E.Type) throws -> E { + public func typed(_ type: E.Type) throws -> E { try _runtime.decode(from: data) { _ in _eventTypeId } } @@ -53,27 +53,22 @@ public extension AnyEventRecord { } } -extension AnyEventRecord.Phase: RuntimeDynamicValidatable { - public static func validate(runtime: Runtime, - type id: NetworkType.Id) -> Result +extension AnyEventRecord.Phase: VariantValidatableType { + public static func validate(info: TypeInfo, type: NetworkType.Info, + runtime: Runtime) -> Result { - guard let typeInfo = runtime.resolve(type: id)?.flatten(runtime) else { - return .failure(.typeNotFound(id)) - } - guard case .variant(variants: let variants) = typeInfo.definition else { - return .failure(.wrongType(got: typeInfo, for: "EventPhase")) - } - guard let apply = variants.first(where: { $0.name == "ApplyExtrinsic" }) else { - return .failure(.variantNotFound(name: "ApplyExtrinsic", in: typeInfo)) + guard let apply = info.first(where: { $0.name == "ApplyExtrinsic" }) else { + return .failure(.variantNotFound(for: Self.self, + variant: "ApplyExtrinsic", in: type.type)) } guard apply.fields.count == 1 else { - return .failure(.wrongValuesCount(in: typeInfo, expected: 1, for: "EventPhase")) - } - guard let applyValue = runtime.resolve(type: apply.fields[0].type)?.flatten(runtime) else { - return .failure(.typeNotFound(apply.fields[0].type)) + return .failure(.wrongVariantFieldsCount(for: Self.self, + variant: "ApplyExtrinsic", + expected: 1, in: type.type)) } - guard applyValue.asPrimitive(runtime)?.isUInt != nil else { - return .failure(.wrongType(got: applyValue, for: "EventPhase.ApplyExtrinsic(Index)")) + guard apply.fields[0].type.type.asPrimitive(runtime)?.isUInt != nil else { + return .failure(.wrongType(for: Self.self, + got: type.type, reason: "ApplyExtrinsic.Index is not UInt")) } return .success(()) } @@ -84,7 +79,7 @@ extension AnyEventRecord.Phase: RuntimeDynamicDecodable { as type: NetworkType.Id, runtime: Runtime) throws { - try Self.validate(runtime: runtime, type: type).getCodableError() + let info = try Self.validate(runtime: runtime, type: type).get() let value = try Value(from: &decoder, as: type, runtime: runtime) let extrinsicId: Value switch value.value { @@ -104,42 +99,27 @@ extension AnyEventRecord.Phase: RuntimeDynamicDecodable { default: fatalError("Should never happen! We checked that it is a variant") } guard let uint = extrinsicId.uint, let u32 = UInt32(exactly: uint) else { - throw DynamicCodableError.badDecodedValue(value: extrinsicId.value, - forType: extrinsicId.context) + throw TypeError.wrongType(for: Self.self, + got: info.type, + reason: "Bad extrinsic id: \(extrinsicId)") } self = .applyExtrinsic(u32) } } -extension AnyEventRecord: RuntimeDynamicValidatable { - private static func _validate( - runtime: Runtime, type id: NetworkType.Id - ) -> Result<[NetworkType.Field], DynamicValidationError> { - guard let typeInfo = runtime.resolve(type: id)?.flatten(runtime) else { - return .failure(.typeNotFound(id)) - } - guard case .composite(fields: let fields) = typeInfo.definition else { - return .failure(.wrongType(got: typeInfo, for: "EventRecord")) - } - guard fields.first?.name != nil else { - return .failure(.wrongType(got: typeInfo, for: "EventRecord")) - } - guard let phase = fields.first(where: { $0.name == "phase" }) else { - return .failure(.fieldNotFound(name: "phase", in: typeInfo)) +extension AnyEventRecord: CompositeValidatableType { + public static func validate(info: TypeInfo, type: NetworkType.Info, + runtime: any Runtime) -> Result + { + guard let phase = info.first(where: { $0.name == "phase" }) else { + return .failure(.fieldNotFound(for: Self.self, field: "phase", in: type.type)) } let eventNames = ["event", "e", "ev"] - guard let event = fields.first(where: { eventNames.contains($0.name ?? "") }) else { - return .failure(.fieldNotFound(name: "event", in: typeInfo)) + guard let event = info.first(where: { eventNames.contains($0.name ?? "") }) else { + return .failure(.fieldNotFound(for: Self.self, field: "event", in: type.type)) } return Phase.validate(runtime: runtime, type: phase.type) - .flatMap { AnyEvent.validate(runtime: runtime, type: event.type) } - .map { fields } - } - - public static func validate(runtime: Runtime, - type id: NetworkType.Id) -> Result - { - _validate(runtime: runtime, type: id).map{_ in} + .flatMap { _ in AnyEvent.validate(runtime: runtime, type: event.type) } } } @@ -148,18 +128,22 @@ extension AnyEventRecord: RuntimeDynamicDecodable { as type: NetworkType.Id, runtime: Runtime) throws { - let fields = try Self._validate(runtime: runtime, type: type).getCodableError() + guard let tinfo = runtime.resolve(type: type) else { + throw TypeError.typeNotFound(for: Self.self, id: type) + } + let info = try Self.typeInfo(runtime: runtime, type: type.i(tinfo)).get() + try Self.validate(info: info, type: type.i(tinfo), runtime: runtime).get() var phase: Phase? = nil var event: (name: String, pallet: String, data: Data, type: NetworkType.Id)? = nil var other: [String: Value] = [:] - for field in fields { + for field in info { switch field.name! { - case "phase": phase = try runtime.decode(from: &decoder, id: field.type) + case "phase": phase = try runtime.decode(from: &decoder, id: field.type.id) case "event", "e", "ev": - let info = try AnyEvent.fetchEventData(from: &decoder, runtime: runtime, type: field.type) - event = (name: info.name, pallet: info.pallet, data: info.data, type: field.type) + let info = try AnyEvent.fetchEventData(from: &decoder, runtime: runtime, type: field.type.id) + event = (name: info.name, pallet: info.pallet, data: info.data, type: field.type.id) default: - other[field.name!] = try Value(from: &decoder, as: field.type, runtime: runtime) + other[field.name!] = try Value(from: &decoder, as: field.type.id, runtime: runtime) } } self._runtime = runtime diff --git a/Sources/Substrate/Types/Dynamic/AnyExtrinsicFailureEvent.swift b/Sources/Substrate/Types/Dynamic/AnyExtrinsicFailureEvent.swift index 8a450c8..3f85bef 100644 --- a/Sources/Substrate/Types/Dynamic/AnyExtrinsicFailureEvent.swift +++ b/Sources/Substrate/Types/Dynamic/AnyExtrinsicFailureEvent.swift @@ -20,7 +20,7 @@ public struct AnyExtrinsicFailureEvent: SomeExtrinsicFailureEvent { self.error = ExtrinsicFailed(body: value) } - public static func validate(runtime: Runtime) -> Result { + public static func validate(runtime: any Runtime) -> Result { .success(()) } diff --git a/Sources/Substrate/Types/Dynamic/AnyFixedHasher.swift b/Sources/Substrate/Types/Dynamic/AnyFixedHasher.swift index 8095652..0cf0721 100644 --- a/Sources/Substrate/Types/Dynamic/AnyFixedHasher.swift +++ b/Sources/Substrate/Types/Dynamic/AnyFixedHasher.swift @@ -11,7 +11,7 @@ public struct AnyFixedHasher: FixedHasher, Equatable { public enum HashType: Equatable, Hashable { case blake2b128 case blake2b256 - case blake2b512 + //case blake2b512 case xx128 case xx256 @@ -19,7 +19,7 @@ public struct AnyFixedHasher: FixedHasher, Equatable { switch name.lowercased() { case "blake2b128", "blaketwo128": self = .blake2b128 case "blake2b256", "blaketwo256": self = .blake2b256 - case "blake2b512", "blaketwo512": self = .blake2b512 + //case "blake2b512", "blaketwo512": self = .blake2b512 case "xx128", "twox128": self = .xx128 case "xx256", "twox256": self = .xx256 default: return nil @@ -32,7 +32,7 @@ public struct AnyFixedHasher: FixedHasher, Equatable { case .xx128: return HXX128.instance case .blake2b128: return HBlake2b128.instance case .blake2b256: return HBlake2b256.instance - case .blake2b512: return HBlake2b512.instance + //case .blake2b512: return HBlake2b512.instance } } } @@ -57,7 +57,7 @@ public struct AnyFixedHasher: FixedHasher, Equatable { } @inlinable - public var name: String { hasher.name } + public var type: LastMetadata.StorageHasher { hasher.type } @inlinable public var hashPartByteLength: Int { hasher.hashPartByteLength } @inlinable @@ -66,17 +66,15 @@ public struct AnyFixedHasher: FixedHasher, Equatable { public func hash(data: Data) -> Data { hasher.hash(data: data) } public static func == (lhs: AnyFixedHasher, rhs: AnyFixedHasher) -> Bool { - lhs.name == rhs.name + lhs.type == rhs.type } public static func validate(runtime: Runtime, - type id: NetworkType.Id) -> Result + type: NetworkType.Info) -> Result { - guard let info = runtime.resolve(type: id) else { - return .failure(.typeNotFound(id)) - } - guard let name = info.path.last, HashType(name: name) != nil else { - return .failure(.wrongType(got: info, for: String(describing: Self.self))) + guard let name = type.type.path.last, HashType(name: name) != nil else { + return .failure(.wrongType(for: Self.self, got: type.type, + reason: "Unknown hash: \(type.type)")) } return .success(()) } diff --git a/Sources/Substrate/Types/Dynamic/AnyHash.swift b/Sources/Substrate/Types/Dynamic/AnyHash.swift index 9b78ef4..8381c97 100644 --- a/Sources/Substrate/Types/Dynamic/AnyHash.swift +++ b/Sources/Substrate/Types/Dynamic/AnyHash.swift @@ -23,13 +23,15 @@ public struct AnyHash: Hash { { let type = try id() guard let info = metadata.resolve(type: type) else { - throw ValueRepresentableError.typeNotFound(type) + throw TypeError.typeNotFound(for: Self.self, id: type) } guard let count = info.asBytes(metadata) else { - throw ValueRepresentableError.wrongType(got: info, for: "AnyHash") + throw TypeError.wrongType(for: Self.self, got: info, + reason: "Isn't bytes") } guard count == 0 || count == raw.count else { - throw SizeMismatchError(size: raw.count, expected: Int(count)) + throw TypeError.wrongValuesCount(for: Self.self, + expected: raw.count, in: info) } self.raw = raw } @@ -41,13 +43,7 @@ public struct AnyHash: Hash { } public static func validate(runtime: Runtime, - type id: NetworkType.Id) -> Result { - guard let info = runtime.resolve(type: id) else { - return .failure(.typeNotFound(id)) - } - guard info.asBytes(runtime) != nil else { - return .failure(.wrongType(got: info, for: "AnyHash")) - } - return .success(()) + type: NetworkType.Info) -> Result { + Data.validate(runtime: runtime, type: type) } } diff --git a/Sources/Substrate/Types/Dynamic/AnySignature.swift b/Sources/Substrate/Types/Dynamic/AnySignature.swift index 1d1610a..68c354a 100644 --- a/Sources/Substrate/Types/Dynamic/AnySignature.swift +++ b/Sources/Substrate/Types/Dynamic/AnySignature.swift @@ -36,7 +36,7 @@ public struct AnySignature: Signature { .flatten(runtime: runtime) switch value.value { case .variant(let variant): - let algos = try Self.parseTypeInfo(runtime: runtime, typeId: type).getValueError() + let algos = try Self.parseTypeInfo(runtime: runtime, typeId: type).get() guard let algo = algos[variant.name] else { throw Error.unsupportedCrypto(name: variant.name) } @@ -62,7 +62,7 @@ public struct AnySignature: Signature { } public func asValue(runtime: Runtime, type: NetworkType.Id) throws -> Value { - let algos = try Self.parseTypeInfo(runtime: runtime, typeId: type).getValueError() + let algos = try Self.parseTypeInfo(runtime: runtime, typeId: type).get() if algos.count == 1 { guard algos.first!.value == _sig.algorithm else { throw Error.unsupportedCrypto(id: algos.first!.value) @@ -78,30 +78,36 @@ public struct AnySignature: Signature { public static func algorithms(runtime: Runtime, id: NetworkType.LazyId) throws -> [CryptoTypeId] { - try Array(parseTypeInfo(runtime: runtime, typeId: id(runtime)).getValueError().values) + try Array(parseTypeInfo(runtime: runtime, typeId: id(runtime)).get().values) } public static func validate(runtime: Runtime, - type id: NetworkType.Id) -> Result + type: NetworkType.Info) -> Result { - parseTypeInfo(runtime: runtime, typeId: id).map{_ in} + parseTypeInfo(runtime: runtime, type: type.type).map{_ in} } - public static func parseTypeInfo( + @inlinable public static func parseTypeInfo( runtime: Runtime, typeId: NetworkType.Id - ) -> Result<[String: CryptoTypeId], DynamicValidationError> { + ) -> Result<[String: CryptoTypeId], TypeError> { guard let type = runtime.resolve(type: typeId) else { - return .failure(.typeNotFound(typeId)) + return .failure(.typeNotFound(for: Self.self, id: typeId)) } + return parseTypeInfo(runtime: runtime, type: type) + } + + public static func parseTypeInfo( + runtime: Runtime, type: NetworkType + ) -> Result<[String: CryptoTypeId], TypeError> { switch type.definition { case .variant(variants: let variants): - let mapped: Result<[(String, CryptoTypeId)], DynamicValidationError> = variants.resultMap { item in + let mapped: Result<[(String, CryptoTypeId)], TypeError> = variants.resultMap { item in if let id = CryptoTypeId.byName[item.name.lowercased()] { return .success((item.name, id)) } guard item.fields.count == 1 else { - return .failure(.wrongValuesCount(in: type, expected: 1, - for: "Signature")) + return .failure(.wrongValuesCount(for: Self.self, expected: 1, + in: type)) } if let typeName = item.fields[0].typeName?.lowercased() { if let name = CryptoTypeId.byName.keys.first(where: { typeName.contains($0) }) { @@ -109,14 +115,15 @@ public struct AnySignature: Signature { } } guard let type = runtime.resolve(type: item.fields[0].type) else { - return .failure(.typeNotFound(item.fields[0].type)) + return .failure(.typeNotFound(for: Self.self, id: item.fields[0].type)) } if let typeName = type.name?.lowercased() { if let name = CryptoTypeId.byName.keys.first(where: { typeName.contains($0) }) { return .success((item.name, CryptoTypeId.byName[name]!)) } } - return .failure(.wrongType(got: type, for: "Signature")) + return .failure(.wrongType(for: Self.self, got: type, + reason: "Unknown signature type: \(type)")) } return mapped.map{Dictionary(uniqueKeysWithValues: $0)} case .composite(fields: let fields): @@ -126,8 +133,7 @@ public struct AnySignature: Signature { } } guard fields.count == 1 else { - return .failure(.wrongValuesCount(in: type, expected: 1, - for: "Signature")) + return .failure(.wrongValuesCount(for: Self.self, expected: 1, in: type)) } if let typeName = fields[0].typeName?.lowercased() { if let name = CryptoTypeId.byName.keys.first(where: { typeName.contains($0) }) { @@ -142,12 +148,12 @@ public struct AnySignature: Signature { } } guard ids.count == 1 else { - return .failure(.wrongValuesCount(in: type, expected: 1, - for: "Signature")) + return .failure(.wrongValuesCount(for: Self.self, expected: 1, in: type)) } return parseTypeInfo(runtime: runtime, typeId: ids[0]) default: - return .failure(.wrongType(got: type, for: "Signature")) + return .failure(.wrongType(for: Self.self, got: type, + reason: "Can't be parsed as signature")) } } } diff --git a/Sources/Substrate/Types/Dynamic/DynamicExtrinsicExtensions.swift b/Sources/Substrate/Types/Dynamic/DynamicExtrinsicExtensions.swift index ddae5b6..4f607a3 100644 --- a/Sources/Substrate/Types/Dynamic/DynamicExtrinsicExtensions.swift +++ b/Sources/Substrate/Types/Dynamic/DynamicExtrinsicExtensions.swift @@ -30,8 +30,8 @@ public struct DynamicCheckSpecVersionExtension: DynamicExtrinsicExtension { public func validate( config: C.Type, runtime: any Runtime, - extra: NetworkType.Id, additionalSigned: NetworkType.Id - ) -> Result { + extra: NetworkType.Info, additionalSigned: NetworkType.Info + ) -> Result { Nothing.validate(runtime: runtime, type: extra).flatMap { SBT.Version.validate(runtime: runtime, type: additionalSigned) } @@ -60,8 +60,8 @@ public struct DynamicCheckTxVersionExtension: DynamicExtrinsicExtension { public func validate( config: C.Type, runtime: any Runtime, - extra: NetworkType.Id, additionalSigned: NetworkType.Id - ) -> Result { + extra: NetworkType.Info, additionalSigned: NetworkType.Info + ) -> Result { Nothing.validate(runtime: runtime, type: extra).flatMap { SBT.Version.validate(runtime: runtime, type: additionalSigned) } @@ -90,8 +90,8 @@ public struct DynamicCheckGenesisExtension: DynamicExtrinsicExtension { public func validate( config: C.Type, runtime: any Runtime, - extra: NetworkType.Id, additionalSigned: NetworkType.Id - ) -> Result { + extra: NetworkType.Info, additionalSigned: NetworkType.Info + ) -> Result { Nothing.validate(runtime: runtime, type: extra).flatMap { SBT.Hash.validate(runtime: runtime, type: additionalSigned) } @@ -117,8 +117,8 @@ public struct DynamicCheckNonZeroSenderExtension: DynamicExtrinsicExtension { public func validate( config: C.Type, runtime: any Runtime, - extra: NetworkType.Id, additionalSigned: NetworkType.Id - ) -> Result { + extra: NetworkType.Info, additionalSigned: NetworkType.Info + ) -> Result { Nothing.validate(runtime: runtime, type: extra).flatMap { Nothing.validate(runtime: runtime, type: additionalSigned) } @@ -158,8 +158,8 @@ public struct DynamicCheckNonceExtension: DynamicExtrinsicExtension { public func validate( config: C.Type, runtime: any Runtime, - extra: NetworkType.Id, additionalSigned: NetworkType.Id - ) -> Result { + extra: NetworkType.Info, additionalSigned: NetworkType.Info + ) -> Result { Compact.TPartial.TNonce>.validate(runtime: runtime, type: extra).flatMap { Nothing.validate(runtime: runtime, type: additionalSigned) } @@ -199,8 +199,8 @@ public struct DynamicCheckMortalityExtension: DynamicExtrinsicExtension { public func validate( config: C.Type, runtime: any Runtime, - extra: NetworkType.Id, additionalSigned: NetworkType.Id - ) -> Result { + extra: NetworkType.Info, additionalSigned: NetworkType.Info + ) -> Result { AnySigningParams.TPartial.TEra.validate(runtime: runtime, type: extra).flatMap { AnySigningParams.TPartial.THash.validate(runtime: runtime, type: additionalSigned) } @@ -227,8 +227,8 @@ public struct DynamicCheckWeightExtension: DynamicExtrinsicExtension { public func validate( config: C.Type, runtime: any Runtime, - extra: NetworkType.Id, additionalSigned: NetworkType.Id - ) -> Result { + extra: NetworkType.Info, additionalSigned: NetworkType.Info + ) -> Result { Nothing.validate(runtime: runtime, type: extra).flatMap { Nothing.validate(runtime: runtime, type: additionalSigned) } @@ -264,8 +264,8 @@ public struct DynamicChargeTransactionPaymentExtension: DynamicExtrinsicExtensio public func validate( config: C.Type, runtime: any Runtime, - extra: NetworkType.Id, additionalSigned: NetworkType.Id - ) -> Result { + extra: NetworkType.Info, additionalSigned: NetworkType.Info + ) -> Result { AnySigningParams.TPartial.TPayment.validate(runtime: runtime, type: extra).flatMap { Nothing.validate(runtime: runtime, type: additionalSigned) } @@ -300,8 +300,8 @@ public struct DynamicPrevalidateAttestsExtension: DynamicExtrinsicExtension { public func validate( config: C.Type, runtime: any Runtime, - extra: NetworkType.Id, additionalSigned: NetworkType.Id - ) -> Result { + extra: NetworkType.Info, additionalSigned: NetworkType.Info + ) -> Result { Nothing.validate(runtime: runtime, type: extra).flatMap { Nothing.validate(runtime: runtime, type: additionalSigned) } diff --git a/Sources/Substrate/Types/FrameType.swift b/Sources/Substrate/Types/FrameType.swift new file mode 100644 index 0000000..21ada72 --- /dev/null +++ b/Sources/Substrate/Types/FrameType.swift @@ -0,0 +1,220 @@ +// +// FrameType.swift +// +// +// Created by Yehor Popovych on 02/09/2023. +// + +import Foundation + +public protocol FrameType { + static var frame: String { get } + static var name: String { get } + static var frameTypeName: String { get } + + static func validate(runtime: any Runtime) -> Result +} + +public extension FrameType { + var name: String { Self.name } + var frame: String { Self.frame } +} + +public enum FrameTypeError: Error { + case typeInfoNotFound(for: String) + case typeInfoNotFound(for: String, index: UInt8, frame: UInt8) + case foundWrongType(for: String, found: (name: String, frame: String)) + case wrongFieldsCount(for: String, expected: Int, got: Int) + case paramMismatch(for: String, index: Int, expected: String, got: String) + case valueNotFound(for: String, key: String) + case childError(for: String, index: Int, error: TypeError) +} + +public enum FrameTypeDefinition { + case call(FrameType.Type, fields: [TypeDefinition.Field]) + case event(FrameType.Type, fields: [TypeDefinition.Field]) + case constant(FrameType.Type, type: TypeDefinition) + case runtime(FrameType.Type, params: [TypeDefinition.Field], return: TypeDefinition) + case storage(FrameType.Type, + keys: [(key: TypeDefinition, hasher: LastMetadata.StorageHasher)], + value: TypeDefinition) +} + + +public protocol ComplexFrameType: FrameType { + associatedtype TypeInfo + + static func typeInfo(runtime: any Runtime) -> Result + static func validate(info: TypeInfo, runtime: any Runtime) -> Result +} + +public extension ComplexFrameType { + @inlinable static func validate(runtime: any Runtime) -> Result { + typeInfo(runtime: runtime).flatMap { validate(info: $0, runtime: runtime) } + } +} + +public protocol ComplexStaticFrameType: ComplexFrameType { + associatedtype ChildTypes + static var childTypes: ChildTypes { get } +} + +// Call & Event +public extension ComplexStaticFrameType where + TypeInfo == EventTypeInfo, ChildTypes == EventChildTypes +{ + static func validate(info: TypeInfo, + runtime: any Runtime) -> Result + { + let ourTypes = childTypes + guard ourTypes.count == info.count else { + return .failure(.wrongFieldsCount(for: Self.self, expected: ourTypes.count, + got: info.count)) + } + return zip(ourTypes, info).enumerated().voidErrorMap { index, zip in + let (our, info) = zip + return our.validate(runtime: runtime, type: info.type.i(info.field.type)).mapError { + .childError(for: Self.self, index: index, error: $0) + } + } + } +} + +public protocol IdentifiableFrameType: FrameType { + static var definition: FrameTypeDefinition { get } +} + +public extension IdentifiableFrameType { + static func validate(runtime: any Runtime) -> Result + { + definition.validate(type: Self.self, runtime: runtime) + } +} + +public extension FrameTypeDefinition { + func validate(type: any IdentifiableFrameType.Type, + runtime: any Runtime) -> Result + { + switch self { + case .call(let ftype, fields: let fields): + guard let info = runtime.resolve(callParams: ftype.name, pallet: ftype.frame) else { + return .failure(.typeInfoNotFound(for: type)) + } + return validate(fields: fields, + ifields: info.map{($0.field.name, $0.type.i($0.field.type))}, + type: type, runtime: runtime) + case .event(let ftype, fields: let fields): + guard let info = runtime.resolve(eventParams: ftype.name, pallet: ftype.frame) else { + return .failure(.typeInfoNotFound(for: type)) + } + return validate(fields: fields, + ifields: info.map{($0.field.name, $0.type.i($0.field.type))}, + type: type, runtime: runtime) + case .runtime(let ftype, params: let params, return: let rtype): + guard let info = runtime.resolve(runtimeCall: ftype.name, api: ftype.frame) else { + return .failure(.typeInfoNotFound(for: type)) + } + return validate(fields: params, ifields: info.params, + type: type, runtime: runtime).flatMap { + return rtype.validate(runtime: runtime, type: info.result.type).mapError { + .childError(for: type, index: -1, error: $0) + } + } + case .constant(let ftype, type: let vtype): + guard let info = runtime.resolve(constant: ftype.name, pallet: ftype.frame) else { + return .failure(.typeInfoNotFound(for: type)) + } + return vtype.validate(runtime: runtime, type: info.type.type).mapError { + .childError(for: type, index: -1, error: $0) + } + case .storage(let ftype, keys: let path, value: let vtype): + guard let info = runtime.resolve(storage: ftype.name, pallet: ftype.frame) else { + return .failure(.typeInfoNotFound(for: type)) + } + guard path.count == info.keys.count else { + return .failure(.wrongFieldsCount(for: type, expected: path.count, + got: info.keys.count)) + } + return zip(path, info.keys).enumerated().voidErrorMap { index, zip in + let (pkey, ikey) = zip + guard pkey.hasher == ikey.hasher else { + return .failure(.paramMismatch(for: type, index: index, + expected: pkey.hasher.description, + got: ikey.hasher.description)) + } + return pkey.key.validate(runtime: runtime, type: ikey.type.type) + .mapError { .childError(for: type, index: index, error: $0) } + }.flatMap { + vtype.validate(runtime: runtime, type: info.value.type).mapError { + .childError(for: type, index: -1, error: $0) + } + } + } + } + + func validate(fields: [TypeDefinition.Field], + ifields: [(name: String?, type: NetworkType.Info)], + type: any IdentifiableFrameType.Type, + runtime: any Runtime) -> Result + { + guard fields.count == ifields.count else { + return .failure(.wrongFieldsCount(for: type, expected: fields.count, got: ifields.count)) + } + return zip(fields, ifields).enumerated().voidErrorMap { index, zip in + let (field, info) = zip + return field.type.validate(runtime: runtime, type: info.type.type).mapError { + .childError(for: type, index: index, error: $0) + } + } + } +} + +public extension FrameTypeError { // FrameType + @inlinable + static func typeInfoNotFound(for type: FrameType.Type) -> Self { + .typeInfoNotFound(for: "\(type.frameTypeName): \(type.frame).\(type.name)") + } + + @inlinable + static func typeInfoNotFound(for type: FrameType.Type, + index: UInt8, frame: UInt8) -> Self { + .typeInfoNotFound(for: "\(type.frameTypeName): \(type.frame).\(type.name)", + index: index, frame: frame) + } + + @inlinable + static func foundWrongType(for type: FrameType.Type, + name: String, frame: String) -> Self { + .foundWrongType(for: "\(type.frameTypeName): \(type.frame).\(type.name)", + found: (name, frame)) + } + + @inlinable + static func wrongFieldsCount(for type: FrameType.Type, + expected: Int, got: Int) -> Self { + .wrongFieldsCount(for: "\(type.frameTypeName): \(type.frame).\(type.name)", + expected: expected, got: got) + } + + @inlinable + static func paramMismatch(for type: FrameType.Type, index: Int, + expected: Any, got: Any) -> Self { + .paramMismatch(for: "\(type.frameTypeName): \(type.frame).\(type.name)", + index: index, + expected: String(describing: expected), + got: String(describing: got)) + } + + @inlinable + static func valueNotFound(for type: FrameType.Type, key: String) -> Self { + .valueNotFound(for: "\(type.frameTypeName): \(type.frame).\(type.name)", + key: key) + } + + @inlinable + static func childError(for type: FrameType.Type, + index: Int, error: TypeError) -> Self { + .childError(for: "\(type.frameTypeName): \(type.frame).\(type.name)", + index: index, error: error) + } +} diff --git a/Sources/Substrate/Types/Hash.swift b/Sources/Substrate/Types/Hash.swift index a751f3a..70dc7fe 100644 --- a/Sources/Substrate/Types/Hash.swift +++ b/Sources/Substrate/Types/Hash.swift @@ -11,7 +11,7 @@ import ContextCodable public protocol Hash: ContextDecodable, Swift.Encodable, ValueRepresentable, VoidValueRepresentable, - RuntimeDynamicValidatable, Equatable, CustomStringConvertible + ValidatableType, Equatable, CustomStringConvertible where DecodingContext == (metadata: any Metadata, id: () throws -> NetworkType.Id) { var raw: Data { get } @@ -39,14 +39,14 @@ public extension Hash { func asValue(runtime: Runtime, type: NetworkType.Id) throws -> Value { guard let info = runtime.resolve(type: type) else { - throw ValueRepresentableError.typeNotFound(type) + throw TypeError.typeNotFound(for: Self.self, id: type) } guard let count = info.asBytes(runtime) else { - throw ValueRepresentableError.wrongType(got: info, for: String(describing: Self.self)) + throw TypeError.wrongType(for: Self.self, got: info, + reason: "isn't byte array") } guard count == 0 || raw.count == count else { - throw ValueRepresentableError.wrongValuesCount(in: info, expected: raw.count, - for: String(describing: Self.self)) + throw TypeError.wrongValuesCount(for: Self.self, expected: raw.count, in: info) } return .bytes(raw, type) } @@ -56,7 +56,7 @@ public extension Hash { } } -public protocol StaticHash: Hash, FixedDataCodable, RuntimeCodable, Swift.Decodable { +public protocol StaticHash: Hash, IdentifiableType, FixedDataCodable, RuntimeCodable, Swift.Decodable { init(raw: Data) throws } @@ -88,19 +88,9 @@ public extension StaticHash { @inlinable func serialize() -> Data { raw } - static func validate(runtime: any Runtime, - type id: NetworkType.Id) -> Result { - guard let info = runtime.resolve(type: id) else { - return .failure(.typeNotFound(id)) - } - guard let count = info.asBytes(runtime) else { - return .failure(.wrongType(got: info, for: String(describing: Self.self))) - } - guard Self.fixedBytesCount == count else { - return .failure(.wrongValuesCount(in: info, expected: Self.fixedBytesCount, - for: String(describing: Self.self))) - } - return .success(()) + @inlinable + static var definition: TypeDefinition { + .data(count: UInt32(Self.fixedBytesCount)) } } diff --git a/Sources/Substrate/Types/Hasher.swift b/Sources/Substrate/Types/Hasher.swift index 7b4d875..8081cee 100644 --- a/Sources/Substrate/Types/Hasher.swift +++ b/Sources/Substrate/Types/Hasher.swift @@ -10,33 +10,31 @@ import Foundation public protocol Hasher { var hashPartByteLength: Int { get } var isConcat: Bool { get } - var name: String { get } + var type: LastMetadata.StorageHasher { get } func hash(data: Data) -> Data } -public protocol StaticHasher: Hasher, RuntimeDynamicValidatable { - static var name: String { get } +public protocol StaticHasher: Hasher, ValidatableType { + static var hasherType: LastMetadata.StorageHasher { get } static var instance: Self { get } } public extension StaticHasher { - @inlinable var name: String { Self.name } + @inlinable var type: LastMetadata.StorageHasher { Self.hasherType } static func validate(runtime: any Runtime, - type id: NetworkType.Id) -> Result + type: NetworkType.Info) -> Result { - guard let info = runtime.resolve(type: id) else { - return .failure(.typeNotFound(id)) - } - guard let name = info.path.last, name == Self.name else { - return .failure(.wrongType(got: info, for: String(describing: Self.self))) + guard let name = type.type.path.last, name == hasherType.name else { + return .failure(.wrongType(for: Self.self, got: type.type, + reason: "Unknown hasher: \(type.type)")) } return .success(()) } } -public protocol FixedHasher: Hasher, RuntimeDynamicValidatable { +public protocol FixedHasher: Hasher, ValidatableType { associatedtype THash: Hash func hash(data: Data, runtime: any Runtime) throws -> THash diff --git a/Sources/Substrate/Types/IdentifiableType.swift b/Sources/Substrate/Types/IdentifiableType.swift new file mode 100644 index 0000000..b183bcc --- /dev/null +++ b/Sources/Substrate/Types/IdentifiableType.swift @@ -0,0 +1,342 @@ +// +// IdentifiableType.swift +// +// +// Created by Yehor Popovych on 02/09/2023. +// + +import Foundation + +public indirect enum TypeDefinition: CustomStringConvertible { + case composite(fields: [Field]) + case variant(variants: [Variant]) + case sequence(of: Self) + case array(count: UInt32, of: Self) + case compact(of: Self) + case primitive(is: NetworkType.Primitive) + case bitsequence + case void +} + +public extension TypeDefinition { + @inlinable + static var data: Self { .sequence(of: .primitive(is: .u8)) } + + @inlinable static func data(count: UInt32) -> Self { + .array(count: count, of: .primitive(is: .u8)) + } + + var description: String { + switch self { + case .composite(fields: let fields): return fields.description + case .variant(variants: let vars): return vars.description + case .sequence(of: let t): return "[](\(t))" + case .array(count: let c, of: let t): return "[\(c)](\(t))" + case .compact(of: let t): return "Compact(\(t))" + case .primitive(is: let p): return p.description + case .bitsequence: return "BitSeq" + case .void: return "()" + } + } +} + +public extension TypeDefinition { + struct Field: CustomStringConvertible { + public let name: String? + public let type: TypeDefinition + + public init(_ name: String?, _ type: TypeDefinition) { + self.name = name; self.type = type + } + @inlinable + public static func kv(_ name: String, _ type: TypeDefinition) -> Self { + Self(name, type) + } + @inlinable + public static func v(_ type: TypeDefinition) -> Self { Self(nil, type) } + + public var description: String { + name != nil ? "\(name!): \(type)" : "\(type)" + } + } +} + +public extension TypeDefinition { + struct Variant: CustomStringConvertible { + public let index: UInt8 + public let name: String + public let fields: [Field] + + public init(_ index: UInt8, _ name: String, _ fields: [Field]) { + self.index = index; self.name = name; self.fields = fields + } + @inlinable + public static func e(_ index: UInt8, _ name: String) -> Self { + Self(index, name, []) + } + @inlinable + public static func s(_ index: UInt8, _ name: String, _ field: Field) -> Self { + Self(index, name, [field]) + } + @inlinable + public static func s(_ index: UInt8, _ name: String, _ field: TypeDefinition) -> Self { + Self(index, name, [.v(field)]) + } + @inlinable + public static func m(_ index: UInt8, _ name: String, _ fields: [Field]) -> Self { + Self(index, name, fields) + } + @inlinable + public static func m(_ index: UInt8, _ name: String, _ fields: [TypeDefinition]) -> Self { + Self(index, name, fields.map{.v($0)}) + } + + public var description: String { + fields.count == 0 ? "\(name)[\(index)]" + : "\(name)[\(index)](\(fields.map{$0.description}.joined(separator: ", ")))" + } + } +} + +public protocol IdentifiableType: ValidatableType { + static var definition: TypeDefinition { get } +} + + +public extension IdentifiableType { + static func validate(runtime: any Runtime, + type: NetworkType.Info) -> Result + { + definition.validate(runtime: runtime, type: type.type) + } +} + +public extension TypeDefinition { + @inlinable + init(network: NetworkType.Definition, runtime: any Runtime) throws { + self = try Self.from(network: network, runtime: runtime).get() + } + + @inlinable + init(type id: NetworkType.Id, runtime: any Runtime) throws { + self = try Self.from(type: id, runtime: runtime).get() + } + + @inlinable + static func from(type id: NetworkType.Id, + runtime: any Runtime) -> Result + { + guard let type = runtime.resolve(type: id) else { + return .failure(.typeNotFound(path: [], id: id)) + } + return from(network: type.definition, runtime: runtime) + } + + static func from(network: NetworkType.Definition, + runtime: any Runtime) -> Result + { + switch network.flatten(metadata: runtime.metadata) { + case .primitive(is: let p): return .success(.primitive(is: p)) + case .bitsequence(store: _, order: _): return .success(.bitsequence) + case .array(count: let count, of: let id): + return from(type: id, runtime: runtime).map{.array(count: count, of: $0)} + case .compact(of: let id): + return from(type: id, runtime: runtime).map{.compact(of: $0)} + case .sequence(of: let id): + return from(type: id, runtime: runtime).map{.sequence(of: $0)} + case .tuple(components: let ids): + return ids.resultMap { + from(type: $0, runtime: runtime).map{.v($0)} + }.map { .composite(fields: $0) } + case .composite(fields: let fields): + return fields.resultMap { field in + from(type: field.type, runtime: runtime).map{.init(field.name, $0)} + }.map { .composite(fields: $0) } + case .variant(variants: let variants): + return variants.resultMap { variant in + return variant.fields.resultMap { field in + from(type: field.type, runtime: runtime).map{.init(field.name, $0)} + }.map { .m(variant.index, variant.name, $0) } + }.map { .variant(variants: $0) } + } + } + + @inlinable + func validate(runtime: any Runtime, + type id: NetworkType.Id) -> Result + { + var path: [Self] = [] + return validate(runtime: runtime, type: id, path: &path) + } + + @inlinable + func validate(runtime: any Runtime, type id: NetworkType.Id, + path: inout [Self]) -> Result + { + guard let type = runtime.resolve(type: id) else { + return .failure(.typeNotFound(path: path, id: id)) + } + return validate(runtime: runtime, type: type, path: &path) + } + + @inlinable + func validate(runtime: any Runtime, + type: NetworkType) -> Result + { + var path: [Self] = [] + return validate(runtime: runtime, type: type, path: &path) + } + + func validate(runtime: any Runtime, type: NetworkType, + path: inout [Self]) -> Result + { + path.append(self); defer { let _ = path.dropLast() } + switch (self, type.flatten(runtime).definition) { + case (.array(count: let scount, of: let stype), + .array(count: let tcount, of: let ttype)): + guard scount == tcount else { + return .failure(.wrongValuesCount(path: path, + expected: Int(scount), in: type)) + } + return stype.validate(runtime: runtime, + type: ttype, + path: &path) + case (.sequence(of: let stype), .sequence(of: let ttype)): + return stype.validate(runtime: runtime, + type: ttype, path: &path) + case (.compact(of: let stype), .compact(of: let ttype)): + return validate(compact: stype, runtime: runtime, + type: ttype, path: &path) + case (.primitive(is: let sprim), .primitive(is: let tprim)): + return sprim == tprim + ? .success(()) + : .failure(.wrongType(path: path, got: type, + reason: "Expected \(sprim)")) + case (.bitsequence, .bitsequence(store: _, order: _)): return .success(()) + case (.void, .composite(fields: let fields)): + return fields.count == 0 + ? .success(()) + : .failure(.wrongType(path: path, got: type, + reason: "Expected empty fields for void")) + case (.void, .tuple(components: let components)): + return components.count == 0 + ? .success(()) + : .failure(.wrongType(path: path, got: type, + reason: "Expected empty components for void")) + case (.composite(fields: let sfields), .composite(fields: let tfields)): + guard sfields.count == tfields.count else { + return .failure(.wrongValuesCount(path: path, + expected: sfields.count, in: type)) + } + return zip(sfields, tfields).voidErrorMap { sfield, tfield in + sfield.type.validate(runtime: runtime, + type: tfield.type, path: &path) + } + case (.composite(fields: let sfields), .tuple(components: let ids)): + guard sfields.count == ids.count else { + return .failure(.wrongValuesCount(path: path, expected: sfields.count, in: type)) + } + return zip(sfields, ids).voidErrorMap { sfield, id in + sfield.type.validate(runtime: runtime, + type: id, path: &path) + } + case (.variant(variants: let svars), .variant(variants: let tvars)): + guard svars.count == tvars.count else { + return .failure(.wrongValuesCount(path: path, expected: svars.count, in: type)) + } + let tvarsDict = Dictionary(uniqueKeysWithValues: tvars.map { ($0.name, $0) }) + return svars.voidErrorMap { svar in + guard let inVariant = tvarsDict[svar.name] else { + return .failure(.variantNotFound(path: path, variant: svar.name, in: type)) + } + guard svar.index == inVariant.index else { + return .failure(.wrongVariantIndex(path: path, variant: svar.name, + expected: svar.index, in: type)) + } + guard svar.fields.count == inVariant.fields.count else { + return .failure(.wrongVariantFieldsCount(path: path, + variant: svar.name, + expected: svar.fields.count, + in: type)) + } + return zip(svar.fields, inVariant.fields).voidErrorMap { field, info in + field.type.validate(runtime: runtime, + type: info.type, path: &path) + } + } + default: + return .failure(.wrongType(path: path, got: type, + reason: "Types can't be matched")) + } + + func validate(compact: Self, runtime: any Runtime, + type id: NetworkType.Id, path: inout [Self]) -> Result + { + path.append(compact); defer { let _ = path.dropLast() } + guard let type = runtime.resolve(type: id) else { + return .failure(.typeNotFound(path: path, id: id)) + } + switch (compact, type.flatten(runtime).definition) { + case (.primitive(is: let sprim), .primitive(is: let iprim)): + guard let suint = sprim.isUInt, let iuint = iprim.isUInt else { + return .failure(.wrongType(path: path, + got: type, + reason: "primitive is not UInt")) + } + guard iuint <= suint else { + return .failure(.wrongType(path: path, got: type, + reason: "UInt\(suint) can't store\(iuint) bits")) + } + case (.void, let t): + guard t.isEmpty(metadata: runtime.metadata) else { + return .failure(.wrongType(path: path, got: type, + reason: "Got type for Compact")) + } + default: + return .failure(.wrongType(path: path, got: type, + reason: "Can't be Compact")) + } + return .success(()) + } + } +} + +public extension TypeError { + static func typeNotFound(path: [TypeDefinition], id: NetworkType.Id) -> Self { + .typeNotFound(for: path.pathString, id: id) + } + static func runtimeTypeLookupFailed(path: [TypeDefinition], type: String, reason: Error) -> Self { + .runtimeTypeLookupFailed(for: path.pathString, type: type, reason: reason) + } + + static func wrongType(path: [TypeDefinition], got: NetworkType, reason: String) -> Self { + .wrongType(for: path.pathString, got: got, reason: reason) + } + static func wrongValuesCount(path: [TypeDefinition], expected: Int, in: NetworkType) -> Self { + .wrongValuesCount(for: path.pathString, expected: expected, in: `in`) + } + + static func fieldNotFound(path: [TypeDefinition], field: String, in: NetworkType) -> Self { + .fieldNotFound(for: path.pathString, field: field, in: `in`) + } + + static func variantNotFound(path: [TypeDefinition], variant: String, in: NetworkType) -> Self { + .variantNotFound(for: path.pathString, variant: variant, in: `in`) + } + + static func wrongVariantFieldsCount(path: [TypeDefinition], variant: String, + expected: Int, in: NetworkType) -> Self { + .wrongVariantFieldsCount(for: path.pathString, variant: variant, + expected: expected, in: `in`) + } + + static func wrongVariantIndex(path: [TypeDefinition], variant: String, + expected: UInt8, in: NetworkType) -> Self { + .wrongVariantIndex(for: path.pathString, variant: variant, + expected: expected, in: `in`) + } +} + +private extension Array where Element == TypeDefinition { + var pathString: String { map { $0.description }.joined(separator: ".") } +} diff --git a/Sources/Substrate/Types/Static/AccountId32.swift b/Sources/Substrate/Types/Static/AccountId32.swift index 16e1200..1ffa4a1 100644 --- a/Sources/Substrate/Types/Static/AccountId32.swift +++ b/Sources/Substrate/Types/Static/AccountId32.swift @@ -43,7 +43,7 @@ public struct AccountId32: StaticAccountId, Hashable, Equatable { extension AccountId32: ValueRepresentable { public func asValue(runtime: Runtime, type: NetworkType.Id) throws -> Value { - try Self.validate(runtime: runtime, type: type).getValueError() + let _ = try Self.validate(runtime: runtime, type: type).get() return .bytes(raw, type) } } diff --git a/Sources/Substrate/Types/Static/BatchCalls.swift b/Sources/Substrate/Types/Static/BatchCalls.swift index 4490f94..4377029 100644 --- a/Sources/Substrate/Types/Static/BatchCalls.swift +++ b/Sources/Substrate/Types/Static/BatchCalls.swift @@ -8,7 +8,8 @@ import Foundation import ScaleCodec -public protocol BatchCallCommon: SomeBatchCall, RuntimeValidatableStaticComposite {} +public protocol BatchCallCommon: SomeBatchCall, ComplexStaticFrameType + where TypeInfo == CallTypeInfo, ChildTypes == CallChildTypes {} public extension BatchCallCommon { func add(_ call: any Call) -> Self { Self(calls: calls + [call]) } @@ -19,11 +20,10 @@ public extension BatchCallCommon { let modIndex = try decoder.decode(.enumCaseId) let callIndex = try decoder.decode(.enumCaseId) guard let info = runtime.resolve(callName: callIndex, pallet: modIndex) else { - throw CallCodingError.callNotFound(index: callIndex, pallet: modIndex) + throw FrameTypeError.typeInfoNotFound(for: Self.self, index: callIndex, frame: modIndex) } guard Self.pallet == info.pallet && Self.name == info.name else { - throw CallCodingError.foundWrongCall(found: (name: info.name, pallet: info.pallet), - expected: (name: Self.name, pallet: Self.pallet)) + throw FrameTypeError.foundWrongType(for: Self.self, name: info.name, frame: info.pallet) } let calls = try Array>(from: &decoder) { decoder in try AnyCall(from: &decoder, as: type, runtime: runtime) @@ -33,7 +33,7 @@ public extension BatchCallCommon { func encode(in encoder: inout E, as type: NetworkType.Id, runtime: Runtime) throws { guard let info = runtime.resolve(callIndex: name, pallet: pallet) else { - throw CallCodingError.callNotFound(name: name, pallet: pallet) + throw FrameTypeError.typeInfoNotFound(for: Self.self) } try encoder.encode(info.pallet, .enumCaseId) try encoder.encode(info.index, .enumCaseId) @@ -42,10 +42,11 @@ public extension BatchCallCommon { } } - static var validatableFields: [RuntimeDynamicValidatable.Type] { [AnyCall.self] } + @inlinable + static var childTypes: ChildTypes { [Array>.self] } } -public struct BatchCall: BatchCallCommon { +public struct BatchCall: BatchCallCommon { public let calls: [any Call] public init(calls: [any Call]) { self.calls = calls diff --git a/Sources/Substrate/Types/Static/BlockEvents.swift b/Sources/Substrate/Types/Static/BlockEvents.swift index d62ebc7..969fef1 100644 --- a/Sources/Substrate/Types/Static/BlockEvents.swift +++ b/Sources/Substrate/Types/Static/BlockEvents.swift @@ -8,7 +8,8 @@ import Foundation import ScaleCodec -public struct BlockEvents: SomeBlockEvents, CustomStringConvertible { +public struct BlockEvents: SomeBlockEvents, CompositeStaticValidatableType, + CustomStringConvertible { public typealias ER = ER public let events: [ER] @@ -38,28 +39,20 @@ public struct BlockEvents: SomeBlockEvents, CustomStringCon public static func recordTypeId(metadata: any Metadata, events id: NetworkType.Id) throws -> NetworkType.Id { guard let typeInfo = metadata.resolve(type: id)?.flatten(metadata) else { - throw DynamicCodableError.typeNotFound(id) + throw TypeError.typeNotFound(for: Self.self, id: id) } switch typeInfo.definition { case .sequence(of: let recordId): return recordId default: - throw DynamicCodableError.wrongType(got: typeInfo, - for: "Array") + throw TypeError.wrongType(for: Self.self, got: typeInfo, + reason: "Not a sequence") } } - public static func validate(runtime: any Runtime, - type id: NetworkType.Id) -> Result { - guard let typeInfo = runtime.resolve(type: id)?.flatten(runtime) else { - return .failure(.typeNotFound(id)) - } - switch typeInfo.definition { - case .sequence(of: let recordId): - return ER.validate(runtime: runtime, type: recordId) - default: - return .failure(.wrongType(got: typeInfo, for: "Array")) - } + @inlinable + public static var childTypes: ChildTypes { + [Array.self] } public static var `default`: Self { Self(events: []) } diff --git a/Sources/Substrate/Types/Static/DispatchError.swift b/Sources/Substrate/Types/Static/DispatchError.swift index abf2d52..cc3a4e5 100644 --- a/Sources/Substrate/Types/Static/DispatchError.swift +++ b/Sources/Substrate/Types/Static/DispatchError.swift @@ -10,7 +10,7 @@ import ScaleCodec /// An error dispatching a transaction. public enum DispatchError: SomeDispatchError, StaticCallError, Equatable, - ScaleCodec.Encodable, RuntimeDynamicValidatableStaticVariant + ScaleCodec.Encodable { /// Some unknown error occurred. case other @@ -58,9 +58,9 @@ public enum DispatchError: SomeDispatchError, StaticCallError, Equatable, error: data.error[0], metadata: data._runtime.metadata) default: - throw ModuleError.DecodingError.dispatchErrorIsNotModule( - description: String(describing: self) - ) + throw FrameTypeError.paramMismatch(for: "DispatchError", + index: -1, expected: "ModuleError", + got: "\(self)") } }} @@ -112,22 +112,24 @@ public enum DispatchError: SomeDispatchError, StaticCallError, Equatable, } } - public static var validatableVariants: [ValidatableStaticVariant] { - [(0, "Other", []), (1, "CannotLookup", []), (2, "BadOrigin", []), - (3, "Module", [ModuleErrorData.self]), (4, "ConsumerRemaining", []), - (5, "NoProviders", []), (6, "TooManyConsumers", []), - (7, "Token", [TokenError.self]), (8, "Arithmetic", [ArithmeticError.self]), - (9, "Transactional", [TransactionalError.self]), (10, "Exhausted", []), - (11, "Corruption", []), (12, "Unavailable", [])] - //(13, "RootNotAllowed", []) + public static var definition: TypeDefinition { + .variant(variants: [ + .e(0, "Other"), .e(1, "CannotLookup"), .e(2, "BadOrigin"), + .s(3, "Module", ModuleErrorData.definition), .e(4, "ConsumerRemaining"), + .e(5, "NoProviders"), .e(6, "TooManyConsumers"), + .s(7, "Token", TokenError.definition), + .s(8, "Arithmetic", ArithmeticError.definition), + .s(9, "Transactional", TransactionalError.definition), + .e(10, "Exhausted"), .e(11, "Corruption"), .e(12, "Unavailable") + //(13, "RootNotAllowed", []) + ]) } } public extension DispatchError { /// Reason why a pallet call failed. struct ModuleErrorData: Equatable, RuntimeDecodable, ScaleCodec.Encodable, - RuntimeSwiftCodable, Swift.Encodable, - RuntimeDynamicValidatableStaticComposite + RuntimeSwiftCodable, Swift.Encodable, IdentifiableType { /// Module index, matching the metadata module index. public let index: UInt8 @@ -164,14 +166,13 @@ public extension DispatchError { lhs.index == rhs.index && lhs.error == rhs.error } - public static var validatableFields: [RuntimeDynamicValidatable.Type] { - [UInt8.self, Data.self] + public static var definition: TypeDefinition { + .composite(fields: [.v(UInt8.definition), .v(.data(count: 4))]) } } enum TokenError: UInt8, CaseIterable, ScaleCodec.Codable, - RuntimeCodable, Swift.Codable, - RuntimeDynamicValidatableStaticVariant + RuntimeCodable, Swift.Codable, IdentifiableType { /// Funds are unavailable. case fundsUnavailable @@ -196,8 +197,8 @@ public extension DispatchError { // case blocked } - enum ArithmeticError: UInt8, CaseIterable, ScaleCodec.Codable, RuntimeCodable, Swift.Codable, - RuntimeDynamicValidatableStaticVariant + enum ArithmeticError: UInt8, CaseIterable, ScaleCodec.Codable, + RuntimeCodable, Swift.Codable, IdentifiableType { case underflow case overflow @@ -205,8 +206,8 @@ public extension DispatchError { } /// Errors related to transactional storage layers. - enum TransactionalError: UInt8, CaseIterable, ScaleCodec.Codable, RuntimeCodable, Swift.Codable, - RuntimeDynamicValidatableStaticVariant + enum TransactionalError: UInt8, CaseIterable, ScaleCodec.Codable, + RuntimeCodable, Swift.Codable, IdentifiableType { /// Too many transactional layers have been spawned. case limitReached diff --git a/Sources/Substrate/Types/Static/EventRecord.swift b/Sources/Substrate/Types/Static/EventRecord.swift index d8382af..77363a6 100644 --- a/Sources/Substrate/Types/Static/EventRecord.swift +++ b/Sources/Substrate/Types/Static/EventRecord.swift @@ -8,8 +8,7 @@ import Foundation import ScaleCodec -public struct EventRecord: SomeEventRecord, CustomStringConvertible, - RuntimeDynamicValidatableStaticComposite +public struct EventRecord: SomeEventRecord, CompositeStaticValidatableType, CustomStringConvertible { public let phase: EventPhase public let header: (name: String, pallet: String) @@ -30,7 +29,7 @@ public struct EventRecord: SomeEventRecord, CustomStringConvertible, try _runtime.decode(from: data) { _ in _eventTypeId } } } - public func typed(_ type: E.Type) throws -> E { + public func typed(_ type: E.Type) throws -> E { try _runtime.decode(from: data) { _ in _eventTypeId } } @@ -38,14 +37,14 @@ public struct EventRecord: SomeEventRecord, CustomStringConvertible, "{phase: \(phase), event: \(header.pallet).\(header.name), topics: \(topics)}" } - public static var validatableFields: [RuntimeDynamicValidatable.Type] { - [EventPhase.self, AnyEvent.self, [H].self] + @inlinable + public static var childTypes: Array { + [EventPhase.self, AnyEvent.self, Array.self] } } public extension EventRecord { - enum EventPhase: Equatable, Hashable, CustomStringConvertible, - RuntimeDynamicValidatableStaticVariant + enum EventPhase: Equatable, Hashable, CustomStringConvertible, IdentifiableType { // Applying an extrinsic. case applyExtrinsic(UInt32) @@ -62,9 +61,11 @@ public extension EventRecord { } } - public static var validatableVariants: [ValidatableStaticVariant] { - [(0, "ApplyExtrinsic", [UInt32.self]), (1, "Finalization", []), - (2, "Initialization", [])] + public static var definition: TypeDefinition { + .variant(variants: [ + .s(0, "ApplyExtrinsic", UInt32.definition), .e(1, "Finalization"), + .e(2, "Initialization") + ]) } } } diff --git a/Sources/Substrate/Types/Static/EventsStorageKey.swift b/Sources/Substrate/Types/Static/EventsStorageKey.swift index 7d3d53b..89162b5 100644 --- a/Sources/Substrate/Types/Static/EventsStorageKey.swift +++ b/Sources/Substrate/Types/Static/EventsStorageKey.swift @@ -8,9 +8,11 @@ import Foundation import ScaleCodec -public struct EventsStorageKey: PlainStorageKey, ValidatableStorageKey { +public struct EventsStorageKey: PlainStorageKey, ComplexStaticFrameType { public typealias TParams = Void public typealias TBaseParams = Void + public typealias TypeInfo = StorageKeyTypeInfo + public typealias ChildTypes = StorageKeyChildTypes public typealias TValue = BE public static var pallet: String { "System" } diff --git a/Sources/Substrate/Types/Static/ExtrinsicEra.swift b/Sources/Substrate/Types/Static/ExtrinsicEra.swift index 0bb920e..de95c92 100644 --- a/Sources/Substrate/Types/Static/ExtrinsicEra.swift +++ b/Sources/Substrate/Types/Static/ExtrinsicEra.swift @@ -119,35 +119,22 @@ extension ExtrinsicEra: ScaleCodec.Codable { extension ExtrinsicEra: RuntimeCodable, RuntimeDynamicDecodable, RuntimeDynamicEncodable {} -extension ExtrinsicEra { - static func _validate(runtime: Runtime, - type id: NetworkType.Id) -> Result - { - guard let info = runtime.resolve(type: id) else { - return .failure(.typeNotFound(id)) - } - guard case .variant(variants: let vars) = info.flatten(runtime).definition else { - return .failure(.wrongType(got: info, for: "ExtrinsicEra")) - } - guard vars.count == Int(UInt8.max) + 1 else { - return .failure(.wrongType(got: info, for: "ExtrinsicEra")) - } - guard vars[0].name == "Immortal", vars[1].name.hasPrefix("Mortal") else { - return .failure(.wrongType(got: info, for: "ExtrinsicEra")) - } - return .success(vars[1].fields.first!.type) - } -} - -extension ExtrinsicEra: RuntimeDynamicValidatable { - public static func validate(runtime: Runtime, type id: NetworkType.Id) -> Result { - _validate(runtime: runtime, type: id).map{_ in} +extension ExtrinsicEra: IdentifiableType { + public static var definition: TypeDefinition { + return .variant(variants: + [.e(0, "Immortal")] + + Array((1...255).map{.s(UInt8($0), "Mortal\($0)", UInt8.definition)}) + ) } } extension ExtrinsicEra: ValueRepresentable { public func asValue(runtime: Runtime, type: NetworkType.Id) throws -> Value { - let bodyType = try Self._validate(runtime: runtime, type: type).getValueError() + let info = try Self.validate(runtime: runtime, type: type).get() + guard case .variant(variants: let vars) = info.type.flatten(runtime).definition else { + throw TypeError.wrongType(for: Self.self, got: info.type, reason: "Not a variant") + } + let bodyType = vars[1].fields[0].type switch self { case .immortal: return .variant(name: "Immortal", values: [], type) case .mortal(period: _, phase: _): diff --git a/Sources/Substrate/Types/Static/ExtrinsicFailureEvent.swift b/Sources/Substrate/Types/Static/ExtrinsicFailureEvent.swift index 1f3093e..afa0f77 100644 --- a/Sources/Substrate/Types/Static/ExtrinsicFailureEvent.swift +++ b/Sources/Substrate/Types/Static/ExtrinsicFailureEvent.swift @@ -8,7 +8,7 @@ import Foundation import ScaleCodec -public struct ExtrinsicFailureEvent: SomeExtrinsicFailureEvent, StaticEvent, RuntimeValidatableStaticComposite { +public struct ExtrinsicFailureEvent: SomeExtrinsicFailureEvent, StaticEvent, IdentifiableFrameType { public struct ExtrinsicFailed: Error { public let error: DispatchError public let info: DispatchInfo @@ -23,8 +23,8 @@ public struct ExtrinsicFailureEvent: SomeExtrinsicFailureEvent, StaticEvent, Run } @inlinable - public static var validatableFields: [RuntimeDynamicValidatable.Type] { - [DispatchError.self, DispatchInfo.self] + public static var definition: FrameTypeDefinition { + .event(Self.self, fields: [.v(DispatchError.definition), .v(DispatchInfo.definition)]) } public static let pallet: String = "System" diff --git a/Sources/Substrate/Types/Static/Hashers.swift b/Sources/Substrate/Types/Static/Hashers.swift index 0db11de..843152a 100644 --- a/Sources/Substrate/Types/Static/Hashers.swift +++ b/Sources/Substrate/Types/Static/Hashers.swift @@ -17,7 +17,7 @@ public struct HBlake2b128: StaticFixedHasher, Equatable { try! Hash128(raw: Blake2b.hash(size: Self.bitWidth / 8, data: data)) } - public static let name = "Blake2_128" + public static let hasherType: LastMetadata.StorageHasher = .blake2b128 public static let bitWidth: Int = 128 public static let instance = Self() } @@ -28,7 +28,7 @@ public struct HBlake2b128Concat: StaticConcatHasher, Equatable { try! Blake2b.hash(size: Self.hashPartBitWidth / 8, data: data) + data } - public static let name = "Blake2_128Concat" + public static let hasherType: LastMetadata.StorageHasher = .blake2b128concat public static let hashPartBitWidth: Int = 128 public static let instance = Self() } @@ -41,20 +41,19 @@ public struct HBlake2b256: StaticFixedHasher, Equatable { try! Hash256(raw: Blake2b.hash(size: Self.bitWidth / 8, data: data)) } - public static let name = "Blake2_256" + public static let hasherType: LastMetadata.StorageHasher = .blake2b256 public static let bitWidth: Int = 256 public static let instance = Self() } -public struct HBlake2b512: StaticFixedHasher, Equatable { +public struct HBlake2b512: Equatable { public typealias THash = Hash512 - + @inlinable public func hash(data: Data) -> THash { try! Hash512(raw: Blake2b.hash(size: Self.bitWidth / 8, data: data)) } - - public static let name = "Blake2_512" + public static let bitWidth: Int = 512 public static let instance = Self() } @@ -64,7 +63,7 @@ public struct HXX64Concat: StaticConcatHasher, Equatable { xxHash(data: data, bitWidth: Self.hashPartBitWidth) + data } - public static let name = "Twox64Concat" + public static let hasherType: LastMetadata.StorageHasher = .xx64concat public static let hashPartBitWidth: Int = 64 public static let instance = Self() } @@ -76,7 +75,7 @@ public struct HXX128: StaticFixedHasher, Equatable { try! Hash128(raw: xxHash(data: data, bitWidth: Self.bitWidth)) } - public static let name = "Twox128" + public static let hasherType: LastMetadata.StorageHasher = .xx128 public static let bitWidth: Int = 128 public static let instance = Self() } @@ -88,7 +87,7 @@ public struct HXX256: StaticFixedHasher, Equatable { try! Hash256(raw: xxHash(data: data, bitWidth: Self.bitWidth)) } - public static let name = "Twox256" + public static let hasherType: LastMetadata.StorageHasher = .xx256 public static let bitWidth: Int = 256 public static let instance = Self() } @@ -99,7 +98,7 @@ public struct HIdentity: StaticConcatHasher, Equatable { return data } - public static let name = "Identity" + public static let hasherType: LastMetadata.StorageHasher = .identity public static let hashPartBitWidth: Int = 0 public static let instance = Self() } diff --git a/Sources/Substrate/Types/Static/MetadataRuntimeCalls.swift b/Sources/Substrate/Types/Static/MetadataRuntimeCalls.swift index cdea83d..f4b8668 100644 --- a/Sources/Substrate/Types/Static/MetadataRuntimeCalls.swift +++ b/Sources/Substrate/Types/Static/MetadataRuntimeCalls.swift @@ -8,16 +8,20 @@ import Foundation import ScaleCodec -public struct MetadataVersionsRuntimeCall: StaticCodableRuntimeCall { +public struct MetadataVersionsRuntimeCall: StaticCodableRuntimeCall, IdentifiableFrameType { public typealias TReturn = [UInt32] public static let method = "metadata_versions" public static let api = "Metadata" public init() {} public func encodeParams(in encoder: inout E) throws {} + + public static var definition: FrameTypeDefinition { + .runtime(Self.self, params: [], return: TReturn.definition) + } } -public struct MetadataAtVersionRuntimeCall: StaticCodableRuntimeCall { +public struct MetadataAtVersionRuntimeCall: StaticCodableRuntimeCall, IdentifiableFrameType { public typealias TReturn = Optional public static let method = "metadata_at_version" public static let api = "Metadata" @@ -28,4 +32,8 @@ public struct MetadataAtVersionRuntimeCall: StaticCodableRuntimeCall { public func encodeParams(in encoder: inout E) throws { try encoder.encode(version) } + + public static var definition: FrameTypeDefinition { + .runtime(Self.self, params: [.v(UInt32.definition)], return: TReturn.definition) + } } diff --git a/Sources/Substrate/Types/Static/MultiAddress.swift b/Sources/Substrate/Types/Static/MultiAddress.swift index 3f1a321..228e1a9 100644 --- a/Sources/Substrate/Types/Static/MultiAddress.swift +++ b/Sources/Substrate/Types/Static/MultiAddress.swift @@ -8,9 +8,9 @@ import Foundation import ScaleCodec -public enum MultiAddress: RuntimeDynamicValidatableStaticVariant - where Index: CompactCodable & ValueRepresentable & RuntimeDynamicValidatable, - Id: AccountId +public enum MultiAddress: IdentifiableType + where Index: CompactCodable & ValueRepresentable & IdentifiableType, + Id: StaticAccountId { case id(Id) case index(Index) @@ -33,9 +33,12 @@ public enum MultiAddress: RuntimeDynamicValidatableStaticVariant self = .index(index) } - public static var validatableVariants: [ValidatableStaticVariant] { - [(0, "Id", [Id.self]), (1, "Index", [Compact.self]), (2, "Raw", [Data.self]), - (3, "Address32", [Data.self]), (4, "Address20", [Data.self])] + public static var definition: TypeDefinition { + .variant(variants: [.s(0, "Id", Id.definition), + .s(1, "Index", Compact.definition), + .s(2, "Raw", Data.definition), + .s(3, "Address32", .data(count: 32)), + .s(4, "Address20", .data(count: 20))]) } } @@ -44,9 +47,9 @@ extension MultiAddress: Hashable where Id: Hashable, Index: Hashable {} extension MultiAddress: ValueRepresentable { public func asValue(runtime: Runtime, type: NetworkType.Id) throws -> Value { - let info = try Self._validate(runtime: runtime, type: type).getValueError() - guard case .variant(variants: let variants) = info.definition else { - throw ValueRepresentableError.wrongType(got: info, for: "MultiAddress") + let info = try Self.validate(runtime: runtime, type: type).get() + guard case .variant(variants: let variants) = info.type.definition else { + throw TypeError.wrongType(for: Self.self, got: info.type, reason: "Not a variant") } switch self { case .id(let id): diff --git a/Sources/Substrate/Types/Static/MultiSignature.swift b/Sources/Substrate/Types/Static/MultiSignature.swift index 08316bc..929cba1 100644 --- a/Sources/Substrate/Types/Static/MultiSignature.swift +++ b/Sources/Substrate/Types/Static/MultiSignature.swift @@ -9,16 +9,30 @@ import Foundation import ScaleCodec public enum MultiSignature: Hashable, Equatable, CustomStringConvertible, - RuntimeDynamicValidatableStaticVariant + VariantStaticValidatableType, IdentifiableType { case ed25519(Ed25519Signature) case sr25519(Sr25519Signature) case ecdsa(EcdsaSignature) - public static var validatableVariants: [ValidatableStaticVariant] { + public static func validate(runtime: Runtime, type: NetworkType.Info) -> Result { + typeInfo(runtime: runtime, type: type).flatMap { + validate(info: $0, type: type, runtime: runtime) + } + } + + public static var childTypes: ChildTypes { [(0, "Ed25519", [Ed25519Signature.self]), (1, "Sr25519", [Sr25519Signature.self]), (2, "Ecdsa", [EcdsaSignature.self])] } + + public static var definition: TypeDefinition { + .variant(variants: [ + .s(0, "Ed25519", Ed25519Signature.definition), + .s(1, "Sr25519", Sr25519Signature.definition), + .s(2, "Ecdsa", EcdsaSignature.definition) + ]) + } } extension MultiSignature: StaticSignature { @@ -63,9 +77,9 @@ extension MultiSignature: StaticSignature { extension MultiSignature: ValueRepresentable { public func asValue(runtime: Runtime, type: NetworkType.Id) throws -> Value { - let info = try Self._validate(runtime: runtime, type: type).getValueError() - guard case .variant(variants: let variants) = info.flatten(runtime).definition else { - throw ValueRepresentableError.wrongType(got: info, for: "MultiSignature") + let info = try Self.validate(runtime: runtime, type: type).get() + guard case .variant(variants: let variants) = info.type.flatten(runtime).definition else { + throw TypeError.wrongType(for: Self.self, got: info.type, reason: "Should be variant") } switch self { case .sr25519(let sig): diff --git a/Sources/Substrate/Types/Static/Signatures.swift b/Sources/Substrate/Types/Static/Signatures.swift index 45d72a1..4f2d228 100644 --- a/Sources/Substrate/Types/Static/Signatures.swift +++ b/Sources/Substrate/Types/Static/Signatures.swift @@ -9,7 +9,7 @@ import Foundation import ScaleCodec public protocol SingleTypeStaticSignature: StaticSignature, FixedDataCodable, VoidValueRepresentable, - RuntimeDynamicValidatable, Hashable, Equatable, + IdentifiableType, Hashable, Equatable, CustomStringConvertible { var raw: Data { get } @@ -40,37 +40,30 @@ public extension SingleTypeStaticSignature { static var fixedBytesCount: Int { algorithm.signatureBytesCount } func asValue(runtime: Runtime, type: NetworkType.Id) throws -> Value { - guard let info = runtime.resolve(type: type) else { - throw ValueRepresentableError.typeNotFound(type) - } - guard let count = info.asBytes(runtime) else { - throw ValueRepresentableError.wrongType(got: info, for: String(describing: Self.self)) - } - let bytes = raw - guard count == 0 || bytes.count == count else { - throw ValueRepresentableError.wrongValuesCount(in: info, expected: bytes.count, - for: String(describing: Self.self)) - } - return .bytes(bytes, type) + let _ = try Self.validate(runtime: runtime, type: type).get() + return .bytes(raw, type) } func asValue() -> Value { .bytes(raw) } - static func validate(runtime: any Runtime, - type id: NetworkType.Id) -> Result { - guard let info = runtime.resolve(type: id) else { - return .failure(.typeNotFound(id)) - } - return AnySignature.parseTypeInfo(runtime: runtime, typeId: id).flatMap { types in + @inlinable + static var definition: TypeDefinition { .data(count: UInt32(fixedBytesCount)) } + + static func _validate(runtime: any Runtime, + type: NetworkType) -> Result { + return AnySignature.parseTypeInfo(runtime: runtime, type: type).flatMap { types in guard types.count == 1, types.values.first == algorithm else { - return .failure(.wrongType(got: info, for: String(describing: Self.self))) + return .failure(.wrongType(for: Self.self, got: type, + reason: "Unknown signature type: \(types)")) } - guard let count = info.asBytes(runtime) else { - return .failure(.wrongType(got: info, for: String(describing: Self.self))) + guard let count = type.asBytes(runtime) else { + return .failure(.wrongType(for: Self.self, got: type, + reason: "Signature is not byte sequence")) } guard Self.fixedBytesCount == count else { - return .failure(.wrongValuesCount(in: info, expected: Self.fixedBytesCount, - for: String(describing: Self.self))) + return .failure(.wrongValuesCount(for: Self.self, + expected: Self.fixedBytesCount, + in: type)) } return .success(()) } @@ -87,6 +80,12 @@ public struct EcdsaSignature: SingleTypeStaticSignature { raw = data } + @inlinable + public static func validate(runtime: Runtime, + type: NetworkType) -> Result { + _validate(runtime: runtime, type: type) + } + @inlinable public static var algorithm: CryptoTypeId { .ecdsa } } @@ -101,6 +100,12 @@ public struct Ed25519Signature: SingleTypeStaticSignature { raw = data } + @inlinable + public static func validate(runtime: Runtime, + type: NetworkType) -> Result { + _validate(runtime: runtime, type: type) + } + @inlinable public static var algorithm: CryptoTypeId { .ed25519 } } @@ -115,6 +120,12 @@ public struct Sr25519Signature: SingleTypeStaticSignature { raw = data } + @inlinable + public static func validate(runtime: Runtime, + type: NetworkType) -> Result { + _validate(runtime: runtime, type: type) + } + @inlinable public static var algorithm: CryptoTypeId { .sr25519 } } diff --git a/Sources/Substrate/Types/Static/SubstrateFeeInfo.swift b/Sources/Substrate/Types/Static/SubstrateFeeInfo.swift index 3ec06bc..ead7e39 100644 --- a/Sources/Substrate/Types/Static/SubstrateFeeInfo.swift +++ b/Sources/Substrate/Types/Static/SubstrateFeeInfo.swift @@ -9,11 +9,10 @@ import Foundation import ScaleCodec public struct DispatchInfo: ScaleCodec.Decodable, RuntimeDecodable, - RuntimeDynamicDecodable, RuntimeDynamicValidatableStaticComposite + RuntimeDynamicDecodable, IdentifiableType { public struct Weight: ScaleCodec.Decodable, RuntimeDecodable, - RuntimeDynamicDecodable, - RuntimeDynamicValidatableStaticComposite + RuntimeDynamicDecodable, IdentifiableType { public let refTime: UInt64 public let proofSize: UInt64 @@ -23,13 +22,15 @@ public struct DispatchInfo: ScaleCodec.Decodable, RuntimeDecodable, proofSize = try decoder.decode(.compact) } - public static var validatableFields: [RuntimeDynamicValidatable.Type] { - [Compact.self, Compact.self] + @inlinable + public static var definition: TypeDefinition { + .composite(fields: [.v(Compact.definition), + .v(Compact.definition)]) } } public enum DispatchClass: UInt8, CaseIterable, ScaleCodec.Codable, RuntimeDecodable, - RuntimeDynamicDecodable, RuntimeDynamicValidatableStaticVariant + RuntimeDynamicDecodable, IdentifiableType { case normal case operational @@ -37,7 +38,7 @@ public struct DispatchInfo: ScaleCodec.Decodable, RuntimeDecodable, } public enum Pays: UInt8, CaseIterable, ScaleCodec.Codable, RuntimeDecodable, - RuntimeDynamicDecodable, RuntimeDynamicValidatableStaticVariant + RuntimeDynamicDecodable, IdentifiableType { case yes case no @@ -53,15 +54,18 @@ public struct DispatchInfo: ScaleCodec.Decodable, RuntimeDecodable, paysFee = try decoder.decode() } - public static var validatableFields: [RuntimeDynamicValidatable.Type] { - [Weight.self, DispatchClass.self, Pays.self] + @inlinable + public static var definition: TypeDefinition { + .composite(fields: [.v(Weight.definition), + .v(DispatchClass.definition), + .v(Pays.definition)]) } } public struct RuntimeDispatchInfo: RuntimeDecodable, RuntimeDynamicDecodable, - RuntimeDynamicValidatableStaticComposite + IdentifiableType { public let weight: DispatchInfo.Weight public let clazz: DispatchInfo.DispatchClass @@ -73,16 +77,18 @@ public struct RuntimeDispatchInfo: RuntimeDecodable, partialFee = try runtime.decode(from: &decoder) } - public static var validatableFields: [RuntimeDynamicValidatable.Type] { - [DispatchInfo.Weight.self, DispatchInfo.DispatchClass.self, Bal.self] + @inlinable + public static var definition: TypeDefinition { + .composite(fields: [.v(DispatchInfo.Weight.definition), + .v(DispatchInfo.DispatchClass.definition), + .v(Bal.definition)]) } } -public struct FeeDetails: RuntimeDecodable, - RuntimeDynamicDecodable, - RuntimeDynamicValidatableStaticComposite +public struct FeeDetails: RuntimeDecodable, RuntimeDynamicDecodable, IdentifiableType + where Bal: ConfigUnsignedInteger { - public struct InclusionFee: RuntimeDecodable, RuntimeDynamicValidatableStaticComposite { + public struct InclusionFee: RuntimeDecodable, IdentifiableType { /// minimum amount a user pays for a transaction. public let baseFee: Bal /// amount paid for the encoded length (in bytes) of the transaction. @@ -102,8 +108,11 @@ public struct FeeDetails: RuntimeDecodable, adjustedWeightFee = try runtime.decode(from: &decoder) } - public static var validatableFields: [RuntimeDynamicValidatable.Type] { - [Bal.self, Bal.self, Bal.self] + @inlinable + public static var definition: TypeDefinition { + .composite(fields: [.v(Bal.definition), + .v(Bal.definition), + .v(Bal.definition)]) } } @@ -117,7 +126,9 @@ public struct FeeDetails: RuntimeDecodable, tip = try runtime.decode(from: &decoder) } - public static var validatableFields: [RuntimeDynamicValidatable.Type] { - [Optional.self, Bal.self] + @inlinable + public static var definition: TypeDefinition { + .composite(fields: [.v(Optional.definition), + .v(Bal.definition)]) } } diff --git a/Sources/Substrate/Types/Static/SubstrateSigningParameters.swift b/Sources/Substrate/Types/Static/SubstrateSigningParameters.swift index 33d5667..ceb69a7 100644 --- a/Sources/Substrate/Types/Static/SubstrateSigningParameters.swift +++ b/Sources/Substrate/Types/Static/SubstrateSigningParameters.swift @@ -9,8 +9,8 @@ import Foundation public struct SubstrateSigningParameters< E: SomeExtrinsicEra, H: Hash, A: AccountId, - N: UnsignedInteger & ValueRepresentable & RuntimeDynamicValidatable, - P: ValueRepresentable & RuntimeDynamicValidatable + N: UnsignedInteger & ValueRepresentable & ValidatableType, + P: ValueRepresentable & ValidatableType >: EraSigningParameters, NonceSigningParameters, PaymentSigningParameters { public typealias TPartial = Partial diff --git a/Sources/Substrate/Types/Static/TransactionQueryInfoRuntimeCalls.swift b/Sources/Substrate/Types/Static/TransactionQueryInfoRuntimeCalls.swift index 1b33848..cae944d 100644 --- a/Sources/Substrate/Types/Static/TransactionQueryInfoRuntimeCalls.swift +++ b/Sources/Substrate/Types/Static/TransactionQueryInfoRuntimeCalls.swift @@ -8,8 +8,9 @@ import Foundation import ScaleCodec -public protocol TransactionQueryRuntimeCallCommon: StaticRuntimeCall, RuntimeValidatableStaticComposite - where TReturn: RuntimeDynamicDecodable & RuntimeDynamicValidatable +public protocol TransactionQueryRuntimeCallCommon: StaticRuntimeCall, ComplexStaticFrameType + where TReturn: RuntimeDynamicDecodable & ValidatableType, + TypeInfo == RuntimeCallTypeInfo, ChildTypes == RuntimeCallChildTypes { var extrinsic: Data { get } } @@ -34,13 +35,12 @@ public extension TransactionQueryRuntimeCallCommon { } } - static var validatableFields: [RuntimeDynamicValidatable.Type] { - [Data.self, UInt32.self] - } + @inlinable + static var childTypes: ChildTypes { (params: [Data.self, UInt32.self], result: TReturn.self) } } public struct TransactionQueryInfoRuntimeCall: TransactionQueryRuntimeCallCommon - where DI: RuntimeDynamicDecodable & RuntimeDynamicValidatable + where DI: RuntimeDynamicDecodable & ValidatableType { public typealias TReturn = DI @@ -55,7 +55,7 @@ public struct TransactionQueryInfoRuntimeCall: TransactionQueryRuntimeCallCo } public struct TransactionQueryFeeDetailsRuntimeCall: TransactionQueryRuntimeCallCommon - where FD: RuntimeDynamicDecodable & RuntimeDynamicValidatable + where FD: RuntimeDynamicDecodable & ValidatableType { public typealias TReturn = FD diff --git a/Sources/Substrate/Types/Static/TransactionValidityError.swift b/Sources/Substrate/Types/Static/TransactionValidityError.swift index 24cd902..bd0a698 100644 --- a/Sources/Substrate/Types/Static/TransactionValidityError.swift +++ b/Sources/Substrate/Types/Static/TransactionValidityError.swift @@ -10,8 +10,7 @@ import ScaleCodec /// Errors that can occur while checking the validity of a transaction. public enum TransactionValidityError: StaticCallError, Equatable, Swift.Codable, - RuntimeSwiftCodable, ScaleCodec.Codable, RuntimeCodable, - RuntimeDynamicValidatableStaticVariant + RuntimeSwiftCodable, ScaleCodec.Codable, RuntimeCodable { /// The transaction is invalid. case invalid(InvalidTransaction) @@ -69,16 +68,19 @@ public enum TransactionValidityError: StaticCallError, Equatable, Swift.Codable, } } - public static var validatableVariants: [ValidatableStaticVariant] { - [(0, "Invalid", [InvalidTransaction.self]), (1, "Unknown", [UnknownTransaction.self])] + @inlinable + public static var definition: TypeDefinition { + .variant(variants: [ + .s(0, "Invalid", InvalidTransaction.definition), + .s(1, "Unknown", UnknownTransaction.definition) + ]) } } public extension TransactionValidityError { /// An invalid transaction validity. enum InvalidTransaction: Error, Equatable, Swift.Codable, ScaleCodec.Codable, - RuntimeCodable, RuntimeSwiftCodable, - RuntimeDynamicValidatableStaticVariant + RuntimeCodable, RuntimeSwiftCodable { /// The call of the transaction is not expected. case call @@ -228,10 +230,12 @@ public extension TransactionValidityError { } @inlinable - public static var validatableVariants: [ValidatableStaticVariant] { - [(0, "Call", []), (1, "Payment", []), (2, "Future", []), (3, "Stale", []), - (4, "BadProof", []), (5, "AncientBirthBlock", []), (6, "ExhaustsResources", []), - (7, "Custom", [UInt8.self]), (8, "BadMandatory", []), (9, "MandatoryDispatch", [])] + public static var definition: TypeDefinition { + .variant(variants: [ + .e(0, "Call"), .e(1, "Payment"), .e(2, "Future"), .e(3, "Stale"), + .e(4, "BadProof"), .e(5, "AncientBirthBlock"), .e(6, "ExhaustsResources"), + .s(7, "Custom", UInt8.definition), .e(8, "BadMandatory"), .e(9, "MandatoryDispatch") + ]) } } } @@ -239,8 +243,7 @@ public extension TransactionValidityError { public extension TransactionValidityError { /// An unknown transaction validity. enum UnknownTransaction: Error, Equatable, Swift.Codable, ScaleCodec.Codable, - RuntimeCodable, RuntimeSwiftCodable, - RuntimeDynamicValidatableStaticVariant + RuntimeCodable, RuntimeSwiftCodable { /// Could not lookup some information that is required to validate the transaction. case cannotLookup @@ -311,9 +314,12 @@ public extension TransactionValidityError { } } - public static var validatableVariants: [ValidatableStaticVariant] { - [(0, "CannotLookup", []), (1, "NoUnsignedValidator", []), - (2, "Custom", [UInt8.self])] + @inlinable + public static var definition: TypeDefinition { + .variant(variants: [ + .e(0, "CannotLookup"), .e(1, "NoUnsignedValidator"), + .s(2, "Custom", UInt8.definition) + ]) } } } diff --git a/Sources/Substrate/Types/Static/Tuples+StaticExtrinsicExtensions.swift b/Sources/Substrate/Types/Static/Tuples+StaticExtrinsicExtensions.swift index d27b127..08335a1 100644 --- a/Sources/Substrate/Types/Static/Tuples+StaticExtrinsicExtensions.swift +++ b/Sources/Substrate/Types/Static/Tuples+StaticExtrinsicExtensions.swift @@ -44,7 +44,7 @@ public extension SomeTuple1 where func validate( runtime: any Runtime, types: [ExtrinsicExtensionId: (extId: NetworkType.Id, addId: NetworkType.Id)] - ) -> Result> { + ) -> Result> { guard let info = types[first.identifier] else { return .failure(.left(.unknownExtension(identifier: first.identifier))) } @@ -96,7 +96,7 @@ public extension ListTuple where func validate( runtime: any Runtime, types: [ExtrinsicExtensionId: (extId: NetworkType.Id, addId: NetworkType.Id)] - ) -> Result> { + ) -> Result> { guard let info = types[last.identifier] else { return .failure(.left(.unknownExtension(identifier: last.identifier))) } diff --git a/Sources/Substrate/Types/Static/Tuples+TupleStorageKey.swift b/Sources/Substrate/Types/Static/Tuples+TupleStorageKey.swift index 12b71ea..4eea5b5 100644 --- a/Sources/Substrate/Types/Static/Tuples+TupleStorageKey.swift +++ b/Sources/Substrate/Types/Static/Tuples+TupleStorageKey.swift @@ -27,8 +27,20 @@ public extension SomeTuple1 where } public extension SomeTuple1 where Self: TupleStorageValidatableKeyPath { - static var path: [(RuntimeDynamicValidatable.Type, StaticHasher.Type)] { - [(T1.TKey.self, T1.THasher.self)] + @inlinable + static var validatablePath: [(hasher: StaticHasher.Type, + type: ValidatableType.Type)] + { + [(T1.THasher.self, T1.TKey.self)] + } +} + +public extension SomeTuple1 where Self: TupleStorageIdentifiableKeyPath { + @inlinable + static var identifiablePath: [(key: TypeDefinition, + hasher: LastMetadata.StorageHasher)] + { + [(T1.TKey.definition, T1.THasher.hasherType)] } } @@ -58,8 +70,22 @@ public extension ListTuple where Self: TupleStorageNKeyPath & TupleStorageValidatableKeyPath, DroppedFirst: TupleStorageValidatableKeyPath { - static var path: [(RuntimeDynamicValidatable.Type, StaticHasher.Type)] { - [(First.TKey.self, First.THasher.self)] + DroppedFirst.path + static var validatablePath: [(hasher: StaticHasher.Type, + type: ValidatableType.Type)] + { + [(First.THasher.self, First.TKey.self)] + DroppedFirst.validatablePath + } +} + +public extension ListTuple where + Self: TupleStorageNKeyPath & TupleStorageIdentifiableKeyPath, + DroppedFirst: TupleStorageIdentifiableKeyPath +{ + @inlinable + static var identifiablePath: [(key: TypeDefinition, + hasher: LastMetadata.StorageHasher)] + { + [(First.TKey.definition, First.THasher.hasherType)] + DroppedFirst.identifiablePath } } @@ -69,7 +95,9 @@ extension Tuple1: TupleStorageKeyPath where T1: TupleStorageKeyHasherPair { public typealias THashes = Tuple1 } extension Tuple1: TupleStorageValidatableKeyPath where - Self: TupleStorageKeyPath, T1.TKey: RuntimeDynamicValidatable {} + Self: TupleStorageKeyPath, T1.TKey: ValidatableType {} +extension Tuple1: TupleStorageIdentifiableKeyPath where + Self: TupleStorageKeyPath, T1.TKey: IdentifiableType {} extension Tuple2: TupleStorageNKeyPath, TupleStorageKeyPath where T1: TupleStorageKeyHasherPair, T2: TupleStorageKeyHasherPair @@ -79,8 +107,11 @@ extension Tuple2: TupleStorageNKeyPath, TupleStorageKeyPath where public typealias THashes = Tuple2 } extension Tuple2: TupleStorageValidatableKeyPath where - Self: TupleStorageNKeyPath, T1.TKey: RuntimeDynamicValidatable, - T2.TKey: RuntimeDynamicValidatable {} + Self: TupleStorageNKeyPath, T1.TKey: ValidatableType, + T2.TKey: ValidatableType {} +extension Tuple2: TupleStorageIdentifiableKeyPath where + Self: TupleStorageKeyPath, T1.TKey: IdentifiableType, + T2.TKey: IdentifiableType {} extension Tuple3: TupleStorageNKeyPath, TupleStorageKeyPath where T1: TupleStorageKeyHasherPair, T2: TupleStorageKeyHasherPair, @@ -91,8 +122,11 @@ extension Tuple3: TupleStorageNKeyPath, TupleStorageKeyPath where public typealias THashes = Tuple3 } extension Tuple3: TupleStorageValidatableKeyPath where - Self: TupleStorageNKeyPath, T1.TKey: RuntimeDynamicValidatable, - T2.TKey: RuntimeDynamicValidatable, T3.TKey: RuntimeDynamicValidatable {} + Self: TupleStorageNKeyPath, T1.TKey: ValidatableType, + T2.TKey: ValidatableType, T3.TKey: ValidatableType {} +extension Tuple3: TupleStorageIdentifiableKeyPath where + Self: TupleStorageKeyPath, T1.TKey: IdentifiableType, + T2.TKey: IdentifiableType, T3.TKey: IdentifiableType {} extension Tuple4: TupleStorageNKeyPath, TupleStorageKeyPath where T1: TupleStorageKeyHasherPair, T2: TupleStorageKeyHasherPair, @@ -104,9 +138,13 @@ extension Tuple4: TupleStorageNKeyPath, TupleStorageKeyPath where public typealias THashes = Tuple4 } extension Tuple4: TupleStorageValidatableKeyPath where - Self: TupleStorageNKeyPath, T1.TKey: RuntimeDynamicValidatable, - T2.TKey: RuntimeDynamicValidatable, T3.TKey: RuntimeDynamicValidatable, - T4.TKey: RuntimeDynamicValidatable {} + Self: TupleStorageNKeyPath, T1.TKey: ValidatableType, + T2.TKey: ValidatableType, T3.TKey: ValidatableType, + T4.TKey: ValidatableType {} +extension Tuple4: TupleStorageIdentifiableKeyPath where + Self: TupleStorageKeyPath, T1.TKey: IdentifiableType, + T2.TKey: IdentifiableType, T3.TKey: IdentifiableType, + T4.TKey: IdentifiableType {} extension Tuple5: TupleStorageNKeyPath, TupleStorageKeyPath where T1: TupleStorageKeyHasherPair, T2: TupleStorageKeyHasherPair, @@ -119,9 +157,13 @@ extension Tuple5: TupleStorageNKeyPath, TupleStorageKeyPath where public typealias THashes = Tuple5 } extension Tuple5: TupleStorageValidatableKeyPath where - Self: TupleStorageNKeyPath, T1.TKey: RuntimeDynamicValidatable, - T2.TKey: RuntimeDynamicValidatable, T3.TKey: RuntimeDynamicValidatable, - T4.TKey: RuntimeDynamicValidatable, T5.TKey: RuntimeDynamicValidatable {} + Self: TupleStorageNKeyPath, T1.TKey: ValidatableType, + T2.TKey: ValidatableType, T3.TKey: ValidatableType, + T4.TKey: ValidatableType, T5.TKey: ValidatableType {} +extension Tuple5: TupleStorageIdentifiableKeyPath where + Self: TupleStorageKeyPath, T1.TKey: IdentifiableType, + T2.TKey: IdentifiableType, T3.TKey: IdentifiableType, + T4.TKey: IdentifiableType, T5.TKey: IdentifiableType {} extension Tuple6: TupleStorageNKeyPath, TupleStorageKeyPath where T1: TupleStorageKeyHasherPair, T2: TupleStorageKeyHasherPair, @@ -134,10 +176,15 @@ extension Tuple6: TupleStorageNKeyPath, TupleStorageKeyPath where public typealias THashes = Tuple6 } extension Tuple6: TupleStorageValidatableKeyPath where - Self: TupleStorageNKeyPath, T1.TKey: RuntimeDynamicValidatable, - T2.TKey: RuntimeDynamicValidatable, T3.TKey: RuntimeDynamicValidatable, - T4.TKey: RuntimeDynamicValidatable, T5.TKey: RuntimeDynamicValidatable, - T6.TKey: RuntimeDynamicValidatable {} + Self: TupleStorageNKeyPath, T1.TKey: ValidatableType, + T2.TKey: ValidatableType, T3.TKey: ValidatableType, + T4.TKey: ValidatableType, T5.TKey: ValidatableType, + T6.TKey: ValidatableType {} +extension Tuple6: TupleStorageIdentifiableKeyPath where + Self: TupleStorageKeyPath, T1.TKey: IdentifiableType, + T2.TKey: IdentifiableType, T3.TKey: IdentifiableType, + T4.TKey: IdentifiableType, T5.TKey: IdentifiableType, + T6.TKey: IdentifiableType {} extension Tuple7: TupleStorageNKeyPath, TupleStorageKeyPath where T1: TupleStorageKeyHasherPair, T2: TupleStorageKeyHasherPair, @@ -153,10 +200,15 @@ extension Tuple7: TupleStorageNKeyPath, TupleStorageKeyPath where public typealias THashes = Tuple7 } extension Tuple7: TupleStorageValidatableKeyPath where - Self: TupleStorageNKeyPath, T1.TKey: RuntimeDynamicValidatable, - T2.TKey: RuntimeDynamicValidatable, T3.TKey: RuntimeDynamicValidatable, - T4.TKey: RuntimeDynamicValidatable, T5.TKey: RuntimeDynamicValidatable, - T6.TKey: RuntimeDynamicValidatable, T7.TKey: RuntimeDynamicValidatable {} + Self: TupleStorageNKeyPath, T1.TKey: ValidatableType, + T2.TKey: ValidatableType, T3.TKey: ValidatableType, + T4.TKey: ValidatableType, T5.TKey: ValidatableType, + T6.TKey: ValidatableType, T7.TKey: ValidatableType {} +extension Tuple7: TupleStorageIdentifiableKeyPath where + Self: TupleStorageKeyPath, T1.TKey: IdentifiableType, + T2.TKey: IdentifiableType, T3.TKey: IdentifiableType, + T4.TKey: IdentifiableType, T5.TKey: IdentifiableType, + T6.TKey: IdentifiableType, T7.TKey: IdentifiableType {} extension Tuple8: TupleStorageNKeyPath, TupleStorageKeyPath where T1: TupleStorageKeyHasherPair, T2: TupleStorageKeyHasherPair, @@ -172,11 +224,17 @@ extension Tuple8: TupleStorageNKeyPath, TupleStorageKeyPath where public typealias THashes = Tuple8 } extension Tuple8: TupleStorageValidatableKeyPath where - Self: TupleStorageNKeyPath, T1.TKey: RuntimeDynamicValidatable, - T2.TKey: RuntimeDynamicValidatable, T3.TKey: RuntimeDynamicValidatable, - T4.TKey: RuntimeDynamicValidatable, T5.TKey: RuntimeDynamicValidatable, - T6.TKey: RuntimeDynamicValidatable, T7.TKey: RuntimeDynamicValidatable, - T8.TKey: RuntimeDynamicValidatable {} + Self: TupleStorageNKeyPath, T1.TKey: ValidatableType, + T2.TKey: ValidatableType, T3.TKey: ValidatableType, + T4.TKey: ValidatableType, T5.TKey: ValidatableType, + T6.TKey: ValidatableType, T7.TKey: ValidatableType, + T8.TKey: ValidatableType {} +extension Tuple8: TupleStorageIdentifiableKeyPath where + Self: TupleStorageKeyPath, T1.TKey: IdentifiableType, + T2.TKey: IdentifiableType, T3.TKey: IdentifiableType, + T4.TKey: IdentifiableType, T5.TKey: IdentifiableType, + T6.TKey: IdentifiableType, T7.TKey: IdentifiableType, + T8.TKey: IdentifiableType {} extension Tuple9: TupleStorageNKeyPath, TupleStorageKeyPath where T1: TupleStorageKeyHasherPair, T2: TupleStorageKeyHasherPair, @@ -193,11 +251,17 @@ extension Tuple9: TupleStorageNKeyPath, TupleStorageKeyPath where public typealias THashes = Tuple9 } extension Tuple9: TupleStorageValidatableKeyPath where - Self: TupleStorageNKeyPath, T1.TKey: RuntimeDynamicValidatable, - T2.TKey: RuntimeDynamicValidatable, T3.TKey: RuntimeDynamicValidatable, - T4.TKey: RuntimeDynamicValidatable, T5.TKey: RuntimeDynamicValidatable, - T6.TKey: RuntimeDynamicValidatable, T7.TKey: RuntimeDynamicValidatable, - T8.TKey: RuntimeDynamicValidatable, T9.TKey: RuntimeDynamicValidatable {} + Self: TupleStorageNKeyPath, T1.TKey: ValidatableType, + T2.TKey: ValidatableType, T3.TKey: ValidatableType, + T4.TKey: ValidatableType, T5.TKey: ValidatableType, + T6.TKey: ValidatableType, T7.TKey: ValidatableType, + T8.TKey: ValidatableType, T9.TKey: ValidatableType {} +extension Tuple9: TupleStorageIdentifiableKeyPath where + Self: TupleStorageKeyPath, T1.TKey: IdentifiableType, + T2.TKey: IdentifiableType, T3.TKey: IdentifiableType, + T4.TKey: IdentifiableType, T5.TKey: IdentifiableType, + T6.TKey: IdentifiableType, T7.TKey: IdentifiableType, + T8.TKey: IdentifiableType, T9.TKey: IdentifiableType {} extension Tuple10: TupleStorageNKeyPath, TupleStorageKeyPath where T1: TupleStorageKeyHasherPair, T2: TupleStorageKeyHasherPair, @@ -216,12 +280,19 @@ extension Tuple10: TupleStorageNKeyPath, TupleStorageKeyPath where Data> } extension Tuple10: TupleStorageValidatableKeyPath where - Self: TupleStorageNKeyPath, T1.TKey: RuntimeDynamicValidatable, - T2.TKey: RuntimeDynamicValidatable, T3.TKey: RuntimeDynamicValidatable, - T4.TKey: RuntimeDynamicValidatable, T5.TKey: RuntimeDynamicValidatable, - T6.TKey: RuntimeDynamicValidatable, T7.TKey: RuntimeDynamicValidatable, - T8.TKey: RuntimeDynamicValidatable, T9.TKey: RuntimeDynamicValidatable, - T10.TKey: RuntimeDynamicValidatable {} + Self: TupleStorageNKeyPath, T1.TKey: ValidatableType, + T2.TKey: ValidatableType, T3.TKey: ValidatableType, + T4.TKey: ValidatableType, T5.TKey: ValidatableType, + T6.TKey: ValidatableType, T7.TKey: ValidatableType, + T8.TKey: ValidatableType, T9.TKey: ValidatableType, + T10.TKey: ValidatableType {} +extension Tuple10: TupleStorageIdentifiableKeyPath where + Self: TupleStorageKeyPath, T1.TKey: IdentifiableType, + T2.TKey: IdentifiableType, T3.TKey: IdentifiableType, + T4.TKey: IdentifiableType, T5.TKey: IdentifiableType, + T6.TKey: IdentifiableType, T7.TKey: IdentifiableType, + T8.TKey: IdentifiableType, T9.TKey: IdentifiableType, + T10.TKey: IdentifiableType {} extension Tuple11: TupleStorageNKeyPath, TupleStorageKeyPath where T1: TupleStorageKeyHasherPair, T2: TupleStorageKeyHasherPair, @@ -241,12 +312,19 @@ extension Tuple11: TupleStorageNKeyPath, TupleStorageKeyPath where Data, Data> } extension Tuple11: TupleStorageValidatableKeyPath where - Self: TupleStorageNKeyPath, T1.TKey: RuntimeDynamicValidatable, - T2.TKey: RuntimeDynamicValidatable, T3.TKey: RuntimeDynamicValidatable, - T4.TKey: RuntimeDynamicValidatable, T5.TKey: RuntimeDynamicValidatable, - T6.TKey: RuntimeDynamicValidatable, T7.TKey: RuntimeDynamicValidatable, - T8.TKey: RuntimeDynamicValidatable, T9.TKey: RuntimeDynamicValidatable, - T10.TKey: RuntimeDynamicValidatable, T11.TKey: RuntimeDynamicValidatable {} + Self: TupleStorageNKeyPath, T1.TKey: ValidatableType, + T2.TKey: ValidatableType, T3.TKey: ValidatableType, + T4.TKey: ValidatableType, T5.TKey: ValidatableType, + T6.TKey: ValidatableType, T7.TKey: ValidatableType, + T8.TKey: ValidatableType, T9.TKey: ValidatableType, + T10.TKey: ValidatableType, T11.TKey: ValidatableType {} +extension Tuple11: TupleStorageIdentifiableKeyPath where + Self: TupleStorageKeyPath, T1.TKey: IdentifiableType, + T2.TKey: IdentifiableType, T3.TKey: IdentifiableType, + T4.TKey: IdentifiableType, T5.TKey: IdentifiableType, + T6.TKey: IdentifiableType, T7.TKey: IdentifiableType, + T8.TKey: IdentifiableType, T9.TKey: IdentifiableType, + T10.TKey: IdentifiableType, T11.TKey: IdentifiableType {} extension Tuple12: TupleStorageNKeyPath, TupleStorageKeyPath where T1: TupleStorageKeyHasherPair, T2: TupleStorageKeyHasherPair, @@ -266,13 +344,21 @@ extension Tuple12: TupleStorageNKeyPath, TupleStorageKeyPath where Data, Data, Data> } extension Tuple12: TupleStorageValidatableKeyPath where - Self: TupleStorageNKeyPath, T1.TKey: RuntimeDynamicValidatable, - T2.TKey: RuntimeDynamicValidatable, T3.TKey: RuntimeDynamicValidatable, - T4.TKey: RuntimeDynamicValidatable, T5.TKey: RuntimeDynamicValidatable, - T6.TKey: RuntimeDynamicValidatable, T7.TKey: RuntimeDynamicValidatable, - T8.TKey: RuntimeDynamicValidatable, T9.TKey: RuntimeDynamicValidatable, - T10.TKey: RuntimeDynamicValidatable, T11.TKey: RuntimeDynamicValidatable, - T12.TKey: RuntimeDynamicValidatable {} + Self: TupleStorageNKeyPath, T1.TKey: ValidatableType, + T2.TKey: ValidatableType, T3.TKey: ValidatableType, + T4.TKey: ValidatableType, T5.TKey: ValidatableType, + T6.TKey: ValidatableType, T7.TKey: ValidatableType, + T8.TKey: ValidatableType, T9.TKey: ValidatableType, + T10.TKey: ValidatableType, T11.TKey: ValidatableType, + T12.TKey: ValidatableType {} +extension Tuple12: TupleStorageIdentifiableKeyPath where + Self: TupleStorageKeyPath, T1.TKey: IdentifiableType, + T2.TKey: IdentifiableType, T3.TKey: IdentifiableType, + T4.TKey: IdentifiableType, T5.TKey: IdentifiableType, + T6.TKey: IdentifiableType, T7.TKey: IdentifiableType, + T8.TKey: IdentifiableType, T9.TKey: IdentifiableType, + T10.TKey: IdentifiableType, T11.TKey: IdentifiableType, + T12.TKey: IdentifiableType {} extension Tuple13: TupleStorageNKeyPath, TupleStorageKeyPath where T1: TupleStorageKeyHasherPair, T2: TupleStorageKeyHasherPair, @@ -295,13 +381,21 @@ extension Tuple13: TupleStorageNKeyPath, TupleStorageKeyPath where Data, Data, Data, Data> } extension Tuple13: TupleStorageValidatableKeyPath where - Self: TupleStorageNKeyPath, T1.TKey: RuntimeDynamicValidatable, - T2.TKey: RuntimeDynamicValidatable, T3.TKey: RuntimeDynamicValidatable, - T4.TKey: RuntimeDynamicValidatable, T5.TKey: RuntimeDynamicValidatable, - T6.TKey: RuntimeDynamicValidatable, T7.TKey: RuntimeDynamicValidatable, - T8.TKey: RuntimeDynamicValidatable, T9.TKey: RuntimeDynamicValidatable, - T10.TKey: RuntimeDynamicValidatable, T11.TKey: RuntimeDynamicValidatable, - T12.TKey: RuntimeDynamicValidatable, T13.TKey: RuntimeDynamicValidatable {} + Self: TupleStorageNKeyPath, T1.TKey: ValidatableType, + T2.TKey: ValidatableType, T3.TKey: ValidatableType, + T4.TKey: ValidatableType, T5.TKey: ValidatableType, + T6.TKey: ValidatableType, T7.TKey: ValidatableType, + T8.TKey: ValidatableType, T9.TKey: ValidatableType, + T10.TKey: ValidatableType, T11.TKey: ValidatableType, + T12.TKey: ValidatableType, T13.TKey: ValidatableType {} +extension Tuple13: TupleStorageIdentifiableKeyPath where + Self: TupleStorageKeyPath, T1.TKey: IdentifiableType, + T2.TKey: IdentifiableType, T3.TKey: IdentifiableType, + T4.TKey: IdentifiableType, T5.TKey: IdentifiableType, + T6.TKey: IdentifiableType, T7.TKey: IdentifiableType, + T8.TKey: IdentifiableType, T9.TKey: IdentifiableType, + T10.TKey: IdentifiableType, T11.TKey: IdentifiableType, + T12.TKey: IdentifiableType, T13.TKey: IdentifiableType {} extension Tuple14: TupleStorageNKeyPath, TupleStorageKeyPath where T1: TupleStorageKeyHasherPair, T2: TupleStorageKeyHasherPair, @@ -324,14 +418,23 @@ extension Tuple14: TupleStorageNKeyPath, TupleStorageKeyPath where Data, Data, Data, Data, Data> } extension Tuple14: TupleStorageValidatableKeyPath where - Self: TupleStorageNKeyPath, T1.TKey: RuntimeDynamicValidatable, - T2.TKey: RuntimeDynamicValidatable, T3.TKey: RuntimeDynamicValidatable, - T4.TKey: RuntimeDynamicValidatable, T5.TKey: RuntimeDynamicValidatable, - T6.TKey: RuntimeDynamicValidatable, T7.TKey: RuntimeDynamicValidatable, - T8.TKey: RuntimeDynamicValidatable, T9.TKey: RuntimeDynamicValidatable, - T10.TKey: RuntimeDynamicValidatable, T11.TKey: RuntimeDynamicValidatable, - T12.TKey: RuntimeDynamicValidatable, T13.TKey: RuntimeDynamicValidatable, - T14.TKey: RuntimeDynamicValidatable {} + Self: TupleStorageNKeyPath, T1.TKey: ValidatableType, + T2.TKey: ValidatableType, T3.TKey: ValidatableType, + T4.TKey: ValidatableType, T5.TKey: ValidatableType, + T6.TKey: ValidatableType, T7.TKey: ValidatableType, + T8.TKey: ValidatableType, T9.TKey: ValidatableType, + T10.TKey: ValidatableType, T11.TKey: ValidatableType, + T12.TKey: ValidatableType, T13.TKey: ValidatableType, + T14.TKey: ValidatableType {} +extension Tuple14: TupleStorageIdentifiableKeyPath where + Self: TupleStorageKeyPath, T1.TKey: IdentifiableType, + T2.TKey: IdentifiableType, T3.TKey: IdentifiableType, + T4.TKey: IdentifiableType, T5.TKey: IdentifiableType, + T6.TKey: IdentifiableType, T7.TKey: IdentifiableType, + T8.TKey: IdentifiableType, T9.TKey: IdentifiableType, + T10.TKey: IdentifiableType, T11.TKey: IdentifiableType, + T12.TKey: IdentifiableType, T13.TKey: IdentifiableType, + T14.TKey: IdentifiableType {} extension Tuple15: TupleStorageNKeyPath, TupleStorageKeyPath where T1: TupleStorageKeyHasherPair, T2: TupleStorageKeyHasherPair, @@ -355,11 +458,20 @@ extension Tuple15: TupleStorageNKeyPath, TupleStorageKeyPath where Data, Data, Data, Data, Data, Data> } extension Tuple15: TupleStorageValidatableKeyPath where - Self: TupleStorageNKeyPath, T1.TKey: RuntimeDynamicValidatable, - T2.TKey: RuntimeDynamicValidatable, T3.TKey: RuntimeDynamicValidatable, - T4.TKey: RuntimeDynamicValidatable, T5.TKey: RuntimeDynamicValidatable, - T6.TKey: RuntimeDynamicValidatable, T7.TKey: RuntimeDynamicValidatable, - T8.TKey: RuntimeDynamicValidatable, T9.TKey: RuntimeDynamicValidatable, - T10.TKey: RuntimeDynamicValidatable, T11.TKey: RuntimeDynamicValidatable, - T12.TKey: RuntimeDynamicValidatable, T13.TKey: RuntimeDynamicValidatable, - T14.TKey: RuntimeDynamicValidatable, T15.TKey: RuntimeDynamicValidatable {} + Self: TupleStorageNKeyPath, T1.TKey: ValidatableType, + T2.TKey: ValidatableType, T3.TKey: ValidatableType, + T4.TKey: ValidatableType, T5.TKey: ValidatableType, + T6.TKey: ValidatableType, T7.TKey: ValidatableType, + T8.TKey: ValidatableType, T9.TKey: ValidatableType, + T10.TKey: ValidatableType, T11.TKey: ValidatableType, + T12.TKey: ValidatableType, T13.TKey: ValidatableType, + T14.TKey: ValidatableType, T15.TKey: ValidatableType {} +extension Tuple15: TupleStorageIdentifiableKeyPath where + Self: TupleStorageKeyPath, T1.TKey: IdentifiableType, + T2.TKey: IdentifiableType, T3.TKey: IdentifiableType, + T4.TKey: IdentifiableType, T5.TKey: IdentifiableType, + T6.TKey: IdentifiableType, T7.TKey: IdentifiableType, + T8.TKey: IdentifiableType, T9.TKey: IdentifiableType, + T10.TKey: IdentifiableType, T11.TKey: IdentifiableType, + T12.TKey: IdentifiableType, T13.TKey: IdentifiableType, + T14.TKey: IdentifiableType, T15.TKey: IdentifiableType {} diff --git a/Sources/Substrate/Types/Tuples+Identifiable.swift b/Sources/Substrate/Types/Tuples+Identifiable.swift new file mode 100644 index 0000000..f322695 --- /dev/null +++ b/Sources/Substrate/Types/Tuples+Identifiable.swift @@ -0,0 +1,220 @@ +// +// Tuples+Identifiable.swift +// +// +// Created by Yehor Popovych on 03/09/2023. +// + +import Foundation +import Tuples + +public protocol IdentifiableTuple: IdentifiableType, SomeTuple { + static var elementsFieldDefinifions: [TypeDefinition.Field] { get } +} + +public extension SomeTuple0 { + @inlinable + static var definition: TypeDefinition { .void } + @inlinable + static var elementsFieldDefinifions: [TypeDefinition.Field] { [] } +} + +public extension ListTuple where Self: IdentifiableTuple, + DroppedLast: IdentifiableTuple, Last: IdentifiableType +{ + @inlinable + static var definition: TypeDefinition { + .composite(fields: elementsFieldDefinifions) + } + + @inlinable + static var elementsFieldDefinifions: [TypeDefinition.Field] { + DroppedLast.elementsFieldDefinifions + [.init(nil, Last.definition)] + } +} + +extension Tuple0: IdentifiableTuple { + public static func validate(runtime: any Runtime, + type: NetworkType.Info) -> Result + { + definition.validate(runtime: runtime, type: type.type) + } +} + +extension Tuple1: IdentifiableTuple, IdentifiableType where T1: IdentifiableType { + public static func validate(runtime: Runtime, + type: NetworkType.Info) -> Result + { + definition.validate(runtime: runtime, type: type.type) + } +} + +extension Tuple2: IdentifiableTuple, IdentifiableType where + T1: IdentifiableType, T2: IdentifiableType +{ + public static func validate(runtime: Runtime, + type: NetworkType.Info) -> Result + { + definition.validate(runtime: runtime, type: type.type) + } +} + +extension Tuple3: IdentifiableTuple, IdentifiableType where + T1: IdentifiableType, T2: IdentifiableType, T3: IdentifiableType +{ + public static func validate(runtime: Runtime, + type: NetworkType.Info) -> Result + { + definition.validate(runtime: runtime, type: type.type) + } +} + +extension Tuple4: IdentifiableTuple, IdentifiableType where + T1: IdentifiableType, T2: IdentifiableType, T3: IdentifiableType, + T4: IdentifiableType +{ + public static func validate(runtime: Runtime, + type: NetworkType.Info) -> Result + { + definition.validate(runtime: runtime, type: type.type) + } +} + +extension Tuple5: IdentifiableTuple, IdentifiableType where + T1: IdentifiableType, T2: IdentifiableType, T3: IdentifiableType, + T4: IdentifiableType, T5: IdentifiableType +{ + public static func validate(runtime: Runtime, + type: NetworkType.Info) -> Result + { + definition.validate(runtime: runtime, type: type.type) + } +} + +extension Tuple6: IdentifiableTuple, IdentifiableType where + T1: IdentifiableType, T2: IdentifiableType, T3: IdentifiableType, + T4: IdentifiableType, T5: IdentifiableType, T6: IdentifiableType +{ + public static func validate(runtime: Runtime, + type: NetworkType.Info) -> Result + { + definition.validate(runtime: runtime, type: type.type) + } +} + +extension Tuple7: IdentifiableTuple, IdentifiableType where + T1: IdentifiableType, T2: IdentifiableType, T3: IdentifiableType, + T4: IdentifiableType, T5: IdentifiableType, T6: IdentifiableType, + T7: IdentifiableType +{ + public static func validate(runtime: Runtime, + type: NetworkType.Info) -> Result + { + definition.validate(runtime: runtime, type: type.type) + } +} + +extension Tuple8: IdentifiableTuple, IdentifiableType where + T1: IdentifiableType, T2: IdentifiableType, T3: IdentifiableType, + T4: IdentifiableType, T5: IdentifiableType, T6: IdentifiableType, + T7: IdentifiableType, T8: IdentifiableType +{ + public static func validate(runtime: Runtime, + type: NetworkType.Info) -> Result + { + definition.validate(runtime: runtime, type: type.type) + } +} + +extension Tuple9: IdentifiableTuple, IdentifiableType where + T1: IdentifiableType, T2: IdentifiableType, T3: IdentifiableType, + T4: IdentifiableType, T5: IdentifiableType, T6: IdentifiableType, + T7: IdentifiableType, T8: IdentifiableType, T9: IdentifiableType +{ + public static func validate(runtime: Runtime, + type: NetworkType.Info) -> Result + { + definition.validate(runtime: runtime, type: type.type) + } +} + +extension Tuple10: IdentifiableTuple, IdentifiableType where + T1: IdentifiableType, T2: IdentifiableType, T3: IdentifiableType, + T4: IdentifiableType, T5: IdentifiableType, T6: IdentifiableType, + T7: IdentifiableType, T8: IdentifiableType, T9: IdentifiableType, + T10: IdentifiableType +{ + public static func validate(runtime: Runtime, + type: NetworkType.Info) -> Result + { + definition.validate(runtime: runtime, type: type.type) + } +} + +extension Tuple11: IdentifiableTuple, IdentifiableType where + T1: IdentifiableType, T2: IdentifiableType, T3: IdentifiableType, + T4: IdentifiableType, T5: IdentifiableType, T6: IdentifiableType, + T7: IdentifiableType, T8: IdentifiableType, T9: IdentifiableType, + T10: IdentifiableType, T11: IdentifiableType +{ + public static func validate(runtime: Runtime, + type: NetworkType.Info) -> Result + { + definition.validate(runtime: runtime, type: type.type) + } +} + +extension Tuple12: IdentifiableTuple, IdentifiableType where + T1: IdentifiableType, T2: IdentifiableType, T3: IdentifiableType, + T4: IdentifiableType, T5: IdentifiableType, T6: IdentifiableType, + T7: IdentifiableType, T8: IdentifiableType, T9: IdentifiableType, + T10: IdentifiableType, T11: IdentifiableType, T12: IdentifiableType +{ + public static func validate(runtime: Runtime, + type: NetworkType.Info) -> Result + { + definition.validate(runtime: runtime, type: type.type) + } +} + +extension Tuple13: IdentifiableTuple, IdentifiableType where + T1: IdentifiableType, T2: IdentifiableType, T3: IdentifiableType, + T4: IdentifiableType, T5: IdentifiableType, T6: IdentifiableType, + T7: IdentifiableType, T8: IdentifiableType, T9: IdentifiableType, + T10: IdentifiableType, T11: IdentifiableType, T12: IdentifiableType, + T13: IdentifiableType +{ + public static func validate(runtime: Runtime, + type: NetworkType.Info) -> Result + { + definition.validate(runtime: runtime, type: type.type) + } +} + +extension Tuple14: IdentifiableTuple, IdentifiableType where + T1: IdentifiableType, T2: IdentifiableType, T3: IdentifiableType, + T4: IdentifiableType, T5: IdentifiableType, T6: IdentifiableType, + T7: IdentifiableType, T8: IdentifiableType, T9: IdentifiableType, + T10: IdentifiableType, T11: IdentifiableType, T12: IdentifiableType, + T13: IdentifiableType, T14: IdentifiableType +{ + public static func validate(runtime: Runtime, + type: NetworkType.Info) -> Result + { + definition.validate(runtime: runtime, type: type.type) + } +} + +extension Tuple15: IdentifiableTuple, IdentifiableType where + T1: IdentifiableType, T2: IdentifiableType, T3: IdentifiableType, + T4: IdentifiableType, T5: IdentifiableType, T6: IdentifiableType, + T7: IdentifiableType, T8: IdentifiableType, T9: IdentifiableType, + T10: IdentifiableType, T11: IdentifiableType, T12: IdentifiableType, + T13: IdentifiableType, T14: IdentifiableType, T15: IdentifiableType +{ + public static func validate(runtime: Runtime, + type: NetworkType.Info) -> Result + { + definition.validate(runtime: runtime, type: type.type) + } +} diff --git a/Sources/Substrate/Types/Tuples+Validatable.swift b/Sources/Substrate/Types/Tuples+Validatable.swift new file mode 100644 index 0000000..b222df9 --- /dev/null +++ b/Sources/Substrate/Types/Tuples+Validatable.swift @@ -0,0 +1,164 @@ +// +// Tuples+Validatable.swift +// +// +// Created by Yehor Popovych on 03/09/2023. +// + +import Foundation +import Tuples + +public protocol ValidatableTuple: ValidatableType, SomeTuple { + static func validate(type: NetworkType.Info, + fields: [NetworkType.Info], + runtime: any Runtime) -> Result +} + +public extension SomeTuple0 { + @inlinable + static func validate(runtime: Runtime, + type: NetworkType.Info) -> Result + { + type.type.isEmpty(runtime) ? + .success(()) : .failure(.wrongType(for: Self.self, got: type.type, + reason: "Expected ()")) + } + + static func validate(type: NetworkType.Info, + fields: [NetworkType.Info], + runtime: any Runtime) -> Result + { + fields.count == 0 ? .success(()) : .failure(.wrongValuesCount(for: Self.self, + expected: 0, + in: type.type)) + } +} + +public extension ListTuple where DroppedLast: ValidatableTuple, Last: ValidatableType { + @inlinable + static func validate(runtime: Runtime, + type: NetworkType.Info) -> Result + { + switch type.type.definition { + case .array(count: let count, of: let id): + guard count == self.count else { + return .failure(.wrongValuesCount(for: Self.self, + expected: self.count, in: type.type)) + } + guard let chType = runtime.resolve(type: id) else { + return .failure(.typeNotFound(for: Self.self, id: id)) + } + return validate(type: type, + fields: Array(repeating: chType.i(id), count: Int(count)), + runtime: runtime) + case .tuple(components: let ids): + guard ids.count == self.count else { + return .failure(.wrongValuesCount(for: Self.self, + expected: self.count, in: type.type)) + } + return ids.resultMap { id in + guard let chType = runtime.resolve(type: id) else { + return .failure(.typeNotFound(for: Self.self, id: id)) + } + return .success(chType.i(id)) + }.flatMap { validate(type: type, fields: $0, runtime: runtime) } + case .composite(fields: let fields): + guard fields.count == self.count else { + return .failure(.wrongValuesCount(for: Self.self, + expected: self.count, in: type.type)) + } + return fields.resultMap { field in + guard let chType = runtime.resolve(type: field.type) else { + return .failure(.typeNotFound(for: Self.self, id: field.type)) + } + return .success(chType.i(field.type)) + }.flatMap { validate(type: type, fields: $0, runtime: runtime) } + default: + return .failure(.wrongType(for: Self.self, + got: type.type, + reason: "Isn't composite")) + } + } + + static func validate(type: NetworkType.Info, + fields: [NetworkType.Info], + runtime: any Runtime) -> Result + { + guard fields.count == self.count else { + return .failure(.wrongValuesCount(for: Self.self, + expected: self.count, in: type.type)) + } + var fields = fields + let ltype = fields.removeLast() + return DroppedLast.validate(type: type, fields: fields, runtime: runtime).flatMap { + Last.validate(runtime: runtime, type: ltype) + } + } +} + +extension Tuple0: ValidatableTuple {} + +extension Tuple1: ValidatableTuple, ValidatableType where T1: ValidatableType {} + +extension Tuple2: ValidatableTuple, ValidatableType where + T1: ValidatableType, T2: ValidatableType {} + +extension Tuple3: ValidatableTuple, ValidatableType where + T1: ValidatableType, T2: ValidatableType, T3: ValidatableType {} + +extension Tuple4: ValidatableTuple, ValidatableType where + T1: ValidatableType, T2: ValidatableType, T3: ValidatableType, T4: ValidatableType {} + +extension Tuple5: ValidatableTuple, ValidatableType where + T1: ValidatableType, T2: ValidatableType, T3: ValidatableType, T4: ValidatableType, + T5: ValidatableType {} + +extension Tuple6: ValidatableTuple, ValidatableType where + T1: ValidatableType, T2: ValidatableType, T3: ValidatableType, T4: ValidatableType, + T5: ValidatableType, T6: ValidatableType {} + +extension Tuple7: ValidatableTuple, ValidatableType where + T1: ValidatableType, T2: ValidatableType, T3: ValidatableType, T4: ValidatableType, + T5: ValidatableType, T6: ValidatableType, T7: ValidatableType {} + +extension Tuple8: ValidatableTuple, ValidatableType where + T1: ValidatableType, T2: ValidatableType, T3: ValidatableType, T4: ValidatableType, + T5: ValidatableType, T6: ValidatableType, T7: ValidatableType, T8: ValidatableType {} + +extension Tuple9: ValidatableTuple, ValidatableType where + T1: ValidatableType, T2: ValidatableType, T3: ValidatableType, T4: ValidatableType, + T5: ValidatableType, T6: ValidatableType, T7: ValidatableType, T8: ValidatableType, + T9: ValidatableType {} + +extension Tuple10: ValidatableTuple, ValidatableType where + T1: ValidatableType, T2: ValidatableType, T3: ValidatableType, T4: ValidatableType, + T5: ValidatableType, T6: ValidatableType, T7: ValidatableType, T8: ValidatableType, + T9: ValidatableType, T10: ValidatableType {} + +extension Tuple11: ValidatableTuple, ValidatableType where + T1: ValidatableType, T2: ValidatableType, T3: ValidatableType, T4: ValidatableType, + T5: ValidatableType, T6: ValidatableType, T7: ValidatableType, T8: ValidatableType, + T9: ValidatableType, T10: ValidatableType, T11: ValidatableType {} + +extension Tuple12: ValidatableTuple, ValidatableType where + T1: ValidatableType, T2: ValidatableType, T3: ValidatableType, T4: ValidatableType, + T5: ValidatableType, T6: ValidatableType, T7: ValidatableType, T8: ValidatableType, + T9: ValidatableType, T10: ValidatableType, T11: ValidatableType, T12: ValidatableType {} + +extension Tuple13: ValidatableTuple, ValidatableType where + T1: ValidatableType, T2: ValidatableType, T3: ValidatableType, T4: ValidatableType, + T5: ValidatableType, T6: ValidatableType, T7: ValidatableType, T8: ValidatableType, + T9: ValidatableType, T10: ValidatableType, T11: ValidatableType, T12: ValidatableType, + T13: ValidatableType {} + +extension Tuple14: ValidatableTuple, ValidatableType where + T1: ValidatableType, T2: ValidatableType, T3: ValidatableType, T4: ValidatableType, + T5: ValidatableType, T6: ValidatableType, T7: ValidatableType, T8: ValidatableType, + T9: ValidatableType, T10: ValidatableType, T11: ValidatableType, T12: ValidatableType, + T13: ValidatableType, T14: ValidatableType {} + +extension Tuple15: ValidatableTuple, ValidatableType where + T1: ValidatableType, T2: ValidatableType, T3: ValidatableType, T4: ValidatableType, + T5: ValidatableType, T6: ValidatableType, T7: ValidatableType, T8: ValidatableType, + T9: ValidatableType, T10: ValidatableType, T11: ValidatableType, T12: ValidatableType, + T13: ValidatableType, T14: ValidatableType, T15: ValidatableType {} diff --git a/Sources/Substrate/Types/Types+Identifiable.swift b/Sources/Substrate/Types/Types+Identifiable.swift new file mode 100644 index 0000000..272ea60 --- /dev/null +++ b/Sources/Substrate/Types/Types+Identifiable.swift @@ -0,0 +1,121 @@ +// +// Types+Identifiable.swift +// +// +// Created by Yehor Popovych on 02/09/2023. +// + +import Foundation +import ScaleCodec +import Numberick + +public extension CaseIterable where Self: IdentifiableType { + @inlinable + static var definition: TypeDefinition { + .variant(variants: allCases.enumerated().map { (idx, cs) in + .e(UInt8(idx), String(describing: cs).uppercasedFirst) + }) + } +} + +extension UInt8: IdentifiableType { + @inlinable + public static var definition: TypeDefinition { .primitive(is: .u8) } +} +extension UInt16: IdentifiableType { + @inlinable + public static var definition: TypeDefinition { .primitive(is: .u16) } +} +extension UInt32: IdentifiableType { + @inlinable + public static var definition: TypeDefinition { .primitive(is: .u32) } +} +extension UInt64: IdentifiableType { + @inlinable + public static var definition: TypeDefinition { .primitive(is: .u64) } +} +extension UInt: IdentifiableType { + @inlinable + public static var definition: TypeDefinition { + MemoryLayout.size == 4 ? .primitive(is: .u32) : .primitive(is: .u64) + } +} +extension NBKDoubleWidth: IdentifiableType { + @inlinable + public static var definition: TypeDefinition { + switch bitWidth { + case 128: return isSigned ? .primitive(is: .i128) : .primitive(is: .u128) + case 256: return isSigned ? .primitive(is: .i256) : .primitive(is: .u256) + default: preconditionFailure("Bad big integer size: \(bitWidth)") + } + } +} +extension Int8: IdentifiableType { + @inlinable + public static var definition: TypeDefinition { .primitive(is: .i8) } +} +extension Int16: IdentifiableType { + @inlinable + public static var definition: TypeDefinition { .primitive(is: .i16) } +} +extension Int32: IdentifiableType { + @inlinable + public static var definition: TypeDefinition { .primitive(is: .i32) } +} +extension Int64: IdentifiableType { + @inlinable + public static var definition: TypeDefinition { .primitive(is: .i64) } +} +extension Int: IdentifiableType { + @inlinable + public static var definition: TypeDefinition { + MemoryLayout.size == 4 ? .primitive(is: .i32) : .primitive(is: .i64) + } +} + +extension Bool: IdentifiableType { + @inlinable + public static var definition: TypeDefinition { .primitive(is: .bool) } +} + +extension String: IdentifiableType { + @inlinable + public static var definition: TypeDefinition { .primitive(is: .str) } +} + +extension Data: IdentifiableType { + @inlinable + public static var definition: TypeDefinition { .data } +} + +extension Compact: IdentifiableType where T: IdentifiableType { + @inlinable + public static var definition: TypeDefinition { + .compact(of: T.definition) + } +} + +extension Character: IdentifiableType { + @inlinable + public static var definition: TypeDefinition { .primitive(is: .char) } +} + +extension Array: IdentifiableType where Element: IdentifiableType { + @inlinable + public static var definition: TypeDefinition { .sequence(of: Element.definition) } +} + +extension Optional: IdentifiableType where Wrapped: IdentifiableType { + @inlinable + public static var definition: TypeDefinition { + .variant(variants: [.e(0, "None"), .s(1, "Some", Wrapped.definition)]) + } +} + +extension Either: IdentifiableType where Left: IdentifiableType, Right: IdentifiableType { + @inlinable + public static var definition: TypeDefinition { + .variant(variants: [.s(0, "Ok", Right.definition), + .s(1, "Err", Left.definition)]) + } +} diff --git a/Sources/Substrate/Types/Types+Validatable.swift b/Sources/Substrate/Types/Types+Validatable.swift new file mode 100644 index 0000000..00ed81d --- /dev/null +++ b/Sources/Substrate/Types/Types+Validatable.swift @@ -0,0 +1,116 @@ +// +// Types+Validatable.swift +// +// +// Created by Yehor Popovych on 03/09/2023. +// + +import Foundation +import ScaleCodec + +extension Value: ValidatableType { + @inlinable + public static func validate(runtime: any Runtime, + type: NetworkType.Info) -> Result + { + .success(()) + } +} + +extension Data: ValidatableType { + public static func validate(runtime: Runtime, + type: NetworkType.Info) -> Result + { + guard type.type.asBytes(runtime) != nil else { + return .failure(.wrongType(for: Self.self, got: type.type, + reason: "Isn't byte array")) + } + return .success(()) + } +} + +extension Compact: ValidatableType { + public static func validate(runtime: any Runtime, + type: NetworkType.Info) -> Result + { + guard let compact = type.type.asCompact(runtime) else { + return .failure(.wrongType(for: Self.self, got: type.type, + reason: "Isn't Compact")) + } + if let primitive = compact.asPrimitive(runtime) { // Compact + guard let bits = primitive.isUInt else { + return .failure(.wrongType(for: Self.self, got: type.type, + reason: "Type isn't UInt")) + } + guard bits <= T.compactBitWidth else { + return .failure(.wrongType(for: Self.self, got: type.type, + reason: "UInt\(bits) can't be stored in \(T.self)")) + } + } else if compact.isEmpty(runtime) { // Compact<()> + guard T.compactBitWidth == 0 else { + return .failure(.wrongType(for: Self.self, got: type.type, + reason: "Compact<\(T.self)> != Compact<()>")) + } + } else { // Unknown Compact + return .failure(.wrongType(for: Self.self, got: type.type, + reason: "Unknown Compact")) + } + return .success(()) + } +} + +extension Array: ValidatableType where Element: ValidatableType { + public static func validate(runtime: Runtime, + type: NetworkType.Info) -> Result + { + switch type.type.flatten(runtime).definition { + case .array(count: _, of: let eType), .sequence(of: let eType): + return Element.validate(runtime: runtime, type: eType).map{_ in} + case .composite(fields: let fields): + for field in fields { + switch Element.validate(runtime: runtime, type: field.type) { + case .success(_): continue + case .failure(let err): return .failure(err) + } + } + return .success(()) + case .tuple(components: let ids): + for id in ids { + switch Element.validate(runtime: runtime, type: id) { + case .success(_): continue + case .failure(let err): return .failure(err) + } + } + return .success(()) + default: + return .failure(.wrongType(for: Self.self, got: type.type, + reason: "Isn't Array")) + } + } +} + +extension Optional: ValidatableType where Wrapped: ValidatableType { + public static func validate(runtime: Runtime, + type: NetworkType.Info) -> Result + { + guard let field = type.type.asOptional(runtime) else { + return .failure(.wrongType(for: Self.self, got: type.type, + reason: "Isn't Optional")) + } + return Wrapped.validate(runtime: runtime, type: field.type).map{_ in} + } +} + +extension Either: ValidatableType where Left: ValidatableType, Right: ValidatableType { + public static func validate(runtime: Runtime, + type: NetworkType.Info) -> Result + { + guard let result = type.type.asResult(runtime) else { + return .failure(.wrongType(for: Self.self, got: type.type, + reason: "Isn't Result")) + } + return Left.validate(runtime: runtime, type: result.err.type).flatMap { _ in + Right.validate(runtime: runtime, type: result.ok.type).map{_ in} + } + } +} diff --git a/Sources/Substrate/Types/ValidatableType.swift b/Sources/Substrate/Types/ValidatableType.swift new file mode 100644 index 0000000..9ba1dd8 --- /dev/null +++ b/Sources/Substrate/Types/ValidatableType.swift @@ -0,0 +1,207 @@ +// +// ValidatableType.swift +// +// +// Created by Yehor Popovych on 21/08/2023. +// + +import Foundation +import ScaleCodec +import Numberick + +public protocol ValidatableType { + static func validate(runtime: any Runtime, + type: NetworkType.Info) -> Result + + static func validate(runtime: any Runtime, + type id: NetworkType.Id) -> Result +} + +public enum TypeError: Error { + case typeNotFound(for: String, id: NetworkType.Id) + case runtimeTypeLookupFailed(for: String, type: String, reason: Error) + case wrongType(for: String, got: NetworkType, reason: String) + case wrongValuesCount(for: String, expected: Int, in: NetworkType) + case fieldNotFound(for: String, field: String, in: NetworkType) + case variantNotFound(for: String, variant: String, in: NetworkType) + case wrongVariantIndex(for: String, variant: String, + expected: UInt8, in: NetworkType) + case wrongVariantFieldsCount(for: String, variant: String, + expected: Int, in: NetworkType) +} + +public extension ValidatableType { + @inlinable + static func validate(runtime: any Runtime, + type id: NetworkType.Id) -> Result + { + guard let type = runtime.resolve(type: id) else { + return .failure(.typeNotFound(for: Self.self, id: id)) + } + return validate(runtime: runtime, type: id.i(type)).map{type.i(id)} + } +} + +public protocol ComplexValidatableType: ValidatableType { + associatedtype TypeInfo + + static func typeInfo(runtime: any Runtime, + type: NetworkType.Info) -> Result + static func validate(info: TypeInfo, type: NetworkType.Info, + runtime: any Runtime) -> Result +} + +public extension ComplexValidatableType { + @inlinable static func validate(runtime: any Runtime, + type: NetworkType.Info) -> Result + { + typeInfo(runtime: runtime, type: type).flatMap { + validate(info: $0, type: type, runtime: runtime) + } + } +} + +public protocol ComplexStaticValidatableType: ComplexValidatableType { + associatedtype ChildTypes + static var childTypes: ChildTypes { get } +} + +public protocol CompositeValidatableType: ComplexValidatableType + where TypeInfo == [(name: String?, type: NetworkType.Info)] {} + +public extension CompositeValidatableType { + static func typeInfo(runtime: any Runtime, + type: NetworkType.Info) -> Result + { + switch type.type.flatten(runtime).definition { + case .composite(fields: let fs): + return fs.resultMap { + guard let type = runtime.resolve(type: $0.type) else { + return .failure(.typeNotFound(for: Self.self, id: $0.type)) + } + return .success(($0.name, type.i($0.type))) + } + case .tuple(components: let ids): + return ids.resultMap { + guard let type = runtime.resolve(type: $0) else { + return .failure(.typeNotFound(for: Self.self, id: $0)) + } + return .success((nil, $0.i(type))) + } + default: return .success([(nil, type)]) // Composite wrapper over simple type + } + } +} + +public protocol CompositeStaticValidatableType: CompositeValidatableType, + ComplexStaticValidatableType + where ChildTypes == [ValidatableType.Type] {} + +public extension CompositeStaticValidatableType { + static func validate(info: TypeInfo, type: NetworkType.Info, + runtime: any Runtime) -> Result + { + let ourFields = childTypes + guard ourFields.count == info.count else { + return .failure(.wrongValuesCount(for: Self.self, + expected: ourFields.count, + in: type.type)) + } + return zip(ourFields, info).voidErrorMap { field, info in + field.validate(runtime: runtime, type: info.type) + } + } +} + +public protocol VariantValidatableType: ComplexValidatableType + where TypeInfo == [(index: UInt8, name: String, + fields: [(name: String?, type: NetworkType.Info)])] {} + +public extension VariantValidatableType { + static func typeInfo(runtime: any Runtime, + type: NetworkType.Info) -> Result + { + guard case .variant(variants: let variants) = type.type.definition else { + return .failure(.wrongType(for: Self.self, got: type.type, + reason: "Type isn't Variant")) + } + return variants.resultMap { variant in + return variant.fields.resultMap { field in + guard let type = runtime.resolve(type: field.type) else { + return .failure(.typeNotFound(for: Self.self, id: field.type)) + } + return .success((field.name, type.i(field.type))) + }.map { (variant.index, variant.name, $0) } + } + } +} + +public protocol VariantStaticValidatableType: VariantValidatableType, + ComplexStaticValidatableType + where ChildTypes == [(index: UInt8, name: String, fields: [ValidatableType.Type])] {} + +public extension VariantStaticValidatableType { + static func validate(info: TypeInfo, type: NetworkType.Info, + runtime: any Runtime) -> Result + { + let ourVariants = childTypes + guard ourVariants.count == info.count else { + return .failure(.wrongValuesCount(for: Self.self, + expected: ourVariants.count, + in: type.type)) + } + let variantsDict = Dictionary(uniqueKeysWithValues: info.map { ($0.name, $0) }) + return ourVariants.voidErrorMap { variant in + guard let inVariant = variantsDict[variant.name] else { + return .failure(.variantNotFound(for: Self.self, variant: variant.name, in: type.type)) + } + guard variant.index == inVariant.index else { + return .failure(.wrongVariantIndex(for: Self.self, variant: variant.name, + expected: variant.index, in: type.type)) + } + guard variant.fields.count == inVariant.fields.count else { + return .failure(.wrongVariantFieldsCount(for: Self.self, variant: variant.name, + expected: variant.fields.count, in: type.type)) + } + return zip(variant.fields, inVariant.fields).voidErrorMap { field, info in + field.validate(runtime: runtime, type: info.type) + } + } + } +} + +public extension TypeError { + static func typeNotFound(for type: Any.Type, id: NetworkType.Id) -> Self { + .typeNotFound(for: String(describing: type), id: id) + } + static func runtimeTypeLookupFailed(for t: Any.Type, type: String, reason: Error) -> Self { + .runtimeTypeLookupFailed(for: String(describing: t), type: type, reason: reason) + } + + static func wrongType(for type: Any.Type, got: NetworkType, reason: String) -> Self { + .wrongType(for: String(describing: type), got: got, reason: reason) + } + static func wrongValuesCount(for type: Any.Type, expected: Int, in: NetworkType) -> Self { + .wrongValuesCount(for: String(describing: type), expected: expected, in: `in`) + } + + static func fieldNotFound(for type: Any.Type, field: String, in: NetworkType) -> Self { + .fieldNotFound(for: String(describing: type), field: field, in: `in`) + } + + static func variantNotFound(for type: Any.Type, variant: String, in: NetworkType) -> Self { + .variantNotFound(for: String(describing: type), variant: variant, in: `in`) + } + + static func wrongVariantFieldsCount(for type: Any.Type, variant: String, + expected: Int, in: NetworkType) -> Self { + .wrongVariantFieldsCount(for: String(describing: type), variant: variant, + expected: expected, in: `in`) + } + + static func wrongVariantIndex(for type: Any.Type, variant: String, + expected: UInt8, in: NetworkType) -> Self { + .wrongVariantIndex(for: String(describing: type), variant: variant, + expected: expected, in: `in`) + } +} diff --git a/Sources/Substrate/Types/Value/Value+Representable.swift b/Sources/Substrate/Types/Value/Value+Representable.swift index 246ec2a..9351506 100644 --- a/Sources/Substrate/Types/Value/Value+Representable.swift +++ b/Sources/Substrate/Types/Value/Value+Representable.swift @@ -9,43 +9,6 @@ import Foundation import ScaleCodec import Numberick -public enum ValueRepresentableError: Error { - case typeNotFound(NetworkType.Id) - case wrongType(got: NetworkType, for: String) - case wrongValuesCount(in: NetworkType, expected: Int, for: String) - case nonVariant(map: [String: ValueRepresentable]) - case variantNotFound(name: String, in: NetworkType) - case fieldNotFound(name: String, in: NetworkType) - case keyNotFound(key: String, in: [String: ValueRepresentable]) - case runtimeTypeLookupFailed(name: String, reason: Error) - case typeIdMismatch(got: NetworkType.Id, has: NetworkType.Id) - - public init(_ error: DynamicValidationError) { - switch error { - case .typeIdMismatch(got: let gid, has: let hid): - self = .typeIdMismatch(got: gid, has: hid) - case .typeNotFound(let tid): - self = .typeNotFound(tid) - case .variantNotFound(name: let n, in: let t): - self = .variantNotFound(name: n, in: t) - case .wrongType(got: let t, for: let n): - self = .wrongType(got: t, for: n) - case .wrongValuesCount(in: let t, expected: let c, for: let n): - self = .wrongValuesCount(in: t, expected: c, for: n) - case .fieldNotFound(name: let n, in: let t): - self = .fieldNotFound(name: n, in: t) - case .runtimeTypeLookupFailed(name: let n, reason: let e): - self = .runtimeTypeLookupFailed(name: n, reason: e) - } - } -} - -public extension Result where Failure == DynamicValidationError { - @inlinable func getValueError() throws -> Success { - try mapError { ValueRepresentableError($0) }.get() - } -} - public extension FixedWidthInteger { func asValue() -> Value { Self.isSigned ? .int(Int256(self)) : .uint(UInt256(self)) @@ -53,10 +16,11 @@ public extension FixedWidthInteger { func asValue(runtime: any Runtime, type: NetworkType.Id) throws -> Value { guard let info = runtime.resolve(type: type) else { - throw ValueRepresentableError.typeNotFound(type) + throw TypeError.typeNotFound(for: Self.self, id: type) } guard let primitive = info.asPrimitive(runtime), primitive.isAnyInt != nil else { - throw ValueRepresentableError.wrongType(got: info, for: String(describing: Self.self)) + throw TypeError.wrongType(for: Self.self, got: info, + reason: "Not an integer") } return Self.isSigned ? .int(Int256(self), type) : .uint(UInt256(self), type) } @@ -78,13 +42,7 @@ extension Value: ValueRepresentable, VoidValueRepresentable { public func asValue() -> Value { mapContext{_ in} } public func asValue(runtime: any Runtime, type: NetworkType.Id) throws -> Value { - if let sself = self as? Value { - guard sself.context == type else { - throw ValueRepresentableError.typeIdMismatch(got: type, has: sself.context) - } - return sself - } - return mapContext{_ in type} + mapContext{_ in type} } } @@ -92,7 +50,7 @@ extension Bool: ValueRepresentable, VoidValueRepresentable { public func asValue() -> Value { .bool(self) } public func asValue(runtime: any Runtime, type: NetworkType.Id) throws -> Value { - try Self.validate(runtime: runtime, type: type).getValueError() + let _ = try Self.validate(runtime: runtime, type: type).get() return .bool(self, type) } } @@ -101,7 +59,7 @@ extension String: ValueRepresentable, VoidValueRepresentable { public func asValue() -> Value { .string(self) } public func asValue(runtime: any Runtime, type: NetworkType.Id) throws -> Value { - try Self.validate(runtime: runtime, type: type).getValueError() + let _ = try Self.validate(runtime: runtime, type: type).get() return .string(self, type) } } @@ -111,15 +69,13 @@ extension Data: ValueRepresentable, VoidValueRepresentable { public func asValue(runtime: any Runtime, type: NetworkType.Id) throws -> Value { guard let info = runtime.resolve(type: type) else { - throw ValueRepresentableError.typeNotFound(type) + throw TypeError.typeNotFound(for: Self.self, id: type) } guard let count = info.asBytes(runtime) else { - throw ValueRepresentableError.wrongType(got: info, for: String(describing: Self.self)) + throw TypeError.wrongType(for: Self.self, got: info, reason: "Isn't bytes") } guard count == 0 || self.count == count else { - throw ValueRepresentableError.wrongValuesCount( - in: info, expected: self.count, for: String(describing: Self.self) - ) + throw TypeError.wrongValuesCount(for: Self.self, expected: self.count, in: info) } return .bytes(self, type) } @@ -130,10 +86,10 @@ extension Compact: ValueRepresentable, VoidValueRepresentable { public func asValue(runtime: any Runtime, type: NetworkType.Id) throws -> Value { guard let info = runtime.resolve(type: type) else { - throw ValueRepresentableError.typeNotFound(type) + throw TypeError.typeNotFound(for: Self.self, id: type) } guard info.asCompact(runtime) != nil else { - throw ValueRepresentableError.wrongType(got: info, for: String(describing: Self.self)) + throw TypeError.wrongType(for: Self.self, got: info, reason: "Isn't Compact") } return .uint(UInt256(value.uint), type) } @@ -144,10 +100,10 @@ extension Character: ValueRepresentable, VoidValueRepresentable { public func asValue(runtime: any Runtime, type: NetworkType.Id) throws -> Value { guard let info = runtime.resolve(type: type) else { - throw ValueRepresentableError.typeNotFound(type) + throw TypeError.typeNotFound(for: Self.self, id: type) } guard let primitive = info.asPrimitive(runtime), primitive.isChar else { - throw ValueRepresentableError.wrongType(got: info, for: String(describing: Self.self)) + throw TypeError.wrongType(for: Self.self, got: info, reason: "Isn't char") } return .char(self, type) } @@ -156,15 +112,14 @@ extension Character: ValueRepresentable, VoidValueRepresentable { public extension Collection where Element == ValueRepresentable { func asValue(runtime: any Runtime, type: NetworkType.Id) throws -> Value { guard let info = runtime.resolve(type: type) else { - throw ValueRepresentableError.typeNotFound(type) + throw TypeError.typeNotFound(for: Self.self, id: type) } - let flat = info.flatten(runtime) - switch flat.definition { + switch info.flatten(runtime).definition { case .array(count: let count, of: let eType): guard count == self.count else { - throw ValueRepresentableError.wrongValuesCount(in: info, - expected: self.count, - for: String(describing: Self.self)) + throw TypeError.wrongValuesCount(for: Self.self, + expected: self.count, + in: info) } fallthrough case .sequence(of: let eType): @@ -172,26 +127,24 @@ public extension Collection where Element == ValueRepresentable { return .sequence(mapped, type) case .composite(fields: let fields): let arr = try asCompositeValue(runtime: runtime, - type: .init(id: type, type: info), + type: info, types: fields.map { $0.type }) return .sequence(arr, type) case .tuple(components: let ids): let arr = try asCompositeValue(runtime: runtime, - type: .init(id: type, type: info), + type: info, types: ids) return .sequence(arr, type) default: - throw ValueRepresentableError.wrongType(got: info, for: String(describing: Self.self)) + throw TypeError.wrongType(for: Self.self, got: info, reason: "Isn't collection") } } func asCompositeValue(runtime: any Runtime, - type: NetworkType.Info, + type: NetworkType, types: [NetworkType.Id]) throws -> [Value] { guard types.count == count else { - throw ValueRepresentableError.wrongValuesCount(in: type.type, - expected: count, - for: String(describing: Self.self)) + throw TypeError.wrongValuesCount(for: Self.self, expected: count, in: type) } return try zip(self, types).map { try $0.asValue(runtime: runtime, type: $1) } } @@ -207,28 +160,28 @@ extension Array: VoidValueRepresentable where Element == VoidValueRepresentable extension Dictionary: ValueRepresentable where Key == String, Value == ValueRepresentable { public func asValue(runtime: Runtime, type: NetworkType.Id) throws -> Substrate.Value { guard let info = runtime.resolve(type: type) else { - throw ValueRepresentableError.typeNotFound(type) + throw TypeError.typeNotFound(for: Self.self, id: type) } - let flat = info.flatten(runtime) - switch flat.definition { + switch info.flatten(runtime).definition { case .composite(fields: let fields): let types = try fields.map { field in guard let key = field.name else { - throw ValueRepresentableError.wrongType(got: info, - for: String(describing: Self.self)) + throw TypeError.wrongType(for: Self.self, got: info, + reason: "field name is nil") } return (key, field.type) } let map = try asCompositeValue(runtime: runtime, - type: .init(id: type, type: info), + type: info, types: Dictionary<_,_>(uniqueKeysWithValues: types)) return .map(map, type) case .variant(variants: let variants): guard count == 1 else { - throw ValueRepresentableError.nonVariant(map: self) + throw TypeError.wrongType(for: Self.self, got: info, + reason: "count != 1 for variant") } guard let variant = variants.first(where: { $0.name == first!.key }) else { - throw ValueRepresentableError.variantNotFound(name: first!.key, in: info) + throw TypeError.variantNotFound(for: Self.self, variant: first!.key, in: info) } if variant.fields.count == 1 { let val = try first!.value.asValue(runtime: runtime, @@ -242,42 +195,41 @@ extension Dictionary: ValueRepresentable where Key == String, Value == ValueRepr switch first!.value { case let arr as Array: let seq = try arr.asCompositeValue(runtime: runtime, - type: .init(id: type, type: info), + type: info, types: variant.fields.map{$0.type}) return .variant(name: variant.name, values: seq, type) case let map as Dictionary: let types = try variant.fields.map { field in guard let key = field.name else { - throw ValueRepresentableError.wrongType(got: info, - for: String(describing: Self.self)) + throw TypeError.wrongType(for: Self.self, got: info, + reason: "field name is nil") } return (key, field.type) } let map = try map.asCompositeValue(runtime: runtime, - type: .init(id: type, type: info), + type: info, types: Dictionary<_,_>(uniqueKeysWithValues: types)) return .variant(name: variant.name, fields: map, type) - default: throw ValueRepresentableError.wrongType(got: info, - for: String(describing: Self.self)) + default: throw TypeError.wrongType(for: Self.self, got: info, + reason: "Can't be a variant type") } default: - throw ValueRepresentableError.wrongType(got: info, for: String(describing: Self.self)) + throw TypeError.wrongType(for: Self.self, got: info, + reason: "Isn't map") } } func asCompositeValue( runtime: any Runtime, - type: NetworkType.Info, + type: NetworkType, types: [String: NetworkType.Id]) throws -> [String: Substrate.Value] { guard types.count == count else { - throw ValueRepresentableError.wrongValuesCount(in: type.type, - expected: count, - for: String(describing: Self.self)) + throw TypeError.wrongValuesCount(for: Self.self, expected: count, in: type) } let pairs = try types.map { field in guard let value = self[field.key] else { - throw ValueRepresentableError.keyNotFound(key: field.key, in: self) + throw TypeError.fieldNotFound(for: Self.self, field: field.key, in: type) } return try (field.key, value.asValue(runtime: runtime, type: field.value)) } @@ -294,10 +246,10 @@ extension Dictionary: VoidValueRepresentable where Key: StringProtocol, Value == extension Optional: ValueRepresentable where Wrapped == ValueRepresentable { public func asValue(runtime: Runtime, type: NetworkType.Id) throws -> Value { guard let info = runtime.resolve(type: type) else { - throw ValueRepresentableError.typeNotFound(type) + throw TypeError.typeNotFound(for: Self.self, id: type) } guard let field = info.asOptional(runtime) else { - throw ValueRepresentableError.wrongType(got: info, for: String(describing: Self.self)) + throw TypeError.wrongType(for: Self.self, got: info, reason: "Isn't Optional") } switch self { case .some(let val): @@ -320,10 +272,10 @@ extension Optional: VoidValueRepresentable where Wrapped == VoidValueRepresentab extension Either: ValueRepresentable where Left == ValueRepresentable, Right == ValueRepresentable { public func asValue(runtime: Runtime, type: NetworkType.Id) throws -> Value { guard let info = runtime.resolve(type: type) else { - throw ValueRepresentableError.typeNotFound(type) + throw TypeError.typeNotFound(for: Self.self, id: type) } guard let result = info.asResult(runtime) else { - throw ValueRepresentableError.wrongType(got: info, for: String(describing: Self.self)) + throw TypeError.wrongType(for: Self.self, got: info, reason: "Isn't Result") } switch self { case .left(let err): diff --git a/Sources/Substrate/Utils/Nothing.swift b/Sources/Substrate/Utils/Nothing.swift index b536ee7..b17a2e6 100644 --- a/Sources/Substrate/Utils/Nothing.swift +++ b/Sources/Substrate/Utils/Nothing.swift @@ -39,7 +39,7 @@ extension Nothing: ScaleCodec.Codable, RuntimeCodable { extension Nothing: ValueRepresentable { public func asValue(runtime: Runtime, type: NetworkType.Id) throws -> Value { - try Self.validate(runtime: runtime, type: type).getValueError() + let _ = try Self.validate(runtime: runtime, type: type).get() return .nil(type) } } @@ -48,18 +48,8 @@ extension Nothing: VoidValueRepresentable { public func asValue() -> Value { .nil } } -extension Nothing: RuntimeDynamicValidatable { - public static func validate(runtime: Runtime, - type id: NetworkType.Id) -> Result - { - guard let info = runtime.resolve(type: id) else { - return .failure(.typeNotFound(id)) - } - guard info.isEmpty(runtime) else { - return .failure(.wrongType(got: info, for: "()")) - } - return .success(()) - } +extension Nothing: IdentifiableType { + @inlinable public static var definition: TypeDefinition { .void } } // Somehow substrate has Compact<()> type diff --git a/Sources/Substrate/Utils/SS58.swift b/Sources/Substrate/Utils/SS58.swift index cb08d46..bee966c 100644 --- a/Sources/Substrate/Utils/SS58.swift +++ b/Sources/Substrate/Utils/SS58.swift @@ -209,7 +209,7 @@ public struct SS58 { } public static func hash(data: Data) -> Data { - HBlake2b512.instance.hash(data: Self.prefix + data) + HBlake2b512.instance.hash(data: Self.prefix + data).raw } public static func checksumLength(for dataLength: Int, prefix: Int) throws -> Int { diff --git a/Tests/SubstrateTests/StorageKeysTests.swift b/Tests/SubstrateTests/StorageKeysTests.swift index 62bbc62..2bdbf6e 100644 --- a/Tests/SubstrateTests/StorageKeysTests.swift +++ b/Tests/SubstrateTests/StorageKeysTests.swift @@ -103,22 +103,22 @@ final class StorageKeysTests: XCTestCase { func testFixedMapTupleStorageKey() throws { let runtime = try self.runtime() let value = UInt256(123456) << UInt(140) - let key = try TupleKey>>(value, runtime: runtime) + let key = try TupleKey>>(value, runtime: runtime) let prefix = keyPrefix(key: key) - let valHash = try hashValue(value, runtime: runtime, in: HBlake2b512.self) + let valHash = try hashValue(value, runtime: runtime, in: HBlake2b256.self) // Encode XCTAssertEqual(key.prefix.hex(), prefix.hex()) XCTAssertEqual(key.pathHash.hex(), valHash.hex()) XCTAssertEqual(key.hash.hex(), (prefix + valHash).hex()) // Decode let decoded = try runtime.decode(from: prefix + valHash, - TupleKey>>.self) + TupleKey>>.self) XCTAssertEqual(decoded.hash.hex(), key.hash.hex()) // Decode fail XCTAssertThrowsError(try runtime.decode(from: prefix + Data(repeating: 0, count: valHash.count - 1), - TupleKey>>.self)) + TupleKey>>.self)) // Iterator - let iterator = TupleKey>>.TIterator() + let iterator = TupleKey>>.TIterator() XCTAssertEqual(iterator.hash.hex(), prefix.hex()) var decoder = runtime.decoder(with: prefix + valHash) let iterDecoded = try iterator.decode(keyFrom: &decoder, runtime: runtime) @@ -406,7 +406,7 @@ final class StorageKeysTests: XCTestCase { XCTAssertEqual(TL(mapIterDecoded.keys), TL(key.keys)) } - struct TupleKey: TupleStorageKey { + struct TupleKey: TupleStorageKey, IdentifiableFrameType { typealias TPath = Path typealias TParams = Path.TKeys.STuple typealias TValue = UInt32 @@ -418,16 +418,17 @@ final class StorageKeysTests: XCTestCase { init(path: TPath) { self.path = path } } - struct PlainKey: PlainStorageKey { + struct PlainKey: PlainStorageKey, IdentifiableFrameType { typealias TValue = String static var pallet: String { "Key" } static var name: String { "Plain" } init() {} } - struct FixedMapKey: MapStorageKey { + struct FixedMapKey: MapStorageKey, IdentifiableFrameType { typealias TKH = FKH typealias TBaseParams = Void + typealias TypeInfo = StorageKeyTypeInfo typealias TParams = TKH.TKey typealias TValue = String @@ -439,7 +440,8 @@ final class StorageKeysTests: XCTestCase { } - struct ConcatMapKey: MapStorageKey { + struct ConcatMapKey: MapStorageKey, IdentifiableFrameType { + typealias TypeInfo = StorageKeyTypeInfo typealias TKH = CKH typealias TBaseParams = Void typealias TParams = TKH.TKey @@ -452,9 +454,9 @@ final class StorageKeysTests: XCTestCase { static var name: String { "ConcatMapKey" } } - struct FixedDMapKey: DoubleMapStorageKey { + struct FixedDMapKey: DoubleMapStorageKey, IdentifiableFrameType { typealias TKH1 = FKH - typealias TKH2 = FKH, HBlake2b512> + typealias TKH2 = FKH, HBlake2b256> typealias TBaseParams = Void typealias TParams = (TKH1.TKey, TKH2.TKey) typealias TValue = String @@ -472,7 +474,7 @@ final class StorageKeysTests: XCTestCase { } - struct ConcatDMapKey: DoubleMapStorageKey { + struct ConcatDMapKey: DoubleMapStorageKey, IdentifiableFrameType { typealias TKH1 = CKH, HXX64Concat> typealias TKH2 = CKH typealias TBaseParams = Void