use std::{
cell::{RefCell, RefMut},
collections::{BTreeMap, BTreeSet, HashMap, HashSet},
collections::hash_map::Entry,
sync::Arc,
fmt,
};
use common_types::{
state_diff::StateDiff,
basic_account::BasicAccount,
errors::VapcoreError as Error,
};
use vapory_types::{Address, H256, U256};
use vaptrie::{TrieDB, Result as TrieResult};
use trie_vm_factories::{Factories, VmFactory};
use tetsy_hash_db::HashDB;
use tetsy_keccak_hash::{KECCAK_EMPTY, KECCAK_NULL_RLP};
use tetsy_keccak_hasher::KeccakHasher;
use tetsy_kvdb::DBValue;
use log::{warn, trace};
use tetsy_bytes::Bytes;
use vapcore_pod::{self, PodAccount, PodState};
use tetsy_trie_db::{Trie, TrieError, Recorder};
use crate::{
account::Account,
backend::Backend,
};
#[derive(Eq, PartialEq, Clone, Copy, Debug)]
enum AccountState {
CleanFresh,
CleanCached,
Dirty,
Committed,
}
#[derive(Debug)]
struct AccountEntry {
account: Option<Account>,
old_balance: Option<U256>,
state: AccountState,
}
impl AccountEntry {
fn is_dirty(&self) -> bool {
self.state == AccountState::Dirty
}
fn exists_and_is_null(&self) -> bool {
self.account.as_ref().map_or(false, |a| a.is_null())
}
fn clone_if_dirty(&self) -> Option<AccountEntry> {
match self.is_dirty() {
true => Some(self.clone_dirty()),
false => None,
}
}
fn clone_dirty(&self) -> AccountEntry {
AccountEntry {
old_balance: self.old_balance,
account: self.account.as_ref().map(Account::clone_dirty),
state: self.state,
}
}
fn new_dirty(account: Option<Account>) -> AccountEntry {
AccountEntry {
old_balance: account.as_ref().map(|a| a.balance().clone()),
account,
state: AccountState::Dirty,
}
}
fn new_clean(account: Option<Account>) -> AccountEntry {
AccountEntry {
old_balance: account.as_ref().map(|a| a.balance().clone()),
account,
state: AccountState::CleanFresh,
}
}
fn new_clean_cached(account: Option<Account>) -> AccountEntry {
AccountEntry {
old_balance: account.as_ref().map(|a| a.balance().clone()),
account,
state: AccountState::CleanCached,
}
}
fn overwrite_with(&mut self, other: AccountEntry) {
self.state = other.state;
match other.account {
Some(acc) => {
if let Some(ref mut ours) = self.account {
ours.overwrite_with(acc);
} else {
self.account = Some(acc);
}
},
None => self.account = None,
}
}
}
pub struct State<B> {
db: B,
root: H256,
cache: RefCell<HashMap<Address, AccountEntry>>,
checkpoints: RefCell<Vec<HashMap<Address, Option<AccountEntry>>>>,
account_start_nonce: U256,
factories: Factories,
}
#[derive(Copy, Clone)]
enum RequireCache {
None,
CodeSize,
Code,
}
#[derive(Debug, PartialEq)]
pub enum CleanupMode<'a> {
ForceCreate,
NoEmpty,
TrackTouched(&'a mut HashSet<Address>),
}
pub trait StateInfo {
fn nonce(&self, a: &Address) -> TrieResult<U256>;
fn balance(&self, a: &Address) -> TrieResult<U256>;
fn storage_at(&self, address: &Address, key: &H256) -> TrieResult<H256>;
fn code(&self, a: &Address) -> TrieResult<Option<Arc<Bytes>>>;
}
impl<B: Backend> StateInfo for State<B> {
fn nonce(&self, a: &Address) -> TrieResult<U256> { State::nonce(self, a) }
fn balance(&self, a: &Address) -> TrieResult<U256> { State::balance(self, a) }
fn storage_at(&self, address: &Address, key: &H256) -> TrieResult<H256> { State::storage_at(self, address, key) }
fn code(&self, address: &Address) -> TrieResult<Option<Arc<Bytes>>> { State::code(self, address) }
}
const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with valid root. Creating a SecTrieDB with a valid root will not fail. \
Therefore creating a SecTrieDB with this state's root will not fail.";
impl<B: Backend> State<B> {
pub fn new(mut db: B, account_start_nonce: U256, factories: Factories) -> State<B> {
let mut root = H256::zero();
{
let _ = factories.trie.create(db.as_hash_db_mut(), &mut root);
}
State {
db,
root,
cache: RefCell::new(HashMap::new()),
checkpoints: RefCell::new(Vec::new()),
account_start_nonce,
factories,
}
}
pub fn from_existing(db: B, root: H256, account_start_nonce: U256, factories: Factories) -> TrieResult<State<B>> {
if !db.as_hash_db().contains(&root, tetsy_hash_db::EMPTY_PREFIX) {
return Err(Box::new(TrieError::InvalidStateRoot(root)));
}
let state = State {
db,
root,
cache: RefCell::new(HashMap::new()),
checkpoints: RefCell::new(Vec::new()),
account_start_nonce,
factories,
};
Ok(state)
}
pub fn vm_factory(&self) -> VmFactory {
self.factories.vm.clone()
}
pub fn checkpoint(&mut self) -> usize {
let checkpoints = self.checkpoints.get_mut();
let index = checkpoints.len();
checkpoints.push(HashMap::new());
index
}
pub fn discard_checkpoint(&mut self) {
let last = self.checkpoints.get_mut().pop();
if let Some(mut checkpoint) = last {
if let Some(ref mut prev) = self.checkpoints.get_mut().last_mut() {
if prev.is_empty() {
**prev = checkpoint;
} else {
for (k, v) in checkpoint.drain() {
prev.entry(k).or_insert(v);
}
}
}
}
}
pub fn revert_to_checkpoint(&mut self) {
if let Some(mut checkpoint) = self.checkpoints.get_mut().pop() {
for (k, v) in checkpoint.drain() {
match v {
Some(v) => {
match self.cache.get_mut().entry(k) {
Entry::Occupied(mut e) => {
e.get_mut().overwrite_with(v);
},
Entry::Vacant(e) => {
e.insert(v);
}
}
},
None => {
if let Entry::Occupied(e) = self.cache.get_mut().entry(k) {
if e.get().is_dirty() {
e.remove();
}
}
}
}
}
}
}
fn insert_cache(&self, address: &Address, account: AccountEntry) {
let is_dirty = account.is_dirty();
let old_value = self.cache.borrow_mut().insert(*address, account);
if is_dirty {
if let Some(ref mut checkpoint) = self.checkpoints.borrow_mut().last_mut() {
checkpoint.entry(*address).or_insert(old_value);
}
}
}
fn note_cache(&self, address: &Address) {
if let Some(ref mut checkpoint) = self.checkpoints.borrow_mut().last_mut() {
checkpoint.entry(*address)
.or_insert_with(|| self.cache.borrow().get(address).map(AccountEntry::clone_dirty));
}
}
pub fn drop(mut self) -> (H256, B) {
self.propagate_to_global_cache();
(self.root, self.db)
}
pub fn into_account(self, account: &Address) -> TrieResult<(Option<Arc<Bytes>>, HashMap<H256, H256>)> {
let account = self.require(account, true)?;
Ok((account.code().clone(), account.storage_changes().clone()))
}
pub fn root(&self) -> &H256 {
&self.root
}
pub fn new_contract(&mut self, contract: &Address, balance: U256, nonce_offset: U256, version: U256) -> TrieResult<()> {
let original_storage_root = self.original_storage_root(contract)?;
let (nonce, overflow) = self.account_start_nonce.overflowing_add(nonce_offset);
if overflow {
return Err(Box::new(TrieError::DecoderError(H256::from(*contract), tetsy_rlp::DecoderError::Custom("Nonce overflow".into()))));
}
self.insert_cache(contract, AccountEntry::new_dirty(Some(Account::new_contract(balance, nonce, version, original_storage_root))));
Ok(())
}
pub fn kill_account(&mut self, account: &Address) {
self.insert_cache(account, AccountEntry::new_dirty(None));
}
pub fn exists(&self, a: &Address) -> TrieResult<bool> {
self.ensure_cached(a, RequireCache::None, false, |a| a.is_some())
}
pub fn exists_and_not_null(&self, a: &Address) -> TrieResult<bool> {
self.ensure_cached(a, RequireCache::None, false, |a| a.map_or(false, |a| !a.is_null()))
}
pub fn exists_and_has_code_or_nonce(&self, a: &Address) -> TrieResult<bool> {
self.ensure_cached(a, RequireCache::CodeSize, false,
|a| a.map_or(false, |a| a.code_hash() != KECCAK_EMPTY || *a.nonce() != self.account_start_nonce))
}
pub fn balance(&self, a: &Address) -> TrieResult<U256> {
self.ensure_cached(a, RequireCache::None, true,
|a| a.as_ref().map_or(U256::zero(), |account| *account.balance()))
}
pub fn nonce(&self, a: &Address) -> TrieResult<U256> {
self.ensure_cached(a, RequireCache::None, true,
|a| a.as_ref().map_or(self.account_start_nonce, |account| *account.nonce()))
}
pub fn is_base_storage_root_unchanged(&self, a: &Address) -> TrieResult<bool> {
Ok(self.ensure_cached(a, RequireCache::None, true,
|a| a.as_ref().map(|account| account.is_base_storage_root_unchanged()))?
.unwrap_or(true))
}
pub fn storage_root(&self, a: &Address) -> TrieResult<Option<H256>> {
self.ensure_cached(a, RequireCache::None, true,
|a| a.as_ref().and_then(|account| account.storage_root()))
}
pub fn original_storage_root(&self, a: &Address) -> TrieResult<H256> {
Ok(self.ensure_cached(a, RequireCache::None, true,
|a| a.as_ref().map(|account| account.original_storage_root()))?
.unwrap_or(KECCAK_NULL_RLP))
}
pub fn checkpoint_storage_at(&self, start_checkpoint_index: usize, address: &Address, key: &H256) -> TrieResult<Option<H256>> {
#[must_use]
enum ReturnKind {
OriginalAt,
SameAsNext,
}
let kind = {
let checkpoints = self.checkpoints.borrow();
if start_checkpoint_index >= checkpoints.len() {
return Ok(None);
}
let mut kind = None;
for checkpoint in checkpoints.iter().skip(start_checkpoint_index) {
match checkpoint.get(address) {
Some(Some(AccountEntry { account: Some(ref account), .. })) => {
if let Some(value) = account.cached_storage_at(key) {
return Ok(Some(value));
} else {
if account.base_storage_root() == self.original_storage_root(address)? {
kind = Some(ReturnKind::OriginalAt);
break
} else {
warn!(target: "state", "Trying to get an account's cached storage value, but base storage root does not equal to original storage root! Assuming the value is empty.");
return Ok(Some(H256::zero()));
}
}
},
Some(Some(AccountEntry { account: None, .. })) => return Ok(Some(H256::zero())),
Some(None) => {
kind = Some(ReturnKind::OriginalAt);
break
},
None => {
kind = Some(ReturnKind::SameAsNext);
},
}
}
kind.expect("start_checkpoint_index is checked to be below checkpoints_len; for loop above must have been executed at least once; it will either early return, or set the kind value to Some; qed")
};
match kind {
ReturnKind::SameAsNext => {
Ok(Some(self.storage_at(address, key)?))
},
ReturnKind::OriginalAt => Ok(Some(self.original_storage_at(address, key)?)),
}
}
fn storage_at_inner<FCachedStorageAt, FStorageAt>(
&self, address: &Address, key: &H256, f_cached_at: FCachedStorageAt, f_at: FStorageAt,
) -> TrieResult<H256> where
FCachedStorageAt: Fn(&Account, &H256) -> Option<H256>,
FStorageAt: Fn(&Account, &dyn HashDB<KeccakHasher, DBValue>, &H256) -> TrieResult<H256>
{
{
let local_cache = self.cache.borrow_mut();
let mut local_account = None;
if let Some(maybe_acc) = local_cache.get(address) {
match maybe_acc.account {
Some(ref account) => {
if let Some(value) = f_cached_at(account, key) {
return Ok(value);
} else {
local_account = Some(maybe_acc);
}
},
_ => return Ok(H256::zero()),
}
}
let trie_res = self.db.get_cached(address, |acc| match acc {
None => Ok(H256::zero()),
Some(a) => {
let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), a.address_hash(address));
f_at(a, account_db.as_hash_db(), key)
}
});
if let Some(res) = trie_res {
return res;
}
if let Some(ref mut acc) = local_account {
if let Some(ref account) = acc.account {
let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(address));
return f_at(account, account_db.as_hash_db(), key)
} else {
return Ok(H256::zero())
}
}
}
if self.db.is_known_null(address) { return Ok(H256::zero()) }
let db = &self.db.as_hash_db();
let db = self.factories.trie.readonly(db, &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed");
let maybe_acc = db.get_with(address.as_bytes(), from_rlp)?;
let r = maybe_acc.as_ref().map_or(Ok(H256::zero()), |a| {
let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), a.address_hash(address));
f_at(a, account_db.as_hash_db(), key)
});
self.insert_cache(address, AccountEntry::new_clean(maybe_acc));
r
}
pub fn storage_at(&self, address: &Address, key: &H256) -> TrieResult<H256> {
self.storage_at_inner(
address,
key,
|account, key| { account.cached_storage_at(key) },
|account, db, key| { account.storage_at(db, key) },
)
}
pub fn original_storage_at(&self, address: &Address, key: &H256) -> TrieResult<H256> {
self.storage_at_inner(
address,
key,
|account, key| { account.cached_original_storage_at(key) },
|account, db, key| { account.original_storage_at(db, key) },
)
}
pub fn code(&self, a: &Address) -> TrieResult<Option<Arc<Bytes>>> {
self.ensure_cached(a, RequireCache::Code, true,
|a| a.as_ref().map_or(None, |a| a.code().clone()))
}
pub fn code_hash(&self, a: &Address) -> TrieResult<Option<H256>> {
self.ensure_cached(a, RequireCache::None, true,
|a| a.as_ref().map(|a| a.code_hash()))
}
pub fn code_version(&self, a: &Address) -> TrieResult<U256> {
self.ensure_cached(a, RequireCache::None, true,
|a| a.as_ref().map(|a| *a.code_version()).unwrap_or(U256::zero()))
}
pub fn code_size(&self, a: &Address) -> TrieResult<Option<usize>> {
self.ensure_cached(a, RequireCache::CodeSize, true,
|a| a.as_ref().and_then(|a| a.code_size()))
}
pub fn add_balance(&mut self, a: &Address, incr: &U256, cleanup_mode: CleanupMode) -> TrieResult<()> {
trace!(target: "state", "add_balance({}, {}): {}", a, incr, self.balance(a)?);
let is_value_transfer = !incr.is_zero();
if is_value_transfer || (cleanup_mode == CleanupMode::ForceCreate && !self.exists(a)?) {
self.require(a, false)?.add_balance(incr);
} else if let CleanupMode::TrackTouched(set) = cleanup_mode {
if self.exists(a)? {
set.insert(*a);
self.touch(a)?;
}
}
Ok(())
}
pub fn sub_balance(&mut self, a: &Address, decr: &U256, cleanup_mode: &mut CleanupMode) -> TrieResult<()> {
trace!(target: "state", "sub_balance({}, {}): {}", a, decr, self.balance(a)?);
if !decr.is_zero() || !self.exists(a)? {
self.require(a, false)?.sub_balance(decr);
}
if let CleanupMode::TrackTouched(ref mut set) = *cleanup_mode {
set.insert(*a);
}
Ok(())
}
pub fn transfer_balance(&mut self, from: &Address, to: &Address, by: &U256, mut cleanup_mode: CleanupMode) -> TrieResult<()> {
self.sub_balance(from, by, &mut cleanup_mode)?;
self.add_balance(to, by, cleanup_mode)?;
Ok(())
}
pub fn inc_nonce(&mut self, a: &Address) -> TrieResult<()> {
self.require(a, false).map(|mut x| x.inc_nonce())
}
pub fn set_storage(&mut self, a: &Address, key: H256, value: H256) -> TrieResult<()> {
trace!(target: "state", "set_storage({}:{:x} to {:x})", a, key, value);
if self.storage_at(a, &key)? != value {
self.require(a, false)?.set_storage(key, value)
}
Ok(())
}
pub fn init_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> {
self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, 0.into(),KECCAK_NULL_RLP), |_| {})?.init_code(code);
Ok(())
}
pub fn reset_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> {
self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, 0.into(), KECCAK_NULL_RLP), |_| {})?.reset_code(code);
Ok(())
}
fn touch(&mut self, a: &Address) -> TrieResult<()> {
self.require(a, false)?;
Ok(())
}
pub fn commit(&mut self) -> Result<(), Error> {
assert!(self.checkpoints.borrow().is_empty());
let mut accounts = self.cache.borrow_mut();
for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) {
if let Some(ref mut account) = a.account {
let addr_hash = account.address_hash(address);
{
let mut account_db = self.factories.accountdb.create(self.db.as_hash_db_mut(), addr_hash);
account.commit_storage(&self.factories.trie, account_db.as_hash_db_mut())?;
account.commit_code(account_db.as_hash_db_mut());
}
if !account.is_empty() {
self.db.note_non_null_account(address);
}
}
}
{
let mut trie = self.factories.trie.from_existing(self.db.as_hash_db_mut(), &mut self.root)?;
for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) {
a.state = AccountState::Committed;
match a.account {
Some(ref mut account) => {
trie.insert(address.as_bytes(), &account.rlp())?;
},
None => {
trie.remove(address.as_bytes())?;
},
};
}
}
Ok(())
}
fn propagate_to_global_cache(&mut self) {
let mut addresses = self.cache.borrow_mut();
trace!(target: "state", "Committing cache {:?} entries", addresses.len());
for (address, a) in addresses.drain().filter(|&(_, ref a)| a.state == AccountState::Committed || a.state == AccountState::CleanFresh) {
self.db.add_to_account_cache(address, a.account, a.state == AccountState::Committed);
}
}
pub fn clear(&mut self) {
assert!(self.checkpoints.borrow().is_empty());
self.cache.borrow_mut().clear();
}
pub fn kill_garbage(&mut self, touched: &HashSet<Address>, remove_empty_touched: bool, min_balance: &Option<U256>, kill_contracts: bool) -> TrieResult<()> {
let to_kill: HashSet<_> = {
self.cache.borrow().iter().filter_map(|(address, ref entry)|
if touched.contains(address) && ((remove_empty_touched && entry.exists_and_is_null()) || min_balance.map_or(false, |ref balance| entry.account.as_ref().map_or(false, |account|
(account.is_basic() || kill_contracts) && account.balance() < balance && entry.old_balance.as_ref().map_or(false, |b| account.balance() < b)))) {
Some(address.clone())
} else { None }).collect()
};
for address in to_kill {
self.kill_account(&address);
}
Ok(())
}
pub fn populate_from(&mut self, accounts: PodState) {
assert!(self.checkpoints.borrow().is_empty());
for (add, acc) in accounts.drain().into_iter() {
self.cache.borrow_mut().insert(add, AccountEntry::new_dirty(Some(Account::from_pod(acc))));
}
}
fn to_pod_cache(&self) -> PodState {
assert!(self.checkpoints.borrow().is_empty());
PodState::from(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| {
if let Some(ref acc) = opt.account {
m.insert(*add, acc.to_pod());
}
m
}))
}
pub fn to_pod_full(&self) -> Result<PodState, Error> {
assert!(self.checkpoints.borrow().is_empty());
assert!(self.factories.trie.is_fat());
let mut result = BTreeMap::new();
let db = &self.db.as_hash_db();
let trie = self.factories.trie.readonly(db, &self.root)?;
for item in trie.iter()? {
if let Ok((addr, _dbval)) = item {
let address = Address::from_slice(&addr);
let _ = self.require(&address, true);
}
}
for (add, opt) in self.cache.borrow().iter() {
if let Some(ref acc) = opt.account {
let pod_account = self.account_to_pod_account(acc, add)?;
result.insert(add.clone(), pod_account);
}
}
Ok(PodState::from(result))
}
fn account_to_pod_account(&self, account: &Account, address: &Address) -> Result<PodAccount, Error> {
use vapory_types::BigEndianHash;
assert!(self.factories.trie.is_fat());
let mut pod_storage = BTreeMap::new();
let addr_hash = account.address_hash(address);
let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), addr_hash);
let root = account.base_storage_root();
let accountdb = &accountdb.as_hash_db();
let trie = self.factories.trie.readonly(accountdb, &root)?;
for o_kv in trie.iter()? {
if let Ok((key, val)) = o_kv {
pod_storage.insert(
H256::from_slice(&key[..]),
BigEndianHash::from_uint(
&tetsy_rlp::decode::<U256>(&val[..]).expect("Decoded from trie which was encoded from the same type; qed")
),
);
}
}
let mut pod_account = account.to_pod();
pod_storage.append(&mut pod_account.storage);
pod_account.storage = pod_storage;
Ok(pod_account)
}
fn to_pod_diff<X: Backend>(&mut self, query: &State<X>) -> TrieResult<PodState> {
assert!(self.checkpoints.borrow().is_empty());
let all_addresses = self.cache.borrow().keys().cloned()
.chain(query.cache.borrow().keys().cloned())
.collect::<BTreeSet<_>>();
Ok(PodState::from(all_addresses.into_iter().fold(Ok(BTreeMap::new()), |m: TrieResult<_>, address| {
let mut m = m?;
let account = self.ensure_cached(&address, RequireCache::Code, true, |acc| {
acc.map(|acc| {
let all_keys = {
let self_keys = acc.storage_changes().keys().cloned()
.collect::<BTreeSet<_>>();
if let Some(ref query_storage) = query.cache.borrow().get(&address)
.and_then(|opt| {
Some(opt.account.as_ref()?.storage_changes().keys().cloned()
.collect::<BTreeSet<_>>())
})
{
self_keys.union(&query_storage).cloned().collect::<Vec<_>>()
} else {
self_keys.into_iter().collect::<Vec<_>>()
}
};
(*acc.balance(), *acc.nonce(), all_keys, acc.code().map(|x| x.to_vec()), *acc.code_version())
})
})?;
if let Some((balance, nonce, storage_keys, code, version)) = account {
let storage = storage_keys.into_iter().fold(Ok(BTreeMap::new()), |s: TrieResult<_>, key| {
let mut s = s?;
s.insert(key, self.storage_at(&address, &key)?);
Ok(s)
})?;
m.insert(address, PodAccount {
balance, nonce, storage, code, version
});
}
Ok(m)
})?))
}
pub fn diff_from<X: Backend>(&self, mut orig: State<X>) -> TrieResult<StateDiff> {
let pod_state_post = self.to_pod_cache();
let pod_state_pre = orig.to_pod_diff(self)?;
Ok(vapcore_pod::state::diff_pod(&pod_state_pre, &pod_state_post))
}
#[must_use]
fn update_account_cache(require: RequireCache, account: &mut Account, state_db: &B, db: &dyn HashDB<KeccakHasher, DBValue>) -> bool {
if let RequireCache::None = require {
return true;
}
if account.is_cached() {
return true;
}
let hash = account.code_hash();
match state_db.get_cached_code(&hash) {
Some(code) => {
account.cache_given_code(code);
true
},
None => match require {
RequireCache::None => true,
RequireCache::Code => {
if let Some(code) = account.cache_code(db) {
state_db.cache_code(hash, code);
true
} else {
false
}
},
RequireCache::CodeSize => {
account.cache_code_size(db)
}
}
}
}
fn ensure_cached<F, U>(&self, a: &Address, require: RequireCache, check_null: bool, f: F) -> TrieResult<U>
where F: Fn(Option<&Account>) -> U {
if let Some(ref mut maybe_acc) = self.cache.borrow_mut().get_mut(a) {
if let Some(ref mut account) = maybe_acc.account {
let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(a));
if Self::update_account_cache(require, account, &self.db, accountdb.as_hash_db()) {
return Ok(f(Some(account)));
} else {
return Err(Box::new(TrieError::IncompleteDatabase(H256::from(*a))));
}
}
return Ok(f(None));
}
let result = self.db.get_cached(a, |mut acc| {
if let Some(ref mut account) = acc {
let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(a));
if !Self::update_account_cache(require, account, &self.db, accountdb.as_hash_db()) {
return Err(Box::new(TrieError::IncompleteDatabase(H256::from(*a))));
}
}
Ok(f(acc.map(|a| &*a)))
});
match result {
Some(r) => Ok(r?),
None => {
if check_null && self.db.is_known_null(a) { return Ok(f(None)); }
let db = &self.db.as_hash_db();
let db = self.factories.trie.readonly(db, &self.root)?;
let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed");
let mut maybe_acc = db.get_with(a.as_bytes(), from_rlp)?;
if let Some(ref mut account) = maybe_acc.as_mut() {
let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(a));
if !Self::update_account_cache(require, account, &self.db, accountdb.as_hash_db()) {
return Err(Box::new(TrieError::IncompleteDatabase(H256::from(*a))));
}
}
let r = f(maybe_acc.as_ref());
self.insert_cache(a, AccountEntry::new_clean(maybe_acc));
Ok(r)
}
}
}
pub fn require<'a>(&'a self, a: &Address, require_code: bool) -> TrieResult<RefMut<'a, Account>> {
self.require_or_from(a, require_code, || Account::new_basic(0u8.into(), self.account_start_nonce), |_| {})
}
pub fn require_or_from<'a, F, G>(&'a self, a: &Address, require_code: bool, default: F, not_default: G) -> TrieResult<RefMut<'a, Account>>
where F: FnOnce() -> Account, G: FnOnce(&mut Account),
{
let contains_key = self.cache.borrow().contains_key(a);
if !contains_key {
match self.db.get_cached_account(a) {
Some(acc) => self.insert_cache(a, AccountEntry::new_clean_cached(acc)),
None => {
let maybe_acc = if !self.db.is_known_null(a) {
let db = &self.db.as_hash_db();
let db = self.factories.trie.readonly(db, &self.root)?;
let from_rlp = |b:&[u8]| { Account::from_rlp(b).expect("decoding db value failed") };
AccountEntry::new_clean(db.get_with(a.as_bytes(), from_rlp)?)
} else {
AccountEntry::new_clean(None)
};
self.insert_cache(a, maybe_acc);
}
}
}
self.note_cache(a);
let mut account = RefMut::map(self.cache.borrow_mut(), |c| {
let entry = c.get_mut(a).expect("entry known to exist in the cache; qed");
match &mut entry.account {
&mut Some(ref mut acc) => not_default(acc),
slot => *slot = Some(default()),
}
entry.state = AccountState::Dirty;
entry.account.as_mut().expect("Required account must always exist; qed")
});
if require_code {
let addr_hash = account.address_hash(a);
let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), addr_hash);
if !Self::update_account_cache(RequireCache::Code, &mut account, &self.db, accountdb.as_hash_db()) {
return Err(Box::new(TrieError::IncompleteDatabase(H256::from(*a))))
}
}
Ok(account)
}
pub fn patch_account(&self, a: &Address, code: Arc<Bytes>, storage: HashMap<H256, H256>) -> TrieResult<()> {
Ok(self.require(a, false)?.reset_code_and_storage(code, storage))
}
}
impl<B: Backend> State<B> {
pub fn prove_account(&self, account_key: H256) -> TrieResult<(Vec<Bytes>, BasicAccount)> {
let mut recorder = Recorder::new();
let db = &self.db.as_hash_db();
let trie = TrieDB::new(db, &self.root)?;
let maybe_account: Option<BasicAccount> = {
let panicky_decoder = |bytes: &[u8]| {
::tetsy_rlp::decode(bytes).unwrap_or_else(|_| panic!("prove_account, could not query trie for account key={}", &account_key))
};
let query = (&mut recorder, panicky_decoder);
trie.get_with(account_key.as_bytes(), query)?
};
let account = maybe_account.unwrap_or_else(|| BasicAccount {
balance: 0.into(),
nonce: self.account_start_nonce,
code_hash: KECCAK_EMPTY,
storage_root: KECCAK_NULL_RLP,
code_version: 0.into(),
});
Ok((recorder.drain().into_iter().map(|r| r.data).collect(), account))
}
pub fn prove_storage(&self, account_key: H256, storage_key: H256) -> TrieResult<(Vec<Bytes>, H256)> {
let db = &self.db.as_hash_db();
let trie = TrieDB::new(db, &self.root)?;
let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed");
let acc = match trie.get_with(account_key.as_bytes(), from_rlp)? {
Some(acc) => acc,
None => return Ok((Vec::new(), H256::zero())),
};
let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), account_key);
acc.prove_storage(account_db.as_hash_db(), storage_key)
}
}
impl<B: Backend> fmt::Debug for State<B> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.cache.borrow())
}
}
impl<B: Backend> State<B> {
pub fn db(&self) -> &B {
&self.db
}
}
impl<B: Backend + Clone> Clone for State<B> {
fn clone(&self) -> State<B> {
let cache = {
let mut cache: HashMap<Address, AccountEntry> = HashMap::new();
for (key, val) in self.cache.borrow().iter() {
if let Some(entry) = val.clone_if_dirty() {
cache.insert(key.clone(), entry);
}
}
cache
};
State {
db: self.db.clone(),
root: self.root.clone(),
cache: RefCell::new(cache),
checkpoints: RefCell::new(Vec::new()),
account_start_nonce: self.account_start_nonce.clone(),
factories: self.factories.clone(),
}
}
}