Merge branch 'master' into periodic_snapshot

This commit is contained in:
Robert Habermeier
2016-09-05 14:28:28 +02:00
39 changed files with 6284 additions and 213 deletions

View File

@@ -16,11 +16,13 @@
//! Evm interface.
use common::*;
use std::{ops, cmp, fmt};
use util::{U128, U256, U512, Uint};
use action_params::ActionParams;
use evm::Ext;
/// Evm errors.
#[derive(Debug)]
#[derive(Debug, Clone, Copy)]
pub enum Error {
/// `OutOfGas` is returned when transaction execution runs out of gas.
/// The state should be reverted to the state from before the
@@ -63,6 +65,21 @@ pub enum Error {
Internal,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Error::*;
let message = match *self {
OutOfGas => "Out of gas",
BadJumpDestination { .. } => "Bad jump destination",
BadInstruction { .. } => "Bad instruction",
StackUnderflow { .. } => "Stack underflow",
OutOfStack { .. } => "Out of stack",
Internal => "Internal error",
};
message.fmt(f)
}
}
/// A specialized version of Result over EVM errors.
pub type Result<T> = ::std::result::Result<T, Error>;
@@ -193,53 +210,55 @@ pub trait Evm {
fn exec(&mut self, params: ActionParams, ext: &mut Ext) -> Result<GasLeft>;
}
#[test]
#[cfg(test)]
fn should_calculate_overflow_mul_shr_without_overflow() {
// given
let num = 1048576;
mod tests {
use util::{U256, Uint};
use super::CostType;
// when
let (res1, o1) = U256::from(num).overflow_mul_shr(U256::from(num), 20);
let (res2, o2) = num.overflow_mul_shr(num, 20);
#[test]
fn should_calculate_overflow_mul_shr_without_overflow() {
// given
let num = 1048576;
// then
assert_eq!(res1, U256::from(num));
assert!(!o1);
assert_eq!(res2, num);
assert!(!o2);
}
#[test]
#[cfg(test)]
fn should_calculate_overflow_mul_shr_with_overflow() {
// given
let max = ::std::u64::MAX;
let num1 = U256([max, max, max, max]);
let num2 = ::std::usize::MAX;
// when
let (res1, o1) = num1.overflow_mul_shr(num1, 256);
let (res2, o2) = num2.overflow_mul_shr(num2, 64);
// then
assert_eq!(res2, num2 - 1);
assert!(o2);
assert_eq!(res1, !U256::zero() - U256::one());
assert!(o1);
}
#[test]
#[cfg(test)]
fn should_validate_u256_to_usize_conversion() {
// given
let v = U256::from(::std::usize::MAX) + U256::from(1);
// when
let res = usize::from_u256(v);
// then
assert!(res.is_err());
// when
let (res1, o1) = U256::from(num).overflow_mul_shr(U256::from(num), 20);
let (res2, o2) = num.overflow_mul_shr(num, 20);
// then
assert_eq!(res1, U256::from(num));
assert!(!o1);
assert_eq!(res2, num);
assert!(!o2);
}
#[test]
fn should_calculate_overflow_mul_shr_with_overflow() {
// given
let max = u64::max_value();
let num1 = U256([max, max, max, max]);
let num2 = usize::max_value();
// when
let (res1, o1) = num1.overflow_mul_shr(num1, 256);
let (res2, o2) = num2.overflow_mul_shr(num2, 64);
// then
assert_eq!(res2, num2 - 1);
assert!(o2);
assert_eq!(res1, !U256::zero() - U256::one());
assert!(o1);
}
#[test]
fn should_validate_u256_to_usize_conversion() {
// given
let v = U256::from(usize::max_value()) + U256::from(1);
// when
let res = usize::from_u256(v);
// then
assert!(res.is_err());
}
}

View File

@@ -286,7 +286,7 @@ impl<'a> Executive<'a> {
// just drain the whole gas
self.state.revert_snapshot();
tracer.trace_failed_call(trace_info, vec![]);
tracer.trace_failed_call(trace_info, vec![], evm::Error::OutOfGas.into());
Err(evm::Error::OutOfGas)
}
@@ -320,7 +320,7 @@ impl<'a> Executive<'a> {
trace_output,
traces
),
_ => tracer.trace_failed_call(trace_info, traces),
Err(e) => tracer.trace_failed_call(trace_info, traces, e.into()),
};
trace!(target: "executive", "substate={:?}; unconfirmed_substate={:?}\n", substate, unconfirmed_substate);
@@ -385,7 +385,7 @@ impl<'a> Executive<'a> {
created,
subtracer.traces()
),
_ => tracer.trace_failed_create(trace_info, subtracer.traces())
Err(e) => tracer.trace_failed_create(trace_info, subtracer.traces(), e.into())
};
self.enact_result(&res, substate, unconfirmed_substate);

