evm-stateful 0.11.1

Stateful evm-rs wrapped with tries.
Documentation
#![deny(
    unused_import_braces,
    unused_imports,
    unused_comparisons,
    unused_must_use,
    unused_variables,
    non_shorthand_field_patterns,
    unreachable_code
)]

use bigint::{Address, H256, M256, U256};
use block::{Account, Transaction};
use evm::errors::{PreExecutionError, RequireError};
use evm::{
    AccountChange, AccountCommitment, AccountState, HeaderParams, Memory, Patch, TransactionVM, ValidTransaction, VM,
};
use sha3::{Digest, Keccak256};
use std::cmp::min;
use std::collections::HashMap;
use std::ops::Deref;
use std::rc::Rc;
use trie::{Database, DatabaseGuard, DatabaseOwned, FixedSecureTrie, MemoryDatabase};

pub struct LiteralAccount {
    pub nonce: U256,
    pub balance: U256,
    pub storage: HashMap<U256, M256>,
    pub code: Vec<u8>,
}

#[derive(Copy, Clone, Debug)]
pub struct Stateful<'a, D: 'a> {
    database: &'a D,
    root: H256,
}

impl<'a, D> Stateful<'a, D> {
    pub fn new(database: &'a D, root: H256) -> Self {
        Self { database, root }
    }

    pub fn empty(database: &'a D) -> Self {
        Self::new(database, MemoryDatabase::new().create_empty().root())
    }
}

impl<'b, D: DatabaseOwned> Stateful<'b, D> {
    fn is_empty_hash(hash: H256) -> bool {
        hash == H256::from(Keccak256::digest(&[]).as_slice())
    }

