diff --git a/examples/echo/README.md b/examples/echo/README.md index f58b9cace794d015ce9b4a31034e1214e79621b6..a83927864ae458a10d05afa9ac6b1c8330c90ec5 100644 --- a/examples/echo/README.md +++ b/examples/echo/README.md @@ -25,18 +25,19 @@ From `go-libp2p` base folder: ## Usage ``` -> ./echo -l 1235 -2016/11/10 10:45:37 I am /ip4/127.0.0.1/tcp/1235/ipfs/QmNtX1cvrm2K6mQmMEaMxAuB4rTexhd87vpYVot4sEZzxc -2016/11/10 10:45:37 listening for connections +> ./echo -l 10000 +2017/03/15 14:11:32 I am /ip4/127.0.0.1/tcp/10000/ipfs/QmYo41GybvrXk8y8Xnm1P7pfA4YEXCpfnLyzgRPnNbG35e +2017/03/15 14:11:32 Now run "./echo -l 10001 -d /ip4/127.0.0.1/tcp/10000/ipfs/QmYo41GybvrXk8y8Xnm1P7pfA4YEXCpfnLyzgRPnNbG35e -secio" on a different terminal +2017/03/15 14:11:32 listening for connections ``` The listener libp2p host will print its `Multiaddress`, which indicates how it -can be reached (ip4+tcp) and its randomly generated ID (`QmNtX1cv...`) +can be reached (ip4+tcp) and its randomly generated ID (`QmYo41Gyb...`) Now, launch another node that talks to the listener: ``` -> ./echo -d /ip4/127.0.0.1/tcp/1235/ipfs/QmNtX1cvrm2K6mQmMEaMxAuB4rTexhd87vpYVot4sEZzxc -l 1236 +> ./echo -l 10001 -d /ip4/127.0.0.1/tcp/10000/ipfs/QmYo41GybvrXk8y8Xnm1P7pfA4YEXCpfnLyzgRPnNbG35e ``` The new node with send the message `"Hello, world!"` to the @@ -61,9 +62,9 @@ In order to create the swarm (and a `basichost`), the example needs: * An [ipfs-procotol ID](https://godoc.org/github.com/libp2p/go-libp2p-peer#ID) like `QmNtX1cvrm2K6mQmMEaMxAuB4rTexhd87vpYVot4sEZzxc`. The example - autogenerates this on every run. An optional key-pair to secure - communications can be added to it. The example autogenerates them when - using `-secio`. + autogenerates a key pair on every run and uses an ID extracted from the + public key (the hash of the public key). When using `-secio`, it uses + the key pair to encrypt communications. * A [Multiaddress](https://godoc.org/github.com/multiformats/go-multiaddr), which indicates how to reach this peer. There can be several of them (using different protocols or locations for example). Example: diff --git a/examples/echo/main.go b/examples/echo/main.go index 022a1a446ae72028d0f19d0fb3cbc2e6ea7643e2..6c198dc7926ae9ff81e9042e8fc99cddf037a756 100644 --- a/examples/echo/main.go +++ b/examples/echo/main.go @@ -1,88 +1,109 @@ package main import ( + "bufio" "context" "flag" "fmt" "io/ioutil" "log" - "math/rand" - "strings" - "time" golog "github.com/ipfs/go-log" + crypto "github.com/libp2p/go-libp2p-crypto" host "github.com/libp2p/go-libp2p-host" net "github.com/libp2p/go-libp2p-net" peer "github.com/libp2p/go-libp2p-peer" pstore "github.com/libp2p/go-libp2p-peerstore" swarm "github.com/libp2p/go-libp2p-swarm" - bhost "github.com/libp2p/go-libp2p/p2p/host/basic" - testutil "github.com/libp2p/go-testutil" ma "github.com/multiformats/go-multiaddr" gologging "github.com/whyrusleeping/go-logging" + + bhost "github.com/libp2p/go-libp2p/p2p/host/basic" ) -// create a 'Host' with a random peer to listen on the given address -func makeBasicHost(listen string, secio bool) (host.Host, error) { - addr, err := ma.NewMultiaddr(listen) +// 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) (host.Host, error) { + // Generate a key pair for this host. We will use it at least + // to obtain a valid host ID. + priv, pub, err := crypto.GenerateKeyPair(crypto.RSA, 2048) + if err != nil { + return nil, err + } + + // Obtain Peer ID from public key + pid, err := peer.IDFromPublicKey(pub) if err != nil { return nil, err } + // Create a multiaddress + addr, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", listenPort)) + + if err != nil { + return nil, err + } + + // Create a peerstore ps := pstore.NewPeerstore() - var pid peer.ID + // If using secio, we add the keys to the peerstore + // for this peer ID. if secio { - ident, err := testutil.RandIdentity() - if err != nil { - return nil, err - } - - ident.PrivateKey() - ps.AddPrivKey(ident.ID(), ident.PrivateKey()) - ps.AddPubKey(ident.ID(), ident.PublicKey()) - pid = ident.ID() - } else { - fakepid, err := testutil.RandPeerID() - if err != nil { - return nil, err - } - pid = fakepid + ps.AddPrivKey(pid, priv) + ps.AddPubKey(pid, pub) } - ctx := context.Background() + // Create swarm (implements libP2P Network) + netwrk, err := swarm.NewNetwork( + context.Background(), + []ma.Multiaddr{addr}, + pid, + ps, + nil) - // create a new swarm to be used by the service host - netw, err := swarm.NewNetwork(ctx, []ma.Multiaddr{addr}, pid, ps, nil) - if err != nil { - return nil, err + basicHost := bhost.New(netwrk) + + // Build host multiaddress + hostAddr, _ := ma.NewMultiaddr(fmt.Sprintf("/ipfs/%s", basicHost.ID().Pretty())) + + // Now we can build a full multiaddress to reach this host + // by encapsulating both addresses: + 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) + } else { + log.Printf("Now run \"./echo -l %d -d %s\" on a different terminal\n", listenPort+1, fullAddr) } - log.Printf("I am %s/ipfs/%s\n", addr, pid.Pretty()) - return bhost.New(netw), nil + return basicHost, nil } func main() { - rand.Seed(time.Now().UnixNano()) + // LibP2P code uses golog to log messages. They log with different + // string IDs (i.e. "swarm"). We can control the verbosity level for + // all loggers with: golog.SetAllLoggers(gologging.INFO) // Change to DEBUG for extra info + + // 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") - flag.Parse() if *listenF == 0 { log.Fatal("Please provide a port to bind on with -l") } - listenaddr := fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", *listenF) - - ha, err := makeBasicHost(listenaddr, *secio) + // Make a host that listens on the given multiaddress + ha, err := makeBasicHost(*listenF, *secio) if err != nil { log.Fatal(err) } - // Set a stream handler on host A + // Set a stream handler on host A. /echo/1.0.0 is + // a user-defined protocol name. ha.SetStreamHandler("/echo/1.0.0", func(s net.Stream) { log.Println("Got a new stream!") defer s.Close() @@ -93,8 +114,10 @@ func main() { log.Println("listening for connections") select {} // hang forever } - // This is where the listener code ends + /**** This is where the listener code ends ****/ + // The following code extracts target's the peer ID from the + // given multiaddress ipfsaddr, err := ma.NewMultiaddr(*target) if err != nil { log.Fatalln(err) @@ -110,26 +133,26 @@ func main() { log.Fatalln(err) } - tptaddr := strings.Split(ipfsaddr.String(), "/ipfs/")[0] - // This creates a MA with the "/ip4/ipaddr/tcp/port" part of the target - tptmaddr, err := ma.NewMultiaddr(tptaddr) - if err != nil { - log.Fatalln(err) - } + // Decapsulate the /ipfs/ part from the target + // /ip4//ipfs/ becomes /ip4/ + targetPeerAddr, _ := ma.NewMultiaddr( + fmt.Sprintf("/ipfs/%s", peer.IDB58Encode(peerid))) + targetAddr := ipfsaddr.Decapsulate(targetPeerAddr) - // We need to add the target to our peerstore, so we know how we can - // contact it - ha.Peerstore().AddAddr(peerid, tptmaddr, pstore.PermanentAddrTTL) + // We have a peer ID and a targetAddr so we add it to the peerstore + // so LibP2P knows how to contact it + ha.Peerstore().AddAddr(peerid, targetAddr, peerstore.PermanentAddrTTL) log.Println("opening stream") // make a new stream from host B to host A - // it should be handled on host A by the handler we set above + // it should be handled on host A by the handler we set above because + // we use the same /echo/1.0.0 protocol s, err := ha.NewStream(context.Background(), peerid, "/echo/1.0.0") if err != nil { log.Fatalln(err) } - _, err = s.Write([]byte("Hello, world!")) + _, err = s.Write([]byte("Hello, world!\n")) if err != nil { log.Fatalln(err) } @@ -142,18 +165,17 @@ func main() { log.Printf("read reply: %q\n", out) } -// doEcho reads some data from a stream, writes it back and closes the -// stream. +// doEcho reads a line of data a stream and writes it back func doEcho(s net.Stream) { - buf := make([]byte, 1024) - n, err := s.Read(buf) + buf := bufio.NewReader(s) + str, err := buf.ReadString('\n') if err != nil { log.Println(err) return } - log.Printf("read request: %q\n", buf[:n]) - _, err = s.Write(buf[:n]) + log.Printf("read: %s\n", str) + _, err = s.Write([]byte(str)) if err != nil { log.Println(err) return