View File

@@ -340,4 +340,92 @@ impl SnapshotReader for LooseReader {
Ok(buf)
}
}
#[cfg(test)]
mod tests {
use devtools::RandomTempPath;
use util::sha3::Hashable;
use snapshot::ManifestData;
use super::{SnapshotWriter, SnapshotReader, PackedWriter, PackedReader, LooseWriter, LooseReader};
const STATE_CHUNKS: &'static [&'static [u8]] = &[b"dog", b"cat", b"hello world", b"hi", b"notarealchunk"];
const BLOCK_CHUNKS: &'static [&'static [u8]] = &[b"hello!", b"goodbye!", b"abcdefg", b"hijklmnop", b"qrstuvwxy", b"and", b"z"];
#[test]
fn packed_write_and_read() {
let path = RandomTempPath::new();
let mut writer = PackedWriter::new(path.as_path()).unwrap();
let mut state_hashes = Vec::new();
let mut block_hashes = Vec::new();
for chunk in STATE_CHUNKS {
let hash = chunk.sha3();
state_hashes.push(hash.clone());
writer.write_state_chunk(hash, chunk).unwrap();
}
for chunk in BLOCK_CHUNKS {
let hash = chunk.sha3();
block_hashes.push(hash.clone());
writer.write_block_chunk(chunk.sha3(), chunk).unwrap();
}
let manifest = ManifestData {
state_hashes: state_hashes,
block_hashes: block_hashes,
state_root: b"notarealroot".sha3(),
block_number: 12345678987654321,
block_hash: b"notarealblock".sha3(),
};
writer.finish(manifest.clone()).unwrap();
let reader = PackedReader::new(path.as_path()).unwrap().unwrap();
assert_eq!(reader.manifest(), &manifest);
for hash in manifest.state_hashes.iter().chain(&manifest.block_hashes) {
reader.chunk(hash.clone()).unwrap();
}
}
#[test]
fn loose_write_and_read() {
let path = RandomTempPath::new();
let mut writer = LooseWriter::new(path.as_path().into()).unwrap();
let mut state_hashes = Vec::new();
let mut block_hashes = Vec::new();
for chunk in STATE_CHUNKS {
let hash = chunk.sha3();
state_hashes.push(hash.clone());
writer.write_state_chunk(hash, chunk).unwrap();
}
for chunk in BLOCK_CHUNKS {
let hash = chunk.sha3();
block_hashes.push(hash.clone());
writer.write_block_chunk(chunk.sha3(), chunk).unwrap();
}
let manifest = ManifestData {
state_hashes: state_hashes,
block_hashes: block_hashes,
state_root: b"notarealroot".sha3(),
block_number: 12345678987654321,
block_hash: b"notarealblock".sha3(),
};
writer.finish(manifest.clone()).unwrap();
let reader = LooseReader::new(path.as_path().into()).unwrap();
assert_eq!(reader.manifest(), &manifest);
for hash in manifest.state_hashes.iter().chain(&manifest.block_hashes) {
reader.chunk(hash.clone()).unwrap();
}
}
}

View File

@@ -655,4 +655,4 @@ impl BlockRebuilder {
}
}
}
}
}

View File

