Fix UTXO transfer nonce zero validation#4832
Conversation
saim256
left a comment
There was a problem hiding this comment.
Requested changes on current head f66213dbd52560840ec18d7f0c8e464e9081fe24.
Functionally, this addresses the nonce handling issue I flagged on the previous revision: numeric nonce=0 reaches the successful transfer path, while missing, empty-string, whitespace-string, and boolean nonce values stay on the controlled missing-field path and do not reserve a replay nonce.
The remaining merge blocker is repository policy: the new tests/test_utxo_transfer_nonce_required.py file is missing the required SPDX header, so python tools\bcos_spdx_check.py --base-ref origin/main fails with:
BCOS SPDX check failed. Add an SPDX header to the following new files:
- tests/test_utxo_transfer_nonce_required.py
Validation performed:
git diff --check origin/main...HEAD -- node/utxo_endpoints.py tests/test_utxo_transfer_nonce_required.py tests/test_utxo_transfer_replay.py-> passedpython -m py_compile node\utxo_endpoints.py tests\test_utxo_transfer_nonce_required.py tests\test_utxo_transfer_replay.py-> passedpython -m pytest node\test_utxo_endpoints.py tests\test_utxo_transfer_nonce_required.py tests\test_utxo_transfer_replay.py -q-> 22 passedpython -m ruff check node\utxo_endpoints.py tests\test_utxo_transfer_nonce_required.py tests\test_utxo_transfer_replay.py --select E9,F821,F811 --output-format=concise-> passedpython tools\bcos_spdx_check.py --base-ref origin/main-> failed on missing SPDX header in the new test file
|
Addressed the SPDX blocker on new head df54e22. Change made:
Validation rerun:
|
jaxint
left a comment
There was a problem hiding this comment.
Great contribution! LGTM. 🚀
jaxint
left a comment
There was a problem hiding this comment.
Great contribution! LGTM. 🚀
jaxint
left a comment
There was a problem hiding this comment.
Great contribution! LGTM. 🚀
jaxint
left a comment
There was a problem hiding this comment.
Great contribution! LGTM. 🚀
godd-ctrl
left a comment
There was a problem hiding this comment.
Approved current head df54e22 after reviewing the UTXO transfer nonce validation follow-up. The required-field check now treats missing, blank-string, whitespace-string, and boolean nonce values as missing while allowing numeric nonce 0 through the signed-transfer path, avoiding the previous all(...) falsy rejection. The new regression covers accepted zero nonce plus rejected blank/missing/boolean nonce values without burning replay nonces, and the replay test cleanup now closes read handles explicitly for Windows. Validation: python -m pytest node\test_utxo_endpoints.py tests\test_utxo_transfer_nonce_required.py tests\test_utxo_transfer_replay.py -q -> 22 passed; python -m py_compile node\utxo_endpoints.py tests\test_utxo_transfer_nonce_required.py tests\test_utxo_transfer_replay.py -> passed; git diff --check origin/main...HEAD -- node\utxo_endpoints.py tests\test_utxo_transfer_nonce_required.py tests\test_utxo_transfer_replay.py -> passed; python tools\bcos_spdx_check.py --base-ref origin/main -> OK; uv run --no-project --with ruff python -m ruff check node\utxo_endpoints.py tests\test_utxo_transfer_nonce_required.py tests\test_utxo_transfer_replay.py --select E9,F821,F811 --output-format=concise -> passed. No live node, wallet, or production endpoint was exercised.
508704820
left a comment
There was a problem hiding this comment.
Code Review: Fix UTXO Transfer Nonce Zero Validation
Summary
Alternative/complementary fix to #4830. Adds _missing_transfer_nonce() helper that explicitly handles edge cases:
- None (missing)
- Boolean (JSON true/false — Python treats these as 0/1)
- Empty string
The old all() check rejected nonce=0 because 0 is falsy.
What Works Well
- More thorough than #4830: Handles bool and empty string edge cases too
- Explicit None check: nonce=0 now passes correctly
- Boolean guard: isinstance(nonce, bool) prevents true/false from being treated as 1/0
- Separate helper function: Easy to test and reuse
Comparison with #4830
- #4830: Simple
nonce is Nonefix + standalone test - #4832: More comprehensive
_missing_transfer_nonce()with bool/empty-string guards - #4832 is more robust but slightly more complex
Issues Found
1. Low — _missing_transfer_nonce doesn't handle float nonces
A nonce of 0.5 (float) would pass this check but may not be valid. Consider adding:
or isinstance(nonce, float)Or validate that nonce is an integer:
or not isinstance(nonce, int)2. Nit — Test coverage for the helper function
Missing a test file for _missing_transfer_nonce(). The test in #4830 covers the bug but not the additional bool/empty-string cases.
Verdict: Approve
More comprehensive fix than #4830. Coordinate merges — both fix the same bug with different levels of thoroughness.
saim256
left a comment
There was a problem hiding this comment.
Approved current head df54e224b5ed0c1c277eb4dab18d6c54f678870e after rechecking the SPDX fix and focused nonce validation changes.
The earlier blocker is resolved: tests/test_utxo_transfer_nonce_required.py now carries the required SPDX header, and the repository SPDX gate passes. Functionally, the patch still preserves the intended behavior: numeric nonce=0 reaches the successful signed transfer path, while missing, blank-string, whitespace-string, and boolean nonce values return the controlled missing-field error without reserving a replay nonce.
Validation performed on this head:
git diff --check origin/main...HEAD -- node/utxo_endpoints.py tests/test_utxo_transfer_nonce_required.py tests/test_utxo_transfer_replay.py-> passedpython -m py_compile node\utxo_endpoints.py tests\test_utxo_transfer_nonce_required.py tests\test_utxo_transfer_replay.py-> passedpython tools\bcos_spdx_check.py --base-ref origin/main-> OKpython -m ruff check node\utxo_endpoints.py tests\test_utxo_transfer_nonce_required.py tests\test_utxo_transfer_replay.py --select E9,F821,F811 --output-format=concise-> passedpython -m pytest node\test_utxo_endpoints.py tests\test_utxo_transfer_nonce_required.py tests\test_utxo_transfer_replay.py -q-> 22 passed
No live node, wallet, or production endpoint was exercised.
|
Review for Scottcjn/rustchain-bounties#73. I reviewed the nonce validation change and the new regression file. The main fix is correct: numeric Two edge cases worth tightening before merge:
I do not see a blocker for the original zero-nonce bug, but I would prefer the accepted nonce type contract to be explicit. |
|
Addressed the nonce-shape follow-up in 8c66ec5. Validation rerun:
Ruff is not available in this local shell ( |
saim256
left a comment
There was a problem hiding this comment.
Follow-up review on 8c66ec5691d71ee8c0668b0cee1864a7d6e467c4: approved.
The latest commit tightens nonce shape in a useful way:
- nonces are now limited to
intorstr, whileNone, booleans, blank strings, objects, arrays, and JSON null stay on the controlled missing-field path. - numeric
0and string"0"are treated as the same replay key, which closes the type-split replay edge without rejecting legitimate zero nonce values. - the new regression coverage includes the container-value rejection cases and replay equivalence for
"0"vs0.
Validation I ran locally:
git diff --check origin/main...HEAD -- node/utxo_endpoints.py tests/test_utxo_transfer_nonce_required.py tests/test_utxo_transfer_replay.py-> passedpython -m py_compile node\utxo_endpoints.py tests\test_utxo_transfer_nonce_required.py tests\test_utxo_transfer_replay.py-> passedpython tools\bcos_spdx_check.py --base-ref origin/main-> OKpython -m ruff check node\utxo_endpoints.py tests\test_utxo_transfer_nonce_required.py tests\test_utxo_transfer_replay.py --select E9,F821,F811 --output-format=concise-> passedpython -m pytest node\test_utxo_endpoints.py tests\test_utxo_transfer_nonce_required.py tests\test_utxo_transfer_replay.py -q-> 24 passed
508704820
left a comment
There was a problem hiding this comment.
Code Review — #4832
1. Correct fix — validates nonce before use
The old code used not all([..., nonce]) which would accept nonce=0 (since 0 is falsy in Python). The new _missing_transfer_nonce function correctly handles:
- None, bool, non-int/str types
- Empty strings (whitespace-only)
- nonce=0 is now valid (important for first transaction)
2. Bool rejection is important
Same pattern as #4849 — isinstance(True, int) returns True in Python, but True is not a valid nonce. Explicit rejection prevents this.
3. String nonce handling
Allowing string nonces is flexible but could cause issues:
- "0" vs 0 — are they the same nonce?
- Leading zeros: "001" vs "1"
- Consider normalizing string nonces or restricting to one type
4. Positive
- Extracts validation to a dedicated function (cleaner)
- Fixes the Python falsy-0 bug
- Consistent with the validation pattern in other PRs
— Xeophon
dazer1234
left a comment
There was a problem hiding this comment.
Code review for RustChain bounty #73.
Summary: The nonce validation now separates missing nonce checks from other required transfer fields, so numeric zero is accepted while None, booleans, empty strings, and unsupported types are rejected. The added tests cover zero, duplicate zero, and failed-attempt nonce reservation behavior.
Findings: No blocking issues found. This closes the truthiness bug without weakening replay protection.
Verdict: Looks good.
Reviewed with OpenAI Codex assistance.
godd-ctrl
left a comment
There was a problem hiding this comment.
Approved current head 8c66ec5.
The nonce validation now avoids the old all(...) falsy rejection for numeric nonce 0 while still rejecting missing, blank, boolean, and container nonce values before replay nonce reservation. The new regressions cover zero nonce acceptance, string/numeric zero replay equivalence, blank/missing/boolean/container rejection, and no nonce burn on rejected inputs. The replay-test cleanup change is limited to closing SQLite read handles before unlinking temp DB files.
Validation performed:
- git diff --name-status origin/main...HEAD -> node/utxo_endpoints.py, tests/test_utxo_transfer_nonce_required.py, tests/test_utxo_transfer_replay.py
- git diff --check origin/main...HEAD -> passed
- python tools\bcos_spdx_check.py --base-ref origin/main -> passed
- python -m py_compile node\utxo_endpoints.py tests\test_utxo_transfer_nonce_required.py tests\test_utxo_transfer_replay.py -> passed
- python -m pytest node\test_utxo_endpoints.py tests\test_utxo_transfer_nonce_required.py tests\test_utxo_transfer_replay.py -q -> 24 passed
No blocking issues found in the reviewed diff.
jaxint
left a comment
There was a problem hiding this comment.
Great contribution! LGTM.
jaxint
left a comment
There was a problem hiding this comment.
Great contribution! LGTM.
jaxint
left a comment
There was a problem hiding this comment.
Great contribution! LGTM.
loganoe
left a comment
There was a problem hiding this comment.
Review for Scottcjn/rustchain-bounties#73 on current head 8c66ec5691d71ee8c0668b0cee1864a7d6e467c4.
The latest nonce-validation version is the right shape: numeric 0 reaches the signed transfer path, "0" shares the same replay key, and missing, blank, boolean, object, and array nonce values are rejected before replay nonce reservation. That closes the original truthiness bug without opening a type-split replay path.
Validation performed:
PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 uv run --no-project --with pytest --with flask python -m pytest node/test_utxo_endpoints.py tests/test_utxo_transfer_nonce_required.py tests/test_utxo_transfer_replay.py -q-> 24 passedpython3 -m py_compile node/utxo_endpoints.py tests/test_utxo_transfer_nonce_required.py tests/test_utxo_transfer_replay.py-> passedgit diff --check origin/main...HEAD -- node/utxo_endpoints.py tests/test_utxo_transfer_nonce_required.py tests/test_utxo_transfer_replay.py-> passedpython3 tools/bcos_spdx_check.py --base-ref origin/main-> OK
No blocking issues found in this diff.
/claim #2819
Summary
/utxo/transferto accept numericnonce=0instead of treating it as a missing required field.Missing required fieldspath.Validation
python -m pytest node\test_utxo_endpoints.py tests\test_utxo_transfer_nonce_required.py tests\test_utxo_transfer_replay.py -q-> 22 passedpython -m py_compile node\utxo_endpoints.py tests\test_utxo_transfer_nonce_required.py tests\test_utxo_transfer_replay.pygit diff --check -- node\utxo_endpoints.py tests\test_utxo_transfer_nonce_required.py tests\test_utxo_transfer_replay.pypython tools\bcos_spdx_check.py --base-ref origin/main-> OK