diff --git a/.gitignore b/.gitignore index 6aefa1c6e..a939ebb2f 100644 --- a/.gitignore +++ b/.gitignore @@ -189,3 +189,4 @@ $RECYCLE.BIN/ # Node node_modules package-lock.json +/src/jmh/java/generated/ diff --git a/build.gradle.kts b/build.gradle.kts index 57fa398b6..cbf1ac3c7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,7 +12,7 @@ import java.nio.file.Paths // ./gradlew publish -PcloudsmithUser= -PcloudsmithApiKey= group = "io.libp2p" -version = "0.9.1-RELEASE" +version = "0.9.2-RELEASE" description = "a minimal implementation of libp2p for the jvm" plugins { @@ -26,6 +26,7 @@ plugins { id("maven-publish") id("org.jetbrains.dokka").version("1.6.10") id("org.jmailen.kotlinter").version("3.8.0") + id("java-test-fixtures") } repositories { @@ -36,6 +37,13 @@ repositories { val log4j2Version = "2.17.1" +sourceSets.create("jmh") { + compileClasspath += sourceSets["main"].runtimeClasspath + compileClasspath += sourceSets["testFixtures"].runtimeClasspath + runtimeClasspath += sourceSets["main"].runtimeClasspath + runtimeClasspath += sourceSets["testFixtures"].runtimeClasspath +} + dependencies { api("io.netty:netty-all:4.1.69.Final") api("com.google.protobuf:protobuf-java:3.19.2") @@ -53,6 +61,9 @@ dependencies { implementation("org.apache.logging.log4j:log4j-core:${log4j2Version}") implementation("javax.xml.bind:jaxb-api:2.3.1") + testFixturesImplementation("org.apache.logging.log4j:log4j-api:${log4j2Version}") + testFixturesImplementation("com.google.guava:guava:31.0.1-jre") + testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") testImplementation("org.junit.jupiter:junit-jupiter-params:5.8.2") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") @@ -61,14 +72,13 @@ dependencies { testImplementation("org.mockito:mockito-junit-jupiter:4.2.0") testImplementation("org.assertj:assertj-core:3.22.0") + "jmhImplementation"("org.openjdk.jmh:jmh-core:1.35") + "jmhAnnotationProcessor"("org.openjdk.jmh:jmh-generator-annprocess:1.35") } -sourceSets { - main { - proto { - srcDir("src/main/proto") - } - } +task("jmh") { + mainClass.set("org.openjdk.jmh.Main") + classpath = sourceSets["jmh"].compileClasspath + sourceSets["jmh"].runtimeClasspath } protobuf { diff --git a/src/jmh/java/io/libp2p/pubsub/gossip/GossipScoreBenchmark.java b/src/jmh/java/io/libp2p/pubsub/gossip/GossipScoreBenchmark.java new file mode 100644 index 000000000..90481e7f0 --- /dev/null +++ b/src/jmh/java/io/libp2p/pubsub/gossip/GossipScoreBenchmark.java @@ -0,0 +1,116 @@ +package io.libp2p.pubsub.gossip; + +import io.libp2p.core.PeerId; +import io.libp2p.core.multiformats.Multiaddr; +import io.libp2p.core.multiformats.Protocol; +import io.libp2p.pubsub.DefaultPubsubMessage; +import io.libp2p.tools.schedulers.ControlledExecutorServiceImpl; +import io.libp2p.tools.schedulers.TimeController; +import io.libp2p.tools.schedulers.TimeControllerImpl; +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; +import pubsub.pb.Rpc; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +@State(Scope.Thread) +@Fork(5) +@Warmup(iterations = 5, time = 1000, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 10, time = 1000, timeUnit = TimeUnit.MILLISECONDS) +public class GossipScoreBenchmark { + + private final int peerCount = 5000; + private final int connectedCount = 2000; + private final int topicCount = 128; + + private final List topics = IntStream + .range(0, topicCount) + .mapToObj(i -> "Topic-" + i) + .collect(Collectors.toList()); + + private final List peerIds = Stream.generate(PeerId::random).limit(peerCount).collect(Collectors.toList()); + private final List peerAddresses = IntStream + .range(0, peerCount) + .mapToObj(idx -> + Multiaddr.empty() + .withComponent(Protocol.IP4, new byte[]{(byte) (idx >>> 8 & 0xFF), (byte) (idx & 0xFF), 0, 0}) + .withComponent(Protocol.TCP, new byte[]{0x23, 0x28})) + .collect(Collectors.toList()); + + private final TimeController timeController = new TimeControllerImpl(); + private final ControlledExecutorServiceImpl controlledExecutor = new ControlledExecutorServiceImpl(); + private final GossipScoreParams gossipScoreParams; + private final DefaultGossipScore score; + + public GossipScoreBenchmark() { + Map topicParamMap = topics.stream() + .collect(Collectors.toMap(Function.identity(), __ -> new GossipTopicScoreParams())); + GossipTopicsScoreParams gossipTopicsScoreParams = new GossipTopicsScoreParams(new GossipTopicScoreParams(), topicParamMap); + + gossipScoreParams = new GossipScoreParams(new GossipPeerScoreParams(), gossipTopicsScoreParams, 0, 0, 0, 0, 0); + controlledExecutor.setTimeController(timeController); + score = new DefaultGossipScore(gossipScoreParams, controlledExecutor, timeController::getTime); + + for (int i = 0; i < peerCount; i++) { + PeerId peerId = peerIds.get(i); + score.notifyConnected(peerId, peerAddresses.get(i)); + for (String topic : topics) { + notifyUnseenMessage(peerId, topic); + } + } + + for (int i = connectedCount; i < peerCount; i++) { + score.notifyDisconnected(peerIds.get(i)); + } + } + + private void notifyUnseenMessage(PeerId peerId, String topic) { + Rpc.Message message = Rpc.Message.newBuilder() + .addTopicIDs(topic) + .build(); + score.notifyUnseenValidMessage(peerId, new DefaultPubsubMessage(message)); + } + + @Benchmark + public void scoresDelay0(Blackhole bh) { + for (int i = 0; i < connectedCount; i++) { + double s = score.score(peerIds.get(i)); + bh.consume(s); + } + } + + @Benchmark + public void scoresDelay100(Blackhole bh) { + timeController.addTime(100); + + for (int i = 0; i < connectedCount; i++) { + double s = score.score(peerIds.get(i)); + bh.consume(s); + } + } + + @Benchmark + public void scoresDelay10000(Blackhole bh) { + timeController.addTime(10000); + + for (int i = 0; i < connectedCount; i++) { + double s = score.score(peerIds.get(i)); + bh.consume(s); + } + } + + /** + * Uncomment for debugging + */ +// public static void main(String[] args) { +// GossipScoreBenchmark benchmark = new GossipScoreBenchmark(); +// Blackhole blackhole = new Blackhole("Today's password is swordfish. I understand instantiating Blackholes directly is dangerous."); +// benchmark.scoresDelay0(blackhole); +// } +} diff --git a/src/main/kotlin/io/libp2p/etc/types/MultiBiMap.kt b/src/main/kotlin/io/libp2p/etc/types/MultiBiMap.kt new file mode 100644 index 000000000..d6ce27f19 --- /dev/null +++ b/src/main/kotlin/io/libp2p/etc/types/MultiBiMap.kt @@ -0,0 +1,90 @@ +package io.libp2p.etc.types + +fun mutableMultiBiMap(): MutableMultiBiMap = HashSetMultiBiMap() + +/** + * Associates values of type [TFirst] to a set of values of type [TSecond] + * and back: associates values of type [TSecond] to a set of values of type [TFirst] + */ +interface MultiBiMap { + + fun getByFirst(first: TFirst): Set + fun getBySecond(second: TSecond): Set + + fun valuesFirst(): Set + fun valuesSecond(): Set + + fun asFirstToSecondMap(): Map> = valuesFirst().associateWith { getByFirst(it) } + fun asSecondToFirstMap(): Map> = valuesSecond().associateWith { getBySecond(it) } +} + +interface MutableMultiBiMap : MultiBiMap { + + fun add(first: TFirst, second: TSecond) + + fun remove(first: TFirst, second: TSecond) + fun removeAllByFirst(first: TFirst) + fun removeAllBySecond(second: TSecond) +} + +internal class HashSetMultiBiMap : MutableMultiBiMap { + private val firstToSecondMap = mutableMapOf>() + private val secondToFirstMap = mutableMapOf>() + + override fun getByFirst(first: TFirst): Set = firstToSecondMap[first] ?: emptySet() + override fun getBySecond(second: TSecond): Set = secondToFirstMap[second] ?: emptySet() + override fun valuesFirst(): Set = firstToSecondMap.keys + override fun valuesSecond(): Set = secondToFirstMap.keys + override fun asFirstToSecondMap(): Map> = firstToSecondMap + override fun asSecondToFirstMap(): Map> = secondToFirstMap + + override fun add(first: TFirst, second: TSecond) { + firstToSecondMap.computeIfAbsent(first) { mutableSetOf() } += second + secondToFirstMap.computeIfAbsent(second) { mutableSetOf() } += first + } + + private fun removeFromFirstToSecondMap(first: TFirst, second: TSecond) { + firstToSecondMap.compute(first) { _, curSecondValues -> + if (curSecondValues != null) { + curSecondValues -= second + if (curSecondValues.isNotEmpty()) { + curSecondValues + } else { + null + } + } else { + null + } + } + } + + private fun removeFromSecondToFirstMap(first: TFirst, second: TSecond) { + secondToFirstMap.compute(second) { _, curFirstValues -> + if (curFirstValues != null) { + curFirstValues -= first + if (curFirstValues.isNotEmpty()) { + curFirstValues + } else { + null + } + } else { + null + } + } + } + + override fun remove(first: TFirst, second: TSecond) { + removeFromFirstToSecondMap(first, second) + removeFromSecondToFirstMap(first, second) + } + + override fun removeAllByFirst(first: TFirst) { + val removedSecondValues = firstToSecondMap.remove(first) ?: emptySet() + removedSecondValues.forEach { removeFromSecondToFirstMap(first, it) } + } + + override fun removeAllBySecond(second: TSecond) { + val removedFirstValues = secondToFirstMap.remove(second) ?: emptySet() + removedFirstValues.forEach { removeFromFirstToSecondMap(it, second) } + } +} diff --git a/src/main/kotlin/io/libp2p/etc/util/P2PService.kt b/src/main/kotlin/io/libp2p/etc/util/P2PService.kt index 0c712ce14..f48f05b50 100644 --- a/src/main/kotlin/io/libp2p/etc/util/P2PService.kt +++ b/src/main/kotlin/io/libp2p/etc/util/P2PService.kt @@ -166,8 +166,8 @@ abstract class P2PService( } protected open fun streamDisconnected(stream: StreamHandler) { - val peerHandler = stream.getPeerHandler() if (stream.aborted) return + val peerHandler = stream.getPeerHandler() activePeersMutable -= peerHandler if (peersMutable.remove(peerHandler)) { onPeerDisconnected(peerHandler) diff --git a/src/main/kotlin/io/libp2p/pubsub/AbstractRouter.kt b/src/main/kotlin/io/libp2p/pubsub/AbstractRouter.kt index 91a8c1d23..9d6dfbb27 100644 --- a/src/main/kotlin/io/libp2p/pubsub/AbstractRouter.kt +++ b/src/main/kotlin/io/libp2p/pubsub/AbstractRouter.kt @@ -4,12 +4,7 @@ import io.libp2p.core.BadPeerException import io.libp2p.core.PeerId import io.libp2p.core.Stream import io.libp2p.core.pubsub.ValidationResult -import io.libp2p.etc.types.MultiSet -import io.libp2p.etc.types.completedExceptionally -import io.libp2p.etc.types.copy -import io.libp2p.etc.types.forward -import io.libp2p.etc.types.thenApplyAll -import io.libp2p.etc.types.toWBytes +import io.libp2p.etc.types.* import io.libp2p.etc.util.P2PServiceSemiDuplex import io.libp2p.etc.util.netty.protobuf.LimitedProtobufVarint32FrameDecoder import io.netty.channel.ChannelHandler @@ -51,7 +46,7 @@ abstract class AbstractRouter( protected var msgHandler: PubsubMessageHandler = { throw IllegalStateException("Message handler is not initialized for PubsubRouter") } - protected open val peerTopics = MultiSet() + protected open val peersTopics = mutableMultiBiMap() protected open val subscribedTopics = linkedSetOf() protected open val pendingRpcParts = PendingRpcPartsMap { DefaultRpcPartsQueue() } protected open val pendingMessagePromises = MultiSet>() @@ -176,7 +171,8 @@ abstract class AbstractRouter( try { val subscriptions = msg.subscriptionsList.map { PubsubSubscription(it.topicid, it.subscribe) } - subscriptionFilter.filterIncomingSubscriptions(subscriptions, peerTopics[peer]) + subscriptionFilter + .filterIncomingSubscriptions(subscriptions, peersTopics.getByFirst(peer)) .forEach { handleMessageSubscriptions(peer, it) } } catch (e: Exception) { logger.debug("Subscription filter error, ignoring message from peer $peer", e) @@ -274,7 +270,7 @@ abstract class AbstractRouter( override fun onPeerDisconnected(peer: PeerHandler) { super.onPeerDisconnected(peer) - peerTopics.removeAll(peer) + peersTopics.removeAllByFirst(peer) } override fun onPeerWireException(peer: PeerHandler?, cause: Throwable) { @@ -293,19 +289,15 @@ abstract class AbstractRouter( private fun handleMessageSubscriptions(peer: PeerHandler, msg: PubsubSubscription) { if (msg.subscribe) { - peerTopics[peer] += msg.topic + peersTopics.add(peer, msg.topic) } else { - peerTopics[peer] -= msg.topic + peersTopics.remove(peer, msg.topic) } } - protected fun getTopicsPeers(topics: Collection) = - activePeers.filter { topics.intersect(peerTopics[it]).isNotEmpty() } + protected fun getTopicPeers(topic: Topic) = peersTopics.getBySecond(topic) - protected fun getTopicPeers(topic: String) = - activePeers.filter { topic in peerTopics[it] } - - override fun subscribe(vararg topics: String) { + override fun subscribe(vararg topics: Topic) { runOnEventThread { topics.forEach(::subscribe) flushAllPending() @@ -331,11 +323,7 @@ abstract class AbstractRouter( override fun getPeerTopics(): CompletableFuture>> { return submitOnEventThread { - val topicsByPeerId = hashMapOf>() - peerTopics.forEach { entry -> - topicsByPeerId[entry.key.peerId] = HashSet(entry.value) - } - topicsByPeerId + peersTopics.asFirstToSecondMap().mapKeys { it.key.peerId } } } diff --git a/src/main/kotlin/io/libp2p/pubsub/flood/FloodRouter.kt b/src/main/kotlin/io/libp2p/pubsub/flood/FloodRouter.kt index 7cc380b61..9bed00ddd 100644 --- a/src/main/kotlin/io/libp2p/pubsub/flood/FloodRouter.kt +++ b/src/main/kotlin/io/libp2p/pubsub/flood/FloodRouter.kt @@ -37,7 +37,10 @@ class FloodRouter(executor: ScheduledExecutorService = Executors.newSingleThread } private fun broadcast(msg: PubsubMessage, receivedFrom: PeerHandler?): CompletableFuture { - val sentFutures = getTopicsPeers(msg.topics) + val peers = msg.topics + .map { getTopicPeers(it) } + .reduce { p1, p2 -> p1 + p2 } + val sentFutures = peers .filter { it != receivedFrom } .map { submitPublishMessage(it, msg) } return anyComplete(sentFutures) diff --git a/src/test/kotlin/io/libp2p/etc/types/MultiBiMapTest.kt b/src/test/kotlin/io/libp2p/etc/types/MultiBiMapTest.kt new file mode 100644 index 000000000..9bec1dfc2 --- /dev/null +++ b/src/test/kotlin/io/libp2p/etc/types/MultiBiMapTest.kt @@ -0,0 +1,190 @@ +package io.libp2p.etc.types + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertDoesNotThrow + +class MultiBiMapTest { + + val map = mutableMultiBiMap() + + @Test + fun `empty map test`() { + checkEmpty() + assertThat(map.getByFirst("any")).isEmpty() + assertThat(map.getBySecond(111)).isEmpty() + } + + private fun checkEmpty() { + assertThat(map.valuesFirst()).isEmpty() + assertThat(map.valuesSecond()).isEmpty() + assertThat(map.asFirstToSecondMap()).isEmpty() + assertThat(map.asSecondToFirstMap()).isEmpty() + } + + @Test + fun `add one test`() { + map.add("a", 1) + + assertThat(map.valuesFirst()).containsExactlyInAnyOrder("a") + assertThat(map.valuesSecond()).containsExactlyInAnyOrder(1) + assertThat(map.asFirstToSecondMap()).isEqualTo(mapOf("a" to setOf(1))) + assertThat(map.asSecondToFirstMap()).isEqualTo(mapOf(1 to setOf("a"))) + assertThat(map.getByFirst("b")).isEmpty() + assertThat(map.getByFirst("a")).isEqualTo(setOf(1)) + assertThat(map.getBySecond(2)).isEmpty() + assertThat(map.getBySecond(1)).isEqualTo(setOf("a")) + } + + @Test + fun `add-remove one test`() { + map.add("a", 1) + map.remove("a", 1) + + checkEmpty() + assertThat(map.getByFirst("a")).isEmpty() + assertThat(map.getBySecond(1)).isEmpty() + } + + @Test + fun `add-remove two test`() { + map.add("a", 1) + map.add("a", 2) + map.remove("a", 1) + map.remove("a", 2) + + checkEmpty() + assertThat(map.getByFirst("a")).isEmpty() + assertThat(map.getBySecond(1)).isEmpty() + assertThat(map.getBySecond(2)).isEmpty() + } + + @Test + fun `add two remove by first test`() { + map.add("a", 1) + map.add("a", 2) + map.removeAllByFirst("a") + + checkEmpty() + assertThat(map.getByFirst("a")).isEmpty() + assertThat(map.getBySecond(1)).isEmpty() + assertThat(map.getBySecond(2)).isEmpty() + } + + @Test + fun `add two remove by second test`() { + map.add("a", 1) + map.add("b", 1) + map.removeAllBySecond(1) + + checkEmpty() + assertThat(map.getByFirst("a")).isEmpty() + assertThat(map.getBySecond(1)).isEmpty() + assertThat(map.getBySecond(2)).isEmpty() + } + + @Test + fun `add two test`() { + map.add("a", 1) + map.add("a", 2) + + assertThat(map.valuesFirst()).containsExactlyInAnyOrder("a") + assertThat(map.valuesSecond()).containsExactlyInAnyOrder(1, 2) + assertThat(map.asFirstToSecondMap()).isEqualTo(mapOf("a" to setOf(1, 2))) + assertThat(map.asSecondToFirstMap()).isEqualTo(mapOf(1 to setOf("a"), 2 to setOf("a"))) + assertThat(map.getByFirst("b")).isEmpty() + assertThat(map.getByFirst("a")).isEqualTo(setOf(1, 2)) + assertThat(map.getBySecond(1)).isEqualTo(setOf("a")) + assertThat(map.getBySecond(2)).isEqualTo(setOf("a")) + assertThat(map.getBySecond(3)).isEmpty() + } + + @Test + fun `add two remove one test`() { + map.add("a", 1) + map.add("a", 2) + map.remove("a", 1) + + assertThat(map.valuesFirst()).containsExactlyInAnyOrder("a") + assertThat(map.valuesSecond()).containsExactlyInAnyOrder(2) + assertThat(map.asFirstToSecondMap()).isEqualTo(mapOf("a" to setOf(2))) + assertThat(map.asSecondToFirstMap()).isEqualTo(mapOf(2 to setOf("a"))) + assertThat(map.getByFirst("b")).isEmpty() + assertThat(map.getByFirst("a")).isEqualTo(setOf(2)) + assertThat(map.getBySecond(1)).isEmpty() + assertThat(map.getBySecond(2)).isEqualTo(setOf("a")) + } + + @Test + fun `remove missing values does nothing`() { + map.add("a", 1) + + assertDoesNotThrow { map.remove("a", 2) } + assertDoesNotThrow { map.remove("b", 1) } + assertDoesNotThrow { map.removeAllByFirst("b") } + assertDoesNotThrow { map.removeAllBySecond(2) } + + assertThat(map.valuesFirst()).containsExactlyInAnyOrder("a") + assertThat(map.valuesSecond()).containsExactlyInAnyOrder(1) + assertThat(map.asFirstToSecondMap()).isEqualTo(mapOf("a" to setOf(1))) + assertThat(map.asSecondToFirstMap()).isEqualTo(mapOf(1 to setOf("a"))) + assertThat(map.getByFirst("b")).isEmpty() + assertThat(map.getByFirst("a")).isEqualTo(setOf(1)) + assertThat(map.getBySecond(2)).isEmpty() + assertThat(map.getBySecond(1)).isEqualTo(setOf("a")) + } + + @Test + fun `add four remove one test`() { + map.add("a", 1) + map.add("a", 2) + map.add("b", 1) + map.add("b", 2) + map.remove("a", 1) + + assertThat(map.valuesFirst()).containsExactlyInAnyOrder("a", "b") + assertThat(map.valuesSecond()).containsExactlyInAnyOrder(1, 2) + assertThat(map.asFirstToSecondMap()).isEqualTo(mapOf("a" to setOf(2), "b" to setOf(1, 2))) + assertThat(map.asSecondToFirstMap()).isEqualTo(mapOf(1 to setOf("b"), 2 to setOf("a", "b"))) + assertThat(map.getByFirst("a")).isEqualTo(setOf(2)) + assertThat(map.getByFirst("b")).isEqualTo(setOf(1, 2)) + assertThat(map.getBySecond(1)).isEqualTo(setOf("b")) + assertThat(map.getBySecond(2)).isEqualTo(setOf("a", "b")) + } + + @Test + fun `add four remove all by first test`() { + map.add("a", 1) + map.add("a", 2) + map.add("b", 1) + map.add("b", 2) + map.removeAllByFirst("a") + + assertThat(map.valuesFirst()).containsExactlyInAnyOrder("b") + assertThat(map.valuesSecond()).containsExactlyInAnyOrder(1, 2) + assertThat(map.asFirstToSecondMap()).isEqualTo(mapOf("b" to setOf(1, 2))) + assertThat(map.asSecondToFirstMap()).isEqualTo(mapOf(1 to setOf("b"), 2 to setOf("b"))) + assertThat(map.getByFirst("a")).isEmpty() + assertThat(map.getByFirst("b")).isEqualTo(setOf(1, 2)) + assertThat(map.getBySecond(1)).isEqualTo(setOf("b")) + assertThat(map.getBySecond(2)).isEqualTo(setOf("b")) + } + + @Test + fun `add four remove all by second test`() { + map.add("a", 1) + map.add("a", 2) + map.add("b", 1) + map.add("b", 2) + map.removeAllBySecond(1) + + assertThat(map.valuesFirst()).containsExactlyInAnyOrder("a", "b") + assertThat(map.valuesSecond()).containsExactlyInAnyOrder(2) + assertThat(map.asFirstToSecondMap()).isEqualTo(mapOf("a" to setOf(2), "b" to setOf(2))) + assertThat(map.asSecondToFirstMap()).isEqualTo(mapOf(2 to setOf("a", "b"))) + assertThat(map.getByFirst("a")).isEqualTo(setOf(2)) + assertThat(map.getByFirst("b")).isEqualTo(setOf(2)) + assertThat(map.getBySecond(1)).isEmpty() + assertThat(map.getBySecond(2)).isEqualTo(setOf("a", "b")) + } +} diff --git a/src/test/java/io/libp2p/tools/p2pd/AsyncDaemonExecutor.java b/src/testFixtures/java/io/libp2p/tools/p2pd/AsyncDaemonExecutor.java similarity index 100% rename from src/test/java/io/libp2p/tools/p2pd/AsyncDaemonExecutor.java rename to src/testFixtures/java/io/libp2p/tools/p2pd/AsyncDaemonExecutor.java diff --git a/src/test/java/io/libp2p/tools/p2pd/ControlConnector.java b/src/testFixtures/java/io/libp2p/tools/p2pd/ControlConnector.java similarity index 100% rename from src/test/java/io/libp2p/tools/p2pd/ControlConnector.java rename to src/testFixtures/java/io/libp2p/tools/p2pd/ControlConnector.java diff --git a/src/test/java/io/libp2p/tools/p2pd/DaemonChannelHandler.java b/src/testFixtures/java/io/libp2p/tools/p2pd/DaemonChannelHandler.java similarity index 100% rename from src/test/java/io/libp2p/tools/p2pd/DaemonChannelHandler.java rename to src/testFixtures/java/io/libp2p/tools/p2pd/DaemonChannelHandler.java diff --git a/src/test/java/io/libp2p/tools/p2pd/DaemonLauncher.java b/src/testFixtures/java/io/libp2p/tools/p2pd/DaemonLauncher.java similarity index 100% rename from src/test/java/io/libp2p/tools/p2pd/DaemonLauncher.java rename to src/testFixtures/java/io/libp2p/tools/p2pd/DaemonLauncher.java diff --git a/src/test/java/io/libp2p/tools/p2pd/NettyStream.java b/src/testFixtures/java/io/libp2p/tools/p2pd/NettyStream.java similarity index 100% rename from src/test/java/io/libp2p/tools/p2pd/NettyStream.java rename to src/testFixtures/java/io/libp2p/tools/p2pd/NettyStream.java diff --git a/src/test/java/io/libp2p/tools/p2pd/P2PDDht.java b/src/testFixtures/java/io/libp2p/tools/p2pd/P2PDDht.java similarity index 100% rename from src/test/java/io/libp2p/tools/p2pd/P2PDDht.java rename to src/testFixtures/java/io/libp2p/tools/p2pd/P2PDDht.java diff --git a/src/test/java/io/libp2p/tools/p2pd/P2PDError.java b/src/testFixtures/java/io/libp2p/tools/p2pd/P2PDError.java similarity index 100% rename from src/test/java/io/libp2p/tools/p2pd/P2PDError.java rename to src/testFixtures/java/io/libp2p/tools/p2pd/P2PDError.java diff --git a/src/test/java/io/libp2p/tools/p2pd/P2PDHost.java b/src/testFixtures/java/io/libp2p/tools/p2pd/P2PDHost.java similarity index 100% rename from src/test/java/io/libp2p/tools/p2pd/P2PDHost.java rename to src/testFixtures/java/io/libp2p/tools/p2pd/P2PDHost.java diff --git a/src/test/java/io/libp2p/tools/p2pd/P2PDPubsub.java b/src/testFixtures/java/io/libp2p/tools/p2pd/P2PDPubsub.java similarity index 100% rename from src/test/java/io/libp2p/tools/p2pd/P2PDPubsub.java rename to src/testFixtures/java/io/libp2p/tools/p2pd/P2PDPubsub.java diff --git a/src/test/java/io/libp2p/tools/p2pd/StreamHandlerWrapper.java b/src/testFixtures/java/io/libp2p/tools/p2pd/StreamHandlerWrapper.java similarity index 100% rename from src/test/java/io/libp2p/tools/p2pd/StreamHandlerWrapper.java rename to src/testFixtures/java/io/libp2p/tools/p2pd/StreamHandlerWrapper.java diff --git a/src/test/java/io/libp2p/tools/p2pd/TCPControlConnector.java b/src/testFixtures/java/io/libp2p/tools/p2pd/TCPControlConnector.java similarity index 100% rename from src/test/java/io/libp2p/tools/p2pd/TCPControlConnector.java rename to src/testFixtures/java/io/libp2p/tools/p2pd/TCPControlConnector.java diff --git a/src/test/java/io/libp2p/tools/p2pd/UnixSocketControlConnector.java b/src/testFixtures/java/io/libp2p/tools/p2pd/UnixSocketControlConnector.java similarity index 100% rename from src/test/java/io/libp2p/tools/p2pd/UnixSocketControlConnector.java rename to src/testFixtures/java/io/libp2p/tools/p2pd/UnixSocketControlConnector.java diff --git a/src/test/java/io/libp2p/tools/p2pd/Util.java b/src/testFixtures/java/io/libp2p/tools/p2pd/Util.java similarity index 100% rename from src/test/java/io/libp2p/tools/p2pd/Util.java rename to src/testFixtures/java/io/libp2p/tools/p2pd/Util.java diff --git a/src/test/java/io/libp2p/tools/p2pd/libp2pj/Connector.java b/src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/Connector.java similarity index 100% rename from src/test/java/io/libp2p/tools/p2pd/libp2pj/Connector.java rename to src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/Connector.java diff --git a/src/test/java/io/libp2p/tools/p2pd/libp2pj/DHT.java b/src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/DHT.java similarity index 100% rename from src/test/java/io/libp2p/tools/p2pd/libp2pj/DHT.java rename to src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/DHT.java diff --git a/src/test/java/io/libp2p/tools/p2pd/libp2pj/Host.java b/src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/Host.java similarity index 100% rename from src/test/java/io/libp2p/tools/p2pd/libp2pj/Host.java rename to src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/Host.java diff --git a/src/test/java/io/libp2p/tools/p2pd/libp2pj/Muxer.java b/src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/Muxer.java similarity index 100% rename from src/test/java/io/libp2p/tools/p2pd/libp2pj/Muxer.java rename to src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/Muxer.java diff --git a/src/test/java/io/libp2p/tools/p2pd/libp2pj/Peer.java b/src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/Peer.java similarity index 100% rename from src/test/java/io/libp2p/tools/p2pd/libp2pj/Peer.java rename to src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/Peer.java diff --git a/src/test/java/io/libp2p/tools/p2pd/libp2pj/PeerInfo.java b/src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/PeerInfo.java similarity index 100% rename from src/test/java/io/libp2p/tools/p2pd/libp2pj/PeerInfo.java rename to src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/PeerInfo.java diff --git a/src/test/java/io/libp2p/tools/p2pd/libp2pj/Protocol.java b/src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/Protocol.java similarity index 100% rename from src/test/java/io/libp2p/tools/p2pd/libp2pj/Protocol.java rename to src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/Protocol.java diff --git a/src/test/java/io/libp2p/tools/p2pd/libp2pj/Stream.java b/src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/Stream.java similarity index 100% rename from src/test/java/io/libp2p/tools/p2pd/libp2pj/Stream.java rename to src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/Stream.java diff --git a/src/test/java/io/libp2p/tools/p2pd/libp2pj/StreamHandler.java b/src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/StreamHandler.java similarity index 100% rename from src/test/java/io/libp2p/tools/p2pd/libp2pj/StreamHandler.java rename to src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/StreamHandler.java diff --git a/src/test/java/io/libp2p/tools/p2pd/libp2pj/Transport.java b/src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/Transport.java similarity index 100% rename from src/test/java/io/libp2p/tools/p2pd/libp2pj/Transport.java rename to src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/Transport.java diff --git a/src/test/java/io/libp2p/tools/p2pd/libp2pj/exceptions/MalformedMultiaddressException.java b/src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/exceptions/MalformedMultiaddressException.java similarity index 100% rename from src/test/java/io/libp2p/tools/p2pd/libp2pj/exceptions/MalformedMultiaddressException.java rename to src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/exceptions/MalformedMultiaddressException.java diff --git a/src/test/java/io/libp2p/tools/p2pd/libp2pj/exceptions/UnsupportedTransportException.java b/src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/exceptions/UnsupportedTransportException.java similarity index 100% rename from src/test/java/io/libp2p/tools/p2pd/libp2pj/exceptions/UnsupportedTransportException.java rename to src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/exceptions/UnsupportedTransportException.java diff --git a/src/test/java/io/libp2p/tools/p2pd/libp2pj/util/Base58.java b/src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/util/Base58.java similarity index 100% rename from src/test/java/io/libp2p/tools/p2pd/libp2pj/util/Base58.java rename to src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/util/Base58.java diff --git a/src/test/java/io/libp2p/tools/p2pd/libp2pj/util/Cid.java b/src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/util/Cid.java similarity index 100% rename from src/test/java/io/libp2p/tools/p2pd/libp2pj/util/Cid.java rename to src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/util/Cid.java diff --git a/src/test/java/io/libp2p/tools/p2pd/libp2pj/util/Util.java b/src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/util/Util.java similarity index 100% rename from src/test/java/io/libp2p/tools/p2pd/libp2pj/util/Util.java rename to src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/util/Util.java diff --git a/src/test/java/io/libp2p/tools/schedulers/AbstractSchedulers.java b/src/testFixtures/java/io/libp2p/tools/schedulers/AbstractSchedulers.java similarity index 100% rename from src/test/java/io/libp2p/tools/schedulers/AbstractSchedulers.java rename to src/testFixtures/java/io/libp2p/tools/schedulers/AbstractSchedulers.java diff --git a/src/test/java/io/libp2p/tools/schedulers/ControlledExecutorService.java b/src/testFixtures/java/io/libp2p/tools/schedulers/ControlledExecutorService.java similarity index 100% rename from src/test/java/io/libp2p/tools/schedulers/ControlledExecutorService.java rename to src/testFixtures/java/io/libp2p/tools/schedulers/ControlledExecutorService.java diff --git a/src/test/java/io/libp2p/tools/schedulers/ControlledExecutorServiceImpl.java b/src/testFixtures/java/io/libp2p/tools/schedulers/ControlledExecutorServiceImpl.java similarity index 100% rename from src/test/java/io/libp2p/tools/schedulers/ControlledExecutorServiceImpl.java rename to src/testFixtures/java/io/libp2p/tools/schedulers/ControlledExecutorServiceImpl.java diff --git a/src/test/java/io/libp2p/tools/schedulers/ControlledSchedulers.java b/src/testFixtures/java/io/libp2p/tools/schedulers/ControlledSchedulers.java similarity index 100% rename from src/test/java/io/libp2p/tools/schedulers/ControlledSchedulers.java rename to src/testFixtures/java/io/libp2p/tools/schedulers/ControlledSchedulers.java diff --git a/src/test/java/io/libp2p/tools/schedulers/ControlledSchedulersImpl.java b/src/testFixtures/java/io/libp2p/tools/schedulers/ControlledSchedulersImpl.java similarity index 100% rename from src/test/java/io/libp2p/tools/schedulers/ControlledSchedulersImpl.java rename to src/testFixtures/java/io/libp2p/tools/schedulers/ControlledSchedulersImpl.java diff --git a/src/test/java/io/libp2p/tools/schedulers/DefaultSchedulers.java b/src/testFixtures/java/io/libp2p/tools/schedulers/DefaultSchedulers.java similarity index 100% rename from src/test/java/io/libp2p/tools/schedulers/DefaultSchedulers.java rename to src/testFixtures/java/io/libp2p/tools/schedulers/DefaultSchedulers.java diff --git a/src/test/java/io/libp2p/tools/schedulers/ErrorHandlingScheduler.java b/src/testFixtures/java/io/libp2p/tools/schedulers/ErrorHandlingScheduler.java similarity index 100% rename from src/test/java/io/libp2p/tools/schedulers/ErrorHandlingScheduler.java rename to src/testFixtures/java/io/libp2p/tools/schedulers/ErrorHandlingScheduler.java diff --git a/src/test/java/io/libp2p/tools/schedulers/ExecutorScheduler.java b/src/testFixtures/java/io/libp2p/tools/schedulers/ExecutorScheduler.java similarity index 100% rename from src/test/java/io/libp2p/tools/schedulers/ExecutorScheduler.java rename to src/testFixtures/java/io/libp2p/tools/schedulers/ExecutorScheduler.java diff --git a/src/test/java/io/libp2p/tools/schedulers/LatestExecutor.java b/src/testFixtures/java/io/libp2p/tools/schedulers/LatestExecutor.java similarity index 100% rename from src/test/java/io/libp2p/tools/schedulers/LatestExecutor.java rename to src/testFixtures/java/io/libp2p/tools/schedulers/LatestExecutor.java diff --git a/src/test/java/io/libp2p/tools/schedulers/LoggerMDCExecutor.java b/src/testFixtures/java/io/libp2p/tools/schedulers/LoggerMDCExecutor.java similarity index 100% rename from src/test/java/io/libp2p/tools/schedulers/LoggerMDCExecutor.java rename to src/testFixtures/java/io/libp2p/tools/schedulers/LoggerMDCExecutor.java diff --git a/src/test/java/io/libp2p/tools/schedulers/RunnableEx.java b/src/testFixtures/java/io/libp2p/tools/schedulers/RunnableEx.java similarity index 100% rename from src/test/java/io/libp2p/tools/schedulers/RunnableEx.java rename to src/testFixtures/java/io/libp2p/tools/schedulers/RunnableEx.java diff --git a/src/test/java/io/libp2p/tools/schedulers/Scheduler.java b/src/testFixtures/java/io/libp2p/tools/schedulers/Scheduler.java similarity index 100% rename from src/test/java/io/libp2p/tools/schedulers/Scheduler.java rename to src/testFixtures/java/io/libp2p/tools/schedulers/Scheduler.java diff --git a/src/test/java/io/libp2p/tools/schedulers/Schedulers.java b/src/testFixtures/java/io/libp2p/tools/schedulers/Schedulers.java similarity index 100% rename from src/test/java/io/libp2p/tools/schedulers/Schedulers.java rename to src/testFixtures/java/io/libp2p/tools/schedulers/Schedulers.java diff --git a/src/test/java/io/libp2p/tools/schedulers/TaskQueue.java b/src/testFixtures/java/io/libp2p/tools/schedulers/TaskQueue.java similarity index 100% rename from src/test/java/io/libp2p/tools/schedulers/TaskQueue.java rename to src/testFixtures/java/io/libp2p/tools/schedulers/TaskQueue.java diff --git a/src/test/java/io/libp2p/tools/schedulers/TimeController.java b/src/testFixtures/java/io/libp2p/tools/schedulers/TimeController.java similarity index 100% rename from src/test/java/io/libp2p/tools/schedulers/TimeController.java rename to src/testFixtures/java/io/libp2p/tools/schedulers/TimeController.java diff --git a/src/test/java/io/libp2p/tools/schedulers/TimeControllerImpl.java b/src/testFixtures/java/io/libp2p/tools/schedulers/TimeControllerImpl.java similarity index 100% rename from src/test/java/io/libp2p/tools/schedulers/TimeControllerImpl.java rename to src/testFixtures/java/io/libp2p/tools/schedulers/TimeControllerImpl.java