// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of OpenEthereum. // OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with OpenEthereum. If not, see . //! Diff between two accounts. use crate::bytes::Bytes; use ethereum_types::{H256, U256}; use std::{cmp::*, collections::BTreeMap, fmt}; #[derive(Debug, PartialEq, Eq, Clone)] /// Diff type for specifying a change (or not). pub enum Diff { /// Both sides are the same. Same, /// Left (pre, source) side doesn't include value, right side (post, destination) does. Born(T), /// Both sides include data; it chaged value between them. Changed(T, T), /// Left (pre, source) side does include value, right side (post, destination) does not. Died(T), } impl Diff { /// Construct new object with given `pre` and `post`. pub fn new(pre: T, post: T) -> Self where T: Eq, { if pre == post { Diff::Same } else { Diff::Changed(pre, post) } } /// Get the before value, if there is one. pub fn pre(&self) -> Option<&T> { match *self { Diff::Died(ref x) | Diff::Changed(ref x, _) => Some(x), _ => None, } } /// Get the after value, if there is one. pub fn post(&self) -> Option<&T> { match *self { Diff::Born(ref x) | Diff::Changed(_, ref x) => Some(x), _ => None, } } /// Determine whether there was a change or not. pub fn is_same(&self) -> bool { match *self { Diff::Same => true, _ => false, } } } #[derive(Debug, PartialEq, Eq, Clone)] /// Account diff. pub struct AccountDiff { /// Change in balance, allowed to be `Diff::Same`. pub balance: Diff, /// Change in nonce, allowed to be `Diff::Same`. pub nonce: Diff, // Allowed to be Same /// Change in code, allowed to be `Diff::Same`. pub code: Diff, // Allowed to be Same /// Change in storage, values are not allowed to be `Diff::Same`. pub storage: BTreeMap>, } #[derive(Debug, PartialEq, Eq, Clone)] /// Change in existance type. // TODO: include other types of change. pub enum Existance { /// Item came into existance. Born, /// Item stayed in existance. Alive, /// Item went out of existance. Died, } impl fmt::Display for Existance { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Existance::Born => write!(f, "+++")?, Existance::Alive => write!(f, "***")?, Existance::Died => write!(f, "XXX")?, } Ok(()) } } impl AccountDiff { /// Get `Existance` projection. pub fn existance(&self) -> Existance { match self.balance { Diff::Born(_) => Existance::Born, Diff::Died(_) => Existance::Died, _ => Existance::Alive, } } } // TODO: refactor into something nicer. fn interpreted_hash(u: &H256) -> String { if u <= &H256::from_low_u64_be(0xffffffff) { format!( "{} = 0x{:x}", U256::from(u.as_bytes()).low_u32(), U256::from(u.as_bytes()).low_u32() ) } else if u <= &H256::from_low_u64_be(u64::max_value()) { format!( "{} = 0x{:x}", U256::from(u.as_bytes()).low_u64(), U256::from(u.as_bytes()).low_u64() ) // } else if u <= &H256::from("0xffffffffffffffffffffffffffffffffffffffff") { // format!("@{}", Address::from(u)) } else { format!("#{}", u) } } impl fmt::Display for AccountDiff { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use crate::bytes::ToPretty; match self.nonce { Diff::Born(ref x) => write!(f, " non {}", x)?, Diff::Changed(ref pre, ref post) => write!( f, "#{} ({} {} {})", post, pre, if pre > post { "-" } else { "+" }, *max(pre, post) - *min(pre, post) )?, _ => {} } match self.balance { Diff::Born(ref x) => write!(f, " bal {}", x)?, Diff::Changed(ref pre, ref post) => write!( f, "${} ({} {} {})", post, pre, if pre > post { "-" } else { "+" }, *max(pre, post) - *min(pre, post) )?, _ => {} } if let Diff::Born(ref x) = self.code { write!(f, " code {}", x.pretty())?; } write!(f, "\n")?; for (k, dv) in &self.storage { match *dv { Diff::Born(ref v) => write!( f, " + {} => {}\n", interpreted_hash(k), interpreted_hash(v) )?, Diff::Changed(ref pre, ref post) => write!( f, " * {} => {} (was {})\n", interpreted_hash(k), interpreted_hash(post), interpreted_hash(pre) )?, Diff::Died(_) => write!(f, " X {}\n", interpreted_hash(k))?, _ => {} } } Ok(()) } }