Merge branch 'master' into evm
Conflicts: src/executive.rs src/tests/state.rs
This commit is contained in:
		
						commit
						7010e870a4
					
				| @ -18,7 +18,6 @@ rust-crypto = "0.2.34" | ||||
| time = "0.1" | ||||
| #interpolate_idents = { git = "https://github.com/SkylerLipthay/interpolate_idents" } | ||||
| evmjit = { path = "rust-evmjit", optional = true } | ||||
| itertools = "0.4" | ||||
| 
 | ||||
| [features] | ||||
| default = ["jit"] | ||||
|  | ||||
							
								
								
									
										619
									
								
								src/account.rs
									
									
									
									
									
								
							
							
						
						
									
										619
									
								
								src/account.rs
									
									
									
									
									
								
							| @ -1,396 +1,5 @@ | ||||
| use util::*; | ||||
| use itertools::Itertools; | ||||
| 
 | ||||
| pub const SHA3_EMPTY: H256 = H256( [0xc5, 0xd2, 0x46, 0x01, 0x86, 0xf7, 0x23, 0x3c, 0x92, 0x7e, 0x7d, 0xb2, 0xdc, 0xc7, 0x03, 0xc0, 0xe5, 0x00, 0xb6, 0x53, 0xca, 0x82, 0x27, 0x3b, 0x7b, 0xfa, 0xd8, 0x04, 0x5d, 0x85, 0xa4, 0x70] ); | ||||
| 
 | ||||
| #[derive(Debug,Clone,PartialEq,Eq)] | ||||
| pub enum Diff<T> where T: Eq { | ||||
| 	Same, | ||||
| 	Born(T), | ||||
| 	Changed(T, T), | ||||
| 	Died(T), | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug,Clone,PartialEq,Eq)] | ||||
| pub enum Existance { | ||||
| 	Born, | ||||
| 	Alive, | ||||
| 	Died, | ||||
| } | ||||
| 
 | ||||
| impl<T> Diff<T> where T: Eq { | ||||
| 	pub fn new(pre: T, post: T) -> Self { if pre == post { Diff::Same } else { Diff::Changed(pre, post) } } | ||||
| 	pub fn pre(&self) -> Option<&T> { match self { &Diff::Died(ref x) | &Diff::Changed(ref x, _) => Some(x), _ => None } } | ||||
| 	pub fn post(&self) -> Option<&T> { match self { &Diff::Born(ref x) | &Diff::Changed(_, ref x) => Some(x), _ => None } } | ||||
| 	pub fn is_same(&self) -> bool { match self { &Diff::Same => true, _ => false }} | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug,Clone,PartialEq,Eq)] | ||||
| /// Genesis account data. Does not have a DB overlay cache.
 | ||||
| pub struct PodAccount { | ||||
| 	// Balance of the account.
 | ||||
| 	pub balance: U256, | ||||
| 	// Nonce of the account.
 | ||||
| 	pub nonce: U256, | ||||
| 	pub code: Bytes, | ||||
| 	pub storage: BTreeMap<H256, H256>, | ||||
| } | ||||
| 
 | ||||
| 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()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug,Clone,PartialEq,Eq)] | ||||
| pub struct PodState (BTreeMap<Address, PodAccount>); | ||||
| 
 | ||||
