all messages + tests
diff --git a/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/packet/FindNodeMessage.kt b/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/packet/FindNodeMessage.kt
index fb65863..b39ae46 100644
--- a/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/packet/FindNodeMessage.kt
+++ b/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/packet/FindNodeMessage.kt
@@ -36,7 +36,6 @@
   override fun getMessageType(): Bytes = encodedMessageType
 
   companion object {
-
     fun create(content: Bytes): FindNodeMessage {
       return RLP.decodeList(content) { reader ->
         val requestId = reader.readValue()
diff --git a/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/packet/NodesMessage.kt b/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/packet/NodesMessage.kt
new file mode 100644
index 0000000..5c213e5
--- /dev/null
+++ b/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/packet/NodesMessage.kt
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tuweni.devp2p.v5.packet
+
+import org.apache.tuweni.bytes.Bytes
+import org.apache.tuweni.rlp.RLP
+
+class NodesMessage(
+  val requestId: Bytes = UdpMessage.requestId(),
+  val total: Int,
+  val nodeRecords: List<Bytes>
+) : UdpMessage() {
+
+  private val encodedMessageType: Bytes = Bytes.fromHexString("0x04")
+
+  override fun getMessageType(): Bytes = encodedMessageType
+
+  override fun encode(): Bytes {
+    return RLP.encodeList { writer ->
+      writer.writeValue(requestId)
+      writer.writeInt(total)
+      writer.writeList(nodeRecords) { listWriter, it ->
+        listWriter.writeValue(it)
+      }
+    }
+  }
+
+  companion object {
+    fun create(content: Bytes): NodesMessage {
+      return RLP.decodeList(content) { reader ->
+        val requestId = reader.readValue()
+        val total = reader.readInt()
+        val nodeRecords = reader.readListContents { listReader ->
+          listReader.readValue()
+        }
+        return@decodeList NodesMessage(requestId, total, nodeRecords)
+      }
+    }
+  }
+}
diff --git a/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/packet/PingMessage.kt b/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/packet/PingMessage.kt
new file mode 100644
index 0000000..c9cc06a
--- /dev/null
+++ b/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/packet/PingMessage.kt
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tuweni.devp2p.v5.packet
+
+import org.apache.tuweni.bytes.Bytes
+import org.apache.tuweni.rlp.RLP
+
+class PingMessage(
+  val requestId: Bytes = UdpMessage.requestId(),
+  val enrSeq: Long = 0
+) : UdpMessage() {
+
+  private val encodedMessageType: Bytes = Bytes.fromHexString("0x01")
+
+  override fun getMessageType(): Bytes = encodedMessageType
+
+  override fun encode(): Bytes {
+    return RLP.encodeList { reader ->
+      reader.writeValue(requestId)
+      reader.writeLong(enrSeq)
+    }
+  }
+
+  companion object {
+    fun create(content: Bytes): PingMessage {
+      return RLP.decodeList(content) { reader ->
+        val requestId = reader.readValue()
+        val enrSeq = reader.readLong()
+        return@decodeList PingMessage(requestId, enrSeq)
+      }
+    }
+  }
+}
diff --git a/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/packet/PongMessage.kt b/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/packet/PongMessage.kt
new file mode 100644
index 0000000..0323853
--- /dev/null
+++ b/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/packet/PongMessage.kt
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tuweni.devp2p.v5.packet
+
+import org.apache.tuweni.bytes.Bytes
+import org.apache.tuweni.rlp.RLP
+import java.net.InetAddress
+
+class PongMessage(
+  val requestId: Bytes = UdpMessage.requestId(),
+  val enrSeq: Long = 0,
+  val recipientIp: InetAddress,
+  val recipientPort: Int
+) : UdpMessage() {
+
+  private val encodedMessageType: Bytes = Bytes.fromHexString("0x02")
+
+  override fun getMessageType(): Bytes = encodedMessageType
+
+  override fun encode(): Bytes {
+    return RLP.encodeList { writer ->
+      writer.writeValue(requestId)
+      writer.writeLong(enrSeq)
+
+      val bytesIp = Bytes.wrap(recipientIp.address)
+      writer.writeValue(bytesIp)
+      writer.writeInt(recipientPort)
+    }
+  }
+
+  companion object {
+    fun create(content: Bytes): PongMessage {
+      return RLP.decodeList(content) { reader ->
+        val requestId = reader.readValue()
+        val enrSeq = reader.readLong()
+        val address = InetAddress.getByAddress(reader.readValue().toArray())
+        val recipientPort = reader.readInt()
+        return@decodeList PongMessage(requestId, enrSeq, address, recipientPort)
+      }
+    }
+  }
+}
diff --git a/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/packet/RegConfirmationMessage.kt b/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/packet/RegConfirmationMessage.kt
new file mode 100644
index 0000000..bd643dd
--- /dev/null
+++ b/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/packet/RegConfirmationMessage.kt
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tuweni.devp2p.v5.packet
+
+import org.apache.tuweni.bytes.Bytes
+import org.apache.tuweni.rlp.RLP
+
+class RegConfirmationMessage(
+  val requestId: Bytes = UdpMessage.requestId(),
+  val registered: Boolean = true
+) : UdpMessage() {
+
+  private val encodedMessageType: Bytes = Bytes.fromHexString("0x08")
+
+  override fun getMessageType(): Bytes = encodedMessageType
+
+  override fun encode(): Bytes {
+    return RLP.encodeList { writer ->
+      writer.writeValue(requestId)
+      writer.writeByte(if (registered) 1 else 0)
+    }
+  }
+
+  companion object {
+    fun create(content: Bytes): RegConfirmationMessage {
+      return RLP.decodeList(content) { reader ->
+        val requestId = reader.readValue()
+        val registered = (reader.readByte() == 1.toByte())
+        return@decodeList RegConfirmationMessage(requestId, registered)
+      }
+    }
+  }
+}
diff --git a/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/packet/RegTopicMessage.kt b/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/packet/RegTopicMessage.kt
new file mode 100644
index 0000000..afe2c23
--- /dev/null
+++ b/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/packet/RegTopicMessage.kt
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tuweni.devp2p.v5.packet
+
+import org.apache.tuweni.bytes.Bytes
+import org.apache.tuweni.rlp.RLP
+
+class RegTopicMessage(
+  val requestId: Bytes = UdpMessage.requestId(),
+  val ticket: Bytes,
+  val nodeRecord: Bytes
+) : UdpMessage() {
+
+  private val encodedMessageType: Bytes = Bytes.fromHexString("0x07")
+
+  override fun getMessageType(): Bytes = encodedMessageType
+
+  override fun encode(): Bytes {
+    return RLP.encodeList { writer ->
+      writer.writeValue(requestId)
+      writer.writeValue(ticket)
+      writer.writeValue(nodeRecord)
+    }
+  }
+
+  companion object {
+    fun create(content: Bytes): RegTopicMessage {
+      return RLP.decodeList(content) { reader ->
+        val requestId = reader.readValue()
+        val ticket = reader.readValue()
+        val nodeRecord = reader.readValue()
+        return@decodeList RegTopicMessage(requestId, ticket, nodeRecord)
+      }
+    }
+  }
+}
diff --git a/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/packet/ReqTicketMessage.kt b/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/packet/ReqTicketMessage.kt
new file mode 100644
index 0000000..0381a85
--- /dev/null
+++ b/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/packet/ReqTicketMessage.kt
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tuweni.devp2p.v5.packet
+
+import org.apache.tuweni.bytes.Bytes
+import org.apache.tuweni.rlp.RLP
+
+class ReqTicketMessage(
+  val requestId: Bytes = UdpMessage.requestId(),
+  val topic: Bytes
+) : UdpMessage() {
+
+  private val encodedMessageType: Bytes = Bytes.fromHexString("0x05")
+
+  override fun getMessageType(): Bytes = encodedMessageType
+
+  override fun encode(): Bytes {
+    return RLP.encodeList { writer ->
+      writer.writeValue(requestId)
+      writer.writeValue(topic)
+    }
+  }
+
+  companion object {
+    fun create(content: Bytes): ReqTicketMessage {
+      return RLP.decodeList(content) { reader ->
+        val requestId = reader.readValue()
+        val topic = reader.readValue()
+        return@decodeList ReqTicketMessage(requestId, topic)
+      }
+    }
+  }
+}
diff --git a/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/packet/TicketMessage.kt b/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/packet/TicketMessage.kt
new file mode 100644
index 0000000..8d628f2
--- /dev/null
+++ b/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/packet/TicketMessage.kt
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tuweni.devp2p.v5.packet
+
+import org.apache.tuweni.bytes.Bytes
+import org.apache.tuweni.rlp.RLP
+
+class TicketMessage(
+  val requestId: Bytes = UdpMessage.requestId(),
+  val ticket: Bytes,
+  val waitTime: Long
+) : UdpMessage() {
+
+  private val encodedMessageType: Bytes = Bytes.fromHexString("0x06")
+
+  override fun getMessageType(): Bytes = encodedMessageType
+
+  override fun encode(): Bytes {
+    return RLP.encodeList { writer ->
+      writer.writeValue(requestId)
+      writer.writeValue(ticket)
+      writer.writeLong(waitTime)
+    }
+  }
+
+  companion object {
+    fun create(content: Bytes): TicketMessage {
+      return RLP.decodeList(content) { reader ->
+        val requestId = reader.readValue()
+        val ticket = reader.readValue()
+        val waitTime = reader.readLong()
+        return@decodeList TicketMessage(requestId, ticket, waitTime)
+      }
+    }
+  }
+}
diff --git a/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/packet/TopicQueryMessage.kt b/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/packet/TopicQueryMessage.kt
new file mode 100644
index 0000000..14736ff
--- /dev/null
+++ b/devp2p/src/main/kotlin/org/apache/tuweni/devp2p/v5/packet/TopicQueryMessage.kt
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tuweni.devp2p.v5.packet
+
+import org.apache.tuweni.bytes.Bytes
+import org.apache.tuweni.rlp.RLP
+
+class TopicQueryMessage(
+  val requestId: Bytes = UdpMessage.requestId(),
+  val topic: Bytes
+) : UdpMessage() {
+
+  private val encodedMessageType: Bytes = Bytes.fromHexString("0x09")
+
+  override fun getMessageType(): Bytes = encodedMessageType
+
+  override fun encode(): Bytes {
+    return RLP.encodeList { writer ->
+      writer.writeValue(requestId)
+      writer.writeValue(topic)
+    }
+  }
+
+  companion object {
+    fun create(content: Bytes): TopicQueryMessage {
+      return RLP.decodeList(content) { reader ->
+        val requestId = reader.readValue()
+        val topic = reader.readValue()
+        return@decodeList TopicQueryMessage(requestId, topic)
+      }
+    }
+  }
+}
diff --git a/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/internal/DefaultUdpConnectorTest.kt b/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/internal/DefaultUdpConnectorTest.kt
index 33f84eb..a345208 100644
--- a/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/internal/DefaultUdpConnectorTest.kt
+++ b/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/internal/DefaultUdpConnectorTest.kt
@@ -97,5 +97,6 @@
 
       assert(message.data == data)
     }
+    socketChannel.close()
   }
 }
