From d3de4752051b35da8f7817b740295319cc841682 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 3 Nov 2016 22:22:25 +0100 Subject: [PATCH] EIPs 155, 160, 161 (#2976) * The front-end for each hard-fork, also EIP-160. * Address EIP161 a/c * Include EIP-161b * EIP-161 part d. * Fix test build. * Fix one test, add another. * Fix use of bloom & renaming. * Initial groundwork for EIP-155 * Fix minor bug. * Fix all tests finally. * Rest of EIP-155. * Add tests for EIP-155 algorithm. Update transaction tests validation. * Minor reformat. * Address grumbles. * Remove unused code. * Fix SUICIDE gas mechanism and add consensus tests. * Remove commented code. * Set Frontier hardfork block number * Fix warning. * Transaction tests, --- .gitignore | 2 + ethcore/res/ethereum/classic.json | 6 +- ethcore/res/ethereum/eip150_test.json | 6 +- ethcore/res/ethereum/eip161_test.json | 47 ++++++ ethcore/res/ethereum/expanse.json | 6 +- ethcore/res/ethereum/frontier.json | 6 +- ethcore/res/ethereum/frontier_like_test.json | 6 +- ethcore/res/ethereum/frontier_test.json | 6 +- ethcore/res/ethereum/homestead_test.json | 6 +- ethcore/res/ethereum/morden.json | 6 +- ethcore/res/ethereum/olympic.json | 6 +- ethcore/res/ethereum/tests | 2 +- ethcore/res/ethereum/transition_test.json | 6 +- ethcore/src/blockchain/blockchain.rs | 14 +- ethcore/src/client/client.rs | 38 +++-- ethcore/src/client/test_client.rs | 8 +- ethcore/src/client/traits.rs | 9 +- ethcore/src/engines/mod.rs | 3 + ethcore/src/error.rs | 3 + ethcore/src/ethereum/ethash.rs | 44 +++++- ethcore/src/ethereum/mod.rs | 3 + ethcore/src/evm/ext.rs | 6 + ethcore/src/evm/interpreter/gasometer.rs | 20 ++- ethcore/src/evm/schedule.rs | 18 ++- ethcore/src/evm/tests.rs | 8 + ethcore/src/executive.rs | 60 +++++--- ethcore/src/externalities.rs | 12 +- ethcore/src/json_tests/chain.rs | 1 + ethcore/src/json_tests/eip161_state.rs | 51 +++++++ ethcore/src/json_tests/executive.rs | 8 + ethcore/src/json_tests/mod.rs | 1 + ethcore/src/json_tests/state.rs | 1 + ethcore/src/json_tests/transaction.rs | 9 +- ethcore/src/miner/banning_queue.rs | 2 +- ethcore/src/miner/miner.rs | 6 +- ethcore/src/miner/transaction_queue.rs | 30 ++-- ethcore/src/spec/spec.rs | 6 +- ethcore/src/state/account.rs | 10 +- ethcore/src/state/mod.rs | 150 ++++++++++++------- ethcore/src/state/substate.rs | 15 ++ ethcore/src/state_db.rs | 4 +- ethcore/src/tests/client.rs | 3 +- ethcore/src/tests/helpers.rs | 3 +- ethcore/src/types/transaction.rs | 97 +++++++++--- ethcore/src/verification/verification.rs | 4 +- evmbin/src/ext.rs | 8 + json/src/spec/ethash.rs | 21 ++- json/src/spec/params.rs | 8 +- parity/cli/config.full.toml | 2 +- parity/cli/mod.rs | 8 +- parity/cli/version.txt | 3 +- parity/configuration.rs | 10 +- parity/run.rs | 4 +- rpc/src/v1/helpers/dispatch.rs | 7 +- rpc/src/v1/helpers/errors.rs | 1 + rpc/src/v1/tests/eth.rs | 2 +- rpc/src/v1/tests/helpers/sync_provider.rs | 4 +- rpc/src/v1/tests/mocked/eth.rs | 14 +- rpc/src/v1/tests/mocked/eth_signing.rs | 4 +- rpc/src/v1/tests/mocked/ethcore.rs | 4 +- rpc/src/v1/tests/mocked/net.rs | 2 +- rpc/src/v1/tests/mocked/personal.rs | 8 +- rpc/src/v1/tests/mocked/personal_signer.rs | 4 +- sync/src/api.rs | 4 +- sync/src/chain.rs | 8 +- 65 files changed, 652 insertions(+), 232 deletions(-) create mode 100644 ethcore/res/ethereum/eip161_test.json create mode 100644 ethcore/src/json_tests/eip161_state.rs diff --git a/.gitignore b/.gitignore index 3226ea5a2..47546d0ed 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,5 @@ # Build artifacts out/ + +.vscode diff --git a/ethcore/res/ethereum/classic.json b/ethcore/res/ethereum/classic.json index 5be7b1caf..d8749ba91 100644 --- a/ethcore/res/ethereum/classic.json +++ b/ethcore/res/ethereum/classic.json @@ -11,7 +11,11 @@ "blockReward": "0x4563918244F40000", "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", "homesteadTransition": "0x118c30", - "eip150Transition": "0x2625a0" + "eip150Transition": "0x2625a0", + "eip155Transition": "0x7fffffffffffffff", + "eip160Transition": "0x7fffffffffffffff", + "eip161abcTransition": "0x7fffffffffffffff", + "eip161dTransition": "0x7fffffffffffffff" } } }, diff --git a/ethcore/res/ethereum/eip150_test.json b/ethcore/res/ethereum/eip150_test.json index 39d4b3fe8..34ef478dc 100644 --- a/ethcore/res/ethereum/eip150_test.json +++ b/ethcore/res/ethereum/eip150_test.json @@ -10,7 +10,11 @@ "blockReward": "0x4563918244F40000", "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", "homesteadTransition": "0x0", - "eip150Transition": "0x0" + "eip150Transition": "0x0", + "eip155Transition": "0x7fffffffffffffff", + "eip160Transition": "0x7fffffffffffffff", + "eip161abcTransition": "0x7fffffffffffffff", + "eip161dTransition": "0x7fffffffffffffff" } } }, diff --git a/ethcore/res/ethereum/eip161_test.json b/ethcore/res/ethereum/eip161_test.json new file mode 100644 index 000000000..884053d2a --- /dev/null +++ b/ethcore/res/ethereum/eip161_test.json @@ -0,0 +1,47 @@ +{ + "name": "Homestead (Test)", + "engine": { + "Ethash": { + "params": { + "gasLimitBoundDivisor": "0x0400", + "minimumDifficulty": "0x020000", + "difficultyBoundDivisor": "0x0800", + "durationLimit": "0x0d", + "blockReward": "0x4563918244F40000", + "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", + "homesteadTransition": "0x0", + "eip150Transition": "0x0", + "eip155Transition": "0x7fffffffffffffff", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0" + } + } + }, + "params": { + "accountStartNonce": "0x00", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID" : "0x1" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000042", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x400000000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", + "gasLimit": "0x1388" + }, + "accounts": { + "0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } } + } +} diff --git a/ethcore/res/ethereum/expanse.json b/ethcore/res/ethereum/expanse.json index d2d036487..8d580b6f5 100644 --- a/ethcore/res/ethereum/expanse.json +++ b/ethcore/res/ethereum/expanse.json @@ -15,7 +15,11 @@ "difficultyHardforkTransition": "0x59d9", "difficultyHardforkBoundDivisor": "0x0200", "bombDefuseTransition": "0x30d40", - "eip150Transition": "0x7fffffffffffffff" + "eip150Transition": "0x7fffffffffffffff", + "eip155Transition": "0x7fffffffffffffff", + "eip160Transition": "0x7fffffffffffffff", + "eip161abcTransition": "0x7fffffffffffffff", + "eip161dTransition": "0x7fffffffffffffff" } } }, diff --git a/ethcore/res/ethereum/frontier.json b/ethcore/res/ethereum/frontier.json index 111dff30e..ecaefa4c3 100644 --- a/ethcore/res/ethereum/frontier.json +++ b/ethcore/res/ethereum/frontier.json @@ -130,7 +130,11 @@ "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" ], - "eip150Transition": "0x259518" + "eip150Transition": "0x259518", + "eip155Transition": 2642462, + "eip160Transition": 2642462, + "eip161abcTransition": 2642462, + "eip161dTransition": 2642462 } } }, diff --git a/ethcore/res/ethereum/frontier_like_test.json b/ethcore/res/ethereum/frontier_like_test.json index 99a7ad712..8f41c61c8 100644 --- a/ethcore/res/ethereum/frontier_like_test.json +++ b/ethcore/res/ethereum/frontier_like_test.json @@ -130,7 +130,11 @@ "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" ], - "eip150Transition": "0x7fffffffffffffff" + "eip150Transition": "0x7fffffffffffffff", + "eip155Transition": "0x7fffffffffffffff", + "eip160Transition": "0x7fffffffffffffff", + "eip161abcTransition": "0x7fffffffffffffff", + "eip161dTransition": "0x7fffffffffffffff" } } }, diff --git a/ethcore/res/ethereum/frontier_test.json b/ethcore/res/ethereum/frontier_test.json index 1cb3d8cfc..0fad8f37e 100644 --- a/ethcore/res/ethereum/frontier_test.json +++ b/ethcore/res/ethereum/frontier_test.json @@ -10,7 +10,11 @@ "blockReward": "0x4563918244F40000", "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", "homesteadTransition": "0x7fffffffffffffff", - "eip150Transition": "0x7fffffffffffffff" + "eip150Transition": "0x7fffffffffffffff", + "eip155Transition": "0x7fffffffffffffff", + "eip160Transition": "0x7fffffffffffffff", + "eip161abcTransition": "0x7fffffffffffffff", + "eip161dTransition": "0x7fffffffffffffff" } } }, diff --git a/ethcore/res/ethereum/homestead_test.json b/ethcore/res/ethereum/homestead_test.json index ad64ce2d5..a757a7bc6 100644 --- a/ethcore/res/ethereum/homestead_test.json +++ b/ethcore/res/ethereum/homestead_test.json @@ -10,7 +10,11 @@ "blockReward": "0x4563918244F40000", "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", "homesteadTransition": "0x0", - "eip150Transition": "0x7fffffffffffffff" + "eip150Transition": "0x7fffffffffffffff", + "eip155Transition": "0x7fffffffffffffff", + "eip160Transition": "0x7fffffffffffffff", + "eip161abcTransition": "0x7fffffffffffffff", + "eip161dTransition": "0x7fffffffffffffff" } } }, diff --git a/ethcore/res/ethereum/morden.json b/ethcore/res/ethereum/morden.json index 67d9ce044..9d54169c3 100644 --- a/ethcore/res/ethereum/morden.json +++ b/ethcore/res/ethereum/morden.json @@ -10,7 +10,11 @@ "blockReward": "0x4563918244F40000", "registrar": "0x52dff57a8a1532e6afb3dc07e2af58bb9eb05b3d", "homesteadTransition": "0x789b0", - "eip150Transition": "0x1b34d8" + "eip150Transition": "0x1b34d8", + "eip155Transition": "0x7fffffffffffffff", + "eip160Transition": "0x7fffffffffffffff", + "eip161abcTransition": "0x7fffffffffffffff", + "eip161dTransition": "0x7fffffffffffffff" } } }, diff --git a/ethcore/res/ethereum/olympic.json b/ethcore/res/ethereum/olympic.json index ebc7abd4e..655410ee1 100644 --- a/ethcore/res/ethereum/olympic.json +++ b/ethcore/res/ethereum/olympic.json @@ -10,7 +10,11 @@ "blockReward": "0x14D1120D7B160000", "registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050", "homesteadTransition": "0x7fffffffffffffff", - "eip150Transition": "0x7fffffffffffffff" + "eip150Transition": "0x7fffffffffffffff", + "eip155Transition": "0x7fffffffffffffff", + "eip160Transition": "0x7fffffffffffffff", + "eip161abcTransition": "0x7fffffffffffffff", + "eip161dTransition": "0x7fffffffffffffff" } } }, diff --git a/ethcore/res/ethereum/tests b/ethcore/res/ethereum/tests index 97066e40c..853333e7d 160000 --- a/ethcore/res/ethereum/tests +++ b/ethcore/res/ethereum/tests @@ -1 +1 @@ -Subproject commit 97066e40ccd061f727deb5cd860e4d9135aa2551 +Subproject commit 853333e7da312775fb8f32f2c2771b8578cd0d79 diff --git a/ethcore/res/ethereum/transition_test.json b/ethcore/res/ethereum/transition_test.json index c004bc2ba..aebea2b4f 100644 --- a/ethcore/res/ethereum/transition_test.json +++ b/ethcore/res/ethereum/transition_test.json @@ -130,7 +130,11 @@ "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" ], - "eip150Transition": "0xa" + "eip150Transition": "0xa", + "eip155Transition": "0x7fffffffffffffff", + "eip160Transition": "0x7fffffffffffffff", + "eip161abcTransition": "0x7fffffffffffffff", + "eip161dTransition": "0x7fffffffffffffff" } } }, diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index d95c199ed..5910d0309 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -1468,7 +1468,7 @@ mod tests { action: Action::Create, value: 100.into(), data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), - }.sign(&"".sha3()); + }.sign(&"".sha3(), None); let b1a = canon_chain @@ -1532,7 +1532,7 @@ mod tests { action: Action::Create, value: 100.into(), data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), - }.sign(&"".sha3()); + }.sign(&"".sha3(), None); let t2 = Transaction { nonce: 1.into(), @@ -1541,7 +1541,7 @@ mod tests { action: Action::Create, value: 100.into(), data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), - }.sign(&"".sha3()); + }.sign(&"".sha3(), None); let t3 = Transaction { nonce: 2.into(), @@ -1550,7 +1550,7 @@ mod tests { action: Action::Create, value: 100.into(), data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), - }.sign(&"".sha3()); + }.sign(&"".sha3(), None); let b1a = canon_chain .with_transaction(t1.clone()) @@ -1856,7 +1856,7 @@ mod tests { action: Action::Create, value: 101.into(), data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), - }.sign(&"".sha3()); + }.sign(&"".sha3(), None); let t2 = Transaction { nonce: 0.into(), gas_price: 0.into(), @@ -1864,7 +1864,7 @@ mod tests { action: Action::Create, value: 102.into(), data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), - }.sign(&"".sha3()); + }.sign(&"".sha3(), None); let t3 = Transaction { nonce: 0.into(), gas_price: 0.into(), @@ -1872,7 +1872,7 @@ mod tests { action: Action::Create, value: 103.into(), data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), - }.sign(&"".sha3()); + }.sign(&"".sha3(), None); let tx_hash1 = t1.hash(); let tx_hash2 = t2.hash(); let tx_hash3 = t3.hash(); diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index bf612852e..1bd8adc25 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -33,7 +33,7 @@ use io::*; use views::{HeaderView, BodyView, BlockView}; use error::{ImportError, ExecutionError, CallError, BlockError, ImportResult, Error as EthcoreError}; use header::BlockNumber; -use state::State; +use state::{State, CleanupMode}; use spec::Spec; use basic_types::Seal; use engines::Engine; @@ -268,6 +268,22 @@ impl Client { } } + /// The env info as of the best block. + fn latest_env_info(&self) -> EnvInfo { + let header_data = self.best_block_header(); + let view = HeaderView::new(&header_data); + + EnvInfo { + number: view.number(), + author: view.author(), + timestamp: view.timestamp(), + difficulty: view.difficulty(), + last_hashes: self.build_last_hashes(view.hash()), + gas_used: U256::default(), + gas_limit: view.gas_limit(), + } + } + fn build_last_hashes(&self, parent_hash: H256) -> Arc { { let hashes = self.last_hashes.read(); @@ -790,7 +806,7 @@ impl BlockChainClient for Client { let needed_balance = t.value + t.gas * t.gas_price; if balance < needed_balance { // give the sender a sufficient balance - state.add_balance(&sender, &(needed_balance - balance)); + state.add_balance(&sender, &(needed_balance - balance), CleanupMode::NoEmpty); } let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false }; let mut ret = try!(Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm).transact(t, options)); @@ -1167,24 +1183,16 @@ impl BlockChainClient for Client { fn pending_transactions(&self) -> Vec { self.miner.pending_transactions(self.chain.read().best_block_number()) } + + fn signing_network_id(&self) -> Option { + self.engine.signing_network_id(&self.latest_env_info()) + } } impl MiningBlockChainClient for Client { fn latest_schedule(&self) -> Schedule { - let header_data = self.best_block_header(); - let view = HeaderView::new(&header_data); - - let env_info = EnvInfo { - number: view.number(), - author: view.author(), - timestamp: view.timestamp(), - difficulty: view.difficulty(), - last_hashes: self.build_last_hashes(view.hash()), - gas_used: U256::default(), - gas_limit: view.gas_limit(), - }; - self.engine.schedule(&env_info) + self.engine.schedule(&self.latest_env_info()) } fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock { diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index a12d94ae4..e7731b73d 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -227,7 +227,7 @@ impl TestBlockChainClient { gas_price: U256::one(), nonce: U256::zero() }; - let signed_tx = tx.sign(keypair.secret()); + let signed_tx = tx.sign(keypair.secret(), None); txs.append(&signed_tx); txs.out() }, @@ -293,7 +293,7 @@ impl TestBlockChainClient { gas_price: U256::one(), nonce: U256::zero() }; - let signed_tx = tx.sign(keypair.secret()); + let signed_tx = tx.sign(keypair.secret(), None); self.set_balance(signed_tx.sender().unwrap(), 10_000_000.into()); let res = self.miner.import_external_transactions(self, vec![signed_tx]); let res = res.into_iter().next().unwrap().expect("Successful import"); @@ -314,7 +314,7 @@ pub fn get_temp_state_db() -> GuardedTempResult { impl MiningBlockChainClient for TestBlockChainClient { fn latest_schedule(&self) -> Schedule { - Schedule::new_homestead_gas_fix() + Schedule::new_post_eip150(true, true, true) } fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock { @@ -633,6 +633,8 @@ impl BlockChainClient for TestBlockChainClient { self.miner.pending_transactions(self.chain_info().best_block_number) } + fn signing_network_id(&self) -> Option { None } + fn mode(&self) -> Mode { Mode::Active } fn set_mode(&self, _: Mode) { unimplemented!(); } diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index d7844ba3d..60be4ba1b 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -215,7 +215,7 @@ pub trait BlockChainClient : Sync + Send { /// Calculate median gas price from recent blocks if they have any transactions. fn gas_price_median(&self, sample_size: usize) -> Option { let corpus = self.gas_price_corpus(sample_size); - corpus.get(corpus.len()/2).cloned() + corpus.get(corpus.len() / 2).cloned() } /// Get the gas price distribution based on recent blocks if they have any transactions. @@ -223,12 +223,17 @@ pub trait BlockChainClient : Sync + Send { let raw_corpus = self.gas_price_corpus(sample_size); let raw_len = raw_corpus.len(); // Throw out outliers. - let (corpus, _) = raw_corpus.split_at(raw_len-raw_len/40); + let (corpus, _) = raw_corpus.split_at(raw_len - raw_len / 40); Histogram::new(corpus, bucket_number) } + /// Get the preferred network ID to sign on + fn signing_network_id(&self) -> Option; + + /// Get the mode. fn mode(&self) -> Mode; + /// Set the mode. fn set_mode(&self, mode: Mode); } diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index 250529dad..7af88790c 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -108,6 +108,9 @@ pub trait Engine : Sync + Send { /// Verify a particular transaction is valid. fn verify_transaction(&self, _t: &SignedTransaction, _header: &Header) -> Result<(), Error> { Ok(()) } + /// The network ID that transactions should be signed with. + fn signing_network_id(&self, _env_info: &EnvInfo) -> Option { None } + /// 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. diff --git a/ethcore/src/error.rs b/ethcore/src/error.rs index 04a0920fa..ae68aefa1 100644 --- a/ethcore/src/error.rs +++ b/ethcore/src/error.rs @@ -76,6 +76,8 @@ pub enum TransactionError { RecipientBanned, /// Contract creation code is banned. CodeBanned, + /// Invalid network ID given. + InvalidNetworkId, } impl fmt::Display for TransactionError { @@ -99,6 +101,7 @@ impl fmt::Display for TransactionError { SenderBanned => "Sender is temporarily banned.".into(), RecipientBanned => "Recipient is temporarily banned.".into(), CodeBanned => "Contract code is temporarily banned.".into(), + InvalidNetworkId => "Transaction of this network ID is not allowed on this chain.".into(), }; f.write_fmt(format_args!("Transaction error ({})", msg)) diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 060a20aa2..7fbb408cc 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -19,8 +19,9 @@ use util::*; use block::*; use builtin::Builtin; use env_info::EnvInfo; -use error::{BlockError, Error}; +use error::{BlockError, TransactionError, Error}; use header::Header; +use state::CleanupMode; use spec::CommonParams; use transaction::SignedTransaction; use engines::Engine; @@ -59,8 +60,16 @@ pub struct EthashParams { pub difficulty_hardfork_bound_divisor: U256, /// Block on which there is no additional difficulty from the exponential bomb. pub bomb_defuse_transition: u64, - /// Bad gas transition block number. + /// Number of first block where EIP-150 rules begin. pub eip150_transition: u64, + /// Number of first block where EIP-155 rules begin. + pub eip155_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, } impl From for EthashParams { @@ -81,6 +90,10 @@ impl From for EthashParams { 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(0x7fffffffffffffff, Into::into), eip150_transition: p.eip150_transition.map_or(0, Into::into), + eip155_transition: p.eip155_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(0x7fffffffffffffff, Into::into), } } } @@ -132,7 +145,19 @@ impl Engine for Ethash { } else if env_info.number < self.ethash_params.eip150_transition { Schedule::new_homestead() } else { - Schedule::new_homestead_gas_fix() + Schedule::new_post_eip150( + env_info.number >= self.ethash_params.eip160_transition, + env_info.number >= self.ethash_params.eip161abc_transition, + env_info.number >= self.ethash_params.eip161d_transition + ) + } + } + + fn signing_network_id(&self, env_info: &EnvInfo) -> Option { + if env_info.number >= self.ethash_params.eip155_transition && self.params().network_id < 127 { + Some(self.params().network_id as u8) + } else { + None } } @@ -169,7 +194,7 @@ impl Engine for Ethash { let mut state = block.fields_mut().state; for child in &self.ethash_params.dao_hardfork_accounts { let b = state.balance(child); - state.transfer_balance(child, &self.ethash_params.dao_hardfork_beneficiary, &b); + state.transfer_balance(child, &self.ethash_params.dao_hardfork_beneficiary, &b, CleanupMode::NoEmpty); } // } } @@ -182,12 +207,12 @@ impl Engine for Ethash { let fields = block.fields_mut(); // Bestow block reward - fields.state.add_balance(fields.header.author(), &(reward + reward / U256::from(32) * U256::from(fields.uncles.len()))); + fields.state.add_balance(fields.header.author(), &(reward + reward / U256::from(32) * U256::from(fields.uncles.len())), CleanupMode::NoEmpty); // Bestow uncle rewards let current_number = fields.header.number(); for u in fields.uncles.iter() { - fields.state.add_balance(u.author(), &(reward * U256::from(8 + u.number() - current_number) / U256::from(8))); + fields.state.add_balance(u.author(), &(reward * U256::from(8 + u.number() - current_number) / U256::from(8)), CleanupMode::NoEmpty); } // Commit state so that we can actually figure out the state root. @@ -277,6 +302,13 @@ impl Engine for Ethash { if header.number() >= self.ethash_params.homestead_transition { try!(t.check_low_s()); } + + if let Some(n) = t.network_id() { + if header.number() < self.ethash_params.eip155_transition || n as usize != self.params().network_id { + return Err(TransactionError::InvalidNetworkId.into()) + } + } + Ok(()) } diff --git a/ethcore/src/ethereum/mod.rs b/ethcore/src/ethereum/mod.rs index d8299324d..253a12372 100644 --- a/ethcore/src/ethereum/mod.rs +++ b/ethcore/src/ethereum/mod.rs @@ -54,6 +54,9 @@ pub fn new_homestead_test() -> Spec { load(include_bytes!("../../res/ethereum/ho /// Create a new Homestead-EIP150 chain spec as though it never changed from Homestead/Frontier. pub fn new_eip150_test() -> Spec { load(include_bytes!("../../res/ethereum/eip150_test.json")) } +/// Create a new Homestead-EIP150 chain spec as though it never changed from Homestead/Frontier. +pub fn new_eip161_test() -> Spec { load(include_bytes!("../../res/ethereum/eip161_test.json")) } + /// Create a new Frontier/Homestead/DAO chain spec with transition points at #5 and #8. pub fn new_transition_test() -> Spec { load(include_bytes!("../../res/ethereum/transition_test.json")) } diff --git a/ethcore/src/evm/ext.rs b/ethcore/src/evm/ext.rs index 6397f067e..1c340b5b1 100644 --- a/ethcore/src/evm/ext.rs +++ b/ethcore/src/evm/ext.rs @@ -52,6 +52,12 @@ pub trait Ext { /// Determine whether an account exists. fn exists(&self, address: &Address) -> bool; + /// Determine whether an account exists and is not null (zero balance/nonce, no code). + fn exists_and_not_null(&self, address: &Address) -> bool; + + /// Balance of the origin account. + fn origin_balance(&self) -> U256; + /// Returns address balance. fn balance(&self, address: &Address) -> U256; diff --git a/ethcore/src/evm/interpreter/gasometer.rs b/ethcore/src/evm/interpreter/gasometer.rs index a2b940655..beaaadac5 100644 --- a/ethcore/src/evm/interpreter/gasometer.rs +++ b/ethcore/src/evm/interpreter/gasometer.rs @@ -146,8 +146,13 @@ impl Gasometer { instructions::SUICIDE => { let mut gas = Gas::from(schedule.suicide_gas); + let is_value_transfer = !ext.origin_balance().is_zero(); let address = u256_to_address(stack.peek(0)); - if !ext.exists(&address) { + if ( + !schedule.no_empty && !ext.exists(&address) + ) || ( + schedule.no_empty && is_value_transfer && !ext.exists_and_not_null(&address) + ) { gas = overflowing!(gas.overflow_add(schedule.suicide_to_new_account_cost.into())); } @@ -190,12 +195,19 @@ impl Gasometer { ); let address = u256_to_address(stack.peek(1)); + let is_value_transfer = !stack.peek(2).is_zero(); - if instruction == instructions::CALL && !ext.exists(&address) { - gas = overflowing!(gas.overflow_add(schedule.call_new_account_gas.into())); + if instruction == instructions::CALL { + if ( + !schedule.no_empty && !ext.exists(&address) + ) || ( + schedule.no_empty && is_value_transfer && !ext.exists_and_not_null(&address) + ) { + gas = overflowing!(gas.overflow_add(schedule.call_new_account_gas.into())); + } }; - if !stack.peek(2).is_zero() { + if is_value_transfer { gas = overflowing!(gas.overflow_add(schedule.call_value_transfer_gas.into())); }; diff --git a/ethcore/src/evm/schedule.rs b/ethcore/src/evm/schedule.rs index b8de785b3..b68f6acb5 100644 --- a/ethcore/src/evm/schedule.rs +++ b/ethcore/src/evm/schedule.rs @@ -93,6 +93,10 @@ pub struct Schedule { /// If Some(x): let limit = GAS * (x - 1) / x; let CALL's gas = min(requested, limit). let CREATE's gas = limit. /// If None: let CALL's gas = (requested > GAS ? [OOG] : GAS). let CREATE's gas = GAS pub sub_gas_cap_divisor: Option, + /// Don't ever make empty accounts; contracts start with nonce=1. Also, don't charge 25k when sending/suicide zero-value. + pub no_empty: bool, + /// Kill empty accounts if touched. + pub kill_empty: bool, } impl Schedule { @@ -106,16 +110,16 @@ impl Schedule { Self::new(true, true, 53000) } - /// Schedule for the Homestead-era of the Ethereum main net. - pub fn new_homestead_gas_fix() -> Schedule { - Schedule{ + /// Schedule for the post-EIP-150-era of the Ethereum main net. + pub fn new_post_eip150(fix_exp: bool, no_empty: bool, kill_empty: bool) -> Schedule { + Schedule { exceptional_failed_code_deposit: true, have_delegate_call: true, stack_limit: 1024, max_depth: 1024, tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0], exp_gas: 10, - exp_byte_gas: 10, + exp_byte_gas: if fix_exp {50} else {10}, sha3_gas: 30, sha3_word_gas: 6, sload_gas: 200, @@ -146,11 +150,13 @@ impl Schedule { suicide_gas: 5000, suicide_to_new_account_cost: 25000, sub_gas_cap_divisor: Some(64), + no_empty: no_empty, + kill_empty: kill_empty, } } fn new(efcd: bool, hdc: bool, tcg: usize) -> Schedule { - Schedule{ + Schedule { exceptional_failed_code_deposit: efcd, have_delegate_call: hdc, stack_limit: 1024, @@ -188,6 +194,8 @@ impl Schedule { suicide_gas: 0, suicide_to_new_account_cost: 0, sub_gas_cap_divisor: None, + no_empty: false, + kill_empty: false, } } } diff --git a/ethcore/src/evm/tests.rs b/ethcore/src/evm/tests.rs index ba002d649..7e69c0771 100644 --- a/ethcore/src/evm/tests.rs +++ b/ethcore/src/evm/tests.rs @@ -94,6 +94,14 @@ impl Ext for FakeExt { self.balances.contains_key(address) } + fn exists_and_not_null(&self, address: &Address) -> bool { + self.balances.get(address).map_or(false, |b| !b.is_zero()) + } + + fn origin_balance(&self) -> U256 { + unimplemented!() + } + fn balance(&self, address: &Address) -> U256 { *self.balances.get(address).unwrap() } diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index f05cc4fd8..5da105e2f 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -17,7 +17,7 @@ //! Transaction Execution environment. use util::*; use action_params::{ActionParams, ActionValue}; -use state::{State, Substate}; +use state::{State, Substate, CleanupMode}; use engines::Engine; use types::executed::CallType; use env_info::EnvInfo; @@ -256,9 +256,11 @@ impl<'a> Executive<'a> { // backup used in case of running out of gas self.state.checkpoint(); + let schedule = self.engine.schedule(self.info); + // at first, transfer value to destination if let ActionValue::Transfer(val) = params.value { - self.state.transfer_balance(¶ms.sender, ¶ms.address, &val); + self.state.transfer_balance(¶ms.sender, ¶ms.address, &val, substate.to_cleanup_mode(&schedule)); } trace!("Executive::call(params={:?}) self.env_info={:?}", params, self.info); @@ -364,12 +366,14 @@ impl<'a> Executive<'a> { let mut unconfirmed_substate = Substate::new(); // create contract and transfer value to it if necessary + let schedule = self.engine.schedule(self.info); + 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 { self.state.sub_balance(¶ms.sender, &val); - self.state.new_contract(¶ms.address, val + prev_bal); + self.state.new_contract(¶ms.address, val + prev_bal, nonce_offset); } else { - self.state.new_contract(¶ms.address, prev_bal); + self.state.new_contract(¶ms.address, prev_bal, nonce_offset); } let trace_info = tracer.prepare_trace_create(¶ms); @@ -405,7 +409,7 @@ impl<'a> Executive<'a> { fn finalize( &mut self, t: &SignedTransaction, - substate: Substate, + mut substate: Substate, result: evm::Result, output: Bytes, trace: Vec, @@ -440,15 +444,23 @@ impl<'a> Executive<'a> { }; trace!("exec::finalize: Refunding refund_value={}, sender={}\n", refund_value, sender); - self.state.add_balance(&sender, &refund_value); + // Below: NoEmpty is safe since the sender must already be non-null to have sent this transaction + self.state.add_balance(&sender, &refund_value, CleanupMode::NoEmpty); trace!("exec::finalize: Compensating author: fees_value={}, author={}\n", fees_value, &self.info.author); - self.state.add_balance(&self.info.author, &fees_value); + self.state.add_balance(&self.info.author, &fees_value, substate.to_cleanup_mode(&schedule)); // perform suicides for address in &substate.suicides { self.state.kill_account(address); } + // perform garbage-collection + for address in &substate.garbage { + if self.state.exists(address) && !self.state.exists_and_not_null(address) { + self.state.kill_account(address); + } + } + match result { Err(evm::Error::Internal) => Err(ExecutionError::Internal), Err(_) => { @@ -509,7 +521,7 @@ mod tests { use env_info::EnvInfo; use evm::{Factory, VMType}; use error::ExecutionError; - use state::Substate; + use state::{Substate, CleanupMode}; use tests::helpers::*; use trace::trace; use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer}; @@ -538,7 +550,7 @@ mod tests { params.value = ActionValue::Transfer(U256::from(0x7)); let mut state_result = get_temp_state(); let mut state = state_result.reference_mut(); - state.add_balance(&sender, &U256::from(0x100u64)); + state.add_balance(&sender, &U256::from(0x100u64), CleanupMode::NoEmpty); let info = EnvInfo::default(); let engine = TestEngine::new(0); let mut substate = Substate::new(); @@ -597,7 +609,7 @@ mod tests { params.value = ActionValue::Transfer(U256::from(100)); let mut state_result = get_temp_state(); let mut state = state_result.reference_mut(); - state.add_balance(&sender, &U256::from(100)); + state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty); let info = EnvInfo::default(); let engine = TestEngine::new(0); let mut substate = Substate::new(); @@ -656,7 +668,7 @@ mod tests { params.call_type = CallType::Call; let mut state_result = get_temp_state(); let mut state = state_result.reference_mut(); - state.add_balance(&sender, &U256::from(100)); + state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty); let info = EnvInfo::default(); let engine = TestEngine::new(5); let mut substate = Substate::new(); @@ -767,7 +779,7 @@ mod tests { params.value = ActionValue::Transfer(100.into()); let mut state_result = get_temp_state(); let mut state = state_result.reference_mut(); - state.add_balance(&sender, &U256::from(100)); + state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty); let info = EnvInfo::default(); let engine = TestEngine::new(5); let mut substate = Substate::new(); @@ -855,7 +867,7 @@ mod tests { params.value = ActionValue::Transfer(U256::from(100)); let mut state_result = get_temp_state(); let mut state = state_result.reference_mut(); - state.add_balance(&sender, &U256::from(100)); + state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty); let info = EnvInfo::default(); let engine = TestEngine::new(0); let mut substate = Substate::new(); @@ -907,7 +919,7 @@ mod tests { params.value = ActionValue::Transfer(U256::from(100)); let mut state_result = get_temp_state(); let mut state = state_result.reference_mut(); - state.add_balance(&sender, &U256::from(100)); + state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty); let info = EnvInfo::default(); let engine = TestEngine::new(1024); let mut substate = Substate::new(); @@ -967,7 +979,7 @@ mod tests { let mut state = state_result.reference_mut(); state.init_code(&address_a, code_a.clone()); state.init_code(&address_b, code_b.clone()); - state.add_balance(&sender, &U256::from(100_000)); + state.add_balance(&sender, &U256::from(100_000), CleanupMode::NoEmpty); let info = EnvInfo::default(); let engine = TestEngine::new(0); @@ -1040,13 +1052,13 @@ mod tests { gas: U256::from(100_000), gas_price: U256::zero(), nonce: U256::zero() - }.sign(keypair.secret()); + }.sign(keypair.secret(), None); let sender = t.sender().unwrap(); let contract = contract_address(&sender, &U256::zero()); let mut state_result = get_temp_state(); let mut state = state_result.reference_mut(); - state.add_balance(&sender, &U256::from(18)); + state.add_balance(&sender, &U256::from(18), CleanupMode::NoEmpty); let mut info = EnvInfo::default(); info.gas_limit = U256::from(100_000); let engine = TestEngine::new(0); @@ -1107,12 +1119,12 @@ mod tests { gas: U256::from(100_000), gas_price: U256::zero(), nonce: U256::one() - }.sign(keypair.secret()); + }.sign(keypair.secret(), None); let sender = t.sender().unwrap(); let mut state_result = get_temp_state(); let mut state = state_result.reference_mut(); - state.add_balance(&sender, &U256::from(17)); + state.add_balance(&sender, &U256::from(17), CleanupMode::NoEmpty); let mut info = EnvInfo::default(); info.gas_limit = U256::from(100_000); let engine = TestEngine::new(0); @@ -1140,12 +1152,12 @@ mod tests { gas: U256::from(80_001), gas_price: U256::zero(), nonce: U256::zero() - }.sign(keypair.secret()); + }.sign(keypair.secret(), None); let sender = t.sender().unwrap(); let mut state_result = get_temp_state(); let mut state = state_result.reference_mut(); - state.add_balance(&sender, &U256::from(17)); + state.add_balance(&sender, &U256::from(17), CleanupMode::NoEmpty); let mut info = EnvInfo::default(); info.gas_used = U256::from(20_000); info.gas_limit = U256::from(100_000); @@ -1175,12 +1187,12 @@ mod tests { gas: U256::from(100_000), gas_price: U256::one(), nonce: U256::zero() - }.sign(keypair.secret()); + }.sign(keypair.secret(), None); let sender = t.sender().unwrap(); let mut state_result = get_temp_state(); let mut state = state_result.reference_mut(); - state.add_balance(&sender, &U256::from(100_017)); + state.add_balance(&sender, &U256::from(100_017), CleanupMode::NoEmpty); let mut info = EnvInfo::default(); info.gas_limit = U256::from(100_000); let engine = TestEngine::new(0); @@ -1215,7 +1227,7 @@ mod tests { params.value = ActionValue::Transfer(U256::from_str("0de0b6b3a7640000").unwrap()); let mut state_result = get_temp_state(); let mut state = state_result.reference_mut(); - state.add_balance(&sender, &U256::from_str("152d02c7e14af6800000").unwrap()); + state.add_balance(&sender, &U256::from_str("152d02c7e14af6800000").unwrap(), CleanupMode::NoEmpty); let info = EnvInfo::default(); let engine = TestEngine::new(0); let mut substate = Substate::new(); diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index bbe81a511..df1b64e67 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -114,6 +114,12 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT self.state.exists(address) } + fn exists_and_not_null(&self, address: &Address) -> bool { + self.state.exists_and_not_null(address) + } + + fn origin_balance(&self) -> U256 { self.balance(&self.origin_info.address) } + fn balance(&self, address: &Address) -> U256 { self.state.balance(address) } @@ -269,11 +275,11 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT let address = self.origin_info.address.clone(); let balance = self.balance(&address); if &address == refund_address { - // TODO [todr] To be consisted with CPP client we set balance to 0 in that case. + // TODO [todr] To be consistent with CPP client we set balance to 0 in that case. self.state.sub_balance(&address, &balance); } else { - trace!("Suiciding {} -> {} (xfer: {})", address, refund_address, balance); - self.state.transfer_balance(&address, refund_address, &balance); + trace!(target: "ext", "Suiciding {} -> {} (xfer: {})", address, refund_address, balance); + self.state.transfer_balance(&address, refund_address, &balance, self.substate.to_cleanup_mode(&self.schedule)); } self.tracer.trace_suicide(address, balance, refund_address.clone()); diff --git a/ethcore/src/json_tests/chain.rs b/ethcore/src/json_tests/chain.rs index bf32db133..b50241199 100644 --- a/ethcore/src/json_tests/chain.rs +++ b/ethcore/src/json_tests/chain.rs @@ -49,6 +49,7 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec { ChainEra::Frontier => ethereum::new_frontier_test(), ChainEra::Homestead => ethereum::new_homestead_test(), ChainEra::Eip150 => ethereum::new_eip150_test(), + ChainEra::Eip161 => ethereum::new_eip161_test(), ChainEra::TransitionTest => ethereum::new_transition_test(), }; spec.set_genesis_state(state); diff --git a/ethcore/src/json_tests/eip161_state.rs b/ethcore/src/json_tests/eip161_state.rs new file mode 100644 index 000000000..da7997fa1 --- /dev/null +++ b/ethcore/src/json_tests/eip161_state.rs @@ -0,0 +1,51 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use super::test_common::*; +use tests::helpers::*; +use super::state::json_chain_test; + +fn do_json_test(json_data: &[u8]) -> Vec { + json_chain_test(json_data, ChainEra::Eip161) +} + +declare_test!{StateTests_EIP158_stEIP158SpecificTest, "StateTests/EIP158/stEIP158SpecificTest"} +declare_test!{StateTests_EIP158_stNonZeroCallsTest, "StateTests/EIP158/stNonZeroCallsTest"} +declare_test!{StateTests_EIP158_stZeroCallsTest, "StateTests/EIP158/stZeroCallsTest"} + +declare_test!{StateTests_EIP158_EIP150_stMemExpandingEIPCalls, "StateTests/EIP158/EIP150/stMemExpandingEIPCalls"} +declare_test!{StateTests_EIP158_EIP150_stEIPSpecificTest, "StateTests/EIP158/EIP150/stEIPSpecificTest"} +declare_test!{StateTests_EIP158_EIP150_stEIPsingleCodeGasPrices, "StateTests/EIP158/EIP150/stEIPsingleCodeGasPrices"} +declare_test!{StateTests_EIP158_EIP150_stChangedTests, "StateTests/EIP158/EIP150/stChangedTests"} + +declare_test!{StateTests_EIP158_Homestead_stBoundsTest, "StateTests/EIP158/Homestead/stBoundsTest"} +declare_test!{StateTests_EIP158_Homestead_stCallCodes, "StateTests/EIP158/Homestead/stCallCodes"} +declare_test!{StateTests_EIP158_Homestead_stCallCreateCallCodeTest, "StateTests/EIP158/Homestead/stCallCreateCallCodeTest"} +declare_test!{StateTests_EIP158_Homestead_stCallDelegateCodes, "StateTests/EIP158/Homestead/stCallDelegateCodes"} +declare_test!{StateTests_EIP158_Homestead_stCallDelegateCodesCallCode, "StateTests/EIP158/Homestead/stCallDelegateCodesCallCode"} +declare_test!{StateTests_EIP158_Homestead_stDelegatecallTest, "StateTests/EIP158/Homestead/stDelegatecallTest"} +declare_test!{StateTests_EIP158_Homestead_stHomeSteadSpecific, "StateTests/EIP158/Homestead/stHomeSteadSpecific"} +declare_test!{StateTests_EIP158_Homestead_stInitCodeTest, "StateTests/EIP158/Homestead/stInitCodeTest"} +declare_test!{StateTests_EIP158_Homestead_stLogTests, "StateTests/EIP158/Homestead/stLogTests"} +declare_test!{heavy => StateTests_EIP158_Homestead_stMemoryTest, "StateTests/EIP158/Homestead/stMemoryTest"} +declare_test!{StateTests_EIP158_Homestead_stPreCompiledContracts, "StateTests/EIP158/Homestead/stPreCompiledContracts"} +declare_test!{heavy => StateTests_EIP158_Homestead_stQuadraticComplexityTest, "StateTests/EIP158/Homestead/stQuadraticComplexityTest"} +declare_test!{StateTests_EIP158_Homestead_stRecursiveCreate, "StateTests/EIP158/Homestead/stRecursiveCreate"} +declare_test!{StateTests_EIP158_Homestead_stRefundTest, "StateTests/EIP158/Homestead/stRefundTest"} +declare_test!{StateTests_EIP158_Homestead_stSpecialTest, "StateTests/EIP158/Homestead/stSpecialTest"} +declare_test!{StateTests_EIP158_Homestead_stSystemOperationsTest, "StateTests/EIP158/Homestead/stSystemOperationsTest"} +declare_test!{StateTests_EIP158_Homestead_stTransactionTest, "StateTests/EIP158/Homestead/stTransactionTest"} +declare_test!{StateTests_EIP158_Homestead_stWalletTest, "StateTests/EIP158/Homestead/stWalletTest"} \ No newline at end of file diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs index 1d4faec62..60321f971 100644 --- a/ethcore/src/json_tests/executive.rs +++ b/ethcore/src/json_tests/executive.rs @@ -92,10 +92,18 @@ impl<'a, T, V> Ext for TestExt<'a, T, V> where T: Tracer, V: VMTracer { self.ext.exists(address) } + fn exists_and_not_null(&self, address: &Address) -> bool { + self.ext.exists_and_not_null(address) + } + fn balance(&self, address: &Address) -> U256 { self.ext.balance(address) } + fn origin_balance(&self) -> U256 { + self.ext.origin_balance() + } + fn blockhash(&self, number: &U256) -> H256 { self.ext.blockhash(number) } diff --git a/ethcore/src/json_tests/mod.rs b/ethcore/src/json_tests/mod.rs index 7a5dd30d2..13d3fb5bb 100644 --- a/ethcore/src/json_tests/mod.rs +++ b/ethcore/src/json_tests/mod.rs @@ -24,4 +24,5 @@ mod chain; mod homestead_state; mod homestead_chain; mod eip150_state; +mod eip161_state; mod trie; diff --git a/ethcore/src/json_tests/state.rs b/ethcore/src/json_tests/state.rs index c3e74af5d..bf84d50ee 100644 --- a/ethcore/src/json_tests/state.rs +++ b/ethcore/src/json_tests/state.rs @@ -29,6 +29,7 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec { ChainEra::Frontier => ethereum::new_mainnet_like().engine, ChainEra::Homestead => ethereum::new_homestead_test().engine, ChainEra::Eip150 => ethereum::new_eip150_test().engine, + ChainEra::Eip161 => ethereum::new_eip161_test().engine, ChainEra::TransitionTest => ethereum::new_transition_test().engine, }; diff --git a/ethcore/src/json_tests/transaction.rs b/ethcore/src/json_tests/transaction.rs index 50061cbfd..dd5a4ef14 100644 --- a/ethcore/src/json_tests/transaction.rs +++ b/ethcore/src/json_tests/transaction.rs @@ -35,17 +35,24 @@ fn do_json_test(json_data: &[u8]) -> Vec { Some(x) if x < 1_150_000 => &old_schedule, Some(_) => &new_schedule }; + let allow_network_id_of_one = number.map_or(false, |n| n > 2600000); let rlp: Vec = test.rlp.into(); let res = UntrustedRlp::new(&rlp) .as_val() .map_err(From::from) - .and_then(|t: SignedTransaction| t.validate(schedule, schedule.have_delegate_call)); + .and_then(|t: SignedTransaction| t.validate(schedule, schedule.have_delegate_call, allow_network_id_of_one)); fail_unless(test.transaction.is_none() == res.is_err()); if let (Some(tx), Some(sender)) = (test.transaction, test.sender) { let t = res.unwrap(); fail_unless(t.sender().unwrap() == sender.into()); + let is_acceptable_network_id = match t.network_id() { + None => true, + Some(1) if allow_network_id_of_one => true, + _ => false, + }; + fail_unless(is_acceptable_network_id); let data: Vec = tx.data.into(); fail_unless(t.data == data); fail_unless(t.gas_price == tx.gas_price.into()); diff --git a/ethcore/src/miner/banning_queue.rs b/ethcore/src/miner/banning_queue.rs index f127dc7e8..0fdea2ac3 100644 --- a/ethcore/src/miner/banning_queue.rs +++ b/ethcore/src/miner/banning_queue.rs @@ -245,7 +245,7 @@ mod tests { gas: U256::from(100_000), gas_price: U256::from(10), nonce: U256::from(0), - }.sign(keypair.secret()) + }.sign(keypair.secret(), None) } fn unwrap_err(res: Result) -> TransactionError { diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index d36869a74..6ad85cb38 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -21,7 +21,7 @@ use util::*; use util::using_queue::{UsingQueue, GetAction}; use account_provider::AccountProvider; use views::{BlockView, HeaderView}; -use state::State; +use state::{State, CleanupMode}; use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockID, CallAnalytics}; use executive::contract_address; use block::{ClosedBlock, SealedBlock, IsBlock, Block}; @@ -650,7 +650,7 @@ impl MinerService for Miner { let needed_balance = t.value + t.gas * t.gas_price; if balance < needed_balance { // give the sender a sufficient balance - state.add_balance(&sender, &(needed_balance - balance)); + state.add_balance(&sender, &(needed_balance - balance), CleanupMode::NoEmpty); } let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false }; let mut ret = try!(Executive::new(&mut state, &env_info, &*self.engine, chain.vm_factory()).transact(t, options)); @@ -1173,7 +1173,7 @@ mod tests { gas: U256::from(100_000), gas_price: U256::zero(), nonce: U256::zero(), - }.sign(keypair.secret()) + }.sign(keypair.secret(), None) } #[test] diff --git a/ethcore/src/miner/transaction_queue.rs b/ethcore/src/miner/transaction_queue.rs index 51c1863f6..cc10bbe98 100644 --- a/ethcore/src/miner/transaction_queue.rs +++ b/ethcore/src/miner/transaction_queue.rs @@ -42,8 +42,8 @@ //! let t2 = Transaction { action: Action::Create, value: U256::from(100), data: "3331600055".from_hex().unwrap(), //! gas: U256::from(100_000), gas_price: U256::one(), nonce: U256::from(11) }; //! -//! let st1 = t1.sign(&key.secret()); -//! let st2 = t2.sign(&key.secret()); +//! let st1 = t1.sign(&key.secret(), None); +//! let st2 = t2.sign(&key.secret(), None); //! let default_account_details = |_a: &Address| AccountDetails { //! nonce: U256::from(10), //! balance: U256::from(1_000_000), @@ -1104,12 +1104,12 @@ mod test { fn new_tx(nonce: U256, gas_price: U256) -> SignedTransaction { let keypair = Random.generate().unwrap(); - new_unsigned_tx(nonce, default_gas_val(), gas_price).sign(keypair.secret()) + new_unsigned_tx(nonce, default_gas_val(), gas_price).sign(keypair.secret(), None) } fn new_tx_with_gas(gas: U256, gas_price: U256) -> SignedTransaction { let keypair = Random.generate().unwrap(); - new_unsigned_tx(default_nonce(), gas, gas_price).sign(keypair.secret()) + new_unsigned_tx(default_nonce(), gas, gas_price).sign(keypair.secret(), None) } fn new_tx_default() -> SignedTransaction { @@ -1133,7 +1133,7 @@ mod test { let keypair = Random.generate().unwrap(); let secret = &keypair.secret(); - (tx1.sign(secret), tx2.sign(secret)) + (tx1.sign(secret, None), tx2.sign(secret, None)) } /// Returns two consecutive transactions, both with increased gas price @@ -1144,7 +1144,7 @@ mod test { let keypair = Random.generate().unwrap(); let secret = &keypair.secret(); - (tx1.sign(secret), tx2.sign(secret)) + (tx1.sign(secret, None), tx2.sign(secret, None)) } fn new_tx_pair_default(nonce_increment: U256, gas_price_increment: U256) -> (SignedTransaction, SignedTransaction) { @@ -1798,9 +1798,9 @@ mod test { let mut txq = TransactionQueue::default(); let kp = Random.generate().unwrap(); let secret = kp.secret(); - let tx = new_unsigned_tx(123.into(), default_gas_val(), 1.into()).sign(secret); - let tx1 = new_unsigned_tx(124.into(), default_gas_val(), 1.into()).sign(secret); - let tx2 = new_unsigned_tx(125.into(), default_gas_val(), 1.into()).sign(secret); + let tx = new_unsigned_tx(123.into(), default_gas_val(), 1.into()).sign(secret, None); + let tx1 = new_unsigned_tx(124.into(), default_gas_val(), 1.into()).sign(secret, None); + let tx2 = new_unsigned_tx(125.into(), default_gas_val(), 1.into()).sign(secret, None); txq.add(tx, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().pending, 1); @@ -2038,11 +2038,11 @@ mod test { // given let mut txq = TransactionQueue::default(); let keypair = Random.generate().unwrap(); - let tx = new_unsigned_tx(123.into(), default_gas_val(), 1.into()).sign(keypair.secret()); + let tx = new_unsigned_tx(123.into(), default_gas_val(), 1.into()).sign(keypair.secret(), None); let tx2 = { let mut tx2 = (*tx).clone(); tx2.gas_price = U256::from(200); - tx2.sign(keypair.secret()) + tx2.sign(keypair.secret(), None) }; // when @@ -2061,16 +2061,16 @@ mod test { // given let mut txq = TransactionQueue::default(); let keypair = Random.generate().unwrap(); - let tx0 = new_unsigned_tx(123.into(), default_gas_val(), 1.into()).sign(keypair.secret()); + let tx0 = new_unsigned_tx(123.into(), default_gas_val(), 1.into()).sign(keypair.secret(), None); let tx1 = { let mut tx1 = (*tx0).clone(); tx1.nonce = U256::from(124); - tx1.sign(keypair.secret()) + tx1.sign(keypair.secret(), None) }; let tx2 = { let mut tx2 = (*tx1).clone(); tx2.gas_price = U256::from(200); - tx2.sign(keypair.secret()) + tx2.sign(keypair.secret(), None) }; // when @@ -2223,7 +2223,7 @@ mod test { let tx3 = new_unsigned_tx(nonce + 2.into(), gas, 1.into()); - (tx.sign(secret), tx2.sign(secret), tx2_2.sign(secret), tx3.sign(secret)) + (tx.sign(secret, None), tx2.sign(secret, None), tx2_2.sign(secret, None), tx3.sign(secret, None)) }; let sender = tx1.sender().unwrap(); txq.add(tx1, TransactionOrigin::Local, &default_account_details, &gas_estimator).unwrap(); diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index 46e99c12e..2babfb708 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -38,7 +38,7 @@ pub struct CommonParams { /// Maximum size of extra data. pub maximum_extra_data_size: usize, /// Network id. - pub network_id: U256, + pub network_id: usize, /// Main subprotocol name. pub subprotocol_name: String, /// Minimum gas limit. @@ -160,7 +160,7 @@ impl Spec { pub fn nodes(&self) -> &[String] { &self.nodes } /// Get the configured Network ID. - pub fn network_id(&self) -> U256 { self.params.network_id } + pub fn network_id(&self) -> usize { self.params.network_id } /// Get the configured Network ID. pub fn subprotocol_name(&self) -> String { self.params.subprotocol_name.clone() } @@ -250,7 +250,7 @@ impl Spec { } trace!(target: "spec", "ensure_db_good: Populated sec trie; root is {}", root); for (address, account) in self.genesis_state.get().iter() { - db.note_account_bloom(address); + db.note_non_null_account(address); account.insert_additional(&mut AccountDBMut::new(db.as_hashdb_mut(), address)); } assert!(db.as_hashdb().contains(&self.state_root())); diff --git a/ethcore/src/state/account.rs b/ethcore/src/state/account.rs index d8d281b17..76061f6a0 100644 --- a/ethcore/src/state/account.rs +++ b/ethcore/src/state/account.rs @@ -300,11 +300,17 @@ impl Account { pub fn storage_is_clean(&self) -> bool { self.storage_changes.is_empty() } /// Check if account has zero nonce, balance, no code and no storage. + /// + /// NOTE: Will panic if `!self.storage_is_clean()` pub fn is_empty(&self) -> bool { - self.storage_changes.is_empty() && + assert!(self.storage_is_clean(), "Account::is_empty() may only legally be called when storage is clean."); + self.is_null() && self.storage_root == SHA3_NULL_RLP + } + + /// Check if account has zero nonce, balance, no code. + pub fn is_null(&self) -> bool { self.balance.is_zero() && self.nonce.is_zero() && - self.storage_root == SHA3_NULL_RLP && self.code_hash == SHA3_EMPTY } diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index 7c0f43d97..01a7e3b15 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -199,6 +199,13 @@ enum RequireCache { Code, } +#[derive(PartialEq)] +pub enum CleanupMode<'a> { + ForceCreate, + NoEmpty, + KillEmpty(&'a mut HashSet
), +} + const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with valid root. Creating a SecTrieDB with a valid root will not fail. \ Therefore creating a SecTrieDB with this state's root will not fail."; @@ -329,8 +336,8 @@ impl State { /// Create a new contract at address `contract`. If there is already an account at the address /// it will have its code reset, ready for `init_code()`. - pub fn new_contract(&mut self, contract: &Address, balance: U256) { - self.insert_cache(contract, AccountEntry::new_dirty(Some(Account::new_contract(balance, self.account_start_nonce)))); + pub fn new_contract(&mut self, contract: &Address, balance: U256, nonce_offset: U256) { + self.insert_cache(contract, AccountEntry::new_dirty(Some(Account::new_contract(balance, self.account_start_nonce + nonce_offset)))); } /// Remove an existing account. @@ -341,10 +348,15 @@ impl State { /// Determine whether an account exists. pub fn exists(&self, a: &Address) -> bool { // Bloom filter does not contain empty accounts, so it is important here to - // check if account exists in the database directly before EIP-158 is in effect. + // check if account exists in the database directly before EIP-161 is in effect. self.ensure_cached(a, RequireCache::None, false, |a| a.is_some()) } + /// Determine whether an account exists and if not empty. + pub fn exists_and_not_null(&self, a: &Address) -> bool { + self.ensure_cached(a, RequireCache::None, false, |a| a.map_or(false, |a| !a.is_null())) + } + /// Get the balance of account `a`. pub fn balance(&self, a: &Address) -> U256 { self.ensure_cached(a, RequireCache::None, true, @@ -399,7 +411,7 @@ impl State { } // check bloom before any requests to trie - if !self.db.check_account_bloom(address) { return H256::zero() } + if !self.db.check_non_null_bloom(address) { return H256::zero() } // account is not found in the global cache, get from the DB and insert into local let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); @@ -433,10 +445,18 @@ impl State { } /// Add `incr` to the balance of account `a`. - pub fn add_balance(&mut self, a: &Address, incr: &U256) { + pub fn add_balance(&mut self, a: &Address, incr: &U256, cleanup_mode: CleanupMode) { trace!(target: "state", "add_balance({}, {}): {}", a, incr, self.balance(a)); - if !incr.is_zero() || !self.exists(a) { + let is_value_transfer = !incr.is_zero(); + if is_value_transfer || (cleanup_mode == CleanupMode::ForceCreate && !self.exists(a)) { self.require(a, false).add_balance(incr); + } else { + match cleanup_mode { + CleanupMode::KillEmpty(set) => if !is_value_transfer && self.exists(a) && !self.exists_and_not_null(a) { + set.insert(a.clone()); + }, + _ => {} + } } } @@ -449,9 +469,9 @@ impl State { } /// Subtracts `by` from the balance of `from` and adds it to that of `to`. - pub fn transfer_balance(&mut self, from: &Address, to: &Address, by: &U256) { + pub fn transfer_balance(&mut self, from: &Address, to: &Address, by: &U256, cleanup_mode: CleanupMode) { self.sub_balance(from, by); - self.add_balance(to, by); + self.add_balance(to, by, cleanup_mode); } /// Increment the nonce of account `a` by 1. @@ -507,13 +527,15 @@ impl State { // first, commit the sub trees. for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) { if let Some(ref mut account) = a.account { - if !account.is_empty() { - db.note_account_bloom(address); - } let addr_hash = account.address_hash(address); - let mut account_db = factories.accountdb.create(db.as_hashdb_mut(), addr_hash); - account.commit_storage(&factories.trie, account_db.as_hashdb_mut()); - account.commit_code(account_db.as_hashdb_mut()); + { + let mut account_db = factories.accountdb.create(db.as_hashdb_mut(), addr_hash); + account.commit_storage(&factories.trie, account_db.as_hashdb_mut()); + account.commit_code(account_db.as_hashdb_mut()); + } + if !account.is_empty() { + db.note_non_null_account(address); + } } } @@ -653,7 +675,7 @@ impl State { Some(r) => r, None => { // first check bloom if it is not in database for sure - if check_bloom && !self.db.check_account_bloom(a) { return f(None); } + if check_bloom && !self.db.check_non_null_bloom(a) { return f(None); } // not found in the global cache, get from the DB and insert into local let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); @@ -687,7 +709,7 @@ impl State { match self.db.get_cached_account(a) { Some(acc) => self.insert_cache(a, AccountEntry::new_clean_cached(acc)), None => { - let maybe_acc = if self.db.check_account_bloom(a) { + let maybe_acc = if self.db.check_non_null_bloom(a) { let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); match db.get(a) { Ok(Some(acc)) => AccountEntry::new_clean(Some(Account::from_rlp(&acc))), @@ -793,9 +815,9 @@ fn should_apply_create_transaction() { action: Action::Create, value: 100.into(), data: FromHex::from_hex("601080600c6000396000f3006000355415600957005b60203560003555").unwrap(), - }.sign(&"".sha3()); + }.sign(&"".sha3(), None); - state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), @@ -853,9 +875,9 @@ fn should_trace_failed_create_transaction() { action: Action::Create, value: 100.into(), data: FromHex::from_hex("5b600056").unwrap(), - }.sign(&"".sha3()); + }.sign(&"".sha3(), None); - state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), @@ -890,10 +912,10 @@ fn should_trace_call_transaction() { action: Action::Call(0xa.into()), value: 100.into(), data: vec![], - }.sign(&"".sha3()); + }.sign(&"".sha3(), None); state.init_code(&0xa.into(), FromHex::from_hex("6000").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), @@ -933,9 +955,9 @@ fn should_trace_basic_call_transaction() { action: Action::Call(0xa.into()), value: 100.into(), data: vec![], - }.sign(&"".sha3()); + }.sign(&"".sha3(), None); - state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), @@ -975,7 +997,7 @@ fn should_trace_call_transaction_to_builtin() { action: Action::Call(0x1.into()), value: 0.into(), data: vec![], - }.sign(&"".sha3()); + }.sign(&"".sha3(), None); let result = state.apply(&info, engine, &t, true).unwrap(); @@ -1017,7 +1039,7 @@ fn should_not_trace_subcall_transaction_to_builtin() { action: Action::Call(0xa.into()), value: 0.into(), data: vec![], - }.sign(&"".sha3()); + }.sign(&"".sha3(), None); state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060006001610be0f1").unwrap()); let result = state.apply(&info, engine, &t, true).unwrap(); @@ -1060,7 +1082,7 @@ fn should_not_trace_callcode() { action: Action::Call(0xa.into()), value: 0.into(), data: vec![], - }.sign(&"".sha3()); + }.sign(&"".sha3(), None); state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b611000f2").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); @@ -1122,7 +1144,7 @@ fn should_not_trace_delegatecall() { action: Action::Call(0xa.into()), value: 0.into(), data: vec![], - }.sign(&"".sha3()); + }.sign(&"".sha3(), None); state.init_code(&0xa.into(), FromHex::from_hex("6000600060006000600b618000f4").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); @@ -1181,10 +1203,10 @@ fn should_trace_failed_call_transaction() { action: Action::Call(0xa.into()), value: 100.into(), data: vec![], - }.sign(&"".sha3()); + }.sign(&"".sha3(), None); state.init_code(&0xa.into(), FromHex::from_hex("5b600056").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), @@ -1221,11 +1243,11 @@ fn should_trace_call_with_subcall_transaction() { action: Action::Call(0xa.into()), value: 100.into(), data: vec![], - }.sign(&"".sha3()); + }.sign(&"".sha3(), None); state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { @@ -1281,10 +1303,10 @@ fn should_trace_call_with_basic_subcall_transaction() { action: Action::Call(0xa.into()), value: 100.into(), data: vec![], - }.sign(&"".sha3()); + }.sign(&"".sha3(), None); state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006045600b6000f1").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), @@ -1336,10 +1358,10 @@ fn should_not_trace_call_with_invalid_basic_subcall_transaction() { action: Action::Call(0xa.into()), value: 100.into(), data: vec![], - }.sign(&"".sha3()); + }.sign(&"".sha3(), None); state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060ff600b6000f1").unwrap()); // not enough funds. - state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), @@ -1379,11 +1401,11 @@ fn should_trace_failed_subcall_transaction() { action: Action::Call(0xa.into()), value: 100.into(), data: vec![],//600480600b6000396000f35b600056 - }.sign(&"".sha3()); + }.sign(&"".sha3(), None); state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("5b600056").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), @@ -1435,12 +1457,12 @@ fn should_trace_call_with_subcall_with_subcall_transaction() { action: Action::Call(0xa.into()), value: 100.into(), data: vec![], - }.sign(&"".sha3()); + }.sign(&"".sha3(), None); state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1").unwrap()); state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), @@ -1510,12 +1532,12 @@ fn should_trace_failed_subcall_with_subcall_transaction() { action: Action::Call(0xa.into()), value: 100.into(), data: vec![],//600480600b6000396000f35b600056 - }.sign(&"".sha3()); + }.sign(&"".sha3(), None); state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1505b601256").unwrap()); state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { @@ -1583,11 +1605,11 @@ fn should_trace_suicide() { action: Action::Call(0xa.into()), value: 100.into(), data: vec![], - }.sign(&"".sha3()); + }.sign(&"".sha3(), None); state.init_code(&0xa.into(), FromHex::from_hex("73000000000000000000000000000000000000000bff").unwrap()); - state.add_balance(&0xa.into(), &50.into()); - state.add_balance(t.sender().as_ref().unwrap(), &100.into()); + state.add_balance(&0xa.into(), &50.into(), CleanupMode::NoEmpty); + state.add_balance(t.sender().as_ref().unwrap(), &100.into(), CleanupMode::NoEmpty); let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), @@ -1658,7 +1680,7 @@ fn get_from_database() { let (root, db) = { let mut state = get_temp_state_in(temp.as_path()); state.inc_nonce(&a); - state.add_balance(&a, &U256::from(69u64)); + state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty); state.commit().unwrap(); assert_eq!(state.balance(&a), U256::from(69u64)); state.drop() @@ -1675,27 +1697,47 @@ fn remove() { let mut state_result = get_temp_state(); let mut state = state_result.reference_mut(); assert_eq!(state.exists(&a), false); + assert_eq!(state.exists_and_not_null(&a), false); state.inc_nonce(&a); assert_eq!(state.exists(&a), true); + assert_eq!(state.exists_and_not_null(&a), true); assert_eq!(state.nonce(&a), U256::from(1u64)); state.kill_account(&a); assert_eq!(state.exists(&a), false); + assert_eq!(state.exists_and_not_null(&a), false); assert_eq!(state.nonce(&a), U256::from(0u64)); } #[test] -fn empty_account_exists() { +fn empty_account_is_not_created() { let a = Address::zero(); let path = RandomTempPath::new(); let db = get_temp_state_db_in(path.as_path()); let (root, db) = { let mut state = State::new(db, U256::from(0), Default::default()); - state.add_balance(&a, &U256::default()); // create an empty account + state.add_balance(&a, &U256::default(), CleanupMode::NoEmpty); // create an empty account + state.commit().unwrap(); + state.drop() + }; + let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + assert!(!state.exists(&a)); + assert!(!state.exists_and_not_null(&a)); +} + +#[test] +fn empty_account_exists_when_creation_forced() { + let a = Address::zero(); + let path = RandomTempPath::new(); + let db = get_temp_state_db_in(path.as_path()); + let (root, db) = { + let mut state = State::new(db, U256::from(0), Default::default()); + state.add_balance(&a, &U256::default(), CleanupMode::ForceCreate); // create an empty account state.commit().unwrap(); state.drop() }; let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); assert!(state.exists(&a)); + assert!(!state.exists_and_not_null(&a)); } #[test] @@ -1733,7 +1775,7 @@ fn alter_balance() { let mut state = state_result.reference_mut(); let a = Address::zero(); let b = 1u64.into(); - state.add_balance(&a, &U256::from(69u64)); + state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty); assert_eq!(state.balance(&a), U256::from(69u64)); state.commit().unwrap(); assert_eq!(state.balance(&a), U256::from(69u64)); @@ -1741,7 +1783,7 @@ fn alter_balance() { assert_eq!(state.balance(&a), U256::from(27u64)); state.commit().unwrap(); assert_eq!(state.balance(&a), U256::from(27u64)); - state.transfer_balance(&a, &b, &U256::from(18u64)); + state.transfer_balance(&a, &b, &U256::from(18u64), CleanupMode::NoEmpty); assert_eq!(state.balance(&a), U256::from(9u64)); assert_eq!(state.balance(&b), U256::from(18u64)); state.commit().unwrap(); @@ -1794,12 +1836,12 @@ fn checkpoint_basic() { let mut state = state_result.reference_mut(); let a = Address::zero(); state.checkpoint(); - state.add_balance(&a, &U256::from(69u64)); + state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty); assert_eq!(state.balance(&a), U256::from(69u64)); state.discard_checkpoint(); assert_eq!(state.balance(&a), U256::from(69u64)); state.checkpoint(); - state.add_balance(&a, &U256::from(1u64)); + state.add_balance(&a, &U256::from(1u64), CleanupMode::NoEmpty); assert_eq!(state.balance(&a), U256::from(70u64)); state.revert_to_checkpoint(); assert_eq!(state.balance(&a), U256::from(69u64)); @@ -1812,7 +1854,7 @@ fn checkpoint_nested() { let a = Address::zero(); state.checkpoint(); state.checkpoint(); - state.add_balance(&a, &U256::from(69u64)); + state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty); assert_eq!(state.balance(&a), U256::from(69u64)); state.discard_checkpoint(); assert_eq!(state.balance(&a), U256::from(69u64)); @@ -1835,7 +1877,7 @@ fn should_not_panic_on_state_diff_with_storage() { let a: Address = 0xa.into(); state.init_code(&a, b"abcdefg".to_vec()); - state.add_balance(&a, &256.into()); + state.add_balance(&a, &256.into(), CleanupMode::NoEmpty); state.set_storage(&a, 0xb.into(), 0xc.into()); let mut new_state = state.clone(); diff --git a/ethcore/src/state/substate.rs b/ethcore/src/state/substate.rs index de703f369..853b0e422 100644 --- a/ethcore/src/state/substate.rs +++ b/ethcore/src/state/substate.rs @@ -18,6 +18,8 @@ use std::collections::HashSet; use util::{Address, U256}; use log_entry::LogEntry; +use evm::Schedule; +use super::CleanupMode; /// State changes which should be applied in finalize, /// after transaction is fully executed. @@ -26,6 +28,9 @@ pub struct Substate { /// Any accounts that have suicided. pub suicides: HashSet
, + /// Any accounts that are tagged for garbage collection. + pub garbage: HashSet
, + /// Any logs. pub logs: Vec, @@ -45,10 +50,20 @@ impl Substate { /// Merge secondary substate `s` into self, accruing each element correspondingly. pub fn accrue(&mut self, s: Substate) { self.suicides.extend(s.suicides.into_iter()); + self.garbage.extend(s.garbage.into_iter()); self.logs.extend(s.logs.into_iter()); self.sstore_clears_count = self.sstore_clears_count + s.sstore_clears_count; self.contracts_created.extend(s.contracts_created.into_iter()); } + + /// Get the cleanup mode object from this. + pub fn to_cleanup_mode(&mut self, schedule: &Schedule) -> CleanupMode { + match (schedule.no_empty, schedule.kill_empty) { + (false, _) => CleanupMode::ForceCreate, + (true, false) => CleanupMode::NoEmpty, + (true, true) => CleanupMode::KillEmpty(&mut self.garbage), + } + } } #[cfg(test)] diff --git a/ethcore/src/state_db.rs b/ethcore/src/state_db.rs index 3506b8951..3a3595a35 100644 --- a/ethcore/src/state_db.rs +++ b/ethcore/src/state_db.rs @@ -165,13 +165,13 @@ impl StateDB { bloom } - pub fn check_account_bloom(&self, address: &Address) -> bool { + pub fn check_non_null_bloom(&self, address: &Address) -> bool { trace!(target: "account_bloom", "Check account bloom: {:?}", address); let bloom = self.account_bloom.lock(); bloom.check(&*address.sha3()) } - pub fn note_account_bloom(&self, address: &Address) { + pub fn note_non_null_account(&self, address: &Address) { trace!(target: "account_bloom", "Note account bloom: {:?}", address); let mut bloom = self.account_bloom.lock(); bloom.set(&*address.sha3()); diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index 38db8a4c3..99b251d66 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -16,6 +16,7 @@ use io::IoChannel; use client::{BlockChainClient, MiningBlockChainClient, Client, ClientConfig, BlockID}; +use state::CleanupMode; use ethereum; use block::IsBlock; use tests::helpers::*; @@ -272,7 +273,7 @@ fn change_history_size() { let client = Client::new(ClientConfig::default(), &test_spec, dir.as_path(), Arc::new(Miner::with_spec(&test_spec)), IoChannel::disconnected(), &db_config).unwrap(); for _ in 0..20 { let mut b = client.prepare_open_block(Address::default(), (3141562.into(), 31415620.into()), vec![]); - b.block_mut().fields_mut().state.add_balance(&address, &5.into()); + b.block_mut().fields_mut().state.add_balance(&address, &5.into(), CleanupMode::NoEmpty); b.block_mut().fields_mut().state.commit().unwrap(); let b = b.close_and_lock().seal(&*test_spec.engine, vec![]).unwrap(); client.import_sealed_block(b).unwrap(); // account change is in the journal overlay diff --git a/ethcore/src/tests/helpers.rs b/ethcore/src/tests/helpers.rs index 7b952b30c..ac8ce1885 100644 --- a/ethcore/src/tests/helpers.rs +++ b/ethcore/src/tests/helpers.rs @@ -40,6 +40,7 @@ pub enum ChainEra { Frontier, Homestead, Eip150, + Eip161, TransitionTest, } @@ -194,7 +195,7 @@ pub fn generate_dummy_client_with_spec_and_data(get_test_spec: F, block_numbe action: Action::Create, data: vec![], value: U256::zero(), - }.sign(kp.secret()), None).unwrap(); + }.sign(kp.secret(), None), None).unwrap(); n += 1; } diff --git a/ethcore/src/types/transaction.rs b/ethcore/src/types/transaction.rs index 247a1b301..e26f98cfa 100644 --- a/ethcore/src/types/transaction.rs +++ b/ethcore/src/types/transaction.rs @@ -72,8 +72,8 @@ pub struct Transaction { impl Transaction { /// Append object with a without signature into RLP stream - pub fn rlp_append_unsigned_transaction(&self, s: &mut RlpStream) { - s.begin_list(6); + pub fn rlp_append_unsigned_transaction(&self, s: &mut RlpStream, network_id: Option) { + s.begin_list(if let None = network_id { 6 } else { 9 }); s.append(&self.nonce); s.append(&self.gas_price); s.append(&self.gas); @@ -83,6 +83,11 @@ impl Transaction { }; s.append(&self.value); s.append(&self.data); + if let Some(n) = network_id { + s.append(&n); + s.append(&0u8); + s.append(&0u8); + } } } @@ -105,7 +110,7 @@ impl From for SignedTransaction { }, value: t.value.into(), data: t.data.into(), - }.sign(&t.secret.into()) + }.sign(&t.secret.into(), None) } } @@ -135,26 +140,26 @@ impl From for SignedTransaction { impl Transaction { /// The message hash of the transaction. - pub fn hash(&self) -> H256 { + pub fn hash(&self, network_id: Option) -> H256 { let mut stream = RlpStream::new(); - self.rlp_append_unsigned_transaction(&mut stream); + self.rlp_append_unsigned_transaction(&mut stream, network_id); stream.out().sha3() } /// Signs the transaction as coming from `sender`. - pub fn sign(self, secret: &Secret) -> SignedTransaction { - let sig = ::ethkey::sign(secret, &self.hash()) + pub fn sign(self, secret: &Secret, network_id: Option) -> SignedTransaction { + let sig = ::ethkey::sign(secret, &self.hash(network_id)) .expect("data is valid and context has signing capabilities; qed"); - self.with_signature(sig) + self.with_signature(sig, network_id) } /// Signs the transaction with signature. - pub fn with_signature(self, sig: Signature) -> SignedTransaction { + pub fn with_signature(self, sig: Signature, network_id: Option) -> SignedTransaction { SignedTransaction { unsigned: self, r: sig.r().into(), s: sig.s().into(), - v: sig.v() + 27, + v: sig.v() + if let Some(n) = network_id { 1 + n * 2 } else { 27 }, hash: Cell::new(None), sender: Cell::new(None), } @@ -204,7 +209,8 @@ impl Transaction { pub struct SignedTransaction { /// Plain Transaction. unsigned: Transaction, - /// The V field of the signature, either 27 or 28; helps describe the point on the curve. + /// The V field of the signature; the LS bit described which half of the curve our point falls + /// in. The MS bits describe which network this transaction is for. If 27/28, its for all networks. v: u8, /// The R field of the signature; helps describe the point on the curve. r: U256, @@ -266,7 +272,7 @@ impl HeapSizeOf for SignedTransaction { impl SignedTransaction { /// Append object with a signature into RLP stream - pub fn rlp_append_sealed_transaction(&self, s: &mut RlpStream) { + fn rlp_append_sealed_transaction(&self, s: &mut RlpStream) { s.begin_list(9); s.append(&self.nonce); s.append(&self.gas_price); @@ -295,8 +301,16 @@ impl SignedTransaction { } } - /// 0 is `v` is 27, 1 if 28, and 4 otherwise. - pub fn standard_v(&self) -> u8 { match self.v { 27 => 0, 28 => 1, _ => 4 } } + /// 0 if `v` would have been 27 under "Electrum" notation, 1 if 28 or 4 if invalid. + pub fn standard_v(&self) -> u8 { match self.v { 0 => 4, v => (v - 1) & 1, } } + + /// The network ID, or `None` if this is a global transaction. + pub fn network_id(&self) -> Option { + match self.v { + 0 | 27 | 28 => None, + v => Some((v - 1) / 2), + } + } /// Construct a signature object from the sig. pub fn signature(&self) -> Signature { @@ -327,20 +341,25 @@ impl SignedTransaction { /// Returns the public key of the sender. pub fn public_key(&self) -> Result { - Ok(try!(recover(&self.signature(), &self.unsigned.hash()))) + Ok(try!(recover(&self.signature(), &self.unsigned.hash(self.network_id())))) } /// Do basic validation, checking for valid signature and minimum gas, // TODO: consider use in block validation. #[cfg(test)] #[cfg(feature = "json-tests")] - pub fn validate(self, schedule: &Schedule, require_low: bool) -> Result { + pub fn validate(self, schedule: &Schedule, require_low: bool, allow_network_id_of_one: bool) -> Result { if require_low && !self.signature().is_low_s() { return Err(EthkeyError::InvalidSignature.into()) } + match self.network_id() { + None => {}, + Some(1) if allow_network_id_of_one => {}, + _ => return Err(TransactionError::InvalidNetworkId.into()), + } try!(self.sender()); if self.gas < U256::from(self.gas_required(&schedule)) { - Err(From::from(TransactionError::InvalidGasLimit(::util::OutOfBounds{min: Some(U256::from(self.gas_required(&schedule))), max: None, found: self.gas}))) + Err(TransactionError::InvalidGasLimit(::util::OutOfBounds{min: Some(U256::from(self.gas_required(&schedule))), max: None, found: self.gas}).into()) } else { Ok(self) } @@ -380,6 +399,7 @@ fn sender_test() { } else { panic!(); } assert_eq!(t.value, U256::from(0x0au64)); assert_eq!(t.sender().unwrap(), "0f65fe9276bc9a24ae7083ae28e2660ef72df99e".into()); + assert_eq!(t.network_id(), None); } #[test] @@ -394,8 +414,9 @@ fn signing() { gas: U256::from(50_000), value: U256::from(1), data: b"Hello!".to_vec() - }.sign(&key.secret()); + }.sign(&key.secret(), None); assert_eq!(Address::from(key.public().sha3()), t.sender().unwrap()); + assert_eq!(t.network_id(), None); } #[test] @@ -409,7 +430,47 @@ fn fake_signing() { data: b"Hello!".to_vec() }.fake_sign(Address::from(0x69)); assert_eq!(Address::from(0x69), t.sender().unwrap()); + assert_eq!(t.network_id(), None); let t = t.clone(); assert_eq!(Address::from(0x69), t.sender().unwrap()); + assert_eq!(t.network_id(), None); } + +#[test] +fn should_recover_from_network_specific_signing() { + use ethkey::{Random, Generator}; + let key = Random.generate().unwrap(); + let t = Transaction { + action: Action::Create, + nonce: U256::from(42), + gas_price: U256::from(3000), + gas: U256::from(50_000), + value: U256::from(1), + data: b"Hello!".to_vec() + }.sign(&key.secret(), Some(69)); + assert_eq!(Address::from(key.public().sha3()), t.sender().unwrap()); + assert_eq!(t.network_id(), Some(69)); +} + +#[test] +fn should_agree_with_vitalik() { + use rustc_serialize::hex::FromHex; + use std::str::FromStr; + + let test_vector = |tx_data: &str, address: &'static str| { + let signed: SignedTransaction = decode(&FromHex::from_hex(tx_data).unwrap()); + assert_eq!(signed.sender().unwrap(), address.into()); + }; + + test_vector("f864808504a817c800825208943535353535353535353535353535353535353535808025a0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116da0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d", "0xda39a520355857fdb37ecb527fe814230fa9962c"); + test_vector("f864018504a817c80182a410943535353535353535353535353535353535353535018025a0c89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc8a0c89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6", "0xd240215f30eafee2aaa5184d8f051ebb41c90b19"); + test_vector("f864028504a817c80282f618943535353535353535353535353535353535353535088025a0ad7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a8a0ad7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5", "0x2f2822c55d894df1ba32961c43325dcb3d614ee8"); + test_vector("f865038504a817c803830148209435353535353535353535353535353535353535351b8025a02a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4e0a02a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4de", "0xc21df0434ceab6e18a1300d18206e54e807b4456"); + test_vector("f865048504a817c80483019a28943535353535353535353535353535353535353535408025a013600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c063a013600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c060", "0xf975cee81edae2ab5883f4e2fb2a7f2fd56f4131"); + test_vector("f865058504a817c8058301ec309435353535353535353535353535353535353535357d8025a0ceebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1a0ceebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1", "0xd477474c9f48dcbfde5d97f30646242ab7a17e06"); + test_vector("f866068504a817c80683023e3894353535353535353535353535353535353535353581d88025a0e455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2da0e455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2d", "0xb49948deb719ca21e38d29e3360f534b39db0e76"); + test_vector("f867078504a817c807830290409435353535353535353535353535353535353535358201578025a052f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021a052f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021", "0xbddfc81a8ce87b2360837049a6eda68ab2f58999"); + test_vector("f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a0e4b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c11a0e4b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10", "0x8f2edcf67f329a146dd4cb1e6b3a072daff85b38"); + test_vector("f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a0d2f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba0d2f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb", "0x4ec38d4782fd4a6ff85c1cde77ccf1ae3c54472c"); +} \ No newline at end of file diff --git a/ethcore/src/verification/verification.rs b/ethcore/src/verification/verification.rs index 1b8eddfe8..bb9f042ae 100644 --- a/ethcore/src/verification/verification.rs +++ b/ethcore/src/verification/verification.rs @@ -395,7 +395,7 @@ mod tests { gas: U256::from(30_000), gas_price: U256::from(40_000), nonce: U256::one() - }.sign(keypair.secret()); + }.sign(keypair.secret(), None); let tr2 = Transaction { action: Action::Create, @@ -404,7 +404,7 @@ mod tests { gas: U256::from(30_000), gas_price: U256::from(40_000), nonce: U256::from(2) - }.sign(keypair.secret()); + }.sign(keypair.secret(), None); let good_transactions = [ tr1.clone(), tr2.clone() ]; diff --git a/evmbin/src/ext.rs b/evmbin/src/ext.rs index 11fb3a876..cac89d76c 100644 --- a/evmbin/src/ext.rs +++ b/evmbin/src/ext.rs @@ -51,6 +51,14 @@ impl Ext for FakeExt { unimplemented!(); } + fn exists_and_not_null(&self, address: &Address) -> bool { + unimplemented!(); + } + + fn origin_balance(&self) -> U256 { + unimplemented!(); + } + fn balance(&self, _address: &Address) -> U256 { unimplemented!(); } diff --git a/json/src/spec/ethash.rs b/json/src/spec/ethash.rs index 025ae8475..f8412bb97 100644 --- a/json/src/spec/ethash.rs +++ b/json/src/spec/ethash.rs @@ -70,6 +70,21 @@ pub struct EthashParams { /// See main EthashParams docs. #[serde(rename="eip150Transition")] pub eip150_transition: Option, + + /// See main EthashParams docs. + #[serde(rename="eip155Transition")] + pub eip155_transition: Option, + + /// See main EthashParams docs. + #[serde(rename="eip160Transition")] + pub eip160_transition: Option, + + /// See main EthashParams docs. + #[serde(rename="eip161abcTransition")] + pub eip161abc_transition: Option, + /// See main EthashParams docs. + #[serde(rename="eip161dTransition")] + pub eip161d_transition: Option, } /// Ethash engine deserialization. @@ -122,7 +137,11 @@ mod tests { "difficultyHardforkTransition": "0x59d9", "difficultyHardforkBoundDivisor": "0x0200", "bombDefuseTransition": "0x42", - "eip150Transition": "0x42" + "eip150Transition": "0x42", + "eip155Transition": "0x42", + "eip160Transition": "0x42", + "eip161abcTransition": "0x42", + "eip161dTransition": "0x42" } }"#; diff --git a/json/src/spec/params.rs b/json/src/spec/params.rs index ba81fb48a..aa634221d 100644 --- a/json/src/spec/params.rs +++ b/json/src/spec/params.rs @@ -28,15 +28,17 @@ pub struct Params { /// Maximum size of extra data. #[serde(rename="maximumExtraDataSize")] pub maximum_extra_data_size: Uint, + /// Minimum gas limit. + #[serde(rename="minGasLimit")] + pub min_gas_limit: Uint, + /// Network id. #[serde(rename="networkID")] pub network_id: Uint, /// Name of the main ("eth") subprotocol. #[serde(rename="subprotocolName")] pub subprotocol_name: Option, - /// Minimum gas limit. - #[serde(rename="minGasLimit")] - pub min_gas_limit: Uint, + /// Option fork block number to check. #[serde(rename="forkBlock")] pub fork_block: Option, diff --git a/parity/cli/config.full.toml b/parity/cli/config.full.toml index 84e44ee77..c8c2f792c 100644 --- a/parity/cli/config.full.toml +++ b/parity/cli/config.full.toml @@ -25,7 +25,7 @@ port = 30303 min_peers = 25 max_peers = 50 nat = "any" -id = "0x1" +id = 1 bootnodes = [] discovery = true warp = true diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index d13d791ad..3f66cfba5 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -46,7 +46,7 @@ usage! { flag_testnet: bool, flag_import_geth_keys: bool, flag_datadir: Option, - flag_networkid: Option, + flag_networkid: Option, flag_peers: Option, flag_nodekey: Option, flag_nodiscover: bool, @@ -122,7 +122,7 @@ usage! { or |c: &Config| otry!(c.network).nat.clone(), flag_allow_ips: String = "all", or |c: &Config| otry!(c.network).allow_ips.clone(), - flag_network_id: Option = None, + flag_network_id: Option = None, or |c: &Config| otry!(c.network).id.clone().map(Some), flag_bootnodes: Option = None, or |c: &Config| otry!(c.network).bootnodes.clone().map(|vec| Some(vec.join(","))), @@ -323,7 +323,7 @@ struct Network { max_pending_peers: Option, nat: Option, allow_ips: Option, - id: Option, + id: Option, bootnodes: Option>, discovery: Option, node_key: Option, @@ -530,7 +530,7 @@ mod tests { flag_snapshot_peers: 0u16, flag_allow_ips: "all".into(), flag_nat: "any".into(), - flag_network_id: Some("0x1".into()), + flag_network_id: Some(1), flag_bootnodes: Some("".into()), flag_no_discovery: false, flag_node_key: None, diff --git a/parity/cli/version.txt b/parity/cli/version.txt index acb7cd9e6..259efe941 100644 --- a/parity/cli/version.txt +++ b/parity/cli/version.txt @@ -5,5 +5,6 @@ License GPLv3+: GNU GPL version 3 or later . This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. -By Wood/Paronyan/Kotewicz/Drwięga/Volf. +By Wood/Paronyan/Kotewicz/Drwięga/Volf + Habermeier/Czaban/Greeff/Gotchac/Redmann diff --git a/parity/configuration.rs b/parity/configuration.rs index f98225e8d..3484f8a4b 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -86,7 +86,7 @@ impl Configuration { let http_conf = try!(self.http_config()); let ipc_conf = try!(self.ipc_config()); let net_conf = try!(self.net_config()); - let network_id = try!(self.network_id()); + let network_id = self.network_id(); let cache_config = self.cache_config(); let spec = try!(self.chain().parse()); let tracing = try!(self.args.flag_tracing.parse()); @@ -517,12 +517,8 @@ impl Configuration { Ok(ret) } - fn network_id(&self) -> Result, String> { - let net_id = self.args.flag_network_id.as_ref().or(self.args.flag_networkid.as_ref()); - match net_id { - Some(id) => Ok(Some(try!(to_u256(id)))), - None => Ok(None), - } + fn network_id(&self) -> Option { + self.args.flag_network_id.or(self.args.flag_networkid) } fn rpc_apis(&self) -> String { diff --git a/parity/run.rs b/parity/run.rs index de73ecb45..9c3b00737 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -20,7 +20,7 @@ use ctrlc::CtrlC; use fdlimit::raise_fd_limit; use ethcore_rpc::{NetworkSettings, is_major_importing}; use ethsync::NetworkConfiguration; -use util::{Colour, version, U256, RotatingLogger}; +use util::{Colour, version, RotatingLogger}; use io::{MayPanic, ForwardPanic, PanicHandler}; use ethcore_logger::{Config as LogConfig}; use ethcore::client::{Mode, DatabaseCompactionProfile, VMType, ChainNotify, BlockChainClient}; @@ -70,7 +70,7 @@ pub struct RunCmd { pub http_conf: HttpConfiguration, pub ipc_conf: IpcConfiguration, pub net_conf: NetworkConfiguration, - pub network_id: Option, + pub network_id: Option, pub warp_sync: bool, pub acc_conf: AccountsConfig, pub gas_pricer: GasPricerConfig, diff --git a/rpc/src/v1/helpers/dispatch.rs b/rpc/src/v1/helpers/dispatch.rs index 1944d9035..457f96552 100644 --- a/rpc/src/v1/helpers/dispatch.rs +++ b/rpc/src/v1/helpers/dispatch.rs @@ -63,15 +63,16 @@ pub fn decrypt(accounts: &AccountProvider, address: Address, password: Option(client: &C, miner: &M, accounts: &AccountProvider, request: TransactionRequest, password: Option) -> Result where C: MiningBlockChainClient, M: MinerService { + let network_id = client.signing_network_id(); let address = request.from; let signed_transaction = { let t = prepare_transaction(client, miner, request); - let hash = t.hash(); + let hash = t.hash(network_id); let signature = try!(signature(accounts, address, password, hash)); - t.with_signature(signature) + t.with_signature(signature, network_id) }; - trace!(target: "miner", "send_transaction: dispatching tx: {}", ::rlp::encode(&signed_transaction).to_vec().pretty()); + trace!(target: "miner", "send_transaction: dispatching tx: {} for network ID {:?}", ::rlp::encode(&signed_transaction).to_vec().pretty(), network_id); dispatch_transaction(&*client, &*miner, signed_transaction) } diff --git a/rpc/src/v1/helpers/errors.rs b/rpc/src/v1/helpers/errors.rs index fad044866..8cbf26b7c 100644 --- a/rpc/src/v1/helpers/errors.rs +++ b/rpc/src/v1/helpers/errors.rs @@ -255,6 +255,7 @@ pub fn from_transaction_error(error: EthcoreError) -> Error { SenderBanned => "Sender is banned in local queue.".into(), RecipientBanned => "Recipient is banned in local queue.".into(), CodeBanned => "Code is banned in local queue.".into(), + e => format!("{}", e).into(), }; Error { code: ErrorCode::ServerError(codes::TRANSACTION_ERROR), diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index c4341622e..a458a0570 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -45,7 +45,7 @@ fn account_provider() -> Arc { fn sync_provider() -> Arc { Arc::new(TestSyncProvider::new(Config { - network_id: U256::from(3), + network_id: 3, num_peers: 120, })) } diff --git a/rpc/src/v1/tests/helpers/sync_provider.rs b/rpc/src/v1/tests/helpers/sync_provider.rs index 531b9bd2d..e0f811fc0 100644 --- a/rpc/src/v1/tests/helpers/sync_provider.rs +++ b/rpc/src/v1/tests/helpers/sync_provider.rs @@ -16,13 +16,13 @@ //! Test implementation of SyncProvider. -use util::{RwLock, U256}; +use util::{RwLock}; use ethsync::{SyncProvider, SyncStatus, SyncState, PeerInfo}; /// TestSyncProvider config. pub struct Config { /// Protocol version. - pub network_id: U256, + pub network_id: usize, /// Number of peers. pub num_peers: usize, } diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index 24aa5e480..145b179ee 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -43,7 +43,7 @@ fn accounts_provider() -> Arc { fn sync_provider() -> Arc { Arc::new(TestSyncProvider::new(Config { - network_id: U256::from(3), + network_id: 3, num_peers: 120, })) } @@ -737,8 +737,8 @@ fn rpc_eth_send_transaction() { value: U256::from(0x9184e72au64), data: vec![] }; - let signature = tester.accounts_provider.sign(address, None, t.hash()).unwrap(); - let t = t.with_signature(signature); + let signature = tester.accounts_provider.sign(address, None, t.hash(None)).unwrap(); + let t = t.with_signature(signature, None); let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#; @@ -754,8 +754,8 @@ fn rpc_eth_send_transaction() { value: U256::from(0x9184e72au64), data: vec![] }; - let signature = tester.accounts_provider.sign(address, None, t.hash()).unwrap(); - let t = t.with_signature(signature); + let signature = tester.accounts_provider.sign(address, None, t.hash(None)).unwrap(); + let t = t.with_signature(signature, None); let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#; @@ -819,8 +819,8 @@ fn rpc_eth_send_raw_transaction() { value: U256::from(0x9184e72au64), data: vec![] }; - let signature = tester.accounts_provider.sign(address, None, t.hash()).unwrap(); - let t = t.with_signature(signature); + let signature = tester.accounts_provider.sign(address, None, t.hash(None)).unwrap(); + let t = t.with_signature(signature, None); let rlp = ::rlp::encode(&t).to_vec().to_hex(); diff --git a/rpc/src/v1/tests/mocked/eth_signing.rs b/rpc/src/v1/tests/mocked/eth_signing.rs index 58b5e0546..0f0125f87 100644 --- a/rpc/src/v1/tests/mocked/eth_signing.rs +++ b/rpc/src/v1/tests/mocked/eth_signing.rs @@ -246,8 +246,8 @@ fn should_dispatch_transaction_if_account_is_unlock() { value: U256::from(0x9184e72au64), data: vec![] }; - let signature = tester.accounts.sign(acc, None, t.hash()).unwrap(); - let t = t.with_signature(signature); + let signature = tester.accounts.sign(acc, None, t.hash(None)).unwrap(); + let t = t.with_signature(signature, None); // when let request = r#"{ diff --git a/rpc/src/v1/tests/mocked/ethcore.rs b/rpc/src/v1/tests/mocked/ethcore.rs index e33f1a8f7..a64f133bf 100644 --- a/rpc/src/v1/tests/mocked/ethcore.rs +++ b/rpc/src/v1/tests/mocked/ethcore.rs @@ -16,7 +16,7 @@ use std::sync::Arc; use util::log::RotatingLogger; -use util::{U256, Address}; +use util::{Address}; use ethsync::ManageNetwork; use ethcore::client::{TestBlockChainClient}; use ethstore::ethkey::{Generator, Random}; @@ -46,7 +46,7 @@ impl Dependencies { miner: Arc::new(TestMinerService::default()), client: Arc::new(TestBlockChainClient::default()), sync: Arc::new(TestSyncProvider::new(Config { - network_id: U256::from(3), + network_id: 3, num_peers: 120, })), logger: Arc::new(RotatingLogger::new("rpc=trace".to_owned())), diff --git a/rpc/src/v1/tests/mocked/net.rs b/rpc/src/v1/tests/mocked/net.rs index 3bc341dcb..0a5eb43e7 100644 --- a/rpc/src/v1/tests/mocked/net.rs +++ b/rpc/src/v1/tests/mocked/net.rs @@ -21,7 +21,7 @@ use v1::tests::helpers::{Config, TestSyncProvider}; fn sync_provider() -> Arc { Arc::new(TestSyncProvider::new(Config { - network_id: 3.into(), + network_id: 3, num_peers: 120, })) } diff --git a/rpc/src/v1/tests/mocked/personal.rs b/rpc/src/v1/tests/mocked/personal.rs index 3bf4a9fa8..511f23415 100644 --- a/rpc/src/v1/tests/mocked/personal.rs +++ b/rpc/src/v1/tests/mocked/personal.rs @@ -201,8 +201,8 @@ fn sign_and_send_transaction() { data: vec![] }; tester.accounts.unlock_account_temporarily(address, "password123".into()).unwrap(); - let signature = tester.accounts.sign(address, None, t.hash()).unwrap(); - let t = t.with_signature(signature); + let signature = tester.accounts.sign(address, None, t.hash(None)).unwrap(); + let t = t.with_signature(signature, None); let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#; @@ -219,8 +219,8 @@ fn sign_and_send_transaction() { data: vec![] }; tester.accounts.unlock_account_temporarily(address, "password123".into()).unwrap(); - let signature = tester.accounts.sign(address, None, t.hash()).unwrap(); - let t = t.with_signature(signature); + let signature = tester.accounts.sign(address, None, t.hash(None)).unwrap(); + let t = t.with_signature(signature, None); let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#; diff --git a/rpc/src/v1/tests/mocked/personal_signer.rs b/rpc/src/v1/tests/mocked/personal_signer.rs index 7b3e18b11..ffcc47432 100644 --- a/rpc/src/v1/tests/mocked/personal_signer.rs +++ b/rpc/src/v1/tests/mocked/personal_signer.rs @@ -186,8 +186,8 @@ fn should_confirm_transaction_and_dispatch() { data: vec![] }; tester.accounts.unlock_account_temporarily(address, "test".into()).unwrap(); - let signature = tester.accounts.sign(address, None, t.hash()).unwrap(); - let t = t.with_signature(signature); + let signature = tester.accounts.sign(address, None, t.hash(None)).unwrap(); + let t = t.with_signature(signature, None); assert_eq!(tester.signer.requests().len(), 1); diff --git a/sync/src/api.rs b/sync/src/api.rs index 2e18fd5cc..1a33bc727 100644 --- a/sync/src/api.rs +++ b/sync/src/api.rs @@ -42,7 +42,7 @@ pub struct SyncConfig { /// Max blocks to download ahead pub max_download_ahead_blocks: usize, /// Network ID - pub network_id: U256, + pub network_id: usize, /// Main "eth" subprotocol name. pub subprotocol_name: [u8; 3], /// Fork block to check @@ -55,7 +55,7 @@ impl Default for SyncConfig { fn default() -> SyncConfig { SyncConfig { max_download_ahead_blocks: 20000, - network_id: U256::from(1), + network_id: 1, subprotocol_name: *b"eth", fork_block: None, warp_sync: false, diff --git a/sync/src/chain.rs b/sync/src/chain.rs index dc12fc926..063d60e6b 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -188,7 +188,7 @@ pub struct SyncStatus { /// Syncing protocol version. That's the maximum protocol version we connect to. pub protocol_version: u8, /// The underlying p2p network version. - pub network_id: U256, + pub network_id: usize, /// `BlockChain` height for the moment the sync started. pub start_block_number: BlockNumber, /// Last fully downloaded and imported block number (if any). @@ -269,7 +269,7 @@ struct PeerInfo { /// Peer chain genesis hash genesis: H256, /// Peer network id - network_id: U256, + network_id: usize, /// Peer best block hash latest_hash: H256, /// Peer total difficulty if known @@ -328,7 +328,7 @@ pub struct ChainSync { /// Last propagated block number last_sent_block_number: BlockNumber, /// Network ID - network_id: U256, + network_id: usize, /// Optional fork block to check fork_block: Option<(BlockNumber, H256)>, /// Snapshot downloader. @@ -2145,7 +2145,7 @@ mod tests { PeerInfo { protocol_version: 0, genesis: H256::zero(), - network_id: U256::zero(), + network_id: 0, latest_hash: peer_latest_hash, difficulty: None, asking: PeerAsking::Nothing,