-
Notifications
You must be signed in to change notification settings - Fork 354
Comparing changes
Open a pull request
base repository: mysteriumnetwork/node
base: master
head repository: shellroute/node
compare: main
- 15 commits
- 36 files changed
- 1 contributor
Commits on May 28, 2026
-
fix: detect WireGuard handshake when nanosecond component is zero
The condition required both hsSec > 0 AND hsNano > 0, but a valid handshake can have nsec=0 (when it happens at an exact second boundary). Changed to OR so any non-zero timestamp is recognized as a successful handshake.
Configuration menu - View commit details
-
Copy full SHA for db8ce1c - Browse repository at this point
Copy the full SHA db8ce1cView commit details -
fix: WireGuard log level respects --log-level flag
WireGuard device logger was hardcoded to LogLevelVerbose, producing excessive DEBUG output (peer handshakes, keepalives) regardless of the node's --log-level setting. Added logconfig.WireGuardLogLevel() that maps zerolog levels: trace/debug → LogLevelVerbose (handshakes, keepalives) info/warn → LogLevelError (errors only) error+ → LogLevelSilent All 5 device creation sites now call logconfig.WireGuardLogLevel() instead of hardcoding a level.
Configuration menu - View commit details
-
Copy full SHA for b28a2c7 - Browse repository at this point
Copy the full SHA b28a2c7View commit details -
fix: proxyclient lifecycle — immediate close, sync bind, nil guards
- Close(): close device immediately instead of 2-minute deferred goroutine. Orphaned devices accumulated, sending rogue keepalives. - ConfigureDevice(): close old device/proxy before creating new ones. ReConfigureDevice leaked old netstack, device, and proxy server. - Proxy(): synchronous net.Listen + server.Serve so port conflicts fail ConfigureDevice immediately instead of silently in background. - PeerStats(): nil/lock guard on Device to prevent panic after Close. - Suppress http.ErrServerClosed log noise on normal shutdown.
Configuration menu - View commit details
-
Copy full SHA for 57a54d8 - Browse repository at this point
Copy the full SHA 57a54d8View commit details -
fix: skip interface release and cleanup in proxymode
- Stop(): skip ReleaseInterface when ProxyPort > 0. Proxymode names interfaces myst<port> without AllocateInterface, so release always errored with "allocated interface not found". - StartConsumerMode(): same skip on configure failure path. - cleanAbandonedInterfaces(): skip in proxymode (same reason as dVPN — multiple concurrent connections should not destroy each other).
Configuration menu - View commit details
-
Copy full SHA for 6194722 - Browse repository at this point
Copy the full SHA 6194722View commit details -
fix: skip keepalive auto-disconnect in proxymode
In proxymode the gateway manages tunnel health via its own sentinel and probe mechanisms. The P2P keepalive failure (3 × 5s) was killing perfectly healthy WireGuard tunnels because the P2P channel (NATS/UDP signaling) is less stable than the tunnel itself in Docker.
Configuration menu - View commit details
-
Copy full SHA for e1db169 - Browse repository at this point
Copy the full SHA e1db169View commit details -
fix: suppress context canceled error in proxy CONNECT handler
When a probe client closes before the tunnel dial completes (deadline exceeded), DialContext returns context.Canceled. This is not a tunnel failure — just the client leaving early. Silencing prevents noisy ERR logs every 30 seconds from the gateway probe loop.
Configuration menu - View commit details
-
Copy full SHA for 1b8adde - Browse repository at this point
Copy the full SHA 1b8addeView commit details
Commits on May 30, 2026
-
fix: force balance resync when spendable balance is near zero
NeedsForceSync only checked BCBalance (channel capacity), not the actual spendable balance (BCBalance - GrandTotalPromised + BCSettled). After a Pilvytis top-up, BCBalance stays the same but Hermes resets GrandTotalPromised. Without checking spendable balance, the node used stale GrandTotalPromised and thought balance was zero for up to 30 minutes (offchain sync interval).
Configuration menu - View commit details
-
Copy full SHA for 29a03b4 - Browse repository at this point
Copy the full SHA 29a03b4View commit details -
fix: enable periodic balance sync for offchain (Pilvytis) identities
lifetimeBCSync skipped entirely for offchain identities, meaning the balance was never refreshed from Hermes during the node's lifetime. After a top-up or limit change, the node used stale GrandTotalPromised indefinitely. Now offchain identities get the same periodic sync as on-chain ones (default: every 1 hour, configurable via --payments.balance-long-poll.interval).
Configuration menu - View commit details
-
Copy full SHA for d494546 - Browse repository at this point
Copy the full SHA d494546View commit details -
fix: fast balance sync when spendable balance below 1 MYST
When GetBalance() drops below 1 MYST, the periodic sync interval switches from the configured poll interval (1 min) to 10 seconds. The node detects low balance itself and resyncs with Hermes frequently until a top-up is picked up — no external trigger needed.
Configuration menu - View commit details
-
Copy full SHA for a1821fc - Browse repository at this point
Copy the full SHA a1821fcView commit details -
test: balance sync — force sync on low spendable, fast interval, thre…
…shold - TestNeedsForceSync_SpendableBalanceNearZero: spendable=0 triggers sync even when BCBalance (channel capacity) is high - TestNeedsForceSync_HealthyBalance: 4 MYST spendable does not trigger - TestPeriodicSync_FastIntervalWhenLowBalance: verifies threshold comparison - TestLowBalanceThreshold_Is1MYST: threshold is exactly 1e18 wei
Configuration menu - View commit details
-
Copy full SHA for d48c949 - Browse repository at this point
Copy the full SHA d48c949View commit details -
build: add scripts/run-tests.sh for our modified packages
Runs proxyclient, balance tracker, and connection manager tests with race detector, plus build checks across all three packages.
Configuration menu - View commit details
-
Copy full SHA for 441031d - Browse repository at this point
Copy the full SHA 441031dView commit details
Commits on Aug 4, 2026
-
Configuration menu - View commit details
-
Copy full SHA for 0a2d83d - Browse repository at this point
Copy the full SHA 0a2d83dView commit details
Commits on Aug 12, 2026
-
Merge pull request #1 from shellroute/fix/manager-lifecycle-clean
Serialize connection manager lifecycle to prevent stale managers during concurrent connect/disconnect
Configuration menu - View commit details
-
Copy full SHA for c035faa - Browse repository at this point
Copy the full SHA c035faaView commit details
Commits on Sep 3, 2026
-
fix: make node connection lifecycle bounded to prevent gateway startu…
…p deadlock (#2) * docs: add AGENTS.md as primary agent instructions, symlink CLAUDE.md * docs: add node lifecycle recovery plan * fix: make node connection lifecycle bounded Replace RWMutex/stripe barriers with generation-fenced state machine. No coordinator mutex spans Manager network calls. - Context-aware MultiManager API (Connect, Disconnect, Reconnect) - Generation fencing: bulk cleanup covers all pre-existing managers - Single-flight bulk: concurrent callers share one cleanup operation - Bounded shutdown: 5s deadline in Node.Kill, all stops proceed - ErrLifecycleBusy sentinel for active reconciliation - TequilAPI passes request context, returns 503 on lifecycle busy - Goroutine dump on bulk timeout for incident diagnosis - 15 deterministic lifecycle/race tests * fix: address review findings — orphan prevention, bulk ctx continuation, CancelCurrentOperation, retryable cleanup, port reservation * fix: worker-owned Connect finalization, server-owned bulk worker goroutine * fix: atomic port reservation, operation tracking, cleanup waits for operation completion * fix: Disconnect waits for existing cleanup instead of returning false nil * fix: worker owns cleanup on cancel/supersede, resultErr separates connect success from stale * fix: publish cleanup channel atomically with Disconnecting state, single-flight disconnect completion * fix: nil-guard Status/Stats/cancelManager for reserved entries with nil manager * fix: check supersession after factory construction, don't invoke Connect when generation advanced * fix: claim cleanup ownership under lock before waiting on operationDone, prevents double Disconnect * fix: cancelManager outside mu, context deadline/cancel maps to 503 in TequilAPI * test: Node.Kill bounded shutdown, cancelManager outside mu, context 503 mapping * fix: retireEntry waits for operation before nil-manager check, bulk fails on cleanup error instead of auto-retrying, recording shutdown test * fix: retry keeps generation, clear stale cleanup on new attempt, ctx check post-factory, linearize completion-vs-cancel tie, wrap errors with ErrLifecycleBusy * fix: immutable per-attempt cleanup result, ctx error preserves ErrConnectionCancelled+ErrLifecycleBusy, completion-vs-cancel linearization * fix: immutable per-attempt cleanup result object, waiters capture attempt not entry field * fix: tracked generation-fenced Reconnect, rejects during reconciliation, retires on supersession * fix: Reconnect uses worker-owned pattern with new operationDone, ctx select, phase admission, cleanup coordination * fix: production lifecycleManager capability — ConnectContext, DisconnectContext, ReconnectContext, CancelCurrentOperation * fix: lifecycleManager methods run synchronously in worker, Reconnect opDone closes after result/phase set * fix: deep manager refactor — ConnectContext owns lifetime ctx with AfterFunc, ReconnectContext sequential, legacy methods delegate to Context variants * fix: shared discoAttempt for truthful disconnect, NotConnected guard before lifetime ctx, error defer uses DisconnectContext, ReconnectContext uses DisconnectContext * fix: async cleanup in DisconnectContext, AfterFunc captures lifetimeCancel directly, error defer from lifetime creation, cancel outside ctxLock * fix(connection): ctx checks, CancelCurrentOperation cleanup, reconnect error capture - Add ctx.Err() checks after newConnection/initSession/getPublicIP in ConnectContext - Error defer: always call lifetimeCancel(), only DisconnectContext if state past NotConnected - runDisconnectCleanup uses CancelCurrentOperation() instead of direct ctxLock cancel - Multi.Reconnect captures and returns ReconnectContext error - Multi.retireEntry uses 30s deadline context for DisconnectContext instead of Background * test(connection): strengthen assertions, add reconnect error propagation tests - Fix weak/empty assertions in TestBulkDisconnectJoinsErrors and TestBlockedConnectVsBulkDisconnect - Add TestReconnectPropagatesError: verifies multi.Reconnect returns ReconnectContext errors - Add TestReconnectSucceeds: verifies happy-path reconnect via lifecycleManager - Add reconnectableManager mock implementing lifecycleManager interface * test(connection): gateway retry, stress test; docs: swagger 503 annotations - TestGatewayRetryAfterTimeout: first bulk times out, stuck cleanup blocks new connects, second bulk succeeds after unblock - TestRaceStressConnectDisconnect: 10 goroutines x 20 cycles + concurrent bulk disconnects, verified clean with -race -count=10 - Add 503 Swagger annotations to PUT/DELETE /connection endpoints * style: gofmt formatting fixes * fix(test): reset activeProposal.Price in SetupTest to fix flaky TestDisconnectDueToPriceDrop TestDisconnectDueToPriceDrop mutates the package-level activeProposal price but never restores it. Under -count=2+, subsequent runs start with the already-mutated price so the drop threshold is never hit. * fix(connection): operation context per entry, disco ordering, error normalization, clearIPCache Addresses gateway agent review messages 869-876: - 876: Each Connect/Reconnect entry owns opCtx/opCancel derived from caller ctx. Bulk/individual retirement extracts and cancels opCancel outside mu before CancelCurrentOperation. Prevents fresh connect phase after bulk. - 869: DisconnectContext clears discoAttempt pointer under lock before closing done channel. Prevents ReconnectContext from racing a concurrent DisconnectContext that attaches to the stale completed attempt. - 870: Error defer normalizes cancellation: if ctx.Err()!=nil and error doesn't already wrap ErrConnectionCancelled, joins both. TequilAPI maps 503 instead of 422. - 871: Re-register clearIPCache cleanup after statusConnecting so it runs on every disconnect but isn't stranded by early proposal/validation errors. - 872: Remove NotConnected guard around error-defer DisconnectContext. Unconditional call correctly attaches to running cleanup or returns ErrNoConnection harmlessly. - 873: retireEntry uses mcm.BulkTimeout instead of literal 30s. Remove e.cleanupAttempt=nil in Reconnect admission. Test: TestBulkCancelsReconnectBeforeFreshConnect — deterministic, 10/10 -race. Full suite: -race -count=20 clean. * fix(connection): normalize ctx error by concrete type, channel-based lifecycle test - Error defer: check !errors.Is(err, ctxErr) instead of ErrConnectionCancelled. Catches handleStartError returning bare ErrConnectionCancelled without the concrete ctx error that TequilAPI needs for 503 mapping. - Move TestBulkCancelsReconnectBeforeFreshConnect to multi_lifecycle_test.go per plan file-split guidance. - Replace sleep-based synchronization with disconnectEntered/cancelObserved channels for deterministic ordering. - Assert reconnect matches ErrConnectionCancelled/ErrLifecycleBusy, bulk==nil. * fix(connection): Stats only from phaseActive entries, regression test Multi.Stats now captures manager under mu only when phase==phaseActive. Connecting/reconnecting managers may not have initialized statsTracker, causing a data race between Stats read and Connect write (invariant 8). Status remains safe during any phase — manager.Status uses its own lock. Test: TestStatsSafeDuringConnect — concurrent Stats during blocked ConnectContext returns zero without race or panic. * test(connection): Stats phase gate proves manager not called during connecting Override Stats on slowConnectManager to panic if not activated and count calls. Asserts zero Stats calls during phaseConnecting (would panic without phase gate), then verifies one call with real data after active. * fix(connection): guard statsTracker with dedicated mutex for safe concurrent access statsTracker was a value type replaced wholesale during ConnectContext. A concurrent Stats() call could read the old tracker's RWMutex while a reconnect replaced it — causing "RUnlock of unlocked RWMutex" panic. Fix: statsTracker is now a pointer protected by statsLock. Stats() captures the pointer under lock then reads outside it. ConnectContext publishes new tracker under lock. Cleanup nils the pointer only if it still points to the current session's tracker. * Revert "fix(connection): guard statsTracker with dedicated mutex for safe concurrent access" This reverts commit 14c6045. * fix(connection): complete plan acceptance — tests, diagnostics, file split Manager-level lifecycle tests (from gateway probes): - TestConnectContextKeepsUnderlyingOperationTracked - TestDisconnectContextSharesRunningCleanup - TestConnectContextPreservesCallerCancellationFromWait Endpoint 503 tests: - TestCreateReturns503OnLifecycleBusy - TestDeleteReturns503OnLifecycleBusy - TestDeleteReturns503OnContextCanceled - TestDeleteReturns202OnSuccess Restored pre-plan tests (adapted for context-aware API): - TestConcurrentSamePortConnect - TestBlockedConnectVsIndividualDisconnect - TestExactManagerRemoval - TestConnectRacingBulkDisconnect Strengthened stress test: collect and assert only expected error types, assert final bulk succeeds. Structured lifecycle diagnostics: operation/port/generation/elapsed on Connect/Disconnect/Reconnect. Bulk timeout dumps state counts, sorted cleanup-running/pending port lists, elapsed. Caller timeout logged when shared cleanup continues server-side. One goroutine dump per generation. File split: types, retireEntry, waitRetirement, cancelManager, stateChanged, waitNotify extracted to multi_lifecycle.go (~210 LOC). * fix(connection): ctx-capturing endpoint tests, tighten restored tests, diagnostics improvements Endpoint tests: ctx-capturing mock proves PUT/DELETE forward request context. Table-driven for ErrLifecycleBusy, context.Canceled, context.DeadlineExceeded → 503; success DELETE → 202. Restored tests tightened: - BlockedConnectVsIndividualDisconnect: deadline instead of busy-spin, assert connect/disconnect results - ExactManagerRemoval: assert all errors - ConnectRacingBulkDisconnect: assert bulk error, final reconciliation Diagnostics: - Operation logs include returned error + generation - Caller-timeout log on both activeBulk joiner and starter paths - Cleanup-error path includes elapsed, retiring count, pending ports * refactor(connection): complete file split, prior-phase diagnostics, tighten tests File split: multi.go=417, multi_lifecycle.go=431, multi_test.go=410 LOC. disconnectAll + bulkWorker moved to multi_lifecycle.go alongside types, retireEntry, waitRetirement, cancelManager. All source files under 500. Diagnostics: portEntry.retire() preserves priorPhase. Bulk timeout snapshot reports was_connecting/was_reconnecting/was_active port lists. Operation logs include error + generation via named returns. Tests: endpoint 503 uses ctx-capturing mock + table-driven for all three error types + ctx!=nil assertion. Restored tests assert all errors and final reconciliation. Reconnect/timeout/retry tests moved to lifecycle test file. * fix(connection): retry failed cleanup, idempotent retire, ctx propagation tests Bug fix (invariant 7): waitRetirement resets a completed failed cleanupAttempt when no bulk owns the entry, allowing retireEntry to run again. TestIndividualDisconnectRetriesFailedCleanup verifies first cleanup fails, second retries and removes reservation. retire() is now idempotent — preserves priorPhase when already retiring. Prevents bulk re-retirement from overwriting diagnostic phase identity. Endpoint tests: ctx marker via context.WithValue proves exact request context propagation, not just non-nil. Manager lifecycle test: replaced busy-spin with sleep+channel approach. Renamed to TestConnectContextPreservesCallerCancellationFromLookup using bare connectionManager for deterministic control. * fix(connection): pre-canceled wrapping, retiring Reconnect, retry admission, test determinism Bugs: - Pre-canceled Connect/Reconnect wraps ErrConnectionCancelled+ctx.Err (was raw ctx.Err, mapping 422 not 503) - Reconnect checks retiring[id] → ErrLifecycleBusy (was ErrNoConnection) - waitRetirement resets failed cleanupAttempt for individual retry Tests: - TestConnectContextPreservesCallerCancellationFromWait: real eventbus, sync subscriber on Connecting, cancel during waitForConnectedState - TestPreCanceledConnectWrapsError, TestPreCanceledReconnectWrapsError - TestReconnectRejectsRetiringPort - TestBulkSingleFlight: blocking disconnect, asserts exactly 1 call - TestBlockedConnectVsBulkDisconnect: factory channel, notify wait - TestBlockedConnectVsIndividualDisconnect: factory channel - GPL copyright headers on all new test files - Swagger 503 regenerated blockingDisconnectManager.Disconnect increments disconnCount. * fix(connection): lifecycle snapshot diagnostics, embedded docs, headers Diagnostics §9: locked snapshot() helper returns sorted current/connecting/reconnecting/retiring/cleanup-running/was-* port lists. Used at bulk start/success/error/timeout and caller-timeout. Field names: operation=disconnect_all|connect|disconnect|reconnect, generation, elapsed, error, caller_timed_out. Timeout snapshot is fresh (recomputed after mu lock, not stale from before select). One pprof dump per unresolved generation preserved. Generated docs: swagger.json + docs.go re-embedded via mage generateSwagger + generateDocs. Exactly 2 new 503 response blocks. GPL copyright header added to multi_test.go (CI CheckCopyright fix). * fix(connection): diagnostics counts, caller_timed_out schema, admission-time generation - logSnapshot includes *_count for each list (current_count, connecting_count, etc.) - caller_timed_out=false on bulk start/success/error/timeout; =true on caller-timeout - Caller-timeout logs include elapsed (captured at disconnectAll entry) - Per-port defers capture generation at admission (opGen), not at return time * fix(connection): compile-time interface assertions for lifecycleManager and MultiManager * fix(connection): deterministic tests, compile-time assertions, remove all sleeps - TestTwoUnrelatedPortsConcurrent: block both managers, require both entered before release — proves concurrency not serialization - TestConnectCancellation: factory-entered channel replaces sleep - Replace all time.Sleep retirement waits with authoritative bulk cleanup - Compile-time interface assertions for lifecycleManager and MultiManager * fix(connection): TestTwoUnrelatedPorts signals from Manager.Connect, not factory * test(connection): deterministic bulk/retry/timeout tests, file split Tests added/rewritten per plan §937(3): - TestBulkSingleFlight: leader waits disconnectEntered, then joiners attach, asserts exactly 1 Manager.Disconnect - TestBlockedConnectVsIndividualDisconnect: waits retiring state via notify loop, asserts cleanup==1 - TestBlockedConnectVsBulkDisconnect: asserts cleanup==1 - TestBulkCleanupErrorRetrySucceeds: first error, second success, same generation, reconcile clears - TestCallerDeadlineServerContinues: leader times out, retry attaches, cleanup==1 - TestGatewayRetryAfterTimeout: retry during running cleanup, no duplicate, cleanup==1 File split: multi_bulk_test.go (413 LOC) has all bulk/timeout/retry/ reconnect/invariant7/pre-canceled/retiring tests. multi_lifecycle_test.go (492 LOC) has phasedReconnect/stats/stress/restored connect tests. All test files under 500 LOC. * fix(connection): eliminate scheduling false-positives in deterministic tests - TestBulkSingleFlight: verify activeBulk exists while leader blocks, joiners attach via fast path - TestBlockedConnectVsBulkDisconnect: use connectEntered + notify predicate loop until retiring[100] exact, then release - TestGatewayRetryAfterTimeout: wait for activeBulk installed before release, assert cleanup==1 before release, restore Connect ErrLifecycleBusy + post-reconciliation success assertions - TestCallerDeadlineServerContinues: wait for activeBulk installed before release Targeted -race -count=100 clean on all four tests. * fix(connection): observingContext barrier for deterministic bulk tests observingContext wraps a context; closes observed channel on first Done() call, proving the goroutine reached its select statement. - TestBulkSingleFlight: two joiners use observingContext, wait for both observed channels before releasing gate - TestCallerDeadlineServerContinues: retry uses observingContext before release - TestGatewayRetryAfterTimeout: keeps activeBulk predicate (valid because first worker cleared it) - BulkSingleFlight moved to multi_bulk_test.go; reconnect tests back in multi_lifecycle_test.go Targeted -race -count=100 clean. Full suite -race -count=20 clean. * refactor(connection): extract reconnect tests to multi_reconnect_test.go, all files <=500
Configuration menu - View commit details
-
Copy full SHA for 6f92883 - Browse repository at this point
Copy the full SHA 6f92883View commit details
Commits on Sep 9, 2026
-
fix(p2p): honour ctx and channel stop when the send queue is full (#3)
The send queue enqueue in sendRequest was a plain blocking chan send placed before the ctx select, so once the send loop got stuck on a peer that stopped acknowledging (kcp write blocked) the queue filled up and every Send blocked forever regardless of its context. Connection cleanup calls Send (session destroy) before closing the channel, so cleanup never finished, the port never left retirement, and bulk disconnect kept failing until the container was restarted. Enqueue now selects on ctx.Done() and the channel stop signal; reply enqueues in handleRequest give up once the channel is stopped. Regression test reproduces the full-queue hang.
Configuration menu - View commit details
-
Copy full SHA for eb13008 - Browse repository at this point
Copy the full SHA eb13008View commit details
This comparison is taking too long to generate.
Unfortunately it looks like we can’t render this comparison for you right now. It might be too big, or there might be something weird with your repository.
You can try running this command locally to see the comparison on your machine:
git diff master...main