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

commit da996b8d129f8e667e8d859b681bd3eb5ba94e5a
Author: Antoine Toulme <[email protected]>
AuthorDate: Mon Jun 10 16:35:46 2019 -0700

    Add relayer
---
 dist/build.gradle                                  |  35 +++++-
 dist/docker/relayer.Dockerfile                     |  19 +++
 settings.gradle => hobbits-relayer/build.gradle    |  58 ++++-----
 .../kotlin/org/apache/tuweni/relayer/RelayerApp.kt |  46 ++++++++
 .../org/apache/tuweni/relayer/RelayerAppTest.kt    |  57 +++++++++
 .../kotlin/org/apache/tuweni/hobbits/Message.kt    |   5 +
 .../kotlin/org/apache/tuweni/hobbits/Relayer.kt    |  81 +++++++++++++
 .../org/apache/tuweni/hobbits/InteractionTest.kt   |  24 ++++
 .../org/apache/tuweni/hobbits/RelayerTest.kt       | 131 +++++++++++++++++++++
 settings.gradle                                    |   1 +
 10 files changed, 420 insertions(+), 37 deletions(-)

diff --git a/dist/build.gradle b/dist/build.gradle
index 62bd44a..cbb8ca9 100644
--- a/dist/build.gradle
+++ b/dist/build.gradle
@@ -97,6 +97,22 @@ distributions {
       }
     }
   }
+  relayer {
+    baseName = 'tuweni-relayer'
+    contents {
+      mandatoryFiles(it)
+      into('bin') {
+        from { project(':hobbits-relayer').startScripts.outputs.files }
+        fileMode = 0755
+      }
+      into('lib') {
+        def libs = []
+        libs << project(':hobbits-relayer').configurations.runtime
+        from libs
+        from project(':hobbits-relayer').jar
+      }
+    }
+  }
 }
 
 import org.gradle.crypto.checksum.Checksum
@@ -107,6 +123,8 @@ sourcesDistTar{ compression = Compression.GZIP }
 
 gossipDistTar{ compression = Compression.GZIP }
 
