Skip to content
This repository was archived by the owner on Nov 6, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions ethcore/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,15 +261,16 @@ impl<'x> OpenBlock<'x> {
gas_range_target: (U256, U256),
extra_data: Bytes,
) -> Result<Self, Error> {
let state = State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce(), factories)?;
let number = parent.number() + 1;
let state = State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce(number), factories)?;
let mut r = OpenBlock {
block: ExecutedBlock::new(state, tracing),
engine: engine,
last_hashes: last_hashes.clone(),
};

r.block.header.set_parent_hash(parent.hash());
r.block.header.set_number(parent.number() + 1);
r.block.header.set_number(number);
r.block.header.set_author(author);
r.block.header.set_timestamp_now(parent.timestamp());
r.block.header.set_extra_data(extra_data);
Expand Down Expand Up @@ -556,7 +557,7 @@ pub fn enact(
) -> Result<LockedBlock, Error> {
{
if ::log::max_log_level() >= ::log::LogLevel::Trace {
let s = State::from_existing(db.boxed_clone(), parent.state_root().clone(), engine.account_start_nonce(), factories.clone())?;
let s = State::from_existing(db.boxed_clone(), parent.state_root().clone(), engine.account_start_nonce(parent.number() + 1), factories.clone())?;
trace!(target: "enact", "num={}, root={}, author={}, author_balance={}\n",
header.number(), s.root(), header.author(), s.balance(&header.author())?);
}
Expand Down
6 changes: 3 additions & 3 deletions ethcore/src/client/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -810,7 +810,7 @@ impl Client {
}

let root = header.state_root();
State::from_existing(db, root, self.engine.account_start_nonce(), self.factories.clone()).ok()
State::from_existing(db, root, self.engine.account_start_nonce(block_number), self.factories.clone()).ok()
})
}

