Skip to content

t8n: currentGasLimit >= 2^63 wraps signed and rejects valid txs #1484

@chfast

Description

@chfast

Summary

evmone-t8n treats env.currentGasLimit as signed int64_t (via JSON loader), so values >= 2^63 wrap negative and cause valid transactions to be rejected as "gas limit reached".

go-ethereum evm t8n treats currentGasLimit as uint64 and executes the same transaction.

Reproducer (Cancun)

alloc.json

{
  "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
    "code": "0x",
    "nonce": "0x0",
    "balance": "0xde0b6b3a7640000",
    "storage": {}
  }
}

env.json

{
  "currentCoinbase": "0x8888f1f195afa192cfee860698584c030f4c9db1",
  "currentNumber": "0x01",
  "currentTimestamp": "0x54c99069",
  "currentGasLimit": "0x8000000000000000",
  "currentRandom": "0x0000000000000000000000000000000000000000000000000000000000000001",
  "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "currentBaseFee": "0x1",
  "currentExcessBlobGas": "0x0",
  "withdrawals": []
}

txs.json

[
  {
    "to": "0x095E7BAea6a6c7c4c2DfeB977eFac326aF552d87",
    "input": "0x",
    "gas": "0x5208",
    "nonce": "0x0",
    "value": "0x0",
    "gasPrice": "0x1",
    "chainId": "0x1",
    "sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
    "v": "0x26",
    "r": "0x62d32a1bac6b89021fce16cf51479a8b00f26523933afde1b62f7957ed1943a3",
    "s": "0x6f3941435cb48b0c649c1b0d707fc6878407f6287c9cd89c33c0048bd2710e45"
  }
]

Commands

# evmone
evmone-t8n \
  --state.fork Cancun --state.chainid 1 \
  --input.alloc alloc.json --input.env env.json --input.txs txs.json \
  --output.basedir out-evmone --output.result result.json --output.alloc alloc.json

# geth
evm t8n \
  --state.fork Cancun --state.chainid 1 \
  --input.alloc alloc.json --input.env env.json --input.txs txs.json \
  --output.basedir out-geth --output.result result.json --output.alloc alloc.json

Observed divergence

  • evmone:
    • rejected[0].error = "gas limit reached"
    • gasUsed = 0x0
    • empty tx/receipt roots (0x56e81f...)
  • geth:
    • tx executes successfully
    • gasUsed = 0x5208
    • non-empty tx/receipt roots

State roots differ:

  • evmone: 0x517f2cdf6adb1a644878c390ffab4e130f1bed4b498ef7ce58c5addd98d61018
  • geth: 0x5a605f957d4aa4145411d63287ddf1bae2202c30cc6ec86ae003423bb6343778

Control (boundary)

If only currentGasLimit is changed to 0x7fffffffffffffff (2^63 - 1), evmone and geth agree and both execute the tx.

This isolates the issue to signed handling above int64 max.

Suspected root cause

currentGasLimit and tx gas are parsed via from_json<int64_t> using unsigned parse + signed cast:

  • test/utils/statetest_loader.cpp
    • integer_from_json<T> uses static_cast<T>(std::stoull(...))
    • block field assignment: .gas_limit = from_json<int64_t>(j.at("currentGasLimit"))

Execution path then uses signed comparison:

  • test/t8n/t8n.cpp: int64_t block_gas_left = block.gas_limit;
  • test/state/state.cpp: if (tx.gas_limit > block_gas_left) return GAS_LIMIT_REACHED;

For 0x8000000000000000, block_gas_left becomes negative, causing false rejection.

For reference, geth t8n env models currentGasLimit as uint64:

  • cmd/evm/internal/t8ntool/execution.go: GasLimit uint64 \json:"currentGasLimit"``

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions