package main import ( "context" "encoding/json" "errors" "fmt" "io" "log" "net/http" "sync" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/peerstore" ma "github.com/multiformats/go-multiaddr" ) var ( IPFS_PEERS = convertPeers([]string{ "/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", "/ip4/104.236.179.241/tcp/4001/p2p/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM", "/ip4/128.199.219.111/tcp/4001/p2p/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu", "/ip4/104.236.76.40/tcp/4001/p2p/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64", "/ip4/178.62.158.247/tcp/4001/p2p/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd", "/ip6/2604:a880:1:20::203:d001/tcp/4001/p2p/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM", "/ip6/2400:6180:0:d0::151:6001/tcp/4001/p2p/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu", "/ip6/2604:a880:800:10::4a:5001/tcp/4001/p2p/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64", "/ip6/2a03:b0c0:0:1010::23:1001/tcp/4001/p2p/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd", }) LOCAL_PEER_ENDPOINT = "http://localhost:5001/api/v0/id" ) // Borrowed from ipfs code to parse the results of the command `ipfs id` type IdOutput struct { ID string PublicKey string Addresses []string AgentVersion string ProtocolVersion string } // quick and dirty function to get the local ipfs daemons address for bootstrapping func getLocalPeerInfo() []peer.AddrInfo { resp, err := http.PostForm(LOCAL_PEER_ENDPOINT, nil) if err != nil { log.Fatalln(err) } body, err := io.ReadAll(resp.Body) if err != nil { log.Fatalln(err) } var js IdOutput err = json.Unmarshal(body, &js) if err != nil { log.Fatalln(err) } for _, addr := range js.Addresses { // For some reason, possibly NAT traversal, we need to grab the loopback ip address if addr[0:8] == "/ip4/127" { return convertPeers([]string{addr}) } } log.Fatalln(err) return make([]peer.AddrInfo, 1) // not reachable, but keeps the compiler happy } func convertPeers(peers []string) []peer.AddrInfo { pinfos := make([]peer.AddrInfo, len(peers)) for i, addr := range peers { maddr := ma.StringCast(addr) p, err := peer.AddrInfoFromP2pAddr(maddr) if err != nil { log.Fatalln(err) } pinfos[i] = *p } return pinfos } // This code is borrowed from the go-ipfs bootstrap process func bootstrapConnect(ctx context.Context, ph host.Host, peers []peer.AddrInfo) error { if len(peers) < 1 { return errors.New("not enough bootstrap peers") } errs := make(chan error, len(peers)) var wg sync.WaitGroup for _, p := range peers { // performed asynchronously because when performed synchronously, if // one `Connect` call hangs, subsequent calls are more likely to // fail/abort due to an expiring context. // Also, performed asynchronously for dial speed. wg.Add(1) go func(p peer.AddrInfo) { defer wg.Done() defer log.Println(ctx, "bootstrapDial", ph.ID(), p.ID) log.Printf("%s bootstrapping to %s", ph.ID(), p.ID) ph.Peerstore().AddAddrs(p.ID, p.Addrs, peerstore.PermanentAddrTTL) if err := ph.Connect(ctx, p); err != nil { log.Println(ctx, "bootstrapDialFailed", p.ID) log.Printf("failed to bootstrap with %v: %s", p.ID, err) errs <- err return } log.Println(ctx, "bootstrapDialSuccess", p.ID) log.Printf("bootstrapped with %v", p.ID) }(p) } wg.Wait() // our failure condition is when no connection attempt succeeded. // So drain the errs channel, counting the results. close(errs) count := 0 var err error for err = range errs { if err != nil { count++ } } if count == len(peers) { return fmt.Errorf("failed to bootstrap. %s", err) } return nil }