libp2p / go-libp2p-examples

Example libp2p applications
MIT License
338 stars 145 forks source link

how to create my own bootstrap peer like DefaultBootstrapPeers in github.com/libp2p/go-libp2p-kad-dht/dht_bootstrap.go:16 #72

Open maimai88 opened 5 years ago

maimai88 commented 5 years ago

how to create my own bootstrap peer like DefaultBootstrapPeers in github.com/libp2p/go-libp2p-kad-dht/dht_bootstrap.go:16

In the chat-with-rendezvous example, I tried to use a chat-with-rendezvous instance as a bootstrap. For nodes in other networks, the peer parameter points to it. Peers in the same network can be interconnected, and different networks can't communicate. If all nodes use the default dht.DefaultBootstrapPeers, different networks can communicate.

@raulk

upperwal commented 5 years ago

@maimai88 You can try this code.

Peers in the same network can be interconnected, and different networks can't communicate. If all nodes use the default dht.DefaultBootstrapPeers, different networks can communicate.

This should not happen. Can you enable logs and say more about the failures.

maimai88 commented 5 years ago

@maimai88 You can try this code.

Peers in the same network can be interconnected, and different networks can't communicate. If all nodes use the default dht.DefaultBootstrapPeers, different networks can communicate.

This should not happen. Can you enable logs and say more about the failures.


Thanks upperwal

@upperwal First of all, thank you very much for your reply. I don't know why, I still didn't succeed. My code is as follows


I have three nodes in Japan, Hong Kong and South Korea.

bootstrap node

A node in Japan as bootstrap server, the code running on this node like this

(from your suggestion code)

libp2p_with_dht.go

package main

import (
    "context"
    "fmt"

    "github.com/libp2p/go-libp2p"
    crypto "github.com/libp2p/go-libp2p-crypto"
    dht "github.com/libp2p/go-libp2p-kad-dht"

    //inet "github.com/libp2p/go-libp2p-net"
    mrand "math/rand"

    ma "github.com/multiformats/go-multiaddr"
)

func main() {
    ctx := context.Background()

    // libp2p.New constructs a new libp2p Host.
    // Other options can be added here.
    sourceMultiAddr, _ := ma.NewMultiaddr("/ip4/0.0.0.0/tcp/3000")

    r := mrand.New(mrand.NewSource(int64(10)))

    prvKey, _, err := crypto.GenerateKeyPairWithReader(crypto.RSA, 2048, r)

    if err != nil {
        panic(err)
    }

    opts := []libp2p.Option{
        libp2p.ListenAddrs(sourceMultiAddr),
        //libp2p.NATPortMap(),
        //libp2p.DefaultSecurity,
        //libp2p.EnableRelay(circuit.OptActive, circuit.OptHop, circuit.OptDiscovery),
        libp2p.Identity(prvKey),
    }

    host, err := libp2p.New(ctx, opts...)
    if err != nil {
        panic(err)
    }

    fmt.Println("This node: ", host.ID().Pretty(), " ", host.Addrs())

    _, err = dht.New(ctx, host)
    if err != nil {
        panic(err)
    }

    select {}
}

The result of running the code above in the terminal:

> go run libp2p_with_dht.go

This node:  QmQnAZsyiJSovuqg8zjP3nKdm6Pwb75Mpn8HnGyD5WYZ15   [/ip4/127.0.0.1/tcp/3000 /ip4/163.44.166.18/tcp/3000]

chat_HK node

the code from https://github.com/libp2p/go-libp2p-examples/tree/master/chat-with-rendezvous

chat.go

package main

