Skip to content

test: mutation-harden LibDataContract coverage#22

Merged
thedavidmeister merged 1 commit into
mainfrom
2026-06-15-datacontract-coverage
Jun 15, 2026
Merged

test: mutation-harden LibDataContract coverage#22
thedavidmeister merged 1 commit into
mainfrom
2026-06-15-datacontract-coverage

Conversation

@thedavidmeister

Copy link
Copy Markdown
Contributor

Summary

A scoped adversarial-mutation-test coverage pass over src/lib/LibDataContract.sol (the SSTORE2-style data-contract write / read / readSlice paths). Test-only — src/ is byte-for-byte unchanged (verified git diff src/ empty). The pass mutated each load-bearing line, ran the suite, and added discriminating tests only for behaviors that survived or were covered solely by fuzz luck.

No anchoring issue exists (the only open issue, #1, is an EIP-3540 research item and is out of scope for coverage hardening), so this is standalone.

Group hardened: LibDataContract write/read/readSlice boundaries

The existing suite is a strong set of fuzz round-trips, so most mutants were already killed. The gaps were edge inputs the bytes/uint16 fuzzer effectively never generates — empty data, the max valid length, and exact-end slices — which left several boundary mutants alive or killed only when the fuzzer happened to land on the edge.

Mutation matrix

# Mutation Pre-existing coverage Action
1 DataTooLarge >=> killed by testContractCreationCodeDataTooLargeRevert already covered
2 prefix length +1+0 killed by fuzz only + testRoundLargestData, testReadSkipsPrefixExactly pin deterministically
3 shl(232,…)shl(233,…) killed by fuzz already covered
4 readSlice size < end<= killed by testSameReads (fuzz) + testReadSliceExactEndBoundary (deterministic)
5 read extcodecopy offset 10 killed by fuzz + testReadSkipsPrefixExactly, testRoundLargestData (deterministic)
6 readSlice offset +1+0 killed by fuzz already covered
7 readSlice end = offset+length+0 killed by testRoundSliceError (fuzz) already covered
8 readSlice copy offsetstart killed by fuzz already covered
9 PREFIX_BYTES_LENGTH 1312 killed by fuzz already covered
10 read size == 0size == 1 SURVIVED except on fuzz-generated empty bytes + testReadEmptyData, testReadZeroCodeReverts pin both sides of the size guard
11 DataTooLarge guard maxmax-1 (tighten accepted side) SURVIVED (existing revert test only checks the rejected side) + testContractCreationCodeLargestAccepted, testRoundLargestData pin the accepted boundary

Every new test was confirmed to pass clean and fail under its target mutation, then the source was restored.

New tests

  • testReadEmptyData / testReadZeroCodeReverts — writing empty data yields a 1-byte container (just the 0x00 prefix) that reads back empty without reverting, while a no-code address reverts ReadError. Pins the read size guard to exactly zero.
  • testRoundLargestData — round-trips at the largest valid length (type(uint16).max - 1), exercising the full 2-byte length field (shl(232, data.length + 1)) and the prefix copy offset at maximum width, plus a slice of the final byte.
  • testContractCreationCodeLargestAccepted / testContractCreationCodeSmallestRejected — pin both sides of the GTE DataTooLarge boundary (the existing revert test only covered the rejected side).
  • testReadSliceExactEndBoundary — a slice ending exactly at the data end is valid; one byte past reverts ReadError.
  • testReadSkipsPrefixExactly — a non-zero leading data byte appears at index 0 of read/readSlice, pinning the skip-one-byte offset.

Verification

  • forge build clean
  • forge fmt --check clean
  • forge test: 13 passed, 0 failed (6 original + 7 new)
  • git diff src/ empty (source untouched)

Remaining gaps / not done

  • Issue https://eips.ethereum.org/EIPS/eip-3540 #1 (EIP-3540 / EOF) is not addressed — research item, explicitly out of scope.
  • Multiple contracts deployed from the same memory container / overlapping-allocation reuse is exercised by the fuzz garbage-injection test but has no dedicated deterministic case.
  • read on a contract whose first byte is non-zero (i.e. NOT produced by this lib) is documented as caller responsibility (MUST have a leading byte that can be safely ignored) and intentionally not guarded; left as-is.
  • No gas-regression / snapshot assertions added beyond the existing testSameReads relative-cost check.

Add deterministic boundary tests that pin behaviors the existing fuzz
suite only covered by chance, surfaced via a mutation pass over
src/lib/LibDataContract.sol (write/read/readSlice). Source is unchanged.

New tests:
- testReadEmptyData / testReadZeroCodeReverts: pin the `read` size guard
  to exactly zero. Empty data deploys to a 1-byte container (the 0x00
  prefix) that reads back empty WITHOUT reverting, while a no-code address
  reverts ReadError. Kills size==0 -> size==N mutants the fuzzer only
  caught when it happened to generate empty bytes.
- testRoundLargestData / testContractCreationCodeLargestAccepted /
  testContractCreationCodeSmallestRejected: round-trip at the largest
  valid length (type(uint16).max - 1) and pin both sides of the GTE
  DataTooLarge guard. The existing revert test only covered the rejected
  side; the accepted boundary and the full 2-byte length-encoding path
  (shl 232, +1 prefix math) were never reached by `bytes` fuzzing.
- testReadSliceExactEndBoundary: a slice ending exactly at the data end is
  valid; one byte past reverts. Pins `size < end` deterministically.
- testReadSkipsPrefixExactly: a non-zero leading data byte appears at
  index 0 of read/readSlice, pinning the skip-one-byte read offset.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@thedavidmeister thedavidmeister self-assigned this Jun 15, 2026
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@thedavidmeister, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 58 minutes and 55 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: f76d5971-7df1-446e-9917-801dbe257cf5

📥 Commits

Reviewing files that changed from the base of the PR and between 61a2c78 and 1a4a178.

📒 Files selected for processing (2)
  • .gitignore
  • test/lib/LibDataContract.t.sol
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 2026-06-15-datacontract-coverage

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@thedavidmeister

Copy link
Copy Markdown
Contributor Author

Reviewed 1a4a178: mutation-validated tests-only coverage for LibDataContract (read size==0 + max-size boundary, 7 tests); source byte-identical. LGTM.

@thedavidmeister thedavidmeister merged commit 8040f29 into main Jun 15, 2026
4 checks passed
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.

1 participant