diff --git a/ethcore/light/src/client.rs b/ethcore/light/src/client.rs index 73e85b31c..80100f17c 100644 --- a/ethcore/light/src/client.rs +++ b/ethcore/light/src/client.rs @@ -24,7 +24,7 @@ use ethcore::service::ClientIoMessage; use ethcore::block_import_error::BlockImportError; use ethcore::block_status::BlockStatus; use ethcore::verification::queue::{HeaderQueue, QueueInfo}; -use ethcore::transaction::SignedTransaction; +use ethcore::transaction::{SignedTransaction, PendingTransaction}; use ethcore::blockchain_info::BlockChainInfo; use io::IoChannel; @@ -114,7 +114,7 @@ impl Provider for Client { Vec::new() } - fn pending_transactions(&self) -> Vec { + fn pending_transactions(&self) -> Vec { Vec::new() } -} \ No newline at end of file +} diff --git a/ethcore/light/src/net/tests/mod.rs b/ethcore/light/src/net/tests/mod.rs index 99f695b81..f9551fc9a 100644 --- a/ethcore/light/src/net/tests/mod.rs +++ b/ethcore/light/src/net/tests/mod.rs @@ -20,7 +20,7 @@ use ethcore::blockchain_info::BlockChainInfo; use ethcore::client::{BlockChainClient, EachBlockWith, TestBlockChainClient}; use ethcore::ids::BlockId; -use ethcore::transaction::SignedTransaction; +use ethcore::transaction::PendingTransaction; use network::{PeerId, NodeId}; use net::buffer_flow::FlowParams; @@ -169,7 +169,7 @@ impl Provider for TestProvider { req.requests.into_iter().map(|_| ::rlp::EMPTY_LIST_RLP.to_vec()).collect() } - fn pending_transactions(&self) -> Vec { + fn pending_transactions(&self) -> Vec { self.0.client.pending_transactions() } } diff --git a/ethcore/light/src/provider.rs b/ethcore/light/src/provider.rs index d820ee6d0..ce80929c2 100644 --- a/ethcore/light/src/provider.rs +++ b/ethcore/light/src/provider.rs @@ -19,7 +19,7 @@ use ethcore::blockchain_info::BlockChainInfo; use ethcore::client::{BlockChainClient, ProvingBlockChainClient}; -use ethcore::transaction::SignedTransaction; +use ethcore::transaction::PendingTransaction; use ethcore::ids::BlockId; use util::{Bytes, H256}; @@ -79,7 +79,7 @@ pub trait Provider: Send + Sync { fn header_proofs(&self, req: request::HeaderProofs) -> Vec; /// Provide pending transactions. - fn pending_transactions(&self) -> Vec; + fn pending_transactions(&self) -> Vec; } // Implementation of a light client data provider for a client. @@ -178,7 +178,7 @@ impl Provider for T { req.requests.into_iter().map(|_| ::rlp::EMPTY_LIST_RLP.to_vec()).collect() } - fn pending_transactions(&self) -> Vec { + fn pending_transactions(&self) -> Vec { BlockChainClient::pending_transactions(self) } -} \ No newline at end of file +} diff --git a/ethcore/res/ethereum/tests b/ethcore/res/ethereum/tests index 9028c4801..e8f4624b7 160000 --- a/ethcore/res/ethereum/tests +++ b/ethcore/res/ethereum/tests @@ -1 +1 @@ -Subproject commit 9028c4801fd39fbb71a9796979182549a24e81c8 +Subproject commit e8f4624b7f1a15c63674eecf577c7ab76c3b16be diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index dce594617..39f7420f8 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -42,7 +42,7 @@ use env_info::LastHashes; use verification; use verification::{PreverifiedBlock, Verifier}; use block::*; -use transaction::{LocalizedTransaction, SignedTransaction, Action}; +use transaction::{LocalizedTransaction, SignedTransaction, PendingTransaction, Action}; use blockchain::extras::TransactionAddress; use types::filter::Filter; use types::mode::Mode as IpcMode; @@ -1286,7 +1286,7 @@ impl BlockChainClient for Client { } } - fn pending_transactions(&self) -> Vec { + fn pending_transactions(&self) -> Vec { self.miner.pending_transactions(self.chain.read().best_block_number()) } diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 44954f99d..f730c7d9c 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -21,7 +21,7 @@ use util::*; use rlp::*; use ethkey::{Generator, Random}; use devtools::*; -use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action}; +use transaction::{Transaction, LocalizedTransaction, SignedTransaction, PendingTransaction, Action}; use blockchain::TreeRoute; use client::{ BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockId, @@ -663,7 +663,7 @@ impl BlockChainClient for TestBlockChainClient { self.miner.import_external_transactions(self, txs); } - fn pending_transactions(&self) -> Vec { + fn pending_transactions(&self) -> Vec { self.miner.pending_transactions(self.chain_info().best_block_number) } diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index f44a4496f..7afe96f57 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -21,7 +21,7 @@ use blockchain::TreeRoute; use verification::queue::QueueInfo as BlockQueueInfo; use block::{OpenBlock, SealedBlock}; use header::{BlockNumber}; -use transaction::{LocalizedTransaction, SignedTransaction}; +use transaction::{LocalizedTransaction, SignedTransaction, PendingTransaction}; use log_entry::LocalizedLogEntry; use filter::Filter; use views::{BlockView}; @@ -203,7 +203,7 @@ pub trait BlockChainClient : Sync + Send { fn queue_transactions(&self, transactions: Vec, peer_id: usize); /// list all transactions - fn pending_transactions(&self) -> Vec; + fn pending_transactions(&self) -> Vec; /// Sorted list of transaction gas prices from at least last sample_size blocks. fn gas_price_corpus(&self, sample_size: usize) -> Vec { diff --git a/ethcore/src/miner/banning_queue.rs b/ethcore/src/miner/banning_queue.rs index d8b038c15..1fe345614 100644 --- a/ethcore/src/miner/banning_queue.rs +++ b/ethcore/src/miner/banning_queue.rs @@ -115,7 +115,7 @@ impl BanningTransactionQueue { } } } - self.queue.add(transaction, TransactionOrigin::External, account_details, gas_estimator) + self.queue.add(transaction, TransactionOrigin::External, None, account_details, gas_estimator) } /// Ban transaction with given hash. @@ -263,7 +263,7 @@ mod tests { let mut txq = queue(); // when - txq.queue().add(tx, TransactionOrigin::External, &default_account_details, &gas_required).unwrap(); + txq.queue().add(tx, TransactionOrigin::External, None, &default_account_details, &gas_required).unwrap(); // then // should also deref to queue diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index a6a63ccaf..a01f16523 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -28,7 +28,7 @@ use client::TransactionImportResult; use executive::contract_address; use block::{ClosedBlock, SealedBlock, IsBlock, Block}; use error::*; -use transaction::{Action, SignedTransaction}; +use transaction::{Action, SignedTransaction, PendingTransaction}; use receipt::{Receipt, RichReceipt}; use spec::Spec; use engines::Engine; @@ -320,11 +320,12 @@ 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.lock().top_transactions()}; + let transactions = {self.transaction_queue.lock().top_transactions_at(chain_info.best_block_number)}; 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.best_block_header().sha3(); + let best_hash = chain_info.best_block_hash; /* // check to see if last ClosedBlock in would_seals is actually same parent block. // if so @@ -568,8 +569,14 @@ impl Miner { prepare_new } - fn add_transactions_to_queue(&self, chain: &MiningBlockChainClient, transactions: Vec, default_origin: TransactionOrigin, transaction_queue: &mut BanningTransactionQueue) -> - Vec> { + fn add_transactions_to_queue( + &self, + chain: &MiningBlockChainClient, + transactions: Vec, + default_origin: TransactionOrigin, + min_block: Option, + transaction_queue: &mut BanningTransactionQueue) + -> Vec> { let fetch_account = |a: &Address| AccountDetails { nonce: chain.latest_nonce(a), @@ -604,7 +611,7 @@ impl Miner { match origin { TransactionOrigin::Local | TransactionOrigin::RetractedBlock => { - transaction_queue.add(tx, origin, &fetch_account, &gas_required) + transaction_queue.add(tx, origin, min_block, &fetch_account, &gas_required) }, TransactionOrigin::External => { transaction_queue.add_with_banlist(tx, &fetch_account, &gas_required) @@ -830,7 +837,7 @@ impl MinerService for Miner { let results = { let mut transaction_queue = self.transaction_queue.lock(); self.add_transactions_to_queue( - chain, transactions, TransactionOrigin::External, &mut transaction_queue + chain, transactions, TransactionOrigin::External, None, &mut transaction_queue ) }; @@ -848,17 +855,17 @@ impl MinerService for Miner { fn import_own_transaction( &self, chain: &MiningBlockChainClient, - transaction: SignedTransaction, + pending: PendingTransaction, ) -> Result { - let hash = transaction.hash(); - trace!(target: "own_tx", "Importing transaction: {:?}", transaction); + let hash = pending.transaction.hash(); + trace!(target: "own_tx", "Importing transaction: {:?}", pending); let imported = { // Be sure to release the lock before we call prepare_work_sealing let mut transaction_queue = self.transaction_queue.lock(); let import = self.add_transactions_to_queue( - chain, vec![transaction], TransactionOrigin::Local, &mut transaction_queue + chain, vec![pending.transaction], TransactionOrigin::Local, pending.min_block, &mut transaction_queue ).pop().expect("one result returned per added transaction; one added => one result; qed"); match import { @@ -893,9 +900,9 @@ impl MinerService for Miner { imported } - fn all_transactions(&self) -> Vec { + fn all_transactions(&self) -> Vec { let queue = self.transaction_queue.lock(); - queue.top_transactions() + queue.pending_transactions(BlockNumber::max_value()) } fn local_transactions(&self) -> BTreeMap { @@ -906,22 +913,26 @@ impl MinerService for Miner { .collect() } - fn pending_transactions(&self, best_block: BlockNumber) -> Vec { + fn future_transactions(&self) -> Vec { + self.transaction_queue.lock().future_transactions() + } + + fn pending_transactions(&self, best_block: BlockNumber) -> Vec { let queue = self.transaction_queue.lock(); match self.options.pending_set { - PendingSet::AlwaysQueue => queue.top_transactions(), + PendingSet::AlwaysQueue => queue.pending_transactions(best_block), PendingSet::SealingOrElseQueue => { self.from_pending_block( best_block, - || queue.top_transactions(), - |sealing| sealing.transactions().to_owned() + || queue.pending_transactions(best_block), + |sealing| sealing.transactions().iter().map(|t| t.clone().into()).collect() ) }, PendingSet::AlwaysSealing => { self.from_pending_block( best_block, || vec![], - |sealing| sealing.transactions().to_owned() + |sealing| sealing.transactions().iter().map(|t| t.clone().into()).collect() ) }, } @@ -1116,7 +1127,7 @@ impl MinerService for Miner { }).for_each(|txs| { let mut transaction_queue = self.transaction_queue.lock(); let _ = self.add_transactions_to_queue( - chain, txs, TransactionOrigin::RetractedBlock, &mut transaction_queue + chain, txs, TransactionOrigin::RetractedBlock, None, &mut transaction_queue ); }); } @@ -1149,7 +1160,7 @@ mod tests { use ethkey::{Generator, Random}; use client::{BlockChainClient, TestBlockChainClient, EachBlockWith, TransactionImportResult}; use header::BlockNumber; - use types::transaction::{Transaction, SignedTransaction, Action}; + use types::transaction::{Transaction, SignedTransaction, PendingTransaction, Action}; use spec::Spec; use tests::helpers::{generate_dummy_client}; @@ -1228,7 +1239,7 @@ mod tests { let transaction = transaction(); let best_block = 0; // when - let res = miner.import_own_transaction(&client, transaction); + let res = miner.import_own_transaction(&client, PendingTransaction::new(transaction, None)); // then assert_eq!(res.unwrap(), TransactionImportResult::Current); @@ -1248,7 +1259,7 @@ mod tests { let transaction = transaction(); let best_block = 10; // when - let res = miner.import_own_transaction(&client, transaction); + let res = miner.import_own_transaction(&client, PendingTransaction::new(transaction, None)); // then assert_eq!(res.unwrap(), TransactionImportResult::Current); @@ -1305,7 +1316,7 @@ mod tests { assert!(miner.pending_block().is_none()); assert_eq!(client.chain_info().best_block_number, 3 as BlockNumber); - assert_eq!(miner.import_own_transaction(client, transaction()).unwrap(), TransactionImportResult::Current); + assert_eq!(miner.import_own_transaction(client, PendingTransaction::new(transaction(), None)).unwrap(), TransactionImportResult::Current); miner.update_sealing(client); client.flush_queue(); diff --git a/ethcore/src/miner/mod.rs b/ethcore/src/miner/mod.rs index 814953c81..fa8e7df0a 100644 --- a/ethcore/src/miner/mod.rs +++ b/ethcore/src/miner/mod.rs @@ -62,7 +62,7 @@ use block::ClosedBlock; use header::BlockNumber; use receipt::{RichReceipt, Receipt}; use error::{Error, CallError}; -use transaction::SignedTransaction; +use transaction::{SignedTransaction, PendingTransaction}; /// Miner client API pub trait MinerService : Send + Sync { @@ -118,7 +118,7 @@ pub trait MinerService : Send + Sync { Vec>; /// Imports own (node owner) transaction to queue. - fn import_own_transaction(&self, chain: &MiningBlockChainClient, transaction: SignedTransaction) -> + fn import_own_transaction(&self, chain: &MiningBlockChainClient, transaction: PendingTransaction) -> Result; /// Returns hashes of transactions currently in pending @@ -144,11 +144,14 @@ pub trait MinerService : Send + Sync { /// Query pending transactions for hash. fn transaction(&self, best_block: BlockNumber, hash: &H256) -> Option; - /// Get a list of all transactions. - fn all_transactions(&self) -> Vec; + /// Get a list of all ready transactions in the queue. + fn all_transactions(&self) -> Vec; /// Get a list of all pending transactions. - fn pending_transactions(&self, best_block: BlockNumber) -> Vec; + fn pending_transactions(&self, best_block: BlockNumber) -> Vec; + + /// Get a list of all future transactions. + fn future_transactions(&self) -> Vec; /// Get a list of local transactions with statuses. fn local_transactions(&self) -> BTreeMap; diff --git a/ethcore/src/miner/transaction_queue.rs b/ethcore/src/miner/transaction_queue.rs index 28b39c7e6..a13fc01c4 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, &default_account_details, &gas_estimator).unwrap(); -//! txq.add(st1.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); +//! 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(); //! //! // Check status //! assert_eq!(txq.status().pending, 2); @@ -94,6 +94,7 @@ use util::table::Table; use transaction::*; use error::{Error, TransactionError}; use client::TransactionImportResult; +use header::BlockNumber; use miner::local_transactions::{LocalTransactionsList, Status as LocalTransactionStatus}; /// Transaction origin @@ -253,18 +254,21 @@ impl Ord for TransactionOrder { /// Verified transaction (with sender) #[derive(Debug)] struct VerifiedTransaction { - /// Transaction + /// Transaction. transaction: SignedTransaction, - /// transaction origin + /// Transaction origin. origin: TransactionOrigin, + /// Delay until specifid block. + min_block: Option, } impl VerifiedTransaction { - fn new(transaction: SignedTransaction, origin: TransactionOrigin) -> Result { + fn new(transaction: SignedTransaction, origin: TransactionOrigin, min_block: Option) -> Result { try!(transaction.sender()); Ok(VerifiedTransaction { transaction: transaction, origin: origin, + min_block: min_block, }) } @@ -620,6 +624,7 @@ impl TransactionQueue { &mut self, tx: SignedTransaction, origin: TransactionOrigin, + min_block: Option, fetch_account: &F, gas_estimator: &G, ) -> Result where @@ -630,7 +635,7 @@ impl TransactionQueue { let hash = tx.hash(); let cloned_tx = tx.clone(); - let result = self.add_internal(tx, origin, fetch_account, gas_estimator); + let result = self.add_internal(tx, origin, min_block, fetch_account, gas_estimator); match result { Ok(TransactionImportResult::Current) => { self.local_transactions.mark_pending(hash); @@ -651,7 +656,7 @@ impl TransactionQueue { } result } else { - self.add_internal(tx, origin, fetch_account, gas_estimator) + self.add_internal(tx, origin, min_block, fetch_account, gas_estimator) } } @@ -660,6 +665,7 @@ impl TransactionQueue { &mut self, tx: SignedTransaction, origin: TransactionOrigin, + min_block: Option, fetch_account: &F, gas_estimator: &G, ) -> Result where @@ -728,7 +734,7 @@ impl TransactionQueue { // Verify signature try!(tx.check_low_s()); - let vtx = try!(VerifiedTransaction::new(tx, origin)); + let vtx = try!(VerifiedTransaction::new(tx, origin, min_block)); let client_account = fetch_account(&vtx.sender()); let cost = vtx.transaction.value + vtx.transaction.gas_price * vtx.transaction.gas; @@ -968,10 +974,48 @@ impl TransactionQueue { /// Returns top transactions from the queue ordered by priority. pub fn top_transactions(&self) -> Vec { - self.current.by_priority + self.top_transactions_at(BlockNumber::max_value()) + + } + + fn collect_pending_transaction(&self, best_block: BlockNumber, mut f: F) + where F: FnMut(&VerifiedTransaction) { + + let mut delayed = HashSet::new(); + for t in self.current.by_priority.iter() { + let tx = self.by_hash.get(&t.hash).expect("All transactions in `current` and `future` are always included in `by_hash`"); + let sender = tx.transaction.sender().expect("Queue only contains transactions with valid sender"); + if delayed.contains(&sender) { + continue; + } + if tx.min_block.unwrap_or(0) > best_block { + delayed.insert(sender); + continue; + } + f(&tx); + } + } + + /// Returns top transactions from the queue ordered by priority. + pub fn top_transactions_at(&self, best_block: BlockNumber) -> Vec { + let mut r = Vec::new(); + self.collect_pending_transaction(best_block, |tx| r.push(tx.transaction.clone())); + r + } + + /// Return all ready transactions. + pub fn pending_transactions(&self, best_block: BlockNumber) -> Vec { + let mut r = Vec::new(); + self.collect_pending_transaction(best_block, |tx| r.push(PendingTransaction::new(tx.transaction.clone(), tx.min_block))); + r + } + + /// Return all ready transactions. + pub fn future_transactions(&self) -> Vec { + self.future.by_priority .iter() .map(|t| self.by_hash.get(&t.hash).expect("All transactions in `current` and `future` are always included in `by_hash`")) - .map(|t| t.transaction.clone()) + .map(|t| PendingTransaction { transaction: t.transaction.clone(), min_block: t.min_block }) .collect() } @@ -980,15 +1024,6 @@ impl TransactionQueue { self.local_transactions.all_transactions() } - #[cfg(test)] - fn future_transactions(&self) -> Vec { - self.future.by_priority - .iter() - .map(|t| self.by_hash.get(&t.hash).expect("All transactions in `current` and `future` are always included in `by_hash`")) - .map(|t| t.transaction.clone()) - .collect() - } - /// Returns hashes of all transactions from current, ordered by priority. pub fn pending_hashes(&self) -> Vec { self.current.by_priority @@ -1353,14 +1388,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, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, &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(); 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, &default_account_details, &gas_estimator); + let res = txq.add(tx.clone(), TransactionOrigin::External, 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 @@ -1392,12 +1427,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).unwrap(); - let tx2 = VerifiedTransaction::new(tx2, TransactionOrigin::External).unwrap(); + let tx1 = VerifiedTransaction::new(tx1, TransactionOrigin::External, None).unwrap(); + let tx2 = VerifiedTransaction::new(tx2, TransactionOrigin::External, None).unwrap(); let mut by_hash = { let mut x = HashMap::new(); - let tx1 = VerifiedTransaction::new(tx1.transaction.clone(), TransactionOrigin::External).unwrap(); - let tx2 = VerifiedTransaction::new(tx2.transaction.clone(), TransactionOrigin::External).unwrap(); + let tx1 = VerifiedTransaction::new(tx1.transaction.clone(), TransactionOrigin::External, None).unwrap(); + let tx2 = VerifiedTransaction::new(tx2.transaction.clone(), TransactionOrigin::External, None).unwrap(); x.insert(tx1.hash(), tx1); x.insert(tx2.hash(), tx2); x @@ -1435,12 +1470,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).unwrap(); - let tx2 = VerifiedTransaction::new(tx2, TransactionOrigin::External).unwrap(); + let tx1 = VerifiedTransaction::new(tx1, TransactionOrigin::External, None).unwrap(); + let tx2 = VerifiedTransaction::new(tx2, TransactionOrigin::External, None).unwrap(); let by_hash = { let mut x = HashMap::new(); - let tx1 = VerifiedTransaction::new(tx1.transaction.clone(), TransactionOrigin::External).unwrap(); - let tx2 = VerifiedTransaction::new(tx2.transaction.clone(), TransactionOrigin::External).unwrap(); + let tx1 = VerifiedTransaction::new(tx1.transaction.clone(), TransactionOrigin::External, None).unwrap(); + let tx2 = VerifiedTransaction::new(tx2.transaction.clone(), TransactionOrigin::External, None).unwrap(); x.insert(tx1.hash(), tx1); x.insert(tx2.hash(), tx2); x @@ -1482,10 +1517,10 @@ mod test { gas_limit: !U256::zero(), }; let tx = new_tx_default(); - let tx1 = VerifiedTransaction::new(tx.clone(), TransactionOrigin::External).unwrap(); + let tx1 = VerifiedTransaction::new(tx.clone(), TransactionOrigin::External, 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).unwrap(); + let tx2 = VerifiedTransaction::new(tx, TransactionOrigin::External, None).unwrap(); let order2 = TransactionOrder::for_transaction(&tx2, 0.into(), 1.into(), PrioritizationStrategy::GasPriceOnly); assert!(set.insert(tx2.sender(), tx2.nonce(), order2).is_some()); } @@ -1502,7 +1537,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).unwrap(); + let tx1 = VerifiedTransaction::new(tx.clone(), TransactionOrigin::External, 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()); @@ -1517,12 +1552,12 @@ mod test { !U256::zero() }; // First insert one transaction to future - let res = txq.add(tx, TransactionOrigin::External, &prev_nonce, &gas_estimator); + let res = txq.add(tx, TransactionOrigin::External, 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, &default_account_details, &gas_estimator); + let res = txq.add(tx2.clone(), TransactionOrigin::External, 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); @@ -1542,12 +1577,12 @@ mod test { !U256::zero() }; // First insert one transaction to future - let res = txq.add(tx.clone(), TransactionOrigin::External, &prev_nonce, &gas_estimator); + let res = txq.add(tx.clone(), TransactionOrigin::External, 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, &default_account_details, &gas_estimator); + let res = txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator); // then assert_eq!(res.unwrap(), TransactionImportResult::Current); @@ -1566,7 +1601,7 @@ mod test { let tx = new_tx_default(); // when - let res = txq.add(tx, TransactionOrigin::External, &default_account_details, &gas_estimator); + let res = txq.add(tx, TransactionOrigin::External, None, &default_account_details, &gas_estimator); // then assert_eq!(res.unwrap(), TransactionImportResult::Current); @@ -1585,10 +1620,10 @@ mod test { txq.set_minimal_gas_price(15.into()); // when - let res1 = txq.add(tx1, TransactionOrigin::External, &default_account_details, &gas_estimator); - let res2 = txq.add(tx2, TransactionOrigin::External, &default_account_details, &gas_estimator); - let res3 = txq.add(tx3, TransactionOrigin::External, &default_account_details, &gas_estimator); - let res4 = txq.add(tx4, TransactionOrigin::External, &default_account_details, &gas_estimator); + 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); // then assert_eq!(res1.unwrap(), TransactionImportResult::Current); @@ -1619,10 +1654,10 @@ mod test { txq.set_minimal_gas_price(15.into()); // when - let res1 = txq.add(tx1, TransactionOrigin::External, &default_account_details, &gas_estimator); - let res2 = txq.add(tx2, TransactionOrigin::External, &default_account_details, &gas_estimator); - let res3 = txq.add(tx3, TransactionOrigin::External, &default_account_details, &gas_estimator); - let res4 = txq.add(tx4, TransactionOrigin::External, &default_account_details, &gas_estimator); + 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); // then assert_eq!(res1.unwrap(), TransactionImportResult::Current); @@ -1665,7 +1700,7 @@ mod test { txq.set_gas_limit(limit); // when - let res = txq.add(tx, TransactionOrigin::External, &default_account_details, &gas_estimator); + let res = txq.add(tx, TransactionOrigin::External, None, &default_account_details, &gas_estimator); // then assert_eq!(unwrap_tx_err(res), TransactionError::GasLimitExceeded { @@ -1689,7 +1724,7 @@ mod test { }; // when - let res = txq.add(tx, TransactionOrigin::External, &account, &gas_estimator); + let res = txq.add(tx, TransactionOrigin::External, None, &account, &gas_estimator); // then assert_eq!(unwrap_tx_err(res), TransactionError::InsufficientBalance { @@ -1709,7 +1744,7 @@ mod test { txq.set_minimal_gas_price(tx.gas_price + U256::one()); // when - let res = txq.add(tx, TransactionOrigin::External, &default_account_details, &gas_estimator); + let res = txq.add(tx, TransactionOrigin::External, None, &default_account_details, &gas_estimator); // then assert_eq!(unwrap_tx_err(res), TransactionError::InsufficientGasPrice { @@ -1729,7 +1764,7 @@ mod test { txq.set_minimal_gas_price(tx.gas_price + U256::one()); // when - let res = txq.add(tx, TransactionOrigin::Local, &default_account_details, &gas_estimator); + let res = txq.add(tx, TransactionOrigin::Local, None, &default_account_details, &gas_estimator); // then assert_eq!(res.unwrap(), TransactionImportResult::Current); @@ -1759,7 +1794,7 @@ mod test { rlp::decode(s.as_raw()) }; // when - let res = txq.add(stx, TransactionOrigin::External, &default_account_details, &gas_estimator); + let res = txq.add(stx, TransactionOrigin::External, None, &default_account_details, &gas_estimator); // then assert!(res.is_err()); @@ -1773,8 +1808,8 @@ mod test { let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); // when - txq.add(tx.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, &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, None, &default_account_details, &gas_estimator).unwrap(); // then let top = txq.top_transactions(); @@ -1793,9 +1828,9 @@ mod test { // when // first insert the one with higher gas price - txq.add(tx2.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); // then the one with lower gas price, but local - txq.add(tx.clone(), TransactionOrigin::Local, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx.clone(), TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap(); // then let top = txq.top_transactions(); @@ -1812,15 +1847,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, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx1.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + 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(); // 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, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap(); // then // the order should be updated @@ -1839,9 +1874,9 @@ mod test { // when // first insert local one with higher gas price - txq.add(tx2.clone(), TransactionOrigin::Local, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap(); // then the one with lower gas price, but from retracted block - txq.add(tx.clone(), TransactionOrigin::RetractedBlock, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx.clone(), TransactionOrigin::RetractedBlock, None, &default_account_details, &gas_estimator).unwrap(); // then let top = txq.top_transactions(); @@ -1857,8 +1892,8 @@ mod test { let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); // when - txq.add(tx.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::Local, &default_account_details, &gas_estimator).unwrap(); + 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(); // then let top = txq.top_transactions(); @@ -1877,10 +1912,10 @@ mod test { let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into()); // insert everything - txq.add(txa.clone(), TransactionOrigin::External, &prev_nonce, &gas_estimator).unwrap(); - txq.add(txb.clone(), TransactionOrigin::External, &prev_nonce, &gas_estimator).unwrap(); - txq.add(tx1.clone(), TransactionOrigin::External, &prev_nonce, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, &prev_nonce, &gas_estimator).unwrap(); + 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(); assert_eq!(txq.status().future, 4); @@ -1889,10 +1924,10 @@ mod test { // then let top = txq.future_transactions(); - assert_eq!(top[0], txa); - assert_eq!(top[1], txb); - assert_eq!(top[2], tx1); - assert_eq!(top[3], tx2); + assert_eq!(top[0].transaction, txa); + assert_eq!(top[1].transaction, txb); + assert_eq!(top[2].transaction, tx1); + assert_eq!(top[3].transaction, tx2); assert_eq!(top.len(), 4); } @@ -1905,10 +1940,10 @@ mod test { let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into()); // insert everything - txq.add(txa.clone(), TransactionOrigin::Local, &default_account_details, &gas_estimator).unwrap(); - txq.add(txb.clone(), TransactionOrigin::Local, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx1.clone(), TransactionOrigin::Local, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::Local, &default_account_details, &gas_estimator).unwrap(); + 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(); let top = txq.top_transactions(); assert_eq!(top[0], tx1); @@ -1938,10 +1973,10 @@ mod test { let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into()); // insert everything - txq.add(txa.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); - txq.add(txb.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx1.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + 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(); let top = txq.top_transactions(); assert_eq!(top[0], tx1); @@ -1970,8 +2005,8 @@ mod test { let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); // when - txq.add(tx.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, &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, None, &default_account_details, &gas_estimator).unwrap(); // then let top = txq.pending_hashes(); @@ -1988,8 +2023,8 @@ mod test { let (tx, tx2) = new_tx_pair_default(2.into(), 0.into()); // when - let res1 = txq.add(tx.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); - let res2 = txq.add(tx2.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + 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(); // then assert_eq!(res1, TransactionImportResult::Current); @@ -2002,6 +2037,26 @@ mod test { assert_eq!(top[0], tx); } + #[test] + fn should_handle_min_block() { + // given + let mut txq = TransactionQueue::default(); + + 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(); + + // then + assert_eq!(res1, TransactionImportResult::Current); + assert_eq!(res2, TransactionImportResult::Current); + let top = txq.top_transactions_at(0); + assert_eq!(top.len(), 0); + let top = txq.top_transactions_at(1); + assert_eq!(top.len(), 2); + } + #[test] fn should_correctly_update_futures_when_removing() { // given @@ -2012,8 +2067,8 @@ mod test { let mut txq = TransactionQueue::default(); let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); - txq.add(tx.clone(), TransactionOrigin::External, &prev_nonce, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, &prev_nonce, &gas_estimator).unwrap(); + txq.add(tx.clone(), TransactionOrigin::External, None, &prev_nonce, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, None, &prev_nonce, &gas_estimator).unwrap(); assert_eq!(txq.status().future, 2); // when @@ -2035,13 +2090,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, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().pending, 1); - txq.add(tx2, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().future, 1); // when - txq.add(tx1, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx1, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); // then let stats = txq.status(); @@ -2057,8 +2112,8 @@ 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, &default_account_details, &gas_estimator).unwrap(); - txq2.add(tx2.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + 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(); assert_eq!(txq2.status().pending, 1); assert_eq!(txq2.status().future, 1); @@ -2079,10 +2134,10 @@ 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, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().future, 1); - txq.add(tx3.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + 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(); assert_eq!(txq.status().pending, 3); // when @@ -2101,8 +2156,8 @@ mod test { let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); // add - txq.add(tx2.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx.clone(), TransactionOrigin::External, &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, None, &default_account_details, &gas_estimator).unwrap(); let stats = txq.status(); assert_eq!(stats.pending, 2); @@ -2121,11 +2176,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, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().pending, 1); // when - let res = txq.add(tx2.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator); + let res = txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator); // then let t = txq.top_transactions(); @@ -2142,14 +2197,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, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx3.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + 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(); assert_eq!(txq.status().pending, 2); // when - txq.add(tx2.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().future, 1); - txq.add(tx4.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx4.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); // then assert_eq!(txq.status().future, 1); @@ -2160,11 +2215,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, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx3.clone(), TransactionOrigin::External, &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(tx3.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); // limited by gas - txq.add(tx4.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap_err(); + txq.add(tx4.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap_err(); assert_eq!(txq.status().pending, 2); } @@ -2174,12 +2229,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, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::Local, &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(); // Not accepted because of limit - txq.add(tx5.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap_err(); - txq.add(tx3.clone(), TransactionOrigin::Local, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx4.clone(), TransactionOrigin::Local, &default_account_details, &gas_estimator).unwrap(); + 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(); assert_eq!(txq.status().pending, 4); } @@ -2191,7 +2246,7 @@ mod test { let fetch_last_nonce = |_a: &Address| AccountDetails { nonce: last_nonce, balance: !U256::zero() }; // when - let res = txq.add(tx, TransactionOrigin::External, &fetch_last_nonce, &gas_estimator); + let res = txq.add(tx, TransactionOrigin::External, None, &fetch_last_nonce, &gas_estimator); // then assert_eq!(unwrap_tx_err(res), TransactionError::Old); @@ -2207,12 +2262,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, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, 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, &nonce, &gas_estimator); + let res = txq.add(tx2.clone(), TransactionOrigin::External, None, &nonce, &gas_estimator); // then assert_eq!(unwrap_tx_err(res), TransactionError::AlreadyImported); @@ -2226,15 +2281,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, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, &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(); assert_eq!(txq.status().pending, 2); // when txq.remove_invalid(&tx1.hash(), &default_account_details); assert_eq!(txq.status().pending, 0); assert_eq!(txq.status().future, 1); - txq.add(tx1.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); // then let stats = txq.status(); @@ -2248,10 +2303,10 @@ 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, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().future, 1); - txq.add(tx3.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + 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(); assert_eq!(txq.status().pending, 3); // when @@ -2278,8 +2333,8 @@ mod test { }; // when - txq.add(tx, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); // then let stats = txq.status(); @@ -2306,10 +2361,10 @@ mod test { }; // when - txq.add(tx1, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx1, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().future, 1); - txq.add(tx0, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx0, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(); // then let stats = txq.status(); @@ -2327,8 +2382,8 @@ mod test { !U256::zero() }; let mut txq = TransactionQueue::default(); let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); - txq.add(tx1.clone(), TransactionOrigin::External, &previous_nonce, &gas_estimator).unwrap(); - txq.add(tx2, TransactionOrigin::External, &previous_nonce, &gas_estimator).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::External, None, &previous_nonce, &gas_estimator).unwrap(); + txq.add(tx2, TransactionOrigin::External, None, &previous_nonce, &gas_estimator).unwrap(); assert_eq!(txq.status().future, 2); // when @@ -2359,7 +2414,7 @@ mod test { let details = |_a: &Address| AccountDetails { nonce: nonce, balance: !U256::zero() }; // when - txq.add(tx, TransactionOrigin::External, &details, &gas_estimator).unwrap(); + txq.add(tx, TransactionOrigin::External, None, &details, &gas_estimator).unwrap(); // then assert_eq!(txq.last_nonce(&from), Some(nonce)); @@ -2374,7 +2429,7 @@ mod test { let details1 = |_a: &Address| AccountDetails { nonce: nonce1, balance: !U256::zero() }; // Insert first transaction - txq.add(tx1, TransactionOrigin::External, &details1, &gas_estimator).unwrap(); + txq.add(tx1, TransactionOrigin::External, None, &details1, &gas_estimator).unwrap(); // when txq.remove_all(tx2.sender().unwrap(), nonce2 + U256::one()); @@ -2394,9 +2449,9 @@ mod test { // when // Insert first transaction - assert_eq!(txq.add(tx1, TransactionOrigin::External, &details1, &gas_estimator).unwrap(), TransactionImportResult::Current); + assert_eq!(txq.add(tx1, TransactionOrigin::External, None, &details1, &gas_estimator).unwrap(), TransactionImportResult::Current); // Second should go to future - assert_eq!(txq.add(tx2, TransactionOrigin::External, &details1, &gas_estimator).unwrap(), TransactionImportResult::Future); + assert_eq!(txq.add(tx2, TransactionOrigin::External, None, &details1, &gas_estimator).unwrap(), TransactionImportResult::Future); // Now block is imported txq.remove_all(sender, nonce2 - U256::from(1)); // tx2 should be not be promoted to current @@ -2415,9 +2470,9 @@ mod test { assert_eq!(txq.has_local_pending_transactions(), false); // when - assert_eq!(txq.add(tx1, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(), TransactionImportResult::Current); + assert_eq!(txq.add(tx1, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(), TransactionImportResult::Current); assert_eq!(txq.has_local_pending_transactions(), false); - assert_eq!(txq.add(tx2, TransactionOrigin::Local, &default_account_details, &gas_estimator).unwrap(), TransactionImportResult::Current); + assert_eq!(txq.add(tx2, TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap(), TransactionImportResult::Current); // then assert_eq!(txq.has_local_pending_transactions(), true); @@ -2432,8 +2487,8 @@ mod test { default_account_details(a).balance }; // when - assert_eq!(txq.add(tx2, TransactionOrigin::External, &prev_nonce, &gas_estimator).unwrap(), TransactionImportResult::Future); - assert_eq!(txq.add(tx1.clone(), TransactionOrigin::External, &prev_nonce, &gas_estimator).unwrap(), TransactionImportResult::Future); + 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); // then assert_eq!(txq.future.by_priority.len(), 1); @@ -2458,14 +2513,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, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2, TransactionOrigin::Local, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx3, TransactionOrigin::Local, &default_account_details, &gas_estimator).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(); 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, &default_account_details, &gas_estimator); + let res = txq.add(tx2_2, TransactionOrigin::Local, None, &default_account_details, &gas_estimator); // then assert_eq!(txq.last_nonce(&sender).unwrap(), 125.into()); @@ -2481,8 +2536,8 @@ mod test { let high_gas = |_: &SignedTransaction| 100_001.into(); // when - let res1 = txq.add(tx1, TransactionOrigin::Local, &default_account_details, &gas_estimator); - let res2 = txq.add(tx2, TransactionOrigin::Local, &default_account_details, &high_gas); + 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); // then assert_eq!(res1.unwrap(), TransactionImportResult::Current); @@ -2502,10 +2557,10 @@ mod test { let nonce1 = tx1.nonce; // Insert all transactions - txq.add(tx1, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx3, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx4, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + 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(); assert_eq!(txq.top_transactions().len(), 4); // when diff --git a/ethcore/src/types/transaction.rs b/ethcore/src/types/transaction.rs index 6388120c3..681e38fcc 100644 --- a/ethcore/src/types/transaction.rs +++ b/ethcore/src/types/transaction.rs @@ -390,6 +390,34 @@ impl Deref for LocalizedTransaction { } } +/// Queued information with additional information. +#[derive(Debug, Clone, PartialEq, Eq, Binary)] +pub struct PendingTransaction { + /// Signed transaction data. + pub transaction: SignedTransaction, + /// Gas price. + pub min_block: Option, +} + +impl PendingTransaction { + /// Create a new pending transaction from signed transaction. + pub fn new(signed: SignedTransaction, min_block: Option) -> Self { + PendingTransaction { + transaction: signed, + min_block: min_block, + } + } +} + +impl From for PendingTransaction { + fn from(t: SignedTransaction) -> Self { + PendingTransaction { + transaction: t, + min_block: None, + } + } +} + #[test] fn sender_test() { let t: SignedTransaction = decode(&::rustc_serialize::hex::FromHex::from_hex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap()); diff --git a/rpc/src/v1/helpers/dispatch.rs b/rpc/src/v1/helpers/dispatch.rs index 93e99646b..79727f50e 100644 --- a/rpc/src/v1/helpers/dispatch.rs +++ b/rpc/src/v1/helpers/dispatch.rs @@ -21,7 +21,7 @@ use util::bytes::ToPretty; use ethkey::Signature; use ethcore::miner::MinerService; use ethcore::client::MiningBlockChainClient; -use ethcore::transaction::{Action, SignedTransaction, Transaction}; +use ethcore::transaction::{Action, SignedTransaction, PendingTransaction, Transaction}; use ethcore::account_provider::AccountProvider; use jsonrpc_core::Error; @@ -79,9 +79,9 @@ fn decrypt(accounts: &AccountProvider, address: Address, msg: Bytes, password: O }) } -pub fn dispatch_transaction(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result +pub fn dispatch_transaction(client: &C, miner: &M, signed_transaction: PendingTransaction) -> Result where C: MiningBlockChainClient, M: MinerService { - let hash = signed_transaction.hash(); + let hash = signed_transaction.transaction.hash(); miner.import_own_transaction(client, signed_transaction) .map_err(errors::from_transaction_error) @@ -120,10 +120,12 @@ pub fn sign_and_dispatch(client: &C, miner: &M, accounts: &AccountProvider { let network_id = client.signing_network_id(); + let min_block = filled.min_block.clone(); let signed_transaction = try!(sign_no_dispatch(client, miner, accounts, filled, password)); trace!(target: "miner", "send_transaction: dispatching tx: {} for network ID {:?}", rlp::encode(&signed_transaction).to_vec().pretty(), network_id); - dispatch_transaction(&*client, &*miner, signed_transaction) + let pending_transaction = PendingTransaction::new(signed_transaction, min_block); + dispatch_transaction(&*client, &*miner, pending_transaction) } pub fn fill_optional_fields(request: TransactionRequest, client: &C, miner: &M) -> FilledTransactionRequest @@ -137,6 +139,7 @@ pub fn fill_optional_fields(request: TransactionRequest, client: &C, miner gas: request.gas.unwrap_or_else(|| miner.sensible_gas_limit()), value: request.value.unwrap_or_else(|| 0.into()), data: request.data.unwrap_or_else(Vec::new), + min_block: request.min_block, } } diff --git a/rpc/src/v1/helpers/requests.rs b/rpc/src/v1/helpers/requests.rs index 7249e4c4a..2c14a4b99 100644 --- a/rpc/src/v1/helpers/requests.rs +++ b/rpc/src/v1/helpers/requests.rs @@ -33,6 +33,8 @@ pub struct TransactionRequest { pub data: Option, /// Transaction's nonce pub nonce: Option, + /// Delay until this block if specified. + pub min_block: Option, } /// Transaction request coming from RPC with default values filled in. @@ -52,6 +54,8 @@ pub struct FilledTransactionRequest { pub data: Bytes, /// Transaction's nonce pub nonce: Option, + /// Delay until this block if specified. + pub min_block: Option, } impl From for TransactionRequest { @@ -64,6 +68,7 @@ impl From for TransactionRequest { value: Some(r.value), data: Some(r.data), nonce: r.nonce, + min_block: r.min_block, } } } diff --git a/rpc/src/v1/helpers/signing_queue.rs b/rpc/src/v1/helpers/signing_queue.rs index 03489bad7..7f5d3a6d1 100644 --- a/rpc/src/v1/helpers/signing_queue.rs +++ b/rpc/src/v1/helpers/signing_queue.rs @@ -328,6 +328,7 @@ mod test { value: 10_000_000.into(), data: vec![], nonce: None, + min_block: None, }) } diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 1364af033..4bb5458cb 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -38,7 +38,7 @@ use ethcore::header::{Header as BlockHeader, BlockNumber as EthBlockNumber}; use ethcore::block::IsBlock; use ethcore::views::*; use ethcore::ethereum::Ethash; -use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action}; +use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, PendingTransaction, Action}; use ethcore::log_entry::LogEntry; use ethcore::filter::Filter as EthcoreFilter; use ethcore::snapshot::SnapshotService; @@ -613,7 +613,7 @@ impl Eth for EthClient where let raw_transaction = raw.to_vec(); match UntrustedRlp::new(&raw_transaction).as_val() { - Ok(signed_transaction) => dispatch_transaction(&*take_weak!(self.client), &*take_weak!(self.miner), signed_transaction).map(Into::into), + Ok(signed_transaction) => dispatch_transaction(&*take_weak!(self.client), &*take_weak!(self.miner), PendingTransaction::new(signed_transaction, None)).map(Into::into), Err(e) => Err(errors::from_rlp_error(e)), } } diff --git a/rpc/src/v1/impls/parity.rs b/rpc/src/v1/impls/parity.rs index 2c6a498a1..a2781b1c6 100644 --- a/rpc/src/v1/impls/parity.rs +++ b/rpc/src/v1/impls/parity.rs @@ -265,6 +265,12 @@ impl Parity for ParityClient where Ok(take_weak!(self.miner).all_transactions().into_iter().map(Into::into).collect::>()) } + fn future_transactions(&self) -> Result, Error> { + try!(self.active()); + + Ok(take_weak!(self.miner).future_transactions().into_iter().map(Into::into).collect::>()) + } + fn pending_transactions_stats(&self) -> Result, Error> { try!(self.active()); diff --git a/rpc/src/v1/impls/signer.rs b/rpc/src/v1/impls/signer.rs index 6e09a5ec8..5a81b8205 100644 --- a/rpc/src/v1/impls/signer.rs +++ b/rpc/src/v1/impls/signer.rs @@ -21,7 +21,7 @@ use std::sync::{Arc, Weak}; use rlp::{UntrustedRlp, View}; use ethcore::account_provider::AccountProvider; use ethcore::client::MiningBlockChainClient; -use ethcore::transaction::SignedTransaction; +use ethcore::transaction::{SignedTransaction, PendingTransaction}; use ethcore::miner::MinerService; use jsonrpc_core::Error; @@ -96,6 +96,9 @@ impl Signer for SignerClient where C: MiningBlockC if let Some(gas) = modification.gas { request.gas = gas.into(); } + if let Some(min_block) = modification.min_block { + request.min_block = min_block; + } } // Execute let result = dispatch::execute(&*client, &*miner, &*accounts, payload, Some(pass)); @@ -135,7 +138,8 @@ impl Signer for SignerClient where C: MiningBlockC // Dispatch if everything is ok if sender_matches && data_matches && value_matches && nonce_matches { - dispatch_transaction(&*client, &*miner, signed_transaction) + let pending_transaction = PendingTransaction::new(signed_transaction, request.min_block); + dispatch_transaction(&*client, &*miner, pending_transaction) .map(Into::into) .map(ConfirmationResponse::SendTransaction) } else { diff --git a/rpc/src/v1/tests/helpers/miner_service.rs b/rpc/src/v1/tests/helpers/miner_service.rs index 132e2a5e0..0ef0ee0a9 100644 --- a/rpc/src/v1/tests/helpers/miner_service.rs +++ b/rpc/src/v1/tests/helpers/miner_service.rs @@ -22,7 +22,7 @@ use ethcore::error::{Error, CallError}; use ethcore::client::{MiningBlockChainClient, Executed, CallAnalytics}; use ethcore::block::{ClosedBlock, IsBlock}; use ethcore::header::BlockNumber; -use ethcore::transaction::SignedTransaction; +use ethcore::transaction::{SignedTransaction, PendingTransaction}; use ethcore::receipt::{Receipt, RichReceipt}; use ethcore::miner::{MinerService, MinerStatus, TransactionImportResult, LocalTransactionStatus}; use ethcore::account_provider::Error as AccountError; @@ -160,17 +160,17 @@ impl MinerService for TestMinerService { } /// Imports transactions to transaction queue. - fn import_own_transaction(&self, chain: &MiningBlockChainClient, transaction: SignedTransaction) -> + fn import_own_transaction(&self, chain: &MiningBlockChainClient, pending: PendingTransaction) -> Result { // keep the pending nonces up to date - if let Ok(ref sender) = transaction.sender() { + if let Ok(ref sender) = pending.transaction.sender() { let nonce = self.last_nonce(sender).unwrap_or(chain.latest_nonce(sender)); self.last_nonces.write().insert(sender.clone(), nonce + U256::from(1)); } // lets assume that all txs are valid - self.imported_transactions.lock().push(transaction); + self.imported_transactions.lock().push(pending.transaction); Ok(TransactionImportResult::Current) } @@ -204,16 +204,20 @@ impl MinerService for TestMinerService { self.pending_transactions.lock().get(hash).cloned() } - fn all_transactions(&self) -> Vec { - self.pending_transactions.lock().values().cloned().collect() + fn all_transactions(&self) -> Vec { + self.pending_transactions.lock().values().cloned().map(Into::into).collect() } fn local_transactions(&self) -> BTreeMap { self.local_transactions.lock().iter().map(|(hash, stats)| (*hash, stats.clone())).collect() } - fn pending_transactions(&self, _best_block: BlockNumber) -> Vec { - self.pending_transactions.lock().values().cloned().collect() + fn pending_transactions(&self, _best_block: BlockNumber) -> Vec { + self.pending_transactions.lock().values().cloned().map(Into::into).collect() + } + + fn future_transactions(&self) -> Vec { + vec![] } fn pending_receipt(&self, _best_block: BlockNumber, hash: &H256) -> Option { diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index ea8409ef2..b8707a159 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -494,7 +494,7 @@ fn rpc_eth_pending_transaction_by_hash() { tester.miner.pending_transactions.lock().insert(H256::zero(), tx); } - let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x1","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","networkId":null,"nonce":"0x0","publicKey":"0x7ae46da747962c2ee46825839c1ef9298e3bd2e70ca2938495c3693a485ec3eaa8f196327881090ff64cf4fbb0a48485d4f83098e189ed3b7a87d5941b59f789","r":"0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353","raw":"0xf85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","s":"0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","standardV":"0x0","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"v":"0x1b","value":"0xa"},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x1","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","minBlock":null,"networkId":null,"nonce":"0x0","publicKey":"0x7ae46da747962c2ee46825839c1ef9298e3bd2e70ca2938495c3693a485ec3eaa8f196327881090ff64cf4fbb0a48485d4f83098e189ed3b7a87d5941b59f789","r":"0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353","raw":"0xf85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","s":"0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","standardV":"0x0","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"v":"0x1b","value":"0xa"},"id":1}"#; let request = r#"{ "jsonrpc": "2.0", "method": "eth_getTransactionByHash", @@ -810,6 +810,7 @@ fn rpc_eth_sign_transaction() { r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# + &format!("\"hash\":\"0x{:?}\",", t.hash()) + r#""input":"0x","# + + r#""minBlock":null,"# + &format!("\"networkId\":{},", t.network_id().map_or("null".to_owned(), |n| format!("{}", n))) + r#""nonce":"0x1","# + &format!("\"publicKey\":\"0x{:?}\",", t.public_key().unwrap()) + diff --git a/rpc/src/v1/tests/mocked/signer.rs b/rpc/src/v1/tests/mocked/signer.rs index 3537717d4..7ec023a96 100644 --- a/rpc/src/v1/tests/mocked/signer.rs +++ b/rpc/src/v1/tests/mocked/signer.rs @@ -82,6 +82,7 @@ fn should_return_list_of_items_to_confirm() { value: U256::from(1), data: vec![], nonce: None, + min_block: None, })).unwrap(); tester.signer.add_request(ConfirmationPayload::Signature(1.into(), 5.into())).unwrap(); @@ -89,7 +90,7 @@ fn should_return_list_of_items_to_confirm() { let request = r#"{"jsonrpc":"2.0","method":"signer_requestsToConfirm","params":[],"id":1}"#; let response = concat!( r#"{"jsonrpc":"2.0","result":["#, - r#"{"id":"0x1","payload":{"sendTransaction":{"data":"0x","from":"0x0000000000000000000000000000000000000001","gas":"0x989680","gasPrice":"0x2710","nonce":null,"to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","value":"0x1"}}},"#, + r#"{"id":"0x1","payload":{"sendTransaction":{"data":"0x","from":"0x0000000000000000000000000000000000000001","gas":"0x989680","gasPrice":"0x2710","minBlock":null,"nonce":null,"to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","value":"0x1"}}},"#, r#"{"id":"0x2","payload":{"sign":{"address":"0x0000000000000000000000000000000000000001","hash":"0x0000000000000000000000000000000000000000000000000000000000000005"}}}"#, r#"],"id":1}"# ); @@ -111,6 +112,7 @@ fn should_reject_transaction_from_queue_without_dispatching() { value: U256::from(1), data: vec![], nonce: None, + min_block: None, })).unwrap(); assert_eq!(tester.signer.requests().len(), 1); @@ -136,6 +138,7 @@ fn should_not_remove_transaction_if_password_is_invalid() { value: U256::from(1), data: vec![], nonce: None, + min_block: None, })).unwrap(); assert_eq!(tester.signer.requests().len(), 1); @@ -178,6 +181,7 @@ fn should_confirm_transaction_and_dispatch() { value: U256::from(1), data: vec![], nonce: None, + min_block: None, })).unwrap(); let t = Transaction { @@ -223,6 +227,7 @@ fn should_confirm_transaction_with_rlp() { value: U256::from(1), data: vec![], nonce: None, + min_block: None, })).unwrap(); let t = Transaction { @@ -270,6 +275,7 @@ fn should_return_error_when_sender_does_not_match() { value: U256::from(1), data: vec![], nonce: None, + min_block: None, })).unwrap(); let t = Transaction { diff --git a/rpc/src/v1/tests/mocked/signing.rs b/rpc/src/v1/tests/mocked/signing.rs index 5217ad402..4040b57b1 100644 --- a/rpc/src/v1/tests/mocked/signing.rs +++ b/rpc/src/v1/tests/mocked/signing.rs @@ -285,6 +285,7 @@ fn should_add_sign_transaction_to_the_queue() { r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# + &format!("\"hash\":\"0x{:?}\",", t.hash()) + r#""input":"0x","# + + r#""minBlock":null,"# + &format!("\"networkId\":{},", t.network_id().map_or("null".to_owned(), |n| format!("{}", n))) + r#""nonce":"0x1","# + &format!("\"publicKey\":\"0x{:?}\",", t.public_key().unwrap()) + diff --git a/rpc/src/v1/traits/parity.rs b/rpc/src/v1/traits/parity.rs index fecc05667..da7125f16 100644 --- a/rpc/src/v1/traits/parity.rs +++ b/rpc/src/v1/traits/parity.rs @@ -122,6 +122,10 @@ build_rpc_trait! { #[rpc(name = "parity_pendingTransactions")] fn pending_transactions(&self) -> Result, Error>; + /// Returns all future transactions from transaction queue. + #[rpc(name = "parity_futureTransactions")] + fn future_transactions(&self) -> Result, Error>; + /// Returns propagation statistics on transactions pending in the queue. #[rpc(name = "parity_pendingTransactionsStats")] fn pending_transactions_stats(&self) -> Result, Error>; diff --git a/rpc/src/v1/types/block.rs b/rpc/src/v1/types/block.rs index 6dd441ee8..680cbd562 100644 --- a/rpc/src/v1/types/block.rs +++ b/rpc/src/v1/types/block.rs @@ -139,7 +139,7 @@ mod tests { fn test_serialize_block_transactions() { let t = BlockTransactions::Full(vec![Transaction::default()]); let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"networkId":null,"standardV":"0x0","v":"0x0","r":"0x0","s":"0x0"}]"#); + assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"networkId":null,"standardV":"0x0","v":"0x0","r":"0x0","s":"0x0","minBlock":null}]"#); let t = BlockTransactions::Hashes(vec![H256::default().into()]); let serialized = serde_json::to_string(&t).unwrap(); diff --git a/rpc/src/v1/types/confirmations.rs b/rpc/src/v1/types/confirmations.rs index fd81bf6e7..92567575e 100644 --- a/rpc/src/v1/types/confirmations.rs +++ b/rpc/src/v1/types/confirmations.rs @@ -144,6 +144,9 @@ pub struct TransactionModification { pub gas_price: Option, /// Modified gas pub gas: Option, + /// Modified min block + #[serde(rename="minBlock")] + pub min_block: Option>, } /// Represents two possible return values. @@ -218,12 +221,13 @@ mod tests { value: 100_000.into(), data: vec![1, 2, 3], nonce: Some(1.into()), + min_block: None, }), }; // when let res = serde_json::to_string(&ConfirmationRequest::from(request)); - let expected = r#"{"id":"0xf","payload":{"sendTransaction":{"from":"0x0000000000000000000000000000000000000000","to":null,"gasPrice":"0x2710","gas":"0x3a98","value":"0x186a0","data":"0x010203","nonce":"0x1"}}}"#; + let expected = r#"{"id":"0xf","payload":{"sendTransaction":{"from":"0x0000000000000000000000000000000000000000","to":null,"gasPrice":"0x2710","gas":"0x3a98","value":"0x186a0","data":"0x010203","nonce":"0x1","minBlock":null}}}"#; // then assert_eq!(res.unwrap(), expected.to_owned()); @@ -242,12 +246,13 @@ mod tests { value: 100_000.into(), data: vec![1, 2, 3], nonce: Some(1.into()), + min_block: None, }), }; // when let res = serde_json::to_string(&ConfirmationRequest::from(request)); - let expected = r#"{"id":"0xf","payload":{"signTransaction":{"from":"0x0000000000000000000000000000000000000000","to":null,"gasPrice":"0x2710","gas":"0x3a98","value":"0x186a0","data":"0x010203","nonce":"0x1"}}}"#; + let expected = r#"{"id":"0xf","payload":{"signTransaction":{"from":"0x0000000000000000000000000000000000000000","to":null,"gasPrice":"0x2710","gas":"0x3a98","value":"0x186a0","data":"0x010203","nonce":"0x1","minBlock":null}}}"#; // then assert_eq!(res.unwrap(), expected.to_owned()); @@ -275,7 +280,8 @@ mod tests { fn should_deserialize_modification() { // given let s1 = r#"{ - "gasPrice":"0xba43b7400" + "gasPrice":"0xba43b7400", + "minBlock":42 }"#; let s2 = r#"{"gas": "0x1233"}"#; let s3 = r#"{}"#; @@ -289,14 +295,17 @@ mod tests { assert_eq!(res1, TransactionModification { gas_price: Some(U256::from_str("0ba43b7400").unwrap()), gas: None, + min_block: Some(Some(42)), }); assert_eq!(res2, TransactionModification { gas_price: None, gas: Some(U256::from_str("1233").unwrap()), + min_block: None, }); assert_eq!(res3, TransactionModification { gas_price: None, gas: None, + min_block: None, }); } } diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index 31374e912..04275323d 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -17,7 +17,7 @@ use serde::{Serialize, Serializer}; use ethcore::miner; use ethcore::contract_address; -use ethcore::transaction::{LocalizedTransaction, Action, SignedTransaction}; +use ethcore::transaction::{LocalizedTransaction, Action, PendingTransaction, SignedTransaction}; use v1::helpers::errors; use v1::types::{Bytes, H160, H256, U256, H512}; @@ -69,6 +69,9 @@ pub struct Transaction { pub r: U256, /// The S field of the signature. pub s: U256, + /// Transaction activates at specified block. + #[serde(rename="minBlock")] + pub min_block: Option, } /// Local Transaction Status @@ -187,6 +190,7 @@ impl From for Transaction { v: t.original_v().into(), r: signature.r().into(), s: signature.s().into(), + min_block: None, } } } @@ -220,10 +224,19 @@ impl From for Transaction { v: t.original_v().into(), r: signature.r().into(), s: signature.s().into(), + min_block: None, } } } +impl From for Transaction { + fn from(t: PendingTransaction) -> Transaction { + let mut r = Transaction::from(t.transaction); + r.min_block = t.min_block; + r + } +} + impl From for LocalTransactionStatus { fn from(s: miner::LocalTransactionStatus) -> Self { use ethcore::miner::LocalTransactionStatus::*; @@ -248,7 +261,7 @@ mod tests { fn test_transaction_serialize() { let t = Transaction::default(); let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"networkId":null,"standardV":"0x0","v":"0x0","r":"0x0","s":"0x0"}"#); + assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"networkId":null,"standardV":"0x0","v":"0x0","r":"0x0","s":"0x0","minBlock":null}"#); } #[test] diff --git a/rpc/src/v1/types/transaction_request.rs b/rpc/src/v1/types/transaction_request.rs index 258346d56..389efa467 100644 --- a/rpc/src/v1/types/transaction_request.rs +++ b/rpc/src/v1/types/transaction_request.rs @@ -38,6 +38,9 @@ pub struct TransactionRequest { pub data: Option, /// Transaction's nonce pub nonce: Option, + /// Delay until this block if specified. + #[serde(rename="minBlock")] + pub min_block: Option, } impl From for TransactionRequest { @@ -50,6 +53,7 @@ impl From for TransactionRequest { value: r.value.map(Into::into), data: r.data.map(Into::into), nonce: r.nonce.map(Into::into), + min_block: r.min_block, } } } @@ -64,6 +68,7 @@ impl From for TransactionRequest { value: Some(r.value.into()), data: Some(r.data.into()), nonce: r.nonce.map(Into::into), + min_block: r.min_block, } } } @@ -78,6 +83,7 @@ impl Into for TransactionRequest { value: self.value.map(Into::into), data: self.data.map(Into::into), nonce: self.nonce.map(Into::into), + min_block: self.min_block, } } } @@ -100,7 +106,8 @@ mod tests { "gas":"0x2", "value":"0x3", "data":"0x123456", - "nonce":"0x4" + "nonce":"0x4", + "minBlock":13 }"#; let deserialized: TransactionRequest = serde_json::from_str(s).unwrap(); @@ -112,6 +119,7 @@ mod tests { value: Some(U256::from(3)), data: Some(vec![0x12, 0x34, 0x56].into()), nonce: Some(U256::from(4)), + min_block: Some(13), }); } @@ -134,7 +142,8 @@ mod tests { gas: Some(U256::from_str("76c0").unwrap()), value: Some(U256::from_str("9184e72a").unwrap()), data: Some("d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675".from_hex().unwrap().into()), - nonce: None + nonce: None, + min_block: None, }); } @@ -151,6 +160,7 @@ mod tests { value: None, data: None, nonce: None, + min_block: None, }); } @@ -174,6 +184,7 @@ mod tests { value: None, data: Some(vec![0x85, 0x95, 0xba, 0xb1].into()), nonce: None, + min_block: None, }); } diff --git a/sync/src/chain.rs b/sync/src/chain.rs index 350a42d0e..67d65ad84 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -1901,10 +1901,10 @@ impl ChainSync { return 0; } - let all_transactions_hashes = transactions.iter().map(|tx| tx.hash()).collect::>(); + let all_transactions_hashes = transactions.iter().map(|tx| tx.transaction.hash()).collect::>(); let all_transactions_rlp = { let mut packet = RlpStream::new_list(transactions.len()); - for tx in &transactions { packet.append(tx); } + for tx in &transactions { packet.append(&tx.transaction); } packet.out() }; @@ -1942,11 +1942,11 @@ impl ChainSync { // Construct RLP let mut packet = RlpStream::new_list(to_send.len()); for tx in &transactions { - if to_send.contains(&tx.hash()) { - packet.append(tx); + if to_send.contains(&tx.transaction.hash()) { + packet.append(&tx.transaction); // update stats let id = io.peer_session_info(*peer_id).and_then(|info| info.id); - stats.propagated(tx.hash(), id, block_number); + stats.propagated(tx.transaction.hash(), id, block_number); } } diff --git a/sync/src/tests/consensus.rs b/sync/src/tests/consensus.rs index b96997d1e..78da9258b 100644 --- a/sync/src/tests/consensus.rs +++ b/sync/src/tests/consensus.rs @@ -55,7 +55,7 @@ fn test_authority_round() { }.sign(s1.secret(), None); // exhange statuses net.sync_steps(5); - net.peer(0).chain.miner().import_own_transaction(&net.peer(0).chain, tx1).unwrap(); + net.peer(0).chain.miner().import_own_transaction(&net.peer(0).chain, PendingTransaction::new(tx1, None)).unwrap(); net.sync(); assert_eq!(net.peer(0).chain.chain_info().best_block_number, 1); assert_eq!(net.peer(1).chain.chain_info().best_block_number, 1); @@ -68,7 +68,7 @@ fn test_authority_round() { value: 0.into(), data: Vec::new(), }.sign(s2.secret(), None); - net.peer(1).chain.miner().import_own_transaction(&net.peer(1).chain, tx2).unwrap(); + net.peer(1).chain.miner().import_own_transaction(&net.peer(1).chain, PendingTransaction::new(tx2, None)).unwrap(); net.peer(1).chain.engine().step(); net.peer(1).chain.miner().update_sealing(&net.peer(1).chain); net.sync();