Skip to content

Commit

Permalink
many state updates
Browse files Browse the repository at this point in the history
  • Loading branch information
qiweiii committed Nov 9, 2024
1 parent 9391dcb commit ce4ab7c
Show file tree
Hide file tree
Showing 17 changed files with 216 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
29 changes: 22 additions & 7 deletions Blockchain/Sources/Blockchain/Config/ProtocolConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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.
Expand All @@ -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

Expand Down Expand Up @@ -125,6 +128,7 @@ public struct ProtocolConfig: Sendable, Codable, Equatable {
totalAccumulationGas: Gas,
recentHistorySize: Int,
maxWorkItems: Int,
maxDepsInWorkReport: Int,
maxTicketsPerExtrinsic: Int,
maxLookupAnchorAge: Int,
transferMemoSize: Int,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
)
Expand Down Expand Up @@ -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 {
Expand Down
10 changes: 10 additions & 0 deletions Blockchain/Sources/Blockchain/RuntimeProtocols/Accumulation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ public enum AccumulationError: Error {
case duplicatedServiceIndex
}

public struct AccumulationQueueItem: Sendable, Equatable, Codable {
public var workReport: WorkReport
public var dependencies: Set<Data32>

public init(workReport: WorkReport, dependencies: Set<Data32>) {
self.workReport = workReport
self.dependencies = dependencies
}
}

// accumulation output pairing
public struct Commitment: Hashable {
public var serviceIndex: ServiceIndex
Expand Down
54 changes: 44 additions & 10 deletions Blockchain/Sources/Blockchain/RuntimeProtocols/Guaranteeing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -38,6 +40,14 @@ public protocol Guaranteeing {
> { get }
var recentHistory: RecentHistory { get }
var offenders: Set<Ed25519PublicKey> { get }
var accumulationQueue: ConfigFixedSizeArray<
[AccumulationQueueItem],
ProtocolConfig.EpochLength
> { get }
var accumulationHistory: ConfigFixedSizeArray<
Set<Data32>,
ProtocolConfig.EpochLength
> { get }

func serviceAccount(index: ServiceIndex) -> ServiceAccountDetails?
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -146,6 +160,10 @@ extension Guaranteeing {
throw .invalidResultCodeHash
}

guard result.gasRatio >= acc.minAccumlateGas else {
throw .invalidServiceGas
}

totalMinGasRequirement += acc.minAccumlateGas
}
}
Expand All @@ -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<Data32> = 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
Expand All @@ -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
}
}
}
Expand Down
9 changes: 6 additions & 3 deletions Blockchain/Sources/Blockchain/RuntimeProtocols/Runtime.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
}

Expand Down
2 changes: 1 addition & 1 deletion Blockchain/Sources/Blockchain/State/State+Genesis.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ extension State {
headerHash: block.hash,
mmr: MMR([]),
stateRoot: Data32(),
workReportHashes: ConfigLimitedSizeArray(config: config)
lookup: [Data32: Data32]()
))

return (StateRef(state), block)
Expand Down
32 changes: 31 additions & 1 deletion Blockchain/Sources/Blockchain/State/State.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand All @@ -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<Data32>()
)

let kv: [(any StateKey, Codable & Sendable)] = [
(StateKeys.CoreAuthorizationPoolKey(), coreAuthorizationPool),
Expand All @@ -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] = [:]
Expand Down
30 changes: 30 additions & 0 deletions Blockchain/Sources/Blockchain/State/StateKeys.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<Data32>,
ProtocolConfig.EpochLength
>
>

public init() {}

public func encode() -> Data32 {
constructKey(15)
}
}

public struct ServiceAccountKey: StateKey {
public typealias Value = StateOptionalValue<ServiceAccountDetails>

Expand Down
Loading

0 comments on commit ce4ab7c

Please sign in to comment.