openethereum/ethcore/src/pod_account.rs

221 lines
6.7 KiB
Rust
Raw Normal View History

2016-02-05 13:40:41 +01:00
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity 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.
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
use util::*;
use account::*;
2016-02-05 01:49:06 +01:00
use account_db::*;
2016-03-17 15:51:40 +01:00
use ethjson;
use types::account_diff::*;
#[derive(Debug, Clone, PartialEq, Eq)]
2016-02-03 13:20:32 +01:00
/// An account, expressed as Plain-Old-Data (hence the name).
/// Does not have a DB overlay cache, code hash or anything like that.
pub struct PodAccount {
2016-02-03 13:20:32 +01:00
/// The balance of the account.
pub balance: U256,
2016-02-03 13:20:32 +01:00
/// The nonce of the account.
pub nonce: U256,
2016-02-03 13:20:32 +01:00
/// The code of the account.
pub code: Bytes,
2016-02-03 13:20:32 +01:00
/// The storage of the account.
pub storage: BTreeMap<H256, H256>,
}
impl PodAccount {
/// Construct new object.
#[cfg(test)]
pub fn new(balance: U256, nonce: U256, code: Bytes, storage: BTreeMap<H256, H256>) -> PodAccount {
PodAccount { balance: balance, nonce: nonce, code: code, storage: storage }
}
/// Convert Account to a PodAccount.
/// NOTE: This will silently fail unless the account is fully cached.
pub fn from_account(acc: &Account) -> PodAccount {
PodAccount {
2016-02-14 12:54:27 +01:00
balance: *acc.balance(),
nonce: *acc.nonce(),
storage: acc.storage_overlay().iter().fold(BTreeMap::new(), |mut m, (k, &(_, ref v))| {m.insert(k.clone(), v.clone()); m}),
code: acc.code().unwrap().to_vec(),
}
}
/// Returns the RLP for this account.
pub fn rlp(&self) -> Bytes {
let mut stream = RlpStream::new_list(4);
stream.append(&self.nonce);
stream.append(&self.balance);
2016-01-27 17:22:01 +01:00
stream.append(&sec_trie_root(self.storage.iter().map(|(k, v)| (k.to_vec(), encode(&U256::from(v.as_slice())).to_vec())).collect()));
stream.append(&self.code.sha3());
stream.out()
}
/// Place additional data into given hash DB.
2016-02-04 02:40:35 +01:00
pub fn insert_additional(&self, db: &mut AccountDBMut) {
if !self.code.is_empty() {
db.insert(&self.code);
}
let mut r = H256::new();
let mut t = SecTrieDBMut::new(db, &mut r);
for (k, v) in &self.storage {
t.insert(k, &encode(&U256::from(v.as_slice())));
}
}
}
2016-03-17 15:51:40 +01:00
impl From<ethjson::blockchain::Account> for PodAccount {
fn from(a: ethjson::blockchain::Account) -> Self {
PodAccount {
balance: a.balance.into(),
nonce: a.nonce.into(),
code: a.code.into(),
storage: a.storage.into_iter().map(|(key, value)| {
2016-03-17 15:51:40 +01:00
let key: U256 = key.into();
let value: U256 = value.into();
(H256::from(key), H256::from(value))
}).collect()
}
}
}
impl From<ethjson::spec::Account> for PodAccount {
fn from(a: ethjson::spec::Account) -> Self {
PodAccount {
balance: a.balance.map_or_else(U256::zero, Into::into),
nonce: a.nonce.map_or_else(U256::zero, Into::into),
code: vec![],
storage: BTreeMap::new()
2016-03-17 15:51:40 +01:00
}
}
}
impl fmt::Display for PodAccount {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "(bal={}; nonce={}; code={} bytes, #{}; storage={} items)", self.balance, self.nonce, self.code.len(), self.code.sha3(), self.storage.len())
}
}
/// Determine difference between two optionally existant `Account`s. Returns None
/// if they are the same.
pub fn diff_pod(pre: Option<&PodAccount>, post: Option<&PodAccount>) -> Option<AccountDiff> {
match (pre, post) {
(None, Some(x)) => Some(AccountDiff {
balance: Diff::Born(x.balance),
nonce: Diff::Born(x.nonce),
code: Diff::Born(x.code.clone()),
storage: x.storage.iter().map(|(k, v)| (k.clone(), Diff::Born(v.clone()))).collect(),
}),
(Some(x), None) => Some(AccountDiff {
balance: Diff::Died(x.balance),
nonce: Diff::Died(x.nonce),
code: Diff::Died(x.code.clone()),
storage: x.storage.iter().map(|(k, v)| (k.clone(), Diff::Died(v.clone()))).collect(),
}),
(Some(pre), Some(post)) => {
let storage: Vec<_> = pre.storage.keys().merge(post.storage.keys())
.filter(|k| pre.storage.get(k).unwrap_or(&H256::new()) != post.storage.get(k).unwrap_or(&H256::new()))
.collect();
let r = AccountDiff {
balance: Diff::new(pre.balance, post.balance),
nonce: Diff::new(pre.nonce, post.nonce),
code: Diff::new(pre.code.clone(), post.code.clone()),
storage: storage.into_iter().map(|k|
(k.clone(), Diff::new(
pre.storage.get(&k).cloned().unwrap_or_else(H256::new),
post.storage.get(&k).cloned().unwrap_or_else(H256::new)
))).collect(),
};
if r.balance.is_same() && r.nonce.is_same() && r.code.is_same() && r.storage.is_empty() {
None
} else {
Some(r)
}
},
_ => None,
}
}
#[cfg(test)]
mod test {
use common::*;
use types::account_diff::*;
use super::diff_pod;
#[test]
fn existence() {
let a = PodAccount{balance: x!(69), nonce: x!(0), code: vec![], storage: map![]};
assert_eq!(diff_pod(Some(&a), Some(&a)), None);
assert_eq!(diff_pod(None, Some(&a)), Some(AccountDiff{
balance: Diff::Born(x!(69)),
nonce: Diff::Born(x!(0)),
code: Diff::Born(vec![]),
storage: map![],
}));
}
#[test]
fn basic() {
let a = PodAccount{balance: x!(69), nonce: x!(0), code: vec![], storage: map![]};
let b = PodAccount{balance: x!(42), nonce: x!(1), code: vec![], storage: map![]};
assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
balance: Diff::Changed(x!(69), x!(42)),
nonce: Diff::Changed(x!(0), x!(1)),
code: Diff::Same,
storage: map![],
}));
}
#[test]
fn code() {
let a = PodAccount{balance: x!(0), nonce: x!(0), code: vec![], storage: map![]};
let b = PodAccount{balance: x!(0), nonce: x!(1), code: vec![0], storage: map![]};
assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
balance: Diff::Same,
nonce: Diff::Changed(x!(0), x!(1)),
code: Diff::Changed(vec![], vec![0]),
storage: map![],
}));
}
#[test]
fn storage() {
let a = PodAccount {
balance: x!(0),
nonce: x!(0),
code: vec![],
storage: mapx![1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 0, 6 => 0, 7 => 0]
};
let b = PodAccount {
balance: x!(0),
nonce: x!(0),
code: vec![],
storage: mapx![1 => 1, 2 => 3, 3 => 0, 5 => 0, 7 => 7, 8 => 0, 9 => 9]
};
assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
balance: Diff::Same,
nonce: Diff::Same,
code: Diff::Same,
storage: map![
x!(2) => Diff::new(x!(2), x!(3)),
x!(3) => Diff::new(x!(3), x!(0)),
x!(4) => Diff::new(x!(4), x!(0)),
x!(7) => Diff::new(x!(0), x!(7)),
x!(9) => Diff::new(x!(0), x!(9))
],
}));
}
}