diff --git a/ethcore/res/null_morden.json b/ethcore/res/null_morden.json index b615cdc29..02b810765 100644 --- a/ethcore/res/null_morden.json +++ b/ethcore/res/null_morden.json @@ -8,7 +8,8 @@ "accountStartNonce": "0x0", "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", - "networkID" : "0x2" + "networkID" : "0x2", + "applyReward": false }, "genesis": { "seal": { diff --git a/ethcore/res/null_morden_with_reward.json b/ethcore/res/null_morden_with_reward.json new file mode 100644 index 000000000..724e58a7a --- /dev/null +++ b/ethcore/res/null_morden_with_reward.json @@ -0,0 +1,34 @@ +{ + "name": "Morden", + "engine": { + "null": null + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID" : "0x2", + "applyReward": true + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x00006d6f7264656e", + "mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578" + } + }, + "difficulty": "0x20000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x2fefd8" + }, + "accounts": { + "0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0000000000000000000000000000000000000002": { "balance": "1", "nonce": "1048576", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0000000000000000000000000000000000000003": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0000000000000000000000000000000000000004": { "balance": "1", "nonce": "1048576", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, + "102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" } + } +} diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index de04dd8bc..fa82c33e8 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -404,12 +404,12 @@ impl<'x> OpenBlock<'x> { let unclosed_state = s.block.state.clone(); match s.engine.on_close_block(&mut s.block) { - Ok(outcome) => { - let t = outcome.trace; - if t.is_some() { - s.block.traces.as_mut().map(|traces| traces.push(t.unwrap())); - } - } + Ok(outcome) => match outcome.trace { + Some(t) => { + s.block.traces.as_mut().map(|traces| traces.push(t)); + }, + None => {}, + }, Err(e) => warn!("Encountered error on closing the block: {}", e), } @@ -436,8 +436,14 @@ impl<'x> OpenBlock<'x> { pub fn close_and_lock(self) -> LockedBlock { let mut s = self; - if let Err(e) = s.engine.on_close_block(&mut s.block) { - warn!("Encountered error on closing the block: {}", e); + match s.engine.on_close_block(&mut s.block) { + Ok(outcome) => match outcome.trace { + Some(t) => { + s.block.traces.as_mut().map(|traces| traces.push(t)); + }, + None => {}, + }, + Err(e) => warn!("Encountered error on closing the block: {}", e), } if let Err(e) = s.block.state.commit() { diff --git a/ethcore/src/engines/null_engine.rs b/ethcore/src/engines/null_engine.rs index 3bdef480c..2e752709f 100644 --- a/ethcore/src/engines/null_engine.rs +++ b/ethcore/src/engines/null_engine.rs @@ -17,10 +17,16 @@ use std::collections::BTreeMap; use util::Address; use builtin::Builtin; -use engines::Engine; +use block::*; +use util::*; +use engines::{Engine, CloseOutcome}; use spec::CommonParams; use evm::Schedule; use header::BlockNumber; +use error::Error; +use state::CleanupMode; +use trace::{Tracer, ExecutiveTracer}; +use types::trace_types::trace::{RewardType}; /// An engine which does not provide any consensus mechanism and does not seal blocks. pub struct NullEngine { @@ -64,4 +70,29 @@ impl Engine for NullEngine { fn snapshot_components(&self) -> Option> { Some(Box::new(::snapshot::PowSnapshot(10000))) } + + fn on_close_block(&self, block: &mut ExecutedBlock) -> Result { + if !self.params.apply_reward { + return Ok(CloseOutcome{trace: None}); + } + + let fields = block.fields_mut(); + let mut tracer = ExecutiveTracer::default(); + + let result_block_reward = U256::from(1000000000); + fields.state.add_balance( + fields.header.author(), + &result_block_reward, + CleanupMode::NoEmpty + )?; + + let block_miner = fields.header.author().clone(); + tracer.trace_reward(block_miner, result_block_reward, RewardType::Block); + + fields.state.commit()?; + match *fields.tracing_enabled { + true => Ok(CloseOutcome{trace: Some(tracer.traces())}), + false => Ok(CloseOutcome{trace: None}) + } + } } diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index 959793e4c..090beb3cc 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -98,6 +98,8 @@ pub struct CommonParams { pub block_reward: U256, /// Registrar contract address. pub registrar: Address, + /// Apply reward + pub apply_reward: bool, } impl CommonParams { @@ -166,9 +168,13 @@ impl From for CommonParams { nonce_cap_increment: p.nonce_cap_increment.map_or(64, Into::into), remove_dust_contracts: p.remove_dust_contracts.unwrap_or(false), wasm: p.wasm.unwrap_or(false), +<<<<<<< HEAD gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(), block_reward: p.block_reward.map_or_else(U256::zero, Into::into), registrar: p.registrar.map_or_else(Address::new, Into::into), +======= + apply_reward: p.apply_reward.unwrap_or(true), +>>>>>>> 7312803... Working test with block reward added } } } @@ -476,6 +482,9 @@ impl Spec { /// Create a new Spec which conforms to the Frontier-era Morden chain except that it's a NullEngine consensus. pub fn new_test() -> Spec { load_bundled!("null_morden") } + /// Create a new Spec which conforms to the Frontier-era Morden chain except that it's a NullEngine consensus with applying reward on block close. + pub fn new_test_with_reward() -> Spec { load_bundled!("null_morden_with_reward") } + /// Create a new Spec which is a NullEngine consensus with a premine of address whose secret is sha3(''). pub fn new_null() -> Spec { load_bundled!("null") } diff --git a/ethcore/src/tests/mod.rs b/ethcore/src/tests/mod.rs index 31f195725..c86240f33 100644 --- a/ethcore/src/tests/mod.rs +++ b/ethcore/src/tests/mod.rs @@ -17,6 +17,7 @@ pub mod helpers; mod client; mod evm; +mod trace; #[cfg(feature="ipc")] mod rpc; diff --git a/ethcore/src/tests/trace.rs b/ethcore/src/tests/trace.rs new file mode 100644 index 000000000..e54b8b5df --- /dev/null +++ b/ethcore/src/tests/trace.rs @@ -0,0 +1,93 @@ +// Copyright 2015-2017 Parity Technologies (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 . + +//! Client tests of tracing + +use ethkey::KeyPair; +use block::*; +use util::*; +use io::*; +use spec::*; +use client::*; +use tests::helpers::*; +use devtools::RandomTempPath; +use client::{BlockChainClient, Client, ClientConfig}; +use util::kvdb::{Database, DatabaseConfig}; +use std::sync::Arc; +use header::Header; +use miner::Miner; +use transaction::{Action, Transaction}; + +#[test] +fn can_trace_block_and_uncle_reward() { + let dir = RandomTempPath::new(); + let spec = Spec::new_test_with_reward(); + let engine = &*spec.engine; + let genesis_header = spec.genesis_header(); + let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); + let last_hashes = Arc::new(vec![genesis_header.hash()]); + let mut b = OpenBlock::new(engine, Default::default(), true, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false).unwrap(); + + let kp = KeyPair::from_secret_slice(&"".sha3()).unwrap(); + let mut n = 0; + for _ in 0..1 { + b.push_transaction(Transaction { + nonce: n.into(), + gas_price: 0.into(), + gas: 100000.into(), + action: Action::Create, + data: vec![], + value: U256::zero(), + }.sign(kp.secret(), Some(spec.network_id())), None).unwrap(); + n += 1; + } + + let mut uncle = Header::new(); + let uncle_author: Address = "ef2d6d194084c2de36e0dabfce45d046b37d1106".into(); + uncle.set_author(uncle_author); + //b.push_uncle(uncle).unwrap(); + let b = b.close_and_lock().seal(engine, vec![]).unwrap(); + + let db_config = DatabaseConfig::with_columns(::db::NUM_COLUMNS); + let mut client_config = ClientConfig::default(); + client_config.tracing.enabled = true; + let client_db = Arc::new(Database::open(&db_config, dir.as_path().to_str().unwrap()).unwrap()); + let client = Client::new( + client_config, + &spec, + client_db, + Arc::new(Miner::with_spec(&spec)), + IoChannel::disconnected(), + ).unwrap(); + + let res = client.import_block(b.rlp_bytes()); + if res.is_err() { + panic!("error importing block: {:#?}", res.err().unwrap()); + } + + client.flush_queue(); + client.import_verified_blocks(); + + let filter = TraceFilter { + range: (BlockId::Number(1)..BlockId::Number(1)), + from_address: vec![], + to_address: vec![], + }; + + let traces = client.filter_traces(filter); + assert!(traces.is_some(), "Genesis trace should be always present."); + //panic!("Traces size is: {:#?}", traces.unwrap().len()); +} \ No newline at end of file diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index fe90ffe41..afb0fef49 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -215,8 +215,7 @@ impl TraceDB where T: DatabaseExtras { block_number: BlockNumber, tx_number: usize ) -> Vec { - let tx_hash = self.extras.transaction_hash(block_number, tx_number) - .expect("Expected to find transaction hash. Database is probably corrupted"); + let tx_hash = self.extras.transaction_hash(block_number, tx_number); let flat_traces: Vec = traces.into(); flat_traces.into_iter() @@ -228,7 +227,11 @@ impl TraceDB where T: DatabaseExtras { subtraces: trace.subtraces, trace_address: trace.trace_address.into_iter().collect(), transaction_number: tx_number, - transaction_hash: tx_hash.clone(), + transaction_hash: match tx_hash { + Some(hash) => hash.clone(), + /// None tx hash means non transaction's trace + None => 0.into(), + }, block_number: block_number, block_hash: block_hash }), diff --git a/ethcore/src/verification/verification.rs b/ethcore/src/verification/verification.rs index 823d2ef70..6980a21d1 100644 --- a/ethcore/src/verification/verification.rs +++ b/ethcore/src/verification/verification.rs @@ -591,6 +591,7 @@ mod tests { let mut params = CommonParams::default(); params.dust_protection_transition = 0; params.nonce_cap_increment = 2; + params.apply_reward = false; let mut header = Header::default(); header.set_number(1); diff --git a/json/src/spec/params.rs b/json/src/spec/params.rs index d72ead89a..f01a1ffb3 100644 --- a/json/src/spec/params.rs +++ b/json/src/spec/params.rs @@ -102,6 +102,9 @@ pub struct Params { pub block_reward: Option, /// See `CommonParams` docs. pub registrar: Option
, + /// Apply reward flag + #[serde(rename="applyReward")] + pub apply_reward: Option, } #[cfg(test)]