Skip to content

Commit

Permalink
dynamic types refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
ypopovych committed Jun 22, 2023
1 parent 202091d commit e560b1a
Show file tree
Hide file tree
Showing 19 changed files with 435 additions and 202 deletions.
2 changes: 1 addition & 1 deletion Sources/Substrate/Block/AnyBlockHeader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public struct AnyBlockHeader<THasher: FixedHasher>: SomeBlockHeader {
public init(from decoder: Decoder) throws {
self._runtime = decoder.runtime
var container = ValueDecodingContainer(decoder)
let type = try _runtime.blockHeaderType
let type = try _runtime.types.blockHeader
self.type = type
let value = try Value<RuntimeTypeId>(from: &container, as: type.id, runtime: _runtime)
guard let map = value.map else {
Expand Down
78 changes: 23 additions & 55 deletions Sources/Substrate/Block/Event.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ public protocol StaticEvent: Event {
static var pallet: String { get }
static var name: String { get }

init(params: [Value<RuntimeTypeId>]) throws
init(paramsFrom decoder: ScaleDecoder, runtime: Runtime) throws
}

Expand All @@ -37,56 +36,6 @@ public extension StaticEvent {
}
try self.init(paramsFrom: decoder, runtime: runtime)
}

init(paramsFrom decoder: ScaleDecoder, runtime: Runtime) throws {
guard let type = runtime.resolve(eventType: Self.pallet) else {
throw EventDecodingError.noEventsInPallet(pallet: Self.pallet)
}
let value = try Value(from: decoder, as: type.id, runtime: runtime)
switch (value.value, type.type.definition) {
case (.variant(let valvar), .variant(variants: let variants)):
guard Self.name == valvar.name else {
throw EventDecodingError.foundWrongEvent(found: (name: valvar.name, pallet: Self.pallet),
expected: (name: Self.name, pallet: Self.pallet))
}
try self.init(params: value, info: variants.first { $0.name == Self.name }!)
default: throw EventDecodingError.decodedNonVariantValue(value, type.id)
}
}

init(params: Value<RuntimeTypeId>, info: RuntimeTypeVariantItem) throws {
switch params.value {
case .sequence(let fields):
try self.init(params: fields)
case .variant(.sequence(name: let name, values: let fields)):
guard Self.name == name else {
throw EventDecodingError.foundWrongEvent(found: (name: name, pallet: Self.pallet),
expected: (name: Self.name, pallet: Self.pallet))
}
try self.init(params: fields)
case .map(let fields):
let ordered = try info.fields.map {
guard let field = fields[$0.name!] else {
throw EventDecodingError.fieldNotFound(name: $0.name!)
}
return field
}
try self.init(params: ordered)
case .variant(.map(name: let name, fields: let fields)):
guard Self.name == name else {
throw EventDecodingError.foundWrongEvent(found: (name: name, pallet: Self.pallet),
expected: (name: Self.name, pallet: Self.pallet))
}
let ordered = try info.fields.map {
guard let field = fields[$0.name!] else {
throw EventDecodingError.fieldNotFound(name: $0.name!)
}
return field
}
try self.init(params: ordered)
default: throw EventDecodingError.decodedNonVariantValue(params, params.context)
}
}
}

public struct AnyEvent: Event, CustomStringConvertible {
Expand All @@ -95,12 +44,16 @@ public struct AnyEvent: Event, CustomStringConvertible {

public let params: Value<RuntimeTypeId>
public let info: RuntimeTypeVariantItem
public let runtime: any Runtime

public init(name: String, pallet: String, params: Value<RuntimeTypeId>, info: RuntimeTypeVariantItem) {
public init(name: String, pallet: String, params: Value<RuntimeTypeId>,
info: RuntimeTypeVariantItem, runtime: any Runtime)
{
self.pallet = pallet
self.name = name
self.params = params
self.info = info
self.runtime = runtime
}

public init(from decoder: ScaleDecoder, runtime: Runtime) throws {
Expand All @@ -120,12 +73,14 @@ public struct AnyEvent: Event, CustomStringConvertible {
self.init(name: name,
pallet: pallet,
params: Value(value: .sequence(values), context: value.context),
info: variants.first { $0.name == name }!)
info: variants.first { $0.name == name }!,
runtime: runtime)
case .variant(.map(name: let name, fields: let fields)):
self.init(name: name,
pallet: pallet,
params: Value(value: .map(fields), context: value.context),
info: variants.first { $0.name == name }!)
info: variants.first { $0.name == name }!,
runtime: runtime)
default: throw EventDecodingError.decodedNonVariantValue(value, type.id)
}
}
Expand All @@ -135,7 +90,20 @@ public struct AnyEvent: Event, CustomStringConvertible {
throw EventDecodingError.foundWrongEvent(found: (E.name, E.pallet),
expected: (name, pallet))
}
return try E(params: params, info: info)
let pIndex = runtime.resolve(palletIndex: pallet)!
let encoder = runtime.encoder()
let value: Value<RuntimeTypeId>
try encoder.encode(pIndex)
switch params.value {
case .map(let fields):
value = .variant(name: name, fields: fields, params.context)
case .sequence(let vals):
value = .variant(name: name, values: vals, params.context)
default:
value = params
}
try value.encode(in: encoder, runtime: runtime)
return try E(from: runtime.decoder(with: encoder.output), runtime: runtime)
}

public var description: String {
Expand Down
4 changes: 2 additions & 2 deletions Sources/Substrate/Client/RpcClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import JsonRPC

public struct RpcClient<RC: Config, CL: RpcCallableClient & RuntimeHolder> {
public let client: CL
public let rpcMethods: LazyAsyncProperty<Set<String>>
public let rpcMethods: LazyAsyncThrowingProperty<Set<String>>

public init(client: CL) {
self.client = client
self.rpcMethods = LazyAsyncProperty {
self.rpcMethods = LazyAsyncThrowingProperty {
try await Self._rpcMethods(client: client)
}
}
Expand Down
15 changes: 11 additions & 4 deletions Sources/Substrate/Config/Config.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,33 @@ public protocol Config {
associatedtype TExtrinsicPayment: ValueRepresentable & Default
associatedtype TBlockEvents: SomeBlockEvents
associatedtype TExtrinsicFailureEvent: SomeExtrinsicFailureEvent<TDispatchError>
associatedtype TDispatchError: SomeDispatchError
associatedtype TDispatchError: ApiError
associatedtype TTransactionValidityError: ApiError
associatedtype TDispatchInfo: ScaleRuntimeDynamicDecodable
associatedtype TFeeDetails: ScaleRuntimeDynamicDecodable
associatedtype TTransactionStatus: SomeTransactionStatus<TBlock.THeader.THasher.THash>
associatedtype TSystemProperties: SystemProperties
associatedtype TRuntimeVersion: RuntimeVersion
associatedtype TTransactionValidityError: Decodable & Error

associatedtype TStorageChangeSet: SomeStorageChangeSet

associatedtype TTransactionPaymentQueryInfoRuntimeCall: SomeTransactionPaymentQueryInfoRuntimeCall<TDispatchInfo>
associatedtype TTransactionPaymentFeeDetailsRuntimeCall: SomeTransactionPaymentFeeDetailsRuntimeCall<TFeeDetails>
associatedtype TMetadataAtVersionRuntimeCall: SomeMetadataAtVersionRuntimeCall
associatedtype TMetadataVersionsRuntimeCall: SomeMetadataVersionsRuntimeCall

associatedtype TExtrinsicManager: ExtrinsicManager<Self>

func eventsStorageKey(runtime: any Runtime) throws -> any StorageKey<TBlockEvents>
func blockHeaderType(metadata: Metadata) throws -> RuntimeTypeInfo
func blockHeaderType(metadata: any Metadata) throws -> RuntimeTypeInfo
func extrinsicTypes(
metadata: Metadata
) throws -> (addr: RuntimeTypeInfo, signature: RuntimeTypeInfo, extra: RuntimeTypeInfo)
func hasher(metadata: Metadata) throws -> THasher
func dispatchInfoType(metadata: any Metadata) throws -> RuntimeTypeInfo
func feeDetailsType(metadata: any Metadata) throws -> RuntimeTypeInfo
func dispatchErrorType(metadata: any Metadata) throws -> RuntimeTypeInfo
func transactionValidityErrorType(metadata: any Metadata) throws -> RuntimeTypeInfo
func hasher(metadata: any Metadata) throws -> THasher
func extrinsicManager() throws -> TExtrinsicManager
func encoder() -> ScaleEncoder
func decoder(data: Data) -> ScaleDecoder
Expand Down
132 changes: 107 additions & 25 deletions Sources/Substrate/Config/DynamicRuntime.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,29 +27,44 @@ public struct DynamicRuntime: Config {
public typealias TFeeDetails = Value<RuntimeTypeId>
public typealias TDispatchInfo = Value<RuntimeTypeId>
public typealias TDispatchError = AnyDispatchError
public typealias TTransactionValidityError = AnyTransactionValidityError
public typealias TExtrinsicFailureEvent = System.Events.ExtrinsicFailure<TDispatchError>
public typealias TBlockEvents = BlockEvents<EventRecord<THasher.THash>>
public typealias TRuntimeVersion = AnyRuntimeVersion
public typealias TTransactionValidityError = SerializableValue
public typealias TStorageChangeSet = StorageChangeSet<THasher.THash>

public typealias TMetadataAtVersionRuntimeCall = Api.Metadata.MetadataAtVersion
public typealias TMetadataVersionsRuntimeCall = Api.Metadata.MetadataVersions
public typealias TTransactionPaymentQueryInfoRuntimeCall = Api.TransactionPayment.QueryInfo<TDispatchInfo>
public typealias TTransactionPaymentFeeDetailsRuntimeCall = Api.TransactionPayment.QueryFeeDetails<TFeeDetails>

public enum Error: Swift.Error {
case headerTypeNotFound(String)
case typeNotFound(name: String, selector: Set<String>)
case hashTypeNotFound(header: RuntimeTypeInfo)
case extrinsicInfoNotFound
case unknownHashName(String)
}

public let extrinsicExtensions: [DynamicExtrinsicExtension]
public let headerTypeName: String
public let headerSelector: Set<String>
public let dispatchInfoSelector: Set<String>
public let dispatchErrorSelector: Set<String>
public let transactionValidityErrorSelector: Set<String>
public let feeDetailsSelector: Set<String>

public init(extrinsicExtensions: [DynamicExtrinsicExtension] = Self.allExtensions,
headerTypeName: String = "sp_runtime.generic.header.Header") {
headerSelector: [String] = ["Header"],
dispatchInfoSelector: [String] = ["DispatchInfo"],
dispatchErrorSelector: [String] = ["DispatchError"],
transactionValidityErrorSelector: [String] = ["TransactionValidityError"],
feeDetailsSelector: [String] = ["FeeDetails"])
{
self.extrinsicExtensions = extrinsicExtensions
self.headerTypeName = headerTypeName
self.headerSelector = Set(headerSelector)
self.dispatchInfoSelector = Set(dispatchInfoSelector)
self.dispatchErrorSelector = Set(dispatchErrorSelector)
self.transactionValidityErrorSelector = Set(transactionValidityErrorSelector)
self.feeDetailsSelector = Set(feeDetailsSelector)
}

public func eventsStorageKey(runtime: any Runtime) throws -> any StorageKey<TBlockEvents> {
Expand All @@ -75,9 +90,8 @@ public struct DynamicRuntime: Config {
}

public func blockHeaderType(metadata: Metadata) throws -> RuntimeTypeInfo {
let path = headerTypeName.split(separator: ".").map { String($0) }
guard let type = metadata.resolve(type: path) else {
throw Error.headerTypeNotFound(headerTypeName)
guard let type = metadata.search(type: {headerSelector.isSubset(of: $0)}) else {
throw Error.typeNotFound(name: "header", selector: headerSelector)
}
return type
}
Expand Down Expand Up @@ -109,6 +123,34 @@ public struct DynamicRuntime: Config {
extra: RuntimeTypeInfo(id: extraTypeId, type: extraType))
}

public func dispatchInfoType(metadata: any Metadata) throws -> RuntimeTypeInfo {
guard let type = metadata.search(type: {dispatchInfoSelector.isSubset(of: $0)}) else {
throw Error.typeNotFound(name: "dispatchInfo", selector: dispatchInfoSelector)
}
return type
}

public func dispatchErrorType(metadata: any Metadata) throws -> RuntimeTypeInfo {
guard let type = metadata.search(type: {dispatchErrorSelector.isSubset(of: $0)}) else {
throw Error.typeNotFound(name: "dispatchError", selector: dispatchErrorSelector)
}
return type
}

public func transactionValidityErrorType(metadata: any Metadata) throws -> RuntimeTypeInfo {
guard let type = metadata.search(type: {transactionValidityErrorSelector.isSubset(of: $0)}) else {
throw Error.typeNotFound(name: "transactionValidityError", selector: transactionValidityErrorSelector)
}
return type
}

public func feeDetailsType(metadata: any Metadata) throws -> RuntimeTypeInfo {
guard let type = metadata.search(type: {feeDetailsSelector.isSubset(of: $0)}) else {
throw Error.typeNotFound(name: "feeDetails", selector: feeDetailsSelector)
}
return type
}

public static let allExtensions: [DynamicExtrinsicExtension] = [
DynamicCheckSpecVersionExtension(),
DynamicCheckTxVersionExtension(),
Expand All @@ -134,7 +176,7 @@ public extension DynamicRuntime {

public init(_ params: Void) throws {}

public func encodeParams(in encoder: ScaleCodec.ScaleEncoder) throws {}
public func encodeParams(in encoder: ScaleEncoder) throws {}
}

public struct MetadataAtVersion: SomeMetadataAtVersionRuntimeCall {
Expand Down Expand Up @@ -165,28 +207,71 @@ public extension DynamicRuntime {

public static let name = "Metadata"
}

public struct TransactionPayment {
public struct QueryInfo<DI: ScaleRuntimeDynamicDecodable>: SomeTransactionPaymentQueryInfoRuntimeCall {
public typealias TReturn = DI

public let extrinsic: Data

public init(extrinsic: Data) {
self.extrinsic = extrinsic
}

public func encodeParams(in encoder: ScaleEncoder, runtime: Runtime) throws {
try encoder.encode(extrinsic).encode(UInt32(extrinsic.count))
}

public func decode(returnFrom decoder: ScaleCodec.ScaleDecoder, runtime: Runtime) throws -> DI {
try TReturn(from: decoder, runtime: runtime) { runtime in
try runtime.types.dispatchInfo.id
}
}

public static var method: String { "query_info" }
public static var api: String { TransactionPayment.name }
}

public struct QueryFeeDetails<FD: ScaleRuntimeDynamicDecodable>:
SomeTransactionPaymentFeeDetailsRuntimeCall
{
public typealias TReturn = FD

public let extrinsic: Data

public init(extrinsic: Data) {
self.extrinsic = extrinsic
}

public func encodeParams(in encoder: ScaleEncoder, runtime: Runtime) throws {
try encoder.encode(extrinsic).encode(UInt32(extrinsic.count))
}

public func decode(returnFrom decoder: ScaleCodec.ScaleDecoder, runtime: Runtime) throws -> FD {
try TReturn(from: decoder, runtime: runtime) { runtime in
try runtime.types.feeDetails.id
}
}

public static var method: String { "query_fee_details" }
public static var api: String { TransactionPayment.name }
}

public static let name = "TransactionPaymentApi"
}
}

struct System {
public struct Events {
public struct ExtrinsicFailure<Err: SomeDispatchError>: SomeExtrinsicFailureEvent {
public struct ExtrinsicFailure<Err: ApiError>: SomeExtrinsicFailureEvent {
public typealias Err = Err
public static var pallet: String { System.name }
public static var name: String { "ExtrinsicFailure" }

public let error: Value<RuntimeTypeId>
public let error: Err

public init(params: [Value<RuntimeTypeId>]) throws {
guard params.count == 1, let err = params.first else {
throw ValueInitializableError<RuntimeTypeId>.wrongValuesCount(in: .sequence(params),
expected: 1,
for: Self.name)
}
self.error = err
}

public func asError() throws -> Err {
try Err(value: error)
public init(paramsFrom decoder: ScaleDecoder, runtime: Runtime) throws {
self.error = try Err(from: decoder, runtime: runtime)
}
}
}
Expand All @@ -206,6 +291,3 @@ public extension DynamicRuntime {
public static let name = "System"
}
}

// Should be removed
extension SerializableValue: Error {}

0 comments on commit e560b1a

Please sign in to comment.