@@ -59,7 +59,7 @@ impl Drop for Guard {
}
/// Statuses for restorations.
#[derive(PartialEq, Clone, Copy, Debug)]
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum RestorationStatus {
/// No restoration.
Inactive,
@@ -277,7 +277,7 @@ impl Service {
// delete the temporary snapshot dir if it does exist.
if let Err(e) = fs::remove_dir_all(service.temp_snapshot_dir()) {
if e.kind() != ErrorKind {
if e.kind() != ErrorKind::NotFound {
return Err(e.into())
}
}
@@ -581,3 +581,50 @@ impl SnapshotService for Service {
.expect("snapshot service and io service are kept alive by client service; qed");
}
}
#[cfg(test)]
mod tests {
use service::ClientIoMessage;
use io::{IoService};
use devtools::RandomTempPath;
use tests::helpers::get_test_spec;
use util::journaldb::Algorithm;
use snapshot::ManifestData;
use super::*;
#[test]
fn sends_async_messages() {
let service = IoService::<ClientIoMessage>::start().unwrap();
let dir = RandomTempPath::new();
let mut dir = dir.as_path().to_owned();
dir.push("pruning");
dir.push("db");
let service = Service::new(
&get_test_spec(),
Algorithm::Archive,
dir,
service.channel()
).unwrap();
assert!(service.manifest().is_none());
assert!(service.chunk(Default::default()).is_none());
assert_eq!(service.status(), RestorationStatus::Inactive);
assert_eq!(service.chunks_done(), (0, 0));
let manifest = ManifestData {
state_hashes: vec![],
block_hashes: vec![],
state_root: Default::default(),
block_number: 0,
block_hash: Default::default(),
};
service.begin_restore(manifest);
service.abort_restore();
service.restore_state_chunk(Default::default(), vec![]);
service.restore_block_chunk(Default::default(), vec![]);
}
}

View File

@@ -78,7 +78,7 @@ impl StateProducer {
let new_accs = rng.gen::<u32>() % 5;
for _ in 0..new_accs {
let address_hash = H256::random();
let address_hash = H256(rng.gen());
let balance: usize = rng.gen();
let nonce: usize = rng.gen();
let acc = ::state::Account::new_basic(balance.into(), nonce.into()).rlp();

View File

@@ -19,4 +19,19 @@
mod blocks;
mod state;
pub mod helpers;
pub mod helpers;
use super::ManifestData;
#[test]
fn manifest_rlp() {
let manifest = ManifestData {
block_hashes: Vec::new(),
state_hashes: Vec::new(),
block_number: 1234567,
state_root: Default::default(),
block_hash: Default::default(),
};
let raw = manifest.clone().into_rlp();
assert_eq!(ManifestData::from_rlp(&raw).unwrap(), manifest);
}

View File

@@ -20,7 +20,7 @@ use snapshot::{chunk_state, Progress, StateRebuilder};
use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter};
use super::helpers::{compare_dbs, StateProducer};
use rand;
use rand::{XorShiftRng, SeedableRng};
use util::hash::H256;
use util::journaldb::{self, Algorithm};
use util::kvdb::{Database, DatabaseConfig};
@@ -33,7 +33,7 @@ use std::sync::Arc;
#[test]
fn snap_and_restore() {
let mut producer = StateProducer::new();
let mut rng = rand::thread_rng();
let mut rng = XorShiftRng::from_seed([1, 2, 3, 4]);
let mut old_db = MemoryDB::new();
let db_cfg = DatabaseConfig::with_columns(::db::NUM_COLUMNS);

View File

@@ -444,8 +444,7 @@ use env_info::*;
use spec::*;
use transaction::*;
use util::log::init_log;
use trace::trace;
use trace::FlatTrace;
use trace::{FlatTrace, TraceError, trace};
use types::executed::CallType;
#[test]
@@ -538,7 +537,7 @@ fn should_trace_failed_create_transaction() {
gas: 78792.into(),
init: vec![91, 96, 0, 86],
}),
result: trace::Res::FailedCreate,
result: trace::Res::FailedCreate(TraceError::OutOfGas),
subtraces: 0
}];
@@ -869,7 +868,7 @@ fn should_trace_failed_call_transaction() {
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::FailedCall,
result: trace::Res::FailedCall(TraceError::OutOfGas),
subtraces: 0,
}];
@@ -1084,7 +1083,7 @@ fn should_trace_failed_subcall_transaction() {
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::FailedCall,
result: trace::Res::FailedCall(TraceError::OutOfGas),
}];
assert_eq!(result.trace, expected_trace);
@@ -1217,7 +1216,7 @@ fn should_trace_failed_subcall_with_subcall_transaction() {
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::FailedCall,
result: trace::Res::FailedCall(TraceError::OutOfGas),
}, FlatTrace {
trace_address: vec![0, 0].into_iter().collect(),
subtraces: 0,

View File

@@ -420,7 +420,7 @@ mod tests {
use devtools::RandomTempPath;
use header::BlockNumber;
use trace::{Config, Switch, TraceDB, Database as TraceDatabase, DatabaseExtras, ImportRequest};
use trace::{Filter, LocalizedTrace, AddressesFilter};
use trace::{Filter, LocalizedTrace, AddressesFilter, TraceError};
use trace::trace::{Call, Action, Res};
use trace::flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces};
use types::executed::CallType;
@@ -560,7 +560,7 @@ mod tests {
input: vec![],
call_type: CallType::Call,
}),
result: Res::FailedCall,
result: Res::FailedCall(TraceError::OutOfGas),
}])]),
block_hash: block_hash.clone(),
block_number: block_number,
@@ -579,7 +579,7 @@ mod tests {
input: vec![],
call_type: CallType::Call,
}),
result: Res::FailedCall,
result: Res::FailedCall(TraceError::OutOfGas),
trace_address: vec![],
subtraces: 0,
transaction_number: 0,

