2020-09-22 14:53:52 +02:00
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
2016-02-05 13:40:41 +01:00
2020-09-22 14:53:52 +02:00
// OpenEthereum is free software: you can redistribute it and/or modify
2016-02-05 13:40:41 +01:00
// 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.
2020-09-22 14:53:52 +02:00
// OpenEthereum is distributed in the hope that it will be useful,
2016-02-05 13:40:41 +01:00
// 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
2020-09-22 14:53:52 +02:00
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
2016-02-05 13:40:41 +01:00
2018-09-27 17:17:23 +02:00
//! Account system expressed in Plain Old Data.
2020-08-05 06:08:03 +02:00
use bytes ::Bytes ;
2018-01-10 13:35:18 +01:00
use ethereum_types ::{ H256 , U256 } ;
2020-08-05 06:08:03 +02:00
use ethjson ;
use ethtrie ::RlpCodec ;
use hash ::keccak ;
2019-02-20 19:09:34 +01:00
use hash_db ::HashDB ;
2020-08-05 06:08:03 +02:00
use itertools ::Itertools ;
2018-07-02 18:50:05 +02:00
use keccak_hasher ::KeccakHasher ;
2020-08-05 06:08:03 +02:00
use kvdb ::DBValue ;
2017-03-20 19:14:29 +01:00
use rlp ::{ self , RlpStream } ;
2018-11-25 20:12:59 +01:00
use rustc_hex ::ToHex ;
2020-08-05 06:08:03 +02:00
use serde ::Serializer ;
use state ::Account ;
use std ::{ collections ::BTreeMap , fmt } ;
use trie ::TrieFactory ;
use triehash ::sec_trie_root ;
use types ::account_diff ::* ;
2016-01-14 16:46:32 +01:00
2018-11-25 20:12:59 +01:00
#[ derive(Debug, Clone, PartialEq, Eq, Serialize) ]
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.
2016-01-14 16:46:32 +01:00
pub struct PodAccount {
2020-08-05 06:08:03 +02:00
/// 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 > ,
2016-01-14 16:46:32 +01:00
}
2018-11-25 20:12:59 +01:00
fn opt_bytes_to_hex < S > ( opt_bytes : & Option < Bytes > , serializer : S ) -> Result < S ::Ok , S ::Error >
2020-08-05 06:08:03 +02:00
where
S : Serializer ,
2018-11-25 20:12:59 +01:00
{
2020-08-05 06:08:03 +02:00
serializer . collect_str ( & format_args! (
" 0x{} " ,
opt_bytes . as_ref ( ) . map_or ( " " . to_string ( ) , | b | b . to_hex ( ) )
) )
2018-11-25 20:12:59 +01:00
}
2016-01-14 16:46:32 +01:00
impl PodAccount {
2020-08-05 06:08:03 +02:00
/// 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 ( ) ) ,
}
}
2016-01-14 16:46:32 +01:00
2020-08-05 06:08:03 +02:00
/// 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 ( & U256 ::from ( & * * v ) ) ) ) ,
) ) ;
stream . append ( & keccak ( & self . code . as_ref ( ) . unwrap_or ( & vec! [ ] ) ) ) ;
stream . out ( )
}
2016-01-26 15:00:22 +01:00
2020-08-05 06:08:03 +02:00
/// Place additional data into given hash DB.
pub fn insert_additional (
& self ,
2020-07-29 10:36:15 +02:00
db : & mut dyn HashDB < KeccakHasher , DBValue > ,
2020-08-05 06:08:03 +02:00
factory : & TrieFactory < KeccakHasher , RlpCodec > ,
) {
match self . code {
Some ( ref c ) if ! c . is_empty ( ) = > {
db . insert ( c ) ;
}
_ = > { }
}
let mut r = H256 ::new ( ) ;
let mut t = factory . create ( db , & mut r ) ;
for ( k , v ) in & self . storage {
if let Err ( e ) = t . insert ( k , & rlp ::encode ( & U256 ::from ( & * * v ) ) ) {
warn! ( " Encountered potential DB corruption: {} " , e ) ;
}
}
}
2016-01-14 16:46:32 +01:00
}
2016-03-17 15:51:40 +01:00
impl From < ethjson ::blockchain ::Account > for PodAccount {
2020-08-05 06:08:03 +02:00
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 ( ) ;
( H256 ::from ( key ) , H256 ::from ( value ) )
} )
. collect ( ) ,
}
}
2016-04-09 19:20:35 +02:00
}
impl From < ethjson ::spec ::Account > for PodAccount {
2020-08-05 06:08:03 +02:00
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 ( ) ;
( H256 ::from ( key ) , H256 ::from ( value ) )
} )
. collect ( )
} ) ,
}
}
2016-03-17 15:51:40 +01:00
}
2016-01-14 16:46:32 +01:00
impl fmt ::Display for PodAccount {
2020-08-05 06:08:03 +02:00
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 ::new , | c | keccak ( c ) ) ,
self . storage . len ( ) ,
)
}
2016-01-14 16:46:32 +01:00
}
2016-05-31 21:03:44 +02:00
/// 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 > {
2020-08-05 06:08:03 +02:00
match ( pre , post ) {
2016-05-31 21:03:44 +02:00
( None , Some ( x ) ) = > Some ( AccountDiff {
balance : Diff ::Born ( x . balance ) ,
nonce : Diff ::Born ( x . nonce ) ,
2016-07-26 16:48:50 +02:00
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 ( ) ) ,
2016-05-31 21:03:44 +02:00
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 ) ,
2016-07-26 16:48:50 +02:00
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 ( ) ) ,
2016-05-31 21:03:44 +02:00
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 ) ,
2016-07-26 16:48:50 +02:00
code : match ( pre . code . clone ( ) , post . code . clone ( ) ) {
( Some ( pre_code ) , Some ( post_code ) ) = > Diff ::new ( pre_code , post_code ) ,
_ = > Diff ::Same ,
} ,
2016-05-31 21:03:44 +02:00
storage : storage . into_iter ( ) . map ( | k |
( k . clone ( ) , Diff ::new (
2016-07-26 20:31:25 +02:00
pre . storage . get ( k ) . cloned ( ) . unwrap_or_else ( H256 ::new ) ,
post . storage . get ( k ) . cloned ( ) . unwrap_or_else ( H256 ::new )
2016-05-31 21:03:44 +02:00
) ) ) . collect ( ) ,
} ;
if r . balance . is_same ( ) & & r . nonce . is_same ( ) & & r . code . is_same ( ) & & r . storage . is_empty ( ) {
None
} else {
Some ( r )
}
} ,
_ = > None ,
}
}
2016-01-14 16:46:32 +01:00
#[ cfg(test) ]
mod test {
2020-08-05 06:08:03 +02:00
use super ::{ diff_pod , PodAccount } ;
use std ::collections ::BTreeMap ;
use types ::account_diff ::* ;
2016-01-14 16:46:32 +01:00
2020-08-05 06:08:03 +02:00
#[ 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 ! [ ] ,
} )
) ;
}
2016-01-14 16:46:32 +01:00
2020-08-05 06:08:03 +02:00
#[ 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 ! [ ] ,
} )
) ;
}
2016-01-14 16:46:32 +01:00
2020-08-05 06:08:03 +02:00
#[ 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_into ! [ 1 = > 1 , 2 = > 2 , 3 = > 3 , 4 = > 4 , 5 = > 0 , 6 = > 0 , 7 = > 0 ] ,
} ;
let b = PodAccount {
balance : 0. into ( ) ,
nonce : 0. into ( ) ,
code : Some ( vec! [ ] ) ,
storage : map_into ! [ 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 ! [
2. into ( ) = > Diff ::new ( 2. into ( ) , 3. into ( ) ) ,
3. into ( ) = > Diff ::new ( 3. into ( ) , 0. into ( ) ) ,
4. into ( ) = > Diff ::new ( 4. into ( ) , 0. into ( ) ) ,
7. into ( ) = > Diff ::new ( 0. into ( ) , 7. into ( ) ) ,
9. into ( ) = > Diff ::new ( 0. into ( ) , 9. into ( ) )
] ,
} )
) ;
}
2016-01-14 16:46:32 +01:00
}