mirror of
				https://source.quilibrium.com/quilibrium/ceremonyclient.git
				synced 2025-11-04 10:37:27 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			161 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			161 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package main
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"log"
 | 
						|
 | 
						|
	"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"
 | 
						|
 | 
						|
	ma "github.com/multiformats/go-multiaddr"
 | 
						|
)
 | 
						|
 | 
						|
func main() {
 | 
						|
	run()
 | 
						|
}
 | 
						|
 | 
						|
func run() {
 | 
						|
	// 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 explicitly again.
 | 
						|
		libp2p.EnableRelay(),
 | 
						|
	)
 | 
						|
	if err != nil {
 | 
						|
		log.Printf("Failed to create unreachable1: %v", err)
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	unreachable2, err := libp2p.New(
 | 
						|
		libp2p.NoListenAddrs,
 | 
						|
		libp2p.EnableRelay(),
 | 
						|
	)
 | 
						|
	if err != nil {
 | 
						|
		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.")
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	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 create relay1: %v", err)
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	// Configure the host to offer the circuit 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 instantiate the relay: %v", err)
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	relay1info := peer.AddrInfo{
 | 
						|
		ID:    relay1.ID(),
 | 
						|
		Addrs: relay1.Addrs(),
 | 
						|
	}
 | 
						|
 | 
						|
	// 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 := unreachable2.Connect(context.Background(), relay1info); err != nil {
 | 
						|
		log.Printf("Failed to connect unreachable2 and relay1: %v", err)
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	// 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()
 | 
						|
	})
 | 
						|
 | 
						|
	// 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
 | 
						|
	}
 | 
						|
 | 
						|
	// 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
 | 
						|
	}
 | 
						|
 | 
						|
	// Since we just tried and failed to dial, the dialer system will, by default
 | 
						|
	// 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"
 | 
						|
	unreachable1.Network().(*swarm.Swarm).Backoff().Clear(unreachable2.ID())
 | 
						|
 | 
						|
	log.Println("Now let's attempt to connect the hosts via the relay node")
 | 
						|
 | 
						|
	// Open a connection to the previously unreachable host via the relay address
 | 
						|
	unreachable2relayinfo := peer.AddrInfo{
 | 
						|
		ID:    unreachable2.ID(),
 | 
						|
		Addrs: []ma.Multiaddr{relayaddr},
 | 
						|
	}
 | 
						|
	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!")
 | 
						|
 | 
						|
	// Woohoo! we're connected!
 | 
						|
	// Let's start talking!
 | 
						|
 | 
						|
	// Because we don't have a direct connection to the destination node - we have a relayed connection -
 | 
						|
	// the connection is marked as transient. Since the relay limits the amount of data that can be
 | 
						|
	// exchanged over the relayed connection, the application needs to explicitly opt-in into using a
 | 
						|
	// relayed connection. In general, we should only do this if we have low bandwidth requirements,
 | 
						|
	// and we're happy for the connection to be killed when the relayed connection is replaced with a
 | 
						|
	// direct (holepunched) connection.
 | 
						|
	s, err := unreachable1.NewStream(network.WithUseTransient(context.Background(), "customprotocol"), unreachable2.ID(), "/customprotocol")
 | 
						|
	if err != nil {
 | 
						|
		log.Println("Whoops, this should have worked...: ", err)
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	s.Read(make([]byte, 1)) // block until the handler closes the stream
 | 
						|
}
 |