This is an automated email from the ASF dual-hosted git repository. toulmean pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-tuweni.git
The following commit(s) were added to refs/heads/master by this push: new de49f47 Add support for list entries in ENRs new 30a04f7 Merge pull request #115 from atoulme/add_support_for_lists_in_enrs de49f47 is described below commit de49f47e57a4a908adc13e56ca447dd4d9396a91 Author: Antoine Toulme <anto...@lunar-ocean.com> AuthorDate: Sun Jul 5 10:26:44 2020 -0700 Add support for list entries in ENRs --- .../devp2p/v5/DefaultNodeDiscoveryServiceTest.kt | 1 + .../org/apache/tuweni/devp2p/DiscoveryService.kt | 2 +- .../org/apache/tuweni/devp2p/EthereumNodeRecord.kt | 38 +++++++++++++++++----- .../tuweni/devp2p/v5/NodeDiscoveryService.kt | 1 + .../tuweni/devp2p/v5/storage/RoutingTable.kt | 2 +- .../apache/tuweni/devp2p/ENRResponsePacketTest.kt | 7 ++-- .../apache/tuweni/devp2p/EthereumNodeRecordTest.kt | 29 ++++++++++------- .../tuweni/devp2p/v5/storage/RoutingTableTest.kt | 17 +++++++--- 8 files changed, 69 insertions(+), 28 deletions(-) diff --git a/devp2p/src/integrationTest/kotlin/org/apache/tuweni/devp2p/v5/DefaultNodeDiscoveryServiceTest.kt b/devp2p/src/integrationTest/kotlin/org/apache/tuweni/devp2p/v5/DefaultNodeDiscoveryServiceTest.kt index e448415..92e3838 100644 --- a/devp2p/src/integrationTest/kotlin/org/apache/tuweni/devp2p/v5/DefaultNodeDiscoveryServiceTest.kt +++ b/devp2p/src/integrationTest/kotlin/org/apache/tuweni/devp2p/v5/DefaultNodeDiscoveryServiceTest.kt @@ -52,6 +52,7 @@ class DefaultNodeDiscoveryServiceTest { keyPair, enrSeq, emptyMap(), + emptyMap(), bindAddress.address, null, bindAddress.port diff --git a/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/DiscoveryService.kt b/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/DiscoveryService.kt index 24021b1..f5b9b73 100644 --- a/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/DiscoveryService.kt +++ b/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/DiscoveryService.kt @@ -349,7 +349,7 @@ internal class CoroutineDiscoveryService( ) enr = EthereumNodeRecord.toRLP( - keyPair, seq, enrData, selfEndpoint.address, selfEndpoint.tcpPort, + keyPair, seq, enrData, null, selfEndpoint.address, selfEndpoint.tcpPort, selfEndpoint.udpPort ) diff --git a/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/EthereumNodeRecord.kt b/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/EthereumNodeRecord.kt index cc6f7f7..abeea22 100644 --- a/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/EthereumNodeRecord.kt +++ b/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/EthereumNodeRecord.kt @@ -31,7 +31,12 @@ import java.time.Instant /** * Ethereum Node Record (ENR) as described in [EIP-778](https://eips.ethereum.org/EIPS/eip-778). */ -class EthereumNodeRecord(val signature: Bytes, val seq: Long, val data: Map<String, Bytes>) { +class EthereumNodeRecord( + val signature: Bytes, + val seq: Long, + val data: Map<String, Bytes>, + val listData: Map<String, List<Bytes>> = emptyMap() +) { companion object { @@ -52,19 +57,26 @@ class EthereumNodeRecord(val signature: Bytes, val seq: Long, val data: Map<Stri val seq = it.readLong() val data = mutableMapOf<String, Bytes>() + val listData = mutableMapOf<String, List<Bytes>>() while (!it.isComplete) { val key = it.readString() if (it.nextIsList()) { - it.skipNext() - // TODO("not ready yet to read list values") - // data[key] = it.readListContents { listreader -> listreader.readValue()} + listData[key] = it.readListContents { listreader -> + if (listreader.nextIsList()) { + // TODO complex structures not supported + listreader.skipNext() + null + } else { + listreader.readValue() + } + }.filterNotNull() } else { val value = it.readValue() data[key] = value } } - EthereumNodeRecord(sig, seq, data) + EthereumNodeRecord(sig, seq, data, listData) } } @@ -75,6 +87,7 @@ class EthereumNodeRecord(val signature: Bytes, val seq: Long, val data: Map<Stri tcp: Int? = null, udp: Int? = null, data: Map<String, Bytes>? = null, + listData: Map<String, List<Bytes>>? = null, writer: RLPWriter ) { writer.writeLong(seq) @@ -92,11 +105,18 @@ class EthereumNodeRecord(val signature: Bytes, val seq: Long, val data: Map<Stri udp?.let { mutableData["udp"] = Bytes.ofUnsignedShort(it) } - mutableData.keys.sorted().forEach { key -> + val keys = mutableListOf<String>() + keys.addAll(mutableData.keys) + listData?.let { keys.addAll(it.keys) } + keys.sorted().forEach { key -> mutableData[key]?.let { value -> writer.writeString(key) writer.writeValue(value) } + listData?.get(key)?.let { value -> + writer.writeString(key) + writer.writeList(value) { writer, v -> writer.writeValue(v) } + } } } @@ -105,6 +125,7 @@ class EthereumNodeRecord(val signature: Bytes, val seq: Long, val data: Map<Stri * @param signatureKeyPair the key pair to use to sign the ENR * @param seq the sequence number for the ENR. It should be higher than the previous time the ENR was generated. It defaults to the current time since epoch in milliseconds. * @param data the key pairs to encode in the ENR + * @param listData the key pairs of list values to encode in the ENR * @param ip the IP address of the host * @param tcp an optional parameter to a TCP port used for the wire protocol * @param udp an optional parameter to a UDP port used for discovery @@ -116,12 +137,13 @@ class EthereumNodeRecord(val signature: Bytes, val seq: Long, val data: Map<Stri signatureKeyPair: SECP256K1.KeyPair, seq: Long = Instant.now().toEpochMilli(), data: Map<String, Bytes>? = null, + listData: Map<String, List<Bytes>>? = null, ip: InetAddress, tcp: Int? = null, udp: Int? = null ): Bytes { val encoded = RLP.encode { writer -> - encode(signatureKeyPair, seq, ip, tcp, udp, data, writer) + encode(signatureKeyPair, seq, ip, tcp, udp, data, listData, writer) } val signature = SECP256K1.sign(Hash.keccak256(encoded), signatureKeyPair) val sigBytes = MutableBytes.create(64) @@ -130,7 +152,7 @@ class EthereumNodeRecord(val signature: Bytes, val seq: Long, val data: Map<Stri val completeEncoding = RLP.encodeList { writer -> writer.writeValue(sigBytes) - encode(signatureKeyPair, seq, ip, tcp, udp, data, writer) + encode(signatureKeyPair, seq, ip, tcp, udp, data, listData, writer) } return completeEncoding } diff --git a/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/NodeDiscoveryService.kt b/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/NodeDiscoveryService.kt index 2862ac1..653a392 100644 --- a/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/NodeDiscoveryService.kt +++ b/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/NodeDiscoveryService.kt @@ -90,6 +90,7 @@ class DefaultNodeDiscoveryService( keyPair, enrSeq, emptyMap(), + emptyMap(), bindAddress.address, null, bindAddress.port diff --git a/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/storage/RoutingTable.kt b/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/storage/RoutingTable.kt index 6777272..252a382 100644 --- a/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/storage/RoutingTable.kt +++ b/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/storage/RoutingTable.kt @@ -36,7 +36,7 @@ class RoutingTable( nodeId = nodeIdCalculation, distanceToSelf = { val xorResult = key(it) xorDist selfNodeId - IntMath.log2(xorResult, RoundingMode.FLOOR) + if (xorResult == 0) 0 else IntMath.log2(xorResult, RoundingMode.FLOOR) }) val size: Int diff --git a/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/ENRResponsePacketTest.kt b/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/ENRResponsePacketTest.kt index df0f929..63f2b75 100644 --- a/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/ENRResponsePacketTest.kt +++ b/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/ENRResponsePacketTest.kt @@ -35,8 +35,11 @@ internal class ENRResponsePacketTest { val keyPair = SECP256K1.KeyPair.random() val requestHash = Bytes32.random() - val enr = EthereumNodeRecord.toRLP(SECP256K1.KeyPair.random(), 2, emptyMap(), - InetAddress.getByName("localhost"), 3000, 12000) + val enr = EthereumNodeRecord.toRLP( + SECP256K1.KeyPair.random(), 2, emptyMap(), + emptyMap(), + InetAddress.getByName("localhost"), 3000, 12000 + ) val now = System.currentTimeMillis() val pong = ENRResponsePacket.create(keyPair, now, requestHash, enr) diff --git a/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/EthereumNodeRecordTest.kt b/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/EthereumNodeRecordTest.kt index eeb1552..87cc06b 100644 --- a/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/EthereumNodeRecordTest.kt +++ b/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/EthereumNodeRecordTest.kt @@ -23,7 +23,6 @@ import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.extension.ExtendWith -import java.lang.IllegalArgumentException import java.net.InetAddress @ExtendWith(BouncyCastleExtension::class) @@ -40,34 +39,42 @@ class EthereumNodeRecordTest { @Test fun readFromRLP() { - val enr = EthereumNodeRecord.fromRLP(Bytes.fromHexString( - "f884b8407098ad865b00a582051940cb9cf36836572411a4727878307701" + - "1599ed5cd16b76f2635f4e234738f30813a89eb9137e3e3df5266e3a1f11" + - "df72ecf1145ccb9c01826964827634826970847f00000189736563703235" + - "366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1" + - "400f3258cd31388375647082765f")) + val enr = EthereumNodeRecord.fromRLP( + Bytes.fromHexString( + "f884b8407098ad865b00a582051940cb9cf36836572411a4727878307701" + + "1599ed5cd16b76f2635f4e234738f30813a89eb9137e3e3df5266e3a1f11" + + "df72ecf1145ccb9c01826964827634826970847f00000189736563703235" + + "366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1" + + "400f3258cd31388375647082765f" + ) + ) assertEquals(1L, enr.seq) assertEquals(Bytes.wrap("v4".toByteArray()), enr.data["id"]) assertEquals(Bytes.fromHexString("7f000001"), enr.data["ip"]) assertEquals( Bytes.fromHexString("03ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138"), - enr.data["secp256k1"]) + enr.data["secp256k1"] + ) assertEquals(Bytes.fromHexString("765f"), enr.data["udp"]) enr.validate() - System.out.println(enr.publicKey().bytes()) } @Test fun toRLP() { val keypair = SECP256K1.KeyPair.random() - val rlp = EthereumNodeRecord.toRLP(keypair, + val rlp = EthereumNodeRecord.toRLP( + keypair, seq = 1L, data = mutableMapOf(Pair("key", Bytes.fromHexString("deadbeef"))), - ip = InetAddress.getByName("127.0.0.1")) + listData = mutableMapOf(Pair("foo", listOf(Bytes.fromHexString("deadbeef")))), + ip = InetAddress.getByName("127.0.0.1") + ) val record = EthereumNodeRecord.fromRLP(rlp) assertEquals(1L, record.seq) assertEquals(Bytes.wrap("v4".toByteArray()), record.data["id"]) assertEquals(Bytes.fromHexString("7f000001"), record.data["ip"]) assertEquals(keypair.publicKey(), record.publicKey()) + assertEquals(Bytes.fromHexString("deadbeef"), record.data["key"]) + assertEquals(Bytes.fromHexString("deadbeef"), (record.listData["foo"] ?: error("None"))[0]) } } diff --git a/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/storage/RoutingTableTest.kt b/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/storage/RoutingTableTest.kt index 9792645..7293590 100644 --- a/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/storage/RoutingTableTest.kt +++ b/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/storage/RoutingTableTest.kt @@ -21,6 +21,9 @@ import org.apache.tuweni.crypto.SECP256K1 import org.apache.tuweni.devp2p.EthereumNodeRecord import org.apache.tuweni.junit.BouncyCastleExtension import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotEquals +import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import java.net.InetAddress @@ -38,26 +41,30 @@ class RoutingTableTest { @Test fun addCreatesRecordInBucket() { routingTable.add(newEnr) - - assert(!routingTable.isEmpty()) + assertTrue(!routingTable.isEmpty()) } @Test fun evictRemovesRecord() { routingTable.add(newEnr) - assert(!routingTable.isEmpty()) + assertTrue(!routingTable.isEmpty()) routingTable.evict(newEnr) - assert(routingTable.isEmpty()) + assertTrue(routingTable.isEmpty()) } @Test fun getSelfEnrGivesTableOwnerEnr() { val result = routingTable.getSelfEnr() + assertEquals(enr, result) + } - assert(result == enr) + @Test + fun distanceToSelf() { + assertEquals(0, routingTable.distanceToSelf(routingTable.getSelfEnr())) + assertNotEquals(0, routingTable.distanceToSelf(newEnr)) } @AfterEach --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@tuweni.apache.org For additional commands, e-mail: commits-h...@tuweni.apache.org