2017-01-25 18:51:41 +01:00
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
2016-02-05 13:40:41 +01:00
// 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/>.
2017-07-29 17:12:07 +02:00
use std ::fmt ;
use std ::collections ::BTreeMap ;
2017-08-17 16:05:26 +02:00
use itertools ::Itertools ;
2017-08-30 19:18:28 +02:00
use hash ::{ keccak } ;
2018-01-10 13:35:18 +01:00
use ethereum_types ::{ H256 , U256 } ;
use hashdb ::HashDB ;
2017-09-03 09:11:14 +02:00
use triehash ::sec_trie_root ;
2017-09-06 20:47:45 +02:00
use bytes ::Bytes ;
use trie ::TrieFactory ;
2016-08-18 15:24:27 +02:00
use state ::Account ;
2016-03-17 15:51:40 +01:00
use ethjson ;
2016-05-31 21:03:44 +02:00
use types ::account_diff ::* ;
2017-03-20 19:14:29 +01:00
use rlp ::{ self , RlpStream } ;
2016-01-14 16:46:32 +01:00
2016-03-24 01:25:59 +01:00
#[ 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.
2016-01-14 16:46:32 +01:00
pub struct PodAccount {
2016-02-03 13:20:32 +01:00
/// The balance of the account.
2016-01-14 16:46:32 +01:00
pub balance : U256 ,
2016-02-03 13:20:32 +01:00
/// The nonce of the account.
2016-01-14 16:46:32 +01:00
pub nonce : U256 ,
2016-07-26 16:48:50 +02:00
/// The code of the account or `None` in the special case that it is unknown.
pub code : Option < Bytes > ,
2016-02-03 13:20:32 +01:00
/// The storage of the account.
2016-01-14 16:46:32 +01:00
pub storage : BTreeMap < H256 , H256 > ,
}
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 {
2016-02-14 12:54:27 +01:00
balance : * acc . balance ( ) ,
nonce : * acc . nonce ( ) ,
2016-09-27 18:02:11 +02:00
storage : acc . storage_changes ( ) . iter ( ) . fold ( BTreeMap ::new ( ) , | mut m , ( k , v ) | { m . insert ( k . clone ( ) , v . clone ( ) ) ; m } ) ,
2016-07-26 16:48:50 +02:00
code : acc . code ( ) . map ( | x | x . to_vec ( ) ) ,
2016-01-14 16:46:32 +01:00
}
}
2016-01-26 15:00:22 +01:00
/// Returns the RLP for this account.
2016-01-14 16:46:32 +01:00
pub fn rlp ( & self ) -> Bytes {
let mut stream = RlpStream ::new_list ( 4 ) ;
stream . append ( & self . nonce ) ;
stream . append ( & self . balance ) ;
2018-02-16 20:24:16 +01:00
stream . append ( & sec_trie_root ( self . storage . iter ( ) . map ( | ( k , v ) | ( k , rlp ::encode ( & U256 ::from ( & * * v ) ) ) ) ) ) ;
2017-08-30 19:18:28 +02:00
stream . append ( & keccak ( & self . code . as_ref ( ) . unwrap_or ( & vec! [ ] ) ) ) ;
2016-01-14 16:46:32 +01:00
stream . out ( )
}
2016-01-26 15:00:22 +01:00
/// Place additional data into given hash DB.
2017-05-03 09:00:02 +02:00
pub fn insert_additional ( & self , db : & mut HashDB , factory : & TrieFactory ) {
2016-07-26 16:48:50 +02:00
match self . code {
Some ( ref c ) if ! c . is_empty ( ) = > { db . insert ( c ) ; }
_ = > { }
2016-01-26 15:00:22 +01:00
}
let mut r = H256 ::new ( ) ;
2016-11-27 11:11:56 +01:00
let mut t = factory . create ( db , & mut r ) ;
2016-01-26 15:00:22 +01:00
for ( k , v ) in & self . storage {
2016-09-01 15:07:06 +02:00
if let Err ( e ) = t . insert ( k , & rlp ::encode ( & U256 ::from ( & * * v ) ) ) {
2016-08-03 18:35:48 +02:00
warn! ( " Encountered potential DB corruption: {} " , e ) ;
}
2016-01-26 15:00:22 +01:00
}
}
2016-01-14 16:46:32 +01:00
}
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 ( ) ,
2016-07-26 16:48:50 +02:00
code : Some ( a . code . into ( ) ) ,
2016-04-09 19:20:35 +02:00
storage : a . storage . into_iter ( ) . map ( | ( key , value ) | {
2016-03-17 15:51:40 +01:00
let key : U256 = key . into ( ) ;
2016-03-19 11:02:44 +01:00
let value : U256 = value . into ( ) ;
2016-04-09 19:20:35 +02:00
( H256 ::from ( key ) , H256 ::from ( value ) )
2016-11-13 13:58:42 +01:00
} ) . collect ( ) ,
2016-04-09 19:20:35 +02:00
}
}
}
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 ) ,
2016-11-13 13:58:42 +01:00
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 {
fn fmt ( & self , f : & mut fmt ::Formatter ) -> fmt ::Result {
2016-07-26 16:48:50 +02:00
write! ( f , " (bal={}; nonce={}; code={} bytes, #{}; storage={} items) " ,
self . balance ,
self . nonce ,
self . code . as_ref ( ) . map_or ( 0 , | c | c . len ( ) ) ,
2017-08-30 19:18:28 +02:00
self . code . as_ref ( ) . map_or_else ( H256 ::new , | c | keccak ( c ) ) ,
2016-11-13 13:58:42 +01:00
self . storage . len ( ) ,
2016-07-26 16:48:50 +02:00
)
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 > {
match ( pre , post ) {
( 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 {
2017-07-29 17:12:07 +02:00
use std ::collections ::BTreeMap ;
2016-05-31 21:03:44 +02:00
use types ::account_diff ::* ;
2016-06-02 12:28:09 +02:00
use super ::{ PodAccount , diff_pod } ;
2016-01-14 16:46:32 +01:00
#[ test ]
fn existence ( ) {
2016-07-26 16:48:50 +02:00
let a = PodAccount { balance : 69. into ( ) , nonce : 0. into ( ) , code : Some ( vec! [ ] ) , storage : map ! [ ] } ;
2016-05-31 21:03:44 +02:00
assert_eq! ( diff_pod ( Some ( & a ) , Some ( & a ) ) , None ) ;
assert_eq! ( diff_pod ( None , Some ( & a ) ) , Some ( AccountDiff {
2016-05-31 16:59:01 +02:00
balance : Diff ::Born ( 69. into ( ) ) ,
nonce : Diff ::Born ( 0. into ( ) ) ,
2016-01-14 16:46:32 +01:00
code : Diff ::Born ( vec! [ ] ) ,
storage : map ! [ ] ,
} ) ) ;
}
#[ test ]
fn basic ( ) {
2016-07-26 16:48:50 +02:00
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 ! [ ] } ;
2016-05-31 21:03:44 +02:00
assert_eq! ( diff_pod ( Some ( & a ) , Some ( & b ) ) , Some ( AccountDiff {
2016-05-31 16:59:01 +02:00
balance : Diff ::Changed ( 69. into ( ) , 42. into ( ) ) ,
nonce : Diff ::Changed ( 0. into ( ) , 1. into ( ) ) ,
2016-01-14 16:46:32 +01:00
code : Diff ::Same ,
storage : map ! [ ] ,
} ) ) ;
}
#[ test ]
fn code ( ) {
2016-07-26 16:48:50 +02:00
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 ! [ ] } ;
2016-05-31 21:03:44 +02:00
assert_eq! ( diff_pod ( Some ( & a ) , Some ( & b ) ) , Some ( AccountDiff {
2016-01-14 16:46:32 +01:00
balance : Diff ::Same ,
2016-05-31 16:59:01 +02:00
nonce : Diff ::Changed ( 0. into ( ) , 1. into ( ) ) ,
2016-01-14 16:46:32 +01:00
code : Diff ::Changed ( vec! [ ] , vec! [ 0 ] ) ,
storage : map ! [ ] ,
} ) ) ;
}
#[ test ]
fn storage ( ) {
let a = PodAccount {
2016-05-31 16:59:01 +02:00
balance : 0. into ( ) ,
nonce : 0. into ( ) ,
2016-07-26 16:48:50 +02:00
code : Some ( vec! [ ] ) ,
2016-05-31 17:29:01 +02:00
storage : map_into ! [ 1 = > 1 , 2 = > 2 , 3 = > 3 , 4 = > 4 , 5 = > 0 , 6 = > 0 , 7 = > 0 ]
2016-01-14 16:46:32 +01:00
} ;
let b = PodAccount {
2016-05-31 16:59:01 +02:00
balance : 0. into ( ) ,
nonce : 0. into ( ) ,
2016-07-26 16:48:50 +02:00
code : Some ( vec! [ ] ) ,
2016-05-31 17:29:01 +02:00
storage : map_into ! [ 1 = > 1 , 2 = > 3 , 3 = > 0 , 5 = > 0 , 7 = > 7 , 8 = > 0 , 9 = > 9 ]
2016-01-14 16:46:32 +01:00
} ;
2016-05-31 21:03:44 +02:00
assert_eq! ( diff_pod ( Some ( & a ) , Some ( & b ) ) , Some ( AccountDiff {
2016-01-14 16:46:32 +01:00
balance : Diff ::Same ,
nonce : Diff ::Same ,
code : Diff ::Same ,
storage : map ! [
2016-05-31 16:59:01 +02:00
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 ( ) ) ,
2016-05-31 21:01:47 +02:00
9. into ( ) = > Diff ::new ( 0. into ( ) , 9. into ( ) )
2016-01-14 16:46:32 +01:00
] ,
} ) ) ;
}
}