From 701399fc77b2d692b8f7c8698b9bc170da9f6894 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Thu, 15 Dec 2016 10:23:01 +0100 Subject: [PATCH 01/18] Don't log auth token --- js/src/secureApi.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/js/src/secureApi.js b/js/src/secureApi.js index 80e76720b..d91a974d7 100644 --- a/js/src/secureApi.js +++ b/js/src/secureApi.js @@ -38,7 +38,7 @@ export default class SecureApi extends Api { setToken = () => { window.localStorage.setItem('sysuiToken', this._transport.token); - console.log('SecureApi:setToken', this._transport.token); + // DEBUG: console.log('SecureApi:setToken', this._transport.token); } _checkNodeUp () { @@ -149,14 +149,14 @@ export default class SecureApi extends Api { this._signerPort = signerPort.toNumber(); }); - console.log('SecureApi:connectSuccess', this._transport.token); + // DEBUG: console.log('SecureApi:connectSuccess', this._transport.token); } updateToken (token, connectState = 0) { this._connectState = connectState; this._transport.updateToken(token.replace(/[^a-zA-Z0-9]/g, '')); this._followConnection(); - console.log('SecureApi:updateToken', this._transport.token, connectState); + // DEBUG: console.log('SecureApi:updateToken', this._transport.token, connectState); } get dappsPort () { From 2952ea1b854d0e5f8d3f69ed14320014887bad59 Mon Sep 17 00:00:00 2001 From: arkpar Date: Thu, 15 Dec 2016 18:19:19 +0100 Subject: [PATCH 02/18] Delayed transactions --- ethcore/light/src/client.rs | 6 +- ethcore/light/src/net/tests/mod.rs | 4 +- ethcore/light/src/provider.rs | 8 +- ethcore/res/ethereum/tests | 2 +- ethcore/src/client/client.rs | 4 +- ethcore/src/client/test_client.rs | 4 +- ethcore/src/client/traits.rs | 4 +- ethcore/src/miner/banning_queue.rs | 4 +- ethcore/src/miner/miner.rs | 57 ++-- ethcore/src/miner/mod.rs | 13 +- ethcore/src/miner/transaction_queue.rs | 341 +++++++++++++--------- ethcore/src/types/transaction.rs | 28 ++ rpc/src/v1/helpers/dispatch.rs | 11 +- rpc/src/v1/helpers/requests.rs | 5 + rpc/src/v1/helpers/signing_queue.rs | 1 + rpc/src/v1/impls/eth.rs | 4 +- rpc/src/v1/impls/parity.rs | 6 + rpc/src/v1/impls/signer.rs | 8 +- rpc/src/v1/tests/helpers/miner_service.rs | 20 +- rpc/src/v1/tests/mocked/eth.rs | 3 +- rpc/src/v1/tests/mocked/signer.rs | 8 +- rpc/src/v1/tests/mocked/signing.rs | 1 + rpc/src/v1/traits/parity.rs | 4 + rpc/src/v1/types/block.rs | 2 +- rpc/src/v1/types/confirmations.rs | 15 +- rpc/src/v1/types/transaction.rs | 17 +- rpc/src/v1/types/transaction_request.rs | 15 +- sync/src/chain.rs | 10 +- sync/src/tests/consensus.rs | 4 +- 29 files changed, 387 insertions(+), 222 deletions(-) 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(); From 3ac0794d2862288dcce12a5f56d689aa186e81b7 Mon Sep 17 00:00:00 2001 From: arkpar Date: Thu, 15 Dec 2016 19:31:26 +0100 Subject: [PATCH 03/18] Bump jsonrpc-core for rpc_cli --- Cargo.lock | 27 +-------------------------- rpc_client/Cargo.toml | 2 +- rpc_client/src/client.rs | 8 ++++---- 3 files changed, 6 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0caea9b8e..6285f159b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -880,18 +880,6 @@ name = "itoa" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "jsonrpc-core" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_codegen 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "jsonrpc-core" version = "4.0.0" @@ -1332,7 +1320,7 @@ dependencies = [ "ethcore-signer 1.5.0", "ethcore-util 1.5.0", "futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1368,17 +1356,6 @@ dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "parking_lot" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "parking_lot" version = "0.3.6" @@ -2182,7 +2159,6 @@ dependencies = [ "checksum isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7408a548dc0e406b7912d9f84c261cc533c1866e047644a811c133c56041ac0c" "checksum itertools 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "086e1fa5fe48840b1cfdef3a20c7e3115599f8d5c4c87ef32a794a7cdd184d76" "checksum itoa 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae3088ea4baeceb0284ee9eea42f591226e6beaecf65373e41b38d95a1b8e7a1" -"checksum jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3c5094610b07f28f3edaf3947b732dadb31dbba4941d4d0c1c7a8350208f4414" "checksum jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)" = "" "checksum jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc.git)" = "" "checksum jsonrpc-ipc-server 0.2.4 (git+https://github.com/ethcore/jsonrpc.git)" = "" @@ -2230,7 +2206,6 @@ dependencies = [ "checksum owning_ref 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8d91377085359426407a287ab16884a0111ba473aa6844ff01d4ec20ce3d75e7" "checksum parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "98378dec0a185da2b7180308752f0bad73aaa949c3e0a3b0528d0e067945f7ab" "checksum parity-ui-precompiled 1.4.0 (git+https://github.com/ethcore/js-precompiled.git)" = "" -"checksum parking_lot 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "968f685642555d2f7e202c48b8b11de80569e9bfea817f7f12d7c61aac62d4e6" "checksum parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e1435e7a2a00dfebededd6c6bdbd54008001e94b4a2aadd6aef0dc4c56317621" "checksum parking_lot_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb1b97670a2ffadce7c397fb80a3d687c4f3060140b885621ef1653d0e5d5068" "checksum phf 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)" = "447d9d45f2e0b4a9b532e808365abf18fc211be6ca217202fcd45236ef12f026" diff --git a/rpc_client/Cargo.toml b/rpc_client/Cargo.toml index 9b93a1a5b..e5c8e9b12 100644 --- a/rpc_client/Cargo.toml +++ b/rpc_client/Cargo.toml @@ -8,7 +8,6 @@ version = "1.4.0" [dependencies] futures = "0.1" -jsonrpc-core = "3.0.2" lazy_static = "0.2.1" log = "0.3.6" matches = "0.1.2" @@ -17,6 +16,7 @@ serde = "0.8" serde_json = "0.8" tempdir = "0.3.5" url = "1.2.0" +jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git" } ws = { git = "https://github.com/ethcore/ws-rs.git", branch = "mio-upstream-stable" } ethcore-rpc = { path = "../rpc" } ethcore-signer = { path = "../signer" } diff --git a/rpc_client/src/client.rs b/rpc_client/src/client.rs index 9ee9f5c9d..c32b5be7a 100644 --- a/rpc_client/src/client.rs +++ b/rpc_client/src/client.rs @@ -36,7 +36,7 @@ use futures::{BoxFuture, Canceled, Complete, Future, oneshot, done}; use jsonrpc_core::{Id, Version, Params, Error as JsonRpcError}; use jsonrpc_core::request::MethodCall; -use jsonrpc_core::response::{SyncOutput, Success, Failure}; +use jsonrpc_core::response::{Output, Success, Failure}; /// The actual websocket connection handler, passed into the /// event loop of ws-rs @@ -107,13 +107,13 @@ impl Handler for RpcHandler { let ret: Result; let response_id; let string = &msg.to_string(); - match json::from_str::(&string) { - Ok(SyncOutput::Success(Success { result, id: Id::Num(id), .. })) => + match json::from_str::(&string) { + Ok(Output::Success(Success { result, id: Id::Num(id), .. })) => { ret = Ok(result); response_id = id as usize; } - Ok(SyncOutput::Failure(Failure { error, id: Id::Num(id), .. })) => { + Ok(Output::Failure(Failure { error, id: Id::Num(id), .. })) => { ret = Err(error); response_id = id as usize; } From 27580586e00c97de272fe0237629e2a778a5f40b Mon Sep 17 00:00:00 2001 From: arkpar Date: Thu, 15 Dec 2016 20:09:05 +0100 Subject: [PATCH 04/18] Minor tweaks --- ethcore/src/miner/transaction_queue.rs | 2 +- ethcore/src/types/transaction.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ethcore/src/miner/transaction_queue.rs b/ethcore/src/miner/transaction_queue.rs index a13fc01c4..1c8a3d17d 100644 --- a/ethcore/src/miner/transaction_queue.rs +++ b/ethcore/src/miner/transaction_queue.rs @@ -984,7 +984,7 @@ impl TransactionQueue { 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"); + let sender = tx.sender(); if delayed.contains(&sender) { continue; } diff --git a/ethcore/src/types/transaction.rs b/ethcore/src/types/transaction.rs index 681e38fcc..25e91afe5 100644 --- a/ethcore/src/types/transaction.rs +++ b/ethcore/src/types/transaction.rs @@ -390,7 +390,7 @@ impl Deref for LocalizedTransaction { } } -/// Queued information with additional information. +/// Queued transaction with additional information. #[derive(Debug, Clone, PartialEq, Eq, Binary)] pub struct PendingTransaction { /// Signed transaction data. From e1dd986c413e9a20177d2fcbe42580886858fc2f Mon Sep 17 00:00:00 2001 From: arkpar Date: Thu, 15 Dec 2016 21:16:32 +0100 Subject: [PATCH 05/18] Hex-encoded block numbers --- rpc/src/v1/impls/signer.rs | 4 ++-- rpc/src/v1/types/block_number.rs | 25 +++++++++++++++++++++++-- rpc/src/v1/types/confirmations.rs | 10 +++++----- rpc/src/v1/types/transaction.rs | 6 +++--- rpc/src/v1/types/transaction_request.rs | 16 ++++++++-------- 5 files changed, 41 insertions(+), 20 deletions(-) diff --git a/rpc/src/v1/impls/signer.rs b/rpc/src/v1/impls/signer.rs index 6dc813b73..375cfdf6e 100644 --- a/rpc/src/v1/impls/signer.rs +++ b/rpc/src/v1/impls/signer.rs @@ -82,8 +82,8 @@ impl SignerClient where C: MiningBlockChainClient, if let Some(gas) = modification.gas { request.gas = gas.into(); } - if let Some(min_block) = modification.min_block { - request.min_block = min_block; + if let Some(ref min_block) = modification.min_block { + request.min_block = min_block.as_ref().and_then(|b| b.to_min_block_num()); } } let result = f(&*client, &*miner, &*accounts, payload); diff --git a/rpc/src/v1/types/block_number.rs b/rpc/src/v1/types/block_number.rs index 00ee1f22b..65ebd76be 100644 --- a/rpc/src/v1/types/block_number.rs +++ b/rpc/src/v1/types/block_number.rs @@ -14,12 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use serde::{Deserialize, Deserializer, Error}; +use serde::{Deserialize, Deserializer, Error, Serialize, Serializer}; use serde::de::Visitor; use ethcore::client::BlockId; /// Represents rpc api block number param. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Hash, Eq)] pub enum BlockNumber { /// Number Num(u64), @@ -44,6 +44,27 @@ impl Deserialize for BlockNumber { } } +impl BlockNumber { + /// Convert block number to min block target. + pub fn to_min_block_num(&self) -> Option { + match *self { + BlockNumber::Num(ref x) => Some(*x), + _ => None, + } + } +} + +impl Serialize for BlockNumber { + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer { + match *self { + BlockNumber::Num(ref x) => serializer.serialize_str(&format!("0x{:x}", x)), + BlockNumber::Latest => serializer.serialize_str("latest"), + BlockNumber::Earliest => serializer.serialize_str("earliest"), + BlockNumber::Pending => serializer.serialize_str("pending"), + } + } +} + struct BlockNumberVisitor; impl Visitor for BlockNumberVisitor { diff --git a/rpc/src/v1/types/confirmations.rs b/rpc/src/v1/types/confirmations.rs index 0df3e4b52..5f79f675d 100644 --- a/rpc/src/v1/types/confirmations.rs +++ b/rpc/src/v1/types/confirmations.rs @@ -20,7 +20,7 @@ use std::fmt; use serde::{Serialize, Serializer}; use util::log::Colour; -use v1::types::{U256, TransactionRequest, RichRawTransaction, H160, H256, H520, Bytes}; +use v1::types::{U256, TransactionRequest, RichRawTransaction, H160, H256, H520, Bytes, BlockNumber}; use v1::helpers; /// Confirmation waiting in a queue @@ -195,7 +195,7 @@ pub struct TransactionModification { pub gas: Option, /// Modified min block #[serde(rename="minBlock")] - pub min_block: Option>, + pub min_block: Option>, } /// Represents two possible return values. @@ -237,7 +237,7 @@ impl Serialize for Either where mod tests { use std::str::FromStr; use serde_json; - use v1::types::{U256, H256}; + use v1::types::{U256, H256, BlockNumber}; use v1::helpers; use super::*; @@ -330,7 +330,7 @@ mod tests { // given let s1 = r#"{ "gasPrice":"0xba43b7400", - "minBlock":42 + "minBlock":"0x42" }"#; let s2 = r#"{"gas": "0x1233"}"#; let s3 = r#"{}"#; @@ -344,7 +344,7 @@ mod tests { assert_eq!(res1, TransactionModification { gas_price: Some(U256::from_str("0ba43b7400").unwrap()), gas: None, - min_block: Some(Some(42)), + min_block: Some(Some(BlockNumber::Num(0x42))), }); assert_eq!(res2, TransactionModification { gas_price: None, diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index 04275323d..0db614887 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -19,7 +19,7 @@ use ethcore::miner; use ethcore::contract_address; use ethcore::transaction::{LocalizedTransaction, Action, PendingTransaction, SignedTransaction}; use v1::helpers::errors; -use v1::types::{Bytes, H160, H256, U256, H512}; +use v1::types::{Bytes, H160, H256, U256, H512, BlockNumber}; /// Transaction #[derive(Debug, Default, Clone, PartialEq, Serialize)] @@ -71,7 +71,7 @@ pub struct Transaction { pub s: U256, /// Transaction activates at specified block. #[serde(rename="minBlock")] - pub min_block: Option, + pub min_block: Option, } /// Local Transaction Status @@ -232,7 +232,7 @@ impl From for Transaction { impl From for Transaction { fn from(t: PendingTransaction) -> Transaction { let mut r = Transaction::from(t.transaction); - r.min_block = t.min_block; + r.min_block = t.min_block.map(|b| BlockNumber::Num(b)); r } } diff --git a/rpc/src/v1/types/transaction_request.rs b/rpc/src/v1/types/transaction_request.rs index 07ab88588..49cf37ce0 100644 --- a/rpc/src/v1/types/transaction_request.rs +++ b/rpc/src/v1/types/transaction_request.rs @@ -16,7 +16,7 @@ //! `TransactionRequest` type -use v1::types::{Bytes, H160, U256}; +use v1::types::{Bytes, H160, U256, BlockNumber}; use v1::helpers; use util::log::Colour; @@ -43,7 +43,7 @@ pub struct TransactionRequest { pub nonce: Option, /// Delay until this block if specified. #[serde(rename="minBlock")] - pub min_block: Option, + pub min_block: Option, } pub fn format_ether(i: U256) -> String { @@ -93,7 +93,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, + min_block: r.min_block.map(|b| BlockNumber::Num(b)), } } } @@ -108,7 +108,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, + min_block: r.min_block.map(|b| BlockNumber::Num(b)), } } } @@ -123,7 +123,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, + min_block: self.min_block.and_then(|b| b.to_min_block_num()), } } } @@ -134,7 +134,7 @@ mod tests { use std::str::FromStr; use rustc_serialize::hex::FromHex; use serde_json; - use v1::types::{U256, H160}; + use v1::types::{U256, H160, BlockNumber}; use super::*; #[test] @@ -147,7 +147,7 @@ mod tests { "value":"0x3", "data":"0x123456", "nonce":"0x4", - "minBlock":13 + "minBlock":"0x13" }"#; let deserialized: TransactionRequest = serde_json::from_str(s).unwrap(); @@ -159,7 +159,7 @@ mod tests { value: Some(U256::from(3)), data: Some(vec![0x12, 0x34, 0x56].into()), nonce: Some(U256::from(4)), - min_block: Some(13), + min_block: Some(BlockNumber::Num(0x13)), }); } From 060cc799a627392578efd164e831786e50b94cf5 Mon Sep 17 00:00:00 2001 From: arkpar Date: Thu, 15 Dec 2016 21:44:31 +0100 Subject: [PATCH 06/18] Minor tweaks --- ethcore/src/miner/transaction_queue.rs | 6 +++--- rpc_client/src/signer_client.rs | 6 ++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/ethcore/src/miner/transaction_queue.rs b/ethcore/src/miner/transaction_queue.rs index 1c8a3d17d..9f5b6399a 100644 --- a/ethcore/src/miner/transaction_queue.rs +++ b/ethcore/src/miner/transaction_queue.rs @@ -978,7 +978,7 @@ impl TransactionQueue { } - fn collect_pending_transaction(&self, best_block: BlockNumber, mut f: F) + fn filter_pending_transaction(&self, best_block: BlockNumber, mut f: F) where F: FnMut(&VerifiedTransaction) { let mut delayed = HashSet::new(); @@ -999,14 +999,14 @@ impl TransactionQueue { /// 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())); + self.filter_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))); + self.filter_pending_transaction(best_block, |tx| r.push(PendingTransaction::new(tx.transaction.clone(), tx.min_block))); r } diff --git a/rpc_client/src/signer_client.rs b/rpc_client/src/signer_client.rs index 996fb0787..6284111ca 100644 --- a/rpc_client/src/signer_client.rs +++ b/rpc_client/src/signer_client.rs @@ -1,7 +1,5 @@ use client::{Rpc, RpcError}; -use rpc::v1::types::{ConfirmationRequest, - TransactionModification, - U256}; +use rpc::v1::types::{ConfirmationRequest, TransactionModification, U256, BlockNumber}; use serde_json::{Value as JsonValue, to_value}; use std::path::PathBuf; use futures::{BoxFuture, Canceled}; @@ -24,7 +22,7 @@ impl SignerRpc { id: U256, new_gas: Option, new_gas_price: Option, - new_min_block: Option>, + new_min_block: Option>, pwd: &str ) -> BoxFuture, Canceled> { From 3450538208409ca522e6ae1ce4b8e8c9f55af8db Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Fri, 16 Dec 2016 12:22:56 +0000 Subject: [PATCH 07/18] [ci skip] js-precompiled 20161216-121923 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 25c241c12..97bd90491 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1379,7 +1379,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#2cdda91549dfeebd94775b348a443f8ee5446e9f" +source = "git+https://github.com/ethcore/js-precompiled.git#3d390b35737ce212d358f26b5ec8d9644b252a88" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index edaf527cd..6f8693b39 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.2.127", + "version": "0.2.128", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From f967713d0815b96e2316bf7904cd60c26185fbd6 Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 16 Dec 2016 13:27:14 +0100 Subject: [PATCH 08/18] Added a test --- ethcore/src/tests/client.rs | 37 ++++++++++++++++++++++++++++++++ ethcore/src/types/transaction.rs | 2 +- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index 51c7086b6..2e9087133 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -28,6 +28,9 @@ use rlp::{Rlp, View}; use spec::Spec; use views::BlockView; use util::stats::Histogram; +use ethkey::KeyPair; +use transaction::{PendingTransaction, Transaction, Action}; +use miner::MinerService; #[test] fn imports_from_empty() { @@ -284,3 +287,37 @@ fn change_history_size() { let client = Client::new(config, &test_spec, dir.as_path(), Arc::new(Miner::with_spec(&test_spec)), IoChannel::disconnected(), &db_config).unwrap(); assert_eq!(client.state().balance(&address), 100.into()); } + +#[test] +fn does_not_propagate_delayed_transactions() { + let key = KeyPair::from_secret("test".sha3()).unwrap(); + let secret = key.secret(); + let tx0 = PendingTransaction::new(Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 21000.into(), + action: Action::Call(Address::default()), + value: 0.into(), + data: Vec::new(), + }.sign(secret, None), Some(2)); + let tx1 = PendingTransaction::new(Transaction { + nonce: 1.into(), + gas_price: 0.into(), + gas: 21000.into(), + action: Action::Call(Address::default()), + value: 0.into(), + data: Vec::new(), + }.sign(secret, None), None); + let client_result = generate_dummy_client(1); + let client = client_result.reference(); + + client.miner().import_own_transaction(&**client, tx0).unwrap(); + client.miner().import_own_transaction(&**client, tx1).unwrap(); + assert_eq!(0, client.pending_transactions().len()); + assert_eq!(2, client.miner().all_transactions().len()); + push_blocks_to_client(client, 53, 2, 1); + client.import_verified_blocks(); + assert_eq!(2, client.pending_transactions().len()); + assert_eq!(2, client.miner().all_transactions().len()); +} + diff --git a/ethcore/src/types/transaction.rs b/ethcore/src/types/transaction.rs index 25e91afe5..2cd001bdb 100644 --- a/ethcore/src/types/transaction.rs +++ b/ethcore/src/types/transaction.rs @@ -395,7 +395,7 @@ impl Deref for LocalizedTransaction { pub struct PendingTransaction { /// Signed transaction data. pub transaction: SignedTransaction, - /// Gas price. + /// To be activated at this block. `None` for immediately. pub min_block: Option, } From 65f07e5aa73b9c5635dc7c14771e003231f60cba Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 16 Dec 2016 14:54:26 +0100 Subject: [PATCH 09/18] Renamed some functions --- ethcore/light/src/client.rs | 2 +- ethcore/light/src/net/tests/mod.rs | 4 ++-- ethcore/light/src/provider.rs | 6 +++--- ethcore/src/client/client.rs | 4 ++-- ethcore/src/client/test_client.rs | 4 ++-- ethcore/src/client/traits.rs | 4 ++-- ethcore/src/miner/miner.rs | 16 ++++++++-------- ethcore/src/miner/mod.rs | 8 ++++---- ethcore/src/tests/client.rs | 10 +++++----- rpc/src/v1/impls/parity.rs | 2 +- rpc/src/v1/tests/helpers/miner_service.rs | 4 ++-- sync/src/chain.rs | 2 +- 12 files changed, 33 insertions(+), 33 deletions(-) diff --git a/ethcore/light/src/client.rs b/ethcore/light/src/client.rs index 80100f17c..edadc440c 100644 --- a/ethcore/light/src/client.rs +++ b/ethcore/light/src/client.rs @@ -114,7 +114,7 @@ impl Provider for Client { Vec::new() } - fn pending_transactions(&self) -> Vec { + fn ready_transactions(&self) -> Vec { Vec::new() } } diff --git a/ethcore/light/src/net/tests/mod.rs b/ethcore/light/src/net/tests/mod.rs index f9551fc9a..64d53d9c8 100644 --- a/ethcore/light/src/net/tests/mod.rs +++ b/ethcore/light/src/net/tests/mod.rs @@ -169,8 +169,8 @@ impl Provider for TestProvider { req.requests.into_iter().map(|_| ::rlp::EMPTY_LIST_RLP.to_vec()).collect() } - fn pending_transactions(&self) -> Vec { - self.0.client.pending_transactions() + fn ready_transactions(&self) -> Vec { + self.0.client.ready_transactions() } } diff --git a/ethcore/light/src/provider.rs b/ethcore/light/src/provider.rs index ce80929c2..1f9bbf8aa 100644 --- a/ethcore/light/src/provider.rs +++ b/ethcore/light/src/provider.rs @@ -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 ready_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 { - BlockChainClient::pending_transactions(self) + fn ready_transactions(&self) -> Vec { + BlockChainClient::ready_transactions(self) } } diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 4e54f22b1..25f000c89 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -1334,8 +1334,8 @@ impl BlockChainClient for Client { } } - fn pending_transactions(&self) -> Vec { - self.miner.pending_transactions(self.chain.read().best_block_number()) + fn ready_transactions(&self) -> Vec { + self.miner.ready_transactions(self.chain.read().best_block_number()) } fn queue_consensus_message(&self, message: Bytes) { diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index a3344dab7..a384f1227 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -688,8 +688,8 @@ impl BlockChainClient for TestBlockChainClient { fn broadcast_consensus_message(&self, _message: Bytes) {} - fn pending_transactions(&self) -> Vec { - self.miner.pending_transactions(self.chain_info().best_block_number) + fn ready_transactions(&self) -> Vec { + self.miner.ready_transactions(self.chain_info().best_block_number) } fn signing_network_id(&self) -> Option { None } diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index 4c3b2a37e..caba647d1 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -211,8 +211,8 @@ pub trait BlockChainClient : Sync + Send { /// Used by PoA to communicate with peers. fn broadcast_consensus_message(&self, message: Bytes); - /// list all transactions - fn pending_transactions(&self) -> Vec; + /// List all transactions that are allowed into the next block. + fn ready_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/miner.rs b/ethcore/src/miner/miner.rs index 3002a1ffc..c7bff4784 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -909,7 +909,7 @@ impl MinerService for Miner { imported } - fn all_transactions(&self) -> Vec { + fn pending_transactions(&self) -> Vec { let queue = self.transaction_queue.lock(); queue.pending_transactions(BlockNumber::max_value()) } @@ -926,7 +926,7 @@ impl MinerService for Miner { self.transaction_queue.lock().future_transactions() } - fn pending_transactions(&self, best_block: BlockNumber) -> Vec { + fn ready_transactions(&self, best_block: BlockNumber) -> Vec { let queue = self.transaction_queue.lock(); match self.options.pending_set { PendingSet::AlwaysQueue => queue.pending_transactions(best_block), @@ -1253,8 +1253,8 @@ mod tests { // then assert_eq!(res.unwrap(), TransactionImportResult::Current); - assert_eq!(miner.all_transactions().len(), 1); - assert_eq!(miner.pending_transactions(best_block).len(), 1); + assert_eq!(miner.pending_transactions().len(), 1); + assert_eq!(miner.ready_transactions(best_block).len(), 1); assert_eq!(miner.pending_transactions_hashes(best_block).len(), 1); assert_eq!(miner.pending_receipts(best_block).len(), 1); // This method will let us know if pending block was created (before calling that method) @@ -1273,8 +1273,8 @@ mod tests { // then assert_eq!(res.unwrap(), TransactionImportResult::Current); - assert_eq!(miner.all_transactions().len(), 1); - assert_eq!(miner.pending_transactions(best_block).len(), 0); + assert_eq!(miner.pending_transactions().len(), 1); + assert_eq!(miner.ready_transactions(best_block).len(), 0); assert_eq!(miner.pending_transactions_hashes(best_block).len(), 0); assert_eq!(miner.pending_receipts(best_block).len(), 0); } @@ -1291,9 +1291,9 @@ mod tests { // then assert_eq!(res.unwrap(), TransactionImportResult::Current); - assert_eq!(miner.all_transactions().len(), 1); + assert_eq!(miner.pending_transactions().len(), 1); assert_eq!(miner.pending_transactions_hashes(best_block).len(), 0); - assert_eq!(miner.pending_transactions(best_block).len(), 0); + assert_eq!(miner.ready_transactions(best_block).len(), 0); assert_eq!(miner.pending_receipts(best_block).len(), 0); // This method will let us know if pending block was created (before calling that method) assert!(miner.prepare_work_sealing(&client)); diff --git a/ethcore/src/miner/mod.rs b/ethcore/src/miner/mod.rs index fa8e7df0a..563e068a6 100644 --- a/ethcore/src/miner/mod.rs +++ b/ethcore/src/miner/mod.rs @@ -144,11 +144,11 @@ pub trait MinerService : Send + Sync { /// Query pending transactions for hash. fn transaction(&self, best_block: BlockNumber, hash: &H256) -> Option; - /// Get a list of all ready transactions in the queue. - fn all_transactions(&self) -> Vec; + /// Get a list of all pending transactions in the queue. + fn pending_transactions(&self) -> Vec; - /// Get a list of all pending transactions. - fn pending_transactions(&self, best_block: BlockNumber) -> Vec; + /// Get a list of all transactions that can go into the given block. + fn ready_transactions(&self, best_block: BlockNumber) -> Vec; /// Get a list of all future transactions. fn future_transactions(&self) -> Vec; diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index 2e9087133..b56feef05 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -313,11 +313,11 @@ fn does_not_propagate_delayed_transactions() { client.miner().import_own_transaction(&**client, tx0).unwrap(); client.miner().import_own_transaction(&**client, tx1).unwrap(); - assert_eq!(0, client.pending_transactions().len()); - assert_eq!(2, client.miner().all_transactions().len()); - push_blocks_to_client(client, 53, 2, 1); + assert_eq!(0, client.ready_transactions().len()); + assert_eq!(2, client.miner().pending_transactions().len()); + push_blocks_to_client(client, 53, 2, 2); client.import_verified_blocks(); - assert_eq!(2, client.pending_transactions().len()); - assert_eq!(2, client.miner().all_transactions().len()); + assert_eq!(2, client.ready_transactions().len()); + assert_eq!(2, client.miner().pending_transactions().len()); } diff --git a/rpc/src/v1/impls/parity.rs b/rpc/src/v1/impls/parity.rs index 0cfc1ba4d..326dc9c2b 100644 --- a/rpc/src/v1/impls/parity.rs +++ b/rpc/src/v1/impls/parity.rs @@ -270,7 +270,7 @@ impl Parity for ParityClient where fn pending_transactions(&self) -> Result, Error> { try!(self.active()); - Ok(take_weak!(self.miner).all_transactions().into_iter().map(Into::into).collect::>()) + Ok(take_weak!(self.miner).pending_transactions().into_iter().map(Into::into).collect::>()) } fn future_transactions(&self) -> Result, Error> { diff --git a/rpc/src/v1/tests/helpers/miner_service.rs b/rpc/src/v1/tests/helpers/miner_service.rs index 0ef0ee0a9..b25bcc39c 100644 --- a/rpc/src/v1/tests/helpers/miner_service.rs +++ b/rpc/src/v1/tests/helpers/miner_service.rs @@ -204,7 +204,7 @@ impl MinerService for TestMinerService { self.pending_transactions.lock().get(hash).cloned() } - fn all_transactions(&self) -> Vec { + fn pending_transactions(&self) -> Vec { self.pending_transactions.lock().values().cloned().map(Into::into).collect() } @@ -212,7 +212,7 @@ impl MinerService for TestMinerService { self.local_transactions.lock().iter().map(|(hash, stats)| (*hash, stats.clone())).collect() } - fn pending_transactions(&self, _best_block: BlockNumber) -> Vec { + fn ready_transactions(&self, _best_block: BlockNumber) -> Vec { self.pending_transactions.lock().values().cloned().map(Into::into).collect() } diff --git a/sync/src/chain.rs b/sync/src/chain.rs index 375cf6595..968107ba5 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -1919,7 +1919,7 @@ impl ChainSync { return 0; } - let transactions = io.chain().pending_transactions(); + let transactions = io.chain().ready_transactions(); if transactions.is_empty() { return 0; } From f345458b65b9334d1ddd14d3bfa9d6d74fd500e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 16 Dec 2016 15:03:31 +0100 Subject: [PATCH 10/18] Loading config from default path --- parity/cli/usage.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parity/cli/usage.rs b/parity/cli/usage.rs index 8c8b54c87..378ae8ce4 100644 --- a/parity/cli/usage.rs +++ b/parity/cli/usage.rs @@ -145,7 +145,7 @@ macro_rules! usage { } let config_file = raw_args.flag_config.clone().unwrap_or_else(|| raw_args.clone().into_args(Config::default()).flag_config); - let config_file = replace_home("", &config_file); + let config_file = replace_home(&::dir::default_data_path(), &config_file); let config = match (fs::File::open(&config_file), raw_args.flag_config.is_some()) { // Load config file (Ok(mut file), _) => { From 6966fd6c636859b90451f8dd30bdb05fb5e177d5 Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 16 Dec 2016 15:24:38 +0100 Subject: [PATCH 11/18] New paths --- parity/dir.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/parity/dir.rs b/parity/dir.rs index e788fbd56..1a045ca09 100644 --- a/parity/dir.rs +++ b/parity/dir.rs @@ -21,6 +21,16 @@ use util::journaldb::Algorithm; use helpers::replace_home; use app_dirs::{AppInfo, get_app_root, AppDataType}; +#[cfg(target_os = "macos")] const AUTHOR: &'static str = "Parity"; +#[cfg(target_os = "macos")] const PRODUCT: &'static str = "io.parity.ethereum"; +#[cfg(target_os = "macos")] const PRODUCT_HYPERVISOR: &'static str = "io.parity.ethereum-hypervisor"; +#[cfg(target_os = "windows")] const AUTHOR: &'static str = "Parity"; +#[cfg(target_os = "windows")] const PRODUCT: &'static str = "Ethereum"; +#[cfg(target_os = "windows")] const PRODUCT_HYPERVISOR: &'static str = "EthereumHypervisor"; +#[cfg(not(any(target_os = "windows", target_os = "macos")))] const AUTHOR: &'static str = "parity"; +#[cfg(not(any(target_os = "windows", target_os = "macos")))] const PRODUCT: &'static str = "parity"; +#[cfg(not(any(target_os = "windows", target_os = "macos")))] const PRODUCT_HYPERVISOR: &'static str = "parity-hypervisor"; + // this const is irrelevent cause we do have migrations now, // but we still use it for backwards compatibility const LEGACY_CLIENT_DB_VER_STR: &'static str = "5.3"; @@ -195,12 +205,12 @@ impl DatabaseDirectories { } pub fn default_data_path() -> String { - let app_info = AppInfo { name: "parity", author: "parity" }; + let app_info = AppInfo { name: PRODUCT, author: AUTHOR }; get_app_root(AppDataType::UserData, &app_info).map(|p| p.to_string_lossy().into_owned()).unwrap_or_else(|_| "$HOME/.parity".to_owned()) } pub fn default_hypervisor_path() -> String { - let app_info = AppInfo { name: "parity-hypervisor", author: "parity" }; + let app_info = AppInfo { name: PRODUCT_HYPERVISOR, author: AUTHOR }; get_app_root(AppDataType::UserData, &app_info).map(|p| p.to_string_lossy().into_owned()).unwrap_or_else(|_| "$HOME/.parity-hypervisor".to_owned()) } From b9cd68e7e6a0a9a8a2cf7bf1d7910dc59eebfcdf Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 16 Dec 2016 17:38:02 +0100 Subject: [PATCH 12/18] Minor paths change --- parity/dir.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/parity/dir.rs b/parity/dir.rs index 1a045ca09..5b11d8066 100644 --- a/parity/dir.rs +++ b/parity/dir.rs @@ -23,13 +23,13 @@ use app_dirs::{AppInfo, get_app_root, AppDataType}; #[cfg(target_os = "macos")] const AUTHOR: &'static str = "Parity"; #[cfg(target_os = "macos")] const PRODUCT: &'static str = "io.parity.ethereum"; -#[cfg(target_os = "macos")] const PRODUCT_HYPERVISOR: &'static str = "io.parity.ethereum-hypervisor"; +#[cfg(target_os = "macos")] const PRODUCT_HYPERVISOR: &'static str = "io.parity.ethereum-updates"; #[cfg(target_os = "windows")] const AUTHOR: &'static str = "Parity"; #[cfg(target_os = "windows")] const PRODUCT: &'static str = "Ethereum"; -#[cfg(target_os = "windows")] const PRODUCT_HYPERVISOR: &'static str = "EthereumHypervisor"; +#[cfg(target_os = "windows")] const PRODUCT_HYPERVISOR: &'static str = "EthereumUpdates"; #[cfg(not(any(target_os = "windows", target_os = "macos")))] const AUTHOR: &'static str = "parity"; -#[cfg(not(any(target_os = "windows", target_os = "macos")))] const PRODUCT: &'static str = "parity"; -#[cfg(not(any(target_os = "windows", target_os = "macos")))] const PRODUCT_HYPERVISOR: &'static str = "parity-hypervisor"; +#[cfg(not(any(target_os = "windows", target_os = "macos")))] const PRODUCT: &'static str = "io.parity.ethereum"; +#[cfg(not(any(target_os = "windows", target_os = "macos")))] const PRODUCT_HYPERVISOR: &'static str = "io.parity.ethereum-updates"; // this const is irrelevent cause we do have migrations now, // but we still use it for backwards compatibility From 14e4fefbcfcb2193bdc2eea1daa999f1b5a26558 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 16 Dec 2016 18:17:15 +0100 Subject: [PATCH 13/18] Fix updater permissions and refactor existing code. --- parity/signer.rs | 2 +- updater/src/updater.rs | 6 ++++-- util/network/src/host.rs | 4 ++-- util/src/path.rs | 15 ++++++--------- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/parity/signer.rs b/parity/signer.rs index 7cc258ed9..b4b3679ef 100644 --- a/parity/signer.rs +++ b/parity/signer.rs @@ -72,7 +72,7 @@ pub fn start(conf: Configuration, deps: Dependencies) -> Result PathBuf { let mut p = PathBuf::from(path); p.push(CODES_FILENAME); - let _ = restrict_permissions_owner(&p); + let _ = restrict_permissions_owner(&p, true, false); p } diff --git a/updater/src/updater.rs b/updater/src/updater.rs index 9f781ed5a..5af36b52f 100644 --- a/updater/src/updater.rs +++ b/updater/src/updater.rs @@ -18,8 +18,9 @@ use std::sync::{Arc, Weak}; use std::fs; use std::io::Write; use std::path::{PathBuf}; -use util::misc::platform; use ipc_common_types::{VersionInfo, ReleaseTrack}; +use util::path::restrict_permissions_owner; +use util::misc::platform; use util::{Address, H160, H256, FixedHash, Mutex, Bytes}; use ethsync::{SyncProvider}; use ethcore::client::{BlockId, BlockChainClient, ChainNotify}; @@ -197,7 +198,8 @@ impl Updater { let dest = self.updates_path(&Self::update_file_name(&fetched.version)); fs::create_dir_all(dest.parent().expect("at least one thing pushed; qed")).map_err(|e| format!("Unable to create updates path: {:?}", e))?; fs::copy(&b, &dest).map_err(|e| format!("Unable to copy update: {:?}", e))?; - info!(target: "updater", "Copied file to {}", dest.display()); + restrict_permissions_owner(&dest, false, true).map_err(|e| format!("Unable to update permissions: {}", e))?; + info!(target: "updater", "Installed updated binary to {}", dest.display()); let auto = match self.update_policy.filter { UpdateFilter::All => true, UpdateFilter::Critical if fetched.is_critical /* TODO: or is on a bad fork */ => true, diff --git a/util/network/src/host.rs b/util/network/src/host.rs index 2bdeab93e..681773c36 100644 --- a/util/network/src/host.rs +++ b/util/network/src/host.rs @@ -1168,8 +1168,8 @@ fn save_key(path: &Path, key: &Secret) { return; } }; - if let Err(e) = restrict_permissions_owner(path) { - warn!(target: "network", "Failed to modify permissions of the file (chmod: {})", e); + if let Err(e) = restrict_permissions_owner(path, true, false) { + warn!(target: "network", "Failed to modify permissions of the file ({})", e); } if let Err(e) = file.write(&key.hex().into_bytes()) { warn!("Error writing key file: {:?}", e); diff --git a/util/src/path.rs b/util/src/path.rs index 4cb0b413d..cbeec8a44 100644 --- a/util/src/path.rs +++ b/util/src/path.rs @@ -86,18 +86,15 @@ pub mod ethereum { } /// Restricts the permissions of given path only to the owner. -#[cfg(not(windows))] -pub fn restrict_permissions_owner(file_path: &Path) -> Result<(), i32> { - let cstr = ::std::ffi::CString::new(file_path.to_str().unwrap()).unwrap(); - match unsafe { ::libc::chmod(cstr.as_ptr(), ::libc::S_IWUSR | ::libc::S_IRUSR) } { - 0 => Ok(()), - x => Err(x), - } +#[cfg(unix)] +pub fn restrict_permissions_owner(file_path: &Path, write: bool, executable: bool) -> Result<(), String> { + let perms = ::std::os::unix::fs::PermissionsExt::from_mode(0o400 + write as u32 * 0o200 + executable as u32 * 0o100); + ::std::fs::set_permissions(file_path, perms).map_err(|e| format!("{:?}", e)) } /// Restricts the permissions of given path only to the owner. -#[cfg(windows)] -pub fn restrict_permissions_owner(_file_path: &Path) -> Result<(), i32> { +#[cfg(not(unix))] +pub fn restrict_permissions_owner(_file_path: &Path) -> Result<(), String> { //TODO: implement me Ok(()) } From c2448f6c2fdf9eb553aae15b1fabb604af8d4970 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 16 Dec 2016 18:32:33 +0100 Subject: [PATCH 14/18] Add mac icon. --- "mac/Parity Ethereum.app/Icon\r" | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 "mac/Parity Ethereum.app/Icon\r" diff --git "a/mac/Parity Ethereum.app/Icon\r" "b/mac/Parity Ethereum.app/Icon\r" new file mode 100644 index 000000000..e69de29bb From 6f977f03ae5d8c4c410b697d20e1951ff812c345 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 16 Dec 2016 20:26:49 +0100 Subject: [PATCH 15/18] Rename from troublesome filename --- "mac/Parity Ethereum.app/Icon\r" => mac/icon | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename "mac/Parity Ethereum.app/Icon\r" => mac/icon (100%) diff --git "a/mac/Parity Ethereum.app/Icon\r" b/mac/icon similarity index 100% rename from "mac/Parity Ethereum.app/Icon\r" rename to mac/icon From 75c92e935622090d34c9bd91a6b385856d451c48 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 16 Dec 2016 20:34:08 +0100 Subject: [PATCH 16/18] Add Parity.png --- mac/Parity Ethereum.app/Contents/Info.plist | 2 +- mac/Parity Ethereum.app/Contents/Parity.png | Bin 0 -> 108096 bytes mac/icon | 0 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 mac/Parity Ethereum.app/Contents/Parity.png delete mode 100644 mac/icon diff --git a/mac/Parity Ethereum.app/Contents/Info.plist b/mac/Parity Ethereum.app/Contents/Info.plist index 8bb76f95c..6732cf753 100644 --- a/mac/Parity Ethereum.app/Contents/Info.plist +++ b/mac/Parity Ethereum.app/Contents/Info.plist @@ -1,4 +1,4 @@ -CFBundlePackageTypeAPPLCFBundleInfoDictionaryVersion6.0 +CFBundleIconFilesParity.pngCFBundlePackageTypeAPPLCFBundleInfoDictionaryVersion6.0 CFBundleName Parity Ethereum CFBundleExecutable go.sh diff --git a/mac/Parity Ethereum.app/Contents/Parity.png b/mac/Parity Ethereum.app/Contents/Parity.png new file mode 100644 index 0000000000000000000000000000000000000000..d54082a9ede759b0423b08963562e994f5415e71 GIT binary patch literal 108096 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 Date: Fri, 16 Dec 2016 22:26:12 +0100 Subject: [PATCH 17/18] Fixed windows stub signature --- util/src/path.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/src/path.rs b/util/src/path.rs index cbeec8a44..09e7d8e72 100644 --- a/util/src/path.rs +++ b/util/src/path.rs @@ -94,7 +94,7 @@ pub fn restrict_permissions_owner(file_path: &Path, write: bool, executable: boo /// Restricts the permissions of given path only to the owner. #[cfg(not(unix))] -pub fn restrict_permissions_owner(_file_path: &Path) -> Result<(), String> { +pub fn restrict_permissions_owner(_file_path: &Path, _write: bool, _executable: bool) -> Result<(), String> { //TODO: implement me Ok(()) } From b34d3ee5a22325b93e01bf1cdd4e1af72657636d Mon Sep 17 00:00:00 2001 From: arkpar Date: Sat, 17 Dec 2016 12:28:31 +0100 Subject: [PATCH 18/18] Fixin racy test --- ethcore/src/tests/client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index b56feef05..fa5522d7f 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -316,7 +316,7 @@ fn does_not_propagate_delayed_transactions() { assert_eq!(0, client.ready_transactions().len()); assert_eq!(2, client.miner().pending_transactions().len()); push_blocks_to_client(client, 53, 2, 2); - client.import_verified_blocks(); + client.flush_queue(); assert_eq!(2, client.ready_transactions().len()); assert_eq!(2, client.miner().pending_transactions().len()); }