Skip to content

Add ~90 cross-validation tests for alloy.rs parity#5

Merged
koko1123 merged 2 commits intomainfrom
test/alloy-parity-validation
Feb 26, 2026
Merged

Add ~90 cross-validation tests for alloy.rs parity#5
koko1123 merged 2 commits intomainfrom
test/alloy-parity-validation

Conversation

@koko1123
Copy link
Contributor

@koko1123 koko1123 commented Feb 26, 2026

Summary

  • Adds ~90 new inline tests across 15 source files validating byte-exact accuracy parity with alloy.rs
  • Test vectors sourced from Solidity ABI spec, EIP-55/712/137, BIP-32/39 (Trezor vectors), Hardhat well-known accounts, and ethereum/tests
  • Covers all core modules: ABI encode/decode, RLP, keccak256, secp256k1, transactions (Legacy/EIP-1559/EIP-4844), EIP-712 typed data, HD wallet derivation, mnemonic seeds, ENS namehash, address checksums, hex codec, and unit conversions

Test plan

  • zig build test passes with all ~1068 inline tests (0 failures)
  • CI passes on all targets

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests
    • Expanded test coverage across ABI encoding/decoding, EIP-712, ENS namehashing, HD wallet derivation, hex/Keccak utilities, BIP-39 mnemonics, primitives (EIP-55), RLP, secp256k1, signer utilities, transaction formats (legacy/EIP-2930/1559/4844), uint256 arithmetic, and unit parsing/formatting.
    • Added extensive edge-case, roundtrip, determinism, and error-path validations to improve reliability.

@coderabbitai
Copy link

coderabbitai bot commented Feb 26, 2026

📝 Walkthrough

Walkthrough

Adds extensive test cases across many modules (ABI encode/decode, EIP-712, Keccak, secp256k1, HD wallet, mnemonic, hex, primitives, RLP, transactions, uint256, units, ENS). No production code or public API signatures changed.

Changes

Cohort / File(s) Summary
ABI Tests
src/abi_encode.zig, src/abi_decode.zig
Added many encode/decode roundtrip and edge-case tests: fixed bytes/arrays, dynamic bytes/strings, head/tail offsets, function selectors, ERC20 return encodings, and error-path bounds checks.
EIP-712 & ENS
src/eip712.zig, src/ens/namehash.zig
Added EIP-712 typed-data hashing tests (Permit, nested structs, domain variations) and ENS namehash vectors including trailing-dot handling and manual reconstruction.
Hashing & Crypto Primitives
src/keccak.zig, src/secp256k1.zig, src/primitives.zig, src/signer.zig
Expanded Keccak-256 vectors and determinism checks; added secp256k1 signing/recovery and low-S tests; EIP-55 checksum and address parsing tests; signer personal-message hashing tests.
Wallet & Mnemonic
src/hd_wallet.zig, src/mnemonic.zig
Added BIP-32/BIP-39 tests: master key derivation, mnemonic→seed vectors, deterministic account derivations, index variation, and expected address checks for known mnemonics.
Encoding Formats
src/rlp.zig, src/hex.zig
Added RLP long-string and integer-size vectors, byte-edge cases and roundtrips; hex decoding (case-insensitive) and large roundtrip tests including 0x/0X prefix handling.
Transactions & Serialization
src/transaction.zig
Large suite of tests for legacy, EIP-2930, EIP-1559, and EIP-4844 signing/serialization: payload structure, access lists, blob hashes, and deterministic serialization/hashes.
Uint256 & Units
src/uint256.zig, src/utils/units.zig
Added u256 arithmetic tests (fastDiv, mulDiv, Q96 cases), hex and big-endian roundtrips, and parse/format roundtrip tests for Ether and Gwei.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I hopped through hex and hash and key,

I tested bytes from A to Z,
I signed and namehashed, one, two, three —
Roundtrips safe as carrot tea,
Hop, patch, and flourish, tests agree! 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding ~90 cross-validation tests for alloy.rs parity across the codebase.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch test/alloy-parity-validation

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (5)
src/utils/units.zig (1)

57-59: Precision concern with large value test.

The value 10_000_000_000_000_000_000_000 (10^22) exceeds f64's exact integer representation range (~9×10^15). While this test may pass due to favorable rounding, it's testing in a range where parseEther inherently loses precision. Consider either:

  1. Adding a comment acknowledging this tests the precision boundary
  2. Using a value within f64's exact range (e.g., parseEther(9000.0) → 9×10^21 is still borderline)

