Skip to content

Commit 6c4bb08

Browse files
authored
Accumulate tests (#301)
* rename and clean up * clean up vm invocation calls * refactor * fix get code blob * fix edit queue * tests * fix lint
1 parent 8922237 commit 6c4bb08

File tree

14 files changed

+345
-416
lines changed

14 files changed

+345
-416
lines changed

Blockchain/Sources/Blockchain/RuntimeProtocols/AccumulateFunction.swift

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -76,21 +76,3 @@ public struct AccumlateResultContext {
7676
/// y
7777
public var yield: Data32?
7878
}
79-
80-
public protocol AccumulateFunction {
81-
func invoke(
82-
config: ProtocolConfigRef,
83-
// prior accounts
84-
accounts: inout some ServiceAccounts,
85-
// u
86-
state: AccumulateState,
87-
// s
88-
serviceIndex: ServiceIndex,
89-
// g
90-
gas: Gas,
91-
// o
92-
arguments: [AccumulateArguments],
93-
initialIndex: ServiceIndex,
94-
timeslot: TimeslotIndex
95-
) async throws -> (state: AccumulateState, transfers: [DeferredTransfers], result: Data32?, gas: Gas)
96-
}

Blockchain/Sources/Blockchain/RuntimeProtocols/Accumulation.swift

Lines changed: 107 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -58,21 +58,19 @@ public struct SingleAccumulationOutput {
5858

5959
public protocol Accumulation: ServiceAccounts {
6060
var timeslot: TimeslotIndex { get }
61-
var privilegedServices: PrivilegedServices { get }
61+
var privilegedServices: PrivilegedServices { get set }
6262
var validatorQueue: ConfigFixedSizeArray<
6363
ValidatorKey, ProtocolConfig.TotalNumberOfValidators
64-
> { get }
64+
> { get set }
6565
var authorizationQueue: ConfigFixedSizeArray<
6666
ConfigFixedSizeArray<
6767
Data32,
6868
ProtocolConfig.MaxAuthorizationsQueueItems
6969
>,
7070
ProtocolConfig.TotalNumberOfCores
71-
> { get }
72-
var accumlateFunction: AccumulateFunction { get }
73-
var onTransferFunction: OnTransferFunction { get }
74-
var accumulationQueue: StateKeys.AccumulationQueueKey.Value { get }
75-
var accumulationHistory: StateKeys.AccumulationHistoryKey.Value { get }
71+
> { get set }
72+
var accumulationQueue: StateKeys.AccumulationQueueKey.Value { get set }
73+
var accumulationHistory: StateKeys.AccumulationHistoryKey.Value { get set }
7674
}
7775

7876
extension Accumulation {
@@ -105,7 +103,7 @@ extension Accumulation {
105103
}
106104
}
107105

108-
let (newState, transfers, commitment, gasUsed) = try await accumlateFunction.invoke(
106+
let (newState, transfers, commitment, gasUsed) = try await accumulate(
109107
config: config,
110108
accounts: &self,
111109
state: state,
@@ -269,30 +267,29 @@ extension Accumulation {
269267
}
270268
}
271269

272-
// E: edit the accumulation queue, remove the dependencies of the items that are already accumulated
273-
public func editAccumulatedItems(items: inout [AccumulationQueueItem], accumulatedPackages: Set<Data32>) {
274-
for index in items.indices
275-
where !accumulatedPackages.contains(items[index].workReport.packageSpecification.workPackageHash)
276-
{
277-
items[index].dependencies.subtract(accumulatedPackages)
270+
// E: edit the accumulation queue items when some work reports are accumulated
271+
private func editQueue(items: inout [AccumulationQueueItem], accumulatedPackages: Set<Data32>) {
272+
items = items.filter { !accumulatedPackages.contains($0.workReport.packageSpecification.workPackageHash) }
273+
274+
for i in items.indices {
275+
items[i].dependencies.subtract(accumulatedPackages)
278276
}
279277
}
280278

281-
// Q: find the reports that have no dependencies
282-
private func findNoDepsReports(items: inout [AccumulationQueueItem]) -> [WorkReport] {
279+
// Q: provides the sequence of work-reports which are accumulatable given queue items
280+
private func getAccumulatables(items: inout [AccumulationQueueItem]) -> [WorkReport] {
283281
let noDepsReports = items.filter(\.dependencies.isEmpty).map(\.workReport)
284282
if noDepsReports.isEmpty {
285283
return []
286284
} else {
287-
editAccumulatedItems(items: &items, accumulatedPackages: Set(noDepsReports.map(\.packageSpecification.workPackageHash)))
288-
return noDepsReports + findNoDepsReports(items: &items)
285+
editQueue(items: &items, accumulatedPackages: Set(noDepsReports.map(\.packageSpecification.workPackageHash)))
286+
return noDepsReports + getAccumulatables(items: &items)
289287
}
290288
}
291289

292290
// newly available work-reports, W, are partitioned into two sequences based on the condition of having zero prerequisite work-reports
293-
public func partitionWorkReports(
294-
availableReports: [WorkReport],
295-
history: StateKeys.AccumulationHistoryKey.Value
291+
private func partitionWorkReports(
292+
availableReports: [WorkReport]
296293
) -> (zeroPrereqReports: [WorkReport], newQueueItems: [AccumulationQueueItem]) {
297294
let zeroPrereqReports = availableReports.filter { report in
298295
report.refinementContext.prerequisiteWorkPackages.isEmpty && report.lookup.isEmpty
@@ -308,40 +305,42 @@ extension Accumulation {
308305
))
309306
}
310307

311-
editAccumulatedItems(
308+
editQueue(
312309
items: &newQueueItems,
313-
accumulatedPackages: Set(history.array.reduce(into: Set<Data32>()) { $0.formUnion($1.array) })
310+
accumulatedPackages: Set(accumulationHistory.array.reduce(into: Set<Data32>()) { $0.formUnion($1.array) })
314311
)
315312

316313
return (zeroPrereqReports, newQueueItems)
317314
}
318315

319-
public func getAccumulatableReports(
320-
index: Int, availableReports: [WorkReport],
321-
history: StateKeys.AccumulationHistoryKey.Value
316+
// get all the work reports that can be accumulated in this block
317+
private func getAllAccumulatableReports(
318+
availableReports: [WorkReport],
319+
index: Int
322320
) -> (accumulatableReports: [WorkReport], newQueueItems: [AccumulationQueueItem]) {
323-
let (zeroPrereqReports, newQueueItems) = partitionWorkReports(availableReports: availableReports, history: history)
321+
let (zeroPrereqReports, newQueueItems) = partitionWorkReports(availableReports: availableReports)
324322

325323
let rightQueueItems = accumulationQueue.array[index...]
326324
let leftQueueItems = accumulationQueue.array[0 ..< index]
327325
var allQueueItems = rightQueueItems.flatMap(\.self) + leftQueueItems.flatMap(\.self) + newQueueItems
328326

329-
editAccumulatedItems(items: &allQueueItems, accumulatedPackages: Set(zeroPrereqReports.map(\.packageSpecification.workPackageHash)))
327+
editQueue(items: &allQueueItems, accumulatedPackages: Set(zeroPrereqReports.map(\.packageSpecification.workPackageHash)))
330328

331-
return (zeroPrereqReports + findNoDepsReports(items: &allQueueItems), newQueueItems)
329+
return (zeroPrereqReports + getAccumulatables(items: &allQueueItems), newQueueItems)
332330
}
333331

334-
public mutating func update(
332+
// accumulate execution
333+
private mutating func execution(
335334
config: ProtocolConfigRef,
336335
workReports: [WorkReport],
337336
entropy: Data32,
338337
timeslot: TimeslotIndex
339-
) async throws -> (numAccumulated: Int, state: AccumulateState, commitments: Set<Commitment>) {
338+
) async throws -> AccumulationOutput {
340339
let sumPrevilegedGas = privilegedServices.basicGas.values.reduce(Gas(0)) { $0 + $1.value }
341340
let minTotalGas = config.value.workReportAccumulationGas * Gas(config.value.totalNumberOfCores) + sumPrevilegedGas
342341
let gasLimit = max(config.value.totalAccumulationGas, minTotalGas)
343342

344-
let res = try await outerAccumulate(
343+
return try await outerAccumulate(
345344
config: config,
346345
state: AccumulateState(
347346
newServiceAccounts: [:],
@@ -355,23 +354,93 @@ extension Accumulation {
355354
entropy: entropy,
356355
timeslot: timeslot
357356
)
357+
}
358358

359-
var transferGroups = [ServiceIndex: [DeferredTransfers]]()
359+
/// Accumulate execution, state integration and deferred transfers
360+
///
361+
/// Return accumulation-result merkle tree root
362+
public mutating func update(
363+
config: ProtocolConfigRef,
364+
availableReports: [WorkReport],
365+
timeslot: TimeslotIndex,
366+
prevTimeslot: TimeslotIndex,
367+
entropy: Data32
368+
) async throws -> Data32 {
369+
let index = Int(timeslot) %% config.value.epochLength
370+
371+
var (accumulatableReports, newQueueItems) = getAllAccumulatableReports(
372+
availableReports: availableReports,
373+
index: index
374+
)
360375

361-
for transfer in res.transfers {
362-
transferGroups[transfer.destination, default: []].append(transfer)
376+
let accumulateOutput = try await execution(
377+
config: config,
378+
workReports: accumulatableReports,
379+
entropy: entropy,
380+
timeslot: timeslot
381+
)
382+
383+
authorizationQueue = accumulateOutput.state.authorizationQueue
384+
validatorQueue = accumulateOutput.state.validatorQueue
385+
privilegedServices = accumulateOutput.state.privilegedServices
386+
387+
// add new service accounts
388+
for (service, account) in accumulateOutput.state.newServiceAccounts {
389+
set(serviceAccount: service, account: account.toDetails())
390+
for (hash, value) in account.storage {
391+
set(serviceAccount: service, storageKey: hash, value: value)
392+
}
393+
for (hash, value) in account.preimages {
394+
set(serviceAccount: service, preimageHash: hash, value: value)
395+
}
396+
for (hashLength, value) in account.preimageInfos {
397+
set(serviceAccount: service, preimageHash: hashLength.hash, length: hashLength.length, value: value)
398+
}
363399
}
364400

401+
// transfers
402+
var transferGroups = [ServiceIndex: [DeferredTransfers]]()
403+
for transfer in accumulateOutput.transfers {
404+
transferGroups[transfer.destination, default: []].append(transfer)
405+
}
365406
for (service, transfers) in transferGroups {
366-
try await onTransferFunction.invoke(
407+
try await onTransfer(
367408
config: config,
368-
service: service,
409+
serviceIndex: service,
369410
serviceAccounts: &self,
370411
timeslot: timeslot,
371412
transfers: transfers
372413
)
373414
}
374415

375-
return (res.numAccumulated, res.state, res.commitments)
416+
// update accumulation history
417+
let accumulated = accumulatableReports[0 ..< accumulateOutput.numAccumulated]
418+
let newHistoryItem = Set(accumulated.map(\.packageSpecification.workPackageHash))
419+
for i in 0 ..< config.value.epochLength {
420+
if i == config.value.epochLength - 1 {
421+
accumulationHistory[i] = .init(newHistoryItem)
422+
} else {
423+
accumulationHistory[i] = accumulationHistory[i + 1]
424+
}
425+
}
426+
427+
// update accumulation queue
428+
for i in 0 ..< config.value.epochLength {
429+
let queueIdx = (index - i) %% config.value.epochLength
430+
if i == 0 {
431+
editQueue(items: &newQueueItems, accumulatedPackages: newHistoryItem)
432+
accumulationQueue[queueIdx] = newQueueItems
433+
} else if i >= 1, i < timeslot - prevTimeslot {
434+
accumulationQueue[queueIdx] = []
435+
} else {
436+
editQueue(items: &accumulationQueue[queueIdx], accumulatedPackages: newHistoryItem)
437+
}
438+
}
439+
440+
// accumulate root
441+
let nodes = try accumulateOutput.commitments.map { try JamEncoder.encode($0.serviceIndex) + JamEncoder.encode($0.hash) }
442+
let root = Merklization.binaryMerklize(nodes, hasher: Keccak.self)
443+
444+
return root
376445
}
377446
}

Blockchain/Sources/Blockchain/RuntimeProtocols/OnTransferFunction.swift

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

Blockchain/Sources/Blockchain/RuntimeProtocols/Runtime.swift

Lines changed: 8 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -170,13 +170,13 @@ public final class Runtime {
170170
// depends on Safrole and Disputes
171171
let availableReports = try updateReports(block: block, state: &newState)
172172

173-
// accumulation
174-
try await accumulate(
173+
// accumulate
174+
let accumulateRoot = try await newState.update(
175175
config: config,
176-
block: block,
177176
availableReports: availableReports,
178-
state: &newState,
179-
prevTimeslot: prevState.value.timeslot
177+
timeslot: block.header.timeslot,
178+
prevTimeslot: prevState.value.timeslot,
179+
entropy: newState.entropyPool.t0
180180
)
181181

182182
do {
@@ -200,7 +200,7 @@ public final class Runtime {
200200
)
201201

202202
// after reports as it need old recent history
203-
try updateRecentHistory(block: block, state: &newState)
203+
try updateRecentHistory(block: block, state: &newState, accumulateRoot: accumulateRoot)
204204

205205
try await newState.save()
206206
} catch let error as Error {
@@ -216,79 +216,15 @@ public final class Runtime {
216216
return StateRef(newState)
217217
}
218218

219-
// accumulation related state updates
220-
public func accumulate(
221-
config: ProtocolConfigRef,
222-
block: BlockRef,
223-
availableReports: [WorkReport],
224-
state: inout State,
225-
prevTimeslot: TimeslotIndex
226-
) async throws {
227-
let curIndex = Int(block.header.timeslot) % config.value.epochLength
228-
var (accumulatableReports, newQueueItems) = state.getAccumulatableReports(
229-
index: curIndex,
230-
availableReports: availableReports,
231-
history: state.accumulationHistory
232-
)
233-
234-
// accumulate and transfers
235-
let (numAccumulated, accumulateState, _) = try await state.update(
236-
config: config,
237-
workReports: accumulatableReports,
238-
entropy: state.entropyPool.t0,
239-
timeslot: block.header.timeslot
240-
)
241-
242-
state.authorizationQueue = accumulateState.authorizationQueue
243-
state.validatorQueue = accumulateState.validatorQueue
244-
state.privilegedServices = accumulateState.privilegedServices
245-
for (service, account) in accumulateState.newServiceAccounts {
246-
state[serviceAccount: service] = account.toDetails()
247-
for (hash, value) in account.storage {
248-
state[serviceAccount: service, storageKey: hash] = value
249-
}
250-
for (hash, value) in account.preimages {
251-
state[serviceAccount: service, preimageHash: hash] = value
252-
}
253-
for (hashLength, value) in account.preimageInfos {
254-
state[serviceAccount: service, preimageHash: hashLength.hash, length: hashLength.length] = value
255-
}
256-
}
257-
258-
// update accumulation history
259-
let accumulated = accumulatableReports[0 ..< numAccumulated]
260-
let newHistoryItem = Set(accumulated.map(\.packageSpecification.workPackageHash))
261-
for i in 0 ..< config.value.epochLength {
262-
if i == config.value.epochLength - 1 {
263-
state.accumulationHistory[i] = .init(newHistoryItem)
264-
} else {
265-
state.accumulationHistory[i] = state.accumulationHistory[i + 1]
266-
}
267-
}
268-
269-
// update accumulation queue
270-
for i in 0 ..< config.value.epochLength {
271-
let queueIdx = (curIndex - i) %% config.value.epochLength
272-
if i == 0 {
273-
state.editAccumulatedItems(items: &newQueueItems, accumulatedPackages: newHistoryItem)
274-
state.accumulationQueue[queueIdx] = newQueueItems
275-
} else if i >= 1, i < state.timeslot - prevTimeslot {
276-
state.accumulationQueue[queueIdx] = []
277-
} else {
278-
state.editAccumulatedItems(items: &state.accumulationQueue[queueIdx], accumulatedPackages: newHistoryItem)
279-
}
280-
}
281-
}
282-
283-
public func updateRecentHistory(block: BlockRef, state newState: inout State) throws {
219+
public func updateRecentHistory(block: BlockRef, state newState: inout State, accumulateRoot: Data32) throws {
284220
let lookup: [Data32: Data32] = Dictionary(uniqueKeysWithValues: block.extrinsic.reports.guarantees.map { (
285221
$0.workReport.packageSpecification.workPackageHash,
286222
$0.workReport.packageSpecification.segmentRoot
287223
) })
288224
newState.recentHistory.update(
289225
headerHash: block.hash,
290226
parentStateRoot: block.header.priorStateRoot,
291-
accumulateRoot: Data32(), // TODO: calculate accumulation result
227+
accumulateRoot: accumulateRoot,
292228
lookup: lookup
293229
)
294230
}

0 commit comments

Comments
 (0)