From ce4ab7c1cee21c77296ad35509be02b571477070 Mon Sep 17 00:00:00 2001 From: Qiwei Yang Date: Sat, 9 Nov 2024 23:09:05 +0800 Subject: [PATCH] many state updates --- .../Config/ProtocolConfig+Preset.swift | 3 ++ .../Blockchain/Config/ProtocolConfig.swift | 29 +++++++--- .../RuntimeProtocols/Accumulation.swift | 10 ++++ .../RuntimeProtocols/Guaranteeing.swift | 54 +++++++++++++++---- .../Blockchain/RuntimeProtocols/Runtime.swift | 9 ++-- .../Blockchain/State/State+Genesis.swift | 2 +- .../Sources/Blockchain/State/State.swift | 32 ++++++++++- .../Sources/Blockchain/State/StateKeys.swift | 30 +++++++++++ .../Sources/Blockchain/State/StateLayer.swift | 22 ++++++++ .../Blockchain/Types/RecentHistory.swift | 14 ++--- .../Blockchain/Types/RefinementContext.swift | 20 +++---- .../Sources/Blockchain/Types/WorkItem.swift | 2 +- .../Sources/Blockchain/Types/WorkReport.swift | 15 ++++-- .../ExtrinsicPoolServiceTests.swift | 2 +- .../BlockchainTests/SafroleServiceTests.swift | 4 +- JAMTests/Tests/JAMTests/CodecTests.swift | 6 +-- .../Tests/JAMTests/RecentHistoryTests.swift | 14 +++-- 17 files changed, 216 insertions(+), 52 deletions(-) diff --git a/Blockchain/Sources/Blockchain/Config/ProtocolConfig+Preset.swift b/Blockchain/Sources/Blockchain/Config/ProtocolConfig+Preset.swift index c57b677a..b79d1e4c 100644 --- a/Blockchain/Sources/Blockchain/Config/ProtocolConfig+Preset.swift +++ b/Blockchain/Sources/Blockchain/Config/ProtocolConfig+Preset.swift @@ -17,6 +17,7 @@ extension Ref where T == ProtocolConfig { totalAccumulationGas: Gas(341_000_000), recentHistorySize: 8, maxWorkItems: 4, + maxDepsInWorkReport: 8, maxTicketsPerExtrinsic: 4, maxLookupAnchorAge: 14400, transferMemoSize: 128, @@ -56,6 +57,7 @@ extension Ref where T == ProtocolConfig { totalAccumulationGas: Gas(341_000_000), recentHistorySize: 8, maxWorkItems: 4, + maxDepsInWorkReport: 8, maxTicketsPerExtrinsic: 16, maxLookupAnchorAge: 14400, transferMemoSize: 128, @@ -94,6 +96,7 @@ extension Ref where T == ProtocolConfig { totalAccumulationGas: Gas(341_000_000), recentHistorySize: 8, maxWorkItems: 4, + maxDepsInWorkReport: 8, maxTicketsPerExtrinsic: 16, maxLookupAnchorAge: 14400, transferMemoSize: 128, diff --git a/Blockchain/Sources/Blockchain/Config/ProtocolConfig.swift b/Blockchain/Sources/Blockchain/Config/ProtocolConfig.swift index 036933c3..193f0cdf 100644 --- a/Blockchain/Sources/Blockchain/Config/ProtocolConfig.swift +++ b/Blockchain/Sources/Blockchain/Config/ProtocolConfig.swift @@ -46,15 +46,15 @@ public struct ProtocolConfig: Sendable, Codable, Equatable { // I = 4: The maximum amount of work items in a package. public var maxWorkItems: Int + // J = 8: The maximum sum of dependency items in a work-report. + public var maxDepsInWorkReport: Int + // K = 16: The maximum number of tickets which may be submitted in a single extrinsic. public var maxTicketsPerExtrinsic: Int // L = 14, 400: The maximum age in timeslots of the lookup anchor. public var maxLookupAnchorAge: Int - // WT = 128: The size of a transfer memo in octets. - public var transferMemoSize: Int - // N = 2: The number of ticket entries per validator. public var ticketEntriesPerValidator: Int @@ -70,16 +70,16 @@ public struct ProtocolConfig: Sendable, Codable, Equatable { // R = 10: The rotation period of validator-core assignments, in timeslots. public var coreAssignmentRotationPeriod: Int - // S = 4,000,000: The maximum size of service code in octets. - public var maxServiceCodeSize: Int - // U = 5: The period in timeslots after which reported but unavailable work may be replaced. public var preimageReplacementPeriod: Int // V = 1023: The total number of validators. public var totalNumberOfValidators: Int - // WC = 684: The basic size of our erasure-coded pieces. + // WC = 4,000,000: The maximum size of service code in octets. + public var maxServiceCodeSize: Int + + // WE = 684: The basic size of our erasure-coded pieces. public var erasureCodedPieceSize: Int // WM = 2^11: The maximum number of entries in a work-package manifest. @@ -95,6 +95,9 @@ public struct ProtocolConfig: Sendable, Codable, Equatable { // WS = 6: The size of an exported segment in erasure-coded pieces. public var erasureCodedSegmentSize: Int + // WT = 128: The size of a transfer memo in octets. + public var transferMemoSize: Int + // Y = 500: The number of slots into an epoch at which ticket-submission ends. public var ticketSubmissionEndSlot: Int @@ -125,6 +128,7 @@ public struct ProtocolConfig: Sendable, Codable, Equatable { totalAccumulationGas: Gas, recentHistorySize: Int, maxWorkItems: Int, + maxDepsInWorkReport: Int, maxTicketsPerExtrinsic: Int, maxLookupAnchorAge: Int, transferMemoSize: Int, @@ -161,6 +165,7 @@ public struct ProtocolConfig: Sendable, Codable, Equatable { self.totalAccumulationGas = totalAccumulationGas self.recentHistorySize = recentHistorySize self.maxWorkItems = maxWorkItems + self.maxDepsInWorkReport = maxDepsInWorkReport self.maxTicketsPerExtrinsic = maxTicketsPerExtrinsic self.maxLookupAnchorAge = maxLookupAnchorAge self.transferMemoSize = transferMemoSize @@ -225,6 +230,8 @@ extension ProtocolConfig { recentHistorySize: other.recentHistorySize != 0 ? other.recentHistorySize : recentHistorySize, maxWorkItems: other.maxWorkItems != 0 ? other.maxWorkItems : maxWorkItems, + maxDepsInWorkReport: other.maxDepsInWorkReport != 0 + ? other.maxDepsInWorkReport : maxDepsInWorkReport, maxTicketsPerExtrinsic: other.maxTicketsPerExtrinsic != 0 ? other.maxTicketsPerExtrinsic : maxTicketsPerExtrinsic, maxLookupAnchorAge: other.maxLookupAnchorAge != 0 @@ -307,6 +314,7 @@ extension ProtocolConfig { ) recentHistorySize = try decode(.recentHistorySize, defaultValue: 0, required: required) maxWorkItems = try decode(.maxWorkItems, defaultValue: 0, required: required) + maxDepsInWorkReport = try decode(.maxDepsInWorkReport, defaultValue: 0, required: required) maxTicketsPerExtrinsic = try decode( .maxTicketsPerExtrinsic, defaultValue: 0, required: required ) @@ -466,6 +474,13 @@ extension ProtocolConfig { } } + public enum MaxDepsInWorkReport: ReadInt { + public typealias TConfig = ProtocolConfigRef + public static func read(config: ProtocolConfigRef) -> Int { + config.value.maxDepsInWorkReport + } + } + public enum MaxTicketsPerExtrinsic: ReadInt { public typealias TConfig = ProtocolConfigRef public static func read(config: ProtocolConfigRef) -> Int { diff --git a/Blockchain/Sources/Blockchain/RuntimeProtocols/Accumulation.swift b/Blockchain/Sources/Blockchain/RuntimeProtocols/Accumulation.swift index 37f40dea..09a2fada 100644 --- a/Blockchain/Sources/Blockchain/RuntimeProtocols/Accumulation.swift +++ b/Blockchain/Sources/Blockchain/RuntimeProtocols/Accumulation.swift @@ -5,6 +5,16 @@ public enum AccumulationError: Error { case duplicatedServiceIndex } +public struct AccumulationQueueItem: Sendable, Equatable, Codable { + public var workReport: WorkReport + public var dependencies: Set + + public init(workReport: WorkReport, dependencies: Set) { + self.workReport = workReport + self.dependencies = dependencies + } +} + // accumulation output pairing public struct Commitment: Hashable { public var serviceIndex: ServiceIndex diff --git a/Blockchain/Sources/Blockchain/RuntimeProtocols/Guaranteeing.swift b/Blockchain/Sources/Blockchain/RuntimeProtocols/Guaranteeing.swift index d85bbf56..867db13f 100644 --- a/Blockchain/Sources/Blockchain/RuntimeProtocols/Guaranteeing.swift +++ b/Blockchain/Sources/Blockchain/RuntimeProtocols/Guaranteeing.swift @@ -10,9 +10,11 @@ public enum GuaranteeingError: Error { case outOfGas case invalidContext case duplicatedWorkPackage - case prerequistieNotFound + case prerequisiteNotFound case invalidResultCodeHash + case invalidServiceGas case invalidPublicKey + case invalidSegmentLookup } public protocol Guaranteeing { @@ -38,6 +40,14 @@ public protocol Guaranteeing { > { get } var recentHistory: RecentHistory { get } var offenders: Set { get } + var accumulationQueue: ConfigFixedSizeArray< + [AccumulationQueueItem], + ProtocolConfig.EpochLength + > { get } + var accumulationHistory: ConfigFixedSizeArray< + Set, + ProtocolConfig.EpochLength + > { get } func serviceAccount(index: ServiceIndex) -> ServiceAccountDetails? } @@ -102,9 +112,13 @@ extension Guaranteeing { var totalMinGasRequirement = Gas(0) + var oldLookups = [Data32: Data32]() + for guarantee in extrinsic.guarantees { let report = guarantee.workReport + oldLookups[report.packageSpecification.workPackageHash] = report.packageSpecification.segmentRoot + for credential in guarantee.credential { let isCurrent = (guarantee.timeslot / coreAssignmentRotationPeriod) == (timeslot / coreAssignmentRotationPeriod) let keys = isCurrent ? currentCoreKeys : pareviousCoreKeys @@ -146,6 +160,10 @@ extension Guaranteeing { throw .invalidResultCodeHash } + guard result.gasRatio >= acc.minAccumlateGas else { + throw .invalidServiceGas + } + totalMinGasRequirement += acc.minAccumlateGas } } @@ -154,14 +172,24 @@ extension Guaranteeing { throw .outOfGas } - let allRecentWorkReportHashes = Set(recentHistory.items.flatMap(\.workReportHashes.array)) - guard allRecentWorkReportHashes.isDisjoint(with: workReportHashes) else { + let recentWorkReportHashes: Set = Set(recentHistory.items.flatMap(\.lookup.keys)) + let accumulateHistoryReports = Set(accumulationHistory.array.flatMap { $0 }) + let accumulateQueueReports = Set(accumulationQueue.array.flatMap { $0 } + .flatMap(\.workReport.refinementContext.prerequisiteWorkPackages)) + let pendingWorkReportHashes = Set(reports.array.flatMap { $0?.workReport.refinementContext.prerequisiteWorkPackages ?? [] }) + let pipelinedWorkReportHashes = recentWorkReportHashes.union(accumulateHistoryReports).union(accumulateQueueReports) + .union(pendingWorkReportHashes) + guard pipelinedWorkReportHashes.isDisjoint(with: workReportHashes) else { throw .duplicatedWorkPackage } - let contexts = Set(extrinsic.guarantees.map(\.workReport.refinementContext)) + for item in recentHistory.items { + oldLookups.merge(item.lookup, uniquingKeysWith: { _, new in new }) + } - for context in contexts { + for guarantee in extrinsic.guarantees { + let report = guarantee.workReport + let context = report.refinementContext let history = recentHistory.items.first { $0.headerHash == context.anchor.headerHash } guard let history else { throw .invalidContext @@ -172,15 +200,21 @@ extension Guaranteeing { guard context.anchor.beefyRoot == history.mmr.hash() else { throw .invalidContext } - guard context.lokupAnchor.timeslot >= timeslot - UInt32(config.value.maxLookupAnchorAge) else { + guard context.lookupAnchor.timeslot >= timeslot - UInt32(config.value.maxLookupAnchorAge) else { throw .invalidContext } - if let prerequistieWorkPackage = context.prerequistieWorkPackage { - guard allRecentWorkReportHashes.contains(prerequistieWorkPackage) || - workReportHashes.contains(prerequistieWorkPackage) + for prerequisiteWorkPackage in context.prerequisiteWorkPackages.union(report.lookup.keys) { + guard recentWorkReportHashes.contains(prerequisiteWorkPackage) || + workReportHashes.contains(prerequisiteWorkPackage) else { - throw .prerequistieNotFound + throw .prerequisiteNotFound + } + } + + for (hash, root) in report.lookup { + guard oldLookups[hash] == root else { + throw .invalidSegmentLookup } } } diff --git a/Blockchain/Sources/Blockchain/RuntimeProtocols/Runtime.swift b/Blockchain/Sources/Blockchain/RuntimeProtocols/Runtime.swift index 4361d684..6cb3e27c 100644 --- a/Blockchain/Sources/Blockchain/RuntimeProtocols/Runtime.swift +++ b/Blockchain/Sources/Blockchain/RuntimeProtocols/Runtime.swift @@ -213,12 +213,15 @@ public final class Runtime { } public func updateRecentHistory(block: BlockRef, state newState: inout State) throws { - let workReportHashes = block.extrinsic.reports.guarantees.map(\.workReport.packageSpecification.workPackageHash) - try newState.recentHistory.update( + let lookup: [Data32: Data32] = Dictionary(uniqueKeysWithValues: block.extrinsic.reports.guarantees.map { ( + $0.workReport.packageSpecification.workPackageHash, + $0.workReport.packageSpecification.segmentRoot + ) }) + newState.recentHistory.update( headerHash: block.hash, parentStateRoot: block.header.priorStateRoot, accumulateRoot: Data32(), // TODO: calculate accumulation result - workReportHashes: ConfigLimitedSizeArray(config: config, array: workReportHashes) + lookup: lookup ) } diff --git a/Blockchain/Sources/Blockchain/State/State+Genesis.swift b/Blockchain/Sources/Blockchain/State/State+Genesis.swift index 2b60149b..7e142721 100644 --- a/Blockchain/Sources/Blockchain/State/State+Genesis.swift +++ b/Blockchain/Sources/Blockchain/State/State+Genesis.swift @@ -37,7 +37,7 @@ extension State { headerHash: block.hash, mmr: MMR([]), stateRoot: Data32(), - workReportHashes: ConfigLimitedSizeArray(config: config) + lookup: [Data32: Data32]() )) return (StateRef(state), block) diff --git a/Blockchain/Sources/Blockchain/State/State.swift b/Blockchain/Sources/Blockchain/State/State.swift index e9ce1df8..25fb309c 100644 --- a/Blockchain/Sources/Blockchain/State/State.swift +++ b/Blockchain/Sources/Blockchain/State/State.swift @@ -146,6 +146,26 @@ public struct State: Sendable { } } + // ϑ: The accumulation queue. + public var accumulationQueue: StateKeys.AccumulationQueueKey.Value.ValueType { + get { + layer.accumulationQueue + } + set { + layer.accumulationQueue = newValue + } + } + + // ξ: The accumulation history. + public var accumulationHistory: StateKeys.AccumulationHistoryKey.Value.ValueType { + get { + layer.accumulationHistory + } + set { + layer.accumulationHistory = newValue + } + } + // δ: The (prior) state of the service accounts. public subscript(serviceAccount index: ServiceIndex) -> StateKeys.ServiceAccountKey.Value.ValueType? { get { @@ -280,7 +300,7 @@ extension State: Dummy { headerHash: block.hash, mmr: MMR([]), stateRoot: Data32(), - workReportHashes: try! ConfigLimitedSizeArray(config: config) + lookup: [Data32: Data32]() )) } let safroleState: StateKeys.SafroleStateKey.Value.ValueType = SafroleState.dummy(config: config) @@ -303,6 +323,14 @@ extension State: Dummy { ) let judgements: StateKeys.JudgementsKey.Value.ValueType = JudgementsState.dummy(config: config) let activityStatistics: StateKeys.ActivityStatisticsKey.Value.ValueType = ValidatorActivityStatistics.dummy(config: config) + let accumulationQueue: StateKeys.AccumulationQueueKey.Value.ValueType = try! ConfigLimitedSizeArray( + config: config, + defaultValue: [AccumulationQueueItem]() + ) + let accumulationHistory: StateKeys.AccumulationHistoryKey.Value.ValueType = try! ConfigLimitedSizeArray( + config: config, + defaultValue: Set() + ) let kv: [(any StateKey, Codable & Sendable)] = [ (StateKeys.CoreAuthorizationPoolKey(), coreAuthorizationPool), @@ -318,6 +346,8 @@ extension State: Dummy { (StateKeys.TimeslotKey(), timeslot), (StateKeys.PrivilegedServicesKey(), privilegedServices), (StateKeys.ActivityStatisticsKey(), activityStatistics), + (StateKeys.AccumulationQueueKey(), accumulationQueue), + (StateKeys.AccumulationHistoryKey(), accumulationHistory), ] var store: [Data32: Data] = [:] diff --git a/Blockchain/Sources/Blockchain/State/StateKeys.swift b/Blockchain/Sources/Blockchain/State/StateKeys.swift index 422c474a..d601476c 100644 --- a/Blockchain/Sources/Blockchain/State/StateKeys.swift +++ b/Blockchain/Sources/Blockchain/State/StateKeys.swift @@ -235,6 +235,36 @@ public enum StateKeys { } } + public struct AccumulationQueueKey: StateKey { + public typealias Value = StateValue< + ConfigFixedSizeArray< + [AccumulationQueueItem], + ProtocolConfig.EpochLength + > + > + + public init() {} + + public func encode() -> Data32 { + constructKey(14) + } + } + + public struct AccumulationHistoryKey: StateKey { + public typealias Value = StateValue< + ConfigFixedSizeArray< + Set, + ProtocolConfig.EpochLength + > + > + + public init() {} + + public func encode() -> Data32 { + constructKey(15) + } + } + public struct ServiceAccountKey: StateKey { public typealias Value = StateOptionalValue diff --git a/Blockchain/Sources/Blockchain/State/StateLayer.swift b/Blockchain/Sources/Blockchain/State/StateLayer.swift index 487dec08..65742986 100644 --- a/Blockchain/Sources/Blockchain/State/StateLayer.swift +++ b/Blockchain/Sources/Blockchain/State/StateLayer.swift @@ -20,6 +20,8 @@ public struct StateLayer: @unchecked Sendable { StateKeys.TimeslotKey(), StateKeys.PrivilegedServicesKey(), StateKeys.ActivityStatisticsKey(), + StateKeys.AccumulationQueueKey(), + StateKeys.AccumulationHistoryKey(), ] let results = try await backend.batchRead(keys) @@ -165,6 +167,26 @@ public struct StateLayer: @unchecked Sendable { } } + // ϑ: The accumulation queue. + public var accumulationQueue: StateKeys.AccumulationQueueKey.Value.ValueType { + get { + changes[StateKeys.AccumulationQueueKey()] as! StateKeys.AccumulationQueueKey.Value.ValueType + } + set { + changes[StateKeys.AccumulationQueueKey()] = newValue + } + } + + // ξ: The accumulation history. + public var accumulationHistory: StateKeys.AccumulationHistoryKey.Value.ValueType { + get { + changes[StateKeys.AccumulationHistoryKey()] as! StateKeys.AccumulationHistoryKey.Value.ValueType + } + set { + changes[StateKeys.AccumulationHistoryKey()] = newValue + } + } + // δ: The (prior) state of the service accounts. public subscript(serviceAccount index: ServiceIndex) -> StateKeys.ServiceAccountKey.Value.ValueType? { get { diff --git a/Blockchain/Sources/Blockchain/Types/RecentHistory.swift b/Blockchain/Sources/Blockchain/Types/RecentHistory.swift index b5848f84..574a5311 100644 --- a/Blockchain/Sources/Blockchain/Types/RecentHistory.swift +++ b/Blockchain/Sources/Blockchain/Types/RecentHistory.swift @@ -12,19 +12,19 @@ public struct RecentHistory: Sendable, Equatable, Codable { // s public var stateRoot: Data32 - // p - public var workReportHashes: ConfigLimitedSizeArray + // p: work report: segment root lookup + public var lookup: [Data32: Data32] public init( headerHash: Data32, mmr: MMR, stateRoot: Data32, - workReportHashes: ConfigLimitedSizeArray + lookup: [Data32: Data32] ) { self.headerHash = headerHash self.mmr = mmr self.stateRoot = stateRoot - self.workReportHashes = workReportHashes + self.lookup = lookup } } @@ -40,7 +40,7 @@ extension RecentHistory: Dummy { headerHash: Data32(), mmr: MMR([]), stateRoot: Data32(), - workReportHashes: ConfigLimitedSizeArray(config: config) + lookup: [Data32: Data32]() )] )) } @@ -51,7 +51,7 @@ extension RecentHistory { headerHash: Data32, parentStateRoot: Data32, accumulateRoot: Data32, - workReportHashes: ConfigLimitedSizeArray + lookup: [Data32: Data32] ) { if items.count > 0 { // if this is not block #0 // write the state root of last block @@ -65,7 +65,7 @@ extension RecentHistory { headerHash: headerHash, mmr: mmr, stateRoot: Data32(), // empty and will be updated upon next block - workReportHashes: workReportHashes + lookup: lookup ) items.safeAppend(newItem) diff --git a/Blockchain/Sources/Blockchain/Types/RefinementContext.swift b/Blockchain/Sources/Blockchain/Types/RefinementContext.swift index ef0027ba..84587da2 100644 --- a/Blockchain/Sources/Blockchain/Types/RefinementContext.swift +++ b/Blockchain/Sources/Blockchain/Types/RefinementContext.swift @@ -23,7 +23,7 @@ public struct RefinementContext: Sendable, Equatable, Codable, Hashable { } } - public struct LokupAnchor: Sendable, Equatable, Codable, Hashable { + public struct LookupAnchor: Sendable, Equatable, Codable, Hashable { // l public var headerHash: Data32 // t @@ -40,15 +40,15 @@ public struct RefinementContext: Sendable, Equatable, Codable, Hashable { public var anchor: Anchor - public var lokupAnchor: LokupAnchor + public var lookupAnchor: LookupAnchor // p - public var prerequistieWorkPackage: Data32? + public var prerequisiteWorkPackages: Set - public init(anchor: Anchor, lokupAnchor: LokupAnchor, prerequistieWorkPackage: Data32?) { + public init(anchor: Anchor, lookupAnchor: LookupAnchor, prerequisiteWorkPackages: Set) { self.anchor = anchor - self.lokupAnchor = lokupAnchor - self.prerequistieWorkPackage = prerequistieWorkPackage + self.lookupAnchor = lookupAnchor + self.prerequisiteWorkPackages = prerequisiteWorkPackages } } @@ -61,11 +61,11 @@ extension RefinementContext: Dummy { stateRoot: Data32(), beefyRoot: Data32() ), - lokupAnchor: LokupAnchor( + lookupAnchor: LookupAnchor( headerHash: Data32(), timeslot: 0 ), - prerequistieWorkPackage: nil + prerequisiteWorkPackages: Set() ) } } @@ -80,7 +80,7 @@ extension RefinementContext.Anchor: EncodedSize { } } -extension RefinementContext.LokupAnchor: EncodedSize { +extension RefinementContext.LookupAnchor: EncodedSize { public var encodedSize: Int { headerHash.encodedSize + timeslot.encodedSize } @@ -92,7 +92,7 @@ extension RefinementContext.LokupAnchor: EncodedSize { extension RefinementContext: EncodedSize { public var encodedSize: Int { - anchor.encodedSize + lokupAnchor.encodedSize + prerequistieWorkPackage.encodedSize + anchor.encodedSize + lookupAnchor.encodedSize + prerequisiteWorkPackages.encodedSize } public static var encodeedSizeHint: Int? { diff --git a/Blockchain/Sources/Blockchain/Types/WorkItem.swift b/Blockchain/Sources/Blockchain/Types/WorkItem.swift index 41bfa59b..a2383ccd 100644 --- a/Blockchain/Sources/Blockchain/Types/WorkItem.swift +++ b/Blockchain/Sources/Blockchain/Types/WorkItem.swift @@ -25,7 +25,7 @@ public struct WorkItem: Sendable, Equatable, Codable { // g public var gasLimit: Gas - // i: a sequence of imported data segments i identified by the root of the segments tree and an index into it + // i: a sequence of imported data segments which identify a prior exported segment through an index public var inputs: [ImportedDataSegment] // x: a sequence of hashed of blob hashes and lengths to be introduced in this block diff --git a/Blockchain/Sources/Blockchain/Types/WorkReport.swift b/Blockchain/Sources/Blockchain/Types/WorkReport.swift index d7bbfd28..c554633a 100644 --- a/Blockchain/Sources/Blockchain/Types/WorkReport.swift +++ b/Blockchain/Sources/Blockchain/Types/WorkReport.swift @@ -18,6 +18,9 @@ public struct WorkReport: Sendable, Equatable, Codable { // o: authorization output public var authorizationOutput: Data + // l: segment-root lookup dictionary + public var lookup: [Data32: Data32] + // r: the results of the evaluation of each of the items in the package public var results: ConfigLimitedSizeArray< WorkResult, @@ -31,6 +34,7 @@ public struct WorkReport: Sendable, Equatable, Codable { authorizationOutput: Data, refinementContext: RefinementContext, packageSpecification: AvailabilitySpecifications, + lookup: [Data32: Data32], results: ConfigLimitedSizeArray ) { self.authorizerHash = authorizerHash @@ -38,6 +42,7 @@ public struct WorkReport: Sendable, Equatable, Codable { self.authorizationOutput = authorizationOutput self.refinementContext = refinementContext self.packageSpecification = packageSpecification + self.lookup = lookup self.results = results } } @@ -51,6 +56,7 @@ extension WorkReport: Dummy { authorizationOutput: Data(), refinementContext: RefinementContext.dummy(config: config), packageSpecification: AvailabilitySpecifications.dummy(config: config), + lookup: [:], results: try! ConfigLimitedSizeArray(config: config, defaultValue: WorkResult.dummy(config: config)) ) } @@ -64,9 +70,8 @@ extension WorkReport { extension WorkReport: EncodedSize { public var encodedSize: Int { - authorizerHash.encodedSize + coreIndex.encodedSize + authorizationOutput.encodedSize + refinementContext - .encodedSize + packageSpecification - .encodedSize + results.encodedSize + authorizerHash.encodedSize + coreIndex.encodedSize + authorizationOutput.encodedSize + + refinementContext.encodedSize + packageSpecification.encodedSize + lookup.encodedSize + results.encodedSize } public static var encodeedSizeHint: Int? { @@ -78,9 +83,13 @@ extension WorkReport: Validate { public enum WorkReportError: Swift.Error { case tooBig case invalidCoreIndex + case tooManyDependencies } public func validate(config: Config) throws(WorkReportError) { + guard refinementContext.prerequisiteWorkPackages.count + lookup.count <= config.value.maxDepsInWorkReport else { + throw .tooManyDependencies + } guard encodedSize <= config.value.maxEncodedWorkReportSize else { throw .tooBig } diff --git a/Blockchain/Tests/BlockchainTests/ExtrinsicPoolServiceTests.swift b/Blockchain/Tests/BlockchainTests/ExtrinsicPoolServiceTests.swift index 9822e0ae..9c993ea0 100644 --- a/Blockchain/Tests/BlockchainTests/ExtrinsicPoolServiceTests.swift +++ b/Blockchain/Tests/BlockchainTests/ExtrinsicPoolServiceTests.swift @@ -215,7 +215,7 @@ struct ExtrinsicPoolServiceTests { headerHash: newBlock.hash, mmr: MMR([]), stateRoot: Data32(), - workReportHashes: ConfigLimitedSizeArray(config: config) + lookup: [Data32: Data32]() )) } diff --git a/Blockchain/Tests/BlockchainTests/SafroleServiceTests.swift b/Blockchain/Tests/BlockchainTests/SafroleServiceTests.swift index 0f50da12..268db2d5 100644 --- a/Blockchain/Tests/BlockchainTests/SafroleServiceTests.swift +++ b/Blockchain/Tests/BlockchainTests/SafroleServiceTests.swift @@ -62,7 +62,7 @@ struct SafroleServiceTests { headerHash: newBlock.hash, mmr: MMR([]), stateRoot: Data32(), - workReportHashes: ConfigLimitedSizeArray(config: config) + lookup: [Data32: Data32]() )) } @@ -91,7 +91,7 @@ struct SafroleServiceTests { headerHash: newBlock.hash, mmr: MMR([]), stateRoot: Data32(), - workReportHashes: ConfigLimitedSizeArray(config: config) + lookup: [Data32: Data32]() )) } diff --git a/JAMTests/Tests/JAMTests/CodecTests.swift b/JAMTests/Tests/JAMTests/CodecTests.swift index 490081e2..a3c68e17 100644 --- a/JAMTests/Tests/JAMTests/CodecTests.swift +++ b/JAMTests/Tests/JAMTests/CodecTests.swift @@ -103,9 +103,9 @@ struct CodecTests { return [ "anchor": json["anchor"]!["headerHash"]!, "beefy_root": json["anchor"]!["beefyRoot"]!, - "lookup_anchor": json["lokupAnchor"]!["headerHash"]!, - "lookup_anchor_slot": json["lokupAnchor"]!["timeslot"]!, - "prerequisite": json["prerequistieWorkPackage"] ?? .null, + "lookup_anchor": json["lookupAnchor"]!["headerHash"]!, + "lookup_anchor_slot": json["lookupAnchor"]!["timeslot"]!, + "prerequisite": json["prerequisiteWorkPackages"] ?? .null, "state_root": json["anchor"]!["stateRoot"]!, ].json } diff --git a/JAMTests/Tests/JAMTests/RecentHistoryTests.swift b/JAMTests/Tests/JAMTests/RecentHistoryTests.swift index e6636ab9..ecfce568 100644 --- a/JAMTests/Tests/JAMTests/RecentHistoryTests.swift +++ b/JAMTests/Tests/JAMTests/RecentHistoryTests.swift @@ -6,11 +6,16 @@ import Utils @testable import JAMTests +struct ReportedWorkPackage: Codable { + var hash: Data32 + var exportsRoot: Data32 +} + struct RecentHistoryInput: Codable { var headerHash: Data32 var parentStateRoot: Data32 var accumulateRoot: Data32 - var workPackages: [Data32] + var workPackages: [ReportedWorkPackage] } struct RecentHisoryTestcase: Codable { @@ -30,11 +35,14 @@ struct RecentHistoryTests { let testcase = try JamDecoder.decode(RecentHisoryTestcase.self, from: testcase.data, withConfig: config) var state = testcase.preState - try state.update( + state.update( headerHash: testcase.input.headerHash, parentStateRoot: testcase.input.parentStateRoot, accumulateRoot: testcase.input.accumulateRoot, - workReportHashes: ConfigLimitedSizeArray(config: config, array: testcase.input.workPackages) + lookup: Dictionary(uniqueKeysWithValues: testcase.input.workPackages.map { ( + $0.hash, + $0.exportsRoot + ) }) ) #expect(state == testcase.postState)