Bug Report
DTVM_INTERP_CREATE_GARBAGE_BALANCE.zip
The DTVM interpreter build (-m interpreter) credits a freshly CREATEd
account with a non-zero balance out of thin air, violating EVM balance
conservation. With the test case below, a created account receives
0x3fffffffa4dabfff wei (~4.6e18) that no account is debited for — the
post-state total supply is roughly double the pre-state total.
1. Minimal reproduce step (Required)
The test case is a 26-byte contract (22 instructions) executed by the
interpreter build, with an 80-byte calldata and a two-account pre-state.
All inputs are packaged in the attached archive
DTVM_INTERP_CREATE_GARBAGE_BALANCE.zip — the repro/ directory contains the
three input files plus a run.sh that runs the exact command below and prints
the offending balance.
File contract.evm.hex (contract bytecode under test):
5f5f5f5f5f5f5f63304130305f5ff13030f45f5f5ff05f5f5ff0
Disassembly:
[ 0] PUSH0 [ 1] PUSH0 [ 2] PUSH0 [ 3] PUSH0
[ 4] PUSH0 [ 5] PUSH0 [ 6] PUSH0 [ 7] PUSH4 0x30413030
[ 8] PUSH0 [ 9] PUSH0 [10] CALL [11] ADDRESS
[12] ADDRESS [13] DELEGATECALL [14] PUSH0
[15] PUSH0 [16] PUSH0 [17] CREATE [18] PUSH0
[19] PUSH0 [20] PUSH0 [21] CREATE
Semantics: CALL to 0x30413030 with zero gas (returns immediately);
DELEGATECALL then re-enters the contract itself with gas = ADDRESS and
retOffset = 1, so the contract recurses into itself until gas is exhausted;
each recursion level (and the top level) then performs two CREATEs with
value = 0, offset = 0, size = 0 (empty initcode).
File calldata.hex (0x01 repeated 80 times):
0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101
File state.json (pre-state):
{
"accounts": {
"000000000000000000000000000000ca1100f022": {
"balance": "0000000000000000000000000000000000000000000000000000000000000000",
"code": "0x5f5f5f5f5f5f5f63304130305f5ff13030f45f5f5ff05f5f5ff0",
"nonce": 0,
"storage": {}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
"balance": "0000000000000000000000000000000000000000000000003fffffffffffffff",
"code": "0x",
"nonce": 0,
"storage": {}
}
},
"tx_context": {
"block_base_fee": "0000000000000000000000000000000000000000000000000000000000000010",
"block_coinbase": "b94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"block_gas_limit": 10944489199640098,
"block_number": 1,
"block_prev_randao": "0000000000000000000000000000000000000000000000000000000000200000",
"block_timestamp": 1000,
"chain_id": "0000000000000000000000000000000000000000000000000000000000000001",
"gas_price": "0000000000000000000000000000000000000000000000000000000000000080"
}
}
Run the interpreter build (replace <dtvm_interp> with your dtvm binary,
built per Section 6):
<dtvm_interp> --format evm -m interpreter --evm-revision cancun --log-level debug \
--load-state state.json \
--sender a94f5374fce5edbc8e2a8697c15331677e6ebf0b \
--gas-limit 20000000 \
--contract-address 000000000000000000000000000000ca1100f022 \
--calldata "$(tr -d '\n\r' < calldata.hex)" \
--save-state result.json contract.evm.hex
echo $? # -> 0
Inspect the created account's balance in result.json:
python3 -c "
import json
d = json.load(open('result.json'))
print(d['accounts']['C83ED00DE536FBD81ED08F34B8CDB26DB29BF1A1']['balance'])
"
# -> 0000000000000000000000000000000000000000000000003FFFFFFFA4DABFFF (expected: all zeros)
2. What did you expect to see? (Required)
The bytecode is valid and halts successfully. Every CREATE in this test case
has value = 0, so each created account must have balance 0; the only
value movement is gas-fee accounting (sender debited the fee, coinbase credited
the fee, base fee burned). The sum of all balances must stay equal to the
sender's pre-state balance 0x3fffffffffffffff. The reference implementations
(Section 4) all create 0xc83ed00de5...1a1 with balance 0.
3. What did you see instead (Required)
The interpreter build exits 0, but credits the created account
0xc83ed00de536fbd81ed08f34b8cdb26db29bf1a1 with 0x3fffffffa4dabfff wei,
while no account is debited for it:
| Account |
Balance (wei) |
0xc83ed00d...1a1 (created) |
0x3fffffffa4dabfff = 4611686016898220031 |
sender 0xa94f5374...ebf0b |
0x3fffffff67697fff (gas fee only) |
coinbase 0xb94f5374...ebf0b |
0x4fc09800 (fee only) |
contract 0x...ca1100f022 |
0 |
Post-state total supply = 9223372034103629822 ≈ 2× the pre-state total
4611686018427387903. The increase equals the created account's balance minus
the burned base fee — no account was debited for the created balance.
Reproduced deterministically (3/3 CLI runs; 40/40 executor runs).
4. Error logs / Stack trace
DTVM's CLI does not expose a per-opcode EVM execution trace at any
--log-level. Evidence below: (a) the EELS reference execution of the same
test case, and (b) the cross-EVM comparison.
(a) EELS reference execution (same test case, Cancun):
- Receipt:
succeeded = True (no revert, no out-of-gas),
cumulative_gas_used = 10918771, 0 logs.
- 196 accounts in the post-state, including
0xc83ed00de536fbd81ed08f34b8cdb26db29bf1a1 with balance 0.
- Top-level instruction trace (stack before each instruction):
[ 0] PUSH0 gas=19977720 stack=[]
[10] CALL gas=19977699 stack=[0x0, 0x0, 0x0, 0x30413030, 0x0, 0x0]
(gas=0, to=0x30413030, value=0, in=0/0, out=0/0)
[11] ADDRESS gas=19943399 stack=[0x0, 0x0, 0x0, 0x1]
[13] DELEGATECALL gas=19943395 stack=[0x0, 0x0, 0x0, 0x1, 0xca1100f022, 0xca1100f022]
(gas=0xca1100f022, to=self, argsOffset=0, argsSize=0, retOffset=1, retSize=0)
[17] CREATE gas= 116 stack=[0x0, 0x0, 0x0, 0x0]
(value=0, offset=0, size=0)
[21] CREATE gas= 284 stack=[0x0, f0454382..., 0x0, 0x0, 0x0]
(value=0, offset=0, size=0)
(b) Cross-EVM comparison (same test case):
| EVM |
Execution |
Gas used |
Accounts |
c83ed00de5... balance |
| EELS (reference) |
SUCCEEDED (succeeded=True) |
10,918,771 |
196 |
0 |
| evmone 0.11.0 (t8n) |
SUCCESS (exit 0) |
~10.9M |
196 |
0 |
| geth 1.14.9 (statetest) |
SUCCESS (exit 0) |
n/a (statetest) |
1512 |
0 |
| DTVM interpreter build |
SUCCESS (exit 0) |
20,000,000 |
165 |
0x3fffffffa4dabfff |
All implementations execute the same bytecode to a successful halt and create
the same 0xc83ed00de5...1a1 account; only the DTVM interpreter build gives it
a non-zero balance.
5. What is the version of this project you are using? (Required)
DTVM git commit 8268abb94531b2553d20a77fcb08586cfcc014fc.
6. Environment (Required)
- OS: Ubuntu 20.04.6 LTS (Linux 6.8.0-111-generic x86_64)
- GCC: 11.4.0
- Clang: 10.0.0-4ubuntu1
- LLVM: 20.1.5 (
/usr/lib/llvm-20)
- CMake: 3.28.1
- Build (interpreter):
cmake -B build_interpreter_evm -DCMAKE_BUILD_TYPE=Debug -DZEN_ENABLE_EVM=ON -DZEN_ENABLE_SINGLEPASS_JIT=OFF
cmake --build build_interpreter_evm --parallel 4
produces build_interpreter_evm/dtvm (use this path as <dtvm_interp> in
Section 1).
- Execution command: see Section 1.
Bug Report
DTVM_INTERP_CREATE_GARBAGE_BALANCE.zip
The DTVM interpreter build (
-m interpreter) credits a freshlyCREATEdaccount with a non-zero balance out of thin air, violating EVM balance
conservation. With the test case below, a created account receives
0x3fffffffa4dabfffwei (~4.6e18) that no account is debited for — thepost-state total supply is roughly double the pre-state total.
1. Minimal reproduce step (Required)
The test case is a 26-byte contract (22 instructions) executed by the
interpreter build, with an 80-byte calldata and a two-account pre-state.
All inputs are packaged in the attached archive
DTVM_INTERP_CREATE_GARBAGE_BALANCE.zip— therepro/directory contains thethree input files plus a
run.shthat runs the exact command below and printsthe offending balance.
File
contract.evm.hex(contract bytecode under test):Disassembly:
Semantics:
CALLto0x30413030with zero gas (returns immediately);DELEGATECALLthen re-enters the contract itself withgas = ADDRESSandretOffset = 1, so the contract recurses into itself until gas is exhausted;each recursion level (and the top level) then performs two
CREATEs withvalue = 0,offset = 0,size = 0(empty initcode).File
calldata.hex(0x01repeated 80 times):File
state.json(pre-state):{ "accounts": { "000000000000000000000000000000ca1100f022": { "balance": "0000000000000000000000000000000000000000000000000000000000000000", "code": "0x5f5f5f5f5f5f5f63304130305f5ff13030f45f5f5ff05f5f5ff0", "nonce": 0, "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { "balance": "0000000000000000000000000000000000000000000000003fffffffffffffff", "code": "0x", "nonce": 0, "storage": {} } }, "tx_context": { "block_base_fee": "0000000000000000000000000000000000000000000000000000000000000010", "block_coinbase": "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", "block_gas_limit": 10944489199640098, "block_number": 1, "block_prev_randao": "0000000000000000000000000000000000000000000000000000000000200000", "block_timestamp": 1000, "chain_id": "0000000000000000000000000000000000000000000000000000000000000001", "gas_price": "0000000000000000000000000000000000000000000000000000000000000080" } }Run the interpreter build (replace
<dtvm_interp>with yourdtvmbinary,built per Section 6):
Inspect the created account's balance in
result.json:2. What did you expect to see? (Required)
The bytecode is valid and halts successfully. Every
CREATEin this test casehas
value = 0, so each created account must have balance 0; the onlyvalue movement is gas-fee accounting (sender debited the fee, coinbase credited
the fee, base fee burned). The sum of all balances must stay equal to the
sender's pre-state balance
0x3fffffffffffffff. The reference implementations(Section 4) all create
0xc83ed00de5...1a1with balance 0.3. What did you see instead (Required)
The interpreter build exits
0, but credits the created account0xc83ed00de536fbd81ed08f34b8cdb26db29bf1a1with0x3fffffffa4dabfffwei,while no account is debited for it:
0xc83ed00d...1a1(created)0x3fffffffa4dabfff= 46116860168982200310xa94f5374...ebf0b0x3fffffff67697fff(gas fee only)0xb94f5374...ebf0b0x4fc09800(fee only)0x...ca1100f0220Post-state total supply =
9223372034103629822≈ 2× the pre-state total4611686018427387903. The increase equals the created account's balance minusthe burned base fee — no account was debited for the created balance.
Reproduced deterministically (3/3 CLI runs; 40/40 executor runs).
4. Error logs / Stack trace
DTVM's CLI does not expose a per-opcode EVM execution trace at any
--log-level. Evidence below: (a) the EELS reference execution of the sametest case, and (b) the cross-EVM comparison.
(a) EELS reference execution (same test case, Cancun):
succeeded = True(no revert, no out-of-gas),cumulative_gas_used = 10918771, 0 logs.0xc83ed00de536fbd81ed08f34b8cdb26db29bf1a1with balance 0.(b) Cross-EVM comparison (same test case):
c83ed00de5...balancesucceeded=True)All implementations execute the same bytecode to a successful halt and create
the same
0xc83ed00de5...1a1account; only the DTVM interpreter build gives ita non-zero balance.
5. What is the version of this project you are using? (Required)
DTVM git commit
8268abb94531b2553d20a77fcb08586cfcc014fc.6. Environment (Required)
/usr/lib/llvm-20)build_interpreter_evm/dtvm(use this path as<dtvm_interp>inSection 1).