196 lines
5.8 KiB
Rust
196 lines
5.8 KiB
Rust
// 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 <http://www.gnu.org/licenses/>.
|
|
|
|
//! 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<T> {
|
|
/// 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<T> Diff<T> {
|
|
/// 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<U256>,
|
|
/// Change in nonce, allowed to be `Diff::Same`.
|
|
pub nonce: Diff<U256>, // Allowed to be Same
|
|
/// Change in code, allowed to be `Diff::Same`.
|
|
pub code: Diff<Bytes>, // Allowed to be Same
|
|
/// Change in storage, values are not allowed to be `Diff::Same`.
|
|
pub storage: BTreeMap<H256, Diff<H256>>,
|
|
}
|
|
|
|
#[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(())
|
|
}
|
|
}
|