| pub fn map_h256_h256_from_json(json: &Json) -> BTreeMap<H256, H256> { | ||||
| 	json.as_object().unwrap().iter().fold(BTreeMap::new(), |mut m, (key, value)| { | ||||
| 		m.insert(H256::from(&u256_from_str(key)), H256::from(&u256_from_json(value))); | ||||
| 		m | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| impl PodState { | ||||
| 	/// Contruct a new object from the `m`.
 | ||||
| 	pub fn from(m: BTreeMap<Address, PodAccount>) -> PodState { PodState(m) } | ||||
| 
 | ||||
| 	/// Translate the JSON object into a hash map of account information ready for insertion into State.
 | ||||
| 	pub fn from_json(json: &Json) -> PodState { | ||||
| 		PodState(json.as_object().unwrap().iter().fold(BTreeMap::new(), |mut state, (address, acc)| { | ||||
| 			let balance = acc.find("balance").map(&u256_from_json); | ||||
| 			let nonce = acc.find("nonce").map(&u256_from_json); | ||||
| 			let storage = acc.find("storage").map(&map_h256_h256_from_json);; | ||||
| 			let code = acc.find("code").map(&bytes_from_json); | ||||
| 			if balance.is_some() || nonce.is_some() || storage.is_some() || code.is_some() { | ||||
| 				state.insert(address_from_hex(address), PodAccount{ | ||||
| 					balance: balance.unwrap_or(U256::zero()), | ||||
| 					nonce: nonce.unwrap_or(U256::zero()), | ||||
| 					storage: storage.unwrap_or(BTreeMap::new()), | ||||
| 					code: code.unwrap_or(Vec::new()) | ||||
| 				}); | ||||
| 			} | ||||
| 			state | ||||
| 		})) | ||||
| 	} | ||||
| 
 | ||||
| 	/// Drain object to get the underlying map.
 | ||||
| 	pub fn drain(self) -> BTreeMap<Address, PodAccount> { self.0 } | ||||
| } | ||||
| 
 | ||||
| impl fmt::Display for PodState { | ||||
| 	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
| 		for (add, acc) in &self.0 { | ||||
| 			try!(writeln!(f, "{} => {}", add, acc)); | ||||
| 		} | ||||
| 		Ok(()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug,Clone,PartialEq,Eq)] | ||||
| pub struct AccountDiff { | ||||
| 	pub balance: Diff<U256>,				// Allowed to be Same
 | ||||
| 	pub nonce: Diff<U256>,					// Allowed to be Same
 | ||||
| 	pub code: Diff<Bytes>,					// Allowed to be Same
 | ||||
| 	pub storage: BTreeMap<H256, Diff<H256>>,// Not allowed to be Same
 | ||||
| } | ||||
| 
 | ||||
| impl AccountDiff { | ||||
| 	pub fn existance(&self) -> Existance { | ||||
| 		match self.balance { | ||||
| 			Diff::Born(_) => Existance::Born, | ||||
| 			Diff::Died(_) => Existance::Died, | ||||
| 			_ => Existance::Alive, | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn format(u: &H256) -> String { | ||||
| 	if u <= &H256::from(0xffffffff) { | ||||
| 		format!("{} = 0x{:x}", U256::from(u.as_slice()).low_u32(), U256::from(u.as_slice()).low_u32()) | ||||
| 	} else if u <= &H256::from(u64::max_value()) { | ||||
| 		format!("{} = 0x{:x}", U256::from(u.as_slice()).low_u64(), U256::from(u.as_slice()).low_u64()) | ||||
| //	} else if u <= &H256::from("0xffffffffffffffffffffffffffffffffffffffff") {
 | ||||
| //		format!("@{}", Address::from(u))
 | ||||
| 	} else { | ||||
| 		format!("#{}", u) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug,Clone,PartialEq,Eq)] | ||||
| pub struct StateDiff (BTreeMap<Address, AccountDiff>); | ||||
| 
 | ||||
| impl fmt::Display for AccountDiff { | ||||
| 	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
| 		match self.nonce { | ||||
| 			Diff::Born(ref x) => try!(write!(f, "  non {}", x)), | ||||
| 			Diff::Changed(ref pre, ref post) => try!(write!(f, "#{} ({} {} {})", post, pre, if pre > post {"-"} else {"+"}, *max(pre, post) - *	min(pre, post))), | ||||
| 			_ => {}, | ||||
| 		} | ||||
| 		match self.balance { | ||||
| 			Diff::Born(ref x) => try!(write!(f, "  bal {}", x)), | ||||
| 			Diff::Changed(ref pre, ref post) => try!(write!(f, "${} ({} {} {})", post, pre, if pre > post {"-"} else {"+"}, *max(pre, post) - *min(pre, post))), | ||||
| 			_ => {}, | ||||
| 		} | ||||
| 		match self.code { | ||||
| 			Diff::Born(ref x) => try!(write!(f, "  code {}", x.pretty())), | ||||
| 			_ => {}, | ||||
| 		} | ||||
| 		try!(write!(f, "\n")); | ||||
| 		for (k, dv) in self.storage.iter() { | ||||
| 			match dv { | ||||
| 				&Diff::Born(ref v) => try!(write!(f, "    +  {} => {}\n", format(k), format(v))), | ||||
| 				&Diff::Changed(ref pre, ref post) => try!(write!(f, "    *  {} => {} (was {})\n", format(k), format(post), format(pre))), | ||||
| 				&Diff::Died(_) => try!(write!(f, "    X  {}\n", format(k))), | ||||
| 				_ => {}, | ||||
| 			} | ||||
| 		} | ||||
| 		Ok(()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl fmt::Display for Existance { | ||||
| 	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
| 		match self { | ||||
| 			&Existance::Born => try!(write!(f, "+++")), | ||||
| 			&Existance::Alive => try!(write!(f, "***")), | ||||
| 			&Existance::Died => try!(write!(f, "XXX")), | ||||
| 		} | ||||
| 		Ok(()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl fmt::Display for StateDiff { | ||||
| 	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
| 		for (add, acc) in self.0.iter() { | ||||
| 			try!(write!(f, "{} {}: {}", acc.existance(), add, acc)); | ||||
| 		} | ||||
| 		Ok(()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| pub fn pod_diff(pre: Option<&PodAccount>, post: Option<&PodAccount>) -> Option<AccountDiff> { | ||||
| 	match (pre, post) { | ||||
| 		(None, Some(x)) => Some(AccountDiff { | ||||
| 			balance: Diff::Born(x.balance.clone()), | ||||
| 			nonce: Diff::Born(x.nonce.clone()), | ||||
| 			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.clone()), | ||||
| 			nonce: Diff::Died(x.nonce.clone()), | ||||
| 			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.clone(), post.balance.clone()), | ||||
| 				nonce: Diff::new(pre.nonce.clone(), post.nonce.clone()), | ||||
| 				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(H256::new()), | ||||
| 						post.storage.get(&k).cloned().unwrap_or(H256::new()) | ||||
| 					))).collect(), | ||||
| 			}; | ||||
| 			if r.balance.is_same() && r.nonce.is_same() && r.code.is_same() && r.storage.len() == 0 { | ||||
| 				None | ||||
| 			} else { | ||||
| 				Some(r) | ||||
| 			} | ||||
| 		}, | ||||
| 		_ => None, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| pub fn pod_state_diff(pre: &PodState, post: &PodState) -> StateDiff { | ||||
| 	StateDiff(pre.0.keys().merge(post.0.keys()).filter_map(|acc| pod_diff(pre.0.get(acc), post.0.get(acc)).map(|d|(acc.clone(), d))).collect()) | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn state_diff_create_delete() { | ||||
| 	let a = PodState(map![ | ||||
| 		x!(1) => PodAccount{ | ||||
| 			balance: x!(69), | ||||
| 			nonce: x!(0), | ||||
| 			code: vec![], | ||||
| 			storage: map![] | ||||
| 		} | ||||
| 	]); | ||||
| 	assert_eq!(pod_state_diff(&a, &PodState(map![])), StateDiff(map![ | ||||
| 		x!(1) => AccountDiff{ | ||||
| 			balance: Diff::Died(x!(69)), | ||||
| 			nonce: Diff::Died(x!(0)), | ||||
| 			code: Diff::Died(vec![]), | ||||
| 			storage: map![], | ||||
| 		} | ||||
| 	])); | ||||
| 	assert_eq!(pod_state_diff(&PodState(map![]), &a), StateDiff(map![ | ||||
| 		x!(1) => AccountDiff{ | ||||
| 			balance: Diff::Born(x!(69)), | ||||
| 			nonce: Diff::Born(x!(0)), | ||||
| 			code: Diff::Born(vec![]), | ||||
| 			storage: map![], | ||||
| 		} | ||||
| 	])); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn state_diff_cretae_delete_with_unchanged() { | ||||
| 	let a = PodState(map![ | ||||
| 		x!(1) => PodAccount{ | ||||
| 			balance: x!(69), | ||||
| 			nonce: x!(0), | ||||
| 			code: vec![], | ||||
| 			storage: map![] | ||||
| 		} | ||||
| 	]); | ||||
| 	let b = PodState(map![ | ||||
| 		x!(1) => PodAccount{ | ||||
| 			balance: x!(69), | ||||
| 			nonce: x!(0), | ||||
| 			code: vec![], | ||||
| 			storage: map![] | ||||
| 		}, | ||||
| 		x!(2) => PodAccount{ | ||||
| 			balance: x!(69), | ||||
| 			nonce: x!(0), | ||||
| 			code: vec![], | ||||
| 			storage: map![] | ||||
| 		} | ||||
| 	]); | ||||
| 	assert_eq!(pod_state_diff(&a, &b), StateDiff(map![ | ||||
| 		x!(2) => AccountDiff{ | ||||
| 			balance: Diff::Born(x!(69)), | ||||
| 			nonce: Diff::Born(x!(0)), | ||||
| 			code: Diff::Born(vec![]), | ||||
| 			storage: map![], | ||||
| 		} | ||||
| 	])); | ||||
| 	assert_eq!(pod_state_diff(&b, &a), StateDiff(map![ | ||||
| 		x!(2) => AccountDiff{ | ||||
| 			balance: Diff::Died(x!(69)), | ||||
| 			nonce: Diff::Died(x!(0)), | ||||
| 			code: Diff::Died(vec![]), | ||||
| 			storage: map![], | ||||
| 		} | ||||
| 	])); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn state_diff_change_with_unchanged() { | ||||
| 	let a = PodState(map![ | ||||
| 		x!(1) => PodAccount{ | ||||
| 			balance: x!(69), | ||||
| 			nonce: x!(0), | ||||
| 			code: vec![], | ||||
| 			storage: map![] | ||||
| 		}, | ||||
| 		x!(2) => PodAccount{ | ||||
| 			balance: x!(69), | ||||
| 			nonce: x!(0), | ||||
| 			code: vec![], | ||||
| 			storage: map![] | ||||
| 		} | ||||
| 	]); | ||||
| 	let b = PodState(map![ | ||||
| 		x!(1) => PodAccount{ | ||||
| 			balance: x!(69), | ||||
| 			nonce: x!(1), | ||||
| 			code: vec![], | ||||
| 			storage: map![] | ||||
| 		}, | ||||
| 		x!(2) => PodAccount{ | ||||
| 			balance: x!(69), | ||||
| 			nonce: x!(0), | ||||
| 			code: vec![], | ||||
| 			storage: map![] | ||||
| 		} | ||||
| 	]); | ||||
| 	assert_eq!(pod_state_diff(&a, &b), StateDiff(map![ | ||||
| 		x!(1) => AccountDiff{ | ||||
| 			balance: Diff::Same, | ||||
| 			nonce: Diff::Changed(x!(0), x!(1)), | ||||
| 			code: Diff::Same, | ||||
| 			storage: map![], | ||||
| 		} | ||||
| 	])); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn account_diff_existence() { | ||||
| 	let a = PodAccount{balance: x!(69), nonce: x!(0), code: vec![], storage: map![]}; | ||||
| 	assert_eq!(pod_diff(Some(&a), Some(&a)), None); | ||||
| 	assert_eq!(pod_diff(None, Some(&a)), Some(AccountDiff{ | ||||
| 		balance: Diff::Born(x!(69)), | ||||
| 		nonce: Diff::Born(x!(0)), | ||||
| 		code: Diff::Born(vec![]), | ||||
| 		storage: map![], | ||||
| 	})); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn account_diff_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!(pod_diff(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 account_diff_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!(pod_diff(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 account_diff_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!(pod_diff(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)) | ||||
| 		], | ||||
| 	})); | ||||
| } | ||||
| 
 | ||||
| #[derive(PartialEq,Eq,Clone,Copy)] | ||||
| pub enum Filth { | ||||
| 	Clean, | ||||
| 	Dirty, | ||||
| } | ||||
| use pod_account::*; | ||||
| 
 | ||||
| /// Single account in the system.
 | ||||
| #[derive(Clone)] | ||||
| @ -409,29 +18,6 @@ pub struct Account { | ||||
| 	code_cache: Bytes, | ||||
| } | ||||
| 
 | ||||
| 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.clone(), | ||||
| 			nonce: acc.nonce.clone(), | ||||
| 			storage: acc.storage_overlay.borrow().iter().fold(BTreeMap::new(), |mut m, (k, &(_, ref v))| {m.insert(k.clone(), v.clone()); m}), | ||||
| 			code: acc.code_cache.clone() | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn rlp(&self) -> Bytes { | ||||
| 		let mut stream = RlpStream::new_list(4); | ||||
| 		stream.append(&self.nonce); | ||||
| 		stream.append(&self.balance); | ||||
| 		// TODO.
 | ||||
| 		stream.append(&SHA3_NULL_RLP); | ||||
| 		stream.append(&self.code.sha3()); | ||||
| 		stream.out() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl Account { | ||||
| 	/// General constructor.
 | ||||
| 	pub fn new(balance: U256, nonce: U256, storage: HashMap<H256, H256>, code: Bytes) -> Account { | ||||
| @ -563,18 +149,14 @@ impl Account { | ||||
| 	/// Provide a database to lookup `code_hash`. Should not be called if it is a contract without code.
 | ||||
| 	pub fn cache_code(&mut self, db: &HashDB) -> bool { | ||||
| 		// TODO: fill out self.code_cache;
 | ||||
| /*		return !self.is_cached() ||
 | ||||
| 			match db.lookup(&self.code_hash.unwrap()) {	// why doesn't this work? unwrap causes move?!
 | ||||
| 				Some(x) => { self.code_cache = x.to_vec(); true }, | ||||
| 				_ => { false } | ||||
| 			}*/ | ||||
| 		if self.is_cached() { return true; } | ||||
| 		return if let Some(ref h) = self.code_hash { | ||||
| 			match db.lookup(&h) { | ||||
| 				Some(x) => { self.code_cache = x.to_vec(); true }, | ||||
| 				_ => { false } | ||||
| 		return self.is_cached() || | ||||
| 			match self.code_hash { | ||||
| 				Some(ref h) => match db.lookup(h) { | ||||
| 					Some(x) => { self.code_cache = x.to_vec(); true }, | ||||
| 					_ => false, | ||||
| 				}, | ||||
| 				_ => false, | ||||
| 			} | ||||
| 		} else { false } | ||||
| 	} | ||||
| 
 | ||||
| 	/// return the storage root associated with this account.
 | ||||
| @ -603,12 +185,15 @@ impl Account { | ||||
| 
 | ||||
| 	/// Commit the `storage_overlay` to the backing DB and update `storage_root`.
 | ||||
| 	pub fn commit_storage(&mut self, db: &mut HashDB) { | ||||
| 		let mut t = SecTrieDBMut::new(db, &mut self.storage_root); | ||||
| 		let mut t = SecTrieDBMut::from_existing(db, &mut self.storage_root); | ||||
| 		for (k, &mut (ref mut f, ref mut v)) in self.storage_overlay.borrow_mut().iter_mut() { | ||||
| 			if f == &Filth::Dirty { | ||||
| 				// cast key and value to trait type,
 | ||||
| 				// so we can call overloaded `to_bytes` method
 | ||||
| 				t.insert(k, &encode(&U256::from(v.as_slice()))); | ||||
| 				match v.is_zero() { | ||||
| 					true => { t.remove(k); }, | ||||
| 					false => { t.insert(k, &encode(&U256::from(v.as_slice()))); }, | ||||
| 				} | ||||
| 				*f = Filth::Clean; | ||||
| 			} | ||||
| 		} | ||||
| @ -647,98 +232,106 @@ impl fmt::Debug for Account { | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
| 
 | ||||
| use super::*; | ||||
| use std::collections::HashMap; | ||||
| use util::hash::*; | ||||
| use util::bytes::*; | ||||
| use util::rlp::*; | ||||
| use util::uint::*; | ||||
| use util::overlaydb::*; | ||||
| 	use util::*; | ||||
| 	use super::*; | ||||
| 
 | ||||
| #[test] | ||||
| fn storage_at() { | ||||
| 	let mut db = OverlayDB::new_temp(); | ||||
| 	let rlp = { | ||||
| 	#[test] | ||||
| 	fn storage_at() { | ||||
| 		let mut db = OverlayDB::new_temp(); | ||||
| 		let rlp = { | ||||
| 			let mut a = Account::new_contract(U256::from(69u8)); | ||||
| 			a.set_storage(H256::from(&U256::from(0x00u64)), H256::from(&U256::from(0x1234u64))); | ||||
| 			a.commit_storage(&mut db); | ||||
| 			a.init_code(vec![]); | ||||
| 			a.commit_code(&mut db); | ||||
| 			a.rlp() | ||||
| 		}; | ||||
| 
 | ||||
| 		let a = Account::from_rlp(&rlp); | ||||
| 		assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2"); | ||||
| 		assert_eq!(a.storage_at(&mut db, &H256::from(&U256::from(0x00u64))), H256::from(&U256::from(0x1234u64))); | ||||
| 		assert_eq!(a.storage_at(&mut db, &H256::from(&U256::from(0x01u64))), H256::new()); | ||||
| 	} | ||||
| 
 | ||||
| 	#[test] | ||||
| 	fn note_code() { | ||||
| 		let mut db = OverlayDB::new_temp(); | ||||
| 
 | ||||
| 		let rlp = { | ||||
| 			let mut a = Account::new_contract(U256::from(69u8)); | ||||
| 			a.init_code(vec![0x55, 0x44, 0xffu8]); | ||||
| 			a.commit_code(&mut db); | ||||
| 			a.rlp() | ||||
| 		}; | ||||
| 
 | ||||
| 		let mut a = Account::from_rlp(&rlp); | ||||
| 		assert!(a.cache_code(&db)); | ||||
| 
 | ||||
| 		let mut a = Account::from_rlp(&rlp); | ||||
| 		assert_eq!(a.note_code(vec![0x55, 0x44, 0xffu8]), Ok(())); | ||||
| 	} | ||||
| 
 | ||||
| 	#[test] | ||||
| 	fn commit_storage() { | ||||
| 		let mut a = Account::new_contract(U256::from(69u8)); | ||||
| 		a.set_storage(H256::from(&U256::from(0x00u64)), H256::from(&U256::from(0x1234u64))); | ||||
| 		let mut db = OverlayDB::new_temp(); | ||||
| 		a.set_storage(x!(0), x!(0x1234)); | ||||
| 		assert_eq!(a.storage_root(), None); | ||||
| 		a.commit_storage(&mut db); | ||||
| 		a.init_code(vec![]); | ||||
| 		a.commit_code(&mut db); | ||||
| 		a.rlp() | ||||
| 	}; | ||||
| 		assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2"); | ||||
| 	} | ||||
| 
 | ||||
| 	let a = Account::from_rlp(&rlp); | ||||
| 	assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2"); | ||||
| 	assert_eq!(a.storage_at(&mut db, &H256::from(&U256::from(0x00u64))), H256::from(&U256::from(0x1234u64))); | ||||
| 	assert_eq!(a.storage_at(&mut db, &H256::from(&U256::from(0x01u64))), H256::new()); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn note_code() { | ||||
| 	let mut db = OverlayDB::new_temp(); | ||||
| 
 | ||||
| 	let rlp = { | ||||
| 	#[test] | ||||
| 	fn commit_remove_commit_storage() { | ||||
| 		let mut a = Account::new_contract(U256::from(69u8)); | ||||
| 		let mut db = OverlayDB::new_temp(); | ||||
| 		a.set_storage(x!(0), x!(0x1234)); | ||||
| 		a.commit_storage(&mut db); | ||||
| 		a.set_storage(x!(1), x!(0x1234)); | ||||
| 		a.commit_storage(&mut db); | ||||
| 		a.set_storage(x!(1), x!(0)); | ||||
| 		a.commit_storage(&mut db); | ||||
| 		assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2"); | ||||
| 	} | ||||
| 
 | ||||
| 	#[test] | ||||
| 	fn commit_code() { | ||||
| 		let mut a = Account::new_contract(U256::from(69u8)); | ||||
| 		let mut db = OverlayDB::new_temp(); | ||||
| 		a.init_code(vec![0x55, 0x44, 0xffu8]); | ||||
| 		assert_eq!(a.code_hash(), SHA3_EMPTY); | ||||
| 		a.commit_code(&mut db); | ||||
| 		a.rlp() | ||||
| 	}; | ||||
| 		assert_eq!(a.code_hash().hex(), "af231e631776a517ca23125370d542873eca1fb4d613ed9b5d5335a46ae5b7eb"); | ||||
| 	} | ||||
| 
 | ||||
| 	let mut a = Account::from_rlp(&rlp); | ||||
| 	assert_eq!(a.cache_code(&db), true); | ||||
| 	#[test] | ||||
| 	fn rlpio() { | ||||
| 		let a = Account::new(U256::from(69u8), U256::from(0u8), HashMap::new(), Bytes::new()); | ||||
| 		let b = Account::from_rlp(&a.rlp()); | ||||
| 		assert_eq!(a.balance(), b.balance()); | ||||
| 		assert_eq!(a.nonce(), b.nonce()); | ||||
| 		assert_eq!(a.code_hash(), b.code_hash()); | ||||
| 		assert_eq!(a.storage_root(), b.storage_root()); | ||||
| 	} | ||||
| 
 | ||||
| 	#[test] | ||||
| 	fn new_account() { | ||||
| 		use rustc_serialize::hex::ToHex; | ||||
| 
 | ||||
| 		let a = Account::new(U256::from(69u8), U256::from(0u8), HashMap::new(), Bytes::new()); | ||||
| 		assert_eq!(a.rlp().to_hex(), "f8448045a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); | ||||
| 		assert_eq!(a.balance(), &U256::from(69u8)); | ||||
| 		assert_eq!(a.nonce(), &U256::from(0u8)); | ||||
| 		assert_eq!(a.code_hash(), SHA3_EMPTY); | ||||
| 		assert_eq!(a.storage_root().unwrap(), &SHA3_NULL_RLP); | ||||
| 	} | ||||
| 
 | ||||
| 	#[test] | ||||
| 	fn create_account() { | ||||
| 		use rustc_serialize::hex::ToHex; | ||||
| 
 | ||||
| 		let a = Account::new(U256::from(69u8), U256::from(0u8), HashMap::new(), Bytes::new()); | ||||
| 		assert_eq!(a.rlp().to_hex(), "f8448045a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); | ||||
| 	} | ||||
| 
 | ||||
| 	let mut a = Account::from_rlp(&rlp); | ||||
| 	assert_eq!(a.note_code(vec![0x55, 0x44, 0xffu8]), Ok(())); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn commit_storage() { | ||||
| 	let mut a = Account::new_contract(U256::from(69u8)); | ||||
| 	let mut db = OverlayDB::new_temp(); | ||||
| 	a.set_storage(H256::from(&U256::from(0x00u64)), H256::from(&U256::from(0x1234u64))); | ||||
| 	assert_eq!(a.storage_root(), None); | ||||
| 	a.commit_storage(&mut db); | ||||
| 	assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2"); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn commit_code() { | ||||
| 	let mut a = Account::new_contract(U256::from(69u8)); | ||||
| 	let mut db = OverlayDB::new_temp(); | ||||
| 	a.init_code(vec![0x55, 0x44, 0xffu8]); | ||||
| 	assert_eq!(a.code_hash(), SHA3_EMPTY); | ||||
| 	a.commit_code(&mut db); | ||||
| 	assert_eq!(a.code_hash().hex(), "af231e631776a517ca23125370d542873eca1fb4d613ed9b5d5335a46ae5b7eb"); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn rlpio() { | ||||
| 	let a = Account::new(U256::from(69u8), U256::from(0u8), HashMap::new(), Bytes::new()); | ||||
| 	let b = Account::from_rlp(&a.rlp()); | ||||
| 	assert_eq!(a.balance(), b.balance()); | ||||
| 	assert_eq!(a.nonce(), b.nonce()); | ||||
| 	assert_eq!(a.code_hash(), b.code_hash()); | ||||
| 	assert_eq!(a.storage_root(), b.storage_root()); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn new_account() { | ||||
| 	use rustc_serialize::hex::ToHex; | ||||
| 
 | ||||
| 	let a = Account::new(U256::from(69u8), U256::from(0u8), HashMap::new(), Bytes::new()); | ||||
| 	assert_eq!(a.rlp().to_hex(), "f8448045a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); | ||||
| 	assert_eq!(a.balance(), &U256::from(69u8)); | ||||
| 	assert_eq!(a.nonce(), &U256::from(0u8)); | ||||
| 	assert_eq!(a.code_hash(), SHA3_EMPTY); | ||||
| 	assert_eq!(a.storage_root().unwrap(), &SHA3_NULL_RLP); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn create_account() { | ||||
| 	use rustc_serialize::hex::ToHex; | ||||
| 
 | ||||
| 	let a = Account::new(U256::from(69u8), U256::from(0u8), HashMap::new(), Bytes::new()); | ||||
| 	assert_eq!(a.rlp().to_hex(), "f8448045a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); | ||||
| } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										121
									
								
								src/account_diff.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								src/account_diff.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,121 @@ | ||||
| use util::*; | ||||
| use pod_account::*; | ||||
| 
 | ||||
| #[derive(Debug,Clone,PartialEq,Eq)] | ||||
| /// Change in existance type. 
 | ||||
| // TODO: include other types of change.
 | ||||
| pub enum Existance { | ||||
| 	Born, | ||||
| 	Alive, | ||||
| 	Died, | ||||
| } | ||||
| 
 | ||||
| impl fmt::Display for Existance { | ||||
| 	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
| 		match self { | ||||
| 			&Existance::Born => try!(write!(f, "+++")), | ||||
| 			&Existance::Alive => try!(write!(f, "***")), | ||||
| 			&Existance::Died => try!(write!(f, "XXX")), | ||||
| 		} | ||||
| 		Ok(()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug,Clone,PartialEq,Eq)] | ||||
| pub struct AccountDiff { | ||||
| 	pub balance: Diff<U256>,				// Allowed to be Same
 | ||||
| 	pub nonce: Diff<U256>,					// Allowed to be Same
 | ||||
| 	pub code: Diff<Bytes>,					// Allowed to be Same
 | ||||
| 	pub storage: BTreeMap<H256, Diff<H256>>,// Not allowed to be Same
 | ||||
| } | ||||
| 
 | ||||
| impl AccountDiff { | ||||
| 	pub fn existance(&self) -> Existance { | ||||
| 		match self.balance { | ||||
| 			Diff::Born(_) => Existance::Born, | ||||
| 			Diff::Died(_) => Existance::Died, | ||||
| 			_ => Existance::Alive, | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	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.clone()), | ||||
| 				nonce: Diff::Born(x.nonce.clone()), | ||||
| 				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.clone()), | ||||
| 				nonce: Diff::Died(x.nonce.clone()), | ||||
| 				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.clone(), post.balance.clone()), | ||||
| 					nonce: Diff::new(pre.nonce.clone(), post.nonce.clone()), | ||||
| 					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(H256::new()), | ||||
| 							post.storage.get(&k).cloned().unwrap_or(H256::new()) | ||||
| 						))).collect(), | ||||
| 				}; | ||||
| 				if r.balance.is_same() && r.nonce.is_same() && r.code.is_same() && r.storage.len() == 0 { | ||||
| 					None | ||||
| 				} else { | ||||
| 					Some(r) | ||||
| 				} | ||||
| 			}, | ||||
| 			_ => None, | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // TODO: refactor into something nicer.
 | ||||
| fn interpreted_hash(u: &H256) -> String { | ||||
| 	if u <= &H256::from(0xffffffff) { | ||||
| 		format!("{} = 0x{:x}", U256::from(u.as_slice()).low_u32(), U256::from(u.as_slice()).low_u32()) | ||||
| 	} else if u <= &H256::from(u64::max_value()) { | ||||
| 		format!("{} = 0x{:x}", U256::from(u.as_slice()).low_u64(), U256::from(u.as_slice()).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 { | ||||
| 		match self.nonce { | ||||
| 			Diff::Born(ref x) => try!(write!(f, "  non {}", x)), | ||||
| 			Diff::Changed(ref pre, ref post) => try!(write!(f, "#{} ({} {} {})", post, pre, if pre > post {"-"} else {"+"}, *max(pre, post) - *	min(pre, post))), | ||||
| 			_ => {}, | ||||
| 		} | ||||
| 		match self.balance { | ||||
| 			Diff::Born(ref x) => try!(write!(f, "  bal {}", x)), | ||||
| 			Diff::Changed(ref pre, ref post) => try!(write!(f, "${} ({} {} {})", post, pre, if pre > post {"-"} else {"+"}, *max(pre, post) - *min(pre, post))), | ||||
| 			_ => {}, | ||||
| 		} | ||||
| 		match self.code { | ||||
| 			Diff::Born(ref x) => try!(write!(f, "  code {}", x.pretty())), | ||||
| 			_ => {}, | ||||
| 		} | ||||
| 		try!(write!(f, "\n")); | ||||
| 		for (k, dv) in self.storage.iter() { | ||||
| 			match dv { | ||||
| 				&Diff::Born(ref v) => try!(write!(f, "    +  {} => {}\n", interpreted_hash(k), interpreted_hash(v))), | ||||
| 				&Diff::Changed(ref pre, ref post) => try!(write!(f, "    *  {} => {} (was {})\n", interpreted_hash(k), interpreted_hash(post), interpreted_hash(pre))), | ||||
| 				&Diff::Died(_) => try!(write!(f, "    X  {}\n", interpreted_hash(k))), | ||||
| 				_ => {}, | ||||
| 			} | ||||
| 		} | ||||
| 		Ok(()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -39,6 +39,12 @@ impl Builtin { | ||||
| 		}) | ||||
| 	} | ||||
| 
 | ||||
| 	/// Simple forwarder for cost.
 | ||||
| 	pub fn cost(&self, s: usize) -> U256 { (*self.cost)(s) } | ||||
| 
 | ||||
| 	/// Simple forwarder for execute.
 | ||||
| 	pub fn execute(&self, input: &[u8], output: &mut[u8]) { (*self.execute)(input, output); } | ||||
| 
 | ||||
| 	/// Create a builtin from JSON.
 | ||||
| 	///
 | ||||
| 	/// JSON must be of the form `{ "name": "identity", "linear": {"base": 10, "word": 20} }`.
 | ||||
| @ -55,12 +61,6 @@ impl Builtin { | ||||
| 		} | ||||
| 		None | ||||
| 	} | ||||
| 
 | ||||
| 	/// Simple forwarder for cost.
 | ||||
| 	pub fn cost(&self, s: usize) -> U256 { (*self.cost)(s) } | ||||
| 
 | ||||
| 	/// Simple forwarder for execute.
 | ||||
| 	pub fn execute(&self, input: &[u8], output: &mut[u8]) { (*self.execute)(input, output); } | ||||
| } | ||||
| 
 | ||||
| pub fn copy_to(src: &[u8], dest: &mut[u8]) { | ||||
|  | ||||
| @ -36,16 +36,18 @@ impl EnvInfo { | ||||
| 			gas_used: x!(0), | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 	pub fn from_json(json: &Json) -> EnvInfo { | ||||
| 		let current_number = u64_from_json(&json["currentNumber"]); | ||||
| impl FromJson for EnvInfo { | ||||
| 	fn from_json(json: &Json) -> EnvInfo { | ||||
| 		let current_number: u64 = xjson!(&json["currentNumber"]); | ||||
| 		EnvInfo { | ||||
| 			number: current_number, | ||||
| 			author: address_from_json(&json["currentCoinbase"]), | ||||
| 			difficulty: u256_from_json(&json["currentDifficulty"]), | ||||
| 			gas_limit: u256_from_json(&json["currentGasLimit"]), | ||||
| 			timestamp: u64_from_json(&json["currentTimestamp"]), | ||||
| 			last_hashes: (1..257).map(|i| format!("{}", current_number - i).as_bytes().sha3()).collect(), | ||||
| 			author: xjson!(&json["currentCoinbase"]), | ||||
| 			difficulty: xjson!(&json["currentDifficulty"]), | ||||
| 			gas_limit: xjson!(&json["currentGasLimit"]), | ||||
| 			timestamp: xjson!(&json["currentTimestamp"]), | ||||
| 			last_hashes: (1..cmp::min(current_number + 1, 257)).map(|i| format!("{}", current_number - i).as_bytes().sha3()).collect(), | ||||
| 			gas_used: x!(0), | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @ -20,15 +20,14 @@ pub trait Ext { | ||||
| 
 | ||||
| 	/// Creates new contract.
 | ||||
| 	/// 
 | ||||
| 	/// If contract creation is successfull, return gas_left and contract address,
 | ||||
| 	/// If depth is too big or transfer value exceeds balance, return None
 | ||||
| 	/// Otherwise return appropriate `Error`.
 | ||||
| 	fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> Result<(U256, Option<Address>), Error>; | ||||
| 	/// Returns gas_left and contract address if contract creation was succesfull.
 | ||||
| 	fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> (U256, Option<Address>); | ||||
| 
 | ||||
| 	/// Message call.
 | ||||
| 	/// 
 | ||||
| 	/// If call is successfull, returns gas left.
 | ||||
| 	/// otherwise `Error`.
 | ||||
| 	/// Returns Err, if we run out of gas.
 | ||||
| 	/// Otherwise returns call_result which contains gas left 
 | ||||
| 	/// and true if subcall was successfull.
 | ||||
| 	fn call(&mut self, 
 | ||||
| 			gas: &U256, 
 | ||||
| 			call_gas: &U256, 
 | ||||
| @ -36,7 +35,7 @@ pub trait Ext { | ||||
| 			value: &U256, 
 | ||||
| 			data: &[u8], 
 | ||||
| 			code_address: &Address, 
 | ||||
| 			output: &mut [u8]) -> Result<U256, Error>; | ||||
| 			output: &mut [u8]) -> Result<(U256, bool), Error>; | ||||
| 
 | ||||
| 	/// Returns code at given address
 | ||||
| 	fn extcode(&self, address: &Address) -> Vec<u8>; | ||||
|  | ||||
| @ -209,23 +209,12 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> { | ||||
| 			  init_size: u64, | ||||
| 			  address: *mut evmjit::H256) { | ||||
| 		unsafe { | ||||
| 			match self.ext.create(&U256::from(*io_gas), &U256::from_jit(&*endowment), slice::from_raw_parts(init_beg, init_size as usize)) { | ||||
| 				Ok((gas_left, opt)) => { | ||||
| 					*io_gas = gas_left.low_u64(); | ||||
| 					*address = match opt { | ||||
| 						Some(addr) => addr.into_jit(), | ||||
| 						_ => Address::new().into_jit() | ||||
| 					}; | ||||
| 				}, | ||||
| 				Err(err @ evm::Error::OutOfGas) => { | ||||
| 					*self.err = Some(err); | ||||
| 					// hack to propagate `OutOfGas` to evmjit and stop
 | ||||
| 					// the execution immediately.
 | ||||
| 					// Works, cause evmjit uses i64, not u64
 | ||||
| 					*io_gas = -1i64 as u64; | ||||
| 				}, | ||||
| 				Err(err) => *self.err = Some(err) | ||||
| 			} | ||||
| 			let (gas_left, opt_addr) = self.ext.create(&U256::from(*io_gas), &U256::from_jit(&*endowment), slice::from_raw_parts(init_beg, init_size as usize)); | ||||
| 			*io_gas = gas_left.low_u64(); | ||||
| 			*address = match opt_addr { | ||||
| 				Some(addr) => addr.into_jit(), | ||||
| 				_ => Address::new().into_jit() | ||||
| 			}; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| @ -247,22 +236,21 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> { | ||||
| 									slice::from_raw_parts(in_beg, in_size as usize), | ||||
| 									&Address::from_jit(&*code_address), | ||||
| 									slice::from_raw_parts_mut(out_beg, out_size as usize)); | ||||
| 
 | ||||
| 			match res { | ||||
| 				Ok(gas_left) => { | ||||
| 				Ok((gas_left, ok)) => { | ||||
| 					*io_gas = gas_left.low_u64(); | ||||
| 					true | ||||
| 				}, | ||||
| 				Err(err @ evm::Error::OutOfGas) => { | ||||
| 					*self.err = Some(err); | ||||
| 					// hack to propagate `OutOfGas` to evmjit and stop
 | ||||
| 					// the execution immediately.
 | ||||
| 					// Works, cause evmjit uses i64, not u64
 | ||||
| 					ok | ||||
| 				} | ||||
| 				Err(evm::Error::OutOfGas) => { | ||||
| 					// hack to propagate out_of_gas to evmjit.
 | ||||
| 					// must be negative
 | ||||
| 					*io_gas = -1i64 as u64; | ||||
| 					false | ||||
| 				}, | ||||
| 				Err(err) => { | ||||
| 					// internal error.
 | ||||
| 					*self.err = Some(err); | ||||
| 					*io_gas = -1i64 as u64; | ||||
| 					false | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| @ -14,7 +14,7 @@ mod jit; | ||||
| mod tests; | ||||
| 
 | ||||
| pub use self::evm::{Evm, Error, Result}; | ||||
| pub use self::ext::Ext; | ||||
| pub use self::ext::{Ext}; | ||||
| pub use self::factory::Factory; | ||||
| pub use self::schedule::Schedule; | ||||
| pub use self::factory::VMType; | ||||
|  | ||||
| @ -51,7 +51,7 @@ impl Ext for FakeExt { | ||||
| 		self.blockhashes.get(number).unwrap_or(&H256::new()).clone() | ||||
| 	} | ||||
| 
 | ||||
| 	fn create(&mut self, _gas: &U256, _value: &U256, _code: &[u8]) -> result::Result<(U256, Option<Address>), evm::Error> { | ||||
| 	fn create(&mut self, _gas: &U256, _value: &U256, _code: &[u8]) -> (U256, Option<Address>) { | ||||
| 		unimplemented!(); | ||||
| 	} | ||||
| 
 | ||||
| @ -62,7 +62,7 @@ impl Ext for FakeExt { | ||||
| 			_value: &U256, 
 | ||||
| 			_data: &[u8], 
 | ||||
| 			_code_address: &Address, 
 | ||||
| 			_output: &mut [u8]) -> result::Result<U256, evm::Error> { | ||||
| 			_output: &mut [u8]) -> result::Result<(U256, bool), evm::Error> { | ||||
| 		unimplemented!(); | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										161
									
								
								src/executive.rs
									
									
									
									
									
								
							
							
						
						
									
										161
									
								
								src/executive.rs
									
									
									
									
									
								
							| @ -21,6 +21,8 @@ pub struct Substate { | ||||
| 	logs: Vec<LogEntry>, | ||||
| 	/// Refund counter of SSTORE nonzero->zero.
 | ||||
| 	refunds_count: U256, | ||||
| 	/// True if transaction, or one of its subcalls runs out of gas.
 | ||||
| 	excepted: bool, | ||||
| 	/// Created contracts.
 | ||||
| 	contracts_created: Vec<Address> | ||||
| } | ||||
| @ -32,9 +34,20 @@ impl Substate { | ||||
| 			suicides: HashSet::new(), | ||||
| 			logs: vec![], | ||||
| 			refunds_count: U256::zero(), | ||||
| 			excepted: false, | ||||
| 			contracts_created: vec![] | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn accrue(&mut self, s: Substate) { | ||||
| 		self.suicides.extend(s.suicides.into_iter()); | ||||
| 		self.logs.extend(s.logs.into_iter()); | ||||
| 		self.refunds_count = self.refunds_count + s.refunds_count; | ||||
| 		self.excepted |= s.excepted; | ||||
| 		self.contracts_created.extend(s.contracts_created.into_iter()); | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn excepted(&self) -> bool { self.excepted } | ||||
| } | ||||
| 
 | ||||
| /// Transaction execution receipt.
 | ||||
| @ -56,7 +69,7 @@ pub struct Executed { | ||||
| 	/// Vector of logs generated by transaction.
 | ||||
| 	pub logs: Vec<LogEntry>, | ||||
| 	/// Execution ended running out of gas.
 | ||||
| 	pub out_of_gas: bool, | ||||
| 	pub excepted: bool, | ||||
| 	/// Addresses of contracts created during execution of transaction.
 | ||||
| 	/// Ordered from earliest creation.
 | ||||
| 	/// 
 | ||||
| @ -135,7 +148,6 @@ impl<'a> Executive<'a> { | ||||
| 		self.state.sub_balance(&sender, &U256::from(gas_cost)); | ||||
| 
 | ||||
| 		let mut substate = Substate::new(); | ||||
| 		let backup = self.state.clone(); | ||||
| 
 | ||||
| 		let schedule = self.engine.schedule(self.info); | ||||
| 		let init_gas = t.gas - U256::from(t.gas_required(&schedule)); | ||||
| @ -172,7 +184,7 @@ impl<'a> Executive<'a> { | ||||
| 		}; | ||||
| 
 | ||||
| 		// finalize here!
 | ||||
| 		Ok(try!(self.finalize(t, substate, backup, res))) | ||||
| 		Ok(try!(self.finalize(t, substate, res))) | ||||
| 	} | ||||
| 
 | ||||
| 	/// Calls contract function with given contract params.
 | ||||
| @ -180,6 +192,9 @@ impl<'a> Executive<'a> { | ||||
| 	/// Modifies the substate and the output.
 | ||||
| 	/// Returns either gas_left or `evm::Error`.
 | ||||
| 	pub fn call(&mut self, params: &ActionParams, substate: &mut Substate, mut output: BytesRef) -> evm::Result { | ||||
| 		// backup used in case of running out of gas
 | ||||
| 		let backup = self.state.clone(); | ||||
| 
 | ||||
| 		// at first, transfer value to destination
 | ||||
| 		self.state.transfer_balance(¶ms.sender, ¶ms.address, ¶ms.value); | ||||
| 
 | ||||
| @ -191,12 +206,21 @@ impl<'a> Executive<'a> { | ||||
| 					self.engine.execute_builtin(¶ms.address, ¶ms.data, &mut output); | ||||
| 					Ok(params.gas - cost) | ||||
| 				}, | ||||
| 				false => Err(evm::Error::OutOfGas) | ||||
| 				// just drain the whole gas
 | ||||
| 				false => Ok(U256::zero()) | ||||
| 			} | ||||
| 		} else if params.code.len() > 0 { | ||||
| 			// if destination is a contract, do normal message call
 | ||||
| 			let mut ext = Externalities::from_executive(self, params, substate, OutputPolicy::Return(output)); | ||||
| 			self.engine.vm_factory().create().exec(¶ms, &mut ext) | ||||
| 			
 | ||||
| 			// part of substate that may be reverted
 | ||||
| 			let mut unconfirmed_substate = Substate::new(); | ||||
| 
 | ||||
| 			let res = { | ||||
| 				let mut ext = Externalities::from_executive(self, params, &mut unconfirmed_substate, OutputPolicy::Return(output)); | ||||
| 				self.engine.vm_factory().create().exec(¶ms, &mut ext) | ||||
| 			}; | ||||
| 			self.enact_result(&res, substate, unconfirmed_substate, backup); | ||||
| 			res | ||||
| 		} else { | ||||
| 			// otherwise, nothing
 | ||||
| 			Ok(params.gas) | ||||
| @ -207,17 +231,57 @@ impl<'a> Executive<'a> { | ||||
| 	/// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides).
 | ||||
| 	/// Modifies the substate.
 | ||||
| 	fn create(&mut self, params: &ActionParams, substate: &mut Substate) -> evm::Result { | ||||
| 		// backup used in case of running out of gas
 | ||||
| 		let backup = self.state.clone(); | ||||
| 
 | ||||
| 		// part of substate that may be reverted
 | ||||
| 		let mut unconfirmed_substate = Substate::new(); | ||||
| 
 | ||||
| 		// at first create new contract
 | ||||
| 		self.state.new_contract(¶ms.address); | ||||
| 
 | ||||
| 		// then transfer value to it
 | ||||
| 		self.state.transfer_balance(¶ms.sender, ¶ms.address, ¶ms.value); | ||||
| 
 | ||||
| 		let mut ext = Externalities::from_executive(self, params, substate, OutputPolicy::InitContract); | ||||
| 		self.engine.vm_factory().create().exec(¶ms, &mut ext) | ||||
| 		let res = { | ||||
| 			let mut ext = Externalities::from_executive(self, params, &mut unconfirmed_substate, OutputPolicy::InitContract); | ||||
| 			self.engine.vm_factory().create().exec(¶ms, &mut ext) | ||||
| 		}; | ||||
| 		self.enact_result(&res, substate, unconfirmed_substate, backup); | ||||
| 		res | ||||
| 	} | ||||
| 
 | ||||
| 	/// Finalizes the transaction (does refunds and suicides).
 | ||||
| 	fn finalize(&mut self, t: &Transaction, substate: Substate, backup: State, result: evm::Result) -> ExecutionResult { | ||||
| 	fn finalize(&mut self, t: &Transaction, substate: Substate, result: evm::Result) -> ExecutionResult { | ||||
| 		let schedule = self.engine.schedule(self.info); | ||||
| 
 | ||||
| 		// refunds from SSTORE nonzero -> zero
 | ||||
| 		let sstore_refunds = U256::from(schedule.sstore_refund_gas) * substate.refunds_count; | ||||
| 		// refunds from contract suicides
 | ||||
| 		let suicide_refunds = U256::from(schedule.suicide_refund_gas) * U256::from(substate.suicides.len()); | ||||
| 
 | ||||
| 		// real ammount to refund
 | ||||
| 		let gas_left = match &result { &Ok(x) => x, _ => x!(0) }; | ||||
| 		let refund = cmp::min(sstore_refunds + suicide_refunds, (t.gas - gas_left) / U256::from(2)) + gas_left; | ||||
| 		let refund_value = refund * t.gas_price; | ||||
| 		trace!("Refunding sender: gas_left: {}, refund: {}, refund_value: {}, sender: {}", gas_left, refund, refund_value, t.sender().unwrap()); | ||||
| 		self.state.add_balance(&t.sender().unwrap(), &refund_value); | ||||
| 		
 | ||||
| 		// fees earned by author
 | ||||
| 		let fees = t.gas - refund; | ||||
| 		let fees_value = fees * t.gas_price; | ||||
| 		let author = &self.info.author; | ||||
| 		self.state.add_balance(author, &fees_value); | ||||
| 		trace!("Compensating author: fees: {}, fees_value: {}, author: {}", fees, fees_value, author); | ||||
| 
 | ||||
| 		// perform suicides
 | ||||
| 		for address in substate.suicides.iter() { | ||||
| 			trace!("Killing {}", address); | ||||
| 			self.state.kill_account(address); | ||||
| 		} | ||||
| 
 | ||||
| 		let gas_used = t.gas - gas_left; | ||||
| 
 | ||||
| 		match result { 
 | ||||
| 			Err(evm::Error::Internal) => Err(ExecutionError::Internal), | ||||
| 			// TODO [ToDr] BadJumpDestination @debris - how to handle that?
 | ||||
| @ -237,43 +301,41 @@ impl<'a> Executive<'a> { | ||||
| 					contracts_created: vec![] | ||||
| 				}) | ||||
| 			}, | ||||
| 			Ok(gas_left) => { | ||||
| 				let schedule = self.engine.schedule(self.info); | ||||
| 
 | ||||
| 				// refunds from SSTORE nonzero -> zero
 | ||||
| 				let sstore_refunds = U256::from(schedule.sstore_refund_gas) * substate.refunds_count; | ||||
| 				// refunds from contract suicides
 | ||||
| 				let suicide_refunds = U256::from(schedule.suicide_refund_gas) * U256::from(substate.suicides.len()); | ||||
| 
 | ||||
| 				// real ammount to refund
 | ||||
| 				let refund = cmp::min(sstore_refunds + suicide_refunds, (t.gas - gas_left) / U256::from(2)) + gas_left; | ||||
| 				let refund_value = refund * t.gas_price; | ||||
| 				self.state.add_balance(&t.sender().unwrap(), &refund_value); | ||||
| 				
 | ||||
| 				// fees earned by author
 | ||||
| 				let fees = t.gas - refund; | ||||
| 				let fees_value = fees * t.gas_price; | ||||
| 				let author = &self.info.author; | ||||
| 				self.state.add_balance(author, &fees_value); | ||||
| 
 | ||||
| 				// perform suicides
 | ||||
| 				for address in substate.suicides.iter() { | ||||
| 					self.state.kill_account(address); | ||||
| 				} | ||||
| 
 | ||||
| 				let gas_used = t.gas - gas_left; | ||||
| 			Ok(_) => { | ||||
| 				Ok(Executed { | ||||
| 					gas: t.gas, | ||||
| 					gas_used: gas_used, | ||||
| 					refunded: refund, | ||||
| 					cumulative_gas_used: self.info.gas_used + gas_used, | ||||
| 					logs: substate.logs, | ||||
| 					out_of_gas: false, | ||||
| 					excepted: substate.excepted, | ||||
| 					contracts_created: substate.contracts_created | ||||
| 				}) | ||||
| 			}, | ||||
| 			_err => { | ||||
| 				Ok(Executed { | ||||
| 					gas: t.gas, | ||||
| 					gas_used: t.gas, | ||||
| 					refunded: U256::zero(), | ||||
| 					cumulative_gas_used: self.info.gas_used + t.gas, | ||||
| 					logs: vec![], | ||||
| 					excepted: true, | ||||
| 					contracts_created: vec![] | ||||
| 				}) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	fn enact_result(&mut self, result: &evm::Result, substate: &mut Substate, un_substate: Substate, backup: State) { | ||||
| 		// TODO: handle other evm::Errors same as OutOfGas once they are implemented
 | ||||
| 		match result { | ||||
| 			&Err(evm::Error::OutOfGas) => { | ||||
| 				substate.excepted = true; | ||||
| 				self.state.revert(backup); | ||||
| 			}, | ||||
| 			&Ok(_) | &Err(evm::Error::Internal) => substate.accrue(un_substate) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /// Policy for handling output data on `RETURN` opcode.
 | ||||
| @ -357,10 +419,10 @@ impl<'a> Ext for Externalities<'a> { | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> Result<(U256, Option<Address>), evm::Error> { | ||||
| 	fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> (U256, Option<Address>) { | ||||
| 		// if balance is insufficient or we are to deep, return
 | ||||
| 		if self.state.balance(&self.params.address) < *value || self.depth >= self.schedule.max_depth { | ||||
| 			return Ok((*gas, None)); | ||||
| 			return (*gas, None); | ||||
| 		} | ||||
| 
 | ||||
| 		// create new contract address
 | ||||
| @ -380,10 +442,20 @@ impl<'a> Ext for Externalities<'a> { | ||||
| 
 | ||||
| 		let mut ex = Executive::from_parent(self.state, self.info, self.engine, self.depth); | ||||
| 		ex.state.inc_nonce(&self.params.address); | ||||
| 		ex.create(¶ms, self.substate).map(|gas_left| (gas_left, Some(address))) | ||||
| 		match ex.create(¶ms, self.substate) { | ||||
| 			Ok(gas_left) => (gas_left, Some(address)), | ||||
| 			_ => (U256::zero(), None) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	fn call(&mut self, gas: &U256, call_gas: &U256, receive_address: &Address, value: &U256, data: &[u8], code_address: &Address, output: &mut [u8]) -> Result<U256, evm::Error> { | ||||
| 	fn call(&mut self, 
 | ||||
| 			gas: &U256, 
 | ||||
| 			call_gas: &U256, 
 | ||||
| 			receive_address: &Address, 
 | ||||
| 			value: &U256, 
 | ||||
| 			data: &[u8], 
 | ||||
| 			code_address: &Address, 
 | ||||
| 			output: &mut [u8]) -> Result<(U256, bool), evm::Error> { | ||||
| 		let mut gas_cost = *call_gas; | ||||
| 		let mut call_gas = *call_gas; | ||||
| 
 | ||||
| @ -399,14 +471,14 @@ impl<'a> Ext for Externalities<'a> { | ||||
| 		} | ||||
| 
 | ||||
| 		if gas_cost > *gas { | ||||
| 			return Err(evm::Error::OutOfGas) | ||||
| 			return Err(evm::Error::OutOfGas); | ||||
| 		} | ||||
| 
 | ||||
| 		let gas = *gas - gas_cost; | ||||
| 
 | ||||
| 		// if balance is insufficient or we are to deep, return
 | ||||
| 		if self.state.balance(&self.params.address) < *value || self.depth >= self.schedule.max_depth { | ||||
| 			return Ok(gas + call_gas) | ||||
| 			return Ok((gas + call_gas, true)); | ||||
| 		} | ||||
| 
 | ||||
| 		let params = ActionParams { | ||||
| @ -421,7 +493,10 @@ impl<'a> Ext for Externalities<'a> { | ||||
| 		}; | ||||
| 
 | ||||
| 		let mut ex = Executive::from_parent(self.state, self.info, self.engine, self.depth); | ||||
| 		ex.call(¶ms, self.substate, BytesRef::Fixed(output)).map(|gas_left| gas + gas_left) | ||||
| 		match ex.call(¶ms, self.substate, BytesRef::Fixed(output)) { | ||||
| 			Ok(gas_left) => Ok((gas + gas_left, true)), | ||||
| 			_ => Ok((gas, false)) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	fn extcode(&self, address: &Address) -> Vec<u8> { | ||||
| @ -846,7 +921,7 @@ mod tests { | ||||
| 		assert_eq!(executed.refunded, U256::from(58_699)); | ||||
| 		assert_eq!(executed.cumulative_gas_used, U256::from(41_301)); | ||||
| 		assert_eq!(executed.logs.len(), 0); | ||||
| 		assert_eq!(executed.out_of_gas, false); | ||||
| 		assert_eq!(executed.excepted, false); | ||||
| 		assert_eq!(executed.contracts_created.len(), 0); | ||||
| 		assert_eq!(state.balance(&sender), U256::from(1)); | ||||
| 		assert_eq!(state.balance(&contract), U256::from(17)); | ||||
|  | ||||
							
								
								
									
										15
									
								
								src/lib.rs
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								src/lib.rs
									
									
									
									
									
								
							| @ -73,18 +73,19 @@ | ||||
| //!       sudo ldconfig
 | ||||
| //!       ```
 | ||||
| 
 | ||||
| #[macro_use] extern crate log; | ||||
| #[macro_use] | ||||
| extern crate log; | ||||
| extern crate rustc_serialize; | ||||
| #[macro_use] extern crate itertools; | ||||
| extern crate flate2; | ||||
| extern crate rocksdb; | ||||
| extern crate heapsize; | ||||
| extern crate crypto; | ||||
| extern crate time; | ||||
| extern crate env_logger; | ||||
| #[cfg(feature = "jit" )] extern crate evmjit; | ||||
| 
 | ||||
| #[macro_use] extern crate ethcore_util as util; | ||||
| #[cfg(feature = "jit" )] | ||||
| extern crate evmjit; | ||||
| #[macro_use] | ||||
| extern crate ethcore_util as util; | ||||
| 
 | ||||
| pub mod common; | ||||
| pub mod basic_types; | ||||
| @ -94,6 +95,10 @@ pub mod executive; | ||||
| pub mod error; | ||||
| pub mod log_entry; | ||||
| pub mod env_info; | ||||
| pub mod pod_account; | ||||
| pub mod pod_state; | ||||
| pub mod account_diff; | ||||
| pub mod state_diff; | ||||
| pub mod engine; | ||||
| pub mod state; | ||||
| pub mod account; | ||||
|  | ||||
| @ -28,16 +28,6 @@ impl LogEntry { | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/// Convert given JSON object to a LogEntry.
 | ||||
| 	pub fn from_json(json: &Json) -> LogEntry { | ||||
| 		// TODO: check bloom.
 | ||||
| 		LogEntry { | ||||
| 			address: address_from_json(&json["address"]), | ||||
| 			topics: vec_h256_from_json(&json["topics"]), | ||||
| 			data: bytes_from_json(&json["data"]), | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/// Returns reference to address.
 | ||||
| 	pub fn address(&self) -> &Address { | ||||
| 		&self.address | ||||
| @ -59,6 +49,18 @@ impl LogEntry { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl FromJson for LogEntry { | ||||
| 	/// Convert given JSON object to a LogEntry.
 | ||||
| 	fn from_json(json: &Json) -> LogEntry { | ||||
| 		// TODO: check bloom.
 | ||||
| 		LogEntry { | ||||
| 			address: xjson!(&json["address"]), | ||||
| 			topics: xjson!(&json["topics"]), | ||||
| 			data: xjson!(&json["data"]), | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
| 	use util::*; | ||||
|  | ||||
							
								
								
									
										116
									
								
								src/pod_account.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								src/pod_account.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,116 @@ | ||||
| use util::*; | ||||
| use account::*; | ||||
| 
 | ||||
| #[derive(Debug,Clone,PartialEq,Eq)] | ||||
| /// Genesis account data. Does not have a DB overlay cache.
 | ||||
| pub struct PodAccount { | ||||
| 	pub balance: U256, | ||||
| 	pub nonce: U256, | ||||
| 	pub code: Bytes, | ||||
| 	pub storage: BTreeMap<H256, H256>, | ||||
| } | ||||
| 
 | ||||
| impl PodAccount { | ||||
| 	/// Construct new object.
 | ||||
| 	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 { | ||||
| 			balance: acc.balance().clone(), | ||||
| 			nonce: acc.nonce().clone(), | ||||
| 			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(), | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn rlp(&self) -> Bytes { | ||||
| 		let mut stream = RlpStream::new_list(4); | ||||
| 		stream.append(&self.nonce); | ||||
| 		stream.append(&self.balance); | ||||
| 		// TODO.
 | ||||
| 		stream.append(&SHA3_NULL_RLP); | ||||
| 		stream.append(&self.code.sha3()); | ||||
| 		stream.out() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 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()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod test { | ||||
| 	use common::*; | ||||
| 	use account_diff::*; | ||||
| 	use super::*; | ||||
| 
 | ||||
| 	#[test] | ||||
| 	fn existence() { | ||||
| 		let a = PodAccount{balance: x!(69), nonce: x!(0), code: vec![], storage: map![]}; | ||||
| 		assert_eq!(AccountDiff::diff_pod(Some(&a), Some(&a)), None); | ||||
| 		assert_eq!(AccountDiff::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!(AccountDiff::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!(AccountDiff::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!(AccountDiff::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)) | ||||
| 			], | ||||
| 		})); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										46
									
								
								src/pod_state.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/pod_state.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | ||||
| use util::*; | ||||
| use pod_account::*; | ||||
| 
 | ||||
| #[derive(Debug,Clone,PartialEq,Eq)] | ||||
| pub struct PodState (BTreeMap<Address, PodAccount>); | ||||
| 
 | ||||
| impl PodState { | ||||
| 	/// Contruct a new object from the `m`.
 | ||||
| 	pub fn new(m: BTreeMap<Address, PodAccount>) -> PodState { PodState(m) } | ||||
| 
 | ||||
| 	/// Get the underlying map.
 | ||||
| 	pub fn get(&self) -> &BTreeMap<Address, PodAccount> { &self.0 } | ||||
| 
 | ||||
| 	/// Drain object to get the underlying map.
 | ||||
| 	pub fn drain(self) -> BTreeMap<Address, PodAccount> { self.0 } | ||||
| } | ||||
| 
 | ||||
| impl FromJson for PodState { | ||||
| 	/// Translate the JSON object into a hash map of account information ready for insertion into State.
 | ||||
| 	fn from_json(json: &Json) -> PodState { | ||||
| 		PodState(json.as_object().unwrap().iter().fold(BTreeMap::new(), |mut state, (address, acc)| { | ||||
| 			let balance = acc.find("balance").map(&U256::from_json); | ||||
| 			let nonce = acc.find("nonce").map(&U256::from_json); | ||||
| 			let storage = acc.find("storage").map(&BTreeMap::from_json); | ||||
| 			let code = acc.find("code").map(&Bytes::from_json); | ||||
| 			if balance.is_some() || nonce.is_some() || storage.is_some() || code.is_some() { | ||||
| 				state.insert(address_from_hex(address), PodAccount{ | ||||
| 					balance: balance.unwrap_or(U256::zero()), | ||||
| 					nonce: nonce.unwrap_or(U256::zero()), | ||||
| 					storage: storage.unwrap_or(BTreeMap::new()), | ||||
| 					code: code.unwrap_or(Vec::new()) | ||||
| 				}); | ||||
| 			} | ||||
| 			state | ||||
| 		})) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl fmt::Display for PodState { | ||||
| 	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
| 		for (add, acc) in &self.0 { | ||||
| 			try!(writeln!(f, "{} => {}", add, acc)); | ||||
| 		} | ||||
| 		Ok(()) | ||||
| 	} | ||||
| } | ||||
| @ -148,9 +148,11 @@ impl Spec { | ||||
| 		ret.append_raw(&empty_list, 1); | ||||
| 		ret.out() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl FromJson for Spec { | ||||
| 	/// Loads a chain-specification from a json data structure
 | ||||
| 	pub fn from_json(json: Json) -> Spec { | ||||
| 	fn from_json(json: &Json) -> Spec { | ||||
| 		// once we commit ourselves to some json parsing library (serde?)
 | ||||
| 		// move it to proper data structure
 | ||||
| 		let mut state = HashMap::new(); | ||||
| @ -210,7 +212,9 @@ impl Spec { | ||||
| 			state_root_memo: RwLock::new(genesis.find("stateRoot").and_then(|_| genesis["stateRoot"].as_string()).map(|s| H256::from_str(&s[2..]).unwrap())), | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl Spec { | ||||
| 	/// Ensure that the given state DB has the trie nodes in for the genesis state.
 | ||||
| 	pub fn ensure_db_good(&self, db: &mut HashDB) { | ||||
| 		if !db.contains(&self.state_root()) { | ||||
| @ -229,8 +233,7 @@ impl Spec { | ||||
| 
 | ||||
| 	/// Create a new Spec from a JSON string.
 | ||||
| 	pub fn from_json_str(s: &str) -> Spec { | ||||
| 		let json = Json::from_str(s).expect("Json is invalid"); | ||||
| 		Self::from_json(json) | ||||
| 		Self::from_json(&Json::from_str(s).expect("Json is invalid")) | ||||
| 	} | ||||
| 
 | ||||
| 	/// Create a new Spec which conforms to the Morden chain except that it's a NullEngine consensus.
 | ||||
|  | ||||
							
								
								
									
										11
									
								
								src/state.rs
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								src/state.rs
									
									
									
									
									
								
							| @ -1,6 +1,8 @@ | ||||
| use common::*; | ||||
| use engine::Engine; | ||||
| use executive::Executive; | ||||
| use pod_account::*; | ||||
| use pod_state::*; | ||||
| 
 | ||||
| pub type ApplyResult = Result<Receipt, Error>; | ||||
| 
 | ||||
| @ -125,7 +127,7 @@ impl State { | ||||
| 
 | ||||
| 	/// Mutate storage of account `a` so that it is `value` for `key`.
 | ||||
| 	pub fn set_storage(&mut self, a: &Address, key: H256, value: H256) { | ||||
| 		self.require(a, false).set_storage(key, value); | ||||
| 		self.require(a, false).set_storage(key, value) | ||||
| 	} | ||||
| 
 | ||||
| 	/// Initialise the code of account `a` so that it is `value` for `key`.
 | ||||
| @ -138,10 +140,15 @@ impl State { | ||||
| 	/// This will change the state accordingly.
 | ||||
| 	pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, t: &Transaction) -> ApplyResult { | ||||
| 		let e = try!(Executive::new(self, env_info, engine).transact(t)); | ||||
| 		//println!("Executed: {:?}", e);
 | ||||
| 		self.commit(); | ||||
| 		Ok(Receipt::new(self.root().clone(), e.gas_used, e.logs)) | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn revert(&mut self, backup: State) { | ||||
| 		self.cache = backup.cache; | ||||
| 	} | ||||
| 
 | ||||
| 	/// Convert into a JSON representation.
 | ||||
| 	pub fn as_json(&self) -> String { | ||||
| 		unimplemented!(); | ||||
| @ -201,7 +208,7 @@ impl State { | ||||
| 	/// Populate a PodAccount map from this state.
 | ||||
| 	pub fn to_pod(&self) -> PodState { | ||||
| 		// TODO: handle database rather than just the cache.
 | ||||
| 		PodState::from(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| { | ||||
| 		PodState::new(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| { | ||||
| 			if let &Some(ref acc) = opt { | ||||
| 				m.insert(add.clone(), PodAccount::from_account(acc)); | ||||
| 			} | ||||
|  | ||||
							
								
								
									
										98
									
								
								src/state_diff.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								src/state_diff.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,98 @@ | ||||
| use util::*; | ||||
| use pod_state::*; | ||||
| use account_diff::*; | ||||
| 
 | ||||
| #[derive(Debug,Clone,PartialEq,Eq)] | ||||
| pub struct StateDiff (BTreeMap<Address, AccountDiff>); | ||||
| 
 | ||||
| impl StateDiff { | ||||
| 	/// Calculate and return diff between `pre` state and `post` state.
 | ||||
| 	pub fn diff_pod(pre: &PodState, post: &PodState) -> StateDiff { | ||||
| 		StateDiff(pre.get().keys().merge(post.get().keys()).filter_map(|acc| AccountDiff::diff_pod(pre.get().get(acc), post.get().get(acc)).map(|d|(acc.clone(), d))).collect()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl fmt::Display for StateDiff { | ||||
| 	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
| 		for (add, acc) in self.0.iter() { | ||||
| 			try!(write!(f, "{} {}: {}", acc.existance(), add, acc)); | ||||
| 		} | ||||
| 		Ok(()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod test { | ||||
| 	use common::*; | ||||
| 	use pod_state::*; | ||||
| 	use account_diff::*; | ||||
| 	use pod_account::*; | ||||
| 	use super::*; | ||||
| 
 | ||||
| 	#[test] | ||||
| 	fn create_delete() { | ||||
| 		let a = PodState::new(map![ x!(1) => PodAccount::new(x!(69), x!(0), vec![], map![]) ]); | ||||
| 		assert_eq!(StateDiff::diff_pod(&a, &PodState::new(map![])), StateDiff(map![ | ||||
| 			x!(1) => AccountDiff{ | ||||
| 				balance: Diff::Died(x!(69)), | ||||
| 				nonce: Diff::Died(x!(0)), | ||||
| 				code: Diff::Died(vec![]), | ||||
| 				storage: map![], | ||||
| 			} | ||||
| 		])); | ||||
| 		assert_eq!(StateDiff::diff_pod(&PodState::new(map![]), &a), StateDiff(map![ | ||||
| 			x!(1) => AccountDiff{ | ||||
| 				balance: Diff::Born(x!(69)), | ||||
| 				nonce: Diff::Born(x!(0)), | ||||
| 				code: Diff::Born(vec![]), | ||||
| 				storage: map![], | ||||
| 			} | ||||
| 		])); | ||||
| 	} | ||||
| 
 | ||||
| 	#[test] | ||||
| 	fn create_delete_with_unchanged() { | ||||
| 		let a = PodState::new(map![ x!(1) => PodAccount::new(x!(69), x!(0), vec![], map![]) ]); | ||||
| 		let b = PodState::new(map![ | ||||
| 			x!(1) => PodAccount::new(x!(69), x!(0), vec![], map![]), | ||||
| 			x!(2) => PodAccount::new(x!(69), x!(0), vec![], map![]) | ||||
| 		]); | ||||
| 		assert_eq!(StateDiff::diff_pod(&a, &b), StateDiff(map![ | ||||
| 			x!(2) => AccountDiff{ | ||||
| 				balance: Diff::Born(x!(69)), | ||||
| 				nonce: Diff::Born(x!(0)), | ||||
| 				code: Diff::Born(vec![]), | ||||
| 				storage: map![], | ||||
| 			} | ||||
| 		])); | ||||
| 		assert_eq!(StateDiff::diff_pod(&b, &a), StateDiff(map![ | ||||
| 			x!(2) => AccountDiff{ | ||||
| 				balance: Diff::Died(x!(69)), | ||||
| 				nonce: Diff::Died(x!(0)), | ||||
| 				code: Diff::Died(vec![]), | ||||
| 				storage: map![], | ||||
| 			} | ||||
| 		])); | ||||
| 	} | ||||
| 
 | ||||
| 	#[test] | ||||
| 	fn change_with_unchanged() { | ||||
| 		let a = PodState::new(map![ | ||||
| 			x!(1) => PodAccount::new(x!(69), x!(0), vec![], map![]), | ||||
| 			x!(2) => PodAccount::new(x!(69), x!(0), vec![], map![]) | ||||
| 		]); | ||||
| 		let b = PodState::new(map![ | ||||
| 			x!(1) => PodAccount::new(x!(69), x!(1), vec![], map![]), | ||||
| 			x!(2) => PodAccount::new(x!(69), x!(0), vec![], map![]) | ||||
| 		]); | ||||
| 		assert_eq!(StateDiff::diff_pod(&a, &b), StateDiff(map![ | ||||
| 			x!(1) => AccountDiff{ | ||||
| 				balance: Diff::Same, | ||||
| 				nonce: Diff::Changed(x!(0), x!(1)), | ||||
| 				code: Diff::Same, | ||||
| 				storage: map![], | ||||
| 			} | ||||
| 		])); | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| @ -36,7 +36,7 @@ impl Engine for TestEngine { | ||||
| 
 | ||||
| struct CallCreate { | ||||
| 	data: Bytes, | ||||
| 	destination: Address, | ||||
| 	destination: Option<Address>, | ||||
| 	_gas_limit: U256, | ||||
| 	value: U256 | ||||
| } | ||||
| @ -74,33 +74,33 @@ impl<'a> Ext for TestExt<'a> { | ||||
| 		self.ext.blockhash(number) | ||||
| 	} | ||||
| 
 | ||||
| 	fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> Result<(U256, Option<Address>), evm::Error> { | ||||
| 	fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> (U256, Option<Address>) { | ||||
| 		// in call and create we need to check if we exited with insufficient balance or max limit reached.
 | ||||
| 		// in case of reaching max depth, we should store callcreates. Otherwise, ignore.
 | ||||
| 		let res = self.ext.create(gas, value, code); | ||||
| 		let ext = &self.ext; | ||||
| 		match res { | ||||
| 			// just record call create
 | ||||
| 			Ok((gas_left, Some(address))) => { | ||||
| 			(gas_left, Some(address)) => { | ||||
| 				self.callcreates.push(CallCreate { | ||||
| 					data: code.to_vec(), | ||||
| 					destination: address.clone(), | ||||
| 					destination: Some(address.clone()), | ||||
| 					_gas_limit: *gas, | ||||
| 					value: *value | ||||
| 				}); | ||||
| 				Ok((gas_left, Some(address))) | ||||
| 				(gas_left, Some(address)) | ||||
| 			}, | ||||
| 			// creation failed only due to reaching max_depth
 | ||||
| 			Ok((gas_left, None)) if ext.state.balance(&ext.params.address) >= *value => { | ||||
| 				let address = contract_address(&ext.params.address, &ext.state.nonce(&ext.params.address)); | ||||
| 			(gas_left, None) if ext.state.balance(&ext.params.address) >= *value => { | ||||
| 				self.callcreates.push(CallCreate { | ||||
| 					data: code.to_vec(), | ||||
| 					// TODO: address is not stored here?
 | ||||
| 					destination: Address::new(), | ||||
| 					// callcreate test does not need an address
 | ||||
| 					destination: None, | ||||
| 					_gas_limit: *gas, | ||||
| 					value: *value | ||||
| 				}); | ||||
| 				Ok((gas_left, Some(address))) | ||||
| 				let address = contract_address(&ext.params.address, &ext.state.nonce(&ext.params.address)); | ||||
| 				(gas_left, Some(address)) | ||||
| 			}, | ||||
| 			other => other | ||||
| 		} | ||||
| @ -113,21 +113,20 @@ impl<'a> Ext for TestExt<'a> { | ||||
| 			value: &U256, 
 | ||||
| 			data: &[u8], 
 | ||||
| 			code_address: &Address, 
 | ||||
| 			output: &mut [u8]) -> Result<U256, evm::Error> { | ||||
| 			output: &mut [u8]) -> Result<(U256, bool), evm::Error> { | ||||
| 		let res = self.ext.call(gas, call_gas, receive_address, value, data, code_address, output); | ||||
| 		let ext = &self.ext; | ||||
| 		match res { | ||||
| 			Ok(gas_left) if ext.state.balance(&ext.params.address) >= *value => { | ||||
| 		if let &Ok(_some) = &res { | ||||
| 			if ext.state.balance(&ext.params.address) >= *value { | ||||
| 				self.callcreates.push(CallCreate { | ||||
| 					data: data.to_vec(), | ||||
| 					destination: receive_address.clone(), | ||||
| 					destination: Some(receive_address.clone()), | ||||
| 					_gas_limit: *call_gas, | ||||
| 					value: *value | ||||
| 				}); | ||||
| 				Ok(gas_left) | ||||
| 			}, | ||||
| 			other => other | ||||
| 			} | ||||
| 		} | ||||
| 		res | ||||
| 	} | ||||
| 
 | ||||
| 	fn extcode(&self, address: &Address) -> Vec<u8> { | ||||
| @ -168,6 +167,7 @@ fn do_json_test_for(vm: &VMType, json_data: &[u8]) -> Vec<String> { | ||||
| 	let json = Json::from_str(::std::str::from_utf8(json_data).unwrap()).expect("Json is invalid"); | ||||
| 	let mut failed = Vec::new(); | ||||
| 	for (name, test) in json.as_object().unwrap() { | ||||
| 		println!("name: {:?}", name); | ||||
| 		// sync io is usefull when something crashes in jit
 | ||||
| 		// ::std::io::stdout().write(&name.as_bytes());
 | ||||
| 		// ::std::io::stdout().write(b"\n");
 | ||||
| @ -184,29 +184,24 @@ fn do_json_test_for(vm: &VMType, json_data: &[u8]) -> Vec<String> { | ||||
| 
 | ||||
| 		test.find("pre").map(|pre| for (addr, s) in pre.as_object().unwrap() { | ||||
| 			let address = Address::from(addr.as_ref()); | ||||
| 			let balance = u256_from_json(&s["balance"]); | ||||
| 			let code = bytes_from_json(&s["code"]); | ||||
| 			let _nonce = u256_from_json(&s["nonce"]); | ||||
| 			let balance = xjson!(&s["balance"]); | ||||
| 			let code = xjson!(&s["code"]); | ||||
| 			let _nonce: U256 = xjson!(&s["nonce"]); | ||||
| 
 | ||||
| 			state.new_contract(&address); | ||||
| 			state.add_balance(&address, &balance); | ||||
| 			state.init_code(&address, code); | ||||
| 
 | ||||
| 			for (k, v) in s["storage"].as_object().unwrap() { | ||||
| 				let key = H256::from(&u256_from_str(k)); | ||||
| 				let val = H256::from(&u256_from_json(v)); | ||||
| 				state.set_storage(&address, key, val); | ||||
| 			} | ||||
| 			BTreeMap::from_json(&s["storage"]).into_iter().foreach(|(k, v)| state.set_storage(&address, k, v)); | ||||
| 		}); | ||||
| 
 | ||||
| 		let mut info = EnvInfo::new(); | ||||
| 
 | ||||
| 		test.find("env").map(|env| { | ||||
| 			info.author = address_from_json(&env["currentCoinbase"]); | ||||
| 			info.difficulty = u256_from_json(&env["currentDifficulty"]); | ||||
| 			info.gas_limit = u256_from_json(&env["currentGasLimit"]); | ||||
| 			info.number = u256_from_json(&env["currentNumber"]).low_u64(); | ||||
| 			info.timestamp = u256_from_json(&env["currentTimestamp"]).low_u64(); | ||||
| 			info.author = xjson!(&env["currentCoinbase"]); | ||||
| 			info.difficulty = xjson!(&env["currentDifficulty"]); | ||||
| 			info.gas_limit = xjson!(&env["currentGasLimit"]); | ||||
| 			info.number = xjson!(&env["currentNumber"]); | ||||
| 			info.timestamp = xjson!(&env["currentTimestamp"]); | ||||
| 		}); | ||||
| 
 | ||||
| 		let engine = TestEngine::new(0, vm.clone()); | ||||
| @ -214,14 +209,14 @@ fn do_json_test_for(vm: &VMType, json_data: &[u8]) -> Vec<String> { | ||||
| 		// params
 | ||||
| 		let mut params = ActionParams::new(); | ||||
| 		test.find("exec").map(|exec| { | ||||
| 			params.address = address_from_json(&exec["address"]); | ||||
| 			params.sender = address_from_json(&exec["caller"]); | ||||
| 			params.origin = address_from_json(&exec["origin"]); | ||||
| 			params.code = bytes_from_json(&exec["code"]); | ||||
| 			params.data = bytes_from_json(&exec["data"]); | ||||
| 			params.gas = u256_from_json(&exec["gas"]); | ||||
| 			params.gas_price = u256_from_json(&exec["gasPrice"]); | ||||
| 			params.value = u256_from_json(&exec["value"]); | ||||
| 			params.address = xjson!(&exec["address"]); | ||||
| 			params.sender = xjson!(&exec["caller"]); | ||||
| 			params.origin = xjson!(&exec["origin"]); | ||||
| 			params.code = xjson!(&exec["code"]); | ||||
| 			params.data = xjson!(&exec["data"]); | ||||
| 			params.gas = xjson!(&exec["gas"]); | ||||
| 			params.gas_price = xjson!(&exec["gasPrice"]); | ||||
| 			params.value = xjson!(&exec["value"]); | ||||
| 		}); | ||||
| 
 | ||||
| 		let out_of_gas = test.find("callcreates").map(|_calls| { | ||||
| @ -243,41 +238,33 @@ fn do_json_test_for(vm: &VMType, json_data: &[u8]) -> Vec<String> { | ||||
| 		match res { | ||||
| 			Err(_) => fail_unless(out_of_gas, "didn't expect to run out of gas."), | ||||
| 			Ok(gas_left) => { | ||||
| 				//println!("name: {}, gas_left : {:?}, expected: {:?}", name, gas_left, u256_from_json(&test["gas"]));
 | ||||
| 				//println!("name: {}, gas_left : {:?}, expected: {:?}", name, gas_left, U256::from(&test["gas"]));
 | ||||
| 				fail_unless(!out_of_gas, "expected to run out of gas."); | ||||
| 				fail_unless(gas_left == u256_from_json(&test["gas"]), "gas_left is incorrect"); | ||||
| 				fail_unless(output == bytes_from_json(&test["out"]), "output is incorrect"); | ||||
| 
 | ||||
| 				fail_unless(gas_left == xjson!(&test["gas"]), "gas_left is incorrect"); | ||||
| 				fail_unless(output == Bytes::from_json(&test["out"]), "output is incorrect"); | ||||
| 
 | ||||
| 				test.find("post").map(|pre| for (addr, s) in pre.as_object().unwrap() { | ||||
| 					let address = Address::from(addr.as_ref()); | ||||
| 					//let balance = u256_from_json(&s["balance"]);
 | ||||
| 
 | ||||
| 					fail_unless(state.code(&address).unwrap_or(vec![]) == bytes_from_json(&s["code"]), "code is incorrect"); | ||||
| 					fail_unless(state.balance(&address) == u256_from_json(&s["balance"]), "balance is incorrect"); | ||||
| 					fail_unless(state.nonce(&address) == u256_from_json(&s["nonce"]), "nonce is incorrect"); | ||||
| 
 | ||||
| 					for (k, v) in s["storage"].as_object().unwrap() { | ||||
| 						let key = H256::from(&u256_from_str(k)); | ||||
| 						let val = H256::from(&u256_from_json(v)); | ||||
| 
 | ||||
| 						fail_unless(state.storage_at(&address, &key) == val, "storage is incorrect"); | ||||
| 					} | ||||
| 					fail_unless(state.code(&address).unwrap_or(vec![]) == Bytes::from_json(&s["code"]), "code is incorrect"); | ||||
| 					fail_unless(state.balance(&address) == xjson!(&s["balance"]), "balance is incorrect"); | ||||
| 					fail_unless(state.nonce(&address) == xjson!(&s["nonce"]), "nonce is incorrect"); | ||||
| 					BTreeMap::from_json(&s["storage"]).iter().foreach(|(k, v)| fail_unless(&state.storage_at(&address, &k) == v, "storage is incorrect")); | ||||
| 				}); | ||||
| 
 | ||||
| 				let cc = test["callcreates"].as_array().unwrap(); | ||||
| 				fail_unless(callcreates.len() == cc.len(), "callcreates does not match"); | ||||
| 				for i in 0..cc.len() { | ||||
| 					let is = &callcreates[i]; | ||||
| 					let callcreate = &callcreates[i]; | ||||
| 					let expected = &cc[i]; | ||||
| 					fail_unless(is.data == bytes_from_json(&expected["data"]), "callcreates data is incorrect"); | ||||
| 					fail_unless(is.destination == address_from_json(&expected["destination"]), "callcreates destination is incorrect"); | ||||
| 					fail_unless(is.value == u256_from_json(&expected["value"]), "callcreates value is incorrect"); | ||||
| 					fail_unless(callcreate.data == Bytes::from_json(&expected["data"]), "callcreates data is incorrect"); | ||||
| 					fail_unless(callcreate.destination == xjson!(&expected["destination"]), "callcreates destination is incorrect"); | ||||
| 					fail_unless(callcreate.value == xjson!(&expected["value"]), "callcreates value is incorrect"); | ||||
| 
 | ||||
| 					// TODO: call_gas is calculated in externalities and is not exposed to TestExt.
 | ||||
| 					// maybe move it to it's own function to simplify calculation?
 | ||||
| 					//println!("name: {:?}, is {:?}, expected: {:?}", name, is.gas_limit, u256_from_json(&expected["gasLimit"]));
 | ||||
| 					//fail_unless(is.gas_limit == u256_from_json(&expected["gasLimit"]), "callcreates gas_limit is incorrect");
 | ||||
| 					//println!("name: {:?}, callcreate {:?}, expected: {:?}", name, callcreate.gas_limit, U256::from(&expected["gasLimit"]));
 | ||||
| 					//fail_unless(callcreate.gas_limit == U256::from(&expected["gasLimit"]), "callcreates gas_limit is incorrect");
 | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| @ -1,5 +1,7 @@ | ||||
| use super::test_common::*; | ||||
| use state::*; | ||||
| use pod_state::*; | ||||
| use state_diff::*; | ||||
| use ethereum; | ||||
| 
 | ||||
| fn do_json_test(json_data: &[u8]) -> Vec<String> { | ||||
| @ -9,13 +11,14 @@ fn do_json_test(json_data: &[u8]) -> Vec<String> { | ||||
| 	let engine = ethereum::new_frontier_test().to_engine().unwrap(); | ||||
| 
 | ||||
| 	for (name, test) in json.as_object().unwrap() { | ||||
| 		println!("name: {:?}", name); | ||||
| 		let mut fail = false; | ||||
| 		let mut fail_unless = |cond: bool| if !cond && !fail { failed.push(name.to_string()); fail = true; true } else {false}; | ||||
| 
 | ||||
| 		let t = Transaction::from_json(&test["transaction"]); | ||||
| 		let env = EnvInfo::from_json(&test["env"]); | ||||
| 		let _out = bytes_from_json(&test["out"]); | ||||
| 		let post_state_root = h256_from_json(&test["postStateRoot"]); | ||||
| 		let _out = Bytes::from_json(&test["out"]); | ||||
| 		let post_state_root = xjson!(&test["postStateRoot"]); | ||||
| 		let pre = PodState::from_json(&test["pre"]); | ||||
| 		let post = PodState::from_json(&test["post"]); | ||||
| 		let logs: Vec<_> = test["logs"].as_array().unwrap().iter().map(&LogEntry::from_json).collect(); | ||||
| @ -39,7 +42,7 @@ fn do_json_test(json_data: &[u8]) -> Vec<String> { | ||||
| 			let our_post = s.to_pod(); | ||||
| 			println!("Got:\n{}", our_post); | ||||
| 			println!("Expect:\n{}", post); | ||||
| 			println!("Diff ---expect -> +++got:\n{}", pod_state_diff(&post, &our_post)); | ||||
| 			println!("Diff ---expect -> +++got:\n{}", StateDiff::diff_pod(&post, &our_post)); | ||||
| 		} | ||||
| 
 | ||||
| 		if fail_unless(logs == r.logs) { | ||||
| @ -56,4 +59,7 @@ fn do_json_test(json_data: &[u8]) -> Vec<String> { | ||||
| } | ||||
| 
 | ||||
| declare_test!{StateTests_stExample, "StateTests/stExample"} | ||||
| declare_test!{StateTests_stBlockHashTest, "StateTests/stBlockHashTest"} | ||||
| declare_test!{StateTests_stLogTests, "StateTests/stLogTests"} | ||||
| declare_test!{StateTests_stCallCodes, "StateTests/stCallCodes"} | ||||
| declare_test_ignore!{StateTests_stCallCreateCallCodeTest, "StateTests/stCallCreateCallCodeTest"} | ||||
|  | ||||
| @ -14,23 +14,23 @@ fn do_json_test(json_data: &[u8]) -> Vec<String> { | ||||
| 			.and_then(|j| j.as_string()) | ||||
| 			.and_then(|s| BlockNumber::from_str(s).ok()) | ||||
| 			.unwrap_or(0) { x if x < 900000 => &old_schedule, _ => &new_schedule }; | ||||
| 		let rlp = bytes_from_json(&test["rlp"]); | ||||
| 		let rlp = Bytes::from_json(&test["rlp"]); | ||||
| 		let res = UntrustedRlp::new(&rlp).as_val().map_err(|e| From::from(e)).and_then(|t: Transaction| t.validate(schedule, schedule.have_delegate_call)); | ||||
| 		fail_unless(test.find("transaction").is_none() == res.is_err()); | ||||
| 		if let (Some(&Json::Object(ref tx)), Some(&Json::String(ref expect_sender))) = (test.find("transaction"), test.find("sender")) { | ||||
| 			let t = res.unwrap(); | ||||
| 			fail_unless(t.sender().unwrap() == address_from_hex(clean(expect_sender))); | ||||
| 			fail_unless(t.data == bytes_from_json(&tx["data"])); | ||||
| 			fail_unless(t.gas == u256_from_json(&tx["gasLimit"])); | ||||
| 			fail_unless(t.gas_price == u256_from_json(&tx["gasPrice"])); | ||||
| 			fail_unless(t.nonce == u256_from_json(&tx["nonce"])); | ||||
| 			fail_unless(t.value == u256_from_json(&tx["value"])); | ||||
| 			fail_unless(t.data == Bytes::from_json(&tx["data"])); | ||||
| 			fail_unless(t.gas == xjson!(&tx["gasLimit"])); | ||||
| 			fail_unless(t.gas_price == xjson!(&tx["gasPrice"])); | ||||
| 			fail_unless(t.nonce == xjson!(&tx["nonce"])); | ||||
| 			fail_unless(t.value == xjson!(&tx["value"])); | ||||
| 			if let Action::Call(ref to) = t.action { | ||||
| 				*ot.borrow_mut() = t.clone(); | ||||
| 				fail_unless(to == &address_from_json(&tx["to"])); | ||||
| 				fail_unless(to == &xjson!(&tx["to"])); | ||||
| 			} else { | ||||
| 				*ot.borrow_mut() = t.clone(); | ||||
| 				fail_unless(bytes_from_json(&tx["to"]).len() == 0); | ||||
| 				fail_unless(Bytes::from_json(&tx["to"]).len() == 0); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @ -32,15 +32,15 @@ pub struct Transaction { | ||||
| impl Transaction { | ||||
| 	pub fn new() -> Self { | ||||
| 		Transaction { | ||||
| 			nonce: U256::zero(), | ||||
| 			gas_price: U256::zero(), | ||||
| 			gas: U256::zero(), | ||||
| 			nonce: x!(0), | ||||
| 			gas_price: x!(0), | ||||
| 			gas: x!(0), | ||||
| 			action: Action::Create, | ||||
| 			value: U256::zero(), | ||||
| 			value: x!(0), | ||||
| 			data: vec![], | ||||
| 			v: 0, | ||||
| 			r: U256::zero(), | ||||
| 			s: U256::zero(), | ||||
| 			r: x!(0), | ||||
| 			s: x!(0), | ||||
| 			hash: RefCell::new(None), | ||||
| 			sender: RefCell::new(None), | ||||
| 		} | ||||
| @ -55,8 +55,8 @@ impl Transaction { | ||||
| 			value: value, | ||||
| 			data: data, | ||||
| 			v: 0, | ||||
| 			r: U256::zero(), | ||||
| 			s: U256::zero(), | ||||
| 			r: x!(0), | ||||
| 			s: x!(0), | ||||
| 			hash: RefCell::new(None), | ||||
| 			sender: RefCell::new(None), | ||||
| 		} | ||||
| @ -72,39 +72,13 @@ impl Transaction { | ||||
| 			value: value, | ||||
| 			data: data, | ||||
| 			v: 0, | ||||
| 			r: U256::zero(), | ||||
| 			s: U256::zero(), | ||||
| 			r: x!(0), | ||||
| 			s: x!(0), | ||||
| 			hash: RefCell::new(None), | ||||
| 			sender: RefCell::new(None), | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn from_json(json: &Json) -> Transaction { | ||||
| 		let mut r = Transaction { | ||||
| 			nonce: u256_from_json(&json["nonce"]), | ||||
| 			gas_price: u256_from_json(&json["gasPrice"]), | ||||
| 			gas: u256_from_json(&json["gasLimit"]), | ||||
| 			action: match bytes_from_json(&json["to"]) { | ||||
| 				ref x if x.len() == 0 => Action::Create, | ||||
| 				ref x => Action::Call(Address::from_slice(x)), | ||||
| 			}, | ||||
| 			value: u256_from_json(&json["value"]), | ||||
| 			data: bytes_from_json(&json["data"]), | ||||
| 			v: match json.find("v") { Some(ref j) => u8_from_json(j), None => 0 }, | ||||
| 			r: match json.find("r") { Some(ref j) => u256_from_json(j), None => U256::zero() }, | ||||
| 			s: match json.find("s") { Some(ref j) => u256_from_json(j), None => U256::zero() }, | ||||
| 			hash: RefCell::new(None), | ||||
| 			sender: match json.find("sender") { | ||||
| 				Some(&Json::String(ref sender)) => RefCell::new(Some(address_from_hex(clean(sender)))), | ||||
| 				_ => RefCell::new(None), | ||||
| 			}, | ||||
| 		}; | ||||
| 		if let Some(&Json::String(ref secret_key)) = json.find("secretKey") { | ||||
| 			r.sign(&h256_from_hex(clean(secret_key))); | ||||
| 		} | ||||
| 		r | ||||
| 	} | ||||
| 
 | ||||
| 	/// Get the nonce of the transaction.
 | ||||
| 	pub fn nonce(&self) -> &U256 { &self.nonce } | ||||
| 	/// Get the gas price of the transaction.
 | ||||
| @ -144,6 +118,34 @@ impl Transaction { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl FromJson for Transaction { | ||||
| 	fn from_json(json: &Json) -> Transaction { | ||||
| 		let mut r = Transaction { | ||||
| 			nonce: xjson!(&json["nonce"]), | ||||
| 			gas_price: xjson!(&json["gasPrice"]), | ||||
| 			gas: xjson!(&json["gasLimit"]), | ||||
| 			action: match Bytes::from_json(&json["to"]) { | ||||
| 				ref x if x.len() == 0 => Action::Create, | ||||
| 				ref x => Action::Call(Address::from_slice(x)), | ||||
| 			}, | ||||
| 			value: xjson!(&json["value"]), | ||||
| 			data: xjson!(&json["data"]), | ||||
| 			v: match json.find("v") { Some(ref j) => u16::from_json(j) as u8, None => 0 }, | ||||
| 			r: match json.find("r") { Some(j) => xjson!(j), None => x!(0) }, | ||||
| 			s: match json.find("s") { Some(j) => xjson!(j), None => x!(0) }, | ||||
| 			hash: RefCell::new(None), | ||||
| 			sender: match json.find("sender") { | ||||
| 				Some(&Json::String(ref sender)) => RefCell::new(Some(address_from_hex(clean(sender)))), | ||||
| 				_ => RefCell::new(None), | ||||
| 			}, | ||||
| 		}; | ||||
| 		if let Some(&Json::String(ref secret_key)) = json.find("secretKey") { | ||||
| 			r.sign(&h256_from_hex(clean(secret_key))); | ||||
| 		} | ||||
| 		r | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl RlpStandard for Transaction { | ||||
| 	fn rlp_append(&self, s: &mut RlpStream) { self.rlp_append_opt(s, Seal::With) } | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user