Skip to content

feat(C3 partial): MerkleTree property tests (determinism + collision-freedom)#1425

Merged
AceHack merged 1 commit intomainfrom
otto/c3-merkle-property-tests-2026-05-03
May 3, 2026
Merged

feat(C3 partial): MerkleTree property tests (determinism + collision-freedom)#1425
AceHack merged 1 commit intomainfrom
otto/c3-merkle-property-tests-2026-05-03

Conversation

@AceHack
Copy link
Copy Markdown
Member

@AceHack AceHack commented May 3, 2026

Summary

Two FsCheck properties added to tests/Tests.FSharp/Storage/Merkle.Tests.fs that generalise the per-leaf-edit [<Fact>] check into shrinking-friendly property tests:

  • MerkleTree root is deterministic for any leaf set
  • MerkleTree root differs under any single-leaf edit (collision-free in practice)

Both properties guard against future regressions:

  • determinism catches accidental reference-equality hashing
  • collision-freedom catches a regression to a smaller or structural hash (relies on XxHash128's 128-bit hash space making FsCheck-generator-collisions astronomically unlikely)

C3 progress

Math-proofs assessment matrix C3 row: closes 2 more after the CRDT batch (G + PN + OR + LWW each had 3). Running total: ~11 of 15 target after this PR.

Test plan

  • All 6 Merkle tests pass (4 original + 2 new properties): dotnet test --filter "FullyQualifiedName~MerkleTree" → 6 passed in 137ms.

🤖 Generated with Claude Code

…-freedom

Two FsCheck properties added to existing Merkle.Tests.fs that
generalise the per-leaf-edit [<Fact>] check into shrinking-friendly
property tests:

- MerkleTree root is deterministic for any leaf set
- MerkleTree root differs under any single-leaf edit
  (collision-free in practice; relies on XxHash128's 128-bit hash
  space making FsCheck-generator-collisions astronomically unlikely)

Both properties guard against future regressions: determinism
catches accidental reference-equality hashing; collision-freedom
catches a regression to a smaller or structural hash.

Math-proofs assessment matrix C3 row: closes 2 more after
G/PN/OR/LWW counters (running total: 11 of ~15 target).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 3, 2026 15:35
@AceHack AceHack enabled auto-merge (squash) May 3, 2026 15:35
@AceHack AceHack merged commit 505b515 into main May 3, 2026
27 of 28 checks passed
@AceHack AceHack deleted the otto/c3-merkle-property-tests-2026-05-03 branch May 3, 2026 15:37
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds FsCheck property-based tests around MerkleTree.Root to broaden the existing example-based [<Fact>] checks, aiming to guard against determinism regressions and practical hash-collision regressions.

Changes:

  • Add FsCheck/Xunit property tests for Merkle root determinism across arbitrary leaf sets.
  • Add a property asserting the Merkle root changes under a single-leaf replacement (as a practical collision-freedom check).

Comment on lines +51 to +55
// Both trees built from the SAME input must produce equal roots.
// Treats null arrays as empty (FsCheck can generate null).
let safe = if isNull leaves then [||] else leaves |> Array.map (fun b -> if isNull b then [||] else b)
let t1 = MerkleTree safe
let t2 = MerkleTree safe
Comment on lines +60 to +71
(NonEmptyArray (leaves: byte array array)) (replacement: byte array) =
// Replace the first leaf, expect a different root. The 128-bit
// XxHash makes practical collisions astronomically unlikely for
// the small inputs FsCheck generates; if the hashing pipeline
// regresses to <128 bits or to a non-collision-resistant hash,
// this property starts failing.
let arr = leaves |> Array.map (fun b -> if isNull b then [||] else b)
let r = if isNull replacement then [||] else replacement
if arr.[0] = r then true // skip cases where replacement is identical
else
let modified = Array.copy arr
modified.[0] <- r
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 914b8890b9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +68 to +71
if arr.[0] = r then true // skip cases where replacement is identical
else
let modified = Array.copy arr
modified.[0] <- r
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exercise non-zero leaf positions in edit property

This property always edits arr.[0], so it does not actually validate the stated "any single-leaf edit" behavior across leaf positions. A position-dependent bug in tree construction (e.g., incorrect indexing beyond the first leaf) could still pass this test, which weakens the regression coverage the new property is intended to provide.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants