Coder Social home page Coder Social logo

go-libp2p-circuit's Introduction

DEPRECATION NOTICE

This package has moved into go-libp2p as a sub-package, github.com/libp2p/go-libp2p/p2p/protocol/internal/circuitv1-deprecated.

go-libp2p-circuit

Coverage Status Travis CI Discourse posts

The libp2p relay allows peers to relay connections on behalf of others.

Table of Contents

Install

go get -u github.com/libp2p/go-libp2p-circuit

Usage

Refer to the relay example in the go-libp2p-examples repository for usage instructions.

Contribute

PRs are welcome!

Small note: If editing the Readme, please conform to the standard-readme specification.

License

MIT © Jeromy Johnson


The last gx published version of this module was: 2.3.15: QmRTkLxADQRbgnhpt2zzQQJr8Ri764b7dujoDkZw33b3iE

go-libp2p-circuit's People

Contributors

aarshkshah1992 avatar anacrolix avatar biglep avatar bigs avatar coeniebeyers avatar dependabot-preview[bot] avatar dryajov avatar galargh avatar hannahhoward avatar hsanjuan avatar kevina avatar kubuxu avatar lanzafame avatar magik6k avatar marten-seemann avatar raulk avatar stebalien avatar victorb avatar vyzo avatar web3-bot avatar whyrusleeping avatar yusefnapora avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

go-libp2p-circuit's Issues

