Merge pull request #4501 from ethcore/light-txq
Light Client transaction queue, initial LightDispatcher
This commit is contained in:
		
						commit
						36ea5550ba
					
				@ -16,24 +16,22 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
//! Light client implementation. Stores data from light sync
 | 
					//! Light client implementation. Stores data from light sync
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use std::sync::Arc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use ethcore::block_import_error::BlockImportError;
 | 
					use ethcore::block_import_error::BlockImportError;
 | 
				
			||||||
use ethcore::block_status::BlockStatus;
 | 
					use ethcore::block_status::BlockStatus;
 | 
				
			||||||
use ethcore::client::ClientReport;
 | 
					use ethcore::client::ClientReport;
 | 
				
			||||||
 | 
					use ethcore::engines::Engine;
 | 
				
			||||||
use ethcore::ids::BlockId;
 | 
					use ethcore::ids::BlockId;
 | 
				
			||||||
use ethcore::header::Header;
 | 
					use ethcore::header::Header;
 | 
				
			||||||
use ethcore::verification::queue::{self, HeaderQueue};
 | 
					use ethcore::verification::queue::{self, HeaderQueue};
 | 
				
			||||||
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;
 | 
				
			||||||
use ethcore::encoded;
 | 
					use ethcore::encoded;
 | 
				
			||||||
use io::IoChannel;
 | 
					use io::IoChannel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use util::hash::{H256, H256FastMap};
 | 
					use util::{Bytes, H256, Mutex, RwLock};
 | 
				
			||||||
use util::{Bytes, Mutex, RwLock};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use provider::Provider;
 | 
					 | 
				
			||||||
