From e91eb337c9d01ff339565e682bcd2b4b7608df6d Mon Sep 17 00:00:00 2001 From: gabriel klawitter Date: Wed, 8 May 2019 17:58:51 +0200 Subject: [PATCH 1/9] ci: publish docs debug (#10638) --- .gitlab-ci.yml | 1 + scripts/gitlab/publish-docs.sh | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5bf37df9db5..acea999e2e5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -289,3 +289,4 @@ publish-docs: - scripts/gitlab/publish-docs.sh tags: - linux-docker + allow_failure: true diff --git a/scripts/gitlab/publish-docs.sh b/scripts/gitlab/publish-docs.sh index 262ea80807f..2508c825556 100755 --- a/scripts/gitlab/publish-docs.sh +++ b/scripts/gitlab/publish-docs.sh @@ -51,8 +51,11 @@ commit_files() { upload_files() { echo "__________Upload files__________" - git push -q origin HEAD - git push -q -f --tags + # this version of git (2.7.4) will dump the token on failure + git push -q origin HEAD 2>&1 \ + | sed -r "s|(${GITHUB_USER}):[a-f0-9]+@|\1:REDACTED@|g" + git push -q -f --tags 2>&1 \ + | sed -r "s|(${GITHUB_USER}):[a-f0-9]+@|\1:REDACTED@|g" } RPC_TRAITS_DIR="rpc/src/v1/traits" From de91a5532d5af90793d691f4839e9e2b00140bc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ux=C3=ADo?= Date: Thu, 9 May 2019 15:28:30 +0200 Subject: [PATCH 2/9] Fix rinkeby petersburg fork (#10632) --- ethcore/res/ethereum/rinkeby.json | 1 + 1 file changed, 1 insertion(+) diff --git a/ethcore/res/ethereum/rinkeby.json b/ethcore/res/ethereum/rinkeby.json index 7d9cc80237b..18736177672 100644 --- a/ethcore/res/ethereum/rinkeby.json +++ b/ethcore/res/ethereum/rinkeby.json @@ -25,6 +25,7 @@ "eip1014Transition": "0x37db77", "eip1052Transition": "0x37db77", "eip1283Transition": "0x37db77", + "eip1283DisableTransition": "0x41efd2", "gasLimitBoundDivisor": "0x400", "maxCodeSize": "0x6000", "maxCodeSizeTransition": "0x0", From 2cf0d7d7757829a8d6d3a65547c96258c73b9b91 Mon Sep 17 00:00:00 2001 From: Seun LanLege Date: Fri, 10 May 2019 20:20:42 +0100 Subject: [PATCH 3/9] Adds parity_getRawBlockByNumber, parity_submitRawBlock (#10609) * adds parity_getRawBlockByNumber (cherry picked from commit 1b418f592a03d45997f54304ca10889a96199509) * added parity_submitRawBlock * remove space Co-Authored-By: Andronik Ordian --- rpc/src/v1/helpers/errors.rs | 9 +++++++++ rpc/src/v1/impls/light/parity.rs | 14 ++++++++++++++ rpc/src/v1/impls/parity.rs | 19 +++++++++++++++++++ rpc/src/v1/traits/parity.rs | 8 ++++++++ 4 files changed, 50 insertions(+) diff --git a/rpc/src/v1/helpers/errors.rs b/rpc/src/v1/helpers/errors.rs index 4845f76f561..023f7df3ccc 100644 --- a/rpc/src/v1/helpers/errors.rs +++ b/rpc/src/v1/helpers/errors.rs @@ -39,6 +39,7 @@ mod codes { pub const NO_NEW_WORK: i64 = -32003; pub const NO_WORK_REQUIRED: i64 = -32004; pub const CANNOT_SUBMIT_WORK: i64 = -32005; + pub const CANNOT_SUBMIT_BLOCK: i64 = -32006; pub const UNKNOWN_ERROR: i64 = -32009; pub const TRANSACTION_ERROR: i64 = -32010; pub const EXECUTION_ERROR: i64 = -32015; @@ -246,6 +247,14 @@ pub fn unavailable_block(no_ancient_block: bool, by_hash: bool) -> Error { } } +pub fn cannot_submit_block(err: EthcoreError) -> Error { + Error { + code: ErrorCode::ServerError(codes::CANNOT_SUBMIT_BLOCK), + message: "Cannot submit block.".into(), + data: Some(Value::String(err.to_string())), + } +} + pub fn check_block_number_existence<'a, T, C>( client: &'a C, num: BlockNumber, diff --git a/rpc/src/v1/impls/light/parity.rs b/rpc/src/v1/impls/light/parity.rs index 0486366de62..f3dea5485db 100644 --- a/rpc/src/v1/impls/light/parity.rs +++ b/rpc/src/v1/impls/light/parity.rs @@ -47,6 +47,8 @@ use v1::types::{ Log, Filter, }; use Host; +use v1::helpers::errors::light_unimplemented; +use v1::types::block_number_to_id; /// Parity implementation for light client. pub struct ParityClient @@ -407,4 +409,16 @@ where fn verify_signature(&self, is_prefixed: bool, message: Bytes, r: H256, s: H256, v: U64) -> Result { verify_signature(is_prefixed, message, r, s, v, self.light_dispatch.client.signing_chain_id()) } + + fn get_raw_block_by_number(&self, block: BlockNumber) -> BoxFuture> { + Box::new( + self.fetcher() + .block(block_number_to_id(block)) + .map(|block| Some(Bytes::from(block.raw().to_vec()))) + ) + } + + fn submit_raw_block(&self, _block: Bytes) -> Result { + Err(light_unimplemented(None)) + } } diff --git a/rpc/src/v1/impls/parity.rs b/rpc/src/v1/impls/parity.rs index 796b3f9f5e2..0430acb7568 100644 --- a/rpc/src/v1/impls/parity.rs +++ b/rpc/src/v1/impls/parity.rs @@ -49,6 +49,7 @@ use v1::types::{ block_number_to_id }; use Host; +use ethcore::verification::queue::kind::blocks::Unverified; /// Parity implementation. pub struct ParityClient { @@ -462,4 +463,22 @@ impl Parity for ParityClient where fn verify_signature(&self, is_prefixed: bool, message: Bytes, r: H256, s: H256, v: U64) -> Result { verify_signature(is_prefixed, message, r, s, v, self.client.signing_chain_id()) } + + fn get_raw_block_by_number(&self, block_number: BlockNumber) -> BoxFuture> { + Box::new(futures::done( + Ok( + self.client + .block(block_number_to_id(block_number)) + .map(|block| Bytes::from(block.raw().to_vec())) + ) + )) + } + + + fn submit_raw_block(&self, block: Bytes) -> Result { + let result = self.client.import_block( + Unverified::from_rlp(block.into_vec()).map_err(errors::rlp)? + ); + Ok(result.map_err(errors::cannot_submit_block)?) + } } diff --git a/rpc/src/v1/traits/parity.rs b/rpc/src/v1/traits/parity.rs index e3821355eee..a89e1317191 100644 --- a/rpc/src/v1/traits/parity.rs +++ b/rpc/src/v1/traits/parity.rs @@ -232,4 +232,12 @@ pub trait Parity { /// Is allowed to skip filling transaction hash for faster query. #[rpc(name = "parity_getLogsNoTransactionHash")] fn logs_no_tx_hash(&self, Filter) -> BoxFuture>; + + /// Returns raw block RLP with given number. + #[rpc(name = "parity_getRawBlockByNumber")] + fn get_raw_block_by_number(&self, BlockNumber) -> BoxFuture>; + + /// Submit raw block to be published to the network + #[rpc(name = "parity_submitRawBlock")] + fn submit_raw_block(&self, Bytes) -> Result; } From 42268fd1efed660f615956fac41b0abc7032adba Mon Sep 17 00:00:00 2001 From: David Date: Mon, 13 May 2019 11:25:56 +0200 Subject: [PATCH 4/9] [whisper] Move needed aes_gcm crypto in-crate (#10647) In the latest `parity-crypto` release (upcoming 0.4), the aes GCM features were removed (done to remove the dependency on `ring`). This PR adds the bare minimum crypto needed for Whisper directly to the crate itself and as those were the only features needed from `parity-crypto`, removes the dependency on that crate altogether. --- Cargo.lock | 22 +++++--- whisper/Cargo.toml | 2 +- whisper/src/aes_gcm/mod.rs | 113 +++++++++++++++++++++++++++++++++++++ whisper/src/lib.rs | 4 +- whisper/src/rpc/crypto.rs | 4 +- 5 files changed, 134 insertions(+), 11 deletions(-) create mode 100644 whisper/src/aes_gcm/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 5578689eb04..e08b8af5727 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2504,7 +2504,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2861,9 +2861,9 @@ dependencies = [ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "memzero 0.1.0", "ordered-float 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-crypto 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3370,13 +3370,15 @@ dependencies = [ [[package]] name = "ring" -version = "0.14.3" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3482,7 +3484,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", "sct 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3521,7 +3523,7 @@ name = "sct" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "ring 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3657,6 +3659,11 @@ dependencies = [ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "spin" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "stable_deref_trait" version = "1.1.1" @@ -4399,7 +4406,7 @@ name = "webpki" version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "ring 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -4752,7 +4759,7 @@ dependencies = [ "checksum regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "747ba3b235651f6e2f67dfa8bcdcd073ddb7c243cb21c442fc12395dfcac212d" "checksum relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a" "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" -"checksum ring 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)" = "be5386a5f59e5f5bcaea38b50ad26c09e3918a0abc0610640b3be5cfd85d6894" +"checksum ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)" = "426bc186e3e95cac1e4a4be125a4aca7e84c2d616ffc02244eef36e2a60a093c" "checksum rlp 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "524c5ad554859785dfc8469df3ed5e0b5784d4d335877ed47c8d90fc0eb238fe" "checksum rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "16d1effe9845d54f90e7be8420ee49e5c94623140b97ee4bc6fb5bfddb745720" "checksum rpassword 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b273c91bd242ca03ad6d71c143b6f17a48790e61f21a6c78568fa2b6774a24a4" @@ -4788,6 +4795,7 @@ dependencies = [ "checksum slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5f9776d6b986f77b35c6cf846c11ad986ff128fe0b2b63a3628e3755e8d3102d" "checksum smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "153ffa32fd170e9944f7e0838edf824a754ec4c1fc64746fcc9fe1f8fa602e5d" "checksum socket2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c4d11a52082057d87cb5caa31ad812f4504b97ab44732cd8359df2e9ff9f48e7" +"checksum spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44363f6f51401c34e7be73db0db371c04705d35efbe9f7d6082e03a921a32c55" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum string 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00caf261d6f90f588f8450b8e1230fa0d5be49ee6140fdfbcb55335aff350970" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" diff --git a/whisper/Cargo.toml b/whisper/Cargo.toml index 97aa8f23ac3..c23df60cd2a 100644 --- a/whisper/Cargo.toml +++ b/whisper/Cargo.toml @@ -9,7 +9,7 @@ bitflags = "0.9" byteorder = "1.0.0" ethereum-types = "0.4" ethcore-network = { path = "../util/network" } -parity-crypto = "0.3.0" +ring = "0.14.6" ethkey = { path = "../accounts/ethkey" } hex = "0.2" log = "0.4" diff --git a/whisper/src/aes_gcm/mod.rs b/whisper/src/aes_gcm/mod.rs new file mode 100644 index 00000000000..d7db87b32a3 --- /dev/null +++ b/whisper/src/aes_gcm/mod.rs @@ -0,0 +1,113 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use ring; + +/// AES GCM encryptor. +pub struct Encryptor<'a> { + key: ring::aead::SealingKey, + ad: &'a [u8], + offset: usize, +} + +impl<'a> Encryptor<'a> { + pub fn aes_256_gcm(key: &[u8; 32]) -> Result, ring::error::Unspecified> { + let sk = ring::aead::SealingKey::new(&ring::aead::AES_256_GCM, key)?; + Ok(Encryptor { + key: sk, + ad: &[], + offset: 0, + }) + } + + /// Optional offset value. Only the slice `[offset..]` will be encrypted. + pub fn offset(&mut self, off: usize) -> &mut Self { + self.offset = off; + self + } + + /// Please note that the pair (key, nonce) must never be reused. Using random nonces + /// limits the number of messages encrypted with the same key to 2^32 (cf. [[1]]) + /// + /// [1]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf + pub fn encrypt(&self, nonce: &[u8; 12], mut data: Vec) -> Result, ring::error::Unspecified> { + if self.offset > data.len() { + return Err(ring::error::Unspecified) + } + let tag_len = ring::aead::AES_256_GCM.tag_len(); + data.extend(::std::iter::repeat(0).take(tag_len)); + let nonce = ring::aead::Nonce::assume_unique_for_key(*nonce); + let aad = ring::aead::Aad::from(self.ad); + let len = ring::aead::seal_in_place(&self.key, nonce, aad, &mut data[self.offset ..], tag_len)?; + data.truncate(self.offset + len); + Ok(data) + } +} + +/// AES GCM decryptor. +pub struct Decryptor<'a> { + key: ring::aead::OpeningKey, + ad: &'a [u8], + offset: usize, +} + +impl<'a> Decryptor<'a> { + pub fn aes_256_gcm(key: &[u8; 32]) -> Result, ring::error::Unspecified> { + let ok = ring::aead::OpeningKey::new(&ring::aead::AES_256_GCM, key)?; + Ok(Decryptor { + key: ok, + ad: &[], + offset: 0, + }) + } + + pub fn decrypt(&self, nonce: &[u8; 12], mut data: Vec) -> Result, ring::error::Unspecified> { + if self.offset > data.len() { + return Err(ring::error::Unspecified) + } + let nonce = ring::aead::Nonce::assume_unique_for_key(*nonce); + let aad = ring::aead::Aad::from(self.ad); + let len = ring::aead::open_in_place(&self.key, nonce, aad, 0, &mut data[self.offset ..])?.len(); + data.truncate(self.offset + len); + Ok(data) + } +} + +#[cfg(test)] +mod tests { + use super::{Encryptor, Decryptor}; + + #[test] + fn aes_gcm_256() { + let secret = b"12345678901234567890123456789012"; + let nonce = b"123456789012"; + let message = b"So many books, so little time"; + + let ciphertext = Encryptor::aes_256_gcm(secret) + .unwrap() + .encrypt(nonce, message.to_vec()) + .unwrap(); + + assert!(ciphertext != message); + + let plaintext = Decryptor::aes_256_gcm(secret) + .unwrap() + .decrypt(nonce, ciphertext) + .unwrap(); + + assert_eq!(plaintext, message) + } +} diff --git a/whisper/src/lib.rs b/whisper/src/lib.rs index cdc88780d4d..80447ef1176 100644 --- a/whisper/src/lib.rs +++ b/whisper/src/lib.rs @@ -20,7 +20,6 @@ #![cfg_attr(feature = "time_checked_add", feature(time_checked_add))] extern crate byteorder; -extern crate parity_crypto as crypto; extern crate ethcore_network as network; extern crate ethereum_types; extern crate ethkey; @@ -29,6 +28,7 @@ extern crate memzero; extern crate ordered_float; extern crate parking_lot; extern crate rand; +extern crate ring; extern crate rlp; extern crate serde; extern crate slab; @@ -60,3 +60,5 @@ pub use self::net::{Network, MessageHandler}; pub mod message; pub mod net; pub mod rpc; + +mod aes_gcm; diff --git a/whisper/src/rpc/crypto.rs b/whisper/src/rpc/crypto.rs index 89116345313..ba4435e7cb9 100644 --- a/whisper/src/rpc/crypto.rs +++ b/whisper/src/rpc/crypto.rs @@ -16,7 +16,7 @@ //! Encryption schemes supported by RPC layer. -use crypto::aes_gcm::{Encryptor, Decryptor}; +use aes_gcm::{Encryptor, Decryptor}; use ethkey::crypto::ecies; use ethereum_types::H256; use ethkey::{self, Public, Secret}; @@ -77,7 +77,7 @@ impl EncryptionInstance { EncryptionInner::AES(key, nonce, encode) => { match encode { AesEncode::AppendedNonce => { - let mut enc = Encryptor::aes_256_gcm(&*key).ok()?; + let enc = Encryptor::aes_256_gcm(&*key).ok()?; let mut buf = enc.encrypt(&nonce, plain.to_vec()).ok()?; buf.extend(&nonce[..]); Some(buf) From 87699f8de031ffdb60646e150bf31223caa5e257 Mon Sep 17 00:00:00 2001 From: Niklas Adolfsson Date: Mon, 13 May 2019 15:10:25 +0200 Subject: [PATCH 5/9] fix(compilation warnings) (#10649) --- ethcore/src/client/client.rs | 2 +- ethcore/src/engines/clique/mod.rs | 5 ++--- ethcore/src/ethereum/ethash.rs | 2 +- parity/informant.rs | 2 +- rpc/src/tests/http_client.rs | 8 ++++---- rpc/src/v1/helpers/dispatch/prospective_signer.rs | 2 +- util/network-devp2p/src/host.rs | 2 +- 7 files changed, 11 insertions(+), 12 deletions(-) diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 0c1dd86d431..bf980ac5fd0 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -273,7 +273,7 @@ impl Importer { let (imported_blocks, import_results, invalid_blocks, imported, proposed_blocks, duration, has_more_blocks_to_import) = { let mut imported_blocks = Vec::with_capacity(max_blocks_to_import); let mut invalid_blocks = HashSet::new(); - let mut proposed_blocks = Vec::with_capacity(max_blocks_to_import); + let proposed_blocks = Vec::with_capacity(max_blocks_to_import); let mut import_results = Vec::with_capacity(max_blocks_to_import); let _import_lock = self.import_lock.lock(); diff --git a/ethcore/src/engines/clique/mod.rs b/ethcore/src/engines/clique/mod.rs index b63bf76b23e..1ae30905e0e 100644 --- a/ethcore/src/engines/clique/mod.rs +++ b/ethcore/src/engines/clique/mod.rs @@ -287,8 +287,7 @@ impl Clique { "Back-filling block state. last_checkpoint_number: {}, target: {}({}).", last_checkpoint_number, header.number(), header.hash()); - let mut chain: &mut VecDeque
= &mut VecDeque::with_capacity( - (header.number() - last_checkpoint_number + 1) as usize); + let mut chain = VecDeque::with_capacity((header.number() - last_checkpoint_number + 1) as usize); // Put ourselves in. chain.push_front(header.clone()); @@ -332,7 +331,7 @@ impl Clique { // Backfill! let mut new_state = last_checkpoint_state.clone(); - for item in chain { + for item in &chain { new_state.apply(item, false)?; } new_state.calc_next_timestamp(header.timestamp(), self.period)?; diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 1c47e114b43..8a19d9a0116 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -277,7 +277,7 @@ impl Engine for Arc { let n_uncles = block.uncles.len(); // Bestow block rewards. - let mut result_block_reward = reward + reward.shr(5) * U256::from(n_uncles); + let result_block_reward = reward + reward.shr(5) * U256::from(n_uncles); rewards.push((author, RewardKind::Author, result_block_reward)); diff --git a/parity/informant.rs b/parity/informant.rs index e0ba66f072e..2855579762c 100644 --- a/parity/informant.rs +++ b/parity/informant.rs @@ -259,7 +259,7 @@ impl Informant { let elapsed = now.duration_since(*self.last_tick.read()); let (client_report, full_report) = { - let mut last_report = self.last_report.lock(); + let last_report = self.last_report.lock(); let full_report = self.target.report(); let diffed = full_report.client_report.clone() - &*last_report; (diffed, full_report) diff --git a/rpc/src/tests/http_client.rs b/rpc/src/tests/http_client.rs index 0588c791e77..38f2ae93c19 100644 --- a/rpc/src/tests/http_client.rs +++ b/rpc/src/tests/http_client.rs @@ -113,20 +113,20 @@ pub fn request(address: &SocketAddr, request: &str) -> Response { pub fn assert_security_headers_present(headers: &[String], port: Option) { if port.is_none() { assert!( - headers.iter().any(|header| header.as_str() == "X-Frame-Options: SAMEORIGIN") + headers.iter().any(|header| header.as_str() == "X-Frame-Options: SAMEORIGIN"), "X-Frame-Options: SAMEORIGIN missing: {:?}", headers ); } assert!( - headers.iter().any(|header| header.as_str() == "X-XSS-Protection: 1; mode=block") + headers.iter().any(|header| header.as_str() == "X-XSS-Protection: 1; mode=block"), "X-XSS-Protection missing: {:?}", headers ); assert!( - headers.iter().any(|header| header.as_str() == "X-Content-Type-Options: nosniff") + headers.iter().any(|header| header.as_str() == "X-Content-Type-Options: nosniff"), "X-Content-Type-Options missing: {:?}", headers ); assert!( - headers.iter().any(|header| header.starts_with("Content-Security-Policy: ")) + headers.iter().any(|header| header.starts_with("Content-Security-Policy: ")), "Content-Security-Policy missing: {:?}", headers ) } diff --git a/rpc/src/v1/helpers/dispatch/prospective_signer.rs b/rpc/src/v1/helpers/dispatch/prospective_signer.rs index 034d19dc659..6d4b47089fc 100644 --- a/rpc/src/v1/helpers/dispatch/prospective_signer.rs +++ b/rpc/src/v1/helpers/dispatch/prospective_signer.rs @@ -129,7 +129,7 @@ impl Future for ProspectiveSigner

{ .into_future()); }, WaitForPostSign => { - if let Some(mut fut) = self.post_sign_future.as_mut() { + if let Some(fut) = self.post_sign_future.as_mut() { match fut.poll()? { Async::Ready(item) => { let nonce = self.ready diff --git a/util/network-devp2p/src/host.rs b/util/network-devp2p/src/host.rs index 3dc107d2b20..5f096975178 100644 --- a/util/network-devp2p/src/host.rs +++ b/util/network-devp2p/src/host.rs @@ -616,7 +616,7 @@ impl Host { let socket = { let address = { - let mut nodes = self.nodes.read(); + let nodes = self.nodes.read(); if let Some(node) = nodes.get(id) { node.endpoint.address } else { From 5a581c1c90199a0e7946c9db50ebad82ee7f1fa7 Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Tue, 14 May 2019 11:21:22 +0200 Subject: [PATCH 6/9] Trivial journal for private transactions (#10056) * Journal for private txs added * Tests after adding logging to private tx fixed * Logs getter and tests added * Time and amount limit for logs added * RPC method for log retrieving added * Correct path name and time validation implemented * References for parameters added, redundant cloning reworked * References for parameters added, redundant cloning reworked * Work with json moved to the separate struct * Serialization test added * Fixed build after the merge with head * Documentation for methods fixed, redundant field removed * Fixed error usages * Timestamp trait implemented for std struct * Commented code removed * Remove timestamp source, rework serialization test * u64 replaced with SystemTime * Path made mandatory for logging * Source of monotonic time added * into_system_time method renamed * Initialize time source by max from current system time and max creation time from already saved logs * Redundant conversions removed, code a little bit reworked according to review comments * One more redundant conversion removed, rpc call simplified --- Cargo.lock | 1 + ethcore/private-tx/Cargo.toml | 1 + ethcore/private-tx/src/error.rs | 20 + ethcore/private-tx/src/lib.rs | 54 ++- ethcore/private-tx/src/log.rs | 408 ++++++++++++++++++ .../private-tx/src/private_transactions.rs | 2 +- ethcore/private-tx/tests/private_contract.rs | 2 + ethcore/sync/src/tests/private.rs | 2 + parity/configuration.rs | 8 +- rpc/src/v1/impls/private.rs | 10 +- rpc/src/v1/traits/private.rs | 6 +- rpc/src/v1/types/mod.rs | 6 +- rpc/src/v1/types/private_log.rs | 93 ++++ 13 files changed, 596 insertions(+), 17 deletions(-) create mode 100644 ethcore/private-tx/src/log.rs create mode 100644 rpc/src/v1/types/private_log.rs diff --git a/Cargo.lock b/Cargo.lock index e08b8af5727..367a86f575b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1050,6 +1050,7 @@ dependencies = [ "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "time-utils 0.1.0", "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "transaction-pool 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "trie-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ethcore/private-tx/Cargo.toml b/ethcore/private-tx/Cargo.toml index 2ce127a8b7a..cd484d53f7b 100644 --- a/ethcore/private-tx/Cargo.toml +++ b/ethcore/private-tx/Cargo.toml @@ -35,6 +35,7 @@ rustc-hex = "1.0" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" +time-utils = { path = "../../util/time-utils" } tiny-keccak = "1.4" transaction-pool = "2.0" url = "1" diff --git a/ethcore/private-tx/src/error.rs b/ethcore/private-tx/src/error.rs index eda08b2a567..b4fc4a3fad3 100644 --- a/ethcore/private-tx/src/error.rs +++ b/ethcore/private-tx/src/error.rs @@ -25,6 +25,7 @@ use ethkey::Error as KeyError; use ethkey::crypto::Error as CryptoError; use txpool::VerifiedTransaction; use private_transactions::VerifiedPrivateTransaction; +use serde_json::{Error as SerdeError}; type TxPoolError = txpool::Error<::Hash>; @@ -45,6 +46,9 @@ pub enum Error { /// Crypto error. #[display(fmt = "Crypto Error {}", _0)] Crypto(CryptoError), + /// Serialization error. + #[display(fmt = "Serialization Error {}", _0)] + Json(SerdeError), /// Encryption error. #[display(fmt = "Encryption error. ({})", _0)] Encrypt(String), @@ -99,6 +103,15 @@ pub enum Error { /// Key server URL is not set. #[display(fmt = "Key server URL is not set.")] KeyServerNotSet, + /// Transaction not found in logs. + #[display(fmt = "Private transaction not found in logs.")] + TxNotFoundInLog, + /// Path for logging not set. + #[display(fmt = "Path for logging not set.")] + LoggingPathNotSet, + /// Timestamp overflow error. + #[display(fmt = "Timestamp overflow error.")] + TimestampOverflow, /// VM execution error. #[display(fmt = "VM execution error {}", _0)] Execution(ExecutionError), @@ -123,6 +136,7 @@ impl error::Error for Error { Error::Decoder(e) => Some(e), Error::Trie(e) => Some(e), Error::TxPool(e) => Some(e), + Error::Json(e) => Some(e), Error::Crypto(e) => Some(e), Error::Execution(e) => Some(e), Error::Key(e) => Some(e), @@ -187,6 +201,12 @@ impl From for Error { } } +impl From for Error { + fn from(err: SerdeError) -> Self { + Error::Json(err).into() + } +} + impl From for Error { fn from(err: EthcoreError) -> Self { Error::Ethcore(err).into() diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index d487b4d835b..aaf44d572b6 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -25,6 +25,7 @@ mod key_server_keys; mod private_transactions; mod messages; mod error; +mod log; extern crate common_types as types; extern crate ethabi; @@ -45,11 +46,15 @@ extern crate parking_lot; extern crate trie_db as trie; extern crate patricia_trie_ethereum as ethtrie; extern crate rlp; +#[macro_use] +extern crate serde_derive; +extern crate serde; +extern crate serde_json; extern crate rustc_hex; extern crate transaction_pool as txpool; extern crate url; #[macro_use] -extern crate log; +extern crate log as ethlog; #[macro_use] extern crate ethabi_derive; #[macro_use] @@ -58,6 +63,9 @@ extern crate derive_more; #[macro_use] extern crate rlp_derive; +#[cfg(not(time_checked_add))] +extern crate time_utils; + #[cfg(test)] extern crate rand; #[cfg(test)] @@ -68,6 +76,7 @@ pub use key_server_keys::{KeyProvider, SecretStoreKeys, StoringKeyProvider}; pub use private_transactions::{VerifiedPrivateTransaction, VerificationStore, PrivateTransactionSigningDesc, SigningStore}; pub use messages::{PrivateTransaction, SignedPrivateTransaction}; pub use error::Error; +pub use log::{Logging, TransactionLog, ValidatorLog, PrivateTxStatus, FileLogsSerializer}; use std::sync::{Arc, Weak}; use std::collections::{HashMap, HashSet, BTreeMap}; @@ -117,6 +126,8 @@ pub struct ProviderConfig { pub validator_accounts: Vec

, /// Account used for signing public transactions created from private transactions pub signer_account: Option
, + /// Path to private tx logs + pub logs_path: Option, } #[derive(Debug)] @@ -177,6 +188,7 @@ pub struct Provider { accounts: Arc, channel: IoChannel, keys_provider: Arc, + logging: Option, } #[derive(Debug)] @@ -211,6 +223,7 @@ impl Provider { accounts, channel, keys_provider, + logging: config.logs_path.map(|path| Logging::new(Arc::new(FileLogsSerializer::with_path(path)))), } } @@ -257,8 +270,11 @@ impl Provider { trace!(target: "privatetx", "Required validators: {:?}", contract_validators); let private_state_hash = self.calculate_state_hash(&private_state, contract_nonce); trace!(target: "privatetx", "Hashed effective private state for sender: {:?}", private_state_hash); - self.transactions_for_signing.write().add_transaction(private.hash(), signed_transaction, contract_validators, private_state, contract_nonce)?; + self.transactions_for_signing.write().add_transaction(private.hash(), signed_transaction, &contract_validators, private_state, contract_nonce)?; self.broadcast_private_transaction(private.hash(), private.rlp_bytes()); + if let Some(ref logging) = self.logging { + logging.private_tx_created(&tx_hash, &contract_validators); + } Ok(Receipt { hash: tx_hash, contract_address: contract, @@ -354,8 +370,9 @@ impl Provider { Some(desc) => desc, }; let last = self.last_required_signature(&desc, signed_tx.signature())?; + let original_tx_hash = desc.original_transaction.hash(); - if last { + if last.0 { let mut signatures = desc.received_signatures.clone(); signatures.push(signed_tx.signature()); let rsv: Vec = signatures.into_iter().map(|sign| sign.into_electrum().into()).collect(); @@ -373,8 +390,8 @@ impl Provider { trace!(target: "privatetx", "Last required signature received, public transaction created: {:?}", public_tx); // Sign and add it to the queue let chain_id = desc.original_transaction.chain_id(); - let hash = public_tx.hash(chain_id); - let signature = self.accounts.sign(signer_account, hash)?; + let public_tx_hash = public_tx.hash(chain_id); + let signature = self.accounts.sign(signer_account, public_tx_hash)?; let signed = SignedTransaction::new(public_tx.with_signature(signature, chain_id))?; match self.miner.import_own_transaction(&*self.client, signed.into()) { Ok(_) => trace!(target: "privatetx", "Public transaction added to queue"), @@ -392,6 +409,11 @@ impl Provider { Err(err) => warn!(target: "privatetx", "Failed to send private state changed notification, error: {:?}", err), } } + // Store logs + if let Some(ref logging) = self.logging { + logging.signature_added(&original_tx_hash, &last.1); + logging.tx_deployed(&original_tx_hash, &public_tx_hash); + } // Remove from store for signing if let Err(err) = self.transactions_for_signing.write().remove(&private_hash) { warn!(target: "privatetx", "Failed to remove transaction from signing store, error: {:?}", err); @@ -400,7 +422,12 @@ impl Provider { } else { // Add signature to the store match self.transactions_for_signing.write().add_signature(&private_hash, signed_tx.signature()) { - Ok(_) => trace!(target: "privatetx", "Signature stored for private transaction"), + Ok(_) => { + trace!(target: "privatetx", "Signature stored for private transaction"); + if let Some(ref logging) = self.logging { + logging.signature_added(&original_tx_hash, &last.1); + } + } Err(err) => { warn!(target: "privatetx", "Failed to add signature to signing store, error: {:?}", err); return Err(err); @@ -420,17 +447,14 @@ impl Provider { } } - fn last_required_signature(&self, desc: &PrivateTransactionSigningDesc, sign: Signature) -> Result { - if desc.received_signatures.contains(&sign) { - return Ok(false); - } + fn last_required_signature(&self, desc: &PrivateTransactionSigningDesc, sign: Signature) -> Result<(bool, Address), Error> { let state_hash = self.calculate_state_hash(&desc.state, desc.contract_nonce); match recover(&sign, &state_hash) { Ok(public) => { let sender = public_to_address(&public); match desc.validators.contains(&sender) { true => { - Ok(desc.received_signatures.len() + 1 == desc.validators.len()) + Ok((desc.received_signatures.len() + 1 == desc.validators.len(), sender)) } false => { warn!(target: "privatetx", "Sender's state doesn't correspond to validator's"); @@ -674,6 +698,14 @@ impl Provider { Ok(result.result) } + /// Retrieves log information about private transaction + pub fn private_log(&self, tx_hash: H256) -> Result { + match self.logging { + Some(ref logging) => logging.tx_log(&tx_hash).ok_or(Error::TxNotFoundInLog), + None => Err(Error::LoggingPathNotSet), + } + } + /// Returns private validators for a contract. pub fn get_validators(&self, block: BlockId, address: &Address) -> Result, Error> { let (data, decoder) = private_contract::functions::get_validators::call(); diff --git a/ethcore/private-tx/src/log.rs b/ethcore/private-tx/src/log.rs new file mode 100644 index 00000000000..d836dfa3654 --- /dev/null +++ b/ethcore/private-tx/src/log.rs @@ -0,0 +1,408 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Private transactions logs. + +use ethereum_types::{H256, Address}; +use std::collections::HashMap; +use std::fs::File; +use std::path::PathBuf; +use std::sync::Arc; +use std::time::{SystemTime, Duration, Instant}; +use parking_lot::RwLock; +use serde::ser::{Serializer, SerializeSeq}; +use error::Error; + +#[cfg(not(time_checked_add))] +use time_utils::CheckedSystemTime; + +/// Maximum amount of stored private transaction logs. +const MAX_JOURNAL_LEN: usize = 1000; + +/// Maximum period for storing private transaction logs. +/// Logs older than 20 days will not be processed +const MAX_STORING_TIME: Duration = Duration::from_secs(60 * 60 * 24 * 20); + +/// Source of monotonic time for log timestamps +struct MonoTime { + start_time: SystemTime, + start_inst: Instant +} + +impl MonoTime { + fn new(start: SystemTime) -> Self { + Self { + start_time: start, + start_inst: Instant::now() + } + } + + fn elapsed(&self) -> Duration { + self.start_inst.elapsed() + } + + fn to_system_time(&self) -> SystemTime { + self.start_time + self.elapsed() + } +} + +impl Default for MonoTime { + fn default() -> Self { + MonoTime::new(SystemTime::now()) + } +} + +/// Current status of the private transaction +#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] +pub enum PrivateTxStatus { + /// Private tx was created but no validation received yet + Created, + /// Several validators (but not all) validated the transaction + Validating, + /// All validators has validated the private tx + /// Corresponding public tx was created and added into the pool + Deployed, +} + +/// Information about private tx validation +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ValidatorLog { + /// Account of the validator + pub account: Address, + /// Validation timestamp, None if the transaction is not validated + pub validation_timestamp: Option, +} + +#[cfg(test)] +impl PartialEq for ValidatorLog { + fn eq(&self, other: &Self) -> bool { + self.account == other.account + } +} + +/// Information about the private transaction +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TransactionLog { + /// Original signed transaction hash (used as a source for private tx) + pub tx_hash: H256, + /// Current status of the private transaction + pub status: PrivateTxStatus, + /// Creation timestamp + pub creation_timestamp: SystemTime, + /// List of validations + pub validators: Vec, + /// Timestamp of the resulting public tx deployment + pub deployment_timestamp: Option, + /// Hash of the resulting public tx + pub public_tx_hash: Option, +} + +#[cfg(test)] +impl PartialEq for TransactionLog { + fn eq(&self, other: &Self) -> bool { + self.tx_hash == other.tx_hash && + self.status == other.status && + self.validators == other.validators && + self.public_tx_hash == other.public_tx_hash + } +} + +/// Wrapper other JSON serializer +pub trait LogsSerializer: Send + Sync + 'static { + /// Read logs from the source + fn read_logs(&self) -> Result, Error>; + + /// Write all logs to the source + fn flush_logs(&self, logs: &HashMap) -> Result<(), Error>; +} + +/// Logs serializer to the json file +pub struct FileLogsSerializer { + logs_dir: PathBuf, +} + +impl FileLogsSerializer { + pub fn with_path>(logs_dir: P) -> Self { + FileLogsSerializer { + logs_dir: logs_dir.into(), + } + } + + fn open_file(&self, to_create: bool) -> Result { + let file_path = self.logs_dir.with_file_name("private_tx.log"); + if to_create { + File::create(&file_path).map_err(From::from) + } else { + File::open(&file_path).map_err(From::from) + } + } +} + +impl LogsSerializer for FileLogsSerializer { + fn read_logs(&self) -> Result, Error> { + let log_file = self.open_file(false)?; + match serde_json::from_reader(log_file) { + Ok(logs) => Ok(logs), + Err(err) => { + error!(target: "privatetx", "Cannot deserialize logs from file: {}", err); + return Err(format!("Cannot deserialize logs from file: {:?}", err).into()); + } + } + } + + fn flush_logs(&self, logs: &HashMap) -> Result<(), Error> { + if logs.is_empty() { + // Do not create empty file + return Ok(()); + } + let log_file = self.open_file(true)?; + let mut json = serde_json::Serializer::new(log_file); + let mut json_array = json.serialize_seq(Some(logs.len()))?; + for v in logs.values() { + json_array.serialize_element(v)?; + } + json_array.end()?; + Ok(()) + } +} + +/// Private transactions logging +pub struct Logging { + logs: RwLock>, + logs_serializer: Arc, + mono_time: MonoTime, +} + +impl Logging { + /// Creates the logging object + pub fn new(logs_serializer: Arc) -> Self { + let mut logging = Logging { + logs: RwLock::new(HashMap::new()), + logs_serializer, + mono_time: MonoTime::default(), + }; + match logging.read_logs() { + // Initialize time source by max from current system time and max creation time from already saved logs + Ok(initial_time) => logging.mono_time = MonoTime::new(initial_time), + Err(err) => warn!(target: "privatetx", "Cannot read logs: {:?}", err), + } + logging + } + + /// Retrieves log for the corresponding tx hash + pub fn tx_log(&self, tx_hash: &H256) -> Option { + self.logs.read().get(&tx_hash).cloned() + } + + /// Logs the creation of the private transaction + pub fn private_tx_created(&self, tx_hash: &H256, validators: &[Address]) { + let mut validator_logs = Vec::new(); + for account in validators { + validator_logs.push(ValidatorLog { + account: *account, + validation_timestamp: None, + }); + } + let mut logs = self.logs.write(); + if logs.len() > MAX_JOURNAL_LEN { + // Remove the oldest log + if let Some(tx_hash) = logs.values() + .min_by(|x, y| x.creation_timestamp.cmp(&y.creation_timestamp)) + .map(|oldest| oldest.tx_hash) + { + logs.remove(&tx_hash); + } + } + logs.insert(*tx_hash, TransactionLog { + tx_hash: *tx_hash, + status: PrivateTxStatus::Created, + creation_timestamp: self.mono_time.to_system_time(), + validators: validator_logs, + deployment_timestamp: None, + public_tx_hash: None, + }); + } + + /// Logs the validation of the private transaction by one of its validators + pub fn signature_added(&self, tx_hash: &H256, validator: &Address) { + let mut logs = self.logs.write(); + if let Some(transaction_log) = logs.get_mut(&tx_hash) { + if let Some(ref mut validator_log) = transaction_log.validators.iter_mut().find(|log| log.account == *validator) { + transaction_log.status = PrivateTxStatus::Validating; + validator_log.validation_timestamp = Some(self.mono_time.to_system_time()); + } + } + } + + /// Logs the final deployment of the resulting public transaction + pub fn tx_deployed(&self, tx_hash: &H256, public_tx_hash: &H256) { + let mut logs = self.logs.write(); + if let Some(log) = logs.get_mut(&tx_hash) { + log.status = PrivateTxStatus::Deployed; + log.deployment_timestamp = Some(self.mono_time.to_system_time()); + log.public_tx_hash = Some(*public_tx_hash); + } + } + + fn read_logs(&self) -> Result { + let mut transaction_logs = self.logs_serializer.read_logs()?; + // Drop old logs + let earliest_possible = SystemTime::now().checked_sub(MAX_STORING_TIME).ok_or(Error::TimestampOverflow)?; + transaction_logs.retain(|tx_log| tx_log.creation_timestamp > earliest_possible); + // Sort logs by their creation time in order to find the most recent + transaction_logs.sort_by(|a, b| b.creation_timestamp.cmp(&a.creation_timestamp)); + let initial_timestamp = transaction_logs.first() + .map_or(SystemTime::now(), |l| std::cmp::max(SystemTime::now(), l.creation_timestamp)); + let mut logs = self.logs.write(); + for log in transaction_logs { + logs.insert(log.tx_hash, log); + } + Ok(initial_timestamp) + } + + fn flush_logs(&self) -> Result<(), Error> { + let logs = self.logs.read(); + self.logs_serializer.flush_logs(&logs) + } +} + +// Flush all logs on drop +impl Drop for Logging { + fn drop(&mut self) { + if let Err(err) = self.flush_logs() { + warn!(target: "privatetx", "Cannot write logs: {:?}", err); + } + } +} + +#[cfg(test)] +mod tests { + use serde_json; + use error::Error; + use ethereum_types::H256; + use std::collections::{HashMap, BTreeMap}; + use std::sync::Arc; + use std::time::{SystemTime, Duration}; + use types::transaction::Transaction; + use parking_lot::RwLock; + use super::{TransactionLog, Logging, PrivateTxStatus, LogsSerializer, ValidatorLog}; + + #[cfg(not(time_checked_add))] + use time_utils::CheckedSystemTime; + + struct StringLogSerializer { + string_log: RwLock, + } + + impl StringLogSerializer { + fn new(source: String) -> Self { + StringLogSerializer { + string_log: RwLock::new(source), + } + } + + fn log(&self) -> String { + let log = self.string_log.read(); + log.clone() + } + } + + impl LogsSerializer for StringLogSerializer { + fn read_logs(&self) -> Result, Error> { + let source = self.string_log.read(); + if source.is_empty() { + return Ok(Vec::new()) + } + let logs = serde_json::from_str(&source).unwrap(); + Ok(logs) + } + + fn flush_logs(&self, logs: &HashMap) -> Result<(), Error> { + // Sort logs in order to have the same order + let sorted_logs: BTreeMap<&H256, &TransactionLog> = logs.iter().collect(); + *self.string_log.write() = serde_json::to_string(&sorted_logs.values().collect::>())?; + Ok(()) + } + } + + #[test] + fn private_log_format() { + let s = r#"{ + "tx_hash":"0x64f648ca7ae7f4138014f860ae56164d8d5732969b1cea54d8be9d144d8aa6f6", + "status":"Deployed", + "creation_timestamp":{"secs_since_epoch":1557220355,"nanos_since_epoch":196382053}, + "validators":[{ + "account":"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1", + "validation_timestamp":{"secs_since_epoch":1557220355,"nanos_since_epoch":196382053} + }], + "deployment_timestamp":{"secs_since_epoch":1557220355,"nanos_since_epoch":196382053}, + "public_tx_hash":"0x69b9c691ede7993effbcc88911c309af1c82be67b04b3882dd446b808ae146da" + }"#; + + let _deserialized: TransactionLog = serde_json::from_str(s).unwrap(); + } + + #[test] + fn private_log_status() { + let logger = Logging::new(Arc::new(StringLogSerializer::new("".into()))); + let private_tx = Transaction::default(); + let hash = private_tx.hash(None); + logger.private_tx_created(&hash, &vec!["0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1".into()]); + logger.signature_added(&hash, &"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1".into()); + logger.tx_deployed(&hash, &hash); + let tx_log = logger.tx_log(&hash).unwrap(); + assert_eq!(tx_log.status, PrivateTxStatus::Deployed); + } + + #[test] + fn serialization() { + let current_timestamp = SystemTime::now(); + let initial_validator_log = ValidatorLog { + account: "0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1".into(), + validation_timestamp: Some(current_timestamp.checked_add(Duration::from_secs(1)).unwrap()), + }; + let initial_log = TransactionLog { + tx_hash: "0x64f648ca7ae7f4138014f860ae56164d8d5732969b1cea54d8be9d144d8aa6f6".into(), + status: PrivateTxStatus::Deployed, + creation_timestamp: current_timestamp, + validators: vec![initial_validator_log], + deployment_timestamp: Some(current_timestamp.checked_add(Duration::from_secs(2)).unwrap()), + public_tx_hash: Some("0x69b9c691ede7993effbcc88911c309af1c82be67b04b3882dd446b808ae146da".into()), + }; + let serializer = Arc::new(StringLogSerializer::new(serde_json::to_string(&vec![initial_log.clone()]).unwrap())); + let logger = Logging::new(serializer.clone()); + let hash: H256 = "0x63c715e88f7291e66069302f6fcbb4f28a19ef5d7cbd1832d0c01e221c0061c6".into(); + logger.private_tx_created(&hash, &vec!["0x7ffbe3512782069be388f41be4d8eb350672d3a5".into()]); + logger.signature_added(&hash, &"0x7ffbe3512782069be388f41be4d8eb350672d3a5".into()); + logger.tx_deployed(&hash, &"0xde2209a8635b9cab9eceb67928b217c70ab53f6498e5144492ec01e6f43547d7".into()); + drop(logger); + let added_validator_log = ValidatorLog { + account: "0x7ffbe3512782069be388f41be4d8eb350672d3a5".into(), + validation_timestamp: Some(current_timestamp.checked_add(Duration::from_secs(7)).unwrap()), + }; + let added_log = TransactionLog { + tx_hash: "0x63c715e88f7291e66069302f6fcbb4f28a19ef5d7cbd1832d0c01e221c0061c6".into(), + status: PrivateTxStatus::Deployed, + creation_timestamp: current_timestamp.checked_add(Duration::from_secs(6)).unwrap(), + validators: vec![added_validator_log], + deployment_timestamp: Some(current_timestamp.checked_add(Duration::from_secs(8)).unwrap()), + public_tx_hash: Some("0xde2209a8635b9cab9eceb67928b217c70ab53f6498e5144492ec01e6f43547d7".into()), + }; + let should_be_final = vec![added_log, initial_log]; + let deserialized_logs: Vec = serde_json::from_str(&serializer.log()).unwrap(); + assert_eq!(deserialized_logs, should_be_final); + } +} \ No newline at end of file diff --git a/ethcore/private-tx/src/private_transactions.rs b/ethcore/private-tx/src/private_transactions.rs index d0456657b06..dd263ee1868 100644 --- a/ethcore/private-tx/src/private_transactions.rs +++ b/ethcore/private-tx/src/private_transactions.rs @@ -224,7 +224,7 @@ impl SigningStore { &mut self, private_hash: H256, transaction: SignedTransaction, - validators: Vec
, + validators: &Vec
, state: Bytes, contract_nonce: U256, ) -> Result<(), Error> { diff --git a/ethcore/private-tx/tests/private_contract.rs b/ethcore/private-tx/tests/private_contract.rs index 6365b10eecd..5d760fe0706 100644 --- a/ethcore/private-tx/tests/private_contract.rs +++ b/ethcore/private-tx/tests/private_contract.rs @@ -59,6 +59,7 @@ fn private_contract() { let config = ProviderConfig{ validator_accounts: vec![key3.address(), key4.address()], signer_account: None, + logs_path: None, }; let io = ethcore_io::IoChannel::disconnected(); @@ -193,6 +194,7 @@ fn call_other_private_contract() { let config = ProviderConfig{ validator_accounts: vec![key3.address(), key4.address()], signer_account: None, + logs_path: None, }; let io = ethcore_io::IoChannel::disconnected(); diff --git a/ethcore/sync/src/tests/private.rs b/ethcore/sync/src/tests/private.rs index 24de14d936d..b56f0ecc218 100644 --- a/ethcore/sync/src/tests/private.rs +++ b/ethcore/sync/src/tests/private.rs @@ -69,11 +69,13 @@ fn send_private_transaction() { let validator_config = ProviderConfig{ validator_accounts: vec![s1.address()], signer_account: None, + logs_path: None, }; let signer_config = ProviderConfig{ validator_accounts: Vec::new(), signer_account: Some(s0.address()), + logs_path: None, }; let private_keys = Arc::new(StoringKeyProvider::default()); diff --git a/parity/configuration.rs b/parity/configuration.rs index 10b0f3904d5..2f0c9df2cf3 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -914,9 +914,11 @@ impl Configuration { } fn private_provider_config(&self) -> Result<(ProviderConfig, EncryptorConfig, bool), String> { + let dirs = self.directories(); let provider_conf = ProviderConfig { validator_accounts: to_addresses(&self.args.arg_private_validators)?, signer_account: self.args.arg_private_signer.clone().and_then(|account| to_address(Some(account)).ok()), + logs_path: Some(dirs.base), }; let encryptor_conf = EncryptorConfig { @@ -1458,7 +1460,11 @@ mod tests { net_settings: Default::default(), ipfs_conf: Default::default(), secretstore_conf: Default::default(), - private_provider_conf: Default::default(), + private_provider_conf: ProviderConfig { + validator_accounts: Default::default(), + signer_account: Default::default(), + logs_path: Some(Directories::default().base), + }, private_encryptor_conf: Default::default(), private_tx_enabled: false, name: "".into(), diff --git a/rpc/src/v1/impls/private.rs b/rpc/src/v1/impls/private.rs index c3be3f91506..abd15500608 100644 --- a/rpc/src/v1/impls/private.rs +++ b/rpc/src/v1/impls/private.rs @@ -26,7 +26,8 @@ use types::transaction::SignedTransaction; use jsonrpc_core::{Error}; use v1::types::{Bytes, PrivateTransactionReceipt, TransactionRequest, - BlockNumber, PrivateTransactionReceiptAndTransaction, CallRequest, block_number_to_id}; + BlockNumber, PrivateTransactionReceiptAndTransaction, CallRequest, + block_number_to_id, PrivateTransactionLog}; use v1::traits::Private; use v1::metadata::Metadata; use v1::helpers::{errors, fake_sign}; @@ -119,4 +120,11 @@ impl Private for PrivateClient { let key = client.contract_key_id(&contract_address).map_err(errors::private_message)?; Ok(key) } + + fn private_log(&self, tx_hash: H256) -> Result { + self.unwrap_manager()? + .private_log(tx_hash) + .map_err(errors::private_message) + .map(Into::into) + } } diff --git a/rpc/src/v1/traits/private.rs b/rpc/src/v1/traits/private.rs index 732e3914bda..887e7126780 100644 --- a/rpc/src/v1/traits/private.rs +++ b/rpc/src/v1/traits/private.rs @@ -21,7 +21,7 @@ use jsonrpc_core::Error; use jsonrpc_derive::rpc; use v1::types::{Bytes, PrivateTransactionReceipt, BlockNumber, - PrivateTransactionReceiptAndTransaction, CallRequest}; + PrivateTransactionReceiptAndTransaction, CallRequest, PrivateTransactionLog}; /// Private transaction management RPC interface. #[rpc] @@ -44,4 +44,8 @@ pub trait Private { /// Retrieve the id of the key associated with the contract #[rpc(name = "private_contractKey")] fn private_contract_key(&self, H160) -> Result; + + /// Retrieve log information about private transaction + #[rpc(name = "private_log")] + fn private_log(&self, H256) -> Result; } diff --git a/rpc/src/v1/types/mod.rs b/rpc/src/v1/types/mod.rs index a41f49fab18..3518aea56ee 100644 --- a/rpc/src/v1/types/mod.rs +++ b/rpc/src/v1/types/mod.rs @@ -32,6 +32,8 @@ mod histogram; mod index; mod log; mod node_kind; +mod private_receipt; +mod private_log; mod provenance; mod receipt; mod rpc_settings; @@ -43,7 +45,6 @@ mod transaction; mod transaction_request; mod transaction_condition; mod work; -mod private_receipt; mod eip191; pub mod pubsub; @@ -65,6 +66,8 @@ pub use self::histogram::Histogram; pub use self::index::Index; pub use self::log::Log; pub use self::node_kind::{NodeKind, Availability, Capability}; +pub use self::private_receipt::{PrivateTransactionReceipt, PrivateTransactionReceiptAndTransaction}; +pub use self::private_log::PrivateTransactionLog; pub use self::provenance::Origin; pub use self::receipt::Receipt; pub use self::rpc_settings::RpcSettings; @@ -79,7 +82,6 @@ pub use self::transaction::{Transaction, RichRawTransaction, LocalTransactionSta pub use self::transaction_request::TransactionRequest; pub use self::transaction_condition::TransactionCondition; pub use self::work::Work; -pub use self::private_receipt::{PrivateTransactionReceipt, PrivateTransactionReceiptAndTransaction}; // TODO [ToDr] Refactor to a proper type Vec of enums? /// Expected tracing type. diff --git a/rpc/src/v1/types/private_log.rs b/rpc/src/v1/types/private_log.rs new file mode 100644 index 00000000000..2c90bcfa905 --- /dev/null +++ b/rpc/src/v1/types/private_log.rs @@ -0,0 +1,93 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::time::SystemTime; +use ethereum_types::{H160, H256}; +use ethcore_private_tx::{TransactionLog as EthTransactionLog, ValidatorLog as EthValidatorLog, PrivateTxStatus as EthStatus}; + +/// Current status of the private transaction +#[derive(Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub enum Status { + /// Private tx was created but no validation received yet + Created, + /// Several validators (but not all) validated the transaction + Validating, + /// All validators validated the private tx + /// Corresponding public tx was created and added into the pool + Deployed, +} + +impl From for Status { + fn from(c: EthStatus) -> Self { + match c { + EthStatus::Created => Status::Created, + EthStatus::Validating => Status::Validating, + EthStatus::Deployed => Status::Deployed, + } + } +} + +/// Information about private tx validation +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ValidatorLog { + /// Account of the validator + pub account: H160, + /// Validation timestamp, None, if the transaction is not validated yet + pub validation_timestamp: Option, +} + +impl From for ValidatorLog { + fn from(r: EthValidatorLog) -> Self { + ValidatorLog { + account: r.account, + validation_timestamp: r.validation_timestamp.map(|t| t.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_default().as_secs()), + } + } +} + +/// Information about the private transaction +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct PrivateTransactionLog { + /// Original signed transaction hash (used as a source for private tx) + pub tx_hash: H256, + /// Current status of the private transaction + pub status: Status, + /// Creation timestamp + pub creation_timestamp: u64, + /// List of validations + pub validators: Vec, + /// Timestamp of the resulting public tx deployment + pub deployment_timestamp: Option, + /// Hash of the resulting public tx + pub public_tx_hash: Option, +} + +impl From for PrivateTransactionLog { + fn from(r: EthTransactionLog) -> Self { + PrivateTransactionLog { + tx_hash: r.tx_hash, + status: r.status.into(), + creation_timestamp: r.creation_timestamp.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_default().as_secs(), + validators: r.validators.into_iter().map(Into::into).collect(), + deployment_timestamp: r.deployment_timestamp.map(|t| t.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_default().as_secs()), + public_tx_hash: r.public_tx_hash, + } + } +} + From 981ad0bc539e7d53d19e5005ae9b9c8ebab3de05 Mon Sep 17 00:00:00 2001 From: Luke Schoen Date: Tue, 14 May 2019 11:47:13 +0200 Subject: [PATCH 7/9] docs: Add PR template (#10654) --- PULL_REQUEST_TEMPLATE.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 PULL_REQUEST_TEMPLATE.md diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000000..f8ff2a969e9 --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,21 @@ +Thank you for your Pull Request! + +Before you submitting, please check that: + +- [ ] You added a brief description of the PR, e.g.: + - What does it do? + - What important points reviewers should know? + - Is there something left for follow-up PRs? +- [ ] You labeled the PR with appropriate labels if you have permissions to do so. +- [ ] You mentioned a related issue if this PR related to it, e.g. `Fixes #228` or `Related #1337`. +- [ ] You asked any particular reviewers to review. If you aren't sure, start with GH suggestions. +- [ ] Your PR adheres [the style guide](https://wiki.parity.io/Coding-guide) + - In particular, mind the maximal line length. + - There is no commented code checked in unless necessary. + - Any panickers have a proof or removed. +- [ ] You updated any rustdocs which may have changed + +After you've read this notice feel free to remove it. +Thank you! + +✄ ----------------------------------------------------------------------------- From 73f2ff38ece50c8c39ab88ce2c1ce8e776e9116d Mon Sep 17 00:00:00 2001 From: Luke Schoen Date: Tue, 14 May 2019 15:54:29 +0200 Subject: [PATCH 8/9] fix: Move PR template into .github/ folder (#10663) --- PULL_REQUEST_TEMPLATE.md => .github/PULL_REQUEST_TEMPLATE.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename PULL_REQUEST_TEMPLATE.md => .github/PULL_REQUEST_TEMPLATE.md (100%) diff --git a/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md similarity index 100% rename from PULL_REQUEST_TEMPLATE.md rename to .github/PULL_REQUEST_TEMPLATE.md From 412e2ce3a067244ac2bcd9dc23288cc2347872bb Mon Sep 17 00:00:00 2001 From: Luke Schoen Date: Wed, 15 May 2019 09:24:38 +0200 Subject: [PATCH 9/9] docs: Add ProgPoW Rust docs to ethash module (#10653) * docs: Add ProgPoW Rust docs to ethash module * review-fix: Remove unnecessary lines * fix: Fix incorrect naming of ProgPoW Co-Authored-By: Talha Cross <47772477+soc1c@users.noreply.github.com> --- ethash/src/progpow.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ethash/src/progpow.rs b/ethash/src/progpow.rs index 038f38c2259..7c7fb531aa7 100644 --- a/ethash/src/progpow.rs +++ b/ethash/src/progpow.rs @@ -14,6 +14,22 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +//! ProgPoW (Programmatic Proof-of-Work) is the Ethereum network's proposed new Application-Specific Integrated +//! Circuit (ASIC) resistant Proof-of-Work mining algorithm. +//! +//! ProgPoW's aim is to reduce the efficiencies of specialized mining devices known as ASIC chips +//! (and accelerated GPU-based setups), and to maximize the performance of General Purpose Hardware (GPUs) to enable +//! more users to compete for new cryptocurrency awarded by the protocol. +//! +//! ASIC chips are those that are tailored to efficiently mining cryptocurrency based on a specific hashing algorithm. +//! +//! GPU mining setups are less specialised are struggle to compete for mining rewards. +//! +//! It would be a change from Ethereum's current PoW mining algorithm known as Ethash. +//! +//! ProgPoW audits have been proposed to analyse the efficiency of a ProgPoW ASICs over +//! GPUs and analysis of the economic impact on the Ethereum protocol. + use compute::{FNV_PRIME, calculate_dag_item}; use keccak::H256; use shared::{ETHASH_ACCESSES, ETHASH_MIX_BYTES, Node, get_data_size};