Repository navigation
fix(trace_verify/_checkpoint): enforce canonical lowercase hex on key_id, root, and signature and reject unknown CheckpointRecord fields (#148) - #152
Conversation
rajnisht7
left a comment
There was a problem hiding this comment.
the hex checks and unknown-field rejection looks good, and the verify paths now properly reject non-canonical values. But this doesn’t fully close #148.CheckpointRecord.from_dict() still only checks required fields, types, and unknown keys. It does not enforce the semantic invariants the issue explicitly asked for:
v == 1kind == "mmr_checkpoint"mmr_size >= 10 <= prev_size < mmr_size- canonical lowercase hex on
key_id,root, non-emptyprev_root, andsignature
so these still parse successfully:
{"v": 999, "kind": "garbage", "mmr_size": 0, ...}
{"mmr_size": 10, "prev_size": 10, ...}
{"root": "AA" * 32, ...}verify_checkpoint_signature_offline only checks hex + the signature. It does not look at v/kind/mmr_size and verify_checkpoint_chain on a singleton only runs the signature check, a correctly signed but semantically invalid checkpoint (wrong v, wrong kind, mmr_size=0, etc.) currently returns success.
Also:
- later checkpoints without consistency_proof are still accepted by from_dict (the class docstring says they must carry one).
- new test only covers unknown field + a couple of uppercase/padded cases via the verify path. It does not cover the missing from_dict invariants.
- Minor: sorted(set(d) - _ALLOWED_FIELDS) can raise TypeError instead of ValueError if a non-string key is passed.
kindly add the full set of checks inside from_dict, harden the unknown-key error path, and add regression tests for each case (including rejection through from_dict itself and a singleton chain). Existing valid fixtures should still round-trip cleanly.
|
Thanks @rajnisht7! Addressed in the latest commit: both |
rajnisht7
left a comment
There was a problem hiding this comment.
the semantic checks in from_dict and the expanded tests look good, and the earlier gaps (unknown fields, non-string keys, v/kind/size, main hex fields) are addressed
A few things in this latest commit still doesnt match the response or the class contract:
- No shared validator - the comment says both from_dict and verify_checkpoint_signature_offline now share _validate_checkpoint_fields(). That helper doesn’t exist, the checks are still duplicated, and the offline path is a subset.
- consistency_proof elements aren’t canonical. from_dict just hands the dict to ConsistencyProof.from_dict, which doesn’t enforce ^[0-9a-f]{64}$. Uppercase and whitespace-padded digests parse fine (schema and the claimed “64-char lowercase hex” both require it).
- [] vs claim. Response said None or [] for prev_size == 0. Code rejects [] (raises). Class docstring only allows None for the first checkpoint, kindly align the comment with the actual intended behavior.
- direct/singleton bypass - CheckpointRecord constructed directly with prev_size > 0 and consistency_proof=None still makes verify_checkpoint_signature_offline and verify_checkpoint_chain([cp]) return True. Class contract says later checkpoints must carry one. Same for non-string log_id / timestamp, offline path doesn’t check them (while from_dict and verify_checkpoint_link do).
dinakarjs
left a comment
There was a problem hiding this comment.
Reviewed this head; PYTHONPATH=src:. python -m unittest discover -s tests -q passes 407 tests. I also reproduced the inconsistency already reported by Rajnish: a correctly signed checkpoint with prev_size=1, prev_root present and consistency_proof=None can pass the offline signature and singleton-chain paths while from_dict rejects it. This confirms the existing finding rather than adding a separate issue. Please use consistent structural validation across the entry points and retain independently failing regressions with valid signed controls before closing #148.
|
Thanks @rajnisht7! Addressed all four points in the latest commit: (1) extracted |
1a50ffb to
1a57c3a
Compare
…_id, root, and signature and reject unknown CheckpointRecord fields (agentrust-io#148) - Share _validate_checkpoint_fields across CheckpointRecord.from_dict, verify_checkpoint_signature_offline, and verify_checkpoint_link / verify_checkpoint_chain so semantic invariants (v == 1, kind == 'mmr_checkpoint', mmr_size >= 1, 0 <= prev_size < mmr_size, string log_id/timestamp, 64-char lowercase hex root/key_id/prev_root, 128-char lowercase hex signature, and consistency_proof presence/absence) are enforced uniformly on both parsed dicts and directly constructed CheckpointRecord instances. - Enforce 64-character lowercase hex on consistency_proof digests in ConsistencyProof.from_dict and _validate_consistency_proof. - Add independent regression tests with valid signed controls in tests/test_checkpoint_record.py. Closes agentrust-io#148 Signed-off-by: Raghuvar Karthik Durga <177662415+drk-ram@users.noreply.github.com>
1a57c3a to
79e666f
Compare
|
Thanks @rajnisht7 and @dinakarjs! Addressed all points in |
dinakarjs
left a comment
There was a problem hiding this comment.
Follow-up review at exact head 79e666f, Linux/Python 3.12.14 with the repository's hash-locked CI/witness/runtime dependencies and the local editable checkout.
The shared checkpoint validation now covers parsed and directly constructed records across offline signature/link/chain entry points; consistency-proof digests use the same lowercase-hex restriction. Full unittest discovery: 410 tests pass; repository Ruff passes. I inspected the shared validation and added direct-record regressions with signed valid controls. The previously reported entry-point inconsistency is addressed in this scope. Normal hosted checks and merge requirements remain separate.
Reviewed with OpenAI Codex.
What and why
Closes #148
_validate_checkpoint_fieldsacrossCheckpointRecord.from_dict,verify_checkpoint_signature_offline, andverify_checkpoint_link/verify_checkpoint_chainso semantic invariants (v == 1,kind == "mmr_checkpoint",mmr_size >= 1,0 <= prev_size < mmr_size, stringlog_id/timestamp, 64-character lowercase hexroot/key_id/prev_root, 128-character lowercase hexsignature, andconsistency_proofpresence/absence) are enforced uniformly on both parsed dicts and directly constructedCheckpointRecordinstances._REQUIRED_FIELDS | {"prev_root", "consistency_proof"}and non-string keys inCheckpointRecord.from_dict.^[0-9a-f]{64}$) on allconsistency_proofdigests (old_peaks,new_peaks, andwitness) inConsistencyProof.from_dictand_validate_consistency_proof.tests/test_checkpoint_record.py.Validation
python -m unittest discover -s tests -q— passed (410 tests)python -m pytest tests/test_checkpoint_record.py tests/test_mmr_checkpoint_adversarial.py tests/test_fuzz_regressions.py -q— passed (32 passed, 25 subtests passed)python tools/validate_registry.py— passed (OK: 3 day file(s) valid)python tools/validate_producers.py— passed (OK: 3 producer key file(s) valid)python -m ruff check src/trace_verify/_checkpoint.py src/trace_verify/_mmr.py tests/test_checkpoint_record.py— passed (All checks passed!)Registry impact
registry/Existing entries are append-only. If you are changing one, say why here: mirrors
and auditors may already have observed the current commit hashes, and a rewrite
diverges them.
Checks
pytestpassestools/validate_registry.py,tools/validate_producers.py)docs/anchor-format.mdis updated in the same PR, and existing inclusion proofs still verify