Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement core authorization pool stf #62

Merged
merged 1 commit into from
Aug 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading