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:
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¶
Option 1: Regenerate Fixture (RECOMMENDED)¶
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