Better handle laddrs in Listen

  1. Listen("/p2p-circuit") should behave as it does today.
  2. Listen("/ipfs/QmOther/p2p-circuit") should dial QmOther, keep the connection open (and fail to listen if we can't dial them), and check if they support relaying.
  3. Listen should only work if: (1) we understand all the protocols in the local addr and (2) the mutliaddr ends with /p2p-circuit. Currently, we just check if it contains /p2p-circuit and accept it.
  4. We should use calls to Listen to signal that we accept relayed connections (and from where).

Docs: Do I _certainly_ not want to enable opt.Discovery?

    // OptDiscovery configures this relay transport to discover new relays
    // by probing every new peer. You almost _certainly_ don't want to
    // enable this.
    OptDiscovery = RelayOpt(2)

But this is enabled by default in go-ipfs. Could someone revisit the documentation for the options? There are 3 things that should be explicit:

  • What it does? -> i.e. This will allow this peer to discover if other peers are active relays that can be used
  • How it does it? -> It does this by probing every new peer to which connections are established to see whether it is an active relay or not (?).
  • What are the consenquences? -> Enabling this results in... [what does it do that it's so bad?]

(should be clear for every option). @vyzo is this in your alley? I keep finding myself confused every time I land on these options. The documentation gives recommendations like "knowing what you are doing". If I knew what I'm doing I certainly would not need to read the docs.

What happens when we have *a lot* of relays?

right now we try relays in random order. If we have a lot of relays and try to connect to a peer that is either offline or very sparsely connected, we will end up sending quite a few messages to random peers all over the network. This feels suboptimal, especially in cases like the DHT where we somewhat often will be trying to connect to peers that are offline (at least, currently).

One thing that might help is to instead sort the 'relays to try' by their XOR distance to the target. Since the DHT currently causes the network to prefer connections to peers close to them in the XOR keyspace, they are more likely to have connections. This helps in the case of a sparsely connected peer, but the degenerate case of an offline peer still sucks.

why Listen "/p2p-circuit"?

I know Listen returns a listener, and when a relay peer sends STOP to my peer, listener.Accept() gets a conn. So, when will the conn open a stream or accept a stream? Why construct such a conn?

Random Test Failure

I occasionally get the following error when running the tests:

--- FAIL: TestFullAddressTransportDial (0.12s)
	transport_test.go:77: dial attempt failed: failed to dial <peer.ID NMJzVN> (default failure)

Optimize latency for secio handshake

The spec allows the relay to immediately send OK to the dialer and start piping its input, before receiving OK from STOP.
This allows the dialer to immediately initiate secio handshake, saving a roundtrip in connection establishment.
We should implement this optimization.

race condition

=== RUN   TestActiveRelay
==================
WARNING: DATA RACE
Write at 0x00c0003e1370 by goroutine 17:
  github.com/libp2p/go-libp2p-circuit_test.TestActiveRelay()
      /home/runner/work/go-libp2p-circuit/go-libp2p-circuit/relay_test.go:444 +0x870
  testing.tRunner()
      /opt/hostedtoolcache/go/1.16.3/x64/src/testing/testing.go:1193 +0x202

Previous write at 0x00c0003e1370 by goroutine 112:
  github.com/libp2p/go-libp2p-circuit_test.TestActiveRelay.func2()
      /home/runner/work/go-libp2p-circuit/go-libp2p-circuit/relay_test.go:424 +0xf1

Goroutine 17 (running) created at:
  testing.(*T).Run()
      /opt/hostedtoolcache/go/1.16.3/x64/src/testing/testing.go:1238 +0x5d7
  testing.runTests.func1()
      /opt/hostedtoolcache/go/1.16.3/x64/src/testing/testing.go:1511 +0xa6
  testing.tRunner()
      /opt/hostedtoolcache/go/1.16.3/x64/src/testing/testing.go:1193 +0x202
  testing.runTests()
      /opt/hostedtoolcache/go/1.16.3/x64/src/testing/testing.go:1509 +0x612
  testing.(*M).Run()
      /opt/hostedtoolcache/go/1.16.3/x64/src/testing/testing.go:1417 +0x3b3
  main.main()
      _testmain.go:65 +0x236

Goroutine 112 (finished) created at:
  github.com/libp2p/go-libp2p-circuit_test.TestActiveRelay()
      /home/runner/work/go-libp2p-circuit/go-libp2p-circuit/relay_test.go:419 +0x509
  testing.tRunner()
      /opt/hostedtoolcache/go/1.16.3/x64/src/testing/testing.go:1193 +0x202
==================
    testing.go:1092: race detected during execution of test

Getting `failure: dial attempt failed: context deadline exceeded` on macos

Version information:

go-ipfs version: 0.4.13-
Repo version: 6
System version: amd64/darwin
Golang version: go1.9.2

Type: Bug

Severity: High

Description:

Getting failure: dial attempt failed: context deadline exceeded when dialing over go circuit to either a js or a go node. This only happens on MacOS, it works fine on both linux and windows platforms.

To reproduce, run the interop tests in ipfs/interop#6 on macos.

  1) circuit go relay go <-> go relay <-> go should connect:
     Error: connect QmPESBd38krVFxtykakWtNzNTn9wAfGDry9nkjSzYcJe1R failure: dial attempt failed: context deadline exceeded
      at parseError (node_modules/ipfs-api/src/utils/send-request.js:16:17)
      at ClientRequest.<anonymous> (node_modules/ipfs-api/src/utils/send-request.js:39:14)
      at HTTPParser.parserOnIncomingClient (_http_client.js:551:21)
      at HTTPParser.parserOnHeadersComplete (_http_common.js:117:23)
      at Socket.socketOnData (_http_client.js:440:20)
      at addChunk (_stream_readable.js:263:12)
      at readableAddChunk (_stream_readable.js:250:11)
      at Socket.Readable.push (_stream_readable.js:208:10)
      at TCP.onread (net.js:594:20)

  2) circuit go relay go <-> go relay <-> go should transfer:
     Error: Timeout of 80000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
 

Multi-Relay Hopping

It would be great to have a way for multi-relay hopping.

Let's say there's the following network:
[ Peer A ] - [ Relay 1 ] - [ Relay 2 ] - [ Peer B ]

In this example Peer A could reach Peer B via Relay 1 and Relay 2.
Nevertheless it looks like this is not possible at the moment.

The following implementation uses Relay 1 as the relay and tries to connected to Peer B.
This results in HOP_NO_CONN_TO_DST. Which is comprehensible, as Relay 1 has no direct connection to Peer B.

