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 }