diff --git a/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/packet/NodesMessageTest.kt b/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/packet/NodesMessageTest.kt
new file mode 100644
index 0000000..d65696d
--- /dev/null
+++ b/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/packet/NodesMessageTest.kt
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tuweni.devp2p.v5.packet
+
+import org.apache.tuweni.bytes.Bytes
+import org.apache.tuweni.crypto.SECP256K1
+import org.apache.tuweni.devp2p.EthereumNodeRecord
+import org.apache.tuweni.junit.BouncyCastleExtension
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.extension.ExtendWith
+import java.net.InetAddress
+
+@ExtendWith(BouncyCastleExtension::class)
+class NodesMessageTest {
+
+  @Test
+  fun encodeCreatesValidBytesSequence() {
+    val requestId = Bytes.fromHexString("0xC6E32C5E89CAA754")
+    val total = 10
+    val nodeRecords = listOf(
+      EthereumNodeRecord.toRLP(SECP256K1.KeyPair.random(), ip = InetAddress.getLocalHost(), udp = 9090),
+      EthereumNodeRecord.toRLP(SECP256K1.KeyPair.random(), ip = InetAddress.getLocalHost(), udp = 9091),
+      EthereumNodeRecord.toRLP(SECP256K1.KeyPair.random(), ip = InetAddress.getLocalHost(), udp = 9092)
+    )
+    val message = NodesMessage(requestId, total, nodeRecords)
+
+    val encodingResult = message.encode()
+
+    val decodingResult = NodesMessage.create(encodingResult)
+
+    assert(decodingResult.requestId == requestId)
+    assert(decodingResult.total == 10)
+    assert(EthereumNodeRecord.fromRLP(decodingResult.nodeRecords[0]).udp() == 9090)
+    assert(EthereumNodeRecord.fromRLP(decodingResult.nodeRecords[1]).udp() == 9091)
+    assert(EthereumNodeRecord.fromRLP(decodingResult.nodeRecords[2]).udp() == 9092)
+  }
+
+  @Test
+  fun getMessageTypeHasValidIndex() {
+    val message = NodesMessage(UdpMessage.requestId(), 0, emptyList())
+
+    assert(4 == message.getMessageType().toInt())
+  }
+}
diff --git a/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/packet/PingMessageTest.kt b/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/packet/PingMessageTest.kt
new file mode 100644
index 0000000..554437d
--- /dev/null
+++ b/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/packet/PingMessageTest.kt
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tuweni.devp2p.v5.packet
+
+import org.apache.tuweni.bytes.Bytes
+import org.junit.jupiter.api.Test
+
+class PingMessageTest {
+
+  @Test
+  fun encodeCreatesValidBytesSequence() {
+    val requestId = Bytes.fromHexString("0xC6E32C5E89CAA754")
+    val message = PingMessage(requestId)
+
+    val encodingResult = message.encode()
+
+    val decodingResult = PingMessage.create(encodingResult)
+
+    assert(decodingResult.requestId == requestId)
+    assert(decodingResult.enrSeq == message.enrSeq)
+  }
+
+  @Test
+  fun getMessageTypeHasValidIndex() {
+    val message = PingMessage()
+
+    assert(1 == message.getMessageType().toInt())
+  }
+}
diff --git a/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/packet/PongMessageTest.kt b/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/packet/PongMessageTest.kt
new file mode 100644
index 0000000..e0847c4
--- /dev/null
+++ b/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/packet/PongMessageTest.kt
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tuweni.devp2p.v5.packet
+
+import org.apache.tuweni.bytes.Bytes
+import org.junit.jupiter.api.Test
+import java.net.InetAddress
+
+class PongMessageTest {
+
+  @Test
+  fun encodeCreatesValidBytesSequence() {
+    val requestId = Bytes.fromHexString("0xC6E32C5E89CAA754")
+    val message = PongMessage(requestId, 0, InetAddress.getLocalHost(), 9090)
+
+    val encodingResult = message.encode()
+
+    val decodingResult = PongMessage.create(encodingResult)
+
+    assert(decodingResult.requestId == requestId)
+    assert(decodingResult.enrSeq == message.enrSeq)
+    assert(decodingResult.recipientIp == message.recipientIp)
+    assert(decodingResult.recipientPort == message.recipientPort)
+  }
+
+  @Test
+  fun getMessageTypeHasValidIndex() {
+    val message = PongMessage(recipientIp = InetAddress.getLocalHost(), recipientPort = 9090)
+
+    assert(2 == message.getMessageType().toInt())
+  }
+}
diff --git a/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/packet/RegConfirmationMessageTest.kt b/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/packet/RegConfirmationMessageTest.kt
new file mode 100644
index 0000000..65f61a6
--- /dev/null
+++ b/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/packet/RegConfirmationMessageTest.kt
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tuweni.devp2p.v5.packet
+
+import org.apache.tuweni.bytes.Bytes
+import org.junit.jupiter.api.Test
+
+class RegConfirmationMessageTest {
+
+  @Test
+  fun encodeCreatesValidBytesSequence() {
+    val requestId = Bytes.fromHexString("0xC6E32C5E89CAA754")
+    val message = RegConfirmationMessage(requestId, false)
+
+    val encodingResult = message.encode()
+
+    val decodingResult = RegConfirmationMessage.create(encodingResult)
+
+    assert(decodingResult.requestId == requestId)
+    assert(decodingResult.registered == message.registered)
+  }
+
+  @Test
+  fun getMessageTypeHasValidIndex() {
+    val message = RegConfirmationMessage()
+
+    assert(8 == message.getMessageType().toInt())
+  }
+}
diff --git a/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/packet/RegTopicMessageTest.kt b/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/packet/RegTopicMessageTest.kt
new file mode 100644
index 0000000..c33b43b
--- /dev/null
+++ b/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/packet/RegTopicMessageTest.kt
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tuweni.devp2p.v5.packet
+
+import org.apache.tuweni.bytes.Bytes
+import org.junit.jupiter.api.Test
+
+class RegTopicMessageTest {
+
+  @Test
+  fun encodeCreatesValidBytesSequence() {
+    val requestId = Bytes.fromHexString("0xC6E32C5E89CAA754")
+    val message = RegTopicMessage(requestId, Bytes.random(32), Bytes.random(32))
+
+    val encodingResult = message.encode()
+
+    val decodingResult = RegTopicMessage.create(encodingResult)
+
+    assert(decodingResult.requestId == requestId)
+    assert(decodingResult.ticket == message.ticket)
+    assert(decodingResult.nodeRecord == message.nodeRecord)
+  }
+
+  @Test
+  fun getMessageTypeHasValidIndex() {
+    val message = RegTopicMessage(ticket = Bytes.random(32), nodeRecord = Bytes.random(32))
+
+    assert(7 == message.getMessageType().toInt())
+  }
+}
diff --git a/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/packet/ReqTicketMessageTest.kt b/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/packet/ReqTicketMessageTest.kt
new file mode 100644
index 0000000..a43a328
--- /dev/null
+++ b/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/packet/ReqTicketMessageTest.kt
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tuweni.devp2p.v5.packet
+
+import org.apache.tuweni.bytes.Bytes
+import org.junit.jupiter.api.Test
+
+class ReqTicketMessageTest {
+
+  @Test
+  fun encodeCreatesValidBytesSequence() {
+    val requestId = Bytes.fromHexString("0xC6E32C5E89CAA754")
+    val message = ReqTicketMessage(requestId, Bytes.random(32))
+
+    val encodingResult = message.encode()
+
+    val decodingResult = ReqTicketMessage.create(encodingResult)
+
+    assert(decodingResult.requestId == requestId)
+    assert(decodingResult.topic == message.topic)
+  }
+
+  @Test
+  fun getMessageTypeHasValidIndex() {
+    val message = ReqTicketMessage(topic = Bytes.random(32))
+
+    assert(5 == message.getMessageType().toInt())
+  }
+}
diff --git a/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/packet/TicketMessageTest.kt b/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/packet/TicketMessageTest.kt
new file mode 100644
index 0000000..f754489
--- /dev/null
+++ b/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/packet/TicketMessageTest.kt
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tuweni.devp2p.v5.packet
+
+import org.apache.tuweni.bytes.Bytes
+import org.junit.jupiter.api.Test
+
+class TicketMessageTest {
+
+  @Test
+  fun encodeCreatesValidBytesSequence() {
+    val requestId = Bytes.fromHexString("0xC6E32C5E89CAA754")
+    val message = TicketMessage(requestId, Bytes.random(32), 1000)
+
+    val encodingResult = message.encode()
+
+    val decodingResult = TicketMessage.create(encodingResult)
+
+    assert(decodingResult.requestId == requestId)
+    assert(decodingResult.ticket == message.ticket)
+    assert(decodingResult.waitTime == message.waitTime)
+  }
+
+  @Test
+  fun getMessageTypeHasValidIndex() {
+    val message = TicketMessage(ticket = Bytes.random(32), waitTime = 1000)
+
+    assert(6 == message.getMessageType().toInt())
+  }
+}
diff --git a/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/packet/TopicQueryMessageTest.kt b/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/packet/TopicQueryMessageTest.kt
new file mode 100644
index 0000000..36d66a8
--- /dev/null
+++ b/devp2p/src/test/kotlin/org/apache/tuweni/devp2p/v5/packet/TopicQueryMessageTest.kt
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tuweni.devp2p.v5.packet
+
+import org.apache.tuweni.bytes.Bytes
+import org.junit.jupiter.api.Test
+
+class TopicQueryMessageTest {
+
+  @Test
+  fun encodeCreatesValidBytesSequence() {
+    val requestId = Bytes.fromHexString("0xC6E32C5E89CAA754")
+    val message = TopicQueryMessage(requestId, Bytes.random(32))
+
+    val encodingResult = message.encode()
+
+    val decodingResult = TopicQueryMessage.create(encodingResult)
+
+    assert(decodingResult.requestId == requestId)
+    assert(decodingResult.topic == message.topic)
+  }
+
+  @Test
+  fun getMessageTypeHasValidIndex() {
+    val message = TopicQueryMessage(topic = Bytes.random(32))
+
+    assert(9 == message.getMessageType().toInt())
+  }
+}