package main

import (
	"context"
	"fmt"
	logging "github.com/ipfs/go-log"
	"github.com/libp2p/go-libp2p"
	circuit "github.com/libp2p/go-libp2p-circuit"
	"github.com/libp2p/go-libp2p-core/network"
	"github.com/libp2p/go-libp2p-core/peer"
	ma "github.com/multiformats/go-multiaddr"
	"log"
)

func main() {
	h1, err := libp2p.New(context.Background(), libp2p.EnableRelay(circuit.OptHop))
	if err != nil {
		log.Fatalln(err)
	}

	h2, err := libp2p.New(context.Background(), libp2p.EnableRelay(circuit.OptHop))
	if err != nil {
		log.Fatalln(err)
	}

	h3, err := libp2p.New(context.Background(), libp2p.EnableRelay(circuit.OptHop))
	if err != nil {
		log.Fatalln(err)
	}

	h4, err := libp2p.New(context.Background(), libp2p.ListenAddrs(), libp2p.EnableRelay())
	if err != nil {
		log.Fatalln(err)
	}

	h2info := peer.AddrInfo{
		ID:    h2.ID(),
		Addrs: h2.Addrs(),
	}

	h3info := peer.AddrInfo{
		ID:    h3.ID(),
		Addrs: h3.Addrs(),
	}

	if err := h1.Connect(context.Background(), h2info); err != nil {
		log.Fatalln(err)
	}
	if err := h2.Connect(context.Background(), h3info); err != nil {
		log.Fatalln(err)
	}
	if err := h4.Connect(context.Background(), h3info); err != nil {
		log.Fatalln(err)
	}

	h4.SetStreamHandler("/cats", func(s network.Stream) {
		fmt.Println("Meow! It worked!")
		s.Close()
	})

	relayaddr, err := ma.NewMultiaddr("/p2p/" + h2.ID().Pretty() + "/p2p-circuit/p2p/" + h4.ID().Pretty())
	if err != nil {
		log.Fatalln(err)
	}

	h4relayInfo := peer.AddrInfo{
		ID:    h4.ID(),
		Addrs: []ma.Multiaddr{relayaddr},
	}
	if err := h1.Connect(context.Background(), h4relayInfo); err != nil {
		log.Fatalln(err)
	}

	s, err := h1.NewStream(context.Background(), h4.ID(), "/cats")
	if err != nil {
		fmt.Println("huh, this should have worked: ", err)
		return
	}

	s.Read(make([]byte, 1))
}

On the other hand, changing the relay address to "/p2p/" + h3.ID().Pretty() + "/p2p-circuit/p2p/" + h4.ID().Pretty() (using Relay 2 instead of Relay 1), leads to a connection error - as Peer A is not able to connect to Relay 2.

To me it would be great to talk to Relay 1 and being able to connect to Peer B.

Detect dial cycles

It's possible to cause a dial cycle in the relay (libp2p/go-libp2p#816). While it would be nice to be able to solve this in the general case, we can at least solve this in this transport by keeping track of dial dependencies.

We need to keep a directed graph of active dials mapping target nodes (the ones we're dialing) to relays (the relays we're dialing those nodes through). Then, before we dial a target node, we can lookup the chosen relay in this graph, to see if it is also a target node. If it is, we need to recursively follow the graph to see if we end back up at the original target. If we do, we have a cycle and we need to abort.

More concretely, if we first try to dial b through c, then a through b, we'd have the following edges in our graph:

  • a (target) -> b (relay)
  • b (target) -> c (relay)

Then, if we try to dial c through a, we'd:

  • Lookup a in the graph, and get b,
  • Lookup b in the graph, and get c.
  • Compare c == c, determine that we have a cycle, and abort.

prefer to avoid nested relays

It would be nice to have a way to signal that you really don't want to relay over a relayed connection. I was running an experiment that ended up having several nested relays, and it made things noticeably slower.

Not a high priority, just figured i would file this here for future reference

Obey dial timeouts

