Skip to content

Commit

Permalink
header validation (#73)
Browse files Browse the repository at this point in the history
  • Loading branch information
xlc authored Aug 23, 2024
1 parent 325021d commit 0b0ca7a
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ extension InMemoryDataProvider: BlockchainDataProvider {

public func add(block: BlockRef) {
blockByHash[block.hash] = block
hashByTimeslot[block.header.timeslotIndex, default: Set()].insert(block.hash)
hashByTimeslot[block.header.timeslot, default: Set()].insert(block.hash)
}

public func setFinalizedHead(hash: Data32) {
Expand All @@ -85,7 +85,7 @@ extension InMemoryDataProvider: BlockchainDataProvider {
}

public func remove(hash: Data32) {
let timeslot = blockByHash[hash]?.header.timeslotIndex ?? stateByBlockHash[hash]?.value.timeslot
let timeslot = blockByHash[hash]?.header.timeslot ?? stateByBlockHash[hash]?.value.timeslot
stateByBlockHash.removeValue(forKey: hash)

if let timeslot {
Expand Down
56 changes: 49 additions & 7 deletions Blockchain/Sources/Blockchain/Runtime.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Codec
import Utils

// the STF
Expand All @@ -6,6 +7,12 @@ public final class Runtime {
case safroleError(SafroleError)
case invalidTimeslot
case invalidReportAuthorizer
case unableToComputeExtrinsicHash(any Swift.Error)
case invalidExtrinsicHash
case invalidParentHash
case invalidHeaderStateRoot
case invalidHeaderEpochMarker
case invalidHeaderWinningTickets
case other(any Swift.Error)
}

Expand All @@ -23,12 +30,39 @@ public final class Runtime {
self.config = config
}

public func validate(block: BlockRef, state _: StateRef, context: ApplyContext) throws(Error) {
guard context.timeslot >= block.header.timeslotIndex else {
public func validateHeader(block: BlockRef, state: StateRef, context: ApplyContext) throws(Error) {
guard block.header.parentHash == state.value.lastBlockHash else {
throw Error.invalidParentHash
}

guard block.header.priorStateRoot == state.stateRoot else {
throw Error.invalidHeaderStateRoot
}

let expectedExtrinsicHash = try Result { try blake2b256(JamEncoder.encode(block.extrinsic)) }
.mapError(Error.unableToComputeExtrinsicHash).get()

guard block.header.extrinsicsHash == expectedExtrinsicHash else {
throw Error.invalidExtrinsicHash
}

guard block.header.timeslot <= context.timeslot else {
throw Error.invalidTimeslot
}

// epoch is validated at apply time

// winning tickets is validated at apply time

// TODO: validate judgementsMarkers
// TODO: validate offendersMarkers

// TODO: validate block.header.seal
}

public func validate(block: BlockRef, state: StateRef, context: ApplyContext) throws(Error) {
try validateHeader(block: block, state: state, context: context)

// TODO: abstract input validation logic from Safrole state update function and call it here
// TODO: validate other things
}
Expand All @@ -41,10 +75,18 @@ public final class Runtime {
do {
newState.recentHistory = try updateRecentHistory(block: block, state: prevState)

let res = try newState.updateSafrole(
config: config, slot: block.header.timeslotIndex, entropy: newState.entropyPool.t0, extrinsics: block.extrinsic.tickets
let safroleResult = try newState.updateSafrole(
config: config, slot: block.header.timeslot, entropy: newState.entropyPool.t0, extrinsics: block.extrinsic.tickets
)
newState.mergeWith(postState: res.state)
newState.mergeWith(postState: safroleResult.state)

guard safroleResult.epochMark == block.header.epoch else {
throw Error.invalidHeaderEpochMarker
}

guard safroleResult.ticketsMark == block.header.winningTickets else {
throw Error.invalidHeaderWinningTickets
}

newState.coreAuthorizationPool = try updateAuthorizationPool(
block: block, state: prevState
Expand Down Expand Up @@ -102,7 +144,7 @@ public final class Runtime {
if coreQueue.count == 0 {
continue
}
let newItem = coreQueue[Int(block.header.timeslotIndex) % coreQueue.count]
let newItem = coreQueue[Int(block.header.timeslot) % coreQueue.count]

// remove used authorizers from pool
for report in block.extrinsic.reports.guarantees {
Expand All @@ -126,7 +168,7 @@ public final class Runtime {
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 newEpoch = block.header.timeslot / epochLength
let isEpochChange = currentEpoch != newEpoch

var acc = try isEpochChange
Expand Down
20 changes: 10 additions & 10 deletions Blockchain/Sources/Blockchain/Types/Header.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ public struct Header: Sendable, Equatable, Codable {
public var priorStateRoot: Data32 // state root of the after parent block execution

// Hx: extrinsic hash
public var extrinsicsRoot: Data32
public var extrinsicsHash: Data32

// Ht: timeslot index
public var timeslotIndex: TimeslotIndex
public var timeslot: TimeslotIndex

// He: the epoch
// the header’s epoch marker He is either empty or, if the block is the first in a new epoch,
Expand Down Expand Up @@ -47,8 +47,8 @@ public struct Header: Sendable, Equatable, Codable {
public init(
parentHash: Data32,
priorStateRoot: Data32,
extrinsicsRoot: Data32,
timeslotIndex: TimeslotIndex,
extrinsicsHash: Data32,
timeslot: TimeslotIndex,
epoch: EpochMarker?,
winningTickets: ConfigFixedSizeArray<
Ticket,
Expand All @@ -61,8 +61,8 @@ public struct Header: Sendable, Equatable, Codable {
) {
self.parentHash = parentHash
self.priorStateRoot = priorStateRoot
self.extrinsicsRoot = extrinsicsRoot
self.timeslotIndex = timeslotIndex
self.extrinsicsHash = extrinsicsHash
self.timeslot = timeslot
self.epoch = epoch
self.winningTickets = winningTickets
self.judgementsMarkers = judgementsMarkers
Expand Down Expand Up @@ -97,8 +97,8 @@ extension Header.Unsigned: Dummy {
Header.Unsigned(
parentHash: Data32(),
priorStateRoot: Data32(),
extrinsicsRoot: Data32(),
timeslotIndex: 0,
extrinsicsHash: Data32(),
timeslot: 0,
epoch: nil,
winningTickets: nil,
judgementsMarkers: [],
Expand Down Expand Up @@ -130,8 +130,8 @@ extension Header {

public var parentHash: Data32 { unsigned.parentHash }
public var priorStateRoot: Data32 { unsigned.priorStateRoot }
public var extrinsicsRoot: Data32 { unsigned.extrinsicsRoot }
public var timeslotIndex: TimeslotIndex { unsigned.timeslotIndex }
public var extrinsicsHash: Data32 { unsigned.extrinsicsHash }
public var timeslot: TimeslotIndex { unsigned.timeslot }
public var epoch: EpochMarker? { unsigned.epoch }
public var winningTickets: ConfigFixedSizeArray<Ticket, ProtocolConfig.EpochLength>? { unsigned.winningTickets }
public var judgementsMarkers: [Data32] { unsigned.judgementsMarkers }
Expand Down

0 comments on commit 0b0ca7a

Please sign in to comment.