linera-io/linera-protocol · critical

Returned AccountInfo should have code: Some(...) and so code

Error message

Returned AccountInfo should have code: Some(...) and so code_by_hash should never be called

What it means

The mutable (&mut self) twin of the ref variant: this Revm Database impl guarantees every AccountInfo carries its bytecode inline (code: Some), so code_by_hash is never supposed to be called and panics to say so. Reaching it indicates the engine took the by-hash code path, which the inline-code design explicitly rules out.

Source

Thrown at linera-execution/src/evm/database.rs:846

    }

    fn block_hash_ref(&self, number: u64) -> Result<B256, ExecutionError> {
        Ok(keccak256(number.to_string().as_bytes()))
    }
}

impl<Runtime> Database for ContractDatabase<Runtime>
where
    Runtime: ContractRuntime,
{
    type Error = ExecutionError;

    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, ExecutionError> {
        self.basic_ref(address)
    }

    fn code_by_hash(&mut self, _code_hash: B256) -> Result<Bytecode, ExecutionError> {
        panic!("Returned AccountInfo should have code: Some(...) and so code_by_hash should never be called");
    }

    fn storage(&mut self, address: Address, index: U256) -> Result<U256, ExecutionError> {
        self.storage_ref(address, index)
    }

    fn block_hash(&mut self, number: u64) -> Result<B256, ExecutionError> {
        <Self as DatabaseRef>::block_hash_ref(self, number)
    }
}

impl<Runtime> DatabaseCommit for ContractDatabase<Runtime>
where
    Runtime: ContractRuntime,
{
    fn commit(&mut self, changes: EvmState) {
        self.inner.changes = changes;
    }

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Revert/pin the revm and alloy versions to the ones validated for this codebase.
  2. Audit any change to basic/basic_ref so they keep returning code: Some(bytecode).
  3. If the upgrade is required, implement code_by_hash by reading bytecode from contract storage rather than leaving the panic.

Example fix

# Cargo.toml — before (bump triggered the path)
revm = "newer"
# after — pin to the version the database design assumes
revm = "=<previously-validated-version>"
Defensive patterns

Strategy: validation

Validate before calling

# Cargo.lock discipline: pin revm & alloy to the versions the Database design assumes
cargo update -p revm --precise <validated-version>

Type guard

// Test invariant: accounts loaded for revm always include their bytecode
let info = db.basic(addr)?.expect("account");
assert!(info.code.is_some(), "code must be inline; code_by_hash must stay unreachable");

Prevention

When it happens

Trigger: A revm version whose Database/DatabaseCommit flow calls code_by_hash during execution or state loading; account-loading code that returns code: None; a custom journaled/override path that stores code by hash. Trigger is engine-internal — normal Linera EVM transactions never reach it.

Common situations: Bumping revm/alloy patch versions that change when the interpreter resolves code; local modifications to the EVM database glue; introducing caching layers that strip bytecode from AccountInfo.

Related errors


AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22). Data as JSON: /api/errors/c7727958a0be354d. Report an issue: GitHub.