Expand All @@ -835,7 +835,7 @@ impl Client {
State::from_existing(
self.state_db.lock().boxed_clone_canon(&header.hash()),
header.state_root(),
self.engine.account_start_nonce(),
self.engine.account_start_nonce(header.number()),
self.factories.clone())
.expect("State root of best block header always valid.")
}
Expand Down Expand Up @@ -987,7 +987,7 @@ impl Client {
fn contract_call_tx(&self, block_id: BlockId, address: Address, data: Bytes) -> SignedTransaction {
let from = Address::default();
Transaction {
nonce: self.nonce(&from, block_id).unwrap_or_else(|| self.engine.account_start_nonce()),
nonce: self.nonce(&from, block_id).unwrap_or_else(|| self.engine.account_start_nonce(0)),
action: Action::Call(address),
gas: U256::from(50_000_000),
gas_price: U256::default(),
Expand Down
2 changes: 1 addition & 1 deletion ethcore/src/client/evm_test_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ impl EvmTestClient {
-> Result<(U256, Vec<u8>), EvmTestError>
{
let genesis = self.spec.genesis_header();
let mut state = state::State::from_existing(self.state_db.boxed_clone(), *genesis.state_root(), self.spec.engine.account_start_nonce(), self.factories.clone())
let mut state = state::State::from_existing(self.state_db.boxed_clone(), *genesis.state_root(), self.spec.engine.account_start_nonce(0), self.factories.clone())
.map_err(EvmTestError::Trie)?;
let info = client::EnvInfo {
number: genesis.number(),
Expand Down
10 changes: 8 additions & 2 deletions ethcore/src/engines/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,14 @@ pub trait Engine : Sync + Send {
fn maximum_uncle_count(&self) -> usize { 2 }
/// The number of generations back that uncles can be.
fn maximum_uncle_age(&self) -> usize { 6 }
/// The nonce with which accounts begin.
fn account_start_nonce(&self) -> U256 { self.params().account_start_nonce }
/// The nonce with which accounts begin at given block.
fn account_start_nonce(&self, block: u64) -> U256 {
if block >= self.params().dust_protection_transition {
U256::from(self.params().nonce_cap_increment) * U256::from(block)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could just multiply in u64

} else {
self.params().account_start_nonce
}
}

/// Block transformation functions, before the transactions.
fn on_new_block(&self, block: &mut ExecutedBlock, last_hashes: Arc<LastHashes>) -> Result<(), Error> {
Expand Down
3 changes: 3 additions & 0 deletions ethcore/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ pub enum BlockError {
InvalidNumber(Mismatch<BlockNumber>),
/// Block number isn't sensible.
RidiculousNumber(OutOfBounds<BlockNumber>),
/// Too many transactions from a particular address.
TooManyTransactions(Address),
/// Parent given is unknown.
UnknownParent(H256),
/// Uncle parent given is unknown.
Expand Down Expand Up @@ -205,6 +207,7 @@ impl fmt::Display for BlockError {
UnknownParent(ref hash) => format!("Unknown parent: {}", hash),
UnknownUncleParent(ref hash) => format!("Unknown uncle parent: {}", hash),
UnknownEpochTransition(ref num) => format!("Unknown transition to epoch number: {}", num),
TooManyTransactions(ref address) => format!("Too many transactions from: {}", address),
};

f.write_fmt(format_args!("Block error ({})", msg))
Expand Down
3 changes: 1 addition & 2 deletions ethcore/src/ethereum/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,7 @@ mod tests {
let engine = &spec.engine;
let genesis_header = spec.genesis_header();
let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();

let s = State::from_existing(db, genesis_header.state_root().clone(), engine.account_start_nonce(), Default::default()).unwrap();
let s = State::from_existing(db, genesis_header.state_root().clone(), engine.account_start_nonce(0), Default::default()).unwrap();
assert_eq!(s.balance(&"0000000000000000000000000000000000000001".into()).unwrap(), 1u64.into());
assert_eq!(s.balance(&"0000000000000000000000000000000000000002".into()).unwrap(), 1u64.into());
assert_eq!(s.balance(&"0000000000000000000000000000000000000003".into()).unwrap(), 1u64.into());
Expand Down
2 changes: 1 addition & 1 deletion ethcore/src/evm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,5 @@ pub use self::ext::{Ext, ContractCreateResult, MessageCallResult, CreateContract
pub use self::instructions::{InstructionInfo, INSTRUCTIONS, push_bytes};
pub use self::vmtype::VMType;
pub use self::factory::Factory;
pub use self::schedule::Schedule;
pub use self::schedule::{Schedule, CleanDustMode};
pub use types::executed::CallType;
22 changes: 20 additions & 2 deletions ethcore/src/evm/schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,19 @@ pub struct Schedule {
pub blockhash_gas: usize,
/// Static Call opcode enabled.
pub have_static_call: bool,
/// Kill basic accounts below this balance if touched.
pub kill_dust: CleanDustMode,
}

/// Dust accounts cleanup mode.
#[derive(PartialEq, Eq)]
pub enum CleanDustMode {
/// Dust cleanup is disabled.
Off,
/// Basic dust accounts will be removed.
BasicOnly,
/// Basic and contract dust accounts will be removed.
WithCodeAndStorage,
}

impl Schedule {
Expand Down Expand Up @@ -168,15 +181,16 @@ impl Schedule {
kill_empty: kill_empty,
blockhash_gas: 20,
have_static_call: false,
kill_dust: CleanDustMode::Off,
}
}

/// Schedule for the Metropolis era from common spec params.
/// Schedule for the post-EIP-150-era of the Ethereum main net.
pub fn from_params(block_number: u64, params: &CommonParams) -> Schedule {
let mut schedule = Schedule::new_post_eip150(usize::max_value(), true, true, true);
schedule.apply_params(block_number, params);
schedule
}
}

/// Apply common spec config parameters to the schedule.
pub fn apply_params(&mut self, block_number: u64, params: &CommonParams) {
Expand All @@ -186,6 +200,9 @@ impl Schedule {
if block_number >= params.eip210_transition {
self.blockhash_gas = 350;
}
if block_number >= params.dust_protection_transition {
self.kill_dust = if params.remove_dust_contracts { CleanDustMode::WithCodeAndStorage } else { CleanDustMode::BasicOnly };
}
}

/// Schedule for the Metropolis of the Ethereum main net.
Expand Down Expand Up @@ -244,6 +261,7 @@ impl Schedule {
kill_empty: false,
blockhash_gas: 20,
have_static_call: false,
kill_dust: CleanDustMode::Off,
}
}
}
Expand Down
21 changes: 11 additions & 10 deletions ethcore/src/executive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use engines::Engine;
use types::executed::CallType;
use env_info::EnvInfo;
use error::ExecutionError;
use evm::{self, Ext, Finalize, CreateContractAddress, FinalizationResult, ReturnData};
use evm::{self, Ext, Finalize, CreateContractAddress, FinalizationResult, ReturnData, CleanDustMode};
use externalities::*;
use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer};
use transaction::{Action, SignedTransaction};
Expand Down Expand Up @@ -164,6 +164,10 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
return Err(From::from(ExecutionError::NotEnoughBaseGas { required: base_gas_required, got: t.gas }));
}

if !t.is_unsigned() && check_nonce && schedule.kill_dust != CleanDustMode::Off && !self.state.exists(&sender)? {
return Err(From::from(ExecutionError::SenderMustExist));
}

let init_gas = t.gas - base_gas_required;

// validate transaction nonce
Expand Down Expand Up @@ -191,13 +195,13 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
return Err(From::from(ExecutionError::NotEnoughCash { required: total_cost, got: balance512 }));
}

let mut substate = Substate::new();

// NOTE: there can be no invalid transactions from this point.
if !t.is_unsigned() {
self.state.inc_nonce(&sender)?;
}
self.state.sub_balance(&sender, &U256::from(gas_cost))?;

let mut substate = Substate::new();
self.state.sub_balance(&sender, &U256::from(gas_cost), &mut substate.to_cleanup_mode(&schedule))?;

let (result, output) = match t.action {
Action::Create => {
Expand Down Expand Up @@ -434,7 +438,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
let nonce_offset = if schedule.no_empty {1} else {0}.into();
let prev_bal = self.state.balance(&params.address)?;
if let ActionValue::Transfer(val) = params.value {
self.state.sub_balance(&params.sender, &val)?;
self.state.sub_balance(&params.sender, &val, &mut substate.to_cleanup_mode(&schedule))?;
self.state.new_contract(&params.address, val + prev_bal, nonce_offset);
} else {
self.state.new_contract(&params.address, prev_bal, nonce_offset);
Expand Down Expand Up @@ -512,11 +516,8 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
}

// 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);
}
}
let min_balance = if schedule.kill_dust != CleanDustMode::Off { Some(U256::from(schedule.tx_gas) * t.gas_price) } else { None };
self.state.kill_garbage(&substate.touched, schedule.kill_empty, &min_balance, schedule.kill_dust == CleanDustMode::WithCodeAndStorage)?;

match result {
Err(evm::Error::Internal(msg)) => Err(ExecutionError::Internal(msg)),
Expand Down
4 changes: 2 additions & 2 deletions ethcore/src/externalities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
//! Transaction Execution environment.
use util::*;
use action_params::{ActionParams, ActionValue};
use state::{Backend as StateBackend, State, Substate};
use state::{Backend as StateBackend, State, Substate, CleanupMode};
use engines::Engine;
use env_info::EnvInfo;
use executive::*;
Expand Down Expand Up @@ -347,7 +347,7 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for Externalities<'a, T, V, B, E>
let balance = self.balance(&address)?;
if &address == refund_address {
// TODO [todr] To be consistent with CPP client we set balance to 0 in that case.
self.state.sub_balance(&address, &balance)?;
self.state.sub_balance(&address, &balance, &mut CleanupMode::NoEmpty)?;
} else {
trace!(target: "ext", "Suiciding {} -> {} (xfer: {})", address, refund_address, balance);
self.state.transfer_balance(
Expand Down
5 changes: 4 additions & 1 deletion ethcore/src/miner/miner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,10 @@ impl Miner {
let _timer = PerfTimer::new("prepare_block");
let chain_info = chain.chain_info();
let (transactions, mut open_block, original_work_hash) = {
let transactions = {self.transaction_queue.read().top_transactions_at(chain_info.best_block_number, chain_info.best_block_timestamp)};
let nonce_cap = if chain_info.best_block_number + 1 >= self.engine.params().dust_protection_transition {
Some((self.engine.params().nonce_cap_increment * (chain_info.best_block_number + 1)).into())
} else { None };
let transactions = {self.transaction_queue.read().top_transactions_at(chain_info.best_block_number, chain_info.best_block_timestamp, nonce_cap)};
let mut sealing_work = self.sealing_work.lock();
let last_work_hash = sealing_work.queue.peek_last_ref().map(|pb| pb.block().fields().header.hash());
let best_hash = chain_info.best_block_hash;
Expand Down
34 changes: 27 additions & 7 deletions ethcore/src/miner/transaction_queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1084,11 +1084,11 @@ impl TransactionQueue {

/// Returns top transactions from the queue ordered by priority.
pub fn top_transactions(&self) -> Vec<SignedTransaction> {
self.top_transactions_at(BlockNumber::max_value(), u64::max_value())
self.top_transactions_at(BlockNumber::max_value(), u64::max_value(), None)

}

fn filter_pending_transaction<F>(&self, best_block: BlockNumber, best_timestamp: u64, mut f: F)
fn filter_pending_transaction<F>(&self, best_block: BlockNumber, best_timestamp: u64, nonce_cap: Option<U256>, mut f: F)
where F: FnMut(&VerifiedTransaction) {

let mut delayed = HashSet::new();
Expand All @@ -1098,6 +1098,11 @@ impl TransactionQueue {
if delayed.contains(&sender) {
continue;
}
if let Some(max_nonce) = nonce_cap {
if tx.nonce() >= max_nonce {
continue;
}
}
let delay = match tx.condition {
Some(Condition::Number(n)) => n > best_block,
Some(Condition::Timestamp(t)) => t > best_timestamp,
Expand All @@ -1112,16 +1117,16 @@ impl TransactionQueue {
}

/// Returns top transactions from the queue ordered by priority.
pub fn top_transactions_at(&self, best_block: BlockNumber, best_timestamp: u64) -> Vec<SignedTransaction> {
pub fn top_transactions_at(&self, best_block: BlockNumber, best_timestamp: u64, nonce_cap: Option<U256>) -> Vec<SignedTransaction> {
let mut r = Vec::new();
self.filter_pending_transaction(best_block, best_timestamp, |tx| r.push(tx.transaction.clone()));
self.filter_pending_transaction(best_block, best_timestamp, nonce_cap, |tx| r.push(tx.transaction.clone()));
r
}

/// Return all ready transactions.
pub fn pending_transactions(&self, best_block: BlockNumber, best_timestamp: u64) -> Vec<PendingTransaction> {
let mut r = Vec::new();
self.filter_pending_transaction(best_block, best_timestamp, |tx| r.push(PendingTransaction::new(tx.transaction.clone(), tx.condition.clone())));
self.filter_pending_transaction(best_block, best_timestamp, None, |tx| r.push(PendingTransaction::new(tx.transaction.clone(), tx.condition.clone())));
r
}

Expand Down Expand Up @@ -2205,9 +2210,9 @@ pub mod test {
// then
assert_eq!(res1, TransactionImportResult::Current);
assert_eq!(res2, TransactionImportResult::Current);
let top = txq.top_transactions_at(0, 0);
let top = txq.top_transactions_at(0, 0, None);
assert_eq!(top.len(), 0);
let top = txq.top_transactions_at(1, 0);
let top = txq.top_transactions_at(1, 0, None);
assert_eq!(top.len(), 2);
}

Expand Down Expand Up @@ -2809,4 +2814,19 @@ pub mod test {
// then
assert_eq!(txq.top_transactions().len(), 1);
}

#[test]
fn should_not_return_transactions_over_nonce_cap() {
// given
let keypair = Random.generate().unwrap();
let mut txq = TransactionQueue::default();
// when
for nonce in 123..130 {
let tx = new_unsigned_tx(nonce.into(), default_gas_val(), default_gas_price()).sign(keypair.secret(), None);
txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
}

// then
assert_eq!(txq.top_transactions_at(BlockNumber::max_value(), u64::max_value(), Some(127.into())).len(), 4);
}
}
4 changes: 2 additions & 2 deletions ethcore/src/snapshot/consensus/authority.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ fn make_tx_and_env(
use transaction::{Action, Transaction};

let transaction = Transaction {
nonce: engine.account_start_nonce(),
nonce: engine.account_start_nonce(header.number()),
action: Action::Call(addr),
gas: 50_000_000.into(),
gas_price: 0.into(),
Expand Down Expand Up @@ -469,7 +469,7 @@ impl Rebuilder for ChunkRebuilder {
let mut state = State::from_existing(
db.boxed_clone(),
self.manifest.state_root.clone(),
engine.account_start_nonce(),
engine.account_start_nonce(target_header.number()),
factories,
).map_err(|e| format!("State root mismatch: {}", e))?;

Expand Down
2 changes: 1 addition & 1 deletion ethcore/src/snapshot/tests/proof_of_authority.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ fn make_chain(accounts: Arc<AccountProvider>, blocks_beyond: usize, transitions:
};

// execution callback for native contract: push transaction to be sealed.
let nonce = RefCell::new(client.engine().account_start_nonce());
let nonce = RefCell::new(client.engine().account_start_nonce(0));
let exec = |addr, data| {
let mut nonce = nonce.borrow_mut();
let transaction = Transaction {
Expand Down
Loading