import (
    "bufio"
    "context"
    "flag"
    "fmt"
    "time"

    //"net/http"
    "os"
    "strings"
    "sync"

    "github.com/libp2p/go-libp2p"
    "github.com/libp2p/go-libp2p-core/network"
    "github.com/libp2p/go-libp2p-core/peer"
    "github.com/libp2p/go-libp2p-core/protocol"
    discovery "github.com/libp2p/go-libp2p-discovery"
    logging "github.com/whyrusleeping/go-logging"

    dht "github.com/libp2p/go-libp2p-kad-dht"
    multiaddr "github.com/multiformats/go-multiaddr"

    "github.com/ipfs/go-log"
    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
}

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 {
        config.BootstrapPeers = dht.DefaultBootstrapPeers
    }

    return config, nil
}

var logger = log.Logger("rendezvous")

func handleStream(stream network.Stream) {
    logger.Info("Got a new stream!")

    // Create a buffer stream for non blocking read and write.
    rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream))

    go readData(rw)
    go writeData(rw)

    // 'stream' will stay open until you close it (or the other side closes it).
}

func readData(rw *bufio.ReadWriter) {
    for {
        str, err := rw.ReadString('\n')
        if err != nil {
            fmt.Println("Error reading from buffer")
            panic(err)
        }

        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 {
            fmt.Println("Error reading from stdin")
            panic(err)
        }

        _, 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() {
    log.SetAllLoggers(logging.WARNING)
    //log.SetAllLoggers(logging.DEBUG)
    log.SetLogLevel("rendezvous", "info")
    help := flag.Bool("h", false, "Display Help")
    config, err := ParseFlags()
    if err != nil {
        panic(err)
    }

    if *help {
        fmt.Println("This program demonstrates a simple p2p chat application using libp2p")
        fmt.Println()
        fmt.Println("Usage: Run './chat in two different terminals. Let them connect to the bootstrap nodes, announce themselves and connect to the peers")
        flag.PrintDefaults()
        return
    }

    ctx := context.Background()

    // libp2p.New constructs a new libp2p Host. Other options can be added
    // here.
    host, err := libp2p.New(ctx,
        libp2p.ListenAddrs([]multiaddr.Multiaddr(config.ListenAddresses)...),
    )
    if err != nil {
        panic(err)
    }
    logger.Info("Host created. We are:", host.ID())
    logger.Info(host.Addrs())

    // Set a function as stream handler. 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
    // inhibiting future peer discovery.
    kademliaDHT, err := dht.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.
    logger.Debug("Bootstrapping the DHT")
    //if err = kademliaDHT.Bootstrap(ctx); err != nil {
    //  panic(err)
    //}

    cfg := dht.BootstrapConfig{1, 10 * time.Second, 10 * time.Second}
    if err = kademliaDHT.BootstrapWithConfig(ctx, cfg); err != nil {
        panic(err)
    }

    // Let's connect to the bootstrap nodes first. They will tell us about the
    // other nodes in the network.
    var wg sync.WaitGroup
    for _, peerAddr := range config.BootstrapPeers {
        peerinfo, _ := peer.AddrInfoFromP2pAddr(peerAddr)
        wg.Add(1)
        go func() {
            defer wg.Done()
            if err := host.Connect(ctx, *peerinfo); err != nil {
                logger.Warning(err)
            } else {
                logger.Info("Connection established with bootstrap node:", *peerinfo)
            }
        }()
    }
    wg.Wait()

    // 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.
    logger.Info("Announcing ourselves...")
    routingDiscovery := discovery.NewRoutingDiscovery(kademliaDHT)
    discovery.Advertise(ctx, routingDiscovery, config.RendezvousString)
    logger.Debug("Successfully announced!")

    // Now, look for others who have announced
    // This is like your friend telling you the location to meet you.
    logger.Debug("Searching for other peers...")
    peerChan, err := routingDiscovery.FindPeers(ctx, config.RendezvousString)
    if err != nil {
        panic(err)
    }

    for peer := range peerChan {

        if peer.ID == host.ID() {
            continue
        }
        logger.Debug("Found peer:", peer)

        logger.Debug("Connecting to:", peer)
        stream, err := host.NewStream(ctx, peer.ID, protocol.ID(config.ProtocolID))

        if err != nil {
            logger.Warning("Connection failed:", err)
            continue
        } else {
            rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream))

            go writeData(rw)
            go readData(rw)
        }

        logger.Info("Connected to:", peer)
    }

    select {}
}

