Skip to content

Commit 29ab217

Browse files
authored
GuaranteeingService refactor (#300)
* guaranteeing service refactor * update test
1 parent b387829 commit 29ab217

File tree

7 files changed

+94
-259
lines changed

7 files changed

+94
-259
lines changed

Blockchain/Sources/Blockchain/RuntimeProtocols/RuntimeEvents.swift

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -52,23 +52,19 @@ public enum RuntimeEvents {
5252
public let block: BlockRef
5353
}
5454

55-
// New WorkPackagesReceived by Guaranteeing Service
55+
// When a work package is recived via CE133
5656
public struct WorkPackagesReceived: Event {
57-
public let items: [WorkPackageRef]
57+
public let item: WorkPackageRef
5858
}
5959

60-
// WorkPackages Finalize by WorkPackages Service
61-
public struct WorkPackagesFinalized: Event {
62-
public let items: [WorkPackageRef]
60+
// When a work package bundle is ready to shared via CE134
61+
public struct WorkPackageBundleReady: Event {
62+
public let bundle: WorkPackageBundle
6363
}
6464

65-
// New WorkReportGenerated by Guaranteeing Service
65+
// When a work report is generated and ready to be distrubuted via CE135
6666
public struct WorkReportGenerated: Event {
67-
public let items: [WorkReport]
68-
}
69-
70-
// New GuaranteeGenerated by Guaranteeing Service
71-
public struct GuaranteeGenerated: Event {
72-
public let items: [WorkPackageRef]
67+
public let item: WorkReport
68+
public let signature: Ed25519Signature
7369
}
7470
}

Blockchain/Sources/Blockchain/Validator/DevKeyStore.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ public final class DevKeyStore: KeyStore {
2828
await keystore.get(type, publicKey: publicKey)
2929
}
3030

31+
public func getAll<K: KeyType>(_ type: K.Type) async -> [K.SecretKey] {
32+
await keystore.getAll(type)
33+
}
34+
3135
@discardableResult
3236
public func addDevKeys(seed: UInt32) async throws -> KeySet {
3337
var seedData = Data(repeating: 0, count: 32)

Blockchain/Sources/Blockchain/Validator/ExtrinsicPools/WorkPackagePoolService.swift

Lines changed: 0 additions & 97 deletions
This file was deleted.

Blockchain/Sources/Blockchain/Validator/GuaranteeingService.swift

Lines changed: 67 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@ import TracingUtils
44
import Utils
55

66
public enum GuaranteeingServiceError: Error {
7-
case invalidValidatorIndex
8-
case customValidatorNotFound
97
case noAuthorizerHash
10-
case invalidCoreIndex
118
case invalidExports
129
}
1310

@@ -17,83 +14,106 @@ struct GuaranteeingRefineInvocation: RefineInvocation {}
1714
public final class GuaranteeingService: ServiceBase2, @unchecked Sendable {
1815
private let dataProvider: BlockchainDataProvider
1916
private let keystore: KeyStore
20-
private let runtime: Runtime
21-
private let safroleTicketPool: SafroleTicketPoolService
22-
private let workPackagePool: WorkPackagePoolService
23-
private let guarantees: ThreadSafeContainer<[RuntimeEvents.GuaranteeGenerated]> = .init([])
24-
private let authorizationFunction: GuaranteeingAuthorizationFunction
2517
private let dataAvailability: DataAvailability
26-
private let refineInvocation: GuaranteeingRefineInvocation
18+
19+
private let authorizationFunction: IsAuthorizedFunction
20+
private let refineInvocation: RefineInvocation
21+
22+
let signingKey: ThreadSafeContainer<(ValidatorIndex, Ed25519.SecretKey)?> = .init(nil)
2723

2824
public init(
2925
config: ProtocolConfigRef,
3026
eventBus: EventBus,
3127
scheduler: Scheduler,
3228
dataProvider: BlockchainDataProvider,
3329
keystore: KeyStore,
34-
runtime: Runtime,
35-
safroleTicketPool: SafroleTicketPoolService,
3630
dataStore: DataStore
3731
) async {
3832
self.dataProvider = dataProvider
3933
self.keystore = keystore
40-
self.runtime = runtime
41-
self.safroleTicketPool = safroleTicketPool
42-
authorizationFunction = GuaranteeingAuthorizationFunction()
43-
refineInvocation = GuaranteeingRefineInvocation()
44-
workPackagePool = await WorkPackagePoolService(config: config, dataProvider: dataProvider, eventBus: eventBus)
4534
dataAvailability = await DataAvailability(
4635
config: config,
4736
eventBus: eventBus,
4837
scheduler: scheduler,
4938
dataProvider: dataProvider,
5039
dataStore: dataStore
5140
)
41+
42+
authorizationFunction = GuaranteeingAuthorizationFunction()
43+
refineInvocation = GuaranteeingRefineInvocation()
44+
5245
super.init(id: "GuaranteeingService", config: config, eventBus: eventBus, scheduler: scheduler)
5346

54-
await subscribe(RuntimeEvents.GuaranteeGenerated.self, id: "GuaranteeingService.GuaranteeGenerated") { [weak self] event in
55-
try await self?.onGuaranteeGenerated(event: event)
47+
await subscribe(RuntimeEvents.WorkPackagesReceived.self, id: "GuaranteeingService.WorkPackagesReceived") { [weak self] event in
48+
try await self?.on(workPackagesReceived: event)
5649
}
5750
}
5851

59-
public func on(genesis _: StateRef) async {
60-
await onGuaranteeing()
52+
public func onSyncCompleted() async {
53+
let nowTimeslot = timeProvider.getTime().timeToTimeslot(config: config)
54+
let epoch = nowTimeslot.timeslotToEpochIndex(config: config)
55+
await onBeforeEpoch(epoch: epoch)
56+
57+
scheduleForNextEpoch("GuaranteeingService.scheduleForNextEpoch") { [weak self] epoch in
58+
await self?.onBeforeEpoch(epoch: epoch)
59+
}
6160
}
6261

63-
public func scheduleGuaranteeTasks() async throws {
64-
let state = try await dataProvider.getState(hash: dataProvider.bestHead.hash)
65-
let header = try await dataProvider.getHeader(hash: dataProvider.bestHead.hash)
66-
let authorIndex = header.value.authorIndex
67-
let authorKey = try Ed25519.PublicKey(from: state.value.currentValidators[Int(authorIndex)].ed25519)
68-
let key = await keystore.get(Ed25519.self, publicKey: authorKey)
69-
if key == nil {
70-
throw GuaranteeingServiceError.customValidatorNotFound
62+
private func onBeforeEpoch(epoch: EpochIndex) async {
63+
await withSpan("GuaranteeingService.onBeforeEpoch", logger: logger) { _ in
64+
let state = try await dataProvider.getState(hash: dataProvider.bestHead.hash)
65+
let timeslot = epoch.epochToTimeslotIndex(config: config)
66+
// simulate next block to determine the correct current validators
67+
// this is more accurate than just using nextValidators from current state
68+
let res = try state.value.updateSafrole(
69+
config: config,
70+
slot: timeslot,
71+
entropy: Data32(),
72+
offenders: [],
73+
extrinsics: .dummy(config: config)
74+
)
75+
let validators = res.state.currentValidators
76+
77+
let keys = await keystore.getAll(Ed25519.self)
78+
var result: (ValidatorIndex, Ed25519.SecretKey)?
79+
for key in keys {
80+
if let idx = validators.array.firstIndex(where: { $0.ed25519 == key.publicKey.data }) {
81+
result = (ValidatorIndex(idx), key)
82+
break
83+
}
84+
}
85+
86+
signingKey.value = result
87+
}
88+
}
89+
90+
private func on(workPackagesReceived event: RuntimeEvents.WorkPackagesReceived) async throws {
91+
try await refine(package: event.item)
92+
}
93+
94+
private func refine(package: WorkPackageRef) async throws {
95+
guard let (validatorIndex, signingKey) = signingKey.value else {
96+
logger.debug("not in current validator set, skipping refine")
97+
return
7198
}
99+
100+
let state = try await dataProvider.getState(hash: dataProvider.bestHead.hash)
101+
102+
// TODO: check for edge cases such as epoch end
72103
let currentCoreAssignment = state.value.getCoreAssignment(
73104
config: config,
74105
randomness: state.value.entropyPool.t2,
75-
timeslot: state.value.timeslot
106+
timeslot: state.value.timeslot + 1
76107
)
77-
guard authorIndex < ValidatorIndex(currentCoreAssignment.count) else {
78-
logger.error("AuthorIndex not found")
79-
throw GuaranteeingServiceError.invalidValidatorIndex
80-
}
81-
let coreIndex = currentCoreAssignment[Int(authorIndex)]
82-
guard coreIndex < CoreIndex(config.value.totalNumberOfCores) else {
83-
throw GuaranteeingServiceError.invalidCoreIndex
108+
guard let coreIndex = currentCoreAssignment[safe: Int(validatorIndex)] else {
109+
try throwUnreachable("invalid validator index/core assignment")
84110
}
85111

86-
let workPackages = await workPackagePool.getPendingPackages()
87-
for workPackage in workPackages {
88-
if try validate(workPackage: workPackage) {
89-
let workReport = try await createWorkReport(for: workPackage, coreIndex: coreIndex)
90-
let event = RuntimeEvents.WorkReportGenerated(items: [workReport])
91-
publish(event)
92-
break
93-
} else {
94-
logger.error("WorkPackage validation failed")
95-
}
96-
}
112+
let workReport = try await createWorkReport(for: package, coreIndex: coreIndex)
113+
let payload = SigningContext.guarantee + workReport.hash().data
114+
let signature = try signingKey.sign(message: payload)
115+
let event = RuntimeEvents.WorkReportGenerated(item: workReport, signature: signature)
116+
publish(event)
97117
}
98118

99119
// workpackage -> workresult -> workreport
@@ -122,9 +142,6 @@ public final class GuaranteeingService: ServiceBase2, @unchecked Sendable {
122142
}
123143

124144
for (i, item) in workPackage.value.workItems.enumerated() {
125-
// TODO: generated by the work-package builder.
126-
let extrinsicDataBlobs = [Data]()
127-
128145
// RefineInvocation invoke up data to workresult
129146
let refineRes = try await refineInvocation
130147
.invoke(
@@ -191,19 +208,4 @@ public final class GuaranteeingService: ServiceBase2, @unchecked Sendable {
191208
throw error
192209
}
193210
}
194-
195-
private func validate(workPackage _: WorkPackageRef) throws -> Bool {
196-
// TODO: Add validate func
197-
true
198-
}
199-
200-
private func onGuaranteeing() async {
201-
await withSpan("GuaranteeingService.onGuaranteeing", logger: logger) { _ in
202-
try await scheduleGuaranteeTasks()
203-
}
204-
}
205-
206-
private func onGuaranteeGenerated(event: RuntimeEvents.GuaranteeGenerated) async throws {
207-
guarantees.write { $0.append(event) }
208-
}
209211
}

Blockchain/Tests/BlockchainTests/GuaranteeingServiceTests.swift

Lines changed: 8 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -17,48 +17,26 @@ struct GuaranteeingServiceTests {
1717
keysCount: keysCount
1818
)
1919

20-
let SafroleTicketPoolService = await SafroleTicketPoolService(
21-
config: config,
22-
dataProvider: services.dataProvider,
23-
eventBus: services.eventBus
24-
)
25-
26-
let runtime = Runtime(config: config)
27-
2820
let guaranteeingService = await GuaranteeingService(
2921
config: config,
3022
eventBus: services.eventBus,
3123
scheduler: services.scheduler,
3224
dataProvider: services.dataProvider,
3325
keystore: services.keystore,
34-
runtime: runtime,
35-
safroleTicketPool: SafroleTicketPoolService,
3626
dataStore: services.dataStore
3727
)
3828
return (services, guaranteeingService)
3929
}
4030

4131
@Test func onGenesis() async throws {
42-
let (services, validatorService) = try await setup()
43-
let genesisState = services.genesisState
44-
let storeMiddleware = services.storeMiddleware
45-
let scheduler = services.scheduler
32+
let (_, guaranteeingService) = try await setup(keysCount: 1)
33+
34+
await guaranteeingService.onSyncCompleted()
35+
36+
let publicKey = try DevKeyStore.getDevKey(seed: 0).ed25519
37+
let signingKey = guaranteeingService.signingKey.value!
4638

47-
var allWorkPackages = [WorkPackageRef]()
48-
for _ in 0 ..< services.config.value.totalNumberOfCores {
49-
let workpackage = WorkPackage(
50-
authorizationToken: Data(),
51-
authorizationServiceIndex: 0,
52-
authorizationCodeHash: Data32.random(),
53-
parameterizationBlob: Data(),
54-
context: RefinementContext.dummy(config: services.config),
55-
workItems: try! ConfigLimitedSizeArray(config: services.config, defaultValue: WorkItem.dummy(config: services.config))
56-
)
57-
allWorkPackages.append(workpackage.asRef())
58-
}
59-
await services.eventBus.publish(RuntimeEvents.WorkPackagesReceived(items: allWorkPackages))
60-
await validatorService.on(genesis: genesisState)
61-
await storeMiddleware.wait()
62-
#expect(scheduler.taskCount == 1)
39+
#expect(signingKey.0 == 0)
40+
#expect(signingKey.1.publicKey == publicKey)
6341
}
6442
}

0 commit comments

Comments
 (0)