Skip to content

Commit

Permalink
Merge pull request #6 from erwin-kok/feature/host-and-swarm
Browse files Browse the repository at this point in the history
Feature/host and swarm
  • Loading branch information
erwin-kok authored Jul 31, 2023
2 parents 98874c7 + fa3cf41 commit 3edd4d1
Show file tree
Hide file tree
Showing 16 changed files with 247 additions and 77 deletions.
108 changes: 107 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,115 @@ See for an in-depth description of libp2p, please see: https://libp2p.io/

## Getting started

First add the proper dependencies to your project:

Kotlin DSL:

```kotlin
repositories {
mavenCentral()
}

dependencies {
implementation("org.erwinkok.result:libp2p-xxx:$latest")
}
```

`libp2p-core` is mandatory, other dependencies are optional depending on your needs. For example, if you need tcp
transport, include `libp2p-transport-tcp`, or if you want the mplex muxer include `libp2p-muxer-plex`.

In your code, first create a host:

```kotlin
val hostBuilder = host {
identity(localIdentity)
muxers {
mplex()
}
securityTransport {
noise()
}
transports {
tcp()
}
peerstore {
gcInterval = 1.hours
keyStore {
password = "APasswordThatIsAtLeast20CharactersLong"
dek {
salt = "W/SC6fnZfBIWdeAD3l+ClLpQtfICEtn+KYTUhfKq6d7l"
}
}
}
swarm {
dialTimeout = 10.minutes
listenAddresses {
multiAddress("/ip4/0.0.0.0/tcp/10333")
}
}
datastore(datastore)
}

val host = hostBuilder.build(scope)
.getOrElse {
logger.error { "The following errors occurred while creating the host: ${errorMessage(it)}" }
return@runBlocking
}
```

The layout is hopefully clear: for example, the code above will use `tcp` as a transport, `noise` for security and
`mplex` as a muxer.

Then you can add a handler:

```kotlin
host.setStreamHandler(ProtocolId.of("/chat/1.0.0")) {
chatHandler(it)
}
```
This means that if a peer connects and requests the `/chat/1.0.0` protocol, the corresponding handler will be called.

To call a peer and open a new stream, use the following code:

```kotlin
val stream = host.newStream(aPeerId, ProtocolId.of("/chat/1.0.0"))
.getOrElse {
logger.error { "Could not open chat stream with peer: ${errorMessage(it)}" }
return@runBlocking
}
chatHandler(stream)
```

This tries to connect to peer `aPeerId` and tries to open a new stream for protocol `/chat/1.0.0`. If it fails, it
returns if it succeeds it progresses to the chatHandler.

See also the example application in `app`.

To use this sample app, start the application. It will create a new random LocalIdentity (key-pair) and logs the adres
on which it listens on the output:

```shell
[main] o.e.l.a.ApplicationKt$main$1: Local addresses the Host listens on: /ip4/0.0.0.0/tcp/10333/p2p/12D3KooWDfaEJxpmjbFLb9wUakCd6Lo6LRntaV3drb4EaYZRtYuY
```

In the libp2p-Go repository you can find `chat` in the examples directory. Then you can connect to the running instance
by using:

```shell
./chat -d /ip4/0.0.0.0/tcp/10333/p2p/12D3KooWDfaEJxpmjbFLb9wUakCd6Lo6LRntaV3drb4EaYZRtYuY
```

On both sides it should mention that a connection is established.

## Contact

If you want to contact me, please write an e-mail to: [[email protected]](mailto:[email protected])
If you want to contact me, please write an e-mail to: [[email protected]](mailto:[email protected])

## Acknowledgements

This work is largely based on the awesome libp2p-go implementation. This work would not have been possible without their
effort. Please consider giving kudos to the libp2p-go authors.
(See also [`ACKNOWLEDGEMENTS`](ACKNOWLEDGEMENTS.md))

## License

Expand Down
2 changes: 1 addition & 1 deletion build-logic/src/main/kotlin/libp2p.common.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ plugins {
}

group = "org.erwinkok.libp2p"
version = "0.1.0-SNAPSHOT"
version = "0.1.0"

