Skip to content

Work-report distribution & Work-report request #328

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

Merged
merged 43 commits into from
Apr 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
1235013
update Work-report distribution
MacOMNI Apr 7, 2025
b94eaa1
update workReportRequest
MacOMNI Apr 7, 2025
daa2f98
update WorkReportRef
MacOMNI Apr 8, 2025
3a55564
update blockchain
MacOMNI Apr 8, 2025
c61f810
update workreport
MacOMNI Apr 8, 2025
abf31c4
update swift testing
MacOMNI Apr 8, 2025
64e90d0
update package
MacOMNI Apr 8, 2025
8f2ed97
fix xcode udpate issues
MacOMNI Apr 9, 2025
032e49f
update tests
MacOMNI Apr 9, 2025
fccdc11
update guaranteedWorkReport
MacOMNI Apr 9, 2025
b187b97
update work report
MacOMNI Apr 9, 2025
8d6087c
update db
MacOMNI Apr 9, 2025
876bc86
update OnSyncCompleted
MacOMNI Apr 10, 2025
b50d929
update networkmanager
MacOMNI Apr 11, 2025
3925ab0
update block request
MacOMNI Apr 14, 2025
0212d13
update more tests
MacOMNI Apr 14, 2025
fa89824
update open rpc
MacOMNI Apr 14, 2025
413d107
fix some unstable tests
MacOMNI Apr 14, 2025
bfb8164
fix some issues
MacOMNI Apr 14, 2025
4ff9b78
fix open rpc
MacOMNI Apr 14, 2025
2694049
update OpenRPC
MacOMNI Apr 14, 2025
96c0969
update OpenRPC
MacOMNI Apr 14, 2025
6cba5ef
update open rpc
MacOMNI Apr 15, 2025
645fc48
Merge branch 'master' into dev-JAMNP
MacOMNI Apr 15, 2025
223b7e7
update swiftlint
MacOMNI Apr 15, 2025
2a6476b
update OpenRPC
MacOMNI Apr 15, 2025
d9c93bc
update OpenRPC
MacOMNI Apr 15, 2025
81c2cd9
update vapor
MacOMNI Apr 15, 2025
438a454
update vapor
MacOMNI Apr 15, 2025
c1a3f6c
fix unstable tests
MacOMNI Apr 15, 2025
8b8a7f5
fix unstable tests
MacOMNI Apr 15, 2025
812a01b
fix unstable tests
MacOMNI Apr 16, 2025
6198b3e
update some issues
MacOMNI Apr 16, 2025
de831e8
update networkmanager
MacOMNI Apr 16, 2025
805bde6
update vapor
MacOMNI Apr 16, 2025
b4e1e6b
update vapor
MacOMNI Apr 17, 2025
2456272
update rpc package
MacOMNI Apr 17, 2025
0ed4dbb
update swift test
MacOMNI Apr 17, 2025
c1f6769
update swift pm
MacOMNI Apr 17, 2025
b32f842
update package
MacOMNI Apr 17, 2025
54be471
update swift testing
MacOMNI Apr 17, 2025
38f70df
update more tests
MacOMNI Apr 18, 2025
d3d277a
update swift testing
MacOMNI Apr 21, 2025
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
12 changes: 6 additions & 6 deletions Blockchain/Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Blockchain/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ let package = Package(
.package(path: "../Utils"),
.package(path: "../TracingUtils"),
.package(path: "../PolkaVM"),
.package(url: "https://github.com/apple/swift-testing.git", branch: "0.10.0"),
.package(url: "https://github.com/apple/swift-testing.git", branch: "6.0.0"),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
Expand Down
5 changes: 4 additions & 1 deletion Blockchain/Sources/Blockchain/Blockchain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ public final class Blockchain: ServiceBase, @unchecked Sendable {
logger.debug("block already imported", metadata: ["hash": "\(block.hash)"])
return
}

// TODO: if current block is light
// check if dataProvider.hasGuaranteedWorkReport
// send workReportDistribution waiting for response
// save to full block
try await withSpan("importBlock") { span in
span.attributes.blockHash = block.hash.description

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,18 @@ public actor BlockchainDataProvider {
}

extension BlockchainDataProvider {
public func hasGuaranteedWorkReport(hash: Data32) async throws -> Bool {
try await dataProvider.hasGuaranteedWorkReport(hash: hash)
}

public func getGuaranteedWorkReport(hash: Data32) async throws -> GuaranteedWorkReportRef? {
try await dataProvider.getGuaranteedWorkReport(hash: hash)
}

public func add(guaranteedWorkReport: GuaranteedWorkReportRef) async throws {
try await dataProvider.add(guaranteedWorkReport: guaranteedWorkReport)
}

public func hasBlock(hash: Data32) async throws -> Bool {
try await dataProvider.hasBlock(hash: hash)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Utils

public protocol BlockchainDataProviderProtocol: Sendable {
func hasGuaranteedWorkReport(hash: Data32) async throws -> Bool
func hasBlock(hash: Data32) async throws -> Bool
func hasState(hash: Data32) async throws -> Bool
func isHead(hash: Data32) async throws -> Bool
Expand All @@ -9,6 +10,8 @@ public protocol BlockchainDataProviderProtocol: Sendable {

func getHeader(hash: Data32) async throws -> HeaderRef?

func getGuaranteedWorkReport(hash: Data32) async throws -> GuaranteedWorkReportRef?

func getBlock(hash: Data32) async throws -> BlockRef?

func getState(hash: Data32) async throws -> StateRef?
Expand All @@ -26,14 +29,15 @@ public protocol BlockchainDataProviderProtocol: Sendable {
/// return empty set if not found
func getBlockHash(byNumber number: UInt32) async throws -> Set<Data32>

func add(guaranteedWorkReport: GuaranteedWorkReportRef) async throws
func add(block: BlockRef) async throws
func add(state: StateRef) async throws
func setFinalizedHead(hash: Data32) async throws

/// throw BlockchainDataProviderError.noData if parent is not a head
func updateHead(hash: Data32, parent: Data32) async throws

/// remove header, block and state
/// remove header, block, workReport, state
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

best to have a remove(blockHash: Data32) and a remove(workReportHash: Data32) to ensure we don't mix different kinds of hashes

func remove(hash: Data32) async throws

var genesisBlockHash: Data32 { get }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public actor InMemoryDataProvider {
private var blockByHash: [Data32: BlockRef] = [:]
private var stateByBlockHash: [Data32: StateRef] = [:]
private var hashByTimeslot: [TimeslotIndex: Set<Data32>] = [:]
private var guaranteedWorkReports: [Data32: GuaranteedWorkReportRef] = [:]
public let genesisBlockHash: Data32

public init(genesisState: StateRef, genesisBlock: BlockRef) async {
Expand All @@ -22,6 +23,18 @@ public actor InMemoryDataProvider {
}

extension InMemoryDataProvider: BlockchainDataProviderProtocol {
public func hasGuaranteedWorkReport(hash: Data32) async throws -> Bool {
guaranteedWorkReports[hash] != nil
}

public func getGuaranteedWorkReport(hash: Data32) async throws -> GuaranteedWorkReportRef? {
guaranteedWorkReports[hash]
}

public func add(guaranteedWorkReport: GuaranteedWorkReportRef) async throws {
guaranteedWorkReports[guaranteedWorkReport.value.workReport.hash()] = guaranteedWorkReport
}

public func getKeys(prefix: Data32, count: UInt32, startKey: Data32?, blockHash: Data32?) async throws -> [String] {
guard let stateRef = try getState(hash: blockHash ?? genesisBlockHash) else {
return []
Expand Down Expand Up @@ -120,6 +133,7 @@ extension InMemoryDataProvider: BlockchainDataProviderProtocol {
let timeslot = blockByHash[hash]?.header.timeslot ?? stateByBlockHash[hash]?.value.timeslot
stateByBlockHash.removeValue(forKey: hash)
blockByHash.removeValue(forKey: hash)
guaranteedWorkReports.removeValue(forKey: hash)

if let timeslot {
hashByTimeslot[timeslot]?.remove(hash)
Expand Down
28 changes: 28 additions & 0 deletions Blockchain/Sources/Blockchain/BlockchainServices.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
private var _guaranteeingService: GuaranteeingService?
private weak var _guaranteeingServiceRef: GuaranteeingService?

private var _dataAvailabilityService: DataAvailabilityService?
private weak var _dataAvailabilityServiceRef: DataAvailabilityService?

private let schedulerService: ServiceBase2

public init(
Expand Down Expand Up @@ -63,6 +66,7 @@
_blockchain = nil
_blockAuthor = nil
_guaranteeingService = nil
_dataAvailabilityService = nil

if let _blockchainRef {
fatalError("BlockchainServices: blockchain still alive. retain count: \(_getRetainCount(_blockchainRef))")
Expand All @@ -75,6 +79,30 @@
if let _guaranteeingServiceRef {
fatalError("BlockchainServices: guaranteeingService still alive. retain count: \(_getRetainCount(_guaranteeingServiceRef))")
}

if let _dataAvailabilityServiceRef {
fatalError(
"BlockchainServices: dataAvailabilityService still alive. retain count: \(_getRetainCount(_dataAvailabilityServiceRef))"
)
}

Check warning on line 87 in Blockchain/Sources/Blockchain/BlockchainServices.swift

View check run for this annotation

Codecov / codecov/patch

Blockchain/Sources/Blockchain/BlockchainServices.swift#L84-L87

Added lines #L84 - L87 were not covered by tests
}

public var dataAvailabilityService: DataAvailabilityService {
get async {
if let _dataAvailabilityService {
return _dataAvailabilityService

Check warning on line 93 in Blockchain/Sources/Blockchain/BlockchainServices.swift

View check run for this annotation

Codecov / codecov/patch

Blockchain/Sources/Blockchain/BlockchainServices.swift#L93

Added line #L93 was not covered by tests
}
_dataAvailabilityService = await DataAvailabilityService(
config: config,
eventBus: eventBus,
scheduler: scheduler,
dataProvider: dataProvider,
dataStore: dataStore
)
_dataAvailabilityServiceRef = _dataAvailabilityService
await _dataAvailabilityService!.onSyncCompleted()
return _dataAvailabilityService!
}
}

public var blockchain: Blockchain {
Expand Down
32 changes: 15 additions & 17 deletions Blockchain/Sources/Blockchain/RuntimeProtocols/RuntimeEvents.swift
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,21 @@ public enum RuntimeEvents {
}
}

public struct WorkReportReceivedResponse: Event {
public let workReportHash: Data32
public let result: Result<Void, Error>

public init(workReportHash: Data32) {
self.workReportHash = workReportHash
result = .success(())
}

public init(workReportHash: Data32, error: Error) {
self.workReportHash = workReportHash
result = .failure(error)
}
}

public struct WorkReportRequestReceived: Event {
public let workReportHash: Data32

Expand Down Expand Up @@ -275,23 +290,6 @@ public enum RuntimeEvents {
}
}

// Response to work report request
public struct WorkReportRequestResponse: Event {
public var workReportHash: Data32

public let result: Result<WorkReport, Error>

public init(workReportHash: Data32, workReport: WorkReport) {
self.workReportHash = workReportHash
result = .success(workReport)
}

public init(workReportHash: Data32, error: Error) {
self.workReportHash = workReportHash
result = .failure(error)
}
}

public struct AuditShardRequestReceived: Event {
public let erasureRoot: Data32
public let shardIndex: UInt32
Expand Down
42 changes: 42 additions & 0 deletions Blockchain/Sources/Blockchain/Types/GuaranteedWorkReport.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import Codec
import Foundation
import Utils

public struct GuaranteedWorkReport: Sendable, Equatable, Codable, Hashable {
public let workReport: WorkReport
public let slot: UInt32
public let signatures: [ValidatorSignature]

public init(
workReport: WorkReport,
slot: UInt32,
signatures: [ValidatorSignature]
) {
self.workReport = workReport
self.slot = slot
self.signatures = signatures
}
}

extension GuaranteedWorkReport: Hashable32 {
public func hash() -> Data32 {
try! JamEncoder.encode(self).blake2b256hash()
}
}

extension GuaranteedWorkReport: Dummy {
public typealias Config = ProtocolConfigRef
public static func dummy(config: Config) -> GuaranteedWorkReport {
GuaranteedWorkReport(
workReport: WorkReport.dummy(config: config),
slot: 0,
signatures: []
)
}
}

extension GuaranteedWorkReport {
public func asRef() -> GuaranteedWorkReportRef {
GuaranteedWorkReportRef(self)
}
}
22 changes: 22 additions & 0 deletions Blockchain/Sources/Blockchain/Types/GuaranteedWorkReportRef.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Codec
import Foundation
import Utils

public final class GuaranteedWorkReportRef: RefWithHash<GuaranteedWorkReport>, @unchecked Sendable {
public var workReport: WorkReport { value.workReport }
public var slot: UInt32 { value.slot }
public var signatures: [ValidatorSignature] { value.signatures }
override public var description: String {
"GuaranteedWorkReport(hash: \(workReport.hash()), timeslot: \(slot))"
}

Check warning on line 11 in Blockchain/Sources/Blockchain/Types/GuaranteedWorkReportRef.swift

View check run for this annotation

Codecov / codecov/patch

Blockchain/Sources/Blockchain/Types/GuaranteedWorkReportRef.swift#L7-L11

Added lines #L7 - L11 were not covered by tests
}

extension GuaranteedWorkReportRef: Codable {
public convenience init(from decoder: Decoder) throws {
try self.init(.init(from: decoder))
}

public func encode(to encoder: Encoder) throws {
try value.encode(to: encoder)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@
case invalidSegmentsRoot
case invalidDataLength
case pagedProofsGenerationError
case invalidWorkReportSlot
case invalidWorkReport
case insufficientSignatures
}

public final class DataAvailabilityService: ServiceBase2, @unchecked Sendable {
public final class DataAvailabilityService: ServiceBase2, @unchecked Sendable, OnSyncCompleted {
private let dataProvider: BlockchainDataProvider
private let dataStore: DataStore

Expand All @@ -40,6 +43,16 @@
}
}

public func onSyncCompleted() async {
await subscribe(RuntimeEvents.WorkReportReceived.self, id: "DataAvailabilityService.WorkReportReceived") { [weak self] event in
await self?.handleWorkReportReceived(event)
}
}

public func handleWorkReportReceived(_ event: RuntimeEvents.WorkReportReceived) async {
await workReportDistribution(workReport: event.workReport, slot: event.slot, signatures: event.signatures)
}

/// Purge old data from the data availability stores
/// - Parameter epoch: The current epoch index
public func purge(epoch _: EpochIndex) async {
Expand Down Expand Up @@ -180,6 +193,49 @@
true
}

// MARK: - Work-report Distribution (CE 135)

public func workReportDistribution(
workReport: WorkReport,
slot: UInt32,
signatures: [ValidatorSignature]
) async {
let hash = workReport.hash()

do {
// verify slot
if await isSlotValid(slot) {
throw DataAvailabilityError.invalidWorkReportSlot

Check warning on line 208 in Blockchain/Sources/Blockchain/Validator/DataAvailabilityService.swift

View check run for this annotation

Codecov / codecov/patch

Blockchain/Sources/Blockchain/Validator/DataAvailabilityService.swift#L208

Added line #L208 was not covered by tests
}
// verify signatures
try await validate(signatures: signatures)

// store guaranteedWorkReport
let report = GuaranteedWorkReport(
workReport: workReport,
slot: slot,
signatures: signatures
)
try await dataProvider.add(guaranteedWorkReport: GuaranteedWorkReportRef(report))
// response success result
publish(RuntimeEvents.WorkReportReceivedResponse(workReportHash: hash))
} catch {
publish(RuntimeEvents.WorkReportReceivedResponse(workReportHash: hash, error: error))
}
}

private func isSlotValid(_ slot: UInt32) async -> Bool {
let currentSlot = await dataProvider.bestHead.timeslot
return slot + 5 >= currentSlot && slot <= currentSlot + 3
}

private func validate(signatures: [ValidatorSignature]) async throws {
guard signatures.count >= 3 else {
throw DataAvailabilityError.insufficientSignatures
}
// TODO: more validates
}

// MARK: - Shard Distribution (CE 137)

/// Distribute shards to validators
Expand Down
Loading