diff --git a/Cargo.lock b/Cargo.lock index e8ed9c0f5..21c36cf58 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -594,6 +594,7 @@ dependencies = [ "native-contracts 0.1.0", "num 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-machine 0.1.0", "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "patricia_trie 0.1.0", "price-info 1.7.0", @@ -2194,6 +2195,14 @@ dependencies = [ "serde_json 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "parity-machine" +version = "0.1.0" +dependencies = [ + "ethcore-bigint 0.1.3", + "ethcore-util 1.8.0", +] + [[package]] name = "parity-reactor" version = "0.1.0" diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index 9d82efd27..d76c07dbe 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -51,6 +51,7 @@ lru-cache = "0.1.0" native-contracts = { path = "native_contracts" } num = "0.1" num_cpus = "1.2" +parity-machine = { path = "../machine" } parking_lot = "0.4" price-info = { path = "../price-info" } rayon = "0.8" diff --git a/ethcore/light/src/client/fetch.rs b/ethcore/light/src/client/fetch.rs index 93a2cde11..3042651ce 100644 --- a/ethcore/light/src/client/fetch.rs +++ b/ethcore/light/src/client/fetch.rs @@ -19,7 +19,8 @@ use std::sync::Arc; use ethcore::encoded; -use ethcore::engines::{Engine, StateDependentProof}; +use ethcore::engines::{EthEngine, StateDependentProof}; +use ethcore::machine::EthereumMachine; use ethcore::header::Header; use ethcore::receipt::Receipt; use futures::future::IntoFuture; @@ -44,7 +45,12 @@ pub trait ChainDataFetcher: Send + Sync + 'static { fn block_receipts(&self, header: &Header) -> Self::Receipts; /// Fetch epoch transition proof at given header. - fn epoch_transition(&self, hash: H256, engine: Arc, checker: Arc) -> Self::Transition; + fn epoch_transition( + &self, + _hash: H256, + _engine: Arc, + _checker: Arc> + ) -> Self::Transition; } /// Fetcher implementation which cannot fetch anything. @@ -68,7 +74,12 @@ impl ChainDataFetcher for Unavailable { Err("fetching block receipts unavailable") } - fn epoch_transition(&self, _h: H256, _e: Arc, _check: Arc) -> Self::Transition { + fn epoch_transition( + &self, + _hash: H256, + _engine: Arc, + _checker: Arc> + ) -> Self::Transition { Err("fetching epoch transition proofs unavailable") } } diff --git a/ethcore/light/src/client/mod.rs b/ethcore/light/src/client/mod.rs index 2b2514d0a..1b32fe11d 100644 --- a/ethcore/light/src/client/mod.rs +++ b/ethcore/light/src/client/mod.rs @@ -20,7 +20,8 @@ use std::sync::{Weak, Arc}; use ethcore::block_status::BlockStatus; use ethcore::client::{ClientReport, EnvInfo}; -use ethcore::engines::{epoch, Engine, EpochChange, EpochTransition, Proof, Unsure}; +use ethcore::engines::{epoch, EthEngine, EpochChange, EpochTransition, Proof}; +use ethcore::machine::EthereumMachine; use ethcore::error::BlockImportError; use ethcore::ids::BlockId; use ethcore::header::{BlockNumber, Header}; @@ -117,7 +118,7 @@ pub trait LightChainClient: Send + Sync { fn env_info(&self, id: BlockId) -> Option; /// Get a handle to the consensus engine. - fn engine(&self) -> &Arc; + fn engine(&self) -> &Arc; /// Query whether a block is known. fn is_known(&self, hash: &H256) -> bool; @@ -165,7 +166,7 @@ impl AsLightClient for T { /// Light client implementation. pub struct Client { queue: HeaderQueue, - engine: Arc, + engine: Arc, chain: HeaderChain, report: RwLock, import_lock: Mutex<()>, @@ -381,7 +382,7 @@ impl Client { } /// Get a handle to the verification engine. - pub fn engine(&self) -> &Arc { + pub fn engine(&self) -> &Arc { &self.engine } @@ -444,7 +445,7 @@ impl Client { }; // Verify Block Family - let verify_family_result = self.engine.verify_block_family(&verified_header, &parent_header.decode(), None); + let verify_family_result = self.engine.verify_block_family(&verified_header, &parent_header.decode()); if let Err(e) = verify_family_result { warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", verified_header.number(), verified_header.hash(), e); @@ -453,7 +454,7 @@ impl Client { }; // "external" verification. - let verify_external_result = self.engine.verify_block_external(&verified_header, None); + let verify_external_result = self.engine.verify_block_external(&verified_header); if let Err(e) = verify_external_result { warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", verified_header.number(), verified_header.hash(), e); @@ -465,50 +466,54 @@ impl Client { true } - fn check_epoch_signal(&self, verified_header: &Header) -> Result, T::Error> { - let (mut block, mut receipts) = (None, None); + fn check_epoch_signal(&self, verified_header: &Header) -> Result>, T::Error> { + use ethcore::machine::{AuxiliaryRequest, AuxiliaryData}; - // First, check without providing auxiliary data. - match self.engine.signals_epoch_end(verified_header, None, None) { - EpochChange::No => return Ok(None), - EpochChange::Yes(proof) => return Ok(Some(proof)), - EpochChange::Unsure(unsure) => { - let (b, r) = match unsure { - Unsure::NeedsBody => - (Some(self.fetcher.block_body(verified_header)), None), - Unsure::NeedsReceipts => - (None, Some(self.fetcher.block_receipts(verified_header))), - Unsure::NeedsBoth => ( - Some(self.fetcher.block_body(verified_header)), - Some(self.fetcher.block_receipts(verified_header)), - ), + let mut block: Option> = None; + let mut receipts: Option> = None; + + loop { + + + let is_signal = { + let auxiliary = AuxiliaryData { + bytes: block.as_ref().map(|x| &x[..]), + receipts: receipts.as_ref().map(|x| &x[..]), }; - if let Some(b) = b { - block = Some(b.into_future().wait()?.into_inner()); - } + self.engine.signals_epoch_end(verified_header, auxiliary) + }; - if let Some(r) = r { - receipts = Some(r.into_future().wait()?); + // check with any auxiliary data fetched so far + match is_signal { + EpochChange::No => return Ok(None), + EpochChange::Yes(proof) => return Ok(Some(proof)), + EpochChange::Unsure(unsure) => { + let (b, r) = match unsure { + AuxiliaryRequest::Body => + (Some(self.fetcher.block_body(verified_header)), None), + AuxiliaryRequest::Receipts => + (None, Some(self.fetcher.block_receipts(verified_header))), + AuxiliaryRequest::Both => ( + Some(self.fetcher.block_body(verified_header)), + Some(self.fetcher.block_receipts(verified_header)), + ), + }; + + if let Some(b) = b { + block = Some(b.into_future().wait()?.into_inner()); + } + + if let Some(r) = r { + receipts = Some(r.into_future().wait()?); + } } } } - - let block = block.as_ref().map(|x| &x[..]); - let receipts = receipts.as_ref().map(|x| &x[..]); - - // Check again now that required data has been fetched. - match self.engine.signals_epoch_end(verified_header, block, receipts) { - EpochChange::No => return Ok(None), - EpochChange::Yes(proof) => return Ok(Some(proof)), - EpochChange::Unsure(_) => - panic!("Detected faulty engine implementation: requests additional \ - data to check epoch end signal when everything necessary provided"), - } } // attempts to fetch the epoch proof from the network until successful. - fn write_pending_proof(&self, header: &Header, proof: Proof) -> Result<(), T::Error> { + fn write_pending_proof(&self, header: &Header, proof: Proof) -> Result<(), T::Error> { let proof = match proof { Proof::Known(known) => known, Proof::WithState(state_dependent) => { @@ -568,7 +573,7 @@ impl LightChainClient for Client { Client::env_info(self, id) } - fn engine(&self) -> &Arc { + fn engine(&self) -> &Arc { Client::engine(self) } diff --git a/ethcore/light/src/on_demand/request.rs b/ethcore/light/src/on_demand/request.rs index ead2e13d7..4b0da5677 100644 --- a/ethcore/light/src/on_demand/request.rs +++ b/ethcore/light/src/on_demand/request.rs @@ -20,8 +20,9 @@ use std::sync::Arc; use ethcore::basic_account::BasicAccount; use ethcore::encoded; -use ethcore::engines::{Engine, StateDependentProof}; -use ethcore::receipt::{Receipt, TransactionOutcome}; +use ethcore::engines::{EthEngine, StateDependentProof}; +use ethcore::machine::EthereumMachine; +use ethcore::receipt::Receipt; use ethcore::state::{self, ProvedExecution}; use ethcore::transaction::SignedTransaction; use vm::EnvInfo; @@ -843,7 +844,7 @@ pub struct TransactionProof { // TODO: it's not really possible to provide this if the header is unknown. pub env_info: EnvInfo, /// Consensus engine. - pub engine: Arc, + pub engine: Arc, } impl TransactionProof { @@ -858,7 +859,7 @@ impl TransactionProof { state_items, root, &self.tx, - &*self.engine, + self.engine.machine(), &self.env_info, ); @@ -877,15 +878,15 @@ pub struct Signal { /// Block hash and number to fetch proof for. pub hash: H256, /// Consensus engine, used to check the proof. - pub engine: Arc, + pub engine: Arc, /// Special checker for the proof. - pub proof_check: Arc, + pub proof_check: Arc>, } impl Signal { /// Check the signal, returning the signal or indicate that it's bad. pub fn check_response(&self, _: &Mutex<::cache::Cache>, signal: &[u8]) -> Result, Error> { - self.proof_check.check_proof(&*self.engine, signal) + self.proof_check.check_proof(self.engine.machine(), signal) .map(|_| signal.to_owned()) .map_err(|_| Error::BadProof) } @@ -904,7 +905,7 @@ mod tests { use ethcore::client::{BlockChainClient, TestBlockChainClient, EachBlockWith}; use ethcore::header::Header; use ethcore::encoded; - use ethcore::receipt::Receipt; + use ethcore::receipt::{Receipt, TransactionOutcome}; fn make_cache() -> ::cache::Cache { ::cache::Cache::new(Default::default(), ::time::Duration::seconds(1)) diff --git a/ethcore/res/constructor.json b/ethcore/res/constructor.json index 9b1de7020..272eb4ac6 100644 --- a/ethcore/res/constructor.json +++ b/ethcore/res/constructor.json @@ -1,7 +1,9 @@ { "name": "GenesisConstructor", "engine": { - "null": null + "null": { + "params": {} + } }, "params": { "gasLimitBoundDivisor": "0x0400", diff --git a/ethcore/res/ethereum/byzantium_test.json b/ethcore/res/ethereum/byzantium_test.json index 4d239c04e..c267f94da 100644 --- a/ethcore/res/ethereum/byzantium_test.json +++ b/ethcore/res/ethereum/byzantium_test.json @@ -6,12 +6,12 @@ "minimumDifficulty": "0x020000", "difficultyBoundDivisor": "0x0800", "durationLimit": "0x0d", + "blockReward": "0x4563918244F40000", "homesteadTransition": "0x0", "eip150Transition": "0x0", "eip160Transition": "0x0", "eip161abcTransition": "0x0", "eip161dTransition": "0x0", - "maxCodeSize": 24576, "eip649Reward": "0x29A2241AF62C0000", "eip100bTransition": "0x0", "eip649Transition": "0x0" @@ -20,12 +20,12 @@ }, "params": { "gasLimitBoundDivisor": "0x0400", - "blockReward": "0x4563918244F40000", "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", "accountStartNonce": "0x00", "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "networkID" : "0x1", + "maxCodeSize": 24576, "eip98Transition": "0xffffffffffffffff", "eip140Transition": "0x0", "eip211Transition": "0x0", diff --git a/ethcore/res/ethereum/classic.json b/ethcore/res/ethereum/classic.json index bfd64248e..5f931cf8b 100644 --- a/ethcore/res/ethereum/classic.json +++ b/ethcore/res/ethereum/classic.json @@ -7,6 +7,7 @@ "minimumDifficulty": "0x020000", "difficultyBoundDivisor": "0x0800", "durationLimit": "0x0d", + "blockReward": "0x4563918244F40000", "homesteadTransition": 1150000, "eip150Transition": 2500000, "eip160Transition": 3000000, @@ -21,7 +22,6 @@ }, "params": { "gasLimitBoundDivisor": "0x0400", - "blockReward": "0x4563918244F40000", "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", "accountStartNonce": "0x00", "maximumExtraDataSize": "0x20", diff --git a/ethcore/res/ethereum/constantinople_test.json b/ethcore/res/ethereum/constantinople_test.json index d6df0dddb..7cf398374 100644 --- a/ethcore/res/ethereum/constantinople_test.json +++ b/ethcore/res/ethereum/constantinople_test.json @@ -6,12 +6,12 @@ "minimumDifficulty": "0x020000", "difficultyBoundDivisor": "0x0800", "durationLimit": "0x0d", + "blockReward": "0x4563918244F40000", "homesteadTransition": "0x0", "eip150Transition": "0x0", "eip160Transition": "0x0", "eip161abcTransition": "0x0", "eip161dTransition": "0x0", - "maxCodeSize": 24576, "eip649Reward": "0x29A2241AF62C0000", "eip100bTransition": "0x0", "eip649Transition": "0x0" @@ -20,12 +20,12 @@ }, "params": { "gasLimitBoundDivisor": "0x0400", - "blockReward": "0x4563918244F40000", "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", "accountStartNonce": "0x00", "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "networkID" : "0x1", + "maxCodeSize": 24576, "eip98Transition": "0xffffffffffffffff", "eip140Transition": "0x0", "eip210Transition": "0x0", diff --git a/ethcore/res/ethereum/eip150_test.json b/ethcore/res/ethereum/eip150_test.json index 22ebf5500..4f3ce9ea3 100644 --- a/ethcore/res/ethereum/eip150_test.json +++ b/ethcore/res/ethereum/eip150_test.json @@ -6,18 +6,17 @@ "minimumDifficulty": "0x020000", "difficultyBoundDivisor": "0x0800", "durationLimit": "0x0d", + "blockReward": "0x4563918244F40000", "homesteadTransition": "0x0", "eip150Transition": "0x0", "eip160Transition": "0x7fffffffffffffff", "eip161abcTransition": "0x7fffffffffffffff", - "eip161dTransition": "0x7fffffffffffffff", - "maxCodeSize": 24576 + "eip161dTransition": "0x7fffffffffffffff" } } }, "params": { "gasLimitBoundDivisor": "0x0400", - "blockReward": "0x4563918244F40000", "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", "accountStartNonce": "0x00", "maximumExtraDataSize": "0x20", @@ -25,7 +24,8 @@ "networkID" : "0x1", "eip98Transition": "0x7fffffffffffffff", "eip86Transition": "0x7fffffffffffffff", - "eip155Transition": "0x7fffffffffffffff" + "eip155Transition": "0x7fffffffffffffff", + "maxCodeSize": 24576 }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/eip161_test.json b/ethcore/res/ethereum/eip161_test.json index 50d28570b..164c3b0c9 100644 --- a/ethcore/res/ethereum/eip161_test.json +++ b/ethcore/res/ethereum/eip161_test.json @@ -6,18 +6,17 @@ "minimumDifficulty": "0x020000", "difficultyBoundDivisor": "0x0800", "durationLimit": "0x0d", + "blockReward": "0x4563918244F40000", "homesteadTransition": "0x0", "eip150Transition": "0x0", "eip160Transition": "0x0", "eip161abcTransition": "0x0", - "eip161dTransition": "0x0", - "maxCodeSize": 24576 + "eip161dTransition": "0x0" } } }, "params": { "gasLimitBoundDivisor": "0x0400", - "blockReward": "0x4563918244F40000", "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", "accountStartNonce": "0x00", "maximumExtraDataSize": "0x20", @@ -25,7 +24,8 @@ "networkID" : "0x1", "eip98Transition": "0x7fffffffffffffff", "eip86Transition": "0x7fffffffffffffff", - "eip155Transition": "0x7fffffffffffffff" + "eip155Transition": "0x7fffffffffffffff", + "maxCodeSize": 24576 }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/expanse.json b/ethcore/res/ethereum/expanse.json index d79ab6b3c..ec7e737ea 100644 --- a/ethcore/res/ethereum/expanse.json +++ b/ethcore/res/ethereum/expanse.json @@ -8,6 +8,7 @@ "difficultyBoundDivisor": "0x0800", "difficultyIncrementDivisor": "60", "durationLimit": "0x3C", + "blockReward": "0x6f05b59d3b200000", "homesteadTransition": "0x30d40", "difficultyHardforkTransition": "0x59d9", "difficultyHardforkBoundDivisor": "0x0200", @@ -21,7 +22,6 @@ }, "params": { "gasLimitBoundDivisor": "0x0400", - "blockReward": "0x6f05b59d3b200000", "registrar" : "0x6c221ca53705f3497ec90ca7b84c59ae7382fc21", "accountStartNonce": "0x00", "maximumExtraDataSize": "0x20", diff --git a/ethcore/res/ethereum/foundation.json b/ethcore/res/ethereum/foundation.json index 3e87d2e14..51e41ed43 100644 --- a/ethcore/res/ethereum/foundation.json +++ b/ethcore/res/ethereum/foundation.json @@ -7,6 +7,7 @@ "minimumDifficulty": "0x020000", "difficultyBoundDivisor": "0x0800", "durationLimit": "0x0d", + "blockReward": "0x4563918244F40000", "homesteadTransition": "0x118c30", "daoHardforkTransition": "0x1d4c00", "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", @@ -131,14 +132,12 @@ "eip150Transition": "0x259518", "eip160Transition": 2675000, "eip161abcTransition": 2675000, - "eip161dTransition": 2675000, - "maxCodeSize": 24576 + "eip161dTransition": 2675000 } } }, "params": { "gasLimitBoundDivisor": "0x0400", - "blockReward": "0x4563918244F40000", "registrar" : "0xe3389675d0338462dC76C6f9A3e432550c36A142", "accountStartNonce": "0x00", "maximumExtraDataSize": "0x20", @@ -149,7 +148,8 @@ "eip155Transition": 2675000, "eip98Transition": "0x7fffffffffffff", - "eip86Transition": "0x7fffffffffffff" + "eip86Transition": "0x7fffffffffffff", + "maxCodeSize": 24576 }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/frontier_like_test.json b/ethcore/res/ethereum/frontier_like_test.json index 0f4b8850f..6d2ea3693 100644 --- a/ethcore/res/ethereum/frontier_like_test.json +++ b/ethcore/res/ethereum/frontier_like_test.json @@ -6,6 +6,7 @@ "minimumDifficulty": "0x020000", "difficultyBoundDivisor": "0x0800", "durationLimit": "0x0d", + "blockReward": "0x4563918244F40000", "homesteadTransition": "0x118c30", "daoHardforkTransition": "0x1d4c00", "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", @@ -136,7 +137,6 @@ }, "params": { "gasLimitBoundDivisor": "0x0400", - "blockReward": "0x4563918244F40000", "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", "accountStartNonce": "0x00", "maximumExtraDataSize": "0x20", diff --git a/ethcore/res/ethereum/frontier_test.json b/ethcore/res/ethereum/frontier_test.json index 802a77f8d..aae59cb07 100644 --- a/ethcore/res/ethereum/frontier_test.json +++ b/ethcore/res/ethereum/frontier_test.json @@ -6,6 +6,7 @@ "minimumDifficulty": "0x020000", "difficultyBoundDivisor": "0x0800", "durationLimit": "0x0d", + "blockReward": "0x4563918244F40000", "homesteadTransition": "0x7fffffffffffffff", "eip150Transition": "0x7fffffffffffffff", "eip160Transition": "0x7fffffffffffffff", @@ -16,7 +17,6 @@ }, "params": { "gasLimitBoundDivisor": "0x0400", - "blockReward": "0x4563918244F40000", "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", "accountStartNonce": "0x00", "maximumExtraDataSize": "0x20", diff --git a/ethcore/res/ethereum/homestead_test.json b/ethcore/res/ethereum/homestead_test.json index 557a48a64..c6d49b545 100644 --- a/ethcore/res/ethereum/homestead_test.json +++ b/ethcore/res/ethereum/homestead_test.json @@ -6,6 +6,7 @@ "minimumDifficulty": "0x020000", "difficultyBoundDivisor": "0x0800", "durationLimit": "0x0d", + "blockReward": "0x4563918244F40000", "homesteadTransition": "0x0", "eip150Transition": "0x7fffffffffffffff", "eip160Transition": "0x7fffffffffffffff", @@ -16,7 +17,6 @@ }, "params": { "gasLimitBoundDivisor": "0x0400", - "blockReward": "0x4563918244F40000", "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", "accountStartNonce": "0x00", "maximumExtraDataSize": "0x20", diff --git a/ethcore/res/ethereum/kovan.json b/ethcore/res/ethereum/kovan.json index ca7638e33..bd27d3b14 100644 --- a/ethcore/res/ethereum/kovan.json +++ b/ethcore/res/ethereum/kovan.json @@ -1,63 +1,63 @@ { - "name": "Kovan", - "dataDir": "kovan", - "engine": { - "authorityRound": { - "params": { - "stepDuration": "4", - "validators" : { - "list": [ - "0x00D6Cc1BA9cf89BD2e58009741f4F7325BAdc0ED", - "0x00427feae2419c15b89d1c21af10d1b6650a4d3d", - "0x4Ed9B08e6354C70fE6F8CB0411b0d3246b424d6c", - "0x0020ee4Be0e2027d76603cB751eE069519bA81A1", + "name": "Kovan", + "dataDir": "kovan", + "engine": { + "authorityRound": { + "params": { + "stepDuration": "4", + "blockReward": "0x4563918244F40000", + "validators" : { + "list": [ + "0x00D6Cc1BA9cf89BD2e58009741f4F7325BAdc0ED", + "0x00427feae2419c15b89d1c21af10d1b6650a4d3d", + "0x4Ed9B08e6354C70fE6F8CB0411b0d3246b424d6c", + "0x0020ee4Be0e2027d76603cB751eE069519bA81A1", - "0x0010f94b296a852aaac52ea6c5ac72e03afd032d", + "0x0010f94b296a852aaac52ea6c5ac72e03afd032d", - "0x007733a1FE69CF3f2CF989F81C7b4cAc1693387A", - "0x00E6d2b931F55a3f1701c7389d592a7778897879", - "0x00e4a10650e5a6D6001C38ff8E64F97016a1645c", + "0x007733a1FE69CF3f2CF989F81C7b4cAc1693387A", + "0x00E6d2b931F55a3f1701c7389d592a7778897879", + "0x00e4a10650e5a6D6001C38ff8E64F97016a1645c", - "0x00a0a24b9f0e5ec7aa4c7389b8302fd0123194de" - ] - }, - "validateScoreTransition": 1000000, - "validateStepTransition": 1500000 - } - } - }, - "params": { - "gasLimitBoundDivisor": "0x400", - "registrar" : "0xfAb104398BBefbd47752E7702D9fE23047E1Bca3", - "blockReward": "0x4563918244F40000", + "0x00a0a24b9f0e5ec7aa4c7389b8302fd0123194de" + ] + }, + "validateScoreTransition": 1000000, + "validateStepTransition": 1500000 + } + } + }, + "params": { + "gasLimitBoundDivisor": "0x400", + "registrar" : "0xfAb104398BBefbd47752E7702D9fE23047E1Bca3", "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "networkID" : "0x2A", - "validateReceiptsTransition" : 1000000, - "eip155Transition": 1000000 - }, - "genesis": { - "seal": { - "authorityRound": { - "step": "0x0", - "signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } - }, - "difficulty": "0x20000", - "gasLimit": "0x5B8D80" - }, - "accounts": { - "0x0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, - "0x0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, - "0x0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, - "0x0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, - "0x00521965e7bd230323c423d96c657db5b79d099f": { "balance": "1606938044258990275541962092341162602522202993782792835301376" } - }, - "nodes": [ - "enode://0518a3d35d4a7b3e8c433e7ffd2355d84a1304ceb5ef349787b556197f0c87fad09daed760635b97d52179d645d3e6d16a37d2cc0a9945c2ddf585684beb39ac@40.68.248.100:30303", - "enode://dcf984764db421fa0cd8dc7fc02ae378545723abb94d179f55325514cc30185eaea3dcefde6e358b7cdbe970c50b7c49e841618713a9a72d6f3f59ad9949ec6b@52.165.239.18:30303", - "enode://7e2e7f00784f516939f94e22bdc6cf96153603ca2b5df1c7cc0f90a38e7a2f218ffb1c05b156835e8b49086d11fdd1b3e2965be16baa55204167aa9bf536a4d9@52.243.47.56:30303", - "enode://d51b3e98bf35addf2f1d0ea1ffc90483e24d7c60b0fb3be1701e818f3d6778c06e53fdec737a534fe222956296f9d6e909baa025916a94601897e5c7136a7d95@40.71.221.215:30303", - "enode://419d42e300e8fd379ff6d045d93d7e66a091441e7b3c9f1d3d10088d8634ad37721e6bf86148f78c3f1b9f1360dc566ca8ee830b2d2079bc9f7171ea6152bb64@52.166.117.77:30303" - ] + "minGasLimit": "0x1388", + "networkID" : "0x2A", + "validateReceiptsTransition" : 1000000, + "eip155Transition": 1000000 + }, + "genesis": { + "seal": { + "authorityRound": { + "step": "0x0", + "signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "gasLimit": "0x5B8D80" + }, + "accounts": { + "0x0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0x0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0x0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0x0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, + "0x00521965e7bd230323c423d96c657db5b79d099f": { "balance": "1606938044258990275541962092341162602522202993782792835301376" } + }, + "nodes": [ + "enode://0518a3d35d4a7b3e8c433e7ffd2355d84a1304ceb5ef349787b556197f0c87fad09daed760635b97d52179d645d3e6d16a37d2cc0a9945c2ddf585684beb39ac@40.68.248.100:30303", + "enode://dcf984764db421fa0cd8dc7fc02ae378545723abb94d179f55325514cc30185eaea3dcefde6e358b7cdbe970c50b7c49e841618713a9a72d6f3f59ad9949ec6b@52.165.239.18:30303", + "enode://7e2e7f00784f516939f94e22bdc6cf96153603ca2b5df1c7cc0f90a38e7a2f218ffb1c05b156835e8b49086d11fdd1b3e2965be16baa55204167aa9bf536a4d9@52.243.47.56:30303", + "enode://d51b3e98bf35addf2f1d0ea1ffc90483e24d7c60b0fb3be1701e818f3d6778c06e53fdec737a534fe222956296f9d6e909baa025916a94601897e5c7136a7d95@40.71.221.215:30303", + "enode://419d42e300e8fd379ff6d045d93d7e66a091441e7b3c9f1d3d10088d8634ad37721e6bf86148f78c3f1b9f1360dc566ca8ee830b2d2079bc9f7171ea6152bb64@52.166.117.77:30303" + ] } diff --git a/ethcore/res/ethereum/morden.json b/ethcore/res/ethereum/morden.json index f316fdf5f..ab32cb3d5 100644 --- a/ethcore/res/ethereum/morden.json +++ b/ethcore/res/ethereum/morden.json @@ -7,6 +7,7 @@ "minimumDifficulty": "0x020000", "difficultyBoundDivisor": "0x0800", "durationLimit": "0x0d", + "blockReward": "0x4563918244F40000", "homesteadTransition": 494000, "eip150Transition": 1783000, "eip160Transition": 1915000, @@ -20,7 +21,6 @@ }, "params": { "gasLimitBoundDivisor": "0x0400", - "blockReward": "0x4563918244F40000", "registrar": "0x52dff57a8a1532e6afb3dc07e2af58bb9eb05b3d", "accountStartNonce": "0x0100000", "maximumExtraDataSize": "0x20", diff --git a/ethcore/res/ethereum/olympic.json b/ethcore/res/ethereum/olympic.json index c73dcb815..6854f2b78 100644 --- a/ethcore/res/ethereum/olympic.json +++ b/ethcore/res/ethereum/olympic.json @@ -6,6 +6,7 @@ "minimumDifficulty": "0x020000", "difficultyBoundDivisor": "0x0800", "durationLimit": "0x08", + "blockReward": "0x14D1120D7B160000", "homesteadTransition": "0x7fffffffffffffff", "eip150Transition": "0x7fffffffffffffff", "eip160Transition": "0x7fffffffffffffff", @@ -16,7 +17,6 @@ }, "params": { "gasLimitBoundDivisor": "0x0400", - "blockReward": "0x14D1120D7B160000", "registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050", "accountStartNonce": "0x00", "maximumExtraDataSize": "0x0400", diff --git a/ethcore/res/ethereum/ropsten.json b/ethcore/res/ethereum/ropsten.json index f146caf52..6085568d9 100644 --- a/ethcore/res/ethereum/ropsten.json +++ b/ethcore/res/ethereum/ropsten.json @@ -7,12 +7,12 @@ "minimumDifficulty": "0x020000", "difficultyBoundDivisor": "0x0800", "durationLimit": "0x0d", + "blockReward": "0x4563918244F40000", "homesteadTransition": 0, "eip150Transition": 0, "eip160Transition": 10, "eip161abcTransition": 10, "eip161dTransition": 10, - "maxCodeSize": 24576, "eip649Reward": "0x29A2241AF62C0000", "eip100bTransition": 1700000, "eip649Transition": 1700000 @@ -21,7 +21,6 @@ }, "params": { "gasLimitBoundDivisor": "0x0400", - "blockReward": "0x4563918244F40000", "registrar": "0x81a4b044831c4f12ba601adb9274516939e9b8a2", "accountStartNonce": "0x0", "maximumExtraDataSize": "0x20", @@ -29,6 +28,7 @@ "networkID" : "0x3", "forkBlock": 641350, "forkCanonHash": "0x8033403e9fe5811a7b6d6b469905915de1c59207ce2172cbcf5d6ff14fa6a2eb", + "maxCodeSize": 24576, "eip155Transition": 10, "eip98Transition": "0x7fffffffffffff", "eip86Transition": "0x7fffffffffffff", diff --git a/ethcore/res/ethereum/transition_test.json b/ethcore/res/ethereum/transition_test.json index 064b651e2..ca2d77a4f 100644 --- a/ethcore/res/ethereum/transition_test.json +++ b/ethcore/res/ethereum/transition_test.json @@ -6,12 +6,12 @@ "minimumDifficulty": "0x020000", "difficultyBoundDivisor": "0x0800", "durationLimit": "0x0d", + "blockReward": "0x4563918244F40000", "homesteadTransition": "0", "eip150Transition": "0", "eip160Transition": "0", "eip161abcTransition": "0", "eip161dTransition": "0", - "maxCodeSize": 24576, "eip649Reward": "0x29A2241AF62C0000", "eip100bTransition": "5", "eip649Transition": "5" @@ -20,12 +20,12 @@ }, "params": { "gasLimitBoundDivisor": "0x0400", - "blockReward": "0x4563918244F40000", "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", "accountStartNonce": "0x00", "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "networkID" : "0x1", + "maxCodeSize": 24576, "eip98Transition": "5", "eip140Transition": "5", "eip211Transition": "5", diff --git a/ethcore/res/null.json b/ethcore/res/null.json index 97ce1afc5..c86be550c 100644 --- a/ethcore/res/null.json +++ b/ethcore/res/null.json @@ -1,7 +1,9 @@ { "name": "Morden", "engine": { - "null": null + "null": { + "params": {} + } }, "params": { "gasLimitBoundDivisor": "0x0400", diff --git a/ethcore/res/null_morden.json b/ethcore/res/null_morden.json index b615cdc29..87dcee6fc 100644 --- a/ethcore/res/null_morden.json +++ b/ethcore/res/null_morden.json @@ -1,7 +1,9 @@ { "name": "Morden", "engine": { - "null": null + "null": { + "params": {} + } }, "params": { "gasLimitBoundDivisor": "0x0400", diff --git a/ethcore/res/null_morden_with_reward.json b/ethcore/res/null_morden_with_reward.json index b7b1c9a0d..03c09c8af 100644 --- a/ethcore/res/null_morden_with_reward.json +++ b/ethcore/res/null_morden_with_reward.json @@ -1,15 +1,18 @@ { "name": "Morden", "engine": { - "null": null + "null": { + "params": { + "blockReward": "0x4563918244F40000" + } + } }, "params": { - "gasLimitBoundDivisor": "0x0400", + "gasLimitBoundDivisor": "0x0400", "accountStartNonce": "0x0", "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", - "networkID" : "0x2", - "blockReward": "0x4563918244F40000" + "networkID" : "0x2" }, "genesis": { "seal": { diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index f16e20eac..d701a17eb 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -31,7 +31,7 @@ use unexpected::{Mismatch, OutOfBounds}; use basic_types::{LogBloom, Seal}; use vm::{EnvInfo, LastHashes}; -use engines::Engine; +use engines::EthEngine; use error::{Error, BlockError, TransactionError}; use factory::Factories; use header::Header; @@ -97,6 +97,7 @@ pub struct ExecutedBlock { transactions_set: HashSet, state: State, traces: Option>>, + last_hashes: Arc, } /// A set of references to `ExecutedBlock` fields that are publicly accessible. @@ -115,6 +116,17 @@ pub struct BlockRefMut<'a> { pub traces: &'a mut Option>>, } +impl<'a> BlockRefMut<'a> { + /// Add traces if tracing is enabled. + pub fn push_traces(&mut self, tracer: ::trace::ExecutiveTracer) { + use trace::Tracer; + + if let Some(ref mut traces) = self.traces.as_mut() { + traces.push(tracer.drain()) + } + } +} + /// A set of immutable references to `ExecutedBlock` fields that are publicly accessible. pub struct BlockRef<'a> { /// Block header. @@ -133,7 +145,7 @@ pub struct BlockRef<'a> { impl ExecutedBlock { /// Create a new block from the given `state`. - fn new(state: State, tracing: bool) -> ExecutedBlock { + fn new(state: State, last_hashes: Arc, tracing: bool) -> ExecutedBlock { ExecutedBlock { header: Default::default(), transactions: Default::default(), @@ -142,6 +154,7 @@ impl ExecutedBlock { transactions_set: Default::default(), state: state, traces: if tracing {Some(Vec::new())} else {None}, + last_hashes: last_hashes, } } @@ -168,6 +181,20 @@ impl ExecutedBlock { traces: &self.traces, } } + + /// Get the environment info concerning this block. + pub fn env_info(&self) -> EnvInfo { + // TODO: memoise. + EnvInfo { + number: self.header.number(), + author: self.header.author().clone(), + timestamp: self.header.timestamp(), + difficulty: self.header.difficulty().clone(), + last_hashes: self.last_hashes.clone(), + gas_used: self.receipts.last().map_or(U256::zero(), |r| r.gas_used), + gas_limit: self.header.gas_limit().clone(), + } + } } /// Trait for a object that is a `ExecutedBlock`. @@ -216,14 +243,33 @@ impl IsBlock for ExecutedBlock { fn block(&self) -> &ExecutedBlock { self } } +impl ::parity_machine::LiveBlock for ExecutedBlock { + type Header = Header; + + fn header(&self) -> &Header { + &self.header + } + + fn uncles(&self) -> &[Header] { + &self.uncles + } +} + +impl ::parity_machine::Transactions for ExecutedBlock { + type Transaction = SignedTransaction; + + fn transactions(&self) -> &[SignedTransaction] { + &self.transactions + } +} + /// Block that is ready for transactions to be added. /// /// It's a bit like a Vec, except that whenever a transaction is pushed, we execute it and /// maintain the system `state()`. We also archive execution receipts in preparation for later block creation. pub struct OpenBlock<'x> { block: ExecutedBlock, - engine: &'x Engine, - last_hashes: Arc, + engine: &'x EthEngine, } /// Just like `OpenBlock`, except that we've applied `Engine::on_close_block`, finished up the non-seal header fields, @@ -234,7 +280,6 @@ pub struct OpenBlock<'x> { pub struct ClosedBlock { block: ExecutedBlock, uncle_bytes: Bytes, - last_hashes: Arc, unclosed_state: State, } @@ -259,7 +304,7 @@ impl<'x> OpenBlock<'x> { #[cfg_attr(feature="dev", allow(too_many_arguments))] /// Create a new `OpenBlock` ready for transaction pushing. pub fn new( - engine: &'x Engine, + engine: &'x EthEngine, factories: Factories, tracing: bool, db: StateDB, @@ -273,9 +318,8 @@ impl<'x> OpenBlock<'x> { let number = parent.number() + 1; let state = State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce(number), factories)?; let mut r = OpenBlock { - block: ExecutedBlock::new(state, tracing), + block: ExecutedBlock::new(state, last_hashes, tracing), engine: engine, - last_hashes: last_hashes.clone(), }; r.block.header.set_parent_hash(parent.hash()); @@ -287,8 +331,12 @@ impl<'x> OpenBlock<'x> { let gas_floor_target = cmp::max(gas_range_target.0, engine.params().min_gas_limit); let gas_ceil_target = cmp::max(gas_range_target.1, gas_floor_target); - engine.populate_from_parent(&mut r.block.header, parent, gas_floor_target, gas_ceil_target); - engine.on_new_block(&mut r.block, last_hashes, is_epoch_begin)?; + + engine.machine().populate_from_parent(&mut r.block.header, parent, gas_floor_target, gas_ceil_target); + engine.populate_from_parent(&mut r.block.header, parent); + + engine.machine().on_new_block(&mut r.block)?; + engine.on_new_block(&mut r.block, is_epoch_begin)?; Ok(r) } @@ -343,16 +391,7 @@ impl<'x> OpenBlock<'x> { /// Get the environment info concerning this block. pub fn env_info(&self) -> EnvInfo { - // TODO: memoise. - EnvInfo { - number: self.block.header.number(), - author: self.block.header.author().clone(), - timestamp: self.block.header.timestamp(), - difficulty: self.block.header.difficulty().clone(), - last_hashes: self.last_hashes.clone(), - gas_used: self.block.receipts.last().map_or(U256::zero(), |r| r.gas_used), - gas_limit: self.block.header.gas_limit().clone(), - } + self.block.env_info() } /// Push a transaction into the block. @@ -365,7 +404,7 @@ impl<'x> OpenBlock<'x> { let env_info = self.env_info(); // info!("env_info says gas_used={}", env_info.gas_used); - match self.block.state.apply(&env_info, self.engine, &t, self.block.traces.is_some()) { + match self.block.state.apply(&env_info, self.engine.machine(), &t, self.block.traces.is_some()) { Ok(outcome) => { self.block.transactions_set.insert(h.unwrap_or_else(||t.hash())); self.block.transactions.push(t.into()); @@ -418,7 +457,6 @@ impl<'x> OpenBlock<'x> { ClosedBlock { block: s.block, uncle_bytes: uncle_bytes, - last_hashes: s.last_hashes, unclosed_state: unclosed_state, } } @@ -485,14 +523,13 @@ impl ClosedBlock { } /// Given an engine reference, reopen the `ClosedBlock` into an `OpenBlock`. - pub fn reopen(self, engine: &Engine) -> OpenBlock { + pub fn reopen(self, engine: &EthEngine) -> OpenBlock { // revert rewards (i.e. set state back at last transaction's state). let mut block = self.block; block.state = self.unclosed_state; OpenBlock { block: block, engine: engine, - last_hashes: self.last_hashes, } } } @@ -504,7 +541,7 @@ impl LockedBlock { /// Provide a valid seal in order to turn this into a `SealedBlock`. /// /// NOTE: This does not check the validity of `seal` with the engine. - pub fn seal(self, engine: &Engine, seal: Vec) -> Result { + pub fn seal(self, engine: &EthEngine, seal: Vec) -> Result { let mut s = self; if seal.len() != engine.seal_fields() { return Err(BlockError::InvalidSealArity(Mismatch{expected: engine.seal_fields(), found: seal.len()})); @@ -518,12 +555,14 @@ impl LockedBlock { /// Returns the `ClosedBlock` back again if the seal is no good. pub fn try_seal( self, - engine: &Engine, + engine: &EthEngine, seal: Vec, ) -> Result { let mut s = self; s.block.header.set_seal(seal); - match engine.verify_block_seal(&s.block.header) { + + // TODO: passing state context to avoid engines owning it? + match engine.verify_local_seal(&s.block.header) { Err(e) => Err((e, s)), _ => Ok(SealedBlock { block: s.block, uncle_bytes: s.uncle_bytes }), } @@ -575,7 +614,7 @@ pub fn enact( header: &Header, transactions: &[SignedTransaction], uncles: &[Header], - engine: &Engine, + engine: &EthEngine, tracing: bool, db: StateDB, parent: &Header, @@ -647,7 +686,7 @@ fn push_transactions(block: &mut OpenBlock, transactions: &[SignedTransaction]) #[cfg_attr(feature="dev", allow(too_many_arguments))] pub fn enact_verified( block: &PreverifiedBlock, - engine: &Engine, + engine: &EthEngine, tracing: bool, db: StateDB, parent: &Header, @@ -675,7 +714,7 @@ pub fn enact_verified( mod tests { use tests::helpers::*; use super::*; - use engines::Engine; + use engines::EthEngine; use vm::LastHashes; use error::Error; use header::Header; @@ -690,7 +729,7 @@ mod tests { #[cfg_attr(feature="dev", allow(too_many_arguments))] fn enact_bytes( block_bytes: &[u8], - engine: &Engine, + engine: &EthEngine, tracing: bool, db: StateDB, parent: &Header, @@ -737,7 +776,7 @@ mod tests { #[cfg_attr(feature="dev", allow(too_many_arguments))] fn enact_and_seal( block_bytes: &[u8], - engine: &Engine, + engine: &EthEngine, tracing: bool, db: StateDB, parent: &Header, diff --git a/ethcore/src/client/ancient_import.rs b/ethcore/src/client/ancient_import.rs index aaf24cdc1..13699ea5a 100644 --- a/ethcore/src/client/ancient_import.rs +++ b/ethcore/src/client/ancient_import.rs @@ -19,8 +19,9 @@ use std::sync::Arc; use blockchain::BlockChain; -use engines::{Engine, EpochVerifier}; +use engines::{EthEngine, EpochVerifier}; use header::Header; +use machine::EthereumMachine; use rand::Rng; use parking_lot::RwLock; @@ -31,13 +32,13 @@ const HEAVY_VERIFY_RATE: f32 = 0.02; /// Ancient block verifier: import an ancient sequence of blocks in order from a starting /// epoch. pub struct AncientVerifier { - cur_verifier: RwLock>, - engine: Arc, + cur_verifier: RwLock>>, + engine: Arc, } impl AncientVerifier { /// Create a new ancient block verifier with the given engine and initial verifier. - pub fn new(engine: Arc, start_verifier: Box) -> Self { + pub fn new(engine: Arc, start_verifier: Box>) -> Self { AncientVerifier { cur_verifier: RwLock::new(start_verifier), engine: engine, diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index c1a74c8dd..afe36a6f3 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -46,7 +46,7 @@ use client::{ ChainNotify, PruningInfo, ProvingBlockChainClient, }; use encoded; -use engines::{Engine, EpochTransition}; +use engines::{EthEngine, EpochTransition}; use error::{ImportError, ExecutionError, CallError, BlockError, ImportResult, Error as EthcoreError}; use vm::{EnvInfo, LastHashes}; use evm::{Factory as EvmFactory, Schedule}; @@ -147,7 +147,7 @@ pub struct Client { mode: Mutex, chain: RwLock>, tracedb: RwLock>, - engine: Arc, + engine: Arc, config: ClientConfig, pruning: journaldb::Algorithm, db: RwLock>, @@ -332,7 +332,7 @@ impl Client { } /// Returns engine reference. - pub fn engine(&self) -> &Engine { + pub fn engine(&self) -> &EthEngine { &*self.engine } @@ -421,55 +421,63 @@ impl Client { return Err(()); } + // Check if parent is in chain + let parent = match chain.block_header(header.parent_hash()) { + Some(h) => h, + None => { + warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash()); + return Err(()); + } + }; + // Verify Block Family - let verify_family_result = self.verifier.verify_block_family(header, &block.bytes, engine, &**chain); + let verify_family_result = self.verifier.verify_block_family( + header, + &parent, + engine, + Some((&block.bytes, &block.transactions, &**chain, self)), + ); + if let Err(e) = verify_family_result { warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); return Err(()); }; - let verify_external_result = self.verifier.verify_block_external(header, &block.bytes, engine); + let verify_external_result = self.verifier.verify_block_external(header, engine); if let Err(e) = verify_external_result { warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); return Err(()); }; - // Check if Parent is in chain - let chain_has_parent = chain.block_header(header.parent_hash()); - if let Some(parent) = chain_has_parent { - // Enact Verified Block - let last_hashes = self.build_last_hashes(header.parent_hash().clone()); - let db = self.state_db.lock().boxed_clone_canon(header.parent_hash()); + // Enact Verified Block + let last_hashes = self.build_last_hashes(header.parent_hash().clone()); + let db = self.state_db.lock().boxed_clone_canon(header.parent_hash()); - let is_epoch_begin = chain.epoch_transition(parent.number(), *header.parent_hash()).is_some(); - let enact_result = enact_verified(block, - engine, - self.tracedb.read().tracing_enabled(), - db, - &parent, - last_hashes, - self.factories.clone(), - is_epoch_begin, - ); - let mut locked_block = enact_result.map_err(|e| { - warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); - })?; + let is_epoch_begin = chain.epoch_transition(parent.number(), *header.parent_hash()).is_some(); + let enact_result = enact_verified(block, + engine, + self.tracedb.read().tracing_enabled(), + db, + &parent, + last_hashes, + self.factories.clone(), + is_epoch_begin, + ); + let mut locked_block = enact_result.map_err(|e| { + warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); + })?; - if header.number() < self.engine().params().validate_receipts_transition && header.receipts_root() != locked_block.block().header().receipts_root() { - locked_block = locked_block.strip_receipts(); - } - - // Final Verification - if let Err(e) = self.verifier.verify_block_final(header, locked_block.block().header()) { - warn!(target: "client", "Stage 5 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); - return Err(()); - } - - Ok(locked_block) - } else { - warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash()); - Err(()) + if header.number() < self.engine().params().validate_receipts_transition && header.receipts_root() != locked_block.block().header().receipts_root() { + locked_block = locked_block.strip_receipts(); } + + // Final Verification + if let Err(e) = self.verifier.verify_block_final(header, locked_block.block().header()) { + warn!(target: "client", "Stage 5 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); + return Err(()); + } + + Ok(locked_block) } fn calculate_enacted_retracted(&self, import_results: &[ImportRoute]) -> (Vec, Vec) { @@ -721,7 +729,12 @@ impl Client { use engines::EpochChange; let hash = header.hash(); - match self.engine.signals_epoch_end(header, Some(block_bytes), Some(&receipts)) { + let auxiliary = ::machine::AuxiliaryData { + bytes: Some(block_bytes), + receipts: Some(&receipts), + }; + + match self.engine.signals_epoch_end(header, auxiliary) { EpochChange::Yes(proof) => { use engines::epoch::PendingTransition; use engines::Proof; @@ -754,7 +767,7 @@ impl Client { ).expect("state known to be available for just-imported block; qed"); let options = TransactOptions::with_no_tracing().dont_check_nonce(); - let res = Executive::new(&mut state, &env_info, &*self.engine) + let res = Executive::new(&mut state, &env_info, self.engine.machine()) .transact(&transaction, options); let res = match res { @@ -821,7 +834,7 @@ impl Client { // use a state-proving closure for the given block. fn with_proving_caller(&self, id: BlockId, with_call: F) -> T - where F: FnOnce(&::engines::Call) -> T + where F: FnOnce(&::machine::Call) -> T { let call = |a, d| { let tx = self.contract_call_tx(id, a, d); @@ -1119,15 +1132,14 @@ impl Client { } fn do_virtual_call(&self, env_info: &EnvInfo, state: &mut State, t: &SignedTransaction, analytics: CallAnalytics) -> Result { - fn call( + fn call( state: &mut State, env_info: &EnvInfo, - engine: &E, + machine: &::machine::EthereumMachine, state_diff: bool, transaction: &SignedTransaction, options: TransactOptions, ) -> Result where - E: Engine + ?Sized, T: trace::Tracer, V: trace::VMTracer, { @@ -1136,7 +1148,7 @@ impl Client { .save_output_from_contract(); let original_state = if state_diff { Some(state.clone()) } else { None }; - let mut ret = Executive::new(state, env_info, engine).transact_virtual(transaction, options)?; + let mut ret = Executive::new(state, env_info, machine).transact_virtual(transaction, options)?; if let Some(original) = original_state { ret.state_diff = Some(state.diff_from(original).map_err(ExecutionError::from)?); @@ -1145,13 +1157,13 @@ impl Client { } let state_diff = analytics.state_diffing; - let engine = &*self.engine; + let machine = self.engine.machine(); match (analytics.transaction_tracing, analytics.vm_tracing) { - (true, true) => call(state, env_info, engine, state_diff, t, TransactOptions::with_tracing_and_vm_tracing()), - (true, false) => call(state, env_info, engine, state_diff, t, TransactOptions::with_tracing()), - (false, true) => call(state, env_info, engine, state_diff, t, TransactOptions::with_vm_tracing()), - (false, false) => call(state, env_info, engine, state_diff, t, TransactOptions::with_no_tracing()), + (true, true) => call(state, env_info, machine, state_diff, t, TransactOptions::with_tracing_and_vm_tracing()), + (true, false) => call(state, env_info, machine, state_diff, t, TransactOptions::with_tracing()), + (false, true) => call(state, env_info, machine, state_diff, t, TransactOptions::with_vm_tracing()), + (false, false) => call(state, env_info, machine, state_diff, t, TransactOptions::with_no_tracing()), } } @@ -1235,7 +1247,7 @@ impl BlockChainClient for Client { let tx = tx.fake_sign(sender); let mut state = original_state.clone(); - Ok(Executive::new(&mut state, &env_info, &*self.engine) + Ok(Executive::new(&mut state, &env_info, self.engine.machine()) .transact_virtual(&tx, options()) .map(|r| r.exception.is_none()) .unwrap_or(false)) @@ -1296,7 +1308,7 @@ impl BlockChainClient for Client { let rest = txs.split_off(address.index); for t in txs { let t = SignedTransaction::new(t).expect(PROOF); - let x = Executive::new(&mut state, &env_info, &*self.engine).transact(&t, TransactOptions::with_no_tracing())?; + let x = Executive::new(&mut state, &env_info, self.engine.machine()).transact(&t, TransactOptions::with_no_tracing())?; env_info.gas_used = env_info.gas_used + x.gas_used; } let first = rest.into_iter().next().expect("We split off < `address.index`; Length is checked earlier; qed"); @@ -1575,7 +1587,7 @@ impl BlockChainClient for Client { .collect(); match (transaction, previous_receipts) { (Some(transaction), Some(previous_receipts)) => { - Some(transaction_receipt(self.engine(), transaction, previous_receipts)) + Some(transaction_receipt(self.engine().machine(), transaction, previous_receipts)) }, _ => None, } @@ -1995,7 +2007,7 @@ impl ProvingBlockChainClient for Client { jdb.as_hashdb_mut(), header.state_root().clone(), &transaction, - &*self.engine, + self.engine.machine(), &env_info, self.factories.clone(), false, @@ -2018,7 +2030,7 @@ impl Drop for Client { /// Returns `LocalizedReceipt` given `LocalizedTransaction` /// and a vector of receipts from given block up to transaction index. -fn transaction_receipt(engine: &Engine, mut tx: LocalizedTransaction, mut receipts: Vec) -> LocalizedReceipt { +fn transaction_receipt(machine: &::machine::EthereumMachine, mut tx: LocalizedTransaction, mut receipts: Vec) -> LocalizedReceipt { assert_eq!(receipts.len(), tx.transaction_index + 1, "All previous receipts are provided."); let sender = tx.sender(); @@ -2042,7 +2054,7 @@ fn transaction_receipt(engine: &Engine, mut tx: LocalizedTransaction, mut receip gas_used: receipt.gas_used - prior_gas_used, contract_address: match tx.action { Action::Call(_) => None, - Action::Create => Some(contract_address(engine.create_address_scheme(block_number), &sender, &tx.nonce, &tx.data).0) + Action::Create => Some(contract_address(machine.create_address_scheme(block_number), &sender, &tx.nonce, &tx.data).0) }, logs: receipt.logs.into_iter().enumerate().map(|(i, log)| LocalizedLogEntry { entry: log, @@ -2102,12 +2114,11 @@ mod tests { use log_entry::{LogEntry, LocalizedLogEntry}; use receipt::{Receipt, LocalizedReceipt, TransactionOutcome}; use transaction::{Transaction, LocalizedTransaction, Action}; - use tests::helpers::TestEngine; // given let key = KeyPair::from_secret_slice(&keccak("test")).unwrap(); let secret = key.secret(); - let engine = TestEngine::new(0); + let machine = ::ethereum::new_frontier_test_machine(); let block_number = 1; let block_hash = 5.into(); @@ -2151,7 +2162,7 @@ mod tests { }]; // when - let receipt = transaction_receipt(&engine, transaction, receipts); + let receipt = transaction_receipt(&machine, transaction, receipts); // then assert_eq!(receipt, LocalizedReceipt { diff --git a/ethcore/src/client/evm_test_client.rs b/ethcore/src/client/evm_test_client.rs index db1a11f79..c6e43fff1 100644 --- a/ethcore/src/client/evm_test_client.rs +++ b/ethcore/src/client/evm_test_client.rs @@ -181,7 +181,7 @@ impl<'a> EvmTestClient<'a> { let mut substate = state::Substate::new(); let mut tracer = trace::NoopTracer; let mut output = vec![]; - let mut executive = executive::Executive::new(&mut self.state, &info, &*self.spec.engine); + let mut executive = executive::Executive::new(&mut self.state, &info, self.spec.engine.machine()); executive.call( params, &mut substate, @@ -211,7 +211,7 @@ impl<'a> EvmTestClient<'a> { // Apply transaction let tracer = trace::NoopTracer; - let result = self.state.apply_with_tracing(&env_info, &*self.spec.engine, &transaction, tracer, vm_tracer); + let result = self.state.apply_with_tracing(&env_info, self.spec.engine.machine(), &transaction, tracer, vm_tracer); match result { Ok(result) => { diff --git a/ethcore/src/engines/authority_round/mod.rs b/ethcore/src/engines/authority_round/mod.rs index 4ea032411..27bd678a9 100644 --- a/ethcore/src/engines/authority_round/mod.rs +++ b/ethcore/src/engines/authority_round/mod.rs @@ -19,19 +19,16 @@ use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering}; use std::sync::{Weak, Arc}; use std::time::{UNIX_EPOCH, Duration}; -use std::collections::{BTreeMap, HashSet, HashMap}; -use std::cmp; +use std::collections::{BTreeMap, HashSet}; use account_provider::AccountProvider; use block::*; -use builtin::Builtin; use client::EngineClient; -use engines::{Call, Engine, Seal, EngineError, ConstructedVerifier}; -use error::{Error, TransactionError, BlockError}; +use engines::{Engine, Seal, EngineError, ConstructedVerifier}; +use error::{Error, BlockError}; use ethjson; +use machine::{AuxiliaryData, Call, EthereumMachine}; use header::{Header, BlockNumber}; -use spec::CommonParams; -use transaction::UnverifiedTransaction; use super::signer::EngineSigner; use super::validator_set::{ValidatorSet, SimpleList, new_validator_set}; @@ -66,6 +63,8 @@ pub struct AuthorityRoundParams { pub validate_step_transition: u64, /// Immediate transitions. pub immediate_transitions: bool, + /// Block reward in base units. + pub block_reward: U256, } impl From for AuthorityRoundParams { @@ -77,6 +76,7 @@ impl From for AuthorityRoundParams { validate_score_transition: p.validate_score_transition.map_or(0, Into::into), validate_step_transition: p.validate_step_transition.map_or(0, Into::into), immediate_transitions: p.immediate_transitions.unwrap_or(false), + block_reward: p.block_reward.map_or_else(Default::default, Into::into), } } } @@ -138,7 +138,7 @@ impl EpochManager { } // zoom to epoch for given header. returns true if succeeded, false otherwise. - fn zoom_to(&mut self, client: &EngineClient, engine: &Engine, validators: &ValidatorSet, header: &Header) -> bool { + fn zoom_to(&mut self, client: &EngineClient, machine: &EthereumMachine, validators: &ValidatorSet, header: &Header) -> bool { let last_was_parent = self.finality_checker.subchain_head() == Some(header.parent_hash().clone()); // early exit for current target == chain head, but only if the epochs are @@ -164,7 +164,6 @@ impl EpochManager { } }; - // extract other epoch set if it's not the same as the last. if last_transition.block_hash != self.epoch_transition_hash { let (signal_number, set_proof, _) = destructure_proofs(&last_transition.proof) @@ -176,7 +175,7 @@ impl EpochManager { let first = signal_number == 0; let epoch_set = validators.epoch_set( first, - engine, + machine, signal_number, // use signal number so multi-set first calculation is correct. set_proof, ) @@ -208,8 +207,6 @@ impl EpochManager { /// Engine using `AuthorityRound` proof-of-authority BFT consensus. pub struct AuthorityRound { - params: CommonParams, - builtins: BTreeMap, transition_service: IoService<()>, step: Arc, can_propose: AtomicBool, @@ -220,6 +217,8 @@ pub struct AuthorityRound { validate_step_transition: u64, epoch_manager: Mutex, immediate_transitions: bool, + block_reward: U256, + machine: EthereumMachine, } // header-chain validator. @@ -228,7 +227,7 @@ struct EpochVerifier { subchain_validators: SimpleList, } -impl super::EpochVerifier for EpochVerifier { +impl super::EpochVerifier for EpochVerifier { fn verify_light(&self, header: &Header) -> Result<(), Error> { // always check the seal since it's fast. // nothing heavier to do. @@ -250,7 +249,6 @@ impl super::EpochVerifier for EpochVerifier { let headers: Vec
= otry!(UntrustedRlp::new(proof).as_list().ok()); - for header in &headers { // ensure all headers have correct number of seal fields so we can `verify_external` // without panic. @@ -347,13 +345,11 @@ impl AsMillis for Duration { impl AuthorityRound { /// Create a new instance of AuthorityRound engine. - pub fn new(params: CommonParams, our_params: AuthorityRoundParams, builtins: BTreeMap) -> Result, Error> { + pub fn new(our_params: AuthorityRoundParams, machine: EthereumMachine) -> Result, Error> { let should_timeout = our_params.start_step.is_none(); let initial_step = our_params.start_step.unwrap_or_else(|| (unix_now().as_secs() / our_params.step_duration.as_secs())) as usize; let engine = Arc::new( AuthorityRound { - params: params, - builtins: builtins, transition_service: IoService::<()>::start()?, step: Arc::new(Step { inner: AtomicUsize::new(initial_step), @@ -368,6 +364,8 @@ impl AuthorityRound { validate_step_transition: our_params.validate_step_transition, epoch_manager: Mutex::new(EpochManager::blank()), immediate_transitions: our_params.immediate_transitions, + block_reward: our_params.block_reward, + machine: machine, }); // Do not initialize timeouts for tests. @@ -410,22 +408,16 @@ impl IoHandler<()> for TransitionHandler { } } -impl Engine for AuthorityRound { +impl Engine for AuthorityRound { fn name(&self) -> &str { "AuthorityRound" } fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) } + fn machine(&self) -> &EthereumMachine { &self.machine } + /// Two fields - consensus step and the corresponding proposer signature. fn seal_fields(&self) -> usize { 2 } - fn params(&self) -> &CommonParams { &self.params } - - fn additional_params(&self) -> HashMap { - hash_map!["registrar".to_owned() => self.params().registrar.hex()] - } - - fn builtins(&self) -> &BTreeMap { &self.builtins } - fn step(&self) { self.step.increment(); self.can_propose.store(true, AtomicOrdering::SeqCst); @@ -444,19 +436,10 @@ impl Engine for AuthorityRound { ] } - fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) { + fn populate_from_parent(&self, header: &mut Header, parent: &Header) { // Chain scoring: total weight is sqrt(U256::max_value())*height - step let new_difficulty = U256::from(U128::max_value()) + header_step(parent).expect("Header has been verified; qed").into() - self.step.load().into(); header.set_difficulty(new_difficulty); - header.set_gas_limit({ - let gas_limit = parent.gas_limit().clone(); - let bound_divisor = self.params().gas_limit_bound_divisor; - if gas_limit < gas_floor_target { - cmp::min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into()) - } else { - cmp::max(gas_floor_target, gas_limit - gas_limit / bound_divisor + 1.into()) - } - }); } fn seals_internally(&self) -> Option { @@ -491,7 +474,7 @@ impl Engine for AuthorityRound { } }; - if !epoch_manager.zoom_to(&*client, self, &*self.validators, header) { + if !epoch_manager.zoom_to(&*client, &self.machine, &*self.validators, header) { debug!(target: "engine", "Unable to zoom to epoch."); return Seal::None; } @@ -518,15 +501,15 @@ impl Engine for AuthorityRound { Seal::None } + fn verify_local_seal(&self, _header: &Header) -> Result<(), Error> { + Ok(()) + } + fn on_new_block( &self, block: &mut ExecutedBlock, - last_hashes: Arc<::vm::LastHashes>, epoch_begin: bool, ) -> Result<(), Error> { - let parent_hash = block.fields().header.parent_hash().clone(); - ::engines::common::push_last_hash(block, last_hashes.clone(), self, &parent_hash)?; - // with immediate transitions, we don't use the epoch mechanism anyway. // the genesis is always considered an epoch, but we ignore it intentionally. if self.immediate_transitions || !epoch_begin { return Ok(()) } @@ -536,10 +519,8 @@ impl Engine for AuthorityRound { let first = header.number() == 0; let mut call = |to, data| { - let result = ::engines::common::execute_as_system( + let result = self.machine.execute_as_system( block, - last_hashes.clone(), - self, to, U256::max_value(), // unbounded gas? maybe make configurable. Some(data), @@ -553,17 +534,13 @@ impl Engine for AuthorityRound { /// Apply the block reward on finalisation of the block. fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> { - ::engines::common::bestow_block_reward(block, self) + // TODO: move to "machine::WithBalances" trait. + ::engines::common::bestow_block_reward(block, self.block_reward) } /// Check the number of seal fields. - fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { - if header.seal().len() != self.seal_fields() { - trace!(target: "engine", "verify_block_basic: wrong number of seal fields"); - Err(From::from(BlockError::InvalidSealArity( - Mismatch { expected: self.seal_fields(), found: header.seal().len() } - ))) - } else if header.number() >= self.validate_score_transition && *header.difficulty() >= U256::from(U128::max_value()) { + fn verify_block_basic(&self, header: &Header,) -> Result<(), Error> { + if header.number() >= self.validate_score_transition && *header.difficulty() >= U256::from(U128::max_value()) { Err(From::from(BlockError::DifficultyOutOfBounds( OutOfBounds { min: None, max: Some(U256::from(U128::max_value())), found: *header.difficulty() } ))) @@ -572,19 +549,10 @@ impl Engine for AuthorityRound { } } - fn verify_block_unordered(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { - Ok(()) - } - /// Do the step and gas limit validation. - fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> { + fn verify_block_family(&self, header: &Header, parent: &Header) -> Result<(), Error> { let step = header_step(header)?; - // Do not calculate difficulty for genesis blocks. - if header.number() == 0 { - return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }))); - } - let parent_step = header_step(parent)?; // Ensure header is from the step after parent. @@ -595,6 +563,7 @@ impl Engine for AuthorityRound { self.validators.report_malicious(header.author(), header.number(), header.number(), Default::default()); Err(EngineError::DoubleVote(header.author().clone()))?; } + // Report skipped primaries. if let (true, Some(me)) = (step > parent_step + 1, self.signer.read().address()) { debug!(target: "engine", "Author {} built block with step gap. current step: {}, parent step: {}", @@ -611,17 +580,11 @@ impl Engine for AuthorityRound { } } - let gas_limit_divisor = self.params().gas_limit_bound_divisor; - let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor; - let max_gas = parent.gas_limit().clone() + parent.gas_limit().clone() / gas_limit_divisor; - if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas { - return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit().clone() }))); - } Ok(()) } // Check the validators. - fn verify_block_external(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { + fn verify_block_external(&self, header: &Header) -> Result<(), Error> { // fetch correct validator set for current epoch, taking into account // finality of previous transitions. let active_set; @@ -639,7 +602,7 @@ impl Engine for AuthorityRound { }; let mut epoch_manager = self.epoch_manager.lock(); - if !epoch_manager.zoom_to(&*client, self, &*self.validators, header) { + if !epoch_manager.zoom_to(&*client, &self.machine, &*self.validators, header) { debug!(target: "engine", "Unable to zoom to epoch."); return Err(EngineError::RequiresClient.into()) } @@ -667,19 +630,19 @@ impl Engine for AuthorityRound { .map(|set_proof| combine_proofs(0, &set_proof, &[])) } - fn signals_epoch_end(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) - -> super::EpochChange + fn signals_epoch_end(&self, header: &Header, aux: AuxiliaryData) + -> super::EpochChange { if self.immediate_transitions { return super::EpochChange::No } let first = header.number() == 0; - self.validators.signals_epoch_end(first, header, block, receipts) + self.validators.signals_epoch_end(first, header, aux) } fn is_epoch_end( &self, chain_head: &Header, - chain: &super::Headers, + chain: &super::Headers
, transition_store: &super::PendingTransitionStore, ) -> Option> { // epochs only matter if we want to support light clients. @@ -703,7 +666,7 @@ impl Engine for AuthorityRound { // find most recently finalized blocks, then check transition store for pending transitions. let mut epoch_manager = self.epoch_manager.lock(); - if !epoch_manager.zoom_to(&*client, self, &*self.validators, chain_head) { + if !epoch_manager.zoom_to(&*client, &self.machine, &*self.validators, chain_head) { return None; } @@ -782,14 +745,14 @@ impl Engine for AuthorityRound { None } - fn epoch_verifier<'a>(&self, _header: &Header, proof: &'a [u8]) -> ConstructedVerifier<'a> { + fn epoch_verifier<'a>(&self, _header: &Header, proof: &'a [u8]) -> ConstructedVerifier<'a, EthereumMachine> { let (signal_number, set_proof, finality_proof) = match destructure_proofs(proof) { Ok(x) => x, Err(e) => return ConstructedVerifier::Err(e), }; let first = signal_number == 0; - match self.validators.epoch_set(first, self, signal_number, set_proof) { + match self.validators.epoch_set(first, &self.machine, signal_number, set_proof) { Ok((list, finalize)) => { let verifier = Box::new(EpochVerifier { step: self.step.clone(), @@ -805,18 +768,6 @@ impl Engine for AuthorityRound { } } - fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> Result<(), Error> { - t.check_low_s()?; - - if let Some(n) = t.chain_id() { - if header.number() >= self.params().eip155_transition && n != self.params().chain_id { - return Err(TransactionError::InvalidChainId.into()); - } - } - - Ok(()) - } - fn register_client(&self, client: Weak) { *self.client.write() = Some(client.clone()); self.validators.register_client(client); @@ -847,7 +798,6 @@ mod tests { use bigint::prelude::U256; use bigint::hash::H520; use header::Header; - use error::{Error, BlockError}; use rlp::encode; use block::*; use tests::helpers::*; @@ -872,27 +822,13 @@ mod tests { assert!(schedule.stack_limit > 0); } - #[test] - fn verification_fails_on_short_seal() { - let engine = Spec::new_test_round().engine; - let header: Header = Header::default(); - - let verify_result = engine.verify_block_basic(&header, None); - - match verify_result { - Err(Error::Block(BlockError::InvalidSealArity(_))) => {}, - Err(_) => { panic!("should be block seal-arity mismatch error (got {:?})", verify_result); }, - _ => { panic!("Should be error, got Ok"); }, - } - } - #[test] fn can_do_signature_verification_fail() { let engine = Spec::new_test_round().engine; let mut header: Header = Header::default(); header.set_seal(vec![encode(&H520::default()).into_vec()]); - let verify_result = engine.verify_block_external(&header, None); + let verify_result = engine.verify_block_external(&header); assert!(verify_result.is_err()); } @@ -946,11 +882,11 @@ mod tests { // Two validators. // Spec starts with step 2. header.set_seal(vec![encode(&2usize).into_vec(), encode(&(&*signature as &[u8])).into_vec()]); - assert!(engine.verify_block_family(&header, &parent_header, None).is_ok()); - assert!(engine.verify_block_external(&header, None).is_err()); + assert!(engine.verify_block_family(&header, &parent_header).is_ok()); + assert!(engine.verify_block_external(&header).is_err()); header.set_seal(vec![encode(&1usize).into_vec(), encode(&(&*signature as &[u8])).into_vec()]); - assert!(engine.verify_block_family(&header, &parent_header, None).is_ok()); - assert!(engine.verify_block_external(&header, None).is_ok()); + assert!(engine.verify_block_family(&header, &parent_header).is_ok()); + assert!(engine.verify_block_external(&header).is_ok()); } #[test] @@ -972,11 +908,11 @@ mod tests { // Two validators. // Spec starts with step 2. header.set_seal(vec![encode(&1usize).into_vec(), encode(&(&*signature as &[u8])).into_vec()]); - assert!(engine.verify_block_family(&header, &parent_header, None).is_ok()); - assert!(engine.verify_block_external(&header, None).is_ok()); + assert!(engine.verify_block_family(&header, &parent_header).is_ok()); + assert!(engine.verify_block_external(&header).is_ok()); header.set_seal(vec![encode(&5usize).into_vec(), encode(&(&*signature as &[u8])).into_vec()]); - assert!(engine.verify_block_family(&header, &parent_header, None).is_ok()); - assert!(engine.verify_block_external(&header, None).is_err()); + assert!(engine.verify_block_family(&header, &parent_header).is_ok()); + assert!(engine.verify_block_external(&header).is_err()); } #[test] @@ -998,9 +934,9 @@ mod tests { // Two validators. // Spec starts with step 2. header.set_seal(vec![encode(&5usize).into_vec(), encode(&(&*signature as &[u8])).into_vec()]); - assert!(engine.verify_block_family(&header, &parent_header, None).is_ok()); + assert!(engine.verify_block_family(&header, &parent_header).is_ok()); header.set_seal(vec![encode(&3usize).into_vec(), encode(&(&*signature as &[u8])).into_vec()]); - assert!(engine.verify_block_family(&header, &parent_header, None).is_err()); + assert!(engine.verify_block_family(&header, &parent_header).is_err()); } #[test] @@ -1013,12 +949,14 @@ mod tests { validate_score_transition: 0, validate_step_transition: 0, immediate_transitions: true, + block_reward: Default::default(), }; let aura = { let mut c_params = ::spec::CommonParams::default(); c_params.gas_limit_bound_divisor = 5.into(); - AuthorityRound::new(c_params, params, Default::default()).unwrap() + let machine = ::machine::EthereumMachine::regular(c_params, Default::default()); + AuthorityRound::new(params, machine).unwrap() }; let mut parent_header: Header = Header::default(); @@ -1030,12 +968,12 @@ mod tests { header.set_seal(vec![encode(&3usize).into_vec()]); // Do not report when signer not present. - assert!(aura.verify_block_family(&header, &parent_header, None).is_ok()); + assert!(aura.verify_block_family(&header, &parent_header).is_ok()); assert_eq!(last_benign.load(AtomicOrdering::SeqCst), 0); aura.set_signer(Arc::new(AccountProvider::transient_provider()), Default::default(), Default::default()); - assert!(aura.verify_block_family(&header, &parent_header, None).is_ok()); + assert!(aura.verify_block_family(&header, &parent_header).is_ok()); assert_eq!(last_benign.load(AtomicOrdering::SeqCst), 1); } } diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index 5382c8fe3..b0d30b6ee 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/src/engines/basic_authority.rs @@ -17,24 +17,18 @@ //! A blockchain engine that supports a basic, non-BFT proof-of-authority. use std::sync::{Weak, Arc}; -use std::collections::BTreeMap; -use std::cmp; -use bigint::prelude::U256; use bigint::hash::{H256, H520}; use parking_lot::RwLock; use util::*; -use unexpected::{Mismatch, OutOfBounds}; use ethkey::{recover, public_to_address, Signature}; use account_provider::AccountProvider; use block::*; -use builtin::Builtin; -use spec::CommonParams; -use engines::{Engine, Seal, Call, ConstructedVerifier, EngineError}; +use engines::{Engine, Seal, ConstructedVerifier, EngineError}; use error::{BlockError, Error}; -use evm::Schedule; use ethjson; -use header::{Header, BlockNumber}; +use header::Header; use client::EngineClient; +use machine::{AuxiliaryData, Call, EthereumMachine}; use semantic_version::SemanticVersion; use super::signer::EngineSigner; use super::validator_set::{ValidatorSet, SimpleList, new_validator_set}; @@ -58,7 +52,7 @@ struct EpochVerifier { list: SimpleList, } -impl super::EpochVerifier for EpochVerifier { +impl super::EpochVerifier for EpochVerifier { fn verify_light(&self, header: &Header) -> Result<(), Error> { verify_external(header, &self.list) } @@ -83,53 +77,31 @@ fn verify_external(header: &Header, validators: &ValidatorSet) -> Result<(), Err /// Engine using `BasicAuthority`, trivial proof-of-authority consensus. pub struct BasicAuthority { - params: CommonParams, - builtins: BTreeMap, + machine: EthereumMachine, signer: RwLock, validators: Box, } impl BasicAuthority { /// Create a new instance of BasicAuthority engine - pub fn new(params: CommonParams, our_params: BasicAuthorityParams, builtins: BTreeMap) -> Self { + pub fn new(our_params: BasicAuthorityParams, machine: EthereumMachine) -> Self { BasicAuthority { - params: params, - builtins: builtins, - validators: new_validator_set(our_params.validators), + machine: machine, signer: Default::default(), + validators: new_validator_set(our_params.validators), } } } -impl Engine for BasicAuthority { +impl Engine for BasicAuthority { fn name(&self) -> &str { "BasicAuthority" } fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) } + + fn machine(&self) -> &EthereumMachine { &self.machine } + // One field - the signature fn seal_fields(&self) -> usize { 1 } - fn params(&self) -> &CommonParams { &self.params } - fn builtins(&self) -> &BTreeMap { &self.builtins } - - /// Additional engine-specific information for the user/developer concerning `header`. - fn extra_info(&self, _header: &Header) -> BTreeMap { map!["signature".to_owned() => "TODO".to_owned()] } - - fn schedule(&self, _block_number: BlockNumber) -> Schedule { - Schedule::new_homestead() - } - - fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) { - header.set_difficulty(parent.difficulty().clone()); - header.set_gas_limit({ - let gas_limit = parent.gas_limit().clone(); - let bound_divisor = self.params().gas_limit_bound_divisor; - if gas_limit < gas_floor_target { - cmp::min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into()) - } else { - cmp::max(gas_floor_target, gas_limit - gas_limit / bound_divisor + 1.into()) - } - }); - } - fn seals_internally(&self) -> Option { Some(self.signer.read().is_some()) } @@ -149,41 +121,11 @@ impl Engine for BasicAuthority { Seal::None } - fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { - // check the seal fields. - // TODO: pull this out into common code. - if header.seal().len() != self.seal_fields() { - return Err(From::from(BlockError::InvalidSealArity( - Mismatch { expected: self.seal_fields(), found: header.seal().len() } - ))); - } + fn verify_local_seal(&self, _header: &Header) -> Result<(), Error> { Ok(()) } - fn verify_block_unordered(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { - Ok(()) - } - - fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> { - // Do not calculate difficulty for genesis blocks. - if header.number() == 0 { - return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }))); - } - - // Check difficulty is correct given the two timestamps. - if header.difficulty() != parent.difficulty() { - return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: *parent.difficulty(), found: *header.difficulty() }))) - } - let gas_limit_divisor = self.params().gas_limit_bound_divisor; - let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor; - let max_gas = parent.gas_limit().clone() + parent.gas_limit().clone() / gas_limit_divisor; - if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas { - return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit().clone() }))); - } - Ok(()) - } - - fn verify_block_external(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { + fn verify_block_external(&self, header: &Header) -> Result<(), Error> { verify_external(header, &*self.validators) } @@ -192,26 +134,26 @@ impl Engine for BasicAuthority { } #[cfg(not(test))] - fn signals_epoch_end(&self, _header: &Header, _block: Option<&[u8]>, _receipts: Option<&[::receipt::Receipt]>) - -> super::EpochChange + fn signals_epoch_end(&self, _header: &Header, _auxiliary: AuxiliaryData) + -> super::EpochChange { // don't bother signalling even though a contract might try. super::EpochChange::No } #[cfg(test)] - fn signals_epoch_end(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) - -> super::EpochChange + fn signals_epoch_end(&self, header: &Header, auxiliary: AuxiliaryData) + -> super::EpochChange { // in test mode, always signal even though they don't be finalized. let first = header.number() == 0; - self.validators.signals_epoch_end(first, header, block, receipts) + self.validators.signals_epoch_end(first, header, auxiliary) } fn is_epoch_end( &self, chain_head: &Header, - _chain: &super::Headers, + _chain: &super::Headers
, _transition_store: &super::PendingTransitionStore, ) -> Option> { let first = chain_head.number() == 0; @@ -220,10 +162,10 @@ impl Engine for BasicAuthority { self.validators.is_epoch_end(first, chain_head) } - fn epoch_verifier<'a>(&self, header: &Header, proof: &'a [u8]) -> ConstructedVerifier<'a> { + fn epoch_verifier<'a>(&self, header: &Header, proof: &'a [u8]) -> ConstructedVerifier<'a, EthereumMachine> { let first = header.number() == 0; - match self.validators.epoch_set(first, self, header.number(), proof) { + match self.validators.epoch_set(first, &self.machine, header.number(), proof) { Ok((list, finalize)) => { let verifier = Box::new(EpochVerifier { list: list }); @@ -260,7 +202,6 @@ mod tests { use hash::keccak; use bigint::hash::H520; use block::*; - use error::{BlockError, Error}; use tests::helpers::*; use account_provider::AccountProvider; use header::Header; @@ -287,27 +228,13 @@ mod tests { assert!(schedule.stack_limit > 0); } - #[test] - fn can_do_seal_verification_fail() { - let engine = new_test_authority().engine; - let header: Header = Header::default(); - - let verify_result = engine.verify_block_basic(&header, None); - - match verify_result { - Err(Error::Block(BlockError::InvalidSealArity(_))) => {}, - Err(_) => { panic!("should be block seal-arity mismatch error (got {:?})", verify_result); }, - _ => { panic!("Should be error, got Ok"); }, - } - } - #[test] fn can_do_signature_verification_fail() { let engine = new_test_authority().engine; let mut header: Header = Header::default(); header.set_seal(vec![::rlp::encode(&H520::default()).into_vec()]); - let verify_result = engine.verify_block_family(&header, &Default::default(), None); + let verify_result = engine.verify_block_external(&header); assert!(verify_result.is_err()); } diff --git a/ethcore/src/engines/epoch.rs b/ethcore/src/engines/epoch.rs index fb0eaa267..b5ffd8a2d 100644 --- a/ethcore/src/engines/epoch.rs +++ b/ethcore/src/engines/epoch.rs @@ -17,8 +17,6 @@ //! Epoch verifiers and transitions. use bigint::hash::H256; -use error::Error; -use header::Header; /// A full epoch transition. #[derive(Debug, Clone, RlpEncodable, RlpDecodable)] @@ -40,15 +38,13 @@ pub struct PendingTransition { } /// Verifier for all blocks within an epoch with self-contained state. -/// -/// See docs on `Engine` relating to proving functions for more details. -pub trait EpochVerifier: Send + Sync { +pub trait EpochVerifier: Send + Sync { /// Lightly verify the next block header. /// This may not be a header belonging to a different epoch. - fn verify_light(&self, header: &Header) -> Result<(), Error>; + fn verify_light(&self, header: &M::Header) -> Result<(), M::Error>; /// Perform potentially heavier checks on the next block header. - fn verify_heavy(&self, header: &Header) -> Result<(), Error> { + fn verify_heavy(&self, header: &M::Header) -> Result<(), M::Error> { self.verify_light(header) } @@ -63,6 +59,6 @@ pub trait EpochVerifier: Send + Sync { /// Special "no-op" verifier for stateless, epoch-less engines. pub struct NoOp; -impl EpochVerifier for NoOp { - fn verify_light(&self, _header: &Header) -> Result<(), Error> { Ok(()) } +impl EpochVerifier for NoOp { + fn verify_light(&self, _header: &M::Header) -> Result<(), M::Error> { Ok(()) } } diff --git a/ethcore/src/engines/instant_seal.rs b/ethcore/src/engines/instant_seal.rs index 8a2d369c1..a0700099d 100644 --- a/ethcore/src/engines/instant_seal.rs +++ b/ethcore/src/engines/instant_seal.rs @@ -14,51 +14,42 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::collections::{BTreeMap, HashMap}; -use util::Address; -use builtin::Builtin; use engines::{Engine, Seal}; -use spec::CommonParams; -use block::{ExecutedBlock, IsBlock}; +use parity_machine::{Machine, Transactions}; /// An engine which does not provide any consensus mechanism, just seals blocks internally. -pub struct InstantSeal { - params: CommonParams, - builtins: BTreeMap, +/// Only seals blocks which have transactions. +pub struct InstantSeal { + machine: M, } -impl InstantSeal { - /// Returns new instance of InstantSeal with default VM Factory - pub fn new(params: CommonParams, builtins: BTreeMap) -> Self { +impl InstantSeal { + /// Returns new instance of InstantSeal over the given state machine. + pub fn new(machine: M) -> Self { InstantSeal { - params: params, - builtins: builtins, + machine: machine, } } } -impl Engine for InstantSeal { +impl Engine for InstantSeal + where M::LiveBlock: Transactions +{ fn name(&self) -> &str { "InstantSeal" } - fn params(&self) -> &CommonParams { - &self.params - } - - fn additional_params(&self) -> HashMap { - hash_map!["registrar".to_owned() => self.params().registrar.hex()] - } - - fn builtins(&self) -> &BTreeMap { - &self.builtins - } + fn machine(&self) -> &M { &self.machine } fn seals_internally(&self) -> Option { Some(true) } - fn generate_seal(&self, block: &ExecutedBlock) -> Seal { + fn generate_seal(&self, block: &M::LiveBlock) -> Seal { if block.transactions().is_empty() { Seal::None } else { Seal::Regular(Vec::new()) } } + + fn verify_local_seal(&self, _header: &M::Header) -> Result<(), M::Error> { + Ok(()) + } } #[cfg(test)] @@ -91,10 +82,10 @@ mod tests { let engine = Spec::new_instant().engine; let mut header: Header = Header::default(); - assert!(engine.verify_block_basic(&header, None).is_ok()); + assert!(engine.verify_block_basic(&header).is_ok()); header.set_seal(vec![::rlp::encode(&H520::default()).into_vec()]); - assert!(engine.verify_block_unordered(&header, None).is_ok()); + assert!(engine.verify_block_unordered(&header).is_ok()); } } diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index 50f787c2b..5078ebc3a 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -42,18 +42,16 @@ use std::fmt; use self::epoch::PendingTransition; use account_provider::AccountProvider; -use block::ExecutedBlock; use builtin::Builtin; -use client::EngineClient; -use vm::{EnvInfo, LastHashes, Schedule, CreateContractAddress}; +use vm::{EnvInfo, Schedule, CreateContractAddress}; use error::Error; use header::{Header, BlockNumber}; -use receipt::Receipt; use snapshot::SnapshotComponents; use spec::CommonParams; use transaction::{UnverifiedTransaction, SignedTransaction}; use ethkey::Signature; +use parity_machine::{Machine, LocalizedMachine as Localized}; use bigint::prelude::U256; use bigint::hash::H256; use semantic_version::SemanticVersion; @@ -82,6 +80,8 @@ pub enum EngineError { InsufficientProof(String), /// Failed system call. FailedSystemCall(String), + /// Malformed consensus message. + MalformedMessage(String), /// Requires client ref, but none registered. RequiresClient, } @@ -97,6 +97,7 @@ impl fmt::Display for EngineError { BadSealFieldSize(ref oob) => format!("Seal field has an unexpected length: {}", oob), InsufficientProof(ref msg) => format!("Insufficient validation proof: {}", msg), FailedSystemCall(ref msg) => format!("Failed to make system call: {}", msg), + MalformedMessage(ref msg) => format!("Received malformed consensus message: {}", msg), RequiresClient => format!("Call requires client but none registered"), }; @@ -115,49 +116,46 @@ pub enum Seal { None, } -/// Type alias for a function we can make calls through synchronously. -/// Returns the call result and state proof for each call. -pub type Call<'a> = Fn(Address, Bytes) -> Result<(Bytes, Vec>), String> + 'a; - /// Type alias for a function we can get headers by hash through. -pub type Headers<'a> = Fn(H256) -> Option
+ 'a; +pub type Headers<'a, H> = Fn(H256) -> Option + 'a; /// Type alias for a function we can query pending transitions by block hash through. pub type PendingTransitionStore<'a> = Fn(H256) -> Option + 'a; /// Proof dependent on state. -pub trait StateDependentProof: Send + Sync { +pub trait StateDependentProof: Send + Sync { /// Generate a proof, given the state. - fn generate_proof(&self, caller: &Call) -> Result, String>; + // TODO: make this into an &M::StateContext + fn generate_proof<'a>(&self, state: &>::StateContext) -> Result, String>; /// Check a proof generated elsewhere (potentially by a peer). // `engine` needed to check state proofs, while really this should // just be state machine params. - fn check_proof(&self, engine: &Engine, proof: &[u8]) -> Result<(), String>; + fn check_proof(&self, machine: &M, proof: &[u8]) -> Result<(), String>; } /// Proof generated on epoch change. -pub enum Proof { +pub enum Proof { /// Known proof (extracted from signal) Known(Vec), /// State dependent proof. - WithState(Arc), + WithState(Arc>), } /// Generated epoch verifier. -pub enum ConstructedVerifier<'a> { +pub enum ConstructedVerifier<'a, M: Machine> { /// Fully trusted verifier. - Trusted(Box), + Trusted(Box>), /// Verifier unconfirmed. Check whether given finality proof finalizes given hash /// under previous epoch. - Unconfirmed(Box, &'a [u8], H256), + Unconfirmed(Box>, &'a [u8], H256), /// Error constructing verifier. Err(Error), } -impl<'a> ConstructedVerifier<'a> { +impl<'a, M: Machine> ConstructedVerifier<'a, M> { /// Convert to a result, indicating that any necessary confirmation has been done /// already. - pub fn known_confirmed(self) -> Result, Error> { + pub fn known_confirmed(self) -> Result>, Error> { match self { ConstructedVerifier::Trusted(v) | ConstructedVerifier::Unconfirmed(v, _, _) => Ok(v), ConstructedVerifier::Err(e) => Err(e), @@ -166,84 +164,53 @@ impl<'a> ConstructedVerifier<'a> { } /// Results of a query of whether an epoch change occurred at the given block. -pub enum EpochChange { +pub enum EpochChange { /// Cannot determine until more data is passed. - Unsure(Unsure), + Unsure(M::AuxiliaryRequest), /// No epoch change. No, /// The epoch will change, with proof. - Yes(Proof), -} - -/// More data required to determine if an epoch change occurred at a given block. -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum Unsure { - /// Needs the body. - NeedsBody, - /// Needs the receipts. - NeedsReceipts, - /// Needs both body and receipts. - NeedsBoth, + Yes(Proof), } /// A consensus mechanism for the chain. Generally either proof-of-work or proof-of-stake-based. /// Provides hooks into each of the major parts of block import. -pub trait Engine : Sync + Send { +pub trait Engine: Sync + Send { /// The name of this engine. fn name(&self) -> &str; /// The version of this engine. Should be of the form fn version(&self) -> SemanticVersion { SemanticVersion::new(0, 0, 0) } + /// Get access to the underlying state machine. + // TODO: decouple. + fn machine(&self) -> &M; + /// The number of additional header fields required for this engine. fn seal_fields(&self) -> usize { 0 } /// Additional engine-specific information for the user/developer concerning `header`. - fn extra_info(&self, _header: &Header) -> BTreeMap { BTreeMap::new() } + fn extra_info(&self, _header: &M::Header) -> BTreeMap { BTreeMap::new() } /// Additional information. fn additional_params(&self) -> HashMap { HashMap::new() } - /// Get the general parameters of the chain. - fn params(&self) -> &CommonParams; - - /// Get the EVM schedule for the given `block_number`. - fn schedule(&self, block_number: BlockNumber) -> Schedule { - self.params().schedule(block_number) - } - - /// Builtin-contracts we would like to see in the chain. - /// (In principle these are just hints for the engine since that has the last word on them.) - fn builtins(&self) -> &BTreeMap; - - /// Some intrinsic operation parameters; by default they take their value from the `spec()`'s `engine_params`. - fn maximum_extra_data_size(&self) -> usize { self.params().maximum_extra_data_size } /// Maximum number of uncles a block is allowed to declare. fn maximum_uncle_count(&self) -> usize { 2 } /// The number of generations back that uncles can be. fn maximum_uncle_age(&self) -> usize { 6 } - /// The nonce with which accounts begin at given block. - fn account_start_nonce(&self, block: u64) -> U256 { - if block >= self.params().dust_protection_transition { - U256::from(self.params().nonce_cap_increment) * U256::from(block) - } else { - self.params().account_start_nonce - } - } /// Block transformation functions, before the transactions. /// `epoch_begin` set to true if this block kicks off an epoch. fn on_new_block( &self, - block: &mut ExecutedBlock, - last_hashes: Arc, + _block: &mut M::LiveBlock, _epoch_begin: bool, - ) -> Result<(), Error> { - let parent_hash = block.fields().header.parent_hash().clone(); - common::push_last_hash(block, last_hashes, self, &parent_hash) + ) -> Result<(), M::Error> { + Ok(()) } /// Block transformation functions, after the transactions. - fn on_close_block(&self, _block: &mut ExecutedBlock) -> Result<(), Error> { + fn on_close_block(&self, _block: &mut M::LiveBlock) -> Result<(), M::Error> { Ok(()) } @@ -251,68 +218,57 @@ pub trait Engine : Sync + Send { /// Some(true) means the engine is currently prime for seal generation (i.e. node is the current validator). /// Some(false) means that the node might seal internally but is not qualified now. fn seals_internally(&self) -> Option { None } + /// Attempt to seal the block internally. /// /// If `Some` is returned, then you get a valid seal. /// /// This operation is synchronous and may (quite reasonably) not be available, in which None will /// be returned. - fn generate_seal(&self, _block: &ExecutedBlock) -> Seal { Seal::None } + /// + /// It is fine to require access to state or a full client for this function, since + /// light clients do not generate seals. + fn generate_seal(&self, _block: &M::LiveBlock) -> Seal { Seal::None } - /// Phase 1 quick block verification. Only does checks that are cheap. `block` (the header's full block) - /// may be provided for additional checks. Returns either a null `Ok` or a general error detailing the problem with import. - fn verify_block_basic(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) } + /// Verify a locally-generated seal of a header. + /// + /// If this engine seals internally, + /// no checks have to be done here, since all internally generated seals + /// should be valid. + /// + /// Externally-generated seals (e.g. PoW) will need to be checked for validity. + /// + /// It is fine to require access to state or a full client for this function, since + /// light clients do not generate seals. + fn verify_local_seal(&self, header: &M::Header) -> Result<(), M::Error>; - /// Phase 2 verification. Perform costly checks such as transaction signatures. `block` (the header's full block) - /// may be provided for additional checks. Returns either a null `Ok` or a general error detailing the problem with import. - fn verify_block_unordered(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) } + /// Phase 1 quick block verification. Only does checks that are cheap. Returns either a null `Ok` or a general error detailing the problem with import. + fn verify_block_basic(&self, _header: &M::Header) -> Result<(), M::Error> { Ok(()) } - /// Phase 3 verification. Check block information against parent and uncles. `block` (the header's full block) - /// may be provided for additional checks. Returns either a null `Ok` or a general error detailing the problem with import. - fn verify_block_family(&self, _header: &Header, _parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) } + /// Phase 2 verification. Perform costly checks such as transaction signatures. Returns either a null `Ok` or a general error detailing the problem with import. + fn verify_block_unordered(&self, _header: &M::Header) -> Result<(), M::Error> { Ok(()) } + + /// Phase 3 verification. Check block information against parent. Returns either a null `Ok` or a general error detailing the problem with import. + fn verify_block_family(&self, _header: &M::Header, _parent: &M::Header) -> Result<(), Error> { Ok(()) } /// Phase 4 verification. Verify block header against potentially external data. - fn verify_block_external(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) } - - /// Additional verification for transactions in blocks. - // TODO: Add flags for which bits of the transaction to check. - // TODO: consider including State in the params. - fn verify_transaction_basic(&self, t: &UnverifiedTransaction, _header: &Header) -> Result<(), Error> { - t.verify_basic(true, Some(self.params().chain_id), true)?; - Ok(()) - } - - /// Verify a particular transaction is valid. - fn verify_transaction(&self, t: UnverifiedTransaction, _header: &Header) -> Result { - SignedTransaction::new(t) - } - - /// The network ID that transactions should be signed with. - fn signing_chain_id(&self, _env_info: &EnvInfo) -> Option { - Some(self.params().chain_id) - } - - /// Verify the seal of a block. This is an auxilliary method that actually just calls other `verify_` methods - /// to get the job done. By default it must pass `verify_basic` and `verify_block_unordered`. If more or fewer - /// methods are needed for an Engine, this may be overridden. - fn verify_block_seal(&self, header: &Header) -> Result<(), Error> { - self.verify_block_basic(header, None).and_then(|_| self.verify_block_unordered(header, None)) - } + /// Should only be called when `register_client` has been called previously. + fn verify_block_external(&self, _header: &M::Header) -> Result<(), Error> { Ok(()) } /// Genesis epoch data. - fn genesis_epoch_data(&self, _header: &Header, _call: &Call) -> Result, String> { Ok(Vec::new()) } + fn genesis_epoch_data<'a>(&self, _header: &M::Header, _state: &>::StateContext) -> Result, String> { Ok(Vec::new()) } /// Whether an epoch change is signalled at the given header but will require finality. /// If a change can be enacted immediately then return `No` from this function but /// `Yes` from `is_epoch_end`. /// - /// If the block or receipts are required, return `Unsure` and the function will be + /// If auxiliary data of the block is required, return an auxiliary request and the function will be /// called again with them. /// Return `Yes` or `No` when the answer is definitively known. /// /// Should not interact with state. - fn signals_epoch_end(&self, _header: &Header, _block: Option<&[u8]>, _receipts: Option<&[Receipt]>) - -> EpochChange + fn signals_epoch_end<'a>(&self, _header: &M::Header, _aux: >::AuxiliaryData) + -> EpochChange { EpochChange::No } @@ -326,8 +282,8 @@ pub trait Engine : Sync + Send { /// Return optional transition proof. fn is_epoch_end( &self, - _chain_head: &Header, - _chain: &Headers, + _chain_head: &M::Header, + _chain: &Headers, _transition_store: &PendingTransitionStore, ) -> Option> { None @@ -335,35 +291,21 @@ pub trait Engine : Sync + Send { /// Create an epoch verifier from validation proof and a flag indicating /// whether finality is required. - fn epoch_verifier<'a>(&self, _header: &Header, _proof: &'a [u8]) -> ConstructedVerifier<'a> { + fn epoch_verifier<'a>(&self, _header: &M::Header, _proof: &'a [u8]) -> ConstructedVerifier<'a, M> { ConstructedVerifier::Trusted(Box::new(self::epoch::NoOp)) } /// Populate a header's fields based on its parent's header. /// Usually implements the chain scoring rule based on weight. - /// The gas floor target must not be lower than the engine's minimum gas limit. - fn populate_from_parent(&self, header: &mut Header, parent: &Header, _gas_floor_target: U256, _gas_ceil_target: U256) { - header.set_difficulty(parent.difficulty().clone()); - header.set_gas_limit(parent.gas_limit().clone()); - } + fn populate_from_parent(&self, _header: &mut M::Header, _parent: &M::Header) { } /// Handle any potential consensus messages; /// updating consensus state and potentially issuing a new one. - fn handle_message(&self, _message: &[u8]) -> Result<(), Error> { Err(EngineError::UnexpectedMessage.into()) } - - /// Attempt to get a handle to a built-in contract. - /// Only returns references to activated built-ins. - // TODO: builtin contract routing - to do this properly, it will require removing the built-in configuration-reading logic - // from Spec into here and removing the Spec::builtins field. - fn builtin(&self, a: &Address, block_number: ::header::BlockNumber) -> Option<&Builtin> { - self.builtins() - .get(a) - .and_then(|b| if b.is_active(block_number) { Some(b) } else { None }) - } + fn handle_message(&self, _message: &[u8]) -> Result<(), EngineError> { Err(EngineError::UnexpectedMessage) } /// Find out if the block is a proposal block and should not be inserted into the DB. /// Takes a header of a fully verified block. - fn is_proposal(&self, _verified_header: &Header) -> bool { false } + fn is_proposal(&self, _verified_header: &M::Header) -> bool { false } /// Register an account which signs consensus messages. fn set_signer(&self, _account_provider: Arc, _address: Address, _password: String) {} @@ -371,8 +313,8 @@ pub trait Engine : Sync + Send { /// Sign using the EngineSigner, to be used for consensus tx signing. fn sign(&self, _hash: H256) -> Result { unimplemented!() } - /// Add Client which can be used for sealing, querying the state and sending messages. - fn register_client(&self, _client: Weak) {} + /// Add Client which can be used for sealing, potentially querying the state and sending messages. + fn register_client(&self, _client: Weak) {} /// Trigger next step of the consensus engine. fn step(&self) {} @@ -390,118 +332,96 @@ pub trait Engine : Sync + Send { fn supports_warp(&self) -> bool { self.snapshot_components().is_some() } +} - /// If this engine supports wasm contracts. - fn supports_wasm(&self) -> bool { - self.params().wasm +/// Common type alias for an engine coupled with an Ethereum-like state machine. +// TODO: make this a _trait_ alias when those exist. +// fortunately the effect is largely the same since engines are mostly used +// via trait objects. +pub trait EthEngine: Engine<::machine::EthereumMachine> { + /// Get the general parameters of the chain. + fn params(&self) -> &CommonParams { + self.machine().params() + } + + /// Get the EVM schedule for the given block number. + fn schedule(&self, block_number: BlockNumber) -> Schedule { + self.machine().schedule(block_number) + } + + /// Builtin-contracts for the chain.. + fn builtins(&self) -> &BTreeMap { + self.machine().builtins() + } + + /// Attempt to get a handle to a built-in contract. + /// Only returns references to activated built-ins. + fn builtin(&self, a: &Address, block_number: BlockNumber) -> Option<&Builtin> { + self.machine().builtin(a, block_number) + } + + /// Some intrinsic operation parameters; by default they take their value from the `spec()`'s `engine_params`. + fn maximum_extra_data_size(&self) -> usize { + self.machine().maximum_extra_data_size() + } + + /// The nonce with which accounts begin at given block. + fn account_start_nonce(&self, block: u64) -> U256 { + self.machine().account_start_nonce(block) + } + + /// The network ID that transactions should be signed with. + fn signing_chain_id(&self, env_info: &EnvInfo) -> Option { + self.machine().signing_chain_id(env_info) } /// Returns new contract address generation scheme at given block number. fn create_address_scheme(&self, number: BlockNumber) -> CreateContractAddress { - if number >= self.params().eip86_transition { - CreateContractAddress::FromCodeHash - } else { - CreateContractAddress::FromSenderAndNonce - } + self.machine().create_address_scheme(number) + } + + /// Verify a particular transaction is valid. + fn verify_transaction_unordered(&self, t: UnverifiedTransaction, header: &Header) -> Result { + self.machine().verify_transaction_unordered(t, header) + } + + /// Additional verification for transactions in blocks. + // TODO: Add flags for which bits of the transaction to check. + // TODO: consider including State in the params. + fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> Result<(), Error> { + self.machine().verify_transaction_basic(t, header) + } + + /// If this machine supports wasm. + fn supports_wasm(&self) -> bool { + self.machine().supports_wasm() } } +// convenience wrappers for existing functions. +impl EthEngine for T where T: Engine<::machine::EthereumMachine> { } + /// Common engine utilities pub mod common { - use std::sync::Arc; use block::ExecutedBlock; use error::Error; - use transaction::SYSTEM_ADDRESS; - use executive::Executive; - use vm::{CallType, ActionParams, ActionValue, EnvInfo, LastHashes}; - use trace::{NoopTracer, NoopVMTracer, Tracer, ExecutiveTracer, RewardType}; - use state::Substate; + use trace::{Tracer, ExecutiveTracer, RewardType}; use state::CleanupMode; use bigint::prelude::U256; - use bigint::hash::H256; - use util::*; - use bytes::{Bytes, BytesRef}; - use super::Engine; - /// Execute a call as the system address. - pub fn execute_as_system( - block: &mut ExecutedBlock, - last_hashes: Arc, - engine: &E, - contract_address: Address, - gas: U256, - data: Option, - ) -> Result { - let env_info = { - let header = block.fields().header; - EnvInfo { - number: header.number(), - author: header.author().clone(), - timestamp: header.timestamp(), - difficulty: header.difficulty().clone(), - last_hashes: last_hashes, - gas_used: U256::zero(), - gas_limit: gas, - } - }; - - let mut state = block.fields_mut().state; - let params = ActionParams { - code_address: contract_address.clone(), - address: contract_address.clone(), - sender: SYSTEM_ADDRESS.clone(), - origin: SYSTEM_ADDRESS.clone(), - gas: gas, - gas_price: 0.into(), - value: ActionValue::Transfer(0.into()), - code: state.code(&contract_address)?, - code_hash: Some(state.code_hash(&contract_address)?), - data: data, - call_type: CallType::Call, - }; - let mut ex = Executive::new(&mut state, &env_info, engine); - let mut substate = Substate::new(); - let mut output = Vec::new(); - if let Err(e) = ex.call(params, &mut substate, BytesRef::Flexible(&mut output), &mut NoopTracer, &mut NoopVMTracer) { - warn!("Encountered error on making system call: {}", e); - } - - Ok(output) - } - - /// Push last known block hash to the state. - pub fn push_last_hash(block: &mut ExecutedBlock, last_hashes: Arc, engine: &E, hash: &H256) -> Result<(), Error> { - if block.fields().header.number() == engine.params().eip210_transition { - let state = block.fields_mut().state; - state.init_code(&engine.params().eip210_contract_address, engine.params().eip210_contract_code.clone())?; - } - if block.fields().header.number() >= engine.params().eip210_transition { - let _ = execute_as_system( - block, - last_hashes, - engine, - engine.params().eip210_contract_address, - engine.params().eip210_contract_gas, - Some(hash.to_vec()), - )?; - } - Ok(()) - } - - /// Trace rewards on closing block - pub fn bestow_block_reward(block: &mut ExecutedBlock, engine: &E) -> Result<(), Error> { + /// Give reward and trace. + pub fn bestow_block_reward(block: &mut ExecutedBlock, reward: U256) -> Result<(), Error> { let fields = block.fields_mut(); // Bestow block reward - let reward = engine.params().block_reward; let res = fields.state.add_balance(fields.header.author(), &reward, CleanupMode::NoEmpty) .map_err(::error::Error::from) .and_then(|_| fields.state.commit()); let block_author = fields.header.author().clone(); - fields.traces.as_mut().map(|mut traces| { + fields.traces.as_mut().map(move |mut traces| { let mut tracer = ExecutiveTracer::default(); - tracer.trace_reward(block_author, engine.params().block_reward, RewardType::Block); + tracer.trace_reward(block_author, reward, RewardType::Block); traces.push(tracer.drain()) }); diff --git a/ethcore/src/engines/null_engine.rs b/ethcore/src/engines/null_engine.rs index a07952ee3..1c7edc99b 100644 --- a/ethcore/src/engines/null_engine.rs +++ b/ethcore/src/engines/null_engine.rs @@ -14,103 +14,92 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::collections::BTreeMap; -use util::Address; -use builtin::Builtin; -use block::{ExecutedBlock, IsBlock}; use bigint::prelude::U256; use engines::Engine; -use spec::CommonParams; -use evm::Schedule; -use header::BlockNumber; -use error::Error; -use state::CleanupMode; -use trace::{Tracer, ExecutiveTracer, RewardType}; +use parity_machine::{Header, LiveBlock, WithBalances}; -/// An engine which does not provide any consensus mechanism and does not seal blocks. -pub struct NullEngine { - params: CommonParams, - builtins: BTreeMap, +/// Params for a null engine. +#[derive(Clone, Default)] +pub struct NullEngineParams { + /// base reward for a block. + pub block_reward: U256, } -impl NullEngine { - /// Returns new instance of NullEngine with default VM Factory - pub fn new(params: CommonParams, builtins: BTreeMap) -> Self { - NullEngine{ - params: params, - builtins: builtins, +impl From<::ethjson::spec::NullEngineParams> for NullEngineParams { + fn from(p: ::ethjson::spec::NullEngineParams) -> Self { + NullEngineParams { + block_reward: p.block_reward.map_or_else(Default::default, Into::into), } } } -impl Default for NullEngine { +/// An engine which does not provide any consensus mechanism and does not seal blocks. +pub struct NullEngine { + params: NullEngineParams, + machine: M, +} + +impl NullEngine { + /// Returns new instance of NullEngine with default VM Factory + pub fn new(params: NullEngineParams, machine: M) -> Self { + NullEngine { + params: params, + machine: machine, + } + } +} + +impl Default for NullEngine { fn default() -> Self { Self::new(Default::default(), Default::default()) } } -impl Engine for NullEngine { +impl Engine for NullEngine { fn name(&self) -> &str { "NullEngine" } - fn params(&self) -> &CommonParams { - &self.params + fn machine(&self) -> &M { &self.machine } + + fn on_close_block(&self, block: &mut M::LiveBlock) -> Result<(), M::Error> { + use std::ops::Shr; + + let author = *LiveBlock::header(&*block).author(); + let number = LiveBlock::header(&*block).number(); + + let reward = self.params.block_reward; + if reward == U256::zero() { return Ok(()) } + + let n_uncles = LiveBlock::uncles(&*block).len(); + + // Bestow block reward + let result_block_reward = reward + reward.shr(5) * U256::from(n_uncles); + let mut uncle_rewards = Vec::with_capacity(n_uncles); + + self.machine.add_balance(block, &author, &result_block_reward)?; + + // bestow uncle rewards. + for u in LiveBlock::uncles(&*block) { + let uncle_author = u.author(); + let result_uncle_reward = (reward * U256::from(8 + u.number() - number)).shr(3); + + uncle_rewards.push((*uncle_author, result_uncle_reward)); + } + + for &(ref a, ref reward) in &uncle_rewards { + self.machine.add_balance(block, a, reward)?; + } + + // note and trace. + self.machine.note_rewards(block, &[(author, result_block_reward)], &uncle_rewards) } - fn builtins(&self) -> &BTreeMap { - &self.builtins - } - - fn schedule(&self, _block_number: BlockNumber) -> Schedule { - Schedule::new_homestead() + fn verify_local_seal(&self, _header: &M::Header) -> Result<(), M::Error> { + Ok(()) } fn snapshot_components(&self) -> Option> { Some(Box::new(::snapshot::PowSnapshot::new(10000, 10000))) } - - fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> { - if self.params.block_reward == U256::zero() { - // we don't have to apply reward in this case - return Ok(()) - } - - /// Block reward - let tracing_enabled = block.tracing_enabled(); - 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 - )?; - - if tracing_enabled { - let block_author = fields.header.author().clone(); - tracer.trace_reward(block_author, result_block_reward, RewardType::Block); - } - - /// Uncle rewards - let result_uncle_reward = U256::from(10000000); - for u in fields.uncles.iter() { - let uncle_author = u.author().clone(); - fields.state.add_balance( - u.author(), - &(result_uncle_reward), - CleanupMode::NoEmpty - )?; - if tracing_enabled { - tracer.trace_reward(uncle_author, result_uncle_reward, RewardType::Uncle); - } - } - - fields.state.commit()?; - if tracing_enabled { - fields.traces.as_mut().map(|mut traces| traces.push(tracer.drain())); - } - Ok(()) - } } diff --git a/ethcore/src/engines/tendermint/mod.rs b/ethcore/src/engines/tendermint/mod.rs index 6fe00aaab..3ccc79baa 100644 --- a/ethcore/src/engines/tendermint/mod.rs +++ b/ethcore/src/engines/tendermint/mod.rs @@ -27,9 +27,8 @@ mod params; use std::sync::{Weak, Arc}; use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering}; -use std::collections::{HashSet, BTreeMap, HashMap}; +use std::collections::{HashSet, BTreeMap}; use hash::keccak; -use std::cmp; use bigint::prelude::{U128, U256}; use bigint::hash::{H256, H520}; use parking_lot::RwLock; @@ -39,12 +38,10 @@ use client::EngineClient; use bytes::Bytes; use error::{Error, BlockError}; use header::{Header, BlockNumber}; -use builtin::Builtin; use rlp::UntrustedRlp; use ethkey::{Message, public_to_address, recover, Signature}; use account_provider::AccountProvider; use block::*; -use spec::CommonParams; use engines::{Engine, Seal, EngineError, ConstructedVerifier}; use io::IoService; use super::signer::EngineSigner; @@ -54,6 +51,7 @@ use super::vote_collector::VoteCollector; use self::message::*; use self::params::TendermintParams; use semantic_version::SemanticVersion; +use machine::{AuxiliaryData, EthereumMachine}; #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] pub enum Step { @@ -78,8 +76,6 @@ pub type BlockHash = H256; /// Engine using `Tendermint` consensus algorithm, suitable for EVM chain. pub struct Tendermint { - params: CommonParams, - builtins: BTreeMap, step_service: IoService, client: RwLock>>, /// Blockchain height. @@ -104,6 +100,10 @@ pub struct Tendermint { last_proposed: RwLock, /// Set used to determine the current validators. validators: Box, + /// Reward per block, in base units. + block_reward: U256, + /// ethereum machine descriptor + machine: EthereumMachine, } struct EpochVerifier @@ -113,7 +113,7 @@ struct EpochVerifier recover: F } -impl super::EpochVerifier for EpochVerifier +impl super::EpochVerifier for EpochVerifier where F: Fn(&Signature, &Message) -> Result + Send + Sync { fn verify_light(&self, header: &Header) -> Result<(), Error> { @@ -167,11 +167,9 @@ fn destructure_proofs(combined: &[u8]) -> Result<(BlockNumber, &[u8], &[u8]), Er impl Tendermint { /// Create a new instance of Tendermint engine - pub fn new(params: CommonParams, our_params: TendermintParams, builtins: BTreeMap) -> Result, Error> { + pub fn new(our_params: TendermintParams, machine: EthereumMachine) -> Result, Error> { let engine = Arc::new( Tendermint { - params: params, - builtins: builtins, client: RwLock::new(None), step_service: IoService::::start()?, height: AtomicUsize::new(1), @@ -185,9 +183,13 @@ impl Tendermint { proposal_parent: Default::default(), last_proposed: Default::default(), validators: our_params.validators, + block_reward: our_params.block_reward, + machine: machine, }); - let handler = TransitionHandler::new(Arc::downgrade(&engine) as Weak, Box::new(our_params.timeouts)); + + let handler = TransitionHandler::new(Arc::downgrade(&engine) as Weak>, Box::new(our_params.timeouts)); engine.step_service.register_handler(Arc::new(handler))?; + Ok(engine) } @@ -438,7 +440,7 @@ impl Tendermint { } } -impl Engine for Tendermint { +impl Engine for Tendermint { fn name(&self) -> &str { "Tendermint" } fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) } @@ -446,13 +448,7 @@ impl Engine for Tendermint { /// (consensus view, proposal signature, authority signatures) fn seal_fields(&self) -> usize { 3 } - fn params(&self) -> &CommonParams { &self.params } - - fn additional_params(&self) -> HashMap { - hash_map!["registrar".to_owned() => self.params().registrar.hex()] - } - - fn builtins(&self) -> &BTreeMap { &self.builtins } + fn machine(&self) -> &EthereumMachine { &self.machine } fn maximum_uncle_count(&self) -> usize { 0 } @@ -469,19 +465,13 @@ impl Engine for Tendermint { ] } - fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) { + fn populate_from_parent(&self, header: &mut Header, parent: &Header) { // Chain scoring: total weight is sqrt(U256::max_value())*height - view - let new_difficulty = U256::from(U128::max_value()) + consensus_view(parent).expect("Header has been verified; qed").into() - self.view.load(AtomicOrdering::SeqCst).into(); + let new_difficulty = U256::from(U128::max_value()) + + consensus_view(parent).expect("Header has been verified; qed").into() + - self.view.load(AtomicOrdering::SeqCst).into(); + header.set_difficulty(new_difficulty); - header.set_gas_limit({ - let gas_limit = parent.gas_limit().clone(); - let bound_divisor = self.params().gas_limit_bound_divisor; - if gas_limit < gas_floor_target { - cmp::min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into()) - } else { - cmp::max(gas_floor_target, gas_limit - gas_limit / bound_divisor + 1.into()) - } - }); } /// Should this node participate. @@ -525,19 +515,27 @@ impl Engine for Tendermint { } } - fn handle_message(&self, rlp: &[u8]) -> Result<(), Error> { + fn handle_message(&self, rlp: &[u8]) -> Result<(), EngineError> { + fn fmt_err(x: T) -> EngineError { + EngineError::MalformedMessage(format!("{:?}", x)) + } + let rlp = UntrustedRlp::new(rlp); - let message: ConsensusMessage = rlp.as_val()?; + let message: ConsensusMessage = rlp.as_val().map_err(fmt_err)?; if !self.votes.is_old_or_known(&message) { - let sender = public_to_address(&recover(&message.signature.into(), &keccak(rlp.at(1)?.as_raw()))?); + let msg_hash = keccak(rlp.at(1).map_err(fmt_err)?.as_raw()); + let sender = public_to_address( + &recover(&message.signature.into(), &msg_hash).map_err(fmt_err)? + ); + if !self.is_authority(&sender) { - return Err(EngineError::NotAuthorized(sender).into()); + return Err(EngineError::NotAuthorized(sender)); } self.broadcast_message(rlp.as_raw().to_vec()); if let Some(double) = self.votes.vote(message.clone(), &sender) { let height = message.vote_step.height as BlockNumber; self.validators.report_malicious(&sender, height, height, ::rlp::encode(&double).into_vec()); - return Err(EngineError::DoubleVote(sender).into()); + return Err(EngineError::DoubleVote(sender)); } trace!(target: "engine", "Handling a valid {:?} from {}.", message, sender); self.handle_valid_message(&message); @@ -545,12 +543,37 @@ impl Engine for Tendermint { Ok(()) } - /// Apply the block reward on finalisation of the block. - fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error>{ - ::engines::common::bestow_block_reward(block, self) + fn on_new_block(&self, block: &mut ExecutedBlock, epoch_begin: bool) -> Result<(), Error> { + if !epoch_begin { return Ok(()) } + + // genesis is never a new block, but might as well check. + let header = block.fields().header.clone(); + let first = header.number() == 0; + + let mut call = |to, data| { + let result = self.machine.execute_as_system( + block, + to, + U256::max_value(), // unbounded gas? maybe make configurable. + Some(data), + ); + + result.map_err(|e| format!("{}", e)) + }; + + self.validators.on_epoch_begin(first, &header, &mut call) } - fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { + /// Apply the block reward on finalisation of the block. + fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error>{ + ::engines::common::bestow_block_reward(block, self.block_reward) + } + + fn verify_local_seal(&self, _header: &Header) -> Result<(), Error> { + Ok(()) + } + + fn verify_block_basic(&self, header: &Header) -> Result<(), Error> { let seal_length = header.seal().len(); if seal_length == self.seal_fields() { // Either proposal or commit. @@ -568,28 +591,7 @@ impl Engine for Tendermint { } } - fn verify_block_unordered(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { - Ok(()) - } - - /// Verify gas limit. - fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> { - if header.number() == 0 { - return Err(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }).into()); - } - - let gas_limit_divisor = self.params().gas_limit_bound_divisor; - let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor; - let max_gas = parent.gas_limit().clone() + parent.gas_limit().clone() / gas_limit_divisor; - if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas { - self.validators.report_malicious(header.author(), header.number(), header.number(), Default::default()); - return Err(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit().clone() }).into()); - } - - Ok(()) - } - - fn verify_block_external(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { + fn verify_block_external(&self, header: &Header) -> Result<(), Error> { if let Ok(proposal) = ConsensusMessage::new_proposal(header) { let proposer = proposal.verify()?; if !self.is_authority(&proposer) { @@ -630,17 +632,17 @@ impl Engine for Tendermint { } } - fn signals_epoch_end(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) - -> super::EpochChange + fn signals_epoch_end(&self, header: &Header, aux: AuxiliaryData) + -> super::EpochChange { let first = header.number() == 0; - self.validators.signals_epoch_end(first, header, block, receipts) + self.validators.signals_epoch_end(first, header, aux) } fn is_epoch_end( &self, chain_head: &Header, - _chain: &super::Headers, + _chain: &super::Headers
, transition_store: &super::PendingTransitionStore, ) -> Option> { let first = chain_head.number() == 0; @@ -657,14 +659,14 @@ impl Engine for Tendermint { None } - fn epoch_verifier<'a>(&self, _header: &Header, proof: &'a [u8]) -> ConstructedVerifier<'a> { + fn epoch_verifier<'a>(&self, _header: &Header, proof: &'a [u8]) -> ConstructedVerifier<'a, EthereumMachine> { let (signal_number, set_proof, finality_proof) = match destructure_proofs(proof) { Ok(x) => x, Err(e) => return ConstructedVerifier::Err(e), }; let first = signal_number == 0; - match self.validators.epoch_set(first, self, signal_number, set_proof) { + match self.validators.epoch_set(first, &self.machine, signal_number, set_proof) { Ok((list, finalize)) => { let verifier = Box::new(EpochVerifier { subchain_validators: list, @@ -785,7 +787,7 @@ mod tests { use tests::helpers::*; use account_provider::AccountProvider; use spec::Spec; - use engines::{Engine, EngineError, Seal}; + use engines::{EthEngine, EngineError, Seal}; use engines::epoch::EpochVerifier; use super::*; @@ -810,7 +812,7 @@ mod tests { } } - fn vote(engine: &Engine, signer: F, height: usize, view: usize, step: Step, block_hash: Option) -> Bytes where F: FnOnce(H256) -> Result { + fn vote(engine: &EthEngine, signer: F, height: usize, view: usize, step: Step, block_hash: Option) -> Bytes where F: FnOnce(H256) -> Result { let mi = message_info_rlp(&VoteStep::new(height, view, step), block_hash); let m = message_full_rlp(&signer(keccak(&mi)).unwrap().into(), &mi); engine.handle_message(&m).unwrap(); @@ -834,7 +836,7 @@ mod tests { addr } - fn insert_and_register(tap: &Arc, engine: &Engine, acc: &str) -> Address { + fn insert_and_register(tap: &Arc, engine: &EthEngine, acc: &str) -> Address { let addr = insert_and_unlock(tap, acc); engine.set_signer(tap.clone(), addr.clone(), acc.into()); addr @@ -871,7 +873,7 @@ mod tests { let engine = Spec::new_test_tendermint().engine; let header = Header::default(); - let verify_result = engine.verify_block_basic(&header, None); + let verify_result = engine.verify_block_basic(&header); match verify_result { Err(Error::Block(BlockError::InvalidSealArity(_))) => {}, @@ -896,14 +898,14 @@ mod tests { let seal = proposal_seal(&tap, &header, 0); header.set_seal(seal); // Good proposer. - assert!(engine.verify_block_external(&header, None).is_ok()); + assert!(engine.verify_block_external(&header).is_ok()); let validator = insert_and_unlock(&tap, "0"); header.set_author(validator); let seal = proposal_seal(&tap, &header, 0); header.set_seal(seal); // Bad proposer. - match engine.verify_block_external(&header, None) { + match engine.verify_block_external(&header) { Err(Error::Engine(EngineError::NotProposer(_))) => {}, _ => panic!(), } @@ -913,7 +915,7 @@ mod tests { let seal = proposal_seal(&tap, &header, 0); header.set_seal(seal); // Not authority. - match engine.verify_block_external(&header, None) { + match engine.verify_block_external(&header) { Err(Error::Engine(EngineError::NotAuthorized(_))) => {}, _ => panic!(), }; @@ -943,7 +945,7 @@ mod tests { header.set_seal(seal.clone()); // One good signature is not enough. - match engine.verify_block_external(&header, None) { + match engine.verify_block_external(&header) { Err(Error::Engine(EngineError::BadSealFieldSize(_))) => {}, _ => panic!(), } @@ -954,7 +956,7 @@ mod tests { seal[2] = ::rlp::encode_list(&vec![H520::from(signature1.clone()), H520::from(signature0.clone())]).into_vec(); header.set_seal(seal.clone()); - assert!(engine.verify_block_external(&header, None).is_ok()); + assert!(engine.verify_block_external(&header).is_ok()); let bad_voter = insert_and_unlock(&tap, "101"); let bad_signature = tap.sign(bad_voter, None, keccak(vote_info)).unwrap(); @@ -963,7 +965,7 @@ mod tests { header.set_seal(seal); // One good and one bad signature. - match engine.verify_block_external(&header, None) { + match engine.verify_block_external(&header) { Err(Error::Engine(EngineError::NotAuthorized(_))) => {}, _ => panic!(), }; diff --git a/ethcore/src/engines/tendermint/params.rs b/ethcore/src/engines/tendermint/params.rs index 7ff3d697f..9cf1c2a6b 100644 --- a/ethcore/src/engines/tendermint/params.rs +++ b/ethcore/src/engines/tendermint/params.rs @@ -18,6 +18,7 @@ use ethjson; use time::Duration; +use bigint::prelude::U256; use super::super::validator_set::{ValidatorSet, new_validator_set}; use super::super::transition::Timeouts; use super::Step; @@ -28,6 +29,8 @@ pub struct TendermintParams { pub validators: Box, /// Timeout durations for different steps. pub timeouts: TendermintTimeouts, + /// Reward per block in base units. + pub block_reward: U256, } /// Base timeout of each step in ms. @@ -81,6 +84,7 @@ impl From for TendermintParams { precommit: p.timeout_precommit.map_or(dt.precommit, to_duration), commit: p.timeout_commit.map_or(dt.commit, to_duration), }, + block_reward: p.block_reward.map_or(U256::default(), Into::into), } } } diff --git a/ethcore/src/engines/transition.rs b/ethcore/src/engines/transition.rs index 590fb6db2..0e8c52a50 100644 --- a/ethcore/src/engines/transition.rs +++ b/ethcore/src/engines/transition.rs @@ -20,6 +20,7 @@ use std::sync::Weak; use time::Duration; use io::{IoContext, IoHandler, TimerToken}; use engines::Engine; +use parity_machine::Machine; /// Timeouts lookup pub trait Timeouts: Send + Sync { @@ -31,14 +32,14 @@ pub trait Timeouts: Send + Sync { } /// Timeout transition handling. -pub struct TransitionHandler { - engine: Weak, +pub struct TransitionHandler { + engine: Weak>, timeouts: Box>, } -impl TransitionHandler where S: Sync + Send + Clone { +impl TransitionHandler where S: Sync + Send + Clone { /// New step caller by timeouts. - pub fn new(engine: Weak, timeouts: Box>) -> Self { + pub fn new(engine: Weak>, timeouts: Box>) -> Self { TransitionHandler { engine: engine, timeouts: timeouts, @@ -54,7 +55,9 @@ fn set_timeout(io: &IoContext, timeout: Duration) { .unwrap_or_else(|e| warn!(target: "engine", "Failed to set consensus step timeout: {}.", e)) } -impl IoHandler for TransitionHandler where S: Sync + Send + Clone + 'static { +impl IoHandler for TransitionHandler + where S: Sync + Send + Clone + 'static, M: Machine +{ fn initialize(&self, io: &IoContext) { let initial = self.timeouts.initial(); trace!(target: "engine", "Setting the initial timeout to {}.", initial); diff --git a/ethcore/src/engines/validator_set/contract.rs b/ethcore/src/engines/validator_set/contract.rs index 3f035d424..54e86b98b 100644 --- a/ethcore/src/engines/validator_set/contract.rs +++ b/ethcore/src/engines/validator_set/contract.rs @@ -27,8 +27,8 @@ use futures::Future; use native_contracts::ValidatorReport as Provider; use client::EngineClient; -use engines::{Call, Engine}; use header::{Header, BlockNumber}; +use machine::{AuxiliaryData, Call, EthereumMachine}; use super::{ValidatorSet, SimpleList, SystemCall}; use super::safe_contract::ValidatorSafeContract; @@ -91,14 +91,13 @@ impl ValidatorSet for ValidatorContract { &self, first: bool, header: &Header, - block: Option<&[u8]>, - receipts: Option<&[::receipt::Receipt]>, - ) -> ::engines::EpochChange { - self.validators.signals_epoch_end(first, header, block, receipts) + aux: AuxiliaryData, + ) -> ::engines::EpochChange { + self.validators.signals_epoch_end(first, header, aux) } - fn epoch_set(&self, first: bool, engine: &Engine, number: BlockNumber, proof: &[u8]) -> Result<(SimpleList, Option), ::error::Error> { - self.validators.epoch_set(first, engine, number, proof) + fn epoch_set(&self, first: bool, machine: &EthereumMachine, number: BlockNumber, proof: &[u8]) -> Result<(SimpleList, Option), ::error::Error> { + self.validators.epoch_set(first, machine, number, proof) } fn contains_with_caller(&self, bh: &H256, address: &Address, caller: &Call) -> bool { @@ -182,7 +181,7 @@ mod tests { header.set_parent_hash(client.chain_info().best_block_hash); // `reportBenign` when the designated proposer releases block from the future (bad clock). - assert!(client.engine().verify_block_external(&header, None).is_err()); + assert!(client.engine().verify_block_external(&header).is_err()); // Seal a block. client.engine().step(); assert_eq!(client.chain_info().best_block_number, 1); @@ -190,7 +189,7 @@ mod tests { assert_eq!(client.call_contract(BlockId::Latest, validator_contract, "d8f2e0bf".from_hex().unwrap()).unwrap().to_hex(), "0000000000000000000000007d577a597b2742b498cb5cf0c26cdcd726d39e6e"); // Simulate a misbehaving validator by handling a double proposal. let header = client.best_block_header().decode(); - assert!(client.engine().verify_block_family(&header, &header, None).is_err()); + assert!(client.engine().verify_block_family(&header, &header).is_err()); // Seal a block. client.engine().step(); client.engine().step(); diff --git a/ethcore/src/engines/validator_set/mod.rs b/ethcore/src/engines/validator_set/mod.rs index 32060f8bf..8046bcff1 100644 --- a/ethcore/src/engines/validator_set/mod.rs +++ b/ethcore/src/engines/validator_set/mod.rs @@ -31,6 +31,7 @@ use bytes::Bytes; use ethjson::spec::ValidatorSet as ValidatorSpec; use client::EngineClient; use header::{Header, BlockNumber}; +use machine::{AuxiliaryData, Call, EthereumMachine}; #[cfg(test)] pub use self::test::TestSet; @@ -39,8 +40,6 @@ use self::contract::ValidatorContract; use self::safe_contract::ValidatorSafeContract; use self::multi::Multi; -use super::{Call, Engine}; - /// A system-calling closure. Enacts calls on a block's state from the system address. pub type SystemCall<'a> = FnMut(Address, Bytes) -> Result + 'a; @@ -113,9 +112,8 @@ pub trait ValidatorSet: Send + Sync { &self, first: bool, header: &Header, - block: Option<&[u8]>, - receipts: Option<&[::receipt::Receipt]>, - ) -> ::engines::EpochChange; + aux: AuxiliaryData, + ) -> ::engines::EpochChange; /// Recover the validator set from the given proof, the block number, and /// whether this header is first in its set. @@ -125,7 +123,7 @@ pub trait ValidatorSet: Send + Sync { /// /// Returns the set, along with a flag indicating whether finality of a specific /// hash should be proven. - fn epoch_set(&self, first: bool, engine: &Engine, number: BlockNumber, proof: &[u8]) + fn epoch_set(&self, first: bool, machine: &EthereumMachine, number: BlockNumber, proof: &[u8]) -> Result<(SimpleList, Option), ::error::Error>; /// Checks if a given address is a validator, with the given function diff --git a/ethcore/src/engines/validator_set/multi.rs b/ethcore/src/engines/validator_set/multi.rs index baf82b88d..9e7e693bb 100644 --- a/ethcore/src/engines/validator_set/multi.rs +++ b/ethcore/src/engines/validator_set/multi.rs @@ -18,7 +18,6 @@ use std::collections::BTreeMap; use std::sync::Weak; -use engines::{Call, Engine}; use bigint::hash::H256; use parking_lot::RwLock; use util::Address; @@ -26,6 +25,7 @@ use bytes::Bytes; use ids::BlockId; use header::{BlockNumber, Header}; use client::EngineClient; +use machine::{AuxiliaryData, Call, EthereumMachine}; use super::{SystemCall, ValidatorSet}; type BlockNumberLookup = Box Result + Send + Sync + 'static>; @@ -93,20 +93,20 @@ impl ValidatorSet for Multi { set.is_epoch_end(first, chain_head) } - fn signals_epoch_end(&self, _first: bool, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) - -> ::engines::EpochChange + fn signals_epoch_end(&self, _first: bool, header: &Header, aux: AuxiliaryData) + -> ::engines::EpochChange { let (set_block, set) = self.correct_set_by_number(header.number()); let first = set_block == header.number(); - set.signals_epoch_end(first, header, block, receipts) + set.signals_epoch_end(first, header, aux) } - fn epoch_set(&self, _first: bool, engine: &Engine, number: BlockNumber, proof: &[u8]) -> Result<(super::SimpleList, Option), ::error::Error> { + fn epoch_set(&self, _first: bool, machine: &EthereumMachine, number: BlockNumber, proof: &[u8]) -> Result<(super::SimpleList, Option), ::error::Error> { let (set_block, set) = self.correct_set_by_number(number); let first = set_block == number; - set.epoch_set(first, engine, number, proof) + set.epoch_set(first, machine, number, proof) } fn contains_with_caller(&self, bh: &H256, address: &Address, caller: &Call) -> bool { @@ -227,7 +227,7 @@ mod tests { let mut header = Header::new(); header.set_number(499); - match multi.signals_epoch_end(false, &header, None, None) { + match multi.signals_epoch_end(false, &header, Default::default()) { EpochChange::No => {}, _ => panic!("Expected no epoch signal change."), } @@ -235,7 +235,7 @@ mod tests { header.set_number(500); - match multi.signals_epoch_end(false, &header, None, None) { + match multi.signals_epoch_end(false, &header, Default::default()) { EpochChange::No => {}, _ => panic!("Expected no epoch signal change."), } diff --git a/ethcore/src/engines/validator_set/safe_contract.rs b/ethcore/src/engines/validator_set/safe_contract.rs index 3e1279228..7489a26e0 100644 --- a/ethcore/src/engines/validator_set/safe_contract.rs +++ b/ethcore/src/engines/validator_set/safe_contract.rs @@ -33,7 +33,7 @@ use rlp::{UntrustedRlp, RlpStream}; use basic_types::LogBloom; use client::EngineClient; -use engines::{Call, Engine}; +use machine::{AuxiliaryData, Call, EthereumMachine, AuxiliaryRequest}; use header::Header; use ids::BlockId; use log_entry::LogEntry; @@ -58,19 +58,19 @@ struct StateProof { provider: Provider, } -impl ::engines::StateDependentProof for StateProof { +impl ::engines::StateDependentProof for StateProof { fn generate_proof(&self, caller: &Call) -> Result, String> { prove_initial(&self.provider, &*self.header.lock(), caller) } - fn check_proof(&self, engine: &Engine, proof: &[u8]) -> Result<(), String> { + fn check_proof(&self, machine: &EthereumMachine, proof: &[u8]) -> Result<(), String> { let (header, state_items) = decode_first_proof(&UntrustedRlp::new(proof)) .map_err(|e| format!("proof incorrectly encoded: {}", e))?; if &header != &*self.header.lock(){ return Err("wrong header in proof".into()); } - check_first_proof(engine, &self.provider, header, &state_items).map(|_| ()) + check_first_proof(machine, &self.provider, header, &state_items).map(|_| ()) } } @@ -94,7 +94,7 @@ fn encode_first_proof(header: &Header, state_items: &[Vec]) -> Bytes { } // check a first proof: fetch the validator set at the given block. -fn check_first_proof(engine: &Engine, provider: &Provider, old_header: Header, state_items: &[DBValue]) +fn check_first_proof(machine: &EthereumMachine, provider: &Provider, old_header: Header, state_items: &[DBValue]) -> Result, String> { use transaction::{Action, Transaction}; @@ -117,12 +117,12 @@ fn check_first_proof(engine: &Engine, provider: &Provider, old_header: Header, s gas_used: 0.into(), }; - // check state proof using given engine. + // check state proof using given machine. let number = old_header.number(); provider.get_validators(move |a, d| { let from = Address::default(); let tx = Transaction { - nonce: engine.account_start_nonce(number), + nonce: machine.account_start_nonce(number), action: Action::Call(a), gas: PROVIDED_GAS.into(), gas_price: U256::default(), @@ -134,7 +134,7 @@ fn check_first_proof(engine: &Engine, provider: &Provider, old_header: Header, s state_items, *old_header.state_root(), &tx, - engine, + machine, &env_info, ); @@ -336,9 +336,11 @@ impl ValidatorSet for ValidatorSafeContract { None // no immediate transitions to contract. } - fn signals_epoch_end(&self, first: bool, header: &Header, _block: Option<&[u8]>, receipts: Option<&[Receipt]>) - -> ::engines::EpochChange + fn signals_epoch_end(&self, first: bool, header: &Header, aux: AuxiliaryData) + -> ::engines::EpochChange { + let receipts = aux.receipts; + // transition to the first block of a contract requires finality but has no log event. if first { debug!(target: "engine", "signalling transition to fresh contract."); @@ -358,7 +360,7 @@ impl ValidatorSet for ValidatorSafeContract { trace!(target: "engine", "detected epoch change event bloom"); match receipts { - None => ::engines::EpochChange::Unsure(::engines::Unsure::NeedsReceipts), + None => ::engines::EpochChange::Unsure(AuxiliaryRequest::Receipts), Some(receipts) => match self.extract_from_event(bloom, header, receipts) { None => ::engines::EpochChange::No, Some(list) => { @@ -372,7 +374,7 @@ impl ValidatorSet for ValidatorSafeContract { } } - fn epoch_set(&self, first: bool, engine: &Engine, _number: ::header::BlockNumber, proof: &[u8]) + fn epoch_set(&self, first: bool, machine: &EthereumMachine, _number: ::header::BlockNumber, proof: &[u8]) -> Result<(SimpleList, Option), ::error::Error> { let rlp = UntrustedRlp::new(proof); @@ -383,7 +385,7 @@ impl ValidatorSet for ValidatorSafeContract { let (old_header, state_items) = decode_first_proof(&rlp)?; let number = old_header.number(); let old_hash = old_header.hash(); - let addresses = check_first_proof(engine, &self.provider, old_header, &state_items) + let addresses = check_first_proof(machine, &self.provider, old_header, &state_items) .map_err(::engines::EngineError::InsufficientProof)?; trace!(target: "engine", "extracted epoch set at #{}: {} addresses", @@ -561,7 +563,8 @@ mod tests { #[test] fn detects_bloom() { use header::Header; - use engines::{EpochChange, Unsure}; + use engines::EpochChange; + use machine::AuxiliaryRequest; use log_entry::LogEntry; let client = generate_dummy_client_with_spec_and_accounts(Spec::new_validator_safe_contract, None); @@ -581,7 +584,7 @@ mod tests { }; new_header.set_log_bloom(event.bloom()); - match engine.signals_epoch_end(&new_header, None, None) { + match engine.signals_epoch_end(&new_header, Default::default()) { EpochChange::No => {}, _ => panic!("Expected bloom to be unrecognized."), }; @@ -590,8 +593,8 @@ mod tests { event.topics.push(last_hash); new_header.set_log_bloom(event.bloom()); - match engine.signals_epoch_end(&new_header, None, None) { - EpochChange::Unsure(Unsure::NeedsReceipts) => {}, + match engine.signals_epoch_end(&new_header, Default::default()) { + EpochChange::Unsure(AuxiliaryRequest::Receipts) => {}, _ => panic!("Expected bloom to be recognized."), }; } @@ -607,7 +610,7 @@ mod tests { let mut new_header = Header::default(); new_header.set_number(0); // so the validator set doesn't look for a log - match engine.signals_epoch_end(&new_header, None, None) { + match engine.signals_epoch_end(&new_header, Default::default()) { EpochChange::Yes(Proof::WithState(_)) => {}, _ => panic!("Expected state to be required to prove initial signal"), }; diff --git a/ethcore/src/engines/validator_set/simple_list.rs b/ethcore/src/engines/validator_set/simple_list.rs index eeeb4cb80..21ea871b9 100644 --- a/ethcore/src/engines/validator_set/simple_list.rs +++ b/ethcore/src/engines/validator_set/simple_list.rs @@ -20,7 +20,7 @@ use heapsize::HeapSizeOf; use bigint::hash::H256; use util::Address; -use engines::{Call, Engine}; +use machine::{AuxiliaryData, Call, EthereumMachine}; use header::{BlockNumber, Header}; use super::ValidatorSet; @@ -76,13 +76,13 @@ impl ValidatorSet for SimpleList { } } - fn signals_epoch_end(&self, _: bool, _: &Header, _: Option<&[u8]>, _: Option<&[::receipt::Receipt]>) - -> ::engines::EpochChange + fn signals_epoch_end(&self, _: bool, _: &Header, _: AuxiliaryData) + -> ::engines::EpochChange { ::engines::EpochChange::No } - fn epoch_set(&self, _first: bool, _: &Engine, _: BlockNumber, _: &[u8]) -> Result<(SimpleList, Option), ::error::Error> { + fn epoch_set(&self, _first: bool, _: &EthereumMachine, _: BlockNumber, _: &[u8]) -> Result<(SimpleList, Option), ::error::Error> { Ok((self.clone(), None)) } diff --git a/ethcore/src/engines/validator_set/test.rs b/ethcore/src/engines/validator_set/test.rs index e2a0c36c7..2e7cbe958 100644 --- a/ethcore/src/engines/validator_set/test.rs +++ b/ethcore/src/engines/validator_set/test.rs @@ -24,7 +24,7 @@ use bigint::hash::H256; use util::Address; use bytes::Bytes; -use engines::{Call, Engine}; +use machine::{AuxiliaryData, Call, EthereumMachine}; use header::{Header, BlockNumber}; use super::{ValidatorSet, SimpleList}; @@ -58,13 +58,13 @@ impl ValidatorSet for TestSet { fn is_epoch_end(&self, _first: bool, _chain_head: &Header) -> Option> { None } - fn signals_epoch_end(&self, _: bool, _: &Header, _: Option<&[u8]>, _: Option<&[::receipt::Receipt]>) - -> ::engines::EpochChange + fn signals_epoch_end(&self, _: bool, _: &Header, _: AuxiliaryData) + -> ::engines::EpochChange { ::engines::EpochChange::No } - fn epoch_set(&self, _: bool, _: &Engine, _: BlockNumber, _: &[u8]) -> Result<(SimpleList, Option), ::error::Error> { + fn epoch_set(&self, _: bool, _: &EthereumMachine, _: BlockNumber, _: &[u8]) -> Result<(SimpleList, Option), ::error::Error> { Ok((self.validator.clone(), None)) } diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 07508a93a..e7a75af58 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -17,33 +17,20 @@ use std::path::Path; use std::cmp; use std::collections::{BTreeMap, HashMap}; -use std::sync::{Arc, Weak}; +use std::sync::Arc; use hash::{KECCAK_EMPTY_LIST_RLP}; use ethash::{quick_get_difficulty, slow_hash_block_number, EthashManager, OptimizeFor}; use bigint::prelude::U256; use bigint::hash::{H256, H64}; -use util::*; use unexpected::{OutOfBounds, Mismatch}; use block::*; -use builtin::Builtin; -use vm::EnvInfo; -use error::{BlockError, Error, TransactionError}; -use trace::{Tracer, ExecutiveTracer, RewardType}; -use header::{Header, BlockNumber}; -use state::CleanupMode; -use spec::CommonParams; -use transaction::{UnverifiedTransaction, SignedTransaction}; -use engines::{self, Engine}; -use evm::Schedule; +use error::{BlockError, Error}; +use header::Header; +use engines::{self, Engine, EthEngine}; use ethjson; use rlp::{self, UntrustedRlp}; -use vm::LastHashes; +use machine::EthereumMachine; use semantic_version::SemanticVersion; -use tx_filter::{TransactionFilter}; -use client::EngineClient; - -/// Parity tries to round block.gas_limit to multiple of this constant -pub const PARITY_GAS_LIMIT_DETERMINANT: U256 = U256([37, 0, 0, 0]); /// Number of blocks in an ethash snapshot. // make dependent on difficulty incrment divisor? @@ -68,12 +55,6 @@ pub struct EthashParams { pub duration_limit: u64, /// Homestead transition block number. pub homestead_transition: u64, - /// DAO hard-fork transition block (X). - pub dao_hardfork_transition: u64, - /// DAO hard-fork refund contract address (C). - pub dao_hardfork_beneficiary: Address, - /// DAO hard-fork DAO accounts list (L) - pub dao_hardfork_accounts: Vec
, /// Transition block for a change of difficulty params (currently just bound_divisor). pub difficulty_hardfork_transition: u64, /// Difficulty param after the difficulty transition. @@ -82,30 +63,14 @@ pub struct EthashParams { pub bomb_defuse_transition: u64, /// Number of first block where EIP-100 rules begin. pub eip100b_transition: u64, - /// Number of first block where EIP-150 rules begin. - pub eip150_transition: u64, - /// Number of first block where EIP-160 rules begin. - pub eip160_transition: u64, - /// Number of first block where EIP-161.abc begin. - pub eip161abc_transition: u64, - /// Number of first block where EIP-161.d begins. - pub eip161d_transition: u64, /// Number of first block where ECIP-1010 begins. pub ecip1010_pause_transition: u64, /// Number of first block where ECIP-1010 ends. pub ecip1010_continue_transition: u64, /// Total block number for one ECIP-1017 era. pub ecip1017_era_rounds: u64, - /// Maximum amount of code that can be deploying into a contract. - pub max_code_size: u64, - /// Number of first block where the max gas limit becomes effective. - pub max_gas_limit_transition: u64, - /// Maximum valid block gas limit, - pub max_gas_limit: U256, - /// Number of first block where the minimum gas price becomes effective. - pub min_gas_price_transition: u64, - /// Do not alow transactions with lower gas price. - pub min_gas_price: U256, + /// Block reward in base units. + pub block_reward: U256, /// EIP-649 transition block. pub eip649_transition: u64, /// EIP-649 bomb delay. @@ -123,25 +88,14 @@ impl From for EthashParams { metropolis_difficulty_increment_divisor: p.metropolis_difficulty_increment_divisor.map_or(9, Into::into), duration_limit: p.duration_limit.map_or(0, Into::into), homestead_transition: p.homestead_transition.map_or(0, Into::into), - dao_hardfork_transition: p.dao_hardfork_transition.map_or(u64::max_value(), Into::into), - dao_hardfork_beneficiary: p.dao_hardfork_beneficiary.map_or_else(Address::new, Into::into), - dao_hardfork_accounts: p.dao_hardfork_accounts.unwrap_or_else(Vec::new).into_iter().map(Into::into).collect(), difficulty_hardfork_transition: p.difficulty_hardfork_transition.map_or(u64::max_value(), Into::into), difficulty_hardfork_bound_divisor: p.difficulty_hardfork_bound_divisor.map_or(p.difficulty_bound_divisor.into(), Into::into), bomb_defuse_transition: p.bomb_defuse_transition.map_or(u64::max_value(), Into::into), eip100b_transition: p.eip100b_transition.map_or(u64::max_value(), Into::into), - eip150_transition: p.eip150_transition.map_or(0, Into::into), - eip160_transition: p.eip160_transition.map_or(0, Into::into), - eip161abc_transition: p.eip161abc_transition.map_or(0, Into::into), - eip161d_transition: p.eip161d_transition.map_or(u64::max_value(), Into::into), ecip1010_pause_transition: p.ecip1010_pause_transition.map_or(u64::max_value(), Into::into), ecip1010_continue_transition: p.ecip1010_continue_transition.map_or(u64::max_value(), Into::into), ecip1017_era_rounds: p.ecip1017_era_rounds.map_or(u64::max_value(), Into::into), - max_code_size: p.max_code_size.map_or(u64::max_value(), Into::into), - max_gas_limit_transition: p.max_gas_limit_transition.map_or(u64::max_value(), Into::into), - max_gas_limit: p.max_gas_limit.map_or(U256::max_value(), Into::into), - min_gas_price_transition: p.min_gas_price_transition.map_or(u64::max_value(), Into::into), - min_gas_price: p.min_gas_price.map_or(U256::zero(), Into::into), + block_reward: p.block_reward.map_or_else(Default::default, Into::into), eip649_transition: p.eip649_transition.map_or(u64::max_value(), Into::into), eip649_delay: p.eip649_delay.map_or(DEFAULT_EIP649_DELAY, Into::into), eip649_reward: p.eip649_reward.map(Into::into), @@ -152,27 +106,22 @@ impl From for EthashParams { /// Engine using Ethash proof-of-work consensus algorithm, suitable for Ethereum /// mainnet chains in the Olympic, Frontier and Homestead eras. pub struct Ethash { - params: CommonParams, ethash_params: EthashParams, - builtins: BTreeMap, pow: EthashManager, - tx_filter: Option, + machine: EthereumMachine, } impl Ethash { /// Create a new instance of Ethash engine pub fn new>>( cache_dir: &Path, - params: CommonParams, ethash_params: EthashParams, - builtins: BTreeMap, + machine: EthereumMachine, optimize_for: T, ) -> Arc { Arc::new(Ethash { - tx_filter: TransactionFilter::from_params(¶ms), - params, ethash_params, - builtins, + machine, pow: EthashManager::new(cache_dir.as_ref(), optimize_for.into()), }) } @@ -186,26 +135,23 @@ impl Ethash { // for any block in the chain. // in the future, we might move the Ethash epoch // caching onto this mechanism as well. -impl engines::EpochVerifier for Arc { +impl engines::EpochVerifier for Arc { fn verify_light(&self, _header: &Header) -> Result<(), Error> { Ok(()) } fn verify_heavy(&self, header: &Header) -> Result<(), Error> { - self.verify_block_unordered(header, None) + self.verify_block_unordered(header) } } -impl Engine for Arc { +impl Engine for Arc { fn name(&self) -> &str { "Ethash" } fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) } - // Two fields - mix + fn machine(&self) -> &EthereumMachine { &self.machine } + + // Two fields - nonce and mix. fn seal_fields(&self) -> usize { 2 } - fn params(&self) -> &CommonParams { &self.params } fn additional_params(&self) -> HashMap { hash_map!["registrar".to_owned() => self.params().registrar.hex()] } - fn builtins(&self) -> &BTreeMap { - &self.builtins - } - /// Additional engine-specific information for the user/developer concerning `header`. fn extra_info(&self, header: &Header) -> BTreeMap { if header.seal().len() == self.seal_fields() { @@ -218,90 +164,16 @@ impl Engine for Arc { } } - fn schedule(&self, block_number: BlockNumber) -> Schedule { - trace!(target: "client", "Creating schedule. fCML={}, bGCML={}", self.ethash_params.homestead_transition, self.ethash_params.eip150_transition); - - if block_number < self.ethash_params.homestead_transition { - Schedule::new_frontier() - } else if block_number < self.ethash_params.eip150_transition { - Schedule::new_homestead() - } else { - /// There's no max_code_size transition so we tie it to eip161abc - let max_code_size = if block_number >= self.ethash_params.eip161abc_transition { self.ethash_params.max_code_size as usize } else { usize::max_value() }; - let mut schedule = Schedule::new_post_eip150( - max_code_size, - block_number >= self.ethash_params.eip160_transition, - block_number >= self.ethash_params.eip161abc_transition, - block_number >= self.ethash_params.eip161d_transition); - - self.params().update_schedule(block_number, &mut schedule); - schedule - } - } - - fn signing_chain_id(&self, env_info: &EnvInfo) -> Option { - if env_info.number >= self.params().eip155_transition { - Some(self.params().chain_id) - } else { - None - } - } - - fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, mut gas_ceil_target: U256) { + fn populate_from_parent(&self, header: &mut Header, parent: &Header) { let difficulty = self.calculate_difficulty(header, parent); - if header.number() >= self.ethash_params.max_gas_limit_transition && gas_ceil_target > self.ethash_params.max_gas_limit { - warn!("Gas limit target is limited to {}", self.ethash_params.max_gas_limit); - gas_ceil_target = self.ethash_params.max_gas_limit; - } - let gas_limit = { - let gas_limit = parent.gas_limit().clone(); - let bound_divisor = self.params().gas_limit_bound_divisor; - let lower_limit = gas_limit - gas_limit / bound_divisor + 1.into(); - let upper_limit = gas_limit + gas_limit / bound_divisor - 1.into(); - let gas_limit = if gas_limit < gas_floor_target { - let gas_limit = cmp::min(gas_floor_target, upper_limit); - round_block_gas_limit(gas_limit, lower_limit, upper_limit) - } else if gas_limit > gas_ceil_target { - let gas_limit = cmp::max(gas_ceil_target, lower_limit); - round_block_gas_limit(gas_limit, lower_limit, upper_limit) - } else { - let total_lower_limit = cmp::max(lower_limit, gas_floor_target); - let total_upper_limit = cmp::min(upper_limit, gas_ceil_target); - let gas_limit = cmp::max(gas_floor_target, cmp::min(total_upper_limit, - lower_limit + (header.gas_used().clone() * 6.into() / 5.into()) / bound_divisor)); - round_block_gas_limit(gas_limit, total_lower_limit, total_upper_limit) - }; - // ensure that we are not violating protocol limits - debug_assert!(gas_limit >= lower_limit); - debug_assert!(gas_limit <= upper_limit); - gas_limit - }; header.set_difficulty(difficulty); - header.set_gas_limit(gas_limit); - if header.number() >= self.ethash_params.dao_hardfork_transition && - header.number() <= self.ethash_params.dao_hardfork_transition + 9 { - header.set_extra_data(b"dao-hard-fork"[..].to_owned()); - } - header.note_dirty(); -// info!("ethash: populate_from_parent #{}: difficulty={} and gas_limit={}", header.number(), header.difficulty(), header.gas_limit()); } fn on_new_block( &self, - block: &mut ExecutedBlock, - last_hashes: Arc, + _block: &mut ExecutedBlock, _begins_epoch: bool, ) -> Result<(), Error> { - let parent_hash = block.fields().header.parent_hash().clone(); - engines::common::push_last_hash(block, last_hashes, self, &parent_hash)?; - if block.fields().header.number() == self.ethash_params.dao_hardfork_transition { - let state = block.fields_mut().state; - for child in &self.ethash_params.dao_hardfork_accounts { - let beneficiary = &self.ethash_params.dao_hardfork_beneficiary; - state.balance(child) - .and_then(|b| state.transfer_balance(child, beneficiary, &b, CleanupMode::NoEmpty))?; - } - } Ok(()) } @@ -309,68 +181,54 @@ impl Engine for Arc { /// This assumes that all uncles are valid uncles (i.e. of at least one generation before the current). fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> { use std::ops::Shr; - let tracing_enabled = block.tracing_enabled(); - let fields = block.fields_mut(); - let reward = if fields.header.number() >= self.ethash_params.eip649_transition { - self.ethash_params.eip649_reward.unwrap_or(self.params().block_reward) + use parity_machine::{LiveBlock, WithBalances}; + + let author = *LiveBlock::header(&*block).author(); + let number = LiveBlock::header(&*block).number(); + + let reward = if number >= self.ethash_params.eip649_transition { + self.ethash_params.eip649_reward.unwrap_or(self.ethash_params.block_reward) } else { - self.params().block_reward + self.ethash_params.block_reward }; let eras_rounds = self.ethash_params.ecip1017_era_rounds; - let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, reward, fields.header.number()); - let mut tracer = ExecutiveTracer::default(); + let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, reward, number); + + let n_uncles = LiveBlock::uncles(&*block).len(); // Bestow block reward - let result_block_reward = reward + reward.shr(5) * U256::from(fields.uncles.len()); - fields.state.add_balance( - fields.header.author(), - &result_block_reward, - CleanupMode::NoEmpty - )?; + let result_block_reward = reward + reward.shr(5) * U256::from(n_uncles); + let mut uncle_rewards = Vec::with_capacity(n_uncles); - if tracing_enabled { - let block_author = fields.header.author().clone(); - tracer.trace_reward(block_author, result_block_reward, RewardType::Block); - } + self.machine.add_balance(block, &author, &result_block_reward)?; - // Bestow uncle rewards - let current_number = fields.header.number(); - for u in fields.uncles.iter() { - let uncle_author = u.author().clone(); - let result_uncle_reward: U256; - - if eras == 0 { - result_uncle_reward = (reward * U256::from(8 + u.number() - current_number)).shr(3); - fields.state.add_balance( - u.author(), - &result_uncle_reward, - CleanupMode::NoEmpty - ) + // bestow uncle rewards. + for u in LiveBlock::uncles(&*block) { + let uncle_author = u.author(); + let result_uncle_reward = if eras == 0 { + (reward * U256::from(8 + u.number() - number)).shr(3) } else { - result_uncle_reward = reward.shr(5); - fields.state.add_balance( - u.author(), - &result_uncle_reward, - CleanupMode::NoEmpty - ) - }?; + reward.shr(5) + }; - // Trace uncle rewards - if tracing_enabled { - tracer.trace_reward(uncle_author, result_uncle_reward, RewardType::Uncle); - } + uncle_rewards.push((*uncle_author, result_uncle_reward)); } - // Commit state so that we can actually figure out the state root. - fields.state.commit()?; - if tracing_enabled { - fields.traces.as_mut().map(|mut traces| traces.push(tracer.drain())); + for &(ref a, ref reward) in &uncle_rewards { + self.machine.add_balance(block, a, reward)?; } - Ok(()) + + // note and trace. + self.machine.note_rewards(block, &[(author, result_block_reward)], &uncle_rewards) } - fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { + fn verify_local_seal(&self, header: &Header) -> Result<(), Error> { + self.verify_block_basic(header) + .and_then(|_| self.verify_block_unordered(header)) + } + + fn verify_block_basic(&self, header: &Header) -> Result<(), Error> { // check the seal fields. if header.seal().len() != self.seal_fields() { return Err(From::from(BlockError::InvalidSealArity( @@ -395,12 +253,6 @@ impl Engine for Arc { return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty().clone()), max: None, found: difficulty }))); } - if header.number() >= self.ethash_params.dao_hardfork_transition && - header.number() <= self.ethash_params.dao_hardfork_transition + 9 && - header.extra_data()[..] != b"dao-hard-fork"[..] { - return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: None, max: None, found: 0 }))); - } - if header.gas_limit() > &0x7fffffffffffffffu64.into() { return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: None, max: Some(0x7fffffffffffffffu64.into()), found: header.gas_limit().clone() }))); } @@ -408,7 +260,7 @@ impl Engine for Arc { Ok(()) } - fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { + fn verify_block_unordered(&self, header: &Header) -> Result<(), Error> { if header.seal().len() != self.seal_fields() { return Err(From::from(BlockError::InvalidSealArity( Mismatch { expected: self.seal_fields(), found: header.seal().len() } @@ -427,7 +279,7 @@ impl Engine for Arc { Ok(()) } - fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> { + fn verify_block_family(&self, header: &Header, parent: &Header) -> Result<(), Error> { // we should not calculate difficulty for genesis blocks if header.number() == 0 { return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }))); @@ -438,81 +290,17 @@ impl Engine for Arc { if header.difficulty() != &expected_difficulty { return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: expected_difficulty, found: header.difficulty().clone() }))) } - let gas_limit_divisor = self.params().gas_limit_bound_divisor; - let parent_gas_limit = *parent.gas_limit(); - let min_gas = parent_gas_limit - parent_gas_limit / gas_limit_divisor; - let max_gas = parent_gas_limit + parent_gas_limit / gas_limit_divisor; - if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas { - return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit().clone() }))); - } - if header.number() >= self.ethash_params.max_gas_limit_transition && header.gas_limit() > &self.ethash_params.max_gas_limit && header.gas_limit() > &parent_gas_limit { - return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(self.ethash_params.max_gas_limit), found: header.gas_limit().clone() }))); - } + Ok(()) } - fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> Result<(), Error> { - if header.number() >= self.ethash_params.min_gas_price_transition && t.gas_price < self.ethash_params.min_gas_price { - return Err(TransactionError::InsufficientGasPrice { minimal: self.ethash_params.min_gas_price, got: t.gas_price }.into()); - } - - let check_low_s = header.number() >= self.ethash_params.homestead_transition; - let chain_id = if header.number() >= self.params().eip155_transition { Some(self.params().chain_id) } else { None }; - t.verify_basic(check_low_s, chain_id, false)?; - Ok(()) - } - - fn verify_transaction(&self, t: UnverifiedTransaction, header: &Header) -> Result { - let signed = SignedTransaction::new(t)?; - if !self.tx_filter.as_ref().map_or(true, |filter| filter.transaction_allowed(header.parent_hash(), &signed)) { - return Err(From::from(TransactionError::NotAllowed)); - } - Ok(signed) - } - - fn epoch_verifier<'a>(&self, _header: &Header, _proof: &'a [u8]) -> engines::ConstructedVerifier<'a> { + fn epoch_verifier<'a>(&self, _header: &Header, _proof: &'a [u8]) -> engines::ConstructedVerifier<'a, EthereumMachine> { engines::ConstructedVerifier::Trusted(Box::new(self.clone())) } fn snapshot_components(&self) -> Option> { Some(Box::new(::snapshot::PowSnapshot::new(SNAPSHOT_BLOCKS, MAX_SNAPSHOT_BLOCKS))) } - - fn register_client(&self, client: Weak) { - if let Some(ref filter) = self.tx_filter { - filter.register_client(client); - } - } - -} - -// Try to round gas_limit a bit so that: -// 1) it will still be in desired range -// 2) it will be a nearest (with tendency to increase) multiple of PARITY_GAS_LIMIT_DETERMINANT -fn round_block_gas_limit(gas_limit: U256, lower_limit: U256, upper_limit: U256) -> U256 { - let increased_gas_limit = gas_limit + (PARITY_GAS_LIMIT_DETERMINANT - gas_limit % PARITY_GAS_LIMIT_DETERMINANT); - if increased_gas_limit > upper_limit { - let decreased_gas_limit = increased_gas_limit - PARITY_GAS_LIMIT_DETERMINANT; - if decreased_gas_limit < lower_limit { - gas_limit - } else { - decreased_gas_limit - } - } else { - increased_gas_limit - } -} - -fn ecip1017_eras_block_reward(era_rounds: u64, mut reward: U256, block_number:u64) -> (u64, U256){ - let eras = if block_number != 0 && block_number % era_rounds == 0 { - block_number / era_rounds - 1 - } else { - block_number / era_rounds - }; - for _ in 0..eras { - reward = reward / U256::from(5) * U256::from(4); - } - (eras, reward) } #[cfg_attr(feature="dev", allow(wrong_self_convention))] @@ -611,7 +399,7 @@ impl Ethash { } impl Header { - /// Get the none field of the header. + /// Get the nonce field of the header. pub fn nonce(&self) -> H64 { rlp::decode(&self.seal()[1]) } @@ -620,29 +408,34 @@ impl Header { pub fn mix_hash(&self) -> H256 { rlp::decode(&self.seal()[0]) } +} - /// Set the nonce and mix hash fields of the header. - pub fn set_nonce_and_mix_hash(&mut self, nonce: &H64, mix_hash: &H256) { - self.set_seal(vec![rlp::encode(mix_hash).into_vec(), rlp::encode(nonce).into_vec()]); +fn ecip1017_eras_block_reward(era_rounds: u64, mut reward: U256, block_number:u64) -> (u64, U256) { + let eras = if block_number != 0 && block_number % era_rounds == 0 { + block_number / era_rounds - 1 + } else { + block_number / era_rounds + }; + for _ in 0..eras { + reward = reward / U256::from(5) * U256::from(4); } + (eras, reward) } #[cfg(test)] mod tests { use std::str::FromStr; - use std::collections::BTreeMap; use std::sync::Arc; use bigint::prelude::U256; use bigint::hash::{H64, H256}; use util::*; use block::*; use tests::helpers::*; - use engines::Engine; use error::{BlockError, Error}; use header::Header; use spec::Spec; - use super::super::{new_morden, new_homestead_test}; - use super::{Ethash, EthashParams, PARITY_GAS_LIMIT_DETERMINANT, ecip1017_eras_block_reward}; + use super::super::{new_morden, new_homestead_test_machine}; + use super::{Ethash, EthashParams, ecip1017_eras_block_reward}; use rlp; fn test_spec() -> Spec { @@ -661,6 +454,38 @@ mod tests { assert_eq!(b.state().balance(&Address::zero()).unwrap(), U256::from_str("4563918244f40000").unwrap()); } + #[test] + fn has_valid_ecip1017_eras_block_reward() { + let eras_rounds = 5000000; + + let start_reward: U256 = "4563918244F40000".parse().unwrap(); + + let block_number = 0; + let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number); + assert_eq!(0, eras); + assert_eq!(U256::from_str("4563918244F40000").unwrap(), reward); + + let block_number = 5000000; + let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number); + assert_eq!(0, eras); + assert_eq!(U256::from_str("4563918244F40000").unwrap(), reward); + + let block_number = 10000000; + let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number); + assert_eq!(1, eras); + assert_eq!(U256::from_str("3782DACE9D900000").unwrap(), reward); + + let block_number = 20000000; + let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number); + assert_eq!(3, eras); + assert_eq!(U256::from_str("2386F26FC1000000").unwrap(), reward); + + let block_number = 80000000; + let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number); + assert_eq!(15, eras); + assert_eq!(U256::from_str("271000000000000").unwrap(), reward); + } + #[test] fn on_close_block_with_uncle() { let spec = test_spec(); @@ -699,10 +524,9 @@ mod tests { #[test] fn can_do_seal_verification_fail() { let engine = test_spec().engine; - //let engine = Ethash::new_test(test_spec()); let header: Header = Header::default(); - let verify_result = engine.verify_block_basic(&header, None); + let verify_result = engine.verify_block_basic(&header); match verify_result { Err(Error::Block(BlockError::InvalidSealArity(_))) => {}, @@ -717,7 +541,7 @@ mod tests { let mut header: Header = Header::default(); header.set_seal(vec![rlp::encode(&H256::zero()).into_vec(), rlp::encode(&H64::zero()).into_vec()]); - let verify_result = engine.verify_block_basic(&header, None); + let verify_result = engine.verify_block_basic(&header); match verify_result { Err(Error::Block(BlockError::DifficultyOutOfBounds(_))) => {}, @@ -733,7 +557,7 @@ mod tests { header.set_seal(vec![rlp::encode(&H256::zero()).into_vec(), rlp::encode(&H64::zero()).into_vec()]); header.set_difficulty(U256::from_str("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa").unwrap()); - let verify_result = engine.verify_block_basic(&header, None); + let verify_result = engine.verify_block_basic(&header); match verify_result { Err(Error::Block(BlockError::InvalidProofOfWork(_))) => {}, @@ -747,7 +571,7 @@ mod tests { let engine = test_spec().engine; let header: Header = Header::default(); - let verify_result = engine.verify_block_unordered(&header, None); + let verify_result = engine.verify_block_unordered(&header); match verify_result { Err(Error::Block(BlockError::InvalidSealArity(_))) => {}, @@ -761,7 +585,7 @@ mod tests { let engine = test_spec().engine; let mut header: Header = Header::default(); header.set_seal(vec![rlp::encode(&H256::zero()).into_vec(), rlp::encode(&H64::zero()).into_vec()]); - let verify_result = engine.verify_block_unordered(&header, None); + let verify_result = engine.verify_block_unordered(&header); match verify_result { Err(Error::Block(BlockError::MismatchedH256SealElement(_))) => {}, @@ -777,7 +601,7 @@ mod tests { header.set_seal(vec![rlp::encode(&H256::from("b251bd2e0283d0658f2cadfdc8ca619b5de94eca5742725e2e757dd13ed7503d")).into_vec(), rlp::encode(&H64::zero()).into_vec()]); header.set_difficulty(U256::from_str("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa").unwrap()); - let verify_result = engine.verify_block_unordered(&header, None); + let verify_result = engine.verify_block_unordered(&header); match verify_result { Err(Error::Block(BlockError::InvalidProofOfWork(_))) => {}, @@ -792,7 +616,7 @@ mod tests { let header: Header = Header::default(); let parent_header: Header = Header::default(); - let verify_result = engine.verify_block_family(&header, &parent_header, None); + let verify_result = engine.verify_block_family(&header, &parent_header); match verify_result { Err(Error::Block(BlockError::RidiculousNumber(_))) => {}, @@ -809,7 +633,7 @@ mod tests { let mut parent_header: Header = Header::default(); parent_header.set_number(1); - let verify_result = engine.verify_block_family(&header, &parent_header, None); + let verify_result = engine.verify_block_family(&header, &parent_header); match verify_result { Err(Error::Block(BlockError::InvalidDifficulty(_))) => {}, @@ -818,24 +642,6 @@ mod tests { } } - #[test] - fn can_verify_block_family_gas_fail() { - let engine = test_spec().engine; - let mut header: Header = Header::default(); - header.set_number(2); - header.set_difficulty(U256::from_str("0000000000000000000000000000000000000000000000000000000000020000").unwrap()); - let mut parent_header: Header = Header::default(); - parent_header.set_number(1); - - let verify_result = engine.verify_block_family(&header, &parent_header, None); - - match verify_result { - Err(Error::Block(BlockError::InvalidGasLimit(_))) => {}, - Err(_) => { panic!("should be invalid difficulty fail (got {:?})", verify_result); }, - _ => { panic!("Should be error, got Ok"); }, - } - } - #[test] fn test_difficulty_to_boundary() { // result of f(0) is undefined, so do not assert the result @@ -848,9 +654,9 @@ mod tests { #[test] fn difficulty_frontier() { - let spec = new_homestead_test(); + let machine = new_homestead_test_machine(); let ethparams = get_default_ethash_params(); - let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new(), None); + let ethash = Ethash::new(&::std::env::temp_dir(), ethparams, machine, None); let mut parent_header = Header::default(); parent_header.set_number(1000000); @@ -866,9 +672,9 @@ mod tests { #[test] fn difficulty_homestead() { - let spec = new_homestead_test(); + let machine = new_homestead_test_machine(); let ethparams = get_default_ethash_params(); - let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new(), None); + let ethash = Ethash::new(&::std::env::temp_dir(), ethparams, machine, None); let mut parent_header = Header::default(); parent_header.set_number(1500000); @@ -882,46 +688,14 @@ mod tests { assert_eq!(U256::from_str("1fc50f118efe").unwrap(), difficulty); } - #[test] - fn has_valid_ecip1017_eras_block_reward() { - let eras_rounds = 5000000; - - let start_reward: U256 = "4563918244F40000".parse().unwrap(); - - let block_number = 0; - let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number); - assert_eq!(0, eras); - assert_eq!(U256::from_str("4563918244F40000").unwrap(), reward); - - let block_number = 5000000; - let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number); - assert_eq!(0, eras); - assert_eq!(U256::from_str("4563918244F40000").unwrap(), reward); - - let block_number = 10000000; - let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number); - assert_eq!(1, eras); - assert_eq!(U256::from_str("3782DACE9D900000").unwrap(), reward); - - let block_number = 20000000; - let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number); - assert_eq!(3, eras); - assert_eq!(U256::from_str("2386F26FC1000000").unwrap(), reward); - - let block_number = 80000000; - let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number); - assert_eq!(15, eras); - assert_eq!(U256::from_str("271000000000000").unwrap(), reward); - } - #[test] fn difficulty_classic_bomb_delay() { - let spec = new_homestead_test(); + let machine = new_homestead_test_machine(); let ethparams = EthashParams { ecip1010_pause_transition: 3000000, ..get_default_ethash_params() }; - let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new(), None); + let ethash = Ethash::new(&::std::env::temp_dir(), ethparams, machine, None); let mut parent_header = Header::default(); parent_header.set_number(3500000); @@ -949,13 +723,13 @@ mod tests { #[test] fn test_difficulty_bomb_continue() { - let spec = new_homestead_test(); + let machine = new_homestead_test_machine(); let ethparams = EthashParams { ecip1010_pause_transition: 3000000, ecip1010_continue_transition: 5000000, ..get_default_ethash_params() }; - let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new(), None); + let ethash = Ethash::new(&::std::env::temp_dir(), ethparams, machine, None); let mut parent_header = Header::default(); parent_header.set_number(5000102); @@ -997,55 +771,11 @@ mod tests { ); } - #[test] - fn gas_limit_is_multiple_of_determinant() { - let spec = new_homestead_test(); - let ethparams = get_default_ethash_params(); - let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new(), None); - let mut parent = Header::new(); - let mut header = Header::new(); - header.set_number(1); - - // this test will work for this constant only - assert_eq!(PARITY_GAS_LIMIT_DETERMINANT, U256::from(37)); - - // when parent.gas_limit < gas_floor_target: - parent.set_gas_limit(U256::from(50_000)); - ethash.populate_from_parent(&mut header, &parent, U256::from(100_000), U256::from(200_000)); - assert_eq!(*header.gas_limit(), U256::from(50_024)); - - // when parent.gas_limit > gas_ceil_target: - parent.set_gas_limit(U256::from(250_000)); - ethash.populate_from_parent(&mut header, &parent, U256::from(100_000), U256::from(200_000)); - assert_eq!(*header.gas_limit(), U256::from(249_787)); - - // when parent.gas_limit is in miner's range - header.set_gas_used(U256::from(150_000)); - parent.set_gas_limit(U256::from(150_000)); - ethash.populate_from_parent(&mut header, &parent, U256::from(100_000), U256::from(200_000)); - assert_eq!(*header.gas_limit(), U256::from(150_035)); - - // when parent.gas_limit is in miner's range - // && we can NOT increase it to be multiple of constant - header.set_gas_used(U256::from(150_000)); - parent.set_gas_limit(U256::from(150_000)); - ethash.populate_from_parent(&mut header, &parent, U256::from(100_000), U256::from(150_002)); - assert_eq!(*header.gas_limit(), U256::from(149_998)); - - // when parent.gas_limit is in miner's range - // && we can NOT increase it to be multiple of constant - // && we can NOT decrease it to be multiple of constant - header.set_gas_used(U256::from(150_000)); - parent.set_gas_limit(U256::from(150_000)); - ethash.populate_from_parent(&mut header, &parent, U256::from(150_000), U256::from(150_002)); - assert_eq!(*header.gas_limit(), U256::from(150_002)); - } - #[test] fn difficulty_max_timestamp() { - let spec = new_homestead_test(); + let machine = new_homestead_test_machine(); let ethparams = get_default_ethash_params(); - let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new(), None); + let ethash = Ethash::new(&::std::env::temp_dir(), ethparams, machine, None); let mut parent_header = Header::default(); parent_header.set_number(1000000); @@ -1058,82 +788,4 @@ mod tests { let difficulty = ethash.calculate_difficulty(&header, &parent_header); assert_eq!(U256::from(12543204905719u64), difficulty); } - - #[test] - fn rejects_blocks_over_max_gas_limit() { - let spec = new_homestead_test(); - let mut ethparams = get_default_ethash_params(); - ethparams.max_gas_limit_transition = 10; - ethparams.max_gas_limit = 100_000.into(); - - let mut parent_header = Header::default(); - parent_header.set_number(1); - parent_header.set_gas_limit(100_000.into()); - let mut header = Header::default(); - header.set_number(parent_header.number() + 1); - header.set_gas_limit(100_001.into()); - header.set_difficulty(ethparams.minimum_difficulty); - let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new(), None); - assert!(ethash.verify_block_family(&header, &parent_header, None).is_ok()); - - parent_header.set_number(9); - header.set_number(parent_header.number() + 1); - - parent_header.set_gas_limit(99_999.into()); - header.set_gas_limit(100_000.into()); - assert!(ethash.verify_block_family(&header, &parent_header, None).is_ok()); - - parent_header.set_gas_limit(200_000.into()); - header.set_gas_limit(200_000.into()); - assert!(ethash.verify_block_family(&header, &parent_header, None).is_ok()); - - parent_header.set_gas_limit(100_000.into()); - header.set_gas_limit(100_001.into()); - assert!(ethash.verify_block_family(&header, &parent_header, None).is_err()); - - parent_header.set_gas_limit(200_000.into()); - header.set_gas_limit(200_001.into()); - assert!(ethash.verify_block_family(&header, &parent_header, None).is_err()); - } - - #[test] - fn rejects_transactions_below_min_gas_price() { - use ethkey::{Generator, Random}; - use transaction::{Transaction, Action}; - - let spec = new_homestead_test(); - let mut ethparams = get_default_ethash_params(); - ethparams.min_gas_price_transition = 10; - ethparams.min_gas_price = 100000.into(); - - let mut header = Header::default(); - header.set_number(1); - - let keypair = Random.generate().unwrap(); - let tx1 = Transaction { - action: Action::Create, - value: U256::zero(), - data: Vec::new(), - gas: 100_000.into(), - gas_price: 100_000.into(), - nonce: U256::zero(), - }.sign(keypair.secret(), None).into(); - - let tx2 = Transaction { - action: Action::Create, - value: U256::zero(), - data: Vec::new(), - gas: 100_000.into(), - gas_price: 99_999.into(), - nonce: U256::zero(), - }.sign(keypair.secret(), None).into(); - - let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new(), None); - assert!(ethash.verify_transaction_basic(&tx1, &header).is_ok()); - assert!(ethash.verify_transaction_basic(&tx2, &header).is_ok()); - - header.set_number(10); - assert!(ethash.verify_transaction_basic(&tx1, &header).is_ok()); - assert!(ethash.verify_transaction_basic(&tx2, &header).is_err()); - } } diff --git a/ethcore/src/ethereum/mod.rs b/ethcore/src/ethereum/mod.rs index 617980e0f..0b45113d4 100644 --- a/ethcore/src/ethereum/mod.rs +++ b/ethcore/src/ethereum/mod.rs @@ -27,6 +27,7 @@ pub mod denominations; pub use self::ethash::{Ethash}; pub use self::denominations::*; +use machine::EthereumMachine; use super::spec::*; /// Most recent fork block that we support on Mainnet. @@ -45,6 +46,10 @@ fn load<'a, T: Into>>>(params: T, b: &[u8]) -> Spec { }.expect("chain spec is invalid") } +fn load_machine(b: &[u8]) -> EthereumMachine { + Spec::load_machine(b).expect("chain spec is invalid") +} + /// Create a new Foundation Olympic chain spec. pub fn new_olympic<'a, T: Into>>(params: T) -> Spec { load(params.into(), include_bytes!("../../res/ethereum/olympic.json")) @@ -106,6 +111,20 @@ pub fn new_byzantium_test() -> Spec { load(None, include_bytes!("../../res/ether /// Create a new Foundation Constantinople era spec. pub fn new_constantinople_test() -> Spec { load(None, include_bytes!("../../res/ethereum/constantinople_test.json")) } +// For tests + +/// Create a new Foundation Frontier-era chain spec as though it never changes to Homestead. +pub fn new_frontier_test_machine() -> EthereumMachine { load_machine(include_bytes!("../../res/ethereum/frontier_test.json")) } + +/// Create a new Foundation Homestead-era chain spec as though it never changed from Frontier. +pub fn new_homestead_test_machine() -> EthereumMachine { load_machine(include_bytes!("../../res/ethereum/homestead_test.json")) } + +/// Create a new Foundation Byzantium era spec. +pub fn new_byzantium_test_machine() -> EthereumMachine { load_machine(include_bytes!("../../res/ethereum/byzantium_test.json")) } + +/// Create a new Foundation Constantinople era spec. +pub fn new_constantinople_test_machine() -> EthereumMachine { load_machine(include_bytes!("../../res/ethereum/constantinople_test.json")) } + #[cfg(test)] mod tests { use bigint::prelude::U256; diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 38239b4a4..bcf239570 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -23,7 +23,7 @@ use bigint::hash::H256; use util::*; use bytes::{Bytes, BytesRef}; use state::{Backend as StateBackend, State, Substate, CleanupMode}; -use engines::Engine; +use machine::EthereumMachine as Machine; use vm::EnvInfo; use error::ExecutionError; use evm::{CallType, Factory, Finalize, FinalizationResult}; @@ -154,10 +154,8 @@ impl TransactOptions { } } -pub fn executor(engine: &E, vm_factory: &Factory, params: &ActionParams) - -> Box where E: Engine + ?Sized -{ - if engine.supports_wasm() && params.code.as_ref().map_or(false, |code| code.len() > 4 && &code[0..4] == WASM_MAGIC_NUMBER) { +pub fn executor(machine: &Machine, vm_factory: &Factory, params: &ActionParams) -> Box { + if machine.supports_wasm() && params.code.as_ref().map_or(false, |code| code.len() > 4 && &code[0..4] == WASM_MAGIC_NUMBER) { Box::new( wasm::WasmInterpreter::new() // prefer to fail fast @@ -169,32 +167,32 @@ pub fn executor(engine: &E, vm_factory: &Factory, params: &ActionParams) } /// Transaction executor. -pub struct Executive<'a, B: 'a + StateBackend, E: 'a + Engine + ?Sized> { +pub struct Executive<'a, B: 'a + StateBackend> { state: &'a mut State, info: &'a EnvInfo, - engine: &'a E, + machine: &'a Machine, depth: usize, static_flag: bool, } -impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { +impl<'a, B: 'a + StateBackend> Executive<'a, B> { /// Basic constructor. - pub fn new(state: &'a mut State, info: &'a EnvInfo, engine: &'a E) -> Self { + pub fn new(state: &'a mut State, info: &'a EnvInfo, machine: &'a Machine) -> Self { Executive { state: state, info: info, - engine: engine, + machine: machine, depth: 0, static_flag: false, } } /// Populates executive from parent properties. Increments executive depth. - pub fn from_parent(state: &'a mut State, info: &'a EnvInfo, engine: &'a E, parent_depth: usize, static_flag: bool) -> Self { + pub fn from_parent(state: &'a mut State, info: &'a EnvInfo, machine: &'a Machine, parent_depth: usize, static_flag: bool) -> Self { Executive { state: state, info: info, - engine: engine, + machine: machine, depth: parent_depth + 1, static_flag: static_flag, } @@ -209,9 +207,9 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { tracer: &'any mut T, vm_tracer: &'any mut V, static_call: bool, - ) -> Externalities<'any, T, V, B, E> where T: Tracer, V: VMTracer { + ) -> Externalities<'any, T, V, B> where T: Tracer, V: VMTracer { let is_static = self.static_flag || static_call; - Externalities::new(self.state, self.info, self.engine, self.depth, origin_info, substate, output, tracer, vm_tracer, is_static) + Externalities::new(self.state, self.info, self.machine, self.depth, origin_info, substate, output, tracer, vm_tracer, is_static) } /// This function should be used to execute transaction. @@ -250,7 +248,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { let sender = t.sender(); let nonce = self.state.nonce(&sender)?; - let schedule = self.engine.schedule(self.info.number); + let schedule = self.machine.schedule(self.info.number); let base_gas_required = U256::from(t.gas_required(&schedule)); if t.gas < base_gas_required { @@ -298,7 +296,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { let (result, output) = match t.action { Action::Create => { - let (new_address, code_hash) = contract_address(self.engine.create_address_scheme(self.info.number), &sender, &nonce, &t.data); + let (new_address, code_hash) = contract_address(self.machine.create_address_scheme(self.info.number), &sender, &nonce, &t.data); let params = ActionParams { code_address: new_address.clone(), code_hash: code_hash, @@ -355,19 +353,19 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { let vm_factory = self.state.vm_factory(); let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer, static_call); trace!(target: "executive", "ext.schedule.have_delegate_call: {}", ext.schedule().have_delegate_call); - return executor(self.engine, &vm_factory, ¶ms).exec(params, &mut ext).finalize(ext); + return executor(self.machine, &vm_factory, ¶ms).exec(params, &mut ext).finalize(ext); } // Start in new thread to reset stack // TODO [todr] No thread builder yet, so we need to reset once for a while // https://github.com/aturon/crossbeam/issues/16 crossbeam::scope(|scope| { - let engine = self.engine; + let machine = self.machine; let vm_factory = self.state.vm_factory(); let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer, static_call); scope.spawn(move || { - executor(engine, &vm_factory, ¶ms).exec(params, &mut ext).finalize(ext) + executor(machine, &vm_factory, ¶ms).exec(params, &mut ext).finalize(ext) }) }).join() } @@ -396,7 +394,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { // backup used in case of running out of gas self.state.checkpoint(); - let schedule = self.engine.schedule(self.info.number); + let schedule = self.machine.schedule(self.info.number); // at first, transfer value to destination if let ActionValue::Transfer(val) = params.value { @@ -404,7 +402,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { } // if destination is builtin, try to execute it - if let Some(builtin) = self.engine.builtin(¶ms.code_address, self.info.number) { + if let Some(builtin) = self.machine.builtin(¶ms.code_address, self.info.number) { // Engines aren't supposed to return builtins until activation, but // prefer to fail rather than silently break consensus. if !builtin.is_active(self.info.number) { @@ -542,7 +540,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { let mut unconfirmed_substate = Substate::new(); // create contract and transfer value to it if necessary - let schedule = self.engine.schedule(self.info.number); + let schedule = self.machine.schedule(self.info.number); let nonce_offset = if schedule.no_empty {1} else {0}.into(); let prev_bal = self.state.balance(¶ms.address)?; if let ActionValue::Transfer(val) = params.value { @@ -591,7 +589,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { trace: Vec, vm_trace: Option ) -> ExecutionResult { - let schedule = self.engine.schedule(self.info.number); + let schedule = self.machine.schedule(self.info.number); // refunds from SSTORE nonzero -> zero let sstore_refunds = U256::from(schedule.sstore_refund_gas) * substate.sstore_clears_count; @@ -700,6 +698,7 @@ mod tests { use vm::{ActionParams, ActionValue, CallType, EnvInfo, CreateContractAddress}; use evm::{Factory, VMType}; use error::ExecutionError; + use machine::EthereumMachine; use state::{Substate, CleanupMode}; use tests::helpers::*; use trace::trace; @@ -707,6 +706,12 @@ mod tests { use trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, VMTracer, NoopVMTracer, ExecutiveVMTracer}; use transaction::{Action, Transaction}; + fn make_frontier_machine(max_depth: usize) -> EthereumMachine { + let mut machine = ::ethereum::new_frontier_test_machine(); + machine.set_schedule_creation_rules(Box::new(move |s, _| s.max_depth = max_depth)); + machine + } + #[test] fn test_contract_address() { let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); @@ -728,11 +733,11 @@ mod tests { let mut state = get_temp_state_with_factory(factory); state.add_balance(&sender, &U256::from(0x100u64), CleanupMode::NoEmpty).unwrap(); let info = EnvInfo::default(); - let engine = TestEngine::new(0); + let machine = make_frontier_machine(0); let mut substate = Substate::new(); let FinalizationResult { gas_left, .. } = { - let mut ex = Executive::new(&mut state, &info, &engine); + let mut ex = Executive::new(&mut state, &info, &machine); ex.create(params, &mut substate, &mut None, &mut NoopTracer, &mut NoopVMTracer).unwrap() }; @@ -786,11 +791,11 @@ mod tests { let mut state = get_temp_state_with_factory(factory); state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty).unwrap(); let info = EnvInfo::default(); - let engine = TestEngine::new(0); + let machine = make_frontier_machine(0); let mut substate = Substate::new(); let FinalizationResult { gas_left, .. } = { - let mut ex = Executive::new(&mut state, &info, &engine); + let mut ex = Executive::new(&mut state, &info, &machine); ex.create(params, &mut substate, &mut None, &mut NoopTracer, &mut NoopVMTracer).unwrap() }; @@ -842,13 +847,13 @@ mod tests { let mut state = get_temp_state(); state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty).unwrap(); let info = EnvInfo::default(); - let engine = TestEngine::new(5); + let machine = make_frontier_machine(5); let mut substate = Substate::new(); let mut tracer = ExecutiveTracer::default(); let mut vm_tracer = ExecutiveVMTracer::toplevel(); let FinalizationResult { gas_left, .. } = { - let mut ex = Executive::new(&mut state, &info, &engine); + let mut ex = Executive::new(&mut state, &info, &machine); let output = BytesRef::Fixed(&mut[0u8;0]); ex.call(params, &mut substate, output, &mut tracer, &mut vm_tracer).unwrap() }; @@ -951,13 +956,13 @@ mod tests { let mut state = get_temp_state(); state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty).unwrap(); let info = EnvInfo::default(); - let engine = TestEngine::new(5); + let machine = make_frontier_machine(5); let mut substate = Substate::new(); let mut tracer = ExecutiveTracer::default(); let mut vm_tracer = ExecutiveVMTracer::toplevel(); let FinalizationResult { gas_left, .. } = { - let mut ex = Executive::new(&mut state, &info, &engine); + let mut ex = Executive::new(&mut state, &info, &machine); ex.create(params.clone(), &mut substate, &mut None, &mut tracer, &mut vm_tracer).unwrap() }; @@ -1038,11 +1043,11 @@ mod tests { let mut state = get_temp_state_with_factory(factory); state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty).unwrap(); let info = EnvInfo::default(); - let engine = TestEngine::new(0); + let machine = make_frontier_machine(0); let mut substate = Substate::new(); let FinalizationResult { gas_left, .. } = { - let mut ex = Executive::new(&mut state, &info, &engine); + let mut ex = Executive::new(&mut state, &info, &machine); ex.create(params, &mut substate, &mut None, &mut NoopTracer, &mut NoopVMTracer).unwrap() }; @@ -1089,11 +1094,11 @@ mod tests { let mut state = get_temp_state_with_factory(factory); state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty).unwrap(); let info = EnvInfo::default(); - let engine = TestEngine::new(1024); + let machine = make_frontier_machine(1024); let mut substate = Substate::new(); { - let mut ex = Executive::new(&mut state, &info, &engine); + let mut ex = Executive::new(&mut state, &info, &machine); ex.create(params, &mut substate, &mut None, &mut NoopTracer, &mut NoopVMTracer).unwrap(); } @@ -1149,11 +1154,11 @@ mod tests { state.add_balance(&sender, &U256::from(100_000), CleanupMode::NoEmpty).unwrap(); let info = EnvInfo::default(); - let engine = TestEngine::new(0); + let machine = make_frontier_machine(0); let mut substate = Substate::new(); let FinalizationResult { gas_left, .. } = { - let mut ex = Executive::new(&mut state, &info, &engine); + let mut ex = Executive::new(&mut state, &info, &machine); ex.call(params, &mut substate, BytesRef::Fixed(&mut []), &mut NoopTracer, &mut NoopVMTracer).unwrap() }; @@ -1193,11 +1198,11 @@ mod tests { let mut state = get_temp_state_with_factory(factory); state.init_code(&address, code).unwrap(); let info = EnvInfo::default(); - let engine = TestEngine::new(0); + let machine = make_frontier_machine(0); let mut substate = Substate::new(); let FinalizationResult { gas_left, .. } = { - let mut ex = Executive::new(&mut state, &info, &engine); + let mut ex = Executive::new(&mut state, &info, &machine); ex.call(params, &mut substate, BytesRef::Fixed(&mut []), &mut NoopTracer, &mut NoopVMTracer).unwrap() }; @@ -1226,10 +1231,10 @@ mod tests { state.add_balance(&sender, &U256::from(18), CleanupMode::NoEmpty).unwrap(); let mut info = EnvInfo::default(); info.gas_limit = U256::from(100_000); - let engine = TestEngine::new(0); + let machine = make_frontier_machine(0); let executed = { - let mut ex = Executive::new(&mut state, &info, &engine); + let mut ex = Executive::new(&mut state, &info, &machine); let opts = TransactOptions::with_no_tracing(); ex.transact(&t, opts).unwrap() }; @@ -1263,10 +1268,10 @@ mod tests { state.add_balance(&sender, &U256::from(17), CleanupMode::NoEmpty).unwrap(); let mut info = EnvInfo::default(); info.gas_limit = U256::from(100_000); - let engine = TestEngine::new(0); + let machine = make_frontier_machine(0); let res = { - let mut ex = Executive::new(&mut state, &info, &engine); + let mut ex = Executive::new(&mut state, &info, &machine); let opts = TransactOptions::with_no_tracing(); ex.transact(&t, opts) }; @@ -1296,10 +1301,10 @@ mod tests { let mut info = EnvInfo::default(); info.gas_used = U256::from(20_000); info.gas_limit = U256::from(100_000); - let engine = TestEngine::new(0); + let machine = make_frontier_machine(0); let res = { - let mut ex = Executive::new(&mut state, &info, &engine); + let mut ex = Executive::new(&mut state, &info, &machine); let opts = TransactOptions::with_no_tracing(); ex.transact(&t, opts) }; @@ -1329,10 +1334,10 @@ mod tests { state.add_balance(&sender, &U256::from(100_017), CleanupMode::NoEmpty).unwrap(); let mut info = EnvInfo::default(); info.gas_limit = U256::from(100_000); - let engine = TestEngine::new(0); + let machine = make_frontier_machine(0); let res = { - let mut ex = Executive::new(&mut state, &info, &engine); + let mut ex = Executive::new(&mut state, &info, &machine); let opts = TransactOptions::with_no_tracing(); ex.transact(&t, opts) }; @@ -1362,11 +1367,11 @@ mod tests { let mut state = get_temp_state_with_factory(factory); state.add_balance(&sender, &U256::from_str("152d02c7e14af6800000").unwrap(), CleanupMode::NoEmpty).unwrap(); let info = EnvInfo::default(); - let engine = TestEngine::new(0); + let machine = make_frontier_machine(0); let mut substate = Substate::new(); let result = { - let mut ex = Executive::new(&mut state, &info, &engine); + let mut ex = Executive::new(&mut state, &info, &machine); ex.create(params, &mut substate, &mut None, &mut NoopTracer, &mut NoopVMTracer) }; @@ -1397,12 +1402,12 @@ mod tests { let mut state = get_temp_state_with_factory(factory); state.add_balance(&sender, &U256::from_str("152d02c7e14af68000000").unwrap(), CleanupMode::NoEmpty).unwrap(); let info = EnvInfo::default(); - let engine = TestEngine::new_byzantium(); + let machine = ::ethereum::new_byzantium_test_machine(); let mut substate = Substate::new(); let mut output = [0u8; 14]; let FinalizationResult { gas_left: result, .. } = { - let mut ex = Executive::new(&mut state, &info, &engine); + let mut ex = Executive::new(&mut state, &info, &machine); ex.call(params, &mut substate, BytesRef::Fixed(&mut output), &mut NoopTracer, &mut NoopVMTracer).unwrap() }; diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index d429f1277..caf14967b 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -22,7 +22,7 @@ use bigint::hash::H256; use util::*; use bytes::{Bytes, BytesRef}; use state::{Backend as StateBackend, State, Substate, CleanupMode}; -use engines::Engine; +use machine::EthereumMachine as Machine; use executive::*; use vm::{ self, ActionParams, ActionValue, EnvInfo, CallType, Schedule, @@ -65,12 +65,12 @@ impl OriginInfo { } /// Implementation of evm Externalities. -pub struct Externalities<'a, T: 'a, V: 'a, B: 'a, E: 'a + Engine + ?Sized> +pub struct Externalities<'a, T: 'a, V: 'a, B: 'a> where T: Tracer, V: VMTracer, B: StateBackend { state: &'a mut State, env_info: &'a EnvInfo, - engine: &'a E, + machine: &'a Machine, depth: usize, origin_info: OriginInfo, substate: &'a mut Substate, @@ -81,14 +81,14 @@ pub struct Externalities<'a, T: 'a, V: 'a, B: 'a, E: 'a + Engine + ?Sized> static_flag: bool, } -impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Externalities<'a, T, V, B, E> - where T: Tracer, V: VMTracer, B: StateBackend, E: Engine + ?Sized +impl<'a, T: 'a, V: 'a, B: 'a> Externalities<'a, T, V, B> + where T: Tracer, V: VMTracer, B: StateBackend { /// Basic `Externalities` constructor. #[cfg_attr(feature="dev", allow(too_many_arguments))] pub fn new(state: &'a mut State, env_info: &'a EnvInfo, - engine: &'a E, + machine: &'a Machine, depth: usize, origin_info: OriginInfo, substate: &'a mut Substate, @@ -100,11 +100,11 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Externalities<'a, T, V, B, E> Externalities { state: state, env_info: env_info, - engine: engine, + machine: machine, depth: depth, origin_info: origin_info, substate: substate, - schedule: engine.schedule(env_info.number), + schedule: machine.schedule(env_info.number), output: output, tracer: tracer, vm_tracer: vm_tracer, @@ -113,8 +113,8 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Externalities<'a, T, V, B, E> } } -impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for Externalities<'a, T, V, B, E> - where T: Tracer, V: VMTracer, B: StateBackend, E: Engine + ?Sized +impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> + where T: Tracer, V: VMTracer, B: StateBackend { fn storage_at(&self, key: &H256) -> vm::Result { self.state.storage_at(&self.origin_info.address, key).map_err(Into::into) @@ -149,8 +149,8 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for Externalities<'a, T, V, B, E> } fn blockhash(&mut self, number: &U256) -> H256 { - if self.env_info.number + 256 >= self.engine.params().eip210_transition { - let blockhash_contract_address = self.engine.params().eip210_contract_address; + if self.env_info.number + 256 >= self.machine.params().eip210_transition { + let blockhash_contract_address = self.machine.params().eip210_contract_address; let code_res = self.state.code(&blockhash_contract_address) .and_then(|code| self.state.code_hash(&blockhash_contract_address).map(|hash| (code, hash))); @@ -165,7 +165,7 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for Externalities<'a, T, V, B, E> value: ActionValue::Apparent(self.origin_info.value), code_address: blockhash_contract_address.clone(), origin: self.origin_info.origin.clone(), - gas: self.engine.params().eip210_contract_gas, + gas: self.machine.params().eip210_contract_gas, gas_price: 0.into(), code: code, code_hash: Some(code_hash), @@ -174,7 +174,7 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for Externalities<'a, T, V, B, E> }; let mut output = H256::new(); - let mut ex = Executive::new(self.state, self.env_info, self.engine); + let mut ex = Executive::new(self.state, self.env_info, self.machine); let r = ex.call(params, self.substate, BytesRef::Fixed(&mut output), self.tracer, self.vm_tracer); trace!("ext: blockhash contract({}) -> {:?}({}) self.env_info.number={}\n", number, r, output, self.env_info.number); output @@ -229,7 +229,7 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for Externalities<'a, T, V, B, E> } } } - let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.depth, self.static_flag); + let mut ex = Executive::from_parent(self.state, self.env_info, self.machine, self.depth, self.static_flag); // TODO: handle internal error separately match ex.create(params, self.substate, &mut None, self.tracer, self.vm_tracer) { @@ -283,7 +283,7 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for Externalities<'a, T, V, B, E> params.value = ActionValue::Transfer(value); } - let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.depth, self.static_flag); + let mut ex = Executive::from_parent(self.state, self.env_info, self.machine, self.depth, self.static_flag); match ex.call(params, self.substate, BytesRef::Fixed(output), self.tracer, self.vm_tracer) { Ok(FinalizationResult{ gas_left, return_data, apply_state: true }) => MessageCallResult::Success(gas_left, return_data), @@ -414,7 +414,6 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for Externalities<'a, T, V, B, E> #[cfg(test)] mod tests { use util::*; - use engines::Engine; use evm::{EnvInfo, Ext, CallType}; use state::{State, Substate}; use tests::helpers::*; @@ -444,7 +443,7 @@ mod tests { struct TestSetup { state: State<::state_db::StateDB>, - engine: Arc, + machine: ::machine::EthereumMachine, sub_state: Substate, env_info: EnvInfo } @@ -459,7 +458,7 @@ mod tests { fn new() -> Self { TestSetup { state: get_temp_state(), - engine: get_test_spec().engine, + machine: ::spec::Spec::new_test_machine(), sub_state: Substate::new(), env_info: get_test_env_info() } @@ -473,7 +472,7 @@ mod tests { let mut tracer = NoopTracer; let mut vm_tracer = NoopVMTracer; - let ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer, false); + let ext = Externalities::new(state, &setup.env_info, &setup.machine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer, false); assert_eq!(ext.env_info().number, 100); } @@ -485,7 +484,7 @@ mod tests { let mut tracer = NoopTracer; let mut vm_tracer = NoopVMTracer; - let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer, false); + let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer, false); let hash = ext.blockhash(&"0000000000000000000000000000000000000000000000000000000000120000".parse::().unwrap()); @@ -509,7 +508,7 @@ mod tests { let mut tracer = NoopTracer; let mut vm_tracer = NoopVMTracer; - let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer, false); + let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer, false); let hash = ext.blockhash(&"0000000000000000000000000000000000000000000000000000000000120000".parse::().unwrap()); @@ -524,7 +523,7 @@ mod tests { let mut tracer = NoopTracer; let mut vm_tracer = NoopVMTracer; - let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer, false); + let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer, false); let mut output = vec![]; @@ -552,7 +551,7 @@ mod tests { let mut vm_tracer = NoopVMTracer; { - let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer, false); + let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer, false); ext.log(log_topics, &log_data).unwrap(); } @@ -569,7 +568,7 @@ mod tests { let mut vm_tracer = NoopVMTracer; { - let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer, false); + let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer, false); ext.suicide(refund_account).unwrap(); } diff --git a/ethcore/src/header.rs b/ethcore/src/header.rs index 5cbd0d22b..3f0c0109f 100644 --- a/ethcore/src/header.rs +++ b/ethcore/src/header.rs @@ -312,6 +312,23 @@ impl HeapSizeOf for Header { } } +impl ::parity_machine::Header for Header { + fn bare_hash(&self) -> H256 { Header::bare_hash(self) } + + fn hash(&self) -> H256 { Header::hash(self) } + + fn seal(&self) -> &[Vec] { Header::seal(self) } + + fn author(&self) -> &Address { Header::author(self) } + + fn number(&self) -> BlockNumber { Header::number(self) } +} + +impl ::parity_machine::ScoredHeader for Header { + fn score(&self) -> &U256 { self.difficulty() } + fn set_score(&mut self, score: U256) { self.set_difficulty(score) } +} + #[cfg(test)] mod tests { use rustc_hex::FromHex; diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs index 3a28a4825..a962ca04f 100644 --- a/ethcore/src/json_tests/executive.rs +++ b/ethcore/src/json_tests/executive.rs @@ -18,7 +18,6 @@ use std::sync::Arc; use super::test_common::*; use state::{Backend as StateBackend, State, Substate}; use executive::*; -use engines::Engine; use evm::{VMType, Finalize}; use vm::{ self, ActionParams, CallType, Schedule, Ext, @@ -34,6 +33,7 @@ use bytes::{Bytes, BytesRef}; use trie; use rlp::RlpStream; use hash::keccak; +use machine::EthereumMachine as Machine; #[derive(Debug, PartialEq, Clone)] struct CallCreate { @@ -57,22 +57,22 @@ impl From for CallCreate { /// Tiny wrapper around executive externalities. /// Stores callcreates. -struct TestExt<'a, T: 'a, V: 'a, B: 'a, E: 'a> - where T: Tracer, V: VMTracer, B: StateBackend, E: Engine + ?Sized +struct TestExt<'a, T: 'a, V: 'a, B: 'a> + where T: Tracer, V: VMTracer, B: StateBackend { - ext: Externalities<'a, T, V, B, E>, + ext: Externalities<'a, T, V, B>, callcreates: Vec, nonce: U256, sender: Address, } -impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> TestExt<'a, T, V, B, E> - where T: Tracer, V: VMTracer, B: StateBackend, E: Engine + ?Sized +impl<'a, T: 'a, V: 'a, B: 'a> TestExt<'a, T, V, B> + where T: Tracer, V: VMTracer, B: StateBackend, { fn new( state: &'a mut State, info: &'a EnvInfo, - engine: &'a E, + machine: &'a Machine, depth: usize, origin_info: OriginInfo, substate: &'a mut Substate, @@ -84,15 +84,15 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> TestExt<'a, T, V, B, E> let static_call = false; Ok(TestExt { nonce: state.nonce(&address)?, - ext: Externalities::new(state, info, engine, depth, origin_info, substate, output, tracer, vm_tracer, static_call), + ext: Externalities::new(state, info, machine, depth, origin_info, substate, output, tracer, vm_tracer, static_call), callcreates: vec![], sender: address, }) } } -impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for TestExt<'a, T, V, B, E> - where T: Tracer, V: VMTracer, B: StateBackend, E: Engine + ?Sized +impl<'a, T: 'a, V: 'a, B: 'a> Ext for TestExt<'a, T, V, B> + where T: Tracer, V: VMTracer, B: StateBackend { fn storage_at(&self, key: &H256) -> vm::Result { self.ext.storage_at(key) @@ -231,7 +231,12 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec { let mut state = get_temp_state(); state.populate_from(From::from(vm.pre_state.clone())); let info = From::from(vm.env); - let engine = TestEngine::new(1); + let machine = { + let mut machine = ::ethereum::new_frontier_test_machine(); + machine.set_schedule_creation_rules(Box::new(move |s, _| s.max_depth = 1)); + machine + }; + let params = ActionParams::from(vm.transaction); let mut substate = Substate::new(); @@ -245,7 +250,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec { let mut ex = try_fail!(TestExt::new( &mut state, &info, - &engine, + &machine, 0, OriginInfo::from(¶ms), &mut substate, diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 689cc67f8..3c8c0714f 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -101,6 +101,7 @@ extern crate lru_cache; extern crate native_contracts; extern crate num_cpus; extern crate num; +extern crate parity_machine; extern crate parking_lot; extern crate price_info; extern crate rand; @@ -154,6 +155,7 @@ pub mod error; pub mod ethereum; pub mod executed; pub mod header; +pub mod machine; pub mod migrations; pub mod miner; pub mod pod_state; diff --git a/ethcore/src/machine.rs b/ethcore/src/machine.rs new file mode 100644 index 000000000..5b37947db --- /dev/null +++ b/ethcore/src/machine.rs @@ -0,0 +1,531 @@ +// 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 . + +//! Ethereum-like state machine definition. + +use std::collections::BTreeMap; +use std::cmp; +use std::sync::Arc; + +use block::ExecutedBlock; +use builtin::Builtin; +use client::BlockChainClient; +use error::{Error, TransactionError}; +use executive::Executive; +use header::{BlockNumber, Header}; +use spec::CommonParams; +use state::{CleanupMode, Substate}; +use trace::{NoopTracer, NoopVMTracer, Tracer, ExecutiveTracer, RewardType}; +use transaction::{SYSTEM_ADDRESS, UnverifiedTransaction, SignedTransaction}; +use tx_filter::TransactionFilter; + +use bigint::prelude::U256; +use bytes::BytesRef; +use util::Address; +use vm::{CallType, ActionParams, ActionValue}; +use vm::{EnvInfo, Schedule, CreateContractAddress}; + +/// Parity tries to round block.gas_limit to multiple of this constant +pub const PARITY_GAS_LIMIT_DETERMINANT: U256 = U256([37, 0, 0, 0]); + +/// Ethash-specific extensions. +#[derive(Debug, Clone)] +pub struct EthashExtensions { + /// Homestead transition block number. + pub homestead_transition: BlockNumber, + /// EIP150 transition block number. + pub eip150_transition: BlockNumber, + /// Number of first block where EIP-160 rules begin. + pub eip160_transition: u64, + /// Number of first block where EIP-161.abc begin. + pub eip161abc_transition: u64, + /// Number of first block where EIP-161.d begins. + pub eip161d_transition: u64, + /// DAO hard-fork transition block (X). + pub dao_hardfork_transition: u64, + /// DAO hard-fork refund contract address (C). + pub dao_hardfork_beneficiary: Address, + /// DAO hard-fork DAO accounts list (L) + pub dao_hardfork_accounts: Vec
, +} + +impl From<::ethjson::spec::EthashParams> for EthashExtensions { + fn from(p: ::ethjson::spec::EthashParams) -> Self { + EthashExtensions { + homestead_transition: p.homestead_transition.map_or(0, Into::into), + eip150_transition: p.eip150_transition.map_or(0, Into::into), + eip160_transition: p.eip160_transition.map_or(0, Into::into), + eip161abc_transition: p.eip161abc_transition.map_or(0, Into::into), + eip161d_transition: p.eip161d_transition.map_or(u64::max_value(), Into::into), + dao_hardfork_transition: p.dao_hardfork_transition.map_or(u64::max_value(), Into::into), + dao_hardfork_beneficiary: p.dao_hardfork_beneficiary.map_or_else(Address::new, Into::into), + dao_hardfork_accounts: p.dao_hardfork_accounts.unwrap_or_else(Vec::new).into_iter().map(Into::into).collect(), + } + } +} + +/// Special rules to be applied to the schedule. +pub type ScheduleCreationRules = Fn(&mut Schedule, BlockNumber) + Sync + Send; + +/// An ethereum-like state machine. +pub struct EthereumMachine { + params: CommonParams, + builtins: Arc>, + tx_filter: Option>, + ethash_extensions: Option, + schedule_rules: Option>, +} + +impl EthereumMachine { + /// Regular ethereum machine. + pub fn regular(params: CommonParams, builtins: BTreeMap) -> EthereumMachine { + let tx_filter = TransactionFilter::from_params(¶ms).map(Arc::new); + EthereumMachine { + params: params, + builtins: Arc::new(builtins), + tx_filter: tx_filter, + ethash_extensions: None, + schedule_rules: None, + } + } + + /// Ethereum machine with ethash extensions. + // TODO: either unify or specify to mainnet specifically and include other specific-chain HFs? + pub fn with_ethash_extensions(params: CommonParams, builtins: BTreeMap, extensions: EthashExtensions) -> EthereumMachine { + let mut machine = EthereumMachine::regular(params, builtins); + machine.ethash_extensions = Some(extensions); + machine + } + + /// Attach special rules to the creation of schedule. + pub fn set_schedule_creation_rules(&mut self, rules: Box) { + self.schedule_rules = Some(rules); + } + + /// Get a reference to the ethash-specific extensions. + pub fn ethash_extensions(&self) -> Option<&EthashExtensions> { + self.ethash_extensions.as_ref() + } +} + +impl EthereumMachine { + /// Execute a call as the system address. + pub fn execute_as_system( + &self, + block: &mut ExecutedBlock, + contract_address: Address, + gas: U256, + data: Option>, + ) -> Result, Error> { + let env_info = { + let mut env_info = block.env_info(); + env_info.gas_limit = env_info.gas_used + gas; + env_info + }; + + let mut state = block.fields_mut().state; + let params = ActionParams { + code_address: contract_address.clone(), + address: contract_address.clone(), + sender: SYSTEM_ADDRESS.clone(), + origin: SYSTEM_ADDRESS.clone(), + gas: gas, + gas_price: 0.into(), + value: ActionValue::Transfer(0.into()), + code: state.code(&contract_address)?, + code_hash: Some(state.code_hash(&contract_address)?), + data: data, + call_type: CallType::Call, + }; + let mut ex = Executive::new(&mut state, &env_info, self); + let mut substate = Substate::new(); + let mut output = Vec::new(); + if let Err(e) = ex.call(params, &mut substate, BytesRef::Flexible(&mut output), &mut NoopTracer, &mut NoopVMTracer) { + warn!("Encountered error on making system call: {}", e); + } + + Ok(output) + } + + /// Push last known block hash to the state. + fn push_last_hash(&self, block: &mut ExecutedBlock) -> Result<(), Error> { + let params = self.params(); + if block.fields().header.number() == params.eip210_transition { + let state = block.fields_mut().state; + state.init_code(¶ms.eip210_contract_address, params.eip210_contract_code.clone())?; + } + if block.fields().header.number() >= params.eip210_transition { + let parent_hash = block.fields().header.parent_hash().clone(); + let _ = self.execute_as_system( + block, + params.eip210_contract_address, + params.eip210_contract_gas, + Some(parent_hash.to_vec()), + )?; + } + Ok(()) + } + + /// Logic to perform on a new block: updating last hashes and the DAO + /// fork, for ethash. + pub fn on_new_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> { + self.push_last_hash(block)?; + + if let Some(ref ethash_params) = self.ethash_extensions { + if block.fields().header.number() == ethash_params.dao_hardfork_transition { + let state = block.fields_mut().state; + for child in ðash_params.dao_hardfork_accounts { + let beneficiary = ðash_params.dao_hardfork_beneficiary; + state.balance(child) + .and_then(|b| state.transfer_balance(child, beneficiary, &b, CleanupMode::NoEmpty))?; + } + } + } + + Ok(()) + } + + /// Populate a header's fields based on its parent's header. + /// Usually implements the chain scoring rule based on weight. + /// The gas floor target must not be lower than the engine's minimum gas limit. + pub fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, gas_ceil_target: U256) { + header.set_difficulty(parent.difficulty().clone()); + + if let Some(ref ethash_params) = self.ethash_extensions { + let gas_limit = { + let gas_limit = parent.gas_limit().clone(); + let bound_divisor = self.params().gas_limit_bound_divisor; + let lower_limit = gas_limit - gas_limit / bound_divisor + 1.into(); + let upper_limit = gas_limit + gas_limit / bound_divisor - 1.into(); + let gas_limit = if gas_limit < gas_floor_target { + let gas_limit = cmp::min(gas_floor_target, upper_limit); + round_block_gas_limit(gas_limit, lower_limit, upper_limit) + } else if gas_limit > gas_ceil_target { + let gas_limit = cmp::max(gas_ceil_target, lower_limit); + round_block_gas_limit(gas_limit, lower_limit, upper_limit) + } else { + let total_lower_limit = cmp::max(lower_limit, gas_floor_target); + let total_upper_limit = cmp::min(upper_limit, gas_ceil_target); + let gas_limit = cmp::max(gas_floor_target, cmp::min(total_upper_limit, + lower_limit + (header.gas_used().clone() * 6.into() / 5.into()) / bound_divisor)); + round_block_gas_limit(gas_limit, total_lower_limit, total_upper_limit) + }; + // ensure that we are not violating protocol limits + debug_assert!(gas_limit >= lower_limit); + debug_assert!(gas_limit <= upper_limit); + gas_limit + }; + + header.set_gas_limit(gas_limit); + if header.number() >= ethash_params.dao_hardfork_transition && + header.number() <= ethash_params.dao_hardfork_transition + 9 { + header.set_extra_data(b"dao-hard-fork"[..].to_owned()); + } + return + } + + header.set_gas_limit({ + let gas_limit = parent.gas_limit().clone(); + let bound_divisor = self.params().gas_limit_bound_divisor; + if gas_limit < gas_floor_target { + cmp::min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into()) + } else { + cmp::max(gas_floor_target, gas_limit - gas_limit / bound_divisor + 1.into()) + } + }); + } + + /// Get the general parameters of the chain. + pub fn params(&self) -> &CommonParams { + &self.params + } + + /// Get the EVM schedule for the given block number. + pub fn schedule(&self, block_number: BlockNumber) -> Schedule { + let mut schedule = match self.ethash_extensions { + None => self.params.schedule(block_number), + Some(ref ext) => { + if block_number < ext.homestead_transition { + Schedule::new_frontier() + } else if block_number < ext.eip150_transition { + Schedule::new_homestead() + } else { + /// There's no max_code_size transition so we tie it to eip161abc + let max_code_size = if block_number >= ext.eip161abc_transition { + self.params.max_code_size as usize + } else { + usize::max_value() + }; + + let mut schedule = Schedule::new_post_eip150( + max_code_size, + block_number >= ext.eip160_transition, + block_number >= ext.eip161abc_transition, + block_number >= ext.eip161d_transition + ); + + self.params.update_schedule(block_number, &mut schedule); + schedule + } + } + }; + + if let Some(ref rules) = self.schedule_rules { + (rules)(&mut schedule, block_number) + } + + schedule + } + + /// Builtin-contracts for the chain.. + pub fn builtins(&self) -> &BTreeMap { + &*self.builtins + } + + /// Attempt to get a handle to a built-in contract. + /// Only returns references to activated built-ins. + // TODO: builtin contract routing - to do this properly, it will require removing the built-in configuration-reading logic + // from Spec into here and removing the Spec::builtins field. + pub fn builtin(&self, a: &Address, block_number: BlockNumber) -> Option<&Builtin> { + self.builtins() + .get(a) + .and_then(|b| if b.is_active(block_number) { Some(b) } else { None }) + } + + /// Some intrinsic operation parameters; by default they take their value from the `spec()`'s `engine_params`. + pub fn maximum_extra_data_size(&self) -> usize { self.params().maximum_extra_data_size } + + /// The nonce with which accounts begin at given block. + pub fn account_start_nonce(&self, block: u64) -> U256 { + let params = self.params(); + + if block >= params.dust_protection_transition { + U256::from(params.nonce_cap_increment) * U256::from(block) + } else { + params.account_start_nonce + } + } + + /// The network ID that transactions should be signed with. + pub fn signing_chain_id(&self, env_info: &EnvInfo) -> Option { + let params = self.params(); + + if env_info.number >= params.eip155_transition { + Some(params.chain_id) + } else { + None + } + } + + /// Returns new contract address generation scheme at given block number. + pub fn create_address_scheme(&self, number: BlockNumber) -> CreateContractAddress { + if number >= self.params().eip86_transition { + CreateContractAddress::FromCodeHash + } else { + CreateContractAddress::FromSenderAndNonce + } + } + + /// Verify a particular transaction is valid, regardless of order. + pub fn verify_transaction_unordered(&self, t: UnverifiedTransaction, _header: &Header) -> Result { + SignedTransaction::new(t) + } + + /// Does basic verification of the transaction. + pub fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> Result<(), Error> { + let check_low_s = match self.ethash_extensions { + Some(ref ext) => header.number() >= ext.homestead_transition, + None => true, + }; + + let chain_id = if header.number() >= self.params().eip155_transition { + Some(self.params().chain_id) + } else { + None + }; + t.verify_basic(check_low_s, chain_id, false)?; + + Ok(()) + } + + /// Does verification of the transaction against the parent state. + // TODO: refine the bound here to be a "state provider" or similar as opposed + // to full client functionality. + pub fn verify_transaction(&self, t: &SignedTransaction, header: &Header, client: &BlockChainClient) -> Result<(), Error> { + if let Some(ref filter) = self.tx_filter.as_ref() { + if !filter.transaction_allowed(header.parent_hash(), t, client) { + return Err(TransactionError::NotAllowed.into()) + } + } + + Ok(()) + } + + /// If this machine supports wasm. + pub fn supports_wasm(&self) -> bool { + self.params().wasm + } +} + +/// Auxiliary data fetcher for an Ethereum machine. In Ethereum-like machines +/// there are two kinds of auxiliary data: bodies and receipts. +#[derive(Default, Clone)] +pub struct AuxiliaryData<'a> { + /// The full block bytes, including the header. + pub bytes: Option<&'a [u8]>, + /// The block receipts. + pub receipts: Option<&'a [::receipt::Receipt]>, +} + +/// Type alias for a function we can make calls through synchronously. +/// Returns the call result and state proof for each call. +pub type Call<'a> = Fn(Address, Vec) -> Result<(Vec, Vec>), String> + 'a; + +/// Request for auxiliary data of a block. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum AuxiliaryRequest { + /// Needs the body. + Body, + /// Needs the receipts. + Receipts, + /// Needs both body and receipts. + Both, +} + +impl ::parity_machine::Machine for EthereumMachine { + type Header = Header; + + type LiveBlock = ExecutedBlock; + type EngineClient = ::client::EngineClient; + type AuxiliaryRequest = AuxiliaryRequest; + + type Error = Error; +} + +impl<'a> ::parity_machine::LocalizedMachine<'a> for EthereumMachine { + type StateContext = Call<'a>; + type AuxiliaryData = AuxiliaryData<'a>; +} + +impl ::parity_machine::WithBalances for EthereumMachine { + fn balance(&self, live: &ExecutedBlock, address: &Address) -> Result { + live.fields().state.balance(address).map_err(Into::into) + } + + fn add_balance(&self, live: &mut ExecutedBlock, address: &Address, amount: &U256) -> Result<(), Error> { + live.fields_mut().state.add_balance(address, amount, CleanupMode::NoEmpty).map_err(Into::into) + } + + fn note_rewards( + &self, + live: &mut Self::LiveBlock, + direct: &[(Address, U256)], + indirect: &[(Address, U256)], + ) -> Result<(), Self::Error> { + use block::IsBlock; + + if !live.tracing_enabled() { return Ok(()) } + + let mut tracer = ExecutiveTracer::default(); + + for &(address, amount) in direct { + tracer.trace_reward(address, amount, RewardType::Block); + } + + for &(address, amount) in indirect { + tracer.trace_reward(address, amount, RewardType::Uncle); + } + + live.fields_mut().push_traces(tracer); + + Ok(()) + } +} + +// Try to round gas_limit a bit so that: +// 1) it will still be in desired range +// 2) it will be a nearest (with tendency to increase) multiple of PARITY_GAS_LIMIT_DETERMINANT +fn round_block_gas_limit(gas_limit: U256, lower_limit: U256, upper_limit: U256) -> U256 { + let increased_gas_limit = gas_limit + (PARITY_GAS_LIMIT_DETERMINANT - gas_limit % PARITY_GAS_LIMIT_DETERMINANT); + if increased_gas_limit > upper_limit { + let decreased_gas_limit = increased_gas_limit - PARITY_GAS_LIMIT_DETERMINANT; + if decreased_gas_limit < lower_limit { + gas_limit + } else { + decreased_gas_limit + } + } else { + increased_gas_limit + } +} + + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn ethash_gas_limit_is_multiple_of_determinant() { + use bigint::prelude::U256; + + let spec = ::ethereum::new_homestead_test(); + let ethparams = ::tests::helpers::get_default_ethash_extensions(); + + let machine = EthereumMachine::with_ethash_extensions( + spec.params().clone(), + Default::default(), + ethparams, + ); + + let mut parent = ::header::Header::new(); + let mut header = ::header::Header::new(); + header.set_number(1); + + // this test will work for this constant only + assert_eq!(PARITY_GAS_LIMIT_DETERMINANT, U256::from(37)); + + // when parent.gas_limit < gas_floor_target: + parent.set_gas_limit(U256::from(50_000)); + machine.populate_from_parent(&mut header, &parent, U256::from(100_000), U256::from(200_000)); + assert_eq!(*header.gas_limit(), U256::from(50_024)); + + // when parent.gas_limit > gas_ceil_target: + parent.set_gas_limit(U256::from(250_000)); + machine.populate_from_parent(&mut header, &parent, U256::from(100_000), U256::from(200_000)); + assert_eq!(*header.gas_limit(), U256::from(249_787)); + + // when parent.gas_limit is in miner's range + header.set_gas_used(U256::from(150_000)); + parent.set_gas_limit(U256::from(150_000)); + machine.populate_from_parent(&mut header, &parent, U256::from(100_000), U256::from(200_000)); + assert_eq!(*header.gas_limit(), U256::from(150_035)); + + // when parent.gas_limit is in miner's range + // && we can NOT increase it to be multiple of constant + header.set_gas_used(U256::from(150_000)); + parent.set_gas_limit(U256::from(150_000)); + machine.populate_from_parent(&mut header, &parent, U256::from(100_000), U256::from(150_002)); + assert_eq!(*header.gas_limit(), U256::from(149_998)); + + // when parent.gas_limit is in miner's range + // && we can NOT increase it to be multiple of constant + // && we can NOT decrease it to be multiple of constant + header.set_gas_used(U256::from(150_000)); + parent.set_gas_limit(U256::from(150_000)); + machine.populate_from_parent(&mut header, &parent, U256::from(150_000), U256::from(150_002)); + assert_eq!(*header.gas_limit(), U256::from(150_002)); + } +} diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index c52459fdf..a8f18b73a 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -35,7 +35,7 @@ use error::*; use transaction::{Action, UnverifiedTransaction, PendingTransaction, SignedTransaction, Condition as TransactionCondition}; use receipt::{Receipt, RichReceipt}; use spec::Spec; -use engines::{Engine, Seal}; +use engines::{EthEngine, Seal}; use miner::{MinerService, MinerStatus, TransactionQueue, RemovalReason, TransactionQueueDetailsProvider, PrioritizationStrategy, AccountDetails, TransactionOrigin}; use miner::banning_queue::{BanningTransactionQueue, Threshold}; @@ -240,7 +240,7 @@ pub struct Miner { gas_range_target: RwLock<(U256, U256)>, author: RwLock
, extra_data: RwLock, - engine: Arc, + engine: Arc, accounts: Option>, notifiers: RwLock>>, @@ -662,7 +662,7 @@ impl Miner { return Err(Error::Transaction(TransactionError::AlreadyImported)); } match self.engine.verify_transaction_basic(&tx, &best_block_header) - .and_then(|_| self.engine.verify_transaction(tx, &best_block_header)) + .and_then(|_| self.engine.verify_transaction_unordered(tx, &best_block_header)) { Err(e) => { debug!(target: "miner", "Rejected tx {:?} with invalid signature: {:?}", hash, e); diff --git a/ethcore/src/snapshot/consensus/authority.rs b/ethcore/src/snapshot/consensus/authority.rs index 0c1d12808..efb30635d 100644 --- a/ethcore/src/snapshot/consensus/authority.rs +++ b/ethcore/src/snapshot/consensus/authority.rs @@ -25,7 +25,8 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use blockchain::{BlockChain, BlockProvider}; -use engines::{Engine, EpochVerifier, EpochTransition}; +use engines::{EthEngine, EpochVerifier, EpochTransition}; +use machine::EthereumMachine; use ids::BlockId; use header::Header; use receipt::Receipt; @@ -168,7 +169,7 @@ struct ChunkRebuilder { // and epoch data from last blocks in chunks. // verification for these will be done at the end. unverified_firsts: Vec<(Header, Bytes, H256)>, - last_epochs: Vec<(Header, Box)>, + last_epochs: Vec<(Header, Box>)>, } // verified data. @@ -180,9 +181,9 @@ struct Verified { impl ChunkRebuilder { fn verify_transition( &mut self, - last_verifier: &mut Option>, + last_verifier: &mut Option>>, transition_rlp: UntrustedRlp, - engine: &Engine, + engine: &EthEngine, ) -> Result { use engines::ConstructedVerifier; @@ -238,7 +239,7 @@ impl Rebuilder for ChunkRebuilder { fn feed( &mut self, chunk: &[u8], - engine: &Engine, + engine: &EthEngine, abort_flag: &AtomicBool, ) -> Result<(), ::error::Error> { let rlp = UntrustedRlp::new(chunk); @@ -346,7 +347,7 @@ impl Rebuilder for ChunkRebuilder { Ok(()) } - fn finalize(&mut self, _engine: &Engine) -> Result<(), ::error::Error> { + fn finalize(&mut self, _engine: &EthEngine) -> Result<(), ::error::Error> { if !self.had_genesis { return Err(Error::WrongChunkFormat("No genesis transition included.".into()).into()); } diff --git a/ethcore/src/snapshot/consensus/mod.rs b/ethcore/src/snapshot/consensus/mod.rs index b24c67be2..baff16940 100644 --- a/ethcore/src/snapshot/consensus/mod.rs +++ b/ethcore/src/snapshot/consensus/mod.rs @@ -21,7 +21,7 @@ use std::sync::atomic::AtomicBool; use std::sync::Arc; use blockchain::BlockChain; -use engines::Engine; +use engines::EthEngine; use snapshot::{Error, ManifestData}; use bigint::hash::H256; @@ -84,7 +84,7 @@ pub trait Rebuilder: Send { fn feed( &mut self, chunk: &[u8], - engine: &Engine, + engine: &EthEngine, abort_flag: &AtomicBool, ) -> Result<(), ::error::Error>; @@ -93,5 +93,5 @@ pub trait Rebuilder: Send { /// /// This should apply the necessary "glue" between chunks, /// and verify against the restored state. - fn finalize(&mut self, engine: &Engine) -> Result<(), ::error::Error>; + fn finalize(&mut self, engine: &EthEngine) -> Result<(), ::error::Error>; } diff --git a/ethcore/src/snapshot/consensus/work.rs b/ethcore/src/snapshot/consensus/work.rs index 349cce685..5ef1b197c 100644 --- a/ethcore/src/snapshot/consensus/work.rs +++ b/ethcore/src/snapshot/consensus/work.rs @@ -27,7 +27,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use blockchain::{BlockChain, BlockProvider}; -use engines::Engine; +use engines::EthEngine; use snapshot::{Error, ManifestData}; use snapshot::block::AbridgedBlock; use bigint::hash::H256; @@ -219,7 +219,7 @@ impl PowRebuilder { impl Rebuilder for PowRebuilder { /// Feed the rebuilder an uncompressed block chunk. /// Returns the number of blocks fed or any errors. - fn feed(&mut self, chunk: &[u8], engine: &Engine, abort_flag: &AtomicBool) -> Result<(), ::error::Error> { + fn feed(&mut self, chunk: &[u8], engine: &EthEngine, abort_flag: &AtomicBool) -> Result<(), ::error::Error> { use basic_types::Seal::With; use views::BlockView; use snapshot::verify_old_block; @@ -271,7 +271,6 @@ impl Rebuilder for PowRebuilder { &block.header, engine, &self.chain, - Some(&block_bytes), is_best )?; @@ -298,7 +297,7 @@ impl Rebuilder for PowRebuilder { } /// Glue together any disconnected chunks and check that the chain is complete. - fn finalize(&mut self, _: &Engine) -> Result<(), ::error::Error> { + fn finalize(&mut self, _: &EthEngine) -> Result<(), ::error::Error> { let mut batch = self.db.transaction(); for (first_num, first_hash) in self.disconnected.drain(..) { diff --git a/ethcore/src/snapshot/mod.rs b/ethcore/src/snapshot/mod.rs index 9b8cfb726..4931d500d 100644 --- a/ethcore/src/snapshot/mod.rs +++ b/ethcore/src/snapshot/mod.rs @@ -26,7 +26,7 @@ use hash::{keccak, KECCAK_NULL_RLP, KECCAK_EMPTY}; use account_db::{AccountDB, AccountDBMut}; use blockchain::{BlockChain, BlockProvider}; -use engines::Engine; +use engines::EthEngine; use header::Header; use ids::BlockId; @@ -126,7 +126,7 @@ impl Progress { } /// Take a snapshot using the given blockchain, starting block hash, and database, writing into the given writer. pub fn take_snapshot( - engine: &Engine, + engine: &EthEngine, chain: &BlockChain, block_at: H256, state_db: &HashDB, @@ -484,13 +484,13 @@ const POW_VERIFY_RATE: f32 = 0.02; /// Verify an old block with the given header, engine, blockchain, body. If `always` is set, it will perform /// the fullest verification possible. If not, it will take a random sample to determine whether it will /// do heavy or light verification. -pub fn verify_old_block(rng: &mut OsRng, header: &Header, engine: &Engine, chain: &BlockChain, body: Option<&[u8]>, always: bool) -> Result<(), ::error::Error> { - engine.verify_block_basic(header, body)?; +pub fn verify_old_block(rng: &mut OsRng, header: &Header, engine: &EthEngine, chain: &BlockChain, always: bool) -> Result<(), ::error::Error> { + engine.verify_block_basic(header)?; if always || rng.gen::() <= POW_VERIFY_RATE { - engine.verify_block_unordered(header, body)?; + engine.verify_block_unordered(header)?; match chain.block_header(header.parent_hash()) { - Some(parent) => engine.verify_block_family(header, &parent, body), + Some(parent) => engine.verify_block_family(header, &parent), None => Ok(()), } } else { diff --git a/ethcore/src/snapshot/service.rs b/ethcore/src/snapshot/service.rs index 9cc841782..ab551f154 100644 --- a/ethcore/src/snapshot/service.rs +++ b/ethcore/src/snapshot/service.rs @@ -28,7 +28,7 @@ use super::io::{SnapshotReader, LooseReader, SnapshotWriter, LooseWriter}; use blockchain::BlockChain; use client::{BlockChainClient, Client}; -use engines::Engine; +use engines::EthEngine; use error::Error; use ids::BlockId; use service::ClientIoMessage; @@ -91,7 +91,7 @@ struct RestorationParams<'a> { writer: Option, // writer for recovered snapshot. genesis: &'a [u8], // genesis block of the chain. guard: Guard, // guard for the restoration directory. - engine: &'a Engine, + engine: &'a EthEngine, } impl Restoration { @@ -145,7 +145,7 @@ impl Restoration { } // feeds a block chunk - fn feed_blocks(&mut self, hash: H256, chunk: &[u8], engine: &Engine, flag: &AtomicBool) -> Result<(), Error> { + fn feed_blocks(&mut self, hash: H256, chunk: &[u8], engine: &EthEngine, flag: &AtomicBool) -> Result<(), Error> { if self.block_chunks_left.contains(&hash) { let len = snappy::decompress_into(chunk, &mut self.snappy_buffer)?; @@ -161,7 +161,7 @@ impl Restoration { } // finish up restoration. - fn finalize(mut self, engine: &Engine) -> Result<(), Error> { + fn finalize(mut self, engine: &EthEngine) -> Result<(), Error> { use trie::TrieError; if !self.is_done() { return Ok(()) } @@ -199,7 +199,7 @@ pub type Channel = IoChannel; /// Snapshot service parameters. pub struct ServiceParams { /// The consensus engine this is built on. - pub engine: Arc, + pub engine: Arc, /// The chain's genesis block. pub genesis_block: Bytes, /// Database configuration options. @@ -225,7 +225,7 @@ pub struct Service { pruning: Algorithm, status: Mutex, reader: RwLock>, - engine: Arc, + engine: Arc, genesis_block: Bytes, state_chunks: AtomicUsize, block_chunks: AtomicUsize, diff --git a/ethcore/src/snapshot/tests/helpers.rs b/ethcore/src/snapshot/tests/helpers.rs index e5e48bf58..d946d7e47 100644 --- a/ethcore/src/snapshot/tests/helpers.rs +++ b/ethcore/src/snapshot/tests/helpers.rs @@ -24,7 +24,7 @@ use account_db::AccountDBMut; use basic_account::BasicAccount; use blockchain::BlockChain; use client::{BlockChainClient, Client}; -use engines::Engine; +use engines::EthEngine; use snapshot::{StateRebuilder}; use snapshot::io::{SnapshotReader, PackedWriter, PackedReader}; @@ -160,7 +160,7 @@ pub fn snap(client: &Client) -> GuardedTempResult> { /// write into the given database. pub fn restore( db: Arc, - engine: &Engine, + engine: &EthEngine, reader: &SnapshotReader, genesis: &[u8], ) -> Result<(), ::error::Error> { diff --git a/ethcore/src/snapshot/tests/proof_of_work.rs b/ethcore/src/snapshot/tests/proof_of_work.rs index 806ebf424..91f2753d4 100644 --- a/ethcore/src/snapshot/tests/proof_of_work.rs +++ b/ethcore/src/snapshot/tests/proof_of_work.rs @@ -38,7 +38,7 @@ fn chunk_and_restore(amount: u64) { let mut finalizer = BlockFinalizer::default(); let genesis = canon_chain.generate(&mut finalizer).unwrap(); - let engine = Arc::new(::engines::NullEngine::default()); + let engine = ::spec::Spec::new_test().engine; let new_path = RandomTempPath::create_dir(); let mut snapshot_path = new_path.as_path().to_owned(); snapshot_path.push("SNAP"); @@ -128,7 +128,7 @@ fn checks_flag() { let chunk = stream.out(); let db = Arc::new(kvdb::in_memory(::db::NUM_COLUMNS.unwrap_or(0))); - let engine = Arc::new(::engines::NullEngine::default()); + let engine = ::spec::Spec::new_test().engine; let chain = BlockChain::new(Default::default(), &genesis, db.clone()); let manifest = ::snapshot::ManifestData { diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index 422892fd0..54c966615 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -16,37 +16,43 @@ //! Parameters for a block chain. -use super::genesis::Genesis; -use super::seal::Generic as GenericSeal; -use bigint::hash::{H2048, H256}; -use bigint::prelude::U256; - -use builtin::Builtin; -use bytes::Bytes; -use engines::{AuthorityRound, BasicAuthority, DEFAULT_BLOCKHASH_CONTRACT, Engine, InstantSeal, - NullEngine, Tendermint}; -use error::Error; - -pub use ethash::OptimizeFor; -use ethereum; -use ethjson; -use executive::Executive; -use factory::Factories; -use hash::{KECCAK_NULL_RLP, keccak}; -use header::{BlockNumber, Header}; -use parking_lot::RwLock; -use pod_state::*; -use rlp::{Rlp, RlpStream}; -use rustc_hex::FromHex; -use state::{Backend, State, Substate}; -use state::backend::Basic as BasicBackend; -use std::collections::BTreeMap; use std::io::Read; +use std::collections::BTreeMap; use std::path::Path; use std::sync::Arc; -use trace::{NoopTracer, NoopVMTracer}; + +use bigint::hash::{H256, H2048}; +use bigint::prelude::U256; +use bytes::Bytes; +use ethjson; +use hash::{KECCAK_NULL_RLP, keccak}; +use parking_lot::RwLock; +use rlp::{Rlp, RlpStream}; +use rustc_hex::FromHex; use util::*; -use vm::{ActionParams, ActionValue, CallType, EnvInfo}; +use vm::{EnvInfo, CallType, ActionValue, ActionParams}; + +use super::genesis::Genesis; +use super::seal::Generic as GenericSeal; + +use builtin::Builtin; +use engines::{EthEngine, NullEngine, InstantSeal, BasicAuthority, AuthorityRound, Tendermint, DEFAULT_BLOCKHASH_CONTRACT}; +use error::Error; +use executive::Executive; +use factory::Factories; +use header::{BlockNumber, Header}; +use machine::EthereumMachine; +use pod_state::*; +use state::{Backend, State, Substate}; +use state::backend::Basic as BasicBackend; +use trace::{NoopTracer, NoopVMTracer}; + +pub use ethash::OptimizeFor; + +// helper for formatting errors. +fn fmt_err(f: F) -> String { + format!("Spec json is invalid: {}", f) +} /// Parameters common to ethereum-like blockchains. /// NOTE: when adding bugfix hard-fork parameters, @@ -106,12 +112,12 @@ pub struct CommonParams { pub wasm: bool, /// Gas limit bound divisor (how much gas limit can change per block) pub gas_limit_bound_divisor: U256, - /// Block reward in wei. - pub block_reward: U256, /// Registrar contract address. pub registrar: Address, /// Node permission managing contract address. pub node_permission_contract: Option
, + /// Maximum contract code size that can be deployed. + pub max_code_size: u64, /// Transaction permission managing contract address. pub transaction_permission_contract: Option
, } @@ -119,7 +125,7 @@ pub struct CommonParams { impl CommonParams { /// Schedule for an EVM in the post-EIP-150-era of the Ethereum main net. pub fn schedule(&self, block_number: u64) -> ::vm::Schedule { - let mut schedule = ::vm::Schedule::new_post_eip150(usize::max_value(), true, true, true); + let mut schedule = ::vm::Schedule::new_post_eip150(self.max_code_size as _, true, true, true); self.update_schedule(block_number, &mut schedule); schedule } @@ -214,9 +220,9 @@ impl From for CommonParams { remove_dust_contracts: p.remove_dust_contracts.unwrap_or(false), wasm: p.wasm.unwrap_or(false), 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), node_permission_contract: p.node_permission_contract.map(Into::into), + max_code_size: p.max_code_size.map_or(u64::max_value(), Into::into), transaction_permission_contract: p.transaction_permission_contract.map(Into::into), } } @@ -266,7 +272,7 @@ pub struct Spec { /// User friendly spec name pub name: String, /// What engine are we using for this? - pub engine: Arc, + pub engine: Arc, /// Name of the subdir inside the main data dir to use for chain data and settings. pub data_dir: String, @@ -329,6 +335,13 @@ impl Clone for Spec { } } +fn load_machine_from(s: ethjson::spec::Spec) -> EthereumMachine { + let builtins = s.accounts.builtins().into_iter().map(|p| (p.0.into(), From::from(p.1))).collect(); + let params = CommonParams::from(s.params); + + Spec::machine(&s.engine, params, builtins) +} + /// Load from JSON object. fn load_from(spec_params: SpecParams, s: ethjson::spec::Spec) -> Result { let builtins = s.accounts @@ -387,7 +400,28 @@ macro_rules! load_bundled { }; } +macro_rules! load_machine_bundled { + ($e:expr) => { + Spec::load_machine( + include_bytes!(concat!("../../res/", $e, ".json")) as &[u8] + ).expect(concat!("Chain spec ", $e, " is invalid.")) + }; +} + impl Spec { + // create an instance of an Ethereum state machine, minus consensus logic. + fn machine( + engine_spec: ðjson::spec::Engine, + params: CommonParams, + builtins: BTreeMap, + ) -> EthereumMachine { + if let ethjson::spec::Engine::Ethash(ref ethash) = *engine_spec { + EthereumMachine::with_ethash_extensions(params, builtins, ethash.params.clone().into()) + } else { + EthereumMachine::regular(params, builtins) + } + } + /// Convert engine spec into a arc'd Engine of the right underlying type. /// TODO avoid this hard-coded nastiness - use dynamic-linked plugin framework instead. fn engine( @@ -395,42 +429,18 @@ impl Spec { engine_spec: ethjson::spec::Engine, params: CommonParams, builtins: BTreeMap, - ) -> Arc { + ) -> Arc { + let machine = Self::machine(&engine_spec, params, builtins); + match engine_spec { - ethjson::spec::Engine::Null => Arc::new(NullEngine::new(params, builtins)), - ethjson::spec::Engine::InstantSeal => Arc::new(InstantSeal::new(params, builtins)), - ethjson::spec::Engine::Ethash(ethash) => Arc::new(ethereum::Ethash::new( - spec_params.cache_dir, - params, - From::from(ethash.params), - builtins, - spec_params.optimization_setting, - )), - ethjson::spec::Engine::BasicAuthority(basic_authority) => Arc::new( - BasicAuthority::new( - params, - From::from( - basic_authority.params, - ), - builtins, - ), - ), - ethjson::spec::Engine::AuthorityRound(authority_round) => AuthorityRound::new( - params, - From::from( - authority_round.params, - ), - builtins, - ).expect( - "Failed to start AuthorityRound consensus engine.", - ), - ethjson::spec::Engine::Tendermint(tendermint) => Tendermint::new( - params, - From::from(tendermint.params), - builtins, - ).expect( - "Failed to start the Tendermint consensus engine.", - ), + ethjson::spec::Engine::Null(null) => Arc::new(NullEngine::new(null.params.into(), machine)), + ethjson::spec::Engine::Ethash(ethash) => Arc::new(::ethereum::Ethash::new(spec_params.cache_dir, ethash.params.into(), machine, spec_params.optimization_setting)), + ethjson::spec::Engine::InstantSeal => Arc::new(InstantSeal::new(machine)), + ethjson::spec::Engine::BasicAuthority(basic_authority) => Arc::new(BasicAuthority::new(basic_authority.params.into(), machine)), + ethjson::spec::Engine::AuthorityRound(authority_round) => AuthorityRound::new(authority_round.params.into(), machine) + .expect("Failed to start AuthorityRound consensus engine."), + ethjson::spec::Engine::Tendermint(tendermint) => Tendermint::new(tendermint.params.into(), machine) + .expect("Failed to start the Tendermint consensus engine."), } } @@ -496,15 +506,8 @@ impl Spec { let mut substate = Substate::new(); { - let mut exec = Executive::new(&mut state, &env_info, self.engine.as_ref()); - if let Err(e) = exec.create( - params, - &mut substate, - &mut None, - &mut NoopTracer, - &mut NoopVMTracer, - ) - { + let mut exec = Executive::new(&mut state, &env_info, self.engine.machine()); + if let Err(e) = exec.create(params, &mut substate, &mut None, &mut NoopTracer, &mut NoopVMTracer) { warn!(target: "spec", "Genesis constructor execution at {} failed: {}.", address, e); } } @@ -639,19 +642,23 @@ impl Spec { Ok(db) } + /// Loads just the state machine from a json file. + pub fn load_machine(reader: R) -> Result { + ethjson::spec::Spec::load(reader) + .map_err(fmt_err) + .map(load_machine_from) + + } + /// Loads spec from json file. Provide factories for executing contracts and ensuring /// storage goes to the right place. pub fn load<'a, T: Into>, R>(params: T, reader: R) -> Result where R: Read, { - fn fmt(f: F) -> String { - format!("Spec json is invalid: {}", f) - } - - ethjson::spec::Spec::load(reader).map_err(fmt).and_then( + ethjson::spec::Spec::load(reader).map_err(fmt_err).and_then( |x| { - load_from(params.into(), x).map_err(fmt) + load_from(params.into(), x).map_err(fmt_err) }, ) } @@ -700,7 +707,7 @@ impl Spec { db.as_hashdb_mut(), *genesis.state_root(), &tx, - &*self.engine, + self.engine.machine(), &env_info, factories.clone(), true, @@ -720,11 +727,12 @@ impl 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 the EthereumMachine corresponding to Spec::new_test. + pub fn new_test_machine() -> EthereumMachine { load_machine_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 keccak(''). @@ -796,7 +804,6 @@ mod tests { use state::State; use std::str::FromStr; use tests::helpers::get_temp_state_db; - use util::*; use views::*; // https://github.com/paritytech/parity/issues/1840 diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index ba4c3f7ae..96fd46ec5 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -27,7 +27,7 @@ use std::sync::Arc; use hash::{KECCAK_NULL_RLP, KECCAK_EMPTY}; use receipt::{Receipt, TransactionOutcome}; -use engines::Engine; +use machine::EthereumMachine as Machine; use vm::EnvInfo; use error::Error; use executive::{Executive, TransactOptions}; @@ -196,7 +196,7 @@ pub fn check_proof( proof: &[::util::DBValue], root: H256, transaction: &SignedTransaction, - engine: &Engine, + machine: &Machine, env_info: &EnvInfo, ) -> ProvedExecution { let backend = self::backend::ProofCheck::new(proof); @@ -206,7 +206,7 @@ pub fn check_proof( let res = State::from_existing( backend, root, - engine.account_start_nonce(env_info.number), + machine.account_start_nonce(env_info.number), factories ); @@ -216,7 +216,7 @@ pub fn check_proof( }; let options = TransactOptions::with_no_tracing().save_output_from_contract(); - match state.execute(env_info, engine, transaction, options, true) { + match state.execute(env_info, machine, transaction, options, true) { Ok(executed) => ProvedExecution::Complete(executed), Err(ExecutionError::Internal(_)) => ProvedExecution::BadProof, Err(e) => ProvedExecution::Failed(e), @@ -230,7 +230,7 @@ pub fn prove_transaction( db: H, root: H256, transaction: &SignedTransaction, - engine: &Engine, + machine: &Machine, env_info: &EnvInfo, factories: Factories, virt: bool, @@ -241,7 +241,7 @@ pub fn prove_transaction( let res = State::from_existing( backend, root, - engine.account_start_nonce(env_info.number), + machine.account_start_nonce(env_info.number), factories, ); @@ -251,7 +251,7 @@ pub fn prove_transaction( }; let options = TransactOptions::with_no_tracing().dont_check_nonce().save_output_from_contract(); - match state.execute(env_info, engine, transaction, options, virt) { + match state.execute(env_info, machine, transaction, options, virt) { Err(ExecutionError::Internal(_)) => None, Err(e) => { trace!(target: "state", "Proved call failed: {}", e); @@ -668,13 +668,13 @@ impl State { /// Execute a given transaction, producing a receipt and an optional trace. /// This will change the state accordingly. - pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, t: &SignedTransaction, tracing: bool) -> ApplyResult { + pub fn apply(&mut self, env_info: &EnvInfo, machine: &Machine, t: &SignedTransaction, tracing: bool) -> ApplyResult { if tracing { let options = TransactOptions::with_tracing(); - self.apply_with_tracing(env_info, engine, t, options.tracer, options.vm_tracer) + self.apply_with_tracing(env_info, machine, t, options.tracer, options.vm_tracer) } else { let options = TransactOptions::with_no_tracing(); - self.apply_with_tracing(env_info, engine, t, options.tracer, options.vm_tracer) + self.apply_with_tracing(env_info, machine, t, options.tracer, options.vm_tracer) } } @@ -683,7 +683,7 @@ impl State { pub fn apply_with_tracing( &mut self, env_info: &EnvInfo, - engine: &Engine, + machine: &Machine, t: &SignedTransaction, tracer: T, vm_tracer: V, @@ -692,12 +692,13 @@ impl State { V: trace::VMTracer, { let options = TransactOptions::new(tracer, vm_tracer); - let e = self.execute(env_info, engine, t, options, false)?; + let e = self.execute(env_info, machine, t, options, false)?; + let params = machine.params(); - let eip658 = env_info.number >= engine.params().eip658_transition; + let eip658 = env_info.number >= params.eip658_transition; let no_intermediate_commits = eip658 || - (env_info.number >= engine.params().eip98_transition && env_info.number >= engine.params().validate_receipts_transition); + (env_info.number >= params.eip98_transition && env_info.number >= params.validate_receipts_transition); let outcome = if no_intermediate_commits { if eip658 { @@ -726,10 +727,10 @@ impl State { // // `virt` signals that we are executing outside of a block set and restrictions like // gas limits and gas costs should be lifted. - fn execute(&mut self, env_info: &EnvInfo, engine: &Engine, t: &SignedTransaction, options: TransactOptions, virt: bool) + fn execute(&mut self, env_info: &EnvInfo, machine: &Machine, t: &SignedTransaction, options: TransactOptions, virt: bool) -> Result where T: trace::Tracer, V: trace::VMTracer, { - let mut e = Executive::new(self, env_info, engine); + let mut e = Executive::new(self, env_info, machine); match virt { true => e.transact_virtual(t, options), @@ -1071,6 +1072,7 @@ mod tests { use bigint::hash::H256; use util::Address; use tests::helpers::*; + use machine::EthereumMachine; use vm::EnvInfo; use spec::*; use transaction::*; @@ -1082,6 +1084,12 @@ mod tests { keccak("").into() } + fn make_frontier_machine(max_depth: usize) -> EthereumMachine { + let mut machine = ::ethereum::new_frontier_test_machine(); + machine.set_schedule_creation_rules(Box::new(move |s, _| s.max_depth = max_depth)); + machine + } + #[test] fn should_apply_create_transaction() { init_log(); @@ -1090,7 +1098,7 @@ mod tests { let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); + let machine = make_frontier_machine(5); let t = Transaction { nonce: 0.into(), @@ -1102,7 +1110,7 @@ mod tests { }.sign(&secret(), None); state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &engine, &t, true).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), subtraces: 0, @@ -1148,7 +1156,7 @@ mod tests { let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); + let machine = make_frontier_machine(5); let t = Transaction { nonce: 0.into(), @@ -1160,7 +1168,7 @@ mod tests { }.sign(&secret(), None); state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &engine, &t, true).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), action: trace::Action::Create(trace::Create { @@ -1184,7 +1192,7 @@ mod tests { let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); + let machine = make_frontier_machine(5); let t = Transaction { nonce: 0.into(), @@ -1197,7 +1205,7 @@ mod tests { state.init_code(&0xa.into(), FromHex::from_hex("6000").unwrap()).unwrap(); state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &engine, &t, true).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), action: trace::Action::Call(trace::Call { @@ -1226,7 +1234,7 @@ mod tests { let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); + let machine = make_frontier_machine(5); let t = Transaction { nonce: 0.into(), @@ -1238,7 +1246,7 @@ mod tests { }.sign(&secret(), None); state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &engine, &t, true).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), action: trace::Action::Call(trace::Call { @@ -1267,7 +1275,7 @@ mod tests { let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); - let engine = &*Spec::new_test().engine; + let machine = Spec::new_test_machine(); let t = Transaction { nonce: 0.into(), @@ -1278,7 +1286,7 @@ mod tests { data: vec![], }.sign(&secret(), None); - let result = state.apply(&info, engine, &t, true).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), @@ -1308,7 +1316,7 @@ mod tests { let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); - let engine = &*Spec::new_test().engine; + let machine = Spec::new_test_machine(); let t = Transaction { nonce: 0.into(), @@ -1320,7 +1328,7 @@ mod tests { }.sign(&secret(), None); state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060006001610be0f1").unwrap()).unwrap(); - let result = state.apply(&info, engine, &t, true).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), @@ -1333,7 +1341,7 @@ mod tests { call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(28_061), + gas_used: U256::from(3_721), // in post-eip150 output: vec![] }), subtraces: 0, @@ -1350,7 +1358,7 @@ mod tests { let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); - let engine = &*Spec::new_test().engine; + let machine = Spec::new_test_machine(); let t = Transaction { nonce: 0.into(), @@ -1363,7 +1371,7 @@ mod tests { state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b611000f2").unwrap()).unwrap(); state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()).unwrap(); - let result = state.apply(&info, engine, &t, true).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), @@ -1377,7 +1385,7 @@ mod tests { call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { - gas_used: 64.into(), + gas_used: 724.into(), // in post-eip150 output: vec![] }), }, FlatTrace { @@ -1409,9 +1417,7 @@ mod tests { let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); info.number = 0x789b0; - let engine = &*Spec::new_test().engine; - - println!("schedule.have_delegate_call: {:?}", engine.schedule(info.number).have_delegate_call); + let machine = Spec::new_test_machine(); let t = Transaction { nonce: 0.into(), @@ -1424,7 +1430,7 @@ mod tests { state.init_code(&0xa.into(), FromHex::from_hex("6000600060006000600b618000f4").unwrap()).unwrap(); state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()).unwrap(); - let result = state.apply(&info, engine, &t, true).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), @@ -1438,7 +1444,7 @@ mod tests { call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(61), + gas_used: U256::from(721), // in post-eip150 output: vec![] }), }, FlatTrace { @@ -1469,7 +1475,7 @@ mod tests { let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); + let machine = make_frontier_machine(5); let t = Transaction { nonce: 0.into(), @@ -1482,7 +1488,7 @@ mod tests { state.init_code(&0xa.into(), FromHex::from_hex("5b600056").unwrap()).unwrap(); state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &engine, &t, true).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), action: trace::Action::Call(trace::Call { @@ -1508,7 +1514,7 @@ mod tests { let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); + let machine = make_frontier_machine(5); let t = Transaction { nonce: 0.into(), @@ -1522,7 +1528,7 @@ mod tests { state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()).unwrap(); state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()).unwrap(); state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &engine, &t, true).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), @@ -1567,7 +1573,7 @@ mod tests { let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); + let machine = make_frontier_machine(5); let t = Transaction { nonce: 0.into(), @@ -1580,7 +1586,7 @@ mod tests { state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006045600b6000f1").unwrap()).unwrap(); state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &engine, &t, true).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), subtraces: 1, @@ -1621,7 +1627,7 @@ mod tests { let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); + let machine = make_frontier_machine(5); let t = Transaction { nonce: 0.into(), @@ -1634,7 +1640,7 @@ mod tests { state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060ff600b6000f1").unwrap()).unwrap(); // not enough funds. state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &engine, &t, true).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), subtraces: 0, @@ -1663,7 +1669,7 @@ mod tests { let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); + let machine = make_frontier_machine(5); let t = Transaction { nonce: 0.into(), @@ -1677,7 +1683,7 @@ mod tests { state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()).unwrap(); state.init_code(&0xb.into(), FromHex::from_hex("5b600056").unwrap()).unwrap(); state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &engine, &t, true).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), subtraces: 1, @@ -1718,7 +1724,7 @@ mod tests { let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); + let machine = make_frontier_machine(5); let t = Transaction { nonce: 0.into(), @@ -1733,7 +1739,7 @@ mod tests { state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1").unwrap()).unwrap(); state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap()).unwrap(); state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &engine, &t, true).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), subtraces: 1, @@ -1792,7 +1798,7 @@ mod tests { let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); + let machine = make_frontier_machine(5); let t = Transaction { nonce: 0.into(), @@ -1807,7 +1813,7 @@ mod tests { state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1505b601256").unwrap()).unwrap(); state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap()).unwrap(); state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &engine, &t, true).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), @@ -1864,7 +1870,7 @@ mod tests { let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); + let machine = make_frontier_machine(5); let t = Transaction { nonce: 0.into(), @@ -1878,7 +1884,7 @@ mod tests { state.init_code(&0xa.into(), FromHex::from_hex("73000000000000000000000000000000000000000bff").unwrap()).unwrap(); state.add_balance(&0xa.into(), &50.into(), CleanupMode::NoEmpty).unwrap(); state.add_balance(&t.sender(), &100.into(), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &engine, &t, true).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), subtraces: 1, diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index 25955679c..4e44573e2 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -362,7 +362,7 @@ fn transaction_proof() { let root = client.best_block_header().state_root(); let mut state = State::from_existing(backend, root, 0.into(), factories.clone()).unwrap(); - Executive::new(&mut state, &client.latest_env_info(), &*test_spec.engine) + Executive::new(&mut state, &client.latest_env_info(), test_spec.engine.machine()) .transact(&transaction, TransactOptions::with_no_tracing().dont_check_nonce()).unwrap(); assert_eq!(state.balance(&Address::default()).unwrap(), 5.into()); diff --git a/ethcore/src/tests/evm.rs b/ethcore/src/tests/evm.rs index 24f1e2acb..021ab444e 100644 --- a/ethcore/src/tests/evm.rs +++ b/ethcore/src/tests/evm.rs @@ -24,7 +24,7 @@ fn test_blockhash_eip210(factory: Factory) { let test_blockhash_contract = "73fffffffffffffffffffffffffffffffffffffffe33141561007a57600143036020526000356101006020510755600061010060205107141561005057600035610100610100602051050761010001555b6000620100006020510714156100755760003561010062010000602051050761020001555b61014a565b4360003512151561009057600060405260206040f35b610100600035430312156100b357610100600035075460605260206060f3610149565b62010000600035430312156100d157600061010060003507146100d4565b60005b156100f6576101006101006000350507610100015460805260206080f3610148565b630100000060003543031215610116576000620100006000350714610119565b60005b1561013c57610100620100006000350507610200015460a052602060a0f3610147565b600060c052602060c0f35b5b5b5b5b"; let blockhash_contract_code = Arc::new(test_blockhash_contract.from_hex().unwrap()); let blockhash_contract_code_hash = keccak(blockhash_contract_code.as_ref()); - let engine = TestEngine::new_constantinople(); + let machine = ::ethereum::new_constantinople_test_machine(); let mut env_info = EnvInfo::default(); // populate state with 256 last hashes @@ -46,7 +46,7 @@ fn test_blockhash_eip210(factory: Factory) { data: Some(H256::from(i - 1).to_vec()), call_type: CallType::Call, }; - let mut ex = Executive::new(&mut state, &env_info, &engine); + let mut ex = Executive::new(&mut state, &env_info, &machine); let mut substate = Substate::new(); let mut output = []; if let Err(e) = ex.call(params, &mut substate, BytesRef::Fixed(&mut output), &mut NoopTracer, &mut NoopVMTracer) { @@ -68,7 +68,7 @@ fn test_blockhash_eip210(factory: Factory) { data: None, call_type: CallType::Call, }; - let mut ex = Executive::new(&mut state, &env_info, &engine); + let mut ex = Executive::new(&mut state, &env_info, &machine); let mut substate = Substate::new(); let mut output = H256::new(); if let Err(e) = ex.call(params, &mut substate, BytesRef::Fixed(&mut output), &mut NoopTracer, &mut NoopVMTracer) { diff --git a/ethcore/src/tests/helpers.rs b/ethcore/src/tests/helpers.rs index f61fa9d5a..df5e83226 100644 --- a/ethcore/src/tests/helpers.rs +++ b/ethcore/src/tests/helpers.rs @@ -14,82 +14,31 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::collections::BTreeMap; -use std::sync::Arc; -use hash::keccak; -use ethkey::KeyPair; -use io::*; -use client::{BlockChainClient, Client, ClientConfig}; -use bigint::prelude::U256; -use bigint::hash::H256; -use util::*; -use bytes::Bytes; -use spec::*; use account_provider::AccountProvider; -use state_db::StateDB; +use bigint::hash::H256; +use bigint::prelude::U256; use block::{OpenBlock, Drain}; use blockchain::{BlockChain, Config as BlockChainConfig}; -use builtin::Builtin; -use state::*; -use evm::{Schedule, Factory as EvmFactory}; -use factory::Factories; -use engines::Engine; -use ethereum; +use bytes::Bytes; +use client::{BlockChainClient, Client, ClientConfig}; use ethereum::ethash::EthashParams; -use miner::Miner; +use ethkey::KeyPair; +use evm::Factory as EvmFactory; +use factory::Factories; +use hash::keccak; use header::Header; -use transaction::{Action, Transaction, SignedTransaction}; +use io::*; +use machine::EthashExtensions; +use miner::Miner; use rlp::{self, RlpStream}; +use spec::*; +use state_db::StateDB; +use state::*; +use std::sync::Arc; +use transaction::{Action, Transaction, SignedTransaction}; +use util::*; use views::BlockView; -pub struct TestEngine { - engine: Arc, - max_depth: usize, -} - -impl TestEngine { - pub fn new(max_depth: usize) -> TestEngine { - TestEngine { - engine: ethereum::new_frontier_test().engine, - max_depth: max_depth, - } - } - - pub fn new_byzantium() -> TestEngine { - TestEngine { - engine: ethereum::new_byzantium_test().engine, - max_depth: 0, - } - } - - pub fn new_constantinople() -> TestEngine { - TestEngine { - engine: ethereum::new_constantinople_test().engine, - max_depth: 0, - } - } -} - -impl Engine for TestEngine { - fn name(&self) -> &str { - "TestEngine" - } - - fn params(&self) -> &CommonParams { - self.engine.params() - } - - fn builtins(&self) -> &BTreeMap { - self.engine.builtins() - } - - fn schedule(&self, _block_number: u64) -> Schedule { - let mut schedule = self.engine.schedule(0); - schedule.max_depth = self.max_depth; - schedule - } -} - // TODO: move everything over to get_null_spec. pub fn get_test_spec() -> Spec { Spec::new_test() @@ -233,9 +182,9 @@ pub fn generate_dummy_client_with_spec_accounts_and_data(get_test_spec: F, ac pub fn push_blocks_to_client(client: &Arc, timestamp_salt: u64, starting_number: usize, block_number: usize) { let test_spec = get_test_spec(); - let test_engine = &test_spec.engine; - //let test_engine = test_spec.to_engine().unwrap(); let state_root = test_spec.genesis_header().state_root().clone(); + let genesis_gas = test_spec.genesis_header().gas_limit().clone(); + let mut rolling_hash = client.chain_info().best_block_hash; let mut rolling_block_number = starting_number as u64; let mut rolling_timestamp = timestamp_salt + starting_number as u64 * 10; @@ -243,7 +192,7 @@ pub fn push_blocks_to_client(client: &Arc, timestamp_salt: u64, starting for _ in 0..block_number { let mut header = Header::new(); - header.set_gas_limit(test_engine.params().min_gas_limit); + header.set_gas_limit(genesis_gas); header.set_difficulty(U256::from(0x20000)); header.set_timestamp(rolling_timestamp); header.set_number(rolling_block_number); @@ -272,9 +221,9 @@ pub fn get_test_client_with_blocks(blocks: Vec) -> Arc { IoChannel::disconnected(), ).unwrap(); - for block in &blocks { - if client.import_block(block.clone()).is_err() { - panic!("panic importing block which is well-formed"); + for block in blocks { + if let Err(e) = client.import_block(block) { + panic!("error importing block which is well-formed: {:?}", e); } } client.flush_queue(); @@ -344,13 +293,13 @@ pub fn get_good_dummy_block_seq(count: usize) -> Vec { pub fn get_good_dummy_block_fork_seq(start_number: usize, count: usize, parent_hash: &H256) -> Vec { let test_spec = get_test_spec(); - let test_engine = &test_spec.engine; + let genesis_gas = test_spec.genesis_header().gas_limit().clone(); let mut rolling_timestamp = start_number as u64 * 10; let mut parent = *parent_hash; let mut r = Vec::new(); for i in start_number .. start_number + count + 1 { let mut block_header = Header::new(); - block_header.set_gas_limit(test_engine.params().min_gas_limit); + block_header.set_gas_limit(genesis_gas); block_header.set_difficulty(U256::from(i) * U256([0, 1, 0, 0])); block_header.set_timestamp(rolling_timestamp); block_header.set_number(i as u64); @@ -368,8 +317,8 @@ pub fn get_good_dummy_block_fork_seq(start_number: usize, count: usize, parent_h pub fn get_good_dummy_block_hash() -> (H256, Bytes) { let mut block_header = Header::new(); let test_spec = get_test_spec(); - let test_engine = &test_spec.engine; - block_header.set_gas_limit(test_engine.params().min_gas_limit); + let genesis_gas = test_spec.genesis_header().gas_limit().clone(); + block_header.set_gas_limit(genesis_gas); block_header.set_difficulty(U256::from(0x20000)); block_header.set_timestamp(40); block_header.set_number(1); @@ -387,8 +336,9 @@ pub fn get_good_dummy_block() -> Bytes { pub fn get_bad_state_dummy_block() -> Bytes { let mut block_header = Header::new(); let test_spec = get_test_spec(); - let test_engine = &test_spec.engine; - block_header.set_gas_limit(test_engine.params().min_gas_limit); + let genesis_gas = test_spec.genesis_header().gas_limit().clone(); + + block_header.set_gas_limit(genesis_gas); block_header.set_difficulty(U256::from(0x20000)); block_header.set_timestamp(40); block_header.set_number(1); @@ -398,33 +348,35 @@ pub fn get_bad_state_dummy_block() -> Bytes { create_test_block(&block_header) } +pub fn get_default_ethash_extensions() -> EthashExtensions { + EthashExtensions { + homestead_transition: 1150000, + eip150_transition: u64::max_value(), + eip160_transition: u64::max_value(), + eip161abc_transition: u64::max_value(), + eip161d_transition: u64::max_value(), + dao_hardfork_transition: u64::max_value(), + dao_hardfork_beneficiary: "0000000000000000000000000000000000000001".into(), + dao_hardfork_accounts: Vec::new(), + } +} + pub fn get_default_ethash_params() -> EthashParams { EthashParams { minimum_difficulty: U256::from(131072), difficulty_bound_divisor: U256::from(2048), difficulty_increment_divisor: 10, metropolis_difficulty_increment_divisor: 9, - duration_limit: 13, homestead_transition: 1150000, - dao_hardfork_transition: u64::max_value(), - dao_hardfork_beneficiary: "0000000000000000000000000000000000000001".into(), - dao_hardfork_accounts: vec![], + duration_limit: 13, + block_reward: 0.into(), difficulty_hardfork_transition: u64::max_value(), difficulty_hardfork_bound_divisor: U256::from(0), bomb_defuse_transition: u64::max_value(), eip100b_transition: u64::max_value(), - eip150_transition: u64::max_value(), - eip160_transition: u64::max_value(), - eip161abc_transition: u64::max_value(), - eip161d_transition: u64::max_value(), ecip1010_pause_transition: u64::max_value(), ecip1010_continue_transition: u64::max_value(), ecip1017_era_rounds: u64::max_value(), - max_code_size: u64::max_value(), - max_gas_limit_transition: u64::max_value(), - max_gas_limit: U256::max_value(), - min_gas_price_transition: u64::max_value(), - min_gas_price: U256::zero(), eip649_transition: u64::max_value(), eip649_delay: 3_000_000, eip649_reward: None, diff --git a/ethcore/src/tests/trace.rs b/ethcore/src/tests/trace.rs index ed434d8e8..8bd52330d 100644 --- a/ethcore/src/tests/trace.rs +++ b/ethcore/src/tests/trace.rs @@ -65,6 +65,8 @@ fn can_trace_block_and_uncle_reward() { // block with transaction and uncle let genesis_header = spec.genesis_header(); + let genesis_gas = genesis_header.gas_limit().clone(); + let mut db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); let mut rolling_timestamp = 40; let mut last_hashes = vec![]; @@ -165,7 +167,7 @@ fn can_trace_block_and_uncle_reward() { let uncle_author: Address = "ef2d6d194084c2de36e0dabfce45d046b37d1106".into(); uncle.set_author(uncle_author); uncle.set_parent_hash(root_header.hash()); - uncle.set_gas_limit(U256::from(50_000)); + uncle.set_gas_limit(genesis_gas); uncle.set_number(root_header.number() + 1); uncle.set_timestamp(rolling_timestamp); block.push_uncle(uncle).unwrap(); diff --git a/ethcore/src/tx_filter.rs b/ethcore/src/tx_filter.rs index 0c46590d6..25c6ed64b 100644 --- a/ethcore/src/tx_filter.rs +++ b/ethcore/src/tx_filter.rs @@ -16,15 +16,14 @@ //! Smart contract based transaction filter. -use std::sync::Weak; use std::collections::HashMap; use std::collections::hash_map::Entry; use bigint::hash::H256; use native_contracts::TransactAcl as Contract; -use client::{EngineClient, BlockId, ChainNotify}; +use client::{BlockChainClient, BlockId, ChainNotify}; use util::Address; use bytes::Bytes; -use parking_lot::{Mutex, RwLock}; +use parking_lot::Mutex; use futures::{self, Future}; use spec::CommonParams; use transaction::{Action, SignedTransaction}; @@ -44,7 +43,6 @@ mod tx_permissions { /// Connection filter that uses a contract to manage permissions. pub struct TransactionFilter { contract: Mutex>, - client: RwLock>>, contract_address: Address, permission_cache: Mutex>, } @@ -55,7 +53,6 @@ impl TransactionFilter { params.transaction_permission_contract.map(|address| TransactionFilter { contract: Mutex::new(None), - client: RwLock::new(None), contract_address: address, permission_cache: Mutex::new(HashMap::new()), } @@ -67,63 +64,47 @@ impl TransactionFilter { self.permission_cache.lock().clear(); } - /// Set client reference to be used for contract call. - pub fn register_client(&self, client: Weak) { - *self.client.write() = Some(client); - } - /// Check if transaction is allowed at given block. - pub fn transaction_allowed(&self, parent_hash: &H256, transaction: &SignedTransaction) -> bool { - self.client.read().as_ref().map_or(false, |client| { - let mut cache = self.permission_cache.lock(); let len = cache.len(); - let client = match client.upgrade() { - Some(client) => client, - _ => return false, - }; + pub fn transaction_allowed(&self, parent_hash: &H256, transaction: &SignedTransaction, client: &BlockChainClient) -> bool { + let mut cache = self.permission_cache.lock(); let len = cache.len(); - let client = match client.as_full_client() { - Some(client) => client, - _ => return false, // TODO: how to handle verification for light clients? - }; - - let tx_type = match transaction.action { - Action::Create => tx_permissions::CREATE, - Action::Call(address) => if client.code_hash(&address, BlockId::Hash(*parent_hash)).map_or(false, |c| c != KECCAK_EMPTY) { - tx_permissions::CALL - } else { - tx_permissions::BASIC - } - }; - let sender = transaction.sender(); - match cache.entry((*parent_hash, sender)) { - Entry::Occupied(entry) => *entry.get() & tx_type != 0, - Entry::Vacant(entry) => { - let mut contract = self.contract.lock(); - if contract.is_none() { - *contract = Some(Contract::new(self.contract_address)); - } - - let permissions = match &*contract { - &Some(ref contract) => { - contract.allowed_tx_types( - |addr, data| futures::done(client.call_contract(BlockId::Hash(*parent_hash), addr, data)), - sender, - ).wait().unwrap_or_else(|e| { - debug!("Error callling tx permissions contract: {:?}", e); - tx_permissions::NONE - }) - } - _ => tx_permissions::NONE, - }; - - if len < MAX_CACHE_SIZE { - entry.insert(permissions); - } - trace!("Permissions required: {}, got: {}", tx_type, permissions); - permissions & tx_type != 0 - } + let tx_type = match transaction.action { + Action::Create => tx_permissions::CREATE, + Action::Call(address) => if client.code_hash(&address, BlockId::Hash(*parent_hash)).map_or(false, |c| c != KECCAK_EMPTY) { + tx_permissions::CALL + } else { + tx_permissions::BASIC } - }) + }; + let sender = transaction.sender(); + match cache.entry((*parent_hash, sender)) { + Entry::Occupied(entry) => *entry.get() & tx_type != 0, + Entry::Vacant(entry) => { + let mut contract = self.contract.lock(); + if contract.is_none() { + *contract = Some(Contract::new(self.contract_address)); + } + + let permissions = match &*contract { + &Some(ref contract) => { + contract.allowed_tx_types( + |addr, data| futures::done(client.call_contract(BlockId::Hash(*parent_hash), addr, data)), + sender, + ).wait().unwrap_or_else(|e| { + debug!("Error callling tx permissions contract: {:?}", e); + tx_permissions::NONE + }) + } + _ => tx_permissions::NONE, + }; + + if len < MAX_CACHE_SIZE { + entry.insert(permissions); + } + trace!("Permissions required: {}, got: {}", tx_type, permissions); + permissions & tx_type != 0 + } + } } } @@ -137,7 +118,7 @@ impl ChainNotify for TransactionFilter { #[cfg(test)] mod test { - use std::sync::{Arc, Weak}; + use std::sync::Arc; use spec::Spec; use client::{BlockChainClient, Client, ClientConfig, BlockId}; use miner::Miner; @@ -212,7 +193,6 @@ mod test { let key4 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000004")).unwrap(); let filter = TransactionFilter::from_params(spec.params()).unwrap(); - filter.register_client(Arc::downgrade(&client) as Weak<_>); let mut basic_tx = Transaction::default(); basic_tx.action = Action::Call(Address::from("000000000000000000000000000000000000032")); let create_tx = Transaction::default(); @@ -221,21 +201,21 @@ mod test { let genesis = client.block_hash(BlockId::Latest).unwrap(); - assert!(filter.transaction_allowed(&genesis, &basic_tx.clone().sign(key1.secret(), None))); - assert!(filter.transaction_allowed(&genesis, &create_tx.clone().sign(key1.secret(), None))); - assert!(filter.transaction_allowed(&genesis, &call_tx.clone().sign(key1.secret(), None))); + assert!(filter.transaction_allowed(&genesis, &basic_tx.clone().sign(key1.secret(), None), &*client)); + assert!(filter.transaction_allowed(&genesis, &create_tx.clone().sign(key1.secret(), None), &*client)); + assert!(filter.transaction_allowed(&genesis, &call_tx.clone().sign(key1.secret(), None), &*client)); - assert!(filter.transaction_allowed(&genesis, &basic_tx.clone().sign(key2.secret(), None))); - assert!(!filter.transaction_allowed(&genesis, &create_tx.clone().sign(key2.secret(), None))); - assert!(filter.transaction_allowed(&genesis, &call_tx.clone().sign(key2.secret(), None))); + assert!(filter.transaction_allowed(&genesis, &basic_tx.clone().sign(key2.secret(), None), &*client)); + assert!(!filter.transaction_allowed(&genesis, &create_tx.clone().sign(key2.secret(), None), &*client)); + assert!(filter.transaction_allowed(&genesis, &call_tx.clone().sign(key2.secret(), None), &*client)); - assert!(filter.transaction_allowed(&genesis, &basic_tx.clone().sign(key3.secret(), None))); - assert!(!filter.transaction_allowed(&genesis, &create_tx.clone().sign(key3.secret(), None))); - assert!(!filter.transaction_allowed(&genesis, &call_tx.clone().sign(key3.secret(), None))); + assert!(filter.transaction_allowed(&genesis, &basic_tx.clone().sign(key3.secret(), None), &*client)); + assert!(!filter.transaction_allowed(&genesis, &create_tx.clone().sign(key3.secret(), None), &*client)); + assert!(!filter.transaction_allowed(&genesis, &call_tx.clone().sign(key3.secret(), None), &*client)); - assert!(!filter.transaction_allowed(&genesis, &basic_tx.clone().sign(key4.secret(), None))); - assert!(!filter.transaction_allowed(&genesis, &create_tx.clone().sign(key4.secret(), None))); - assert!(!filter.transaction_allowed(&genesis, &call_tx.clone().sign(key4.secret(), None))); + assert!(!filter.transaction_allowed(&genesis, &basic_tx.clone().sign(key4.secret(), None), &*client)); + assert!(!filter.transaction_allowed(&genesis, &create_tx.clone().sign(key4.secret(), None), &*client)); + assert!(!filter.transaction_allowed(&genesis, &call_tx.clone().sign(key4.secret(), None), &*client)); } } diff --git a/ethcore/src/verification/canon_verifier.rs b/ethcore/src/verification/canon_verifier.rs index a5d8fe73f..9c942b9b6 100644 --- a/ethcore/src/verification/canon_verifier.rs +++ b/ethcore/src/verification/canon_verifier.rs @@ -16,8 +16,7 @@ //! Canonical verifier. -use blockchain::BlockProvider; -use engines::Engine; +use engines::EthEngine; use error::Error; use header::Header; use super::Verifier; @@ -27,15 +26,21 @@ use super::verification; pub struct CanonVerifier; impl Verifier for CanonVerifier { - fn verify_block_family(&self, header: &Header, bytes: &[u8], engine: &Engine, bc: &BlockProvider) -> Result<(), Error> { - verification::verify_block_family(header, bytes, engine, bc) + fn verify_block_family( + &self, + header: &Header, + parent: &Header, + engine: &EthEngine, + do_full: Option, + ) -> Result<(), Error> { + verification::verify_block_family(header, parent, engine, do_full) } fn verify_block_final(&self, expected: &Header, got: &Header) -> Result<(), Error> { verification::verify_block_final(expected, got) } - fn verify_block_external(&self, header: &Header, bytes: &[u8], engine: &Engine) -> Result<(), Error> { - engine.verify_block_external(header, Some(bytes)) + fn verify_block_external(&self, header: &Header, engine: &EthEngine) -> Result<(), Error> { + engine.verify_block_external(header) } } diff --git a/ethcore/src/verification/noop_verifier.rs b/ethcore/src/verification/noop_verifier.rs index 8464ba1e2..b1c2ec1bc 100644 --- a/ethcore/src/verification/noop_verifier.rs +++ b/ethcore/src/verification/noop_verifier.rs @@ -16,18 +16,23 @@ //! No-op verifier. -use blockchain::BlockProvider; -use engines::Engine; +use engines::EthEngine; use error::Error; use header::Header; -use super::Verifier; +use super::{verification, Verifier}; /// A no-op verifier -- this will verify everything it's given immediately. #[allow(dead_code)] pub struct NoopVerifier; impl Verifier for NoopVerifier { - fn verify_block_family(&self, _header: &Header, _bytes: &[u8], _engine: &Engine, _bc: &BlockProvider) -> Result<(), Error> { + fn verify_block_family( + &self, + _: &Header, + _t: &Header, + _: &EthEngine, + _: Option + ) -> Result<(), Error> { Ok(()) } @@ -35,7 +40,7 @@ impl Verifier for NoopVerifier { Ok(()) } - fn verify_block_external(&self, _header: &Header, _bytes: &[u8], _engine: &Engine) -> Result<(), Error> { + fn verify_block_external(&self, _header: &Header, _engine: &EthEngine) -> Result<(), Error> { Ok(()) } } diff --git a/ethcore/src/verification/queue/kind.rs b/ethcore/src/verification/queue/kind.rs index 47946ef74..c5be79d94 100644 --- a/ethcore/src/verification/queue/kind.rs +++ b/ethcore/src/verification/queue/kind.rs @@ -16,7 +16,7 @@ //! Definition of valid items for the verification queue. -use engines::Engine; +use engines::EthEngine; use error::Error; use heapsize::HeapSizeOf; @@ -59,17 +59,17 @@ pub trait Kind: 'static + Sized + Send + Sync { type Verified: Sized + Send + BlockLike + HeapSizeOf; /// Attempt to create the `Unverified` item from the input. - fn create(input: Self::Input, engine: &Engine) -> Result; + fn create(input: Self::Input, engine: &EthEngine) -> Result; /// Attempt to verify the `Unverified` item using the given engine. - fn verify(unverified: Self::Unverified, engine: &Engine, check_seal: bool) -> Result; + fn verify(unverified: Self::Unverified, engine: &EthEngine, check_seal: bool) -> Result; } /// The blocks verification module. pub mod blocks { use super::{Kind, BlockLike}; - use engines::Engine; + use engines::EthEngine; use error::Error; use header::Header; use verification::{PreverifiedBlock, verify_block_basic, verify_block_unordered}; @@ -87,7 +87,7 @@ pub mod blocks { type Unverified = Unverified; type Verified = PreverifiedBlock; - fn create(input: Self::Input, engine: &Engine) -> Result { + fn create(input: Self::Input, engine: &EthEngine) -> Result { match verify_block_basic(&input.header, &input.bytes, engine) { Ok(()) => Ok(input), Err(e) => { @@ -97,7 +97,7 @@ pub mod blocks { } } - fn verify(un: Self::Unverified, engine: &Engine, check_seal: bool) -> Result { + fn verify(un: Self::Unverified, engine: &EthEngine, check_seal: bool) -> Result { let hash = un.hash(); match verify_block_unordered(un.header, un.bytes, engine, check_seal) { Ok(verified) => Ok(verified), @@ -167,7 +167,7 @@ pub mod blocks { pub mod headers { use super::{Kind, BlockLike}; - use engines::Engine; + use engines::EthEngine; use error::Error; use header::Header; use verification::verify_header_params; @@ -189,13 +189,13 @@ pub mod headers { type Unverified = Header; type Verified = Header; - fn create(input: Self::Input, engine: &Engine) -> Result { + fn create(input: Self::Input, engine: &EthEngine) -> Result { verify_header_params(&input, engine, true).map(|_| input) } - fn verify(unverified: Self::Unverified, engine: &Engine, check_seal: bool) -> Result { + fn verify(unverified: Self::Unverified, engine: &EthEngine, check_seal: bool) -> Result { match check_seal { - true => engine.verify_block_unordered(&unverified, None).map(|_| unverified), + true => engine.verify_block_unordered(&unverified,).map(|_| unverified), false => Ok(unverified), } } diff --git a/ethcore/src/verification/queue/mod.rs b/ethcore/src/verification/queue/mod.rs index 6e2e2f128..198c63287 100644 --- a/ethcore/src/verification/queue/mod.rs +++ b/ethcore/src/verification/queue/mod.rs @@ -28,7 +28,7 @@ use bigint::hash::H256; use parking_lot::{Condvar, Mutex, RwLock}; use io::*; use error::*; -use engines::Engine; +use engines::EthEngine; use service::*; use self::kind::{BlockLike, Kind}; @@ -141,7 +141,7 @@ struct Sizes { /// A queue of items to be verified. Sits between network or other I/O and the `BlockChain`. /// Keeps them in the same order as inserted, minus invalid items. pub struct VerificationQueue { - engine: Arc, + engine: Arc, more_to_verify: Arc, verification: Arc>, deleting: Arc, @@ -213,7 +213,7 @@ struct Verification { impl VerificationQueue { /// Creates a new queue instance. - pub fn new(config: Config, engine: Arc, message_channel: IoChannel, check_seal: bool) -> Self { + pub fn new(config: Config, engine: Arc, message_channel: IoChannel, check_seal: bool) -> Self { let verification = Arc::new(Verification { unverified: Mutex::new(VecDeque::new()), verifying: Mutex::new(VecDeque::new()), @@ -294,7 +294,7 @@ impl VerificationQueue { fn verify( verification: Arc>, - engine: Arc, + engine: Arc, wait: Arc, ready: Arc, empty: Arc, diff --git a/ethcore/src/verification/verification.rs b/ethcore/src/verification/verification.rs index 80d433950..c56641916 100644 --- a/ethcore/src/verification/verification.rs +++ b/ethcore/src/verification/verification.rs @@ -22,20 +22,24 @@ //! 3. Final verification against the blockchain done before enactment. use std::collections::HashSet; -use hash::keccak; -use triehash::ordered_trie_root; -use heapsize::HeapSizeOf; -use bigint::hash::H256; -use unexpected::{Mismatch, OutOfBounds}; -use bytes::Bytes; -use engines::Engine; -use error::{BlockError, Error}; + use blockchain::*; +use client::BlockChainClient; +use engines::EthEngine; +use error::{BlockError, Error}; use header::{BlockNumber, Header}; -use rlp::UntrustedRlp; use transaction::SignedTransaction; use views::BlockView; + +use bigint::hash::H256; +use bigint::prelude::U256; +use bytes::Bytes; +use hash::keccak; +use heapsize::HeapSizeOf; +use rlp::UntrustedRlp; use time::get_time; +use triehash::ordered_trie_root; +use unexpected::{Mismatch, OutOfBounds}; /// Preprocessed block data gathered in `verify_block_unordered` call pub struct PreverifiedBlock { @@ -56,14 +60,14 @@ impl HeapSizeOf for PreverifiedBlock { } /// Phase 1 quick block verification. Only does checks that are cheap. Operates on a single block -pub fn verify_block_basic(header: &Header, bytes: &[u8], engine: &Engine) -> Result<(), Error> { +pub fn verify_block_basic(header: &Header, bytes: &[u8], engine: &EthEngine) -> Result<(), Error> { verify_header_params(&header, engine, true)?; verify_block_integrity(bytes, &header.transactions_root(), &header.uncles_hash())?; - engine.verify_block_basic(&header, Some(bytes))?; + engine.verify_block_basic(&header)?; for u in UntrustedRlp::new(bytes).at(2)?.iter().map(|rlp| rlp.as_val::
()) { let u = u?; verify_header_params(&u, engine, false)?; - engine.verify_block_basic(&u, None)?; + engine.verify_block_basic(&u)?; } // Verify transactions. // TODO: either use transaction views or cache the decoded transactions. @@ -77,11 +81,11 @@ pub fn verify_block_basic(header: &Header, bytes: &[u8], engine: &Engine) -> Res /// Phase 2 verification. Perform costly checks such as transaction signatures and block nonce for ethash. /// Still operates on a individual block /// Returns a `PreverifiedBlock` structure populated with transactions -pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &Engine, check_seal: bool) -> Result { +pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &EthEngine, check_seal: bool) -> Result { if check_seal { - engine.verify_block_unordered(&header, Some(&bytes))?; + engine.verify_block_unordered(&header)?; for u in UntrustedRlp::new(&bytes).at(2)?.iter().map(|rlp| rlp.as_val::
()) { - engine.verify_block_unordered(&u?, None)?; + engine.verify_block_unordered(&u?)?; } } // Verify transactions. @@ -92,7 +96,7 @@ pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &Engine, che { let v = BlockView::new(&bytes); for t in v.transactions() { - let t = engine.verify_transaction(t, &header)?; + let t = engine.verify_transaction_unordered(t, &header)?; if let Some(max_nonce) = nonce_cap { if t.nonce >= max_nonce { return Err(BlockError::TooManyTransactions(t.sender()).into()); @@ -108,13 +112,30 @@ pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &Engine, che }) } -/// Phase 3 verification. Check block information against parent and uncles. -pub fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &BlockProvider) -> Result<(), Error> { - // TODO: verify timestamp - let parent = bc.block_header(&header.parent_hash()).ok_or_else(|| Error::from(BlockError::UnknownParent(header.parent_hash().clone())))?; - verify_parent(&header, &parent)?; - engine.verify_block_family(&header, &parent, Some(bytes))?; +/// Parameters for full verification of block family: block bytes, transactions, blockchain, and state access. +pub type FullFamilyParams<'a> = (&'a [u8], &'a [SignedTransaction], &'a BlockProvider, &'a BlockChainClient); +/// Phase 3 verification. Check block information against parent and uncles. +pub fn verify_block_family(header: &Header, parent: &Header, engine: &EthEngine, do_full: Option) -> Result<(), Error> { + // TODO: verify timestamp + verify_parent(&header, &parent, engine.params().gas_limit_bound_divisor)?; + engine.verify_block_family(&header, &parent)?; + + let (bytes, txs, bc, client) = match do_full { + Some(x) => x, + None => return Ok(()), + }; + + verify_uncles(header, bytes, bc, engine)?; + + for transaction in txs { + engine.machine().verify_transaction(transaction, header, client)?; + } + + Ok(()) +} + +fn verify_uncles(header: &Header, bytes: &[u8], bc: &BlockProvider, engine: &EthEngine) -> Result<(), Error> { let num_uncles = UntrustedRlp::new(bytes).at(2)?.item_count()?; if num_uncles != 0 { if num_uncles > engine.maximum_uncle_count() { @@ -189,11 +210,12 @@ pub fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: & return Err(From::from(BlockError::UncleParentNotInChain(uncle_parent.hash()))); } - verify_parent(&uncle, &uncle_parent)?; - engine.verify_block_family(&uncle, &uncle_parent, Some(bytes))?; + verify_parent(&uncle, &uncle_parent, engine.params().gas_limit_bound_divisor)?; + engine.verify_block_family(&uncle, &uncle_parent)?; verified.insert(uncle.hash()); } } + Ok(()) } @@ -215,7 +237,13 @@ pub fn verify_block_final(expected: &Header, got: &Header) -> Result<(), Error> } /// Check basic header parameters. -pub fn verify_header_params(header: &Header, engine: &Engine, is_full: bool) -> Result<(), Error> { +pub fn verify_header_params(header: &Header, engine: &EthEngine, is_full: bool) -> Result<(), Error> { + if header.seal().len() != engine.seal_fields() { + return Err(From::from(BlockError::InvalidSealArity( + Mismatch { expected: engine.seal_fields(), found: header.seal().len() } + ))); + } + if header.number() >= From::from(BlockNumber::max_value()) { return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { max: Some(From::from(BlockNumber::max_value())), min: None, found: header.number() }))) } @@ -230,6 +258,15 @@ pub fn verify_header_params(header: &Header, engine: &Engine, is_full: bool) -> if header.number() != 0 && header.extra_data().len() > maximum_extra_data_size { return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: None, max: Some(maximum_extra_data_size), found: header.extra_data().len() }))); } + + if let Some(ref ext) = engine.machine().ethash_extensions() { + if header.number() >= ext.dao_hardfork_transition && + header.number() <= ext.dao_hardfork_transition + 9 && + header.extra_data()[..] != b"dao-hard-fork"[..] { + return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: None, max: None, found: 0 }))); + } + } + if is_full { let max_time = get_time().sec as u64 + 30; if header.timestamp() > max_time { @@ -240,7 +277,7 @@ pub fn verify_header_params(header: &Header, engine: &Engine, is_full: bool) -> } /// Check header parameters agains parent header. -fn verify_parent(header: &Header, parent: &Header) -> Result<(), Error> { +fn verify_parent(header: &Header, parent: &Header, gas_limit_divisor: U256) -> Result<(), Error> { if !header.parent_hash().is_zero() && &parent.hash() != header.parent_hash() { return Err(From::from(BlockError::InvalidParentHash(Mismatch { expected: parent.hash(), found: header.parent_hash().clone() }))) } @@ -250,6 +287,18 @@ fn verify_parent(header: &Header, parent: &Header) -> Result<(), Error> { if header.number() != parent.number() + 1 { return Err(From::from(BlockError::InvalidNumber(Mismatch { expected: parent.number() + 1, found: header.number() }))); } + + if header.number() == 0 { + return Err(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }).into()); + } + + let parent_gas_limit = *parent.gas_limit(); + let min_gas = parent_gas_limit - parent_gas_limit / gas_limit_divisor; + let max_gas = parent_gas_limit + parent_gas_limit / gas_limit_divisor; + if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas { + return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit().clone() }))); + } + Ok(()) } @@ -285,7 +334,7 @@ mod tests { use error::BlockError::*; use views::*; use blockchain::*; - use engines::Engine; + use engines::EthEngine; use spec::*; use transaction::*; use tests::helpers::*; @@ -406,17 +455,38 @@ mod tests { } } - fn basic_test(bytes: &[u8], engine: &Engine) -> Result<(), Error> { + fn basic_test(bytes: &[u8], engine: &EthEngine) -> Result<(), Error> { let header = BlockView::new(bytes).header(); verify_block_basic(&header, bytes, engine) } - fn family_test(bytes: &[u8], engine: &Engine, bc: &BC) -> Result<(), Error> where BC: BlockProvider { - let header = BlockView::new(bytes).header(); - verify_block_family(&header, bytes, engine, bc) + fn family_test(bytes: &[u8], engine: &EthEngine, bc: &BC) -> Result<(), Error> where BC: BlockProvider { + let view = BlockView::new(bytes); + let header = view.header(); + let transactions: Vec<_> = view.transactions() + .into_iter() + .map(SignedTransaction::new) + .collect::>()?; + + // TODO: client is really meant to be used for state query here by machine + // additions that need access to state (tx filter in specific) + // no existing tests need access to test, so having this not function + // is fine. + let client = ::client::TestBlockChainClient::default(); + + let parent = bc.block_header(header.parent_hash()) + .ok_or(BlockError::UnknownParent(header.parent_hash().clone()))?; + + let full_params: FullFamilyParams = ( + bytes, + &transactions[..], + bc as &BlockProvider, + &client as &::client::BlockChainClient + ); + verify_block_family(&header, &parent, engine, Some(full_params)) } - fn unordered_test(bytes: &[u8], engine: &Engine) -> Result<(), Error> { + fn unordered_test(bytes: &[u8], engine: &EthEngine) -> Result<(), Error> { let header = BlockView::new(bytes).header(); verify_block_unordered(header, bytes.to_vec(), engine, false)?; Ok(()) @@ -590,6 +660,15 @@ mod tests { check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &bad_uncles), engine, &bc), DuplicateUncle(good_uncle1.hash())); + header = good.clone(); + header.set_gas_limit(0.into()); + header.set_difficulty("0000000000000000000000000000000000000000000000000000000000020000".parse::().unwrap()); + match family_test(&create_test_block(&header), engine, &bc) { + Err(Error::Block(InvalidGasLimit(_))) => {}, + Err(_) => { panic!("should be invalid difficulty fail"); }, + _ => { panic!("Should be error, got Ok"); }, + } + // TODO: some additional uncle checks } @@ -597,6 +676,7 @@ mod tests { fn dust_protection() { use ethkey::{Generator, Random}; use transaction::{Transaction, Action}; + use machine::EthereumMachine; use engines::NullEngine; let mut params = CommonParams::default(); @@ -618,7 +698,8 @@ mod tests { let good_transactions = [bad_transactions[0].clone(), bad_transactions[1].clone()]; - let engine = NullEngine::new(params, BTreeMap::new()); + let machine = EthereumMachine::regular(params, BTreeMap::new()); + let engine = NullEngine::new(Default::default(), machine); check_fail(unordered_test(&create_test_block_with_data(&header, &bad_transactions, &[]), &engine), TooManyTransactions(keypair.address())); unordered_test(&create_test_block_with_data(&header, &good_transactions, &[]), &engine).unwrap(); } diff --git a/ethcore/src/verification/verifier.rs b/ethcore/src/verification/verifier.rs index 55b711c1c..5141cea2f 100644 --- a/ethcore/src/verification/verifier.rs +++ b/ethcore/src/verification/verifier.rs @@ -16,17 +16,24 @@ //! A generic verifier trait. -use blockchain::BlockProvider; -use engines::Engine; +use engines::EthEngine; use error::Error; use header::Header; +use super::verification; /// Should be used to verify blocks. pub trait Verifier: Send + Sync { /// Verify a block relative to its parent and uncles. - fn verify_block_family(&self, header: &Header, bytes: &[u8], engine: &Engine, bc: &BlockProvider) -> Result<(), Error>; + fn verify_block_family( + &self, + header: &Header, + parent: &Header, + engine: &EthEngine, + do_full: Option + ) -> Result<(), Error>; + /// Do a final verification check for an enacted header vs its expected counterpart. fn verify_block_final(&self, expected: &Header, got: &Header) -> Result<(), Error>; /// Verify a block, inspecing external state. - fn verify_block_external(&self, header: &Header, bytes: &[u8], engine: &Engine) -> Result<(), Error>; + fn verify_block_external(&self, header: &Header, engine: &EthEngine) -> Result<(), Error>; } diff --git a/json/src/spec/authority_round.rs b/json/src/spec/authority_round.rs index 2d7d64086..984e2eff2 100644 --- a/json/src/spec/authority_round.rs +++ b/json/src/spec/authority_round.rs @@ -40,6 +40,9 @@ pub struct AuthorityRoundParams { /// Whether transitions should be immediate. #[serde(rename="immediateTransitions")] pub immediate_transitions: Option, + /// Reward per block in wei. + #[serde(rename="blockReward")] + pub block_reward: Option, } /// Authority engine deserialization. @@ -67,7 +70,8 @@ mod tests { "list" : ["0xc6d9d2cd449a754c494264e1809c50e34d64562b"] }, "startStep" : 24, - "validateStepTransition": 150 + "validateStepTransition": 150, + "blockReward": 5000000 } }"#; @@ -76,5 +80,6 @@ mod tests { assert_eq!(deserialized.params.validators, ValidatorSet::List(vec![Address(H160::from("0xc6d9d2cd449a754c494264e1809c50e34d64562b"))])); assert_eq!(deserialized.params.start_step, Some(Uint(U256::from(24)))); assert_eq!(deserialized.params.immediate_transitions, None); + } } diff --git a/json/src/spec/engine.rs b/json/src/spec/engine.rs index 8b487041e..e2545a5f9 100644 --- a/json/src/spec/engine.rs +++ b/json/src/spec/engine.rs @@ -16,14 +16,14 @@ //! Engine deserialization. -use super::{Ethash, BasicAuthority, AuthorityRound, Tendermint}; +use super::{Ethash, BasicAuthority, AuthorityRound, Tendermint, NullEngine}; /// Engine deserialization. #[derive(Debug, PartialEq, Deserialize)] pub enum Engine { /// Null engine. #[serde(rename="null")] - Null, + Null(NullEngine), /// Instantly sealing engine. #[serde(rename="instantSeal")] InstantSeal, @@ -48,11 +48,18 @@ mod tests { #[test] fn engine_deserialization() { let s = r#"{ - "null": null + "null": { + "params": { + "blockReward": "0x0d" + } + } }"#; let deserialized: Engine = serde_json::from_str(s).unwrap(); - assert_eq!(Engine::Null, deserialized); + match deserialized { + Engine::Null(_) => {}, // unit test in its own file. + _ => panic!(), + } let s = r#"{ "instantSeal": null @@ -61,7 +68,7 @@ mod tests { let deserialized: Engine = serde_json::from_str(s).unwrap(); match deserialized { Engine::InstantSeal => {}, // instant seal is unit tested in its own file. - _ => assert!(false), + _ => panic!(), }; let s = r#"{ @@ -82,7 +89,7 @@ mod tests { let deserialized: Engine = serde_json::from_str(s).unwrap(); match deserialized { Engine::Ethash(_) => {}, // ethash is unit tested in its own file. - _ => assert!(false), + _ => panic!(), }; let s = r#"{ @@ -98,7 +105,7 @@ mod tests { let deserialized: Engine = serde_json::from_str(s).unwrap(); match deserialized { Engine::BasicAuthority(_) => {}, // basicAuthority is unit tested in its own file. - _ => assert!(false), + _ => panic!(), }; let s = r#"{ @@ -116,7 +123,7 @@ mod tests { let deserialized: Engine = serde_json::from_str(s).unwrap(); match deserialized { Engine::AuthorityRound(_) => {}, // AuthorityRound is unit tested in its own file. - _ => assert!(false), + _ => panic!(), }; let s = r#"{ @@ -131,7 +138,7 @@ mod tests { let deserialized: Engine = serde_json::from_str(s).unwrap(); match deserialized { Engine::Tendermint(_) => {}, // Tendermint is unit tested in its own file. - _ => assert!(false), + _ => panic!(), }; } } diff --git a/json/src/spec/ethash.rs b/json/src/spec/ethash.rs index bfc6fe315..9f8ec67ca 100644 --- a/json/src/spec/ethash.rs +++ b/json/src/spec/ethash.rs @@ -20,7 +20,7 @@ use uint::Uint; use hash::Address; /// Deserializable doppelganger of EthashParams. -#[derive(Debug, PartialEq, Deserialize)] +#[derive(Clone, Debug, PartialEq, Deserialize)] pub struct EthashParams { /// See main EthashParams docs. #[serde(rename="minimumDifficulty")] @@ -41,6 +41,9 @@ pub struct EthashParams { /// See main EthashParams docs. #[serde(rename="homesteadTransition")] pub homestead_transition: Option, + /// Reward per block in wei. + #[serde(rename="blockReward")] + pub block_reward: Option, /// See main EthashParams docs. #[serde(rename="daoHardforkTransition")] @@ -91,27 +94,6 @@ pub struct EthashParams { /// See main EthashParams docs. #[serde(rename="ecip1017EraRounds")] pub ecip1017_era_rounds: Option, - - /// See main EthashParams docs. - #[serde(rename="maxCodeSize")] - pub max_code_size: Option, - - /// See main EthashParams docs. - #[serde(rename="maxGasLimitTransition")] - pub max_gas_limit_transition: Option, - - /// See main EthashParams docs. - #[serde(rename="maxGasLimit")] - pub max_gas_limit: Option, - - /// See main EthashParams docs. - #[serde(rename="minGasPriceTransition")] - pub min_gas_price_transition: Option, - - /// See main EthashParams docs. - #[serde(rename="minGasPrice")] - pub min_gas_price: Option, - /// EIP-649 transition block. #[serde(rename="eip649Transition")] pub eip649_transition: Option, @@ -148,6 +130,7 @@ mod tests { "difficultyBoundDivisor": "0x0800", "durationLimit": "0x0d", "homesteadTransition": "0x42", + "blockReward": "0x100", "daoHardforkTransition": "0x08", "daoHardforkBeneficiary": "0xabcabcabcabcabcabcabcabcabcabcabcabcabca", "daoHardforkAccounts": [ @@ -193,6 +176,7 @@ mod tests { metropolis_difficulty_increment_divisor: None, duration_limit: Some(Uint(U256::from(0x0d))), homestead_transition: Some(Uint(U256::from(0x42))), + block_reward: Some(Uint(U256::from(0x100))), dao_hardfork_transition: Some(Uint(U256::from(0x08))), dao_hardfork_beneficiary: Some(Address(H160::from("0xabcabcabcabcabcabcabcabcabcabcabcabcabca"))), dao_hardfork_accounts: Some(vec![ @@ -228,11 +212,6 @@ mod tests { ecip1010_pause_transition: None, ecip1010_continue_transition: None, ecip1017_era_rounds: None, - max_code_size: None, - max_gas_limit_transition: None, - max_gas_limit: None, - min_gas_price_transition: None, - min_gas_price: None, eip649_transition: None, eip649_delay: None, eip649_reward: None, @@ -258,6 +237,7 @@ mod tests { metropolis_difficulty_increment_divisor: None, duration_limit: None, homestead_transition: None, + block_reward: None, dao_hardfork_transition: None, dao_hardfork_beneficiary: None, dao_hardfork_accounts: None, @@ -272,11 +252,6 @@ mod tests { ecip1010_pause_transition: None, ecip1010_continue_transition: None, ecip1017_era_rounds: None, - max_code_size: None, - max_gas_limit_transition: None, - max_gas_limit: None, - min_gas_price_transition: None, - min_gas_price: None, eip649_transition: None, eip649_delay: None, eip649_reward: None, diff --git a/json/src/spec/mod.rs b/json/src/spec/mod.rs index bc7e751af..d4521656f 100644 --- a/json/src/spec/mod.rs +++ b/json/src/spec/mod.rs @@ -29,6 +29,7 @@ pub mod validator_set; pub mod basic_authority; pub mod authority_round; pub mod tendermint; +pub mod null_engine; pub use self::account::Account; pub use self::builtin::{Builtin, Pricing, Linear}; @@ -43,3 +44,4 @@ pub use self::validator_set::ValidatorSet; pub use self::basic_authority::{BasicAuthority, BasicAuthorityParams}; pub use self::authority_round::{AuthorityRound, AuthorityRoundParams}; pub use self::tendermint::{Tendermint, TendermintParams}; +pub use self::null_engine::{NullEngine, NullEngineParams}; diff --git a/json/src/spec/null_engine.rs b/json/src/spec/null_engine.rs new file mode 100644 index 000000000..fb6281baf --- /dev/null +++ b/json/src/spec/null_engine.rs @@ -0,0 +1,54 @@ +// 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 . + +//! Null engine params deserialization. + +use uint::Uint; + +/// Authority params deserialization. +#[derive(Debug, PartialEq, Deserialize)] +pub struct NullEngineParams { + /// Block reward. + #[serde(rename="blockReward")] + pub block_reward: Option, +} + +/// Null engine descriptor +#[derive(Debug, PartialEq, Deserialize)] +pub struct NullEngine { + /// Ethash params. + pub params: NullEngineParams, +} + +#[cfg(test)] +mod tests { + use serde_json; + use uint::Uint; + use bigint::prelude::U256; + use super::*; + + #[test] + fn null_engine_deserialization() { + let s = r#"{ + "params": { + "blockReward": "0x0d" + } + }"#; + + let deserialized: NullEngine = serde_json::from_str(s).unwrap(); + assert_eq!(deserialized.params.block_reward, Some(Uint(U256::from(0x0d)))); + } +} diff --git a/json/src/spec/params.rs b/json/src/spec/params.rs index 73c9e8d1b..1bb6abeb3 100644 --- a/json/src/spec/params.rs +++ b/json/src/spec/params.rs @@ -101,9 +101,6 @@ pub struct Params { #[serde(rename="gasLimitBoundDivisor")] pub gas_limit_bound_divisor: Uint, /// See `CommonParams` docs. - #[serde(rename="blockReward")] - pub block_reward: Option, - /// See `CommonParams` docs. pub registrar: Option
, /// Apply reward flag #[serde(rename="applyReward")] @@ -111,6 +108,9 @@ pub struct Params { /// Node permission contract address. #[serde(rename="nodePermissionContract")] pub node_permission_contract: Option
, + /// See main EthashParams docs. + #[serde(rename="maxCodeSize")] + pub max_code_size: Option, /// Transaction permission contract address. #[serde(rename="transactionPermissionContract")] pub transaction_permission_contract: Option
, @@ -132,7 +132,8 @@ mod tests { "subprotocolName" : "exp", "minGasLimit": "0x1388", "accountStartNonce": "0x01", - "gasLimitBoundDivisor": "0x20" + "gasLimitBoundDivisor": "0x20", + "maxCodeSize": "0x1000" }"#; let deserialized: Params = serde_json::from_str(s).unwrap(); @@ -143,5 +144,6 @@ mod tests { assert_eq!(deserialized.min_gas_limit, Uint(U256::from(0x1388))); assert_eq!(deserialized.account_start_nonce, Some(Uint(U256::from(0x01)))); assert_eq!(deserialized.gas_limit_bound_divisor, Uint(U256::from(0x20))); + assert_eq!(deserialized.max_code_size, Some(Uint(U256::from(0x1000)))); } } diff --git a/json/src/spec/tendermint.rs b/json/src/spec/tendermint.rs index f9e35825d..1375b94c4 100644 --- a/json/src/spec/tendermint.rs +++ b/json/src/spec/tendermint.rs @@ -36,6 +36,9 @@ pub struct TendermintParams { /// Commit step timeout in milliseconds. #[serde(rename="timeoutCommit")] pub timeout_commit: Option, + /// Reward per block. + #[serde(rename="blockReward")] + pub block_reward: Option, } /// Tendermint engine deserialization. diff --git a/machine/Cargo.toml b/machine/Cargo.toml new file mode 100644 index 000000000..9deaa296e --- /dev/null +++ b/machine/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "parity-machine" +version = "0.1.0" +description = "Generalization of a state machine for consensus engines" +authors = ["Parity Technologies "] + +[dependencies] +ethcore-util = { path = "../util" } +ethcore-bigint = { path = "../util/bigint" } diff --git a/machine/src/lib.rs b/machine/src/lib.rs new file mode 100644 index 000000000..4b4305d0d --- /dev/null +++ b/machine/src/lib.rs @@ -0,0 +1,120 @@ +// 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 . + +//! Generalization of a state machine for a consensus engine. +//! This will define traits for the header, block, and state of a blockchain. + +extern crate ethcore_util as util; +extern crate ethcore_bigint as bigint; + +use bigint::hash::H256; +use bigint::prelude::U256; +use util::Address; + +/// A header. This contains important metadata about the block, as well as a +/// "seal" that indicates validity to a consensus engine. +pub trait Header { + /// Cryptographic hash of the header, excluding the seal. + fn bare_hash(&self) -> H256; + + /// Cryptographic hash of the header, including the seal. + fn hash(&self) -> H256; + + /// Get a reference to the seal fields. + fn seal(&self) -> &[Vec]; + + /// The author of the header. + fn author(&self) -> &Address; + + /// The number of the header. + fn number(&self) -> u64; +} + +/// a header with an associated score (difficulty in PoW terms) +pub trait ScoredHeader: Header { + /// Get the score of this header. + fn score(&self) -> &U256; + + /// Set the score of this header. + fn set_score(&mut self, score: U256); +} + +/// A "live" block is one which is in the process of the transition. +/// The state of this block can be mutated by arbitrary rules of the +/// state transition function. +pub trait LiveBlock: 'static { + /// The block header type; + type Header: Header; + + /// Get a reference to the header. + fn header(&self) -> &Self::Header; + + /// Get a reference to the uncle headers. If the block type doesn't + /// support uncles, return the empty slice. + fn uncles(&self) -> &[Self::Header]; +} + +/// Trait for blocks which have a transaction type. +pub trait Transactions: LiveBlock { + /// The transaction type. + type Transaction; + + /// Get a reference to the transactions in this block. + fn transactions(&self) -> &[Self::Transaction]; +} + +/// Generalization of types surrounding blockchain-suitable state machines. +pub trait Machine: for<'a> LocalizedMachine<'a> { + /// The block header type. + type Header: Header; + /// The live block type. + type LiveBlock: LiveBlock; + /// A handle to a blockchain client for this machine. + type EngineClient: ?Sized; + /// A description of needed auxiliary data. + type AuxiliaryRequest; + + /// Errors which can occur when querying or interacting with the machine. + type Error; +} + +/// Machine-related types localized to a specific lifetime. +// TODO: this is a workaround for a lack of associated type constructors in the language. +pub trait LocalizedMachine<'a>: Sync + Send { + /// Definition of auxiliary data associated to a specific block. + type AuxiliaryData: 'a; + /// A context providing access to the state in a controlled capacity. + /// Generally also provides verifiable proofs. + type StateContext: ?Sized + 'a; +} + +/// A state machine that uses balances. +pub trait WithBalances: Machine { + /// Get the balance, in base units, associated with an account. + /// Extracts data from the live block. + fn balance(&self, live: &Self::LiveBlock, address: &Address) -> Result; + + /// Increment the balance of an account in the state of the live block. + fn add_balance(&self, live: &mut Self::LiveBlock, address: &Address, amount: &U256) -> Result<(), Self::Error>; + + /// Note block rewards. "direct" rewards are for authors, "indirect" are for e.g. uncles. + fn note_rewards( + &self, + _live: &mut Self::LiveBlock, + _direct: &[(Address, U256)], + _indirect: &[(Address, U256)], + ) -> Result<(), Self::Error> { Ok(()) } +} diff --git a/parity/light_helpers/epoch_fetch.rs b/parity/light_helpers/epoch_fetch.rs index 8fccf049c..cb570d069 100644 --- a/parity/light_helpers/epoch_fetch.rs +++ b/parity/light_helpers/epoch_fetch.rs @@ -17,8 +17,9 @@ use std::sync::{Arc, Weak}; use ethcore::encoded; -use ethcore::engines::{Engine, StateDependentProof}; +use ethcore::engines::{EthEngine, StateDependentProof}; use ethcore::header::Header; +use ethcore::machine::EthereumMachine; use ethcore::receipt::Receipt; use ethsync::LightSync; @@ -78,7 +79,7 @@ impl ChainDataFetcher for EpochFetch { } /// Fetch epoch transition proof at given header. - fn epoch_transition(&self, hash: H256, engine: Arc, checker: Arc) + fn epoch_transition(&self, hash: H256, engine: Arc, checker: Arc>) -> Self::Transition { self.request(request::Signal { diff --git a/parity/run.rs b/parity/run.rs index 4e1e33d5b..7a50054f3 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -27,9 +27,9 @@ use ethcore::miner::{Miner, MinerService, ExternalMiner, MinerOptions}; use ethcore::miner::{StratumOptions, Stratum}; use ethcore::service::ClientService; use ethcore::snapshot; +use ethcore::spec::{SpecParams, OptimizeFor}; use ethcore::verification::queue::VerifierSettings; use ethsync::{self, SyncConfig}; -use ethcore::spec::{SpecParams, OptimizeFor}; use fdlimit::raise_fd_limit; use hash_fetch::fetch::{Fetch, Client as FetchClient}; use informant::{Informant, LightNodeInformantData, FullNodeInformantData}; diff --git a/rpc/src/v1/helpers/light_fetch.rs b/rpc/src/v1/helpers/light_fetch.rs index 8211ffac0..b056171fa 100644 --- a/rpc/src/v1/helpers/light_fetch.rs +++ b/rpc/src/v1/helpers/light_fetch.rs @@ -330,7 +330,7 @@ struct ExecuteParams { tx: EthTransaction, hdr: encoded::Header, env_info: ::vm::EnvInfo, - engine: Arc<::ethcore::engines::Engine>, + engine: Arc<::ethcore::engines::EthEngine>, on_demand: Arc, sync: Arc, } diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index 537586f7b..1a0ceb5b2 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -1097,7 +1097,7 @@ fn rpc_get_work_returns_correct_work_package() { eth_tester.miner.set_author(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()); let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":["0x3bbe93f74e7b97ae00784aeff8819c5cb600dd87e8b282a5d3446f3f871f0347","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000800000000000000000000000000000000000000000000000000000000000","0x1"],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":["0x76c7bd86693aee93d1a80a408a09a0585b1a1292afcb56192f171d925ea18e2d","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000800000000000000000000000000000000000000000000000000000000000","0x1"],"id":1}"#; assert_eq!(eth_tester.io.handle_request_sync(request), Some(response.to_owned())); } @@ -1110,7 +1110,7 @@ fn rpc_get_work_should_not_return_block_number() { eth_tester.miner.set_author(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()); let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":["0x3bbe93f74e7b97ae00784aeff8819c5cb600dd87e8b282a5d3446f3f871f0347","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000800000000000000000000000000000000000000000000000000000000000"],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":["0x76c7bd86693aee93d1a80a408a09a0585b1a1292afcb56192f171d925ea18e2d","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000800000000000000000000000000000000000000000000000000000000000"],"id":1}"#; assert_eq!(eth_tester.io.handle_request_sync(request), Some(response.to_owned())); }