diff --git a/Cargo.lock b/Cargo.lock index d1b6d56e1..c05cbf190 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -884,18 +884,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" @@ -1348,7 +1336,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)", @@ -1379,7 +1367,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)", ] @@ -1400,17 +1388,6 @@ dependencies = [ "target_info 0.1.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" @@ -2231,7 +2208,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)" = "" @@ -2279,7 +2255,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/ethcore/light/src/client.rs b/ethcore/light/src/client.rs index 73e85b31c..edadc440c 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 ready_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..64d53d9c8 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,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 d820ee6d0..1f9bbf8aa 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 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) } -} \ No newline at end of file +} diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index bd1cc85cc..25f000c89 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -44,7 +44,7 @@ use env_info::LastHashes; use verification; use verification::{PreverifiedBlock, Verifier}; use block::*; -use transaction::{LocalizedTransaction, SignedTransaction, Transaction, Action}; +use transaction::{LocalizedTransaction, SignedTransaction, Transaction, PendingTransaction, Action}; use blockchain::extras::TransactionAddress; use types::filter::Filter; use types::mode::Mode as IpcMode; @@ -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 896681350..a384f1227 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, @@ -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 407ecac28..caba647d1 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}; @@ -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/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 eb050f0bd..c7bff4784 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, IsBlock, Block}; use error::*; -use transaction::{Action, SignedTransaction}; +use transaction::{Action, SignedTransaction, PendingTransaction}; use receipt::{Receipt, RichReceipt}; use spec::Spec; use engines::{Engine, Seal}; @@ -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 @@ -577,8 +578,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), @@ -613,7 +620,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) @@ -839,7 +846,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 ) }; @@ -857,17 +864,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 { @@ -902,9 +909,9 @@ impl MinerService for Miner { imported } - fn all_transactions(&self) -> Vec { + fn pending_transactions(&self) -> Vec { let queue = self.transaction_queue.lock(); - queue.top_transactions() + queue.pending_transactions(BlockNumber::max_value()) } fn local_transactions(&self) -> BTreeMap { @@ -915,22 +922,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 ready_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() ) }, } @@ -1126,7 +1137,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 ); }); } @@ -1159,7 +1170,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}; @@ -1238,12 +1249,12 @@ 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); - 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) @@ -1258,12 +1269,12 @@ 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); - 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); } @@ -1280,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)); @@ -1315,7 +1326,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..563e068a6 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 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; /// 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..9f5b6399a 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 filter_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.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.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.filter_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/tests/client.rs b/ethcore/src/tests/client.rs index 51c7086b6..fa5522d7f 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.ready_transactions().len()); + assert_eq!(2, client.miner().pending_transactions().len()); + push_blocks_to_client(client, 53, 2, 2); + client.flush_queue(); + assert_eq!(2, client.ready_transactions().len()); + assert_eq!(2, client.miner().pending_transactions().len()); +} + diff --git a/ethcore/src/types/transaction.rs b/ethcore/src/types/transaction.rs index 6388120c3..2cd001bdb 100644 --- a/ethcore/src/types/transaction.rs +++ b/ethcore/src/types/transaction.rs @@ -390,6 +390,34 @@ impl Deref for LocalizedTransaction { } } +/// Queued transaction with additional information. +#[derive(Debug, Clone, PartialEq, Eq, Binary)] +pub struct PendingTransaction { + /// Signed transaction data. + pub transaction: SignedTransaction, + /// To be activated at this block. `None` for immediately. + 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/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 ", 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 () { 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 000000000..d54082a9e Binary files /dev/null and b/mac/Parity Ethereum.app/Contents/Parity.png differ 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), _) => { diff --git a/parity/dir.rs b/parity/dir.rs index e788fbd56..5b11d8066 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-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 = "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 = "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 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()) } 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/rpc/src/v1/helpers/dispatch.rs b/rpc/src/v1/helpers/dispatch.rs index 122f8126b..cc689ddb5 100644 --- a/rpc/src/v1/helpers/dispatch.rs +++ b/rpc/src/v1/helpers/dispatch.rs @@ -23,7 +23,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; @@ -147,9 +147,9 @@ fn decrypt(accounts: &AccountProvider, address: Address, msg: Bytes, password: S }) } -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) @@ -190,6 +190,7 @@ 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)); let (signed_transaction, token) = match signed_transaction { @@ -198,7 +199,8 @@ pub fn sign_and_dispatch(client: &C, miner: &M, accounts: &AccountProvider }; 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).map(|hash| { + let pending_transaction = PendingTransaction::new(signed_transaction, min_block); + dispatch_transaction(&*client, &*miner, pending_transaction).map(|hash| { match token { Some(ref token) => WithToken::Yes(hash, token.clone()), None => WithToken::No(hash), @@ -217,6 +219,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 055e57475..fbd4fe8ed 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; @@ -617,7 +617,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 3164a93fd..326dc9c2b 100644 --- a/rpc/src/v1/impls/parity.rs +++ b/rpc/src/v1/impls/parity.rs @@ -270,7 +270,13 @@ 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> { + try!(self.active()); + + Ok(take_weak!(self.miner).future_transactions().into_iter().map(Into::into).collect::>()) } fn pending_transactions_stats(&self) -> Result, Error> { diff --git a/rpc/src/v1/impls/signer.rs b/rpc/src/v1/impls/signer.rs index 95c2f9b94..375cfdf6e 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; @@ -82,6 +82,9 @@ impl SignerClient where C: MiningBlockChainClient, if let Some(gas) = modification.gas { request.gas = gas.into(); } + 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); // Execute @@ -155,7 +158,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..b25bcc39c 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 pending_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 ready_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 f428bbcbd..35790a29f 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -501,7 +501,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", @@ -817,6 +817,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 296d0d30c..fdf693ac2 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_token() { value: U256::from(1), data: vec![], nonce: None, + min_block: None, })).unwrap(); let t = Transaction { @@ -270,6 +275,7 @@ fn should_confirm_transaction_with_rlp() { value: U256::from(1), data: vec![], nonce: None, + min_block: None, })).unwrap(); let t = Transaction { @@ -317,6 +323,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 2469132af..d0e3b16ee 100644 --- a/rpc/src/v1/traits/parity.rs +++ b/rpc/src/v1/traits/parity.rs @@ -123,6 +123,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/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 d7dc3f210..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 @@ -193,6 +193,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. @@ -234,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::*; @@ -267,12 +270,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()); @@ -291,12 +295,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()); @@ -324,7 +329,8 @@ mod tests { fn should_deserialize_modification() { // given let s1 = r#"{ - "gasPrice":"0xba43b7400" + "gasPrice":"0xba43b7400", + "minBlock":"0x42" }"#; let s2 = r#"{"gas": "0x1233"}"#; let s3 = r#"{}"#; @@ -338,14 +344,17 @@ mod tests { assert_eq!(res1, TransactionModification { gas_price: Some(U256::from_str("0ba43b7400").unwrap()), gas: None, + min_block: Some(Some(BlockNumber::Num(0x42))), }); 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..0db614887 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -17,9 +17,9 @@ 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}; +use v1::types::{Bytes, H160, H256, U256, H512, BlockNumber}; /// Transaction #[derive(Debug, Default, Clone, PartialEq, Serialize)] @@ -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.map(|b| BlockNumber::Num(b)); + 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 c5613c5b2..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; @@ -41,6 +41,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, } pub fn format_ether(i: U256) -> String { @@ -90,6 +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.map(|b| BlockNumber::Num(b)), } } } @@ -104,6 +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.map(|b| BlockNumber::Num(b)), } } } @@ -118,6 +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.and_then(|b| b.to_min_block_num()), } } } @@ -128,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] @@ -140,7 +146,8 @@ mod tests { "gas":"0x2", "value":"0x3", "data":"0x123456", - "nonce":"0x4" + "nonce":"0x4", + "minBlock":"0x13" }"#; let deserialized: TransactionRequest = serde_json::from_str(s).unwrap(); @@ -152,6 +159,7 @@ mod tests { value: Some(U256::from(3)), data: Some(vec![0x12, 0x34, 0x56].into()), nonce: Some(U256::from(4)), + min_block: Some(BlockNumber::Num(0x13)), }); } @@ -174,7 +182,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, }); } @@ -191,6 +200,7 @@ mod tests { value: None, data: None, nonce: None, + min_block: None, }); } @@ -214,6 +224,7 @@ mod tests { value: None, data: Some(vec![0x85, 0x95, 0xba, 0xb1].into()), nonce: None, + min_block: None, }); } diff --git a/rpc_cli/src/lib.rs b/rpc_cli/src/lib.rs index ce4933071..c79e4cc26 100644 --- a/rpc_cli/src/lib.rs +++ b/rpc_cli/src/lib.rs @@ -91,7 +91,7 @@ fn list_transactions(signer: &mut SignerRpc) -> Result { fn sign_transaction( signer: &mut SignerRpc, id: U256, password: &str ) -> Result { - try!(signer.confirm_request(id, None, None, password).map(|res| { + try!(signer.confirm_request(id, None, None, None, password).map(|res| { match res { Ok(u) => Ok(format!("Signed transaction id: {:#x}", u)), Err(e) => Err(format!("{:?}", e)), 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; } diff --git a/rpc_client/src/signer_client.rs b/rpc_client/src/signer_client.rs index 8a2eccd5d..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,12 +22,13 @@ impl SignerRpc { id: U256, new_gas: Option, new_gas_price: Option, + new_min_block: Option>, pwd: &str ) -> BoxFuture, Canceled> { self.rpc.request("signer_confirmRequest", vec![ to_value(&format!("{:#x}", id)), - to_value(&TransactionModification { gas_price: new_gas_price, gas: new_gas }), + to_value(&TransactionModification { gas_price: new_gas_price, gas: new_gas, min_block: new_min_block }), to_value(&pwd), ]) } diff --git a/sync/src/chain.rs b/sync/src/chain.rs index 81c35fbf1..968107ba5 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -1919,15 +1919,15 @@ impl ChainSync { return 0; } - let transactions = io.chain().pending_transactions(); + let transactions = io.chain().ready_transactions(); if transactions.is_empty() { 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() }; @@ -1965,11 +1965,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 e1f73d066..855386870 100644 --- a/sync/src/tests/consensus.rs +++ b/sync/src/tests/consensus.rs @@ -44,15 +44,16 @@ impl IoHandler for TestIoHandler { } } -fn new_tx(secret: &H256, nonce: U256) -> SignedTransaction { - Transaction { +fn new_tx(secret: &H256, nonce: U256) -> PendingTransaction { + let signed = Transaction { nonce: nonce.into(), gas_price: 0.into(), gas: 21000.into(), action: Action::Call(Address::default()), value: 0.into(), data: Vec::new(), - }.sign(secret, None) + }.sign(secret, None); + PendingTransaction::new(signed, None) } #[test] diff --git a/updater/src/updater.rs b/updater/src/updater.rs index bd6157648..31c021586 100644 --- a/updater/src/updater.rs +++ b/updater/src/updater.rs @@ -21,6 +21,8 @@ use std::path::{PathBuf}; use target_info::Target; use util::misc; 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}; @@ -210,7 +212,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..09e7d8e72 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, _write: bool, _executable: bool) -> Result<(), String> { //TODO: implement me Ok(()) }