From 8f76e02e66ad7af679a2a160527d2bf5d9b8539f Mon Sep 17 00:00:00 2001 From: Discordian Date: Tue, 30 May 2023 17:05:49 -0400 Subject: [PATCH 1/7] Initial work for universal connectivity guide (go peer) --- content/guides/universal-connectivity/go.md | 568 ++++++++++++++++++++ 1 file changed, 568 insertions(+) create mode 100644 content/guides/universal-connectivity/go.md diff --git a/content/guides/universal-connectivity/go.md b/content/guides/universal-connectivity/go.md new file mode 100644 index 00000000..1b7767a1 --- /dev/null +++ b/content/guides/universal-connectivity/go.md @@ -0,0 +1,568 @@ +# Building a Go libp2p Peer + +In this guide you'll learn the process to create your very own distributed peer-to-peer application. This guide was built specifically with the [Go peer](https://github.com/libp2p/universal-connectivity/tree/main/go-peer) from [libp2p/universal-connectivity](https://github.com/libp2p/universal-connectivity) in mind. You can see the finished project at [TheDiscordian/go-libp2p-peer](https://github.com/TheDiscordian/go-libp2p-peer) which can be easily forked and built upon. + +For this guide we'll be assuming you're running a Linux or MacOS system. If you're on Windows, please consider following the [WSL Install Guide](https://learn.microsoft.com/en-us/windows/wsl/install) on Microsoft's website to follow along more easily. + +Having some terminal skills will greatly assist in following this guide. If you're on MacOS, installing [Homebrew](https://brew.sh/) is highly recommended. All commands assume you're in the project directory. + +#### Table of Contents (What you'll learn!) + +[TOC] + + +## Resources + +We'll be using packages from the following repositories: + +- [go-libp2p](https://github.com/libp2p/go-libp2p) +- [go-libp2p-pubsub](https://github.com/libp2p/go-libp2p-pubsub) +- [go-multiaddr](https://github.com/multiformats/go-multiaddr) + +It might be helpful to peek into those repositories to see what they are, but we'll go over what's happening as we go. + +## Getting started + +We begin with an empty Go project `main.go`: + +```go +package main + +import ( + "fmt" +) + +func main() { +} +``` + +And run `go mod init go-peer/tutorial` to initialse your Go project. I've included `fmt` in here as we'll be using it in the near future. + +## Identity + +Every libp2p peer has an identity known as a [PeerID](https://docs.libp2p.io/concepts/fundamentals/peers/#peer-id). This PeerID is derived from a keypair. For this guide we'll be using Ed25519. + +Let's go ahead and create a new file in our project directory titled `identity.go` and populate it with the following: + +```go +package main + +import ( + "fmt" + "os" + + "github.com/libp2p/go-libp2p/core/crypto" +) +``` + +We've grabbed an external package here, specifically the go-libp2p crypto package, which will give us tools that will aid in generating and utilizing our cryptographic keys. Let's add it to our `go.mod` file with the following: + +```bash +go get github.com/libp2p/go-libp2p/core/crypto +``` + +Next, we're going to add in three functions. Don't worry, we'll break down what they do in a moment: + +```go +// GenerateIdentity writes a new random private key to the given path. +func GenerateIdentity(path string) (crypto.PrivKey, error) { + privk, _, err := crypto.GenerateKeyPair(crypto.Ed25519, 0) + if err != nil { + return nil, err + } + + bytes, err := crypto.MarshalPrivateKey(privk) + if err != nil { + return nil, err + } + + err = os.WriteFile(path, bytes, 0400) + + return privk, err +} + +// ReadIdentity reads a private key from the given path. +func ReadIdentity(path string) (crypto.PrivKey, error) { + bytes, err := os.ReadFile(path) + if err != nil { + return nil, err + } + + return crypto.UnmarshalPrivateKey(bytes) +} + +// LoadIdentity reads a private key from the given path and, if it does not +// exist, generates a new one. +func LoadIdentity(path string) (crypto.PrivKey, error) { + if _, err := os.Stat(path); err == nil { + return ReadIdentity(path) + } else if os.IsNotExist(err) { + fmt.Printf("Generating peer identity in %s\n", path) + return GenerateIdentity(path) + } else { + return nil, err + } +} +``` + +Here we have three functions, each taking one string parameter and returning the values `crypto.PrivKey` and `error`: + +- `GenerateIdentity(path string) (crypto.PrivKey, error)` + - Generates a keypair and stores the private key in `path`. +- `ReadIdentity(path string) (crypto.PrivKey, error)` + - Reads a private key from `path`. +- `LoadIdentity(path string) (crypto.PrivKey, error)` + - Reads a private key from `path` using `ReadIdentity` and, if it doesn't exist, generates one using `GenerateIdentity`. + +The easiest way to see what these functions mean to us is to use them, so let's open up `main.go` again and add a few lines to the bottom of the `main` function: + +```go +// Load our private key from "identity.key", if it doesn't exist, +// generate one, and store it in "identity.key". +privk, err := LoadIdentity("identity.key") +if err != nil { + panic(err) +} +``` + +Now we have a variable called `privk` which contains our private key which we'll use to represent our identity and sign messages. We'll use this value in the next step. + +::: warning +**Warning** +Never share your private key with anyone. Only share your public key which is derived from the private key. Typically when someone says "PeerID", they're talking about an encoded version of the public key. We'll get this value in the next step. +::: + +## Creating the Peer + +Now that we have an identity, we have the bare minimum to create and run the peer, so let's do that! In `main.go`... + +Add this to your import list: + +```go +"github.com/libp2p/go-libp2p" +``` + +Don't forget to call `go get github.com/libp2p/go-libp2p` afterwards to add it to your `go.mod` file. + +Next, add the following to the bottom of `main()`: + +```go +var opts []libp2p.Option + +opts = append(opts, + libp2p.Identity(privk), +) +``` + +What we're doing here is building a list of [libp2p options](https://pkg.go.dev/github.com/libp2p/go-libp2p#Option) which are used for configuring our peer. The only option we have right now is `libp2p.Identity(privk)` which lets the libp2p library know which private key we're using for our identity. + +Now, let's create the actual libp2p peer by adding some more lines to the bottom of our `main` function: + +```go +// Create a new libp2p Host with our options. +h, err := libp2p.New(opts...) +if err != nil { + panic(err) +} + +fmt.Println("PeerID:", h.ID().String()) +for _, addr := range h.Addrs() { + fmt.Printf("Listening on: %s/p2p/%s", addr.String(), h.ID()) + fmt.Println() +} +``` + +Run your code with `go run .` and you should see something similar to the following: + +```bash +PeerID: 12D3KooWDCm6EF7TLGGV3h34G7zbBLgiPagbXaFK6VxPM3vaod6s +Listening on: /ip4/127.0.0.1/tcp/53215/p2p/12D3KooWDCm6EF7TLGGV3h34G7zbBLgiPagbXaFK6VxPM3vaod6s +Listening on: /ip4/127.0.0.1/udp/49259/quic/p2p/12D3KooWDCm6EF7TLGGV3h34G7zbBLgiPagbXaFK6VxPM3vaod6s +Listening on: /ip4/127.0.0.1/udp/49259/quic-v1/p2p/12D3KooWDCm6EF7TLGGV3h34G7zbBLgiPagbXaFK6VxPM3vaod6s +Listening on: /ip4/127.0.0.1/udp/55676/quic-v1/webtransport/certhash/uEiDcHLhuZwUZ7zHnvO-O38Xj_5IohFefXo0JOA4AIxEn3A/certhash/uEiCqnFKTggBe3-KbC5IBQYnxovaJWdmvm2IxCYGzyGqItQ/p2p/12D3KooWDCm6EF7TLGGV3h34G7zbBLgiPagbXaFK6VxPM3vaod6s +Listening on: /ip4/192.168.0.168/tcp/53215/p2p/12D3KooWDCm6EF7TLGGV3h34G7zbBLgiPagbXaFK6VxPM3vaod6s +Listening on: /ip4/192.168.0.168/udp/49259/quic/p2p/12D3KooWDCm6EF7TLGGV3h34G7zbBLgiPagbXaFK6VxPM3vaod6s +Listening on: /ip4/192.168.0.168/udp/49259/quic-v1/p2p/12D3KooWDCm6EF7TLGGV3h34G7zbBLgiPagbXaFK6VxPM3vaod6s +Listening on: /ip4/192.168.0.168/udp/55676/quic-v1/webtransport/certhash/uEiDcHLhuZwUZ7zHnvO-O38Xj_5IohFefXo0JOA4AIxEn3A/certhash/uEiCqnFKTggBe3-KbC5IBQYnxovaJWdmvm2IxCYGzyGqItQ/p2p/12D3KooWDCm6EF7TLGGV3h34G7zbBLgiPagbXaFK6VxPM3vaod6s +Listening on: /ip6/::1/tcp/53218/p2p/12D3KooWDCm6EF7TLGGV3h34G7zbBLgiPagbXaFK6VxPM3vaod6s +Listening on: /ip6/::1/udp/55621/quic/p2p/12D3KooWDCm6EF7TLGGV3h34G7zbBLgiPagbXaFK6VxPM3vaod6s +Listening on: /ip6/::1/udp/55621/quic-v1/p2p/12D3KooWDCm6EF7TLGGV3h34G7zbBLgiPagbXaFK6VxPM3vaod6s +Listening on: /ip6/::1/udp/56270/quic-v1/webtransport/certhash/uEiDcHLhuZwUZ7zHnvO-O38Xj_5IohFefXo0JOA4AIxEn3A/certhash/uEiCqnFKTggBe3-KbC5IBQYnxovaJWdmvm2IxCYGzyGqItQ/p2p/12D3KooWDCm6EF7TLGGV3h34G7zbBLgiPagbXaFK6VxPM3vaod6s +``` + +::: info +**Tip** +If you get errors related to missing packages, run `go mod tidy`, then try `go run .` again. +::: + +The first line of output is showing us our PeerID which can be safely shared with anyone. The next lines are multiaddress lines which define ways to connect to us via IPv4, IPv6, a couple addresses, and a variety of transports. If this sounds like a lot, don't worry, we're going over transports in the next step! + +## Defining transports + +With libp2p [transports](https://docs.libp2p.io/concepts/transports/overview/) allow us to connect to other peers in a variety of ways and [multiaddresses](https://docs.libp2p.io/concepts/fundamentals/addressing/) contain information on how to connect to a peer like their PeerID, IP address, and transport. + +Currently with go-libp2p our supported transports looks a bit like like this: + +| WebTransport | WebRTC | QUIC | TCP | WebSocket | +| ------------ | ------ | ---- | --- | --------- | +| ✅ | ❌ | ✅ | ✅ | ✅ | + +Currently our node is already listening locally on a variety of transports, but with more explicit configuration you can get some control over which transports are used, what addresses/ports to listen on, and how they're configured. + +WebRTC is not yet supported, so we'll go over TCP, QUIC, and WebTransport. WebSockets are supported however, not covered in this guide. + +### TCP + +To support libp2p connections over TCP you need to add the following include to your header: + +```go +tcpTransport "github.com/libp2p/go-libp2p/p2p/transport/tcp" +``` + +Locate the lines where we defined our libp2p option `libp2p.Identity(privk)`, and add the following options to the list: + +```go +libp2p.Transport(tcpTransport.NewTCPTransport), +libp2p.ListenAddrStrings("/ip4/0.0.0.0/tcp/9090"), +``` + +What we've done here is explicitly told the libp2p library that we're going to be using a TCP transport on port 9090, and we'd like to listen on all interfaces. Port 9090 was chosen arbitrarily, you can use any port you'd like. + +Done correctly you should end up with an options list which looks like the following: + +```go +opts = append(opts, + libp2p.Identity(privk), + libp2p.Transport(tcpTransport.NewTCPTransport), + libp2p.ListenAddrStrings("/ip4/0.0.0.0/tcp/9090"), +) +``` + +### QUIC + +To support libp2p connections over [QUIC](https://docs.libp2p.io/concepts/transports/quic/) you need to add the following include to your header: + +```go +quicTransport "github.com/libp2p/go-libp2p/p2p/transport/quic" +``` + +Go to the libp2p options list outlined in the [TCP section](#TCP) of this guide and add the following option: + +```go +libp2p.Transport(quicTransport.NewTransport), +``` + +Next, you'll need to add a listen address string as well (similar to the [TCP section](#TCP)), if you've also added the TCP transport, your `ListenAddrStrings` line may look like this: + +```go +libp2p.ListenAddrStrings("/ip4/0.0.0.0/tcp/9090", "/ip4/0.0.0.0/udp/9091/quic-v1"), +``` + +This string is saying to listen on all interfaces, on UDP port 9091. + +### WebTransport + +To support libp2p connections over [WebTransport](https://docs.libp2p.io/concepts/transports/webtransport/) you need to add the following include to your header: + +```go +webTransport "github.com/libp2p/go-libp2p/p2p/transport/webtransport" +``` + +Go to the libp2p options list outlined in the [TCP section](#TCP) of this guide and add the following option: + +```go +libp2p.Transport(webTransport.New), +``` + +Next, you'll need to add a listen address string as well (similar to the [TCP section](#TCP)), if you've also added the TCP and QUIC transports, your `ListenAddrStrings` line may look like this: + +```go +libp2p.ListenAddrStrings("/ip4/0.0.0.0/tcp/9090", "/ip4/0.0.0.0/udp/9091/quic-v1", "/ip4/0.0.0.0/udp/9092/quic-v1/webtransport"), +``` + +Hopefully you see the pattern here, but this new entry is effectively saying "listen on UDP port 9092 for WebTransport connections". + +::: info +**Tip** +If you wanted to support [IPv6](https://en.wikipedia.org/wiki/IPv6), copy all the entries above, and change "ip4" to "ip6". You'll end up with 3 ip4 entries and 3 ip6 entries, now you support both IPv4 and IPv6. +::: + +## Discovery + +So now that we have our libp2p node listening on our specified ports over the specified transports, how do we get other libp2p nodes to discover us? In this guide we'll go over a way to be discovered: a [Kademlia distributed hash table](https://github.com/libp2p/specs/blob/master/kad-dht/README.md) (DHT). + +### Global Discovery with Kademlia DHT + +Often you'll want to connect to peers who aren't on the local network, this is where the [Kademlia DHT](https://curriculum.pl-launchpad.io/curriculum/libp2p/dht/) comes in. Using a discovery service tag, we'll identify ourselves as a type of peer, and also look for peers also identifying by the same tag. First let's add the following to our includes list: + +```go +"context" +"sync" +"time" + +"github.com/libp2p/go-libp2p/core/host" +dht "github.com/libp2p/go-libp2p-kad-dht" +discovery "github.com/libp2p/go-libp2p/p2p/discovery/util" +"github.com/multiformats/go-multiaddr" +"github.com/libp2p/go-libp2p/core/network" +"github.com/libp2p/go-libp2p/core/peer" +"github.com/libp2p/go-libp2p/p2p/discovery/routing" +``` + +Next, we'll need a couple constants: + +```go +// DiscoveryInterval is how often we search for other peers via the DHT. +const DiscoveryInterval = time.Second * 10 + +// DiscoveryServiceTag is used in our DHT advertisements to discover +// other peers. +const DiscoveryServiceTag = "universal-connectivity" +``` + +`DiscoveryInterval` can be set to whatever length you wish. It determines how often we'll search the DHT. We'll use this value in the second of these two functions: + +```go +// Borrowed from https://medium.com/rahasak/libp2p-pubsub-peer-discovery-with-kademlia-dht-c8b131550ac7 +// NewDHT attempts to connect to a bunch of bootstrap peers and returns a new DHT. +// If you don't have any bootstrapPeers, you can use dht.DefaultBootstrapPeers +// or an empty list. +func NewDHT(ctx context.Context, host host.Host, bootstrapPeers []multiaddr.Multiaddr) (*dht.IpfsDHT, error) { + var options []dht.Option + + // if no bootstrap peers, make this peer act as a bootstraping node + // other peers can use this peers ipfs address for peer discovery via dht + if len(bootstrapPeers) == 0 { + options = append(options, dht.Mode(dht.ModeServer)) + } + + // set our DiscoveryServiceTag as the protocol prefix so we can discover + // peers we're interested in. + options = append(options, dht.ProtocolPrefix("/"+DiscoveryServiceTag)) + + kdht, err := dht.New(ctx, host, options...) + if err != nil { + return nil, err + } + + if err = kdht.Bootstrap(ctx); err != nil { + return nil, err + } + + var wg sync.WaitGroup + // loop through bootstrapPeers (if any), and attempt to connect to them + for _, peerAddr := range bootstrapPeers { + peerinfo, _ := peer.AddrInfoFromP2pAddr(peerAddr) + + wg.Add(1) + go func() { + defer wg.Done() + if err := host.Connect(ctx, *peerinfo); err != nil { + fmt.Printf("Error while connecting to node %q: %-v", peerinfo, err) + fmt.Println() + } else { + fmt.Printf("Connection established with bootstrap node: %q", *peerinfo) + fmt.Println() + } + }() + } + wg.Wait() + + return kdht, nil +} + +// Borrowed from https://medium.com/rahasak/libp2p-pubsub-peer-discovery-with-kademlia-dht-c8b131550ac7 +// Search the DHT for peers, then connect to them. +func Discover(ctx context.Context, h host.Host, dht *dht.IpfsDHT, rendezvous string) { + var routingDiscovery = routing.NewRoutingDiscovery(dht) + + // Advertise our addresses on rendezvous + discovery.Advertise(ctx, routingDiscovery, rendezvous) + + // Search for peers every DiscoveryInterval + ticker := time.NewTicker(DiscoveryInterval) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + + // Search for other peers advertising on rendezvous and + // connect to them. + peers, err := discovery.FindPeers(ctx, routingDiscovery, rendezvous) + if err != nil { + panic(err) + } + + for _, p := range peers { + if p.ID == h.ID() { + continue + } + if h.Network().Connectedness(p.ID) != network.Connected { + _, err = h.Network().DialPeer(ctx, p.ID) + if err != nil { + fmt.Printf("Failed to connect to peer (%s): %s", p.ID, err.Error()) + fmt.Println() + continue + } + fmt.Println("Connected to peer", p.ID.Pretty()) + } + } + } + } +} +``` + +Here we have two functions, `NewDHT` and `Discover`. `NewDHT` creates an [IpfsDHT](https://pkg.go.dev/github.com/libp2p/go-libp2p-kad-dht#IpfsDHT) object which we can pass to `Discover`. What `Discover` does is every `DiscoveryInterval` it will search the DHT for new peers to connect to, and attempt a connection. + +In our `main()` function, we put the following two blocks below the block where we create the peer: + +```go +// Setup DHT with empty discovery peers so this will be a discovery peer for other +// peers. This peer should run with a public ip address, otherwise change "nil" to +// a list of peers to bootstrap with. +dht, err := NewDHT(context.TODO(), h, nil) +if err != nil { + panic(err) +} + +// Setup global peer discovery over DiscoveryServiceTag. +go Discover(context.TODO(), h, dht, DiscoveryServiceTag) +``` + +And that's it! Your node will now utilize the DHT to make itself discoverable and discover other peers, provided they're using the same `DiscoveryServiceTag` as you. + +### Announcing External Addresses + +You might have noticed in the addresses listed after running `go run .` that you don't see your public IP address listed. Instead you might see a local address or two. This means your node isn't advertising it's public IP address (however if you do see your public facing IP address, you may not need this step). We can resolve this by creating a list of addressess we want to announce. + +The following code lives in our `main()` function beneath `var opts []libp2p.Option`: +```go +var opts []libp2p.Option // here for reference, don't copy this line + +announceAddrs := []string{"/ip4/1.2.3.5/tcp/9090", "/ip4/1.2.3.5/udp/9091/quic-v1"} // Set to your external IP address for each transport you wish to use. +var announce []multiaddr.Multiaddr + if len(announceAddrs) > 0 { + for _, addr := range announceAddrs { + announce = append(announce, multiaddr.StringCast(addr)) + } + opts = append(opts, libp2p.AddrsFactory(func([]multiaddr.Multiaddr) []multiaddr.Multiaddr { + return announce + })) +} +``` + +With this code we can guarantee we announce exactly how we want other nodes to connect to us by modifying `announceAddrs`. + +:::warning +**Note** +You must modify `announceAddrs` to use your own IP address in each entry, and add an entry for each transport. This code is populated with dummy IPs and only two transports. +::: + +:::info +**Tip** +You can use an address such as `/dns4/mydomain.com/tcp/9090` to announce "you can find me using DNS over IPv4 at mydomain.com to connect to me over TCP port 9090". +::: + + +## Communicating + +Now that we can create a peer that's connectable and connect to other peers, let's communicate. We're going to communicate over [PubSub](https://docs.libp2p.io/concepts/pubsub/overview/) which is short for "publish subscribe". PubSub, specifically [GossipSub](https://docs.libp2p.io/concepts/pubsub/overview/#gossip), will allow us to subscribe and publish to topics of our choosing. First, add this to your include list in `main.go`: + +```go +pubsub "github.com/libp2p/go-libp2p-pubsub" +``` + +Next, let's create our [PubSub object](https://pkg.go.dev/github.com/libp2p/go-libp2p-pubsub#PubSub) and create a [Topic object](https://pkg.go.dev/github.com/libp2p/go-libp2p-pubsub#Topic) which we'll use for both publishing and subscribing. Put the follow code in your main function beneath the code to initialize the peer: + +```go +// Create a new PubSub service using the GossipSub router. +ps, err := pubsub.NewGossipSub(context.TODO(), h) +if err != nil { + panic(err) +} + +// Join a PubSub topic. +topicString := "UniversalPeer" // Change "UniversalPeer" to whatever you want! +topic, err := ps.Join(DiscoveryServiceTag+"/"+topicString) +if err != nil { + panic(err) +} +``` + +In the above code you can change the topic you're joining by simply changing `topicString` to whatever you'd like. + +### Publishing + +Publishing to our topic is quite simple with `topic.Publish`: + +```go +err := topic.Publish(context.TODO(), []byte("Hello world!")) +``` + +For this guide, we're going to spawn a [goroutine](https://go.dev/tour/concurrency/1) which simply publishes the current time every 5 seconds. Add the following below our other PubSub blocks: + +```go +// Publish the current date and time every 5 seconds. +go func() { + for { + err := topic.Publish(context.TODO(), []byte(fmt.Sprintf("The time is: %s", time.Now().Format(time.RFC3339)))) + if err != nil { + panic(err) + } + time.Sleep(time.Second * 5) + } +}() +``` + +### Subscribing + +The final step is to subscribe to the topic so we can actually recieve messages on the topic. Add the following code to the end of the `main` function: + +```go +// Subscribe to the topic. +sub, err := topic.Subscribe() +if err != nil { + panic(err) +} + +for { + // Block until we recieve a new message. + msg, err := sub.Next(context.TODO()) + if err != nil { + panic(err) + } + fmt.Printf("[%s] %s", msg.ReceivedFrom, string(msg.Data)) + fmt.Println() +} +``` + +This code will output whatever it recieves on our PubSub topic we set earlier. If you run two copies of the software at once, you should see output like this: + +``` +[12D3KooWLZVboYR7Ba8BYycTa5zkTbLc9tnL3aed2YTotB66L2MD] The time is: 2023-05-28T13:21:57-04:00 +[12D3KooWAiy4cC9HVv3C8NWYL3dFH1StZ1xGYK4UKrxrtmZVAVfo] The time is: 2023-05-28T13:22:00-04:00 +[12D3KooWLZVboYR7Ba8BYycTa5zkTbLc9tnL3aed2YTotB66L2MD] The time is: 2023-05-28T13:22:02-04:00 +[12D3KooWAiy4cC9HVv3C8NWYL3dFH1StZ1xGYK4UKrxrtmZVAVfo] The time is: 2023-05-28T13:22:05-04:00 +[12D3KooWLZVboYR7Ba8BYycTa5zkTbLc9tnL3aed2YTotB66L2MD] The time is: 2023-05-28T13:22:07-04:00 +``` + +:::info +**Tip** +You can run two copies of the software by building it with `go build .`, and also ensure you're using a unique `identity.key` file for each copy running. If you run multiple copies on the same PC you may have port conflicts unless you change them in some way. +::: + +## Conclusion + +After following this guide you now have a libp2p node which can communicate with other libp2p nodes over PubSub. Using this stack you can greate fully peer-to-peer applications be it a chat app (like [libp2p/universal-connecitvity](https://github.com/libp2p/universal-connectivity)), game, crypto wallet, video player, anything at all! + +libp2p is used as a foundational building block in [IPFS](https://ipfs.tech) and [Filecoin](https://filecoin.io), so make sure to dream big 🚀. Together we can build a resilient scalable world. + +### Links & Resources + +- [TheDiscordian/go-libp2p-peer](https://github.com/TheDiscordian/go-libp2p-peer) (the code from this guide all bundled into one place) +- [libp2p/universal-connecitvity](https://github.com/libp2p/universal-connectivity) (bigger examples in Rust / JS / Go, taking this example to the next level as a series of chat clients) +- [libp2p Docs](https://docs.libp2p.io) (*the* destination for libp2p information) +- [libp2p Forums](https://discuss.libp2p.io/) (a community of developers building on and with libp2p) From ceafebd44bd1e35edf6807698b100bc805e7b74c Mon Sep 17 00:00:00 2001 From: Prithvi Shahi Date: Sat, 17 Jun 2023 15:38:38 -0700 Subject: [PATCH 2/7] . --- content/guides/getting-started/go.md | 2 +- content/guides/getting-started/javascript.md | 2 +- content/guides/getting-started/nim.md | 2 +- content/guides/getting-started/rust.md | 2 +- content/guides/universal-connectivity/go.md | 8 +++++++- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/content/guides/getting-started/go.md b/content/guides/getting-started/go.md index b79424a6..04770100 100644 --- a/content/guides/getting-started/go.md +++ b/content/guides/getting-started/go.md @@ -1,6 +1,6 @@ --- title: "Run a go-libp2p node" -weight: 2 +weight: 10 description: "Learn how to run a go-libp2p node and use the ping protocol" aliases: - "/tutorials/go" diff --git a/content/guides/getting-started/javascript.md b/content/guides/getting-started/javascript.md index 91197316..209683d0 100644 --- a/content/guides/getting-started/javascript.md +++ b/content/guides/getting-started/javascript.md @@ -1,6 +1,6 @@ --- title: "Run a js-libp2p node" -weight: 3 +weight: 11 description: "Learn how to run a js-libp2p node and use the ping protocol" aliases: - "/tutorials/javascript" diff --git a/content/guides/getting-started/nim.md b/content/guides/getting-started/nim.md index 05bb4b4b..2c40ee55 100644 --- a/content/guides/getting-started/nim.md +++ b/content/guides/getting-started/nim.md @@ -1,6 +1,6 @@ --- title: "Run a nim-libp2p node" -weight: 5 +weight: 13 description: "Learn how to run use nim-libp2p" aliases: - "/tutorials/nim" diff --git a/content/guides/getting-started/rust.md b/content/guides/getting-started/rust.md index e5a77065..bf8f5246 100644 --- a/content/guides/getting-started/rust.md +++ b/content/guides/getting-started/rust.md @@ -1,6 +1,6 @@ --- title: "Run a rust-libp2p node" -weight: 4 +weight: 12 description: "Learn how to run a rust-libp2p node and use the ping protocol" aliases: - "/tutorials/rust" diff --git a/content/guides/universal-connectivity/go.md b/content/guides/universal-connectivity/go.md index 1b7767a1..89d0b740 100644 --- a/content/guides/universal-connectivity/go.md +++ b/content/guides/universal-connectivity/go.md @@ -1,8 +1,14 @@ +--- +title: "Run a go-libp2p node" +weight: 21 +description: "Learn how to build and run a go-libp2p peer" +--- + # Building a Go libp2p Peer In this guide you'll learn the process to create your very own distributed peer-to-peer application. This guide was built specifically with the [Go peer](https://github.com/libp2p/universal-connectivity/tree/main/go-peer) from [libp2p/universal-connectivity](https://github.com/libp2p/universal-connectivity) in mind. You can see the finished project at [TheDiscordian/go-libp2p-peer](https://github.com/TheDiscordian/go-libp2p-peer) which can be easily forked and built upon. -For this guide we'll be assuming you're running a Linux or MacOS system. If you're on Windows, please consider following the [WSL Install Guide](https://learn.microsoft.com/en-us/windows/wsl/install) on Microsoft's website to follow along more easily. +For this guide we'll be assuming you're running a Linux or MacOS system. If you're on Windows, please consider following the [WSL Install Guide](https://learn.microsoft.com/en-us/windows/wsl/install) on Microsoft's website to follow along more easily. Having some terminal skills will greatly assist in following this guide. If you're on MacOS, installing [Homebrew](https://brew.sh/) is highly recommended. All commands assume you're in the project directory. From 67f2f5387a153fd1020248f99a7417f11bd455aa Mon Sep 17 00:00:00 2001 From: Prithvi Shahi Date: Sat, 17 Jun 2023 15:43:58 -0700 Subject: [PATCH 3/7] . --- content/guides/universal-connectivity/_index.md | 5 +++++ content/guides/universal-connectivity/overview.md | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 content/guides/universal-connectivity/_index.md create mode 100644 content/guides/universal-connectivity/overview.md diff --git a/content/guides/universal-connectivity/_index.md b/content/guides/universal-connectivity/_index.md new file mode 100644 index 00000000..dea5b69a --- /dev/null +++ b/content/guides/universal-connectivity/_index.md @@ -0,0 +1,5 @@ +--- +title: "Universal Connectivity" +description: "A tutorial about the Universal Connectivity application" +weight: 2 +--- diff --git a/content/guides/universal-connectivity/overview.md b/content/guides/universal-connectivity/overview.md new file mode 100644 index 00000000..acb9ff5d --- /dev/null +++ b/content/guides/universal-connectivity/overview.md @@ -0,0 +1,5 @@ +--- +title: "Introducing the Universal Connectivity Application" +description: An overview and tutorial of the Universal Connectivity application +weight: 20 +--- \ No newline at end of file From 56acb74990e800ad37172a2ef7c7414a63ad4f3f Mon Sep 17 00:00:00 2001 From: Prithvi Shahi Date: Sat, 17 Jun 2023 18:28:53 -0700 Subject: [PATCH 4/7] . --- .../{go.md => go-peer.md} | 0 .../guides/universal-connectivity/js-peer.md | 0 .../guides/universal-connectivity/overview.md | 130 +++++++++++++++++- .../universal-connectivity/rust-peer.md | 0 4 files changed, 129 insertions(+), 1 deletion(-) rename content/guides/universal-connectivity/{go.md => go-peer.md} (100%) create mode 100644 content/guides/universal-connectivity/js-peer.md create mode 100644 content/guides/universal-connectivity/rust-peer.md diff --git a/content/guides/universal-connectivity/go.md b/content/guides/universal-connectivity/go-peer.md similarity index 100% rename from content/guides/universal-connectivity/go.md rename to content/guides/universal-connectivity/go-peer.md diff --git a/content/guides/universal-connectivity/js-peer.md b/content/guides/universal-connectivity/js-peer.md new file mode 100644 index 00000000..e69de29b diff --git a/content/guides/universal-connectivity/overview.md b/content/guides/universal-connectivity/overview.md index acb9ff5d..68f46231 100644 --- a/content/guides/universal-connectivity/overview.md +++ b/content/guides/universal-connectivity/overview.md @@ -2,4 +2,132 @@ title: "Introducing the Universal Connectivity Application" description: An overview and tutorial of the Universal Connectivity application weight: 20 ---- \ No newline at end of file +--- + +As you may know, libp2p is implemented in different programming languages such as Go, Rust, and Javascript to name a few. +The diversity of implementations enables libp2p to run on many different runtime environments i.e. libp2p nodes can run as server nodes (on personal PCs or datacenters) thanks to [rust-libp2p](https://github.com/libp2p/rust-libp2p) and [go-libp2p](https://github.com/libp2p/go-libp2p) or as browser nodes (within the browser) thanks to [js-libp2p](https://github.com/libp2p/js-libp2p). + +Most importantly, these different libp2p nodes running on different runtime environments can all interoperate, or communicate, with one another! +This interoperability is made possible by the wide range of transport protocols supported by different the libp2p implementations. + +In this guide, we will show you how browser nodes can interoperate with other browser nodes, how server nodes can interoperate with other server nodes, and how browser nodes can interoperate with server nodes. +To do this, we will go over the [universal-connectivity](https://github.com/libp2p/universal-connectivity) project: a decentralized chat application that can run in your browser and on your personal computer. +The goal of the universal-connectivity app was to demonstrate the power of libp2p's browser capabilities and show how libp2p can connect everything, everywhere, all at once! + +To start off, we'll begin by building the browser based node in js-libp2p and an equivalent node that can run on your laptop using rust-libp2p. +As we go further, it'll be evident why we need to build both at once and we'll make it clear for you. + +So without further ado, let's go right on ahead. + +## Getting universal-connectivity working in the browser + +Note: we will focus strictly on the libp2p aspects of the browser node and will not cover the details of how to build the chat application itself (i.e. how to build the React app from scratch and how it's frontend works etc.) Instead, our focus will be on how we configure js-libp2p, what each configuration means and so that you can extract this bit of knowledge and apply it to your own application. + +### Creating and initializing js-libp2p +Lets begin with how libp2p is created and initialized in our application. + +The universal-connectivity chat application is a ReactApp. +When initializing the application, we utilize [React's `useEffect` hook](https://react.dev/reference/react/useEffect), which is a way to [synchronize our application with an external system](https://react.dev/learn/synchronizing-with-effects), in this case, libp2p. + +Here is what [the snippet](https://github.com/libp2p/universal-connectivity/blob/main/packages/frontend/src/context/ctx.tsx#L32) looks like: + +```JavaScript + useEffect(() => { + const init = async () => { + if (loaded) return + try { + loaded = true + const libp2p = await startLibp2p() + + // @ts-ignore + window.libp2p = libp2p + + setLibp2p(libp2p) + } catch (e) { + console.error('failed to start libp2p', e) + } + } + + init() + }, []) +``` + +As you can see, inside `useEffect` we call the async `startLibp2p()` method. +Let's take a look at what `startLibp2p()` does. + + +```JavaScript +export async function startLibp2p() { + // localStorage.debug = 'libp2p*,-*:trace' + // application-specific data lives in the datastore + + const libp2p = await createLibp2p({ + addresses: { + listen: [ + '/webrtc' + ] + }, + transports: [ + webTransport(), + webSockets({ + filter: filters.all, + }), + webRTC({ + rtcConfiguration: { + iceServers:[{ + urls: [ + 'stun:stun.l.google.com:19302', + 'stun:global.stun.twilio.com:3478' + ] + }] + } + }), + webRTCDirect(), + circuitRelayTransport({ + discoverRelays: 1, + }) + ], + connectionManager: { + maxConnections: 10, + minConnections: 5 + }, + connectionEncryption: [noise()], + streamMuxers: [yamux()], + connectionGater: { + denyDialMultiaddr: async () => false, + }, + peerDiscovery: [ + bootstrap({ + list: [ + WEBRTC_BOOTSTRAP_NODE, + WEBTRANSPORT_BOOTSTRAP_NODE, + ], + }), + ], + services: { + pubsub: gossipsub({ + allowPublishToZeroPeers: true, + msgIdFn: msgIdFnStrictNoSign, + ignoreDuplicatePublishError: true, + }), + dht: kadDHT({ + protocolPrefix: "/universal-connectivity", + maxInboundStreams: 5000, + maxOutboundStreams: 5000, + clientMode: true, + }), + identify: identifyService() + } + }) + + libp2p.services.pubsub.subscribe(CHAT_TOPIC) + + libp2p.addEventListener('self:peer:update', ({detail: { peer }}) => { + const multiaddrs = peer.addresses.map(({ multiaddr }) => multiaddr) + + console.log(`changed multiaddrs: peer ${peer.id.toString()} multiaddrs: ${multiaddrs}`) + }) + + return libp2p +} +``` \ No newline at end of file diff --git a/content/guides/universal-connectivity/rust-peer.md b/content/guides/universal-connectivity/rust-peer.md new file mode 100644 index 00000000..e69de29b From 84c6723f44fd66c030cce024103fcb36843e2a8c Mon Sep 17 00:00:00 2001 From: Prithvi Shahi Date: Sun, 18 Jun 2023 11:03:27 -0700 Subject: [PATCH 5/7] . --- .../guides/universal-connectivity/overview.md | 93 ++++--------------- 1 file changed, 20 insertions(+), 73 deletions(-) diff --git a/content/guides/universal-connectivity/overview.md b/content/guides/universal-connectivity/overview.md index 68f46231..959a446c 100644 --- a/content/guides/universal-connectivity/overview.md +++ b/content/guides/universal-connectivity/overview.md @@ -5,31 +5,33 @@ weight: 20 --- As you may know, libp2p is implemented in different programming languages such as Go, Rust, and Javascript to name a few. -The diversity of implementations enables libp2p to run on many different runtime environments i.e. libp2p nodes can run as server nodes (on personal PCs or datacenters) thanks to [rust-libp2p](https://github.com/libp2p/rust-libp2p) and [go-libp2p](https://github.com/libp2p/go-libp2p) or as browser nodes (within the browser) thanks to [js-libp2p](https://github.com/libp2p/js-libp2p). +The diversity of implementations enables libp2p to run on many different runtime environments i.e. libp2p nodes can run as server nodes (on personal PCs or datacenters) (thanks to [rust-libp2p](https://github.com/libp2p/rust-libp2p) and [go-libp2p](https://github.com/libp2p/go-libp2p)) or as nodes inside the browser (thanks to [js-libp2p](https://github.com/libp2p/js-libp2p)). Most importantly, these different libp2p nodes running on different runtime environments can all interoperate, or communicate, with one another! This interoperability is made possible by the wide range of transport protocols supported by different the libp2p implementations. In this guide, we will show you how browser nodes can interoperate with other browser nodes, how server nodes can interoperate with other server nodes, and how browser nodes can interoperate with server nodes. To do this, we will go over the [universal-connectivity](https://github.com/libp2p/universal-connectivity) project: a decentralized chat application that can run in your browser and on your personal computer. -The goal of the universal-connectivity app was to demonstrate the power of libp2p's browser capabilities and show how libp2p can connect everything, everywhere, all at once! +The goal of the universal-connectivity app is to demonstrate the power of libp2p's browser capabilities and show how libp2p can connect everything, everywhere, all at once! To start off, we'll begin by building the browser based node in js-libp2p and an equivalent node that can run on your laptop using rust-libp2p. -As we go further, it'll be evident why we need to build both at once and we'll make it clear for you. + -So without further ado, let's go right on ahead. + ## Getting universal-connectivity working in the browser -Note: we will focus strictly on the libp2p aspects of the browser node and will not cover the details of how to build the chat application itself (i.e. how to build the React app from scratch and how it's frontend works etc.) Instead, our focus will be on how we configure js-libp2p, what each configuration means and so that you can extract this bit of knowledge and apply it to your own application. +Note: we will focus strictly on the libp2p aspects of the browser node and will not cover the details of how to build the chat application itself (i.e. how to build the React app from scratch and how its frontend works etc.) +Instead, our focus will be on how to configure libp2p for the browser app and explain what each configuration means. +Our aim is for you to extract this bit of knowledge and apply it to your own application! ### Creating and initializing js-libp2p Lets begin with how libp2p is created and initialized in our application. The universal-connectivity chat application is a ReactApp. -When initializing the application, we utilize [React's `useEffect` hook](https://react.dev/reference/react/useEffect), which is a way to [synchronize our application with an external system](https://react.dev/learn/synchronizing-with-effects), in this case, libp2p. +When initializing the app, we utilize [React's `useEffect` hook](https://react.dev/reference/react/useEffect), a way to [synchronize the app with an external system](https://react.dev/learn/synchronizing-with-effects), in this case, libp2p. -Here is what [the snippet](https://github.com/libp2p/universal-connectivity/blob/main/packages/frontend/src/context/ctx.tsx#L32) looks like: +Here is what [the `useEffect` snippet](https://github.com/libp2p/universal-connectivity/blob/main/packages/frontend/src/context/ctx.tsx#L32) looks like: ```JavaScript useEffect(() => { @@ -52,8 +54,7 @@ Here is what [the snippet](https://github.com/libp2p/universal-connectivity/blob }, []) ``` -As you can see, inside `useEffect` we call the async `startLibp2p()` method. -Let's take a look at what `startLibp2p()` does. +Inside `useEffect` we call the async `startLibp2p()` method, which itself is actually a wrapper for the `createLibp2p()` method: ```JavaScript @@ -62,72 +63,18 @@ export async function startLibp2p() { // application-specific data lives in the datastore const libp2p = await createLibp2p({ - addresses: { - listen: [ - '/webrtc' - ] - }, - transports: [ - webTransport(), - webSockets({ - filter: filters.all, - }), - webRTC({ - rtcConfiguration: { - iceServers:[{ - urls: [ - 'stun:stun.l.google.com:19302', - 'stun:global.stun.twilio.com:3478' - ] - }] - } - }), - webRTCDirect(), - circuitRelayTransport({ - discoverRelays: 1, - }) - ], - connectionManager: { - maxConnections: 10, - minConnections: 5 - }, - connectionEncryption: [noise()], - streamMuxers: [yamux()], - connectionGater: { - denyDialMultiaddr: async () => false, - }, - peerDiscovery: [ - bootstrap({ - list: [ - WEBRTC_BOOTSTRAP_NODE, - WEBTRANSPORT_BOOTSTRAP_NODE, - ], - }), - ], - services: { - pubsub: gossipsub({ - allowPublishToZeroPeers: true, - msgIdFn: msgIdFnStrictNoSign, - ignoreDuplicatePublishError: true, - }), - dht: kadDHT({ - protocolPrefix: "/universal-connectivity", - maxInboundStreams: 5000, - maxOutboundStreams: 5000, - clientMode: true, - }), - identify: identifyService() - } + // Options }) - libp2p.services.pubsub.subscribe(CHAT_TOPIC) + return libp2p +} +``` + - libp2p.addEventListener('self:peer:update', ({detail: { peer }}) => { - const multiaddrs = peer.addresses.map(({ multiaddr }) => multiaddr) - console.log(`changed multiaddrs: peer ${peer.id.toString()} multiaddrs: ${multiaddrs}`) - }) +The universal-connectivity application has many options specified in `createLibp2p`, let's describe them: +### Transport Options - return libp2p -} -``` \ No newline at end of file +Central to the libp2p and to our sample app are the libp2p transports. +These transport protocols enable connectivity between nodes. +In our case, what care most about is building an application that From 78c1e62f9572fc99b7947ef7939e197ef7ca7a97 Mon Sep 17 00:00:00 2001 From: Prithvi Shahi Date: Sun, 18 Jun 2023 13:29:42 -0700 Subject: [PATCH 6/7] . --- .../guides/universal-connectivity/overview.md | 41 ++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/content/guides/universal-connectivity/overview.md b/content/guides/universal-connectivity/overview.md index 959a446c..1a1d5be6 100644 --- a/content/guides/universal-connectivity/overview.md +++ b/content/guides/universal-connectivity/overview.md @@ -59,8 +59,6 @@ Inside `useEffect` we call the async `startLibp2p()` method, which itself is act ```JavaScript export async function startLibp2p() { - // localStorage.debug = 'libp2p*,-*:trace' - // application-specific data lives in the datastore const libp2p = await createLibp2p({ // Options @@ -70,11 +68,42 @@ export async function startLibp2p() { } ``` +Our application has many options specified in `createLibp2p`, let's describe them: - -The universal-connectivity application has many options specified in `createLibp2p`, let's describe them: ### Transport Options -Central to the libp2p and to our sample app are the libp2p transports. +Central to libp2p and to our sample app are libp2p transports. These transport protocols enable connectivity between nodes. -In our case, what care most about is building an application that +The transport options we've specified for our browser application (in no particular order) are WebTransport, WebSockets, WebRTC & WebRTC direct, and the Circuit Relay transport. +Please checkout the linked documentation to learn more about each. + +```JavaScript + transports: [ + webTransport(), + webSockets({ + filter: filters.all, + }), + webRTC({ + rtcConfiguration: { + iceServers:[{ + urls: [ + 'stun:stun.l.google.com:19302', + 'stun:global.stun.twilio.com:3478' + ] + }] + } + }), + webRTCDirect(), + circuitRelayTransport({ + discoverRelays: 1, + }) + ], +``` + +#### WebTransport +The first transport option specified is WebTransport. This is primarily specified in order to get the browser node to establish a browser-to-server connection with the go peer. +Learn more about the WebTransport transport here. + +### WebSockets +The second transport option specified is WebSockets. +We have configured this ot From 17ead86be3ad1bf0c1c1b18fe7cc8d6f10efe414 Mon Sep 17 00:00:00 2001 From: Prithvi Shahi Date: Wed, 5 Jul 2023 09:34:58 -0700 Subject: [PATCH 7/7] . --- .../guides/universal-connectivity/overview.md | 167 ++++++++++++++---- 1 file changed, 137 insertions(+), 30 deletions(-) diff --git a/content/guides/universal-connectivity/overview.md b/content/guides/universal-connectivity/overview.md index 1a1d5be6..97d14fd4 100644 --- a/content/guides/universal-connectivity/overview.md +++ b/content/guides/universal-connectivity/overview.md @@ -15,17 +15,14 @@ To do this, we will go over the [universal-connectivity](https://github.com/libp The goal of the universal-connectivity app is to demonstrate the power of libp2p's browser capabilities and show how libp2p can connect everything, everywhere, all at once! To start off, we'll begin by building the browser based node in js-libp2p and an equivalent node that can run on your laptop using rust-libp2p. - - - -## Getting universal-connectivity working in the browser +# Getting universal-connectivity working in the browser Note: we will focus strictly on the libp2p aspects of the browser node and will not cover the details of how to build the chat application itself (i.e. how to build the React app from scratch and how its frontend works etc.) Instead, our focus will be on how to configure libp2p for the browser app and explain what each configuration means. Our aim is for you to extract this bit of knowledge and apply it to your own application! -### Creating and initializing js-libp2p +## Creating and initializing js-libp2p Lets begin with how libp2p is created and initialized in our application. The universal-connectivity chat application is a ReactApp. @@ -77,33 +74,143 @@ These transport protocols enable connectivity between nodes. The transport options we've specified for our browser application (in no particular order) are WebTransport, WebSockets, WebRTC & WebRTC direct, and the Circuit Relay transport. Please checkout the linked documentation to learn more about each. +#### Browser to server connectivity +##### WebTransport +The first transport option specified is WebTransport. +This is primarily specified in order to get the browser node to establish a browser-to-server connection with the go peer. +Today only js-libp2p and go-libp2p support WebTransport. +Learn more about the WebTransport transport here. + +```JavaScript +transports: [ + webTransport() +] +``` + +##### WebRTC Direct +The second and third transport options we specify are for `webRTCDirect` and for `webRTC`: + +```JavaScript +transports: [ + webRTCDirect() +] +``` + +The `webRTCDirect` transport enables browser to server connections i.e. it enables the browser node to connect with server nodes that also support `webRTCDirect`. +Today, apart from js-libp2p, only rust-libp2p supports the WebRTC direct transport. +In terms of establishing browser to server connections, it is similar to WebTransport. +Therefore, enabling both WebTransport and WebRTC direct is important for our browser app if we want to make direct connections from the browser to a go-libp2p peer or a rust-lib2p peer. +To learn more about WebRTC direct, please go here. + +#### Browser to browser connectivity + +##### WebRTC + +The `webRTC` transport serves a different purpose. +This transport enables browser nodes to make direct connections with other browser nodes. +You can learn more about how this direct connectivity is established in the docs. + +```JavaScript +transports: [ + webRTC({ + rtcConfiguration: { + iceServers:[{ + urls: [ + 'stun:stun.l.google.com:19302', + 'stun:global.stun.twilio.com:3478' + ] + }] + } + }) +] +``` +As mentioned in the documentation, browser nodes do not have their public IP address at their disposal. +To learn their public IP address, they must get that information from a STUN server. +Here you can see that we provide the addresses of public STUN servers (one operated by Google, another by Twilio) as configuration options to the `webRTC` transport. +Note: two different browser nodes may use different STUN servers. + +##### Circuit Relay + +Configuring `webRTC` is not quite enough to establish direct connections with other browser peers. +Before two browser peers can establish a direct connection, they must first establish a relayed connection. +Learn more about how circuit relay works here and how it works in the context of WebRTC browser-to-browser here. + +In our `transports` configuration, we can enable Circuit Relay like so: + ```JavaScript - transports: [ - webTransport(), - webSockets({ - filter: filters.all, - }), - webRTC({ - rtcConfiguration: { - iceServers:[{ - urls: [ - 'stun:stun.l.google.com:19302', - 'stun:global.stun.twilio.com:3478' - ] - }] - } - }), - webRTCDirect(), - circuitRelayTransport({ - discoverRelays: 1, - }) +transports: [ + circuitRelayTransport({ + discoverRelays: 1, + }) +] +``` + +By default, our application only makes use of one circuit relay, therefore, we have set `discoverRelays` to `1`. + +The relay node that we use in our application is the rust peer. +In the rust-peer section, we'll discuss more on how to set it up as a relay node that can be used by other libp2p nodes in your network. + + +#### Summarizing the Transports + +With that, our complete list of transporst for the browser node looks like: + +```JavaScript +transports: [ + webTransport(), + webRTCDirect(), + webRTC({ + rtcConfiguration: { + iceServers:[{ + urls: [ + 'stun:stun.l.google.com:19302', + 'stun:global.stun.twilio.com:3478' + ] + }] + } + }), + circuitRelayTransport({ + discoverRelays: 1, + }) +] +``` + +In summary: +- WebTransport enables us to make browser to server connections with go-libp2p peers +- WebRTC Direct enables us to make browser to server connections with rust-libp2p peers +- WebRTC enables us to make direct browser to browser connections +- Circuit Relay is enables us to make connections to relay nodes on the network and helps set up the WebRTC browser-to-browser connections + + +### Peer Discovery + +Transport protocols give our application the ability to connect and send data across runtime environments (i.e. browser to server and browser to browser). +However, we still need the ability to discover peers on the network. +So the next configurations we provide to `createLibp2p` are those for peer discovery. + +#### Bootstrap + +The first of these is the `peerDiscovery` option to specify a a list of `bootstrap` nodes. +To learn more about the bootstrapping process, please refer to this doc. + +``` +peerDiscovery: [ + bootstrap({ + list: [ + WEBRTC_BOOTSTRAP_NODE, + WEBTRANSPORT_BOOTSTRAP_NODE, ], + }), +] ``` -#### WebTransport -The first transport option specified is WebTransport. This is primarily specified in order to get the browser node to establish a browser-to-server connection with the go peer. -Learn more about the WebTransport transport here. +This is a list of multiaddrs and here we provide two variables: +- `WEBRTC_BOOTSTRAP_NODE`: the multiaddr for a peer that we make a `webRTCDirect` connection with. +- `WEBTRANSPORT_BOOTSTRAP_NODE`: the multiaddr for a peer that we make a `webTransport` connection with + + + + -### WebSockets -The second transport option specified is WebSockets. -We have configured this ot +- An example of a local multiaddr is `/ip4/127.0.0.1/udp/9090/webrtc-direct/certhash/uEiBy_U1UNQ0IDvot_PKlQM_QeU3yx-zCAVaMxxVm2JxWBg/p2p/12D3KooWSFfVyasFDa4NBQMzTmzSQBehUV92Exs9dsGjr9DL5TS3` +- An example of a local multiaddr is `/ip4/127.0.0.1/udp/9095/quic-v1/webtransport/certhash/uEiAvY5RHCUKqnnCRWnFs0S0AGP76-hifxZMLA8FjskcAvQ/certhash/uEiABdqm3hcMoQ6_NoDB6drEJLRgIX-lQ_0f-IGDH7ESPfA/p2p/12D3KooWEMhCiXfgYuo6kzep7F5gLbj8rGnncSxvS8zShKgeEnGS` \ No newline at end of file