Skip to content

Commit 13e53e6

Browse files
fix(payments): clear only the retested node's paid flag (double-pay fix)
audit/node-test.js retest branch called clearPaidNodes() — which wipes the ENTIRE paid-set — when it only meant to clear the one node about to be re-paid. That dropped every other node's paid flag and re-opened the duplicate-payment guard for the whole run: any node re-encountered after that point could be paid a second time from the live wallet. Add core/session.js clearPaidNode(addr) (deletes one entry) and call it in the retest branch instead. clearPaidNodes() (clear-all) stays for the legitimate run-/pass-start resets in pipeline.js. Adversarially reviewed: fix correct + complete for its target; no TEST RUN/import regressions; test suite green (188/31/45/35). NOTE (follow-up, pre-existing): the mid-run retest blocks at pipeline.js:1064/1137 still clearPaidNodes()+re-pay already-paid nodes — a separate MEDIUM double-pay vector to fix next.
1 parent ca7396a commit 13e53e6

2 files changed

Lines changed: 11 additions & 3 deletions

File tree

audit/node-test.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { BRIDGE_AVAILABLE, bridgeNodeStatus, bridgeHandshakeWG, bridgeHandshakeV
1414
import { TKD_AVAILABLE, tkdNodeStatus, tkdHandshakeWG, tkdHandshakeV2Ray } from '../core/tkd-bridge.js';
1515
import {
1616
getCredential, saveCredential, clearCredential,
17-
markSessionPoisoned, isPaid, markPaid, clearPaidNodes,
17+
markSessionPoisoned, isPaid, markPaid, clearPaidNode,
1818
addToSessionMap, findExistingSession, invalidateSessionCache,
1919
waitForSessionActive, parseNodePriceUdvpn,
2020
} from '../core/session.js';
@@ -294,8 +294,10 @@ export async function testNode(client, account, privkey, node, opts, preSessionI
294294
}
295295
}
296296
if (!sessionId && isPaid(node.address) && state.retestMode) {
297-
// In retest: clear stale paid flag, allow fresh payment
298-
clearPaidNodes();
297+
// In retest: clear the stale paid flag for THIS node only, so it can be
298+
// re-paid. clearPaidNodes() (clear-all) here was a double-pay vector — it
299+
// dropped every other node's paid flag too, re-opening the dup-pay guard.
300+
clearPaidNode(node.address);
299301
}
300302

301303
// Balance check — return PAUSE signal instead of failing

core/session.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ export function isPaid(nodeAddr) {
8181
export function clearPaidNodes() {
8282
paidNodesThisRun.clear();
8383
}
84+
// Clear the paid flag for ONE node (retest re-payment of a single node). Do NOT
85+
// use clearPaidNodes() for that — it wipes every node's flag and re-opens the
86+
// duplicate-payment guard for the whole run (double-pay vector).
87+
export function clearPaidNode(nodeAddr) {
88+
paidNodesThisRun.delete(nodeAddr);
89+
}
8490

8591
/** Invalidate session map after payment creates new sessions */
8692
export function invalidateSessionCache() { sessionMap = null; }

0 commit comments

Comments
 (0)