use request;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
use self::header_chain::HeaderChain;
 | 
					use self::header_chain::HeaderChain;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -58,6 +56,12 @@ pub trait LightChainClient: Send + Sync {
 | 
				
			|||||||
	/// parent queued prior.
 | 
						/// parent queued prior.
 | 
				
			||||||
	fn queue_header(&self, header: Header) -> Result<H256, BlockImportError>;
 | 
						fn queue_header(&self, header: Header) -> Result<H256, BlockImportError>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/// Attempt to get block header by block id.
 | 
				
			||||||
 | 
						fn block_header(&self, id: BlockId) -> Option<encoded::Header>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/// Get the best block header.
 | 
				
			||||||
 | 
						fn best_block_header(&self) -> encoded::Header;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/// Query whether a block is known.
 | 
						/// Query whether a block is known.
 | 
				
			||||||
	fn is_known(&self, hash: &H256) -> bool;
 | 
						fn is_known(&self, hash: &H256) -> bool;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -74,11 +78,26 @@ pub trait LightChainClient: Send + Sync {
 | 
				
			|||||||
	fn cht_root(&self, i: usize) -> Option<H256>;
 | 
						fn cht_root(&self, i: usize) -> Option<H256>;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Something which can be treated as a `LightChainClient`.
 | 
				
			||||||
 | 
					pub trait AsLightClient {
 | 
				
			||||||
 | 
						/// The kind of light client this can be treated as.
 | 
				
			||||||
 | 
						type Client: LightChainClient;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/// Access the underlying light client.
 | 
				
			||||||
 | 
						fn as_light_client(&self) -> &Self::Client;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<T: LightChainClient> AsLightClient for T {
 | 
				
			||||||
 | 
						type Client = Self;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fn as_light_client(&self) -> &Self { self }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Light client implementation.
 | 
					/// Light client implementation.
 | 
				
			||||||
pub struct Client {
 | 
					pub struct Client {
 | 
				
			||||||
	queue: HeaderQueue,
 | 
						queue: HeaderQueue,
 | 
				
			||||||
 | 
						engine: Arc<Engine>,
 | 
				
			||||||
	chain: HeaderChain,
 | 
						chain: HeaderChain,
 | 
				
			||||||
	tx_pool: Mutex<H256FastMap<PendingTransaction>>,
 | 
					 | 
				
			||||||
	report: RwLock<ClientReport>,
 | 
						report: RwLock<ClientReport>,
 | 
				
			||||||
	import_lock: Mutex<()>,
 | 
						import_lock: Mutex<()>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -88,8 +107,8 @@ impl Client {
 | 
				
			|||||||
	pub fn new(config: Config, spec: &Spec, io_channel: IoChannel<ClientIoMessage>) -> Self {
 | 
						pub fn new(config: Config, spec: &Spec, io_channel: IoChannel<ClientIoMessage>) -> Self {
 | 
				
			||||||
		Client {
 | 
							Client {
 | 
				
			||||||
			queue: HeaderQueue::new(config.queue, spec.engine.clone(), io_channel, true),
 | 
								queue: HeaderQueue::new(config.queue, spec.engine.clone(), io_channel, true),
 | 
				
			||||||
 | 
								engine: spec.engine.clone(),
 | 
				
			||||||
			chain: HeaderChain::new(&::rlp::encode(&spec.genesis_header())),
 | 
								chain: HeaderChain::new(&::rlp::encode(&spec.genesis_header())),
 | 
				
			||||||
			tx_pool: Mutex::new(Default::default()),
 | 
					 | 
				
			||||||
			report: RwLock::new(ClientReport::default()),
 | 
								report: RwLock::new(ClientReport::default()),
 | 
				
			||||||
			import_lock: Mutex::new(()),
 | 
								import_lock: Mutex::new(()),
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -100,25 +119,6 @@ impl Client {
 | 
				
			|||||||
		self.queue.import(header).map_err(Into::into)
 | 
							self.queue.import(header).map_err(Into::into)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/// Import a local transaction.
 | 
					 | 
				
			||||||
	pub fn import_own_transaction(&self, tx: PendingTransaction) {
 | 
					 | 
				
			||||||
		self.tx_pool.lock().insert(tx.transaction.hash(), tx);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/// Fetch a vector of all pending transactions.
 | 
					 | 
				
			||||||
	pub fn ready_transactions(&self) -> Vec<PendingTransaction> {
 | 
					 | 
				
			||||||
		let best = self.chain.best_header();
 | 
					 | 
				
			||||||
		self.tx_pool.lock()
 | 
					 | 
				
			||||||
			.values()
 | 
					 | 
				
			||||||
			.filter(|t| match t.condition {
 | 
					 | 
				
			||||||
				Some(TransactionCondition::Number(x)) => x <= best.number(),
 | 
					 | 
				
			||||||
				Some(TransactionCondition::Timestamp(x)) => x <= best.timestamp(),
 | 
					 | 
				
			||||||
				None => true,
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
			.cloned()
 | 
					 | 
				
			||||||
			.collect()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/// Inquire about the status of a given header.
 | 
						/// Inquire about the status of a given header.
 | 
				
			||||||
	pub fn status(&self, hash: &H256) -> BlockStatus {
 | 
						pub fn status(&self, hash: &H256) -> BlockStatus {
 | 
				
			||||||
		match self.queue.status(hash) {
 | 
							match self.queue.status(hash) {
 | 
				
			||||||
@ -159,6 +159,11 @@ impl Client {
 | 
				
			|||||||
		self.chain.block_header(id)
 | 
							self.chain.block_header(id)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/// Get the best block header.
 | 
				
			||||||
 | 
						pub fn best_block_header(&self) -> encoded::Header {
 | 
				
			||||||
 | 
							self.chain.best_header()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/// Flush the header queue.
 | 
						/// Flush the header queue.
 | 
				
			||||||
	pub fn flush_queue(&self) {
 | 
						pub fn flush_queue(&self) {
 | 
				
			||||||
		self.queue.flush()
 | 
							self.queue.flush()
 | 
				
			||||||
@ -207,6 +212,11 @@ impl Client {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		self.chain.heap_size_of_children()
 | 
							self.chain.heap_size_of_children()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/// Get a handle to the verification engine.
 | 
				
			||||||
 | 
						pub fn engine(&self) -> &Engine {
 | 
				
			||||||
 | 
							&*self.engine
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl LightChainClient for Client {
 | 
					impl LightChainClient for Client {
 | 
				
			||||||
@ -216,6 +226,14 @@ impl LightChainClient for Client {
 | 
				
			|||||||
		self.import_header(header)
 | 
							self.import_header(header)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fn block_header(&self, id: BlockId) -> Option<encoded::Header> {
 | 
				
			||||||
 | 
							Client::block_header(self, id)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fn best_block_header(&self) -> encoded::Header {
 | 
				
			||||||
 | 
							Client::best_block_header(self)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn is_known(&self, hash: &H256) -> bool {
 | 
						fn is_known(&self, hash: &H256) -> bool {
 | 
				
			||||||
		self.status(hash) == BlockStatus::InChain
 | 
							self.status(hash) == BlockStatus::InChain
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -237,8 +255,8 @@ impl LightChainClient for Client {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// dummy implementation -- may draw from canonical cache further on.
 | 
					// dummy implementation, should be removed when a `TestClient` is added.
 | 
				
			||||||
impl Provider for Client {
 | 
					impl ::provider::Provider for Client {
 | 
				
			||||||
	fn chain_info(&self) -> BlockChainInfo {
 | 
						fn chain_info(&self) -> BlockChainInfo {
 | 
				
			||||||
		Client::chain_info(self)
 | 
							Client::chain_info(self)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -263,19 +281,19 @@ impl Provider for Client {
 | 
				
			|||||||
		None
 | 
							None
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn state_proof(&self, _req: request::StateProof) -> Vec<Bytes> {
 | 
						fn state_proof(&self, _req: ::request::StateProof) -> Vec<Bytes> {
 | 
				
			||||||
		Vec::new()
 | 
							Vec::new()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn contract_code(&self, _req: request::ContractCode) -> Bytes {
 | 
						fn contract_code(&self, _req: ::request::ContractCode) -> Bytes {
 | 
				
			||||||
		Vec::new()
 | 
							Vec::new()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn header_proof(&self, _req: request::HeaderProof) -> Option<(encoded::Header, Vec<Bytes>)> {
 | 
						fn header_proof(&self, _req: ::request::HeaderProof) -> Option<(encoded::Header, Vec<Bytes>)> {
 | 
				
			||||||
		None
 | 
							None
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn ready_transactions(&self) -> Vec<PendingTransaction> {
 | 
						fn ready_transactions(&self) -> Vec<::ethcore::transaction::PendingTransaction> {
 | 
				
			||||||
		Vec::new()
 | 
							Vec::new()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -36,6 +36,7 @@ pub mod client;
 | 
				
			|||||||
pub mod cht;
 | 
					pub mod cht;
 | 
				
			||||||
pub mod net;
 | 
					pub mod net;
 | 
				
			||||||
pub mod on_demand;
 | 
					pub mod on_demand;
 | 
				
			||||||
 | 
					pub mod transaction_queue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(not(feature = "ipc"))]
 | 
					#[cfg(not(feature = "ipc"))]
 | 
				
			||||||
pub mod provider;
 | 
					pub mod provider;
 | 
				
			||||||
@ -54,6 +55,7 @@ pub mod remote {
 | 
				
			|||||||
mod types;
 | 
					mod types;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub use self::provider::Provider;
 | 
					pub use self::provider::Provider;
 | 
				
			||||||
 | 
					pub use self::transaction_queue::TransactionQueue;
 | 
				
			||||||
pub use types::les_request as request;
 | 
					pub use types::les_request as request;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[macro_use]
 | 
					#[macro_use]
 | 
				
			||||||
 | 
				
			|||||||
@ -17,15 +17,19 @@
 | 
				
			|||||||
//! A provider for the LES protocol. This is typically a full node, who can
 | 
					//! A provider for the LES protocol. This is typically a full node, who can
 | 
				
			||||||
//! give as much data as necessary to its peers.
 | 
					//! give as much data as necessary to its peers.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use std::sync::Arc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use ethcore::blockchain_info::BlockChainInfo;
 | 
					use ethcore::blockchain_info::BlockChainInfo;
 | 
				
			||||||
use ethcore::client::{BlockChainClient, ProvingBlockChainClient};
 | 
					use ethcore::client::{BlockChainClient, ProvingBlockChainClient};
 | 
				
			||||||
use ethcore::transaction::PendingTransaction;
 | 
					use ethcore::transaction::PendingTransaction;
 | 
				
			||||||
use ethcore::ids::BlockId;
 | 
					use ethcore::ids::BlockId;
 | 
				
			||||||
use ethcore::encoded;
 | 
					use ethcore::encoded;
 | 
				
			||||||
 | 
					use util::{Bytes, RwLock, H256};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use cht::{self, BlockInfo};
 | 
					use cht::{self, BlockInfo};
 | 
				
			||||||
 | 
					use client::{LightChainClient, AsLightClient};
 | 
				
			||||||
 | 
					use transaction_queue::TransactionQueue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use util::{Bytes, H256};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
use request;
 | 
					use request;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -284,6 +288,75 @@ impl<T: ProvingBlockChainClient + ?Sized> Provider for T {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// The light client "provider" implementation. This wraps a `LightClient` and
 | 
				
			||||||
 | 
					/// a light transaction queue.
 | 
				
			||||||
 | 
					pub struct LightProvider<L> {
 | 
				
			||||||
 | 
						client: Arc<L>,
 | 
				
			||||||
 | 
						txqueue: Arc<RwLock<TransactionQueue>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<L> LightProvider<L> {
 | 
				
			||||||
 | 
						/// Create a new `LightProvider` from the given client and transaction queue.
 | 
				
			||||||
 | 
						pub fn new(client: Arc<L>, txqueue: Arc<RwLock<TransactionQueue>>) -> Self {
 | 
				
			||||||
 | 
							LightProvider {
 | 
				
			||||||
 | 
								client: client,
 | 
				
			||||||
 | 
								txqueue: txqueue,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: draw from cache (shared between this and the RPC layer)
 | 
				
			||||||
 | 
					impl<L: AsLightClient + Send + Sync> Provider for LightProvider<L> {
 | 
				
			||||||
 | 
						fn chain_info(&self) -> BlockChainInfo {
 | 
				
			||||||
 | 
							self.client.as_light_client().chain_info()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fn reorg_depth(&self, _a: &H256, _b: &H256) -> Option<u64> {
 | 
				
			||||||
 | 
							None
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fn earliest_state(&self) -> Option<u64> {
 | 
				
			||||||
 | 
							None
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fn block_header(&self, id: BlockId) -> Option<encoded::Header> {
 | 
				
			||||||
 | 
							self.client.as_light_client().block_header(id)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fn block_body(&self, _id: BlockId) -> Option<encoded::Body> {
 | 
				
			||||||
 | 
							None
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fn block_receipts(&self, _hash: &H256) -> Option<Bytes> {
 | 
				
			||||||
 | 
							None
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fn state_proof(&self, _req: request::StateProof) -> Vec<Bytes> {
 | 
				
			||||||
 | 
							Vec::new()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fn contract_code(&self, _req: request::ContractCode) -> Bytes {
 | 
				
			||||||
 | 
							Vec::new()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fn header_proof(&self, _req: request::HeaderProof) -> Option<(encoded::Header, Vec<Bytes>)> {
 | 
				
			||||||
 | 
							None
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fn ready_transactions(&self) -> Vec<PendingTransaction> {
 | 
				
			||||||
 | 
							let chain_info = self.chain_info();
 | 
				
			||||||
 | 
							self.txqueue.read().ready_transactions(chain_info.best_block_number, chain_info.best_block_timestamp)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<L: AsLightClient> AsLightClient for LightProvider<L> {
 | 
				
			||||||
 | 
						type Client = L::Client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fn as_light_client(&self) -> &L::Client {
 | 
				
			||||||
 | 
							self.client.as_light_client()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(test)]
 | 
					#[cfg(test)]
 | 
				
			||||||
mod tests {
 | 
					mod tests {
 | 
				
			||||||
	use ethcore::client::{EachBlockWith, TestBlockChainClient};
 | 
						use ethcore::client::{EachBlockWith, TestBlockChainClient};
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										474
									
								
								ethcore/light/src/transaction_queue.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										474
									
								
								ethcore/light/src/transaction_queue.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,474 @@
 | 
				
			|||||||
 | 
					// 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/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//! Light Transaction Queue.
 | 
				
			||||||
 | 
					//!
 | 
				
			||||||
 | 
					//! Manages local transactions,
 | 
				
			||||||
 | 
					//! but stores all local transactions, removing only on invalidated nonce.
 | 
				
			||||||
 | 
					//!
 | 
				
			||||||
 | 
					//! Under the assumption that light nodes will have a relatively limited set of
 | 
				
			||||||
 | 
					//! accounts for which they create transactions, this queue is structured in an
 | 
				
			||||||
 | 
					//! address-wise manner.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use std::collections::{BTreeMap, HashMap};
 | 
				
			||||||
 | 
					use std::collections::hash_map::Entry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use ethcore::error::TransactionError;
 | 
				
			||||||
 | 
					use ethcore::transaction::{Condition, PendingTransaction, SignedTransaction};
 | 
				
			||||||
 | 
					use ethcore::transaction_import::TransactionImportResult;
 | 
				
			||||||
 | 
					use util::{Address, U256, H256, H256FastMap};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Knowledge of an account's current nonce.
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, PartialEq, Eq)]
 | 
				
			||||||
 | 
					enum CurrentNonce {
 | 
				
			||||||
 | 
						// Assumed current nonce.
 | 
				
			||||||
 | 
						Assumed(U256),
 | 
				
			||||||
 | 
						// Known current nonce.
 | 
				
			||||||
 | 
						Known(U256),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl CurrentNonce {
 | 
				
			||||||
 | 
						// whether this nonce is assumed
 | 
				
			||||||
 | 
						fn is_assumed(&self) -> bool {
 | 
				
			||||||
 | 
							match *self {
 | 
				
			||||||
 | 
								CurrentNonce::Assumed(_) => true,
 | 
				
			||||||
 | 
								CurrentNonce::Known(_) => false,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// whether this nonce is known for certain from an external source.
 | 
				
			||||||
 | 
						fn is_known(&self) -> bool {
 | 
				
			||||||
 | 
							!self.is_assumed()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// the current nonce's value.
 | 
				
			||||||
 | 
						fn value(&self) -> &U256 {
 | 
				
			||||||
 | 
							match *self {
 | 
				
			||||||
 | 
								CurrentNonce::Assumed(ref val) => val,
 | 
				
			||||||
 | 
								CurrentNonce::Known(ref val) => val,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, PartialEq, Eq)]
 | 
				
			||||||
 | 
					struct TransactionInfo {
 | 
				
			||||||
 | 
						hash: H256,
 | 
				
			||||||
 | 
						nonce: U256,
 | 
				
			||||||
 | 
						condition: Option<Condition>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'a> From<&'a PendingTransaction> for TransactionInfo {
 | 
				
			||||||
 | 
						fn from(tx: &'a PendingTransaction) -> Self {
 | 
				
			||||||
 | 
							TransactionInfo {
 | 
				
			||||||
 | 
								hash: tx.hash(),
 | 
				
			||||||
 | 
								nonce: tx.nonce.clone(),
 | 
				
			||||||
 | 
								condition: tx.condition.clone(),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// transactions associated with a specific account.
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, PartialEq, Eq)]
 | 
				
			||||||
 | 
					struct AccountTransactions {
 | 
				
			||||||
 | 
						// believed current nonce (gotten from initial given TX or `cull` calls).
 | 
				
			||||||
 | 
						cur_nonce: CurrentNonce,
 | 
				
			||||||
 | 
						current: Vec<TransactionInfo>, // ordered "current" transactions (cur_nonce onwards)
 | 
				
			||||||
 | 
						future: BTreeMap<U256, TransactionInfo>, // "future" transactions.
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl AccountTransactions {
 | 
				
			||||||
 | 
						fn is_empty(&self) -> bool {
 | 
				
			||||||
 | 
							self.current.is_empty() && self.future.is_empty()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fn next_nonce(&self) -> U256 {
 | 
				
			||||||
 | 
							self.current.last().map(|last| last.nonce + 1.into())
 | 
				
			||||||
 | 
								.unwrap_or_else(|| *self.cur_nonce.value())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// attempt to move transactions from the future queue into the current queue.
 | 
				
			||||||
 | 
						fn adjust_future(&mut self) {
 | 
				
			||||||
 | 
							let mut next_nonce = self.next_nonce();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							loop {
 | 
				
			||||||
 | 
								match self.future.remove(&next_nonce) {
 | 
				
			||||||
 | 
									Some(tx) => self.current.push(tx),
 | 
				
			||||||
 | 
									None => break,
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								next_nonce = next_nonce + 1.into();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Light transaction queue. See module docs for more details.
 | 
				
			||||||
 | 
					#[derive(Debug, Default, Clone, PartialEq, Eq)]
 | 
				
			||||||
 | 
					pub struct TransactionQueue {
 | 
				
			||||||
 | 
						by_account: HashMap<Address, AccountTransactions>,
 | 
				
			||||||
 | 
						by_hash: H256FastMap<PendingTransaction>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl TransactionQueue {
 | 
				
			||||||
 | 
						/// Import a pending transaction to be queued.
 | 
				
			||||||
 | 
						pub fn import(&mut self, tx: PendingTransaction) -> Result<TransactionImportResult, TransactionError>  {
 | 
				
			||||||
 | 
							let sender = tx.sender();
 | 
				
			||||||
 | 
							let hash = tx.hash();
 | 
				
			||||||
 | 
							let nonce = tx.nonce;
 | 
				
			||||||
 | 
							let tx_info = TransactionInfo::from(&tx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if self.by_hash.contains_key(&hash) { return Err(TransactionError::AlreadyImported) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    let res = match self.by_account.entry(sender) {
 | 
				
			||||||
 | 
								Entry::Vacant(entry) => {
 | 
				
			||||||
 | 
									entry.insert(AccountTransactions {
 | 
				
			||||||
 | 
										cur_nonce: CurrentNonce::Assumed(nonce),
 | 
				
			||||||
 | 
										current: vec![tx_info],
 | 
				
			||||||
 | 
										future: BTreeMap::new(),
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									TransactionImportResult::Current
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								Entry::Occupied(mut entry) => {
 | 
				
			||||||
 | 
									let acct_txs = entry.get_mut();
 | 
				
			||||||
 | 
									if &nonce < acct_txs.cur_nonce.value() {
 | 
				
			||||||
 | 
										// don't accept txs from before known current nonce.
 | 
				
			||||||
 | 
										if acct_txs.cur_nonce.is_known() {
 | 
				
			||||||
 | 
											return Err(TransactionError::Old)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										// lower our assumption until corrected later.
 | 
				
			||||||
 | 
										acct_txs.cur_nonce = CurrentNonce::Assumed(nonce);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									match acct_txs.current.binary_search_by(|x| x.nonce.cmp(&nonce)) {
 | 
				
			||||||
 | 
										Ok(idx) => {
 | 
				
			||||||
 | 
											trace!(target: "txqueue", "Replacing existing transaction from {} with nonce {}",
 | 
				
			||||||
 | 
												sender, nonce);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											let old = ::std::mem::replace(&mut acct_txs.current[idx], tx_info);
 | 
				
			||||||
 | 
											self.by_hash.remove(&old.hash);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											TransactionImportResult::Current
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										Err(idx) => {
 | 
				
			||||||
 | 
											let cur_len = acct_txs.current.len();
 | 
				
			||||||
 | 
											let incr_nonce = nonce + 1.into();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											// current is sorted with one tx per nonce,
 | 
				
			||||||
 | 
											// so if a tx with given nonce wasn't found that means it is either
 | 
				
			||||||
 | 
											// earlier in nonce than all other "current" transactions or later.
 | 
				
			||||||
 | 
											assert!(idx == 0 || idx == cur_len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											if idx == 0 && acct_txs.current.first().map_or(false, |f| f.nonce != incr_nonce) {
 | 
				
			||||||
 | 
												let old_cur = ::std::mem::replace(&mut acct_txs.current, vec![tx_info]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												trace!(target: "txqueue", "Moving {} transactions with nonce > {} to future",
 | 
				
			||||||
 | 
													old_cur.len(), incr_nonce);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												for future in old_cur {
 | 
				
			||||||
 | 
													let future_nonce = future.nonce;
 | 
				
			||||||
 | 
													acct_txs.future.insert(future_nonce, future);
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												TransactionImportResult::Current
 | 
				
			||||||
 | 
											} else if idx == cur_len && acct_txs.current.last().map_or(false, |f| f.nonce + 1.into() != nonce) {
 | 
				
			||||||
 | 
												trace!(target: "txqueue", "Queued future transaction for {}, nonce={}", sender, nonce);
 | 
				
			||||||
 | 
												let future_nonce = nonce;
 | 
				
			||||||
 | 
												acct_txs.future.insert(future_nonce, tx_info);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												TransactionImportResult::Future
 | 
				
			||||||
 | 
											} else {
 | 
				
			||||||
 | 
												trace!(target: "txqueue", "Queued current transaction for {}, nonce={}", sender, nonce);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												// insert, then check if we've filled any gaps.
 | 
				
			||||||
 | 
												acct_txs.current.insert(idx, tx_info);
 | 
				
			||||||
 | 
												acct_txs.adjust_future();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												TransactionImportResult::Current
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							self.by_hash.insert(hash, tx);
 | 
				
			||||||
 | 
							Ok(res)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/// Get pending transaction by hash.
 | 
				
			||||||
 | 
						pub fn transaction(&self, hash: &H256) -> Option<SignedTransaction> {
 | 
				
			||||||
 | 
							self.by_hash.get(hash).map(|tx| (&**tx).clone())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/// Get the next nonce for a given address based on what's within the queue.
 | 
				
			||||||
 | 
						/// If the address has no queued transactions, then `None` will be returned
 | 
				
			||||||
 | 
						/// and the next nonce will have to be deduced via other means.
 | 
				
			||||||
 | 
						pub fn next_nonce(&self, address: &Address) -> Option<U256> {
 | 
				
			||||||
 | 
							self.by_account.get(address).map(AccountTransactions::next_nonce)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/// Get all transactions ready to be propagated.
 | 
				
			||||||
 | 
						/// `best_block_number` and `best_block_timestamp` are used to filter out conditionally
 | 
				
			||||||
 | 
						/// propagated transactions.
 | 
				
			||||||
 | 
						///
 | 
				
			||||||
 | 
						/// Returned transactions are batched by sender, in order of ascending nonce.
 | 
				
			||||||
 | 
						pub fn ready_transactions(&self, best_block_number: u64, best_block_timestamp: u64) -> Vec<PendingTransaction> {
 | 
				
			||||||
 | 
							self.by_account.values()
 | 
				
			||||||
 | 
								.flat_map(|acct_txs| {
 | 
				
			||||||
 | 
									acct_txs.current.iter().take_while(|tx| match tx.condition {
 | 
				
			||||||
 | 
										None => true,
 | 
				
			||||||
 | 
										Some(Condition::Number(blk_num)) => blk_num <= best_block_number,
 | 
				
			||||||
 | 
										Some(Condition::Timestamp(time)) => time <= best_block_timestamp,
 | 
				
			||||||
 | 
									}).map(|info| info.hash)
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
								.filter_map(|hash| match self.by_hash.get(&hash) {
 | 
				
			||||||
 | 
									Some(tx) => Some(tx.clone()),
 | 
				
			||||||
 | 
									None => {
 | 
				
			||||||
 | 
										warn!(target: "txqueue", "Inconsistency detected between `by_hash` and `by_account`: {} not stored.",
 | 
				
			||||||
 | 
											hash);
 | 
				
			||||||
 | 
										None
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
								.collect()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/// Addresses for which we store transactions.
 | 
				
			||||||
 | 
						pub fn queued_senders(&self) -> Vec<Address> {
 | 
				
			||||||
 | 
							self.by_account.keys().cloned().collect()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/// Cull out all transactions by the given address which are invalidated by the given nonce.
 | 
				
			||||||
 | 
						pub fn cull(&mut self, address: Address, cur_nonce: U256) {
 | 
				
			||||||
 | 
							let mut removed_hashes = vec![];
 | 
				
			||||||
 | 
							if let Entry::Occupied(mut entry) = self.by_account.entry(address) {
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									let acct_txs = entry.get_mut();
 | 
				
			||||||
 | 
									acct_txs.cur_nonce = CurrentNonce::Known(cur_nonce);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// cull old "future" keys.
 | 
				
			||||||
 | 
									let old_future: Vec<_> = acct_txs.future.keys().take_while(|&&k| k < cur_nonce).cloned().collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									for old in old_future {
 | 
				
			||||||
 | 
										let hash = acct_txs.future.remove(&old)
 | 
				
			||||||
 | 
											.expect("key extracted from keys iterator; known to exist; qed")
 | 
				
			||||||
 | 
											.hash;
 | 
				
			||||||
 | 
										removed_hashes.push(hash);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// then cull from "current".
 | 
				
			||||||
 | 
									let valid_pos = acct_txs.current.iter().position(|tx| tx.nonce >= cur_nonce);
 | 
				
			||||||
 | 
									match valid_pos {
 | 
				
			||||||
 | 
										None =>
 | 
				
			||||||
 | 
											removed_hashes.extend(acct_txs.current.drain(..).map(|tx| tx.hash)),
 | 
				
			||||||
 | 
										Some(valid) =>
 | 
				
			||||||
 | 
											removed_hashes.extend(acct_txs.current.drain(..valid).map(|tx| tx.hash)),
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// now try and move stuff out of future into current.
 | 
				
			||||||
 | 
									acct_txs.adjust_future();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if entry.get_mut().is_empty() {
 | 
				
			||||||
 | 
									trace!(target: "txqueue", "No more queued transactions for {} after nonce {}",
 | 
				
			||||||
 | 
										address, cur_nonce);
 | 
				
			||||||
 | 
									entry.remove();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							trace!(target: "txqueue", "Culled {} old transactions from sender {} (nonce={})",
 | 
				
			||||||
 | 
								removed_hashes.len(), address, cur_nonce);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for hash in removed_hashes {
 | 
				
			||||||
 | 
								self.by_hash.remove(&hash);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod tests {
 | 
				
			||||||
 | 
						use super::TransactionQueue;
 | 
				
			||||||
 | 
						use util::Address;
 | 
				
			||||||
 | 
						use ethcore::transaction::{Transaction, PendingTransaction, Condition};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						#[test]
 | 
				
			||||||
 | 
						fn queued_senders() {
 | 
				
			||||||
 | 
							let sender = Address::default();
 | 
				
			||||||
 | 
							let mut txq = TransactionQueue::default();
 | 
				
			||||||
 | 
							let tx = Transaction::default().fake_sign(sender);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							txq.import(tx.into()).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assert_eq!(txq.queued_senders(), vec![sender]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							txq.cull(sender, 1.into());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assert_eq!(txq.queued_senders(), vec![]);
 | 
				
			||||||
 | 
							assert!(txq.by_hash.is_empty());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						#[test]
 | 
				
			||||||
 | 
						fn next_nonce() {
 | 
				
			||||||
 | 
							let sender = Address::default();
 | 
				
			||||||
 | 
							let mut txq = TransactionQueue::default();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for i in (0..5).chain(10..15) {
 | 
				
			||||||
 | 
								let mut tx = Transaction::default();
 | 
				
			||||||
 | 
								tx.nonce = i.into();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								let tx = tx.fake_sign(sender);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								txq.import(tx.into()).unwrap();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// current: 0..5, future: 10..15
 | 
				
			||||||
 | 
							assert_eq!(txq.ready_transactions(0, 0).len(), 5);
 | 
				
			||||||
 | 
							assert_eq!(txq.next_nonce(&sender).unwrap(), 5.into());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							txq.cull(sender, 8.into());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// current: empty, future: 10..15
 | 
				
			||||||
 | 
							assert_eq!(txq.ready_transactions(0, 0).len(), 0);
 | 
				
			||||||
 | 
							assert_eq!(txq.next_nonce(&sender).unwrap(), 8.into());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							txq.cull(sender, 10.into());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// current: 10..15, future: empty
 | 
				
			||||||
 | 
							assert_eq!(txq.ready_transactions(0, 0).len(), 5);
 | 
				
			||||||
 | 
							assert_eq!(txq.next_nonce(&sender).unwrap(), 15.into());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						#[test]
 | 
				
			||||||
 | 
						fn current_to_future() {
 | 
				
			||||||
 | 
							let sender = Address::default();
 | 
				
			||||||
 | 
							let mut txq = TransactionQueue::default();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for i in 5..10 {
 | 
				
			||||||
 | 
								let mut tx = Transaction::default();
 | 
				
			||||||
 | 
								tx.nonce = i.into();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								let tx = tx.fake_sign(sender);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								txq.import(tx.into()).unwrap();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assert_eq!(txq.ready_transactions(0, 0).len(), 5);
 | 
				
			||||||
 | 
							assert_eq!(txq.next_nonce(&sender).unwrap(), 10.into());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for i in 0..3 {
 | 
				
			||||||
 | 
								let mut tx = Transaction::default();
 | 
				
			||||||
 | 
								tx.nonce = i.into();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								let tx = tx.fake_sign(sender);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								txq.import(tx.into()).unwrap();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assert_eq!(txq.ready_transactions(0, 0).len(), 3);
 | 
				
			||||||
 | 
							assert_eq!(txq.next_nonce(&sender).unwrap(), 3.into());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for i in 3..5 {
 | 
				
			||||||
 | 
								let mut tx = Transaction::default();
 | 
				
			||||||
 | 
								tx.nonce = i.into();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								let tx = tx.fake_sign(sender);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								txq.import(tx.into()).unwrap();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assert_eq!(txq.ready_transactions(0, 0).len(), 10);
 | 
				
			||||||
 | 
							assert_eq!(txq.next_nonce(&sender).unwrap(), 10.into());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						#[test]
 | 
				
			||||||
 | 
						fn conditional() {
 | 
				
			||||||
 | 
							let mut txq = TransactionQueue::default();
 | 
				
			||||||
 | 
							let sender = Address::default();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for i in 0..5 {
 | 
				
			||||||
 | 
								let mut tx = Transaction::default();
 | 
				
			||||||
 | 
								tx.nonce = i.into();
 | 
				
			||||||
 | 
								let tx = tx.fake_sign(sender);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								txq.import(match i {
 | 
				
			||||||
 | 
									3 => PendingTransaction::new(tx, Some(Condition::Number(100))),
 | 
				
			||||||
 | 
									4 => PendingTransaction::new(tx, Some(Condition::Timestamp(1234))),
 | 
				
			||||||
 | 
									_ => tx.into(),
 | 
				
			||||||
 | 
								}).unwrap();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assert_eq!(txq.ready_transactions(0, 0).len(), 3);
 | 
				
			||||||
 | 
							assert_eq!(txq.ready_transactions(0, 1234).len(), 3);
 | 
				
			||||||
 | 
							assert_eq!(txq.ready_transactions(100, 0).len(), 4);
 | 
				
			||||||
 | 
							assert_eq!(txq.ready_transactions(100, 1234).len(), 5);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						#[test]
 | 
				
			||||||
 | 
						fn cull_from_future() {
 | 
				
			||||||
 | 
							let sender = Address::default();
 | 
				
			||||||
 | 
							let mut txq = TransactionQueue::default();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for i in (0..1).chain(3..10) {
 | 
				
			||||||
 | 
								let mut tx = Transaction::default();
 | 
				
			||||||
 | 
								tx.nonce = i.into();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								let tx = tx.fake_sign(sender);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								txq.import(tx.into()).unwrap();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							txq.cull(sender, 6.into());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assert_eq!(txq.ready_transactions(0, 0).len(), 4);
 | 
				
			||||||
 | 
							assert_eq!(txq.next_nonce(&sender).unwrap(), 10.into());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						#[test]
 | 
				
			||||||
 | 
						fn import_old() {
 | 
				
			||||||
 | 
							let sender = Address::default();
 | 
				
			||||||
 | 
							let mut txq = TransactionQueue::default();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let mut tx_a = Transaction::default();
 | 
				
			||||||
 | 
							tx_a.nonce = 3.into();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let mut tx_b = Transaction::default();
 | 
				
			||||||
 | 
							tx_b.nonce = 2.into();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							txq.import(tx_a.fake_sign(sender).into()).unwrap();
 | 
				
			||||||
 | 
							txq.cull(sender, 3.into());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assert!(txq.import(tx_b.fake_sign(sender).into()).is_err())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						#[test]
 | 
				
			||||||
 | 
						fn replace_is_removed() {
 | 
				
			||||||
 | 
							let sender = Address::default();
 | 
				
			||||||
 | 
							let mut txq = TransactionQueue::default();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let tx_b: PendingTransaction = Transaction::default().fake_sign(sender).into();
 | 
				
			||||||
 | 
							let tx_a: PendingTransaction = {
 | 
				
			||||||
 | 
								let mut tx_a = Transaction::default();
 | 
				
			||||||
 | 
								tx_a.gas_price = tx_b.gas_price + 1.into();
 | 
				
			||||||
 | 
								tx_a.fake_sign(sender).into()
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let hash = tx_a.hash();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							txq.import(tx_a).unwrap();
 | 
				
			||||||
 | 
							txq.import(tx_b).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assert!(txq.transaction(&hash).is_none());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -18,14 +18,18 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use std::fmt::Debug;
 | 
					use std::fmt::Debug;
 | 
				
			||||||
use std::ops::Deref;
 | 
					use std::ops::Deref;
 | 
				
			||||||
use std::sync::Weak;
 | 
					use std::sync::{Arc, Weak};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use futures::{future, Future, BoxFuture};
 | 
					use futures::{future, Future, BoxFuture};
 | 
				
			||||||
 | 
					use light::client::LightChainClient;
 | 
				
			||||||
 | 
					use light::on_demand::{request, OnDemand};
 | 
				
			||||||
 | 
					use light::TransactionQueue as LightTransactionQueue;
 | 
				
			||||||
use rlp::{self, Stream};
 | 
					use rlp::{self, Stream};
 | 
				
			||||||
use util::{Address, H520, H256, U256, Uint, Bytes};
 | 
					use util::{Address, H520, H256, U256, Uint, Bytes, RwLock};
 | 
				
			||||||
use util::sha3::Hashable;
 | 
					use util::sha3::Hashable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use ethkey::Signature;
 | 
					use ethkey::Signature;
 | 
				
			||||||
 | 
					use ethsync::LightSync;
 | 
				
			||||||
use ethcore::miner::MinerService;
 | 
					use ethcore::miner::MinerService;
 | 
				
			||||||
use ethcore::client::MiningBlockChainClient;
 | 
					use ethcore::client::MiningBlockChainClient;
 | 
				
			||||||
use ethcore::transaction::{Action, SignedTransaction, PendingTransaction, Transaction};
 | 
					use ethcore::transaction::{Action, SignedTransaction, PendingTransaction, Transaction};
 | 
				
			||||||
@ -55,7 +59,7 @@ pub trait Dispatcher: Send + Sync + Clone {
 | 
				
			|||||||
		-> BoxFuture<FilledTransactionRequest, Error>;
 | 
							-> BoxFuture<FilledTransactionRequest, Error>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/// Sign the given transaction request without dispatching, fetching appropriate nonce.
 | 
						/// Sign the given transaction request without dispatching, fetching appropriate nonce.
 | 
				
			||||||
	fn sign(&self, accounts: &AccountProvider, filled: FilledTransactionRequest, password: SignWith)
 | 
						fn sign(&self, accounts: Arc<AccountProvider>, filled: FilledTransactionRequest, password: SignWith)
 | 
				
			||||||
		-> BoxFuture<WithToken<SignedTransaction>, Error>;
 | 
							-> BoxFuture<WithToken<SignedTransaction>, Error>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/// "Dispatch" a local transaction.
 | 
						/// "Dispatch" a local transaction.
 | 
				
			||||||
@ -108,13 +112,13 @@ impl<C: MiningBlockChainClient, M: MinerService> Dispatcher for FullDispatcher<C
 | 
				
			|||||||
		}).boxed()
 | 
							}).boxed()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn sign(&self, accounts: &AccountProvider, filled: FilledTransactionRequest, password: SignWith)
 | 
						fn sign(&self, accounts: Arc<AccountProvider>, filled: FilledTransactionRequest, password: SignWith)
 | 
				
			||||||
		-> BoxFuture<WithToken<SignedTransaction>, Error>
 | 
							-> BoxFuture<WithToken<SignedTransaction>, Error>
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		let (client, miner) = (take_weakf!(self.client), take_weakf!(self.miner));
 | 
							let (client, miner) = (take_weakf!(self.client), take_weakf!(self.miner));
 | 
				
			||||||
		let network_id = client.signing_network_id();
 | 
							let network_id = client.signing_network_id();
 | 
				
			||||||
		let address = filled.from;
 | 
							let address = filled.from;
 | 
				
			||||||
		future::ok({
 | 
							future::done({
 | 
				
			||||||
			let t = Transaction {
 | 
								let t = Transaction {
 | 
				
			||||||
				nonce: filled.nonce
 | 
									nonce: filled.nonce
 | 
				
			||||||
					.or_else(|| miner
 | 
										.or_else(|| miner
 | 
				
			||||||
@ -129,31 +133,15 @@ impl<C: MiningBlockChainClient, M: MinerService> Dispatcher for FullDispatcher<C
 | 
				
			|||||||
				data: filled.data,
 | 
									data: filled.data,
 | 
				
			||||||
			};
 | 
								};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			let hash = t.hash(network_id);
 | 
					 | 
				
			||||||
			if accounts.is_hardware_address(address) {
 | 
								if accounts.is_hardware_address(address) {
 | 
				
			||||||
				let mut stream = rlp::RlpStream::new();
 | 
									hardware_signature(&*accounts, address, t, network_id).map(WithToken::No)
 | 
				
			||||||
				t.rlp_append_unsigned_transaction(&mut stream, network_id);
 | 
					 | 
				
			||||||
				let signature = try_bf!(
 | 
					 | 
				
			||||||
					accounts.sign_with_hardware(address, &stream.as_raw())
 | 
					 | 
				
			||||||
						.map_err(|e| {
 | 
					 | 
				
			||||||
							debug!(target: "miner", "Error signing transaction with hardware wallet: {}", e);
 | 
					 | 
				
			||||||
							errors::account("Error signing transaction with hardware wallet", e)
 | 
					 | 
				
			||||||
						})
 | 
					 | 
				
			||||||
				);
 | 
					 | 
				
			||||||
				let signed = try_bf!(
 | 
					 | 
				
			||||||
					SignedTransaction::new(t.with_signature(signature, network_id))
 | 
					 | 
				
			||||||
						.map_err(|e| {
 | 
					 | 
				
			||||||
						  debug!(target: "miner", "Hardware wallet has produced invalid signature: {}", e);
 | 
					 | 
				
			||||||
						  errors::account("Invalid signature generated", e)
 | 
					 | 
				
			||||||
						})
 | 
					 | 
				
			||||||
				);
 | 
					 | 
				
			||||||
				WithToken::No(signed)
 | 
					 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				let signature = try_bf!(signature(accounts, address, hash, password));
 | 
									let hash = t.hash(network_id);
 | 
				
			||||||
				signature.map(|sig| {
 | 
									let signature = try_bf!(signature(&*accounts, address, hash, password));
 | 
				
			||||||
 | 
									Ok(signature.map(|sig| {
 | 
				
			||||||
					SignedTransaction::new(t.with_signature(sig, network_id))
 | 
										SignedTransaction::new(t.with_signature(sig, network_id))
 | 
				
			||||||
						.expect("Transaction was signed by AccountsProvider; it never produces invalid signatures; qed")
 | 
											.expect("Transaction was signed by AccountsProvider; it never produces invalid signatures; qed")
 | 
				
			||||||
				})
 | 
									}))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}).boxed()
 | 
							}).boxed()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -167,6 +155,117 @@ impl<C: MiningBlockChainClient, M: MinerService> Dispatcher for FullDispatcher<C
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Dispatcher for light clients -- fetches default gas price, next nonce, etc. from network.
 | 
				
			||||||
 | 
					/// Light client `ETH` RPC.
 | 
				
			||||||
 | 
					#[derive(Clone)]
 | 
				
			||||||
 | 
					pub struct LightDispatcher {
 | 
				
			||||||
 | 
						sync: Arc<LightSync>,
 | 
				
			||||||
 | 
						client: Arc<LightChainClient>,
 | 
				
			||||||
 | 
						on_demand: Arc<OnDemand>,
 | 
				
			||||||
 | 
						transaction_queue: Arc<RwLock<LightTransactionQueue>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl LightDispatcher {
 | 
				
			||||||
 | 
						/// Create a new `LightDispatcher` from its requisite parts.
 | 
				
			||||||
 | 
						///
 | 
				
			||||||
 | 
						/// For correct operation, the OnDemand service is assumed to be registered as a network handler,
 | 
				
			||||||
 | 
						pub fn new(
 | 
				
			||||||
 | 
							sync: Arc<LightSync>,
 | 
				
			||||||
 | 
							client: Arc<LightChainClient>,
 | 
				
			||||||
 | 
							on_demand: Arc<OnDemand>,
 | 
				
			||||||
 | 
							transaction_queue: Arc<RwLock<LightTransactionQueue>>,
 | 
				
			||||||
 | 
						) -> Self {
 | 
				
			||||||
 | 
							LightDispatcher {
 | 
				
			||||||
 | 
								sync: sync,
 | 
				
			||||||
 | 
								client: client,
 | 
				
			||||||
 | 
								on_demand: on_demand,
 | 
				
			||||||
 | 
								transaction_queue: transaction_queue,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Dispatcher for LightDispatcher {
 | 
				
			||||||
 | 
						fn fill_optional_fields(&self, request: TransactionRequest, default_sender: Address)
 | 
				
			||||||
 | 
							-> BoxFuture<FilledTransactionRequest, Error>
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							let request = request;
 | 
				
			||||||
 | 
							let gas_limit = self.client.best_block_header().gas_limit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							future::ok(FilledTransactionRequest {
 | 
				
			||||||
 | 
								from: request.from.unwrap_or(default_sender),
 | 
				
			||||||
 | 
								used_default_from: request.from.is_none(),
 | 
				
			||||||
 | 
								to: request.to,
 | 
				
			||||||
 | 
								nonce: request.nonce,
 | 
				
			||||||
 | 
								gas_price: request.gas_price.unwrap_or_else(|| 21_000_000.into()), // TODO: fetch corpus from network.
 | 
				
			||||||
 | 
								gas: request.gas.unwrap_or_else(|| gas_limit / 3.into()),
 | 
				
			||||||
 | 
								value: request.value.unwrap_or_else(|| 0.into()),
 | 
				
			||||||
 | 
								data: request.data.unwrap_or_else(Vec::new),
 | 
				
			||||||
 | 
								condition: request.condition,
 | 
				
			||||||
 | 
							}).boxed()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fn sign(&self, accounts: Arc<AccountProvider>, filled: FilledTransactionRequest, password: SignWith)
 | 
				
			||||||
 | 
							-> BoxFuture<WithToken<SignedTransaction>, Error>
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							let network_id = None; // TODO: fetch from client.
 | 
				
			||||||
 | 
							let address = filled.from;
 | 
				
			||||||
 | 
							let best_header = self.client.best_block_header();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let with_nonce = move |filled: FilledTransactionRequest, nonce| {
 | 
				
			||||||
 | 
								let t = Transaction {
 | 
				
			||||||
 | 
									nonce: nonce,
 | 
				
			||||||
 | 
									action: filled.to.map_or(Action::Create, Action::Call),
 | 
				
			||||||
 | 
									gas: filled.gas,
 | 
				
			||||||
 | 
									gas_price: filled.gas_price,
 | 
				
			||||||
 | 
									value: filled.value,
 | 
				
			||||||
 | 
									data: filled.data,
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if accounts.is_hardware_address(address) {
 | 
				
			||||||
 | 
									return hardware_signature(&*accounts, address, t, network_id).map(WithToken::No)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								let hash = t.hash(network_id);
 | 
				
			||||||
 | 
								let signature = signature(&*accounts, address, hash, password)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								Ok(signature.map(|sig| {
 | 
				
			||||||
 | 
									SignedTransaction::new(t.with_signature(sig, network_id))
 | 
				
			||||||
 | 
										.expect("Transaction was signed by AccountsProvider; it never produces invalid signatures; qed")
 | 
				
			||||||
 | 
								}))
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// fast path where we don't go to network; nonce provided or can be gotten from queue.
 | 
				
			||||||
 | 
							let maybe_nonce = filled.nonce.or_else(|| self.transaction_queue.read().next_nonce(&address));
 | 
				
			||||||
 | 
							if let Some(nonce) = maybe_nonce {
 | 
				
			||||||
 | 
								return future::done(with_nonce(filled, nonce)).boxed()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let nonce_future = self.sync.with_context(|ctx| self.on_demand.account(ctx, request::Account {
 | 
				
			||||||
 | 
								header: best_header,
 | 
				
			||||||
 | 
								address: address,
 | 
				
			||||||
 | 
							}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let nonce_future = match nonce_future {
 | 
				
			||||||
 | 
								Some(x) => x,
 | 
				
			||||||
 | 
								None => return future::err(errors::no_light_peers()).boxed()
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							nonce_future
 | 
				
			||||||
 | 
								.map_err(|_| errors::no_light_peers())
 | 
				
			||||||
 | 
								.and_then(move |acc| with_nonce(filled, acc.nonce))
 | 
				
			||||||
 | 
								.boxed()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fn dispatch_transaction(&self, signed_transaction: PendingTransaction) -> Result<H256, Error> {
 | 
				
			||||||
 | 
							let hash = signed_transaction.transaction.hash();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							self.transaction_queue.write().import(signed_transaction)
 | 
				
			||||||
 | 
								.map_err(Into::into)
 | 
				
			||||||
 | 
								.map_err(errors::from_transaction_error)
 | 
				
			||||||
 | 
								.map(|_| hash)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// default MAC to use.
 | 
					/// default MAC to use.
 | 
				
			||||||
pub const DEFAULT_MAC: [u8; 2] = [0, 0];
 | 
					pub const DEFAULT_MAC: [u8; 2] = [0, 0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -251,7 +350,7 @@ impl<T: Debug> From<(T, Option<AccountToken>)> for WithToken<T> {
 | 
				
			|||||||
/// Execute a confirmation payload.
 | 
					/// Execute a confirmation payload.
 | 
				
			||||||
pub fn execute<D: Dispatcher + 'static>(
 | 
					pub fn execute<D: Dispatcher + 'static>(
 | 
				
			||||||
	dispatcher: D,
 | 
						dispatcher: D,
 | 
				
			||||||
	accounts: &AccountProvider,
 | 
						accounts: Arc<AccountProvider>,
 | 
				
			||||||
	payload: ConfirmationPayload,
 | 
						payload: ConfirmationPayload,
 | 
				
			||||||
	pass: SignWith
 | 
						pass: SignWith
 | 
				
			||||||
) -> BoxFuture<WithToken<ConfirmationResponse>, Error> {
 | 
					) -> BoxFuture<WithToken<ConfirmationResponse>, Error> {
 | 
				
			||||||
@ -281,7 +380,7 @@ pub fn execute<D: Dispatcher + 'static>(
 | 
				
			|||||||
				format!("\x19Ethereum Signed Message:\n{}", data.len())
 | 
									format!("\x19Ethereum Signed Message:\n{}", data.len())
 | 
				
			||||||
				.into_bytes();
 | 
									.into_bytes();
 | 
				
			||||||
			message_data.append(&mut data);
 | 
								message_data.append(&mut data);
 | 
				
			||||||
			let res = signature(accounts, address, message_data.sha3(), pass)
 | 
								let res = signature(&accounts, address, message_data.sha3(), pass)
 | 
				
			||||||
				.map(|result| result
 | 
									.map(|result| result
 | 
				
			||||||
					.map(|rsv| {
 | 
										.map(|rsv| {
 | 
				
			||||||
						let mut vrs = [0u8; 65];
 | 
											let mut vrs = [0u8; 65];
 | 
				
			||||||
@ -297,7 +396,7 @@ pub fn execute<D: Dispatcher + 'static>(
 | 
				
			|||||||
			future::done(res).boxed()
 | 
								future::done(res).boxed()
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		ConfirmationPayload::Decrypt(address, data) => {
 | 
							ConfirmationPayload::Decrypt(address, data) => {
 | 
				
			||||||
			let res = decrypt(accounts, address, data, pass)
 | 
								let res = decrypt(&accounts, address, data, pass)
 | 
				
			||||||
				.map(|result| result
 | 
									.map(|result| result
 | 
				
			||||||
					.map(RpcBytes)
 | 
										.map(RpcBytes)
 | 
				
			||||||
					.map(ConfirmationResponse::Decrypt)
 | 
										.map(ConfirmationResponse::Decrypt)
 | 
				
			||||||
@ -318,6 +417,27 @@ fn signature(accounts: &AccountProvider, address: Address, hash: H256, password:
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// obtain a hardware signature from the given account.
 | 
				
			||||||
 | 
					fn hardware_signature(accounts: &AccountProvider, address: Address, t: Transaction, network_id: Option<u64>)
 | 
				
			||||||
 | 
						-> Result<SignedTransaction, Error>
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						debug_assert!(accounts.is_hardware_address(address));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let mut stream = rlp::RlpStream::new();
 | 
				
			||||||
 | 
						t.rlp_append_unsigned_transaction(&mut stream, network_id);
 | 
				
			||||||
 | 
						let signature = accounts.sign_with_hardware(address, &stream.as_raw())
 | 
				
			||||||
 | 
							.map_err(|e| {
 | 
				
			||||||
 | 
								debug!(target: "miner", "Error signing transaction with hardware wallet: {}", e);
 | 
				
			||||||
 | 
								errors::account("Error signing transaction with hardware wallet", e)
 | 
				
			||||||
 | 
							})?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SignedTransaction::new(t.with_signature(signature, network_id))
 | 
				
			||||||
 | 
							.map_err(|e| {
 | 
				
			||||||
 | 
							  debug!(target: "miner", "Hardware wallet has produced invalid signature: {}", e);
 | 
				
			||||||
 | 
							  errors::account("Invalid signature generated", e)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn decrypt(accounts: &AccountProvider, address: Address, msg: Bytes, password: SignWith) -> Result<WithToken<Bytes>, Error> {
 | 
					fn decrypt(accounts: &AccountProvider, address: Address, msg: Bytes, password: SignWith) -> Result<WithToken<Bytes>, Error> {
 | 
				
			||||||
	match password.clone() {
 | 
						match password.clone() {
 | 
				
			||||||
		SignWith::Nothing => accounts.decrypt(address, None, &DEFAULT_MAC, &msg).map(WithToken::No),
 | 
							SignWith::Nothing => accounts.decrypt(address, None, &DEFAULT_MAC, &msg).map(WithToken::No),
 | 
				
			||||||
 | 
				
			|||||||
@ -49,6 +49,7 @@ mod codes {
 | 
				
			|||||||
	pub const COMPILATION_ERROR: i64 = -32050;
 | 
						pub const COMPILATION_ERROR: i64 = -32050;
 | 
				
			||||||
	pub const ENCRYPTION_ERROR: i64 = -32055;
 | 
						pub const ENCRYPTION_ERROR: i64 = -32055;
 | 
				
			||||||
	pub const FETCH_ERROR: i64 = -32060;
 | 
						pub const FETCH_ERROR: i64 = -32060;
 | 
				
			||||||
 | 
						pub const NO_LIGHT_PEERS: i64 = -32065;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn unimplemented(details: Option<String>) -> Error {
 | 
					pub fn unimplemented(details: Option<String>) -> Error {
 | 
				
			||||||
@ -308,3 +309,11 @@ pub fn unknown_block() -> Error {
 | 
				
			|||||||
		data: None,
 | 
							data: None,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn no_light_peers() -> Error {
 | 
				
			||||||
 | 
						Error {
 | 
				
			||||||
 | 
							code: ErrorCode::ServerError(codes::NO_LIGHT_PEERS),
 | 
				
			||||||
 | 
							message: "No light peers who can serve data".into(),
 | 
				
			||||||
 | 
							data: None,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -25,16 +25,18 @@ use jsonrpc_core::Error;
 | 
				
			|||||||
use jsonrpc_macros::Trailing;
 | 
					use jsonrpc_macros::Trailing;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use light::client::Client as LightClient;
 | 
					use light::client::Client as LightClient;
 | 
				
			||||||
use light::cht;
 | 
					use light::{cht, TransactionQueue};
 | 
				
			||||||
use light::on_demand::{request, OnDemand};
 | 
					use light::on_demand::{request, OnDemand};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use ethcore::account_provider::{AccountProvider, DappId};
 | 
					use ethcore::account_provider::{AccountProvider, DappId};
 | 
				
			||||||
use ethcore::basic_account::BasicAccount;
 | 
					use ethcore::basic_account::BasicAccount;
 | 
				
			||||||
use ethcore::encoded;
 | 
					use ethcore::encoded;
 | 
				
			||||||
use ethcore::ids::BlockId;
 | 
					use ethcore::ids::BlockId;
 | 
				
			||||||
 | 
					use ethcore::transaction::SignedTransaction;
 | 
				
			||||||
use ethsync::LightSync;
 | 
					use ethsync::LightSync;
 | 
				
			||||||
 | 
					use rlp::{UntrustedRlp, View};
 | 
				
			||||||
use util::sha3::{SHA3_NULL_RLP, SHA3_EMPTY_LIST_RLP};
 | 
					use util::sha3::{SHA3_NULL_RLP, SHA3_EMPTY_LIST_RLP};
 | 
				
			||||||
use util::U256;
 | 
					use util::{RwLock, U256};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use futures::{future, Future, BoxFuture};
 | 
					use futures::{future, Future, BoxFuture};
 | 
				
			||||||
use futures::sync::oneshot;
 | 
					use futures::sync::oneshot;
 | 
				
			||||||
@ -56,6 +58,7 @@ pub struct EthClient {
 | 
				
			|||||||
	sync: Arc<LightSync>,
 | 
						sync: Arc<LightSync>,
 | 
				
			||||||
	client: Arc<LightClient>,
 | 
						client: Arc<LightClient>,
 | 
				
			||||||
	on_demand: Arc<OnDemand>,
 | 
						on_demand: Arc<OnDemand>,
 | 
				
			||||||
 | 
						transaction_queue: Arc<RwLock<TransactionQueue>>,
 | 
				
			||||||
	accounts: Arc<AccountProvider>,
 | 
						accounts: Arc<AccountProvider>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -76,12 +79,14 @@ impl EthClient {
 | 
				
			|||||||
		sync: Arc<LightSync>,
 | 
							sync: Arc<LightSync>,
 | 
				
			||||||
		client: Arc<LightClient>,
 | 
							client: Arc<LightClient>,
 | 
				
			||||||
		on_demand: Arc<OnDemand>,
 | 
							on_demand: Arc<OnDemand>,
 | 
				
			||||||
 | 
							transaction_queue: Arc<RwLock<TransactionQueue>>,
 | 
				
			||||||
		accounts: Arc<AccountProvider>,
 | 
							accounts: Arc<AccountProvider>,
 | 
				
			||||||
	) -> Self {
 | 
						) -> Self {
 | 
				
			||||||
		EthClient {
 | 
							EthClient {
 | 
				
			||||||
			sync: sync,
 | 
								sync: sync,
 | 
				
			||||||
			client: client,
 | 
								client: client,
 | 
				
			||||||
			on_demand: on_demand,
 | 
								on_demand: on_demand,
 | 
				
			||||||
 | 
								transaction_queue: transaction_queue,
 | 
				
			||||||
			accounts: accounts,
 | 
								accounts: accounts,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -300,11 +305,27 @@ impl Eth for EthClient {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn send_raw_transaction(&self, raw: Bytes) -> Result<RpcH256, Error> {
 | 
						fn send_raw_transaction(&self, raw: Bytes) -> Result<RpcH256, Error> {
 | 
				
			||||||
		Err(errors::unimplemented(None))
 | 
							let best_header = self.client.best_block_header().decode();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							UntrustedRlp::new(&raw.into_vec()).as_val()
 | 
				
			||||||
 | 
								.map_err(errors::from_rlp_error)
 | 
				
			||||||
 | 
								.and_then(|tx| {
 | 
				
			||||||
 | 
									self.client.engine().verify_transaction_basic(&tx, &best_header)
 | 
				
			||||||
 | 
										.map_err(errors::from_transaction_error)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									let signed = SignedTransaction::new(tx).map_err(errors::from_transaction_error)?;
 | 
				
			||||||
 | 
									let hash = signed.hash();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									self.transaction_queue.write().import(signed.into())
 | 
				
			||||||
 | 
										.map(|_| hash)
 | 
				
			||||||
 | 
										.map_err(Into::into)
 | 
				
			||||||
 | 
										.map_err(errors::from_transaction_error)
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
								.map(Into::into)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn submit_transaction(&self, raw: Bytes) -> Result<RpcH256, Error> {
 | 
						fn submit_transaction(&self, raw: Bytes) -> Result<RpcH256, Error> {
 | 
				
			||||||
		Err(errors::unimplemented(None))
 | 
							self.send_raw_transaction(raw)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn call(&self, req: CallRequest, num: Trailing<BlockNumber>) -> Result<Bytes, Error> {
 | 
						fn call(&self, req: CallRequest, num: Trailing<BlockNumber>) -> Result<Bytes, Error> {
 | 
				
			||||||
 | 
				
			|||||||
@ -113,7 +113,7 @@ impl<D: Dispatcher + 'static> Personal for PersonalClient<D> {
 | 
				
			|||||||
		dispatcher.fill_optional_fields(request.into(), default)
 | 
							dispatcher.fill_optional_fields(request.into(), default)
 | 
				
			||||||
			.and_then(move |filled| {
 | 
								.and_then(move |filled| {
 | 
				
			||||||
				let condition = filled.condition.clone().map(Into::into);
 | 
									let condition = filled.condition.clone().map(Into::into);
 | 
				
			||||||
				dispatcher.sign(&accounts, filled, SignWith::Password(password))
 | 
									dispatcher.sign(accounts, filled, SignWith::Password(password))
 | 
				
			||||||
					.map(|tx| tx.into_value())
 | 
										.map(|tx| tx.into_value())
 | 
				
			||||||
					.map(move |tx| PendingTransaction::new(tx, condition))
 | 
										.map(move |tx| PendingTransaction::new(tx, condition))
 | 
				
			||||||
					.map(move |tx| (tx, dispatcher))
 | 
										.map(move |tx| (tx, dispatcher))
 | 
				
			||||||
 | 
				
			|||||||
@ -52,7 +52,7 @@ impl<D: Dispatcher + 'static> SignerClient<D> {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn confirm_internal<F, T>(&self, id: U256, modification: TransactionModification, f: F) -> BoxFuture<WithToken<ConfirmationResponse>, Error> where
 | 
						fn confirm_internal<F, T>(&self, id: U256, modification: TransactionModification, f: F) -> BoxFuture<WithToken<ConfirmationResponse>, Error> where
 | 
				
			||||||
		F: FnOnce(D, &AccountProvider, ConfirmationPayload) -> T,
 | 
							F: FnOnce(D, Arc<AccountProvider>, ConfirmationPayload) -> T,
 | 
				
			||||||
		T: IntoFuture<Item=WithToken<ConfirmationResponse>, Error=Error>,
 | 
							T: IntoFuture<Item=WithToken<ConfirmationResponse>, Error=Error>,
 | 
				
			||||||
		T::Future: Send + 'static
 | 
							T::Future: Send + 'static
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
@ -87,7 +87,7 @@ impl<D: Dispatcher + 'static> SignerClient<D> {
 | 
				
			|||||||
					request.condition = condition.clone().map(Into::into);
 | 
										request.condition = condition.clone().map(Into::into);
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			let fut = f(dispatcher, &*accounts, payload);
 | 
								let fut = f(dispatcher, accounts, payload);
 | 
				
			||||||
			fut.into_future().then(move |result| {
 | 
								fut.into_future().then(move |result| {
 | 
				
			||||||
				// Execute
 | 
									// Execute
 | 
				
			||||||
				if let Ok(ref response) = result {
 | 
									if let Ok(ref response) = result {
 | 
				
			||||||
 | 
				
			|||||||
@ -95,7 +95,7 @@ impl<D: Dispatcher + 'static> SigningQueueClient<D> {
 | 
				
			|||||||
			.and_then(move |payload| {
 | 
								.and_then(move |payload| {
 | 
				
			||||||
				let sender = payload.sender();
 | 
									let sender = payload.sender();
 | 
				
			||||||
				if accounts.is_unlocked(sender) {
 | 
									if accounts.is_unlocked(sender) {
 | 
				
			||||||
					dispatch::execute(dispatcher, &accounts, payload, dispatch::SignWith::Nothing)
 | 
										dispatch::execute(dispatcher, accounts, payload, dispatch::SignWith::Nothing)
 | 
				
			||||||
						.map(|v| v.into_value())
 | 
											.map(|v| v.into_value())
 | 
				
			||||||
						.map(DispatchResult::Value)
 | 
											.map(DispatchResult::Value)
 | 
				
			||||||
						.boxed()
 | 
											.boxed()
 | 
				
			||||||
 | 
				
			|||||||
@ -61,7 +61,7 @@ impl<D: Dispatcher + 'static> SigningUnsafeClient<D> {
 | 
				
			|||||||
		let dis = self.dispatcher.clone();
 | 
							let dis = self.dispatcher.clone();
 | 
				
			||||||
		dispatch::from_rpc(payload, default, &dis)
 | 
							dispatch::from_rpc(payload, default, &dis)
 | 
				
			||||||
			.and_then(move |payload| {
 | 
								.and_then(move |payload| {
 | 
				
			||||||
				dispatch::execute(dis, &accounts, payload, dispatch::SignWith::Nothing)
 | 
									dispatch::execute(dis, accounts, payload, dispatch::SignWith::Nothing)
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
			.map(|v| v.into_value())
 | 
								.map(|v| v.into_value())
 | 
				
			||||||
			.boxed()
 | 
								.boxed()
 | 
				
			||||||
 | 
				
			|||||||
@ -34,7 +34,7 @@ use ipc::{BinaryConvertable, BinaryConvertError, IpcConfig};
 | 
				
			|||||||
use std::str::FromStr;
 | 
					use std::str::FromStr;
 | 
				
			||||||
use parking_lot::RwLock;
 | 
					use parking_lot::RwLock;
 | 
				
			||||||
use chain::{ETH_PACKET_COUNT, SNAPSHOT_SYNC_PACKET_COUNT};
 | 
					use chain::{ETH_PACKET_COUNT, SNAPSHOT_SYNC_PACKET_COUNT};
 | 
				
			||||||
use light::client::LightChainClient;
 | 
					use light::client::AsLightClient;
 | 
				
			||||||
use light::Provider;
 | 
					use light::Provider;
 | 
				
			||||||
use light::net::{self as light_net, LightProtocol, Params as LightParams, Capabilities, Handler as LightHandler, EventContext};
 | 
					use light::net::{self as light_net, LightProtocol, Params as LightParams, Capabilities, Handler as LightHandler, EventContext};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -642,7 +642,7 @@ pub struct LightSync {
 | 
				
			|||||||
impl LightSync {
 | 
					impl LightSync {
 | 
				
			||||||
	/// Create a new light sync service.
 | 
						/// Create a new light sync service.
 | 
				
			||||||
	pub fn new<L>(params: LightSyncParams<L>) -> Result<Self, NetworkError>
 | 
						pub fn new<L>(params: LightSyncParams<L>) -> Result<Self, NetworkError>
 | 
				
			||||||
		where L: LightChainClient + Provider + 'static
 | 
							where L: AsLightClient + Provider + Sync + Send + 'static
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		use light_sync::LightSync as SyncHandler;
 | 
							use light_sync::LightSync as SyncHandler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -36,7 +36,7 @@ use std::collections::HashMap;
 | 
				
			|||||||
use std::mem;
 | 
					use std::mem;
 | 
				
			||||||
use std::sync::Arc;
 | 
					use std::sync::Arc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use light::client::LightChainClient;
 | 
					use light::client::{AsLightClient, LightChainClient};
 | 
				
			||||||
use light::net::{
 | 
					use light::net::{
 | 
				
			||||||
	Announcement, Handler, BasicContext, EventContext,
 | 
						Announcement, Handler, BasicContext, EventContext,
 | 
				
			||||||
	Capabilities, ReqId, Status,
 | 
						Capabilities, ReqId, Status,
 | 
				
			||||||
@ -106,8 +106,9 @@ impl AncestorSearch {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn process_response<L>(self, ctx: &ResponseContext, client: &L) -> AncestorSearch
 | 
						fn process_response<L>(self, ctx: &ResponseContext, client: &L) -> AncestorSearch
 | 
				
			||||||
		where L: LightChainClient
 | 
							where L: AsLightClient
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
 | 
							let client = client.as_light_client();
 | 
				
			||||||
		let first_num = client.chain_info().first_block_number.unwrap_or(0);
 | 
							let first_num = client.chain_info().first_block_number.unwrap_or(0);
 | 
				
			||||||
		match self {
 | 
							match self {
 | 
				
			||||||
			AncestorSearch::Awaiting(id, start, req) => {
 | 
								AncestorSearch::Awaiting(id, start, req) => {
 | 
				
			||||||
@ -203,7 +204,7 @@ impl<'a> ResponseContext for ResponseCtx<'a> {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Light client synchronization manager. See module docs for more details.
 | 
					/// Light client synchronization manager. See module docs for more details.
 | 
				
			||||||
pub struct LightSync<L: LightChainClient> {
 | 
					pub struct LightSync<L: AsLightClient> {
 | 
				
			||||||
	best_seen: Mutex<Option<ChainInfo>>, // best seen block on the network.
 | 
						best_seen: Mutex<Option<ChainInfo>>, // best seen block on the network.
 | 
				
			||||||
	peers: RwLock<HashMap<PeerId, Mutex<Peer>>>, // peers which are relevant to synchronization.
 | 
						peers: RwLock<HashMap<PeerId, Mutex<Peer>>>, // peers which are relevant to synchronization.
 | 
				
			||||||
	client: Arc<L>,
 | 
						client: Arc<L>,
 | 
				
			||||||
@ -211,7 +212,7 @@ pub struct LightSync<L: LightChainClient> {
 | 
				
			|||||||
	state: Mutex<SyncState>,
 | 
						state: Mutex<SyncState>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<L: LightChainClient> Handler for LightSync<L> {
 | 
					impl<L: AsLightClient + Send + Sync> Handler for LightSync<L> {
 | 
				
			||||||
	fn on_connect(&self, ctx: &EventContext, status: &Status, capabilities: &Capabilities) {
 | 
						fn on_connect(&self, ctx: &EventContext, status: &Status, capabilities: &Capabilities) {
 | 
				
			||||||
		if !capabilities.serve_headers {
 | 
							if !capabilities.serve_headers {
 | 
				
			||||||
			trace!(target: "sync", "Disconnecting irrelevant peer: {}", ctx.peer());
 | 
								trace!(target: "sync", "Disconnecting irrelevant peer: {}", ctx.peer());
 | 
				
			||||||
@ -344,7 +345,7 @@ impl<L: LightChainClient> Handler for LightSync<L> {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// private helpers
 | 
					// private helpers
 | 
				
			||||||
impl<L: LightChainClient> LightSync<L> {
 | 
					impl<L: AsLightClient> LightSync<L> {
 | 
				
			||||||
	// Begins a search for the common ancestor and our best block.
 | 
						// Begins a search for the common ancestor and our best block.
 | 
				
			||||||
	// does not lock state, instead has a mutable reference to it passed.
 | 
						// does not lock state, instead has a mutable reference to it passed.
 | 
				
			||||||
	fn begin_search(&self, state: &mut SyncState) {
 | 
						fn begin_search(&self, state: &mut SyncState) {
 | 
				
			||||||
@ -354,8 +355,8 @@ impl<L: LightChainClient> LightSync<L> {
 | 
				
			|||||||
			return;
 | 
								return;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		self.client.flush_queue();
 | 
							self.client.as_light_client().flush_queue();
 | 
				
			||||||
		let chain_info = self.client.chain_info();
 | 
							let chain_info = self.client.as_light_client().chain_info();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		trace!(target: "sync", "Beginning search for common ancestor from {:?}",
 | 
							trace!(target: "sync", "Beginning search for common ancestor from {:?}",
 | 
				
			||||||
			(chain_info.best_block_number, chain_info.best_block_hash));
 | 
								(chain_info.best_block_number, chain_info.best_block_hash));
 | 
				
			||||||
@ -366,8 +367,10 @@ impl<L: LightChainClient> LightSync<L> {
 | 
				
			|||||||
	fn maintain_sync(&self, ctx: &BasicContext) {
 | 
						fn maintain_sync(&self, ctx: &BasicContext) {
 | 
				
			||||||
		const DRAIN_AMOUNT: usize = 128;
 | 
							const DRAIN_AMOUNT: usize = 128;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let client = self.client.as_light_client();
 | 
				
			||||||
 | 
							let chain_info = client.chain_info();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		let mut state = self.state.lock();
 | 
							let mut state = self.state.lock();
 | 
				
			||||||
		let chain_info = self.client.chain_info();
 | 
					 | 
				
			||||||
		debug!(target: "sync", "Maintaining sync ({:?})", &*state);
 | 
							debug!(target: "sync", "Maintaining sync ({:?})", &*state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// drain any pending blocks into the queue.
 | 
							// drain any pending blocks into the queue.
 | 
				
			||||||
@ -376,7 +379,7 @@ impl<L: LightChainClient> LightSync<L> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			'a:
 | 
								'a:
 | 
				
			||||||
			loop {
 | 
								loop {
 | 
				
			||||||
				if self.client.queue_info().is_full() { break }
 | 
									if client.queue_info().is_full() { break }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				*state = match mem::replace(&mut *state, SyncState::Idle) {
 | 
									*state = match mem::replace(&mut *state, SyncState::Idle) {
 | 
				
			||||||
					SyncState::Rounds(round)
 | 
										SyncState::Rounds(round)
 | 
				
			||||||
@ -388,7 +391,7 @@ impl<L: LightChainClient> LightSync<L> {
 | 
				
			|||||||
				trace!(target: "sync", "Drained {} headers to import", sink.len());
 | 
									trace!(target: "sync", "Drained {} headers to import", sink.len());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				for header in sink.drain(..) {
 | 
									for header in sink.drain(..) {
 | 
				
			||||||
					if let Err(e) = self.client.queue_header(header) {
 | 
										if let Err(e) = client.queue_header(header) {
 | 
				
			||||||
						debug!(target: "sync", "Found bad header ({:?}). Reset to search state.", e);
 | 
											debug!(target: "sync", "Found bad header ({:?}). Reset to search state.", e);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						self.begin_search(&mut state);
 | 
											self.begin_search(&mut state);
 | 
				
			||||||
@ -492,7 +495,7 @@ impl<L: LightChainClient> LightSync<L> {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// public API
 | 
					// public API
 | 
				
			||||||
impl<L: LightChainClient> LightSync<L> {
 | 
					impl<L: AsLightClient> LightSync<L> {
 | 
				
			||||||
	/// Create a new instance of `LightSync`.
 | 
						/// Create a new instance of `LightSync`.
 | 
				
			||||||
	///
 | 
						///
 | 
				
			||||||
	/// This won't do anything until registered as a handler
 | 
						/// This won't do anything until registered as a handler
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user