Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,19 +279,20 @@ Thanks to all our amazing contributors!
</a>
</td>
<td align="center">
<a href="https://github.com/ABHINAVGARG05">
<img src="https://avatars.githubusercontent.com/u/143117260?v=4" width="100;" alt="ABHINAVGARG05"/>
<a href="https://github.com/Janmesh23">
<img src="https://avatars.githubusercontent.com/u/183159485?v=4" width="100;" alt="Janmesh23"/>
<br />
<sub><b>Abhinav Garg</b></sub>
<sub><b>Janmesh </b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Janmesh23">
<img src="https://avatars.githubusercontent.com/u/183159485?v=4" width="100;" alt="Janmesh23"/>
<a href="https://github.com/ABHINAVGARG05">
<img src="https://avatars.githubusercontent.com/u/143117260?v=4" width="100;" alt="ABHINAVGARG05"/>
<br />
<sub><b>Janmesh </b></sub>
<sub><b>Abhinav Garg</b></sub>
</a>
</td>

<td align="center">
<a href="https://github.com/Akash29g">
<img src="https://avatars.githubusercontent.com/u/77738997?v=4" width="100;" alt="Akash29g"/>
Expand Down
140 changes: 139 additions & 1 deletion cmd/discover.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@ import (
"os"
"os/signal"
"syscall"
"time"

"github.com/libp2p/go-libp2p/core/host"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/spf13/cobra"

"github.com/substantialcattle5/sietch/internal/config"
"github.com/substantialcattle5/sietch/internal/discover"
"github.com/substantialcattle5/sietch/internal/p2p"
"github.com/substantialcattle5/sietch/internal/ui"
)

// discoverCmd represents the discover command
Expand All @@ -28,15 +31,20 @@ This command creates a temporary libp2p node that broadcasts its presence and
listens for other Sietch vaults on the local network. When peers are discovered,
their information is displayed, including their peer ID and addresses.

Example:
With the --select flag, you can interactively choose which peers to pair with
for selective key exchange, providing fine-grained control over trust relationships.

Examples:
sietch discover # Run discovery with default settings
sietch discover --timeout 30 # Run discovery for 30 seconds
sietch discover --continuous # Run discovery until interrupted
sietch discover --select # Interactive peer selection for pairing
sietch discover --port 9001 # Use a specific port for the libp2p node`,
RunE: func(cmd *cobra.Command, args []string) error {
// Get command flags
timeout, _ := cmd.Flags().GetInt("timeout")
continuous, _ := cmd.Flags().GetBool("continuous")
selectMode, _ := cmd.Flags().GetBool("select")
port, _ := cmd.Flags().GetInt("port")
verbose, _ := cmd.Flags().GetBool("verbose")
vaultPath, _ := cmd.Flags().GetString("vault-path")
Expand Down Expand Up @@ -101,6 +109,9 @@ Example:
defer func() { _ = discovery.Stop() }()

// Run the discovery loop
if selectMode {
return runDiscoveryWithSelection(ctx, host, syncService, peerChan, timeout, continuous)
}
return discover.RunDiscoveryLoop(ctx, host, syncService, peerChan, timeout, continuous)
},
}
Expand All @@ -119,7 +130,134 @@ func init() {
// Add command flags
discoverCmd.Flags().IntP("timeout", "t", 60, "Discovery timeout in seconds (ignored with --continuous)")
discoverCmd.Flags().BoolP("continuous", "c", false, "Run discovery continuously until interrupted")
discoverCmd.Flags().BoolP("select", "s", false, "Interactive peer selection mode for pairing")
discoverCmd.Flags().IntP("port", "p", 0, "Port to use for libp2p (0 for random port)")
discoverCmd.Flags().BoolP("verbose", "v", false, "Enable verbose output")
discoverCmd.Flags().StringP("vault-path", "V", "", "Path to the vault directory (defaults to current directory)")
}

// runDiscoveryWithSelection runs discovery and allows interactive peer selection for pairing
func runDiscoveryWithSelection(ctx context.Context, host host.Host, syncService *p2p.SyncService, peerChan <-chan peer.AddrInfo, timeout int, continuous bool) error {
// Disable auto-trust for selective pairing
syncService.SetAutoTrustAllPeers(false)

// Collect discovered peers
discoveredPeers := make([]peer.AddrInfo, 0)
discoveredPeersMap := make(map[peer.ID]bool)

var timeoutChan <-chan time.Time
if !continuous {
timeoutChan = time.After(time.Duration(timeout) * time.Second)
fmt.Printf(" Discovery will run for %d seconds. Press Ctrl+C to stop earlier.\n\n", timeout)
} else {
fmt.Println(" Discovery will run until interrupted. Press Ctrl+C to stop.")
fmt.Println()
}

fmt.Println("🔍 Discovering peers for selection...")

// Discovery loop
discoveryComplete := false
for !discoveryComplete {
select {
case p, ok := <-peerChan:
if !ok {
discoveryComplete = true
continue
}

if p.ID == host.ID() || discoveredPeersMap[p.ID] {
continue
}

discoveredPeersMap[p.ID] = true
discoveredPeers = append(discoveredPeers, p)

fmt.Printf("✅ Discovered peer #%d\n", len(discoveredPeers))
fmt.Printf(" ID: %s\n", p.ID.String())
fmt.Println(" Addresses:")
for _, addr := range p.Addrs {
fmt.Printf(" - %s\n", addr.String())
}
fmt.Println()

case <-timeoutChan:
fmt.Printf("\n⌛ Discovery timeout reached after %d seconds.\n", timeout)
discoveryComplete = true

case <-ctx.Done():
fmt.Println("\nDiscovery interrupted")
discoveryComplete = true
}
}

if len(discoveredPeers) == 0 {
fmt.Println("No peers discovered. Make sure other Sietch vaults are running and discoverable.")
return nil
}

// Let user select peers
selectedPeers, err := ui.SelectPeersInteractively(discoveredPeers)
if err != nil {
return fmt.Errorf("peer selection failed: %v", err)
}

if len(selectedPeers) == 0 {
fmt.Println("No peers selected for pairing.")
return nil
}

// Set up pairing window (5 minutes default)
windowDuration := 5 * time.Minute
until := time.Now().Add(windowDuration)

// Request pairing with selected peers
for _, peerID := range selectedPeers {
syncService.RequestPair(peerID, until)
}

fmt.Printf("\n⏰ Pairing window active for %v\n", windowDuration)
fmt.Println("Waiting for mutual pairing...")

// Wait for pairing to complete
timeoutChan = time.After(windowDuration)
pairedCount := 0

for {
select {
case <-timeoutChan:
fmt.Printf("\n⌛ Pairing window expired after %v\n", windowDuration)
fmt.Printf("Successfully paired with %d peer(s)\n", pairedCount)
return nil

case <-ctx.Done():
fmt.Printf("\nPairing interrupted. Successfully paired with %d peer(s)\n", pairedCount)
return nil

default:
// Check if any selected peers have been successfully paired
for _, peerID := range selectedPeers {
if syncService.HasPeer(peerID) {
pairedCount++
fmt.Printf("✅ Successfully paired with peer: %s\n", peerID.String())

// Remove from selected list to avoid counting twice
for i, p := range selectedPeers {
if p == peerID {
selectedPeers = append(selectedPeers[:i], selectedPeers[i+1:]...)
break
}
}
}
}

// If all peers are paired, we're done
if len(selectedPeers) == 0 {
fmt.Printf("\n🎉 All selected peers successfully paired!\n")
return nil
}

time.Sleep(1 * time.Second)
}
}
}
Loading
Loading