diff --git a/.travis.yml b/.travis.yml index 7be88a9369eaecee3f0fe146f40a348c8660e5df..e1be0bfa30b7a22f2ff794a768d60e97fa96a242 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ go: - 1.9.x install: - - make deps-protocol-muxing + - make deps script: - bash <(curl -s https://raw.githubusercontent.com/ipfs/ci-helpers/master/travis-ci/run-standard-tests.sh) diff --git a/Makefile b/Makefile index 075e51f7f556d12a60405264c094b7588e872093..f9dabde9b2004dbe25ff357baf9273352a18c700 100644 --- a/Makefile +++ b/Makefile @@ -2,10 +2,6 @@ gx: go get github.com/whyrusleeping/gx go get github.com/whyrusleeping/gx-go -deps-protocol-muxing: deps - go get -u github.com/multiformats/go-multicodec - go get -u github.com/libp2p/go-msgio - deps: gx gx --verbose install --global gx-go rewrite diff --git a/README.md b/README.md index 7aa6f366bddebd7a3279210f4bffc747361537ea..044e9290edd3b3b7a82e58be125c1c0bde9b2d77 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ There is currently only one bundle of `go-libp2p`, this package. This bundle is ### Examples -Examples can be found on the [examples folder](examples). +Examples can be found in the [examples repo](https://github.com/libp2p/go-libp2p-examples). ## Development diff --git a/examples/README.md b/examples/README.md index a47661a6351a01557a528cd798790c9e789fc6e8..c5e2e943186240825cc2969b5e0785cb8b450efb 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,15 +1,3 @@ +# go-libp2p Examples -# `go-libp2p` examples and tutorials - -In this folder, you can find a variety of examples to help you get started in using go-libp2p. Every example as a specific purpose and some of each incorporate a full tutorial that you can follow through, helping you expand your knowledge about libp2p and p2p networks in general. - -Let us know if you find any issue or if you want to contribute and add a new tutorial, feel welcome to submit a pr, thank you! - -## Examples and Tutorials - -- [The libp2p 'host'](./libp2p-host) -- [Building an http proxy with libp2p](./http-proxy) -- [Protocol Multiplexing with multicodecs](./protocol-multiplexing-with-multicodecs) -- [An echo host](./echo) -- [Multicodecs with protobufs](./multipro) -- [P2P chat application](./chat) \ No newline at end of file +The go-libp2p examples have moved to [go-libp2p-examples](https://github.com/libp2p/go-libp2p-examples). diff --git a/examples/chat/README.md b/examples/chat/README.md deleted file mode 100644 index da01591db282131e8a1e1aee0324694a9fd41253..0000000000000000000000000000000000000000 --- a/examples/chat/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# p2p chat app with libp2p - -This program demonstrates a simple p2p chat application. It can work between two peers if -1. Both have private IP address (same network). -2. At least one of them has a public IP address. - -Assume if 'A' and 'B' are on different networks host 'A' may or may not have a public IP address but host 'B' has one. - -Usage: Run `./chat -sp ` on host 'B' where can be any port number. Now run `./chat -d ` on node 'A' [`` is multiaddress of host 'B' which can be obtained from host 'B' console]. - -## Build - -To build the example, first run `make deps` in the root directory. - -``` -> make deps -> go build ./examples/chat -``` - -## Usage - -On node 'B' - -``` -> ./chat -sp 3001 -Run ./chat -d /ip4/127.0.0.1/tcp/3001/ipfs/QmdXGaeGiVA745XorV1jr11RHxB9z4fqykm6xCUPX1aTJo - -2018/02/27 01:21:32 Got a new stream! -> hi (received messages in green colour) -> hello (sent messages in white colour) -> no -``` - -On node 'A'. Replace 127.0.0.1 with if node 'B' has one. - -``` -> ./chat -d /ip4/127.0.0.1/tcp/3001/ipfs/QmdXGaeGiVA745XorV1jr11RHxB9z4fqykm6xCUPX1aTJo -Run ./chat -d /ip4/127.0.0.1/tcp/3001/ipfs/QmdXGaeGiVA745XorV1jr11RHxB9z4fqykm6xCUPX1aTJo - -This node's multiaddress: -/ip4/0.0.0.0/tcp/0/ipfs/QmWVx9NwsgaVWMRHNCpesq1WQAw2T3JurjGDNeVNWifPS7 -> hi -> hello -``` - -**NOTE: debug mode is enabled by default, debug mode will always generate same node id (on each node) on every execution. Disable debug using `--debug false` flag while running your executable.** - -## Authors -1. Abhishek Upperwal \ No newline at end of file diff --git a/examples/chat/chat.go b/examples/chat/chat.go deleted file mode 100644 index 942cb1e32ebb60ce71e71bbd6feb363a0a6fe9e2..0000000000000000000000000000000000000000 --- a/examples/chat/chat.go +++ /dev/null @@ -1,221 +0,0 @@ -/* -* -* 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 - -import ( - "bufio" - "context" - "crypto/rand" - "flag" - "fmt" - "io" - "log" - mrand "math/rand" - "os" - - "github.com/libp2p/go-libp2p" - - "github.com/libp2p/go-libp2p-crypto" - "github.com/libp2p/go-libp2p-host" - "github.com/libp2p/go-libp2p-net" - "github.com/libp2p/go-libp2p-peer" - "github.com/libp2p/go-libp2p-peerstore" - "github.com/multiformats/go-multiaddr" -) - -/* -* addAddrToPeerstore parses a peer multiaddress and adds -* it to the given host's peerstore, so it knows how to -* contact it. It returns the peer ID of the remote peer. -* @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) - 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, _ := 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 -} - -func handleStream(s net.Stream) { - log.Println("Got a new stream!") - - // Create a buffer stream for non blocking read and write. - rw := bufio.NewReadWriter(bufio.NewReader(s), bufio.NewWriter(s)) - - go readData(rw) - go writeData(rw) - - // stream 's' will stay open until you close it (or the other side closes it). -} -func readData(rw *bufio.ReadWriter) { - for { - str, _ := rw.ReadString('\n') - - if str == "" { - return - } - if str != "\n" { - // Green console colour: \x1b[32m - // Reset console colour: \x1b[0m - fmt.Printf("\x1b[32m%s\x1b[0m> ", str) - } - - } -} - -func writeData(rw *bufio.ReadWriter) { - stdReader := bufio.NewReader(os.Stdin) - - for { - fmt.Print("> ") - sendData, err := stdReader.ReadString('\n') - - if err != nil { - panic(err) - } - - rw.WriteString(fmt.Sprintf("%s\n", sendData)) - rw.Flush() - } - -} - -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.") - - 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") - - os.Exit(0) - } - - // If debug is enabled used constant random source else cryptographic randomness. - 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. - r = mrand.New(mrand.NewSource(int64(*sourcePort))) - } else { - r = rand.Reader - } - - // 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 - sourceMultiAddr, _ := multiaddr.NewMultiaddr(fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", *sourcePort)) - - // libp2p.New constructs a new libp2p Host. - // Other options can be added here. - host, err := libp2p.New( - context.Background(), - libp2p.ListenAddrs(sourceMultiAddr), - libp2p.Identity(prvKey), - ) - - if err != nil { - panic(err) - } - - 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. - 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()) - fmt.Printf("\nWaiting for incoming connection\n\n") - // Hang forever - <-make(chan struct{}) - - } else { - - // Add destination peer multiaddress in the peerstore. - // This will be used during connection and stream creation by libp2p. - peerID := addAddrToPeerstore(host, *dest) - - 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()) - - // 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") - - if err != nil { - panic(err) - } - - // Create a buffered stream so that read and writes are non blocking. - rw := bufio.NewReadWriter(bufio.NewReader(s), bufio.NewWriter(s)) - - // Create a thread to read and write data. - go writeData(rw) - go readData(rw) - - // Hang forever. - select {} - - } -} diff --git a/examples/echo/README.md b/examples/echo/README.md deleted file mode 100644 index 2b0dc24efa0003efeb94b63c7acb3a35b421b5e1..0000000000000000000000000000000000000000 --- a/examples/echo/README.md +++ /dev/null @@ -1,52 +0,0 @@ -# Echo client/server with libp2p - -This is an example that quickly shows how to use the `go-libp2p` stack, including Host/Basichost, Network/Swarm, Streams, Peerstores and Multiaddresses. - -This example can be started in either listen mode, or dial mode. - -In listen mode, it will sit and wait for incoming connections on the `/echo/1.0.0` protocol. Whenever it receives a stream, it will write the message `"Hello, world!"` over the stream and close it. - -In dial mode, the node will start up, connect to the given address, open a stream to the target peer, and read a message on the protocol `/echo/1.0.0`. - -## Build - -From `go-libp2p` base folder: - -``` -> make deps -> go build ./examples/echo -``` - -## Usage - -``` -> ./echo -secio -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 (`QmYo41Gyb...`) - -Now, launch another node that talks to the listener: - -``` -> ./echo -secio -l 10001 -d /ip4/127.0.0.1/tcp/10000/ipfs/QmYo41GybvrXk8y8Xnm1P7pfA4YEXCpfnLyzgRPnNbG35e -``` - -The new node with send the message `"Hello, world!"` to the listener, which will in turn echo it over the stream and close it. The listener logs the message, and the sender logs the response. - -## Details - -The `makeBasicHost()` function creates a [go-libp2p-basichost](https://godoc.org/github.com/libp2p/go-libp2p/p2p/host/basic) object. `basichost` objects wrap [go-libp2 swarms](https://godoc.org/github.com/libp2p/go-libp2p-swarm#Swarm) and should be used preferentially. A [go-libp2p-swarm Network](https://godoc.org/github.com/libp2p/go-libp2p-swarm#Network) is a `swarm` which complies to the [go-libp2p-net Network interface](https://godoc.org/github.com/libp2p/go-libp2p-net#Network) and takes care of maintaining streams, connections, multiplexing different protocols on them, handling incoming connections etc. - -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 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 (otherwise, it leaves the connections unencrypted). -- 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: `/ip4/127.0.0.1/tcp/1234`. -- A [go-libp2p-peerstore](https://godoc.org/github.com/libp2p/go-libp2p-peerstore), which is used as a address book which matches node IDs to the multiaddresses through which they can be contacted. This peerstore gets autopopulated when manually opening a connection (with [`Connect()`](https://godoc.org/github.com/libp2p/go-libp2p/p2p/host/basic#BasicHost.Connect). Alternatively, we can manually [`AddAddr()`](https://godoc.org/github.com/libp2p/go-libp2p-peerstore#AddrManager.AddAddr) as in the example. - -A `basichost` can now open streams (bi-directional channel between to peers) using [NewStream](https://godoc.org/github.com/libp2p/go-libp2p/p2p/host/basic#BasicHost.NewStream) and use them to send and receive data tagged with a `Protocol.ID` (a string). The host can also listen for incoming connections for a given -`Protocol` with [`SetStreamHandle()`](https://godoc.org/github.com/libp2p/go-libp2p/p2p/host/basic#BasicHost.SetStreamHandler). - -The example makes use of all of this to enable communication between a listener and a sender using protocol `/echo/1.0.0` (which could be any other thing). diff --git a/examples/echo/main.go b/examples/echo/main.go deleted file mode 100644 index 32d156b73eba766a103240c118dc5f78714b86a9..0000000000000000000000000000000000000000 --- a/examples/echo/main.go +++ /dev/null @@ -1,178 +0,0 @@ -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" -) - -// 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) { - - // 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 - } - - opts := []libp2p.Option{ - libp2p.ListenAddrStrings(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", listenPort)), - libp2p.Identity(priv), - } - - if !secio { - 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 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) - } - - 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") - secio := flag.Bool("secio", false, "enable secio") - 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, *secio, *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/http-proxy/README.md b/examples/http-proxy/README.md deleted file mode 100644 index 063cefc925c04befead6a3ed3aa232f282c4b40f..0000000000000000000000000000000000000000 --- a/examples/http-proxy/README.md +++ /dev/null @@ -1,58 +0,0 @@ -# HTTP proxy service with libp2p - -This examples shows how to create a simple HTTP proxy service with libp2p: - -``` - XXX - XX XXXXXX - X XX - XXXXXXX XX XX XXXXXXXXXX - +----------------+ +-----------------+ XXX XXX XXX XXX - HTTP Request | | | | XX XX -+-----------------> | libp2p stream | | HTTP X X - | Local peer <----------------> Remote peer <-------------> HTTP SERVER - THE INTERNET XX -<-----------------+ | | | Req & Resp XX X - HTTP Response | libp2p host | | libp2p host | XXXX XXXX XXXXXXXXXXXXXXXXXXXX XXXX - +----------------+ +-----------------+ XXXXX -``` - -In order to proxy an HTTP request, we create a local peer which listens on `localhost:9900`. HTTP requests performed to that address are tunneled via a libp2p stream to a remote peer, which then performs the HTTP requests and sends the response back to the local peer, which relays it to the user. - -Note that this is a very simple approach to a proxy, and does not perform any header management, nor supports HTTPS. The `proxy.go` code is thoroughly commeted, detailing what is happening in every step. - -## Build - -From `go-libp2p` base folder: - -``` -> make deps -> go build ./examples/http-proxy -``` - -## Usage - -First run the "remote" peer as follows. It will print a local peer address. If you would like to run this on a separate machine, please replace the IP accordingly: - -```sh -> ./http-proxy -Proxy server is ready -libp2p-peer addresses: -/ip4/127.0.0.1/tcp/12000/ipfs/QmddTrQXhA9AkCpXPTkcY7e22NK73TwkUms3a44DhTKJTD -``` - -The run the local peer, indicating that it will need to forward http requests to the remote peer as follows: - -``` -> ./http-proxy -d /ip4/127.0.0.1/tcp/12000/ipfs/QmddTrQXhA9AkCpXPTkcY7e22NK73TwkUms3a44DhTKJTD -Proxy server is ready -libp2p-peer addresses: -/ip4/127.0.0.1/tcp/12001/ipfs/Qmaa2AYTha1UqcFVX97p9R1UP7vbzDLY7bqWsZw1135QvN -proxy listening on 127.0.0.1:9900 -``` - -As you can see, the proxy prints the listening address `127.0.0.1:9900`. You can now use this address as proxy, for example with `curl`: - -``` -> curl -x "127.0.0.1:9900" "http://ipfs.io/ipfs/QmfUX75pGRBRDnjeoMkQzuQczuCup2aYbeLxz5NzeSu9G6" -it works! -``` diff --git a/examples/http-proxy/proxy.go b/examples/http-proxy/proxy.go deleted file mode 100644 index 07e36b0dbef34e7fac3301208d22795820f58060..0000000000000000000000000000000000000000 --- a/examples/http-proxy/proxy.go +++ /dev/null @@ -1,276 +0,0 @@ -package main - -import ( - "bufio" - "context" - "flag" - "fmt" - "io" - "log" - "net/http" - "strings" - - // We need to import libp2p's libraries that we use in this project. - // In order to work, these libraries need to be rewritten by gx-go. - host "github.com/libp2p/go-libp2p-host" - inet "github.com/libp2p/go-libp2p-net" - peer "github.com/libp2p/go-libp2p-peer" - ps "github.com/libp2p/go-libp2p-peerstore" - ma "github.com/multiformats/go-multiaddr" - manet "github.com/multiformats/go-multiaddr-net" - - libp2p "github.com/libp2p/go-libp2p" -) - -// Protocol defines the libp2p protocol that we will use for the libp2p proxy -// service that we are going to provide. This will tag the streams used for -// this service. Streams are multiplexed and their protocol tag helps -// libp2p handle them to the right handler functions. -const Protocol = "/proxy-example/0.0.1" - -// makeRandomHost creates a libp2p host with a randomly generated identity. -// This step is described in depth in other tutorials. -func makeRandomHost(port int) host.Host { - host, err := libp2p.New(context.Background(), libp2p.ListenAddrStrings(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", port))) - if err != nil { - log.Fatalln(err) - } - return host -} - -// ProxyService provides HTTP proxying on top of libp2p by launching an -// HTTP server which tunnels the requests to a destination peer running -// ProxyService too. -type ProxyService struct { - host host.Host - dest peer.ID - proxyAddr ma.Multiaddr -} - -// NewProxyService attaches a proxy service to the given libp2p Host. -// The proxyAddr parameter specifies the address on which the -// HTTP proxy server listens. The dest parameter specifies the peer -// ID of the remote peer in charge of performing the HTTP requests. -// -// ProxyAddr/dest may be nil/"" it is not necessary that this host -// provides a listening HTTP server (and instead its only function is to -// perform the proxied http requests it receives from a different peer. -// -// The addresses for the dest peer should be part of the host's peerstore. -func NewProxyService(h host.Host, proxyAddr ma.Multiaddr, dest peer.ID) *ProxyService { - // We let our host know that it needs to handle streams tagged with the - // protocol id that we have defined, and then handle them to - // our own streamHandling function. - h.SetStreamHandler(Protocol, streamHandler) - - fmt.Println("Proxy server is ready") - fmt.Println("libp2p-peer addresses:") - for _, a := range h.Addrs() { - fmt.Printf("%s/ipfs/%s\n", a, peer.IDB58Encode(h.ID())) - } - - return &ProxyService{ - host: h, - dest: dest, - proxyAddr: proxyAddr, - } -} - -// streamHandler is our function to handle any libp2p-net streams that belong -// to our protocol. The streams should contain an HTTP request which we need -// to parse, make on behalf of the original node, and then write the response -// on the stream, before closing it. -func streamHandler(stream inet.Stream) { - // Remember to close the stream when we are done. - defer stream.Close() - - // Create a new buffered reader, as ReadRequest needs one. - // The buffered reader reads from our stream, on which we - // have sent the HTTP request (see ServeHTTP()) - buf := bufio.NewReader(stream) - // Read the HTTP request from the buffer - req, err := http.ReadRequest(buf) - if err != nil { - stream.Reset() - log.Println(err) - return - } - defer req.Body.Close() - - // We need to reset these fields in the request - // URL as they are not maintained. - req.URL.Scheme = "http" - hp := strings.Split(req.Host, ":") - if len(hp) > 1 && hp[1] == "443" { - req.URL.Scheme = "https" - } else { - req.URL.Scheme = "http" - } - req.URL.Host = req.Host - - outreq := new(http.Request) - *outreq = *req - - // We now make the request - fmt.Printf("Making request to %s\n", req.URL) - resp, err := http.DefaultTransport.RoundTrip(outreq) - if err != nil { - stream.Reset() - log.Println(err) - return - } - - // resp.Write writes whatever response we obtained for our - // request back to the stream. - resp.Write(stream) -} - -// Serve listens on the ProxyService's proxy address. This effectively -// allows to set the listening address as http proxy. -func (p *ProxyService) Serve() { - _, serveArgs, _ := manet.DialArgs(p.proxyAddr) - fmt.Println("proxy listening on ", serveArgs) - if p.dest != "" { - http.ListenAndServe(serveArgs, p) - } -} - -// ServeHTTP implements the http.Handler interface. WARNING: This is the -// simplest approach to a proxy. Therefore we do not do any of the things -// that should be done when implementing a reverse proxy (like handling -// headers correctly). For how to do it properly, see: -// https://golang.org/src/net/http/httputil/reverseproxy.go?s=3845:3920#L121 -// -// ServeHTTP opens a stream to the dest peer for every HTTP request. -// Streams are multiplexed over single connections so, unlike connections -// themselves, they are cheap to create and dispose of. -func (p *ProxyService) ServeHTTP(w http.ResponseWriter, r *http.Request) { - fmt.Printf("proxying request for %s to peer %s\n", r.URL, p.dest.Pretty()) - // We need to send the request to the remote libp2p peer, so - // we open a stream to it - stream, err := p.host.NewStream(context.Background(), p.dest, Protocol) - // If an error happens, we write an error for response. - if err != nil { - log.Println(err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - defer stream.Close() - - // r.Write() writes the HTTP request to the stream. - err = r.Write(stream) - if err != nil { - stream.Reset() - log.Println(err) - http.Error(w, err.Error(), http.StatusServiceUnavailable) - return - } - - // Now we read the response that was sent from the dest - // peer - buf := bufio.NewReader(stream) - resp, err := http.ReadResponse(buf, r) - if err != nil { - stream.Reset() - log.Println(err) - http.Error(w, err.Error(), http.StatusServiceUnavailable) - return - } - - // Copy any headers - for k, v := range resp.Header { - for _, s := range v { - w.Header().Add(k, s) - } - } - - // Write response status and headers - w.WriteHeader(resp.StatusCode) - - // Finally copy the body - io.Copy(w, resp.Body) - resp.Body.Close() -} - -// addAddrToPeerstore parses a peer multiaddress and adds -// it to the given host's peerstore, so it knows how to -// contact it. It returns the peer ID of the remote peer. -func addAddrToPeerstore(h host.Host, addr string) peer.ID { - // The following code extracts target's the peer ID from the - // given multiaddress - ipfsaddr, err := ma.NewMultiaddr(addr) - 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 - h.Peerstore().AddAddr(peerid, targetAddr, ps.PermanentAddrTTL) - return peerid -} - -const help = ` -This example creates a simple HTTP Proxy using two libp2p peers. The first peer -provides an HTTP server locally which tunnels the HTTP requests with libp2p -to a remote peer. The remote peer performs the requests and -send the sends the response back. - -Usage: Start remote peer first with: ./proxy - Then start the local peer with: ./proxy -d - -Then you can do something like: curl -x "localhost:9900" "http://ipfs.io". -This proxies sends the request through the local peer, which proxies it to -the remote peer, which makes it and sends the response back. -` - -func main() { - flag.Usage = func() { - fmt.Println(help) - flag.PrintDefaults() - } - - // Parse some flags - destPeer := flag.String("d", "", "destination peer address") - port := flag.Int("p", 9900, "proxy port") - p2pport := flag.Int("l", 12000, "libp2p listen port") - flag.Parse() - - // If we have a destination peer we will start a local server - if *destPeer != "" { - // We use p2pport+1 in order to not collide if the user - // is running the remote peer locally on that port - host := makeRandomHost(*p2pport + 1) - // Make sure our host knows how to reach destPeer - destPeerID := addAddrToPeerstore(host, *destPeer) - proxyAddr, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", *port)) - if err != nil { - log.Fatalln(err) - } - // Create the proxy service and start the http server - proxy := NewProxyService(host, proxyAddr, destPeerID) - proxy.Serve() // serve hangs forever - } else { - host := makeRandomHost(*p2pport) - // In this case we only need to make sure our host - // knows how to handle incoming proxied requests from - // another peer. - _ = NewProxyService(host, nil, "") - <-make(chan struct{}) // hang forever - } - -} diff --git a/examples/libp2p-host/README.md b/examples/libp2p-host/README.md deleted file mode 100644 index 88532053bb4a15c9a79b6fbad325701b6d95a591..0000000000000000000000000000000000000000 --- a/examples/libp2p-host/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# The libp2p 'host' - -For most applications, the host is the basic building block you'll need to get started. This guide will show how to construct and use a simple host. - -The host is an abstraction that manages services on top of a swarm. It provides a clean interface to connect to a service on a given remote peer. - -If you want to create a host with a default configuration, you can do the following: - -```go -import ( - "context" - "crypto/rand" - "fmt" - - libp2p "github.com/libp2p/go-libp2p" - crypto "github.com/libp2p/go-libp2p-crypto" -) - - -// The context governs the lifetime of the libp2p node -ctx, cancel := context.WithCancel(context.Background()) -defer cancel() - -// To construct a simple host with all the default settings, just use `New` -h, err := libp2p.New(ctx) -if err != nil { - panic(err) -} - -fmt.Printf("Hello World, my hosts ID is %s\n", h.ID()) -``` - -If you want more control over the configuration, you can specify some options to the constructor. For a full list of all the configuration supported by the constructor see: [options.go](https://github.com/libp2p/go-libp2p/blob/master/options.go) - -In this snippet we generate our own ID and specified on which address we want to listen: - -```go -// Set your own keypair -priv, _, err := crypto.GenerateEd25519Key(rand.Reader) -if err != nil { - panic(err) -} - -h2, err := libp2p.New(ctx, - // Use your own created keypair - libp2p.Identity(priv), - - // Set your own listen address - // The config takes an array of addresses, specify as many as you want. - libp2p.ListenAddrStrings("/ip4/0.0.0.0/tcp/9000"), -) -if err != nil { - panic(err) -} - -fmt.Printf("Hello World, my second hosts ID is %s\n", h2.ID()) -``` - -And thats it, you have a libp2p host and you're ready to start doing some awesome p2p networking! - -In future guides we will go over ways to use hosts, configure them differently (hint: there are a huge number of ways to set these up), and interesting ways to apply this technology to various applications you might want to build. - -To see this code all put together, take a look at [host.go](host.go). diff --git a/examples/libp2p-host/host.go b/examples/libp2p-host/host.go deleted file mode 100644 index 793cdf4fd8667751a66e0c0a1594228ca12bc8af..0000000000000000000000000000000000000000 --- a/examples/libp2p-host/host.go +++ /dev/null @@ -1,47 +0,0 @@ -package main - -import ( - "context" - "crypto/rand" - "fmt" - - libp2p "github.com/libp2p/go-libp2p" - crypto "github.com/libp2p/go-libp2p-crypto" -) - -func main() { - // The context governs the lifetime of the libp2p node - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - // To construct a simple host with all the default settings, just use `New` - h, err := libp2p.New(ctx) - if err != nil { - panic(err) - } - - fmt.Printf("Hello World, my hosts ID is %s\n", h.ID()) - - // If you want more control over the configuration, you can specify some - // options to the constructor - - // Set your own keypair - priv, _, err := crypto.GenerateEd25519Key(rand.Reader) - if err != nil { - panic(err) - } - - h2, err := libp2p.New(ctx, - // Use your own created keypair - libp2p.Identity(priv), - - // Set your own listen address - // The config takes an array of addresses, specify as many as you want. - libp2p.ListenAddrStrings("/ip4/0.0.0.0/tcp/9000"), - ) - if err != nil { - panic(err) - } - - fmt.Printf("Hello World, my second hosts ID is %s\n", h2.ID()) -} diff --git a/examples/multipro/AUTHORS b/examples/multipro/AUTHORS deleted file mode 100644 index 70a81f0a5dde9671ce3c48994d9eb55a777193c6..0000000000000000000000000000000000000000 --- a/examples/multipro/AUTHORS +++ /dev/null @@ -1,3 +0,0 @@ -# This is the official list of authors for copyright purposes. - -Aviv Eyal diff --git a/examples/multipro/LICENSE b/examples/multipro/LICENSE deleted file mode 100644 index df71bca6adf2a477071a6a1858529eecf004e4c3..0000000000000000000000000000000000000000 --- a/examples/multipro/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2017 Aviv Eyal - -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. diff --git a/examples/multipro/README.md b/examples/multipro/README.md deleted file mode 100644 index c60da8b173730ec4d5cfe4e19a606502adf44195..0000000000000000000000000000000000000000 --- a/examples/multipro/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# Protocol Multiplexing using rpc-style multicodecs, protobufs with libp2p - -This examples shows how to use multicodecs (i.e. protobufs) to encode and transmit information between LibP2P hosts using LibP2P Streams. -Multicodecs present a common interface, making it very easy to swap the codec implementation if needed. -This example expects that you area already familiar with the [echo example](https://github.com/libp2p/go-libp2p/tree/master/examples/echo). - -## 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 -> go build -``` - - -## Usage - -```sh -> ./multipro - -``` - -## Details - -The example creates two LibP2P Hosts supporting 2 protocols: ping and echo. - -Each protocol consists RPC-style requests and responses and each request and response is a typed protobufs message (and a go data object). - -This is a different pattern then defining a whole p2p protocol as one protobuf message with lots of optional fields (as can be observed in various p2p-lib protocols using protobufs such as dht). - -The example shows how to match async received responses with their requests. This is useful when processing a response requires access to the request data. - -The idea is to use lib-p2p protocol multiplexing on a per-message basis. - -### Features -1. 2 fully implemented protocols using an RPC-like request-response pattern - Ping and Echo -2. Scaffolding for quickly implementing new app-level versioned RPC-like protocols -3. Full authentication of incoming message data by author (who might not be the message's sender peer) -4. Base p2p format in protobufs with fields shared by all protocol messages -5. Full access to request data when processing a response. - -## Author -@avive - - diff --git a/examples/multipro/echo.go b/examples/multipro/echo.go deleted file mode 100644 index a3d39f8b890579f1fb703edc6001f3668e389e41..0000000000000000000000000000000000000000 --- a/examples/multipro/echo.go +++ /dev/null @@ -1,157 +0,0 @@ -package main - -import ( - "bufio" - "context" - "fmt" - "log" - - 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" - protobufCodec "github.com/multiformats/go-multicodec/protobuf" -) - -// pattern: /protocol-name/request-or-response-message/version -const echoRequest = "/echo/echoreq/0.0.1" -const echoResponse = "/echo/echoresp/0.0.1" - -type EchoProtocol struct { - node *Node // local host - requests map[string]*pb.EchoRequest // used to access request data from response handlers - done chan bool // only for demo purposes to hold main from terminating -} - -func NewEchoProtocol(node *Node, done chan bool) *EchoProtocol { - e := EchoProtocol{node: node, requests: make(map[string]*pb.EchoRequest), done: done} - node.SetStreamHandler(echoRequest, e.onEchoRequest) - node.SetStreamHandler(echoResponse, e.onEchoResponse) - - // design note: to implement fire-and-forget style messages you may just skip specifying a response callback. - // a fire-and-forget message will just include a request and not specify a response object - - return &e -} - -// remote peer requests handler -func (e *EchoProtocol) onEchoRequest(s inet.Stream) { - // get request data - data := &pb.EchoRequest{} - decoder := protobufCodec.Multicodec(nil).Decoder(bufio.NewReader(s)) - err := decoder.Decode(data) - if err != nil { - log.Println(err) - return - } - - log.Printf("%s: Received echo request from %s. Message: %s", s.Conn().LocalPeer(), s.Conn().RemotePeer(), data.Message) - - valid := e.node.authenticateMessage(data, data.MessageData) - - if !valid { - log.Println("Failed to authenticate message") - return - } - - log.Printf("%s: Sending echo response to %s. Message id: %s...", s.Conn().LocalPeer(), s.Conn().RemotePeer(), data.MessageData.Id) - - // send response to the request using the message string he provided - - resp := &pb.EchoResponse{ - MessageData: e.node.NewMessageData(data.MessageData.Id, false), - Message: data.Message} - - // sign the data - signature, err := e.node.signProtoMessage(resp) - if err != nil { - log.Println("failed to sign response") - return - } - - // add the signature to the message - resp.MessageData.Sign = string(signature) - - s, respErr := e.node.NewStream(context.Background(), s.Conn().RemotePeer(), echoResponse) - if respErr != nil { - log.Println(respErr) - return - } - - ok := e.node.sendProtoMessage(resp, s) - - if ok { - log.Printf("%s: Echo response to %s sent.", s.Conn().LocalPeer().String(), s.Conn().RemotePeer().String()) - } -} - -// remote echo response handler -func (e *EchoProtocol) onEchoResponse(s inet.Stream) { - data := &pb.EchoResponse{} - decoder := protobufCodec.Multicodec(nil).Decoder(bufio.NewReader(s)) - err := decoder.Decode(data) - if err != nil { - return - } - - // authenticate message content - valid := e.node.authenticateMessage(data, data.MessageData) - - if !valid { - log.Println("Failed to authenticate message") - return - } - - // locate request data and remove it if found - req, ok := e.requests[data.MessageData.Id] - if ok { - // remove request from map as we have processed it here - delete(e.requests, data.MessageData.Id) - } else { - log.Println("Failed to locate request data boject for response") - return - } - - if req.Message != data.Message { - log.Fatalln("Expected echo to respond with request message") - } - - log.Printf("%s: Received echo response from %s. Message id:%s. Message: %s.", s.Conn().LocalPeer(), s.Conn().RemotePeer(), data.MessageData.Id, data.Message) - e.done <- true -} - -func (e *EchoProtocol) Echo(host host.Host) bool { - log.Printf("%s: Sending echo to: %s....", e.node.ID(), host.ID()) - - // create message data - req := &pb.EchoRequest{ - MessageData: e.node.NewMessageData(uuid.New().String(), false), - Message: fmt.Sprintf("Echo from %s", e.node.ID())} - - signature, err := e.node.signProtoMessage(req) - if err != nil { - log.Println("failed to sign message") - return false - } - - // add the signature to the message - req.MessageData.Sign = string(signature) - - s, err := e.node.NewStream(context.Background(), host.ID(), echoRequest) - if err != nil { - log.Println(err) - return false - } - - ok := e.node.sendProtoMessage(req, s) - - if !ok { - return false - } - - // store request so response handler has access to it - e.requests[req.MessageData.Id] = req - log.Printf("%s: Echo to: %s was sent. Message Id: %s, Message: %s", e.node.ID(), host.ID(), req.MessageData.Id, req.Message) - return true -} diff --git a/examples/multipro/main.go b/examples/multipro/main.go deleted file mode 100644 index 3350ea3834a40a57f7ae46b0df62e6a5130b598a..0000000000000000000000000000000000000000 --- a/examples/multipro/main.go +++ /dev/null @@ -1,56 +0,0 @@ -package main - -import ( - "context" - "fmt" - "log" - "math/rand" - - libp2p "github.com/libp2p/go-libp2p" - crypto "github.com/libp2p/go-libp2p-crypto" - ps "github.com/libp2p/go-libp2p-peerstore" - ma "github.com/multiformats/go-multiaddr" -) - -// helper method - create a lib-p2p host to listen on a port -func makeRandomNode(port int, done chan bool) *Node { - // Ignoring most errors for brevity - // See echo example for more details and better implementation - priv, _, _ := crypto.GenerateKeyPair(crypto.Secp256k1, 256) - listen, _ := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", port)) - host, _ := libp2p.New( - context.Background(), - libp2p.ListenAddrs(listen), - libp2p.Identity(priv), - ) - - return NewNode(host, done) -} - -func main() { - // Choose random ports between 10000-10100 - rand.Seed(666) - port1 := rand.Intn(100) + 10000 - port2 := port1 + 1 - - done := make(chan bool, 1) - - // Make 2 hosts - h1 := makeRandomNode(port1, done) - h2 := makeRandomNode(port2, done) - h1.Peerstore().AddAddrs(h2.ID(), h2.Addrs(), ps.PermanentAddrTTL) - h2.Peerstore().AddAddrs(h1.ID(), h1.Addrs(), ps.PermanentAddrTTL) - - log.Printf("This is a conversation between %s and %s\n", h1.ID(), h2.ID()) - - // send messages using the protocols - h1.Ping(h2.Host) - h2.Ping(h1.Host) - h1.Echo(h2.Host) - h2.Echo(h1.Host) - - // block until all responses have been processed - for i := 0; i < 4; i++ { - <-done - } -} diff --git a/examples/multipro/node.go b/examples/multipro/node.go deleted file mode 100644 index 58b42330e13fcd428e47fe38efad423afeb82f25..0000000000000000000000000000000000000000 --- a/examples/multipro/node.go +++ /dev/null @@ -1,150 +0,0 @@ -package main - -import ( - "bufio" - "log" - "time" - - "github.com/gogo/protobuf/proto" - crypto "github.com/libp2p/go-libp2p-crypto" - 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" -) - -// node client version -const clientVersion = "go-p2p-node/0.0.1" - -// Node type - a p2p host implementing one or more p2p protocols -type Node struct { - host.Host // lib-p2p host - *PingProtocol // ping protocol impl - *EchoProtocol // echo protocol impl - // add other protocols here... -} - -// Create a new node with its implemented protocols -func NewNode(host host.Host, done chan bool) *Node { - node := &Node{Host: host} - node.PingProtocol = NewPingProtocol(node, done) - node.EchoProtocol = NewEchoProtocol(node, done) - return node -} - -// Authenticate incoming p2p message -// message: a protobufs go data object -// data: common p2p message data -func (n *Node) authenticateMessage(message proto.Message, data *p2p.MessageData) bool { - // 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 = "" - - // marshall data without the signature to protobufs3 binary format - bin, err := proto.Marshal(message) - if err != nil { - log.Println(err, "failed to marshal pb message") - return false - } - - // restore sig in message data (for possible future use) - data.Sign = sign - - // restore peer id binary format from base58 encoded node id data - peerId, err := peer.IDB58Decode(data.NodeId) - if err != nil { - log.Println(err, "Failed to decode node id from base58") - return false - } - - // verify the data was authored by the signing peer identified by the public key - // and signature included in the message - return n.verifyData(bin, []byte(sign), peerId, data.NodePubKey) -} - -// sign an outgoing p2p message payload -func (n *Node) signProtoMessage(message proto.Message) ([]byte, error) { - data, err := proto.Marshal(message) - if err != nil { - return nil, err - } - return n.signData(data) -} - -// sign binary data using the local node's private key -func (n *Node) signData(data []byte) ([]byte, error) { - key := n.Peerstore().PrivKey(n.ID()) - res, err := key.Sign(data) - return res, err -} - -// Verify incoming p2p message data integrity -// data: data to verify -// signature: author signature provided in the message payload -// peerId: author peer id from the message payload -// pubKeyData: author public key from the message payload -func (n *Node) verifyData(data []byte, signature []byte, peerId peer.ID, pubKeyData []byte) bool { - key, err := crypto.UnmarshalPublicKey(pubKeyData) - if err != nil { - log.Println(err, "Failed to extract key from message key data") - return false - } - - // extract node id from the provided public key - idFromKey, err := peer.IDFromPublicKey(key) - - if err != nil { - log.Println(err, "Failed to extract peer id from public key") - return false - } - - // verify that message author node id matches the provided node public key - if idFromKey != peerId { - log.Println(err, "Node id and provided public key mismatch") - return false - } - - res, err := key.Verify(data, signature) - if err != nil { - log.Println(err, "Error authenticating data") - return false - } - - return res -} - -// helper method - generate message data shared between all node's p2p protocols -// messageId: unique for requests, copied from request for responses -func (n *Node) NewMessageData(messageId string, gossip bool) *p2p.MessageData { - // Add protobufs bin data for message author public key - // this is useful for authenticating messages forwarded by a node authored by another node - nodePubKey, err := n.Peerstore().PubKey(n.ID()).Bytes() - - if err != nil { - panic("Failed to get public key for sender from local peer store.") - } - - return &p2p.MessageData{ClientVersion: clientVersion, - NodeId: peer.IDB58Encode(n.ID()), - NodePubKey: nodePubKey, - Timestamp: time.Now().Unix(), - Id: messageId, - Gossip: gossip} -} - -// helper method - writes a protobuf go data object to a network stream -// data: reference of protobuf go data object to send (not the object itself) -// s: network stream to write the data to -func (n *Node) sendProtoMessage(data proto.Message, s inet.Stream) bool { - writer := bufio.NewWriter(s) - enc := protobufCodec.Multicodec(nil).Encoder(writer) - err := enc.Encode(data) - if err != nil { - log.Println(err) - return false - } - writer.Flush() - return true -} diff --git a/examples/multipro/pb/p2p.pb.go b/examples/multipro/pb/p2p.pb.go deleted file mode 100644 index 091ef4afec89702fe832217c469123955a804dee..0000000000000000000000000000000000000000 --- a/examples/multipro/pb/p2p.pb.go +++ /dev/null @@ -1,121 +0,0 @@ -// Code generated by protoc-gen-gogo. -// 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" -import fmt "fmt" -import math "math" - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// 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"` -} - -func (m *MessageData) Reset() { *m = MessageData{} } -func (m *MessageData) String() string { return proto.CompactTextString(m) } -func (*MessageData) ProtoMessage() {} - -// 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"` -} - -func (m *PingRequest) Reset() { *m = PingRequest{} } -func (m *PingRequest) String() string { return proto.CompactTextString(m) } -func (*PingRequest) ProtoMessage() {} - -func (m *PingRequest) GetMessageData() *MessageData { - if m != nil { - return m.MessageData - } - return nil -} - -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"` -} - -func (m *PingResponse) Reset() { *m = PingResponse{} } -func (m *PingResponse) String() string { return proto.CompactTextString(m) } -func (*PingResponse) ProtoMessage() {} - -func (m *PingResponse) GetMessageData() *MessageData { - if m != nil { - return m.MessageData - } - return nil -} - -// 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"` -} - -func (m *EchoRequest) Reset() { *m = EchoRequest{} } -func (m *EchoRequest) String() string { return proto.CompactTextString(m) } -func (*EchoRequest) ProtoMessage() {} - -func (m *EchoRequest) GetMessageData() *MessageData { - if m != nil { - return m.MessageData - } - return nil -} - -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"` -} - -func (m *EchoResponse) Reset() { *m = EchoResponse{} } -func (m *EchoResponse) String() string { return proto.CompactTextString(m) } -func (*EchoResponse) ProtoMessage() {} - -func (m *EchoResponse) GetMessageData() *MessageData { - if m != nil { - return m.MessageData - } - return nil -} - -func init() { - proto.RegisterType((*MessageData)(nil), "protocols.p2p.MessageData") - proto.RegisterType((*PingRequest)(nil), "protocols.p2p.PingRequest") - proto.RegisterType((*PingResponse)(nil), "protocols.p2p.PingResponse") - proto.RegisterType((*EchoRequest)(nil), "protocols.p2p.EchoRequest") - proto.RegisterType((*EchoResponse)(nil), "protocols.p2p.EchoResponse") -} diff --git a/examples/multipro/pb/p2p.proto b/examples/multipro/pb/p2p.proto deleted file mode 100644 index 53e652154b213b2f5d73de604c014a99ad09da86..0000000000000000000000000000000000000000 --- a/examples/multipro/pb/p2p.proto +++ /dev/null @@ -1,56 +0,0 @@ -syntax = "proto3"; - -package protocols.p2p; - -// designed to be shared between all app protocols -message MessageData { - // shared between all requests - string clientVersion = 1; // client version - 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))) - 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) -} - -//// ping protocol - -// a protocol define a set of reuqest and responses -message PingRequest { - MessageData messageData = 1; - - // method specific data - string message = 2; - // add any data here.... -} - -message PingResponse { - MessageData messageData = 1; - - // response specific data - string message = 2; - - // ... add any additional message data here -} - -//// echo protocol - -// a protocol define a set of reuqest and responses -message EchoRequest { - MessageData messageData = 1; - - // method specific data - string message = 2; - - // add any additional message data here.... -} - -message EchoResponse { - MessageData messageData = 1; - - // response specific data - string message = 2; - - // ... add any additional message data here.... -} diff --git a/examples/multipro/pb/readme.md b/examples/multipro/pb/readme.md deleted file mode 100644 index ec7114f6add8544d129918ed3dc55167103002e2..0000000000000000000000000000000000000000 --- a/examples/multipro/pb/readme.md +++ /dev/null @@ -1,4 +0,0 @@ -# building p2p.pb.go: -protoc --gogo_out=. --proto_path=../../../../../../:/usr/local/opt/protobuf/include:. *.proto - - diff --git a/examples/multipro/ping.go b/examples/multipro/ping.go deleted file mode 100644 index f342aa69b3656995fbe350387c48bb6fdf2b696f..0000000000000000000000000000000000000000 --- a/examples/multipro/ping.go +++ /dev/null @@ -1,148 +0,0 @@ -package main - -import ( - "bufio" - "context" - "fmt" - "log" - - uuid "github.com/google/uuid" - "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" -) - -// pattern: /protocol-name/request-or-response-message/version -const pingRequest = "/ping/pingreq/0.0.1" -const pingResponse = "/ping/pingresp/0.0.1" - -// PingProtocol type -type PingProtocol struct { - node *Node // local host - requests map[string]*p2p.PingRequest // used to access request data from response handlers - done chan bool // only for demo purposes to stop main from terminating -} - -func NewPingProtocol(node *Node, done chan bool) *PingProtocol { - p := &PingProtocol{node: node, requests: make(map[string]*p2p.PingRequest), done: done} - node.SetStreamHandler(pingRequest, p.onPingRequest) - node.SetStreamHandler(pingResponse, p.onPingResponse) - return p -} - -// remote peer requests handler -func (p *PingProtocol) onPingRequest(s inet.Stream) { - - // get request data - data := &p2p.PingRequest{} - decoder := protobufCodec.Multicodec(nil).Decoder(bufio.NewReader(s)) - err := decoder.Decode(data) - if err != nil { - log.Println(err) - return - } - - log.Printf("%s: Received ping request from %s. Message: %s", s.Conn().LocalPeer(), s.Conn().RemotePeer(), data.Message) - - valid := p.node.authenticateMessage(data, data.MessageData) - - if !valid { - log.Println("Failed to authenticate message") - return - } - - // generate response message - log.Printf("%s: Sending ping response to %s. Message id: %s...", s.Conn().LocalPeer(), s.Conn().RemotePeer(), data.MessageData.Id) - - resp := &p2p.PingResponse{MessageData: p.node.NewMessageData(data.MessageData.Id, false), - Message: fmt.Sprintf("Ping response from %s", p.node.ID())} - - // sign the data - signature, err := p.node.signProtoMessage(resp) - if err != nil { - log.Println("failed to sign response") - return - } - - // add the signature to the message - resp.MessageData.Sign = string(signature) - - // send the response - s, respErr := p.node.NewStream(context.Background(), s.Conn().RemotePeer(), pingResponse) - if respErr != nil { - log.Println(respErr) - return - } - - ok := p.node.sendProtoMessage(resp, s) - - if ok { - log.Printf("%s: Ping response to %s sent.", s.Conn().LocalPeer().String(), s.Conn().RemotePeer().String()) - } -} - -// remote ping response handler -func (p *PingProtocol) onPingResponse(s inet.Stream) { - data := &p2p.PingResponse{} - decoder := protobufCodec.Multicodec(nil).Decoder(bufio.NewReader(s)) - err := decoder.Decode(data) - if err != nil { - return - } - - valid := p.node.authenticateMessage(data, data.MessageData) - - if !valid { - log.Println("Failed to authenticate message") - return - } - - // locate request data and remove it if found - _, ok := p.requests[data.MessageData.Id] - if ok { - // remove request from map as we have processed it here - delete(p.requests, data.MessageData.Id) - } else { - log.Println("Failed to locate request data boject for response") - return - } - - log.Printf("%s: Received ping response from %s. Message id:%s. Message: %s.", s.Conn().LocalPeer(), s.Conn().RemotePeer(), data.MessageData.Id, data.Message) - p.done <- true -} - -func (p *PingProtocol) Ping(host host.Host) bool { - log.Printf("%s: Sending ping to: %s....", p.node.ID(), host.ID()) - - // create message data - req := &p2p.PingRequest{MessageData: p.node.NewMessageData(uuid.New().String(), false), - Message: fmt.Sprintf("Ping from %s", p.node.ID())} - - // sign the data - signature, err := p.node.signProtoMessage(req) - if err != nil { - log.Println("failed to sign pb data") - return false - } - - // add the signature to the message - req.MessageData.Sign = string(signature) - - s, err := p.node.NewStream(context.Background(), host.ID(), pingRequest) - if err != nil { - log.Println(err) - return false - } - - ok := p.node.sendProtoMessage(req, s) - - if !ok { - return false - } - - // store ref request so response handler has access to it - p.requests[req.MessageData.Id] = req - log.Printf("%s: Ping to: %s was sent. Message Id: %s, Message: %s", p.node.ID(), host.ID(), req.MessageData.Id, req.Message) - return true -} diff --git a/examples/protocol-multiplexing-with-multicodecs/README.md b/examples/protocol-multiplexing-with-multicodecs/README.md deleted file mode 100644 index 0b873f51a6ebd00e0333ea9342e85eac9d6caf92..0000000000000000000000000000000000000000 --- a/examples/protocol-multiplexing-with-multicodecs/README.md +++ /dev/null @@ -1,41 +0,0 @@ - - -# Protocol Multiplexing using multicodecs with libp2p - -This examples shows how to use multicodecs (i.e. json) to encode and transmit information between LibP2P hosts using LibP2P Streams. - -Multicodecs present a common interface, making it very easy to swap the codec implementation if needed. - -This example expects that you area already familiar with the [echo example](https://github.com/libp2p/go-libp2p/tree/master/examples/echo). - -## Build - -From `go-libp2p` base folder: - -``` -> make deps-protocol-muxing -> go build -o multicodecs ./examples/protocol-multiplexing-with-multicodecs -``` - -## Usage - -``` -> ./multicodecs - -``` - -## Details - -The example creates two LibP2P Hosts. Host1 opens a stream to Host2. Host2 has an `StreamHandler` to deal with the incoming stream. This is covered in the `echo` example. - -Both hosts simulate a conversation. But rather than sending raw messages on the stream, each message in the conversation is encoded under a `json` object (using the `json` multicodec). For example: - -``` -{ - "Msg": "This is the message", - "Index": 3, - "HangUp": false -} -``` - -The stream lasts until one of the sides closes it when the HangUp field is `true`. diff --git a/examples/protocol-multiplexing-with-multicodecs/main.go b/examples/protocol-multiplexing-with-multicodecs/main.go deleted file mode 100644 index 5aa4f6fd6b57b6fec90bf73e8bef41e5f0ada8fa..0000000000000000000000000000000000000000 --- a/examples/protocol-multiplexing-with-multicodecs/main.go +++ /dev/null @@ -1,171 +0,0 @@ -package main - -import ( - "bufio" - "context" - "fmt" - "log" - "math/rand" - "time" - - host "github.com/libp2p/go-libp2p-host" - inet "github.com/libp2p/go-libp2p-net" - ps "github.com/libp2p/go-libp2p-peerstore" - - libp2p "github.com/libp2p/go-libp2p" - multicodec "github.com/multiformats/go-multicodec" - json "github.com/multiformats/go-multicodec/json" -) - -const proto = "/example/1.0.0" - -// Message is a serializable/encodable object that we will send -// on a Stream. -type Message struct { - Msg string - Index int - HangUp bool -} - -// streamWrap wraps a libp2p stream. We encode/decode whenever we -// write/read from a stream, so we can just carry the encoders -// and bufios with us -type WrappedStream struct { - stream inet.Stream - enc multicodec.Encoder - dec multicodec.Decoder - w *bufio.Writer - r *bufio.Reader -} - -// wrapStream takes a stream and complements it with r/w bufios and -// decoder/encoder. In order to write raw data to the stream we can use -// wrap.w.Write(). To encode something into it we can wrap.enc.Encode(). -// Finally, we should wrap.w.Flush() to actually send the data. Handling -// incoming data works similarly with wrap.r.Read() for raw-reading and -// wrap.dec.Decode() to decode. -func WrapStream(s inet.Stream) *WrappedStream { - reader := bufio.NewReader(s) - writer := bufio.NewWriter(s) - // This is where we pick our specific multicodec. In order to change the - // codec, we only need to change this place. - // See https://godoc.org/github.com/multiformats/go-multicodec/json - dec := json.Multicodec(false).Decoder(reader) - enc := json.Multicodec(false).Encoder(writer) - return &WrappedStream{ - stream: s, - r: reader, - w: writer, - enc: enc, - dec: dec, - } -} - -// messages that will be sent between the hosts. -var conversationMsgs = []string{ - "Hello!", - "Hey!", - "How are you doing?", - "Very good! It is great that you can send data on a stream to me!", - "Not only that, the data is encoded in a JSON object.", - "Yeah, and we are using the multicodecs interface to encode and decode.", - "This way we could swap it easily for, say, cbor, or msgpack!", - "Let's leave that as an excercise for the reader...", - "Agreed, our last message should activate the HangUp flag", - "Yes, and the example code will close streams. So sad :(. Bye!", -} - -func makeRandomHost(port int) host.Host { - h, err := libp2p.New(context.Background(), libp2p.ListenAddrStrings(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", port))) - if err != nil { - panic(err) - } - return h -} - -func main() { - // Choose random ports between 10000-10100 - rand.Seed(666) - port1 := rand.Intn(100) + 10000 - port2 := port1 + 1 - - // Make 2 hosts - h1 := makeRandomHost(port1) - h2 := makeRandomHost(port2) - h1.Peerstore().AddAddrs(h2.ID(), h2.Addrs(), ps.PermanentAddrTTL) - h2.Peerstore().AddAddrs(h1.ID(), h1.Addrs(), ps.PermanentAddrTTL) - - log.Printf("This is a conversation between %s and %s\n", h1.ID(), h2.ID()) - - // Define a stream handler for host number 2 - h2.SetStreamHandler(proto, func(stream inet.Stream) { - log.Printf("%s: Received a stream", h2.ID()) - wrappedStream := WrapStream(stream) - defer stream.Close() - handleStream(wrappedStream) - }) - - // Create new stream from h1 to h2 and start the conversation - stream, err := h1.NewStream(context.Background(), h2.ID(), proto) - if err != nil { - log.Fatal(err) - } - wrappedStream := WrapStream(stream) - // This sends the first message - sendMessage(0, wrappedStream) - // We keep the conversation on the created stream so we launch - // this to handle any responses - handleStream(wrappedStream) - // When we are done, close the stream on our side and exit. - stream.Close() -} - -// receiveMessage reads and decodes a message from the stream -func receiveMessage(ws *WrappedStream) (*Message, error) { - var msg Message - err := ws.dec.Decode(&msg) - if err != nil { - return nil, err - } - return &msg, nil -} - -// sendMessage encodes and writes a message to the stream -func sendMessage(index int, ws *WrappedStream) error { - msg := &Message{ - Msg: conversationMsgs[index], - Index: index, - HangUp: index >= len(conversationMsgs)-1, - } - - err := ws.enc.Encode(msg) - // Because output is buffered with bufio, we need to flush! - ws.w.Flush() - return err -} - -// handleStream is a for loop which receives and then sends a message -// an artificial delay of 500ms happens in-between. -// When Message.HangUp is true, it exists. This will close the stream -// on one of the sides. The other side's receiveMessage() will error -// with EOF, thus also breaking out from the loop. -func handleStream(ws *WrappedStream) { - for { - // Read - msg, err := receiveMessage(ws) - if err != nil { - break - } - pid := ws.stream.Conn().LocalPeer() - log.Printf("%s says: %s\n", pid, msg.Msg) - time.Sleep(500 * time.Millisecond) - if msg.HangUp { - break - } - // Send response - err = sendMessage(msg.Index+1, ws) - if err != nil { - break - } - } -} diff --git a/package.json b/package.json index 1df54e44e0e8d8b286ede64631a0b8219c21c58d..367fa5491ef358428707865ad0ec165232153748 100644 --- a/package.json +++ b/package.json @@ -160,12 +160,6 @@ "name": "go-smux-yamux", "version": "2.0.3" }, - { - "author": "whyrusleeping", - "hash": "QmQvJiADDe7JR4m968MwXobTCCzUqQkP87aRHe29MEBGHV", - "name": "go-logging", - "version": "0.0.0" - }, { "author": "whyrusleeping", "hash": "QmWzjXAyBTygw6CeCTUnhJzhFucfxY5FJivSoiGuiSbPjS", @@ -202,12 +196,6 @@ "name": "go-smux-multiplex", "version": "3.0.11" }, - { - "author": "multiformats", - "hash": "QmRDePEiL4Yupq5EkcK3L3ko3iMgYaqUdLu7xc1kqs7dnV", - "name": "go-multicodec", - "version": "0.1.5" - }, { "author": "whyrusleeping", "hash": "QmX4UrmHGPnFxwfsunZjPbykzyv8Frg9AVmNariXqrLsMs", @@ -243,12 +231,6 @@ "hash": "Qma7AuxEA7dd1wAy95hTxXgxy4q7mU4Pyd1x4PRAzGP1fs", "name": "go-libp2p-transport-upgrader", "version": "0.1.5" - }, - { - "author": "google", - "hash": "QmSSeQqc5QeuefkaM6JFV5tSF9knLUkXKVhW1eYRiqe72W", - "name": "uuid", - "version": "0.1.0" } ], "gxVersion": "0.4.0",