#![cfg_attr(not(test), warn(unused_crate_dependencies))]
#![cfg_attr(not(feature = "std"), no_std)]
mod account_info;
mod types;
pub use bytecode;
pub use account_info::AccountInfo;
pub use bytecode::Bytecode;
pub use primitives;
pub use types::{EvmState, EvmStorage, TransientStorage};
use bitflags::bitflags;
use core::hash::Hash;
use primitives::{HashMap, U256};
use specification::hardfork::SpecId;
#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Account {
pub info: AccountInfo,
pub storage: EvmStorage,
pub status: AccountStatus,
}
impl Account {
pub fn new_not_existing() -> Self {
Self {
info: AccountInfo::default(),
storage: HashMap::default(),
status: AccountStatus::LoadedAsNotExisting,
}
}
#[inline]
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
}
}
pub fn mark_selfdestruct(&mut self) {
self.status |= AccountStatus::SelfDestructed;
}
pub fn unmark_selfdestruct(&mut self) {
self.status -= AccountStatus::SelfDestructed;
}
pub fn is_selfdestructed(&self) -> bool {
self.status.contains(AccountStatus::SelfDestructed)
}
pub fn mark_touch(&mut self) {
self.status |= AccountStatus::Touched;
}
pub fn unmark_touch(&mut self) {
self.status -= AccountStatus::Touched;
}
pub fn is_touched(&self) -> bool {
self.status.contains(AccountStatus::Touched)
}
pub fn mark_created(&mut self) {
self.status |= AccountStatus::Created;
}
pub fn unmark_created(&mut self) {
self.status -= AccountStatus::Created;
}
pub fn mark_cold(&mut self) {
self.status |= AccountStatus::Cold;
}
pub fn mark_warm(&mut self) -> bool {
if self.status.contains(AccountStatus::Cold) {
self.status -= AccountStatus::Cold;
true
} else {
false
}
}
pub fn is_loaded_as_not_existing(&self) -> bool {
self.status.contains(AccountStatus::LoadedAsNotExisting)
}
pub fn is_created(&self) -> bool {
self.status.contains(AccountStatus::Created)
}
pub fn is_empty(&self) -> bool {
self.info.is_empty()
}
pub fn changed_storage_slots(&self) -> impl Iterator<Item = (&U256, &EvmStorageSlot)> {
self.storage.iter().filter(|(_, slot)| slot.is_changed())
}
}
impl From<AccountInfo> for Account {
fn from(info: AccountInfo) -> Self {
Self {
info,
storage: HashMap::default(),
status: AccountStatus::Loaded,
}
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
pub struct AccountStatus: u8 {
const Loaded = 0b00000000;
const Created = 0b00000001;
const SelfDestructed = 0b00000010;
const Touched = 0b00000100;
const LoadedAsNotExisting = 0b0001000;
const Cold = 0b0010000;
}
}
impl Default for AccountStatus {
fn default() -> Self {
Self::Loaded
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct EvmStorageSlot {
pub original_value: U256,
pub present_value: U256,
pub is_cold: bool,
}
impl EvmStorageSlot {
pub fn new(original: U256) -> Self {
Self {
original_value: original,
present_value: original,
is_cold: false,
}
}
pub fn new_changed(original_value: U256, present_value: U256) -> Self {
Self {
original_value,
present_value,
is_cold: false,
}
}
pub fn is_changed(&self) -> bool {
self.original_value != self.present_value
}
pub fn original_value(&self) -> U256 {
self.original_value
}
pub fn present_value(&self) -> U256 {
self.present_value
}
pub fn mark_cold(&mut self) {
self.is_cold = true;
}
pub fn mark_warm(&mut self) -> bool {
core::mem::replace(&mut self.is_cold, false)
}
}
#[cfg(test)]
mod tests {
use crate::Account;
use primitives::{KECCAK_EMPTY, U256};
#[test]
fn account_is_empty_balance() {
let mut account = Account::default();
assert!(account.is_empty());
account.info.balance = U256::from(1);
assert!(!account.is_empty());
account.info.balance = U256::ZERO;
assert!(account.is_empty());
}
#[test]
fn account_is_empty_nonce() {
let mut account = Account::default();
assert!(account.is_empty());
account.info.nonce = 1;
assert!(!account.is_empty());
account.info.nonce = 0;
assert!(account.is_empty());
}
#[test]
fn account_is_empty_code_hash() {
let mut account = Account::default();
assert!(account.is_empty());
account.info.code_hash = [1; 32].into();
assert!(!account.is_empty());
account.info.code_hash = [0; 32].into();
assert!(account.is_empty());
account.info.code_hash = KECCAK_EMPTY;
assert!(account.is_empty());
}
#[test]
fn account_state() {
let mut account = Account::default();
assert!(!account.is_touched());
assert!(!account.is_selfdestructed());
account.mark_touch();
assert!(account.is_touched());
assert!(!account.is_selfdestructed());
account.mark_selfdestruct();
assert!(account.is_touched());
assert!(account.is_selfdestructed());
account.unmark_selfdestruct();
assert!(account.is_touched());
assert!(!account.is_selfdestructed());
}
#[test]
fn account_is_cold() {
let mut account = Account::default();
assert!(!account.status.contains(crate::AccountStatus::Cold));
assert!(!account.mark_warm());
account.mark_cold();
assert!(account.status.contains(crate::AccountStatus::Cold));
assert!(account.mark_warm());
}
}