Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The Circuit Relay Specification #22

Merged
merged 5 commits into from
Jul 14, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
273 changes: 162 additions & 111 deletions relay/README.md
Original file line number Diff line number Diff line change
@@ -1,69 +1,48 @@
# Circuit Relay
# Circuit Relay v0.1.0

> Circuit Switching in libp2p
> Circuit Switching for libp2p, also known as TURN or Relay in Networking literature.

Implementations
- [js-libp2p-circuit](https://github.com/libp2p/js-libp2p-circuit) -- status: started
- [go-libp2p-circuit](https://github.com/libp2p/go-libp2p-circuit) -- status: ready-to-start
## Implementations

- [js-libp2p-circuit](https://github.com/libp2p/js-libp2p-circuit)
- [go-libp2p-circuit](https://github.com/libp2p/go-libp2p-circuit)

## Table of Contents

Table of Contents
- [Overview](#overview)
- [Dramatization](#dramatization)
- [Addressing](#addressing)
- [Wire protocol](#wire-protocol)
- [Interfaces](#interfaces)
- [Implementation Details](#implementation-details)
- [Removing existing relay protocol](#removing-existing-relay-protocol)

Note: this document is currently in the process of being double-checked and updated. Please see [libp2p/specs#15 (comment)](https://github.com/libp2p/specs/pull/15#issuecomment-299949023) before using it as the basis for an implementation.


## Overview

The circuit relay is a means of establishing connectivity between
libp2p nodes (such as IPFS) that wouldn't otherwise be able to connect to each other.
The circuit relay is a means to establish connectivity between libp2p nodes (e.g. IPFS nodes) that wouldn't otherwise be able to establish a direct connection to each other.

This helps in situations where nodes are behind NAT or reverse proxies,
or simply don't support the same transports (e.g. go-ipfs vs. browser-ipfs).
libp2p already has modules for NAT ([go-libp2p-nat](https://github.com/libp2p/go-libp2p-nat)),
but these don't always do the job, just because NAT traversal is complicated.
That's why it's useful to have a simple relay protocol.
Relay is needed in situations where nodes are behind NAT, reverse proxies, firewalls and/or simply don't support the same transports (e.g. go-ipfs vs. browser-ipfs). Even though libp2p has modules for NAT traversal ([go-libp2p-nat](https://github.com/libp2p/go-libp2p-nat)), piercing through NATs isn't always an option. The circuit relay protocol exists to overcome those scenarios.

Unlike a transparent **tunnel**, where a libp2p peer would just proxy a
communication stream to a destination (the destination being unaware of the
original source), a circuit-relay makes the destination aware of the original
source and the circuit followed to establish communication between the two.
This provides the destination side with full knowledge of the circuit which,
if needed, could be rebuilt in the opposite direction.
Unlike a transparent **tunnel**, where a libp2p peer would just proxy a communication stream to a destination (the destination being unaware of the original source), a circuit relay makes the destination aware of the original source and the circuit followed to establish communication between the two. This provides the destination side with full knowledge of the circuit which, if needed, could be rebuilt in the opposite direction.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need this? It seems like there could be a lot of complexity involved in making this work properly, as opposed to leaving the job of re-establishing a dropped connection to the source. So far I have left this out in the js implementation.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if needed, could be rebuilt

It is not handled by the module. It is "if needed, the dst has the information to rebuild the conn back"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


Apart from that, this relayed connection behaves just like a regular
connection would, but over an existing swarm stream with another peer
(instead of e.g. TCP.): One node asks a relay node to connect to another node
on its behalf. The relay node shortcircuits its streams to the two nodes,
and they are then connected through the relay.
Apart from that, this relayed connection behaves just like a regular connection would, but over an existing swarm stream with another peer (instead of e.g. TCP). A node asks a relay node to connect to another node on its behalf. The relay node short-circuits streams between the two nodes, enabling them to reach each other.

Relayed connections are end-to-end encrypted just like regular connections.

The circuit relay is both a tunneled transport and a mounted swarm protocol.
The transport is the means of ***establishing*** and ***accepting*** connections,
and the swarm protocol is the means to ***relaying*** connections.
The circuit relay is both a tunneled transport and a mounted swarm protocol. The transport is the means of ***establishing*** and ***accepting*** connections, and the swarm protocol is the means to ***relaying*** connections.

```
+-------+ /ip4/.../tcp/.../ws/p2p/QmRelay +---------+ /ip4/.../tcp/.../p2p/QmTwo +-------+
| QmOne | <------------------------------------> | QmRelay | <-----------------------------------> | QmTwo |
+-------+ (/libp2p/relay/circuit multistream) +---------+ (/libp2p/relay/circuit multistream) +-------+
^ +-----+ ^
| | | |
| /p2p-circuit/QmTwo | | |
+--------------------------------------------+ +-------------------------------------------+
+-----+ /ip4/.../tcp/.../ws/p2p/QmRelay +-------+ /ip4/.../tcp/.../p2p/QmTwo +-----+
|QmOne| <------------------------------------>|QmRelay|<----------------------------------->|QmTwo|
+-----+ (/libp2p/relay/circuit multistream) +-------+ (/libp2p/relay/circuit multistream) +-----+
^ +-----+ ^
| /p2p-circuit/QmTwo | | |
+-----------------------------------------+ +-----------------------------------------+
```

Note: we're using the `/p2p` multiaddr protocol instead of `/ipfs` in this document.
`/ipfs` is currently the canonical way of addressing a libp2p or IPFS node,
but given the growing non-IPFS usage of libp2p, we'll migrate to using `/p2p`.

Note: at the moment we're not including a mechanism for discovering relay nodes.
For the time being, they should be configured statically.
**Notes for the reader:**

- We're using the `/p2p` multiaddr protocol instead of `/ipfs` in this document. `/ipfs` is currently the canonical way of addressing a libp2p or IPFS node, but given the growing non-IPFS usage of libp2p, we'll migrate to using `/p2p`.

## Dramatization

Expand All @@ -73,106 +52,174 @@ Cast:
- QmRelay, a node which speaks the circuit relay protocol (go-ipfs or js-ipfs).

Scene 1:
- QmOne wants to connect to QmTwo,
and through peer routing has acquired a set of addresses of QmTwo.
- QmOne wants to connect to QmTwo, and through peer routing has acquired a set of addresses of QmTwo.
- QmTwo doesn't support any of the transports used by QmOne.
- Awkward silence.

Scene 2:
- All three nodes have learned to speak the `/ipfs/relay/circuit` protocol.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/ipfs/relay/circuit should be /libp2p/relay/circuit.

- QmRelay is configured to allow relaying connections between other nodes.
- QmOne is configured to use QmRelay for relaying.
- QmOne automatically added `/p2p-circuit/ipfs/QmTwo` to its set of QmTwo addresses.
- QmOne automatically added `/p2p-circuit/p2p/QmTwo` to its set of QmTwo addresses.
- QmOne tries to connect via relaying, because it shares this transport with QmTwo.
- A lively and prolonged dialogue ensues.


## Addressing

`/p2p-circuit` multiaddrs don't carry any meaning of their own.
They need to encapsulate a `/p2p` address, or
be encapsulated in a `/p2p` address, or both.
`/p2p-circuit` multiaddrs don't carry any meaning of their own. They need to encapsulate a `/p2p` address, or be encapsulated in a `/p2p` address, or both.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can a /p2p-circuit multiaddr be encapsulated in a /p2p address without encapsulating a /p2p address? In other words, shouldn't the destination /p2p address be mandatory?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wording might not be perfect. Could you illustrate the case that doesn't make sense to you?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The case that is not allowed is <relay peer multiaddr>/p2p-circuit/ (destination omitted). What about the following wording?

A /p2p-circuit multiaddress needs to encapsulate a /p2p destination address, and optionally be encapsulated by a /p2p relay address.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be added as additional bullet points to the main sentense:

/p2p-circuit multiaddrs don't carry any meaning of their own. They need to encapsulate a /p2p address, or be encapsulated in a /p2p address, or both.

  • /p2p-circuit multiaddress encapsulates a /p2p destination address - e.g. /p2p-circuit/p2p/QmHash
  • can optionally be encapsulated by a /p2p relay address - e.g. /p2p/ipfs/QmRelay/p2p-circuit/p2p/QmDest


As with all other multiaddrs, encapsulation of different protocols determines which metaphorical tubes to connect to each other.

A `/p2p-circuit` circuit address, is formated following:

`[<relay peer multiaddr>]/p2p-circuit/<destination peer multiaddr>`

Examples:

- `/p2p-circuit/p2p/QmVT6GYwjeeAF5TR485Yc58S3xRF5EFsZ5YAF4VcP3URHt` - Arbitrary relay node
- `/ip4/127.0.0.1/tcp/5002/p2p/QmdPU7PfRyKehdrP5A3WqmjyD6bhVpU1mLGKppa2FjGDjZ/p2p-circuit/p2p/QmVT6GYwjeeAF5TR485Yc58S3xRF5EFsZ5YAF4VcP3URHt` - Specific relay node

As with all other multiaddrs, encapsulation of different protocols
determines which metaphorical tubes to connect to each other.
This opens the room for multiple hop relay, where the second relay is encapsulated in the first relay multiaddr, such as:

`<1st relay>/p2p-circuit/<2nd relay>/p2p-circuit/<dst multiaddr>`

A few examples:

Using any relay available:

- `/p2p-circuit/p2p/QmTwo`
- Dial QmTwo, through any available relay node.
- The relay node will use peer routing to find an address for QmTwo.
- Dial QmTwo, through any available relay node (or find one node that can relay).
- The relay node will use peer routing to find an address for QmTwo if it doesn't have a direct connection.
- `/p2p-circuit/ip4/../tcp/../p2p/QmTwo`
- Dial QmTwo, through any available relay node, but force the relay node to use the encapsulated `/ip4` multiaddr for connecting to QmTwo.

Specify a relay:

- `/p2p/QmRelay/p2p-circuit/p2p/QmTwo`
- Dial QmTwo, through QmRelay.
- Use peer routing to find an address for QmRelay.
- The relay node will also use peer routing, to find an address for QmTwo.
- `/p2p-circuit/ip4/../tcp/../p2p/QmTwo`
- Dial QmTwo, through any available relay node,
but force the relay node to use the encapsulated `/ip4` multiaddr for connecting to QmTwo.
- We'll probably not support forced addresses for now, just because it's complicated.
- `/ip4/../tcp/../p2p/QmRelay/p2p-circuit`
- Listen for connections relayed through QmRelay.
- Includes info for connecting to QmRelay.
- Also makes QmRelay available for relayed dialing, based on how listeners currently relate to dialers.
- `/p2p/QmRelay/p2p-circuit`
- Same as previous example, but use peer routing to find an address for QmRelay.
- `/p2p-circuit/p2p/QmTwo/p2p-circuit/p2p/QmThree`
- Dial QmThree, through a relayed connection to QmTwo.
- The relay nodes will use peer routing to find an address for QmTwo and QmThree.
- We'll probably not support nested relayed connections for now, there are edge cases to think of.
- `/ip4/../tcp/../p2p/QmRelay/p2p-circuit/p2p/QmTwo`
- Dial QmTwo, through QmRelay.
- Includes info for connecting to QmRelay.
- The relay node will use peer routing to find an address for QmTwo.
- `/p2p-circuit`
- Use relay discovery to find a suitable relay node. (Neither specified nor implemented.)
- Listen for relayed connections.
- Dial through the discovered relay node for any `/p2p-circuit` multiaddr.

TODO: figure out forced addresses.
TODO: figure out nested relayed connections.
Double relay:

- `/p2p-circuit/p2p/QmTwo/p2p-circuit/p2p/QmThree`
- Dial QmThree, through a relayed connection to QmTwo.
- The relay nodes will use peer routing to find an address for QmTwo and QmThree.
- We'll **not support nested relayed connections for now**, see [Future Work](#future-work) section.

## Wire format

The wire format (or codec) is named `/ipfs/relay/circuit` and is simple.
A variable-length header consisting of two length-prefixed multiaddrs
is followed by a bidirectional stream of arbitrary data,
and the eventual closing of the stream.
We start the description of the Wire format by illustrating a possible flow scenario and then describing them in detail by phases.

### Relay Message

Every message in the relay protocol uses the following protobuf:

```
<src><dst><data>

^ ^ ^
| | |
| | +-- bidirectional data stream
| | (usually /multistream-select in the case of /p2p multiaddrs)
| |
| +------- multiaddr of the listening node
|
+------------ multiaddr of the dialing node
message CircuitRelay {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, but should we have a version as part of the message as well? I know its communicated as part of the multistream-select rpc end point, but having a version in the message might help preventing stupid mistakes like we made changes to the protobuff msg, but forgot to bump the multistream version.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • protobufs have a very clean way to be updated already
  • "we made changes to the protobuf msg, but forgot to bump the multistream version" - this is not a valid technical reason, the truth is that adding a version won't stop you from making any of the mistakes you suggest, in fact, it opens the door to even more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@diasdavid you're correct, reading a bit more into protobufs clarified this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


enum Status {
SUCCESS = 100;
SRC_ADDR_TOO_LONG= 220;
DST_ADDR_TOO_LONG= 221;
SRC_MULTIADDR_INVALID= 250;
DST_MULTIADDR_INVALID= 251;
NO_CONN_TO_DST = 260;
CANT_DIAL_DST = 261;
CANT_OPEN_DST_STREAM = 262;
CANT_SPEAK_RELAY = 270;
CANT_REALAY_TO_SELF = 280;
SRC_ADDR_TOO_LONG = 320;
DST_ADDR_TOO_LONG = 321;
SRC_MULTIADDR_INVALID = 350;
DST_MULTIADDR_INVALID = 351;
}

enum Type { // RPC identifier, either HOP, STOP or STATUS
HOP = 1;
STOP = 2;
STATUS = 3;
}

message Peer {
required bytes id = 1; // peer id
repeated bytes addrs = 2; // peer's known addresses
}

optional Type type = 1; // Type of the message

optional Peer srcPeer = 2; // srcPeer and dstPeer are used when Type is HOP or STATUS
optional Peer dstPeer = 3;

optional Status code = 4; // Status code, used when Type is STATUS
}
```

After getting a stream to the relay node from its libp2p swarm,
the dialing transport writes the header to the stream.
The relaying node reads the header, gets a stream to the destination node,
then writes the header to the destination stream and shortcircuits the two streams.
### High level overview of establishing a relayed connection

**Setup:**
- Peers involved, A, B, R
- A wants to connect to B, but needs to relay through R

**Assumptions:**
- A has connection to R, R has connection to B

**Events:**
- phase I: Open a request for a relayed stream (A to R).
- A dials a new stream `sAR` to R using protocol `/libp2p/circuit/relay/0.1.0`.
- A sends a CircuitRelay message with `{ type: 'HOP', srcPeer: '/p2p/QmA', dstPeer: '/p2p/QmB' }` to R through `sAR`.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this HOP message be immediately acknowledged by R with a message? Opening a new stream sRB can take some time, and it would be good for A to be able to distinguish timeouts where R is slow, or where B is slow.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's acknowledged in the sense that:

  • a connection is open
  • a multistream + secio + spdy + multistream again to /libp2p/circuit/relay is done

The next ack is when R finishes sRB

- R receives stream `sAR` and reads the message from it.
- phase II: Open a stream to be relayed (R to B).
- R opens a new stream `sRB` to B using protocol `/libp2p/circuit/relay/0.1.0`.
- R sends a CircuitRelay message with `{ type: 'STOP', srcPeer: '/p2p/QmA', dstPeer: '/p2p/QmB' }` on `sRB`.
- R sends a CircuitRelay message with `{ type: 'STATUS', code: 'OK' }` on `sAR`.
- phase III: Streams are piped together, establishing a circuit
Copy link

@JustinDrake JustinDrake Jul 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where exactly does the piping happen? Does it happen after R sends { type: 'STATUS', code: 'OK' } on sAR? If so, is there a race condition here? Should it happen before R sends that message?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's after R sends that { type: 'STATUS', code: 'OK' }. The order in which the items are listed is relevant.

Where is the race condition here?

Copy link

@JustinDrake JustinDrake Jul 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The race condition is as follows. A may try to send a message for B on sAR after receiving { type: 'STATUS', code: 'OK' } from R, but before piping has happened, i.e. before a circuit to B is properly formed. In practice, network latency will likely be high enough that piping will happen in time. Still, piping should maybe happen before sending { type: 'STATUS', code: 'OK' } to avoid the (theoretical) race condition.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@diasdavid agree with @JustinDrake in the current js implementation OK is sent after the pipe has been established otherwise there is no way to tell if the pipe is ready without an additional message that would indicate that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If A writes on sAR and sAR hasn't been piped to sRB then the message is buffered (backpressure).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If A writes on sAR and sAR hasn't been piped to sRB then the message is buffered (backpressure).

Backpressure may not completely address the race condition. The reason is that the recipient of messages sent to the sAR stream could be either R or B. As it stands the protocol is unambiguous because after A sends the initial HOP message to R it is not expected to send further messages to R. But in theory the protocol could be extended to allow for A to send further messages to R before the circuit is established. Doing things the other way round may be more futureproof.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at this closer, I see another issue that might come up with multihop dialing. In shallow/matreshka dialing mode, there is no way for the R to tell weather the circuit has been completed or not until the last portion of the multihop address is dialed, for that reason I have the STOP handler signaling the OK rather than HOP.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lgierth @whyrusleeping @diasdavid wanna chime in on this one? Seems like the only contention point right now? I agree with the points that @JustinDrake is bringing up.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm not sure i see an issue here, If R sends the message to A, then pipes the connection, any message sent from A to R will just be buffered (or A will not be able to finish the write) until things are piped. It is assumed we are operating over a reliable ordered stream (not something like UDP where packets could get dropped if we don't read them)

Copy link
Member

@dryajov dryajov Jul 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@whyrusleeping I guess you're right. I remember dealing with some issues initially, which made me experiment a bit with where the ok is sent, but most likely it was unrelated to the ordering of the messages. I'll give it another go sinse I'll have to rewrite message handling anyways.

- B receives stream `sRB` and reads the message from it
- B sends a CircuitRelay message with `{ type: 'STATUS', code: 'OK' }` on `sRB`.
- B passes stream to `NewConnHandler` to be handled like any other new incoming connection.

Each relayed connection corresponds to two multistreams,
one between QmOne and QmRelay, the other between QmRelay and QmTwo.
### Under the microscope

Implementation details:
- The relay node has the `Swarm.EnableRelaying` config option enabled
- The relay node allows only one relayed connection between any two nodes.
- The relay node validates the `src` header field.
- The listening node validates the `dst` header field.
- We've defined a max length for the multiaddrs of arbitrarily 1024 bytes
- Multiaddrs are transfered on its binary packed format
- Peer Ids are transfered on its non base encoded format (aka byte array containing the multihash of the Public Key).


## Interfaces
### Status codes table

As explained above, the relay is both a transport (`tpt.Transport`)
and a mounted stream protocol (`p2pnet.StreamHandler`).
In addition it provides a means of specifying relay nodes to listen/dial through.
This is a table of status codes and sample messages that may occur during a relay setup. Codes in the 200 range are returned by the relay node. Codes in the 300 range are returned by the destination node.

TODO: the usage of p2pnet.StreamHandler is a little bit off, but it gets the point across.

| Code | Message | Meaning |
| ----- |:--------------------------------------------------|:----------:|
| 100 | OK | Relay was setup correctly |

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be "One leg of the relay was setup correctly", instead of the full relay?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a very good question, I need it needs to be clarified.

Dialing from A to R self-confirms because of all the libp2p dance (multistream, secio, protocol muxing), we really just need to signal that we managed to reach the other end (B).

This brings be back to => #22 (comment)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

| 220 | "src address too long" | |
| 221 | "dst address too long" | |
| 250 | "failed to parse src addr: no such protocol ipfs" | The `<src>` multiaddr in the header was invalid |
| 251 | "failed to parse dst addr: no such protocol ipfs" | The `<dst>` multiaddr in the header was invalid |
| 260 | "passive relay has no connection to dst" | |
| 261 | "active relay couldn't dial to dst: conn refused" | relay could not form new connection to target peer |
| 262 | "couldn't' dial to dst" | relay has conn to dst, but failed to open a stream |
| 270 | "dst does not support relay" | |
| 280 | "can't relay to itself" | The relay got its own address as destination |
| 320 | "src address too long" | |
| 321 | "dst address too long" | |
| 350 | "failed to parse src addr" | src multiaddr in the header was invalid |
| 351 | "failed to parse dst addr" | dst multiaddr in the header was invalid |

## Implementation details

### Interfaces

> These are go-ipfs specific

As explained above, the relay is both a transport (`tpt.Transport`) and a mounted stream protocol (`p2pnet.StreamHandler`). In addition it provides a means of specifying relay nodes to listen/dial through.

Note: the usage of p2pnet.StreamHandler is a little bit off, but it gets the point across.

```go
import (
Expand All @@ -182,7 +229,7 @@ import (
p2proto "github.com/libp2p/go-libp2p-protocol"
)

const ID p2proto.ID = "/ipfs/relay/circuit/0.1.0"
const ID p2proto.ID = "/libp2p/circuit/relay/0.1.0"

type CircuitRelay interface {
tpt.Transport
Expand All @@ -194,11 +241,9 @@ type CircuitRelay interface {
fund NewCircuitRelay(h p2phost.Host)
```

### Removing existing relay protocol in Go

### Removing existing relay protocol

Note that there is an existing swarm protocol colloqiually called relay.
It lives in the go-libp2p package and is named `/ipfs/relay/line/0.1.0`.
Note that there is an existing swarm protocol colloqiually called relay. It lives in the go-libp2p package and is named `/ipfs/relay/line/0.1.0`.

- Introduced in ipfs/go-ipfs#478 (28-Dec-2014).
- No changes except for ipfs/go-ipfs@de50b2156299829c000b8d2df493b4c46e3f24e9.
Expand All @@ -210,5 +255,11 @@ It lives in the go-libp2p package and is named `/ipfs/relay/line/0.1.0`.
- Capable of *accepting* connections, and *relaying* connections.
- Not capable of *connecting* via relaying.

Since the existing protocol is incomplete, insecure, and certainly not used,
we can safely remove it.
Since the existing protocol is incomplete, insecure, and certainly not used, we can safely remove it.

## Future work

We have considered more features but won't be adding them on the first iteration of Circuit Relay, the features are:

- Multihop relay - With this specification, we are only enabling single hop relays to exist. Multihop relay will come at a later stage as Packet Switching.
Copy link
Member

@dryajov dryajov Jul 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The js does implement multihop dialing in the form of shallot/matreshka dialing. This was based on the conversations we had at the time of me starting the implementation, what has changed that we now have scrapped this part?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is nothing wrong with experimentation. It was part of Future Work spec wise from the beginning.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. :)

- Relay discovery mechanism - At the moment we're not including a mechanism for discovering relay nodes. For the time being, they should be configured statically.