diff --git a/.travis.yml b/.travis.yml index f7ac723e1..f66b38c51 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,6 +41,7 @@ env: - RUN_COVERAGE="false" - RUN_BUILD="false" - RUN_BENCHES="false" + - RUST_BACKTRACE="1" cache: apt: true directories: diff --git a/README.md b/README.md index 47a27e30e..7aa5e8858 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ curl -sf https://raw.githubusercontent.com/brson/multirust/master/quick-install. - OSX with Homebrew: ```bash brew update && brew install multirust +multirust default stable ``` Then, download and build Parity: diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index 7e1a8e23f..43920708b 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -103,6 +103,11 @@ pub trait BlockProvider { self.block(&address.block_hash).and_then(|bytes| BlockView::new(&bytes).localized_transaction_at(address.index)) } + /// Get transaction receipt. + fn transaction_receipt(&self, address: &TransactionAddress) -> Option { + self.block_receipts(&address.block_hash).and_then(|br| br.receipts.into_iter().nth(address.index)) + } + /// Get a list of transactions for a given block. /// Returns None if block does not exist. fn transactions(&self, hash: &H256) -> Option> { diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 1d34bf158..b2254e285 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -39,6 +39,7 @@ use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute}; use client::{BlockId, TransactionId, ClientConfig, BlockChainClient}; use env_info::EnvInfo; use executive::{Executive, Executed}; +use receipt::LocalizedReceipt; pub use blockchain::CacheSize as BlockChainCacheSize; /// General block status @@ -384,6 +385,16 @@ impl Client where V: Verifier { BlockId::Latest => Some(self.chain.best_block_number()) } } + + fn transaction_address(&self, id: TransactionId) -> Option { + match id { + TransactionId::Hash(ref hash) => self.chain.transaction_address(hash), + TransactionId::Location(id, index) => Self::block_hash(&self.chain, id).map(|hash| TransactionAddress { + block_hash: hash, + index: index + }) + } + } } impl BlockChainClient for Client where V: Verifier { @@ -535,13 +546,43 @@ impl BlockChainClient for Client where V: Verifier { } fn transaction(&self, id: TransactionId) -> Option { - match id { - TransactionId::Hash(ref hash) => self.chain.transaction_address(hash), - TransactionId::Location(id, index) => Self::block_hash(&self.chain, id).map(|hash| TransactionAddress { - block_hash: hash, - index: index - }) - }.and_then(|address| self.chain.transaction(&address)) + self.transaction_address(id).and_then(|address| self.chain.transaction(&address)) + } + + fn transaction_receipt(&self, id: TransactionId) -> Option { + self.transaction_address(id).and_then(|address| { + let t = self.chain.block(&address.block_hash) + .and_then(|block| BlockView::new(&block).localized_transaction_at(address.index)); + + match (t, self.chain.transaction_receipt(&address)) { + (Some(tx), Some(receipt)) => { + let block_hash = tx.block_hash.clone(); + let block_number = tx.block_number.clone(); + let transaction_hash = tx.hash(); + let transaction_index = tx.transaction_index; + Some(LocalizedReceipt { + transaction_hash: tx.hash(), + transaction_index: tx.transaction_index, + block_hash: tx.block_hash, + block_number: tx.block_number, + // TODO: to fix this, query all previous transaction receipts and retrieve their gas usage + cumulative_gas_used: receipt.gas_used, + gas_used: receipt.gas_used, + // TODO: to fix this, store created contract address in db + contract_address: None, + logs: receipt.logs.into_iter().enumerate().map(|(i, log)| LocalizedLogEntry { + entry: log, + block_hash: block_hash.clone(), + block_number: block_number, + transaction_hash: transaction_hash.clone(), + transaction_index: transaction_index, + log_index: i + }).collect() + }) + }, + _ => None + } + }) } fn tree_route(&self, from: &H256, to: &H256) -> Option { @@ -626,7 +667,7 @@ impl BlockChainClient for Client where V: Verifier { .map(|(i, log)| LocalizedLogEntry { entry: log, block_hash: hash.clone(), - block_number: number as usize, + block_number: number, transaction_hash: hashes.get(index).cloned().unwrap_or_else(H256::new), transaction_index: index, log_index: log_index + i diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index 9663ab62b..74b05652f 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -39,6 +39,7 @@ use transaction::{LocalizedTransaction, SignedTransaction}; use log_entry::LocalizedLogEntry; use filter::Filter; use error::{ImportResult, Error}; +use receipt::LocalizedReceipt; /// Blockchain database client. Owns and manages a blockchain and a block queue. pub trait BlockChainClient : Sync + Send { @@ -76,6 +77,9 @@ pub trait BlockChainClient : Sync + Send { /// Get transaction with given hash. fn transaction(&self, id: TransactionId) -> Option; + /// Get transaction receipt with given hash. + fn transaction_receipt(&self, id: TransactionId) -> Option; + /// Get a tree route between `from` and `to`. /// See `BlockChain::tree_route`. fn tree_route(&self, from: &H256, to: &H256) -> Option; diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 1aaac7f53..48c16ac10 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -23,7 +23,7 @@ use client::{BlockChainClient, BlockChainInfo, BlockStatus, BlockId, Transaction use header::{Header as BlockHeader, BlockNumber}; use filter::Filter; use log_entry::LocalizedLogEntry; -use receipt::Receipt; +use receipt::{Receipt, LocalizedReceipt}; use extras::BlockReceipts; use error::{ImportResult}; @@ -224,6 +224,10 @@ impl BlockChainClient for TestBlockChainClient { unimplemented!(); } + fn transaction_receipt(&self, _id: TransactionId) -> Option { + unimplemented!(); + } + fn blocks_with_bloom(&self, _bloom: &H2048, _from_block: BlockId, _to_block: BlockId) -> Option> { unimplemented!(); } diff --git a/ethcore/src/log_entry.rs b/ethcore/src/log_entry.rs index 63d09b4f0..cf74a6df9 100644 --- a/ethcore/src/log_entry.rs +++ b/ethcore/src/log_entry.rs @@ -18,6 +18,7 @@ use util::*; use basic_types::LogBloom; +use header::BlockNumber; /// A record of execution for a `LOG` operation. #[derive(Default, Debug, Clone, PartialEq, Eq)] @@ -84,7 +85,7 @@ pub struct LocalizedLogEntry { /// Block in which this log was created. pub block_hash: H256, /// Block number. - pub block_number: usize, + pub block_number: BlockNumber, /// Hash of transaction in which this log was created. pub transaction_hash: H256, /// Index of transaction within block. diff --git a/ethcore/src/receipt.rs b/ethcore/src/receipt.rs index ae83a174a..7f0b0d8dd 100644 --- a/ethcore/src/receipt.rs +++ b/ethcore/src/receipt.rs @@ -18,7 +18,8 @@ use util::*; use basic_types::LogBloom; -use log_entry::LogEntry; +use header::BlockNumber; +use log_entry::{LogEntry, LocalizedLogEntry}; /// Information describing execution of a transaction. #[derive(Default, Debug, Clone)] @@ -74,6 +75,26 @@ impl HeapSizeOf for Receipt { } } +/// Receipt with additional info. +pub struct LocalizedReceipt { + /// Transaction hash. + pub transaction_hash: H256, + /// Transaction index. + pub transaction_index: usize, + /// Block hash. + pub block_hash: H256, + /// Block number. + pub block_number: BlockNumber, + /// Cumulative gas used. + pub cumulative_gas_used: U256, + /// Gas used. + pub gas_used: U256, + /// Contract address. + pub contract_address: Option
, + /// Logs + pub logs: Vec, +} + #[test] fn test_basic() { let expected = FromHex::from_hex("f90162a02f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee83040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap(); diff --git a/ethcore/src/service.rs b/ethcore/src/service.rs index bcfe7724f..95a891198 100644 --- a/ethcore/src/service.rs +++ b/ethcore/src/service.rs @@ -146,7 +146,7 @@ mod tests { fn it_can_be_started() { let spec = get_test_spec(); let temp_path = RandomTempPath::new(); - let service = ClientService::start(ClientConfig::default(), spec, NetworkConfiguration::new_with_port(40456), &temp_path.as_path()); + let service = ClientService::start(ClientConfig::default(), spec, NetworkConfiguration::new_local(), &temp_path.as_path()); assert!(service.is_ok()); } } diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index a99a912f8..d7ee478bf 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -31,7 +31,7 @@ use ethcore::ethereum::Ethash; use ethcore::ethereum::denominations::shannon; use ethcore::transaction::Transaction as EthTransaction; use v1::traits::{Eth, EthFilter}; -use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, TransactionRequest, OptionalValue, Index, Filter, Log}; +use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, TransactionRequest, OptionalValue, Index, Filter, Log, Receipt}; use v1::helpers::{PollFilter, PollManager, ExternalMinerService, ExternalMiner}; use util::keys::store::AccountProvider; @@ -293,6 +293,15 @@ impl Eth for EthClient .and_then(|(number, index)| self.transaction(TransactionId::Location(number.into(), index.value()))) } + fn transaction_receipt(&self, params: Params) -> Result { + from_params::<(H256,)>(params) + .and_then(|(hash,)| { + let client = take_weak!(self.client); + let receipt = client.transaction_receipt(TransactionId::Hash(hash)); + to_value(&receipt.map(Receipt::from)) + }) + } + fn uncle_by_block_hash_and_index(&self, params: Params) -> Result { from_params::<(H256, Index)>(params) .and_then(|(hash, index)| self.uncle(BlockId::Hash(hash), index.value())) diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index ebc3bc0ff..0121e4aea 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -24,6 +24,7 @@ mod optionals; mod sync; mod transaction; mod transaction_request; +mod receipt; pub use self::block::{Block, BlockTransactions}; pub use self::block_number::BlockNumber; @@ -35,4 +36,5 @@ pub use self::optionals::OptionalValue; pub use self::sync::{SyncStatus, SyncInfo}; pub use self::transaction::Transaction; pub use self::transaction_request::TransactionRequest; +pub use self::receipt::Receipt; diff --git a/rpc/src/v1/types/receipt.rs b/rpc/src/v1/types/receipt.rs new file mode 100644 index 000000000..fa34d5df5 --- /dev/null +++ b/rpc/src/v1/types/receipt.rs @@ -0,0 +1,56 @@ +// Copyright 2015, 2016 Ethcore (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 . + +use util::numbers::U256; +use util::hash::{Address, H256}; +use v1::types::Log; +use ethcore::receipt::LocalizedReceipt; + +#[derive(Debug, Serialize)] +pub struct Receipt { + #[serde(rename="transactionHash")] + pub transaction_hash: H256, + #[serde(rename="transactionIndex")] + pub transaction_index: U256, + #[serde(rename="blockHash")] + pub block_hash: H256, + #[serde(rename="blockNumber")] + pub block_number: U256, + #[serde(rename="cumulativeGasUsed")] + pub cumulative_gas_used: U256, + #[serde(rename="gasUsed")] + pub gas_used: U256, + #[serde(rename="contractAddress")] + pub contract_address: Option
, + pub logs: Vec, +} + +impl From for Receipt { + fn from(r: LocalizedReceipt) -> Self { + Receipt { + transaction_hash: r.transaction_hash, + transaction_index: U256::from(r.transaction_index), + block_hash: r.block_hash, + block_number: U256::from(r.block_number), + cumulative_gas_used: r.cumulative_gas_used, + gas_used: r.gas_used, + contract_address: r.contract_address, + logs: r.logs.into_iter().map(From::from).collect(), + } + } +} + + diff --git a/util/src/network/host.rs b/util/src/network/host.rs index 02c576424..d4e6d13ef 100644 --- a/util/src/network/host.rs +++ b/util/src/network/host.rs @@ -105,6 +105,14 @@ impl NetworkConfiguration { config.listen_address = Some(SocketAddr::from_str(&format!("0.0.0.0:{}", port)).unwrap()); config } + + /// Create new default configuration for localhost-only connection with random port (usefull for testing) + pub fn new_local() -> NetworkConfiguration { + let mut config = NetworkConfiguration::new(); + config.listen_address = Some(SocketAddr::from_str("127.0.0.1:0").unwrap()); + config.nat_enabled = false; + config + } } // Tokens @@ -269,12 +277,12 @@ pub struct HostInfo { pub protocol_version: u32, /// Client identifier pub client_version: String, - /// TCP connection port. - pub listen_port: u16, /// Registered capabilities (handlers) pub capabilities: Vec, + /// Local address + discovery port + pub local_endpoint: NodeEndpoint, /// Public address + discovery port - public_endpoint: NodeEndpoint, + pub public_endpoint: Option, } impl HostInfo { @@ -307,7 +315,7 @@ struct ProtocolTimer { /// Root IO handler. Manages protocol handlers, IO timers and network connections. pub struct Host where Message: Send + Sync + Clone { pub info: RwLock, - tcp_listener: Mutex>, + tcp_listener: Mutex, handshakes: Arc>>, sessions: Arc>>, discovery: Mutex>, @@ -321,13 +329,12 @@ pub struct Host where Message: Send + Sync + Clone { impl Host where Message: Send + Sync + Clone { /// Create a new instance - pub fn new(config: NetworkConfiguration) -> Host { - let listen_address = match config.listen_address { + pub fn new(config: NetworkConfiguration) -> Result, UtilError> { + let mut listen_address = match config.listen_address { None => SocketAddr::from_str("0.0.0.0:30304").unwrap(), Some(addr) => addr, }; - let udp_port = config.udp_port.unwrap_or(listen_address.port()); let keys = if let Some(ref secret) = config.use_secret { KeyPair::from_secret(secret.clone()).unwrap() } else { @@ -342,7 +349,12 @@ impl Host where Message: Send + Sync + Clone { |s| KeyPair::from_secret(s).expect("Error creating node secret key")) }; let path = config.config_path.clone(); + // Setup the server socket + let tcp_listener = try!(TcpListener::bind(&listen_address)); + listen_address = SocketAddr::new(listen_address.ip(), try!(tcp_listener.local_addr()).port()); + let udp_port = config.udp_port.unwrap_or(listen_address.port()); let local_endpoint = NodeEndpoint { address: listen_address, udp_port: udp_port }; + let mut host = Host:: { info: RwLock::new(HostInfo { keys: keys, @@ -350,12 +362,12 @@ impl Host where Message: Send + Sync + Clone { nonce: H256::random(), protocol_version: PROTOCOL_VERSION, client_version: version(), - listen_port: 0, capabilities: Vec::new(), - public_endpoint: local_endpoint, // will be replaced by public once it is resolved + public_endpoint: None, + local_endpoint: local_endpoint, }), discovery: Mutex::new(None), - tcp_listener: Mutex::new(None), + tcp_listener: Mutex::new(tcp_listener), handshakes: Arc::new(RwLock::new(Slab::new_starting_at(FIRST_HANDSHAKE, MAX_HANDSHAKES))), sessions: Arc::new(RwLock::new(Slab::new_starting_at(FIRST_SESSION, MAX_SESSIONS))), nodes: RwLock::new(NodeTable::new(path)), @@ -365,14 +377,12 @@ impl Host where Message: Send + Sync + Clone { stats: Arc::new(NetworkStats::default()), pinned_nodes: Vec::new(), }; - let port = listen_address.port(); - host.info.write().unwrap().deref_mut().listen_port = port; let boot_nodes = host.info.read().unwrap().config.boot_nodes.clone(); for n in boot_nodes { host.add_node(&n); } - host + Ok(host) } pub fn stats(&self) -> Arc { @@ -397,50 +407,50 @@ impl Host where Message: Send + Sync + Clone { self.info.read().unwrap().client_version.clone() } - pub fn client_url(&self) -> String { - format!("{}", Node::new(self.info.read().unwrap().id().clone(), self.info.read().unwrap().public_endpoint.clone())) + pub fn external_url(&self) -> Option { + self.info.read().unwrap().public_endpoint.as_ref().map(|e| format!("{}", Node::new(self.info.read().unwrap().id().clone(), e.clone()))) } - fn init_public_interface(&self, io: &IoContext>) { + pub fn local_url(&self) -> String { + let r = format!("{}", Node::new(self.info.read().unwrap().id().clone(), self.info.read().unwrap().local_endpoint.clone())); + println!("{}", r); + r + } + + fn init_public_interface(&self, io: &IoContext>) -> Result<(), UtilError> { io.clear_timer(INIT_PUBLIC).unwrap(); - let mut tcp_listener = self.tcp_listener.lock().unwrap(); - if tcp_listener.is_some() { - return; + if self.info.read().unwrap().public_endpoint.is_some() { + return Ok(()); } - // public_endpoint in host info contains local adderss at this point - let listen_address = self.info.read().unwrap().public_endpoint.address.clone(); - let udp_port = self.info.read().unwrap().config.udp_port.unwrap_or(listen_address.port()); + let local_endpoint = self.info.read().unwrap().local_endpoint.clone(); let public_address = self.info.read().unwrap().config.public_address.clone(); let public_endpoint = match public_address { None => { - let public_address = select_public_address(listen_address.port()); - let local_endpoint = NodeEndpoint { address: public_address, udp_port: udp_port }; + let public_address = select_public_address(local_endpoint.address.port()); + let public_endpoint = NodeEndpoint { address: public_address, udp_port: local_endpoint.udp_port }; if self.info.read().unwrap().config.nat_enabled { match map_external_address(&local_endpoint) { Some(endpoint) => { - info!("NAT mappped to external address {}", endpoint.address); + info!("NAT mapped to external address {}", endpoint.address); endpoint }, - None => local_endpoint + None => public_endpoint } } else { - local_endpoint + public_endpoint } } - Some(addr) => NodeEndpoint { address: addr, udp_port: udp_port } + Some(addr) => NodeEndpoint { address: addr, udp_port: local_endpoint.udp_port } }; - // Setup the server socket - *tcp_listener = Some(TcpListener::bind(&listen_address).unwrap()); - self.info.write().unwrap().public_endpoint = public_endpoint.clone(); - io.register_stream(TCP_ACCEPT).expect("Error registering TCP listener"); - info!("Public node URL: {}", self.client_url()); + self.info.write().unwrap().public_endpoint = Some(public_endpoint.clone()); + info!("Public node URL: {}", self.external_url().unwrap()); // Initialize discovery. let discovery = { let info = self.info.read().unwrap(); if info.config.discovery_enabled && !info.config.pin { - Some(Discovery::new(&info.keys, listen_address.clone(), public_endpoint, DISCOVERY)) + Some(Discovery::new(&info.keys, public_endpoint.address.clone(), public_endpoint, DISCOVERY)) } else { None } }; @@ -449,11 +459,13 @@ impl Host where Message: Send + Sync + Clone { for n in self.nodes.read().unwrap().unordered_entries() { discovery.add_node(n.clone()); } + *self.discovery.lock().unwrap().deref_mut() = Some(discovery); io.register_stream(DISCOVERY).expect("Error registering UDP listener"); io.register_timer(DISCOVERY_REFRESH, 7200).expect("Error registering discovery timer"); io.register_timer(DISCOVERY_ROUND, 300).expect("Error registering discovery timer"); - *self.discovery.lock().unwrap().deref_mut() = Some(discovery); } + try!(io.register_stream(TCP_ACCEPT)); + Ok(()) } fn maintain_network(&self, io: &IoContext>) { @@ -567,7 +579,7 @@ impl Host where Message: Send + Sync + Clone { fn accept(&self, io: &IoContext>) { trace!(target: "network", "Accepting incoming connection"); loop { - let socket = match self.tcp_listener.lock().unwrap().as_ref().unwrap().accept() { + let socket = match self.tcp_listener.lock().unwrap().accept() { Ok(None) => break, Ok(Some((sock, _addr))) => sock, Err(e) => { @@ -861,7 +873,8 @@ impl IoHandler> for Host where Messa fn timeout(&self, io: &IoContext>, token: TimerToken) { match token { IDLE => self.maintain_network(io), - INIT_PUBLIC => self.init_public_interface(io), + INIT_PUBLIC => self.init_public_interface(io).unwrap_or_else(|e| + warn!("Error initializing public interface: {:?}", e)), FIRST_SESSION ... LAST_SESSION => self.connection_timeout(token, io), FIRST_HANDSHAKE ... LAST_HANDSHAKE => self.connection_timeout(token, io), DISCOVERY_REFRESH => { @@ -945,7 +958,7 @@ impl IoHandler> for Host where Messa } } DISCOVERY => self.discovery.lock().unwrap().as_ref().unwrap().register_socket(event_loop).expect("Error registering discovery socket"), - TCP_ACCEPT => event_loop.register(self.tcp_listener.lock().unwrap().as_ref().unwrap(), Token(TCP_ACCEPT), EventSet::all(), PollOpt::edge()).expect("Error registering stream"), + TCP_ACCEPT => event_loop.register(self.tcp_listener.lock().unwrap().deref(), Token(TCP_ACCEPT), EventSet::all(), PollOpt::edge()).expect("Error registering stream"), _ => warn!("Unexpected stream registration") } } @@ -986,7 +999,7 @@ impl IoHandler> for Host where Messa } } DISCOVERY => self.discovery.lock().unwrap().as_ref().unwrap().update_registration(event_loop).expect("Error reregistering discovery socket"), - TCP_ACCEPT => event_loop.reregister(self.tcp_listener.lock().unwrap().as_ref().unwrap(), Token(TCP_ACCEPT), EventSet::all(), PollOpt::edge()).expect("Error reregistering stream"), + TCP_ACCEPT => event_loop.reregister(self.tcp_listener.lock().unwrap().deref(), Token(TCP_ACCEPT), EventSet::all(), PollOpt::edge()).expect("Error reregistering stream"), _ => warn!("Unexpected stream update") } } @@ -1054,6 +1067,6 @@ fn host_client_url() { let mut config = NetworkConfiguration::new(); let key = h256_from_hex("6f7b0d801bc7b5ce7bbd930b84fd0369b3eb25d09be58d64ba811091046f3aa2"); config.use_secret = Some(key); - let host: Host = Host::new(config); - assert!(host.client_url().starts_with("enode://101b3ef5a4ea7a1c7928e24c4c75fd053c235d7b80c22ae5c03d145d0ac7396e2a4ffff9adee3133a7b05044a5cee08115fd65145e5165d646bde371010d803c@")); + let host: Host = Host::new(config).unwrap(); + assert!(host.local_url().starts_with("enode://101b3ef5a4ea7a1c7928e24c4c75fd053c235d7b80c22ae5c03d145d0ac7396e2a4ffff9adee3133a7b05044a5cee08115fd65145e5165d646bde371010d803c@")); } diff --git a/util/src/network/ip_utils.rs b/util/src/network/ip_utils.rs index b37a47064..27ff29737 100644 --- a/util/src/network/ip_utils.rs +++ b/util/src/network/ip_utils.rs @@ -208,6 +208,7 @@ fn can_select_public_address() { assert!(pub_address.port() == 40477); } +#[ignore] #[test] fn can_map_external_address_or_fail() { let pub_address = select_public_address(40478); diff --git a/util/src/network/mod.rs b/util/src/network/mod.rs index 50645f2be..29f3d166c 100644 --- a/util/src/network/mod.rs +++ b/util/src/network/mod.rs @@ -56,7 +56,7 @@ //! } //! //! fn main () { -//! let mut service = NetworkService::::start(NetworkConfiguration::new_with_port(40412)).expect("Error creating network service"); +//! let mut service = NetworkService::::start(NetworkConfiguration::new_local()).expect("Error creating network service"); //! service.register_protocol(Arc::new(MyHandler), "myproto", &[1u8]); //! //! // Wait for quit condition diff --git a/util/src/network/service.rs b/util/src/network/service.rs index 7b9388e85..49957f7e7 100644 --- a/util/src/network/service.rs +++ b/util/src/network/service.rs @@ -28,6 +28,7 @@ use io::*; pub struct NetworkService where Message: Send + Sync + Clone + 'static { io_service: IoService>, host_info: String, + host: Arc>, stats: Arc, panic_handler: Arc } @@ -39,15 +40,16 @@ impl NetworkService where Message: Send + Sync + Clone + 'stat let mut io_service = try!(IoService::>::start()); panic_handler.forward_from(&io_service); - let host = Arc::new(Host::new(config)); + let host = Arc::new(try!(Host::new(config))); let stats = host.stats().clone(); let host_info = host.client_version(); - try!(io_service.register_handler(host)); + try!(io_service.register_handler(host.clone())); Ok(NetworkService { io_service: io_service, host_info: host_info, stats: stats, - panic_handler: panic_handler + panic_handler: panic_handler, + host: host, }) } @@ -71,12 +73,21 @@ impl NetworkService where Message: Send + Sync + Clone + 'stat &mut self.io_service } - /// Returns underlying io service. + /// Returns network statistics. pub fn stats(&self) -> &NetworkStats { &self.stats } -} + /// Returns external url if available. + pub fn external_url(&self) -> Option { + self.host.external_url() + } + + /// Returns external url if available. + pub fn local_url(&self) -> String { + self.host.local_url() + } +} impl MayPanic for NetworkService where Message: Send + Sync + Clone + 'static { fn on_panic(&self, closure: F) where F: OnPanicListener { diff --git a/util/src/network/session.rs b/util/src/network/session.rs index 2f30d7376..7dbcc4229 100644 --- a/util/src/network/session.rs +++ b/util/src/network/session.rs @@ -315,7 +315,7 @@ impl Session { .append(&host.protocol_version) .append(&host.client_version) .append(&host.capabilities) - .append(&host.listen_port) + .append(&host.local_endpoint.address.port()) .append(host.id()); self.connection.send_packet(&rlp.out()) } diff --git a/util/src/network/tests.rs b/util/src/network/tests.rs index f8ef588f6..b43da9320 100644 --- a/util/src/network/tests.rs +++ b/util/src/network/tests.rs @@ -97,23 +97,23 @@ impl NetworkProtocolHandler for TestProtocol { #[test] fn net_service() { - let mut service = NetworkService::::start(NetworkConfiguration::new_with_port(40414)).expect("Error creating network service"); + let mut service = NetworkService::::start(NetworkConfiguration::new_local()).expect("Error creating network service"); service.register_protocol(Arc::new(TestProtocol::new(false)), "myproto", &[1u8]).unwrap(); } #[test] fn net_connect() { + ::log::init_log(); let key1 = KeyPair::create().unwrap(); - let mut config1 = NetworkConfiguration::new_with_port(30354); + let mut config1 = NetworkConfiguration::new_local(); config1.use_secret = Some(key1.secret().clone()); - config1.nat_enabled = false; config1.boot_nodes = vec![ ]; - let mut config2 = NetworkConfiguration::new_with_port(30355); - config2.boot_nodes = vec![ format!("enode://{}@127.0.0.1:30354", key1.public().hex()) ]; - config2.nat_enabled = false; let mut service1 = NetworkService::::start(config1).unwrap(); - let mut service2 = NetworkService::::start(config2).unwrap(); let handler1 = TestProtocol::register(&mut service1, false); + let mut config2 = NetworkConfiguration::new_local(); + info!("net_connect: local URL: {}", service1.local_url()); + config2.boot_nodes = vec![ service1.local_url() ]; + let mut service2 = NetworkService::::start(config2).unwrap(); let handler2 = TestProtocol::register(&mut service2, false); while !handler1.got_packet() && !handler2.got_packet() && (service1.stats().sessions() == 0 || service2.stats().sessions() == 0) { thread::sleep(Duration::from_millis(50)); @@ -125,16 +125,14 @@ fn net_connect() { #[test] fn net_disconnect() { let key1 = KeyPair::create().unwrap(); - let mut config1 = NetworkConfiguration::new_with_port(30364); + let mut config1 = NetworkConfiguration::new_local(); config1.use_secret = Some(key1.secret().clone()); - config1.nat_enabled = false; config1.boot_nodes = vec![ ]; - let mut config2 = NetworkConfiguration::new_with_port(30365); - config2.boot_nodes = vec![ format!("enode://{}@127.0.0.1:30364", key1.public().hex()) ]; - config2.nat_enabled = false; let mut service1 = NetworkService::::start(config1).unwrap(); - let mut service2 = NetworkService::::start(config2).unwrap(); let handler1 = TestProtocol::register(&mut service1, false); + let mut config2 = NetworkConfiguration::new_local(); + config2.boot_nodes = vec![ service1.local_url() ]; + let mut service2 = NetworkService::::start(config2).unwrap(); let handler2 = TestProtocol::register(&mut service2, true); while !(handler1.got_disconnect() && handler2.got_disconnect()) { thread::sleep(Duration::from_millis(50)); @@ -145,7 +143,7 @@ fn net_disconnect() { #[test] fn net_timeout() { - let config = NetworkConfiguration::new_with_port(30346); + let config = NetworkConfiguration::new_local(); let mut service = NetworkService::::start(config).unwrap(); let handler = TestProtocol::register(&mut service, false); while !handler.got_timeout() {