From fe40edc600d802da564a3ff91c19125bad9d4c56 Mon Sep 17 00:00:00 2001 From: Lucas Saldanha Date: Fri, 13 Feb 2026 16:58:22 +1300 Subject: [PATCH 1/4] Removing peer from extensions support registry on disconnect --- libp2p/src/main/kotlin/io/libp2p/pubsub/AbstractRouter.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libp2p/src/main/kotlin/io/libp2p/pubsub/AbstractRouter.kt b/libp2p/src/main/kotlin/io/libp2p/pubsub/AbstractRouter.kt index 66f7e4ab..b329f07d 100644 --- a/libp2p/src/main/kotlin/io/libp2p/pubsub/AbstractRouter.kt +++ b/libp2p/src/main/kotlin/io/libp2p/pubsub/AbstractRouter.kt @@ -149,6 +149,9 @@ abstract class AbstractRouter( subscribedTopics.forEach { partsQueue.addSubscribe(it) } + + // TODO end control extension + flushPending(peer) } From 3f7ff5812cf808db49df8fa147a62ec99d7b8c12 Mon Sep 17 00:00:00 2001 From: Lucas Saldanha Date: Tue, 17 Feb 2026 13:36:42 +1300 Subject: [PATCH 2/4] Updated naming convention --- libp2p/src/main/kotlin/io/libp2p/pubsub/AbstractRouter.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/libp2p/src/main/kotlin/io/libp2p/pubsub/AbstractRouter.kt b/libp2p/src/main/kotlin/io/libp2p/pubsub/AbstractRouter.kt index b329f07d..66f7e4ab 100644 --- a/libp2p/src/main/kotlin/io/libp2p/pubsub/AbstractRouter.kt +++ b/libp2p/src/main/kotlin/io/libp2p/pubsub/AbstractRouter.kt @@ -149,9 +149,6 @@ abstract class AbstractRouter( subscribedTopics.forEach { partsQueue.addSubscribe(it) } - - // TODO end control extension - flushPending(peer) } From 94169f862be60673e515446f2c068000d5bcd0d8 Mon Sep 17 00:00:00 2001 From: Lucas Saldanha Date: Wed, 18 Feb 2026 11:04:14 +1300 Subject: [PATCH 3/4] Updated GossipRouterBuilder to support toggling gossip extensions --- .../kotlin/io/libp2p/pubsub/AbstractRouter.kt | 4 +- .../pubsub/gossip/GossipExtensionsState.kt | 20 ++- .../io/libp2p/pubsub/gossip/GossipRouter.kt | 58 ++++--- .../gossip/builders/GossipRouterBuilder.kt | 7 +- .../gossip/GossipExtensionsStateTest.kt | 150 +++++++++++++++++- .../pubsub/gossip/GossipRouterBuilderTest.kt | 29 ++++ .../libp2p/pubsub/gossip/GossipTestsBase.kt | 14 +- .../GossipExtensionsMessageHandlingTest.kt | 30 +++- .../simulate/gossip/router/SimGossipRouter.kt | 1 + 9 files changed, 282 insertions(+), 31 deletions(-) create mode 100644 libp2p/src/test/kotlin/io/libp2p/pubsub/gossip/GossipRouterBuilderTest.kt diff --git a/libp2p/src/main/kotlin/io/libp2p/pubsub/AbstractRouter.kt b/libp2p/src/main/kotlin/io/libp2p/pubsub/AbstractRouter.kt index 66f7e4ab..a72b93cc 100644 --- a/libp2p/src/main/kotlin/io/libp2p/pubsub/AbstractRouter.kt +++ b/libp2p/src/main/kotlin/io/libp2p/pubsub/AbstractRouter.kt @@ -185,9 +185,7 @@ abstract class AbstractRouter( processControl(msg.control, peer) } - // TODO we need to handle the existence of extension messages more generically (https://github.com/libp2p/jvm-libp2p/issues/441) - - if (protocol.supportsExtensions() && (msg.hasTestExtension() || msg.hasPartial())) { + if (protocol.supportsExtensions()) { processExtensions(msg, peer) } diff --git a/libp2p/src/main/kotlin/io/libp2p/pubsub/gossip/GossipExtensionsState.kt b/libp2p/src/main/kotlin/io/libp2p/pubsub/gossip/GossipExtensionsState.kt index 24daf6ad..94abbd4a 100644 --- a/libp2p/src/main/kotlin/io/libp2p/pubsub/gossip/GossipExtensionsState.kt +++ b/libp2p/src/main/kotlin/io/libp2p/pubsub/gossip/GossipExtensionsState.kt @@ -3,7 +3,17 @@ package io.libp2p.pubsub.gossip import io.libp2p.core.PeerId import pubsub.pb.Rpc -class GossipExtensionsState { +data class GossipExtensionsConfig( + val partialMessagesEnabled: Boolean = false, + val testExtensionEnabled: Boolean = false +) + +class GossipExtensionsState(gossipExtensionsConfig: GossipExtensionsConfig? = null) { + + val localExtensionSupport: Rpc.ControlExtensions = Rpc.ControlExtensions.newBuilder() + .setTestExtension(gossipExtensionsConfig?.testExtensionEnabled ?: false) + .setPartialMessages(gossipExtensionsConfig?.partialMessagesEnabled ?: false) + .build() /* Tracks the peers that we have already sent a control extensions message @@ -35,4 +45,12 @@ class GossipExtensionsState { fun hasSentControlExtensionsTo(peer: PeerId) = outgoingControlExtensionsMsgPeers.contains(peer) + + fun testExtensionsEnabled() = localExtensionSupport.testExtension + fun peerSupportsTestExtensions(peerId: PeerId) = + peerExtensionSupportMap[peerId]?.testExtension == true + + fun partialMessagesEnabled() = localExtensionSupport.partialMessages + fun peerSupportsPartialMessages(peerId: PeerId) = + peerExtensionSupportMap[peerId]?.partialMessages == true } diff --git a/libp2p/src/main/kotlin/io/libp2p/pubsub/gossip/GossipRouter.kt b/libp2p/src/main/kotlin/io/libp2p/pubsub/gossip/GossipRouter.kt index 1bb46343..795f776a 100644 --- a/libp2p/src/main/kotlin/io/libp2p/pubsub/gossip/GossipRouter.kt +++ b/libp2p/src/main/kotlin/io/libp2p/pubsub/gossip/GossipRouter.kt @@ -82,6 +82,7 @@ open class GossipRouter( val name: String, val mCache: MCache, val score: GossipScore, + val gossipExtensionsConfig: GossipExtensionsConfig = GossipExtensionsConfig(), subscriptionTopicSubscriptionFilter: TopicSubscriptionFilter, protocol: PubsubProtocol, @@ -132,7 +133,7 @@ open class GossipRouter( private val acceptRequestsWhitelist = mutableMapOf() override val pendingRpcParts = PendingRpcPartsMap { DefaultGossipRpcPartsQueue(params) } - val gossipExtensionsState = GossipExtensionsState() + val gossipExtensionsState = GossipExtensionsState(gossipExtensionsConfig) private fun setBackOff(peer: PeerHandler, topic: Topic) = setBackOff(peer, topic, params.pruneBackoff.toMillis()) private fun setBackOff(peer: PeerHandler, topic: Topic, delay: Long) { @@ -413,23 +414,46 @@ open class GossipRouter( } override fun processExtensions(msg: Rpc.RPC, receivedFrom: PeerHandler) { - val peerSupportedExtensions = - gossipExtensionsState.peerSupportedExtensions(receivedFrom.peerId) + when { + msg.hasTestExtension() -> { + if (!gossipExtensionsState.testExtensionsEnabled()) { + logger.trace( + "Ignoring test extension message from peer {} - test extension disabled", + msg + ) + return + } - // TODO Revisit this logic as part of adding feature flags (https://github.com/libp2p/jvm-libp2p/issues/441) + if (!gossipExtensionsState.peerSupportsTestExtensions(receivedFrom.peerId)) { + logger.trace( + "Ignoring test extension message from peer {} - did peer send ControlExtensions prior?", + msg + ) + return + } - when { - msg.hasTestExtension() && checkPeerExtensionSupport( - peerSupportedExtensions, - Rpc.ControlExtensions::hasTestExtension - ) -> processTestExtensionMessage(msg.testExtension, receivedFrom) + } + + msg.hasPartial() -> { + if (!gossipExtensionsState.partialMessagesEnabled()) { + logger.trace( + "Ignoring partial messages message from peer {} - partial messages extension disabled", + msg + ) + return + } + + if (!gossipExtensionsState.peerSupportsPartialMessages(receivedFrom.peerId)) { + logger.trace( + "Ignoring partial messages message from peer {} - did peer send ControlExtensions prior?", + msg + ) + return + } - msg.hasPartial() && checkPeerExtensionSupport( - peerSupportedExtensions, - Rpc.ControlExtensions::hasPartialMessages - ) -> processPartialMessageExtension(msg.partial, receivedFrom) + } } } @@ -822,12 +846,8 @@ open class GossipRouter( logger.trace("Sending control extensions message to peer {}", peer.peerId) - pendingRpcParts.getQueue(peer).addControlExtensions( - Rpc.ControlExtensions.newBuilder() - .setTestExtension(true) - .setPartialMessages(true) - .build() - ) + pendingRpcParts.getQueue(peer) + .addControlExtensions(gossipExtensionsState.localExtensionSupport) gossipExtensionsState.registerControlExtensionMessageSentToPeers(peer.peerId) } diff --git a/libp2p/src/main/kotlin/io/libp2p/pubsub/gossip/builders/GossipRouterBuilder.kt b/libp2p/src/main/kotlin/io/libp2p/pubsub/gossip/builders/GossipRouterBuilder.kt index 5c783ce5..f694a4e9 100644 --- a/libp2p/src/main/kotlin/io/libp2p/pubsub/gossip/builders/GossipRouterBuilder.kt +++ b/libp2p/src/main/kotlin/io/libp2p/pubsub/gossip/builders/GossipRouterBuilder.kt @@ -38,7 +38,9 @@ open class GossipRouterBuilder( eventsSubscriber(gossipScore) gossipScore }, - val gossipRouterEventListeners: MutableList = mutableListOf() + val gossipRouterEventListeners: MutableList = mutableListOf(), + + var gossipExtensionsConfig: GossipExtensionsConfig = GossipExtensionsConfig() ) { var seenCache: SeenCache> by lazyVar { TTLSeenCache(SimpleSeenCache(), params.seenTTL, currentTimeSuppluer) } @@ -62,7 +64,8 @@ open class GossipRouterBuilder( executor = scheduledAsyncExecutor, messageFactory = messageFactory, seenMessages = seenCache, - messageValidator = messageValidator + messageValidator = messageValidator, + gossipExtensionsConfig = gossipExtensionsConfig ) router.eventBroadcaster.listeners += gossipRouterEventListeners diff --git a/libp2p/src/test/kotlin/io/libp2p/pubsub/gossip/GossipExtensionsStateTest.kt b/libp2p/src/test/kotlin/io/libp2p/pubsub/gossip/GossipExtensionsStateTest.kt index 315c8dec..93f7aa8b 100644 --- a/libp2p/src/test/kotlin/io/libp2p/pubsub/gossip/GossipExtensionsStateTest.kt +++ b/libp2p/src/test/kotlin/io/libp2p/pubsub/gossip/GossipExtensionsStateTest.kt @@ -4,7 +4,11 @@ import io.libp2p.core.PeerId import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource import pubsub.pb.Rpc +import java.util.stream.Stream class GossipExtensionsStateTest { @@ -15,7 +19,9 @@ class GossipExtensionsStateTest { @BeforeEach fun setup() { - extensionsState = GossipExtensionsState() + extensionsState = GossipExtensionsState( + gossipExtensionsConfig = GossipExtensionsConfig(testExtensionEnabled = true) + ) peer1 = PeerId.random() peer2 = PeerId.random() peer3 = PeerId.random() @@ -193,6 +199,23 @@ class GossipExtensionsStateTest { assertThat(extensionsState.hasReceivedControlExtensionsFrom(peer3)).isTrue() } + @Test + fun `tracks different peer extension support for partial messages`() { + val withPartial = Rpc.ControlExtensions.newBuilder() + .setPartialMessages(true) + .build() + + val withoutPartial = Rpc.ControlExtensions.newBuilder() + .setPartialMessages(false) + .build() + + extensionsState.onControlExtensionsMessage(withPartial, peer1) + extensionsState.onControlExtensionsMessage(withoutPartial, peer2) + + assertThat(extensionsState.peerSupportsPartialMessages(peer1)).isTrue() + assertThat(extensionsState.peerSupportsPartialMessages(peer2)).isFalse() + } + @Test fun `tracks many peers simultaneously`() { val peers = (1..10).map { PeerId.random() } @@ -355,4 +378,129 @@ class GossipExtensionsStateTest { assertThat(extensionsState.hasSentControlExtensionsTo(peer1)).isFalse() assertThat(extensionsState.peerSupportedExtensions(peer1)).isNull() } + + @Test + fun `peerSupportsTestExtensions returns true when peer has extension`() { + val extension = Rpc.ControlExtensions.newBuilder() + .setTestExtension(true) + .build() + + extensionsState.onControlExtensionsMessage(extension, peer1) + + assertThat(extensionsState.peerSupportsTestExtensions(peer1)).isTrue() + } + + @Test + fun `peerSupportsTestExtensions returns false when peer doesn't have extension`() { + val extension = Rpc.ControlExtensions.newBuilder() + .setTestExtension(false) + .setPartialMessages(true) + .build() + + extensionsState.onControlExtensionsMessage(extension, peer1) + + assertThat(extensionsState.peerSupportsTestExtensions(peer1)).isFalse() + } + + @Test + fun `peerSupportsTestExtensions returns false for unknown peer`() { + assertThat(extensionsState.peerSupportsTestExtensions(peer1)).isFalse() + } + + @Test + fun `peerSupportsPartialMessages returns true when peer has extension`() { + val extension = Rpc.ControlExtensions.newBuilder() + .setPartialMessages(true) + .build() + + extensionsState.onControlExtensionsMessage(extension, peer1) + + assertThat(extensionsState.peerSupportsPartialMessages(peer1)).isTrue() + } + + @Test + fun `peerSupportsPartialMessages returns false when peer doesn't have extension`() { + val extension = Rpc.ControlExtensions.newBuilder() + .setPartialMessages(false) + .setTestExtension(true) + .build() + + extensionsState.onControlExtensionsMessage(extension, peer1) + + assertThat(extensionsState.peerSupportsPartialMessages(peer1)).isFalse() + } + + @Test + fun `peerSupportsPartialMessages returns false for unknown peer`() { + assertThat(extensionsState.peerSupportsPartialMessages(peer1)).isFalse() + } + + @Test + fun `default config has both extensions disabled`() { + val state = GossipExtensionsState() + + assertThat(state.testExtensionsEnabled()).isFalse() + assertThat(state.partialMessagesEnabled()).isFalse() + } + + @ParameterizedTest + @MethodSource("gossipExtensionConfigParams") + fun `config flags combinations for all extensions`( + description: String, + testExtensionsEnabled: Boolean, + partialMessagesEnabled: Boolean + ) { + val config = GossipExtensionsConfig( + testExtensionEnabled = testExtensionsEnabled, + partialMessagesEnabled = partialMessagesEnabled + ) + + assertThat(config.testExtensionEnabled).isEqualTo(testExtensionsEnabled) + .withFailMessage("expected $description") + assertThat(config.partialMessagesEnabled).isEqualTo(partialMessagesEnabled) + .withFailMessage("expected $description") + } + + companion object { + @JvmStatic + fun gossipExtensionConfigParams(): Stream { + return Stream.of( + Arguments.of("both extensions enabled", true, true), + Arguments.of("only test extensions enabled", false, true), + Arguments.of("only partial messages enabled", true, false), + Arguments.of("both extensions disabled", false, false) + ) + } + } + + @Test + fun `localExtensionSupport field reflects config`() { + val state = GossipExtensionsState( + GossipExtensionsConfig( + testExtensionEnabled = true, + partialMessagesEnabled = false + ) + ) + + val localSupport = state.localExtensionSupport + assertThat(localSupport.testExtension).isTrue() + assertThat(localSupport.partialMessages).isFalse() + } + + @Test + fun `peer extension support cleared on disconnect`() { + val extension = Rpc.ControlExtensions.newBuilder() + .setTestExtension(true) + .setPartialMessages(true) + .build() + + extensionsState.onControlExtensionsMessage(extension, peer1) + assertThat(extensionsState.peerSupportsTestExtensions(peer1)).isTrue() + assertThat(extensionsState.peerSupportsPartialMessages(peer1)).isTrue() + + extensionsState.onPeerDisconnected(peer1) + + assertThat(extensionsState.peerSupportsTestExtensions(peer1)).isFalse() + assertThat(extensionsState.peerSupportsPartialMessages(peer1)).isFalse() + } } diff --git a/libp2p/src/test/kotlin/io/libp2p/pubsub/gossip/GossipRouterBuilderTest.kt b/libp2p/src/test/kotlin/io/libp2p/pubsub/gossip/GossipRouterBuilderTest.kt new file mode 100644 index 00000000..a1727783 --- /dev/null +++ b/libp2p/src/test/kotlin/io/libp2p/pubsub/gossip/GossipRouterBuilderTest.kt @@ -0,0 +1,29 @@ +package io.libp2p.pubsub.gossip + +import io.libp2p.pubsub.gossip.builders.GossipRouterBuilder +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class GossipRouterBuilderTest { + + @Test + fun `builds GossipRouter with both extensions disabled by default`() { + val router = GossipRouterBuilder().build() + + assertThat(router.gossipExtensionsState.testExtensionsEnabled()).isFalse() + assertThat(router.gossipExtensionsState.partialMessagesEnabled()).isFalse() + } + + @Test + fun `localExtensionSupport reflects config in built router`() { + val config = GossipExtensionsConfig( + testExtensionEnabled = true, + partialMessagesEnabled = false + ) + val router = GossipRouterBuilder(gossipExtensionsConfig = config).build() + + val localSupport = router.gossipExtensionsState.localExtensionSupport + assertThat(localSupport.testExtension).isTrue() + assertThat(localSupport.partialMessages).isFalse() + } +} diff --git a/libp2p/src/test/kotlin/io/libp2p/pubsub/gossip/GossipTestsBase.kt b/libp2p/src/test/kotlin/io/libp2p/pubsub/gossip/GossipTestsBase.kt index ecc91225..f25d7be4 100644 --- a/libp2p/src/test/kotlin/io/libp2p/pubsub/gossip/GossipTestsBase.kt +++ b/libp2p/src/test/kotlin/io/libp2p/pubsub/gossip/GossipTestsBase.kt @@ -62,10 +62,20 @@ abstract class GossipTestsBase { val coreParams: GossipParams = GossipParams(), val scoreParams: GossipScoreParams = GossipScoreParams(), val mockRouterFactory: DeterministicFuzzRouterFactory = createMockFuzzRouterFactory(), - val protocol: PubsubProtocol = PubsubProtocol.Gossip_V_1_1 + val protocol: PubsubProtocol = PubsubProtocol.Gossip_V_1_1, + val gossipExtensionsConfig: GossipExtensionsConfig = GossipExtensionsConfig( + testExtensionEnabled = true + ) ) { val fuzz = DeterministicFuzz() - val gossipRouterBuilderFactory = { GossipRouterBuilder(protocol = protocol, params = coreParams, scoreParams = scoreParams) } + val gossipRouterBuilderFactory = { + GossipRouterBuilder( + protocol = protocol, + params = coreParams, + scoreParams = scoreParams, + gossipExtensionsConfig = gossipExtensionsConfig + ) + } val router1 = fuzz.createTestRouter(createGossipFuzzRouterFactory(gossipRouterBuilderFactory)) val router2 = fuzz.createTestRouter(mockRouterFactory) val gossipRouter = router1.router as GossipRouter diff --git a/libp2p/src/test/kotlin/io/libp2p/pubsub/gossip/extensions/GossipExtensionsMessageHandlingTest.kt b/libp2p/src/test/kotlin/io/libp2p/pubsub/gossip/extensions/GossipExtensionsMessageHandlingTest.kt index 391e7a76..64e4f5fc 100644 --- a/libp2p/src/test/kotlin/io/libp2p/pubsub/gossip/extensions/GossipExtensionsMessageHandlingTest.kt +++ b/libp2p/src/test/kotlin/io/libp2p/pubsub/gossip/extensions/GossipExtensionsMessageHandlingTest.kt @@ -1,6 +1,7 @@ package io.libp2p.pubsub.gossip.extensions import io.libp2p.pubsub.PubsubProtocol +import io.libp2p.pubsub.gossip.GossipExtensionsConfig import io.libp2p.pubsub.gossip.GossipTestsBase import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -93,13 +94,14 @@ class GossipExtensionsMessageHandlingTest : GossipTestsBase() { DEFAULT_WAIT_TIMEOUT_IN_MILLIS ) - assertThat(receivedMessage.control.extensions.partialMessages).isTrue() assertThat(receivedMessage.control.extensions.testExtension).isTrue() } @ParameterizedTest @MethodSource("protocolVersionsWithoutExtensionSupport") - fun `control extension message not sent to peer on connection without extension support`(protocol: PubsubProtocol) { + fun `control extension message not sent to peer on connection without extension support`( + protocol: PubsubProtocol + ) { val test = TwoRoutersTest(protocol = protocol) // Should not receive control extension message on versions without extension support @@ -111,10 +113,32 @@ class GossipExtensionsMessageHandlingTest : GossipTestsBase() { } } + @Test + fun `local peer ignores test extension messages when they are disabled in config`() { + val test = TwoRoutersTest( + protocol = PubsubProtocol.Gossip_V_1_3, + gossipExtensionsConfig = GossipExtensionsConfig( + testExtensionEnabled = false + ) + ) + + test.mockRouter.sendToSingle(rpcMsgWithCtrlExtensionsAndTestExtension) + assertThrows { + test.mockRouter.waitForMessage( + { it.hasTestExtension() }, + DEFAULT_WAIT_TIMEOUT_IN_MILLIS + ) + } + } + @Test fun `control extension message contains all supported extensions flags`() { val test = TwoRoutersTest( - protocol = PubsubProtocol.Gossip_V_1_3 + protocol = PubsubProtocol.Gossip_V_1_3, + gossipExtensionsConfig = GossipExtensionsConfig( + testExtensionEnabled = true, + partialMessagesEnabled = true + ) ) val receivedMessage = test.mockRouter.waitForMessage( diff --git a/tools/simulator/src/main/kotlin/io/libp2p/simulate/gossip/router/SimGossipRouter.kt b/tools/simulator/src/main/kotlin/io/libp2p/simulate/gossip/router/SimGossipRouter.kt index b6f830f9..2acc1569 100644 --- a/tools/simulator/src/main/kotlin/io/libp2p/simulate/gossip/router/SimGossipRouter.kt +++ b/tools/simulator/src/main/kotlin/io/libp2p/simulate/gossip/router/SimGossipRouter.kt @@ -33,6 +33,7 @@ class SimGossipRouter( name, mCache, score, + gossipExtensionsConfig = GossipExtensionsConfig(), subscriptionTopicSubscriptionFilter, protocol, executor, From 049e4dc99bab87653994923573e9d33bd9e9e8a8 Mon Sep 17 00:00:00 2001 From: Lucas Saldanha Date: Wed, 18 Feb 2026 11:37:45 +1300 Subject: [PATCH 4/4] Remove unused method --- .../io/libp2p/pubsub/gossip/GossipRouter.kt | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/libp2p/src/main/kotlin/io/libp2p/pubsub/gossip/GossipRouter.kt b/libp2p/src/main/kotlin/io/libp2p/pubsub/gossip/GossipRouter.kt index 795f776a..5ba315d7 100644 --- a/libp2p/src/main/kotlin/io/libp2p/pubsub/gossip/GossipRouter.kt +++ b/libp2p/src/main/kotlin/io/libp2p/pubsub/gossip/GossipRouter.kt @@ -457,25 +457,6 @@ open class GossipRouter( } } - private fun checkPeerExtensionSupport( - peerSavedPreferences: Rpc.ControlExtensions?, - checkSupportFunction: (Rpc.ControlExtensions) -> Boolean - ): Boolean { - if (peerSavedPreferences == null) { - return false - } - - if (!checkSupportFunction.invoke(peerSavedPreferences)) { - logger.trace( - "Ignoring extension messages from peer {} - did it send an control extensions message?", - peerSavedPreferences - ) - return false - } - - return true - } - private fun processTestExtensionMessage( testExtensionMessage: Rpc.TestExtension, receivedFrom: PeerHandler