From b0f1ef111e3c9152d7a7d12e16d8056b65c31884 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Thu, 9 Jul 2026 16:51:20 +0700 Subject: [PATCH 1/2] fix(swift-sdk): cap the faucet captcha's aggregate proof-of-work MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review hardening on #4049: the per-field bounds still admitted c=256, d=6 (~4.3B expected SHA-256 attempts) from a compromised or misbehaving faucet endpoint — enough to pin the device CPU until the 30 s timeout on every faucet tap. Bound the aggregate expected work (c x 16^d) at 64M attempts (~10x the live faucet's soft challenge); costlier challenges throw and route to the web-faucet fallback. Co-Authored-By: Claude Fable 5 --- .../Sources/SwiftDashSDK/Utils/TestnetFaucet.swift | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/swift-sdk/Sources/SwiftDashSDK/Utils/TestnetFaucet.swift b/packages/swift-sdk/Sources/SwiftDashSDK/Utils/TestnetFaucet.swift index 7baab285e6..89256feedb 100644 --- a/packages/swift-sdk/Sources/SwiftDashSDK/Utils/TestnetFaucet.swift +++ b/packages/swift-sdk/Sources/SwiftDashSDK/Utils/TestnetFaucet.swift @@ -265,6 +265,17 @@ public struct TestnetFaucet { throw FaucetError.message("Unsupported captcha challenge (c=\(c), s=\(s), d=\(d))") } + // The per-field bounds alone still admit c=256, d=6 ≈ 4.3B expected + // attempts — enough to pin the CPU until the 30 s timeout on every + // tap. Cap the AGGREGATE expected work (c × 16^d). The live faucet's + // soft challenge is c=100, d=4 ≈ 6.6M attempts; 64M gives ~10× + // headroom for server-side tuning while keeping the worst case to a + // few seconds of parallel hashing. + let expectedWork = Double(c) * pow(16, Double(d)) + guard expectedWork <= 64_000_000 else { + throw FaucetError.message("Captcha challenge too expensive (c=\(c), d=\(d))") + } + // Solve all c sub-challenges off the main actor, parallelized, under // an overall timeout so even an in-bounds-but-slow challenge can't // hang the UI indefinitely (the timeout cancels the solve tasks). From 7ab9aa6b9a14a16f50a93ab2731dc0510bff7794 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Sun, 12 Jul 2026 18:00:41 +0700 Subject: [PATCH 2/2] fix(swift-sdk): include the computed work and threshold in the PoW-cap error Review nitpick on #4100: the guard's message echoed only the raw c/d inputs; surface expectedWork and the 64M threshold so production logs show how close a drifting faucet endpoint is to the cap. Co-Authored-By: Claude Fable 5 --- .../swift-sdk/Sources/SwiftDashSDK/Utils/TestnetFaucet.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/swift-sdk/Sources/SwiftDashSDK/Utils/TestnetFaucet.swift b/packages/swift-sdk/Sources/SwiftDashSDK/Utils/TestnetFaucet.swift index 89256feedb..235d55a4d5 100644 --- a/packages/swift-sdk/Sources/SwiftDashSDK/Utils/TestnetFaucet.swift +++ b/packages/swift-sdk/Sources/SwiftDashSDK/Utils/TestnetFaucet.swift @@ -273,7 +273,9 @@ public struct TestnetFaucet { // few seconds of parallel hashing. let expectedWork = Double(c) * pow(16, Double(d)) guard expectedWork <= 64_000_000 else { - throw FaucetError.message("Captcha challenge too expensive (c=\(c), d=\(d))") + throw FaucetError.message( + "Captcha challenge too expensive (c=\(c), d=\(d), " + + "expectedWork=\(Int(expectedWork)) > 64000000)") } // Solve all c sub-challenges off the main actor, parallelized, under