Skip to content

Commit

Permalink
validator stats (#49)
Browse files Browse the repository at this point in the history
  • Loading branch information
xlc authored Aug 5, 2024
1 parent 5368b1a commit 31277d2
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 20 deletions.
1 change: 1 addition & 0 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ disabled_rules:
- blanket_disable_command
- type_body_length
- identifier_name
- function_parameter_count

excluded:
- "**/.build"
Expand Down
43 changes: 43 additions & 0 deletions Blockchain/Sources/Blockchain/Runtime.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import Utils

// the STF
public final class Runtime {
public enum Error: Swift.Error {
case safroleError(SafroleError)
case invalidValidatorEd25519Key
}

public let config: ProtocolConfigRef
Expand All @@ -13,6 +16,7 @@ public final class Runtime {
public func apply(block: BlockRef, state prevState: StateRef) throws(Error) -> StateRef {
var newState = prevState.value
newState.lastBlock = block

let res = newState.updateSafrole(
slot: block.header.timeslotIndex, entropy: newState.entropyPool.0, extrinsics: block.extrinsic.tickets
)
Expand All @@ -23,6 +27,45 @@ public final class Runtime {
throw .safroleError(err)
}

newState.activityStatistics = updateValidatorActivityStatistics(block: block, state: prevState)

return StateRef(newState)
}

// TODO: add tests
public func updateValidatorActivityStatistics(block: BlockRef, state: StateRef) -> 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

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

var item = acc[block.header.authorIndex]
item.blocks += 1
item.tickets += UInt32(block.extrinsic.tickets.tickets.count)
item.preimages += UInt32(block.extrinsic.preimages.preimages.count)
item.preimagesBytes += UInt32(block.extrinsic.preimages.preimages.reduce(into: 0) { $0 += $1.size })

acc[block.header.authorIndex] = item

for report in block.extrinsic.reports.guarantees {
for cred in report.credential {
acc[cred.index].guarantees += 1
}
}

for assurance in block.extrinsic.availability.assurances {
acc[assurance.validatorIndex].assurances += 1
}

return ValidatorActivityStatistics(
accumulator: acc,
previous: prev
)
}
}
37 changes: 30 additions & 7 deletions Blockchain/Sources/Blockchain/Types/ExtrinsicGuarantees.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,25 @@ import ScaleCodec
import Utils

public struct ExtrinsicGuarantees: Sendable, Equatable {
public struct IndexAndSignature: Sendable, Equatable {
public var index: UInt32
public var signature: Ed25519Signature

public init(
index: UInt32,
signature: Ed25519Signature
) {
self.index = index
self.signature = signature
}
}

public struct GuaranteeItem: Sendable, Equatable {
public var coreIndex: CoreIndex
public var workReport: WorkReport
public var timeslot: TimeslotIndex
public var credential: LimitedSizeArray<
Ed25519Signature,
IndexAndSignature,
ConstInt2,
ConstInt3
>
Expand All @@ -17,7 +30,7 @@ public struct ExtrinsicGuarantees: Sendable, Equatable {
workReport: WorkReport,
timeslot: TimeslotIndex,
credential: LimitedSizeArray<
Ed25519Signature,
IndexAndSignature,
ConstInt2,
ConstInt3
>
Expand All @@ -42,16 +55,12 @@ public struct ExtrinsicGuarantees: Sendable, Equatable {
) {
self.guarantees = guarantees
}

public init(config: ProtocolConfigRef) {
guarantees = ConfigLimitedSizeArray(config: config)
}
}

extension ExtrinsicGuarantees: Dummy {
public typealias Config = ProtocolConfigRef
public static func dummy(config: Config) -> ExtrinsicGuarantees {
ExtrinsicGuarantees(config: config)
ExtrinsicGuarantees(guarantees: ConfigLimitedSizeArray(config: config))
}
}

Expand Down Expand Up @@ -84,3 +93,17 @@ extension ExtrinsicGuarantees.GuaranteeItem: ScaleCodec.Encodable {
try encoder.encode(credential)
}
}

extension ExtrinsicGuarantees.IndexAndSignature: ScaleCodec.Codable {
public init(from decoder: inout some ScaleCodec.Decoder) throws {
try self.init(
index: decoder.decode(),
signature: decoder.decode()
)
}

public func encode(in encoder: inout some ScaleCodec.Encoder) throws {
try encoder.encode(index)
try encoder.encode(signature)
}
}
14 changes: 7 additions & 7 deletions Blockchain/Sources/Blockchain/Types/Header.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ public struct Header: Sendable, Equatable {
// Ho: offenders markers
public var offendersMarkers: [Ed25519PublicKey]

// Hi: a Bandersnatch block author index
public var authorKey: ValidatorIndex
// Hi: block author index
public var authorIndex: ValidatorIndex

// Hv: the entropy-yielding vrf signature
public var vrfSignature: BandersnatchSignature
Expand All @@ -57,7 +57,7 @@ public struct Header: Sendable, Equatable {
>?,
judgementsMarkers: [Data32],
offendersMarkers: [Ed25519PublicKey],
authorKey: UInt32,
authorIndex: UInt32,
vrfSignature: BandersnatchSignature,
seal: BandersnatchSignature
) {
Expand All @@ -69,7 +69,7 @@ public struct Header: Sendable, Equatable {
self.winningTickets = winningTickets
self.judgementsMarkers = judgementsMarkers
self.offendersMarkers = offendersMarkers
self.authorKey = authorKey
self.authorIndex = authorIndex
self.vrfSignature = vrfSignature
self.seal = seal
}
Expand All @@ -95,7 +95,7 @@ extension Header: Dummy {
winningTickets: nil,
judgementsMarkers: [],
offendersMarkers: [],
authorKey: 0,
authorIndex: 0,
vrfSignature: BandersnatchSignature(),
seal: BandersnatchSignature()
)
Expand All @@ -113,7 +113,7 @@ extension Header: ScaleCodec.Encodable {
winningTickets: ConfigFixedSizeArray(config: config, from: &decoder),
judgementsMarkers: decoder.decode(),
offendersMarkers: decoder.decode(),
authorKey: decoder.decode(),
authorIndex: decoder.decode(),
vrfSignature: decoder.decode(),
seal: decoder.decode()
)
Expand All @@ -128,7 +128,7 @@ extension Header: ScaleCodec.Encodable {
try encoder.encode(winningTickets)
try encoder.encode(judgementsMarkers)
try encoder.encode(offendersMarkers)
try encoder.encode(authorKey)
try encoder.encode(authorIndex)
try encoder.encode(vrfSignature)
try encoder.encode(seal)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,36 @@ public struct ValidatorActivityStatistics: Sendable, Equatable {
}
}

public var accumulator: ConfigFixedSizeArray<StatisticsItem, ProtocolConfig.TotalNumberOfValidators>
public var current: ConfigFixedSizeArray<StatisticsItem, ProtocolConfig.TotalNumberOfValidators>
public var accumulator:
ConfigFixedSizeArray<StatisticsItem, ProtocolConfig.TotalNumberOfValidators>
public var previous:
ConfigFixedSizeArray<StatisticsItem, ProtocolConfig.TotalNumberOfValidators>
}

extension ValidatorActivityStatistics: Dummy {
public typealias Config = ProtocolConfigRef
public static func dummy(config: Config) -> ValidatorActivityStatistics {
ValidatorActivityStatistics(
accumulator: ConfigFixedSizeArray(config: config),
current: ConfigFixedSizeArray(config: config)
accumulator: ConfigFixedSizeArray(
config: config, defaultValue: StatisticsItem.dummy(config: config)
),
previous: ConfigFixedSizeArray(
config: config, defaultValue: StatisticsItem.dummy(config: config)
)
)
}
}

extension ValidatorActivityStatistics.StatisticsItem: Dummy {
public typealias Config = ProtocolConfigRef
public static func dummy(config _: Config) -> ValidatorActivityStatistics.StatisticsItem {
ValidatorActivityStatistics.StatisticsItem(
blocks: 0,
tickets: 0,
preimages: 0,
preimagesBytes: 0,
guarantees: 0,
assurances: 0
)
}
}
Expand All @@ -51,13 +71,13 @@ extension ValidatorActivityStatistics: ScaleCodec.Encodable {
public init(config: ProtocolConfigRef, from decoder: inout some ScaleCodec.Decoder) throws {
try self.init(
accumulator: ConfigFixedSizeArray(config: config, from: &decoder),
current: ConfigFixedSizeArray(config: config, from: &decoder)
previous: ConfigFixedSizeArray(config: config, from: &decoder)
)
}

public func encode(in encoder: inout some ScaleCodec.Encoder) throws {
try encoder.encode(accumulator)
try encoder.encode(current)
try encoder.encode(previous)
}
}

Expand Down
10 changes: 10 additions & 0 deletions Utils/Sources/Utils/ConfigLimitedSizeArray.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,16 @@ extension ConfigLimitedSizeArray: RandomAccessCollection {
}
}

public subscript(position: UInt32) -> T {
get {
array[Int(position)]
}
set {
array[Int(position)] = newValue
validate()
}
}

public func index(after i: Int) -> Int {
i + 1
}
Expand Down
1 change: 1 addition & 0 deletions Utils/Sources/Utils/Ed25519.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public struct Ed25519 {
guard let publicKey = try? Curve25519.Signing.PublicKey(rawRepresentation: publicKey.data) else {
return false
}

return publicKey.isValidSignature(signature.data, for: message)
}
}

0 comments on commit 31277d2

Please sign in to comment.