View File

@@ -19,7 +19,7 @@
use util::{Bytes, Address, U256};
use action_params::ActionParams;
use trace::trace::{Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, Suicide};
use trace::{Tracer, VMTracer, FlatTrace};
use trace::{Tracer, VMTracer, FlatTrace, TraceError};
/// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls.
#[derive(Default)]
@@ -112,23 +112,23 @@ impl Tracer for ExecutiveTracer {
self.traces.extend(update_trace_address(subs));
}
fn trace_failed_call(&mut self, call: Option<Call>, subs: Vec<FlatTrace>) {
fn trace_failed_call(&mut self, call: Option<Call>, subs: Vec<FlatTrace>, error: TraceError) {
let trace = FlatTrace {
trace_address: Default::default(),
subtraces: top_level_subtraces(&subs),
action: Action::Call(call.expect("self.prepare_trace_call().is_some(): so we must be tracing: qed")),
result: Res::FailedCall,
result: Res::FailedCall(error),
};
debug!(target: "trace", "Traced failed call {:?}", trace);
self.traces.push(trace);
self.traces.extend(update_trace_address(subs));
}
fn trace_failed_create(&mut self, create: Option<Create>, subs: Vec<FlatTrace>) {
fn trace_failed_create(&mut self, create: Option<Create>, subs: Vec<FlatTrace>, error: TraceError) {
let trace = FlatTrace {
subtraces: top_level_subtraces(&subs),
action: Action::Create(create.expect("self.prepare_trace_create().is_some(): so we must be tracing: qed")),
result: Res::FailedCreate,
result: Res::FailedCreate(error),
trace_address: Default::default(),
};
debug!(target: "trace", "Traced failed create {:?}", trace);

View File

@@ -24,7 +24,8 @@ mod executive_tracer;
mod import;
mod noop_tracer;
pub use types::trace_types::*;
pub use types::trace_types::{filter, flat, localized, trace};
pub use types::trace_types::error::Error as TraceError;
pub use self::config::{Config, Switch};
pub use self::db::TraceDB;
pub use self::error::Error;
@@ -71,10 +72,10 @@ pub trait Tracer: Send {
);
/// Stores failed call trace.
fn trace_failed_call(&mut self, call: Option<Call>, subs: Vec<FlatTrace>);
fn trace_failed_call(&mut self, call: Option<Call>, subs: Vec<FlatTrace>, error: TraceError);
/// Stores failed create trace.
fn trace_failed_create(&mut self, create: Option<Create>, subs: Vec<FlatTrace>);
fn trace_failed_create(&mut self, create: Option<Create>, subs: Vec<FlatTrace>, error: TraceError);
/// Stores suicide info.
fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address);

View File

@@ -18,7 +18,7 @@
use util::{Bytes, Address, U256};
use action_params::ActionParams;
use trace::{Tracer, VMTracer, FlatTrace};
use trace::{Tracer, VMTracer, FlatTrace, TraceError};
use trace::trace::{Call, Create, VMTrace};
/// Nonoperative tracer. Does not trace anything.
@@ -47,11 +47,11 @@ impl Tracer for NoopTracer {
assert!(code.is_none(), "self.prepare_trace_output().is_none(): so we can't be tracing: qed");
}
fn trace_failed_call(&mut self, call: Option<Call>, _: Vec<FlatTrace>) {
fn trace_failed_call(&mut self, call: Option<Call>, _: Vec<FlatTrace>, _: TraceError) {
assert!(call.is_none(), "self.prepare_trace_call().is_none(): so we can't be tracing: qed");
}
fn trace_failed_create(&mut self, create: Option<Create>, _: Vec<FlatTrace>) {
fn trace_failed_create(&mut self, create: Option<Create>, _: Vec<FlatTrace>, _: TraceError) {
assert!(create.is_none(), "self.prepare_trace_create().is_none(): so we can't be tracing: qed");
}

View File

@@ -0,0 +1,99 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Trace errors.
use std::fmt;
use rlp::{Encodable, RlpStream, Decodable, Decoder, DecoderError, Stream, View};
use evm::Error as EvmError;
/// Trace evm errors.
#[derive(Debug, PartialEq, Clone, Binary)]
pub enum Error {
/// `OutOfGas` is returned when transaction execution runs out of gas.
OutOfGas,
/// `BadJumpDestination` is returned when execution tried to move
/// to position that wasn't marked with JUMPDEST instruction
BadJumpDestination,
/// `BadInstructions` is returned when given instruction is not supported
BadInstruction,
/// `StackUnderflow` when there is not enough stack elements to execute instruction
StackUnderflow,
/// When execution would exceed defined Stack Limit
OutOfStack,
/// Returned on evm internal error. Should never be ignored during development.
/// Likely to cause consensus issues.
Internal,
}
impl From<EvmError> for Error {
fn from(e: EvmError) -> Self {
match e {
EvmError::OutOfGas => Error::OutOfGas,
EvmError::BadJumpDestination { .. } => Error::BadJumpDestination,
EvmError::BadInstruction { .. } => Error::BadInstruction,
EvmError::StackUnderflow { .. } => Error::StackUnderflow,
EvmError::OutOfStack { .. } => Error::OutOfStack,
EvmError::Internal => Error::Internal,
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Error::*;
let message = match *self {
OutOfGas => "Out of gas",
BadJumpDestination => "Bad jump destination",
BadInstruction => "Bad instruction",
StackUnderflow => "Stack underflow",
OutOfStack => "Out of stack",
Internal => "Internal error",
};
message.fmt(f)
}
}
impl Encodable for Error {
fn rlp_append(&self, s: &mut RlpStream) {
use self::Error::*;
let value = match *self {
OutOfGas => 0u8,
BadJumpDestination => 1,
BadInstruction => 2,
StackUnderflow => 3,
OutOfStack => 4,
Internal => 5,
};
s.append(&value);
}
}
impl Decodable for Error {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
use self::Error::*;
let value: u8 = try!(decoder.as_rlp().as_val());
match value {
0 => Ok(OutOfGas),
1 => Ok(BadJumpDestination),
2 => Ok(BadInstruction),
3 => Ok(StackUnderflow),
4 => Ok(OutOfStack),
5 => Ok(Internal),
_ => Err(DecoderError::Custom("Invalid error type")),
}
}
}

View File

@@ -140,7 +140,7 @@ mod tests {
use util::bloom::Bloomable;
use trace::trace::{Action, Call, Res, Create, CreateResult, Suicide};
use trace::flat::FlatTrace;
use trace::{Filter, AddressesFilter};
use trace::{Filter, AddressesFilter, TraceError};
use types::executed::CallType;
#[test]
@@ -286,7 +286,7 @@ mod tests {
input: vec![0x5],
call_type: CallType::Call,
}),
result: Res::FailedCall,
result: Res::FailedCall(TraceError::OutOfGas),
trace_address: vec![0].into_iter().collect(),
subtraces: 0,
};

View File

@@ -16,6 +16,7 @@
//! Types used in the public api
pub mod error;
pub mod filter;
pub mod flat;
pub mod trace;

View File

@@ -24,6 +24,7 @@ use rlp::*;
use action_params::ActionParams;
use basic_types::LogBloom;
use types::executed::CallType;
use super::error::Error;
/// `Call` result.
#[derive(Debug, Clone, PartialEq, Default, Binary)]
@@ -322,9 +323,9 @@ pub enum Res {
/// Successful create action result.
Create(CreateResult),
/// Failed call.
FailedCall,
FailedCall(Error),
/// Failed create.
FailedCreate,
FailedCreate(Error),
/// None
None,
}
@@ -342,13 +343,15 @@ impl Encodable for Res {
s.append(&1u8);
s.append(create);
},
Res::FailedCall => {
s.begin_list(1);
Res::FailedCall(ref err) => {
s.begin_list(2);
s.append(&2u8);
s.append(err);
},
Res::FailedCreate => {
s.begin_list(1);
Res::FailedCreate(ref err) => {
s.begin_list(2);
s.append(&3u8);
s.append(err);
},
Res::None => {
s.begin_list(1);
@@ -365,8 +368,8 @@ impl Decodable for Res {
match action_type {
0 => d.val_at(1).map(Res::Call),
1 => d.val_at(1).map(Res::Create),
2 => Ok(Res::FailedCall),
3 => Ok(Res::FailedCreate),
2 => d.val_at(1).map(Res::FailedCall),
3 => d.val_at(1).map(Res::FailedCreate),
4 => Ok(Res::None),
_ => Err(DecoderError::Custom("Invalid result type.")),
}
@@ -378,7 +381,7 @@ impl Res {
pub fn bloom(&self) -> LogBloom {
match *self {
Res::Create(ref create) => create.bloom(),
Res::Call(_) | Res::FailedCall | Res::FailedCreate | Res::None => Default::default(),
Res::Call(_) | Res::FailedCall(_) | Res::FailedCreate(_) | Res::None => Default::default(),
}
}
}