/ examples / routed-echo / bootstrap.go
bootstrap.go
  1  package main
  2  
  3  import (
  4  	"context"
  5  	"encoding/json"
  6  	"errors"
  7  	"fmt"
  8  	"io"
  9  	"log"
 10  	"net/http"
 11  	"sync"
 12  
 13  	"github.com/libp2p/go-libp2p/core/host"
 14  	"github.com/libp2p/go-libp2p/core/peer"
 15  	"github.com/libp2p/go-libp2p/core/peerstore"
 16  
 17  	ma "github.com/multiformats/go-multiaddr"
 18  )
 19  
 20  var (
 21  	IPFS_PEERS = convertPeers([]string{
 22  		"/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ",
 23  		"/ip4/104.236.179.241/tcp/4001/p2p/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM",
 24  		"/ip4/128.199.219.111/tcp/4001/p2p/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu",
 25  		"/ip4/104.236.76.40/tcp/4001/p2p/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64",
 26  		"/ip4/178.62.158.247/tcp/4001/p2p/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd",
 27  		"/ip6/2604:a880:1:20::203:d001/tcp/4001/p2p/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM",
 28  		"/ip6/2400:6180:0:d0::151:6001/tcp/4001/p2p/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu",
 29  		"/ip6/2604:a880:800:10::4a:5001/tcp/4001/p2p/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64",
 30  		"/ip6/2a03:b0c0:0:1010::23:1001/tcp/4001/p2p/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd",
 31  	})
 32  	LOCAL_PEER_ENDPOINT = "http://localhost:5001/api/v0/id"
 33  )
 34  
 35  // Borrowed from ipfs code to parse the results of the command `ipfs id`
 36  type IdOutput struct {
 37  	ID              string
 38  	PublicKey       string
 39  	Addresses       []string
 40  	AgentVersion    string
 41  	ProtocolVersion string
 42  }
 43  
 44  // quick and dirty function to get the local ipfs daemons address for bootstrapping
 45  func getLocalPeerInfo() []peer.AddrInfo {
 46  	resp, err := http.PostForm(LOCAL_PEER_ENDPOINT, nil)
 47  	if err != nil {
 48  		log.Fatalln(err)
 49  	}
 50  	body, err := io.ReadAll(resp.Body)
 51  	if err != nil {
 52  		log.Fatalln(err)
 53  	}
 54  	var js IdOutput
 55  	err = json.Unmarshal(body, &js)
 56  	if err != nil {
 57  		log.Fatalln(err)
 58  	}
 59  	for _, addr := range js.Addresses {
 60  		// For some reason, possibly NAT traversal, we need to grab the loopback ip address
 61  		if addr[0:8] == "/ip4/127" {
 62  			return convertPeers([]string{addr})
 63  		}
 64  	}
 65  	log.Fatalln(err)
 66  	return make([]peer.AddrInfo, 1) // not reachable, but keeps the compiler happy
 67  }
 68  
 69  func convertPeers(peers []string) []peer.AddrInfo {
 70  	pinfos := make([]peer.AddrInfo, len(peers))
 71  	for i, addr := range peers {
 72  		maddr := ma.StringCast(addr)
 73  		p, err := peer.AddrInfoFromP2pAddr(maddr)
 74  		if err != nil {
 75  			log.Fatalln(err)
 76  		}
 77  		pinfos[i] = *p
 78  	}
 79  	return pinfos
 80  }
 81  
 82  // This code is borrowed from the go-ipfs bootstrap process
 83  func bootstrapConnect(ctx context.Context, ph host.Host, peers []peer.AddrInfo) error {
 84  	if len(peers) < 1 {
 85  		return errors.New("not enough bootstrap peers")
 86  	}
 87  
 88  	errs := make(chan error, len(peers))
 89  	var wg sync.WaitGroup
 90  	for _, p := range peers {
 91  
 92  		// performed asynchronously because when performed synchronously, if
 93  		// one `Connect` call hangs, subsequent calls are more likely to
 94  		// fail/abort due to an expiring context.
 95  		// Also, performed asynchronously for dial speed.
 96  
 97  		wg.Add(1)
 98  		go func(p peer.AddrInfo) {
 99  			defer wg.Done()
100  			defer log.Println(ctx, "bootstrapDial", ph.ID(), p.ID)
101  			log.Printf("%s bootstrapping to %s", ph.ID(), p.ID)
102  
103  			ph.Peerstore().AddAddrs(p.ID, p.Addrs, peerstore.PermanentAddrTTL)
104  			if err := ph.Connect(ctx, p); err != nil {
105  				log.Println(ctx, "bootstrapDialFailed", p.ID)
106  				log.Printf("failed to bootstrap with %v: %s", p.ID, err)
107  				errs <- err
108  				return
109  			}
110  			log.Println(ctx, "bootstrapDialSuccess", p.ID)
111  			log.Printf("bootstrapped with %v", p.ID)
112  		}(p)
113  	}
114  	wg.Wait()
115  
116  	// our failure condition is when no connection attempt succeeded.
117  	// So drain the errs channel, counting the results.
118  	close(errs)
119  	count := 0
120  	var err error
121  	for err = range errs {
122  		if err != nil {
123  			count++
124  		}
125  	}
126  	if count == len(peers) {
127  		return fmt.Errorf("failed to bootstrap. %s", err)
128  	}
129  	return nil
130  }