We need to add timeouts/deadlines to reads/writes when dialing. This should not happen:

goroutine 1813864247 [3h33m0s]:
sync.runtime_SemacquireMutex [0xc0f7765c04, 0x0]
        /usr/lib/go/src/runtime/sema.go:71
sync.(*Mutex).Lock [0xc0f7765c00]
        /usr/lib/go/src/sync/mutex.go:134
sync.(*Once).Do [0xc0f7765c00, 0xc01001b668]
        /usr/lib/go/src/sync/once.go:40
github.com/multiformats/go-multistream.(*lazyClientConn).Read [0xc0f7765c00, 0xc0b4a08000, 0x1, 0x1000, 0x25, 0x25, 0x26]
        pkg/mod/github.com/multiformats/[email protected]/lazyClient.go:64
github.com/libp2p/go-libp2p/p2p/host/basic.(*streamWrapper).Read [0xc01ebbd040, 0xc0b4a08000, 0x1, 0x1000, 0xc01001b6c0, 0xc01001b740, 0xf0f6d2]
        pkg/mod/github.com/libp2p/[email protected]/p2p/host/basic/basic_host.go:732
github.com/libp2p/go-libp2p-circuit.(*delimitedReader).ReadByte [0xc1695b6300, 0x76, 0x0, 0x0]
        pkg/mod/github.com/libp2p/[email protected]/util.go:92
encoding/binary.ReadUvarint [0x1a9a6a0, 0xc1695b6300, 0x0, 0x0, 0x75]
        /usr/lib/go/src/encoding/binary/varint.go:110
github.com/libp2p/go-libp2p-circuit.(*delimitedReader).ReadMsg [0xc1695b6300, 0x1abf380, 0xc0ff1f5880, 0x0, 0x0]
        pkg/mod/github.com/libp2p/[email protected]/util.go:97
github.com/libp2p/go-libp2p-circuit.(*Relay).DialPeer [0xc000184900, 0x1ac53c0, 0xc185e62cc0, 0xc0cfca4780, 0x22, 0x0, 0x0, 0x0, 0xc0a20b0900, 0x22, ...]
        pkg/mod/github.com/libp2p/[email protected]/relay.go:172
github.com/libp2p/go-libp2p-circuit.(*Relay).tryDialRelays [0xc000184900, 0x1ac53c0, 0xc106b30600, 0xc0a20b0900, 0x22, 0xc046de0c70, 0x1, 0x1, 0x0, 0x0, ...]
        pkg/mod/github.com/libp2p/[email protected]/dial.go:79
github.com/libp2p/go-libp2p-circuit.(*Relay).Dial [0xc000184900, 0x1ac53c0, 0xc106b30600, 0x1adbda0, 0xc0f9bde000, 0xc0a20b0900, 0x22, 0xc000107e80, 0x2, 0x2]
        pkg/mod/github.com/libp2p/[email protected]/dial.go:47
github.com/libp2p/go-libp2p-circuit.(*RelayTransport).Dial [0xc000184900, 0x1ac53c0, 0xc106b30600, 0x1adbda0, 0xc0f9bde000, 0xc0a20b0900, 0x22, 0x97bc38, 0x10, 0x1947708, ...]
        pkg/mod/github.com/libp2p/[email protected]/dial.go:15
github.com/libp2p/go-libp2p-swarm.(*Swarm).dialAddr [0xc0004f0b40, 0x1ac53c0, 0xc106b30600, 0xc0a20b0900, 0x22, 0x1adbda0, 0xc0f9bde000, 0x0, 0xc171fca510, 0xc106b30600, ...]
        pkg/mod/github.com/libp2p/[email protected]/swarm_dial.go:461
github.com/libp2p/go-libp2p-swarm.(*dialLimiter).executeDial [0xc0005702d0, 0xc180a437c0]
        pkg/mod/github.com/libp2p/[email protected]/limiter.go:217
