diff --git a/Cargo.lock b/Cargo.lock index 61d380352..966ada42b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1501,7 +1501,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#257b3ce8aaa6797507592200dc78b29b8a305c3f" +source = "git+https://github.com/ethcore/js-precompiled.git#fee5af5a5c915719dab3ae4507b88262bc79d7f7" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index 72dacddf4..dca84e3a2 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -37,6 +37,7 @@ use service::ClientIoMessage; use transaction::SignedTransaction; use env_info::EnvInfo; use builtin::Builtin; +use state::CleanupMode; /// `AuthorityRound` params. #[derive(Debug, PartialEq)] @@ -49,6 +50,8 @@ pub struct AuthorityRoundParams { pub authorities: Vec
, /// Number of authorities. pub authority_n: usize, + /// Block reward. + pub block_reward: U256, /// Starting step, pub start_step: Option, } @@ -60,6 +63,7 @@ impl From for AuthorityRoundParams { step_duration: Duration::from_secs(p.step_duration.into()), authority_n: p.authorities.len(), authorities: p.authorities.into_iter().map(Into::into).collect::>(), + block_reward: p.block_reward.map_or_else(U256::zero, Into::into), start_step: p.start_step.map(Into::into), } } @@ -249,6 +253,20 @@ impl Engine for AuthorityRound { Seal::None } + /// Apply the block reward on finalisation of the block. + fn on_close_block(&self, block: &mut ExecutedBlock) { + let reward = self.our_params.block_reward; + let fields = block.fields_mut(); + + // Bestow block reward + fields.state.add_balance(fields.header.author(), &reward, CleanupMode::NoEmpty); + + // Commit state so that we can actually figure out the state root. + if let Err(e) = fields.state.commit() { + warn!("Encountered error on state commit: {}", e); + } + } + /// Check the number of seal fields. fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { if header.seal().len() != self.seal_fields() { diff --git a/ethcore/src/engines/tendermint/mod.rs b/ethcore/src/engines/tendermint/mod.rs index e124445e9..becc9d9c0 100644 --- a/ethcore/src/engines/tendermint/mod.rs +++ b/ethcore/src/engines/tendermint/mod.rs @@ -45,6 +45,7 @@ use views::HeaderView; use evm::Schedule; use io::{IoService, IoChannel}; use service::ClientIoMessage; +use state::CleanupMode; use self::message::*; use self::transition::TransitionHandler; use self::params::TendermintParams; @@ -469,6 +470,20 @@ impl Engine for Tendermint { Ok(()) } + /// Apply the block reward on finalisation of the block. + fn on_close_block(&self, block: &mut ExecutedBlock) { + let reward = self.our_params.block_reward; + let fields = block.fields_mut(); + + // Bestow block reward + fields.state.add_balance(fields.header.author(), &reward, CleanupMode::NoEmpty); + + // Commit state so that we can actually figure out the state root. + if let Err(e) = fields.state.commit() { + warn!("Encountered error on state commit: {}", e); + } + } + fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { let seal_length = header.seal().len(); if seal_length == self.seal_fields() { diff --git a/ethcore/src/engines/tendermint/params.rs b/ethcore/src/engines/tendermint/params.rs index cf723713b..e31182ae9 100644 --- a/ethcore/src/engines/tendermint/params.rs +++ b/ethcore/src/engines/tendermint/params.rs @@ -18,7 +18,7 @@ use ethjson; use super::transition::TendermintTimeouts; -use util::{Address, U256}; +use util::{Address, Uint, U256}; use time::Duration; /// `Tendermint` params. @@ -32,6 +32,8 @@ pub struct TendermintParams { pub authority_n: usize, /// Timeout durations for different steps. pub timeouts: TendermintTimeouts, + /// Block reward. + pub block_reward: U256, } impl Default for TendermintParams { @@ -42,6 +44,7 @@ impl Default for TendermintParams { gas_limit_bound_divisor: 0x0400.into(), authorities: authorities, authority_n: val_n, + block_reward: U256::zero(), timeouts: TendermintTimeouts::default(), } } @@ -67,6 +70,7 @@ impl From for TendermintParams { precommit: p.timeout_precommit.map_or(dt.precommit, to_duration), commit: p.timeout_commit.map_or(dt.commit, to_duration), }, + block_reward: p.block_reward.map_or_else(U256::zero, Into::into), } } } diff --git a/ethcore/src/json_tests/trie.rs b/ethcore/src/json_tests/trie.rs index 065985b0a..9107675d5 100644 --- a/ethcore/src/json_tests/trie.rs +++ b/ethcore/src/json_tests/trie.rs @@ -33,7 +33,7 @@ fn test_trie(json: &[u8], trie: TrieSpec) -> Vec { let key: Vec = key.into(); let value: Vec = value.map_or_else(Vec::new, Into::into); t.insert(&key, &value) - .expect(&format!("Trie test '{:?}' failed due to internal error", name)) + .expect(&format!("Trie test '{:?}' failed due to internal error", name)); } if *t.root() != test.root.into() { diff --git a/ethcore/src/miner/banning_queue.rs b/ethcore/src/miner/banning_queue.rs index 1fe345614..7d5f5c299 100644 --- a/ethcore/src/miner/banning_queue.rs +++ b/ethcore/src/miner/banning_queue.rs @@ -23,6 +23,7 @@ use std::cell::Cell; use transaction::{SignedTransaction, Action}; use transient_hashmap::TransientHashMap; use miner::{TransactionQueue, TransactionImportResult, TransactionOrigin, AccountDetails}; +use miner::transaction_queue::QueuingInstant; use error::{Error, TransactionError}; use util::{Uint, U256, H256, Address, Hashable}; @@ -78,6 +79,7 @@ impl BanningTransactionQueue { pub fn add_with_banlist( &mut self, transaction: SignedTransaction, + time: QueuingInstant, account_details: &F, gas_estimator: &G, ) -> Result where @@ -115,7 +117,7 @@ impl BanningTransactionQueue { } } } - self.queue.add(transaction, TransactionOrigin::External, None, account_details, gas_estimator) + self.queue.add(transaction, TransactionOrigin::External, time, None, account_details, gas_estimator) } /// Ban transaction with given hash. @@ -158,7 +160,7 @@ impl BanningTransactionQueue { Threshold::BanAfter(threshold) if count > threshold => { // Banlist the sender. // Remove all transactions from the queue. - self.remove_all(address, !U256::zero()); + self.cull(address, !U256::zero()); true }, _ => false @@ -263,7 +265,7 @@ mod tests { let mut txq = queue(); // when - txq.queue().add(tx, TransactionOrigin::External, None, &default_account_details, &gas_required).unwrap(); + txq.queue().add(tx, TransactionOrigin::External, 0, None, &default_account_details, &gas_required).unwrap(); // then // should also deref to queue @@ -279,12 +281,12 @@ mod tests { let banlist1 = txq.ban_sender(tx.sender().unwrap()); assert!(!banlist1, "Threshold not reached yet."); // Insert once - let import1 = txq.add_with_banlist(tx.clone(), &default_account_details, &gas_required).unwrap(); + let import1 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required).unwrap(); assert_eq!(import1, TransactionImportResult::Current); // when let banlist2 = txq.ban_sender(tx.sender().unwrap()); - let import2 = txq.add_with_banlist(tx.clone(), &default_account_details, &gas_required); + let import2 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required); // then assert!(banlist2, "Threshold should be reached - banned."); @@ -303,12 +305,12 @@ mod tests { let banlist1 = txq.ban_recipient(recipient); assert!(!banlist1, "Threshold not reached yet."); // Insert once - let import1 = txq.add_with_banlist(tx.clone(), &default_account_details, &gas_required).unwrap(); + let import1 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required).unwrap(); assert_eq!(import1, TransactionImportResult::Current); // when let banlist2 = txq.ban_recipient(recipient); - let import2 = txq.add_with_banlist(tx.clone(), &default_account_details, &gas_required); + let import2 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required); // then assert!(banlist2, "Threshold should be reached - banned."); @@ -325,12 +327,12 @@ mod tests { let banlist1 = txq.ban_codehash(codehash); assert!(!banlist1, "Threshold not reached yet."); // Insert once - let import1 = txq.add_with_banlist(tx.clone(), &default_account_details, &gas_required).unwrap(); + let import1 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required).unwrap(); assert_eq!(import1, TransactionImportResult::Current); // when let banlist2 = txq.ban_codehash(codehash); - let import2 = txq.add_with_banlist(tx.clone(), &default_account_details, &gas_required); + let import2 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required); // then assert!(banlist2, "Threshold should be reached - banned."); diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index c11f34ecb..3ffb790b1 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -417,15 +417,12 @@ impl Miner { let block = open_block.close(); - let fetch_account = |a: &Address| AccountDetails { - nonce: chain.latest_nonce(a), - balance: chain.latest_balance(a), - }; + let fetch_nonce = |a: &Address| chain.latest_nonce(a); { let mut queue = self.transaction_queue.lock(); for hash in invalid_transactions { - queue.remove_invalid(&hash, &fetch_account); + queue.remove_invalid(&hash, &fetch_nonce); } for hash in transactions_to_penalize { queue.penalize(&hash); @@ -597,6 +594,8 @@ impl Miner { let schedule = chain.latest_schedule(); let gas_required = |tx: &SignedTransaction| tx.gas_required(&schedule).into(); let best_block_header = chain.best_block_header().decode(); + let insertion_time = chain.chain_info().best_block_number; + transactions.into_iter() .map(|tx| { if chain.transaction_block(TransactionId::Hash(tx.hash())).is_some() { @@ -618,10 +617,10 @@ impl Miner { match origin { TransactionOrigin::Local | TransactionOrigin::RetractedBlock => { - transaction_queue.add(tx, origin, min_block, &fetch_account, &gas_required) + transaction_queue.add(tx, origin, insertion_time, min_block, &fetch_account, &gas_required) }, TransactionOrigin::External => { - transaction_queue.add_with_banlist(tx, &fetch_account, &gas_required) + transaction_queue.add_with_banlist(tx, insertion_time, &fetch_account, &gas_required) } } }, @@ -1141,8 +1140,13 @@ impl MinerService for Miner { // ...and at the end remove the old ones { + let fetch_account = |a: &Address| AccountDetails { + nonce: chain.latest_nonce(a), + balance: chain.latest_balance(a), + }; + let time = chain.chain_info().best_block_number; let mut transaction_queue = self.transaction_queue.lock(); - transaction_queue.remove_old(|sender| chain.latest_nonce(sender)); + transaction_queue.remove_old(&fetch_account, time); } if enacted.len() > 0 { diff --git a/ethcore/src/miner/transaction_queue.rs b/ethcore/src/miner/transaction_queue.rs index a51c70bc0..b52ec7972 100644 --- a/ethcore/src/miner/transaction_queue.rs +++ b/ethcore/src/miner/transaction_queue.rs @@ -51,8 +51,8 @@ //! let gas_estimator = |_tx: &SignedTransaction| 2.into(); //! //! let mut txq = TransactionQueue::default(); -//! txq.add(st2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); -//! txq.add(st1.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); +//! txq.add(st2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); +//! txq.add(st1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); //! //! // Check status //! assert_eq!(txq.status().pending, 2); @@ -64,7 +64,7 @@ //! //! // And when transaction is removed (but nonce haven't changed) //! // it will move subsequent transactions to future -//! txq.remove_invalid(&st1.hash(), &default_account_details); +//! txq.remove_invalid(&st1.hash(), &|_| 10.into()); //! assert_eq!(txq.status().pending, 0); //! assert_eq!(txq.status().future, 1); //! assert_eq!(txq.top_transactions().len(), 0); @@ -78,11 +78,11 @@ //! - When it's removed from `future` - all `future` transactions heights are recalculated and then //! we check if the transactions should go to `current` (comparing state nonce) //! - When it's removed from `current` - all transactions from this sender (`current` & `future`) are recalculated. -//! 3. `remove_all` is used to inform the queue about client (state) nonce changes. +//! 3. `cull` is used to inform the queue about client (state) nonce changes. //! - It removes all transactions (either from `current` or `future`) with nonce < client nonce //! - It moves matching `future` transactions to `current` //! 4. `remove_old` is used as convenient method to update the state nonce for all senders in the queue. -//! - Invokes `remove_all` with latest state nonce for all senders. +//! - Invokes `cull` with latest state nonce for all senders. use std::ops::Deref; use std::cmp::Ordering; @@ -258,16 +258,19 @@ struct VerifiedTransaction { transaction: SignedTransaction, /// Transaction origin. origin: TransactionOrigin, + /// Insertion time + insertion_time: QueuingInstant, /// Delay until specifid block. min_block: Option, } impl VerifiedTransaction { - fn new(transaction: SignedTransaction, origin: TransactionOrigin, min_block: Option) -> Result { + fn new(transaction: SignedTransaction, origin: TransactionOrigin, time: QueuingInstant, min_block: Option) -> Result { transaction.sender()?; Ok(VerifiedTransaction { transaction: transaction, origin: origin, + insertion_time: time, min_block: min_block, }) } @@ -283,6 +286,10 @@ impl VerifiedTransaction { fn sender(&self) -> Address { self.transaction.sender().expect("Sender is verified in new; qed") } + + fn cost(&self) -> U256 { + self.transaction.value + self.transaction.gas_price * self.transaction.gas + } } #[derive(Debug, Default)] @@ -488,6 +495,10 @@ pub enum PrioritizationStrategy { GasFactorAndGasPrice, } +/// Point in time when transaction was inserted. +pub type QueuingInstant = BlockNumber; +const DEFAULT_QUEUING_PERIOD: BlockNumber = 128; + /// `TransactionQueue` implementation pub struct TransactionQueue { /// Prioritization strategy for this queue @@ -498,6 +509,10 @@ pub struct TransactionQueue { tx_gas_limit: U256, /// Current gas limit (block gas limit * factor). Transactions above the limit will not be accepted (default to !0) gas_limit: U256, + /// Maximal time transaction may occupy the queue. + /// When we reach `max_time_in_queue / 2^3` we re-validate + /// account balance. + max_time_in_queue: QueuingInstant, /// Priority queue for transactions that can go to block current: TransactionSet, /// Priority queue for transactions that has been received but are not yet valid to go to block @@ -545,6 +560,7 @@ impl TransactionQueue { minimal_gas_price: U256::zero(), tx_gas_limit: tx_gas_limit, gas_limit: !U256::zero(), + max_time_in_queue: DEFAULT_QUEUING_PERIOD, current: current, future: future, by_hash: HashMap::new(), @@ -624,6 +640,7 @@ impl TransactionQueue { &mut self, tx: SignedTransaction, origin: TransactionOrigin, + time: QueuingInstant, min_block: Option, fetch_account: &F, gas_estimator: &G, @@ -635,7 +652,7 @@ impl TransactionQueue { let hash = tx.hash(); let cloned_tx = tx.clone(); - let result = self.add_internal(tx, origin, min_block, fetch_account, gas_estimator); + let result = self.add_internal(tx, origin, time, min_block, fetch_account, gas_estimator); match result { Ok(TransactionImportResult::Current) => { self.local_transactions.mark_pending(hash); @@ -656,7 +673,7 @@ impl TransactionQueue { } result } else { - self.add_internal(tx, origin, min_block, fetch_account, gas_estimator) + self.add_internal(tx, origin, time, min_block, fetch_account, gas_estimator) } } @@ -665,6 +682,7 @@ impl TransactionQueue { &mut self, tx: SignedTransaction, origin: TransactionOrigin, + time: QueuingInstant, min_block: Option, fetch_account: &F, gas_estimator: &G, @@ -734,10 +752,10 @@ impl TransactionQueue { // Verify signature tx.check_low_s()?; - let vtx = VerifiedTransaction::new(tx, origin, min_block)?; + let vtx = VerifiedTransaction::new(tx, origin, time, min_block)?; let client_account = fetch_account(&vtx.sender()); - let cost = vtx.transaction.value + vtx.transaction.gas_price * vtx.transaction.gas; + let cost = vtx.cost(); if client_account.balance < cost { trace!(target: "txqueue", "Dropping transaction without sufficient balance: {:?} ({} < {})", @@ -759,7 +777,7 @@ impl TransactionQueue { /// Removes all transactions from particular sender up to (excluding) given client (state) nonce. /// Client (State) Nonce = next valid nonce for this sender. - pub fn remove_all(&mut self, sender: Address, client_nonce: U256) { + pub fn cull(&mut self, sender: Address, client_nonce: U256) { // Check if there is anything in current... let should_check_in_current = self.current.by_address.row(&sender) // If nonce == client_nonce nothing is changed @@ -775,11 +793,11 @@ impl TransactionQueue { return; } - self.remove_all_internal(sender, client_nonce); + self.cull_internal(sender, client_nonce); } /// Always updates future and moves transactions from current to future. - fn remove_all_internal(&mut self, sender: Address, client_nonce: U256) { + fn cull_internal(&mut self, sender: Address, client_nonce: U256) { // We will either move transaction to future or remove it completely // so there will be no transactions from this sender in current self.last_nonces.remove(&sender); @@ -794,16 +812,45 @@ impl TransactionQueue { } /// Checks the current nonce for all transactions' senders in the queue and removes the old transactions. - pub fn remove_old(&mut self, fetch_nonce: F) where - F: Fn(&Address) -> U256, + pub fn remove_old(&mut self, fetch_account: &F, current_time: QueuingInstant) where + F: Fn(&Address) -> AccountDetails, { let senders = self.current.by_address.keys() .chain(self.future.by_address.keys()) - .cloned() - .collect::>(); + .map(|sender| (*sender, fetch_account(sender))) + .collect::>(); - for sender in senders { - self.remove_all(sender, fetch_nonce(&sender)); + for (sender, details) in senders.iter() { + self.cull(*sender, details.nonce); + } + + let max_time = self.max_time_in_queue; + let balance_check = max_time >> 3; + // Clear transactions occupying the queue too long + let invalid = self.by_hash.iter() + .map(|(hash, tx)| (hash, tx, current_time.saturating_sub(tx.insertion_time))) + .filter_map(|(hash, tx, time_diff)| { + if time_diff > max_time { + return Some(*hash); + } + + if time_diff > balance_check { + return match senders.get(&tx.sender()) { + Some(details) if tx.cost() > details.balance => { + Some(*hash) + }, + _ => None, + }; + } + + None + }) + .collect::>(); + let fetch_nonce = |a: &Address| senders.get(a) + .expect("We fetch details for all senders from both current and future") + .nonce; + for hash in invalid { + self.remove_invalid(&hash, &fetch_nonce); } } @@ -851,8 +898,8 @@ impl TransactionQueue { /// so transactions left in queue are processed according to client nonce. /// /// If gap is introduced marks subsequent transactions as future - pub fn remove_invalid(&mut self, transaction_hash: &H256, fetch_account: &T) - where T: Fn(&Address) -> AccountDetails { + pub fn remove_invalid(&mut self, transaction_hash: &H256, fetch_nonce: &T) + where T: Fn(&Address) -> U256 { assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len()); let transaction = self.by_hash.remove(transaction_hash); @@ -864,7 +911,7 @@ impl TransactionQueue { let transaction = transaction.expect("None is tested in early-exit condition above; qed"); let sender = transaction.sender(); let nonce = transaction.nonce(); - let current_nonce = fetch_account(&sender).nonce; + let current_nonce = fetch_nonce(&sender); trace!(target: "txqueue", "Removing invalid transaction: {:?}", transaction.hash()); @@ -889,7 +936,7 @@ impl TransactionQueue { if order.is_some() { // This will keep consistency in queue // Moves all to future and then promotes a batch from current: - self.remove_all_internal(sender, current_nonce); + self.cull_internal(sender, current_nonce); assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len()); return; } @@ -1039,7 +1086,7 @@ impl TransactionQueue { /// Finds transaction in the queue by hash (if any) pub fn find(&self, hash: &H256) -> Option { - match self.by_hash.get(hash) { Some(transaction_ref) => Some(transaction_ref.transaction.clone()), None => None } + self.by_hash.get(hash).map(|tx| tx.transaction.clone()) } /// Removes all elements (in any state) from the queue @@ -1388,14 +1435,14 @@ mod test { let (tx1, tx2) = new_tx_pair(123.into(), 1.into(), 1.into(), 0.into()); let sender = tx1.sender().unwrap(); let nonce = tx1.nonce; - txq.add(tx1.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().pending, 2); assert_eq!(txq.last_nonce(&sender), Some(nonce + 1.into())); // when let tx = new_tx(123.into(), 1.into()); - let res = txq.add(tx.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator); + let res = txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); // then // No longer the case as we don't even consider a transaction that isn't above a full @@ -1427,12 +1474,12 @@ mod test { gas_limit: !U256::zero(), }; let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); - let tx1 = VerifiedTransaction::new(tx1, TransactionOrigin::External, None).unwrap(); - let tx2 = VerifiedTransaction::new(tx2, TransactionOrigin::External, None).unwrap(); + let tx1 = VerifiedTransaction::new(tx1, TransactionOrigin::External, 0, None).unwrap(); + let tx2 = VerifiedTransaction::new(tx2, TransactionOrigin::External, 0, None).unwrap(); let mut by_hash = { let mut x = HashMap::new(); - let tx1 = VerifiedTransaction::new(tx1.transaction.clone(), TransactionOrigin::External, None).unwrap(); - let tx2 = VerifiedTransaction::new(tx2.transaction.clone(), TransactionOrigin::External, None).unwrap(); + let tx1 = VerifiedTransaction::new(tx1.transaction.clone(), TransactionOrigin::External, 0, None).unwrap(); + let tx2 = VerifiedTransaction::new(tx2.transaction.clone(), TransactionOrigin::External, 0, None).unwrap(); x.insert(tx1.hash(), tx1); x.insert(tx2.hash(), tx2); x @@ -1470,12 +1517,12 @@ mod test { // Create two transactions with same nonce // (same hash) let (tx1, tx2) = new_tx_pair_default(0.into(), 0.into()); - let tx1 = VerifiedTransaction::new(tx1, TransactionOrigin::External, None).unwrap(); - let tx2 = VerifiedTransaction::new(tx2, TransactionOrigin::External, None).unwrap(); + let tx1 = VerifiedTransaction::new(tx1, TransactionOrigin::External, 0, None).unwrap(); + let tx2 = VerifiedTransaction::new(tx2, TransactionOrigin::External, 0, None).unwrap(); let by_hash = { let mut x = HashMap::new(); - let tx1 = VerifiedTransaction::new(tx1.transaction.clone(), TransactionOrigin::External, None).unwrap(); - let tx2 = VerifiedTransaction::new(tx2.transaction.clone(), TransactionOrigin::External, None).unwrap(); + let tx1 = VerifiedTransaction::new(tx1.transaction.clone(), TransactionOrigin::External, 0, None).unwrap(); + let tx2 = VerifiedTransaction::new(tx2.transaction.clone(), TransactionOrigin::External, 0, None).unwrap(); x.insert(tx1.hash(), tx1); x.insert(tx2.hash(), tx2); x @@ -1517,10 +1564,10 @@ mod test { gas_limit: !U256::zero(), }; let tx = new_tx_default(); - let tx1 = VerifiedTransaction::new(tx.clone(), TransactionOrigin::External, None).unwrap(); + let tx1 = VerifiedTransaction::new(tx.clone(), TransactionOrigin::External, 0, None).unwrap(); let order1 = TransactionOrder::for_transaction(&tx1, 0.into(), 1.into(), PrioritizationStrategy::GasPriceOnly); assert!(set.insert(tx1.sender(), tx1.nonce(), order1).is_none()); - let tx2 = VerifiedTransaction::new(tx, TransactionOrigin::External, None).unwrap(); + let tx2 = VerifiedTransaction::new(tx, TransactionOrigin::External, 0, None).unwrap(); let order2 = TransactionOrder::for_transaction(&tx2, 0.into(), 1.into(), PrioritizationStrategy::GasPriceOnly); assert!(set.insert(tx2.sender(), tx2.nonce(), order2).is_some()); } @@ -1537,7 +1584,7 @@ mod test { assert_eq!(set.gas_price_entry_limit(), 0.into()); let tx = new_tx_default(); - let tx1 = VerifiedTransaction::new(tx.clone(), TransactionOrigin::External, None).unwrap(); + let tx1 = VerifiedTransaction::new(tx.clone(), TransactionOrigin::External, 0, None).unwrap(); let order1 = TransactionOrder::for_transaction(&tx1, 0.into(), 1.into(), PrioritizationStrategy::GasPriceOnly); assert!(set.insert(tx1.sender(), tx1.nonce(), order1.clone()).is_none()); assert_eq!(set.gas_price_entry_limit(), 2.into()); @@ -1552,12 +1599,12 @@ mod test { !U256::zero() }; // First insert one transaction to future - let res = txq.add(tx, TransactionOrigin::External, None, &prev_nonce, &gas_estimator); + let res = txq.add(tx, TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator); assert_eq!(res.unwrap(), TransactionImportResult::Future); assert_eq!(txq.status().future, 1); // now import second transaction to current - let res = txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator); + let res = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); // and then there should be only one transaction in current (the one with higher gas_price) assert_eq!(res.unwrap(), TransactionImportResult::Current); @@ -1577,12 +1624,12 @@ mod test { !U256::zero() }; // First insert one transaction to future - let res = txq.add(tx.clone(), TransactionOrigin::External, None, &prev_nonce, &gas_estimator); + let res = txq.add(tx.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator); assert_eq!(res.unwrap(), TransactionImportResult::Future); assert_eq!(txq.status().future, 1); // now import second transaction to current - let res = txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator); + let res = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); // then assert_eq!(res.unwrap(), TransactionImportResult::Current); @@ -1601,7 +1648,7 @@ mod test { let tx = new_tx_default(); // when - let res = txq.add(tx, TransactionOrigin::External, None, &default_account_details, &gas_estimator); + let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); // then assert_eq!(res.unwrap(), TransactionImportResult::Current); @@ -1620,10 +1667,10 @@ mod test { txq.set_minimal_gas_price(15.into()); // when - let res1 = txq.add(tx1, TransactionOrigin::External, None, &default_account_details, &gas_estimator); - let res2 = txq.add(tx2, TransactionOrigin::External, None, &default_account_details, &gas_estimator); - let res3 = txq.add(tx3, TransactionOrigin::External, None, &default_account_details, &gas_estimator); - let res4 = txq.add(tx4, TransactionOrigin::External, None, &default_account_details, &gas_estimator); + let res1 = txq.add(tx1, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); + let res2 = txq.add(tx2, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); + let res3 = txq.add(tx3, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); + let res4 = txq.add(tx4, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); // then assert_eq!(res1.unwrap(), TransactionImportResult::Current); @@ -1654,10 +1701,10 @@ mod test { txq.set_minimal_gas_price(15.into()); // when - let res1 = txq.add(tx1, TransactionOrigin::External, None, &default_account_details, &gas_estimator); - let res2 = txq.add(tx2, TransactionOrigin::External, None, &default_account_details, &gas_estimator); - let res3 = txq.add(tx3, TransactionOrigin::External, None, &default_account_details, &gas_estimator); - let res4 = txq.add(tx4, TransactionOrigin::External, None, &default_account_details, &gas_estimator); + let res1 = txq.add(tx1, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); + let res2 = txq.add(tx2, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); + let res3 = txq.add(tx3, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); + let res4 = txq.add(tx4, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); // then assert_eq!(res1.unwrap(), TransactionImportResult::Current); @@ -1700,7 +1747,7 @@ mod test { txq.set_gas_limit(limit); // when - let res = txq.add(tx, TransactionOrigin::External, None, &default_account_details, &gas_estimator); + let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); // then assert_eq!(unwrap_tx_err(res), TransactionError::GasLimitExceeded { @@ -1724,7 +1771,7 @@ mod test { }; // when - let res = txq.add(tx, TransactionOrigin::External, None, &account, &gas_estimator); + let res = txq.add(tx, TransactionOrigin::External, 0, None, &account, &gas_estimator); // then assert_eq!(unwrap_tx_err(res), TransactionError::InsufficientBalance { @@ -1744,7 +1791,7 @@ mod test { txq.set_minimal_gas_price(tx.gas_price + U256::one()); // when - let res = txq.add(tx, TransactionOrigin::External, None, &default_account_details, &gas_estimator); + let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); // then assert_eq!(unwrap_tx_err(res), TransactionError::InsufficientGasPrice { @@ -1764,7 +1811,7 @@ mod test { txq.set_minimal_gas_price(tx.gas_price + U256::one()); // when - let res = txq.add(tx, TransactionOrigin::Local, None, &default_account_details, &gas_estimator); + let res = txq.add(tx, TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator); // then assert_eq!(res.unwrap(), TransactionImportResult::Current); @@ -1794,7 +1841,7 @@ mod test { rlp::decode(s.as_raw()) }; // when - let res = txq.add(stx, TransactionOrigin::External, None, &default_account_details, &gas_estimator); + let res = txq.add(stx, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); // then assert!(res.is_err()); @@ -1808,8 +1855,8 @@ mod test { let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); // when - txq.add(tx.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); // then let top = txq.top_transactions(); @@ -1828,9 +1875,9 @@ mod test { // when // first insert the one with higher gas price - txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); // then the one with lower gas price, but local - txq.add(tx.clone(), TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); // then let top = txq.top_transactions(); @@ -1847,15 +1894,15 @@ mod test { // the second one has same nonce but higher `gas_price` let (_, tx0) = new_similar_tx_pair(); - txq.add(tx0.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx1.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx0.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); // the one with higher gas price is first assert_eq!(txq.top_transactions()[0], tx0); assert_eq!(txq.top_transactions()[1], tx1); // when // insert second as local - txq.add(tx2.clone(), TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); // then // the order should be updated @@ -1874,9 +1921,9 @@ mod test { // when // first insert local one with higher gas price - txq.add(tx2.clone(), TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); // then the one with lower gas price, but from retracted block - txq.add(tx.clone(), TransactionOrigin::RetractedBlock, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx.clone(), TransactionOrigin::RetractedBlock, 0, None, &default_account_details, &gas_estimator).unwrap(); // then let top = txq.top_transactions(); @@ -1892,8 +1939,8 @@ mod test { let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); // when - txq.add(tx.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); // then let top = txq.top_transactions(); @@ -1912,10 +1959,10 @@ mod test { let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into()); // insert everything - txq.add(txa.clone(), TransactionOrigin::External, None, &prev_nonce, &gas_estimator).unwrap(); - txq.add(txb.clone(), TransactionOrigin::External, None, &prev_nonce, &gas_estimator).unwrap(); - txq.add(tx1.clone(), TransactionOrigin::External, None, &prev_nonce, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, None, &prev_nonce, &gas_estimator).unwrap(); + txq.add(txa.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap(); + txq.add(txb.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap(); assert_eq!(txq.status().future, 4); @@ -1940,10 +1987,10 @@ mod test { let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into()); // insert everything - txq.add(txa.clone(), TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(txb.clone(), TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx1.clone(), TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(txa.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(txb.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); let top = txq.top_transactions(); assert_eq!(top[0], tx1); @@ -1973,10 +2020,10 @@ mod test { let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into()); // insert everything - txq.add(txa.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(txb.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx1.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(txa.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(txb.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); let top = txq.top_transactions(); assert_eq!(top[0], tx1); @@ -2005,8 +2052,8 @@ mod test { let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); // when - txq.add(tx.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); // then let top = txq.pending_hashes(); @@ -2023,8 +2070,8 @@ mod test { let (tx, tx2) = new_tx_pair_default(2.into(), 0.into()); // when - let res1 = txq.add(tx.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); - let res2 = txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + let res1 = txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + let res2 = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); // then assert_eq!(res1, TransactionImportResult::Current); @@ -2045,8 +2092,8 @@ mod test { let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); // when - let res1 = txq.add(tx.clone(), TransactionOrigin::External, Some(1), &default_account_details, &gas_estimator).unwrap(); - let res2 = txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + let res1 = txq.add(tx.clone(), TransactionOrigin::External, 0, Some(1), &default_account_details, &gas_estimator).unwrap(); + let res2 = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); // then assert_eq!(res1, TransactionImportResult::Current); @@ -2067,12 +2114,12 @@ mod test { let mut txq = TransactionQueue::default(); let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); - txq.add(tx.clone(), TransactionOrigin::External, None, &prev_nonce, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, None, &prev_nonce, &gas_estimator).unwrap(); + txq.add(tx.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap(); assert_eq!(txq.status().future, 2); // when - txq.remove_all(tx.sender().unwrap(), next2_nonce); + txq.cull(tx.sender().unwrap(), next2_nonce); // should remove both transactions since they are not valid // then @@ -2090,13 +2137,13 @@ mod test { 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, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().pending, 1); - txq.add(tx2, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().future, 1); // when - txq.add(tx1, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx1, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); // then let stats = txq.status(); @@ -2112,14 +2159,14 @@ mod test { // given let mut txq2 = TransactionQueue::default(); let (tx, tx2) = new_tx_pair_default(3.into(), 0.into()); - txq2.add(tx.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); - txq2.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + txq2.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq2.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq2.status().pending, 1); assert_eq!(txq2.status().future, 1); // when - txq2.remove_all(tx.sender().unwrap(), tx.nonce + U256::one()); - txq2.remove_all(tx2.sender().unwrap(), tx2.nonce + U256::one()); + txq2.cull(tx.sender().unwrap(), tx.nonce + U256::one()); + txq2.cull(tx2.sender().unwrap(), tx2.nonce + U256::one()); // then @@ -2134,14 +2181,14 @@ mod test { let mut txq = TransactionQueue::default(); let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); let tx3 = new_tx_default(); - txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().future, 1); - txq.add(tx3.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx3.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().pending, 3); // when - txq.remove_invalid(&tx.hash(), &default_account_details); + txq.remove_invalid(&tx.hash(), &|_| default_nonce()); // then let stats = txq.status(); @@ -2156,8 +2203,8 @@ mod test { let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); // add - txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); let stats = txq.status(); assert_eq!(stats.pending, 2); @@ -2176,11 +2223,11 @@ mod test { let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); let sender = tx.sender().unwrap(); let nonce = tx.nonce; - txq.add(tx.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().pending, 1); // when - let res = txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator); + let res = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); // then let t = txq.top_transactions(); @@ -2197,14 +2244,14 @@ mod test { txq.current.set_limit(10); let (tx1, tx2) = new_tx_pair_default(4.into(), 1.into()); let (tx3, tx4) = new_tx_pair_default(4.into(), 2.into()); - txq.add(tx1.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx3.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx3.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().pending, 2); // when - txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().future, 1); - txq.add(tx4.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx4.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); // then assert_eq!(txq.status().future, 1); @@ -2215,11 +2262,11 @@ mod test { let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 100, default_gas_val() * U256::from(2), !U256::zero()); let (tx1, tx2) = new_tx_pair_default(U256::from(1), U256::from(1)); let (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2)); - txq.add(tx1.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx3.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx3.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); // limited by gas - txq.add(tx4.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap_err(); + txq.add(tx4.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap_err(); assert_eq!(txq.status().pending, 2); } @@ -2229,12 +2276,12 @@ mod test { let (tx1, tx2) = new_tx_pair_default(U256::from(1), U256::from(1)); let (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2)); let (tx5, _) = new_tx_pair_default(U256::from(1), U256::from(2)); - txq.add(tx1.clone(), TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); // Not accepted because of limit - txq.add(tx5.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap_err(); - txq.add(tx3.clone(), TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx4.clone(), TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx5.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap_err(); + txq.add(tx3.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx4.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().pending, 4); } @@ -2246,7 +2293,7 @@ mod test { let fetch_last_nonce = |_a: &Address| AccountDetails { nonce: last_nonce, balance: !U256::zero() }; // when - let res = txq.add(tx, TransactionOrigin::External, None, &fetch_last_nonce, &gas_estimator); + let res = txq.add(tx, TransactionOrigin::External, 0, None, &fetch_last_nonce, &gas_estimator); // then assert_eq!(unwrap_tx_err(res), TransactionError::Old); @@ -2262,12 +2309,12 @@ mod test { balance: !U256::zero() }; let mut txq = TransactionQueue::default(); let (_tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); - txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().future, 1); assert_eq!(txq.status().pending, 0); // when - let res = txq.add(tx2.clone(), TransactionOrigin::External, None, &nonce, &gas_estimator); + let res = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &nonce, &gas_estimator); // then assert_eq!(unwrap_tx_err(res), TransactionError::AlreadyImported); @@ -2281,15 +2328,15 @@ mod test { // given let mut txq = TransactionQueue::default(); let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); - txq.add(tx1.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().pending, 2); // when - txq.remove_invalid(&tx1.hash(), &default_account_details); + txq.remove_invalid(&tx1.hash(), &|_| default_nonce()); assert_eq!(txq.status().pending, 0); assert_eq!(txq.status().future, 1); - txq.add(tx1.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); // then let stats = txq.status(); @@ -2303,15 +2350,15 @@ mod test { let mut txq = TransactionQueue::default(); let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); let tx3 = new_tx_default(); - txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().future, 1); - txq.add(tx3.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx3.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().pending, 3); // when let sender = tx.sender().unwrap(); - txq.remove_all(sender, default_nonce() + U256::one()); + txq.cull(sender, default_nonce() + U256::one()); // then let stats = txq.status(); @@ -2333,8 +2380,8 @@ mod test { }; // when - txq.add(tx, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); // then let stats = txq.status(); @@ -2361,10 +2408,10 @@ mod test { }; // when - txq.add(tx1, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx1, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().future, 1); - txq.add(tx0, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx0, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); // then let stats = txq.status(); @@ -2376,18 +2423,16 @@ mod test { #[test] fn should_recalculate_height_when_removing_from_future() { // given - let previous_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce - U256::one(), balance: - !U256::zero() }; - let next_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce + U256::one(), balance: - !U256::zero() }; + let previous_nonce = |a: &Address| + AccountDetails { nonce: default_account_details(a).nonce - U256::one(), balance: !U256::zero() }; let mut txq = TransactionQueue::default(); let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); - txq.add(tx1.clone(), TransactionOrigin::External, None, &previous_nonce, &gas_estimator).unwrap(); - txq.add(tx2, TransactionOrigin::External, None, &previous_nonce, &gas_estimator).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &previous_nonce, &gas_estimator).unwrap(); + txq.add(tx2, TransactionOrigin::External, 0, None, &previous_nonce, &gas_estimator).unwrap(); assert_eq!(txq.status().future, 2); // when - txq.remove_invalid(&tx1.hash(), &next_nonce); + txq.remove_invalid(&tx1.hash(), &|_| default_nonce() + 1.into()); // then let stats = txq.status(); @@ -2414,7 +2459,7 @@ mod test { let details = |_a: &Address| AccountDetails { nonce: nonce, balance: !U256::zero() }; // when - txq.add(tx, TransactionOrigin::External, None, &details, &gas_estimator).unwrap(); + txq.add(tx, TransactionOrigin::External, 0, None, &details, &gas_estimator).unwrap(); // then assert_eq!(txq.last_nonce(&from), Some(nonce)); @@ -2429,17 +2474,17 @@ mod test { let details1 = |_a: &Address| AccountDetails { nonce: nonce1, balance: !U256::zero() }; // Insert first transaction - txq.add(tx1, TransactionOrigin::External, None, &details1, &gas_estimator).unwrap(); + txq.add(tx1, TransactionOrigin::External, 0, None, &details1, &gas_estimator).unwrap(); // when - txq.remove_all(tx2.sender().unwrap(), nonce2 + U256::one()); + txq.cull(tx2.sender().unwrap(), nonce2 + U256::one()); // then assert!(txq.top_transactions().is_empty()); } #[test] - fn should_return_valid_last_nonce_after_remove_all() { + fn should_return_valid_last_nonce_after_cull() { // given let mut txq = TransactionQueue::default(); let (tx1, tx2) = new_tx_pair_default(4.into(), 0.into()); @@ -2449,11 +2494,11 @@ mod test { // when // Insert first transaction - assert_eq!(txq.add(tx1, TransactionOrigin::External, None, &details1, &gas_estimator).unwrap(), TransactionImportResult::Current); + assert_eq!(txq.add(tx1, TransactionOrigin::External, 0, None, &details1, &gas_estimator).unwrap(), TransactionImportResult::Current); // Second should go to future - assert_eq!(txq.add(tx2, TransactionOrigin::External, None, &details1, &gas_estimator).unwrap(), TransactionImportResult::Future); + assert_eq!(txq.add(tx2, TransactionOrigin::External, 0, None, &details1, &gas_estimator).unwrap(), TransactionImportResult::Future); // Now block is imported - txq.remove_all(sender, nonce2 - U256::from(1)); + txq.cull(sender, nonce2 - U256::from(1)); // tx2 should be not be promoted to current assert_eq!(txq.status().pending, 0); assert_eq!(txq.status().future, 1); @@ -2470,9 +2515,9 @@ mod test { assert_eq!(txq.has_local_pending_transactions(), false); // when - assert_eq!(txq.add(tx1, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(), TransactionImportResult::Current); + assert_eq!(txq.add(tx1, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(), TransactionImportResult::Current); assert_eq!(txq.has_local_pending_transactions(), false); - assert_eq!(txq.add(tx2, TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap(), TransactionImportResult::Current); + assert_eq!(txq.add(tx2, TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(), TransactionImportResult::Current); // then assert_eq!(txq.has_local_pending_transactions(), true); @@ -2487,8 +2532,8 @@ mod test { default_account_details(a).balance }; // when - assert_eq!(txq.add(tx2, TransactionOrigin::External, None, &prev_nonce, &gas_estimator).unwrap(), TransactionImportResult::Future); - assert_eq!(txq.add(tx1.clone(), TransactionOrigin::External, None, &prev_nonce, &gas_estimator).unwrap(), TransactionImportResult::Future); + assert_eq!(txq.add(tx2, TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap(), TransactionImportResult::Future); + assert_eq!(txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap(), TransactionImportResult::Future); // then assert_eq!(txq.future.by_priority.len(), 1); @@ -2513,14 +2558,14 @@ mod test { (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, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2, TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx3, TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx1, TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2, TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx3, TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.future.by_priority.len(), 0); assert_eq!(txq.current.by_priority.len(), 3); // when - let res = txq.add(tx2_2, TransactionOrigin::Local, None, &default_account_details, &gas_estimator); + let res = txq.add(tx2_2, TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator); // then assert_eq!(txq.last_nonce(&sender).unwrap(), 125.into()); @@ -2536,8 +2581,8 @@ mod test { let high_gas = |_: &SignedTransaction| 100_001.into(); // when - let res1 = txq.add(tx1, TransactionOrigin::Local, None, &default_account_details, &gas_estimator); - let res2 = txq.add(tx2, TransactionOrigin::Local, None, &default_account_details, &high_gas); + let res1 = txq.add(tx1, TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator); + let res2 = txq.add(tx2, TransactionOrigin::Local, 0, None, &default_account_details, &high_gas); // then assert_eq!(res1.unwrap(), TransactionImportResult::Current); @@ -2554,20 +2599,45 @@ mod test { let mut txq = TransactionQueue::default(); let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); let (tx3, tx4) = new_tx_pair_default(1.into(), 0.into()); - let nonce1 = tx1.nonce; + let next_nonce = |_: &Address| + AccountDetails { nonce: default_nonce() + U256::one(), balance: !U256::zero() }; // Insert all transactions - txq.add(tx1, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx3, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx4, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx1, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx3, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx4, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.top_transactions().len(), 4); // when - txq.remove_old(|_| nonce1 + U256::one()); + txq.remove_old(&next_nonce, 0); // then assert_eq!(txq.top_transactions().len(), 2); } + #[test] + fn should_remove_out_of_date_transactions_occupying_queue() { + // given + let mut txq = TransactionQueue::default(); + let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); + let (tx3, tx4) = new_tx_pair_default(2.into(), 0.into()); + + // Insert all transactions + txq.add(tx1, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2, TransactionOrigin::External, 5, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx3.clone(), TransactionOrigin::External, 10, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx4, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + assert_eq!(txq.top_transactions().len(), 3); + assert_eq!(txq.future_transactions().len(), 1); + + // when + txq.remove_old(&default_account_details, 9 + super::DEFAULT_QUEUING_PERIOD); + + // then + assert_eq!(txq.top_transactions().len(), 1); + assert_eq!(txq.future_transactions().len(), 0); + assert_eq!(txq.top_transactions(), vec![tx3]); + } + } diff --git a/js/package.json b/js/package.json index 79e8d473e..abff29f4b 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.2.168", + "version": "0.2.172", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", diff --git a/js/src/api/contract/contract.js b/js/src/api/contract/contract.js index af22191e5..70853749d 100644 --- a/js/src/api/contract/contract.js +++ b/js/src/api/contract/contract.js @@ -218,14 +218,19 @@ export default class Contract { } _encodeOptions (func, options, values) { - options.data = this.getCallData(func, options, values); - return options; + const data = this.getCallData(func, options, values); + + return { + ...options, + data + }; } _addOptionsTo (options = {}) { - return Object.assign({ - to: this._address - }, options); + return { + to: this._address, + ...options + }; } _bindFunction = (func) => { diff --git a/js/src/api/contract/contract.spec.js b/js/src/api/contract/contract.spec.js index 87a7cf558..0258aea58 100644 --- a/js/src/api/contract/contract.spec.js +++ b/js/src/api/contract/contract.spec.js @@ -31,6 +31,7 @@ const eth = new Api(transport); describe('api/contract/Contract', () => { const ADDR = '0x0123456789'; + const ABI = [ { type: 'function', name: 'test', @@ -41,12 +42,42 @@ describe('api/contract/Contract', () => { type: 'function', name: 'test2', outputs: [{ type: 'uint' }, { type: 'uint' }] }, - { type: 'constructor' }, + { + type: 'constructor', + inputs: [{ name: 'boolin', type: 'bool' }, { name: 'stringin', type: 'string' }] + }, { type: 'event', name: 'baz' }, { type: 'event', name: 'foo' } ]; - const VALUES = [true, 'jacogr']; - const ENCODED = '0x023562050000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000066a61636f67720000000000000000000000000000000000000000000000000000'; + + const ABI_NO_PARAMS = [ + { + type: 'function', name: 'test', + inputs: [{ name: 'boolin', type: 'bool' }, { name: 'stringin', type: 'string' }], + outputs: [{ type: 'uint' }] + }, + { + type: 'function', name: 'test2', + outputs: [{ type: 'uint' }, { type: 'uint' }] + }, + { + type: 'constructor' + }, + { type: 'event', name: 'baz' }, + { type: 'event', name: 'foo' } + ]; + + const VALUES = [ true, 'jacogr' ]; + const CALLDATA = ` + 0000000000000000000000000000000000000000000000000000000000000001 + 0000000000000000000000000000000000000000000000000000000000000040 + 0000000000000000000000000000000000000000000000000000000000000006 + 6a61636f67720000000000000000000000000000000000000000000000000000 + `.replace(/\s/g, ''); + const SIGNATURE = '02356205'; + + const ENCODED = `0x${SIGNATURE}${CALLDATA}`; + const RETURN1 = '0000000000000000000000000000000000000000000000000000000000123456'; const RETURN2 = '0000000000000000000000000000000000000000000000000000000000456789'; let scope; @@ -230,6 +261,33 @@ describe('api/contract/Contract', () => { }); }); + describe('deploy without parameters', () => { + const contract = new Contract(eth, ABI_NO_PARAMS); + const CODE = '0x123'; + const ADDRESS = '0xD337e80eEdBdf86eDBba021797d7e4e00Bb78351'; + const RECEIPT_DONE = { contractAddress: ADDRESS.toLowerCase(), gasUsed: 50, blockNumber: 2500 }; + + let scope; + + describe('success', () => { + before(() => { + scope = mockHttp([ + { method: 'eth_estimateGas', reply: { result: 1000 } }, + { method: 'parity_postTransaction', reply: { result: '0x678' } }, + { method: 'parity_checkRequest', reply: { result: '0x890' } }, + { method: 'eth_getTransactionReceipt', reply: { result: RECEIPT_DONE } }, + { method: 'eth_getCode', reply: { result: CODE } } + ]); + + return contract.deploy({ data: CODE }, []); + }); + + it('passes the options through to postTransaction (incl. gas calculation)', () => { + expect(scope.body.parity_postTransaction.params[0].data).to.equal(CODE); + }); + }); + }); + describe('deploy', () => { const contract = new Contract(eth, ABI); const ADDRESS = '0xD337e80eEdBdf86eDBba021797d7e4e00Bb78351'; @@ -252,7 +310,7 @@ describe('api/contract/Contract', () => { { method: 'eth_getCode', reply: { result: '0x456' } } ]); - return contract.deploy({ data: '0x123' }, []); + return contract.deploy({ data: '0x123' }, VALUES); }); it('calls estimateGas, postTransaction, checkRequest, getTransactionReceipt & getCode in order', () => { @@ -261,7 +319,7 @@ describe('api/contract/Contract', () => { it('passes the options through to postTransaction (incl. gas calculation)', () => { expect(scope.body.parity_postTransaction.params).to.deep.equal([ - { data: '0x123', gas: '0x4b0' } + { data: `0x123${CALLDATA}`, gas: '0x4b0' } ]); }); @@ -280,7 +338,7 @@ describe('api/contract/Contract', () => { ]); return contract - .deploy({ data: '0x123' }, []) + .deploy({ data: '0x123' }, VALUES) .catch((error) => { expect(error.message).to.match(/not deployed, gasUsed/); }); @@ -296,7 +354,7 @@ describe('api/contract/Contract', () => { ]); return contract - .deploy({ data: '0x123' }, []) + .deploy({ data: '0x123' }, VALUES) .catch((error) => { expect(error.message).to.match(/not deployed, getCode/); }); diff --git a/js/src/modals/DeployContract/deployContract.js b/js/src/modals/DeployContract/deployContract.js index 4d9c4f40e..6b09986ad 100644 --- a/js/src/modals/DeployContract/deployContract.js +++ b/js/src/modals/DeployContract/deployContract.js @@ -441,9 +441,7 @@ class DeployContract extends Component { } onCodeChange = (code) => { - const { api } = this.context; - - this.setState(validateCode(code, api), this.estimateGas); + this.setState(validateCode(code), this.estimateGas); } onDeployStart = () => { diff --git a/js/src/modals/Shapeshift/AwaitingDepositStep/awaitingDepositStep.js b/js/src/modals/Shapeshift/AwaitingDepositStep/awaitingDepositStep.js index 8dfd29f33..3644852e4 100644 --- a/js/src/modals/Shapeshift/AwaitingDepositStep/awaitingDepositStep.js +++ b/js/src/modals/Shapeshift/AwaitingDepositStep/awaitingDepositStep.js @@ -14,25 +14,21 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +import { observer } from 'mobx-react'; import React, { Component, PropTypes } from 'react'; +import { FormattedMessage } from 'react-intl'; import Value from '../Value'; - import styles from '../shapeshift.css'; +@observer export default class AwaitingDepositStep extends Component { static propTypes = { - coinSymbol: PropTypes.string.isRequired, - depositAddress: PropTypes.string, - price: PropTypes.shape({ - rate: PropTypes.number.isRequired, - minimum: PropTypes.number.isRequired, - limit: PropTypes.number.isRequired - }).isRequired + store: PropTypes.object.isRequired } render () { - const { coinSymbol, depositAddress, price } = this.props; + const { coinSymbol, depositAddress, price } = this.props.store; const typeSymbol = (
{ coinSymbol } @@ -43,22 +39,38 @@ export default class AwaitingDepositStep extends Component { return (
- Awaiting confirmation of the deposit address for your { typeSymbol } funds exchange +
); } + return (
- ShapeShift.io is awaiting a { typeSymbol } deposit. Send the funds from your { typeSymbol } network client to - + ShapeShift.io, + typeSymbol + } } />
{ depositAddress }
- ( minimum, maximum) + , + minimum: + } } />
diff --git a/js/src/modals/Shapeshift/AwaitingDepositStep/awaitingDepositStep.spec.js b/js/src/modals/Shapeshift/AwaitingDepositStep/awaitingDepositStep.spec.js new file mode 100644 index 000000000..65fdefb07 --- /dev/null +++ b/js/src/modals/Shapeshift/AwaitingDepositStep/awaitingDepositStep.spec.js @@ -0,0 +1,50 @@ +// Copyright 2015, 2016 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +import { shallow } from 'enzyme'; +import React from 'react'; + +import AwaitingDepositStep from './'; + +let component; + +function render () { + component = shallow( + + ); + + return component; +} + +describe('modals/Shapeshift/AwaitingDepositStep', () => { + it('renders defaults', () => { + expect(render()).to.be.ok; + }); + + it('displays waiting for address with empty depositAddress', () => { + render(); + expect(component.find('FormattedMessage').props().id).to.match(/awaitingConfirmation/); + }); + + it('displays waiting for deposit with non-empty depositAddress', () => { + render({ depositAddress: 'xyz' }); + expect(component.find('FormattedMessage').first().props().id).to.match(/awaitingDeposit/); + }); +}); diff --git a/js/src/modals/Shapeshift/AwaitingExchangeStep/awaitingExchangeStep.js b/js/src/modals/Shapeshift/AwaitingExchangeStep/awaitingExchangeStep.js index d3e0ce93c..d3760355f 100644 --- a/js/src/modals/Shapeshift/AwaitingExchangeStep/awaitingExchangeStep.js +++ b/js/src/modals/Shapeshift/AwaitingExchangeStep/awaitingExchangeStep.js @@ -15,32 +15,39 @@ // along with Parity. If not, see . import React, { Component, PropTypes } from 'react'; -import Value from '../Value'; +import { FormattedMessage } from 'react-intl'; +import { observer } from 'mobx-react'; +import Value from '../Value'; import styles from '../shapeshift.css'; +@observer export default class AwaitingExchangeStep extends Component { static propTypes = { - depositInfo: PropTypes.shape({ - incomingCoin: PropTypes.number.isRequired, - incomingType: PropTypes.string.isRequired - }).isRequired + store: PropTypes.object.isRequired } render () { - const { depositInfo } = this.props; + const { depositInfo } = this.props.store; const { incomingCoin, incomingType } = depositInfo; return (
- ShapeShift.io has received a deposit of - + ShapeShift.io + } } />
- Awaiting the completion of the funds exchange and transfer of funds to your Parity account. +
); diff --git a/js/src/modals/Shapeshift/AwaitingExchangeStep/awaitingExchangeStep.spec.js b/js/src/modals/Shapeshift/AwaitingExchangeStep/awaitingExchangeStep.spec.js new file mode 100644 index 000000000..a9ed6e5f2 --- /dev/null +++ b/js/src/modals/Shapeshift/AwaitingExchangeStep/awaitingExchangeStep.spec.js @@ -0,0 +1,39 @@ +// Copyright 2015, 2016 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +import { shallow } from 'enzyme'; +import React from 'react'; + +import AwaitingExchangeStep from './'; + +let component; + +function render () { + component = shallow( + + ); + + return component; +} + +describe('modals/Shapeshift/AwaitingExchangeStep', () => { + it('renders defaults', () => { + expect(render()).to.be.ok; + }); +}); diff --git a/js/src/modals/Shapeshift/CompletedStep/completedStep.js b/js/src/modals/Shapeshift/CompletedStep/completedStep.js index 2b5a6b162..681e26c55 100644 --- a/js/src/modals/Shapeshift/CompletedStep/completedStep.js +++ b/js/src/modals/Shapeshift/CompletedStep/completedStep.js @@ -14,39 +14,41 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +import { observer } from 'mobx-react'; import React, { Component, PropTypes } from 'react'; +import { FormattedMessage } from 'react-intl'; import Value from '../Value'; - import styles from '../shapeshift.css'; +@observer export default class CompletedStep extends Component { static propTypes = { - depositInfo: PropTypes.shape({ - incomingCoin: PropTypes.number.isRequired, - incomingType: PropTypes.string.isRequired - }).isRequired, - exchangeInfo: PropTypes.shape({ - outgoingCoin: PropTypes.string.isRequired, - outgoingType: PropTypes.string.isRequired - }).isRequired + store: PropTypes.object.isRequired } render () { - const { depositInfo, exchangeInfo } = this.props; + const { depositInfo, exchangeInfo } = this.props.store; const { incomingCoin, incomingType } = depositInfo; const { outgoingCoin, outgoingType } = exchangeInfo; return (
- ShapeShift.io has completed the funds exchange. + ShapeShift.io + } } />
=>
- The change in funds will be reflected in your Parity account shortly. +
); diff --git a/js/src/modals/Shapeshift/CompletedStep/completedStep.spec.js b/js/src/modals/Shapeshift/CompletedStep/completedStep.spec.js new file mode 100644 index 000000000..c14da892a --- /dev/null +++ b/js/src/modals/Shapeshift/CompletedStep/completedStep.spec.js @@ -0,0 +1,40 @@ +// Copyright 2015, 2016 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +import { shallow } from 'enzyme'; +import React from 'react'; + +import CompletedStep from './'; + +let component; + +function render () { + component = shallow( + + ); + + return component; +} + +describe('modals/Shapeshift/CompletedStep', () => { + it('renders defaults', () => { + expect(render()).to.be.ok; + }); +}); diff --git a/js/src/modals/Shapeshift/ErrorStep/errorStep.js b/js/src/modals/Shapeshift/ErrorStep/errorStep.js index 092494399..441904f22 100644 --- a/js/src/modals/Shapeshift/ErrorStep/errorStep.js +++ b/js/src/modals/Shapeshift/ErrorStep/errorStep.js @@ -14,25 +14,30 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +import { observer } from 'mobx-react'; import React, { Component, PropTypes } from 'react'; +import { FormattedMessage } from 'react-intl'; import styles from '../shapeshift.css'; +@observer export default class ErrorStep extends Component { static propTypes = { - error: PropTypes.shape({ - fatal: PropTypes.bool, - message: PropTypes.string.isRequired - }).isRequired + store: PropTypes.object.isRequired } render () { - const { error } = this.props; + const { error } = this.props.store; return (
- The funds shifting via ShapeShift.io failed with a fatal error on the exchange. The error message received from the exchange is as follow: + ShapeShift.io + } } />
{ error.message } diff --git a/js/src/modals/Shapeshift/ErrorStep/errorStep.spec.js b/js/src/modals/Shapeshift/ErrorStep/errorStep.spec.js new file mode 100644 index 000000000..7cb148334 --- /dev/null +++ b/js/src/modals/Shapeshift/ErrorStep/errorStep.spec.js @@ -0,0 +1,39 @@ +// Copyright 2015, 2016 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +import { shallow } from 'enzyme'; +import React from 'react'; + +import ErrorStep from './'; + +let component; + +function render () { + component = shallow( + + ); + + return component; +} + +describe('modals/Shapeshift/ErrorStep', () => { + it('renders defaults', () => { + expect(render()).to.be.ok; + }); +}); diff --git a/js/src/modals/Shapeshift/OptionsStep/optionsStep.js b/js/src/modals/Shapeshift/OptionsStep/optionsStep.js index 4314d2b5c..5a7afdf7e 100644 --- a/js/src/modals/Shapeshift/OptionsStep/optionsStep.js +++ b/js/src/modals/Shapeshift/OptionsStep/optionsStep.js @@ -14,64 +14,93 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import React, { Component, PropTypes } from 'react'; import { Checkbox, MenuItem } from 'material-ui'; +import { observer } from 'mobx-react'; +import React, { Component, PropTypes } from 'react'; +import { FormattedMessage } from 'react-intl'; -import { Form, Input, Select } from '~/ui'; +import { Form, Input, Select, Warning } from '~/ui'; import Price from '../Price'; - +import { WARNING_NO_PRICE } from '../store'; import styles from './optionsStep.css'; +const WARNING_LABELS = { + [WARNING_NO_PRICE]: ( + + ) +}; + +@observer export default class OptionsStep extends Component { static propTypes = { - refundAddress: PropTypes.string.isRequired, - coinSymbol: PropTypes.string.isRequired, - coins: PropTypes.array.isRequired, - price: PropTypes.object, - hasAccepted: PropTypes.bool.isRequired, - onChangeSymbol: PropTypes.func.isRequired, - onChangeRefund: PropTypes.func.isRequired, - onToggleAccept: PropTypes.func.isRequired + store: PropTypes.object.isRequired }; render () { - const { coinSymbol, coins, refundAddress, hasAccepted, onToggleAccept } = this.props; - const label = `(optional) ${coinSymbol} return address`; + const { coinSymbol, coins, hasAcceptedTerms, price, refundAddress, warning } = this.props.store; if (!coins.length) { return (
- There are currently no exchange pairs/coins available to fund with. +
); } - const items = coins.map(this.renderCoinSelectItem); - return (
+ hint={ + + } + label={ + + } + onSubmit={ this.onChangeRefundAddress } + value={ refundAddress } /> + label={ + + } + onCheck={ this.onToggleAcceptTerms } /> - + +
); } @@ -81,7 +110,9 @@ export default class OptionsStep extends Component { const item = (
- +
{ symbol } @@ -103,11 +134,15 @@ export default class OptionsStep extends Component { ); } - onSelectCoin = (event, idx, value) => { - this.props.onChangeSymbol(event, value); + onChangeRefundAddress = (event, refundAddress) => { + this.props.store.setRefundAddress(refundAddress); } - onChangeAddress = (event, value) => { - this.props.onChangeRefund(value); + onSelectCoin = (event, index, coinSymbol) => { + this.props.store.setCoinSymbol(coinSymbol); + } + + onToggleAcceptTerms = () => { + this.props.store.toggleAcceptTerms(); } } diff --git a/js/src/modals/Shapeshift/OptionsStep/optionsSteps.spec.js b/js/src/modals/Shapeshift/OptionsStep/optionsSteps.spec.js new file mode 100644 index 000000000..3b49d90de --- /dev/null +++ b/js/src/modals/Shapeshift/OptionsStep/optionsSteps.spec.js @@ -0,0 +1,126 @@ +// Copyright 2015, 2016 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +import { shallow } from 'enzyme'; +import React from 'react'; +import sinon from 'sinon'; + +import Store, { WARNING_NO_PRICE } from '../store'; + +import OptionsStep from './'; + +const ADDRESS = '0x1234567890123456789012345678901234567890'; + +let component; +let instance; +let store; + +function render () { + store = new Store(ADDRESS); + component = shallow( + + ); + instance = component.instance(); + + return component; +} + +describe('modals/Shapeshift/OptionsStep', () => { + beforeEach(() => { + render(); + }); + + it('renders defaults', () => { + expect(component).to.be.ok; + }); + + it('renders no coins when none available', () => { + expect(component.find('FormattedMessage').props().id).to.equal('shapeshift.optionsStep.noPairs'); + }); + + describe('components', () => { + beforeEach(() => { + store.setCoins([{ symbol: 'BTC', name: 'Bitcoin' }]); + store.toggleAcceptTerms(); + }); + + describe('terms Checkbox', () => { + it('shows the state of store.hasAcceptedTerms', () => { + expect(component.find('Checkbox').props().checked).to.be.true; + }); + }); + + describe('warning', () => { + let warning; + + beforeEach(() => { + store.setWarning(WARNING_NO_PRICE); + warning = component.find('Warning'); + }); + + it('shows a warning message when available', () => { + expect(warning.props().warning.props.id).to.equal('shapeshift.warning.noPrice'); + }); + }); + }); + + describe('events', () => { + describe('onChangeRefundAddress', () => { + beforeEach(() => { + sinon.stub(store, 'setRefundAddress'); + }); + + afterEach(() => { + store.setRefundAddress.restore(); + }); + + it('sets the refundAddress on the store', () => { + instance.onChangeRefundAddress(null, 'refundAddress'); + expect(store.setRefundAddress).to.have.been.calledWith('refundAddress'); + }); + }); + + describe('onSelectCoin', () => { + beforeEach(() => { + sinon.stub(store, 'setCoinSymbol'); + }); + + afterEach(() => { + store.setCoinSymbol.restore(); + }); + + it('sets the coinSymbol on the store', () => { + instance.onSelectCoin(null, 0, 'XMR'); + expect(store.setCoinSymbol).to.have.been.calledWith('XMR'); + }); + }); + + describe('onToggleAcceptTerms', () => { + beforeEach(() => { + sinon.stub(store, 'toggleAcceptTerms'); + }); + + afterEach(() => { + store.toggleAcceptTerms.restore(); + }); + + it('toggles the terms on the store', () => { + instance.onToggleAcceptTerms(); + expect(store.toggleAcceptTerms).to.have.been.called; + }); + }); + }); +}); diff --git a/js/src/modals/Shapeshift/Price/price.js b/js/src/modals/Shapeshift/Price/price.js index 206587448..e8eb21e52 100644 --- a/js/src/modals/Shapeshift/Price/price.js +++ b/js/src/modals/Shapeshift/Price/price.js @@ -15,6 +15,7 @@ // along with Parity. If not, see . import React, { Component, PropTypes } from 'react'; +import { FormattedMessage } from 'react-intl'; import Value from '../Value'; import styles from '../shapeshift.css'; @@ -42,7 +43,13 @@ export default class Price extends Component { =
- ( minimum, maximum) + , + minimum: + } } />
); diff --git a/js/src/modals/Shapeshift/Price/price.spec.js b/js/src/modals/Shapeshift/Price/price.spec.js new file mode 100644 index 000000000..9b144746b --- /dev/null +++ b/js/src/modals/Shapeshift/Price/price.spec.js @@ -0,0 +1,40 @@ +// Copyright 2015, 2016 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +import { shallow } from 'enzyme'; +import React from 'react'; + +import Price from './'; + +let component; + +function render (props = {}) { + component = shallow( + + ); + + return component; +} + +describe('modals/Shapeshift/Price', () => { + it('renders defaults', () => { + expect(render()).to.be.ok; + }); +}); diff --git a/js/src/modals/Shapeshift/Value/value.spec.js b/js/src/modals/Shapeshift/Value/value.spec.js new file mode 100644 index 000000000..8c5ab5d9c --- /dev/null +++ b/js/src/modals/Shapeshift/Value/value.spec.js @@ -0,0 +1,36 @@ +// Copyright 2015, 2016 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +import { shallow } from 'enzyme'; +import React from 'react'; + +import Value from './'; + +let component; + +function render (props = {}) { + component = shallow( + + ); + + return component; +} + +describe('modals/Shapeshift/Value', () => { + it('renders defaults', () => { + expect(render()).to.be.ok; + }); +}); diff --git a/js/src/modals/Shapeshift/shapeshift.js b/js/src/modals/Shapeshift/shapeshift.js index fc76a8968..bf450122d 100644 --- a/js/src/modals/Shapeshift/shapeshift.js +++ b/js/src/modals/Shapeshift/shapeshift.js @@ -14,26 +14,44 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +import { observer } from 'mobx-react'; import React, { Component, PropTypes } from 'react'; -import ActionDoneAll from 'material-ui/svg-icons/action/done-all'; -import ContentClear from 'material-ui/svg-icons/content/clear'; +import { FormattedMessage } from 'react-intl'; +import shapeshiftLogo from '~/../assets/images/shapeshift-logo.png'; import { Button, IdentityIcon, Modal } from '~/ui'; -import initShapeshift from '~/3rdparty/shapeshift'; -import shapeshiftLogo from '../../../assets/images/shapeshift-logo.png'; +import { CancelIcon, DoneIcon } from '~/ui/Icons'; import AwaitingDepositStep from './AwaitingDepositStep'; import AwaitingExchangeStep from './AwaitingExchangeStep'; import CompletedStep from './CompletedStep'; import ErrorStep from './ErrorStep'; import OptionsStep from './OptionsStep'; +import Store, { STAGE_COMPLETED, STAGE_OPTIONS, STAGE_WAIT_DEPOSIT, STAGE_WAIT_EXCHANGE } from './store'; import styles from './shapeshift.css'; -const shapeshift = initShapeshift(); - -const STAGE_NAMES = ['details', 'awaiting deposit', 'awaiting exchange', 'completed']; +const STAGE_TITLES = [ + , + , + , + +]; +const ERROR_TITLE = ( + +); +@observer export default class Shapeshift extends Component { static contextTypes = { store: PropTypes.object.isRequired @@ -44,46 +62,38 @@ export default class Shapeshift extends Component { onClose: PropTypes.func } - state = { - stage: 0, - coinSymbol: 'BTC', - coinPair: 'btc_eth', - coins: [], - depositAddress: '', - refundAddress: '', - price: null, - depositInfo: null, - exchangeInfo: null, - error: {}, - hasAccepted: false, - shifting: false - } + store = new Store(this.props.address); componentDidMount () { - this.retrieveCoins(); + this.store.retrieveCoins(); } componentWillUnmount () { - this.unsubscribe(); - } - - unsubscribe () { - // Unsubscribe from Shapeshit - const { depositAddress } = this.state; - shapeshift.unsubscribe(depositAddress); + this.store.unsubscribe(); } render () { - const { error, stage } = this.state; + const { error, stage } = this.store; return ( + steps={ + error + ? null + : STAGE_TITLES + } + title={ + error + ? ERROR_TITLE + : null + } + visible + waiting={ [ + STAGE_WAIT_DEPOSIT, + STAGE_WAIT_EXCHANGE + ] }> { this.renderPage() } ); @@ -91,7 +101,7 @@ export default class Shapeshift extends Component { renderDialogActions () { const { address } = this.props; - const { coins, error, stage, hasAccepted, shifting } = this.state; + const { coins, error, hasAcceptedTerms, stage } = this.store; const logo = ( @@ -100,12 +110,16 @@ export default class Shapeshift extends Component { ); const cancelBtn = (
+ ); + } + + renderInput () { + return ( + + } + onBlur={ this.hideInput } + onFocus={ this.showInput } + onSubmit={ this.inputOnSubmit } + style={ INPUT_STYLE } + /> + ); + } + + toggleInput = () => { + const { inputShown } = this.state; + this.setState({ + inputShown: !inputShown + }); + } + + hideInput = () => { + this.setState({ inputShown: false }); + } + + showInput = () => { + this.setState({ inputShown: true }); + } + + inputOnSubmit = (url) => { + const { router } = this.props; + + router.push(`/web/${encodeURIComponent(url)}`); + } +} + +export default withRouter(UrlButton); diff --git a/js/src/views/Dapps/dapps.js b/js/src/views/Dapps/dapps.js index fa7c44878..8ebbb602d 100644 --- a/js/src/views/Dapps/dapps.js +++ b/js/src/views/Dapps/dapps.js @@ -27,6 +27,7 @@ import PermissionStore from '~/modals/DappPermissions/store'; import { Actionbar, Button, Page } from '~/ui'; import { LockedIcon, VisibleIcon } from '~/ui/Icons'; +import UrlButton from './UrlButton'; import DappsStore from './dappsStore'; import Summary from './Summary'; @@ -88,6 +89,7 @@ class Dapps extends Component { defaultMessage='Decentralized Applications' /> } buttons={ [ + ,