Merge branch 'master' into tracing

This commit is contained in:
Gav Wood
2016-03-20 16:30:59 +01:00
67 changed files with 1374 additions and 288 deletions

View File

@@ -452,7 +452,7 @@ mod tests {
open_block.push_uncle(uncle1_header).unwrap();
open_block.push_uncle(uncle2_header).unwrap();
let b = open_block.close().seal(engine.deref(), vec![]).unwrap();
let orig_bytes = b.rlp_bytes();
let orig_db = b.drain();

View File

@@ -19,7 +19,7 @@ use crypto::sha2::Sha256;
use crypto::ripemd160::Ripemd160;
use crypto::digest::Digest;
/// Definition of a contract whose implementation is built-in.
/// Definition of a contract whose implementation is built-in.
pub struct Builtin {
/// The gas cost of running this built-in for the given size of input data.
pub cost: Box<Fn(usize) -> U256>, // TODO: U256 should be bignum.
@@ -63,14 +63,16 @@ impl Builtin {
/// Create a builtin from JSON.
///
/// JSON must be of the form `{ "name": "identity", "linear": {"base": 10, "word": 20} }`.
/// JSON must be of the form `{ "name": "identity", "pricing": {"base": 10, "word": 20} }`.
pub fn from_json(json: &Json) -> Option<Builtin> {
// NICE: figure out a more convenient means of handing errors here.
if let Json::String(ref name) = json["name"] {
if let Json::Object(ref o) = json["linear"] {
if let Json::U64(ref word) = o["word"] {
if let Json::U64(ref base) = o["base"] {
return Self::from_named_linear(&name[..], *base as usize, *word as usize);
if let Json::Object(ref o) = json["pricing"] {
if let Json::Object(ref o) = o["linear"] {
if let Json::U64(ref word) = o["word"] {
if let Json::U64(ref base) = o["base"] {
return Self::from_named_linear(&name[..], *base as usize, *word as usize);
}
}
}
}
@@ -274,7 +276,7 @@ fn from_named_linear() {
#[test]
fn from_json() {
let text = "{ \"name\": \"identity\", \"linear\": {\"base\": 10, \"word\": 20} }";
let text = r#"{"name": "identity", "pricing": {"linear": {"base": 10, "word": 20}}}"#;
let json = Json::from_str(text).unwrap();
let b = Builtin::from_json(&json).unwrap();
assert_eq!((*b.cost)(0), U256::from(10));

View File

@@ -37,6 +37,8 @@ use log_entry::LocalizedLogEntry;
use block_queue::{BlockQueue, BlockQueueInfo};
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
use client::{BlockId, TransactionId, ClientConfig, BlockChainClient};
use env_info::EnvInfo;
use executive::{Executive, Executed};
pub use blockchain::CacheSize as BlockChainCacheSize;
/// General block status
@@ -385,6 +387,29 @@ impl<V> Client<V> where V: Verifier {
}
impl<V> BlockChainClient for Client<V> where V: Verifier {
fn call(&self, t: &SignedTransaction) -> Result<Executed, Error> {
let header = self.block_header(BlockId::Latest).unwrap();
let view = HeaderView::new(&header);
let last_hashes = self.build_last_hashes(view.hash());
let env_info = EnvInfo {
number: view.number(),
author: view.author(),
timestamp: view.timestamp(),
difficulty: view.difficulty(),
last_hashes: last_hashes,
gas_used: U256::zero(),
gas_limit: U256::max_value(),
};
// that's just a copy of the state.
let mut state = self.state();
let sender = try!(t.sender());
let balance = state.balance(&sender);
// give the sender max balance
state.sub_balance(&sender, &balance);
state.add_balance(&sender, &U256::max_value());
Executive::new(&mut state, &env_info, self.engine.deref().deref()).transact(t, false)
}
// TODO [todr] Should be moved to miner crate eventually.
fn try_seal(&self, block: ClosedBlock, seal: Vec<Bytes>) -> Result<SealedBlock, ClosedBlock> {
block.try_seal(self.engine.deref().deref(), seal)

View File

@@ -25,6 +25,7 @@ pub use self::client::*;
pub use self::config::{ClientConfig, BlockQueueConfig, BlockChainConfig};
pub use self::ids::{BlockId, TransactionId};
pub use self::test_client::{TestBlockChainClient, EachBlockWith};
pub use executive::Executed;
use std::collections::HashSet;
use util::bytes::Bytes;
@@ -37,7 +38,7 @@ use header::BlockNumber;
use transaction::{LocalizedTransaction, SignedTransaction};
use log_entry::LocalizedLogEntry;
use filter::Filter;
use error::{ImportResult};
use error::{ImportResult, Error};
/// Blockchain database client. Owns and manages a blockchain and a block queue.
pub trait BlockChainClient : Sync + Send {
@@ -118,5 +119,7 @@ pub trait BlockChainClient : Sync + Send {
/// Attempts to seal given block. Returns `SealedBlock` on success and the same block in case of error.
fn try_seal(&self, block: ClosedBlock, seal: Vec<Bytes>) -> Result<SealedBlock, ClosedBlock>;
/// Makes a non-persistent transaction call.
fn call(&self, t: &SignedTransaction) -> Result<Executed, Error>;
}

View File

@@ -29,6 +29,8 @@ use error::{ImportResult};
use block_queue::BlockQueueInfo;
use block::{SealedBlock, ClosedBlock};
use executive::Executed;
use error::Error;
/// Test client.
pub struct TestBlockChainClient {
@@ -48,6 +50,8 @@ pub struct TestBlockChainClient {
pub storage: RwLock<HashMap<(Address, H256), H256>>,
/// Code.
pub code: RwLock<HashMap<Address, Bytes>>,
/// Execution result.
pub execution_result: RwLock<Option<Executed>>,
}
#[derive(Clone)]
@@ -82,12 +86,18 @@ impl TestBlockChainClient {
balances: RwLock::new(HashMap::new()),
storage: RwLock::new(HashMap::new()),
code: RwLock::new(HashMap::new()),
execution_result: RwLock::new(None),
};
client.add_blocks(1, EachBlockWith::Nothing); // add genesis block
client.genesis_hash = client.last_hash.read().unwrap().clone();
client
}
/// Set the execution result.
pub fn set_execution_result(&self, result: Executed) {
*self.execution_result.write().unwrap() = Some(result);
}
/// Set the balance of account `address` to `balance`.
pub fn set_balance(&self, address: Address, balance: U256) {
self.balances.write().unwrap().insert(address, balance);
@@ -182,6 +192,10 @@ impl TestBlockChainClient {
}
impl BlockChainClient for TestBlockChainClient {
fn call(&self, _t: &SignedTransaction) -> Result<Executed, Error> {
Ok(self.execution_result.read().unwrap().clone().unwrap())
}
fn block_total_difficulty(&self, _id: BlockId) -> Option<U256> {
Some(U256::zero())
}
@@ -219,11 +233,11 @@ impl BlockChainClient for TestBlockChainClient {
}
fn prepare_sealing(&self, _author: Address, _gas_floor_target: U256, _extra_data: Bytes, _transactions: Vec<SignedTransaction>) -> Option<(ClosedBlock, HashSet<H256>)> {
unimplemented!()
None
}
fn try_seal(&self, _block: ClosedBlock, _seal: Vec<Bytes>) -> Result<SealedBlock, ClosedBlock> {
unimplemented!()
fn try_seal(&self, block: ClosedBlock, _seal: Vec<Bytes>) -> Result<SealedBlock, ClosedBlock> {
Err(block)
}
fn block_header(&self, id: BlockId) -> Option<Bytes> {

View File

@@ -65,6 +65,10 @@ pub enum ExecutionError {
#[derive(Debug)]
/// Errors concerning transaction processing.
pub enum TransactionError {
/// Transaction is already imported to the queue
AlreadyImported,
/// Transaction is not valid anymore (state already has higher nonce)
Old,
/// Transaction's gas price is below threshold.
InsufficientGasPrice {
/// Minimal expected gas price

View File

@@ -37,25 +37,25 @@ pub fn contract_address(address: &Address, nonce: &U256) -> Address {
}
/// Transaction execution receipt.
#[derive(Debug)]
#[derive(Debug, PartialEq, Clone)]
pub struct Executed {
/// Gas paid up front for execution of transaction.
pub gas: U256,
/// Gas used during execution of transaction.
pub gas_used: U256,
/// Gas refunded after the execution of transaction.
/// To get gas that was required up front, add `refunded` and `gas_used`.
pub refunded: U256,
/// Cumulative gas used in current block so far.
///
/// `cumulative_gas_used = gas_used(t0) + gas_used(t1) + ... gas_used(tn)`
///
/// where `tn` is current transaction.
pub cumulative_gas_used: U256,
/// Vector of logs generated by transaction.
pub logs: Vec<LogEntry>,
@@ -66,7 +66,8 @@ pub struct Executed {
///
/// B creation ends first, and it will be the first element of the vector.
pub contracts_created: Vec<Address>,
/// Transaction output.
pub output: Bytes,
/// The trace of this transaction.
pub trace: Option<Trace>,
}
@@ -152,7 +153,7 @@ impl<'a> Executive<'a> {
let mut substate = Substate::new(tracing);
let res = match t.action {
let (gas_left, output) = match t.action {
Action::Create => {
let new_address = contract_address(&sender, &nonce);
let params = ActionParams {
@@ -166,7 +167,7 @@ impl<'a> Executive<'a> {
code: Some(t.data.clone()),
data: None,
};
self.create(params, &mut substate)
(self.create(params, &mut substate), vec![])
},
Action::Call(ref address) => {
let params = ActionParams {
@@ -182,12 +183,12 @@ impl<'a> Executive<'a> {
};
// TODO: move output upstream
let mut out = vec![];
self.call(params, &mut substate, BytesRef::Flexible(&mut out))
(self.call(params, &mut substate, BytesRef::Flexible(&mut out)), out)
}
};
// finalize here!
Ok(try!(self.finalize(t, substate, res)))
Ok(try!(self.finalize(t, substate, gas_left, output)))
}
fn exec_vm(&mut self, params: ActionParams, unconfirmed_substate: &mut Substate, output_policy: OutputPolicy) -> evm::Result {
@@ -258,7 +259,7 @@ impl<'a> Executive<'a> {
self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output, trace_output.as_mut()))
};
// if there's tracing, make up trace_info's result with trace_output and some arithmetic.
// if there's tracing, make up trace_info's result with trace_output and some arithmetic.
if let Some((TraceAction::Call(ref mut c), _)) = trace_info {
c.result = res.as_ref().ok().map(|gas_left| (c.gas - *gas_left, trace_output.expect("trace_info is Some: qed")));
}
@@ -314,7 +315,7 @@ impl<'a> Executive<'a> {
}
/// Finalizes the transaction (does refunds and suicides).
fn finalize(&mut self, t: &SignedTransaction, substate: Substate, result: evm::Result) -> ExecutionResult {
fn finalize(&mut self, t: &SignedTransaction, substate: Substate, result: evm::Result, output: Bytes) -> ExecutionResult {
let schedule = self.engine.schedule(self.info);
// refunds from SSTORE nonzero -> zero
@@ -357,6 +358,7 @@ impl<'a> Executive<'a> {
cumulative_gas_used: self.info.gas_used + t.gas,
logs: vec![],
contracts_created: vec![],
output: output,
trace: trace,
})
},
@@ -368,6 +370,7 @@ impl<'a> Executive<'a> {
cumulative_gas_used: self.info.gas_used + gas_used,
logs: substate.logs,
contracts_created: substate.contracts_created,
output: output,
trace: trace,
})
},

View File

@@ -16,18 +16,19 @@
use super::test_common::*;
use client::{BlockChainClient, Client, ClientConfig};
use pod_state::*;
use block::Block;
use ethereum;
use tests::helpers::*;
use devtools::*;
use spec::Genesis;
use ethjson;
pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
init_log();
let json = Json::from_str(::std::str::from_utf8(json_data).unwrap()).expect("Json is invalid");
let tests = ethjson::blockchain::Test::load(json_data).unwrap();
let mut failed = Vec::new();
for (name, test) in json.as_object().unwrap() {
for (name, blockchain) in tests.deref() {
let mut fail = false;
{
let mut fail_unless = |cond: bool| if !cond && !fail {
@@ -39,37 +40,36 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
flush!(" - {}...", name);
let blocks: Vec<(Bytes, bool)> = test["blocks"].as_array().unwrap().iter().map(|e| (xjson!(&e["rlp"]), e.find("blockHeader").is_some())).collect();
let mut spec = match era {
ChainEra::Frontier => ethereum::new_frontier_test(),
ChainEra::Homestead => ethereum::new_homestead_test(),
};
let s = PodState::from_json(test.find("pre").unwrap());
spec.set_genesis_state(s);
spec.overwrite_genesis(test.find("genesisBlockHeader").unwrap());
let genesis = Genesis::from(blockchain.genesis());
let state = From::from(blockchain.pre_state.clone());
spec.set_genesis_state(state);
spec.overwrite_genesis_params(genesis);
assert!(spec.is_state_root_valid());
let genesis_hash = spec.genesis_header().hash();
assert_eq!(genesis_hash, H256::from_json(&test.find("genesisBlockHeader").unwrap()["hash"]));
let temp = RandomTempPath::new();
{
let client = Client::new(ClientConfig::default(), spec, temp.as_path(), IoChannel::disconnected()).unwrap();
assert_eq!(client.chain_info().best_block_hash, genesis_hash);
for (b, is_valid) in blocks.into_iter() {
for b in &blockchain.blocks_rlp() {
if Block::is_good(&b) {
let _ = client.import_block(b.clone());
client.flush_queue();
client.import_verified_blocks(&IoChannel::disconnected());
}
client.flush_queue();
let imported_ok = client.import_verified_blocks(&IoChannel::disconnected()) > 0;
assert_eq!(imported_ok, is_valid);
}
fail_unless(client.chain_info().best_block_hash == H256::from_json(&test["lastblockhash"]));
fail_unless(client.chain_info().best_block_hash == blockchain.best_block.clone().into());
}
}
if !fail {
flushln!("ok");
}
}
println!("!!! {:?} tests from failed.", failed.len());
failed
}

View File

@@ -83,6 +83,7 @@ extern crate time;
extern crate env_logger;
extern crate num_cpus;
extern crate crossbeam;
extern crate ethjson;
#[cfg(test)] extern crate ethcore_devtools as devtools;
#[cfg(feature = "jit" )] extern crate evmjit;
@@ -101,13 +102,13 @@ pub mod spec;
pub mod transaction;
pub mod views;
pub mod receipt;
pub mod pod_state;
mod common;
mod basic_types;
#[macro_use] mod evm;
mod env_info;
mod pod_account;
mod pod_state;
mod account_diff;
mod state_diff;
mod engine;

View File

@@ -17,6 +17,7 @@
use util::*;
use account::*;
use account_db::*;
use ethjson;
#[derive(Debug,Clone,PartialEq,Eq)]
/// An account, expressed as Plain-Old-Data (hence the name).
@@ -73,6 +74,22 @@ impl PodAccount {
}
}
impl From<ethjson::blockchain::Account> for PodAccount {
fn from(a: ethjson::blockchain::Account) -> Self {
PodAccount {
balance: a.balance.into(),
nonce: a.nonce.into(),
code: a.code.into(),
storage: a.storage.into_iter().fold(BTreeMap::new(), |mut acc, (key, value)| {
let key: U256 = key.into();
let value: U256 = value.into();
acc.insert(H256::from(key), H256::from(value));
acc
})
}
}
}
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())

View File

@@ -14,11 +14,14 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! State of all accounts in the system expressed in Plain Old Data.
use util::*;
use pod_account::*;
use ethjson;
#[derive(Debug,Clone,PartialEq,Eq,Default)]
/// State of all accounts in the system expressed in Plain Old Data.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct PodState (BTreeMap<Address, PodAccount>);
impl PodState {
@@ -64,6 +67,15 @@ impl FromJson for PodState {
}
}
impl From<ethjson::blockchain::State> for PodState {
fn from(s: ethjson::blockchain::State) -> PodState {
PodState(s.0.into_iter().fold(BTreeMap::new(), |mut acc, (key, value)| {
acc.insert(key.into(), PodAccount::from(value));
acc
}))
}
}
impl fmt::Display for PodState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (add, acc) in &self.0 {

View File

@@ -0,0 +1,91 @@
// 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/>.
use util::rlp::*;
use util::numbers::{Uint, U256};
use util::hash::{H64, Address, H256};
use ethjson;
/// Genesis seal type.
pub enum Seal {
/// Classic ethereum seal.
Ethereum {
/// Seal nonce.
nonce: H64,
/// Seal mix hash.
mix_hash: H256,
},
/// Generic seal.
Generic {
/// Number of seal fields.
fields: usize,
/// Seal rlp.
rlp: Vec<u8>,
},
}
/// Genesis components.
pub struct Genesis {
/// Seal.
pub seal: Seal,
/// Difficulty.
pub difficulty: U256,
/// Author.
pub author: Address,
/// Timestamp.
pub timestamp: u64,
/// Parent hash.
pub parent_hash: H256,
/// Gas limit.
pub gas_limit: U256,
/// Transactions root.
pub transactions_root: H256,
/// Receipts root.
pub receipts_root: H256,
/// State root.
pub state_root: Option<H256>,
/// Gas used.
pub gas_used: U256,
/// Extra data.
pub extra_data: Vec<u8>,
}
impl From<ethjson::spec::Genesis> for Genesis {
fn from(g: ethjson::spec::Genesis) -> Self {
Genesis {
seal: match (g.nonce, g.mix_hash) {
(Some(nonce), Some(mix_hash)) => Seal::Ethereum {
nonce: nonce.into(),
mix_hash: mix_hash.into(),
},
_ => Seal::Generic {
fields: g.seal_fields.unwrap(),
rlp: g.seal_rlp.unwrap().into(),
}
},
difficulty: g.difficulty.into(),
author: g.author.into(),
timestamp: g.timestamp.into(),
parent_hash: g.parent_hash.into(),
gas_limit: g.gas_limit.into(),
transactions_root: g.transactions_root.map_or_else(|| SHA3_NULL_RLP.clone(), Into::into),
receipts_root: g.receipts_root.map_or_else(|| SHA3_NULL_RLP.clone(), Into::into),
state_root: g.state_root.map(Into::into),
gas_used: g.gas_used.map_or_else(U256::zero, Into::into),
extra_data: g.extra_data.map_or_else(Vec::new, Into::into),
}
}
}

23
ethcore/src/spec/mod.rs Normal file
View File

@@ -0,0 +1,23 @@
// 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/>.
//! Blockchain params.
mod genesis;
pub mod spec;
pub use self::spec::*;
pub use self::genesis::Genesis;

View File

@@ -21,6 +21,8 @@ use engine::*;
use pod_state::*;
use null_engine::*;
use account_db::*;
use ethereum;
use super::genesis::{Seal as GenesisSeal, Genesis};
/// Convert JSON value to equivalent RLP representation.
// TODO: handle container types.
@@ -106,7 +108,7 @@ impl Spec {
pub fn to_engine(self) -> Result<Box<Engine>, Error> {
match self.engine_name.as_ref() {
"NullEngine" => Ok(NullEngine::new_boxed(self)),
"Ethash" => Ok(super::ethereum::Ethash::new_boxed(self)),
"Ethash" => Ok(ethereum::Ethash::new_boxed(self)),
_ => Err(Error::UnknownEngineName(self.engine_name.clone()))
}
}
@@ -197,6 +199,32 @@ impl Spec {
self.state_root_memo = RwLock::new(genesis.find("stateRoot").and_then(|_| Some(H256::from_json(&genesis["stateRoot"]))));
}
/// Overwrite the genesis components.
pub fn overwrite_genesis_params(&mut self, g: Genesis) {
let (seal_fields, seal_rlp) = match g.seal {
GenesisSeal::Generic { fields, rlp } => (fields, rlp),
GenesisSeal::Ethereum { nonce, mix_hash } => {
let mut s = RlpStream::new();
s.append(&mix_hash);
s.append(&nonce);
(2, s.out())
}
};
self.parent_hash = g.parent_hash;
self.transactions_root = g.transactions_root;
self.receipts_root = g.receipts_root;
self.author = g.author;
self.difficulty = g.difficulty;
self.gas_limit = g.gas_limit;
self.gas_used = g.gas_used;
self.timestamp = g.timestamp;
self.extra_data = g.extra_data;
self.seal_fields = seal_fields;
self.seal_rlp = seal_rlp;
self.state_root_memo = RwLock::new(g.state_root);
}
/// Alter the value of the genesis state.
pub fn set_genesis_state(&mut self, s: PodState) {
self.genesis_state = s;
@@ -304,7 +332,7 @@ impl Spec {
}
/// Create a new Spec which conforms to the Morden chain except that it's a NullEngine consensus.
pub fn new_test() -> Spec { Self::from_json_utf8(include_bytes!("../res/null_morden.json")) }
pub fn new_test() -> Spec { Self::from_json_utf8(include_bytes!("../../res/null_morden.json")) }
}
#[cfg(test)]

View File

@@ -256,6 +256,9 @@ impl<'a> HeaderView<'a> {
}
}
/// Returns header hash.
pub fn hash(&self) -> H256 { self.sha3() }
/// Returns raw rlp.
pub fn rlp(&self) -> &Rlp<'a> { &self.rlp }