mirror of
https://source.quilibrium.com/quilibrium/ceremonyclient.git
synced 2024-11-11 02:35:18 +00:00
135 lines
4.6 KiB
Markdown
135 lines
4.6 KiB
Markdown
|
# p2p chat app with libp2p [with peer discovery]
|
||
|
|
||
|
This program demonstrates a simple p2p chat application. You will learn how to discover a peer in the network (using kad-dht), connect to it and open a chat stream.
|
||
|
|
||
|
## Build
|
||
|
|
||
|
From the `go-libp2p/examples` directory run the following:
|
||
|
|
||
|
```
|
||
|
> cd chat-with-rendezvous/
|
||
|
> go build -o chat
|
||
|
```
|
||
|
|
||
|
## Usage
|
||
|
|
||
|
Use two different terminal windows to run
|
||
|
|
||
|
```
|
||
|
./chat -listen /ip4/127.0.0.1/tcp/6666
|
||
|
./chat -listen /ip4/127.0.0.1/tcp/6668
|
||
|
```
|
||
|
## So how does it work?
|
||
|
|
||
|
1. **Configure a p2p host**
|
||
|
```go
|
||
|
// libp2p.New constructs a new libp2p Host.
|
||
|
// Other options can be added here.
|
||
|
host, err := libp2p.New()
|
||
|
```
|
||
|
[libp2p.New](https://pkg.go.dev/github.com/libp2p/go-libp2p#New) is the constructor for a libp2p node. It creates a host with the given configuration. Right now, all the options are default, documented [here](https://pkg.go.dev/github.com/libp2p/go-libp2p#New)
|
||
|
|
||
|
2. **Set a default handler function for incoming connections.**
|
||
|
|
||
|
This function is called on the local peer when a remote peer initiates a connection and starts a stream with the local peer.
|
||
|
```go
|
||
|
// Set a function as stream handler.
|
||
|
host.SetStreamHandler("/chat/1.1.0", handleStream)
|
||
|
```
|
||
|
|
||
|
```handleStream``` is executed for each new incoming stream to the local peer. ```stream``` is used to exchange data between the local and remote peers. This example uses non blocking functions for reading and writing from this stream.
|
||
|
|
||
|
```go
|
||
|
func handleStream(stream net.Stream) {
|
||
|
|
||
|
// Create a buffer stream for non blocking read and write.
|
||
|
rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream))
|
||
|
|
||
|
go readData(rw)
|
||
|
go writeData(rw)
|
||
|
|
||
|
// 'stream' will stay open until you close it (or the other side closes it).
|
||
|
}
|
||
|
```
|
||
|
|
||
|
3. **Initiate a new DHT Client with ```host``` as local peer.**
|
||
|
|
||
|
|
||
|
```go
|
||
|
dht, err := dht.New(ctx, host)
|
||
|
```
|
||
|
|
||
|
4. **Connect to IPFS bootstrap nodes.**
|
||
|
|
||
|
These nodes are used to find nearby peers using DHT.
|
||
|
|
||
|
```go
|
||
|
for _, addr := range bootstrapPeers {
|
||
|
|
||
|
iaddr, _ := ipfsaddr.ParseString(addr)
|
||
|
|
||
|
peerinfo, _ := peerstore.InfoFromP2pAddr(iaddr.Multiaddr())
|
||
|
|
||
|
if err := host.Connect(ctx, *peerinfo); err != nil {
|
||
|
fmt.Println(err)
|
||
|
} else {
|
||
|
fmt.Println("Connection established with bootstrap node: ", *peerinfo)
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
5. **Announce your presence using a rendezvous point.**
|
||
|
|
||
|
[routingDiscovery.Advertise](https://pkg.go.dev/github.com/libp2p/go-libp2p/p2p/discovery/routing#RoutingDiscovery.Advertise) makes this node announce that it can provide a value for the given key. Where a key in this case is ```rendezvousString```. Other peers will hit the same key to find other peers.
|
||
|
|
||
|
```go
|
||
|
routingDiscovery := discovery.NewRoutingDiscovery(kademliaDHT)
|
||
|
discovery.Advertise(ctx, routingDiscovery, config.RendezvousString)
|
||
|
```
|
||
|
|
||
|
6. **Find nearby peers.**
|
||
|
|
||
|
[routingDiscovery.FindPeers](https://pkg.go.dev/github.com/libp2p/go-libp2p/p2p/discovery/routing#RoutingDiscovery.FindPeers) will return a channel of peers who have announced their presence.
|
||
|
|
||
|
```go
|
||
|
peerChan, err := routingDiscovery.FindPeers(ctx, config.RendezvousString)
|
||
|
```
|
||
|
|
||
|
The [discovery](https://pkg.go.dev/github.com/libp2p/go-libp2p/p2p/discovery/routing) package uses the DHT internally to [provide](https://pkg.go.dev/github.com/libp2p/go-libp2p-kad-dht#IpfsDHT.Provide) and [findProviders](https://pkg.go.dev/github.com/libp2p/go-libp2p-kad-dht#IpfsDHT.FindProviders).
|
||
|
|
||
|
**Note:** Although [routingDiscovery.Advertise](https://pkg.go.dev/github.com/libp2p/go-libp2p/p2p/discovery/routing#RoutingDiscovery.Advertise) and [routingDiscovery.FindPeers](https://pkg.go.dev/github.com/libp2p/go-libp2p/p2p/discovery/routing#RoutingDiscovery.FindPeers) works for a rendezvous peer discovery, this is not the right way of doing it. Libp2p is currently working on an actual rendezvous protocol ([libp2p/specs#56](https://github.com/libp2p/specs/pull/56)) which can be used for bootstrap purposes, real time peer discovery and application specific routing.
|
||
|
|
||
|
7. **Open streams to newly discovered peers.**
|
||
|
|
||
|
Finally we open streams to the newly discovered peers.
|
||
|
|
||
|
```go
|
||
|
go func() {
|
||
|
for peer := range peerChan {
|
||
|
if peer.ID == host.ID() {
|
||
|
continue
|
||
|
}
|
||
|
fmt.Println("Found peer:", peer)
|
||
|
|
||
|
fmt.Println("Connecting to:", peer)
|
||
|
stream, err := host.NewStream(ctx, peer.ID, protocol.ID(config.ProtocolID))
|
||
|
|
||
|
if err != nil {
|
||
|
fmt.Println("Connection failed:", err)
|
||
|
continue
|
||
|
} else {
|
||
|
rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream))
|
||
|
|
||
|
go writeData(rw)
|
||
|
go readData(rw)
|
||
|
}
|
||
|
|
||
|
fmt.Println("Connected to:", peer)
|
||
|
}
|
||
|
}()
|
||
|
```
|
||
|
|
||
|
## Authors
|
||
|
1. Abhishek Upperwal
|
||
|
2. Mantas Vidutis
|