Skip to content

Commit 416283d

Browse files
committed
Merge branch 'main' of github.com:Consensys/maru into appDiscoveryTest
2 parents 56ab45c + d18be92 commit 416283d

File tree

5 files changed

+118
-8
lines changed

5 files changed

+118
-8
lines changed

config/src/main/kotlin/maru/config/Config.kt

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,7 @@ data class P2PConfig(
5252
val gossiping: Gossiping = Gossiping(),
5353
) {
5454
init {
55-
// just a sanity check to ensure the IP address is valid
56-
InetAddress.getByName(ipAddress)
55+
validateIpAddress(ipAddress)
5756
require(reputation.smallChange > 0) {
5857
"smallChange must be a positive number"
5958
}
@@ -66,7 +65,27 @@ data class P2PConfig(
6665
val port: UInt = 9000u,
6766
val bootnodes: List<String> = emptyList(),
6867
val refreshInterval: Duration,
69-
)
68+
val advertisedIp: String? = null,
69+
) {
70+
init {
71+
advertisedIp?.let { validateIpAddress(it) }
72+
}
73+
}
74+
75+
companion object {
76+
private fun validateIpAddress(ip: String) {
77+
require(ip.isNotBlank()) {
78+
"IP address must not be blank"
79+
}
80+
// InetAddress.getByName accepts both IP addresses and hostnames.
81+
// We need to ensure it's actually an IP address by checking that
82+
// the parsed address matches the input (no DNS resolution occurred)
83+
val address = InetAddress.getByName(ip)
84+
require(address.hostAddress == ip) {
85+
"Invalid IP address format: $ip"
86+
}
87+
}
88+
}
7089

7190
data class StatusUpdate(
7291
val refreshInterval: Duration = 30.seconds,

config/src/test/kotlin/maru/config/HopliteFriendlinessTest.kt

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ class HopliteFriendlinessTest {
4848
port = 3324
4949
bootnodes = ["enr:-Iu4QHk0YN5IRRnufqsWkbO6Tn0iGTx4H_hnyiIEdXDuhIe0KKrxmaECisyvO40mEmmqKLhz_tdIhx2yFBK8XFKhvxABgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQOgBvD-dv0cX5szOeEsiAMtwxnP1q5CA5toYDrgUyOhV4N0Y3CCJBKDdWRwgiQT"]
5050
refresh-interval = "30 seconds"
51+
advertised-ip = "13.12.11.10"
5152
5253
[p2p.reputation]
5354
small-change = 1
@@ -116,6 +117,7 @@ class HopliteFriendlinessTest {
116117
"enr:-Iu4QHk0YN5IRRnufqsWkbO6Tn0iGTx4H_hnyiIEdXDuhIe0KKrxmaECisyvO40mEmmqKLhz_tdIhx2yFBK8XFKhvxABgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQOgBvD-dv0cX5szOeEsiAMtwxnP1q5CA5toYDrgUyOhV4N0Y3CCJBKDdWRwgiQT",
117118
),
118119
refreshInterval = 30.seconds,
120+
advertisedIp = "13.12.11.10",
119121
),
120122
reputation =
121123
P2PConfig.Reputation(
@@ -652,4 +654,49 @@ class HopliteFriendlinessTest {
652654
),
653655
)
654656
}
657+
658+
@Test
659+
fun `p2p discovery with invalid advertisedIp format should fail`() {
660+
val discoveryConfigToml =
661+
"""
662+
port = 9000
663+
refresh-interval = "30 seconds"
664+
advertised-ip = "127.0.256.1"
665+
""".trimIndent()
666+
667+
assertThatThrownBy {
668+
parseConfig<P2PConfig.Discovery>(discoveryConfigToml)
669+
}.isInstanceOf(ConfigException::class.java)
670+
.hasMessageContaining("UnknownHostException")
671+
.hasMessageContaining("127.0.256.1")
672+
}
673+
674+
@Test
675+
fun `p2p config with invalid ipAddress format should fail`() {
676+
val p2pConfigToml =
677+
"""
678+
ip-address = "127.O.0H.1"
679+
""".trimIndent()
680+
681+
assertThatThrownBy {
682+
parseConfig<P2PConfig>(p2pConfigToml)
683+
}.isInstanceOf(ConfigException::class.java)
684+
.hasMessageContaining("UnknownHostException")
685+
.hasMessageContaining("127.O.0H.1")
686+
}
687+
688+
@Test
689+
fun `p2p config with valid dns instead of IP format should fail`() {
690+
val p2pConfigToml =
691+
"""
692+
ip-address = "test.com"
693+
""".trimIndent()
694+
695+
assertThatThrownBy {
696+
parseConfig<P2PConfig>(p2pConfigToml)
697+
}.isInstanceOf(ConfigException::class.java)
698+
.hasMessageContaining("IllegalArgumentException")
699+
.hasMessageContaining("Invalid IP address format")
700+
.hasMessageContaining("test.com")
701+
}
655702
}

