ceremonyclient/go-libp2p/examples/routed-echo/bootstrap.go
2023-08-20 23:07:43 -05:00

131 lines
3.7 KiB
Go

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
}