Transaction timestamp condition (#4419)
* Transaction timestamp condtiion * Updated docs * Updated docs * Check agains last block timestamp
This commit is contained in:
		
							parent
							
								
									85e9091b29
								
							
						
					
					
						commit
						312aa72747
					
				| @ -206,6 +206,7 @@ impl HeaderChain { | |||||||
| 	pub fn get_header(&self, id: BlockId) -> Option<Bytes> { | 	pub fn get_header(&self, id: BlockId) -> Option<Bytes> { | ||||||
| 		match id { | 		match id { | ||||||
| 			BlockId::Earliest | BlockId::Number(0) => Some(self.genesis_header.clone()), | 			BlockId::Earliest | BlockId::Number(0) => Some(self.genesis_header.clone()), | ||||||
|  | 			BlockId::Latest if self.headers.read().is_empty() => Some(self.genesis_header.clone()), | ||||||
| 			BlockId::Hash(hash) => self.headers.read().get(&hash).map(|x| x.to_vec()), | 			BlockId::Hash(hash) => self.headers.read().get(&hash).map(|x| x.to_vec()), | ||||||
| 			BlockId::Number(num) => { | 			BlockId::Number(num) => { | ||||||
| 				if self.best_block.read().number < num { return None } | 				if self.best_block.read().number < num { return None } | ||||||
|  | |||||||
| @ -21,8 +21,9 @@ use ethcore::block_status::BlockStatus; | |||||||
| use ethcore::client::ClientReport; | use ethcore::client::ClientReport; | ||||||
| use ethcore::ids::BlockId; | use ethcore::ids::BlockId; | ||||||
| use ethcore::header::Header; | use ethcore::header::Header; | ||||||
|  | use ethcore::views::HeaderView; | ||||||
| use ethcore::verification::queue::{self, HeaderQueue}; | use ethcore::verification::queue::{self, HeaderQueue}; | ||||||
| use ethcore::transaction::PendingTransaction; | use ethcore::transaction::{PendingTransaction, Condition as TransactionCondition}; | ||||||
| use ethcore::blockchain_info::BlockChainInfo; | use ethcore::blockchain_info::BlockChainInfo; | ||||||
| use ethcore::spec::Spec; | use ethcore::spec::Spec; | ||||||
| use ethcore::service::ClientIoMessage; | use ethcore::service::ClientIoMessage; | ||||||
| @ -34,6 +35,7 @@ use util::{Bytes, Mutex, RwLock}; | |||||||
| 
 | 
 | ||||||
| use provider::Provider; | use provider::Provider; | ||||||
| use request; | use request; | ||||||
|  | use time; | ||||||
| 
 | 
 | ||||||
| use self::header_chain::HeaderChain; | use self::header_chain::HeaderChain; | ||||||
| 
 | 
 | ||||||
| @ -110,7 +112,11 @@ impl Client { | |||||||
| 		let best_num = self.chain.best_block().number; | 		let best_num = self.chain.best_block().number; | ||||||
| 		self.tx_pool.lock() | 		self.tx_pool.lock() | ||||||
| 			.values() | 			.values() | ||||||
| 			.filter(|t| t.min_block.as_ref().map_or(true, |x| x <= &best_num)) | 			.filter(|t| match t.condition { | ||||||
|  | 				Some(TransactionCondition::Number(ref x)) => x <= &best_num, | ||||||
|  | 				Some(TransactionCondition::Timestamp(ref x)) => *x <= time::get_time().sec as u64, | ||||||
|  | 				None => true, | ||||||
|  | 			}) | ||||||
| 			.cloned() | 			.cloned() | ||||||
| 			.collect() | 			.collect() | ||||||
| 	} | 	} | ||||||
| @ -135,6 +141,7 @@ impl Client { | |||||||
| 			genesis_hash: genesis_hash, | 			genesis_hash: genesis_hash, | ||||||
| 			best_block_hash: best_block.hash, | 			best_block_hash: best_block.hash, | ||||||
| 			best_block_number: best_block.number, | 			best_block_number: best_block.number, | ||||||
|  | 			best_block_timestamp: HeaderView::new(&self.chain.get_header(BlockId::Latest).expect("Latest hash is always in the chain")).timestamp(), | ||||||
| 			ancient_block_hash: if first_block.is_some() { Some(genesis_hash) } else { None }, | 			ancient_block_hash: if first_block.is_some() { Some(genesis_hash) } else { None }, | ||||||
| 			ancient_block_number: if first_block.is_some() { Some(0) } else { None }, | 			ancient_block_number: if first_block.is_some() { Some(0) } else { None }, | ||||||
| 			first_block_hash: first_block.as_ref().map(|first| first.hash), | 			first_block_hash: first_block.as_ref().map(|first| first.hash), | ||||||
|  | |||||||
| @ -24,6 +24,8 @@ pub struct BestBlock { | |||||||
| 	pub hash: H256, | 	pub hash: H256, | ||||||
| 	/// Best block number.
 | 	/// Best block number.
 | ||||||
| 	pub number: BlockNumber, | 	pub number: BlockNumber, | ||||||
|  | 	/// Best block timestamp.
 | ||||||
|  | 	pub timestamp: u64, | ||||||
| 	/// Best block total difficulty.
 | 	/// Best block total difficulty.
 | ||||||
| 	pub total_difficulty: U256, | 	pub total_difficulty: U256, | ||||||
| 	/// Best block uncompressed bytes
 | 	/// Best block uncompressed bytes
 | ||||||
|  | |||||||
| @ -485,6 +485,7 @@ impl BlockChain { | |||||||
| 			let best_block_number = bc.block_number(&best_block_hash).unwrap(); | 			let best_block_number = bc.block_number(&best_block_hash).unwrap(); | ||||||
| 			let best_block_total_difficulty = bc.block_details(&best_block_hash).unwrap().total_difficulty; | 			let best_block_total_difficulty = bc.block_details(&best_block_hash).unwrap().total_difficulty; | ||||||
| 			let best_block_rlp = bc.block(&best_block_hash).unwrap().into_inner(); | 			let best_block_rlp = bc.block(&best_block_hash).unwrap().into_inner(); | ||||||
|  | 			let best_block_timestamp = BlockView::new(&best_block_rlp).header().timestamp(); | ||||||
| 
 | 
 | ||||||
| 			let raw_first = bc.db.get(db::COL_EXTRA, b"first").unwrap().map(|v| v.to_vec()); | 			let raw_first = bc.db.get(db::COL_EXTRA, b"first").unwrap().map(|v| v.to_vec()); | ||||||
| 			let mut best_ancient = bc.db.get(db::COL_EXTRA, b"ancient").unwrap().map(|h| H256::from_slice(&h)); | 			let mut best_ancient = bc.db.get(db::COL_EXTRA, b"ancient").unwrap().map(|h| H256::from_slice(&h)); | ||||||
| @ -533,6 +534,7 @@ impl BlockChain { | |||||||
| 				number: best_block_number, | 				number: best_block_number, | ||||||
| 				total_difficulty: best_block_total_difficulty, | 				total_difficulty: best_block_total_difficulty, | ||||||
| 				hash: best_block_hash, | 				hash: best_block_hash, | ||||||
|  | 				timestamp: best_block_timestamp, | ||||||
| 				block: best_block_rlp, | 				block: best_block_rlp, | ||||||
| 			}; | 			}; | ||||||
| 
 | 
 | ||||||
| @ -585,6 +587,7 @@ impl BlockChain { | |||||||
| 					number: extras.number - 1, | 					number: extras.number - 1, | ||||||
| 					total_difficulty: best_block_total_difficulty, | 					total_difficulty: best_block_total_difficulty, | ||||||
| 					hash: hash, | 					hash: hash, | ||||||
|  | 					timestamp: BlockView::new(&best_block_rlp).header().timestamp(), | ||||||
| 					block: best_block_rlp, | 					block: best_block_rlp, | ||||||
| 				}; | 				}; | ||||||
| 				// update parent extras
 | 				// update parent extras
 | ||||||
| @ -738,6 +741,7 @@ impl BlockChain { | |||||||
| 				blocks_blooms: self.prepare_block_blooms_update(bytes, &info), | 				blocks_blooms: self.prepare_block_blooms_update(bytes, &info), | ||||||
| 				transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info), | 				transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info), | ||||||
| 				info: info, | 				info: info, | ||||||
|  | 				timestamp: header.timestamp(), | ||||||
| 				block: bytes | 				block: bytes | ||||||
| 			}, is_best); | 			}, is_best); | ||||||
| 
 | 
 | ||||||
| @ -786,6 +790,7 @@ impl BlockChain { | |||||||
| 				blocks_blooms: self.prepare_block_blooms_update(bytes, &info), | 				blocks_blooms: self.prepare_block_blooms_update(bytes, &info), | ||||||
| 				transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info), | 				transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info), | ||||||
| 				info: info, | 				info: info, | ||||||
|  | 				timestamp: header.timestamp(), | ||||||
| 				block: bytes, | 				block: bytes, | ||||||
| 			}, is_best); | 			}, is_best); | ||||||
| 			true | 			true | ||||||
| @ -850,6 +855,7 @@ impl BlockChain { | |||||||
| 			blocks_blooms: self.prepare_block_blooms_update(bytes, &info), | 			blocks_blooms: self.prepare_block_blooms_update(bytes, &info), | ||||||
| 			transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info), | 			transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info), | ||||||
| 			info: info.clone(), | 			info: info.clone(), | ||||||
|  | 			timestamp: header.timestamp(), | ||||||
| 			block: bytes, | 			block: bytes, | ||||||
| 		}, true); | 		}, true); | ||||||
| 
 | 
 | ||||||
