dae5d75dd6
* cargo upgrade "ethereum-types" --all --allow-prerelease * [ethash] fix compilation errors * [ethkey] fix compilation errors * [journaldb] fix compilation errors * [dir] fix compilation errors * [ethabi] update to 0.7 * wip * [eip-712] fix compilation errors * [ethjson] fix compilation errors * [Cargo.toml] add TODO to remove patches * [ethstore] fix compilation errors * use patched keccak-hash with new primitive-types * wip * [ethcore-network-devp2p] fix compilation errors * [vm] fix compilation errors * [common-types, evm, wasm] fix compilation errors * [ethcore-db] Require AsRef instead of Deref for keys * [ethcore-blockchain] fix some compilation errors * [blooms-db] fix compilation errors Thanks a lot @dvdplm :) * we don't need no rlp ethereum feature * [ethcore] fix some compilation errors * [parity-ipfs-api] fix compilation error * [ethcore-light] fix compilation errors * [Cargo.lock] update parity-common * [ethcore-private-tx] fix some compilation errors * wip * [ethcore-private-tx] fix compilation errors * [parity-updater] fix compilation errors * [parity-rpc] fix compilation errors * [parity-bin] fix other compilation errors * update to new ethereum-types * update keccak-hash * [fastmap] fix compilation in tests * [blooms-db] fix compilation in tests * [common-types] fix compilation in tests * [triehash-ethereum] fix compilation in tests * [ethkey] fix compilation in tests * [pwasm-run-test] fix compilation errors * [wasm] fix compilation errors * [ethjson] fix compilation in tests * [eip-712] fix compilation in tests * [ethcore-blockchain] fix compilation in tests * [ethstore] fix compilation in tests * [ethstore-accounts] fix compilation in tests * [parity-hash-fetch] fix compilation in tests * [parity-whisper] fix compilation in tests * [ethcore-miner] fix compilation in tests * [ethcore-network-devp2p] fix compilation in tests * [*] upgrade rand to 0.6 * [evm] get rid of num-bigint conversions * [ethcore] downgrade trie-standardmap and criterion * [ethcore] fix some warnings * [ethcore] fix compilation in tests * [evmbin] fix compilation in tests * [updater] fix compilation in tests * [ethash] fix compilation in tests * [ethcore-secretstore] fix compilation in tests * [ethcore-sync] fix compilation in tests * [parity-rpc] fix compilation in tests * [ethcore] finally fix compilation in tests FUCK YEAH!!! * [ethstore] lazy_static is unused * [ethcore] fix test * fix up bad merge * [Cargo.toml] remove unused patches * [*] replace some git dependencies with crates.io * [Cargo.toml] remove unused lazy_static * [*] clean up * [ethcore] fix transaction_filter_deprecated test * [private-tx] fix serialization tests * fix more serialization tests * [ethkey] fix smoky test * [rpc] fix tests, please? * [ethcore] remove commented out code * Apply suggestions from code review Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * [ethstore] remove unused dev-dependency * [ethcore] remove resolved TODO * [*] resolve keccak-hash TODO * [*] s/Address::default()/Address::zero() * [rpc] remove Subscribers::new_test * [rpc] remove EthPubSubClient::new_test * [ethcore] use trie-standardmap from crates.io * [dir] fix db_root_path * [ethcore] simplify snapshot::tests::helpers::fill_storage * Apply suggestions from code review Co-Authored-By: David <dvdplm@gmail.com> * [ethcore-secretstore] resolve TODO in serialization * [ethcore-network-devp2p] resolve TODO in save_key * [Cargo.lock] update triehash * [*] use ethabi from crates.io * [ethkey] use secp256k1 from master branch * [Cargo.lock] update eth-secp256k1
269 lines
9.3 KiB
Rust
269 lines
9.3 KiB
Rust
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
|
// This file is part of Parity Ethereum.
|
|
|
|
// Parity Ethereum 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 Ethereum 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 Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
//! Account system expressed in Plain Old Data.
|
|
|
|
use std::fmt;
|
|
use std::collections::BTreeMap;
|
|
use itertools::Itertools;
|
|
use hash::{keccak};
|
|
use ethereum_types::{H256, U256, BigEndianHash};
|
|
use hash_db::HashDB;
|
|
use kvdb::DBValue;
|
|
use keccak_hasher::KeccakHasher;
|
|
use triehash::sec_trie_root;
|
|
use bytes::Bytes;
|
|
use trie::TrieFactory;
|
|
use ethtrie::RlpCodec;
|
|
use state::Account;
|
|
use ethjson;
|
|
use types::account_diff::*;
|
|
use rlp::{self, RlpStream};
|
|
use serde::Serializer;
|
|
use rustc_hex::ToHex;
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
|
/// 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 {
|
|
/// The balance of the account.
|
|
pub balance: U256,
|
|
/// The nonce of the account.
|
|
pub nonce: U256,
|
|
#[serde(serialize_with="opt_bytes_to_hex")]
|
|
/// The code of the account or `None` in the special case that it is unknown.
|
|
pub code: Option<Bytes>,
|
|
/// The storage of the account.
|
|
pub storage: BTreeMap<H256, H256>,
|
|
}
|
|
|
|
fn opt_bytes_to_hex<S>(opt_bytes: &Option<Bytes>, serializer: S) -> Result<S::Ok, S::Error>
|
|
where S: Serializer
|
|
{
|
|
serializer.collect_str(&format_args!("0x{}",opt_bytes.as_ref().map_or("".to_string(), |b|b.to_hex())))
|
|
}
|
|
|
|
impl PodAccount {
|
|
/// Convert Account to a PodAccount.
|
|
/// NOTE: This will silently fail unless the account is fully cached.
|
|
pub fn from_account(acc: &Account) -> PodAccount {
|
|
PodAccount {
|
|
balance: *acc.balance(),
|
|
nonce: *acc.nonce(),
|
|
storage: acc.storage_changes().iter().fold(BTreeMap::new(), |mut m, (k, v)| {m.insert(k.clone(), v.clone()); m}),
|
|
code: acc.code().map(|x| x.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);
|
|
stream.append(&sec_trie_root(self.storage.iter().map(|(k, v)| (k, rlp::encode(&v.into_uint())))));
|
|
stream.append(&keccak(&self.code.as_ref().unwrap_or(&vec![])));
|
|
stream.out()
|
|
}
|
|
|
|
/// Place additional data into given hash DB.
|
|
pub fn insert_additional(&self, db: &mut HashDB<KeccakHasher, DBValue>, factory: &TrieFactory<KeccakHasher, RlpCodec>) {
|
|
match self.code {
|
|
Some(ref c) if !c.is_empty() => { db.insert(c); }
|
|
_ => {}
|
|
}
|
|
let mut r = H256::zero();
|
|
let mut t = factory.create(db, &mut r);
|
|
for (k, v) in &self.storage {
|
|
if let Err(e) = t.insert(k.as_bytes(), &rlp::encode(&v.into_uint())) {
|
|
warn!("Encountered potential DB corruption: {}", e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<ethjson::blockchain::Account> for PodAccount {
|
|
fn from(a: ethjson::blockchain::Account) -> Self {
|
|
PodAccount {
|
|
balance: a.balance.into(),
|
|
nonce: a.nonce.into(),
|
|
code: Some(a.code.into()),
|
|
storage: a.storage.into_iter().map(|(key, value)| {
|
|
let key: U256 = key.into();
|
|
let value: U256 = value.into();
|
|
(BigEndianHash::from_uint(&key), BigEndianHash::from_uint(&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: Some(a.code.map_or_else(Vec::new, Into::into)),
|
|
storage: a.storage.map_or_else(BTreeMap::new, |s| s.into_iter().map(|(key, value)| {
|
|
let key: U256 = key.into();
|
|
let value: U256 = value.into();
|
|
(BigEndianHash::from_uint(&key), BigEndianHash::from_uint(&value))
|
|
}).collect()),
|
|
}
|
|
}
|
|
}
|
|
|
|
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.as_ref().map_or(0, |c| c.len()),
|
|
self.code.as_ref().map_or_else(H256::zero, |c| keccak(c)),
|
|
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.as_ref().expect("account is newly created; newly created accounts must be given code; all caches should remain in place; qed").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.as_ref().expect("account is deleted; only way to delete account is running SUICIDE; account must have had own code cached to make operation; all caches should remain in place; qed").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::zero()) != post.storage.get(k).unwrap_or(&H256::zero()))
|
|
.collect();
|
|
let r = AccountDiff {
|
|
balance: Diff::new(pre.balance, post.balance),
|
|
nonce: Diff::new(pre.nonce, post.nonce),
|
|
code: match (pre.code.clone(), post.code.clone()) {
|
|
(Some(pre_code), Some(post_code)) => Diff::new(pre_code, post_code),
|
|
_ => Diff::Same,
|
|
},
|
|
storage: storage.into_iter().map(|k|
|
|
(k.clone(), Diff::new(
|
|
pre.storage.get(k).cloned().unwrap_or_else(H256::zero),
|
|
post.storage.get(k).cloned().unwrap_or_else(H256::zero)
|
|
))).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 std::collections::BTreeMap;
|
|
use types::account_diff::*;
|
|
use super::{PodAccount, diff_pod};
|
|
use ethereum_types::H256;
|
|
|
|
#[test]
|
|
fn existence() {
|
|
let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: Some(vec![]), storage: map![]};
|
|
assert_eq!(diff_pod(Some(&a), Some(&a)), None);
|
|
assert_eq!(diff_pod(None, Some(&a)), Some(AccountDiff{
|
|
balance: Diff::Born(69.into()),
|
|
nonce: Diff::Born(0.into()),
|
|
code: Diff::Born(vec![]),
|
|
storage: map![],
|
|
}));
|
|
}
|
|
|
|
#[test]
|
|
fn basic() {
|
|
let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: Some(vec![]), storage: map![]};
|
|
let b = PodAccount{balance: 42.into(), nonce: 1.into(), code: Some(vec![]), storage: map![]};
|
|
assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
|
|
balance: Diff::Changed(69.into(), 42.into()),
|
|
nonce: Diff::Changed(0.into(), 1.into()),
|
|
code: Diff::Same,
|
|
storage: map![],
|
|
}));
|
|
}
|
|
|
|
#[test]
|
|
fn code() {
|
|
let a = PodAccount{balance: 0.into(), nonce: 0.into(), code: Some(vec![]), storage: map![]};
|
|
let b = PodAccount{balance: 0.into(), nonce: 1.into(), code: Some(vec![0]), storage: map![]};
|
|
assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
|
|
balance: Diff::Same,
|
|
nonce: Diff::Changed(0.into(), 1.into()),
|
|
code: Diff::Changed(vec![], vec![0]),
|
|
storage: map![],
|
|
}));
|
|
}
|
|
|
|
#[test]
|
|
fn storage() {
|
|
let a = PodAccount {
|
|
balance: 0.into(),
|
|
nonce: 0.into(),
|
|
code: Some(vec![]),
|
|
storage: map![
|
|
H256::from_low_u64_be(1) => H256::from_low_u64_be(1),
|
|
H256::from_low_u64_be(2) => H256::from_low_u64_be(2),
|
|
H256::from_low_u64_be(3) => H256::from_low_u64_be(3),
|
|
H256::from_low_u64_be(4) => H256::from_low_u64_be(4),
|
|
H256::from_low_u64_be(5) => H256::from_low_u64_be(0),
|
|
H256::from_low_u64_be(6) => H256::from_low_u64_be(0),
|
|
H256::from_low_u64_be(7) => H256::from_low_u64_be(0)
|
|
],
|
|
};
|
|
let b = PodAccount {
|
|
balance: 0.into(),
|
|
nonce: 0.into(),
|
|
code: Some(vec![]),
|
|
storage: map![
|
|
H256::from_low_u64_be(1) => H256::from_low_u64_be(1),
|
|
H256::from_low_u64_be(2) => H256::from_low_u64_be(3),
|
|
H256::from_low_u64_be(3) => H256::from_low_u64_be(0),
|
|
H256::from_low_u64_be(5) => H256::from_low_u64_be(0),
|
|
H256::from_low_u64_be(7) => H256::from_low_u64_be(7),
|
|
H256::from_low_u64_be(8) => H256::from_low_u64_be(0),
|
|
H256::from_low_u64_be(9) => H256::from_low_u64_be(9)
|
|
]
|
|
};
|
|
assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
|
|
balance: Diff::Same,
|
|
nonce: Diff::Same,
|
|
code: Diff::Same,
|
|
storage: map![
|
|
H256::from_low_u64_be(2) => Diff::new(H256::from_low_u64_be(2), H256::from_low_u64_be(3)),
|
|
H256::from_low_u64_be(3) => Diff::new(H256::from_low_u64_be(3), H256::from_low_u64_be(0)),
|
|
H256::from_low_u64_be(4) => Diff::new(H256::from_low_u64_be(4), H256::from_low_u64_be(0)),
|
|
H256::from_low_u64_be(7) => Diff::new(H256::from_low_u64_be(0), H256::from_low_u64_be(7)),
|
|
H256::from_low_u64_be(9) => Diff::new(H256::from_low_u64_be(0), H256::from_low_u64_be(9))
|
|
],
|
|
}));
|
|
}
|
|
}
|