..

Revm State

account_info

比较两个账户的时候,只比较了balancenoncecode_hash,不包括code,并且hash也是只对这三个进行计算。同时我们可以知道,每一次账户进行了转账、交易等操作,那么他们的状态就不一样了

impl PartialEq for AccountInfo {
    fn eq(&self, other: &Self) -> bool {
        self.balance == other.balance
            && self.nonce == other.nonce
            && self.code_hash == other.code_hash
    }
}

impl Hash for AccountInfo {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.balance.hash(state);
        self.nonce.hash(state);
        self.code_hash.hash(state);
    }
}

并且AccountInfo有一些函数:

  • copy_without_code(): 复制出来一个新的AccountInfo,移除了bytecode
  • without_code(): 在原来的AccountInfo基础上,移除bytecode

判断一个账户是否存在,是看账户是否有代码、余额是否为0、nonce是否为0。因此,只要地址上有钱,它就是存在的,比如0地址,虽然没人控制

#[inline]
pub fn is_empty(&self) -> bool {
    let code_empty = self.is_empty_code_hash() || self.code_hash.is_zero();
    code_empty && self.balance.is_zero() && self.nonce == 0
}

/// Returns `true` if the account is not empty.
#[inline]
pub fn exists(&self) -> bool {
    !self.is_empty()
}

下面的函数,估计是合约创建的时候使用的,直接把nonce设置为1了。对应了stackexchange上的疑问:

/// Initializes an [`AccountInfo`] with the given bytecode, setting its balance to zero, its
/// nonce to `1`, and calculating the code hash from the given bytecode.
#[inline]
pub fn from_bytecode(bytecode: Bytecode) -> Self {
    let hash = bytecode.hash_slow();

    AccountInfo {
        balance: U256::ZERO,
        nonce: 1,
        code: Some(bytecode),
        code_hash: hash,
    }
}

types

  • EvmState:每一个地址,对应一个账户状态,用mapping来映射
  • EvmStorage:每个账户也是通过mapping来映射状态,从0开始,一一对应每个slot
  • TransientStorage:这个就很有意思了,对应了Solidity中的tstore操作码。可以发现它的键是元组(Address, U256),值是U256,说明瞬时存储只会简单地记录某个值,独立于storage和state之外
/// EVM State is a mapping from addresses to accounts.
pub type EvmState = HashMap<Address, Account>;

/// Structure used for EIP-1153 transient storage
pub type TransientStorage = HashMap<(Address, U256), U256>;

/// An account's Storage is a mapping from 256-bit integer keys to [EvmStorageSlot]s.
pub type EvmStorage = HashMap<U256, EvmStorageSlot>;

lib

每一个EVM账户的基本信息:

  • info:上面说过了

  • storage: HashMap<U256, EvmStorageSlot>

  • status: 有这么几种类型

    • Loaded: 默认状态,账户已经被加载,但是没有任何交互操作

    • Created: 账户刚刚被创建,用来指示我们不需要去数据库获取它的storage信息

    • SelfDestructed: 是否自毁了

    • Touched: 只有被标记为这个状态的时候,我们才会把这个账户存到数据库

    • LoadedAsNotExisting: 用于兼容硬分叉。

      • 在以太坊的 Spurious Dragon 硬分叉(EIP-161)之前:空账户不存在的账户是两个不同的状态,这种区分导致了一些问题,比如可能会在状态树中存储大量的空账户。用于处理 Spurious Dragon 硬分叉之前的区块

      • 当一个账户被标记为 LoadedAsNotExisting 时,表示这个账户从数据库中加载时就不存在;这与账户存在但为空(balance = 0,nonce = 0,code = empty)是不同的状态

      • 创建一个新的账户的时候,new_not_existing(),用的是LoadedAsNotExisting

      • 比如下面的代码:在 Spurious Dragon 之后:直接检查账户是否为空;在 Spurious Dragon 之前:需要检查账户是否被标记为 LoadedAsNotExisting 且未被触及

        pub fn state_clear_aware_is_empty(&self, spec: SpecId) -> bool {
            if SpecId::is_enabled_in(spec, SpecId::SPURIOUS_DRAGON) {
                self.is_empty()
            } else {
                let loaded_not_existing = self.is_loaded_as_not_existing();
                let is_not_touched = !self.is_touched();
                loaded_not_existing && is_not_touched
            }
        }
        
    • Cold: 是冷状态还是热状态,用于gas计算

pub struct Account {
    /// Balance, nonce, and code
    pub info: AccountInfo,
    /// Storage cache
    pub storage: EvmStorage,
    /// Account status flags
    pub status: AccountStatus,
}

oh?合约被销毁之后,仍然可以重新活过来。这种|=-=作为相反操作第一次见,用bit运算来修改状态,效率很高

/// Marks the account as self destructed.
pub fn mark_selfdestruct(&mut self) {
    self.status |= AccountStatus::SelfDestructed;
}

/// Unmarks the account as self destructed.
pub fn unmark_selfdestruct(&mut self) {
    self.status -= AccountStatus::SelfDestructed;
}

返回所有被修改的slot,这个在fuzz等场景我觉得非常有用

/// Returns an iterator over the storage slots that have been changed.
///
/// See also [EvmStorageSlot::is_changed].
pub fn changed_storage_slots(&self) -> impl Iterator<Item = (&U256, &EvmStorageSlot)> {
    self.storage.iter().filter(|(_, slot)| slot.is_changed())
}

每一个slot,有一个旧值和一个新值,并且通过is_cold来指示这个slot是否已经热了

pub struct EvmStorageSlot {
    /// Original value of the storage slot
    pub original_value: U256,
    /// Present value of the storage slot
    pub present_value: U256,
    /// Represents if the storage slot is cold
    pub is_cold: bool,
}