+relayerDistTar{ compression = Compression.GZIP }
+
 if (System.getenv('ENABLE_SIGNING') == 'true') {
   signing {
     useGpgCmd()
@@ -116,6 +134,8 @@ if (System.getenv('ENABLE_SIGNING') == 'true') {
     sign sourcesDistTar
     sign gossipDistZip
     sign gossipDistTar
+    sign relayerDistZip
+    sign relayerDistTar
   }
 }
 
@@ -125,10 +145,12 @@ task createChecksums(type: Checksum, dependsOn: [
   'sourcesDistZip',
   'sourcesDistTar',
   'gossipDistZip',
-  'gossipDistTar'
+  'gossipDistTar',
+  'relayerDistZip',
+  'relayerDistTar'
 ]) {
   files = distZip.outputs.files + distTar.outputs.files + 
sourcesDistZip.outputs.files + sourcesDistTar.outputs.files \
-      + gossipDistZip.outputs.files + gossipDistTar.outputs.files
+      + gossipDistZip.outputs.files + gossipDistTar.outputs.files + 
relayerDistZip.outputs.files + relayerDistTar.outputs.files
   outputDir = new File(project.buildDir, "distributions")
   algorithm = Checksum.Algorithm.SHA512
 }
@@ -137,9 +159,16 @@ build.dependsOn('createChecksums')
 
 import com.bmuschko.gradle.docker.tasks.image.DockerBuildImage
 
-task buildImage(type: DockerBuildImage) {
+task buildGossipImage(type: DockerBuildImage) {
   dependsOn gossipDistTar
   inputDir = projectDir
   dockerFile = file("docker/gossip.Dockerfile")
   tag = "apache-tuweni/gossip:$project.version"
 }
+
+task buildRelayerImage(type: DockerBuildImage) {
+  dependsOn relayerDistTar
+  inputDir = projectDir
+  dockerFile = file("docker/relayer.Dockerfile")
+  tag = "apache-tuweni/relayer:$project.version"
+}
diff --git a/dist/docker/relayer.Dockerfile b/dist/docker/relayer.Dockerfile
new file mode 100644
index 0000000..b471169
--- /dev/null
+++ b/dist/docker/relayer.Dockerfile
@@ -0,0 +1,19 @@
+# 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.
+
+FROM openjdk:11.0.3-jre-stretch
+
+COPY build/distributions/tuweni-relayer-*.tgz /usr/relayer.tgz
+RUN cd /usr \
+  && tar xzf relayer.tgz \
+  && mv tuweni-relayer-* relayer
+
+ENTRYPOINT ["/usr/relayer/bin/relayer"]
\ No newline at end of file
diff --git a/settings.gradle b/hobbits-relayer/build.gradle
similarity index 54%
copy from settings.gradle
copy to hobbits-relayer/build.gradle
index b198613..b27e0e6 100644
--- a/settings.gradle
+++ b/hobbits-relayer/build.gradle
@@ -1,4 +1,4 @@
-/**
+/*
  * 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
@@ -10,36 +10,26 @@
  * 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.
  */
-rootProject.name='tuweni'
-include 'bytes'
-include 'concurrent'
-include 'concurrent-coroutines'
-include 'config'
-include 'crypto'
-include 'devp2p'
-include 'dist'
-include 'dns-discovery'
-include 'eth'
-include 'eth-reference-tests'
-include 'eth-repository'
-include 'gossip'
-include 'hobbits'
-include 'io'
-include 'junit'
-include 'kademlia'
-include 'kv'
-include 'les'
-include 'merkle-trie'
-include 'net'
-include 'net-coroutines'
-include 'plumtree'
-include 'progpow'
-include 'rlp'
-include 'rlpx'
-include 'scuttlebutt'
-include 'scuttlebutt-discovery'
-include 'scuttlebutt-handshake'
-include 'scuttlebutt-rpc'
-include 'ssz'
-include 'toml'
-include 'units'
+plugins { id 'application' }
+
+description = 'Hobbits relayer application.'
+
+dependencies {
+  compile project(':hobbits')
+
+  compile 'info.picocli:picocli'
+  compile 'io.vertx:vertx-core'
+  compile 'org.bouncycastle:bcprov-jdk15on'
+  compile 'org.logl:logl-api'
+  compile 'org.logl:logl-logl'
+
+  testCompile project(':junit')
+  testCompile 'org.bouncycastle:bcprov-jdk15on'
+  testCompile 'org.junit.jupiter:junit-jupiter-api'
+  testCompile 'org.junit.jupiter:junit-jupiter-params'
+
+  testRuntime 'org.junit.jupiter:junit-jupiter-engine'
+}
+
+
+application { mainClassName = 'org.apache.tuweni.relayer.RelayerApp' }
diff --git 
a/hobbits-relayer/src/main/kotlin/org/apache/tuweni/relayer/RelayerApp.kt 
b/hobbits-relayer/src/main/kotlin/org/apache/tuweni/relayer/RelayerApp.kt
new file mode 100644
index 0000000..201eb12
--- /dev/null
+++ b/hobbits-relayer/src/main/kotlin/org/apache/tuweni/relayer/RelayerApp.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.relayer
+
+import io.vertx.core.Vertx
+import kotlinx.coroutines.runBlocking
+import org.apache.tuweni.hobbits.Relayer
+import org.bouncycastle.jce.provider.BouncyCastleProvider
+import picocli.CommandLine
+import java.security.Security
+
+internal class RelayerAppCommandlineArguments() {
+  @CommandLine.Option(names = ["-b", "--bind"], description = ["Endpoint to 
bind to"]) var bind: String = ""
+  @CommandLine.Option(names = ["-t", "--to"], description = ["Endpoint to 
relay to"]) var to: String = ""
+}
+
+/**
+ * Runs a relayer between two hobbits endpoints.
+ */
+fun main(args: Array<String>) {
+  Security.addProvider(BouncyCastleProvider())
+  val opts = 
CommandLine.populateCommand<RelayerAppCommandlineArguments>(RelayerAppCommandlineArguments(),
 *args)
+  val vertx = Vertx.vertx()
+  val relayer = Relayer(vertx, opts.bind, opts.to, {
+    System.out.println(it)
+  })
+  Runtime.getRuntime().addShutdownHook(Thread { relayer.stop()
+    vertx.close() })
+  runBlocking {
+    relayer.start()
+  }
+}
diff --git 
a/hobbits-relayer/src/test/kotlin/org/apache/tuweni/relayer/RelayerAppTest.kt 
b/hobbits-relayer/src/test/kotlin/org/apache/tuweni/relayer/RelayerAppTest.kt
new file mode 100644
index 0000000..d7d38a1
--- /dev/null
+++ 
b/hobbits-relayer/src/test/kotlin/org/apache/tuweni/relayer/RelayerAppTest.kt
@@ -0,0 +1,57 @@
+/*
+ * 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.relayer
+
+import io.vertx.core.Vertx
+import kotlinx.coroutines.runBlocking
+import org.apache.tuweni.bytes.Bytes
+import org.apache.tuweni.hobbits.HobbitsTransport
+import org.apache.tuweni.hobbits.Message
+import org.apache.tuweni.hobbits.Transport
+import org.apache.tuweni.junit.VertxExtension
+import org.apache.tuweni.junit.VertxInstance
+import org.junit.jupiter.api.Assertions
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.extension.ExtendWith
+import java.util.concurrent.atomic.AtomicReference
+
+@ExtendWith(VertxExtension::class)
+class RelayerAppTest {
+
+  @Test
+  fun testRelayerApp(@VertxInstance vertx: Vertx) {
+    val ref = AtomicReference<Message>()
+    val client1 = HobbitsTransport(vertx)
+    val client2 = HobbitsTransport(vertx)
+    main(arrayOf("-b", "tcp://localhost:12000", "-t", "tcp://0.0.0.0:10000"))
+    runBlocking {
+      client1.createTCPEndpoint("foo", port = 10000, handler = ref::set)
+      client1.start()
+      client2.start()
+      client2.sendMessage(
+        Message(command = "WHO", body = Bytes.fromHexString("deadbeef"), 
headers = Bytes.random(16)),
+        Transport.TCP,
+        "localhost",
+        12000
+      )
+    }
+    Thread.sleep(1000)
+    Assertions.assertEquals(Bytes.fromHexString("deadbeef"), ref.get().body)
+    client1.stop()
+    client2.stop()
+  }
+}
diff --git a/hobbits/src/main/kotlin/org/apache/tuweni/hobbits/Message.kt 
b/hobbits/src/main/kotlin/org/apache/tuweni/hobbits/Message.kt
index 259da84..eba9eeb 100644
--- a/hobbits/src/main/kotlin/org/apache/tuweni/hobbits/Message.kt
+++ b/hobbits/src/main/kotlin/org/apache/tuweni/hobbits/Message.kt
@@ -100,4 +100,9 @@ class Message(
     return protocol.length + 5 + version.length + command.length + 
headers.size().toString().length +
       body.size().toString().length + headers.size() + body.size()
   }
+
+  override fun toString(): String {
+    val requestLine = "$protocol $version $command ${headers.size()} 
${body.size()}\n"
+    return requestLine + headers.toHexString() + "\n" + body.toHexString()
+  }
 }
diff --git a/hobbits/src/main/kotlin/org/apache/tuweni/hobbits/Relayer.kt 
b/hobbits/src/main/kotlin/org/apache/tuweni/hobbits/Relayer.kt
new file mode 100644
index 0000000..cd97281
--- /dev/null
+++ b/hobbits/src/main/kotlin/org/apache/tuweni/hobbits/Relayer.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.hobbits
+
+import io.vertx.core.Vertx
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.async
+import java.net.URI
+import kotlin.coroutines.CoroutineContext
+
+class Relayer(
+  private val vertx: Vertx,
+  bind: String,
+  to: String,
+  interceptor: (Message) -> Unit,
+  override val coroutineContext: CoroutineContext = Dispatchers.Default
+) : CoroutineScope {
+
+  private val transport = HobbitsTransport(vertx, coroutineContext)
+  init {
+    val toURI = URI.create(to)
+    val uri = URI.create(bind)
+    when (uri.scheme) {
+      "http" -> {
+        transport.createHTTPEndpoint(networkInterface = uri.host, port = 
uri.port, handler = {
+          async {
+            interceptor(it)
+            transport.sendMessage(it, Transport.HTTP, toURI.host, toURI.port, 
toURI.path)
+          }
+        })
+      }
+      "tcp" -> {
+        transport.createTCPEndpoint(networkInterface = uri.host, port = 
uri.port, handler = {
+          async {
+            interceptor(it)
+            transport.sendMessage(it, Transport.TCP, toURI.host, toURI.port, 
toURI.path)
+          }
+        })
+      }
+      "udp" -> {
+        transport.createUDPEndpoint(networkInterface = uri.host, port = 
uri.port, handler = {
+          async {
+            interceptor(it)
+            transport.sendMessage(it, Transport.UDP, toURI.host, toURI.port, 
toURI.path)
+          }
+        })
+      }
+      "ws" -> {
+        transport.createWSEndpoint(networkInterface = uri.host, port = 
uri.port, handler = {
+          async {
+            interceptor(it)
+            transport.sendMessage(it, Transport.WS, toURI.host, toURI.port, 
toURI.path)
+          }
+        })
+      }
+    }
+  }
+
+  suspend fun start() {
+    transport.start()
+  }
+
+  fun stop() {
+    transport.stop()
+  }
+}
diff --git 
a/hobbits/src/test/kotlin/org/apache/tuweni/hobbits/InteractionTest.kt 
b/hobbits/src/test/kotlin/org/apache/tuweni/hobbits/InteractionTest.kt
index 49eeb09..5ee5825 100644
--- a/hobbits/src/test/kotlin/org/apache/tuweni/hobbits/InteractionTest.kt
+++ b/hobbits/src/test/kotlin/org/apache/tuweni/hobbits/InteractionTest.kt
@@ -22,6 +22,7 @@ import org.apache.tuweni.bytes.Bytes
 import org.apache.tuweni.junit.VertxExtension
 import org.apache.tuweni.junit.VertxInstance
 import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Disabled
 import org.junit.jupiter.api.Test
 import org.junit.jupiter.api.extension.ExtendWith
 import java.util.concurrent.atomic.AtomicReference
@@ -51,6 +52,29 @@ class TCPPersistentTest {
     client2.stop()
   }
 
+  @Disabled
+  @Test
+  fun testTwoTCPConnectionsWithTLS(@VertxInstance vertx: Vertx) {
+    val ref = AtomicReference<Message>()
+    val client1 = HobbitsTransport(vertx)
+    val client2 = HobbitsTransport(vertx)
+    runBlocking {
+      client1.createTCPEndpoint("foo", port = 10000, handler = ref::set, tls = 
true)
+      client1.start()
+      client2.start()
+      client2.sendMessage(
+        Message(command = "WHO", body = Bytes.fromHexString("deadbeef"), 
headers = Bytes.random(16)),
+        Transport.TCP,
+        "0.0.0.0",
+        10000
+      )
+    }
+    Thread.sleep(200)
+    assertEquals(Bytes.fromHexString("deadbeef"), ref.get().body)
+    client1.stop()
+    client2.stop()
+  }
+
   @Test
   fun testTwoEndpoints(@VertxInstance vertx: Vertx) {
     val ref = AtomicReference<Message>()
diff --git a/hobbits/src/test/kotlin/org/apache/tuweni/hobbits/RelayerTest.kt 
b/hobbits/src/test/kotlin/org/apache/tuweni/hobbits/RelayerTest.kt
new file mode 100644
index 0000000..99192e7
--- /dev/null
+++ b/hobbits/src/test/kotlin/org/apache/tuweni/hobbits/RelayerTest.kt
@@ -0,0 +1,131 @@
+/*
+ * 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.hobbits
+
+import io.vertx.core.Vertx
+import kotlinx.coroutines.runBlocking
+import org.apache.tuweni.bytes.Bytes
+import org.apache.tuweni.junit.VertxExtension
+import org.apache.tuweni.junit.VertxInstance
+import org.junit.jupiter.api.Assertions
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.extension.ExtendWith
+import java.util.concurrent.atomic.AtomicReference
+
+@ExtendWith(VertxExtension::class)
+class RelayerTest {
+
+  @Test
+  fun testTCPRelay(@VertxInstance vertx: Vertx) {
+    val ref = AtomicReference<Message>()
+    val client1 = HobbitsTransport(vertx)
+    val client2 = HobbitsTransport(vertx)
+    val relayer = Relayer(vertx, "tcp://localhost:22000", 
"tcp://0.0.0.0:20000", { })
+    runBlocking {
+      client1.createTCPEndpoint("foo", port = 20000, handler = ref::set)
+      client1.start()
+      client2.start()
+      relayer.start()
+      client2.sendMessage(
+        Message(command = "WHO", body = Bytes.fromHexString("deadbeef"), 
headers = Bytes.random(16)),
+        Transport.TCP,
+        "localhost",
+        22000
+      )
+    }
+    Thread.sleep(1000)
+    Assertions.assertEquals(Bytes.fromHexString("deadbeef"), ref.get().body)
+    client1.stop()
+    client2.stop()
+    relayer.stop()
+  }
+
+  @Test
+  fun testHTTPRelay(@VertxInstance vertx: Vertx) {
+    val ref = AtomicReference<Message>()
+    val client1 = HobbitsTransport(vertx)
+    val client2 = HobbitsTransport(vertx)
+    val relayer = Relayer(vertx, "http://localhost:13000";, 
"http://0.0.0.0:11000";, { })
+    runBlocking {
+      client1.createHTTPEndpoint("foo", port = 11000, handler = ref::set)
+      client1.start()
+      client2.start()
+      relayer.start()
+      client2.sendMessage(
+        Message(command = "WHO", body = Bytes.fromHexString("deadbeef"), 
headers = Bytes.random(16)),
+        Transport.HTTP,
+        "localhost",
+        13000
+      )
+    }
+    Thread.sleep(1000)
+    Assertions.assertEquals(Bytes.fromHexString("deadbeef"), ref.get().body)
+    client1.stop()
+    client2.stop()
+    relayer.stop()
+  }
+
+  @Test
+  fun testUDPRelay(@VertxInstance vertx: Vertx) {
+    val ref = AtomicReference<Message>()
+    val client1 = HobbitsTransport(vertx)
+    val client2 = HobbitsTransport(vertx)
+    val relayer = Relayer(vertx, "udp://localhost:12000", 
"udp://0.0.0.0:10000", { })
+    runBlocking {
+      client1.createUDPEndpoint("foo", port = 10000, handler = ref::set)
+      client1.start()
+      client2.start()
+      relayer.start()
+      client2.sendMessage(
+        Message(command = "WHO", body = Bytes.fromHexString("deadbeef"), 
headers = Bytes.random(16)),
+        Transport.UDP,
+        "localhost",
+        12000
+      )
+    }
+    Thread.sleep(1000)
+    Assertions.assertEquals(Bytes.fromHexString("deadbeef"), ref.get().body)
+    client1.stop()
+    client2.stop()
+    relayer.stop()
+  }
+
+  @Test
+  fun testWSRelay(@VertxInstance vertx: Vertx) {
+    val ref = AtomicReference<Message>()
+    val client1 = HobbitsTransport(vertx)
+    val client2 = HobbitsTransport(vertx)
+    val relayer = Relayer(vertx, "ws://localhost:32000", "ws://0.0.0.0:30000", 
{ })
+    runBlocking {
+      client1.createWSEndpoint("foo", port = 30000, handler = ref::set)
+      client1.start()
+      client2.start()
+      relayer.start()
+      client2.sendMessage(
+        Message(command = "WHO", body = Bytes.fromHexString("deadbeef"), 
headers = Bytes.random(16)),
+        Transport.WS,
+        "localhost",
+        32000
+      )
+    }
+    Thread.sleep(1000)
+    Assertions.assertEquals(Bytes.fromHexString("deadbeef"), ref.get().body)
+    client1.stop()
+    client2.stop()
+    relayer.stop()
+  }
+}
diff --git a/settings.gradle b/settings.gradle
index b198613..d458825 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -24,6 +24,7 @@ include 'eth-reference-tests'
 include 'eth-repository'
 include 'gossip'
 include 'hobbits'
+include 'hobbits-relayer'
 include 'io'
 include 'junit'
 include 'kademlia'


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to