| @ -921,6 +927,7 @@ impl BlockChain { | |||||||
| 						hash: update.info.hash, | 						hash: update.info.hash, | ||||||
| 						number: update.info.number, | 						number: update.info.number, | ||||||
| 						total_difficulty: update.info.total_difficulty, | 						total_difficulty: update.info.total_difficulty, | ||||||
|  | 						timestamp: update.timestamp, | ||||||
| 						block: update.block.to_vec(), | 						block: update.block.to_vec(), | ||||||
| 					}); | 					}); | ||||||
| 				}, | 				}, | ||||||
| @ -1206,6 +1213,11 @@ impl BlockChain { | |||||||
| 		self.best_block.read().number | 		self.best_block.read().number | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	/// Get best block timestamp.
 | ||||||
|  | 	pub fn best_block_timestamp(&self) -> u64 { | ||||||
|  | 		self.best_block.read().timestamp | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	/// Get best block total difficulty.
 | 	/// Get best block total difficulty.
 | ||||||
| 	pub fn best_block_total_difficulty(&self) -> U256 { | 	pub fn best_block_total_difficulty(&self) -> U256 { | ||||||
| 		self.best_block.read().total_difficulty | 		self.best_block.read().total_difficulty | ||||||
| @ -1293,6 +1305,7 @@ impl BlockChain { | |||||||
| 			genesis_hash: self.genesis_hash(), | 			genesis_hash: self.genesis_hash(), | ||||||
| 			best_block_hash: best_block.hash.clone(), | 			best_block_hash: best_block.hash.clone(), | ||||||
| 			best_block_number: best_block.number, | 			best_block_number: best_block.number, | ||||||
|  | 			best_block_timestamp: best_block.timestamp, | ||||||
| 			first_block_hash: self.first_block(), | 			first_block_hash: self.first_block(), | ||||||
| 			first_block_number: From::from(self.first_block_number()), | 			first_block_number: From::from(self.first_block_number()), | ||||||
| 			ancient_block_hash: best_ancient_block.as_ref().map(|b| b.hash.clone()), | 			ancient_block_hash: best_ancient_block.as_ref().map(|b| b.hash.clone()), | ||||||
|  | |||||||
| @ -9,6 +9,8 @@ use super::extras::{BlockDetails, BlockReceipts, TransactionAddress, LogGroupPos | |||||||
| pub struct ExtrasUpdate<'a> { | pub struct ExtrasUpdate<'a> { | ||||||
| 	/// Block info.
 | 	/// Block info.
 | ||||||
| 	pub info: BlockInfo, | 	pub info: BlockInfo, | ||||||
|  | 	/// Block timestamp.
 | ||||||
|  | 	pub timestamp: u64, | ||||||
| 	/// Current block uncompressed rlp bytes
 | 	/// Current block uncompressed rlp bytes
 | ||||||
| 	pub block: &'a [u8], | 	pub block: &'a [u8], | ||||||
| 	/// Modified block hashes.
 | 	/// Modified block hashes.
 | ||||||
|  | |||||||
| @ -1406,7 +1406,11 @@ impl BlockChainClient for Client { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	fn ready_transactions(&self) -> Vec<PendingTransaction> { | 	fn ready_transactions(&self) -> Vec<PendingTransaction> { | ||||||
| 		self.miner.ready_transactions(self.chain.read().best_block_number()) | 		let (number, timestamp) = { | ||||||
|  | 			let chain = self.chain.read(); | ||||||
|  | 			(chain.best_block_number(), chain.best_block_timestamp()) | ||||||
|  | 		}; | ||||||
|  | 		self.miner.ready_transactions(number, timestamp) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	fn queue_consensus_message(&self, message: Bytes) { | 	fn queue_consensus_message(&self, message: Bytes) { | ||||||
|  | |||||||
| @ -669,12 +669,14 @@ impl BlockChainClient for TestBlockChainClient { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	fn chain_info(&self) -> BlockChainInfo { | 	fn chain_info(&self) -> BlockChainInfo { | ||||||
|  | 		let number = self.blocks.read().len() as BlockNumber - 1; | ||||||
| 		BlockChainInfo { | 		BlockChainInfo { | ||||||
| 			total_difficulty: *self.difficulty.read(), | 			total_difficulty: *self.difficulty.read(), | ||||||
| 			pending_total_difficulty: *self.difficulty.read(), | 			pending_total_difficulty: *self.difficulty.read(), | ||||||
| 			genesis_hash: self.genesis_hash.clone(), | 			genesis_hash: self.genesis_hash.clone(), | ||||||
| 			best_block_hash: self.last_hash.read().clone(), | 			best_block_hash: self.last_hash.read().clone(), | ||||||
| 			best_block_number: self.blocks.read().len() as BlockNumber - 1, | 			best_block_number: number, | ||||||
|  | 			best_block_timestamp: number, | ||||||
| 			first_block_hash: self.first_block.read().as_ref().map(|x| x.0), | 			first_block_hash: self.first_block.read().as_ref().map(|x| x.0), | ||||||
| 			first_block_number: self.first_block.read().as_ref().map(|x| x.1), | 			first_block_number: self.first_block.read().as_ref().map(|x| x.1), | ||||||
| 			ancient_block_hash: self.ancient_block.read().as_ref().map(|x| x.0), | 			ancient_block_hash: self.ancient_block.read().as_ref().map(|x| x.0), | ||||||
| @ -709,7 +711,8 @@ impl BlockChainClient for TestBlockChainClient { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	fn ready_transactions(&self) -> Vec<PendingTransaction> { | 	fn ready_transactions(&self) -> Vec<PendingTransaction> { | ||||||
| 		self.miner.ready_transactions(self.chain_info().best_block_number) | 		let info = self.chain_info(); | ||||||
|  | 		self.miner.ready_transactions(info.best_block_number, info.best_block_timestamp) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	fn signing_network_id(&self) -> Option<u64> { None } | 	fn signing_network_id(&self) -> Option<u64> { None } | ||||||
|  | |||||||
| @ -25,7 +25,7 @@ use client::TransactionImportResult; | |||||||
| use executive::contract_address; | use executive::contract_address; | ||||||
| use block::{ClosedBlock, IsBlock, Block}; | use block::{ClosedBlock, IsBlock, Block}; | ||||||
| use error::*; | use error::*; | ||||||
| use transaction::{Action, UnverifiedTransaction, PendingTransaction, SignedTransaction}; | use transaction::{Action, UnverifiedTransaction, PendingTransaction, SignedTransaction, Condition as TransactionCondition}; | ||||||
| use receipt::{Receipt, RichReceipt}; | use receipt::{Receipt, RichReceipt}; | ||||||
| use spec::Spec; | use spec::Spec; | ||||||
| use engines::{Engine, Seal}; | use engines::{Engine, Seal}; | ||||||
| @ -325,7 +325,7 @@ impl Miner { | |||||||
| 		let _timer = PerfTimer::new("prepare_block"); | 		let _timer = PerfTimer::new("prepare_block"); | ||||||
| 		let chain_info = chain.chain_info(); | 		let chain_info = chain.chain_info(); | ||||||
| 		let (transactions, mut open_block, original_work_hash) = { | 		let (transactions, mut open_block, original_work_hash) = { | ||||||
| 			let transactions = {self.transaction_queue.lock().top_transactions_at(chain_info.best_block_number)}; | 			let transactions = {self.transaction_queue.lock().top_transactions_at(chain_info.best_block_number, chain_info.best_block_timestamp)}; | ||||||
| 			let mut sealing_work = self.sealing_work.lock(); | 			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 last_work_hash = sealing_work.queue.peek_last_ref().map(|pb| pb.block().fields().header.hash()); | ||||||
| 			let best_hash = chain_info.best_block_hash; | 			let best_hash = chain_info.best_block_hash; | ||||||
| @ -597,7 +597,7 @@ impl Miner { | |||||||
| 		client: &MiningBlockChainClient, | 		client: &MiningBlockChainClient, | ||||||
| 		transactions: Vec<UnverifiedTransaction>, | 		transactions: Vec<UnverifiedTransaction>, | ||||||
| 		default_origin: TransactionOrigin, | 		default_origin: TransactionOrigin, | ||||||
| 		min_block: Option<BlockNumber>, | 		condition: Option<TransactionCondition>, | ||||||
| 		transaction_queue: &mut BanningTransactionQueue, | 		transaction_queue: &mut BanningTransactionQueue, | ||||||
| 	) -> Vec<Result<TransactionImportResult, Error>> { | 	) -> Vec<Result<TransactionImportResult, Error>> { | ||||||
| 		let accounts = self.accounts.as_ref() | 		let accounts = self.accounts.as_ref() | ||||||
| @ -635,7 +635,7 @@ impl Miner { | |||||||
| 						let details_provider = TransactionDetailsProvider::new(client, &self.service_transaction_action); | 						let details_provider = TransactionDetailsProvider::new(client, &self.service_transaction_action); | ||||||
| 						match origin { | 						match origin { | ||||||
| 							TransactionOrigin::Local | TransactionOrigin::RetractedBlock => { | 							TransactionOrigin::Local | TransactionOrigin::RetractedBlock => { | ||||||
| 								transaction_queue.add(transaction, origin, insertion_time, min_block, &details_provider) | 								transaction_queue.add(transaction, origin, insertion_time, condition.clone(), &details_provider) | ||||||
| 							}, | 							}, | ||||||
| 							TransactionOrigin::External => { | 							TransactionOrigin::External => { | ||||||
| 								transaction_queue.add_with_banlist(transaction, insertion_time, &details_provider) | 								transaction_queue.add_with_banlist(transaction, insertion_time, &details_provider) | ||||||
| @ -892,7 +892,7 @@ impl MinerService for Miner { | |||||||
| 			let mut transaction_queue = self.transaction_queue.lock(); | 			let mut transaction_queue = self.transaction_queue.lock(); | ||||||
| 			// We need to re-validate transactions
 | 			// We need to re-validate transactions
 | ||||||
| 			let import = self.add_transactions_to_queue( | 			let import = self.add_transactions_to_queue( | ||||||
| 				chain, vec![pending.transaction.into()], TransactionOrigin::Local, pending.min_block, &mut transaction_queue | 				chain, vec![pending.transaction.into()], TransactionOrigin::Local, pending.condition, &mut transaction_queue | ||||||
| 			).pop().expect("one result returned per added transaction; one added => one result; qed"); | 			).pop().expect("one result returned per added transaction; one added => one result; qed"); | ||||||
| 
 | 
 | ||||||
| 			match import { | 			match import { | ||||||
| @ -927,7 +927,7 @@ impl MinerService for Miner { | |||||||
| 
 | 
 | ||||||
| 	fn pending_transactions(&self) -> Vec<PendingTransaction> { | 	fn pending_transactions(&self) -> Vec<PendingTransaction> { | ||||||
| 		let queue = self.transaction_queue.lock(); | 		let queue = self.transaction_queue.lock(); | ||||||
| 		queue.pending_transactions(BlockNumber::max_value()) | 		queue.pending_transactions(BlockNumber::max_value(), u64::max_value()) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	fn local_transactions(&self) -> BTreeMap<H256, LocalTransactionStatus> { | 	fn local_transactions(&self) -> BTreeMap<H256, LocalTransactionStatus> { | ||||||
| @ -942,14 +942,14 @@ impl MinerService for Miner { | |||||||
| 		self.transaction_queue.lock().future_transactions() | 		self.transaction_queue.lock().future_transactions() | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	fn ready_transactions(&self, best_block: BlockNumber) -> Vec<PendingTransaction> { | 	fn ready_transactions(&self, best_block: BlockNumber, best_block_timestamp: u64) -> Vec<PendingTransaction> { | ||||||
| 		let queue = self.transaction_queue.lock(); | 		let queue = self.transaction_queue.lock(); | ||||||
| 		match self.options.pending_set { | 		match self.options.pending_set { | ||||||
| 			PendingSet::AlwaysQueue => queue.pending_transactions(best_block), | 			PendingSet::AlwaysQueue => queue.pending_transactions(best_block, best_block_timestamp), | ||||||
| 			PendingSet::SealingOrElseQueue => { | 			PendingSet::SealingOrElseQueue => { | ||||||
| 				self.from_pending_block( | 				self.from_pending_block( | ||||||
| 					best_block, | 					best_block, | ||||||
| 					|| queue.pending_transactions(best_block), | 					|| queue.pending_transactions(best_block, best_block_timestamp), | ||||||
| 					|sealing| sealing.transactions().iter().map(|t| t.clone().into()).collect() | 					|sealing| sealing.transactions().iter().map(|t| t.clone().into()).collect() | ||||||
| 				) | 				) | ||||||
| 			}, | 			}, | ||||||
| @ -1325,7 +1325,7 @@ mod tests { | |||||||
| 		// then
 | 		// then
 | ||||||
| 		assert_eq!(res.unwrap(), TransactionImportResult::Current); | 		assert_eq!(res.unwrap(), TransactionImportResult::Current); | ||||||
| 		assert_eq!(miner.pending_transactions().len(), 1); | 		assert_eq!(miner.pending_transactions().len(), 1); | ||||||
| 		assert_eq!(miner.ready_transactions(best_block).len(), 1); | 		assert_eq!(miner.ready_transactions(best_block, 0).len(), 1); | ||||||
| 		assert_eq!(miner.pending_transactions_hashes(best_block).len(), 1); | 		assert_eq!(miner.pending_transactions_hashes(best_block).len(), 1); | ||||||
| 		assert_eq!(miner.pending_receipts(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)
 | 		// This method will let us know if pending block was created (before calling that method)
 | ||||||
| @ -1345,7 +1345,7 @@ mod tests { | |||||||
| 		// then
 | 		// then
 | ||||||
| 		assert_eq!(res.unwrap(), TransactionImportResult::Current); | 		assert_eq!(res.unwrap(), TransactionImportResult::Current); | ||||||
| 		assert_eq!(miner.pending_transactions().len(), 1); | 		assert_eq!(miner.pending_transactions().len(), 1); | ||||||
| 		assert_eq!(miner.ready_transactions(best_block).len(), 0); | 		assert_eq!(miner.ready_transactions(best_block, 0).len(), 0); | ||||||
| 		assert_eq!(miner.pending_transactions_hashes(best_block).len(), 0); | 		assert_eq!(miner.pending_transactions_hashes(best_block).len(), 0); | ||||||
| 		assert_eq!(miner.pending_receipts(best_block).len(), 0); | 		assert_eq!(miner.pending_receipts(best_block).len(), 0); | ||||||
| 	} | 	} | ||||||
| @ -1364,7 +1364,7 @@ mod tests { | |||||||
| 		assert_eq!(res.unwrap(), TransactionImportResult::Current); | 		assert_eq!(res.unwrap(), TransactionImportResult::Current); | ||||||
| 		assert_eq!(miner.pending_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_hashes(best_block).len(), 0); | ||||||
| 		assert_eq!(miner.ready_transactions(best_block).len(), 0); | 		assert_eq!(miner.ready_transactions(best_block, 0).len(), 0); | ||||||
| 		assert_eq!(miner.pending_receipts(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)
 | 		// This method will let us know if pending block was created (before calling that method)
 | ||||||
| 		assert!(miner.prepare_work_sealing(&client)); | 		assert!(miner.prepare_work_sealing(&client)); | ||||||
|  | |||||||
| @ -154,7 +154,7 @@ pub trait MinerService : Send + Sync { | |||||||
| 	fn pending_transactions(&self) -> Vec<PendingTransaction>; | 	fn pending_transactions(&self) -> Vec<PendingTransaction>; | ||||||
| 
 | 
 | ||||||
| 	/// Get a list of all transactions that can go into the given block.
 | 	/// Get a list of all transactions that can go into the given block.
 | ||||||
| 	fn ready_transactions(&self, best_block: BlockNumber) -> Vec<PendingTransaction>; | 	fn ready_transactions(&self, best_block: BlockNumber, best_block_timestamp: u64) -> Vec<PendingTransaction>; | ||||||
| 
 | 
 | ||||||
| 	/// Get a list of all future transactions.
 | 	/// Get a list of all future transactions.
 | ||||||
| 	fn future_transactions(&self) -> Vec<PendingTransaction>; | 	fn future_transactions(&self) -> Vec<PendingTransaction>; | ||||||
|  | |||||||
| @ -276,17 +276,17 @@ struct VerifiedTransaction { | |||||||
| 	origin: TransactionOrigin, | 	origin: TransactionOrigin, | ||||||
| 	/// Insertion time
 | 	/// Insertion time
 | ||||||
| 	insertion_time: QueuingInstant, | 	insertion_time: QueuingInstant, | ||||||
| 	/// Delay until specifid block.
 | 	/// Delay until specified condition is met.
 | ||||||
| 	min_block: Option<BlockNumber>, | 	condition: Option<Condition>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl VerifiedTransaction { | impl VerifiedTransaction { | ||||||
| 	fn new(transaction: SignedTransaction, origin: TransactionOrigin, time: QueuingInstant, min_block: Option<BlockNumber>) -> Self { | 	fn new(transaction: SignedTransaction, origin: TransactionOrigin, time: QueuingInstant, condition: Option<Condition>) -> Self { | ||||||
| 		VerifiedTransaction { | 		VerifiedTransaction { | ||||||
| 			transaction: transaction, | 			transaction: transaction, | ||||||
| 			origin: origin, | 			origin: origin, | ||||||
| 			insertion_time: time, | 			insertion_time: time, | ||||||
| 			min_block: min_block, | 			condition: condition, | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -666,14 +666,14 @@ impl TransactionQueue { | |||||||
| 		tx: SignedTransaction, | 		tx: SignedTransaction, | ||||||
| 		origin: TransactionOrigin, | 		origin: TransactionOrigin, | ||||||
| 		time: QueuingInstant, | 		time: QueuingInstant, | ||||||
| 		min_block: Option<BlockNumber>, | 		condition: Option<Condition>, | ||||||
| 		details_provider: &TransactionDetailsProvider, | 		details_provider: &TransactionDetailsProvider, | ||||||
| 	) -> Result<TransactionImportResult, Error> { | 	) -> Result<TransactionImportResult, Error> { | ||||||
| 		if origin == TransactionOrigin::Local { | 		if origin == TransactionOrigin::Local { | ||||||
| 			let hash = tx.hash(); | 			let hash = tx.hash(); | ||||||
| 			let cloned_tx = tx.clone(); | 			let cloned_tx = tx.clone(); | ||||||
| 
 | 
 | ||||||
| 			let result = self.add_internal(tx, origin, time, min_block, details_provider); | 			let result = self.add_internal(tx, origin, time, condition, details_provider); | ||||||
| 			match result { | 			match result { | ||||||
| 				Ok(TransactionImportResult::Current) => { | 				Ok(TransactionImportResult::Current) => { | ||||||
| 					self.local_transactions.mark_pending(hash); | 					self.local_transactions.mark_pending(hash); | ||||||
| @ -694,7 +694,7 @@ impl TransactionQueue { | |||||||
| 			} | 			} | ||||||
| 			result | 			result | ||||||
| 		} else { | 		} else { | ||||||
| 			self.add_internal(tx, origin, time, min_block, details_provider) | 			self.add_internal(tx, origin, time, condition, details_provider) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -704,7 +704,7 @@ impl TransactionQueue { | |||||||
| 		tx: SignedTransaction, | 		tx: SignedTransaction, | ||||||
| 		origin: TransactionOrigin, | 		origin: TransactionOrigin, | ||||||
| 		time: QueuingInstant, | 		time: QueuingInstant, | ||||||
| 		min_block: Option<BlockNumber>, | 		condition: Option<Condition>, | ||||||
| 		details_provider: &TransactionDetailsProvider, | 		details_provider: &TransactionDetailsProvider, | ||||||
| 	) -> Result<TransactionImportResult, Error> { | 	) -> Result<TransactionImportResult, Error> { | ||||||
| 		if origin != TransactionOrigin::Local && tx.gas_price < self.minimal_gas_price { | 		if origin != TransactionOrigin::Local && tx.gas_price < self.minimal_gas_price { | ||||||
| @ -815,7 +815,7 @@ impl TransactionQueue { | |||||||
| 		} | 		} | ||||||
| 		tx.check_low_s()?; | 		tx.check_low_s()?; | ||||||
| 		// No invalid transactions beyond this point.
 | 		// No invalid transactions beyond this point.
 | ||||||
| 		let vtx = VerifiedTransaction::new(tx, origin, time, min_block); | 		let vtx = VerifiedTransaction::new(tx, origin, time, condition); | ||||||
| 		let r = self.import_tx(vtx, client_account.nonce).map_err(Error::Transaction); | 		let r = self.import_tx(vtx, client_account.nonce).map_err(Error::Transaction); | ||||||
| 		assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len()); | 		assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len()); | ||||||
| 		r | 		r | ||||||
| @ -1068,11 +1068,11 @@ impl TransactionQueue { | |||||||
| 
 | 
 | ||||||
| 	/// Returns top transactions from the queue ordered by priority.
 | 	/// Returns top transactions from the queue ordered by priority.
 | ||||||
| 	pub fn top_transactions(&self) -> Vec<SignedTransaction> { | 	pub fn top_transactions(&self) -> Vec<SignedTransaction> { | ||||||
| 		self.top_transactions_at(BlockNumber::max_value()) | 		self.top_transactions_at(BlockNumber::max_value(), u64::max_value()) | ||||||
| 
 | 
 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	fn filter_pending_transaction<F>(&self, best_block: BlockNumber, mut f: F) | 	fn filter_pending_transaction<F>(&self, best_block: BlockNumber, best_timestamp: u64, mut f: F) | ||||||
| 		where F: FnMut(&VerifiedTransaction) { | 		where F: FnMut(&VerifiedTransaction) { | ||||||
| 
 | 
 | ||||||
| 		let mut delayed = HashSet::new(); | 		let mut delayed = HashSet::new(); | ||||||
| @ -1082,7 +1082,12 @@ impl TransactionQueue { | |||||||
| 			if delayed.contains(&sender) { | 			if delayed.contains(&sender) { | ||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
| 			if tx.min_block.unwrap_or(0) > best_block { | 			let delay = match tx.condition { | ||||||
|  | 				Some(Condition::Number(n)) => n > best_block, | ||||||
|  | 				Some(Condition::Timestamp(t)) => t > best_timestamp, | ||||||
|  | 				None => false, | ||||||
|  | 			}; | ||||||
|  | 			if delay { | ||||||
| 				delayed.insert(sender); | 				delayed.insert(sender); | ||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
| @ -1091,16 +1096,16 @@ impl TransactionQueue { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/// Returns top transactions from the queue ordered by priority.
 | 	/// Returns top transactions from the queue ordered by priority.
 | ||||||
| 	pub fn top_transactions_at(&self, best_block: BlockNumber) -> Vec<SignedTransaction> { | 	pub fn top_transactions_at(&self, best_block: BlockNumber, best_timestamp: u64) -> Vec<SignedTransaction> { | ||||||
| 		let mut r = Vec::new(); | 		let mut r = Vec::new(); | ||||||
| 		self.filter_pending_transaction(best_block, |tx| r.push(tx.transaction.clone())); | 		self.filter_pending_transaction(best_block, best_timestamp, |tx| r.push(tx.transaction.clone())); | ||||||
| 		r | 		r | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/// Return all ready transactions.
 | 	/// Return all ready transactions.
 | ||||||
| 	pub fn pending_transactions(&self, best_block: BlockNumber) -> Vec<PendingTransaction> { | 	pub fn pending_transactions(&self, best_block: BlockNumber, best_timestamp: u64) -> Vec<PendingTransaction> { | ||||||
| 		let mut r = Vec::new(); | 		let mut r = Vec::new(); | ||||||
| 		self.filter_pending_transaction(best_block, |tx| r.push(PendingTransaction::new(tx.transaction.clone(), tx.min_block))); | 		self.filter_pending_transaction(best_block, best_timestamp, |tx| r.push(PendingTransaction::new(tx.transaction.clone(), tx.condition.clone()))); | ||||||
| 		r | 		r | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -1109,7 +1114,7 @@ impl TransactionQueue { | |||||||
| 		self.future.by_priority | 		self.future.by_priority | ||||||
| 			.iter() | 			.iter() | ||||||
| 			.map(|t| self.by_hash.get(&t.hash).expect("All transactions in `current` and `future` are always included in `by_hash`")) | 			.map(|t| self.by_hash.get(&t.hash).expect("All transactions in `current` and `future` are always included in `by_hash`")) | ||||||
| 			.map(|t| PendingTransaction { transaction: t.transaction.clone(), min_block: t.min_block }) | 			.map(|t| PendingTransaction { transaction: t.transaction.clone(), condition: t.condition.clone() }) | ||||||
| 			.collect() | 			.collect() | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -1382,7 +1387,7 @@ pub mod test { | |||||||
| 	use super::{TransactionSet, TransactionOrder, VerifiedTransaction}; | 	use super::{TransactionSet, TransactionOrder, VerifiedTransaction}; | ||||||
| 	use miner::local_transactions::LocalTransactionsList; | 	use miner::local_transactions::LocalTransactionsList; | ||||||
| 	use client::TransactionImportResult; | 	use client::TransactionImportResult; | ||||||
| 	use transaction::{SignedTransaction, Transaction, Action}; | 	use transaction::{SignedTransaction, Transaction, Action, Condition}; | ||||||
| 
 | 
 | ||||||
| 	pub struct DummyTransactionDetailsProvider { | 	pub struct DummyTransactionDetailsProvider { | ||||||
| 		account_details: AccountDetails, | 		account_details: AccountDetails, | ||||||
| @ -2178,15 +2183,15 @@ pub mod test { | |||||||
| 		let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); | 		let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// when
 | ||||||
| 		let res1 = txq.add(tx.clone(), TransactionOrigin::External, 0, Some(1), &default_tx_provider()).unwrap(); | 		let res1 = txq.add(tx.clone(), TransactionOrigin::External, 0, Some(Condition::Number(1)), &default_tx_provider()).unwrap(); | ||||||
| 		let res2 = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); | 		let res2 = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); | ||||||
| 
 | 
 | ||||||
| 		// then
 | 		// then
 | ||||||
| 		assert_eq!(res1, TransactionImportResult::Current); | 		assert_eq!(res1, TransactionImportResult::Current); | ||||||
| 		assert_eq!(res2, TransactionImportResult::Current); | 		assert_eq!(res2, TransactionImportResult::Current); | ||||||
| 		let top = txq.top_transactions_at(0); | 		let top = txq.top_transactions_at(0, 0); | ||||||
| 		assert_eq!(top.len(), 0); | 		assert_eq!(top.len(), 0); | ||||||
| 		let top = txq.top_transactions_at(1); | 		let top = txq.top_transactions_at(1, 0); | ||||||
| 		assert_eq!(top.len(), 2); | 		assert_eq!(top.len(), 2); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -29,7 +29,7 @@ use spec::Spec; | |||||||
| use views::BlockView; | use views::BlockView; | ||||||
| use util::stats::Histogram; | use util::stats::Histogram; | ||||||
| use ethkey::{KeyPair, Secret}; | use ethkey::{KeyPair, Secret}; | ||||||
| use transaction::{PendingTransaction, Transaction, Action}; | use transaction::{PendingTransaction, Transaction, Action, Condition}; | ||||||
| use miner::MinerService; | use miner::MinerService; | ||||||
| 
 | 
 | ||||||
| #[test] | #[test] | ||||||
| @ -299,7 +299,7 @@ fn does_not_propagate_delayed_transactions() { | |||||||
| 		action: Action::Call(Address::default()), | 		action: Action::Call(Address::default()), | ||||||
| 		value: 0.into(), | 		value: 0.into(), | ||||||
| 		data: Vec::new(), | 		data: Vec::new(), | ||||||
| 	}.sign(secret, None), Some(2)); | 	}.sign(secret, None), Some(Condition::Number(2))); | ||||||
| 	let tx1 = PendingTransaction::new(Transaction { | 	let tx1 = PendingTransaction::new(Transaction { | ||||||
| 		nonce: 1.into(), | 		nonce: 1.into(), | ||||||
| 		gas_price: 0.into(), | 		gas_price: 0.into(), | ||||||
|  | |||||||
| @ -34,6 +34,8 @@ pub struct BlockChainInfo { | |||||||
| 	pub best_block_hash: H256, | 	pub best_block_hash: H256, | ||||||
| 	/// Best blockchain block number.
 | 	/// Best blockchain block number.
 | ||||||
| 	pub best_block_number: BlockNumber, | 	pub best_block_number: BlockNumber, | ||||||
|  | 	/// Best blockchain block timestamp.
 | ||||||
|  | 	pub best_block_timestamp: u64, | ||||||
| 	/// Best ancient block hash.
 | 	/// Best ancient block hash.
 | ||||||
| 	pub ancient_block_hash: Option<H256>, | 	pub ancient_block_hash: Option<H256>, | ||||||
| 	/// Best ancient block number.
 | 	/// Best ancient block number.
 | ||||||
|  | |||||||
| @ -52,6 +52,16 @@ impl Decodable for Action { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /// Transaction activation condition.
 | ||||||
|  | #[derive(Debug, Clone, PartialEq, Eq)] | ||||||
|  | #[cfg_attr(feature = "ipc", binary)] | ||||||
|  | pub enum Condition { | ||||||
|  | 	/// Valid at this block number or later.
 | ||||||
|  | 	Number(BlockNumber), | ||||||
|  | 	/// Valid at this unix time or later.
 | ||||||
|  | 	Timestamp(u64), | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /// A set of information describing an externally-originating message call
 | /// A set of information describing an externally-originating message call
 | ||||||
| /// or contract creation operation.
 | /// or contract creation operation.
 | ||||||
| #[derive(Default, Debug, Clone, PartialEq, Eq)] | #[derive(Default, Debug, Clone, PartialEq, Eq)] | ||||||
| @ -448,16 +458,16 @@ impl Deref for LocalizedTransaction { | |||||||
| pub struct PendingTransaction { | pub struct PendingTransaction { | ||||||
| 	/// Signed transaction data.
 | 	/// Signed transaction data.
 | ||||||
| 	pub transaction: SignedTransaction, | 	pub transaction: SignedTransaction, | ||||||
| 	/// To be activated at this block. `None` for immediately.
 | 	/// To be activated at this condition. `None` for immediately.
 | ||||||
| 	pub min_block: Option<BlockNumber>, | 	pub condition: Option<Condition>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl PendingTransaction { | impl PendingTransaction { | ||||||
| 	/// Create a new pending transaction from signed transaction.
 | 	/// Create a new pending transaction from signed transaction.
 | ||||||
| 	pub fn new(signed: SignedTransaction, min_block: Option<BlockNumber>) -> Self { | 	pub fn new(signed: SignedTransaction, condition: Option<Condition>) -> Self { | ||||||
| 		PendingTransaction { | 		PendingTransaction { | ||||||
| 			transaction: signed, | 			transaction: signed, | ||||||
| 			min_block: min_block, | 			condition: condition, | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @ -466,7 +476,7 @@ impl From<SignedTransaction> for PendingTransaction { | |||||||
| 	fn from(t: SignedTransaction) -> Self { | 	fn from(t: SignedTransaction) -> Self { | ||||||
| 		PendingTransaction { | 		PendingTransaction { | ||||||
| 			transaction: t, | 			transaction: t, | ||||||
| 			min_block: None, | 			condition: None, | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -192,7 +192,7 @@ pub fn sign_and_dispatch<C, M>(client: &C, miner: &M, accounts: &AccountProvider | |||||||
| { | { | ||||||
| 
 | 
 | ||||||
| 	let network_id = client.signing_network_id(); | 	let network_id = client.signing_network_id(); | ||||||
| 	let min_block = filled.min_block.clone(); | 	let condition = filled.condition.clone(); | ||||||
| 	let signed_transaction = sign_no_dispatch(client, miner, accounts, filled, password)?; | 	let signed_transaction = sign_no_dispatch(client, miner, accounts, filled, password)?; | ||||||
| 
 | 
 | ||||||
| 	let (signed_transaction, token) = match signed_transaction { | 	let (signed_transaction, token) = match signed_transaction { | ||||||
| @ -201,7 +201,7 @@ pub fn sign_and_dispatch<C, M>(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); | 	trace!(target: "miner", "send_transaction: dispatching tx: {} for network ID {:?}", rlp::encode(&signed_transaction).to_vec().pretty(), network_id); | ||||||
| 	let pending_transaction = PendingTransaction::new(signed_transaction, min_block); | 	let pending_transaction = PendingTransaction::new(signed_transaction, condition.map(Into::into)); | ||||||
| 	dispatch_transaction(&*client, &*miner, pending_transaction).map(|hash| { | 	dispatch_transaction(&*client, &*miner, pending_transaction).map(|hash| { | ||||||
| 		match token { | 		match token { | ||||||
| 			Some(ref token) => WithToken::Yes(hash, token.clone()), | 			Some(ref token) => WithToken::Yes(hash, token.clone()), | ||||||
| @ -222,7 +222,7 @@ pub fn fill_optional_fields<C, M>(request: TransactionRequest, default_sender: A | |||||||
| 		gas: request.gas.unwrap_or_else(|| miner.sensible_gas_limit()), | 		gas: request.gas.unwrap_or_else(|| miner.sensible_gas_limit()), | ||||||
| 		value: request.value.unwrap_or_else(|| 0.into()), | 		value: request.value.unwrap_or_else(|| 0.into()), | ||||||
| 		data: request.data.unwrap_or_else(Vec::new), | 		data: request.data.unwrap_or_else(Vec::new), | ||||||
| 		min_block: request.min_block, | 		condition: request.condition, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -15,6 +15,7 @@ | |||||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
| 
 | 
 | ||||||
| use util::{Address, U256, Bytes}; | use util::{Address, U256, Bytes}; | ||||||
|  | use v1::types::TransactionCondition; | ||||||
| 
 | 
 | ||||||
| /// Transaction request coming from RPC
 | /// Transaction request coming from RPC
 | ||||||
| #[derive(Debug, Clone, Default, Eq, PartialEq, Hash)] | #[derive(Debug, Clone, Default, Eq, PartialEq, Hash)] | ||||||
| @ -33,8 +34,8 @@ pub struct TransactionRequest { | |||||||
| 	pub data: Option<Bytes>, | 	pub data: Option<Bytes>, | ||||||
| 	/// Transaction's nonce
 | 	/// Transaction's nonce
 | ||||||
| 	pub nonce: Option<U256>, | 	pub nonce: Option<U256>, | ||||||
| 	/// Delay until this block if specified.
 | 	/// Delay until this condition is met.
 | ||||||
| 	pub min_block: Option<u64>, | 	pub condition: Option<TransactionCondition>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Transaction request coming from RPC with default values filled in.
 | /// Transaction request coming from RPC with default values filled in.
 | ||||||
| @ -56,8 +57,8 @@ pub struct FilledTransactionRequest { | |||||||
| 	pub data: Bytes, | 	pub data: Bytes, | ||||||
| 	/// Transaction's nonce
 | 	/// Transaction's nonce
 | ||||||
| 	pub nonce: Option<U256>, | 	pub nonce: Option<U256>, | ||||||
| 	/// Delay until this block if specified.
 | 	/// Delay until this condition is met.
 | ||||||
| 	pub min_block: Option<u64>, | 	pub condition: Option<TransactionCondition>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl From<FilledTransactionRequest> for TransactionRequest { | impl From<FilledTransactionRequest> for TransactionRequest { | ||||||
| @ -70,7 +71,7 @@ impl From<FilledTransactionRequest> for TransactionRequest { | |||||||
| 			value: Some(r.value), | 			value: Some(r.value), | ||||||
| 			data: Some(r.data), | 			data: Some(r.data), | ||||||
| 			nonce: r.nonce, | 			nonce: r.nonce, | ||||||
| 			min_block: r.min_block, | 			condition: r.condition, | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -349,7 +349,7 @@ mod test { | |||||||
| 			value: 10_000_000.into(), | 			value: 10_000_000.into(), | ||||||
| 			data: vec![], | 			data: vec![], | ||||||
| 			nonce: None, | 			nonce: None, | ||||||
| 			min_block: None, | 			condition: None, | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -87,8 +87,8 @@ impl<C: 'static, M: 'static> SignerClient<C, M> where C: MiningBlockChainClient, | |||||||
| 				if let Some(gas) = modification.gas { | 				if let Some(gas) = modification.gas { | ||||||
| 					request.gas = gas.into(); | 					request.gas = gas.into(); | ||||||
| 				} | 				} | ||||||
| 				if let Some(ref min_block) = modification.min_block { | 				if let Some(ref condition) = modification.condition { | ||||||
| 					request.min_block = min_block.as_ref().and_then(|b| b.to_min_block_num()); | 					request.condition = condition.clone().map(Into::into); | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			let result = f(&*client, &*miner, &*accounts, payload); | 			let result = f(&*client, &*miner, &*accounts, payload); | ||||||
| @ -160,7 +160,7 @@ impl<C: 'static, M: 'static> Signer for SignerClient<C, M> where C: MiningBlockC | |||||||
| 
 | 
 | ||||||
| 					// Dispatch if everything is ok
 | 					// Dispatch if everything is ok
 | ||||||
| 					if sender_matches && data_matches && value_matches && nonce_matches { | 					if sender_matches && data_matches && value_matches && nonce_matches { | ||||||
| 						let pending_transaction = PendingTransaction::new(signed_transaction, request.min_block); | 						let pending_transaction = PendingTransaction::new(signed_transaction, request.condition.map(Into::into)); | ||||||
| 						dispatch_transaction(&*client, &*miner, pending_transaction) | 						dispatch_transaction(&*client, &*miner, pending_transaction) | ||||||
| 							.map(Into::into) | 							.map(Into::into) | ||||||
| 							.map(ConfirmationResponse::SendTransaction) | 							.map(ConfirmationResponse::SendTransaction) | ||||||
|  | |||||||
| @ -212,7 +212,7 @@ impl MinerService for TestMinerService { | |||||||
| 		self.local_transactions.lock().iter().map(|(hash, stats)| (*hash, stats.clone())).collect() | 		self.local_transactions.lock().iter().map(|(hash, stats)| (*hash, stats.clone())).collect() | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	fn ready_transactions(&self, _best_block: BlockNumber) -> Vec<PendingTransaction> { | 	fn ready_transactions(&self, _best_block: BlockNumber, _best_timestamp: u64) -> Vec<PendingTransaction> { | ||||||
| 		self.pending_transactions.lock().values().cloned().map(Into::into).collect() | 		self.pending_transactions.lock().values().cloned().map(Into::into).collect() | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -516,7 +516,7 @@ fn rpc_eth_pending_transaction_by_hash() { | |||||||
| 		tester.miner.pending_transactions.lock().insert(H256::zero(), tx); | 		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","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 response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"condition":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 request = r#"{
 | 	let request = r#"{
 | ||||||
| 		"jsonrpc": "2.0", | 		"jsonrpc": "2.0", | ||||||
| 		"method": "eth_getTransactionByHash", | 		"method": "eth_getTransactionByHash", | ||||||
| @ -832,12 +832,11 @@ fn rpc_eth_sign_transaction() { | |||||||
| 	let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned() + | 	let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned() + | ||||||
| 		r#""raw":"0x"# + &rlp.to_hex() + r#"","# + | 		r#""raw":"0x"# + &rlp.to_hex() + r#"","# + | ||||||
| 		r#""tx":{"# + | 		r#""tx":{"# + | ||||||
| 		r#""blockHash":null,"blockNumber":null,"creates":null,"# + | 		r#""blockHash":null,"blockNumber":null,"condition":null,"creates":null,"# + | ||||||
| 		&format!("\"from\":\"0x{:?}\",", &address) + | 		&format!("\"from\":\"0x{:?}\",", &address) + | ||||||
| 		r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# + | 		r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# + | ||||||
| 		&format!("\"hash\":\"0x{:?}\",", t.hash()) + | 		&format!("\"hash\":\"0x{:?}\",", t.hash()) + | ||||||
| 		r#""input":"0x","# + | 		r#""input":"0x","# + | ||||||
| 		r#""minBlock":null,"# + |  | ||||||
| 		&format!("\"networkId\":{},", t.network_id().map_or("null".to_owned(), |n| format!("{}", n))) + | 		&format!("\"networkId\":{},", t.network_id().map_or("null".to_owned(), |n| format!("{}", n))) + | ||||||
| 		r#""nonce":"0x1","# + | 		r#""nonce":"0x1","# + | ||||||
| 		&format!("\"publicKey\":\"0x{:?}\",", t.recover_public().unwrap()) + | 		&format!("\"publicKey\":\"0x{:?}\",", t.recover_public().unwrap()) + | ||||||
|  | |||||||
| @ -85,7 +85,7 @@ fn should_return_list_of_items_to_confirm() { | |||||||
| 		value: U256::from(1), | 		value: U256::from(1), | ||||||
| 		data: vec![], | 		data: vec![], | ||||||
| 		nonce: None, | 		nonce: None, | ||||||
| 		min_block: None, | 		condition: None, | ||||||
| 	})).unwrap(); | 	})).unwrap(); | ||||||
| 	tester.signer.add_request(ConfirmationPayload::Signature(1.into(), vec![5].into())).unwrap(); | 	tester.signer.add_request(ConfirmationPayload::Signature(1.into(), vec![5].into())).unwrap(); | ||||||
| 
 | 
 | ||||||
| @ -93,7 +93,7 @@ fn should_return_list_of_items_to_confirm() { | |||||||
| 	let request = r#"{"jsonrpc":"2.0","method":"signer_requestsToConfirm","params":[],"id":1}"#; | 	let request = r#"{"jsonrpc":"2.0","method":"signer_requestsToConfirm","params":[],"id":1}"#; | ||||||
| 	let response = concat!( | 	let response = concat!( | ||||||
| 		r#"{"jsonrpc":"2.0","result":["#, | 		r#"{"jsonrpc":"2.0","result":["#, | ||||||
| 		r#"{"id":"0x1","payload":{"sendTransaction":{"data":"0x","from":"0x0000000000000000000000000000000000000001","gas":"0x989680","gasPrice":"0x2710","minBlock":null,"nonce":null,"to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","value":"0x1"}}},"#, | 		r#"{"id":"0x1","payload":{"sendTransaction":{"condition":null,"data":"0x","from":"0x0000000000000000000000000000000000000001","gas":"0x989680","gasPrice":"0x2710","nonce":null,"to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","value":"0x1"}}},"#, | ||||||
| 		r#"{"id":"0x2","payload":{"sign":{"address":"0x0000000000000000000000000000000000000001","data":"0x05"}}}"#, | 		r#"{"id":"0x2","payload":{"sign":{"address":"0x0000000000000000000000000000000000000001","data":"0x05"}}}"#, | ||||||
| 		r#"],"id":1}"# | 		r#"],"id":1}"# | ||||||
| 	); | 	); | ||||||
| @ -116,7 +116,7 @@ fn should_reject_transaction_from_queue_without_dispatching() { | |||||||
| 		value: U256::from(1), | 		value: U256::from(1), | ||||||
| 		data: vec![], | 		data: vec![], | ||||||
| 		nonce: None, | 		nonce: None, | ||||||
| 		min_block: None, | 		condition: None, | ||||||
| 	})).unwrap(); | 	})).unwrap(); | ||||||
| 	assert_eq!(tester.signer.requests().len(), 1); | 	assert_eq!(tester.signer.requests().len(), 1); | ||||||
| 
 | 
 | ||||||
| @ -143,7 +143,7 @@ fn should_not_remove_transaction_if_password_is_invalid() { | |||||||
| 		value: U256::from(1), | 		value: U256::from(1), | ||||||
| 		data: vec![], | 		data: vec![], | ||||||
| 		nonce: None, | 		nonce: None, | ||||||
| 		min_block: None, | 		condition: None, | ||||||
| 	})).unwrap(); | 	})).unwrap(); | ||||||
| 	assert_eq!(tester.signer.requests().len(), 1); | 	assert_eq!(tester.signer.requests().len(), 1); | ||||||
| 
 | 
 | ||||||
| @ -187,7 +187,7 @@ fn should_confirm_transaction_and_dispatch() { | |||||||
| 		value: U256::from(1), | 		value: U256::from(1), | ||||||
| 		data: vec![], | 		data: vec![], | ||||||
| 		nonce: None, | 		nonce: None, | ||||||
| 		min_block: None, | 		condition: None, | ||||||
| 	})).unwrap(); | 	})).unwrap(); | ||||||
| 
 | 
 | ||||||
| 	let t = Transaction { | 	let t = Transaction { | ||||||
| @ -233,7 +233,7 @@ fn should_alter_the_sender_and_nonce() { | |||||||
| 		value: U256::from(1), | 		value: U256::from(1), | ||||||
| 		data: vec![], | 		data: vec![], | ||||||
| 		nonce: Some(10.into()), | 		nonce: Some(10.into()), | ||||||
| 		min_block: None, | 		condition: None, | ||||||
| 	})).unwrap(); | 	})).unwrap(); | ||||||
| 
 | 
 | ||||||
| 	let t = Transaction { | 	let t = Transaction { | ||||||
| @ -283,7 +283,7 @@ fn should_confirm_transaction_with_token() { | |||||||
| 		value: U256::from(1), | 		value: U256::from(1), | ||||||
| 		data: vec![], | 		data: vec![], | ||||||
| 		nonce: None, | 		nonce: None, | ||||||
| 		min_block: None, | 		condition: None, | ||||||
| 	})).unwrap(); | 	})).unwrap(); | ||||||
| 
 | 
 | ||||||
| 	let t = Transaction { | 	let t = Transaction { | ||||||
| @ -332,7 +332,7 @@ fn should_confirm_transaction_with_rlp() { | |||||||
| 		value: U256::from(1), | 		value: U256::from(1), | ||||||
| 		data: vec![], | 		data: vec![], | ||||||
| 		nonce: None, | 		nonce: None, | ||||||
| 		min_block: None, | 		condition: None, | ||||||
| 	})).unwrap(); | 	})).unwrap(); | ||||||
| 
 | 
 | ||||||
| 	let t = Transaction { | 	let t = Transaction { | ||||||
| @ -380,7 +380,7 @@ fn should_return_error_when_sender_does_not_match() { | |||||||
| 		value: U256::from(1), | 		value: U256::from(1), | ||||||
| 		data: vec![], | 		data: vec![], | ||||||
| 		nonce: None, | 		nonce: None, | ||||||
| 		min_block: None, | 		condition: None, | ||||||
| 	})).unwrap(); | 	})).unwrap(); | ||||||
| 
 | 
 | ||||||
| 	let t = Transaction { | 	let t = Transaction { | ||||||
|  | |||||||
| @ -277,12 +277,11 @@ fn should_add_sign_transaction_to_the_queue() { | |||||||
| 	let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned() + | 	let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned() + | ||||||
| 		r#""raw":"0x"# + &rlp.to_hex() + r#"","# + | 		r#""raw":"0x"# + &rlp.to_hex() + r#"","# + | ||||||
| 		r#""tx":{"# + | 		r#""tx":{"# + | ||||||
| 		r#""blockHash":null,"blockNumber":null,"creates":null,"# + | 		r#""blockHash":null,"blockNumber":null,"condition":null,"creates":null,"# + | ||||||
| 		&format!("\"from\":\"0x{:?}\",", &address) + | 		&format!("\"from\":\"0x{:?}\",", &address) + | ||||||
| 		r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# + | 		r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# + | ||||||
| 		&format!("\"hash\":\"0x{:?}\",", t.hash()) + | 		&format!("\"hash\":\"0x{:?}\",", t.hash()) + | ||||||
| 		r#""input":"0x","# + | 		r#""input":"0x","# + | ||||||
| 		r#""minBlock":null,"# + |  | ||||||
| 		&format!("\"networkId\":{},", t.network_id().map_or("null".to_owned(), |n| format!("{}", n))) + | 		&format!("\"networkId\":{},", t.network_id().map_or("null".to_owned(), |n| format!("{}", n))) + | ||||||
| 		r#""nonce":"0x1","# + | 		r#""nonce":"0x1","# + | ||||||
| 		&format!("\"publicKey\":\"0x{:?}\",", t.public_key()) + | 		&format!("\"publicKey\":\"0x{:?}\",", t.public_key()) + | ||||||
|  | |||||||
| @ -139,7 +139,7 @@ mod tests { | |||||||
| 	fn test_serialize_block_transactions() { | 	fn test_serialize_block_transactions() { | ||||||
| 		let t = BlockTransactions::Full(vec![Transaction::default()]); | 		let t = BlockTransactions::Full(vec![Transaction::default()]); | ||||||
| 		let serialized = serde_json::to_string(&t).unwrap(); | 		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","minBlock":null}]"#); | 		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","condition":null}]"#); | ||||||
| 
 | 
 | ||||||
| 		let t = BlockTransactions::Hashes(vec![H256::default().into()]); | 		let t = BlockTransactions::Hashes(vec![H256::default().into()]); | ||||||
| 		let serialized = serde_json::to_string(&t).unwrap(); | 		let serialized = serde_json::to_string(&t).unwrap(); | ||||||
|  | |||||||
| @ -21,7 +21,7 @@ use serde::{Serialize, Serializer}; | |||||||
| use util::log::Colour; | use util::log::Colour; | ||||||
| use util::bytes::ToPretty; | use util::bytes::ToPretty; | ||||||
| 
 | 
 | ||||||
| use v1::types::{U256, TransactionRequest, RichRawTransaction, H160, H256, H520, Bytes, BlockNumber}; | use v1::types::{U256, TransactionRequest, RichRawTransaction, H160, H256, H520, Bytes, TransactionCondition}; | ||||||
| use v1::helpers; | use v1::helpers; | ||||||
| 
 | 
 | ||||||
| /// Confirmation waiting in a queue
 | /// Confirmation waiting in a queue
 | ||||||
| @ -196,9 +196,8 @@ pub struct TransactionModification { | |||||||
| 	pub gas_price: Option<U256>, | 	pub gas_price: Option<U256>, | ||||||
| 	/// Modified gas
 | 	/// Modified gas
 | ||||||
| 	pub gas: Option<U256>, | 	pub gas: Option<U256>, | ||||||
| 	/// Modified min block
 | 	/// Modified transaction condition.
 | ||||||
| 	#[serde(rename="minBlock")] | 	pub condition: Option<Option<TransactionCondition>>, | ||||||
| 	pub min_block: Option<Option<BlockNumber>>, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Represents two possible return values.
 | /// Represents two possible return values.
 | ||||||
| @ -240,7 +239,7 @@ impl<A, B> Serialize for Either<A, B>  where | |||||||
| mod tests { | mod tests { | ||||||
| 	use std::str::FromStr; | 	use std::str::FromStr; | ||||||
| 	use serde_json; | 	use serde_json; | ||||||
| 	use v1::types::{U256, H256, BlockNumber}; | 	use v1::types::{U256, H256, TransactionCondition}; | ||||||
| 	use v1::helpers; | 	use v1::helpers; | ||||||
| 	use super::*; | 	use super::*; | ||||||
| 
 | 
 | ||||||
| @ -274,13 +273,13 @@ mod tests { | |||||||
| 				value: 100_000.into(), | 				value: 100_000.into(), | ||||||
| 				data: vec![1, 2, 3], | 				data: vec![1, 2, 3], | ||||||
| 				nonce: Some(1.into()), | 				nonce: Some(1.into()), | ||||||
| 				min_block: None, | 				condition: None, | ||||||
| 			}), | 			}), | ||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// when
 | ||||||
| 		let res = serde_json::to_string(&ConfirmationRequest::from(request)); | 		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","minBlock":null}}}"#; | 		let expected = r#"{"id":"0xf","payload":{"sendTransaction":{"from":"0x0000000000000000000000000000000000000000","to":null,"gasPrice":"0x2710","gas":"0x3a98","value":"0x186a0","data":"0x010203","nonce":"0x1","condition":null}}}"#; | ||||||
| 
 | 
 | ||||||
| 		// then
 | 		// then
 | ||||||
| 		assert_eq!(res.unwrap(), expected.to_owned()); | 		assert_eq!(res.unwrap(), expected.to_owned()); | ||||||
| @ -300,13 +299,13 @@ mod tests { | |||||||
| 				value: 100_000.into(), | 				value: 100_000.into(), | ||||||
| 				data: vec![1, 2, 3], | 				data: vec![1, 2, 3], | ||||||
| 				nonce: Some(1.into()), | 				nonce: Some(1.into()), | ||||||
| 				min_block: None, | 				condition: None, | ||||||
| 			}), | 			}), | ||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// when
 | ||||||
| 		let res = serde_json::to_string(&ConfirmationRequest::from(request)); | 		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","minBlock":null}}}"#; | 		let expected = r#"{"id":"0xf","payload":{"signTransaction":{"from":"0x0000000000000000000000000000000000000000","to":null,"gasPrice":"0x2710","gas":"0x3a98","value":"0x186a0","data":"0x010203","nonce":"0x1","condition":null}}}"#; | ||||||
| 
 | 
 | ||||||
| 		// then
 | 		// then
 | ||||||
| 		assert_eq!(res.unwrap(), expected.to_owned()); | 		assert_eq!(res.unwrap(), expected.to_owned()); | ||||||
| @ -336,7 +335,7 @@ mod tests { | |||||||
| 		let s1 = r#"{
 | 		let s1 = r#"{
 | ||||||
| 			"sender": "0x000000000000000000000000000000000000000a", | 			"sender": "0x000000000000000000000000000000000000000a", | ||||||
| 			"gasPrice":"0xba43b7400", | 			"gasPrice":"0xba43b7400", | ||||||
| 			"minBlock":"0x42" | 			"condition": { "block": 66 } | ||||||
| 		}"#;
 | 		}"#;
 | ||||||
| 		let s2 = r#"{"gas": "0x1233"}"#; | 		let s2 = r#"{"gas": "0x1233"}"#; | ||||||
| 		let s3 = r#"{}"#; | 		let s3 = r#"{}"#; | ||||||
| @ -351,19 +350,19 @@ mod tests { | |||||||
| 			sender: Some(10.into()), | 			sender: Some(10.into()), | ||||||
| 			gas_price: Some(U256::from_str("0ba43b7400").unwrap()), | 			gas_price: Some(U256::from_str("0ba43b7400").unwrap()), | ||||||
| 			gas: None, | 			gas: None, | ||||||
| 			min_block: Some(Some(BlockNumber::Num(0x42))), | 			condition: Some(Some(TransactionCondition::Number(0x42))), | ||||||
| 		}); | 		}); | ||||||
| 		assert_eq!(res2, TransactionModification { | 		assert_eq!(res2, TransactionModification { | ||||||
| 			sender: None, | 			sender: None, | ||||||
| 			gas_price: None, | 			gas_price: None, | ||||||
| 			gas: Some(U256::from_str("1233").unwrap()), | 			gas: Some(U256::from_str("1233").unwrap()), | ||||||
| 			min_block: None, | 			condition: None, | ||||||
| 		}); | 		}); | ||||||
| 		assert_eq!(res3, TransactionModification { | 		assert_eq!(res3, TransactionModification { | ||||||
| 			sender: None, | 			sender: None, | ||||||
| 			gas_price: None, | 			gas_price: None, | ||||||
| 			gas: None, | 			gas: None, | ||||||
| 			min_block: None, | 			condition: None, | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -27,6 +27,7 @@ mod log; | |||||||
| mod sync; | mod sync; | ||||||
| mod transaction; | mod transaction; | ||||||
| mod transaction_request; | mod transaction_request; | ||||||
|  | mod transaction_condition; | ||||||
| mod receipt; | mod receipt; | ||||||
| mod rpc_settings; | mod rpc_settings; | ||||||
| mod trace; | mod trace; | ||||||
| @ -55,6 +56,7 @@ pub use self::sync::{ | |||||||
| }; | }; | ||||||
| pub use self::transaction::{Transaction, RichRawTransaction, LocalTransactionStatus}; | pub use self::transaction::{Transaction, RichRawTransaction, LocalTransactionStatus}; | ||||||
| pub use self::transaction_request::TransactionRequest; | pub use self::transaction_request::TransactionRequest; | ||||||
|  | pub use self::transaction_condition::TransactionCondition; | ||||||
| pub use self::receipt::Receipt; | pub use self::receipt::Receipt; | ||||||
| pub use self::rpc_settings::RpcSettings; | pub use self::rpc_settings::RpcSettings; | ||||||
| pub use self::trace::{LocalizedTrace, TraceResults}; | pub use self::trace::{LocalizedTrace, TraceResults}; | ||||||
|  | |||||||
| @ -19,7 +19,7 @@ use ethcore::miner; | |||||||
| use ethcore::contract_address; | use ethcore::contract_address; | ||||||
| use ethcore::transaction::{LocalizedTransaction, Action, PendingTransaction, SignedTransaction}; | use ethcore::transaction::{LocalizedTransaction, Action, PendingTransaction, SignedTransaction}; | ||||||
| use v1::helpers::errors; | use v1::helpers::errors; | ||||||
| use v1::types::{Bytes, H160, H256, U256, H512, BlockNumber}; | use v1::types::{Bytes, H160, H256, U256, H512, TransactionCondition}; | ||||||
| 
 | 
 | ||||||
| /// Transaction
 | /// Transaction
 | ||||||
| #[derive(Debug, Default, Clone, PartialEq, Serialize)] | #[derive(Debug, Default, Clone, PartialEq, Serialize)] | ||||||
| @ -70,8 +70,7 @@ pub struct Transaction { | |||||||
| 	/// The S field of the signature.
 | 	/// The S field of the signature.
 | ||||||
| 	pub s: U256, | 	pub s: U256, | ||||||
| 	/// Transaction activates at specified block.
 | 	/// Transaction activates at specified block.
 | ||||||
| 	#[serde(rename="minBlock")] | 	pub condition: Option<TransactionCondition>, | ||||||
| 	pub min_block: Option<BlockNumber>, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Local Transaction Status
 | /// Local Transaction Status
 | ||||||
| @ -190,7 +189,7 @@ impl From<LocalizedTransaction> for Transaction { | |||||||
| 			v: t.original_v().into(), | 			v: t.original_v().into(), | ||||||
| 			r: signature.r().into(), | 			r: signature.r().into(), | ||||||
| 			s: signature.s().into(), | 			s: signature.s().into(), | ||||||
| 			min_block: None, | 			condition: None, | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @ -224,7 +223,7 @@ impl From<SignedTransaction> for Transaction { | |||||||
| 			v: t.original_v().into(), | 			v: t.original_v().into(), | ||||||
| 			r: signature.r().into(), | 			r: signature.r().into(), | ||||||
| 			s: signature.s().into(), | 			s: signature.s().into(), | ||||||
| 			min_block: None, | 			condition: None, | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @ -232,7 +231,7 @@ impl From<SignedTransaction> for Transaction { | |||||||
| impl From<PendingTransaction> for Transaction { | impl From<PendingTransaction> for Transaction { | ||||||
| 	fn from(t: PendingTransaction) -> Transaction { | 	fn from(t: PendingTransaction) -> Transaction { | ||||||
| 		let mut r = Transaction::from(t.transaction); | 		let mut r = Transaction::from(t.transaction); | ||||||
| 		r.min_block = t.min_block.map(|b| BlockNumber::Num(b)); | 		r.condition = t.condition.map(|b| b.into()); | ||||||
| 		r | 		r | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @ -261,7 +260,7 @@ mod tests { | |||||||
| 	fn test_transaction_serialize() { | 	fn test_transaction_serialize() { | ||||||
| 		let t = Transaction::default(); | 		let t = Transaction::default(); | ||||||
| 		let serialized = serde_json::to_string(&t).unwrap(); | 		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","minBlock":null}"#); | 		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","condition":null}"#); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	#[test] | 	#[test] | ||||||
|  | |||||||
							
								
								
									
										67
									
								
								rpc/src/v1/types/transaction_condition.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								rpc/src/v1/types/transaction_condition.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,67 @@ | |||||||
|  | // Copyright 2015-2017 Parity Technologies (UK) Ltd.
 | ||||||
|  | // This file is part of Parity.
 | ||||||
|  | 
 | ||||||
|  | // Parity is free software: you can redistribute it and/or modify
 | ||||||
|  | // it under the terms of the GNU General Public License as published by
 | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or
 | ||||||
|  | // (at your option) any later version.
 | ||||||
|  | 
 | ||||||
|  | // Parity is distributed in the hope that it will be useful,
 | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | ||||||
|  | // GNU General Public License for more details.
 | ||||||
|  | 
 | ||||||
|  | // You should have received a copy of the GNU General Public License
 | ||||||
|  | // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  | 
 | ||||||
|  | use ethcore; | ||||||
|  | 
 | ||||||
|  | /// Represents condition on minimum block number or block timestamp.
 | ||||||
|  | #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] | ||||||
|  | pub enum TransactionCondition { | ||||||
|  | 	/// Valid at this minimum block number.
 | ||||||
|  | 	#[serde(rename="block")] | ||||||
|  | 	Number(u64), | ||||||
|  | 	/// Valid at given unix time.
 | ||||||
|  | 	#[serde(rename="time")] | ||||||
|  | 	Timestamp(u64), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Into<ethcore::transaction::Condition> for TransactionCondition { | ||||||
|  | 	fn into(self) -> ethcore::transaction::Condition { | ||||||
|  | 		match self { | ||||||
|  | 			TransactionCondition::Number(n) => ethcore::transaction::Condition::Number(n), | ||||||
|  | 			TransactionCondition::Timestamp(n) => ethcore::transaction::Condition::Timestamp(n), | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl From<ethcore::transaction::Condition> for TransactionCondition { | ||||||
|  | 	fn from(condition: ethcore::transaction::Condition) -> Self { | ||||||
|  | 		match condition { | ||||||
|  | 			ethcore::transaction::Condition::Number(n) => TransactionCondition::Number(n), | ||||||
|  | 			ethcore::transaction::Condition::Timestamp(n) => TransactionCondition::Timestamp(n), | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[cfg(test)] | ||||||
|  | mod tests { | ||||||
|  | 	use ethcore; | ||||||
|  | 	use super::*; | ||||||
|  | 	use serde_json; | ||||||
|  | 
 | ||||||
|  | 	#[test] | ||||||
|  | 	fn condition_deserialization() { | ||||||
|  | 		let s = r#"[{ "block": 51 }, { "time": 10 }]"#; | ||||||
|  | 		let deserialized: Vec<TransactionCondition> = serde_json::from_str(s).unwrap(); | ||||||
|  | 		assert_eq!(deserialized, vec![TransactionCondition::Number(51), TransactionCondition::Timestamp(10)]) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	#[test] | ||||||
|  | 	fn condition_into() { | ||||||
|  | 		assert_eq!(ethcore::transaction::Condition::Number(100), TransactionCondition::Number(100).into()); | ||||||
|  | 		assert_eq!(ethcore::transaction::Condition::Timestamp(100), TransactionCondition::Timestamp(100).into()); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| @ -16,7 +16,7 @@ | |||||||
| 
 | 
 | ||||||
| //! `TransactionRequest` type
 | //! `TransactionRequest` type
 | ||||||
| 
 | 
 | ||||||
| use v1::types::{Bytes, H160, U256, BlockNumber}; | use v1::types::{Bytes, H160, U256, TransactionCondition}; | ||||||
| use v1::helpers; | use v1::helpers; | ||||||
| use util::log::Colour; | use util::log::Colour; | ||||||
| 
 | 
 | ||||||
| @ -41,9 +41,8 @@ pub struct TransactionRequest { | |||||||
| 	pub data: Option<Bytes>, | 	pub data: Option<Bytes>, | ||||||
| 	/// Transaction's nonce
 | 	/// Transaction's nonce
 | ||||||
| 	pub nonce: Option<U256>, | 	pub nonce: Option<U256>, | ||||||
| 	/// Delay until this block if specified.
 | 	/// Delay until this block condition.
 | ||||||
| 	#[serde(rename="minBlock")] | 	pub condition: Option<TransactionCondition>, | ||||||
| 	pub min_block: Option<BlockNumber>, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub fn format_ether(i: U256) -> String { | pub fn format_ether(i: U256) -> String { | ||||||
| @ -93,7 +92,7 @@ impl From<helpers::TransactionRequest> for TransactionRequest { | |||||||
| 			value: r.value.map(Into::into), | 			value: r.value.map(Into::into), | ||||||
| 			data: r.data.map(Into::into), | 			data: r.data.map(Into::into), | ||||||
| 			nonce: r.nonce.map(Into::into), | 			nonce: r.nonce.map(Into::into), | ||||||
| 			min_block: r.min_block.map(|b| BlockNumber::Num(b)), | 			condition: r.condition.map(Into::into), | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @ -108,7 +107,7 @@ impl From<helpers::FilledTransactionRequest> for TransactionRequest { | |||||||
| 			value: Some(r.value.into()), | 			value: Some(r.value.into()), | ||||||
| 			data: Some(r.data.into()), | 			data: Some(r.data.into()), | ||||||
| 			nonce: r.nonce.map(Into::into), | 			nonce: r.nonce.map(Into::into), | ||||||
| 			min_block: r.min_block.map(|b| BlockNumber::Num(b)), | 			condition: r.condition.map(Into::into), | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @ -123,7 +122,7 @@ impl Into<helpers::TransactionRequest> for TransactionRequest { | |||||||
| 			value: self.value.map(Into::into), | 			value: self.value.map(Into::into), | ||||||
| 			data: self.data.map(Into::into), | 			data: self.data.map(Into::into), | ||||||
| 			nonce: self.nonce.map(Into::into), | 			nonce: self.nonce.map(Into::into), | ||||||
| 			min_block: self.min_block.and_then(|b| b.to_min_block_num()), | 			condition: self.condition.map(Into::into), | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @ -134,7 +133,7 @@ mod tests { | |||||||
| 	use std::str::FromStr; | 	use std::str::FromStr; | ||||||
| 	use rustc_serialize::hex::FromHex; | 	use rustc_serialize::hex::FromHex; | ||||||
| 	use serde_json; | 	use serde_json; | ||||||
| 	use v1::types::{U256, H160, BlockNumber}; | 	use v1::types::{U256, H160, TransactionCondition}; | ||||||
| 	use super::*; | 	use super::*; | ||||||
| 
 | 
 | ||||||
| 	#[test] | 	#[test] | ||||||
| @ -147,7 +146,7 @@ mod tests { | |||||||
| 			"value":"0x3", | 			"value":"0x3", | ||||||
| 			"data":"0x123456", | 			"data":"0x123456", | ||||||
| 			"nonce":"0x4", | 			"nonce":"0x4", | ||||||
| 			"minBlock":"0x13" | 			"condition": { "block": 19 } | ||||||
| 		}"#;
 | 		}"#;
 | ||||||
| 		let deserialized: TransactionRequest = serde_json::from_str(s).unwrap(); | 		let deserialized: TransactionRequest = serde_json::from_str(s).unwrap(); | ||||||
| 
 | 
 | ||||||
| @ -159,7 +158,7 @@ mod tests { | |||||||
| 			value: Some(U256::from(3)), | 			value: Some(U256::from(3)), | ||||||
| 			data: Some(vec![0x12, 0x34, 0x56].into()), | 			data: Some(vec![0x12, 0x34, 0x56].into()), | ||||||
| 			nonce: Some(U256::from(4)), | 			nonce: Some(U256::from(4)), | ||||||
| 			min_block: Some(BlockNumber::Num(0x13)), | 			condition: Some(TransactionCondition::Number(0x13)), | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -183,7 +182,7 @@ mod tests { | |||||||
| 			value: Some(U256::from_str("9184e72a").unwrap()), | 			value: Some(U256::from_str("9184e72a").unwrap()), | ||||||
| 			data: Some("d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675".from_hex().unwrap().into()), | 			data: Some("d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675".from_hex().unwrap().into()), | ||||||
| 			nonce: None, | 			nonce: None, | ||||||
| 			min_block: None, | 			condition: None, | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -200,7 +199,7 @@ mod tests { | |||||||
| 			value: None, | 			value: None, | ||||||
| 			data: None, | 			data: None, | ||||||
| 			nonce: None, | 			nonce: None, | ||||||
| 			min_block: None, | 			condition: None, | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -224,7 +223,7 @@ mod tests { | |||||||
| 			value: None, | 			value: None, | ||||||
| 			data: Some(vec![0x85, 0x95, 0xba, 0xb1].into()), | 			data: Some(vec![0x85, 0x95, 0xba, 0xb1].into()), | ||||||
| 			nonce: None, | 			nonce: None, | ||||||
| 			min_block: None, | 			condition: None, | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| use client::{Rpc, RpcError}; | use client::{Rpc, RpcError}; | ||||||
| use rpc::v1::types::{ConfirmationRequest, TransactionModification, U256, BlockNumber}; | use rpc::v1::types::{ConfirmationRequest, TransactionModification, U256, TransactionCondition}; | ||||||
| use serde_json::{Value as JsonValue, to_value}; | use serde_json::{Value as JsonValue, to_value}; | ||||||
| use std::path::PathBuf; | use std::path::PathBuf; | ||||||
| use futures::{BoxFuture, Canceled}; | use futures::{BoxFuture, Canceled}; | ||||||
| @ -22,13 +22,13 @@ impl SignerRpc { | |||||||
| 		id: U256, | 		id: U256, | ||||||
| 		new_gas: Option<U256>, | 		new_gas: Option<U256>, | ||||||
| 		new_gas_price: Option<U256>, | 		new_gas_price: Option<U256>, | ||||||
| 		new_min_block: Option<Option<BlockNumber>>, | 		new_condition: Option<Option<TransactionCondition>>, | ||||||
| 		pwd: &str | 		pwd: &str | ||||||
| 	) -> BoxFuture<Result<U256, RpcError>, Canceled> | 	) -> BoxFuture<Result<U256, RpcError>, Canceled> | ||||||
| 	{ | 	{ | ||||||
| 		self.rpc.request("signer_confirmRequest", vec![ | 		self.rpc.request("signer_confirmRequest", vec![ | ||||||
| 			to_value(&format!("{:#x}", id)), | 			to_value(&format!("{:#x}", id)), | ||||||
| 			to_value(&TransactionModification { sender: None, gas_price: new_gas_price, gas: new_gas, min_block: new_min_block }), | 			to_value(&TransactionModification { sender: None, gas_price: new_gas_price, gas: new_gas, condition: new_condition }), | ||||||
| 			to_value(&pwd), | 			to_value(&pwd), | ||||||
| 		]) | 		]) | ||||||
| 	} | 	} | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user