From de1e143fb27d6675c1e3a7a236debcf816f7f2b2 Mon Sep 17 00:00:00 2001 From: dozyio Date: Sun, 9 Nov 2025 20:24:10 +0000 Subject: [PATCH] feat: dht findpeer --- lib/dht.go | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 102 insertions(+), 1 deletion(-) diff --git a/lib/dht.go b/lib/dht.go index 0bbb82e..9cda62e 100644 --- a/lib/dht.go +++ b/lib/dht.go @@ -2,8 +2,11 @@ package vole import ( "context" + "fmt" "time" + "github.com/libp2p/go-libp2p" + dht "github.com/libp2p/go-libp2p-kad-dht" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" @@ -107,6 +110,53 @@ func DhtGetClosestPeers(ctx context.Context, key []byte, proto protocol.ID, ma m return ais, nil } +func DhtFindPeer(ctx context.Context, peerToFind peer.ID, bootstrappers []peer.AddrInfo) (*peer.AddrInfo, error) { + h, err := libp2p.New(libp2p.NoListenAddrs) + if err != nil { + return nil, fmt.Errorf("failed to create host: %w", err) + } + defer h.Close() + + dhtOpts := []dht.Option{ + dht.Mode(dht.ModeClient), + dht.BootstrapPeers(bootstrappers...), + } + + kademliaDHT, err := dht.New(ctx, h, dhtOpts...) + if err != nil { + return nil, fmt.Errorf("failed to create dht client: %w", err) + } + defer kademliaDHT.Close() + + if err = kademliaDHT.Bootstrap(ctx); err != nil { + return nil, fmt.Errorf("bootstrap failed: %w", err) + } + + deadline := time.NewTimer(5 * time.Second) + defer deadline.Stop() + + ticker := time.NewTicker(100 * time.Millisecond) + defer ticker.Stop() + + for kademliaDHT.RoutingTable().Size() < 10 { + select { + case <-deadline.C: + return nil, fmt.Errorf("routing table warmup timed out (size=%d)", kademliaDHT.RoutingTable().Size()) + case <-ctx.Done(): + return nil, fmt.Errorf("routing table warmup canceled: %w (size=%d)", ctx.Err(), kademliaDHT.RoutingTable().Size()) + case <-ticker.C: + // keep polling + } + } + + peerInfo, err := kademliaDHT.FindPeer(ctx, peerToFind) + if err != nil { + return nil, fmt.Errorf("FindPeer failed: %w", err) + } + + return &peerInfo, nil +} + func DhtPing(ctx context.Context, proto protocol.ID, ma multiaddr.Multiaddr) error { ai, err := peer.AddrInfoFromP2pAddr(ma) if err != nil { diff --git a/main.go b/main.go index 974dea1..dc733c7 100644 --- a/main.go +++ b/main.go @@ -12,6 +12,7 @@ import ( "github.com/urfave/cli/v2" "github.com/ipfs/go-cid" + dht "github.com/libp2p/go-libp2p-kad-dht" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/protocol" "github.com/libp2p/go-libp2p/p2p/protocol/identify" @@ -234,6 +235,56 @@ func main() { }, }, }, + { + Name: "findpeer", + ArgsUsage: " ", + Usage: "findpeer resolves the multiaddrs for a peer ID", + Description: "creates a libp2p peer and sends DHT get closest peers requests until the peer is found. An optional bootstrapper can be provided, otherwise it falls back to the default bootstrappers", + Action: func(c *cli.Context) error { + if c.NArg() == 0 || c.NArg() > 2 { + return fmt.Errorf("invalid number of arguments") + } + + peerIDStr := c.Args().Get(0) + bootstrappers := make([]peer.AddrInfo, 0) + + if c.NArg() == 2 { + maStr := c.Args().Get(1) + + ma, err := multiaddr.NewMultiaddr(maStr) + if err != nil { + return err + } + + bootstrapper, err := peer.AddrInfoFromP2pAddr(ma) + if err != nil { + return fmt.Errorf("failed to parse bootstrapper multiaddr: %w", err) + } + + bootstrappers = append(bootstrappers, *bootstrapper) + + } else { + bootstrappers = dht.GetDefaultBootstrapPeerAddrInfos() + } + + peerID, err := peer.Decode(peerIDStr) + if err != nil { + return err + } + + ais, err := vole.DhtFindPeer(c.Context, peerID, bootstrappers) + if err != nil { + return err + } + + for _, a := range ais.Addrs { + fmt.Println(a) + } + + return nil + }, + Flags: []cli.Flag{}, + }, { Name: "ping", ArgsUsage: "", @@ -345,7 +396,6 @@ Note: may not work with some transports such as p2p-circuit (not applicable) and Name: "ping", ArgsUsage: "", Flags: []cli.Flag{ - &cli.BoolFlag{ Name: "force-relay", Usage: `Ping the peer over a relay instead of a direct connection`, @@ -440,6 +490,7 @@ var bitswapGetCmd = &cli.Command{ return vole.GetBitswapCID(root, ai) }, } + var bitswapCheckCmd = &cli.Command{ Name: "check", ArgsUsage: " ",