|
| 1 | +///* |
| 2 | +// * Copyright Consensys Software Inc. |
| 3 | +// * |
| 4 | +// * This file is dual-licensed under either the MIT license or Apache License 2.0. |
| 5 | +// * See the LICENSE-MIT and LICENSE-APACHE files in the repository root for details. |
| 6 | +// * |
| 7 | +// * SPDX-License-Identifier: MIT OR Apache-2.0 |
| 8 | +// */ |
| 9 | +//package maru.app |
| 10 | +// |
| 11 | +//import kotlin.time.Duration.Companion.milliseconds |
| 12 | +//import kotlin.time.Duration.Companion.seconds |
| 13 | +//import kotlin.time.toJavaDuration |
| 14 | +//import kotlinx.coroutines.Job |
| 15 | +//import org.apache.logging.log4j.LogManager |
| 16 | +//import org.assertj.core.api.Assertions.assertThat |
| 17 | +//import org.awaitility.kotlin.await |
| 18 | +//import org.hyperledger.besu.tests.acceptance.dsl.condition.net.NetConditions |
| 19 | +//import org.hyperledger.besu.tests.acceptance.dsl.node.ThreadBesuNodeRunner |
| 20 | +//import org.hyperledger.besu.tests.acceptance.dsl.node.cluster.Cluster |
| 21 | +//import org.hyperledger.besu.tests.acceptance.dsl.node.cluster.ClusterConfigurationBuilder |
| 22 | +//import org.hyperledger.besu.tests.acceptance.dsl.transaction.net.NetTransactions |
| 23 | +//import org.junit.jupiter.api.AfterEach |
| 24 | +//import org.junit.jupiter.api.Test |
| 25 | +//import testutils.Checks.getBlockNumber |
| 26 | +//import testutils.PeeringNodeNetworkStack |
| 27 | +////import testutils.TestUtils |
| 28 | +//import testutils.TestUtils.findFreePort |
| 29 | +//import testutils.besu.BesuFactory |
| 30 | +//import testutils.besu.BesuTransactionsHelper |
| 31 | +//import testutils.maru.MaruFactory |
| 32 | +// |
| 33 | +///** |
| 34 | +// * Test suite for Maru peer discovery with multiple nodes. |
| 35 | +// * Tests that multiple Maru nodes can discover each other using discovery protocol. |
| 36 | +// */ |
| 37 | +//class MaruDiscoveryTest2 { |
| 38 | +// private lateinit var cluster: Cluster |
| 39 | +// private val networkStacks = mutableListOf<PeeringNodeNetworkStack>() |
| 40 | +// private val maruApps = mutableListOf<MaruApp>() |
| 41 | +// private lateinit var transactionsHelper: BesuTransactionsHelper |
| 42 | +// private val log = LogManager.getLogger(this.javaClass) |
| 43 | +// private val maruFactory = MaruFactory() |
| 44 | +// private var job: Job? = null |
| 45 | +// |
| 46 | +// @AfterEach |
| 47 | +// fun tearDown() { |
| 48 | +// job?.cancel() |
| 49 | +// job = null |
| 50 | +// maruApps.forEach { app -> |
| 51 | +// try { |
| 52 | +// app.stop() |
| 53 | +// app.close() |
| 54 | +// } catch (e: Exception) { |
| 55 | +// log.warn("Error stopping Maru app", e) |
| 56 | +// } |
| 57 | +// } |
| 58 | +// maruApps.clear() |
| 59 | +// |
| 60 | +// networkStacks.clear() |
| 61 | +// |
| 62 | +// if (::cluster.isInitialized) { |
| 63 | +// cluster.close() |
| 64 | +// } |
| 65 | +// } |
| 66 | +// |
| 67 | +// @Test |
| 68 | +// fun `ten nodes discover each other via bootnode`() { |
| 69 | +// testMultiNodeDiscovery(numberOfNodes = 10, expectedPeers = 9u) |
| 70 | +// } |
| 71 | +// |
| 72 | +// /** |
| 73 | +// * Tests peer discovery with multiple Maru nodes. |
| 74 | +// * |
| 75 | +// * Setup: |
| 76 | +// * - Creates N Besu nodes (EL) and N Maru nodes (CL) |
| 77 | +// * - First Maru node is the bootnode |
| 78 | +// * - All other nodes use the bootnode's ENR for discovery |
| 79 | +// * |
| 80 | +// * @param numberOfNodes Total number of Maru nodes to create |
| 81 | +// * @param expectedPeers Number of peers each node should discover |
| 82 | +// */ |
| 83 | +// private fun testMultiNodeDiscovery( |
| 84 | +// numberOfNodes: Int, |
| 85 | +// expectedPeers: UInt, |
| 86 | +// ) { |
| 87 | +// require(numberOfNodes >= 2) { "Need at least 2 nodes for discovery test" } |
| 88 | +// |
| 89 | +// log.info("Starting peer discovery test with $numberOfNodes nodes") |
| 90 | +// |
| 91 | +// // Initialize test infrastructure |
| 92 | +// transactionsHelper = BesuTransactionsHelper() |
| 93 | +// cluster = |
| 94 | +// Cluster( |
| 95 | +// ClusterConfigurationBuilder().build(), |
| 96 | +// NetConditions(NetTransactions()), |
| 97 | +// ThreadBesuNodeRunner(), |
| 98 | +// ) |
| 99 | +// |
| 100 | +// // Create and start all network stacks (Besu + Maru) |
| 101 | +// repeat(numberOfNodes) { index -> |
| 102 | +// val isValidator = index == 0 // First node is validator, rest are followers |
| 103 | +// val stack = |
| 104 | +// PeeringNodeNetworkStack( |
| 105 | +// besuBuilder = { |
| 106 | +// BesuFactory.buildTestBesu(validator = isValidator) |
| 107 | +// }, |
| 108 | +// ) |
| 109 | +// networkStacks.add(stack) |
| 110 | +// } |
| 111 | +// |
| 112 | +// // Start all Besu nodes (they will automatically peer with each other at EL layer) |
| 113 | +// log.info("Starting ${networkStacks.size} Besu nodes") |
| 114 | +// PeeringNodeNetworkStack.startBesuNodes(cluster, *networkStacks.toTypedArray()) |
| 115 | +// |
| 116 | +// // Wait for Besu nodes to be ready |
| 117 | +// await |
| 118 | +// .atMost(100.seconds.toJavaDuration()) |
| 119 | +// .pollInterval(500.milliseconds.toJavaDuration()) |
| 120 | +// .untilAsserted { |
| 121 | +// networkStacks.forEach { stack -> |
| 122 | +// val blockNumber = stack.besuNode.getBlockNumber() |
| 123 | +// assertThat(blockNumber).isNotNull |
| 124 | +// } |
| 125 | +// } |
| 126 | +// |
| 127 | +// log.info("All Besu nodes are ready") |
| 128 | +// |
| 129 | +// // Create and start the bootnode (validator) |
| 130 | +// val bootnodeStack = networkStacks[0] |
| 131 | +// val bootnodeUdpPort = findFreePort() |
| 132 | +// |
| 133 | +// log.info("Creating bootnode Maru on UDP port $bootnodeUdpPort") |
| 134 | +// val bootnodeMaruApp = |
| 135 | +// maruFactory.buildTestMaruValidatorWithDiscovery( |
| 136 | +// ethereumJsonRpcUrl = bootnodeStack.besuNode.jsonRpcBaseUrl().get(), |
| 137 | +// engineApiRpc = bootnodeStack.besuNode.engineRpcUrl().get(), |
| 138 | +// dataDir = bootnodeStack.tmpDir, |
| 139 | +// discoveryPort = bootnodeUdpPort, |
| 140 | +// allowEmptyBlocks = true, |
| 141 | +// ) |
| 142 | +// |
| 143 | +// bootnodeStack.setMaruApp(bootnodeMaruApp) |
| 144 | +// maruApps.add(bootnodeMaruApp) |
| 145 | +// bootnodeMaruApp.start() |
| 146 | +// |
| 147 | +// // Get bootnode ENR for other nodes to use |
| 148 | +// val bootnodeEnr = bootnodeMaruApp.p2pNetwork.localNodeRecord?.asEnr() |
| 149 | +// requireNotNull(bootnodeEnr) { "Bootnode ENR should not be null" } |
| 150 | +// log.info("Bootnode ENR: $bootnodeEnr") |
| 151 | +// |
| 152 | +// // Start block production on validator |
| 153 | +// log.info("Starting block production on validator") |
| 154 | +// |
| 155 | +// job = |
| 156 | +// TestUtils.startTransactionSendingJob(besuNode = bootnodeStack.besuNode, transactionsHelper = transactionsHelper) |
| 157 | +// |
| 158 | +// try { |
| 159 | +// // Wait for some blocks to be produced |
| 160 | +// await |
| 161 | +// .atMost(20.seconds.toJavaDuration()) |
| 162 | +// .pollInterval(500.milliseconds.toJavaDuration()) |
| 163 | +// .untilAsserted { |
| 164 | +// val blockNumber = bootnodeStack.besuNode.getBlockNumber() |
| 165 | +// assertThat(blockNumber.toLong()).isGreaterThanOrEqualTo(5L) |
| 166 | +// } |
| 167 | +// |
| 168 | +// log.info("Validator is producing blocks") |
| 169 | +// |
| 170 | +// // Create and start follower nodes |
| 171 | +// for (i in 1 until numberOfNodes) { |
| 172 | +// val stack = networkStacks[i] |
| 173 | +// val udpPort = findFreePort() |
| 174 | +// |
| 175 | +// log.info("Creating follower node $i on UDP port $udpPort") |
| 176 | +// val followerMaruApp = |
| 177 | +// maruFactory.buildTestMaruFollowerWithDiscovery( |
| 178 | +// ethereumJsonRpcUrl = stack.besuNode.jsonRpcBaseUrl().get(), |
| 179 | +// engineApiRpc = stack.besuNode.engineRpcUrl().get(), |
| 180 | +// dataDir = stack.tmpDir, |
| 181 | +// bootnode = bootnodeEnr, |
| 182 | +// discoveryPort = udpPort, |
| 183 | +// allowEmptyBlocks = true, |
| 184 | +// ) |
| 185 | +// |
| 186 | +// stack.setMaruApp(followerMaruApp) |
| 187 | +// maruApps.add(followerMaruApp) |
| 188 | +// followerMaruApp.start() |
| 189 | +// } |
| 190 | +// |
| 191 | +// log.info("All $numberOfNodes Maru nodes started") |
| 192 | +// |
| 193 | +// // Wait for all nodes to discover each other |
| 194 | +// log.info("Waiting for all nodes to discover $expectedPeers peers") |
| 195 | +// await |
| 196 | +// .atMost(60.seconds.toJavaDuration()) |
| 197 | +// .pollInterval(2.seconds.toJavaDuration()) |
| 198 | +// .untilAsserted { |
| 199 | +// maruApps.forEachIndexed { index, app -> |
| 200 | +// val peerCount = app.peersConnected() |
| 201 | +// log.info("Node $index has $peerCount peers (expected: $expectedPeers)") |
| 202 | +// assertThat(peerCount).isGreaterThanOrEqualTo(expectedPeers) |
| 203 | +// } |
| 204 | +// } |
| 205 | +// |
| 206 | +// log.info("All nodes have discovered their peers!") |
| 207 | +// |
| 208 | +// // Verify each node can see the others |
| 209 | +// maruApps.forEachIndexed { index, app -> |
| 210 | +// val peers = app.p2pNetwork.getPeers() |
| 211 | +// log.info("Node $index peers: ${peers.map { it.nodeId }}") |
| 212 | +// assertThat(peers.size).isGreaterThanOrEqualTo(expectedPeers.toInt()) |
| 213 | +// } |
| 214 | +// |
| 215 | +// log.info("Verifying followers sync EL locks") |
| 216 | +// val validatorBlockHeight = |
| 217 | +// networkStacks[0].besuNode.getBlockNumber().toLong() |
| 218 | +// |
| 219 | +// await |
| 220 | +// .atMost(30.seconds.toJavaDuration()) |
| 221 | +// .pollInterval(1.seconds.toJavaDuration()) |
| 222 | +// .untilAsserted { |
| 223 | +// networkStacks.forEachIndexed { i, stack -> |
| 224 | +// val followerBlockHeight = |
| 225 | +// stack.besuNode.getBlockNumber().toLong() |
| 226 | +// log.info("Follower $i EL block height: $followerBlockHeight (validator: $validatorBlockHeight)") |
| 227 | +// assertThat(followerBlockHeight).isGreaterThanOrEqualTo(validatorBlockHeight - 2) |
| 228 | +// } |
| 229 | +// } |
| 230 | +// |
| 231 | +// log.info("All followers have synced EL blocks successfully!") |
| 232 | +// } catch (e: Exception) { |
| 233 | +// job?.cancel() |
| 234 | +// throw e |
| 235 | +// } |
| 236 | +// } |
| 237 | +//} |
0 commit comments