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

examples: use circuitv2 in relay example #1795

Merged
merged 8 commits into from
Oct 9, 2022
Merged
Show file tree
Hide file tree
Changes from 6 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
3 changes: 3 additions & 0 deletions examples/go.work
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
go 1.18
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we need this.


use ./
13 changes: 13 additions & 0 deletions examples/go.work.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8=
github.com/dgraph-io/ristretto v0.0.2 h1:a5WaUrDa0qm0YrAAS1tUykT5El3kt62KNZZeMxQn3po=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
github.com/ipfs/go-ds-badger v0.3.0 h1:xREL3V0EH9S219kFFueOYJJTcjgNSZ2HY1iSvN7U1Ro=
github.com/ipfs/go-ds-leveldb v0.5.0 h1:s++MEBbD3ZKc9/8/njrn4flZLnCuY9I79v94gBUNumo=
github.com/libp2p/go-mplex v0.7.0 h1:BDhFZdlk5tbr0oyFq/xv/NPGfjbnrsDam1EvutpBDbY=
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
129 changes: 86 additions & 43 deletions examples/relay/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import (

"github.com/libp2p/go-libp2p"
"github.com/libp2p/go-libp2p/p2p/net/swarm"
"github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client"
"github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay"

"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"

relayv1 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv1/relay"

ma "github.com/multiformats/go-multiaddr"
)

Expand All @@ -20,68 +20,102 @@ func main() {
}

