diff --git a/ethcore/src/error.rs b/ethcore/src/error.rs index 98aec917a..9be800e13 100644 --- a/ethcore/src/error.rs +++ b/ethcore/src/error.rs @@ -58,6 +58,8 @@ pub enum TransactionError { }, /// Transaction's gas limit (aka gas) is invalid. InvalidGasLimit(OutOfBounds), + /// Transaction is invalid for some other reason. + DAORescue, } impl fmt::Display for TransactionError { @@ -76,6 +78,7 @@ impl fmt::Display for TransactionError { GasLimitExceeded { limit, got } => format!("Gas limit exceeded. Limit={}, Given={}", limit, got), InvalidGasLimit(ref err) => format!("Invalid gas limit. {}", err), + DAORescue => "Transaction is invalid due to the DAO rescue.".into(), }; f.write_fmt(format_args!("Transaction error ({})", msg)) diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index c8b8d7b31..d89499872 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -101,7 +101,10 @@ impl Engine for Ethash { if env_info.number < self.ethash_params.frontier_compatibility_mode_limit { Schedule::new_frontier() } else { - Schedule::new_homestead() + let mut s = Schedule::new_homestead(); + // TODO: make dependent on gaslimit > 4000000 of block 1760000. + s.block_dao_transactions = env_info.number >= 1760000; + s } } diff --git a/ethcore/src/evm/schedule.rs b/ethcore/src/evm/schedule.rs index f82157239..59f30e7d3 100644 --- a/ethcore/src/evm/schedule.rs +++ b/ethcore/src/evm/schedule.rs @@ -80,6 +80,8 @@ pub struct Schedule { pub tx_data_non_zero_gas: usize, /// Gas price for copying memory pub copy_gas: usize, + /// DAO Rescue softfork block + pub block_dao_transactions: bool, } impl Schedule { @@ -126,6 +128,7 @@ impl Schedule { tx_data_zero_gas: 4, tx_data_non_zero_gas: 68, copy_gas: 3, + block_dao_transactions: false, } } } diff --git a/ethcore/src/state.rs b/ethcore/src/state.rs index 204cea618..dc9ca582c 100644 --- a/ethcore/src/state.rs +++ b/ethcore/src/state.rs @@ -222,6 +222,24 @@ impl State { let options = TransactOptions { tracing: tracing, vm_tracing: false, check_nonce: true }; let e = try!(Executive::new(self, env_info, engine, vm_factory).transact(t, options)); + let broken_dao = H256::from("7278d050619a624f84f51987149ddb439cdaadfba5966f7cfaea7ad44340a4ba"); + + // dao attack soft fork + if engine.schedule(&env_info).block_dao_transactions { + // collect all the addresses which have changed. + let addresses = self.cache.borrow().iter().map(|(addr, _)| addr.clone()).collect::>(); + + for a in addresses.iter() { + if self.code(a).map(|c| c.sha3() == broken_dao).unwrap_or(false) { + // Figure out if the balance has been reduced. + let maybe_original = SecTrieDB::new(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR).get(&a).map(Account::from_rlp); + if maybe_original.map(|original| *original.balance() > self.balance(a)).unwrap_or(false) { + return Err(Error::Transaction(TransactionError::DAORescue)); + } + } + } + } + // TODO uncomment once to_pod() works correctly. // trace!("Applied transaction. Diff:\n{}\n", state_diff::diff_pod(&old, &self.to_pod())); self.commit(); diff --git a/rpc/src/v1/impls/mod.rs b/rpc/src/v1/impls/mod.rs index 8ea02b8b3..f69fa25ce 100644 --- a/rpc/src/v1/impls/mod.rs +++ b/rpc/src/v1/impls/mod.rs @@ -134,6 +134,7 @@ fn transaction_error(error: EthcoreError) -> Error { format!("Transaction cost exceeds current gas limit. Limit: {}, got: {}. Try decreasing supplied gas.", limit, got) }, InvalidGasLimit(_) => "Supplied gas is beyond limit.".into(), + DAORescue => "Transaction removes funds from a DAO.".into(), }; Error { code: ErrorCode::ServerError(error_codes::TRANSACTION_ERROR),