Execute commands at the terminal

go run chat.go -peer /ip4/163.44.166.18/tcp/3000/p2p/QmQnAZsyiJSovuqg8zjP3nKdm6Pwb75Mpn8HnGyD5WYZ15

18:47:41.336  INFO rendezvous: Host created. We are: QmbwMU1kZundvxRRELASN5ZkLZ5JX8qUCwL4dsES5vbXyB chat.go:169
18:47:41.336  INFO rendezvous: [/ip6/::1/tcp/56472 /ip4/127.0.0.1/tcp/56471 /ip4/192.168.31.32/tcp/56471] chat.go:170
18:47:41.337 WARNI        dht: error bootstrapping: failed to find any peer in table dht_bootstrap.go:86
18:47:42.428  INFO rendezvous: Connection established with bootstrap node: {QmQnAZsyiJSovuqg8zjP3nKdm6Pwb75Mpn8HnGyD5WYZ15: [/ip4/163.44.166.18/tcp/3000]} chat.go:208
18:47:42.428  INFO rendezvous: Announcing ourselves... chat.go:216
18:47:47.557 WARNI rendezvous: Connection failed: failed to dial : all dials failed
  * [/ip4/127.0.0.1/tcp/33618] dial tcp4 127.0.0.1:33618: connect: connection refused
  * [/ip4/172.21.0.1/tcp/33618] dial tcp4 0.0.0.0:56471->172.21.0.1:33618: i/o timeout
  * [/ip4/172.18.0.1/tcp/33618] dial tcp4 0.0.0.0:56471->172.18.0.1:33618: i/o timeout
  * [/ip4/172.20.0.1/tcp/33618] dial tcp4 0.0.0.0:56471->172.20.0.1:33618: i/o timeout
  * [/ip4/172.22.0.1/tcp/33618] dial tcp4 0.0.0.0:56471->172.22.0.1:33618: i/o timeout
  * [/ip4/172.17.55.60/tcp/33618] dial tcp4 0.0.0.0:56471->172.17.55.60:33618: i/o timeout chat.go:240
18:47:47.559 WARNI rendezvous: Connection failed: failed to dial : all dials failed
  * [/ip6/::1/tcp/56454] dial tcp6 [::1]:56454: connect: connection refused
  * [/ip4/192.168.31.32/tcp/56453] dial tcp4 192.168.31.32:56453: connect: connection refused
  * [/ip4/127.0.0.1/tcp/56453] dial tcp4 127.0.0.1:56453: connect: connection refused chat.go:240

chat_SK node

chat.go is the same as the HK node

go run chat.go -peer /ip4/163.44.166.18/tcp/3000/p2p/QmQnAZsyiJSovuqg8zjP3nKdm6Pwb75Mpn8HnGyD5WYZ15
go: finding github.com/whyrusleeping/go-logging latest
18:46:22.476  INFO rendezvous: Host created. We are: QmNwfDzHQEWyLnU38zmi7UewedoMyq52r5DJEXhjck6j6e chat.go:170
18:46:22.477  INFO rendezvous: [/ip4/127.0.0.1/tcp/33618 /ip4/172.17.55.60/tcp/33618 /ip4/172.22.0.1/tcp/33618 /ip4/172.18.0.1/tcp/33618 /ip4/172.20.0.1/tcp/33618 /ip4/172.21.0.1/tcp/33618] chat.go:171
18:46:22.477 WARNI        dht: error bootstrapping: failed to find any peer in table dht_bootstrap.go:86
18:46:23.421  INFO rendezvous: Connection established with bootstrap node: {QmQnAZsyiJSovuqg8zjP3nKdm6Pwb75Mpn8HnGyD5WYZ15: [/ip4/163.44.166.18/tcp/3000]} chat.go:209
18:46:23.421  INFO rendezvous: Announcing ourselves... chat.go:217
upperwal commented 5 years ago

