From e6d7db4ef8c2015ec43e0f4557f430f4badb7741 Mon Sep 17 00:00:00 2001 From: backkem Date: Mon, 25 Feb 2019 20:10:59 +0100 Subject: [PATCH] Add libp2p example --- examples/README.md | 69 +-------- examples/libp2p-echo/README.md | 4 + examples/libp2p-echo/main.go | 189 +++++++++++++++++++++++++ examples/standalone/README.md | 70 +++++++++ examples/{ => standalone}/index.js | 0 examples/{ => standalone}/main.go | 0 examples/{ => standalone}/package.json | 0 7 files changed, 266 insertions(+), 66 deletions(-) create mode 100644 examples/libp2p-echo/README.md create mode 100644 examples/libp2p-echo/main.go create mode 100644 examples/standalone/README.md rename examples/{ => standalone}/index.js (100%) rename examples/{ => standalone}/main.go (100%) rename examples/{ => standalone}/package.json (100%) diff --git a/examples/README.md b/examples/README.md index 61d0332..2f23de1 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,69 +1,6 @@ examples === -## Go -This folder contains an example go app that connects using go-libp2p-webrtc-direct. It can be used as follows: - -### Install dependencies -**TODO**: Check the root readme - -### Listener -```sh -go run main.go -listen -``` -*Output* -``` -[listener] Listening -[listener] Got connection -[listener] Got stream -[listener] Received: -hey, how is it going. I am the dialer -Failed to accept data channel: The association is closed -``` -The last line is harmless warning printed by the pions/webrtc library. -### Dialer -```sh -go run main.go -``` -*Output* -``` -Warning: Certificate not checked -[dialer] Opened connection -[dialer] Opened stream -Failed to push SCTP packet: Failed sending reply: dtls: conn is closed -Warning: mux: no endpoint for packet starting with 23 -Failed to push SCTP packet: Failed sending reply: dtls: conn is closed -Warning: mux: no endpoint for packet starting with 21 -Failed to accept data channel: The association is closed -``` -The warnings printed by the pions/webrtc library are harmless. - -## Javascript -The equivalent javascript example is also provided. It can be used as follows: - -### Install dependencies -```sh -npm install -``` - -### Listener -```sh -node index.js --listen -``` -*Output* -``` -[listener] Listening -[listener] Got connection -[listener] Got stream -[listener] Received: -hey, how is it going. I am the dialer -``` -### Dialer -```sh -node index.js -``` -*Output* -``` -[dialer] Opened connection -[dialer] Opened stream -``` \ No newline at end of file +This folder contains the following examples of using the webrtc-direct transport: +1. [standalone](./standalone): This example shows how you can use the go-libp2p-webrtc-direct transport on its own. +1. [libp2p-echo](./libp2p-echo): This example is a variant of the [libp2p echo](https://github.com/libp2p/go-libp2p-examples/blob/master/echo) example. It shows how the webrtc-direct transport can be plugged into libp2p. \ No newline at end of file diff --git a/examples/libp2p-echo/README.md b/examples/libp2p-echo/README.md new file mode 100644 index 0000000..c279fd6 --- /dev/null +++ b/examples/libp2p-echo/README.md @@ -0,0 +1,4 @@ +libp2p-echo +=== + +This example is a variant of the [libp2p echo](https://github.com/libp2p/go-libp2p-examples/blob/master/echo) example. It shows how the webrtc-direct transport can be plugged into libp2p. \ No newline at end of file diff --git a/examples/libp2p-echo/main.go b/examples/libp2p-echo/main.go new file mode 100644 index 0000000..ea6bbd7 --- /dev/null +++ b/examples/libp2p-echo/main.go @@ -0,0 +1,189 @@ +package main + +import ( + "bufio" + "context" + "crypto/rand" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + mrand "math/rand" + + golog "github.com/ipfs/go-log" + libp2p "github.com/libp2p/go-libp2p" + 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" + ma "github.com/multiformats/go-multiaddr" + gologging "github.com/whyrusleeping/go-logging" + + direct "github.com/libp2p/go-libp2p-webrtc-direct" + "github.com/pions/webrtc" + mplex "github.com/whyrusleeping/go-smux-multiplex" +) + +// makeBasicHost creates a LibP2P host with a random peer ID listening on the +// 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 + // across multiple runs + var r io.Reader + if randseed == 0 { + r = rand.Reader + } else { + r = mrand.New(mrand.NewSource(randseed)) + } + + // Generate a key pair for this host. We will use it at least + // to obtain a valid host ID. + priv, _, err := crypto.GenerateKeyPairWithReader(crypto.RSA, 2048, r) + if err != nil { + return nil, err + } + + transport := direct.NewTransport( + webrtc.Configuration{}, + new(mplex.Transport), + ) + + opts := []libp2p.Option{ + libp2p.ListenAddrStrings(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d/http/p2p-webrtc-direct", listenPort)), + libp2p.Identity(priv), + libp2p.DisableRelay(), + libp2p.Transport(transport), + } + + if insecure { + opts = append(opts, libp2p.NoSecurity) + } + + basicHost, err := libp2p.New(context.Background(), opts...) + if err != nil { + return nil, err + } + + // 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: + addr := basicHost.Addrs()[0] + fullAddr := addr.Encapsulate(hostAddr) + log.Printf("I am %s\n", fullAddr) + if insecure { + log.Printf("Now run \"./libp2p-echo -l %d -d %s -insecure\" on a different terminal\n", listenPort+1, fullAddr) + } else { + log.Printf("Now run \"./libp2p-echo -l %d -d %s\" on a different terminal\n", listenPort+1, fullAddr) + } + + return basicHost, nil +} + +func main() { + // 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") + insecure := flag.Bool("insecure", false, "use an unencrypted connection") + seed := flag.Int64("seed", 0, "set random seed for id generation") + flag.Parse() + + if *listenF == 0 { + log.Fatal("Please provide a port to bind on with -l") + } + + // Make a host that listens on the given multiaddress + ha, err := makeBasicHost(*listenF, *insecure, *seed) + if err != nil { + log.Fatal(err) + } + + // 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!") + if err := doEcho(s); err != nil { + log.Println(err) + s.Reset() + } else { + s.Close() + } + }) + + if *target == "" { + log.Println("listening for connections") + select {} // hang forever + } + /**** 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) + } + + pid, err := ipfsaddr.ValueForProtocol(ma.P_IPFS) + if err != nil { + log.Fatalln(err) + } + + peerid, err := peer.IDB58Decode(pid) + 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 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, pstore.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 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!\n")) + if err != nil { + log.Fatalln(err) + } + + out, err := ioutil.ReadAll(s) + if err != nil { + log.Fatalln(err) + } + + log.Printf("read reply: %q\n", out) +} + +// doEcho reads a line of data a stream and writes it back +func doEcho(s net.Stream) error { + buf := bufio.NewReader(s) + str, err := buf.ReadString('\n') + if err != nil { + return err + } + + log.Printf("read: %s\n", str) + _, err = s.Write([]byte(str)) + return err +} diff --git a/examples/standalone/README.md b/examples/standalone/README.md new file mode 100644 index 0000000..5897fb4 --- /dev/null +++ b/examples/standalone/README.md @@ -0,0 +1,70 @@ +standalone +=== + +This example shows the transport being used on its own. It also shows off compatibility with [js-libp2p-webrtc-direct](https://github.com/libp2p/js-libp2p-webrtc-direct). + +## Go + +### Install dependencies +**TODO**: Check the root readme + +### Listener +```sh +go run main.go -listen +``` +*Output* +``` +[listener] Listening +[listener] Got connection +[listener] Got stream +[listener] Received: +hey, how is it going. I am the dialer +Failed to accept data channel: The association is closed +``` +The last line is harmless warning printed by the pions/webrtc library. +### Dialer +```sh +go run main.go +``` +*Output* +``` +Warning: Certificate not checked +[dialer] Opened connection +[dialer] Opened stream +Failed to push SCTP packet: Failed sending reply: dtls: conn is closed +Warning: mux: no endpoint for packet starting with 23 +Failed to push SCTP packet: Failed sending reply: dtls: conn is closed +Warning: mux: no endpoint for packet starting with 21 +Failed to accept data channel: The association is closed +``` +The warnings printed by the pions/webrtc library are harmless. + +## Javascript +The equivalent javascript example is also provided. It can be used as follows: + +### Install dependencies +```sh +npm install +``` + +### Listener +```sh +node index.js --listen +``` +*Output* +``` +[listener] Listening +[listener] Got connection +[listener] Got stream +[listener] Received: +hey, how is it going. I am the dialer +``` +### Dialer +```sh +node index.js +``` +*Output* +``` +[dialer] Opened connection +[dialer] Opened stream +``` \ No newline at end of file diff --git a/examples/index.js b/examples/standalone/index.js similarity index 100% rename from examples/index.js rename to examples/standalone/index.js diff --git a/examples/main.go b/examples/standalone/main.go similarity index 100% rename from examples/main.go rename to examples/standalone/main.go diff --git a/examples/package.json b/examples/standalone/package.json similarity index 100% rename from examples/package.json rename to examples/standalone/package.json