    pub fn database(&self) -> &'b D {
        self.database
    }

    pub fn code(&self, hash: H256) -> Option<Vec<u8>> {
        let code_hashes = self.database.create_guard();

        if Self::is_empty_hash(hash) {
            Some(Vec::new())
        } else {
            code_hashes.get(hash)
        }
    }

    pub fn step<V: VM>(&self, vm: &mut V, block_number: U256, most_recent_block_hashes: &[H256]) {
        assert!(U256::from(most_recent_block_hashes.len()) >= min(block_number, U256::from(256)));

        let state = self.database.create_fixed_secure_trie(self.root);
        let code_hashes = self.database.create_guard();

        loop {
            match vm.step() {
                Ok(()) => break,
                Err(RequireError::Account(address)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let code = if Self::is_empty_hash(account.code_hash) {
                                Vec::new()
                            } else {
                                code_hashes.get(account.code_hash).unwrap()
                            };

                            vm.commit_account(AccountCommitment::Full {
                                nonce: account.nonce,
                                address,
                                balance: account.balance,
                                code: Rc::new(code),
                            })
                            .unwrap();
                        }
                        None => {
                            vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                        }
                    }
                }
                Err(RequireError::AccountCode(address)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let code = if Self::is_empty_hash(account.code_hash) {
                                Vec::new()
                            } else {
                                code_hashes.get(account.code_hash).unwrap()
                            };

                            vm.commit_account(AccountCommitment::Code {
                                address,
                                code: Rc::new(code),
                            })
                            .unwrap();
                        }
                        None => {
                            vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                        }
                    }
                }
                Err(RequireError::AccountStorage(address, index)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let storage = self.database.create_fixed_secure_trie(account.storage_root);
                            let value = storage.get(&H256::from(index)).unwrap_or_else(M256::zero);

                            vm.commit_account(AccountCommitment::Storage { address, index, value })
                                .unwrap();
                        }
                        None => {
                            vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                        }
                    }
                }
                Err(RequireError::Blockhash(number)) => {
                    let index = (block_number - number).as_usize();
                    vm.commit_blockhash(number, most_recent_block_hashes[index]).unwrap();
                }
            }
        }
    }

    #[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
    pub fn require_to_commit(&self, require: RequireError, root: Option<H256>) -> AccountCommitment {
        let state = self.database.create_fixed_secure_trie(root.unwrap_or(self.root));

        match require {
            RequireError::Account(address) => {
                let account: Option<Account> = state.get(&address);

                match account {
                    Some(account) => {
                        let code = if Self::is_empty_hash(account.code_hash) {
                            Vec::new()
                        } else {
                            self.code(account.code_hash).unwrap()
                        };

                        AccountCommitment::Full {
                            nonce: account.nonce,
                            address,
                            balance: account.balance,
                            code: Rc::new(code),
                        }
                    }
                    None => AccountCommitment::Nonexist(address),
                }
            }
            RequireError::AccountCode(address) => {
                let account: Option<Account> = state.get(&address);

                match account {
                    Some(account) => {
                        let code = if Self::is_empty_hash(account.code_hash) {
                            Vec::new()
                        } else {
                            self.code(account.code_hash).unwrap()
                        };

                        AccountCommitment::Code {
                            address,
                            code: Rc::new(code),
                        }
                    }
                    None => AccountCommitment::Nonexist(address),
                }
            }
            RequireError::AccountStorage(address, index) => {
                let account: Option<Account> = state.get(&address);

                match account {
                    Some(account) => {
                        let storage = self.database.create_fixed_secure_trie(account.storage_root);
                        let value = storage.get(&H256::from(index)).unwrap_or_else(M256::zero);

                        AccountCommitment::Storage { address, index, value }
                    }
                    None => AccountCommitment::Nonexist(address),
                }
            }
            RequireError::Blockhash(_) => panic!(),
        }
    }

    pub fn call<'a, M: Memory, P: Patch>(
        &self,
        patch: &'a P,
        transaction: ValidTransaction,
        block: &HeaderParams,
        most_recent_block_hashes: &[H256],
    ) -> TransactionVM<'a, M, P> {
        assert!(U256::from(most_recent_block_hashes.len()) >= min(block.number, U256::from(256)));

        let mut vm = TransactionVM::new(patch, transaction, block.clone());
        let state = self.database.create_fixed_secure_trie(self.root);
        let code_hashes = self.database.create_guard();

        loop {
            match vm.fire() {
                Ok(()) => break,
                Err(RequireError::Account(address)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let code = if Self::is_empty_hash(account.code_hash) {
                                Vec::new()
                            } else {
                                code_hashes.get(account.code_hash).unwrap()
                            };

                            vm.commit_account(AccountCommitment::Full {
                                nonce: account.nonce,
                                address,
                                balance: account.balance,
                                code: Rc::new(code),
                            })
                            .unwrap();
                        }
                        None => {
                            vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                        }
                    }
                }
                Err(RequireError::AccountCode(address)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let code = if Self::is_empty_hash(account.code_hash) {
                                Vec::new()
                            } else {
                                code_hashes.get(account.code_hash).unwrap()
                            };

                            vm.commit_account(AccountCommitment::Code {
                                address,
                                code: Rc::new(code),
                            })
                            .unwrap();
                        }
                        None => {
                            vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                        }
                    }
                }
                Err(RequireError::AccountStorage(address, index)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let storage = self.database.create_fixed_secure_trie(account.storage_root);
                            let value = storage.get(&H256::from(index)).unwrap_or_else(M256::zero);

                            vm.commit_account(AccountCommitment::Storage { address, index, value })
                                .unwrap();
                        }
                        None => {
                            vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                        }
                    }
                }
                Err(RequireError::Blockhash(number)) => {
                    let index = (block.number - number).as_usize();
                    vm.commit_blockhash(number, most_recent_block_hashes[index]).unwrap();
                }
            }
        }

        vm
    }

    pub fn sets(&mut self, accounts: &[(Address, LiteralAccount)]) {
        let mut state = self.database.create_fixed_secure_trie(self.root);
        let mut code_hashes = self.database.create_guard();

        for &(address, ref account) in accounts {
            let mut storage_trie = self.database.create_fixed_secure_empty();
            for (key, value) in &account.storage {
                if *value == M256::zero() {
                    storage_trie.remove(&H256::from(*key));
                } else {
                    storage_trie.insert(H256::from(*key), *value);
                }
            }

            let code_hash = H256::from(Keccak256::digest(&account.code).as_slice());
            code_hashes.set(code_hash, account.code.clone());

            let account = Account {
                nonce: account.nonce,
                balance: account.balance,
                storage_root: storage_trie.root(),
                code_hash,
            };

            state.insert(address, account);
        }

        self.root = state.root();
    }

    pub fn transit(&mut self, accounts: &[AccountChange]) {
        let mut state = self.database.create_fixed_secure_trie(self.root);
        let mut code_hashes = self.database.create_guard();

        for account in accounts {
            match account.clone() {
                AccountChange::Full {
                    nonce,
                    address,
                    balance,
                    changing_storage,
                    code,
                } => {
                    let changing_storage: HashMap<U256, M256> = changing_storage.into();

                    let mut account: Account = state.get(&address).unwrap();

                    let mut storage_trie = self.database.create_fixed_secure_trie(account.storage_root);
                    for (key, value) in changing_storage {
                        if value == M256::zero() {
                            storage_trie.remove(&H256::from(key));
                        } else {
                            storage_trie.insert(H256::from(key), value);
                        }
                    }

                    account.balance = balance;
                    account.nonce = nonce;
                    account.storage_root = storage_trie.root();
                    assert!(account.code_hash == H256::from(Keccak256::digest(&code).as_slice()));

                    state.insert(address, account);
                }
                AccountChange::IncreaseBalance(address, value) => match state.get(&address) {
                    Some(mut account) => {
                        account.balance = account.balance + value;
                        state.insert(address, account);
                    }
                    None => {
                        let account = Account {
                            nonce: U256::zero(),
                            balance: value,
                            storage_root: self.database.create_empty().root(),
                            code_hash: H256::from(Keccak256::digest(&[]).as_slice()),
                        };
                        state.insert(address, account);
                    }
                },
                AccountChange::Create {
                    nonce,
                    address,
                    balance,
                    storage,
                    code,
                } => {
                    let storage: HashMap<U256, M256> = storage.into();

                    let mut storage_trie = self.database.create_fixed_secure_empty();
                    for (key, value) in storage {
                        if value == M256::zero() {
                            storage_trie.remove(&H256::from(key));
                        } else {
                            storage_trie.insert(H256::from(key), value);
                        }
                    }

                    let code_hash = H256::from(Keccak256::digest(&code).as_slice());
                    code_hashes.set(code_hash, code.deref().clone());

                    let account = Account {
                        nonce,
                        balance,
                        storage_root: storage_trie.root(),
                        code_hash,
                    };

                    state.insert(address, account);
                }
                AccountChange::Nonexist(address) => {
                    state.remove(&address);
                }
            }
        }

        self.root = state.root();
    }

    pub fn execute<'a, M: Memory, P: Patch>(
        &mut self,
        patch: &'a P,
        transaction: ValidTransaction,
        block: &HeaderParams,
        most_recent_block_hashes: &[H256],
    ) -> TransactionVM<'a, M, P> {
        let vm = self.call::<_, P>(patch, transaction, &block, most_recent_block_hashes);
        let mut accounts = Vec::new();
        for account in vm.accounts() {
            accounts.push(account.clone());
        }
        self.transit(&accounts);
        vm
    }

    pub fn to_valid<P: Patch>(
        &self,
        patch: &P,
        transaction: &Transaction,
    ) -> Result<ValidTransaction, PreExecutionError> {
        let state = self.database.create_fixed_secure_trie(self.root);
        let code_hashes = self.database.create_guard();
        let account_patch = patch.account_patch().clone();
        let mut account_state = AccountState::new(account_patch);

        loop {
            match ValidTransaction::from_transaction(patch, transaction, &account_state) {
                Ok(val) => return val,
                Err(RequireError::Account(address)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let code = if Self::is_empty_hash(account.code_hash) {
                                Vec::new()
                            } else {
                                code_hashes.get(account.code_hash).unwrap()
                            };

                            account_state
                                .commit(AccountCommitment::Full {
                                    nonce: account.nonce,
                                    address,
                                    balance: account.balance,
                                    code: Rc::new(code),
                                })
                                .unwrap();
                        }
                        None => {
                            account_state.commit(AccountCommitment::Nonexist(address)).unwrap();
                        }
                    }
                }
                Err(RequireError::AccountCode(address)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let code = if Self::is_empty_hash(account.code_hash) {
                                Vec::new()
                            } else {
                                code_hashes.get(account.code_hash).unwrap()
                            };

                            account_state
                                .commit(AccountCommitment::Code {
                                    address,
                                    code: Rc::new(code),
                                })
                                .unwrap();
                        }
                        None => {
                            account_state.commit(AccountCommitment::Nonexist(address)).unwrap();
                        }
                    }
                }
                Err(RequireError::AccountStorage(address, index)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let storage = self.database.create_fixed_secure_trie(account.storage_root);
                            let value = storage.get(&H256::from(index)).unwrap_or_else(M256::zero);

                            account_state
                                .commit(AccountCommitment::Storage { address, index, value })
                                .unwrap();
                        }
                        None => {
                            account_state.commit(AccountCommitment::Nonexist(address)).unwrap();
                        }
                    }
                }
                Err(RequireError::Blockhash(_)) => panic!(),
            }
        }
    }

    pub fn root(&self) -> H256 {
        self.root
    }

    pub fn state_of<'a>(&'a self, root: H256) -> FixedSecureTrie<<D as Database<'a>>::Guard, Address, Account> {
        self.database.create_fixed_secure_trie::<Address, Account>(root)
    }

    pub fn state<'a>(&'a self) -> FixedSecureTrie<<D as Database<'a>>::Guard, Address, Account> {
        self.state_of(self.root())
    }

    pub fn storage_state_of<'a>(&'a self, root: H256) -> FixedSecureTrie<<D as Database<'a>>::Guard, H256, M256> {
        self.database.create_fixed_secure_trie::<H256, M256>(root)
    }

    pub fn storage_state<'a>(
        &'a self,
        address: Address,
    ) -> Option<FixedSecureTrie<<D as Database<'a>>::Guard, H256, M256>> {
        // Specify the full type as a workaround for https://github.com/rust-lang/rust/issues/55756
        let state: FixedSecureTrie<<D as Database<'a>>::Guard, Address, Account> = self.state();
        let account = state.get(&address);

        match account {
            Some(account) => Some(self.storage_state_of(account.storage_root)),
            None => None,
        }
    }
}

pub type MemoryStateful<'a> = Stateful<'a, MemoryDatabase>;