@maimai88

18:47:47.557 WARNI rendezvous: Connection failed: failed to dial : all dials failed
  * [/ip4/127.0.0.1/tcp/33618] dial tcp4 127.0.0.1:33618: connect: connection refused
  * [/ip4/172.21.0.1/tcp/33618] dial tcp4 0.0.0.0:56471->172.21.0.1:33618: i/o timeout
  * [/ip4/172.18.0.1/tcp/33618] dial tcp4 0.0.0.0:56471->172.18.0.1:33618: i/o timeout
  * [/ip4/172.20.0.1/tcp/33618] dial tcp4 0.0.0.0:56471->172.20.0.1:33618: i/o timeout
  * [/ip4/172.22.0.1/tcp/33618] dial tcp4 0.0.0.0:56471->172.22.0.1:33618: i/o timeout
  * [/ip4/172.17.55.60/tcp/33618] dial tcp4 0.0.0.0:56471->172.17.55.60:33618: i/o timeout chat.go:240

The above shows that chat_HK node is able to discover chat_SK node via bootstrap node but chat_HK node could not connect to chat_SK node. One of the reason this could happen is because node chat_SK node is behind a NAT and you can't dial to 172.21.0.1:33618.

Solutions:

  1. If you have access to the firewall: a) Enable UPnP in your firewall/router and pass libp2p.NATPortMap() as libp2p.Option. b) Open and forward a port manually on your farewall/router.
  2. If you don't have access to the firewall, setup a relay node (should have a dialable public IP) and use it to connect chat_HK and chat_SK.
maimai88 commented 5 years ago

2. chat_HK and chat_SK


Thanks again @upperwal

I don't have access to the firewall, so I modified the code above,when I created the host, I added the libp2p.EnableRelay() option to them.

...

libp2p.New(ctx,
        libp2p.ListenAddrs([]multiaddr.Multiaddr(config.ListenAddresses)...),

        libp2p.EnableRelay(),
        # ===== Also tried the following options ======
        //libp2p.EnableRelay(circuit.OptHop),
        //libp2p.EnableRelay(circuit.OptHop, circuit.OptDiscovery),
        //libp2p.EnableRelay(circuit.OptActive, circuit.OptHop, circuit.OptDiscovery),
    )

...

But the node chat_HK and the node chat_SK still can't connect

Can you help me give me a relay example? Thank you.

Thanks again

upperwal commented 5 years ago

@maimai88 Relay dials are enabled by default but you need to connect it to a node which acts as a relay. You can do that by spawning a new node (on a publicly accessible ip:port) and passing circuit.OptHop in Libp2p.Option.

Now connect all your nodes (A -> R, B -> R) to this relay node. Now you can dial A -> B and it would use the relay node if it could not dial directly.

maimai88 commented 5 years ago

@maimai88 Relay dials are enabled by default but you need to connect it to a node which acts as a relay. You can do that by spawning a new node (on a publicly accessible ip:port) and passing circuit.OptHop in Libp2p.Option.

Now connect all your nodes (A -> R, B -> R) to this relay node. Now you can dial A -> B and it would use the relay node if it could not dial directly.


Thanks sir @upperwal Imitation relay example

Node R

Node_JP is a server in Japan that has a separate public IP.I use it as a relay node.

deploy code:

package main

import (
    "context"
    "flag"
    "fmt"

    "github.com/libp2p/go-libp2p"
    circuit "github.com/libp2p/go-libp2p-circuit"
)

