diff --git a/Cargo.lock b/Cargo.lock index 8f709bead..92c2e493a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ [root] name = "parity" -version = "1.3.0" +version = "1.3.1" dependencies = [ "ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.80 (registry+https://github.com/rust-lang/crates.io-index)", @@ -20,7 +20,7 @@ dependencies = [ "ethcore-logger 1.3.0", "ethcore-rpc 1.3.0", "ethcore-signer 1.3.0", - "ethcore-util 1.3.0", + "ethcore-util 1.3.1", "ethsync 1.3.0", "fdlimit 0.1.0", "hyper 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -253,7 +253,7 @@ dependencies = [ "ethcore-ipc 1.3.0", "ethcore-ipc-codegen 1.3.0", "ethcore-ipc-nano 1.3.0", - "ethcore-util 1.3.0", + "ethcore-util 1.3.1", "ethjson 0.1.0", "ethstore 0.1.0", "heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -275,7 +275,7 @@ version = "1.3.0" dependencies = [ "clippy 0.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore-rpc 1.3.0", - "ethcore-util 1.3.0", + "ethcore-util 1.3.1", "hyper 0.9.4 (git+https://github.com/ethcore/hyper)", "jsonrpc-core 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)", @@ -317,7 +317,7 @@ name = "ethcore-ipc" version = "1.3.0" dependencies = [ "ethcore-devtools 1.3.0", - "ethcore-util 1.3.0", + "ethcore-util 1.3.1", "nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)", "semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -362,7 +362,7 @@ dependencies = [ "ethcore-ipc 1.3.0", "ethcore-ipc-codegen 1.3.0", "ethcore-ipc-nano 1.3.0", - "ethcore-util 1.3.0", + "ethcore-util 1.3.1", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)", "semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -374,7 +374,7 @@ name = "ethcore-logger" version = "1.3.0" dependencies = [ "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore-util 1.3.0", + "ethcore-util 1.3.1", "isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -389,7 +389,7 @@ dependencies = [ "ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore-devtools 1.3.0", "ethcore-io 1.3.0", - "ethcore-util 1.3.0", + "ethcore-util 1.3.1", "igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -413,7 +413,7 @@ dependencies = [ "ethcore-devtools 1.3.0", "ethcore-io 1.3.0", "ethcore-ipc 1.3.0", - "ethcore-util 1.3.0", + "ethcore-util 1.3.1", "ethjson 0.1.0", "ethsync 1.3.0", "json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)", @@ -436,7 +436,7 @@ dependencies = [ "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore-io 1.3.0", "ethcore-rpc 1.3.0", - "ethcore-util 1.3.0", + "ethcore-util 1.3.1", "jsonrpc-core 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "parity-dapps-signer 1.4.0 (git+https://github.com/ethcore/parity-ui.git)", @@ -447,7 +447,7 @@ dependencies = [ [[package]] name = "ethcore-util" -version = "1.3.0" +version = "1.3.1" dependencies = [ "ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", @@ -480,7 +480,7 @@ dependencies = [ name = "ethjson" version = "0.1.0" dependencies = [ - "ethcore-util 1.3.0", + "ethcore-util 1.3.1", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", "serde_codegen 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -528,7 +528,7 @@ dependencies = [ "ethcore-ipc-codegen 1.3.0", "ethcore-ipc-nano 1.3.0", "ethcore-network 1.3.0", - "ethcore-util 1.3.0", + "ethcore-util 1.3.1", "heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1183,7 +1183,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "rocksdb" version = "0.4.5" -source = "git+https://github.com/ethcore/rust-rocksdb#84c5ac0bbd6901b6279a5480708e93eb3da6caa4" +source = "git+https://github.com/ethcore/rust-rocksdb#485dd747a2c9a9f910fc8ac696fc9edf5fa22aa3" dependencies = [ "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", "rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)", @@ -1192,7 +1192,7 @@ dependencies = [ [[package]] name = "rocksdb-sys" version = "0.3.0" -source = "git+https://github.com/ethcore/rust-rocksdb#84c5ac0bbd6901b6279a5480708e93eb3da6caa4" +source = "git+https://github.com/ethcore/rust-rocksdb#485dd747a2c9a9f910fc8ac696fc9edf5fa22aa3" dependencies = [ "gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 7d24f8f70..6e261dd9a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Ethcore client." name = "parity" -version = "1.3.0" +version = "1.3.1" license = "GPL-3.0" authors = ["Ethcore "] build = "build.rs" diff --git a/dapps/src/lib.rs b/dapps/src/lib.rs index d9fa06591..b1d4e99f1 100644 --- a/dapps/src/lib.rs +++ b/dapps/src/lib.rs @@ -100,14 +100,26 @@ impl ServerBuilder { /// Asynchronously start server with no authentication, /// returns result with `Server` handle on success or an error. - pub fn start_unsecure_http(&self, addr: &SocketAddr) -> Result { - Server::start_http(addr, NoAuth, self.handler.clone(), self.dapps_path.clone()) + pub fn start_unsecured_http(&self, addr: &SocketAddr, hosts: Option>) -> Result { + Server::start_http( + addr, + hosts, + NoAuth, + self.handler.clone(), + self.dapps_path.clone(), + ) } /// Asynchronously start server with `HTTP Basic Authentication`, /// return result with `Server` handle on success or an error. - pub fn start_basic_auth_http(&self, addr: &SocketAddr, username: &str, password: &str) -> Result { - Server::start_http(addr, HttpBasicAuth::single_user(username, password), self.handler.clone(), self.dapps_path.clone()) + pub fn start_basic_auth_http(&self, addr: &SocketAddr, hosts: Option>, username: &str, password: &str) -> Result { + Server::start_http( + addr, + hosts, + HttpBasicAuth::single_user(username, password), + self.handler.clone(), + self.dapps_path.clone(), + ) } } @@ -118,7 +130,28 @@ pub struct Server { } impl Server { - fn start_http(addr: &SocketAddr, authorization: A, handler: Arc, dapps_path: String) -> Result { + /// Returns a list of allowed hosts or `None` if all hosts are allowed. + fn allowed_hosts(hosts: Option>, bind_address: String) -> Option> { + let mut allowed = Vec::new(); + + match hosts { + Some(hosts) => allowed.extend_from_slice(&hosts), + None => return None, + } + + // Add localhost domain as valid too if listening on loopback interface. + allowed.push(bind_address.replace("127.0.0.1", "localhost").into()); + allowed.push(bind_address.into()); + Some(allowed) + } + + fn start_http( + addr: &SocketAddr, + hosts: Option>, + authorization: A, + handler: Arc, + dapps_path: String, + ) -> Result { let panic_handler = Arc::new(Mutex::new(None)); let authorization = Arc::new(authorization); let endpoints = Arc::new(apps::all_endpoints(dapps_path)); @@ -129,7 +162,7 @@ impl Server { special.insert(router::SpecialEndpoint::Utils, apps::utils()); special }); - let bind_address = format!("{}", addr); + let hosts = Self::allowed_hosts(hosts, format!("{}", addr)); try!(hyper::Server::http(addr)) .handle(move |_| router::Router::new( @@ -137,7 +170,7 @@ impl Server { endpoints.clone(), special.clone(), authorization.clone(), - bind_address.clone(), + hosts.clone(), )) .map(|(l, srv)| { @@ -182,3 +215,24 @@ impl From for ServerError { } } } + +#[cfg(test)] +mod tests { + use super::Server; + + #[test] + fn should_return_allowed_hosts() { + // given + let bind_address = "127.0.0.1".to_owned(); + + // when + let all = Server::allowed_hosts(None, bind_address.clone()); + let address = Server::allowed_hosts(Some(Vec::new()), bind_address.clone()); + let some = Server::allowed_hosts(Some(vec!["ethcore.io".into()]), bind_address.clone()); + + // then + assert_eq!(all, None); + assert_eq!(address, Some(vec!["localhost".into(), "127.0.0.1".into()])); + assert_eq!(some, Some(vec!["ethcore.io".into(), "localhost".into(), "127.0.0.1".into()])); + } +} diff --git a/dapps/src/router/host_validation.rs b/dapps/src/router/host_validation.rs index 3b065cd0c..15c27c60c 100644 --- a/dapps/src/router/host_validation.rs +++ b/dapps/src/router/host_validation.rs @@ -22,14 +22,11 @@ use hyper::net::HttpStream; use jsonrpc_http_server::{is_host_header_valid}; use handlers::ContentHandler; - -pub fn is_valid(request: &server::Request, bind_address: &str, endpoints: Vec) -> bool { - let mut endpoints = endpoints.into_iter() +pub fn is_valid(request: &server::Request, allowed_hosts: &[String], endpoints: Vec) -> bool { + let mut endpoints = endpoints.iter() .map(|endpoint| format!("{}{}", endpoint, DAPPS_DOMAIN)) .collect::>(); - // Add localhost domain as valid too if listening on loopback interface. - endpoints.push(bind_address.replace("127.0.0.1", "localhost").into()); - endpoints.push(bind_address.into()); + endpoints.extend_from_slice(allowed_hosts); is_host_header_valid(request, &endpoints) } diff --git a/dapps/src/router/mod.rs b/dapps/src/router/mod.rs index bdd5fd291..ac699666c 100644 --- a/dapps/src/router/mod.rs +++ b/dapps/src/router/mod.rs @@ -45,7 +45,7 @@ pub struct Router { endpoints: Arc, special: Arc>>, authorization: Arc, - bind_address: String, + allowed_hosts: Option>, handler: Box + Send>, } @@ -53,9 +53,11 @@ impl server::Handler for Router { fn on_request(&mut self, req: server::Request) -> Next { // Validate Host header - if !host_validation::is_valid(&req, &self.bind_address, self.endpoints.keys().cloned().collect()) { - self.handler = host_validation::host_invalid_response(); - return self.handler.on_request(req); + if let Some(ref hosts) = self.allowed_hosts { + if !host_validation::is_valid(&req, hosts, self.endpoints.keys().cloned().collect()) { + self.handler = host_validation::host_invalid_response(); + return self.handler.on_request(req); + } } // Check authorization @@ -114,7 +116,7 @@ impl Router { endpoints: Arc, special: Arc>>, authorization: Arc, - bind_address: String, + allowed_hosts: Option>, ) -> Self { let handler = special.get(&SpecialEndpoint::Rpc).unwrap().to_handler(EndpointPath::default()); @@ -123,7 +125,7 @@ impl Router { endpoints: endpoints, special: special, authorization: authorization, - bind_address: bind_address, + allowed_hosts: allowed_hosts, handler: handler, } } diff --git a/ethcore/res/ethereum/morden.json b/ethcore/res/ethereum/morden.json index 0cc88ac64..0d643e4c0 100644 --- a/ethcore/res/ethereum/morden.json +++ b/ethcore/res/ethereum/morden.json @@ -8,7 +8,7 @@ "difficultyBoundDivisor": "0x0800", "durationLimit": "0x0d", "blockReward": "0x4563918244F40000", - "registrar": "", + "registrar": "0x8e4e9b13d4b45cb0befc93c3061b1408f67316b2", "frontierCompatibilityModeLimit": "0x789b0" } } diff --git a/ethcore/src/account.rs b/ethcore/src/account.rs index 19e5d6488..829198910 100644 --- a/ethcore/src/account.rs +++ b/ethcore/src/account.rs @@ -16,11 +16,12 @@ //! Single account in the system. +use std::collections::hash_map::Entry; use util::*; use pod_account::*; use account_db::*; -use std::cell::{Ref, RefCell}; +use std::cell::{Ref, RefCell, Cell}; /// Single account in the system. #[derive(Clone)] @@ -39,6 +40,8 @@ pub struct Account { code_cache: Bytes, // Account is new or has been modified filth: Filth, + // Cached address hash. + address_hash: Cell>, } impl Account { @@ -53,6 +56,7 @@ impl Account { code_hash: Some(code.sha3()), code_cache: code, filth: Filth::Dirty, + address_hash: Cell::new(None), } } @@ -66,6 +70,7 @@ impl Account { code_hash: pod.code.as_ref().map(|c| c.sha3()), code_cache: pod.code.as_ref().map_or_else(|| { warn!("POD account with unknown code is being created! Assuming no code."); vec![] }, |c| c.clone()), filth: Filth::Dirty, + address_hash: Cell::new(None), } } @@ -79,6 +84,7 @@ impl Account { code_hash: Some(SHA3_EMPTY), code_cache: vec![], filth: Filth::Dirty, + address_hash: Cell::new(None), } } @@ -93,6 +99,7 @@ impl Account { code_hash: Some(r.val_at(3)), code_cache: vec![], filth: Filth::Clean, + address_hash: Cell::new(None), } } @@ -107,6 +114,7 @@ impl Account { code_hash: None, code_cache: vec![], filth: Filth::Dirty, + address_hash: Cell::new(None), } } @@ -126,8 +134,17 @@ impl Account { /// Set (and cache) the contents of the trie's storage at `key` to `value`. pub fn set_storage(&mut self, key: H256, value: H256) { - self.storage_overlay.borrow_mut().insert(key, (Filth::Dirty, value)); - self.filth = Filth::Dirty; + match self.storage_overlay.borrow_mut().entry(key) { + Entry::Occupied(ref mut entry) if entry.get().1 != value => { + entry.insert((Filth::Dirty, value)); + self.filth = Filth::Dirty; + }, + Entry::Vacant(entry) => { + entry.insert((Filth::Dirty, value)); + self.filth = Filth::Dirty; + }, + _ => (), + } } /// Get (and cache) the contents of the trie's storage at `key`. @@ -158,6 +175,16 @@ impl Account { self.code_hash.clone().unwrap_or(SHA3_EMPTY) } + /// return the code hash associated with this account. + pub fn address_hash(&self, address: &Address) -> H256 { + let hash = self.address_hash.get(); + hash.unwrap_or_else(|| { + let hash = address.sha3(); + self.address_hash.set(Some(hash.clone())); + hash + }) + } + /// returns the account's code. If `None` then the code cache isn't available - /// get someone who knows to call `note_code`. pub fn code(&self) -> Option<&[u8]> { @@ -233,16 +260,20 @@ impl Account { /// Increment the nonce of the account by one. pub fn add_balance(&mut self, x: &U256) { - self.balance = self.balance + *x; - self.filth = Filth::Dirty; + if !x.is_zero() { + self.balance = self.balance + *x; + self.filth = Filth::Dirty; + } } /// Increment the nonce of the account by one. /// Panics if balance is less than `x` pub fn sub_balance(&mut self, x: &U256) { - assert!(self.balance >= *x); - self.balance = self.balance - *x; - self.filth = Filth::Dirty; + if !x.is_zero() { + assert!(self.balance >= *x); + self.balance = self.balance - *x; + self.filth = Filth::Dirty; + } } /// Commit the `storage_overlay` to the backing DB and update `storage_root`. diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index e87e5e450..b0f984a63 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -505,7 +505,7 @@ pub fn enact( { if ::log::max_log_level() >= ::log::LogLevel::Trace { let s = try!(State::from_existing(db.boxed_clone(), parent.state_root().clone(), engine.account_start_nonce(), trie_factory.clone())); - trace!("enact(): root={}, author={}, author_balance={}\n", s.root(), header.author(), s.balance(&header.author())); + trace!(target: "enact", "num={}, root={}, author={}, author_balance={}\n", header.number(), s.root(), header.author(), s.balance(&header.author())); } } diff --git a/ethcore/src/blockchain/block_info.rs b/ethcore/src/blockchain/block_info.rs index a7827b043..a407c7181 100644 --- a/ethcore/src/blockchain/block_info.rs +++ b/ethcore/src/blockchain/block_info.rs @@ -31,7 +31,7 @@ pub struct BlockInfo { } /// Describes location of newly inserted block. -#[derive(Clone)] +#[derive(Debug, Clone)] pub enum BlockLocation { /// It's part of the canon chain. CanonChain, @@ -43,7 +43,7 @@ pub enum BlockLocation { BranchBecomingCanonChain(BranchBecomingCanonChainData), } -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct BranchBecomingCanonChainData { /// Hash of the newest common ancestor with old canon chain. pub ancestor: H256, diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index b5cd6076c..fa4805c9d 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -784,7 +784,7 @@ impl BlockChain { } } - /// Applt pending insertion updates + /// Apply pending insertion updates pub fn commit(&self) { let mut pending_best_block = self.pending_best_block.write(); let mut pending_write_hashes = self.pending_block_hashes.write(); @@ -914,15 +914,45 @@ impl BlockChain { let block = BlockView::new(block_bytes); let transaction_hashes = block.transaction_hashes(); - transaction_hashes.into_iter() - .enumerate() - .fold(HashMap::new(), |mut acc, (i ,tx_hash)| { - acc.insert(tx_hash, TransactionAddress { - block_hash: info.hash.clone(), - index: i - }); - acc - }) + match info.location { + BlockLocation::CanonChain => { + transaction_hashes.into_iter() + .enumerate() + .map(|(i ,tx_hash)| { + (tx_hash, TransactionAddress { + block_hash: info.hash.clone(), + index: i + }) + }) + .collect() + }, + BlockLocation::BranchBecomingCanonChain(ref data) => { + let addresses = data.enacted.iter() + .flat_map(|hash| { + let bytes = self.block_body(hash).expect("Enacted block must be in database."); + let hashes = BodyView::new(&bytes).transaction_hashes(); + hashes.into_iter() + .enumerate() + .map(|(i, tx_hash)| (tx_hash, TransactionAddress { + block_hash: hash.clone(), + index: i, + })) + .collect::>() + }); + + let current_addresses = transaction_hashes.into_iter() + .enumerate() + .map(|(i ,tx_hash)| { + (tx_hash, TransactionAddress { + block_hash: info.hash.clone(), + index: i + }) + }); + + addresses.chain(current_addresses).collect() + }, + BlockLocation::Branch => HashMap::new(), + } } /// This functions returns modified blocks blooms. @@ -1070,7 +1100,6 @@ impl BlockChain { #[cfg(test)] mod tests { #![cfg_attr(feature="dev", allow(similar_names))] - use std::str::FromStr; use std::sync::Arc; use rustc_serialize::hex::FromHex; use util::{Database, DatabaseConfig}; @@ -1081,8 +1110,10 @@ mod tests { use tests::helpers::*; use devtools::*; use blockchain::generator::{ChainGenerator, ChainIterator, BlockFinalizer}; + use blockchain::extras::TransactionAddress; use views::BlockView; use client; + use transaction::{Transaction, Action}; fn new_db(path: &str) -> Arc { Arc::new(Database::open(&DatabaseConfig::with_columns(client::DB_NO_OF_COLUMNS), path).unwrap()) @@ -1217,6 +1248,106 @@ mod tests { // TODO: insert block that already includes one of them as an uncle to check it's not allowed. } + #[test] + fn test_overwriting_transaction_addresses() { + let mut canon_chain = ChainGenerator::default(); + let mut finalizer = BlockFinalizer::default(); + let genesis = canon_chain.generate(&mut finalizer).unwrap(); + let mut fork_chain = canon_chain.fork(1); + let mut fork_finalizer = finalizer.fork(); + + let t1 = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Create, + value: 100.into(), + data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), + }.sign(&"".sha3()); + + let t2 = Transaction { + nonce: 1.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Create, + value: 100.into(), + data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), + }.sign(&"".sha3()); + + let t3 = Transaction { + nonce: 2.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Create, + value: 100.into(), + data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), + }.sign(&"".sha3()); + + let b1a = canon_chain + .with_transaction(t1.clone()) + .with_transaction(t2.clone()) + .generate(&mut finalizer).unwrap(); + + // insert transactions in different order + let b1b = fork_chain + .with_transaction(t2.clone()) + .with_transaction(t1.clone()) + .generate(&mut fork_finalizer).unwrap(); + + let b2 = fork_chain + .with_transaction(t3.clone()) + .generate(&mut fork_finalizer).unwrap(); + + let b1a_hash = BlockView::new(&b1a).header_view().sha3(); + let b1b_hash = BlockView::new(&b1b).header_view().sha3(); + let b2_hash = BlockView::new(&b2).header_view().sha3(); + + let t1_hash = t1.hash(); + let t2_hash = t2.hash(); + let t3_hash = t3.hash(); + + let temp = RandomTempPath::new(); + let db = new_db(temp.as_str()); + let bc = BlockChain::new(Config::default(), &genesis, db.clone()); + + let mut batch = db.transaction(); + let _ = bc.insert_block(&mut batch, &b1a, vec![]); + bc.commit(); + let _ = bc.insert_block(&mut batch, &b1b, vec![]); + bc.commit(); + db.write(batch).unwrap(); + + assert_eq!(bc.best_block_hash(), b1a_hash); + assert_eq!(bc.transaction_address(&t1_hash).unwrap(), TransactionAddress { + block_hash: b1a_hash.clone(), + index: 0, + }); + assert_eq!(bc.transaction_address(&t2_hash).unwrap(), TransactionAddress { + block_hash: b1a_hash.clone(), + index: 1, + }); + + // now let's make forked chain the canon chain + let mut batch = db.transaction(); + let _ = bc.insert_block(&mut batch, &b2, vec![]); + bc.commit(); + db.write(batch).unwrap(); + + assert_eq!(bc.best_block_hash(), b2_hash); + assert_eq!(bc.transaction_address(&t1_hash).unwrap(), TransactionAddress { + block_hash: b1b_hash.clone(), + index: 1, + }); + assert_eq!(bc.transaction_address(&t2_hash).unwrap(), TransactionAddress { + block_hash: b1b_hash.clone(), + index: 0, + }); + assert_eq!(bc.transaction_address(&t3_hash).unwrap(), TransactionAddress { + block_hash: b2_hash.clone(), + index: 0, + }); + } + #[test] #[cfg_attr(feature="dev", allow(cyclomatic_complexity))] fn test_small_fork() { @@ -1417,7 +1548,7 @@ mod tests { fn find_transaction_by_hash() { let genesis = "f901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0af81e09f8c46ca322193edfda764fa7e88e81923f802f1d325ec0b0308ac2cd0a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830200008083023e38808454c98c8142a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421880102030405060708c0c0".from_hex().unwrap(); let b1 = "f904a8f901faa0ce1f26f798dd03c8782d63b3e42e79a64eaea5694ea686ac5d7ce3df5171d1aea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0a65c2364cd0f1542d761823dc0109c6b072f14c20459598c5455c274601438f4a070616ebd7ad2ed6fb7860cf7e9df00163842351c38a87cac2c1cb193895035a2a05c5b4fc43c2d45787f54e1ae7d27afdb4ad16dfc567c5692070d5c4556e0b1d7b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830200000183023ec683021536845685109780a029f07836e4e59229b3a065913afc27702642c683bba689910b2b2fd45db310d3888957e6d004a31802f902a7f85f800a8255f094aaaf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca0575da4e21b66fa764be5f74da9389e67693d066fb0d1312e19e17e501da00ecda06baf5a5327595f6619dfc2fcb3f2e6fb410b5810af3cb52d0e7508038e91a188f85f010a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba04fa966bf34b93abc1bcd665554b7f316b50f928477b50be0f3285ead29d18c5ba017bba0eeec1625ab433746955e125d46d80b7fdc97386c51266f842d8e02192ef85f020a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca004377418ae981cc32b1312b4a427a1d69a821b28db8584f5f2bd8c6d42458adaa053a1dba1af177fac92f3b6af0a9fa46a22adf56e686c93794b6a012bf254abf5f85f030a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca04fe13febd28a05f4fcb2f451d7ddc2dda56486d9f8c79a62b0ba4da775122615a0651b2382dd402df9ebc27f8cb4b2e0f3cea68dda2dca0ee9603608f0b6f51668f85f040a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba078e6a0ba086a08f8450e208a399bb2f2d2a0d984acd2517c7c7df66ccfab567da013254002cd45a97fac049ae00afbc43ed0d9961d0c56a3b2382c80ce41c198ddf85f050a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba0a7174d8f43ea71c8e3ca9477691add8d80ac8e0ed89d8d8b572041eef81f4a54a0534ea2e28ec4da3b5b944b18c51ec84a5cf35f5b3343c5fb86521fd2d388f506f85f060a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba034bd04065833536a10c77ee2a43a5371bc6d34837088b861dd9d4b7f44074b59a078807715786a13876d3455716a6b9cb2186b7a4887a5c31160fc877454958616c0".from_hex().unwrap(); - let b1_hash = H256::from_str("f53f268d23a71e85c7d6d83a9504298712b84c1a2ba220441c86eeda0bf0b6e3").unwrap(); + let b1_hash: H256 = "f53f268d23a71e85c7d6d83a9504298712b84c1a2ba220441c86eeda0bf0b6e3".into(); let temp = RandomTempPath::new(); let db = new_db(temp.as_str()); @@ -1445,11 +1576,11 @@ mod tests { #[test] fn test_bloom_filter_simple() { // TODO: From here - let bloom_b1 = H2048::from_str("00000020000000000000000000000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000400000000000000000000002000").unwrap(); + let bloom_b1: H2048 = "00000020000000000000000000000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000400000000000000000000002000".into(); - let bloom_b2 = H2048::from_str("00000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(); + let bloom_b2: H2048 = "00000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into(); - let bloom_ba = H2048::from_str("00000000000000000000000000000000000000000000020000000800000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(); + let bloom_ba: H2048 = "00000000000000000000000000000000000000000000020000000800000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into(); let mut canon_chain = ChainGenerator::default(); let mut finalizer = BlockFinalizer::default(); diff --git a/ethcore/src/blockchain/extras.rs b/ethcore/src/blockchain/extras.rs index 6bb10276c..e2b4e89b3 100644 --- a/ethcore/src/blockchain/extras.rs +++ b/ethcore/src/blockchain/extras.rs @@ -176,7 +176,7 @@ impl Encodable for BlockDetails { } /// Represents address of certain transaction within block -#[derive(Clone)] +#[derive(Debug, PartialEq, Clone)] pub struct TransactionAddress { /// Block hash pub block_hash: H256, diff --git a/ethcore/src/blockchain/generator/block.rs b/ethcore/src/blockchain/generator/block.rs index 0a3dad399..735dd0f9b 100644 --- a/ethcore/src/blockchain/generator/block.rs +++ b/ethcore/src/blockchain/generator/block.rs @@ -16,7 +16,6 @@ use util::rlp::*; use util::{H256, H2048}; -use util::U256; use util::bytes::Bytes; use header::Header; use transaction::SignedTransaction; @@ -24,6 +23,7 @@ use transaction::SignedTransaction; use super::fork::Forkable; use super::bloom::WithBloom; use super::complete::CompleteBlock; +use super::transaction::WithTransaction; /// Helper structure, used for encoding blocks. #[derive(Default)] @@ -44,7 +44,8 @@ impl Encodable for Block { impl Forkable for Block { fn fork(mut self, fork_number: usize) -> Self where Self: Sized { - self.header.difficulty = self.header.difficulty - U256::from(fork_number); + let difficulty = self.header.difficulty().clone() - fork_number.into(); + self.header.difficulty = difficulty; self } } @@ -56,6 +57,13 @@ impl WithBloom for Block { } } +impl WithTransaction for Block { + fn with_transaction(mut self, transaction: SignedTransaction) -> Self where Self: Sized { + self.transactions.push(transaction); + self + } +} + impl CompleteBlock for Block { fn complete(mut self, parent_hash: H256) -> Bytes { self.header.parent_hash = parent_hash; diff --git a/ethcore/src/blockchain/generator/generator.rs b/ethcore/src/blockchain/generator/generator.rs index 88c9577e2..eb107c20d 100644 --- a/ethcore/src/blockchain/generator/generator.rs +++ b/ethcore/src/blockchain/generator/generator.rs @@ -18,10 +18,12 @@ use util::hash::H2048; use util::numbers::U256; use util::bytes::Bytes; use header::BlockNumber; +use transaction::SignedTransaction; use super::fork::Fork; use super::bloom::Bloom; use super::complete::{BlockFinalizer, CompleteBlock, Complete}; use super::block::Block; +use super::transaction::Transaction; /// Chain iterator interface. pub trait ChainIterator: Iterator + Sized { @@ -30,6 +32,8 @@ pub trait ChainIterator: Iterator + Sized { fn fork(&self, fork_number: usize) -> Fork where Self: Clone; /// Should be called to make every consecutive block have given bloom. fn with_bloom(&mut self, bloom: H2048) -> Bloom; + /// Should be called to make every consecutive block have given transaction. + fn with_transaction(&mut self, transaction: SignedTransaction) -> Transaction; /// Should be called to complete block. Without complete, block may have incorrect hash. fn complete<'a>(&'a mut self, finalizer: &'a mut BlockFinalizer) -> Complete<'a, Self>; /// Completes and generates block. @@ -51,6 +55,13 @@ impl ChainIterator for I where I: Iterator + Sized { } } + fn with_transaction(&mut self, transaction: SignedTransaction) -> Transaction { + Transaction { + iter: self, + transaction: transaction, + } + } + fn complete<'a>(&'a mut self, finalizer: &'a mut BlockFinalizer) -> Complete<'a, Self> { Complete { iter: self, @@ -85,7 +96,7 @@ impl Default for ChainGenerator { fn default() -> Self { ChainGenerator { number: 0, - difficulty: U256::from(1000), + difficulty: 1000.into(), } } } diff --git a/ethcore/src/blockchain/generator/mod.rs b/ethcore/src/blockchain/generator/mod.rs index b02030d4e..683ab7dfb 100644 --- a/ethcore/src/blockchain/generator/mod.rs +++ b/ethcore/src/blockchain/generator/mod.rs @@ -21,6 +21,7 @@ mod block; mod complete; mod fork; pub mod generator; +mod transaction; pub use self::complete::BlockFinalizer; pub use self::generator::{ChainIterator, ChainGenerator}; diff --git a/ethcore/src/blockchain/generator/transaction.rs b/ethcore/src/blockchain/generator/transaction.rs new file mode 100644 index 000000000..aa1d3bdd3 --- /dev/null +++ b/ethcore/src/blockchain/generator/transaction.rs @@ -0,0 +1,35 @@ +// 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 transaction::SignedTransaction; + +pub trait WithTransaction { + fn with_transaction(self, transaction: SignedTransaction) -> Self where Self: Sized; +} + +pub struct Transaction<'a, I> where I: 'a { + pub iter: &'a mut I, + pub transaction: SignedTransaction, +} + +impl <'a, I> Iterator for Transaction<'a, I> where I: Iterator, ::Item: WithTransaction { + type Item = ::Item; + + #[inline] + fn next(&mut self) -> Option { + self.iter.next().map(|item| item.with_transaction(self.transaction.clone())) + } +} diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index bf9c86633..66ac25f41 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -13,7 +13,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::collections::{HashSet, HashMap, VecDeque}; +use std::collections::{HashSet, HashMap, BTreeMap, VecDeque}; use std::sync::{Arc, Weak}; use std::path::{Path}; use std::fmt; @@ -49,8 +49,11 @@ use types::filter::Filter; use log_entry::LocalizedLogEntry; use block_queue::{BlockQueue, BlockQueueInfo}; use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute}; -use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig, BlockChainClient, MiningBlockChainClient, - TraceFilter, CallAnalytics, BlockImportError, Mode, ChainNotify}; +use client::{ + BlockID, TransactionID, UncleID, TraceId, ClientConfig, BlockChainClient, + MiningBlockChainClient, TraceFilter, CallAnalytics, BlockImportError, Mode, + ChainNotify +}; use client::Error as ClientError; use env_info::EnvInfo; use executive::{Executive, Executed, TransactOptions, contract_address}; @@ -916,6 +919,10 @@ impl BlockChainClient for Client { } } + fn additional_params(&self) -> BTreeMap { + self.engine.additional_params().into_iter().collect() + } + fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockID, to_block: BlockID) -> Option> { match (self.block_number(from_block), self.block_number(to_block)) { (Some(from), Some(to)) => Some(self.chain.blocks_with_bloom(bloom, from, to)), diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index b4e0d8a90..212dead9a 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -21,9 +21,10 @@ use util::*; use devtools::*; use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action}; use blockchain::TreeRoute; -use client::{BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockID, - TransactionID, UncleID, TraceId, TraceFilter, LastHashes, CallAnalytics, - BlockImportError}; +use client::{ + BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockID, + TransactionID, UncleID, TraceId, TraceFilter, LastHashes, CallAnalytics, BlockImportError +}; use header::{Header as BlockHeader, BlockNumber}; use filter::Filter; use log_entry::LocalizedLogEntry; @@ -517,6 +518,10 @@ impl BlockChainClient for TestBlockChainClient { fn clear_queue(&self) { } + fn additional_params(&self) -> BTreeMap { + Default::default() + } + fn chain_info(&self) -> BlockChainInfo { BlockChainInfo { total_difficulty: *self.difficulty.read(), diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index f91747e7b..76288cb3e 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +use std::collections::{BTreeMap}; use util::bytes::Bytes; use util::hash::{Address, H256, H2048}; use util::numbers::U256; @@ -150,6 +151,9 @@ pub trait BlockChainClient : Sync + Send { /// Get blockchain information. fn chain_info(&self) -> BlockChainInfo; + /// Get the registrar address, if it exists. + fn additional_params(&self) -> BTreeMap; + /// Get the best block header. fn best_block_header(&self) -> Bytes; diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index 44652c538..e7738fbaa 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -44,6 +44,9 @@ pub trait Engine : Sync + Send { /// Additional engine-specific information for the user/developer concerning `header`. fn extra_info(&self, _header: &Header) -> HashMap { HashMap::new() } + /// Additional information. + fn additional_params(&self) -> HashMap { HashMap::new() } + /// Get the general parameters of the chain. fn params(&self) -> &CommonParams; diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 8b137cf69..6afef9871 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -92,6 +92,7 @@ impl Engine for Ethash { fn seal_fields(&self) -> usize { 2 } fn params(&self) -> &CommonParams { &self.params } + fn additional_params(&self) -> HashMap { hash_map!["registrar".to_owned() => self.ethash_params.registrar.hex()] } fn builtins(&self) -> &BTreeMap { &self.builtins diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index f4bc39416..729f8eb30 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -23,10 +23,11 @@ use account_provider::AccountProvider; use views::{BlockView, HeaderView}; use state::State; use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockID, CallAnalytics}; +use executive::contract_address; use block::{ClosedBlock, IsBlock, Block}; use error::*; -use transaction::SignedTransaction; -use receipt::Receipt; +use transaction::{Action, SignedTransaction}; +use receipt::{Receipt, RichReceipt}; use spec::Spec; use engines::Engine; use miner::{MinerService, MinerStatus, TransactionQueue, AccountDetails, TransactionOrigin}; @@ -712,6 +713,35 @@ impl MinerService for Miner { } } + fn pending_receipt(&self, hash: &H256) -> Option { + let sealing_work = self.sealing_work.lock(); + match (sealing_work.enabled, sealing_work.queue.peek_last_ref()) { + (true, Some(pending)) => { + let txs = pending.transactions(); + txs.iter() + .map(|t| t.hash()) + .position(|t| t == *hash) + .map(|index| { + let prev_gas = if index == 0 { Default::default() } else { pending.receipts()[index - 1].gas_used }; + let ref tx = txs[index]; + let ref receipt = pending.receipts()[index]; + RichReceipt { + transaction_hash: hash.clone(), + transaction_index: index, + cumulative_gas_used: receipt.gas_used, + gas_used: receipt.gas_used - prev_gas, + contract_address: match tx.action { + Action::Call(_) => None, + Action::Create => Some(contract_address(&tx.sender().unwrap(), &tx.nonce)), + }, + logs: receipt.logs.clone(), + } + }) + }, + _ => None + } + } + fn pending_receipts(&self) -> BTreeMap { let sealing_work = self.sealing_work.lock(); match (sealing_work.enabled, sealing_work.queue.peek_last_ref()) { diff --git a/ethcore/src/miner/mod.rs b/ethcore/src/miner/mod.rs index f6d41b566..e95ce758a 100644 --- a/ethcore/src/miner/mod.rs +++ b/ethcore/src/miner/mod.rs @@ -56,7 +56,7 @@ use std::collections::BTreeMap; use util::{H256, U256, Address, Bytes}; use client::{MiningBlockChainClient, Executed, CallAnalytics}; use block::ClosedBlock; -use receipt::Receipt; +use receipt::{RichReceipt, Receipt}; use error::{Error, CallError}; use transaction::SignedTransaction; @@ -146,6 +146,9 @@ pub trait MinerService : Send + Sync { /// Get a list of all pending receipts. fn pending_receipts(&self) -> BTreeMap; + /// Get a particular reciept. + fn pending_receipt(&self, hash: &H256) -> Option; + /// Returns highest transaction nonce for given address. fn last_nonce(&self, address: &Address) -> Option; diff --git a/ethcore/src/state.rs b/ethcore/src/state.rs index 03dbcb596..2c1111611 100644 --- a/ethcore/src/state.rs +++ b/ethcore/src/state.rs @@ -181,7 +181,7 @@ impl State { /// Mutate storage of account `address` so that it is `value` for `key`. pub fn storage_at(&self, address: &Address, key: &H256) -> H256 { self.ensure_cached(address, false, - |a| a.as_ref().map_or(H256::new(), |a|a.storage_at(&AccountDB::new(self.db.as_hashdb(), address), key))) + |a| a.as_ref().map_or(H256::new(), |a|a.storage_at(&AccountDB::from_hash(self.db.as_hashdb(), a.address_hash(address)), key))) } /// Mutate storage of account `a` so that it is `value` for `key`. @@ -241,7 +241,7 @@ impl State { // trace!("Applied transaction. Diff:\n{}\n", state_diff::diff_pod(&old, &self.to_pod())); try!(self.commit()); let receipt = Receipt::new(self.root().clone(), e.cumulative_gas_used, e.logs); -// trace!("Transaction receipt: {:?}", receipt); + trace!(target: "state", "Transaction receipt: {:?}", receipt); Ok(ApplyOutcome{receipt: receipt, trace: e.trace}) } @@ -260,7 +260,7 @@ impl State { for (address, ref mut a) in accounts.iter_mut() { match a { &mut&mut Some(ref mut account) if account.is_dirty() => { - let mut account_db = AccountDBMut::new(db, address); + let mut account_db = AccountDBMut::from_hash(db, account.address_hash(address)); account.commit_storage(trie_factory, &mut account_db); account.commit_code(&mut account_db); } @@ -355,7 +355,8 @@ impl State { } if require_code { if let Some(ref mut account) = self.cache.borrow_mut().get_mut(a).unwrap().as_mut() { - account.cache_code(&AccountDB::new(self.db.as_hashdb(), a)); + let addr_hash = account.address_hash(a); + account.cache_code(&AccountDB::from_hash(self.db.as_hashdb(), addr_hash)); } } @@ -393,7 +394,8 @@ impl State { RefMut::map(self.cache.borrow_mut(), |c| { let account = c.get_mut(a).unwrap().as_mut().unwrap(); if require_code { - account.cache_code(&AccountDB::new(self.db.as_hashdb(), a)); + let addr_hash = account.address_hash(a); + account.cache_code(&AccountDB::from_hash(self.db.as_hashdb(), addr_hash)); } account }) diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index e6f930485..1ac26c83b 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -16,6 +16,7 @@ use io::IoChannel; use client::{BlockChainClient, MiningBlockChainClient, Client, ClientConfig, BlockID}; +use ethereum; use block::IsBlock; use tests::helpers::*; use common::*; @@ -31,6 +32,14 @@ fn imports_from_empty() { client.flush_queue(); } +#[test] +fn should_return_registrar() { + let dir = RandomTempPath::new(); + let spec = ethereum::new_morden(); + let client = Client::new(ClientConfig::default(), &spec, dir.as_path(), Arc::new(Miner::with_spec(&spec)), IoChannel::disconnected()).unwrap(); + assert_eq!(client.additional_params().get("registrar"), Some(&"8e4e9b13d4b45cb0befc93c3061b1408f67316b2".to_owned())); +} + #[test] fn returns_state_root_basic() { let client_result = generate_dummy_client(6); diff --git a/ethcore/src/trace/executive_tracer.rs b/ethcore/src/trace/executive_tracer.rs index 5c2e158e9..9963a9f27 100644 --- a/ethcore/src/trace/executive_tracer.rs +++ b/ethcore/src/trace/executive_tracer.rs @@ -52,11 +52,12 @@ fn update_trace_address(traces: Vec) -> Vec { let mut subtrace_subtraces_left = 0; traces.into_iter().map(|mut trace| { let is_top_subtrace = trace.trace_address.is_empty(); + let is_subtrace = trace.trace_address.len() == 1; trace.trace_address.push_front(top_subtrace_index); if is_top_subtrace { subtrace_subtraces_left = trace.subtraces; - } else { + } else if is_subtrace { subtrace_subtraces_left -= 1; } diff --git a/ethcore/src/types/receipt.rs b/ethcore/src/types/receipt.rs index 78216eb16..b3c38bb90 100644 --- a/ethcore/src/types/receipt.rs +++ b/ethcore/src/types/receipt.rs @@ -80,6 +80,23 @@ impl HeapSizeOf for Receipt { } } +/// Receipt with additional info. +#[derive(Debug, Clone, PartialEq, Binary)] +pub struct RichReceipt { + /// Transaction hash. + pub transaction_hash: H256, + /// Transaction index. + pub transaction_index: usize, + /// The total gas used in the block following execution of the transaction. + pub cumulative_gas_used: U256, + /// The gas used in the execution of the transaction. Note the difference of meaning to `Receipt::gas_used`. + pub gas_used: U256, + /// Contract address. + pub contract_address: Option
, + /// Logs + pub logs: Vec, +} + /// Receipt with additional info. #[derive(Debug, Clone, PartialEq, Binary)] pub struct LocalizedReceipt { diff --git a/nsis/installer.nsi b/nsis/installer.nsi index c93234850..bf71b4903 100644 --- a/nsis/installer.nsi +++ b/nsis/installer.nsi @@ -4,7 +4,7 @@ !define DESCRIPTION "Fast, light, robust Ethereum implementation" !define VERSIONMAJOR 1 !define VERSIONMINOR 3 -!define VERSIONBUILD 0 +!define VERSIONBUILD 1 !addplugindir .\ diff --git a/parity/cli.rs b/parity/cli.rs index f01436a96..ce36458c0 100644 --- a/parity/cli.rs +++ b/parity/cli.rs @@ -132,6 +132,11 @@ API and Console Options: --dapps-interface IP Specify the hostname portion of the Dapps server, IP should be an interface's IP address, or local [default: local]. + --dapps-hosts HOSTS List of allowed Host header values. This option will + validate the Host header sent by the browser, it + is additional security against some attack + vectors. Special options: "all", "none", + [default: none]. --dapps-user USERNAME Specify username for Dapps server. It will be used in HTTP Basic Authentication Scheme. If --dapps-pass is not specified you will be @@ -343,6 +348,7 @@ pub struct Args { pub flag_no_dapps: bool, pub flag_dapps_port: u16, pub flag_dapps_interface: String, + pub flag_dapps_hosts: String, pub flag_dapps_user: Option, pub flag_dapps_pass: Option, pub flag_dapps_path: String, diff --git a/parity/configuration.rs b/parity/configuration.rs index ef846ca80..9a6699c28 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -355,6 +355,7 @@ impl Configuration { enabled: self.dapps_enabled(), interface: self.dapps_interface(), port: self.args.flag_dapps_port, + hosts: self.dapps_hosts(), user: self.args.flag_dapps_user.clone(), pass: self.args.flag_dapps_pass.clone(), dapps_path: self.directories().dapps, @@ -474,6 +475,16 @@ impl Configuration { Some(hosts) } + fn dapps_hosts(&self) -> Option> { + match self.args.flag_dapps_hosts.as_ref() { + "none" => return Some(Vec::new()), + "all" => return None, + _ => {} + } + let hosts = self.args.flag_dapps_hosts.split(',').map(|h| h.into()).collect(); + Some(hosts) + } + fn ipc_config(&self) -> Result { let conf = IpcConfiguration { enabled: !(self.args.flag_ipcdisable || self.args.flag_ipc_off || self.args.flag_no_ipc), @@ -832,6 +843,23 @@ mod tests { assert_eq!(conf3.rpc_hosts(), Some(vec!["ethcore.io".into(), "something.io".into()])); } + #[test] + fn should_parse_dapps_hosts() { + // given + + // when + let conf0 = parse(&["parity"]); + let conf1 = parse(&["parity", "--dapps-hosts", "none"]); + let conf2 = parse(&["parity", "--dapps-hosts", "all"]); + let conf3 = parse(&["parity", "--dapps-hosts", "ethcore.io,something.io"]); + + // then + assert_eq!(conf0.dapps_hosts(), Some(Vec::new())); + assert_eq!(conf1.dapps_hosts(), Some(Vec::new())); + assert_eq!(conf2.dapps_hosts(), None); + assert_eq!(conf3.dapps_hosts(), Some(vec!["ethcore.io".into(), "something.io".into()])); + } + #[test] fn should_disable_signer_in_geth_compat() { // given diff --git a/parity/dapps.rs b/parity/dapps.rs index 6b957a5f1..98a01c854 100644 --- a/parity/dapps.rs +++ b/parity/dapps.rs @@ -15,7 +15,6 @@ // along with Parity. If not, see . use std::sync::Arc; -use std::net::SocketAddr; use io::PanicHandler; use rpc_apis; use helpers::replace_home; @@ -30,6 +29,7 @@ pub struct Configuration { pub enabled: bool, pub interface: String, pub port: u16, + pub hosts: Option>, pub user: Option, pub pass: Option, pub dapps_path: String, @@ -41,6 +41,7 @@ impl Default for Configuration { enabled: true, interface: "127.0.0.1".into(), port: 8080, + hosts: Some(Vec::new()), user: None, pass: None, dapps_path: replace_home("$HOME/.parity/dapps"), @@ -72,48 +73,67 @@ pub fn new(configuration: Configuration, deps: Dependencies) -> Result, -) -> Result { - Err("Your Parity version has been compiled without WebApps support.".into()) -} +mod server { + use super::Dependencies; + use std::net::SocketAddr; -#[cfg(feature = "dapps")] -pub fn setup_dapps_server( - deps: Dependencies, - dapps_path: String, - url: &SocketAddr, - auth: Option<(String, String)> -) -> Result { - use ethcore_dapps as dapps; - - let server = dapps::ServerBuilder::new(dapps_path); - let server = rpc_apis::setup_rpc(server, deps.apis.clone(), rpc_apis::ApiSet::UnsafeContext); - let start_result = match auth { - None => { - server.start_unsecure_http(url) - }, - Some((username, password)) => { - server.start_basic_auth_http(url, &username, &password) - }, - }; - - match start_result { - Err(dapps::ServerError::IoError(err)) => Err(format!("WebApps io error: {}", err)), - Err(e) => Err(format!("WebApps error: {:?}", e)), - Ok(server) => { - server.set_panic_handler(move || { - deps.panic_handler.notify_all("Panic in WebApp thread.".to_owned()); - }); - Ok(server) - }, + pub struct WebappServer; + pub fn setup_dapps_server( + _deps: Dependencies, + _dapps_path: String, + _url: &SocketAddr, + _allowed_hosts: Option>, + _auth: Option<(String, String)>, + ) -> Result { + Err("Your Parity version has been compiled without WebApps support.".into()) + } +} + +#[cfg(feature = "dapps")] +mod server { + use super::Dependencies; + use std::net::SocketAddr; + + use rpc_apis; + + pub use ethcore_dapps::Server as WebappServer; + + pub fn setup_dapps_server( + deps: Dependencies, + dapps_path: String, + url: &SocketAddr, + allowed_hosts: Option>, + auth: Option<(String, String)> + ) -> Result { + use ethcore_dapps as dapps; + + let server = dapps::ServerBuilder::new(dapps_path); + let server = rpc_apis::setup_rpc(server, deps.apis.clone(), rpc_apis::ApiSet::UnsafeContext); + let start_result = match auth { + None => { + server.start_unsecured_http(url, allowed_hosts) + }, + Some((username, password)) => { + server.start_basic_auth_http(url, allowed_hosts, &username, &password) + }, + }; + + match start_result { + Err(dapps::ServerError::IoError(err)) => Err(format!("WebApps io error: {}", err)), + Err(e) => Err(format!("WebApps error: {:?}", e)), + Ok(server) => { + server.set_panic_handler(move || { + deps.panic_handler.notify_all("Panic in WebApp thread.".to_owned()); + }); + Ok(server) + }, + } } } diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 2d682954c..fa5ee459e 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -107,7 +107,7 @@ impl EthClient where let view = block_view.header_view(); let block = Block { hash: Some(view.sha3().into()), - size: Some(bytes.len()), + size: Some(bytes.len().into()), parent_hash: view.parent_hash().into(), uncles_hash: view.uncles_hash().into(), author: view.author().into(), @@ -460,8 +460,8 @@ impl Eth for EthClient where .and_then(|(hash,)| { let miner = take_weak!(self.miner); let hash: H256 = hash.into(); - match miner.pending_receipts().get(&hash) { - Some(receipt) if self.options.allow_pending_receipt_query => to_value(&Receipt::from(receipt.clone())), + match (miner.pending_receipt(&hash), self.options.allow_pending_receipt_query) { + (Some(receipt), true) => to_value(&Receipt::from(receipt)), _ => { let client = take_weak!(self.client); let receipt = client.transaction_receipt(TransactionID::Hash(hash)); diff --git a/rpc/src/v1/impls/ethcore.rs b/rpc/src/v1/impls/ethcore.rs index e5ab2962a..391239f69 100644 --- a/rpc/src/v1/impls/ethcore.rs +++ b/rpc/src/v1/impls/ethcore.rs @@ -15,17 +15,18 @@ // along with Parity. If not, see . //! Ethcore-specific rpc implementation. -use util::{RotatingLogger}; -use util::misc::version_data; use std::sync::{Arc, Weak}; +use std::str::FromStr; use std::collections::{BTreeMap}; +use util::{RotatingLogger, Address}; +use util::misc::version_data; use ethsync::{SyncProvider, ManageNetwork}; use ethcore::miner::MinerService; use ethcore::client::{MiningBlockChainClient}; use jsonrpc_core::*; use v1::traits::Ethcore; -use v1::types::{Bytes, U256, Peers}; +use v1::types::{Bytes, U256, Peers, H160}; use v1::helpers::{errors, SigningQueue, ConfirmationsQueue, NetworkSettings}; use v1::helpers::params::expect_no_params; @@ -54,7 +55,7 @@ impl EthcoreClient where C: MiningBlockChainClient, M: logger: Arc, settings: Arc, queue: Option> - ) -> Self { + ) -> Self { EthcoreClient { client: Arc::downgrade(client), miner: Arc::downgrade(miner), @@ -150,6 +151,17 @@ impl Ethcore for EthcoreClient where M: MinerService + to_value(&self.settings.name) } + fn registry_address(&self, params: Params) -> Result { + try!(self.active()); + try!(expect_no_params(params)); + let r = take_weak!(self.client) + .additional_params() + .get("registrar") + .and_then(|s| Address::from_str(s).ok()) + .map(|s| H160::from(s)); + to_value(&r) + } + fn rpc_settings(&self, params: Params) -> Result { try!(self.active()); try!(expect_no_params(params)); diff --git a/rpc/src/v1/tests/helpers/miner_service.rs b/rpc/src/v1/tests/helpers/miner_service.rs index b3a3deedf..0f36b4f54 100644 --- a/rpc/src/v1/tests/helpers/miner_service.rs +++ b/rpc/src/v1/tests/helpers/miner_service.rs @@ -22,7 +22,7 @@ use ethcore::error::{Error, CallError}; use ethcore::client::{MiningBlockChainClient, Executed, CallAnalytics}; use ethcore::block::{ClosedBlock, IsBlock}; use ethcore::transaction::SignedTransaction; -use ethcore::receipt::Receipt; +use ethcore::receipt::{Receipt, RichReceipt}; use ethcore::miner::{MinerService, MinerStatus, TransactionImportResult}; /// Test miner service. @@ -198,6 +198,20 @@ impl MinerService for TestMinerService { self.pending_transactions.lock().values().cloned().collect() } + fn pending_receipt(&self, hash: &H256) -> Option { + // Not much point implementing this since the logic is complex and the only thing it relies on is pending_receipts, which is already tested. + self.pending_receipts().get(hash).map(|r| + RichReceipt { + transaction_hash: Default::default(), + transaction_index: Default::default(), + cumulative_gas_used: r.gas_used.clone(), + gas_used: r.gas_used.clone(), + contract_address: None, + logs: r.logs.clone(), + } + ) + } + fn pending_receipts(&self) -> BTreeMap { self.pending_receipts.lock().clone() } diff --git a/rpc/src/v1/traits/ethcore.rs b/rpc/src/v1/traits/ethcore.rs index fb4b9492b..ad0258d99 100644 --- a/rpc/src/v1/traits/ethcore.rs +++ b/rpc/src/v1/traits/ethcore.rs @@ -67,6 +67,9 @@ pub trait Ethcore: Sized + Send + Sync + 'static { /// Returns error when signer is disabled fn unsigned_transactions_count(&self, _: Params) -> Result; + /// Returns the value of the registrar for this network. + fn registry_address(&self, _: Params) -> Result; + /// Should be used to convert object to io delegate. fn to_delegate(self) -> IoDelegate { let mut delegate = IoDelegate::new(Arc::new(self)); @@ -86,7 +89,7 @@ pub trait Ethcore: Sized + Send + Sync + 'static { delegate.add_method("ethcore_defaultExtraData", Ethcore::default_extra_data); delegate.add_method("ethcore_gasPriceStatistics", Ethcore::gas_price_statistics); delegate.add_method("ethcore_unsignedTransactionsCount", Ethcore::unsigned_transactions_count); - + delegate.add_method("ethcore_registryAddress", Ethcore::registry_address); delegate } } diff --git a/rpc/src/v1/types/block.rs b/rpc/src/v1/types/block.rs index 08fe37c61..bbdd8d696 100644 --- a/rpc/src/v1/types/block.rs +++ b/rpc/src/v1/types/block.rs @@ -90,7 +90,7 @@ pub struct Block { /// Transactions pub transactions: BlockTransactions, /// Size in bytes - pub size: Option, + pub size: Option, } #[cfg(test)] @@ -132,10 +132,10 @@ mod tests { seal_fields: vec![Bytes::default(), Bytes::default()], uncles: vec![], transactions: BlockTransactions::Hashes(vec![].into()), - size: Some(69usize), + size: Some(69.into()), }; let serialized = serde_json::to_string(&block).unwrap(); - assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","author":"0x0000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","number":"0x00","gasUsed":"0x00","gasLimit":"0x00","extraData":"0x","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x00","difficulty":"0x00","totalDifficulty":"0x00","sealFields":["0x","0x"],"uncles":[],"transactions":[],"size":69}"#); + assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","author":"0x0000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","number":"0x00","gasUsed":"0x00","gasLimit":"0x00","extraData":"0x","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x00","difficulty":"0x00","totalDifficulty":"0x00","sealFields":["0x","0x"],"uncles":[],"transactions":[],"size":"0x45"}"#); } } diff --git a/rpc/src/v1/types/receipt.rs b/rpc/src/v1/types/receipt.rs index afe0268ee..a91d38c87 100644 --- a/rpc/src/v1/types/receipt.rs +++ b/rpc/src/v1/types/receipt.rs @@ -15,7 +15,7 @@ // along with Parity. If not, see . use v1::types::{Log, H160, H256, U256}; -use ethcore::receipt::{Receipt as EthReceipt, LocalizedReceipt}; +use ethcore::receipt::{Receipt as EthReceipt, RichReceipt, LocalizedReceipt}; /// Receipt #[derive(Debug, Serialize)] @@ -37,7 +37,7 @@ pub struct Receipt { pub cumulative_gas_used: U256, /// Gas used #[serde(rename="gasUsed")] - pub gas_used: U256, + pub gas_used: Option, /// Contract address #[serde(rename="contractAddress")] pub contract_address: Option, @@ -53,7 +53,22 @@ impl From for Receipt { block_hash: Some(r.block_hash.into()), block_number: Some(r.block_number.into()), cumulative_gas_used: r.cumulative_gas_used.into(), - gas_used: r.gas_used.into(), + gas_used: Some(r.gas_used.into()), + contract_address: r.contract_address.map(Into::into), + logs: r.logs.into_iter().map(Into::into).collect(), + } + } +} + +impl From for Receipt { + fn from(r: RichReceipt) -> Self { + Receipt { + transaction_hash: Some(r.transaction_hash.into()), + transaction_index: Some(r.transaction_index.into()), + block_hash: None, + block_number: None, + cumulative_gas_used: r.cumulative_gas_used.into(), + gas_used: Some(r.gas_used.into()), contract_address: r.contract_address.map(Into::into), logs: r.logs.into_iter().map(Into::into).collect(), } @@ -68,7 +83,7 @@ impl From for Receipt { block_hash: None, block_number: None, cumulative_gas_used: r.gas_used.into(), - gas_used: r.gas_used.into(), + gas_used: None, contract_address: None, logs: r.logs.into_iter().map(Into::into).collect(), } @@ -91,7 +106,7 @@ mod tests { block_hash: Some(H256::from_str("ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5").unwrap()), block_number: Some(U256::from(0x4510c)), cumulative_gas_used: U256::from(0x20), - gas_used: U256::from(0x10), + gas_used: Some(U256::from(0x10)), contract_address: None, logs: vec![Log { address: H160::from_str("33990122638b9132ca29c723bdf037f1a891a70c").unwrap(), diff --git a/rpc/src/v1/types/trace.rs b/rpc/src/v1/types/trace.rs index b9cedce5c..f4380b0a3 100644 --- a/rpc/src/v1/types/trace.rs +++ b/rpc/src/v1/types/trace.rs @@ -32,14 +32,14 @@ pub struct MemoryDiff { /// Offset into memory the change begins. pub off: usize, /// The changed data. - pub data: Vec, + pub data: Bytes, } impl From for MemoryDiff { fn from(c: et::MemoryDiff) -> Self { MemoryDiff { off: c.offset, - data: c.data, + data: c.data.into(), } } } @@ -118,7 +118,7 @@ impl From<(et::VMOperation, Option)> for VMOperation { /// A record of a full VM trace for a CALL/CREATE. pub struct VMTrace { /// The code to be executed. - pub code: Vec, + pub code: Bytes, /// The operations executed. pub ops: Vec, } @@ -128,7 +128,7 @@ impl From for VMTrace { let mut subs = c.subs.into_iter(); let mut next_sub = subs.next(); VMTrace { - code: c.code, + code: c.code.into(), ops: c.operations .into_iter() .enumerate() @@ -485,7 +485,7 @@ impl From for Trace { /// A diff of some chunk of memory. pub struct TraceResults { /// The output of the call/create - pub output: Vec, + pub output: Bytes, /// The transaction trace. pub trace: Vec, /// The transaction trace. @@ -517,13 +517,13 @@ mod tests { #[test] fn should_serialize_trace_results() { let r = TraceResults { - output: vec![0x60], + output: vec![0x60].into(), trace: vec![], vm_trace: None, state_diff: None, }; let serialized = serde_json::to_string(&r).unwrap(); - assert_eq!(serialized, r#"{"output":[96],"trace":[],"vmTrace":null,"stateDiff":null}"#); + assert_eq!(serialized, r#"{"output":"0x60","trace":[],"vmTrace":null,"stateDiff":null}"#); } #[test] @@ -555,7 +555,7 @@ mod tests { #[test] fn test_vmtrace_serialize() { let t = VMTrace { - code: vec![0, 1, 2, 3], + code: vec![0, 1, 2, 3].into(), ops: vec![ VMOperation { pc: 0, @@ -573,15 +573,15 @@ mod tests { store: None, }), sub: Some(VMTrace { - code: vec![0], + code: vec![0].into(), ops: vec![ VMOperation { pc: 0, cost: 0, ex: Some(VMExecutedOperation { used: 10, - push: vec![42.into()], - mem: Some(MemoryDiff {off: 42, data: vec![1, 2, 3]}), + push: vec![42.into()].into(), + mem: Some(MemoryDiff {off: 42, data: vec![1, 2, 3].into()}), store: Some(StorageDiff {key: 69.into(), val: 42.into()}), }), sub: None, @@ -592,7 +592,7 @@ mod tests { ] }; let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"code":[0,1,2,3],"ops":[{"pc":0,"cost":10,"ex":null,"sub":null},{"pc":1,"cost":11,"ex":{"used":10,"push":["0x45"],"mem":null,"store":null},"sub":{"code":[0],"ops":[{"pc":0,"cost":0,"ex":{"used":10,"push":["0x2a"],"mem":{"off":42,"data":[1,2,3]},"store":{"key":"0x45","val":"0x2a"}},"sub":null}]}}]}"#); + assert_eq!(serialized, r#"{"code":"0x00010203","ops":[{"pc":0,"cost":10,"ex":null,"sub":null},{"pc":1,"cost":11,"ex":{"used":10,"push":["0x45"],"mem":null,"store":null},"sub":{"code":"0x00","ops":[{"pc":0,"cost":0,"ex":{"used":10,"push":["0x2a"],"mem":{"off":42,"data":"0x010203"},"store":{"key":"0x45","val":"0x2a"}},"sub":null}]}}]}"#); } #[test] diff --git a/util/Cargo.toml b/util/Cargo.toml index d18fd68ed..30c548db8 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -3,7 +3,7 @@ description = "Ethcore utility library" homepage = "http://ethcore.io" license = "GPL-3.0" name = "ethcore-util" -version = "1.3.0" +version = "1.3.1" authors = ["Ethcore "] build = "build.rs" diff --git a/util/network/src/discovery.rs b/util/network/src/discovery.rs index 2b98f3230..a78f05804 100644 --- a/util/network/src/discovery.rs +++ b/util/network/src/discovery.rs @@ -59,7 +59,7 @@ pub struct BucketEntry { pub timeout: Option, } -struct NodeBucket { +pub struct NodeBucket { nodes: VecDeque, //sorted by last active } @@ -281,12 +281,12 @@ impl Discovery { if count == BUCKET_SIZE { // delete the most distant element let remove = { - let (_, last) = found.iter_mut().next_back().unwrap(); + let (key, last) = found.iter_mut().next_back().unwrap(); last.pop(); - last.is_empty() + if last.is_empty() { Some(key.clone()) } else { None } }; - if remove { - found.remove(&distance); + if let Some(remove) = remove { + found.remove(&remove); } } else { @@ -605,6 +605,21 @@ mod tests { assert!(removed > 0); } + #[test] + fn find_nearest_saturated() { + use super::*; + let mut buckets: Vec<_> = (0..256).map(|_| NodeBucket::new()).collect(); + let ep = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40447").unwrap(), udp_port: 40447 }; + for _ in 0..(16 + 10) { + buckets[0].nodes.push_back(BucketEntry { + address: NodeEntry { id: NodeId::new(), endpoint: ep.clone() }, + timeout: None + }); + } + let nearest = Discovery::nearest_node_entries(&NodeId::new(), &buckets); + assert_eq!(nearest.len(), 16) + } + #[test] fn packets() { let key = KeyPair::create().unwrap(); diff --git a/util/src/crypto.rs b/util/src/crypto.rs index 44b3b5508..96febcfa7 100644 --- a/util/src/crypto.rs +++ b/util/src/crypto.rs @@ -287,6 +287,7 @@ pub mod ecies { use hash::*; use bytes::*; use crypto::*; + use sha3::Hashable; /// Encrypt a message with a public key pub fn encrypt(public: &Public, shared_mac: &[u8], plain: &[u8]) -> Result { @@ -310,9 +311,11 @@ pub mod ecies { { let msgd = &mut msg[1..]; r.public().copy_to(&mut msgd[0..64]); + let iv = H128::random(); + iv.copy_to(&mut msgd[64..(64+16)]); { let cipher = &mut msgd[(64 + 16)..(64 + 16 + plain.len())]; - aes::encrypt(ekey, &H128::new(), plain, cipher); + aes::encrypt(ekey, &iv, plain, cipher); } let mut hmac = Hmac::new(Sha256::new(), &mkey); { @@ -325,6 +328,33 @@ pub mod ecies { Ok(msg) } + /// Encrypt a message with a public key + pub fn encrypt_single_message(public: &Public, plain: &[u8]) -> Result { + use ::rcrypto::digest::Digest; + use ::rcrypto::sha2::Sha256; + let r = try!(KeyPair::create()); + let z = try!(ecdh::agree(r.secret(), public)); + let mut key = [0u8; 32]; + let mut mkey = [0u8; 32]; + kdf(&z, &[0u8; 0], &mut key); + let mut hasher = Sha256::new(); + let mkey_material = &key[16..32]; + hasher.input(mkey_material); + hasher.result(&mut mkey); + let ekey = &key[0..16]; + + let mut msgd = vec![0u8; (64 + plain.len())]; + { + r.public().copy_to(&mut msgd[0..64]); + let iv = H128::from_slice(&z.sha3()[0..16]); + { + let cipher = &mut msgd[64..(64 + plain.len())]; + aes::encrypt(ekey, &iv, plain, cipher); + } + } + Ok(msgd) + } + /// Decrypt a message with a secret key pub fn decrypt(secret: &Secret, shared_mac: &[u8], encrypted: &[u8]) -> Result { use ::rcrypto::digest::Digest; @@ -370,6 +400,36 @@ pub mod ecies { Ok(msg) } + /// Decrypt single message with a secret key + pub fn decrypt_single_message(secret: &Secret, encrypted: &[u8]) -> Result { + use ::rcrypto::digest::Digest; + use ::rcrypto::sha2::Sha256; + + let meta_len = 64; + if encrypted.len() < meta_len { + return Err(CryptoError::InvalidMessage); //invalid message: publickey + } + + let e = encrypted; + let p = Public::from_slice(&e[0..64]); + let z = try!(ecdh::agree(secret, &p)); + let mut key = [0u8; 32]; + kdf(&z, &[0u8; 0], &mut key); + let ekey = &key[0..16]; + let mkey_material = &key[16..32]; + let mut hasher = Sha256::new(); + let mut mkey = [0u8; 32]; + hasher.input(mkey_material); + hasher.result(&mut mkey); + + let clen = encrypted.len() - meta_len; + let cipher = &e[64..(64+clen)]; + let mut msg = vec![0u8; clen]; + let iv = H128::from_slice(&z.sha3()[0..16]); + aes::decrypt(ekey, &iv, cipher, &mut msg[..]); + Ok(msg) + } + fn kdf(secret: &Secret, s1: &[u8], dest: &mut [u8]) { use ::rcrypto::digest::Digest; use ::rcrypto::sha2::Sha256; @@ -458,4 +518,14 @@ mod tests { let decrypted = ecies::decrypt(kp.secret(), shared, &encrypted).unwrap(); assert_eq!(decrypted[..message.len()], message[..]); } + + #[test] + fn ecies_shared_single() { + let kp = KeyPair::create().unwrap(); + let message = b"So many books, so little time"; + let encrypted = ecies::encrypt_single_message(kp.public(), message).unwrap(); + assert!(encrypted[..] != message[..]); + let decrypted = ecies::decrypt_single_message(kp.secret(), &encrypted).unwrap(); + assert_eq!(decrypted[..message.len()], message[..]); + } } diff --git a/util/src/journaldb/archivedb.rs b/util/src/journaldb/archivedb.rs index 1ec46233c..828b0bbb5 100644 --- a/util/src/journaldb/archivedb.rs +++ b/util/src/journaldb/archivedb.rs @@ -163,7 +163,6 @@ impl JournalDB for ArchiveDB { for i in self.overlay.drain().into_iter() { let (key, (value, rc)) = i; if rc > 0 { - assert!(rc == 1); batch.put(self.column, &key, &value).expect("Low-level database error. Some issue with your hard disk?"); inserts += 1; } @@ -192,7 +191,6 @@ impl JournalDB for ArchiveDB { for i in self.overlay.drain().into_iter() { let (key, (value, rc)) = i; if rc > 0 { - assert!(rc == 1); if try!(self.backing.get(self.column, &key)).is_some() { return Err(BaseDataError::AlreadyExists(key).into()); } diff --git a/util/src/kvdb.rs b/util/src/kvdb.rs index 0aa48e45f..fc9e85056 100644 --- a/util/src/kvdb.rs +++ b/util/src/kvdb.rs @@ -213,23 +213,33 @@ impl Database { if let Some(rate_limit) = config.compaction.write_rate_limit { try!(opts.set_parsed_options(&format!("rate_limiter_bytes_per_sec={}", rate_limit))); } + try!(opts.set_parsed_options(&format!("max_total_wal_size={}", 64 * 1024 * 1024))); opts.set_max_open_files(config.max_open_files); opts.create_if_missing(true); opts.set_use_fsync(false); + opts.set_max_background_flushes(DB_BACKGROUND_FLUSHES); + opts.set_max_background_compactions(DB_BACKGROUND_COMPACTIONS); + // compaction settings opts.set_compaction_style(DBCompactionStyle::DBUniversalCompaction); opts.set_target_file_size_base(config.compaction.initial_file_size); opts.set_target_file_size_multiplier(config.compaction.file_size_multiplier); - opts.set_max_background_flushes(DB_BACKGROUND_FLUSHES); - opts.set_max_background_compactions(DB_BACKGROUND_COMPACTIONS); + let mut cf_options = Vec::with_capacity(config.columns.unwrap_or(0) as usize); - if let Some(cache_size) = config.cache_size { - let mut block_opts = BlockBasedOptions::new(); - // all goes to read cache - block_opts.set_cache(Cache::new(cache_size * 1024 * 1024)); - opts.set_block_based_table_factory(&block_opts); + for _ in 0 .. config.columns.unwrap_or(0) { + let mut opts = Options::new(); + opts.set_compaction_style(DBCompactionStyle::DBUniversalCompaction); + opts.set_target_file_size_base(config.compaction.initial_file_size); + opts.set_target_file_size_multiplier(config.compaction.file_size_multiplier); + if let Some(cache_size) = config.cache_size { + let mut block_opts = BlockBasedOptions::new(); + // all goes to read cache + block_opts.set_cache(Cache::new(cache_size * 1024 * 1024)); + opts.set_block_based_table_factory(&block_opts); + } + cf_options.push(opts); } let mut write_opts = WriteOptions::new(); @@ -242,7 +252,7 @@ impl Database { Some(columns) => { let cfnames: Vec<_> = (0..columns).map(|c| format!("col{}", c)).collect(); let cfnames: Vec<&str> = cfnames.iter().map(|n| n as &str).collect(); - match DB::open_cf(&opts, path, &cfnames) { + match DB::open_cf(&opts, path, &cfnames, &cf_options) { Ok(db) => { cfs = cfnames.iter().map(|n| db.cf_handle(n).unwrap()).collect(); assert!(cfs.len() == columns as usize); @@ -250,9 +260,9 @@ impl Database { } Err(_) => { // retry and create CFs - match DB::open_cf(&opts, path, &[]) { + match DB::open_cf(&opts, path, &[], &[]) { Ok(mut db) => { - cfs = cfnames.iter().map(|n| db.create_cf(n, &opts).unwrap()).collect(); + cfs = cfnames.iter().enumerate().map(|(i, n)| db.create_cf(n, &cf_options[i]).unwrap()).collect(); Ok(db) }, err @ Err(_) => err, diff --git a/util/src/trie/triedbmut.rs b/util/src/trie/triedbmut.rs index d560a0ecf..018b10c56 100644 --- a/util/src/trie/triedbmut.rs +++ b/util/src/trie/triedbmut.rs @@ -56,11 +56,11 @@ impl From for NodeHandle { } } -fn empty_children() -> [Option; 16] { - [ +fn empty_children() -> Box<[Option; 16]> { + Box::new([ None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, - ] + ]) } /// Node types in the Trie. @@ -78,7 +78,7 @@ enum Node { /// The child node is always a branch. Extension(Bytes, NodeHandle), /// A branch has up to 16 children and an optional value. - Branch([Option; 16], Option) + Branch(Box<[Option; 16]>, Option) } impl Node { @@ -820,19 +820,19 @@ impl<'a> TrieDBMut<'a> { /// Commit the in-memory changes to disk, freeing their storage and /// updating the state root. pub fn commit(&mut self) { - let handle = match self.root_handle() { - NodeHandle::Hash(_) => return, // no changes necessary. - NodeHandle::InMemory(h) => h, - }; - trace!(target: "trie", "Committing trie changes to db."); - // kill all the nodes on death row. + // always kill all the nodes on death row. trace!(target: "trie", "{:?} nodes to remove from db", self.death_row.len()); for hash in self.death_row.drain() { self.db.remove(&hash); } + let handle = match self.root_handle() { + NodeHandle::Hash(_) => return, // no changes necessary. + NodeHandle::InMemory(h) => h, + }; + match self.storage.destroy(handle) { Stored::New(node) => { let root_rlp = node.into_rlp(|child, stream| self.commit_node(child, stream)); @@ -906,21 +906,29 @@ impl<'a> TrieMut for TrieDBMut<'a> { return self.remove(key); } + trace!(target: "trie", "insert: key={:?}, value={:?}", key.pretty(), value.pretty()); + let root_handle = self.root_handle(); - let (new_handle, _) = try!(self.insert_at(root_handle, NibbleSlice::new(key), value.to_owned())); + let (new_handle, changed) = try!(self.insert_at(root_handle, NibbleSlice::new(key), value.to_owned())); + + trace!(target: "trie", "insert: altered trie={}", changed); self.root_handle = NodeHandle::InMemory(new_handle); Ok(()) } fn remove(&mut self, key: &[u8]) -> super::Result<()> { + trace!(target: "trie", "remove: key={:?}", key.pretty()); + let root_handle = self.root_handle(); let key = NibbleSlice::new(key); match try!(self.remove_at(root_handle, key)) { - Some((handle, _)) => { + Some((handle, changed)) => { + trace!(target: "trie", "remove: altered trie={}", changed); self.root_handle = NodeHandle::InMemory(handle); } None => { + trace!(target: "trie", "remove: obliterated trie"); self.root_handle = NodeHandle::Hash(SHA3_NULL_RLP); *self.root = SHA3_NULL_RLP; }