p2p/src/main/kotlin/maru/p2p/NetworkHelper.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@ object NetworkHelper {
3636
.filter { it is Inet4Address }
3737
.map { it.hostAddress }
3838

39-
fun hasInterfaceWithIpV4(ipV4: String): Boolean = listIpsV4(excludeLoopback = false).contains(ipV4)
40-
4139
fun selectIpV4ForP2P(
4240
targetIpV4: String,
4341
excludeLoopback: Boolean = true,

p2p/src/main/kotlin/maru/p2p/discovery/MaruDiscoveryService.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,9 +223,9 @@ class MaruDiscoveryService(
223223
.secretKey(privateKey)
224224
.seq(UInt64.valueOf(sequenceNumber.toBigInteger()))
225225
.address(
226-
p2pConfig.ipAddress,
227-
p2pConfig.discovery!!.port.toInt(),
228-
p2pConfig.port.toInt(),
226+
/* ipAddress = */ p2pConfig.discovery!!.advertisedIp ?: p2pConfig.ipAddress,
227+
/* udpPort = */ p2pConfig.discovery!!.port.toInt(),
228+
/* tcpPort = */ p2pConfig.port.toInt(),
229229
).customField(FORK_ID_HASH_FIELD_NAME, Bytes.wrap(forkIdHashManager.currentForkHash()))
230230
// TODO: do we want more custom fields to identify version/topics/role/something else?
231231

p2p/src/test/kotlin/maru/p2p/discovery/MaruDiscoveryServiceTest.kt

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,4 +293,50 @@ class MaruDiscoveryServiceTest {
293293

294294
assertThat(result).isFalse()
295295
}
296+
297+
@Test
298+
fun `local node record uses advertisedIp when set`() {
299+
val ipAddress = "127.0.0.1"
300+
val advertisedIp = "203.0.113.50"
301+
val port = 9001u
302+
val discoveryPort = 9000u
303+
304+
val p2pConfig =
305+
P2PConfig(
306+
ipAddress = ipAddress,
307+
port = port,
308+
discovery =
309+
P2PConfig.Discovery(
310+
port = discoveryPort,
311+
bootnodes = listOf(),
312+
refreshInterval = 10.seconds,
313+
advertisedIp = advertisedIp,
314+
),
315+
)
316+
317+
val discoveryService =
318+
MaruDiscoveryService(
319+
privateKeyBytes = keyPair.secretKey().bytesArray(),
320+
p2pConfig = p2pConfig,
321+
forkIdHashManager = forkIdHashProvider,
322+
p2PState = InMemoryP2PState(),
323+
)
324+
325+
val localNodeRecord = discoveryService.getLocalNodeRecord()
326+
327+
assertThat(localNodeRecord.udpAddress.isPresent).isTrue()
328+
assertThat(
329+
localNodeRecord.udpAddress
330+
.get()
331+
.address.hostAddress,
332+
).isEqualTo(advertisedIp)
333+
assertThat(localNodeRecord.udpAddress.get().port).isEqualTo(discoveryPort.toInt())
334+
assertThat(localNodeRecord.tcpAddress.isPresent).isTrue()
335+
assertThat(
336+
localNodeRecord.tcpAddress
337+
.get()
338+
.address.hostAddress,
339+
).isEqualTo(advertisedIp)
340+
assertThat(localNodeRecord.tcpAddress.get().port).isEqualTo(port.toInt())
341+
}
296342
}

0 commit comments

Comments
 (0)