Skip to content

Commit ea1b6ca

Browse files
authored
CE 134: Work-package sharing (#303)
* update issue 278 * update issues 279 * Work-package submission * update networkmanager * update more test * update workpackage hash * update networkmanager * update workpackage * update workpackpool * update NetworkManagerTest * update workpackagepoolserver * update SegmentsRootMapping * update Guaranteeing * udpate guaranteeing * shareWorkPackage adjust * update shareWorkPackage * update local * update shareWorkPackage * update * update handleWorkPackage * update ShareWorkPackage * update Work-package sharing * update WorkPackageShare * update GuaranteeingServiceTests * no message * TODO: add more tests * update work package share
1 parent 6c4bb08 commit ea1b6ca

File tree

9 files changed

+266
-24
lines changed

9 files changed

+266
-24
lines changed

Blockchain/Sources/Blockchain/RuntimeProtocols/RuntimeEvents.swift

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,19 +55,41 @@ public enum RuntimeEvents {
5555
// When a work package is recived via CE133
5656
public struct WorkPackagesReceived: Event {
5757
public let coreIndex: CoreIndex
58-
public let workPackageRef: WorkPackageRef
58+
public let workPackage: WorkPackageRef
5959
public let extrinsics: [Data]
6060

61-
public init(coreIndex: CoreIndex, workPackageRef: WorkPackageRef, extrinsics: [Data]) {
61+
public init(coreIndex: CoreIndex, workPackage: WorkPackageRef, extrinsics: [Data]) {
6262
self.coreIndex = coreIndex
63-
self.workPackageRef = workPackageRef
63+
self.workPackage = workPackage
6464
self.extrinsics = extrinsics
6565
}
6666
}
6767

6868
// When a work package bundle is ready to shared via CE134
69-
public struct WorkPackageBundleReady: Event {
69+
public struct WorkPackageBundleShare: Event {
70+
public let coreIndex: CoreIndex
7071
public let bundle: WorkPackageBundle
72+
public let segmentsRootMappings: SegmentsRootMappings
73+
74+
public init(
75+
coreIndex: CoreIndex,
76+
bundle: WorkPackageBundle,
77+
segmentsRootMappings: SegmentsRootMappings
78+
) {
79+
self.coreIndex = coreIndex
80+
self.bundle = bundle
81+
self.segmentsRootMappings = segmentsRootMappings
82+
}
83+
}
84+
85+
// When a work package bundle is recived via CE134
86+
public struct WorkPackageBundleRecived: Event {
87+
public let workPackageHash: Data32
88+
public let edd25519Signature: Data64
89+
public init(workPackageHash: Data32, edd25519Signature: Data64) {
90+
self.workPackageHash = workPackageHash
91+
self.edd25519Signature = edd25519Signature
92+
}
7193
}
7294

7395
// When a work report is generated and ready to be distrubuted via CE135
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import Foundation
2+
import Utils
3+
4+
public struct SegmentsRootMapping: Sendable, Equatable, Codable, Hashable {
5+
public let workPackageHash: Data32
6+
public let segmentsRoot: Data32
7+
}
8+
9+
public typealias SegmentsRootMappings = [SegmentsRootMapping]

Blockchain/Sources/Blockchain/Types/WorkPackageBundle.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,15 @@ import Foundation
22
import Utils
33

44
// All the necessary data to audit a work package. Stored in audits DA
5-
public struct WorkPackageBundle: Sendable, Equatable, Codable {
5+
public struct WorkPackageBundle: Sendable, Equatable, Codable, Hashable {
66
public var workPackage: WorkPackage
77
public var extrinsic: [Data]
88
public var importSegments: [Data4104]
99
public var justifications: [Data]
10+
public init(workPackage: WorkPackage, extrinsic: [Data], importSegments: [Data4104], justifications: [Data]) {
11+
self.workPackage = workPackage
12+
self.extrinsic = extrinsic
13+
self.importSegments = importSegments
14+
self.justifications = justifications
15+
}
1016
}

Blockchain/Sources/Blockchain/Validator/GuaranteeingService.swift

Lines changed: 136 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import Utils
66
public enum GuaranteeingServiceError: Error {
77
case noAuthorizerHash
88
case invalidExports
9+
case invalidWorkPackage
10+
case invalidBundle
11+
case segmentsRootNotFound
912
}
1013

1114
public final class GuaranteeingService: ServiceBase2, @unchecked Sendable {
@@ -38,6 +41,10 @@ public final class GuaranteeingService: ServiceBase2, @unchecked Sendable {
3841
await subscribe(RuntimeEvents.WorkPackagesReceived.self, id: "GuaranteeingService.WorkPackagesReceived") { [weak self] event in
3942
try await self?.on(workPackagesReceived: event)
4043
}
44+
45+
await subscribe(RuntimeEvents.WorkPackageBundleShare.self, id: "GuaranteeingService.WorkPackageBundleShare") { [weak self] event in
46+
try await self?.on(workPackageBundle: event)
47+
}
4148
}
4249

4350
public func onSyncCompleted() async {
@@ -78,16 +85,89 @@ public final class GuaranteeingService: ServiceBase2, @unchecked Sendable {
7885
}
7986
}
8087

88+
private func on(workPackageBundle event: RuntimeEvents.WorkPackageBundleShare) async throws {
89+
try await receiveWorkPackageBundle(
90+
coreIndex: event.coreIndex,
91+
segmentsRootMappings: event.segmentsRootMappings,
92+
bundle: event.bundle
93+
)
94+
}
95+
96+
private func on(workPackageBundleReceived _: RuntimeEvents.WorkPackageBundleRecived) async throws {
97+
// TODO: check somethings
98+
}
99+
100+
// Method to receive a work package bundle
101+
private func receiveWorkPackageBundle(
102+
coreIndex _: CoreIndex,
103+
segmentsRootMappings: SegmentsRootMappings,
104+
bundle: WorkPackageBundle
105+
) async throws {
106+
// Perform basic verification
107+
guard try validateWorkPackageBundle(bundle, segmentsRootMappings: segmentsRootMappings) else {
108+
throw GuaranteeingServiceError.invalidBundle
109+
}
110+
}
111+
112+
private func validateWorkPackageBundle(
113+
_ bundle: WorkPackageBundle,
114+
segmentsRootMappings: SegmentsRootMappings
115+
) throws -> Bool {
116+
// Validate the work package authorization
117+
guard try validateAuthorization(bundle.workPackage) else {
118+
return false
119+
}
120+
121+
// Validate the segments-root mappings
122+
for mapping in segmentsRootMappings {
123+
guard try validateSegmentsRootMapping(mapping, for: bundle.workPackage) else {
124+
return false
125+
}
126+
}
127+
128+
return true
129+
}
130+
81131
private func on(workPackagesReceived event: RuntimeEvents.WorkPackagesReceived) async throws {
82-
try await refinePkg(package: event.workPackageRef)
132+
try await handleWorkPackage(coreIndex: event.coreIndex, workPackage: event.workPackage, extrinsics: event.extrinsics)
83133
}
84134

85-
private func refinePkg(package: WorkPackageRef) async throws {
135+
// handle Work Package
136+
public func handleWorkPackage(coreIndex: CoreIndex, workPackage: WorkPackageRef, extrinsics: [Data]) async throws {
137+
// Validate the work package
138+
guard try validate(workPackage: workPackage.value) else {
139+
logger.error("Invalid work package: \(workPackage)")
140+
throw GuaranteeingServiceError.invalidWorkPackage
141+
}
86142
guard let (validatorIndex, signingKey) = signingKey.value else {
87143
logger.debug("not in current validator set, skipping refine")
88144
return
89145
}
90146

147+
// check & refine
148+
let (bundle, mappings, workReport) = try await refinePkg(
149+
validatorIndex: validatorIndex,
150+
workPackage: workPackage,
151+
extrinsics: extrinsics
152+
)
153+
154+
// Share work package bundle
155+
let shareWorkBundleEvent = RuntimeEvents.WorkPackageBundleShare(
156+
coreIndex: coreIndex,
157+
bundle: bundle,
158+
segmentsRootMappings: mappings
159+
)
160+
publish(shareWorkBundleEvent)
161+
// Sign work report & work-report distribution via CE 135
162+
let payload = SigningContext.guarantee + workReport.hash().data
163+
let signature = try signingKey.sign(message: payload)
164+
let workReportEvent = RuntimeEvents.WorkReportGenerated(item: workReport, signature: signature)
165+
publish(workReportEvent)
166+
}
167+
168+
private func refinePkg(validatorIndex: ValidatorIndex, workPackage: WorkPackageRef,
169+
extrinsics: [Data]) async throws -> (WorkPackageBundle, SegmentsRootMappings, WorkReport)
170+
{
91171
let state = try await dataProvider.getState(hash: dataProvider.bestHead.hash)
92172

93173
// TODO: check for edge cases such as epoch end
@@ -96,24 +176,63 @@ public final class GuaranteeingService: ServiceBase2, @unchecked Sendable {
96176
randomness: state.value.entropyPool.t2,
97177
timeslot: state.value.timeslot + 1
98178
)
179+
// TODO: coreIndex equal with shareWorkPackage coreIndex?
99180
guard let coreIndex = currentCoreAssignment[safe: Int(validatorIndex)] else {
100181
try throwUnreachable("invalid validator index/core assignment")
101182
}
102183

103-
let workReport = try await createWorkReport(for: package, coreIndex: coreIndex)
104-
let payload = SigningContext.guarantee + workReport.hash().data
105-
let signature = try signingKey.sign(message: payload)
106-
let event = RuntimeEvents.WorkReportGenerated(item: workReport, signature: signature)
107-
publish(event)
184+
// Create work report & WorkPackageBundle
185+
return try await createWorkReport(
186+
coreIndex: coreIndex,
187+
workPackage: workPackage,
188+
extrinsics: extrinsics
189+
)
190+
}
191+
192+
private func validateSegmentsRootMapping(
193+
_: SegmentsRootMapping,
194+
for _: WorkPackage
195+
) throws -> Bool {
196+
// TODO: Implement logic to validate the segments-root mapping
197+
true // Placeholder
198+
}
199+
200+
private func validateAuthorization(_: WorkPackage) throws -> Bool {
201+
// TODO: Implement logic to validate the work package authorization
202+
true // Placeholder
203+
}
204+
205+
// TODO: Add validate func
206+
private func validate(workPackage _: WorkPackage) throws -> Bool {
207+
// 1. Check if it is possible to generate a work-report
208+
// 2. Check all import segments have been retrieved
209+
true
210+
}
211+
212+
private func retrieveImportSegments(for _: WorkPackage) async throws -> [Data4104] {
213+
// TODO: Implement retrieveImportSegments
214+
// Implement logic to retrieve imported data segments
215+
// For example, fetch from the data availability layer
216+
[] // Placeholder
217+
}
218+
219+
private func retrieveJustifications(for _: WorkPackage) async throws -> [Data] {
220+
// TODO: Implement retrieveJustifications
221+
// Implement logic to retrieve justifications for the imported segments
222+
// For example, fetch proofs from the data availability layer
223+
[] // Placeholder
108224
}
109225

110226
// workpackage -> workresult -> workreport
111-
private func createWorkReport(for workPackage: WorkPackageRef, coreIndex: CoreIndex) async throws -> WorkReport {
227+
private func createWorkReport(coreIndex: CoreIndex, workPackage: WorkPackageRef,
228+
extrinsics: [Data]) async throws -> (WorkPackageBundle, SegmentsRootMappings, WorkReport)
229+
{
112230
let state = try await dataProvider.getState(hash: dataProvider.bestHead.hash)
113231
let packageHash = workPackage.hash
114232
let corePool = state.value.coreAuthorizationPool[coreIndex]
115233
let authorizerHash = try corePool.array.first.unwrap(orError: GuaranteeingServiceError.noAuthorizerHash)
116234
var exportSegmentOffset: UInt16 = 0
235+
var mappings: SegmentsRootMappings = []
117236
// B.2. the authorization output, the result of the Is-Authorized function
118237
// TODO: waiting for authorizationFunction done Mock a result
119238
// let res = try await authorizationFunction.invoke(config: config, serviceAccounts: state.value, package: workPackage, coreIndex:
@@ -160,16 +279,16 @@ public final class GuaranteeingService: ServiceBase2, @unchecked Sendable {
160279

161280
exportSegments.append(contentsOf: refineRes.exports)
162281
}
163-
164-
let (erasureRoot, length) = try await dataAvailability.exportWorkpackageBundle(bundle: WorkPackageBundle(
282+
let bundle = try await WorkPackageBundle(
165283
workPackage: workPackage.value,
166-
extrinsic: [], // TODO: get extrinsic data
167-
importSegments: [],
168-
justifications: []
169-
))
284+
extrinsic: extrinsics,
285+
importSegments: retrieveImportSegments(for: workPackage.value),
286+
justifications: retrieveJustifications(for: workPackage.value)
287+
)
288+
let (erasureRoot, length) = try await dataAvailability.exportWorkpackageBundle(bundle: bundle)
170289

171290
let segmentRoot = try await dataAvailability.exportSegments(data: exportSegments, erasureRoot: erasureRoot)
172-
291+
mappings.append(SegmentsRootMapping(workPackageHash: packageHash, segmentsRoot: segmentRoot))
173292
// TODO: generate or find AvailabilitySpecifications 14.4.1 work-package bundle
174293
let packageSpecification = AvailabilitySpecifications(
175294
workPackageHash: packageHash,
@@ -183,15 +302,15 @@ public final class GuaranteeingService: ServiceBase2, @unchecked Sendable {
183302
for item in state.value.recentHistory.items {
184303
oldLookups.merge(item.lookup, uniquingKeysWith: { _, new in new })
185304
}
186-
return try WorkReport(
305+
return try (bundle, mappings, WorkReport(
187306
authorizerHash: authorizerHash,
188307
coreIndex: coreIndex,
189308
authorizationOutput: authorizationOutput,
190309
refinementContext: workPackage.value.context,
191310
packageSpecification: packageSpecification,
192311
lookup: oldLookups,
193312
results: ConfigLimitedSizeArray(config: config, array: workResults)
194-
)
313+
))
195314

196315
case let .failure(error):
197316
logger.error("Authorization failed with error: \(error)")

Blockchain/Tests/BlockchainTests/GuaranteeingServiceTests.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,20 @@ struct GuaranteeingServiceTests {
3939
#expect(signingKey.0 == 0)
4040
#expect(signingKey.1.publicKey == publicKey)
4141
}
42+
// TODO: add more tests
43+
// @Test func workPackagesReceived() async throws {
44+
// let (services, guaranteeingService) = try await setup()
45+
//
46+
// await guaranteeingService.onSyncCompleted()
47+
// let workpackage = WorkPackage(
48+
// authorizationToken: Data(repeating: 0x00, count: 32),
49+
// authorizationServiceIndex: 0,
50+
// authorizationCodeHash: Data32(),
51+
// parameterizationBlob: Data(),
52+
// context: RefinementContext.dummy(config: services.config),
53+
// workItems: try! ConfigLimitedSizeArray(config: services.config, defaultValue: WorkItem.dummy(config: services.config))
54+
// )
55+
// await services.eventBus
56+
// .publish(RuntimeEvents.WorkPackagesReceived(coreIndex: 0, workPackage: workpackage.asRef(), extrinsics: []))
57+
// }
4258
}

Node/Sources/Node/NetworkingProtocol/CommonEphemeral/CERequest.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ public enum CERequest: Sendable, Equatable, Hashable {
88
case safroleTicket1(SafroleTicketMessage)
99
case safroleTicket2(SafroleTicketMessage)
1010
case workPackageSubmission(WorkPackageMessage)
11+
case workPackageSharing(WorkPackageShareMessage)
1112
}
1213

1314
extension CERequest: RequestProtocol {
@@ -23,6 +24,8 @@ extension CERequest: RequestProtocol {
2324
try JamEncoder.encode(message)
2425
case let .workPackageSubmission(message):
2526
try JamEncoder.encode(message)
27+
case let .workPackageSharing(message):
28+
try JamEncoder.encode(message)
2629
}
2730
}
2831

@@ -36,6 +39,8 @@ extension CERequest: RequestProtocol {
3639
.safroleTicket2
3740
case .workPackageSubmission:
3841
.workPackageSubmission
42+
case .workPackageSharing:
43+
.workPackageSharing
3944
}
4045
}
4146

@@ -49,6 +54,8 @@ extension CERequest: RequestProtocol {
4954
SafroleTicketMessage.self
5055
case .workPackageSubmission:
5156
WorkPackageMessage.self
57+
case .workPackageSharing:
58+
WorkPackageShareMessage.self
5259
default:
5360
fatalError("unimplemented")
5461
}
@@ -74,6 +81,9 @@ extension CERequest: RequestProtocol {
7481
case .workPackageSubmission:
7582
guard let message = data as? WorkPackageMessage else { return nil }
7683
return .workPackageSubmission(message)
84+
case .workPackageSharing:
85+
guard let message = data as? WorkPackageShareMessage else { return nil }
86+
return .workPackageSharing(message)
7787
default:
7888
fatalError("unimplemented")
7989
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import Blockchain
2+
import Codec
3+
import Foundation
4+
5+
public struct WorkPackageShareMessage: Codable, Sendable, Equatable, Hashable {
6+
public let coreIndex: CoreIndex
7+
public let bundle: WorkPackageBundle
8+
public let segmentsRootMappings: SegmentsRootMappings
9+
10+
public init(
11+
coreIndex: CoreIndex,
12+
bundle: WorkPackageBundle,
13+
segmentsRootMappings: SegmentsRootMappings
14+
) {
15+
self.coreIndex = coreIndex
16+
self.bundle = bundle
17+
self.segmentsRootMappings = segmentsRootMappings
18+
}
19+
}

0 commit comments

Comments
 (0)