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

multiplePeers tests #265

Draft
wants to merge 20 commits into
base: master
Choose a base branch
from
3 changes: 1 addition & 2 deletions Blockchain/Sources/Blockchain/Blockchain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,7 @@ public final class Blockchain: ServiceBase, @unchecked Sendable {
try await dataProvider.blockImported(block: block, state: state)

publish(RuntimeEvents.BlockImported(block: block, state: state, parentState: parent))

logger.info("Block imported: #\(block.header.timeslot) \(block.hash)")
logger.debug(" Import block: #\(block.header.timeslot) \(block.hash)")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public actor BlockchainDataProvider: Sendable {
bestHead = HeadInfo(hash: block.hash, timeslot: block.header.timeslot, number: number)
}

logger.debug("block imported: \(block.hash)")
logger.debug("Block imported: #\(bestHead.timeslot) \(block.hash)")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ extension Accumulation {

let rightQueueItems = accumulationQueue.array[index...]
let leftQueueItems = accumulationQueue.array[0 ..< index]
var allQueueItems = rightQueueItems.flatMap { $0 } + leftQueueItems.flatMap { $0 } + newQueueItems
var allQueueItems = rightQueueItems.flatMap(\.self) + leftQueueItems.flatMap(\.self) + newQueueItems

editAccumulatedItems(items: &allQueueItems, accumulatedPackages: Set(zeroPrereqReports.map(\.packageSpecification.workPackageHash)))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,8 @@ extension Guaranteeing {
}

let recentWorkPackageHashes: Set<Data32> = Set(recentHistory.items.flatMap(\.lookup.keys))
let accumulateHistoryReports = Set(accumulationHistory.array.flatMap { $0 })
let accumulateQueueReports = Set(accumulationQueue.array.flatMap { $0 }
let accumulateHistoryReports = Set(accumulationHistory.array.flatMap(\.self))
let accumulateQueueReports = Set(accumulationQueue.array.flatMap(\.self)
.flatMap(\.workReport.refinementContext.prerequisiteWorkPackages))
let pendingWorkReportHashes = Set(reports.array.flatMap { $0?.workReport.refinementContext.prerequisiteWorkPackages ?? [] })
let pipelinedWorkReportHashes = recentWorkPackageHashes.union(accumulateHistoryReports).union(accumulateQueueReports)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -909,7 +909,7 @@ public class Invoke: HostCall {
self.context = context
}

public func _callImpl(config: ProtocolConfigRef, state: VMState) async throws {
public func _callImpl(config _: ProtocolConfigRef, state: VMState) async throws {
let pvmIndex: UInt64 = state.readRegister(Registers.Index(raw: 7))
let startAddr: UInt32 = state.readRegister(Registers.Index(raw: 8))

Expand Down
12 changes: 12 additions & 0 deletions Node/.swiftpm/xcode/xcshareddata/xcschemes/Node.xcscheme
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NodeTests"
BuildableName = "NodeTests"
BlueprintName = "NodeTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
Expand Down
101 changes: 76 additions & 25 deletions Node/Tests/NodeTests/NodeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,22 @@ import Utils
@testable import Node

final class NodeTests {
var dataBaseIndex: Int = 0;

let path = {
let tmpDir = FileManager.default.temporaryDirectory
return tmpDir.appendingPathComponent("\(UUID().uuidString)")
}()

func getDatabase(_ idx: Int) -> Database {
Database.rocksDB(path: path.appendingPathComponent("\(idx)"))
func getDatabase() -> Database {
Copy link
Member

@xlc xlc Jan 10, 2025

Choose a reason for hiding this comment

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

Suggested change
func getDatabase() -> Database {
func getDatabase(_ idx: Int? = nil) -> Database {
let idx = idx ?? Int.random(in: 0..<Int.max)

dataBaseIndex += 1;
return Database.rocksDB(path: path.appendingPathComponent("\(dataBaseIndex)"))
}

deinit {
try? FileManager.default.removeItem(at: path)
}

@Test
func validatorNodeInMemory() async throws {
@Test func validatorNodeInMemory() async throws {
let (nodes, scheduler) = try await Topology(
nodes: [NodeDescription(isValidator: true)]
).build(genesis: .preset(.minimal))
Expand All @@ -32,7 +33,7 @@ final class NodeTests {
let initialTimeslot = initialBestHead.timeslot

// Advance time
for _ in 0 ..< 10 {
for _ in 0..<10 {
await scheduler.advance(by: TimeInterval(validatorNode.blockchain.config.value.slotPeriodSeconds))
await storeMiddlware.wait()
}
Expand All @@ -49,10 +50,9 @@ final class NodeTests {
#expect(try await validatorNode.blockchain.dataProvider.hasBlock(hash: newBestHead.hash))
}

@Test
func validatorNodeRocksDB() async throws {
@Test func validatorNodeRocksDB() async throws {
let (nodes, scheduler) = try await Topology(
nodes: [NodeDescription(isValidator: true, database: getDatabase(0))]
nodes: [NodeDescription(isValidator: true, database: getDatabase())]
).build(genesis: .preset(.minimal))

let (validatorNode, storeMiddlware) = nodes[0]
Expand All @@ -62,7 +62,7 @@ final class NodeTests {
let initialTimeslot = initialBestHead.timeslot

// Advance time
for _ in 0 ..< 10 {
for _ in 0..<10 {
await scheduler.advance(by: TimeInterval(validatorNode.blockchain.config.value.slotPeriodSeconds))
await storeMiddlware.wait()
}
Expand All @@ -79,13 +79,12 @@ final class NodeTests {
#expect(try await validatorNode.blockchain.dataProvider.hasBlock(hash: newBestHead.hash))
}

@Test
func sync() async throws {
@Test func sync() async throws {
// Create validator and full node
let (nodes, scheduler) = try await Topology(
nodes: [
NodeDescription(isValidator: true, database: getDatabase(0)),
NodeDescription(devSeed: 1, database: getDatabase(1)),
NodeDescription(isValidator: true, database: getDatabase()),
NodeDescription(devSeed: 1, database: getDatabase()),
],
connections: [(0, 1)]
).build(genesis: .preset(.minimal))
Expand All @@ -94,7 +93,7 @@ final class NodeTests {
let (node, nodeStoreMiddlware) = nodes[1]

// Advance time to produce blocks
for _ in 0 ..< 10 {
for _ in 0..<10 {
await scheduler.advance(by: TimeInterval(validatorNode.blockchain.config.value.slotPeriodSeconds))
await validatorStoreMiddlware.wait()
await nodeStoreMiddlware.wait()
Expand All @@ -110,7 +109,7 @@ final class NodeTests {
#expect(validatorBestHead.hash == nodeBestHead.hash)

// Produce more blocks
for _ in 0 ..< 10 {
for _ in 0..<10 {
await scheduler.advance(by: TimeInterval(validatorNode.blockchain.config.value.slotPeriodSeconds))
await validatorStoreMiddlware.wait()
await nodeStoreMiddlware.wait()
Expand All @@ -129,14 +128,13 @@ final class NodeTests {
#expect(newValidatorBestHead.timeslot > validatorBestHead.timeslot)
}

@Test
func multiplePeers() async throws {
@Test func multiplePeers() async throws {
// Create multiple nodes
let (nodes, scheduler) = try await Topology(
nodes: [
NodeDescription(isValidator: true, database: getDatabase(0)),
NodeDescription(isValidator: true, devSeed: 1, database: getDatabase(1)),
NodeDescription(devSeed: 2, database: getDatabase(2)),
NodeDescription(isValidator: true, database: getDatabase()),
NodeDescription(isValidator: true, devSeed: 1, database: getDatabase()),
NodeDescription(devSeed: 2, database: getDatabase()),
NodeDescription(devSeed: 3, database: .inMemory),
],
connections: [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3)]
Expand All @@ -147,30 +145,83 @@ final class NodeTests {
let (node1, node1StoreMiddlware) = nodes[2]
let (node2, node2StoreMiddlware) = nodes[3]

try await Task.sleep(for: .milliseconds(500))
try await Task.sleep(for: .milliseconds(nodes.count * 200))

// Verify connections
#expect(node1.network.peersCount == 2)
#expect(node2.network.peersCount == 2)

// Advance time and verify sync
for _ in 0 ..< 10 {
for _ in 0..<10 {
await scheduler.advance(by: TimeInterval(validator1.blockchain.config.value.slotPeriodSeconds))
await validator1StoreMiddlware.wait()
await validator2StoreMiddlware.wait()
await node1StoreMiddlware.wait()
await node2StoreMiddlware.wait()
}

try await Task.sleep(for: .milliseconds(1000))
try await Task.sleep(for: .milliseconds(nodes.count * 500))

let validator1BestHead = await validator1.dataProvider.bestHead
let validator2BestHead = await validator2.dataProvider.bestHead
let node1BestHead = await node1.dataProvider.bestHead
let node2BestHead = await node2.dataProvider.bestHead

#expect(validator1BestHead.hash == node1BestHead.hash)
#expect(validator1BestHead.hash == node2BestHead.hash)
#expect(validator2BestHead.hash == node1BestHead.hash)
}

@Test func moreMultiplePeers() async throws {
// Create multiple nodes
var nodeDescriptions: [NodeDescription] = [
NodeDescription(isValidator: true, database: getDatabase()),
NodeDescription(isValidator: true, devSeed: 1, database: getDatabase()),
]

// Add 18 non-validator nodes
for i in 2...19 {
nodeDescriptions.append(NodeDescription(devSeed: UInt32(i), database: .inMemory))
}

let (nodes, scheduler) = try await Topology(
nodes: nodeDescriptions,
connections: (0..<20).flatMap { i in
(i + 1..<20).map { j in (i, j) } // Fully connected topology
}
).build(genesis: .preset(.minimal))

let (validator1, validator1StoreMiddlware) = nodes[0]
let (validator2, validator2StoreMiddlware) = nodes[1]

// Extract non-validator nodes and their middleware
let nonValidatorNodes = nodes[2...].map(\.self)

try await Task.sleep(for: .milliseconds(nodes.count * 100))
let (node1, _ ) = nonValidatorNodes[0]
let (node2, _ ) = nonValidatorNodes[1]
// Verify connections for a sample of non-validator nodes
#expect(node1.network.peersCount == 19)
#expect(node2.network.peersCount == 19)
// Advance time and verify sync
for _ in 0..<10 {
await scheduler.advance(by: TimeInterval(validator1.blockchain.config.value.slotPeriodSeconds))
await validator1StoreMiddlware.wait()
await validator2StoreMiddlware.wait()

for (_, middleware) in nonValidatorNodes {
await middleware.wait()
}
}

try await Task.sleep(for: .milliseconds(nodes.count * 500))

let validator1BestHead = await validator1.dataProvider.bestHead
let validator2BestHead = await validator2.dataProvider.bestHead

for (node, _) in nonValidatorNodes {
let nodeBestHead = await node.dataProvider.bestHead
#expect(validator1BestHead.hash == nodeBestHead.hash)
#expect(validator2BestHead.hash == nodeBestHead.hash)
}
}
}
3 changes: 2 additions & 1 deletion Node/Tests/NodeTests/Topology.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ struct Topology {
for (from, to) in connections {
let fromNode = ret[from].0
let toNode = ret[to].0
_ = try fromNode.network.network.connect(to: toNode.network.network.listenAddress(), role: .validator)
let conn = try fromNode.network.network.connect(to: toNode.network.network.listenAddress(), role: .validator)
try? await conn.ready()
}
return (ret, scheduler)
}
Expand Down
2 changes: 1 addition & 1 deletion Utils/Sources/Utils/Merklization/MMR.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public struct MMR: Sendable, Equatable, Codable {
}
}

let nonNilPeaks = peaks.compactMap { $0 }
let nonNilPeaks = peaks.compactMap(\.self)
return helper(nonNilPeaks[...])
}
}
Loading