The existing code documents this limitation for formatEther/formatGwei but not for parseEther/parseGwei.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/units.zig` around lines 57 - 59, The test "parseEther large value"
uses 10_000_000_000_000_000_000_000 which is beyond f64's exact integer range
and can produce misleading precision behavior in parseEther; update the test to
either (A) replace the input with a value within f64's exact integer range
(e.g., use 9000.0 or another safe decimal) or (B) keep the large value but add a
clear comment above the test and update the module docs to note that
parseEther/parseGwei lose precision for very large values due to f64 limits;
reference the test "parseEther large value" and the parseEther/parseGwei
functions when making the change so reviewers can find and verify the fix.
src/secp256k1.zig (1)

501-537: Reuse existing low-S helper to avoid duplicated comparison logic.

This test currently duplicates half-order bytes and manual lexicographic comparison already represented by HALF_ORDER_BYTES + isHighS.

♻️ Suggested simplification
-    // n/2 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0
-    const half_n: [32]u8 = .{
-        0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-        0x5D, 0x57, 0x6E, 0x73, 0x57, 0xA4, 0x50, 0x1D,
-        0xDF, 0xE9, 0x2F, 0x46, 0x68, 0x1B, 0x20, 0xA0,
-    };
@@
-        // Extract s as big-endian bytes and verify s <= n/2
-        const s_bytes = sig.s;
-        var s_is_lte = false;
-        for (0..32) |i| {
-            if (s_bytes[i] < half_n[i]) {
-                s_is_lte = true;
-                break;
-            } else if (s_bytes[i] > half_n[i]) {
-                break;
-            }
-        }
-        // If we didn't break early, all bytes were equal (s == n/2), which is also valid
-        if (!s_is_lte) {
-            // Check if all bytes are equal (s == n/2)
-            s_is_lte = std.mem.eql(u8, &s_bytes, &half_n);
-        }
-        try std.testing.expect(s_is_lte);
+        const s_scalar = Scalar.fromBytes(sig.s, .big) catch unreachable;
+        try std.testing.expect(!isHighS(s_scalar));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/secp256k1.zig` around lines 501 - 537, The test "sign produces low-s
(EIP-2) canonical signature" duplicates the half-order byte constant and manual
lexicographic comparison; replace that logic by reusing the existing
HALF_ORDER_BYTES constant and the isHighS function to check canonical low-S.
Locate the test and remove the local half_n and byte-by-byte comparison, then
call isHighS(sig.s) (or its inverse) alongside HALF_ORDER_BYTES to assert the
signature is low-s, keeping the rest of the test (message hashing and sign call)
unchanged.
src/hd_wallet.zig (1)

230-258: Strengthen “known seed” test with exact expected key/chain-code assertions.

For parity confidence, this test should pin exact BIP-32 vector outputs instead of only non-zero and deterministic checks.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hd_wallet.zig` around lines 230 - 258, The test "BIP-32 master key from
known seed" currently only checks non-zero and deterministic outputs; replace or
augment those checks with explicit expected BIP-32 vector values: call
masterKeyFromSeed(seed) and assert master.key equals the known expected master
private key bytes and master.chain_code equals the known expected chain code
bytes (use the same fixed seed used in the test). Update the test to use try
std.testing.expectEqualSlices(u8, &master.key, &expected_key) and try
std.testing.expectEqualSlices(u8, &master.chain_code, &expected_chain_code)
(keeping masterKeyFromSeed, master.key, master.chain_code and the test name
intact) so the test pins exact BIP-32 vector outputs.
src/eip712.zig (1)

1004-1066: Add canonical expected digest assertions for typed-data vectors.

These tests currently validate determinism but not correctness against external vectors. For parity objectives, pin expected final hashes for the Permit and Permit2 cases.

Also applies to: 1134-1206

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/eip712.zig` around lines 1004 - 1066, The test "ERC20 Permit typed data
hash" currently only checks determinism (hash1 == hash2); update it to assert
the computed hash matches canonical expected EIP-712 digest vectors by adding
fixed expected 32-byte digests and comparing hash1 against them; locate the test
block that constructs DomainSeparator, permit_type (TypeDef), permit_msg
(StructValue) and calls hashTypedData(allocator, domain, permit_msg, &type_defs)
and add expectations like testing.expectEqualSlices(u8, &hash1,
&expected_permit_hash) (and do the same for the Permit2 test referenced around
lines 1134-1206) so the test verifies correctness against known vectors.
src/transaction.zig (1)

845-870: Assert exact expected bytes for alloy vector tests, not only shape checks.