func main() {

    listenPort := flag.Int("l", 3000, "wait for incoming connections")
    flag.Parse()

    // Tell the host to relay connections for other peers (The ability to *use*
    // a relay vs the ability to *be* a relay)
    h2, err := libp2p.New(
        context.Background(),
        libp2p.ListenAddrStrings(fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", *listenPort)),
        libp2p.EnableRelay(circuit.OptHop))
    if err != nil {
        panic(err)
    }

    for _, ips := range h2.Addrs() {
        fmt.Printf("%s/p2p/%s\n", ips, h2.ID())
    }

    select {}

}

Node A

deploy code:

package main

import (
    "context"
    "flag"
    "fmt"

    "github.com/libp2p/go-libp2p-core/network"
    "github.com/libp2p/go-libp2p-core/peer"

    "github.com/libp2p/go-libp2p"
    "github.com/multiformats/go-multiaddr"
    ma "github.com/multiformats/go-multiaddr"
)

func addr2info(addrStr string) (*peer.AddrInfo, error) {

    addr, err := multiaddr.NewMultiaddr(addrStr)
    if err != nil {
        panic(err)
    }

    //peerstore.InfoFromP2pAddr(addr)

    return peer.AddrInfoFromP2pAddr(addr)
}

func main() {

    var relayHost string
    var dialID string

    flag.StringVar(&relayHost, "relay", "", "relay addr")
    flag.StringVar(&dialID, "dial", "", "dial Node ID")

    flag.Parse()

    relayAddrInfo, err := addr2info(relayHost)

    // Zero out the listen addresses for the host, so it can only communicate
    // via p2p-circuit for our example
    h3, err := libp2p.New(context.Background(), libp2p.ListenAddrs(), libp2p.EnableRelay())
    if err != nil {
        panic(err)
    }

    if err := h3.Connect(context.Background(), *relayAddrInfo); err != nil {
        panic(err)
    }

    // Now, to test things, let's set up a protocol handler on h3
    h3.SetStreamHandler("/cats", func(s network.Stream) {
        fmt.Println("Meow! It worked!")
        s.Close()
    })

    // Creates a relay address
    relayaddr, err := ma.NewMultiaddr("/p2p-circuit/p2p/" + h3.ID().Pretty())
    if err != nil {
        panic(err)
    }

    fmt.Println(relayaddr)
    fmt.Println(h3.ID())

    select {}
}

Node B

package main

import (
    "context"
    "flag"
    "fmt"

    swarm "github.com/libp2p/go-libp2p-swarm"

    "github.com/libp2p/go-libp2p-core/peer"

    "github.com/multiformats/go-multiaddr"
    ma "github.com/multiformats/go-multiaddr"

    "github.com/libp2p/go-libp2p"
    circuit "github.com/libp2p/go-libp2p-circuit"
)

func addr2info(addrStr string) (*peer.AddrInfo, error) {

    addr, err := multiaddr.NewMultiaddr(addrStr)
    if err != nil {
        panic(err)
    }

    //peerstore.InfoFromP2pAddr(addr)

    return peer.AddrInfoFromP2pAddr(addr)
}

func main() {

    var relayHost string
    var dialID string

    flag.StringVar(&relayHost, "relay", "", "relay addr")
    flag.StringVar(&dialID, "dial", "", "dial Node ID")

    flag.Parse()

    relayAddrInfo, err := addr2info(relayHost)

    h1, err := libp2p.New(context.Background(), libp2p.EnableRelay(circuit.OptDiscovery))
    if err != nil {
        panic(err)
    }

    if err := h1.Connect(context.Background(), *relayAddrInfo); err != nil {
        panic(err)
    }

    dialNodeID, err := peer.IDB58Decode(dialID)

    if err != nil {
        panic(err)
    }

    _, err = h1.NewStream(context.Background(), dialNodeID, "/cats")
    if err == nil {
        fmt.Println("Didnt actually expect to get a stream here. What happened?")
        return
    }
    fmt.Println("Okay, no connection from h1 to h3: ", err)
    fmt.Println("Just as we suspected")

    h1.Network().(*swarm.Swarm).Backoff().Clear(dialNodeID)

    relayaddr, err := ma.NewMultiaddr("/p2p-circuit/p2p/" + dialNodeID.Pretty())
    if err != nil {
        panic(err)
    }

    h3relayInfo := peer.AddrInfo{
        ID:    dialNodeID,
        Addrs: []ma.Multiaddr{relayaddr},
    }

    if err := h1.Connect(context.Background(), h3relayInfo); err != nil {
        panic(err)
    }

    // Woohoo! we're connected!
    s, err := h1.NewStream(context.Background(), dialNodeID, "/cats")
    if err != nil {
        fmt.Println("huh, this should have worked: ", err)
        return
    }

    s.Read(make([]byte, 1)) // block until the handler closes the stream

    fmt.Println("end")

}

Run

Run 3 nodes separately

Run Node R

> go run R.go -l 3000

// get result 
/ip4/163.44.166.18/tcp/3000/p2p/QmRFNL6GS9wKsyx678gqEmYcxPX2Nqp355YCwzxiGdrKLE

Run Node A

> go run A.go -relay /ip4/163.44.166.18/tcp/3000/p2p/QmRFNL6GS9wKsyx678gqEmYcxPX2Nqp355YCwzxiGdrKLE

// get result
/p2p-circuit/ipfs/QmUKYigrdedpDLeNJ1ZFcKmwBGB4VXnqaPJBWenEu9mdtb
QmUKYigrdedpDLeNJ1ZFcKmwBGB4VXnqaPJBWenEu9mdtb

Run Node B

> go run B.go -relay /ip4/163.44.166.18/tcp/3000/p2p/QmRFNL6GS9wKsyx678gqEmYcxPX2Nqp355YCwzxiGdrKLE -dial QmUKYigrdedpDLeNJ1ZFcKmwBGB4VXnqaPJBWenEu9mdtb

get result


Okay, no connection from h1 to h3:  failed to dial QmUKYigrdedpDLeNJ1ZFcKmwBGB4VXnqaPJBWenEu9mdtb: no addresses
Just as we suspected
panic: failed to dial : all dials failed
  * [/p2p-circuit/ipfs/QmUKYigrdedpDLeNJ1ZFcKmwBGB4VXnqaPJBWenEu9mdtb] Failed to dial through 0 known relay hosts

goroutine 1 [running]:
main.main()
    /go/src/demo/p2p/simple/relay/B.go:79 +0xb35
exit status 2

PS:If node A and node B are in the same network segment, there is no problem sending data from B to A. Terminal direct output

Meow! It worked!
maimai88 commented 5 years ago

@maimai88 Relay dials are enabled by default but you need to connect it to a node which acts as a relay. You can do that by spawning a new node (on a publicly accessible ip:port) and passing circuit.OptHop in Libp2p.Option.

Now connect all your nodes (A -> R, B -> R) to this relay node. Now you can dial A -> B and it would use the relay node if it could not dial directly.


thanks @upperwal

I find a relay peer example in go-libp2p-relay . Following the project's README.md i get the results I want.

thanks again

th3kave commented 3 years ago

Hi,

For benefit of anyone trying to do this with later versions of libp2p, I wasn't able to get the bootstrap host working with version 0.13.0 (github.com/libp2p/go-libp2p v0.13.0) until I added the option libp2p.ForceReachabilityPublic() as follows:

   ....
   ....
      opts := []libp2p.Option{
        libp2p.ListenAddrs(sourceMultiAddr),
                libp2p.ForceReachabilityPublic(),    //  <--- ADD THIS OPTION
        //libp2p.NATPortMap(),
        //libp2p.DefaultSecurity,
        //libp2p.EnableRelay(circuit.OptActive, circuit.OptHop, circuit.OptDiscovery),
        libp2p.Identity(prvKey),
    }
   ....
   ....

Would be amazing if you guys could elaborate on how this option should be used or how to configure a bootstrap to be used privately.

Thanks a lot :)