go-libp2p-examples
go-libp2p-examples copied to clipboard
how to create my own bootstrap peer like DefaultBootstrapPeers in github.com/libp2p/go-libp2p-kad-dht/dht_bootstrap.go:16
**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
@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 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.
- Node Japan as bootstrap node
- Node Hong Kong as chat_HK node
- Node South Korea as chat_SK node
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
@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:
- If you have access to the firewall:
a) Enable UPnP in your firewall/router and pass
libp2p.NATPortMap()
aslibp2p.Option
. b) Open and forward a port manually on your farewall/router. - 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
andchat_SK
.
2.
chat_HK
andchat_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
@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 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
inLibp2p.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 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
inLibp2p.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
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 :)