Skip to content

ContractTest Analysis

Note: This document is a historical record from our test data validation work. The root cause was identified as a test fixture data issue, not a bug in the gas calculation code.

Date: 2025-11-16
Status: ✅ Root Cause Identified
Finding: Gas calculation code is CORRECT - Issue was in test fixture data

Summary

Investigation confirmed that the EVM gas metering implementation is correct and matches Ethereum reference implementations (Besu, core-geth). The test was failing due to inconsistent test fixture data where accounts had incorrect codeHash values.

Key Finding: The gas calculation correctly charges 21,272 gas for calling an account with no code, which is exactly what the Ethereum specification requires.

Investigation Timeline

1. Initial Hypothesis: Gas Calculation Bug

  • Transaction in block 1 expected to use 47,834 gas
  • Actual gas reported: 21,272 gas
  • Difference: 26,562 gas (55% reduction)

2. Gas Calculation Verification

Transaction Details: - Target: 0x247d9c1a8560acfef96bbc6b4e4740a05e976395 - Data: 0xd6960697 (4 bytes, function selector) - Value: 3.125 ETH - Gas limit: 3,144,590

Intrinsic Gas Calculation:

Base transaction cost:     21,000 gas
Data (4 non-zero bytes):   4 × 68 = 272 gas
Total Intrinsic:           21,272 gas ✓

Expected Execution Gas:

Total expected:            47,834 gas
Intrinsic:                 21,272 gas
Expected EVM execution:    26,562 gas

3. Comparison with Reference Implementations

Verified against ethereum/tests standards and reference implementations:

Implementation TX_BASE_COST TX_DATA_NONZERO Our Implementation
Besu (Java) 21,000 68 ✓ Matches
core-geth (Go) 21,000 68 ✓ Matches
ethereum/tests 21,000 68 ✓ Matches

Conclusion: Our intrinsic gas calculation is CORRECT.

4. Keccak-256 vs SHA3 Investigation

Verified that the codebase correctly uses Keccak-256 (not NIST SHA-3): - crypto.kec256 uses KeccakDigest(256) from BouncyCastle ✓ - Address hashing for state trie: kec256(addr.toArray) ✓ - This is NOT the issue.

5. VM Execution Analysis

Added debug logging to trace VM execution:

[VM] Executing contract at 0x247d9c1a8560acfef96bbc6b4e4740a05e976395
[VM]   Account exists: true
[VM]   Account codeHash: c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470
[VM]   Code size: 0 bytes, startGas=3123318
[VM] Contract execution completed: gasRemaining=3123318, error=None

KEY FINDING: The account exists but has 0 bytes of code!

6. CodeHash Analysis

The account's codeHash is c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470.

This is the Keccak-256 hash of empty bytes, which Ethereum uses as a marker for accounts with NO CODE.

>>> import hashlib
>>> hashlib.sha3_256(b'').hexdigest()
# Note: sha3_256 is NIST SHA-3, not Keccak-256
>>> # For Keccak-256:
'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470'

7. Fixture Data Investigation

Contract code EXISTS in fixture: - File: src/it/resources/txExecTest/purchaseContract/evmCode.txt - Code hash: de3565a1f31ab3ea98fd46e0293d5ef7c05ea05b58e3314807e2293d8bfcb060 - Code: 1738 bytes of Solidity bytecode

Account data in fixture: - File: src/it/resources/txExecTest/purchaseContract/stateTree.txt - Account: 0x247d9c1a8560acfef96bbc6b4e4740a05e976395 - CodeHash: c5d2460186f7... (WRONG - should be de3565a1f31ab3...)

Problem: The account's codeHash field points to empty code when it should point to the actual contract code.

Root Cause

The test fixture has inconsistent data: 1. Contract code exists in evmCode.txt (hash: de3565a1f31ab3...) 2. Account in stateTree.txt has codeHash = c5d2460186f7... (empty code marker) 3. When FixtureProvider loads the fixtures, it only saves code for accounts with non-empty codeHash 4. Result: The code is in the fixture file but never gets loaded into the test database

Why Gas is 21,272 (Correct Behavior)

When a transaction calls an account with no code: 1. Intrinsic gas is charged: 21,272 ✓ 2. VM executes with empty code: uses 0 gas ✓ 3. Total gas used: 21,272 ✓

This is CORRECT behavior per Ethereum specification.

Why Fixture Expects 47,834 (Incorrect Expectation)

The fixture was probably generated from a working blockchain where: 1. The account HAD contract code 2. The transaction executed the contract code 3. Total gas used was 47,834 (21,272 intrinsic + 26,562 execution)

But the fixture's state tree data got corrupted, losing the correct codeHash value.

Solution Implemented

Added code to FixtureProvider.prepareStorages() to pre-load ALL EVM code from fixtures:

// Pre-load ALL EVM code from fixtures into storage
// This is necessary because some fixtures have account codeHash values that don't match
// the actual code hash (they may have empty codeHash when they should have the real hash)
fixtures.evmCode.foreach { case (codeHash, code) =>
  storages.evmCodeStorage.put(codeHash, code).commit()
}

Note: This is a partial workaround. The code gets loaded into storage, but the account still has the wrong codeHash, so world.getCode(address) still returns empty.

Complete Fix Options

Regenerate the purchaseContract test fixture with correct account codeHash values.

Pros: - Fixes root cause - Clean solution - No code workarounds needed

Cons: - Requires fixture generation tools/process - May affect other tests using same fixture

Option 2: Implement Account Patching (COMPLEX)

Modify FixtureProvider to detect and fix incorrect codeHash values when loading.

Approach: 1. For each account with codeHash = emptyEvm 2. Check if any code in evmCode.txt should belong to this address 3. Update the account's codeHash in the state trie 4. Re-save the modified account

Pros: - Handles corrupted fixtures automatically - No fixture regeneration needed

Cons: - Complex implementation (requires state trie manipulation) - Needs mapping of addresses to code hashes - May hide real data integrity issues

Option 3: Skip Test Temporarily

Mark ContractTest as @Ignore with detailed comment.

Pros: - Simple immediate fix - Allows other tests to proceed

Cons: - Loses test coverage - Temporary workaround only

Verification Summary

Code verified as correct ✓ - src/main/scala/com/chipprbots/ethereum/vm/EvmConfig.scala - Intrinsic gas calculation - src/main/scala/com/chipprbots/ethereum/ledger/BlockPreparator.scala - Gas refund calculation - src/main/scala/com/chipprbots/ethereum/vm/VM.scala - VM execution and gas tracking - src/main/scala/com/chipprbots/ethereum/domain/Address.scala - Keccak-256 hashing - crypto/src/main/scala/com/chipprbots/ethereum/crypto/package.scala - Crypto functions

Fixture issue identified: - src/it/resources/txExecTest/purchaseContract/stateTree.txt - Account codeHash needed update