Loading a state JSON whose uint256 hex field (after padding to even length) encodes more
than 32 bytes aborts the process instead of reporting an error.
1. Minimal reproduce step
Contract bytecode (contract.evm.hex): 5f00 — PUSH0; STOP (irrelevant; the crash
happens during --load-state, before execution).
bad.state.json — identical to ok.state.json except the contract account's balance
is 66 hex chars (33 bytes) instead of 64:
{
"accounts": {
"00000000000000000000000000000000000000f1": {
"balance": "00000000000000000000000000000000000000000000000000000000ffff",
"code": "0x5f00",
"nonce": 0,
"storage": {}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
"balance": "000000000000000000000000000000000000000000000000000000ffffffff",
"code": "0x",
"nonce": 0,
"storage": {}
}
},
"tx_context": { "...": "see bad.state.json — all 64-char hex" }
}
<dtvm_bin> --format evm -m interpreter --evm-revision cancun --log-level off \
--load-state bad.state.json \
--sender a94f5374fce5edbc8e2a8697c15331677e6ebf0b \
--gas-limit 100000 \
--contract-address 00000000000000000000000000000000000000f1 \
--save-state bad.result.json \
contract.evm.hex
; echo "rc=$?"
Output: rc=134 (SIGABRT, "Aborted (core dumped)"). Control run with ok.state.json
(balance 64 hex chars, byte-for-byte identical elsewhere): rc=0, ok.result.json
written. The same abort is reached through any other parseUint256-parsed field:
account balance, tx_context.gas_price, block_prev_randao, block_base_fee,
chain_id, blob_base_fee.
2. What did you expect to see
The CLI rejects the malformed state with an error message and a non-zero, non-crash
exit code. parseUint256 already carries the intended message
("Invalid Uint256 hex string too long"); it just never reaches the user.
3. What did you see instead
terminate called after throwing an instance of 'zen::common::Error'
what(): invalid raw data
Aborted (core dumped) # rc=134
4. Error logs / Stack trace
No per-opcode EVM execution trace is involved; the crash is at state load. Chain
(this checkout, commit 338d123a):
src/utils/evm.cpp:109-122 — parseUint256 throws Error(InvalidRawData, ...)
when the hex encodes > 32 bytes.
src/utils/evm.cpp:313 loadState(...) — parses account balance (line ~344)
and the tx_context uint256 fields without any try/catch; the function's error
protocol for structural problems is a bool return, so the exception escapes it.
src/cli/dtvm.cpp:331-336 — the call site only checks the bool return and is
not inside a try block.
Error derives from std::exception (src/common/errors.h:56) → the exception
escapes main → std::terminate → abort().
For contrast, the same file already wraps its address/bytes32 access-list parsing in
catch (...) (src/utils/evm.cpp:485,505).
Suggested fix: catch the parse exceptions inside loadState and return false
(matching its existing error protocol), or wrap the call site so the CLI prints the
intended message and exits cleanly.
5. What is the version
DTVM git commit 338d123a5d9d4a464d8d0151158447d500a9997a (2026-08-11), interpreter
mode binary.
6. Environment
- 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
Loading a state JSON whose uint256 hex field (after padding to even length) encodes more
than 32 bytes aborts the process instead of reporting an error.
1. Minimal reproduce step
Contract bytecode (
contract.evm.hex):5f00—PUSH0; STOP(irrelevant; the crashhappens during
--load-state, before execution).bad.state.json— identical took.state.jsonexcept the contract account'sbalanceis 66 hex chars (33 bytes) instead of 64:
{ "accounts": { "00000000000000000000000000000000000000f1": { "balance": "00000000000000000000000000000000000000000000000000000000ffff", "code": "0x5f00", "nonce": 0, "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { "balance": "000000000000000000000000000000000000000000000000000000ffffffff", "code": "0x", "nonce": 0, "storage": {} } }, "tx_context": { "...": "see bad.state.json — all 64-char hex" } }Output:
rc=134(SIGABRT, "Aborted (core dumped)"). Control run withok.state.json(balance 64 hex chars, byte-for-byte identical elsewhere):
rc=0,ok.result.jsonwritten. The same abort is reached through any other
parseUint256-parsed field:account
balance,tx_context.gas_price,block_prev_randao,block_base_fee,chain_id,blob_base_fee.2. What did you expect to see
The CLI rejects the malformed state with an error message and a non-zero, non-crash
exit code.
parseUint256already carries the intended message(
"Invalid Uint256 hex string too long"); it just never reaches the user.3. What did you see instead
4. Error logs / Stack trace
No per-opcode EVM execution trace is involved; the crash is at state load. Chain
(this checkout, commit
338d123a):src/utils/evm.cpp:109-122—parseUint256throwsError(InvalidRawData, ...)when the hex encodes > 32 bytes.
src/utils/evm.cpp:313loadState(...)— parses accountbalance(line ~344)and the
tx_contextuint256 fields without any try/catch; the function's errorprotocol for structural problems is a
boolreturn, so the exception escapes it.src/cli/dtvm.cpp:331-336— the call site only checks theboolreturn and isnot inside a try block.
Errorderives fromstd::exception(src/common/errors.h:56) → the exceptionescapes
main→std::terminate→abort().For contrast, the same file already wraps its address/bytes32 access-list parsing in
catch (...)(src/utils/evm.cpp:485,505).Suggested fix: catch the parse exceptions inside
loadStateand returnfalse(matching its existing error protocol), or wrap the call site so the CLI prints the
intended message and exits cleanly.
5. What is the version
DTVM git commit
338d123a5d9d4a464d8d0151158447d500a9997a(2026-08-11), interpretermode binary.
6. Environment
/usr/lib/llvm-20), CMake 3.28.1