created by github.com/libp2p/go-libp2p-swarm. [*dialLimiter).addCheckFdLimi]
        pkg/mod/github.com/libp2p/[email protected]/limiter.go:167

Revise the relay protocol

Carried over from libp2p/go-libp2p#578 (comment):

A relay client can be a consumer of a relay service, or a target of a relayed connection. Currently, all of these flow over the same thick protocol. I really have a hard time wrapping my head around this, and so do our users.

I'd like to segregate this protocol if possible into at least two:

  • /libp2p/circuit/relay/hop/v0.0.0: exposed only by relay nodes. Used inside the initiator -> relay connection.
  • /libp2p/circuit/relay/target/v0.0.0: exposed by clients accepting relayed connections. Used in the relay -> target connection.

This would allow for better implicit observability too. Would've helped a lot in the incident we're analysing.

duplicate enum registered: relay.pb.CircuitRelay_Status

Issue

Hi,

I am building a library for go-libp2p and I am getting the following error:

go test -cover
2018/04/01 12:47:06 proto: duplicate proto type registered: relay.pb.CircuitRelay
2018/04/01 12:47:06 proto: duplicate proto type registered: relay.pb.CircuitRelay.Peer
panic: proto: duplicate enum registered: relay.pb.CircuitRelay_Status

goroutine 1 [running]:
github.com/florianlenz/go-libp2p-bootstrap/vendor/gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto.RegisterEnum(0x14ecc2e, 0x1c, 0xc4204746f0, 0xc420474720)
	/Users/florian/projects/go/src/github.com/florianlenz/go-libp2p-bootstrap/vendor/gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto/properties.go:876 +0x252
github.com/florianlenz/go-libp2p-bootstrap/vendor/gx/ipfs/QmWxHPGaiWhJkzoFa1hsYPVoKyMcd79XZTCFPF92RKv9gt/go-libp2p-circuit/pb.init.0()
	/Users/florian/projects/go/src/github.com/florianlenz/go-libp2p-bootstrap/vendor/gx/ipfs/QmWxHPGaiWhJkzoFa1hsYPVoKyMcd79XZTCFPF92RKv9gt/go-libp2p-circuit/pb/relay.pb.go:206 +0xb4
exit status 2
FAIL	github.com/florianlenz/go-libp2p-bootstrap	0.046s
make: *** [test] Error 1

I couldn't find / fix the error my self.

Reproduce

  1. Clone the repo: https://github.com/florianlenz/go-libp2p-bootstrap
  2. Checkout commit 7f6e8d6feeb64a939f445297a53124ef4aac07df
  3. Run make deps
  4. Run make install
  5. Run make deps_hack
  6. Run make test

Interop tests for circuit reconnect fail

I hope this is the correct place to open this.

ipfs/interop#57 adds interop tests for circuit reconnect but they appear to be failing:

1) circuit
       go-go-go
         reconnect:
     Error: Timeout of 80000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/home/travis/build/ipfs/interop/test/node.js)

The test runs in js, but it's only used to orchestrate the three go-IPFS nodes via the HTTP API.

Has something in a recent release of go-libp2p-circuit changed?

What's the difference between `OptActive`, `OptHop` and `OptDiscovery`?

I can see OptDiscovery and OptHop are set in go-ipfs, but their meanings are ambiguous to me, and the config cfg.Swarm.DisableRelay seems not very Orthogonal. Also, OptActive seems never used, but there's an error related:

go-libp2p-circuit/relay.go

Lines 291 to 294 in f83937e

if len(ctp) == 0 && !r.active {
r.handleError(s, pb.CircuitRelay_HOP_NO_CONN_TO_DST)
return
}

Could someone kindly explain the intentions of these options?

Implicit multiaddr dialing does not try circuit relay

When attempting to connect an implicit /ipfs/Qm... multiaddr, libp2p should fallback to circuit-relay. (I.E. try /p2p-circuit/ipfs/Qm...).

As I discovered in this issue, only explicit dialing to /p2p-circuit/ipfs/Qm... will use a relay.

Am I missing something or this is a bug?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.