diff --git a/chat-with-rendezvous/README.md b/chat-with-rendezvous/README.md index 735e1bc..273cf30 100644 --- a/chat-with-rendezvous/README.md +++ b/chat-with-rendezvous/README.md @@ -7,7 +7,7 @@ This program demonstrates a simple p2p chat application. You will learn how to d ``` go get github.com/libp2p/go-libp2p-examples/chat-with-rendezvous -go build chat.go +go build *.go ``` ## Usage @@ -15,11 +15,12 @@ go build chat.go Use two different terminal windows to run ``` -./chat +./chat -listen /ip4/127.0.0.1/tcp/6666 +./chat -listen /ip4/127.0.0.1/tcp/6668 ``` ## So how does it work? -1. **Start a p2p host** +1. **Configure a p2p host** ```go ctx := context.Background() @@ -80,50 +81,55 @@ for _, addr := range bootstrapPeers { 5. **Announce your presence using a rendezvous point.** -[dht.Provide](https://godoc.org/github.com/libp2p/go-libp2p-kad-dht#IpfsDHT.Provide) makes this node announce that it can provide a value for the given key. Where a key in this case is ```rendezvousPoint```. Other peers will hit the same key to find other peers. +[routingDiscovery.Advertise](https://godoc.org/github.com/libp2p/go-libp2p-discovery#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 -if err := dht.Provide(tctx, rendezvousPoint, true); err != nil { - panic(err) -} +routingDiscovery := discovery.NewRoutingDiscovery(kademliaDHT) +discovery.Advertise(ctx, routingDiscovery, config.RendezvousString) ``` 6. **Find peers nearby.** -[dht.FindProviders](https://godoc.org/github.com/libp2p/go-libp2p-kad-dht#IpfsDHT.FindProviders) will return all the peers who have announced their presence before. +[routingDiscovery.FindPeers](https://godoc.org/github.com/libp2p/go-libp2p-discovery#RoutingDiscovery.FindPeers) will return a channel of peers who have announced their presence. ```go -peers, err := dht.FindProviders(tctx, rendezvousPoint) +peerChan, err := routingDiscovery.FindPeers(ctx, config.RendezvousString) ``` -**Note:** Although [dht.Provide](https://godoc.org/github.com/libp2p/go-libp2p-kad-dht#IpfsDHT.Provide) and [dht.FindProviders](https://godoc.org/github.com/libp2p/go-libp2p-kad-dht#IpfsDHT.FindProviders) 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. +The [discovery](https://godoc.org/github.com/libp2p/go-libp2p-discovery#pkg-index) package uses the DHT internally to [provide](https://godoc.org/github.com/libp2p/go-libp2p-kad-dht#IpfsDHT.Provide) and [findProviders](https://godoc.org/github.com/libp2p/go-libp2p-kad-dht#IpfsDHT.FindProviders). + +**Note:** Although [routingDiscovery.Advertise](https://godoc.org/github.com/libp2p/go-libp2p-discovery#RoutingDiscovery.Advertise) and [routingDiscovery.FindPeers](https://godoc.org/github.com/libp2p/go-libp2p-discovery#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 peers found.** -Finally we open stream to the peers we found. +Finally we open stream to the peers we found, as we find them ```go -for _, p := range peers { - - if p.ID == host.ID() || len(p.Addrs) == 0 { - // No sense connecting to ourselves or if addrs are not available - continue - } - - stream, err := host.NewStream(ctx, p.ID, "/chat/1.1.0") - - if err != nil { - fmt.Println(err) - } else { - rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream)) - - go writeData(rw) - go readData(rw) - } - - fmt.Println("Connected to: ", p) -} +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 \ No newline at end of file diff --git a/chat-with-rendezvous/chat.go b/chat-with-rendezvous/chat.go index e9d6dcc..1b57cc5 100644 --- a/chat-with-rendezvous/chat.go +++ b/chat-with-rendezvous/chat.go @@ -5,32 +5,22 @@ import ( "context" "flag" "fmt" - "log" "os" - "time" - cid "github.com/ipfs/go-cid" - iaddr "github.com/ipfs/go-ipfs-addr" - libp2p "github.com/libp2p/go-libp2p" - dht "github.com/libp2p/go-libp2p-kad-dht" + logging "github.com/ipfs/go-log" + "github.com/libp2p/go-libp2p" + "github.com/libp2p/go-libp2p-discovery" + libp2pdht "github.com/libp2p/go-libp2p-kad-dht" inet "github.com/libp2p/go-libp2p-net" - pstore "github.com/libp2p/go-libp2p-peerstore" - mh "github.com/multiformats/go-multihash" + "github.com/libp2p/go-libp2p-peerstore" + "github.com/libp2p/go-libp2p-protocol" + "github.com/multiformats/go-multiaddr" ) -// IPFS bootstrap nodes. Used to find other peers in the network. -var bootstrapPeers = []string{ - "/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", - "/ip4/104.236.179.241/tcp/4001/ipfs/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM", - "/ip4/104.236.76.40/tcp/4001/ipfs/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64", - "/ip4/128.199.219.111/tcp/4001/ipfs/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu", - "/ip4/178.62.158.247/tcp/4001/ipfs/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd", -} - -var rendezvous = "meet me here" +var log = logging.Logger("rendezvous") func handleStream(stream inet.Stream) { - log.Println("Got a new stream!") + log.Info("Got a new stream!") // Create a buffer stream for non blocking read and write. rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream)) @@ -40,9 +30,14 @@ func handleStream(stream inet.Stream) { // 'stream' will stay open until you close it (or the other side closes it). } + func readData(rw *bufio.ReadWriter) { for { - str, _ := rw.ReadString('\n') + str, err := rw.ReadString('\n') + if err != nil { + fmt.Println("Error reading from buffer") + panic(err) + } if str == "" { return @@ -62,20 +57,30 @@ func writeData(rw *bufio.ReadWriter) { for { fmt.Print("> ") sendData, err := stdReader.ReadString('\n') - if err != nil { + fmt.Println("Error reading from stdin") panic(err) } - rw.WriteString(fmt.Sprintf("%s\n", sendData)) - rw.Flush() + _, err = rw.WriteString(fmt.Sprintf("%s\n", sendData)) + if err != nil { + fmt.Println("Error writing to buffer") + panic(err) + } + err = rw.Flush() + if err != nil { + fmt.Println("Error flushing buffer") + panic(err) + } } } func main() { help := flag.Bool("h", false, "Display Help") - rendezvousString := flag.String("r", rendezvous, "Unique string to identify group of nodes. Share this with your friends to let them connect with you") - flag.Parse() + config, err := ParseFlags() + if err != nil { + panic(err) + } if *help { fmt.Printf("This program demonstrates a simple p2p chat application using libp2p\n\n") @@ -86,82 +91,89 @@ func main() { ctx := context.Background() + // Configure p2p host + addrs := make([]multiaddr.Multiaddr, len(config.ListenAddresses)) + copy(addrs, config.ListenAddresses) + + options := []libp2p.Option{libp2p.ListenAddrs(addrs...)} + // libp2p.New constructs a new libp2p Host. // Other options can be added here. - host, err := libp2p.New(ctx) + host, err := libp2p.New(ctx, options...) if err != nil { panic(err) } + fmt.Println("Host created. We are:", host.ID()) // Set a function as stream handler. - // This function is called when a peer initiate a connection and starts a stream with this peer. - host.SetStreamHandler("/chat/1.1.0", handleStream) - - kadDht, err := dht.New(ctx, host) + // This function is called when a peer initiates a connection and starts a stream with this peer. + host.SetStreamHandler(protocol.ID(config.ProtocolID), handleStream) + + // Start a DHT, for use in peer discovery. We can't just make a new DHT client + // because we want each peer to maintain its own local copy of the DHT, so + // that the bootstrapping node of the DHT can go down without inhibitting + // future peer discovery. + kademliaDHT, err := libp2pdht.New(ctx, host) if err != nil { panic(err) } + // Bootstrap the DHT. In the default configuration, this spawns a Background + // thread that will refresh the peer table every five minutes. + fmt.Println("Bootstrapping the DHT") + if err = kademliaDHT.Bootstrap(ctx); err != nil { + panic(err) + } + // Let's connect to the bootstrap nodes first. They will tell us about the other nodes in the network. - for _, peerAddr := range bootstrapPeers { - addr, _ := iaddr.ParseString(peerAddr) - peerinfo, _ := pstore.InfoFromP2pAddr(addr.Multiaddr()) + for _, peerAddr := range config.BootstrapPeers { + peerinfo, _ := peerstore.InfoFromP2pAddr(peerAddr) if err := host.Connect(ctx, *peerinfo); err != nil { fmt.Println(err) } else { - fmt.Println("Connection established with bootstrap node: ", *peerinfo) + fmt.Println("Connection established with bootstrap node:", *peerinfo) } } // We use a rendezvous point "meet me here" to announce our location. // This is like telling your friends to meet you at the Eiffel Tower. - v1b := cid.V1Builder{Codec: cid.Raw, MhType: mh.SHA2_256} - rendezvousPoint, _ := v1b.Sum([]byte(*rendezvousString)) - - fmt.Println("announcing ourselves...") - tctx, cancel := context.WithTimeout(ctx, time.Second*10) - defer cancel() - if err := kadDht.Provide(tctx, rendezvousPoint, true); err != nil { - panic(err) - } + fmt.Println("Announcing ourselves...") + routingDiscovery := discovery.NewRoutingDiscovery(kademliaDHT) + discovery.Advertise(ctx, routingDiscovery, config.RendezvousString) + fmt.Println("Successfully announced!") // Now, look for others who have announced // This is like your friend telling you the location to meet you. - // 'FindProviders' will return 'PeerInfo' of all the peers which - // have 'Provide' or announced themselves previously. - fmt.Println("searching for other peers...") - tctx, cancel = context.WithTimeout(ctx, time.Second*10) - defer cancel() - peers, err := kadDht.FindProviders(tctx, rendezvousPoint) + fmt.Println("Searching for other peers...") + peerChan, err := routingDiscovery.FindPeers(ctx, config.RendezvousString) if err != nil { panic(err) } - fmt.Printf("Found %d peers!\n", len(peers)) - for _, p := range peers { - fmt.Println("Peer: ", p) - } + go func() { + for peer := range peerChan { + if peer.ID == host.ID() { + continue + } + fmt.Println("Found peer:", peer) - for _, p := range peers { - if p.ID == host.ID() || len(p.Addrs) == 0 { - // No sense connecting to ourselves or if addrs are not available - continue - } + fmt.Println("Connecting to:", peer) + stream, err := host.NewStream(ctx, peer.ID, protocol.ID(config.ProtocolID)) - stream, err := host.NewStream(ctx, p.ID, "/chat/1.1.0") + if err != nil { + fmt.Println("Connection failed:", err) + continue + } else { + rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream)) - if err != nil { - fmt.Println(err) - } else { - rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream)) + go writeData(rw) + go readData(rw) + } - go writeData(rw) - go readData(rw) + fmt.Println("Connected to:", peer) } - - fmt.Println("Connected to: ", p) - } + }() select {} } diff --git a/chat-with-rendezvous/flags.go b/chat-with-rendezvous/flags.go new file mode 100644 index 0000000..dfb6d24 --- /dev/null +++ b/chat-with-rendezvous/flags.go @@ -0,0 +1,74 @@ +package main + +import ( + "flag" + "strings" + + maddr "github.com/multiformats/go-multiaddr" +) + +// A new type we need for writing a custom flag parser +type addrList []maddr.Multiaddr + +func (al *addrList) String() string { + strs := make([]string, len(*al)) + for i, addr := range *al { + strs[i] = addr.String() + } + return strings.Join(strs, ",") +} + +func (al *addrList) Set(value string) error { + addr, err := maddr.NewMultiaddr(value) + if err != nil { + return err + } + *al = append(*al, addr) + return nil +} + +// IPFS bootstrap nodes. Used to find other peers in the network. +var defaultBootstrapAddrStrings = []string{ + "/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", + "/ip4/104.236.179.241/tcp/4001/ipfs/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM", + "/ip4/104.236.76.40/tcp/4001/ipfs/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64", + "/ip4/128.199.219.111/tcp/4001/ipfs/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu", + "/ip4/178.62.158.247/tcp/4001/ipfs/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd", +} + +func StringsToAddrs(addrStrings []string) (maddrs []maddr.Multiaddr, err error) { + for _, addrString := range addrStrings { + addr, err := maddr.NewMultiaddr(addrString) + if err != nil { + return maddrs, err + } + maddrs = append(maddrs, addr) + } + return +} + +type Config struct { + RendezvousString string + BootstrapPeers addrList + ListenAddresses addrList + ProtocolID string +} + +func ParseFlags() (Config, error) { + config := Config{} + flag.StringVar(&config.RendezvousString, "rendezvous", "meet me here", "Unique string to identify group of nodes. Share this with your friends to let them connect with you") + flag.Var(&config.BootstrapPeers, "peer", "Adds a peer multiaddress to the bootstrap list") + flag.Var(&config.ListenAddresses, "listen", "Adds a multiaddress to the listen list") + flag.StringVar(&config.ProtocolID, "pid", "/chat/1.1.0","Sets a protocol id for stream headers") + flag.Parse() + + if len(config.BootstrapPeers) == 0 { + bootstrapPeerAddrs, err := StringsToAddrs(defaultBootstrapAddrStrings) + if err != nil { + return config, err + } + config.BootstrapPeers = bootstrapPeerAddrs + } + + return config, nil +} diff --git a/chat/chat.go b/chat/chat.go index 942cb1e..a3cb283 100644 --- a/chat/chat.go +++ b/chat/chat.go @@ -1,29 +1,29 @@ /* -* -* The MIT License (MIT) -* -* Copyright (c) 2014 Juan Batiz-Benet -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. -* -* This program demonstrate a simple chat application using p2p communication. -* + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Juan Batiz-Benet + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This program demonstrate a simple chat application using p2p communication. + * */ package main @@ -56,32 +56,18 @@ import ( * @credit examples/http-proxy/proxy.go */ func addAddrToPeerstore(h host.Host, addr string) peer.ID { - // The following code extracts target's the peer ID from the - // given multiaddress - ipfsaddr, err := multiaddr.NewMultiaddr(addr) - if err != nil { - log.Fatalln(err) - } - pid, err := ipfsaddr.ValueForProtocol(multiaddr.P_IPFS) + maddr, err := multiaddr.NewMultiaddr(addr) if err != nil { log.Fatalln(err) } - peerid, err := peer.IDB58Decode(pid) + info, err := peerstore.InfoFromP2pAddr(maddr) if err != nil { log.Fatalln(err) } - // Decapsulate the /ipfs/ part from the target - // /ip4//ipfs/ becomes /ip4/ - targetPeerAddr, _ := multiaddr.NewMultiaddr( - fmt.Sprintf("/ipfs/%s", peer.IDB58Encode(peerid))) - targetAddr := ipfsaddr.Decapsulate(targetPeerAddr) - - // We have a peer ID and a targetAddr so we add - // it to the peerstore so LibP2P knows how to contact it - h.Peerstore().AddAddr(peerid, targetAddr, peerstore.PermanentAddrTTL) - return peerid + h.Peerstore().AddAddrs(info.ID, info.Addrs, peerstore.PermanentAddrTTL) + return info.ID } func handleStream(s net.Stream) { @@ -129,39 +115,40 @@ func writeData(rw *bufio.ReadWriter) { } func main() { - sourcePort := flag.Int("sp", 0, "Source port number") - dest := flag.String("d", "", "Dest MultiAddr String") - help := flag.Bool("help", false, "Display Help") - debug := flag.Bool("debug", true, "Debug generated same node id on every execution.") + dest := flag.String("d", "", "Destination multiaddr string") + help := flag.Bool("help", false, "Display help") + debug := flag.Bool("debug", false, "Debug generates the same node ID on every execution") flag.Parse() if *help { fmt.Printf("This program demonstrates a simple p2p chat application using libp2p\n\n") - fmt.Printf("Usage: Run './chat -sp ' where can be any port number. Now run './chat -d ' where is multiaddress of previous listener host.\n") + fmt.Println("Usage: Run './chat -sp ' where can be any port number.") + fmt.Println("Now run './chat -d ' where is multiaddress of previous listener host.") os.Exit(0) } - // If debug is enabled used constant random source else cryptographic randomness. + // If debug is enabled, use a constant random source to generate the peer ID. Only useful for debugging, + // off by default. Otherwise, it uses rand.Reader. var r io.Reader if *debug { - // Constant random source. This will always generate the same host ID on multiple execution. - // Don't do this in production code. + // Use the port number as the randomness source. + // This will always generate the same host ID on multiple executions, if the same port number is used. + // Never do this in production code. r = mrand.New(mrand.NewSource(int64(*sourcePort))) } else { r = rand.Reader } - // Creates a new RSA key pair for this host + // Creates a new RSA key pair for this host. prvKey, _, err := crypto.GenerateKeyPairWithReader(crypto.RSA, 2048, r) - if err != nil { panic(err) } - // 0.0.0.0 will listen on any interface device + // 0.0.0.0 will listen on any interface device. sourceMultiAddr, _ := multiaddr.NewMultiaddr(fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", *sourcePort)) // libp2p.New constructs a new libp2p Host. @@ -178,31 +165,55 @@ func main() { if *dest == "" { // Set a function as stream handler. - // This function is called when a peer initiate a connection and starts a stream with this peer. - // Only applicable on the receiving side. + // This function is called when a peer connects, and starts a stream with this protocol. + // Only applies on the receiving side. host.SetStreamHandler("/chat/1.0.0", handleStream) - fmt.Printf("Run './chat -d /ip4/127.0.0.1/tcp/%d/ipfs/%s' on another console.\n You can replace 127.0.0.1 with public IP as well.\n", *sourcePort, host.ID().Pretty()) + // Let's get the actual TCP port from our listen multiaddr, in case we're using 0 (default; random available port). + var port string + for _, la := range host.Network().ListenAddresses() { + if p, err := la.ValueForProtocol(multiaddr.P_TCP); err == nil { + port = p + break + } + } + + if port == "" { + panic("was not able to find actual local port") + } + + fmt.Printf("Run './chat -d /ip4/127.0.0.1/tcp/%v/p2p/%s' on another console.\n", port, host.ID().Pretty()) + fmt.Println("You can replace 127.0.0.1 with public IP as well.") fmt.Printf("\nWaiting for incoming connection\n\n") + // Hang forever <-make(chan struct{}) - } else { + fmt.Println("This node's multiaddresses:") + for _, la := range host.Addrs() { + fmt.Printf(" - %v\n", la) + } + fmt.Println() - // Add destination peer multiaddress in the peerstore. - // This will be used during connection and stream creation by libp2p. - peerID := addAddrToPeerstore(host, *dest) + // Turn the destination into a multiaddr. + maddr, err := multiaddr.NewMultiaddr(*dest) + if err != nil { + log.Fatalln(err) + } - fmt.Println("This node's multiaddress: ") - // IP will be 0.0.0.0 (listen on any interface) and port will be 0 (choose one for me). - // Although this node will not listen for any connection. It will just initiate a connect with - // one of its peer and use that stream to communicate. - fmt.Printf("%s/ipfs/%s\n", sourceMultiAddr, host.ID().Pretty()) + // Extract the peer ID from the multiaddr. + info, err := peerstore.InfoFromP2pAddr(maddr) + if err != nil { + log.Fatalln(err) + } - // Start a stream with peer with peer Id: 'peerId'. - // Multiaddress of the destination peer is fetched from the peerstore using 'peerId'. - s, err := host.NewStream(context.Background(), peerID, "/chat/1.0.0") + // Add the destination's peer multiaddress in the peerstore. + // This will be used during connection and stream creation by libp2p. + host.Peerstore().AddAddrs(info.ID, info.Addrs, peerstore.PermanentAddrTTL) + // Start a stream with the destination. + // Multiaddress of the destination peer is fetched from the peerstore using 'peerId'. + s, err := host.NewStream(context.Background(), info.ID, "/chat/1.0.0") if err != nil { panic(err) } @@ -216,6 +227,5 @@ func main() { // Hang forever. select {} - } } diff --git a/echo/main.go b/echo/main.go index 32d156b..2191edc 100644 --- a/echo/main.go +++ b/echo/main.go @@ -23,8 +23,8 @@ import ( ) // makeBasicHost creates a LibP2P host with a random peer ID listening on the -// given multiaddress. It will use secio if secio is true. -func makeBasicHost(listenPort int, secio bool, randseed int64) (host.Host, error) { +// given multiaddress. It won't encrypt the connection if insecure is true. +func makeBasicHost(listenPort int, insecure bool, randseed int64) (host.Host, error) { // If the seed is zero, use real cryptographic randomness. Otherwise, use a // deterministic randomness source to make generated keys stay the same @@ -46,9 +46,10 @@ func makeBasicHost(listenPort int, secio bool, randseed int64) (host.Host, error opts := []libp2p.Option{ libp2p.ListenAddrStrings(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", listenPort)), libp2p.Identity(priv), + libp2p.DisableRelay(), } - if !secio { + if insecure { opts = append(opts, libp2p.NoSecurity) } @@ -65,8 +66,8 @@ func makeBasicHost(listenPort int, secio bool, randseed int64) (host.Host, error addr := basicHost.Addrs()[0] fullAddr := addr.Encapsulate(hostAddr) log.Printf("I am %s\n", fullAddr) - if secio { - log.Printf("Now run \"./echo -l %d -d %s -secio\" on a different terminal\n", listenPort+1, fullAddr) + if insecure { + log.Printf("Now run \"./echo -l %d -d %s -insecure\" on a different terminal\n", listenPort+1, fullAddr) } else { log.Printf("Now run \"./echo -l %d -d %s\" on a different terminal\n", listenPort+1, fullAddr) } @@ -83,7 +84,7 @@ func main() { // Parse options from the command line listenF := flag.Int("l", 0, "wait for incoming connections") target := flag.String("d", "", "target peer to dial") - secio := flag.Bool("secio", false, "enable secio") + insecure := flag.Bool("insecure", false, "use an unencrypted connection") seed := flag.Int64("seed", 0, "set random seed for id generation") flag.Parse() @@ -92,7 +93,7 @@ func main() { } // Make a host that listens on the given multiaddress - ha, err := makeBasicHost(*listenF, *secio, *seed) + ha, err := makeBasicHost(*listenF, *insecure, *seed) if err != nil { log.Fatal(err) } diff --git a/multipro/README.md b/multipro/README.md index c60da8b..31e1df3 100644 --- a/multipro/README.md +++ b/multipro/README.md @@ -6,23 +6,6 @@ This example expects that you area already familiar with the [echo example](http ## Build -Install gx: -```sh -> go get -u github.com/whyrusleeping/gx - -``` - -Run GX from the root libp2p source dir: -```sh ->gx install -``` - -Build libp2p: -```sh -> make deps -> make -``` - Run from `multipro` directory ```sh diff --git a/multipro/echo.go b/multipro/echo.go index 9d317de..f6fa900 100644 --- a/multipro/echo.go +++ b/multipro/echo.go @@ -9,8 +9,8 @@ import ( inet "github.com/libp2p/go-libp2p-net" uuid "github.com/google/uuid" - "github.com/libp2p/go-libp2p-host" pb "github.com/libp2p/go-libp2p-examples/multipro/pb" + "github.com/libp2p/go-libp2p-host" protobufCodec "github.com/multiformats/go-multicodec/protobuf" ) @@ -71,7 +71,7 @@ func (e *EchoProtocol) onEchoRequest(s inet.Stream) { } // add the signature to the message - resp.MessageData.Sign = string(signature) + resp.MessageData.Sign = signature s, respErr := e.node.NewStream(context.Background(), s.Conn().RemotePeer(), echoResponse) if respErr != nil { @@ -136,7 +136,7 @@ func (e *EchoProtocol) Echo(host host.Host) bool { } // add the signature to the message - req.MessageData.Sign = string(signature) + req.MessageData.Sign = signature s, err := e.node.NewStream(context.Background(), host.ID(), echoRequest) if err != nil { diff --git a/multipro/node.go b/multipro/node.go index 5ea11e6..1d98aa2 100644 --- a/multipro/node.go +++ b/multipro/node.go @@ -7,10 +7,10 @@ import ( "github.com/gogo/protobuf/proto" crypto "github.com/libp2p/go-libp2p-crypto" + p2p "github.com/libp2p/go-libp2p-examples/multipro/pb" host "github.com/libp2p/go-libp2p-host" inet "github.com/libp2p/go-libp2p-net" peer "github.com/libp2p/go-libp2p-peer" - p2p "github.com/libp2p/go-libp2p-examples/multipro/pb" protobufCodec "github.com/multiformats/go-multicodec/protobuf" ) @@ -40,7 +40,7 @@ func (n *Node) authenticateMessage(message proto.Message, data *p2p.MessageData) // store a temp ref to signature and remove it from message data // sign is a string to allow easy reset to zero-value (empty string) sign := data.Sign - data.Sign = "" + data.Sign = nil // marshall data without the signature to protobufs3 binary format bin, err := proto.Marshal(message) diff --git a/multipro/pb/p2p.pb.go b/multipro/pb/p2p.pb.go index 091ef4a..2dc6a44 100644 --- a/multipro/pb/p2p.pb.go +++ b/multipro/pb/p2p.pb.go @@ -1,20 +1,6 @@ -// Code generated by protoc-gen-gogo. +// Code generated by protoc-gen-gogo. DO NOT EDIT. // source: p2p.proto -// DO NOT EDIT! -/* -Package protocols_p2p is a generated protocol buffer package. - -It is generated from these files: - p2p.proto - -It has these top-level messages: - MessageData - PingRequest - PingResponse - EchoRequest - EchoResponse -*/ package protocols_p2p import proto "github.com/gogo/protobuf/proto" @@ -26,32 +12,133 @@ var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + // designed to be shared between all app protocols type MessageData struct { // shared between all requests - ClientVersion string `protobuf:"bytes,1,opt,name=clientVersion,proto3" json:"clientVersion,omitempty"` - Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - Id string `protobuf:"bytes,3,opt,name=id,proto3" json:"id,omitempty"` - Gossip bool `protobuf:"varint,4,opt,name=gossip,proto3" json:"gossip,omitempty"` - NodeId string `protobuf:"bytes,5,opt,name=nodeId,proto3" json:"nodeId,omitempty"` - NodePubKey []byte `protobuf:"bytes,6,opt,name=nodePubKey,proto3" json:"nodePubKey,omitempty"` - Sign string `protobuf:"bytes,7,opt,name=sign,proto3" json:"sign,omitempty"` + ClientVersion string `protobuf:"bytes,1,opt,name=clientVersion,proto3" json:"clientVersion,omitempty"` + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Id string `protobuf:"bytes,3,opt,name=id,proto3" json:"id,omitempty"` + Gossip bool `protobuf:"varint,4,opt,name=gossip,proto3" json:"gossip,omitempty"` + NodeId string `protobuf:"bytes,5,opt,name=nodeId,proto3" json:"nodeId,omitempty"` + NodePubKey []byte `protobuf:"bytes,6,opt,name=nodePubKey,proto3" json:"nodePubKey,omitempty"` + Sign []byte `protobuf:"bytes,7,opt,name=sign,proto3" json:"sign,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *MessageData) Reset() { *m = MessageData{} } func (m *MessageData) String() string { return proto.CompactTextString(m) } func (*MessageData) ProtoMessage() {} +func (*MessageData) Descriptor() ([]byte, []int) { + return fileDescriptor_p2p_c8fd4e6dd1b6d221, []int{0} +} +func (m *MessageData) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_MessageData.Unmarshal(m, b) +} +func (m *MessageData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_MessageData.Marshal(b, m, deterministic) +} +func (dst *MessageData) XXX_Merge(src proto.Message) { + xxx_messageInfo_MessageData.Merge(dst, src) +} +func (m *MessageData) XXX_Size() int { + return xxx_messageInfo_MessageData.Size(m) +} +func (m *MessageData) XXX_DiscardUnknown() { + xxx_messageInfo_MessageData.DiscardUnknown(m) +} + +var xxx_messageInfo_MessageData proto.InternalMessageInfo + +func (m *MessageData) GetClientVersion() string { + if m != nil { + return m.ClientVersion + } + return "" +} + +func (m *MessageData) GetTimestamp() int64 { + if m != nil { + return m.Timestamp + } + return 0 +} + +func (m *MessageData) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *MessageData) GetGossip() bool { + if m != nil { + return m.Gossip + } + return false +} + +func (m *MessageData) GetNodeId() string { + if m != nil { + return m.NodeId + } + return "" +} + +func (m *MessageData) GetNodePubKey() []byte { + if m != nil { + return m.NodePubKey + } + return nil +} + +func (m *MessageData) GetSign() []byte { + if m != nil { + return m.Sign + } + return nil +} // a protocol define a set of reuqest and responses type PingRequest struct { MessageData *MessageData `protobuf:"bytes,1,opt,name=messageData" json:"messageData,omitempty"` // method specific data - Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *PingRequest) Reset() { *m = PingRequest{} } func (m *PingRequest) String() string { return proto.CompactTextString(m) } func (*PingRequest) ProtoMessage() {} +func (*PingRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_p2p_c8fd4e6dd1b6d221, []int{1} +} +func (m *PingRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PingRequest.Unmarshal(m, b) +} +func (m *PingRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PingRequest.Marshal(b, m, deterministic) +} +func (dst *PingRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_PingRequest.Merge(dst, src) +} +func (m *PingRequest) XXX_Size() int { + return xxx_messageInfo_PingRequest.Size(m) +} +func (m *PingRequest) XXX_DiscardUnknown() { + xxx_messageInfo_PingRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_PingRequest proto.InternalMessageInfo func (m *PingRequest) GetMessageData() *MessageData { if m != nil { @@ -60,15 +147,45 @@ func (m *PingRequest) GetMessageData() *MessageData { return nil } +func (m *PingRequest) GetMessage() string { + if m != nil { + return m.Message + } + return "" +} + type PingResponse struct { MessageData *MessageData `protobuf:"bytes,1,opt,name=messageData" json:"messageData,omitempty"` // response specific data - Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *PingResponse) Reset() { *m = PingResponse{} } func (m *PingResponse) String() string { return proto.CompactTextString(m) } func (*PingResponse) ProtoMessage() {} +func (*PingResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_p2p_c8fd4e6dd1b6d221, []int{2} +} +func (m *PingResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PingResponse.Unmarshal(m, b) +} +func (m *PingResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PingResponse.Marshal(b, m, deterministic) +} +func (dst *PingResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_PingResponse.Merge(dst, src) +} +func (m *PingResponse) XXX_Size() int { + return xxx_messageInfo_PingResponse.Size(m) +} +func (m *PingResponse) XXX_DiscardUnknown() { + xxx_messageInfo_PingResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_PingResponse proto.InternalMessageInfo func (m *PingResponse) GetMessageData() *MessageData { if m != nil { @@ -77,16 +194,46 @@ func (m *PingResponse) GetMessageData() *MessageData { return nil } +func (m *PingResponse) GetMessage() string { + if m != nil { + return m.Message + } + return "" +} + // a protocol define a set of reuqest and responses type EchoRequest struct { MessageData *MessageData `protobuf:"bytes,1,opt,name=messageData" json:"messageData,omitempty"` // method specific data - Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *EchoRequest) Reset() { *m = EchoRequest{} } func (m *EchoRequest) String() string { return proto.CompactTextString(m) } func (*EchoRequest) ProtoMessage() {} +func (*EchoRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_p2p_c8fd4e6dd1b6d221, []int{3} +} +func (m *EchoRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_EchoRequest.Unmarshal(m, b) +} +func (m *EchoRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_EchoRequest.Marshal(b, m, deterministic) +} +func (dst *EchoRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_EchoRequest.Merge(dst, src) +} +func (m *EchoRequest) XXX_Size() int { + return xxx_messageInfo_EchoRequest.Size(m) +} +func (m *EchoRequest) XXX_DiscardUnknown() { + xxx_messageInfo_EchoRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_EchoRequest proto.InternalMessageInfo func (m *EchoRequest) GetMessageData() *MessageData { if m != nil { @@ -95,15 +242,45 @@ func (m *EchoRequest) GetMessageData() *MessageData { return nil } +func (m *EchoRequest) GetMessage() string { + if m != nil { + return m.Message + } + return "" +} + type EchoResponse struct { MessageData *MessageData `protobuf:"bytes,1,opt,name=messageData" json:"messageData,omitempty"` // response specific data - Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *EchoResponse) Reset() { *m = EchoResponse{} } func (m *EchoResponse) String() string { return proto.CompactTextString(m) } func (*EchoResponse) ProtoMessage() {} +func (*EchoResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_p2p_c8fd4e6dd1b6d221, []int{4} +} +func (m *EchoResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_EchoResponse.Unmarshal(m, b) +} +func (m *EchoResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_EchoResponse.Marshal(b, m, deterministic) +} +func (dst *EchoResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_EchoResponse.Merge(dst, src) +} +func (m *EchoResponse) XXX_Size() int { + return xxx_messageInfo_EchoResponse.Size(m) +} +func (m *EchoResponse) XXX_DiscardUnknown() { + xxx_messageInfo_EchoResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_EchoResponse proto.InternalMessageInfo func (m *EchoResponse) GetMessageData() *MessageData { if m != nil { @@ -112,6 +289,13 @@ func (m *EchoResponse) GetMessageData() *MessageData { return nil } +func (m *EchoResponse) GetMessage() string { + if m != nil { + return m.Message + } + return "" +} + func init() { proto.RegisterType((*MessageData)(nil), "protocols.p2p.MessageData") proto.RegisterType((*PingRequest)(nil), "protocols.p2p.PingRequest") @@ -119,3 +303,26 @@ func init() { proto.RegisterType((*EchoRequest)(nil), "protocols.p2p.EchoRequest") proto.RegisterType((*EchoResponse)(nil), "protocols.p2p.EchoResponse") } + +func init() { proto.RegisterFile("p2p.proto", fileDescriptor_p2p_c8fd4e6dd1b6d221) } + +var fileDescriptor_p2p_c8fd4e6dd1b6d221 = []byte{ + // 261 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x8f, 0xb1, 0x4e, 0xc3, 0x30, + 0x10, 0x86, 0xe5, 0xb6, 0xa4, 0xe4, 0xdc, 0x32, 0xdc, 0x80, 0x2c, 0x84, 0x50, 0x14, 0x31, 0x64, + 0xca, 0x10, 0x56, 0x46, 0x18, 0x10, 0x42, 0xaa, 0x3c, 0xb0, 0xa7, 0xc9, 0x11, 0x2c, 0x35, 0xb6, + 0xe9, 0xb9, 0x03, 0x0f, 0xc8, 0x7b, 0xa1, 0xba, 0x41, 0x4d, 0x1f, 0xa0, 0x4c, 0xbe, 0xff, 0xf3, + 0xd9, 0xbf, 0x3e, 0x48, 0x7d, 0xe5, 0x4b, 0xbf, 0x75, 0xc1, 0xe1, 0x32, 0x1e, 0x8d, 0xdb, 0x70, + 0xe9, 0x2b, 0x9f, 0xff, 0x08, 0x90, 0x6f, 0xc4, 0x5c, 0x77, 0xf4, 0x54, 0x87, 0x1a, 0xef, 0x61, + 0xd9, 0x6c, 0x0c, 0xd9, 0xf0, 0x4e, 0x5b, 0x36, 0xce, 0x2a, 0x91, 0x89, 0x22, 0xd5, 0xa7, 0x10, + 0x6f, 0x21, 0x0d, 0xa6, 0x27, 0x0e, 0x75, 0xef, 0xd5, 0x24, 0x13, 0xc5, 0x54, 0x1f, 0x01, 0x5e, + 0xc1, 0xc4, 0xb4, 0x6a, 0x1a, 0x1f, 0x4e, 0x4c, 0x8b, 0xd7, 0x90, 0x74, 0x8e, 0xd9, 0x78, 0x35, + 0xcb, 0x44, 0x71, 0xa9, 0x87, 0xb4, 0xe7, 0xd6, 0xb5, 0xf4, 0xd2, 0xaa, 0x8b, 0xb8, 0x3b, 0x24, + 0xbc, 0x03, 0xd8, 0x4f, 0xab, 0xdd, 0xfa, 0x95, 0xbe, 0x55, 0x92, 0x89, 0x62, 0xa1, 0x47, 0x04, + 0x11, 0x66, 0x6c, 0x3a, 0xab, 0xe6, 0xf1, 0x26, 0xce, 0x39, 0x81, 0x5c, 0x19, 0xdb, 0x69, 0xfa, + 0xda, 0x11, 0x07, 0x7c, 0x04, 0xd9, 0x1f, 0xad, 0xa2, 0x84, 0xac, 0x6e, 0xca, 0x13, 0xf7, 0x72, + 0xe4, 0xad, 0xc7, 0xeb, 0xa8, 0x60, 0x3e, 0xc4, 0x28, 0x97, 0xea, 0xbf, 0x98, 0x7f, 0xc0, 0xe2, + 0x50, 0xc3, 0xde, 0x59, 0xa6, 0xb3, 0xf5, 0x10, 0xc8, 0xe7, 0xe6, 0xd3, 0xfd, 0x83, 0xce, 0xa1, + 0xe6, 0xbc, 0x3a, 0xeb, 0x24, 0xfe, 0xf0, 0xf0, 0x1b, 0x00, 0x00, 0xff, 0xff, 0xf4, 0x47, 0x02, + 0x5e, 0x88, 0x02, 0x00, 0x00, +} diff --git a/multipro/pb/p2p.proto b/multipro/pb/p2p.proto index 53e6521..5488f2a 100644 --- a/multipro/pb/p2p.proto +++ b/multipro/pb/p2p.proto @@ -9,9 +9,9 @@ message MessageData { int64 timestamp = 2; // unix time string id = 3; // allows requesters to use request data when processing a response bool gossip = 4; // true to have receiver peer gossip the message to neighbors - string nodeId = 5; // id of node that created the message (not the peer that may have sent it). =base58(mh(sha256(nodePubKey))) + string nodeId = 5; // id of node that created the message (not the peer that may have sent it). =base58(multihash(nodePubKey)) bytes nodePubKey = 6; // Authoring node Secp256k1 public key (32bytes) - protobufs serielized - string sign = 7; // signature of message data + method specific data by message authoring node. format: string([]bytes) + bytes sign = 7; // signature of message data + method specific data by message authoring node. } //// ping protocol diff --git a/multipro/ping.go b/multipro/ping.go index 0992ad0..10b1760 100644 --- a/multipro/ping.go +++ b/multipro/ping.go @@ -7,9 +7,9 @@ import ( "log" uuid "github.com/google/uuid" + p2p "github.com/libp2p/go-libp2p-examples/multipro/pb" "github.com/libp2p/go-libp2p-host" inet "github.com/libp2p/go-libp2p-net" - p2p "github.com/libp2p/go-libp2p-examples/multipro/pb" protobufCodec "github.com/multiformats/go-multicodec/protobuf" ) @@ -66,7 +66,7 @@ func (p *PingProtocol) onPingRequest(s inet.Stream) { } // add the signature to the message - resp.MessageData.Sign = string(signature) + resp.MessageData.Sign = signature // send the response s, respErr := p.node.NewStream(context.Background(), s.Conn().RemotePeer(), pingResponse) @@ -127,7 +127,7 @@ func (p *PingProtocol) Ping(host host.Host) bool { } // add the signature to the message - req.MessageData.Sign = string(signature) + req.MessageData.Sign = signature s, err := p.node.NewStream(context.Background(), host.ID(), pingRequest) if err != nil {