Skip to content

Commit

Permalink
implement core authorization pool stf (#62)
Browse files Browse the repository at this point in the history
  • Loading branch information
xlc authored Aug 17, 2024
1 parent c14af2c commit 22e15e0
Show file tree
Hide file tree
Showing 12 changed files with 138 additions and 43 deletions.
1 change: 1 addition & 0 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ disabled_rules:
- type_body_length
- identifier_name
- function_parameter_count
- force_try

excluded:
- "**/.build"
Expand Down
70 changes: 65 additions & 5 deletions Blockchain/Sources/Blockchain/Runtime.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ public final class Runtime {
case safroleError(SafroleError)
case invalidValidatorEd25519Key
case invalidTimeslot
case invalidReportAuthorizer
case other(any Swift.Error)
}

public struct ApplyContext {
Expand Down Expand Up @@ -47,21 +49,79 @@ public final class Runtime {
throw .safroleError(err)
}

newState.activityStatistics = updateValidatorActivityStatistics(block: block, state: prevState)
do {
newState.coreAuthorizationPool = try updateAuthorizationPool(
block: block, state: prevState
)

newState.activityStatistics = try updateValidatorActivityStatistics(
block: block, state: prevState
)
} catch let error as Error {
throw error
} catch {
throw .other(error)
}

return StateRef(newState)
}

// TODO: add tests
public func updateValidatorActivityStatistics(block: BlockRef, state: StateRef) -> ValidatorActivityStatistics {
public func updateAuthorizationPool(block: BlockRef, state: StateRef) throws -> ConfigFixedSizeArray<
ConfigLimitedSizeArray<
Data32,
ProtocolConfig.Int0,
ProtocolConfig.MaxAuthorizationsPoolItems
>,
ProtocolConfig.TotalNumberOfCores
> {
var pool = state.value.coreAuthorizationPool

for coreIndex in 0 ..< pool.count {
var corePool = pool[coreIndex]
let coreQueue = state.value.authorizationQueue[coreIndex]
if coreQueue.count == 0 {
continue
}
let newItem = coreQueue[Int(block.header.timeslotIndex) % coreQueue.count]

// remove used authorizers from pool
for report in block.extrinsic.reports.guarantees {
let authorizer = report.workReport.authorizerHash
if let idx = corePool.firstIndex(of: authorizer) {
_ = try corePool.remove(at: idx)
} else {
throw Error.invalidReportAuthorizer
}
}

// add new item from queue
if corePool.count < corePool.maxLength {
try corePool.append(newItem)
} else {
try corePool.mutate {
$0.remove(at: 0)
$0.append(newItem)
}
}
pool[coreIndex] = corePool
}

return pool
}

// TODO: add tests
public func updateValidatorActivityStatistics(block: BlockRef, state: StateRef) throws -> ValidatorActivityStatistics {
let epochLength = UInt32(config.value.epochLength)
let currentEpoch = state.value.timeslot / epochLength
let newEpoch = block.header.timeslotIndex / epochLength
let isEpochChange = currentEpoch != newEpoch

var acc = isEpochChange ? ConfigFixedSizeArray<_, ProtocolConfig.TotalNumberOfValidators>(
config: config, defaultValue: ValidatorActivityStatistics.StatisticsItem.dummy(config: config)
) : state.value.activityStatistics.accumulator
var acc = try isEpochChange
? ConfigFixedSizeArray<_, ProtocolConfig.TotalNumberOfValidators>(
config: config,
defaultValue: ValidatorActivityStatistics.StatisticsItem.dummy(config: config)
) : state.value.activityStatistics.accumulator

let prev = isEpochChange ? state.value.activityStatistics.accumulator : state.value.activityStatistics.previous

Expand Down
22 changes: 11 additions & 11 deletions Blockchain/Sources/Blockchain/Safrole.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ public enum SafroleError: Error {
case extrinsicsTooManyEntry
case hashingError
case bandersnatchError(BandersnatchError)
case decodingError
case unspecified
case decodingError(DecodingError)
case other(any Swift.Error)
}

public struct SafrolePostState: Sendable, Equatable {
Expand Down Expand Up @@ -264,7 +264,7 @@ extension Safrole {
currentPhase >= ticketSubmissionEndSlot,
ticketsAccumulator.count == config.value.epochLength
{
.left(ConfigFixedSizeArray(config: config, array: outsideInReorder(ticketsAccumulator.array)))
try .left(ConfigFixedSizeArray(config: config, array: outsideInReorder(ticketsAccumulator.array)))
} else if newEpoch == currentEpoch {
ticketsOrKeys
} else {
Expand All @@ -278,7 +278,7 @@ extension Safrole {
))
}

let epochMark = isEpochChange ? EpochMarker(
let epochMark = try isEpochChange ? EpochMarker(
entropy: newEntropyPool.1,
validators: ConfigFixedSizeArray(config: config, array: newNextValidators.map(\.bandersnatch))
) : nil
Expand All @@ -288,7 +288,7 @@ extension Safrole {
currentPhase < ticketSubmissionEndSlot,
ticketSubmissionEndSlot <= newPhase,
ticketsAccumulator.count == config.value.epochLength {
ConfigFixedSizeArray(
try ConfigFixedSizeArray(
config: config, array: outsideInReorder(ticketsAccumulator.array)
)
} else {
Expand Down Expand Up @@ -330,7 +330,9 @@ extension Safrole {
newTicketsAccumulatorArr.removeLast(newTicketsAccumulatorArr.count - config.value.epochLength)
}

let newTicketsAccumulator = ConfigLimitedSizeArray<Ticket, ProtocolConfig.Int0, ProtocolConfig.EpochLength>(
let newTicketsAccumulator = try ConfigLimitedSizeArray<
Ticket, ProtocolConfig.Int0, ProtocolConfig.EpochLength
>(
config: config,
array: newTicketsAccumulatorArr
)
Expand All @@ -353,12 +355,10 @@ extension Safrole {
return .failure(.bandersnatchError(e))
} catch Blake2Error.hashingError {
return .failure(.hashingError)
} catch is DecodingError {
// TODO: log details
return .failure(.decodingError)
} catch let e as DecodingError {
return .failure(.decodingError(e))
} catch {
// TODO: log details
return .failure(.unspecified)
return .failure(.other(error))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public struct ExtrinsicAvailability: Sendable, Equatable {
extension ExtrinsicAvailability: Dummy {
public typealias Config = ProtocolConfigRef
public static func dummy(config: Config) -> ExtrinsicAvailability {
ExtrinsicAvailability(assurances: ConfigLimitedSizeArray(config: config))
try! ExtrinsicAvailability(assurances: ConfigLimitedSizeArray(config: config))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public struct ExtrinsicGuarantees: Sendable, Equatable {
extension ExtrinsicGuarantees: Dummy {
public typealias Config = ProtocolConfigRef
public static func dummy(config: Config) -> ExtrinsicGuarantees {
ExtrinsicGuarantees(guarantees: ConfigLimitedSizeArray(config: config))
try! ExtrinsicGuarantees(guarantees: ConfigLimitedSizeArray(config: config))
}
}

Expand Down
2 changes: 1 addition & 1 deletion Blockchain/Sources/Blockchain/Types/ExtrinsicTickets.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public struct ExtrinsicTickets: Sendable, Equatable {
extension ExtrinsicTickets: Dummy {
public typealias Config = ProtocolConfigRef
public static func dummy(config: Config) -> ExtrinsicTickets {
ExtrinsicTickets(tickets: ConfigLimitedSizeArray(config: config))
ExtrinsicTickets(tickets: try! ConfigLimitedSizeArray(config: config))
}
}

Expand Down
2 changes: 1 addition & 1 deletion Blockchain/Sources/Blockchain/Types/SafroleState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public struct SafroleState: Sendable, Equatable {
extension SafroleState: Dummy {
public typealias Config = ProtocolConfigRef
public static func dummy(config: Config) -> SafroleState {
SafroleState(
try! SafroleState(
nextValidators: ConfigFixedSizeArray(config: config, defaultValue: ValidatorKey.dummy(config: config)),
ticketsVerifier: BandersnatchRingVRFRoot(),
ticketsOrKeys: .right(ConfigFixedSizeArray(config: config, defaultValue: BandersnatchPublicKey())),
Expand Down
2 changes: 1 addition & 1 deletion Blockchain/Sources/Blockchain/Types/State.swift
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ extension State: Equatable {
extension State: Dummy {
public typealias Config = ProtocolConfigRef
public static func dummy(config: Config) -> State {
State(
try! State(
config: config,
coreAuthorizationPool: ConfigFixedSizeArray(config: config, defaultValue: ConfigLimitedSizeArray(config: config)),
lastBlock: BlockRef.dummy(config: config),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ extension ValidatorActivityStatistics: Dummy {
public typealias Config = ProtocolConfigRef
public static func dummy(config: Config) -> ValidatorActivityStatistics {
ValidatorActivityStatistics(
accumulator: ConfigFixedSizeArray(
accumulator: try! ConfigFixedSizeArray(
config: config, defaultValue: StatisticsItem.dummy(config: config)
),
previous: ConfigFixedSizeArray(
previous: try! ConfigFixedSizeArray(
config: config, defaultValue: StatisticsItem.dummy(config: config)
)
)
Expand Down
2 changes: 1 addition & 1 deletion Blockchain/Sources/Blockchain/Types/WorkReport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ extension WorkReport: Dummy {
output: Data(),
refinementContext: RefinementContext.dummy(config: config),
packageSpecification: AvailabilitySpecifications.dummy(config: config),
results: ConfigLimitedSizeArray(config: config, defaultValue: WorkResult.dummy(config: config))
results: try! ConfigLimitedSizeArray(config: config, defaultValue: WorkResult.dummy(config: config))
)
}
}
Expand Down
68 changes: 51 additions & 17 deletions Utils/Sources/Utils/ConfigLimitedSizeArray.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@ import ScaleCodec

// TODO: add tests

public enum ConfigLimitedSizeArrayError: Swift.Error {
case tooManyElements
case tooFewElements
case invalidMinLength
case invalidMaxLength
case invalidIndex
}

public struct ConfigLimitedSizeArray<T, TMinLength: ReadInt, TMaxLength: ReadInt>
where TMinLength.TConfig == TMaxLength.TConfig
{
Expand All @@ -10,43 +18,56 @@ public struct ConfigLimitedSizeArray<T, TMinLength: ReadInt, TMaxLength: ReadInt
public let minLength: Int
public let maxLength: Int

public init(config: TMinLength.TConfig, defaultValue: T) {
public init(config: TMinLength.TConfig, defaultValue: T) throws(ConfigLimitedSizeArrayError) {
let minLength = TMinLength.read(config: config)
let maxLength = TMaxLength.read(config: config)

self.init(Array(repeating: defaultValue, count: minLength), minLength: minLength, maxLength: maxLength)
try self.init(Array(repeating: defaultValue, count: minLength), minLength: minLength, maxLength: maxLength)
}

// require minLength to be zero
public init(config: TMinLength.TConfig) {
public init(config: TMinLength.TConfig) throws(ConfigLimitedSizeArrayError) {
let minLength = TMinLength.read(config: config)
let maxLength = TMaxLength.read(config: config)

self.init([], minLength: minLength, maxLength: maxLength)
try self.init([], minLength: minLength, maxLength: maxLength)
}

public init(config: TMinLength.TConfig, array: [T]) {
public init(config: TMinLength.TConfig, array: [T]) throws(ConfigLimitedSizeArrayError) {
let minLength = TMinLength.read(config: config)
let maxLength = TMaxLength.read(config: config)

self.init(array, minLength: minLength, maxLength: maxLength)
try self.init(array, minLength: minLength, maxLength: maxLength)
}

private init(_ array: [T], minLength: Int, maxLength: Int) {
assert(minLength >= 0)
assert(maxLength >= minLength)
private init(_ array: [T], minLength: Int, maxLength: Int) throws(ConfigLimitedSizeArrayError) {
guard minLength >= 0 else {
throw ConfigLimitedSizeArrayError.invalidMinLength
}
guard maxLength >= minLength else {
throw ConfigLimitedSizeArrayError.invalidMaxLength
}

self.array = array
self.minLength = minLength
self.maxLength = maxLength

validate()
try validateThrowing()
}

private func validate() {
assert(array.count >= minLength, "count \(array.count) >= minLength \(minLength)")
assert(array.count <= maxLength, "count \(array.count) <= maxLength \(maxLength)")
}

private func validateThrowing() throws(ConfigLimitedSizeArrayError) {
guard array.count >= minLength else {
throw ConfigLimitedSizeArrayError.tooFewElements
}
guard array.count <= maxLength else {
throw ConfigLimitedSizeArrayError.tooManyElements
}
}
}

extension ConfigLimitedSizeArray: Equatable where T: Equatable {}
Expand Down Expand Up @@ -119,19 +140,32 @@ extension ConfigLimitedSizeArray: RandomAccessCollection {
}

extension ConfigLimitedSizeArray {
public mutating func append(_ newElement: T) {
public mutating func append(_ newElement: T) throws(ConfigLimitedSizeArrayError) {
array.append(newElement)
validate()
try validateThrowing()
}

public mutating func insert(_ newElement: T, at i: Int) {
public mutating func insert(_ newElement: T, at i: Int) throws(ConfigLimitedSizeArrayError) {
if i < 0 || i > array.count {
throw ConfigLimitedSizeArrayError.invalidIndex
}
array.insert(newElement, at: i)
validate()
try validateThrowing()
}

public mutating func remove(at i: Int) throws -> T {
if i < 0 || i >= array.count {
throw ConfigLimitedSizeArrayError.invalidIndex
}
let res = array.remove(at: i)
try validateThrowing()
return res
}

public mutating func remove(at i: Int) -> T {
defer { validate() }
return array.remove(at: i)
public mutating func mutate<R>(_ fn: (inout [T]) -> R) throws(ConfigLimitedSizeArrayError) -> R {
let ret = fn(&array)
try validateThrowing()
return ret
}
}

Expand Down
4 changes: 2 additions & 2 deletions Utils/Sources/Utils/merklization.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ public func stateMerklize(kv: [Data32: Data], i: Int = 0) throws -> Data32 {

func leaf(key: Data32, value: Data) throws -> Data64 {
if value.count <= 32 {
return embeddedLeaf(key: key, value: value, size: UInt8(value.count))
embeddedLeaf(key: key, value: value, size: UInt8(value.count))
} else {
return try regularLeaf(key: key, value: value)
try regularLeaf(key: key, value: value)
}
}

Expand Down

0 comments on commit 22e15e0

Please sign in to comment.