ktlint {
verbose.set(true)
Expand Down
2 changes: 1 addition & 1 deletion build-logic/src/main/kotlin/libp2p.publish.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ publishing {
developer {
id.set("erwin-kok")
name.set("Erwin Kok")
email.set("erwin.kok@protonmail.com")
email.set("erwin-kok@gmx.com")
url.set("https://github.com/erwin-kok/")
roles.set(listOf("owner", "developer"))
}
Expand Down
132 changes: 114 additions & 18 deletions detekt-config.yml
Original file line number Diff line number Diff line change
@@ -1,27 +1,123 @@
complexity:
active: false
console-reports:
active: true

naming:
active: false
output-reports:
active: true
exclude:
- 'TxtOutputReport'
- 'XmlOutputReport'
- 'SarifOutputReport'

empty-blocks:
active: false
build:
maxIssues: 0
weights:
complexity: 2
formatting: 1
LongParameterList: 1
comments: 1

style:
active: false
processors:
active: true

potential-bugs:
ExplicitGarbageCollectionCall:
active: false
coroutines:
active: true
GlobalCoroutineUsage:
active: true
SuspendFunSwallowedCancellation:
active: true
SuspendFunWithCoroutineScopeReceiver:
active: true

performance:
SpreadOperator:
complexity:
active: true
ComplexCondition:
active: true
threshold: 6
ComplexInterface:
active: true
threshold: 20
includeStaticDeclarations: false
CyclomaticComplexMethod:
active: true
threshold: 35
LabeledExpression:
active: false

exceptions:
TooGenericExceptionCaught:
LargeClass:
active: true
threshold: 1000
MethodOverloading:
active: false
TooGenericExceptionThrown:
threshold: 5
NestedBlockDepth:
active: true
threshold: 7
StringLiteralDuplication:
active: false
ThrowingExceptionsWithoutMessageOrCause:
threshold: 2
ignoreAnnotation: true
excludeStringsWithLessThan5Characters: true
ignoreStringsRegex: '$^'
TooManyFunctions:
active: true
thresholdInFiles: 50
thresholdInClasses: 50
thresholdInInterfaces: 20
thresholdInObjects: 50
thresholdInEnums: 50

empty-blocks:
active: true
EmptyCatchBlock:
active: true
EmptyClassBlock:
active: true
EmptyDefaultConstructor:
active: true
EmptyDoWhileBlock:
active: true
EmptyElseBlock:
active: true
EmptyFinallyBlock:
active: true
EmptyForBlock:
active: true
EmptyFunctionBlock:
active: false
EmptyIfBlock:
active: true
EmptyInitBlock:
active: true
EmptyKtFile:
active: true
EmptySecondaryConstructor:
active: true
EmptyWhenBlock:
active: true
EmptyWhileBlock:
active: true

exceptions:
active: true
NotImplementedDeclaration:
active: true
ThrowingExceptionInMain:
active: true

naming:
active: true
BooleanPropertyNaming:
active: true

performance:
active: true
CouldBeSequence:
active: true

potential-bugs:
active: true
CastNullableToNonNullableType:
active: true
CastToNullableType:
active: true
DontDowncastCollectionTypes:
active: true
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) 2023 Erwin Kok. BSD-3-Clause license. See LICENSE file for more details.
package org.erwinkok.libp2p.core.base

inline fun hashCodeOf(vararg values: Any?) =
fun hashCodeOf(vararg values: Any?) =
values.fold(0) { acc, value ->
(acc * 31) + value.hashCode()
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ interface Host : AwaitableClosable {
val eventBus: EventBus

fun addresses(): List<InetMultiaddress>
suspend fun connect(peerInfo: AddressInfo): Result<Unit>
suspend fun connect(addressInfo: AddressInfo): Result<Unit>
fun setStreamHandler(protocolId: ProtocolId, handler: StreamHandler)
fun setStreamHandlerMatch(protocolId: ProtocolId, matcher: (ProtocolId) -> Boolean, handler: StreamHandler)
fun removeStreamHandler(protocolId: ProtocolId)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
// Copyright (c) 2023 Erwin Kok. BSD-3-Clause license. See LICENSE file for more details.
@file:OptIn(DelicateCoroutinesApi::class)

package org.erwinkok.libp2p.core.network.swarm

import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.channels.Channel
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,11 @@ import kotlinx.atomicfu.locks.ReentrantLock
import kotlinx.atomicfu.locks.withLock
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.Channel
import mu.KotlinLogging
import org.erwinkok.libp2p.core.base.AwaitableClosable
import org.erwinkok.libp2p.core.event.EventBus
import org.erwinkok.libp2p.core.host.PeerId
import org.erwinkok.libp2p.core.host.builder.SwarmConfig
import org.erwinkok.libp2p.core.network.AddressDelay
import org.erwinkok.libp2p.core.network.Connectedness
import org.erwinkok.libp2p.core.network.Direction
import org.erwinkok.libp2p.core.network.InetMultiaddress
Expand Down Expand Up @@ -165,26 +163,6 @@ class Swarm private constructor(
return peers.computeIfAbsent(peerId) { NetworkPeer(scope, peerId, this, resourceManager, multistreamMuxer) }
}

//
//
//

internal suspend fun bestAcceptableConnectionToPeer(peerId: PeerId): Result<SwarmConnection>? {
return null
}

internal suspend fun addressesForDial(peerId: PeerId): Result<List<InetMultiaddress>> {
return swarmDialer.addressesForDial(peerId)
}

internal suspend fun dialRanker(addresses: List<InetMultiaddress>): List<AddressDelay> {
return dialRanker(addresses)
}

internal suspend fun dialNextAddress(peerId: PeerId, address: InetMultiaddress, responseChannel: Channel<DialResponse>): Result<Unit> {
TODO("Not yet implemented")
}

internal fun addConnection(transportConnection: TransportConnection, direction: Direction): Result<SwarmConnection> {
val peerId = transportConnection.remoteIdentity.peerId
return if (connectionGater != null) {
Expand Down
Loading

0 comments on commit 3edd4d1

Please sign in to comment.