Repository navigation
test(e2e): give test nodes 10 s for DHT requests and an OS-assigned port - #248
Conversation
e2e_cost_estimate.rs was the only e2e file still starting MiniTestnet::start(10). Every other one uses DEFAULT_NODE_COUNT (CLOSE_GROUP_SIZE * 2 = 14), which 41e5bda introduced on 04-21 because a smaller testnet leaves too few peers ready on slow macOS runners. This file landed a day later (#44) with the hard-coded 10. All 4 E2E macOS fail-then-pass runs in the last 50 (40015142708, 40349637751, 40683863980, 40844173225) are one test in this file, test_estimate_all_stored_partial_sample_is_incomplete. Each one failed its upload with "witnessed close group initial lookup found 6 peers, need 7". On dev2, with the test's CPU capped to imitate a slow runner: - 2 cores: 10 and 14 nodes both pass 3/3. - 1.5 cores: at 10 nodes the upload fails 3/3 ("5-6 of 7"). At 14 nodes it gets past the upload 2/3; those 2 runs then fail the re-estimate (a non-zero cost for the stored file). - 1 core: both fail the upload 3/3. So the larger testnet moves the cliff the CI failure sits on, but doesn't remove it under heavy starvation. The merkle-estimate test keeps its 20 nodes (merkle pools need 16). Closes V2-1852 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oncurrently MiniTestnet set each node's saorsa-core connection_timeout to 5 s. saorsa-core also uses that value as the request timeout for every DHT operation (network.rs: request_timeout: config.connection_timeout), so a witnessed close-group lookup gave each peer 5 s to answer, against 25 s in production. On loaded macOS runners slow but live peers missed it, and uploads failed with "found 6 peers, need 7" (V2-1852). The 5 s came in with the saorsa-client port (5d25103), with no comment. The value is now TEST_CORE_CONNECTION_TIMEOUT_SECS = 10, the same name and value as ant-node's e2e harness (V2-1869), and each says to keep the two in step. dev2, test_estimate_all_stored_partial_sample_is_incomplete, 14 nodes, CPU capped: - 5 s: failed 3/3 at 1 core; at 1.5 cores, 1/3 failed the upload and 2/3 failed the re-estimate after it. - 10, 15, 20 and 25 s: passed every run at 1.5 cores (3/3 each) and at 1 core (10 s 3/3, the rest 2/2). Run time didn't change with the value. teardown() now shuts the nodes down concurrently, as ant-node's does (857398c), so their DHT leave waits overlap instead of adding up. dev2, full CPU: - e2e_chunk, 13 tests: 499 s with 72 s of teardown on main (5 s, serial); 504 s / 78 s at 10 s serial; 437 s / 13 s at 10 s concurrent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
E2E ubuntu on this PR failed in harness setup, not in a test:
test_file_download_bytes_written panicked at "create P2P node:
Transport(SetupFailed("Failed to create dual-stack network nodes:
Failed to create transport"))". It isn't new: E2E macOS hit the same
panic on 2026-10-29 (run 40914982712, test_attack_zero_amount_payment,
another branch, before the timeout and teardown changes here).
ant-node's e2e harness already handles this race. A freshly chosen
loopback port can be momentarily held by another socket, so it retries
P2PNode::new up to 4 times on the same port, backing off 250 ms per
attempt (eb049e3). spawn_node now does the same, building a fresh
config per attempt because P2PNode::new consumes it. A real transport
misconfiguration still fails, after the last attempt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ky-4-fail-then-pass-reruns-in-the Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Node creation now and then failed with "Failed to create transport" (V2-1852). Each node bound 127.0.0.1 on a random port in 20000-60000, which overlaps the OS ephemeral range. Every node and client starts a UPnP gateway search that holds a 0.0.0.0:<ephemeral> UDP socket for up to 2 s, and a 127.0.0.1 bind to a port a wildcard socket holds fails with "address in use". The four same-port retries all fell inside one search window, so they didn't help. Nodes now bind port 0, and later nodes bootstrap from the address the bootstrap nodes actually bound (listen_addrs()). The OS never assigns a port that is in use. The retry is removed: it only covered this race, and any other failure should show on the first attempt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaidSafe-Hermes
left a comment
There was a problem hiding this comment.
Focused round-2 re-review of dec642c957cfa8858e38fc57ff027fd43c4e0927..ac94cd277fd6e38b927d5d7e4d33c0a4da9d3191 (current head ac94cd277fd6e38b927d5d7e4d33c0a4da9d3191). No code findings.
The port-0 change addresses the fixed-port collision. I verified against the pinned saorsa-core source (c3c4c81) that the transport records the actual bound socket address, then exposes the actual listener addresses. With local mode and IPv4-only enabled, those are loopback addresses, so later nodes receive the OS-assigned, usable bootstrap port. Removing retries is reasonable for the diagnosed collision: the kernel now chooses an available port at bind time.
Review coverage: 2/2 seats — sol-1 (gpt-6.1-sol, xhigh) returned UNCERTAIN because its evidence bundle omitted the transport implementation; I resolved that uncertainty by inspecting the exact pinned source. glm-1 (z-ai/glm-5.3) returned APPROVE. The seats did not independently agree; the final disposition follows the source verification.
The required linear-link and pr-template checks pass. E2E/Merkle and other CI jobs are still pending; Security Audit is red for the inherited RUSTSEC-2026-0387 / V2-1743 finding, which is not a required check. This approval is a code-review verdict, not a merge-readiness claim.
MaidSafe-Hermes
left a comment
There was a problem hiding this comment.
APPROVE — no blocking code findings on ac94cd2.
I checked the port-0 bootstrap path against the exact saorsa-core revision pinned by Cargo.lock (c3c4c81e63db7e3048f488f7cf0fec4cff3131d4): P2PNode::listen_addrs() returns runtime transport listener addresses, the transport records the bound socket addresses, and local mode uses loopback. The later nodes therefore bootstrap from the actual OS-assigned ports. DEFAULT_NODE_COUNT is 2 × CLOSE_GROUP_SIZE (14); CI exercises the changed test harness and reports E2E and Merkle E2E passing on Ubuntu and macOS.
Review coverage: 2/2 seats reviewed — sol-1 (gpt-6.1-sol, xhigh) returned UNCERTAIN; glm-1 (z-ai/glm-5.3) returned APPROVE. The seats did not agree. I resolved the material uncertainty in sol-1’s report by checking the pinned source, constant definition and CI evidence; this verdict is not presented as seat agreement.
Non-blocking nit: e2e_cost_estimate.rs still says “Use 10 nodes” although DEFAULT_NODE_COUNT is 14. The Security Audit check is failing for the inherited RUSTSEC-2026-0387 / V2-1743 issue noted in the PR; this code approval is not a merge-readiness claim.
The test has used DEFAULT_NODE_COUNT since this PR's first round, but the comment still said 10 nodes (Hermes, non-blocking). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes the two E2E flakes behind V2-1852:
The ticket's fifth run, Merkle ubuntu with "Got 14 merkle candidates, need 16", is a different failure. It's now tracked in V2-1893.
Flake 1: "need 7"
All 4 fail-then-pass macOS E2E runs in the last 50 were one test,
e2e_cost_estimate::test_estimate_all_stored_partial_sample_is_incomplete:InsufficientPeers … witnessed close group initial lookup found 6 peers, need 7.Cause: the test harness gives every DHT request 5 s. saorsa-core uses a node's
connection_timeoutfor two things: the connection timeout, and the request timeout of every DHT operation (network.rs:request_timeout: config.connection_timeout).MiniTestnetset it to 5 s, against 25 s in production. On a loaded macOS runner, a slow but live peer that answers after 5 s isn't counted, so the lookup comes up one short.The 5 s came in with the saorsa-client port (5d25103), with no comment. ant-node's harness has the same trap at 2 s, set to keep teardown short (857398c).
Flake 2: "Failed to create transport"
spawn_nodepanicked withcreate P2P node: Transport(SetupFailed("Failed to create dual-stack network nodes: Failed to create transport"))before any test logic ran:test_external_merkle_partial_payment_is_partial_uploadtest_attack_zero_amount_paymenttest_file_download_bytes_written, the first test in its binarytest_idempotent_chunk_storage, all 4 retries from round 1Cause: a UDP port collision with the UPnP gateway search.
127.0.0.1on a port the harness picked: a random base in 20000-60000, plus the node's index. That range overlaps the OS ephemeral range (Linux 32768-60999, macOS 49152-65535).upnp.rs, enabled by default, in local mode too).0.0.0.0:0(igd-next's defaultSearchOptions) and runs for up to 2 s (DEFAULT_DISCOVERY_TIMEOUT). There is no gateway on a CI runner, so it uses the full 2 s.127.0.0.1:Pbind while another socket holds0.0.0.0:Pfails with "address in use". So when a search socket happened to get the port of a node not yet created, that node failed.{}(transport_handle.rs:545), which drops the underlyingio::Error. That's why the log only says "Failed to create transport".Reproduced on dev2:
0.0.0.0:<base+3>held by a plain UDP socket.create P2P node after 4 attempts: Transport error: Setup failed: Failed to create dual-stack network nodes: Failed to create transport, the same string as run 41897795306, in 4.8 s.ssshowed each node holding only its own127.0.0.1socket, and one short-lived0.0.0.0:<ephemeral>socket appearing with each node spawn.Fix: nodes bind port 0, so the OS picks a free port. The OS never assigns a port that is in use, whatever holds it: a search socket, another process, or an IPv6 dual-stack socket. Later nodes bootstrap from the addresses the bootstrap nodes actually bound (
listen_addrs()). The retry is removed: it only covered this race, and any other failure should show on the first attempt. Nothing else read the fixed ports; no test restarts a node on the same address.Changes
TEST_CORE_CONNECTION_TIMEOUT_SECS = 10, with a comment explaining the double use. ant-node moves to the same name and value in its own PR (V2-1869), and each harness says to keep the two in step.teardown()shuts nodes down concurrently, as ant-node's does, so the DHT leave waits overlap instead of adding up.e2e_cost_estimate.rsusesDEFAULT_NODE_COUNT(14) instead of a hard-coded 10. It was the only e2e file left on 10, and the merkle test in it keeps 20. This helps a little; change 1 is the actual fix.spawn_nodebinds port 0 (round 2), andstart_innerbuilds the bootstrap list from the nodes'listen_addrs(). This removesTEST_PORT_RANGE_MIN/MAX, the random base port and the 4-attempt node-create retry.Linear issue
Closes V2-1852
Risk tier
Compatibility
ant-core/tests/).Semver impact
Test evidence
All runs below were on dev2 (Linux, i5-8500, 4 cores), with debug builds and
-D warningsas in CI.Which timeout holds. I ran the flaky test with
--exactat 14 nodes, with its CPU capped bysystemd-run -p CPUQuota:Run time doesn't depend on the value: about 210 s at 1.5 cores and about 272 s at 1 core. 10 s is the lowest value tried that held.
What it costs. Full targets at full CPU:
e2e_chunk(13 tests)e2e_pointer(17 tests, silenced-peer cases)All runs passed. The suites get faster overall, and
e2e_pointer's silenced-peer tests are no slower at 10 s.Round 2 (port 0), on dev2 at ac94cd2:
cargo clippy -p ant-core --all-targets --all-features -- -D warnings: clean.cargo fmt --check: clean.--test-threads=1, all 9 binaries): 69 passed, 0 failed: adr0004 3, chunk 13, cost_estimate 7, data 7, file 10, manifest 7, native_payment 4, payment 8, security 10. No "create P2P node" in the log.e2e_merkle(35 nodes): 8 passed, 0 failed (2023 s).CI on round 2 (ac94cd2, run 42012971431): everything green except the inherited Security Audit (RUSTSEC-2026-0387, V2-1743). E2E 69/69 on ubuntu and macOS, Merkle E2E 8/8 on both. No "create P2P node" or panic in any E2E or Merkle log.
CI on round 1 (dec642c): macOS E2E passed
e2e_cost_estimate7/7, with no "need 7"; flake 1 held. The macOS E2E job still failed, on flake 2 (e2e_payment), which round 2 fixes.New dependency
none.
futuresis already a dependency of ant-core.ADR
n/a
Mitigation / rollback
Revert. If a test that waits out a dead peer turns out too slow, lower
TEST_CORE_CONNECTION_TIMEOUT_SECS. Don't go to 5 s, which is known to fail.🤖 Generated with Claude Code