func run() {
// Create three libp2p hosts, enable relay client capabilities on all
// of them.

// Tell the host use relays
h1, err := libp2p.New(libp2p.EnableRelay())
// Create 2 "unreachable" libp2p hosts that want to communicate.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// Create 2 "unreachable" libp2p hosts that want to communicate.
// Create two "unreachable" libp2p hosts that want to communicate.

// We are configuring them with no listen addresses to mimic hosts
// that cannot be directly dialed due to problematic firewall/NAT
// configurations.
unreachable1, err := libp2p.New(
libp2p.NoListenAddrs,
// Usually EnableRelay() is not required as it is enabled by default
// but NoListenAddrs overrides this, so we're adding it in explictly again.
libp2p.EnableRelay(),
)
if err != nil {
log.Printf("Failed to create h1: %v", err)
log.Printf("Failed to create unreachable1: %v", err)
return
}

// Tell the host to relay connections for other peers (The ability to *use*
// a relay vs the ability to *be* a relay)
h2, err := libp2p.New(libp2p.DisableRelay())
unreachable2, err := libp2p.New(
libp2p.NoListenAddrs,
libp2p.EnableRelay(),
)
if err != nil {
log.Printf("Failed to create h2: %v", err)
log.Printf("Failed to create unreachable2: %v", err)
return
}

log.Println("First let's attempt to directly connect")

// Attempt to connect the unreachable hosts directly
unreachable2info := peer.AddrInfo{
ID: unreachable2.ID(),
Addrs: unreachable2.Addrs(),
}

err = unreachable1.Connect(context.Background(), unreachable2info)
if err == nil {
log.Printf("This actually should have failed. Did you make changes to the example...?")
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
log.Printf("This actually should have failed. Did you make changes to the example...?")
log.Printf("This actually should have failed.")

return
}
_, err = relayv1.NewRelay(h2)

log.Println("As suspected we cannot directly dial between the unreachable hosts")

// Create a host to act as a middleman to relay messages on our behalf
relay1, err := libp2p.New()
if err != nil {
log.Printf("Failed to instantiate h2 relay: %v", err)
log.Printf("Failed to create relay1: %v", err)
return
}

// Zero out the listen addresses for the host, so it can only communicate
// via p2p-circuit for our example
h3, err := libp2p.New(libp2p.ListenAddrs(), libp2p.EnableRelay())
// Configure the host to offer the ciruit relay service.
// Any host that is directly dialable in the network (or on the internet)
// can offer a circuit relay service, this isn't just the job of
// "dedicated" relay services.
// In circuit relay v2 (which we're using here!) it is rate limited so that
// any node can offer this service safely
_, err = relay.New(relay1)
if err != nil {
log.Printf("Failed to create h3: %v", err)
log.Printf("Failed to instantiate the relay: %v", err)
return
}

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

// Connect both h1 and h3 to h2, but not to each other
if err := h1.Connect(context.Background(), h2info); err != nil {
log.Printf("Failed to connect h1 and h2: %v", err)
// Connect both unreachable1 and unreachable2 to relay1
if err := unreachable1.Connect(context.Background(), relay1info); err != nil {
log.Printf("Failed to connect unreachable1 and relay1: %v", err)
return
}
if err := h3.Connect(context.Background(), h2info); err != nil {
log.Printf("Failed to connect h3 and h2: %v", err)

if err := unreachable2.Connect(context.Background(), relay1info); err != nil {
log.Printf("Failed to connect unreachable2 and relay1: %v", err)
return
}

// Now, to test things, let's set up a protocol handler on h3
h3.SetStreamHandler("/cats", func(s network.Stream) {
log.Println("Meow! It worked!")
// Now, to test the communication, let's set up a protocol handler on unreachable2
unreachable2.SetStreamHandler("/customprotocol", func(s network.Stream) {
log.Println("Awesome! We're now communicating via the relay!")

// End the example
s.Close()
})

_, err = h1.NewStream(context.Background(), h3.ID(), "/cats")
if err == nil {
log.Println("Didnt actually expect to get a stream here. What happened?")
// Hosts that want to have messages relayed on their behalf need to reserve a slot
// with the circuit relay service host
// As we will open a stream to unreachable2, unreachable2 needs to make the
// reservation
_, err = client.Reserve(context.Background(), unreachable2, relay1info)
if err != nil {
log.Printf("unreachable2 failed to receive a relay reservation from relay1. %v", err)
return
}
log.Printf("Okay, no connection from h1 to h3: %v", err)
log.Println("Just as we suspected")

// Creates a relay address to h3 using h2 as the relay
relayaddr, err := ma.NewMultiaddr("/p2p/" + h2.ID().Pretty() + "/p2p-circuit/ipfs/" + h3.ID().Pretty())
// Now create a new address for unreachable2 that specifies to communicate via
// relay1 using a circuit relay
relayaddr, err := ma.NewMultiaddr("/p2p/" + relay1info.ID.String() + "/p2p-circuit/p2p/" + unreachable2.ID().String())
if err != nil {
log.Println(err)
return
Expand All @@ -91,21 +125,30 @@ func run() {
// prevent us from redialing again so quickly. Since we know what we're doing, we
// can use this ugly hack (it's on our TODO list to make it a little cleaner)
// to tell the dialer "no, its okay, let's try this again"
h1.Network().(*swarm.Swarm).Backoff().Clear(h3.ID())
unreachable1.Network().(*swarm.Swarm).Backoff().Clear(unreachable2.ID())

log.Println("Now let's attempt to connect the hosts via the relay node")

h3relayInfo := peer.AddrInfo{
ID: h3.ID(),
// Open a connection to the previously unreachable host via the relay address
unreachable2relayinfo := peer.AddrInfo{
ID: unreachable2.ID(),
Addrs: []ma.Multiaddr{relayaddr},
}
if err := h1.Connect(context.Background(), h3relayInfo); err != nil {
log.Printf("Failed to connect h1 and h3: %v", err)
if err := unreachable1.Connect(context.Background(), unreachable2relayinfo); err != nil {
log.Printf("Unexpected error here. Failed to connect unreachable1 and unreachable2: %v", err)
return
}

log.Println("Yep, that worked!")

// **** MIGHT BE WORTH ADDING A COMMENT ADDRESSING WHAT WithUseTransient() is here,
// and why it is needed ******************
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you do that? :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not really confident with transient connections, as I'm new to libp2p 😅. From what I can piece together from reading some source comments is that transient stream are those that have opened a connection but the stream is not yet fully established, as a protocol hasn't been negotiated yet.

So, my guess would be that, in the context of a circuit relay, we have established an initial connection to the relay peer (which is marked as transient) but we don't have a connection to our destination peer yet, so when we open our stream to the destination peer we indicate that we are using a transient connection, but it is isn't a problem as the relay will connect to the destination peer and our connection will be upgraded once we've negotiated our stream with the destination peer. But it is possible I'm way off there 😁

Copy link
Contributor

Choose a reason for hiding this comment

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

From what I can piece together from reading some source comments is that transient stream are those that have opened a connection but the stream is not yet fully established, as a protocol hasn't been negotiated yet.

Yeah, confusing terminology here. Sorry for that. That would be transient in the context of the resource manager. In this context, it means something completely different.

Transient here means that it's a connection established on a relayed (as opposed to a direct) connection. Since the relay limits the amount of data that can be exchanged over the relayed connection, application controls need to explicitly opt-in into using a relayed connection. In general, they should only do that if they have low bandwidth requirements, and they're ok with the connection being killed when the relayed connection is replaced with a direct (holepunched) connection.

I hope this explanation makes sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah that's interesting, thanks for the details. Is there anything in the docs or source I could read to explore this a bit more? For example, how an application/relay node would go about lifting the relay data limits, or upgrading the connection for devices where hole punching is not possible.


// Woohoo! we're connected!
s, err := h1.NewStream(context.Background(), h3.ID(), "/cats")
// Let's start talking!
s, err := unreachable1.NewStream(network.WithUseTransient(context.Background(), "customprotocol"), unreachable2.ID(), "/customprotocol")
if err != nil {
log.Println("huh, this should have worked: ", err)
log.Println("Whoops, this should have worked...: ", err)
return
}

Expand Down
4 changes: 2 additions & 2 deletions examples/relay/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ func TestMain(t *testing.T) {
t.Skip("This test is flaky on CI, see https://github.com/libp2p/go-libp2p/issues/1158.")
}
var h testutils.LogHarness
h.ExpectPrefix("Okay, no connection from h1 to h3")
h.ExpectPrefix("Meow! It worked!")
h.ExpectPrefix("As suspected we cannot directly dial between the unreachable hosts")
h.ExpectPrefix("Awesome! We're now communicating via the relay!")
h.Run(t, run)
}