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_ref should never be called

What it means

This Database implementation for Revm deliberately always returns AccountInfo with code: Some(...), so the engine never needs to resolve bytecode by hash — code_by_hash_ref is left as a loud unreachable panic. Seeing it means Revm requested bytecode by hash, which happens when some code path returned code: None or when a different Revm version exercises the DB differently than the design assumed.

Source

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

            let modules = self.modules.lock().unwrap();
            let application_id = address_to_user_application_id(address);
            modules.contains_key(&application_id)
        };
        self.inner.read_basic_ref(
            InnerDatabase::<Runtime>::get_contract_account_info,
            address,
            is_newly_created,
        )
    }

    /// There are two ways to implement the trait:
    /// * Returns entries with "code: Some(...)"
    /// * Returns entries with "code: None".
    ///
    /// Since we choose the first design, `code_by_hash_ref` is not needed. There
    /// is an example in the Revm source code of this kind.
    fn code_by_hash_ref(&self, _code_hash: B256) -> Result<Bytecode, ExecutionError> {
        panic!("Returned AccountInfo should have code: Some(...) and so code_by_hash_ref should never be called");
    }

    /// Accesses the storage by the relevant remote access function.
    fn storage_ref(&self, address: Address, index: U256) -> Result<U256, ExecutionError> {
        self.inner.read_storage(
            InnerDatabase::<Runtime>::get_contract_storage_value,
            address,
            index,
        )
    }

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

impl<Runtime> Database for ContractDatabase<Runtime>
where

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Pin/keep the revm and alloy versions this Database was designed against; if upgrading, audit the change notes for code-by-hash behavior.
  2. If your fork of basic_ref ever returns code: None, make it return the full bytecode instead — the panic is the designed tripwire for exactly that.
  3. As a last resort, implement code_by_hash_ref by fetching bytecode from the contract storage instead of panicking.

Example fix

// before (design invariant broken)
fn basic_ref(&self, address: Address) -> ... { AccountInfo { code: None, ... } } // -> code_by_hash_ref panics

// after (restore invariant)
fn basic_ref(&self, address: Address) -> ... { AccountInfo { code: Some(self.load_bytecode(address)?), ... } }
Defensive patterns

Strategy: validation

Validate before calling

# Build/CI check: keep revm & alloy at the validated versions
cargo update --dry-run 2>/dev/null | grep -E 'revm|alloy' || echo 'pinned OK'
# and a smoke test executing EXTCODEHASH-heavy contracts in CI to trip the panic early

Type guard

// Invariant check (test-only): every account exposed to revm must carry inline code
assert!(matches!(db.basic_ref(addr)?, Some(info) if info.code.is_some()));

Prevention

When it happens

Trigger: Upgrading the revm/alloy crates to a version whose interpreter calls code_by_hash_ref (e.g. after basic_ref changes or EXT*CODEHASH handling changes); any modification to basic_ref that starts returning code: None for some accounts; a revm feature (state overrides, block caching) that lazily fetches code.

Common situations: Routine dependency bumps of revm/alloy re-enabling a code path the Linera DB explicitly declined to implement; refactors of the account-loading code; introducing a new execution mode (e.g. state override sets) that stores code by hash.

Related errors


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