In legacy tx alloy.rs test vector, checks like determinism + payload.len > 40 can still pass with subtly wrong encodings. Consider pinning full expected payload (or signing hash) for true byte-exact parity validation.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/eip712.zig`:
- Around line 1004-1066: The test "ERC20 Permit typed data hash" currently only
checks determinism (hash1 == hash2); update it to assert the computed hash
matches canonical expected EIP-712 digest vectors by adding fixed expected
32-byte digests and comparing hash1 against them; locate the test block that
constructs DomainSeparator, permit_type (TypeDef), permit_msg (StructValue) and
calls hashTypedData(allocator, domain, permit_msg, &type_defs) and add
expectations like testing.expectEqualSlices(u8, &hash1, &expected_permit_hash)
(and do the same for the Permit2 test referenced around lines 1134-1206) so the
test verifies correctness against known vectors.

In `@src/hd_wallet.zig`:
- Around line 230-258: The test "BIP-32 master key from known seed" currently
only checks non-zero and deterministic outputs; replace or augment those checks
with explicit expected BIP-32 vector values: call masterKeyFromSeed(seed) and
assert master.key equals the known expected master private key bytes and
master.chain_code equals the known expected chain code bytes (use the same fixed
seed used in the test). Update the test to use try
std.testing.expectEqualSlices(u8, &master.key, &expected_key) and try
std.testing.expectEqualSlices(u8, &master.chain_code, &expected_chain_code)
(keeping masterKeyFromSeed, master.key, master.chain_code and the test name
intact) so the test pins exact BIP-32 vector outputs.

In `@src/secp256k1.zig`:
- Around line 501-537: The test "sign produces low-s (EIP-2) canonical
signature" duplicates the half-order byte constant and manual lexicographic
comparison; replace that logic by reusing the existing HALF_ORDER_BYTES constant
and the isHighS function to check canonical low-S. Locate the test and remove
the local half_n and byte-by-byte comparison, then call isHighS(sig.s) (or its
inverse) alongside HALF_ORDER_BYTES to assert the signature is low-s, keeping
the rest of the test (message hashing and sign call) unchanged.

In `@src/utils/units.zig`:
- Around line 57-59: The test "parseEther large value" uses
10_000_000_000_000_000_000_000 which is beyond f64's exact integer range and can
produce misleading precision behavior in parseEther; update the test to either
(A) replace the input with a value within f64's exact integer range (e.g., use
9000.0 or another safe decimal) or (B) keep the large value but add a clear
comment above the test and update the module docs to note that
parseEther/parseGwei lose precision for very large values due to f64 limits;
reference the test "parseEther large value" and the parseEther/parseGwei
functions when making the change so reviewers can find and verify the fix.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0314016 and a6808c5.

📒 Files selected for processing (15)
  • src/abi_decode.zig
  • src/abi_encode.zig
  • src/eip712.zig
  • src/ens/namehash.zig
  • src/hd_wallet.zig
  • src/hex.zig
  • src/keccak.zig
  • src/mnemonic.zig
  • src/primitives.zig
  • src/rlp.zig
  • src/secp256k1.zig
  • src/signer.zig
  • src/transaction.zig
  • src/uint256.zig
  • src/utils/units.zig

Adds comprehensive test vectors from Solidity ABI spec, EIP-55/712/137,
BIP-32/39, Hardhat accounts, and ethereum/tests to validate byte-exact
accuracy parity with alloy.rs across all core functionality.

Coverage spans ABI encode/decode, RLP, keccak256, secp256k1 signing,
transaction serialization, EIP-712 typed data, HD wallet derivation,
mnemonic seed generation, ENS namehash, address checksums, hex codec,
and unit conversions.
@koko1123 koko1123 force-pushed the test/alloy-parity-validation branch from a6808c5 to 0ee6308 Compare February 26, 2026 19:19
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/hd_wallet.zig (1)

230-258: Strengthen this test with exact expected master key + chain code bytes.

This currently checks non-zero and determinism only, which can still pass with an incorrect derivation. For parity-focused coverage, assert exact expected outputs from a canonical vector.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hd_wallet.zig` around lines 230 - 258, Replace the weak non-zero checks
in the "BIP-32 master key from known seed" test by asserting the exact expected
master private key and chain code bytes from a canonical BIP-32 test vector:
compute the expected 32-byte key and 32-byte chain_code (use
hex_mod.hexToBytesFixed to decode hex literals into arrays) and then call
std.testing.expectEqualSlices(u8, &master.key, &expected_key) and
std.testing.expectEqualSlices(u8, &master.chain_code, &expected_chain_code)
after deriving master via masterKeyFromSeed to ensure exact-match validation;
keep the existing determinism check if desired.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/hd_wallet.zig`:
- Around line 230-258: Replace the weak non-zero checks in the "BIP-32 master
key from known seed" test by asserting the exact expected master private key and
chain code bytes from a canonical BIP-32 test vector: compute the expected
32-byte key and 32-byte chain_code (use hex_mod.hexToBytesFixed to decode hex
literals into arrays) and then call std.testing.expectEqualSlices(u8,
&master.key, &expected_key) and std.testing.expectEqualSlices(u8,
&master.chain_code, &expected_chain_code) after deriving master via
masterKeyFromSeed to ensure exact-match validation; keep the existing
determinism check if desired.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a6808c5 and 6312f61.

📒 Files selected for processing (15)
  • src/abi_decode.zig
  • src/abi_encode.zig
  • src/eip712.zig
  • src/ens/namehash.zig
  • src/hd_wallet.zig
  • src/hex.zig
  • src/keccak.zig
  • src/mnemonic.zig
  • src/primitives.zig
  • src/rlp.zig
  • src/secp256k1.zig
  • src/signer.zig
  • src/transaction.zig
  • src/uint256.zig
  • src/utils/units.zig
🚧 Files skipped from review as they are similar to previous changes (8)
  • src/primitives.zig
  • src/eip712.zig
  • src/secp256k1.zig
  • src/abi_decode.zig
  • src/rlp.zig
  • src/mnemonic.zig
  • src/ens/namehash.zig
  • src/signer.zig

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