diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1c2ad13d6..37e369382 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -62,7 +62,7 @@ linux-stable: name: "stable-x86_64-unknown-linux-gnu_parity" linux-stable-debian: stage: build - image: ethcore/rust-debian:latest + image: parity/rust-debian:gitlab-ci only: - beta - tags @@ -146,7 +146,7 @@ linux-nightly: allow_failure: true linux-centos: stage: build - image: ethcore/rust-centos:latest + image: parity/rust-centos:gitlab-ci only: - beta - tags @@ -177,7 +177,7 @@ linux-centos: name: "x86_64-unknown-centos-gnu_parity" linux-i686: stage: build - image: ethcore/rust-i686:latest + image: parity/rust-i686:gitlab-ci only: - beta - tags @@ -217,7 +217,7 @@ linux-i686: allow_failure: true linux-armv7: stage: build - image: ethcore/rust-armv7:latest + image: parity/rust-armv7:gitlab-ci only: - beta - tags @@ -263,7 +263,7 @@ linux-armv7: allow_failure: true linux-arm: stage: build - image: ethcore/rust-arm:latest + image: parity/rust-arm:gitlab-ci only: - beta - tags @@ -348,7 +348,7 @@ linux-armv6: allow_failure: true linux-aarch64: stage: build - image: ethcore/rust-aarch64:latest + image: parity/rust-arm64:gitlab-ci only: - beta - tags @@ -560,7 +560,7 @@ test-rust-stable: - rust-stable js-test: stage: test - image: ethcore/rust:stable + image: parity/rust:gitlab-ci before_script: - git submodule update --init --recursive - export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep ^js/ | wc -l) @@ -574,11 +574,12 @@ test-rust-beta: stage: test only: - triggers - image: ethcore/rust:beta + image: parity/rust:gitlab-ci before_script: - git submodule update --init --recursive - export RUST_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep -v -e ^js -e ^\\. -e ^LICENSE -e ^README.md -e ^appveyor.yml -e ^test.sh -e ^windows/ -e ^scripts/ -e^mac/ -e ^nsis/ | wc -l) script: + - rustup default beta - export RUST_BACKTRACE=1 - if [ $RUST_FILES_MODIFIED -eq 0 ]; then echo "Skipping Rust tests since no Rust files modified."; else ./test.sh $CARGOFLAGS; fi tags: @@ -589,11 +590,12 @@ test-rust-nightly: stage: test only: - triggers - image: ethcore/rust:nightly + image: parity/rust:gitlab-ci before_script: - git submodule update --init --recursive - export RUST_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep -v -e ^js -e ^\\. -e ^LICENSE -e ^README.md -e ^appveyor.yml -e ^test.sh -e ^windows/ -e ^scripts/ -e^mac/ -e ^nsis/ | wc -l) script: + - rustup default stable - export RUST_BACKTRACE=1 - if [ $RUST_FILES_MODIFIED -eq 0 ]; then echo "Skipping Rust tests since no Rust files modified."; else ./test.sh $CARGOFLAGS; fi tags: @@ -607,12 +609,13 @@ js-release: - beta - stable - tags - image: ethcore/rust:stable + image: parity/rust:gitlab-ci before_script: - export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep ^js/ | wc -l) - echo $JS_FILES_MODIFIED - if [ $JS_FILES_MODIFIED -eq 0 ]; then echo "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi script: + - rustup default stable - echo $JS_FILES_MODIFIED - if [ $JS_FILES_MODIFIED -eq 0 ]; then echo "Skipping JS rebuild since no JS files modified."; else ./js/scripts/build.sh && ./js/scripts/release.sh; fi tags: @@ -621,8 +624,9 @@ push-release: stage: push-release only: - tags - image: ethcore/rust:stable + image: parity/rust:gitlab-ci script: + - rustup default stable - curl --data "secret=$RELEASES_SECRET" http://update.parity.io:1337/push-release/$CI_BUILD_REF_NAME/$CI_BUILD_REF - curl --data "secret=$RELEASES_SECRET" http://update.parity.io:1338/push-release/$CI_BUILD_REF_NAME/$CI_BUILD_REF tags: diff --git a/Cargo.lock b/Cargo.lock index c68707117..583b85559 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1778,7 +1778,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/paritytech/js-precompiled.git#6ae27a1126a30b21bac014d8d2a5eaac0c38ba1f" +source = "git+https://github.com/paritytech/js-precompiled.git#b5581e7b303487d9696b62a70eb54b96bc76f590" dependencies = [ "parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/ethcore/native_contracts/build.rs b/ethcore/native_contracts/build.rs index 7da55a977..9c6fb85c4 100644 --- a/ethcore/native_contracts/build.rs +++ b/ethcore/native_contracts/build.rs @@ -31,7 +31,7 @@ const SECRETSTORE_ACL_STORAGE_ABI: &'static str = r#"[{"constant":true,"inputs": // changes. const VALIDATOR_SET_ABI: &'static str = r#"[{"constant":true,"inputs":[],"name":"transitionNonce","outputs":[{"name":"nonce","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"validators","type":"address[]"}],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_parent_hash","type":"bytes32"},{"indexed":true,"name":"_nonce","type":"uint256"},{"indexed":false,"name":"_new_set","type":"address[]"}],"name":"ValidatorsChanged","type":"event"}]"#; -const VALIDATOR_REPORT_ABI: &'static str = r#"[{"constant":false,"inputs":[{"name":"validator","type":"address"}],"name":"reportMalicious","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"validator","type":"address"}],"name":"reportBenign","outputs":[],"payable":false,"type":"function"}]"#; +const VALIDATOR_REPORT_ABI: &'static str = r#"[{"constant":false,"inputs":[{"name":"validator","type":"address"},{"name":"blockNumber","type":"uint256"},{"name":"proof","type":"bytes"}],"name":"reportMalicious","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"validator","type":"address"},{"name":"blockNumber","type":"uint256"}],"name":"reportBenign","outputs":[],"payable":false,"type":"function"}]"#; fn build_file(name: &str, abi: &str, filename: &str) { let code = ::native_contract_generator::generate_module(name, abi).unwrap(); diff --git a/ethcore/res/validator_contract.json b/ethcore/res/validator_contract.json index 6c2f87758..4ef27e30e 100644 --- a/ethcore/res/validator_contract.json +++ b/ethcore/res/validator_contract.json @@ -36,7 +36,7 @@ "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, "0000000000000000000000000000000000000005": { "balance": "1", - "constructor": "6060604052604060405190810160405280737d577a597b2742b498cb5cf0c26cdcd726d39e6e73ffffffffffffffffffffffffffffffffffffffff1681526020017382a978b3f5962a5b0957d9ee9eef472ee55b42f173ffffffffffffffffffffffffffffffffffffffff1681525060009060028280548282559060005260206000209081019282156100ec579160200282015b828111156100eb5782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555091602001919060010190610093565b5b50905061012f91905b8082111561012b57600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055506001016100f5565b5090565b505034610000575b6000600090505b6000805490508110156101d5578060016000600084815481101561000057906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b808060010191505061013e565b5b505b6105f2806101e76000396000f30060606040523615610076576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806335aa2e441461007b5780634d238c8e146100d8578063b7ab4db51461010b578063bfc708a01461017d578063d8f2e0bf146101b0578063fd6e1b50146101ff575b610000565b34610000576100966004808035906020019091905050610232565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3461000057610109600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061026f565b005b346100005761011861030f565b604051808060200182810382528381815181526020019150805190602001906020028083836000831461016a575b80518252602083111561016a57602082019150602081019050602083039250610146565b5050509050019250505060405180910390f35b34610000576101ae600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506103ad565b005b34610000576101bd61055b565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3461000057610230600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610581565b005b600081815481101561000057906000526020600020900160005b915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600080548060010182818154818355818115116102b8578183600052602060002091820191016102b791905b808211156102b357600081600090555060010161029b565b5090565b5b505050916000526020600020900160005b83909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505b50565b602060405190810160405280600081525060008054806020026020016040519081016040528092919081815260200182805480156103a257602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311610358575b505050505090505b90565b6000600160008054905003815481101561000057906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054815481101561000057906000526020600020900160005b6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600160008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600090556000600160008054905003815481101561000057906000526020600020900160005b6101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905560008054809190600190038154818355818115116105535781836000526020600020918201910161055291905b8082111561054e576000816000905550600101610536565b5090565b5b505050505b50565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b80600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b505600a165627a7a7230582063a0123d8e8f5dde980af6b47e20acc5b7a1acac3e3101fa1c933471ef4b405c0029" + "constructor": "60a06040819052737d577a597b2742b498cb5cf0c26cdcd726d39e6e60609081527382a978b3f5962a5b0957d9ee9eef472ee55b42f1608052600080546002825581805290927f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5639182019291905b828111156100a25782518254600160a060020a031916600160a060020a0390911617825560209092019160019091019061006d565b5b506100cd9291505b808211156100c9578054600160a060020a03191681556001016100ab565b5090565b505034610000575b60005b60005481101561012f578060016000600084815481101561000057906000526020600020900160005b9054600160a060020a036101009290920a90041681526020810191909152604001600020555b6001016100d8565b5b505b610453806101416000396000f3006060604052361561005c5763ffffffff60e060020a60003504166335aa2e4481146100615780634d238c8e1461008d578063b7ab4db5146100a8578063c476dd4014610110578063d69f13bb14610172578063d8f2e0bf14610190575b610000565b34610000576100716004356101b9565b60408051600160a060020a039092168252519081900360200190f35b34610000576100a6600160a060020a03600435166101e9565b005b34610000576100b5610260565b60408051602080825283518183015283519192839290830191858101910280838382156100fd575b8051825260208311156100fd57601f1990920191602091820191016100dd565b5050509050019250505060405180910390f35b3461000057604080516020600460443581810135601f81018490048402850184019095528484526100a6948235600160a060020a03169460248035956064949293919092019181908401838280828437509496506102ca95505050505050565b005b34610000576100a6600160a060020a03600435166024356103eb565b005b3461000057610071610418565b60408051600160a060020a039092168252519081900360200190f35b600081815481101561000057906000526020600020900160005b915054906101000a9004600160a060020a031681565b6000805480600101828181548183558181151161022b5760008381526020902061022b9181019083015b808211156102275760008155600101610213565b5090565b5b505050916000526020600020900160005b8154600160a060020a038086166101009390930a92830292021916179055505b50565b60408051602081810183526000808352805484518184028101840190955280855292939290918301828280156102bf57602002820191906000526020600020905b8154600160a060020a031681526001909101906020018083116102a1575b505050505090505b90565b6000805460001981019081101561000057906000526020600020900160005b9054906101000a9004600160a060020a031660006001600086600160a060020a0316600160a060020a0316815260200190815260200160002054815481101561000057906000526020600020900160005b8154600160a060020a039384166101009290920a918202918402191617905583166000908152600160205260408120819055805460001981019081101561000057906000526020600020900160005b6101000a815490600160a060020a03021916905560008054809190600190038154818355818115116103e0576000838152602090206103e09181019083015b808211156102275760008155600101610213565b5090565b5b505050505b505050565b6002805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0384161790555b5050565b600254600160a060020a0316815600a165627a7a72305820f7876e17abd5f0927fff16788b4b3c9028ed64e6db740d788b07fc5f0a8f10920029" }, "0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e": { "balance": "1606938044258990275541962092341162602522202993782792835301376" }, "0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1": { "balance": "1606938044258990275541962092341162602522202993782792835301376" } diff --git a/ethcore/src/account_db.rs b/ethcore/src/account_db.rs index 3db3e3c81..34dcb9760 100644 --- a/ethcore/src/account_db.rs +++ b/ethcore/src/account_db.rs @@ -78,6 +78,7 @@ pub struct AccountDB<'db> { impl<'db> AccountDB<'db> { /// Create a new AccountDB from an address. + #[cfg(test)] pub fn new(db: &'db HashDB, address: &Address) -> Self { Self::from_hash(db, address.sha3()) } @@ -131,6 +132,7 @@ pub struct AccountDBMut<'db> { impl<'db> AccountDBMut<'db> { /// Create a new AccountDB from an address. + #[cfg(test)] pub fn new(db: &'db mut HashDB, address: &Address) -> Self { Self::from_hash(db, address.sha3()) } @@ -143,7 +145,7 @@ impl<'db> AccountDBMut<'db> { } } - #[allow(dead_code)] + #[cfg(test)] pub fn immutable(&'db self) -> AccountDB<'db> { AccountDB { db: self.db, address_hash: self.address_hash.clone() } } diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 91719ccca..b80d68317 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -166,8 +166,7 @@ impl Client { db: Arc, miner: Arc, message_channel: IoChannel, - ) -> Result, ClientError> { - + ) -> Result, ::error::Error> { let trie_spec = match config.fat_db { true => TrieSpec::Fat, false => TrieSpec::Secure, diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index 937d6ccc4..d2ae144d5 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -160,7 +160,7 @@ fn verify_external(header: &Header, validators: &ValidatorSet, step: &Step) -> R // Give one step slack if step is lagging, double vote is still not possible. if step.is_future(header_step) { trace!(target: "engine", "verify_block_unordered: block from the future"); - validators.report_benign(header.author()); + validators.report_benign(header.author(), header.number()); Err(BlockError::InvalidSeal)? } else { let proposer_signature = header_signature(header)?; @@ -381,7 +381,7 @@ impl Engine for AuthorityRound { let parent_step = header_step(parent)?; if step <= parent_step { trace!(target: "engine", "Multiple blocks proposed for step {}.", parent_step); - self.validators.report_malicious(header.author()); + self.validators.report_malicious(header.author(), header.number(), Default::default()); Err(EngineError::DoubleVote(header.author().clone()))?; } diff --git a/ethcore/src/engines/tendermint/mod.rs b/ethcore/src/engines/tendermint/mod.rs index 65e11d19e..137f70a7a 100644 --- a/ethcore/src/engines/tendermint/mod.rs +++ b/ethcore/src/engines/tendermint/mod.rs @@ -471,8 +471,8 @@ impl Engine for Tendermint { return Err(EngineError::NotAuthorized(sender).into()); } self.broadcast_message(rlp.as_raw().to_vec()); - if self.votes.vote(message.clone(), &sender).is_some() { - self.validators.report_malicious(&sender); + if let Some(double) = self.votes.vote(message.clone(), &sender) { + self.validators.report_malicious(&sender, message.vote_step.height as BlockNumber, ::rlp::encode(&double).to_vec()); return Err(EngineError::DoubleVote(sender).into()); } trace!(target: "engine", "Handling a valid {:?} from {}.", message, sender); @@ -560,7 +560,7 @@ impl Engine for Tendermint { let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor; let max_gas = parent.gas_limit().clone() + parent.gas_limit().clone() / gas_limit_divisor; if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas { - self.validators.report_malicious(header.author()); + self.validators.report_malicious(header.author(), header.number(), Default::default()); return Err(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit().clone() }).into()); } @@ -610,8 +610,9 @@ impl Engine for Tendermint { trace!(target: "engine", "Propose timeout."); if self.proposal.read().is_none() { // Report the proposer if no proposal was received. - let current_proposer = self.view_proposer(&*self.proposal_parent.read(), self.height.load(AtomicOrdering::SeqCst), self.view.load(AtomicOrdering::SeqCst)); - self.validators.report_benign(¤t_proposer); + let height = self.height.load(AtomicOrdering::SeqCst); + let current_proposer = self.view_proposer(&*self.proposal_parent.read(), height, self.view.load(AtomicOrdering::SeqCst)); + self.validators.report_benign(¤t_proposer, height as BlockNumber); } Step::Prevote }, diff --git a/ethcore/src/engines/validator_set/contract.rs b/ethcore/src/engines/validator_set/contract.rs index 22506c348..dd4623023 100644 --- a/ethcore/src/engines/validator_set/contract.rs +++ b/ethcore/src/engines/validator_set/contract.rs @@ -25,7 +25,7 @@ use native_contracts::ValidatorReport as Provider; use client::{Client, BlockChainClient}; use engines::Call; -use header::Header; +use header::{Header, BlockNumber}; use super::ValidatorSet; use super::safe_contract::ValidatorSafeContract; @@ -92,15 +92,15 @@ impl ValidatorSet for ValidatorContract { self.validators.count_with_caller(bh, caller) } - fn report_malicious(&self, address: &Address) { - match self.provider.report_malicious(&*self.transact(), *address).wait() { + fn report_malicious(&self, address: &Address, block: BlockNumber, proof: Bytes) { + match self.provider.report_malicious(&*self.transact(), *address, block.into(), proof).wait() { Ok(_) => warn!(target: "engine", "Reported malicious validator {}", address), Err(s) => warn!(target: "engine", "Validator {} could not be reported {}", address, s), } } - fn report_benign(&self, address: &Address) { - match self.provider.report_benign(&*self.transact(), *address).wait() { + fn report_benign(&self, address: &Address, block: BlockNumber) { + match self.provider.report_benign(&*self.transact(), *address, block.into()).wait() { Ok(_) => warn!(target: "engine", "Reported benign validator misbehaviour {}", address), Err(s) => warn!(target: "engine", "Validator {} could not be reported {}", address, s), } diff --git a/ethcore/src/engines/validator_set/mod.rs b/ethcore/src/engines/validator_set/mod.rs index a7e0f196f..f0765db5d 100644 --- a/ethcore/src/engines/validator_set/mod.rs +++ b/ethcore/src/engines/validator_set/mod.rs @@ -23,10 +23,10 @@ mod multi; use std::sync::Weak; use ids::BlockId; -use util::{Address, H256}; +use util::{Bytes, Address, H256}; use ethjson::spec::ValidatorSet as ValidatorSpec; use client::Client; -use header::Header; +use header::{Header, BlockNumber}; pub use self::simple_list::SimpleList; use self::contract::ValidatorContract; @@ -111,9 +111,9 @@ pub trait ValidatorSet: Send + Sync { fn count_with_caller(&self, parent_block_hash: &H256, caller: &Call) -> usize; /// Notifies about malicious behaviour. - fn report_malicious(&self, _validator: &Address) {} + fn report_malicious(&self, _validator: &Address, _block: BlockNumber, _proof: Bytes) {} /// Notifies about benign misbehaviour. - fn report_benign(&self, _validator: &Address) {} + fn report_benign(&self, _validator: &Address, _block: BlockNumber) {} /// Allows blockchain state access. fn register_contract(&self, _client: Weak) {} } diff --git a/ethcore/src/engines/validator_set/multi.rs b/ethcore/src/engines/validator_set/multi.rs index abb034698..df3659ac3 100644 --- a/ethcore/src/engines/validator_set/multi.rs +++ b/ethcore/src/engines/validator_set/multi.rs @@ -19,7 +19,7 @@ use std::collections::BTreeMap; use std::sync::Weak; use engines::{Call, EpochChange}; -use util::{H256, Address, RwLock}; +use util::{Bytes, H256, Address, RwLock}; use ids::BlockId; use header::{BlockNumber, Header}; use client::{Client, BlockChainClient}; @@ -110,16 +110,12 @@ impl ValidatorSet for Multi { .map_or_else(usize::max_value, |set| set.count_with_caller(bh, caller)) } - fn report_malicious(&self, validator: &Address) { - for set in self.sets.values() { - set.report_malicious(validator); - } + fn report_malicious(&self, validator: &Address, block: BlockNumber, proof: Bytes) { + self.correct_set_by_number(block).1.report_malicious(validator, block, proof); } - fn report_benign(&self, validator: &Address) { - for set in self.sets.values() { - set.report_benign(validator); - } + fn report_benign(&self, validator: &Address, block: BlockNumber) { + self.correct_set_by_number(block).1.report_benign(validator, block); } fn register_contract(&self, client: Weak) { diff --git a/ethcore/src/engines/vote_collector.rs b/ethcore/src/engines/vote_collector.rs index 482dd1d4b..d01d07f15 100644 --- a/ethcore/src/engines/vote_collector.rs +++ b/ethcore/src/engines/vote_collector.rs @@ -18,7 +18,7 @@ use std::fmt::Debug; use util::*; -use rlp::Encodable; +use rlp::{Encodable, RlpStream}; pub trait Message: Clone + PartialEq + Eq + Hash + Encodable + Debug { type Round: Clone + PartialEq + Eq + Hash + Default + Debug + Ord; @@ -40,25 +40,44 @@ pub struct VoteCollector { #[derive(Debug, Default)] struct StepCollector { - voted: HashSet
, + voted: HashMap, pub block_votes: HashMap, HashMap>, messages: HashSet, } +#[derive(Debug)] +pub struct DoubleVote<'a, M: Message> { + pub author: &'a Address, + vote_one: M, + vote_two: M, +} + +impl<'a, M: Message> Encodable for DoubleVote<'a, M> { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(2) + .append(&self.vote_one) + .append(&self.vote_two); + } +} + impl StepCollector { /// Returns Some(&Address) when validator is double voting. - fn insert<'a>(&mut self, message: M, address: &'a Address) -> Option<&'a Address> { + fn insert<'a>(&mut self, message: M, address: &'a Address) -> Option> { // Do nothing when message was seen. if self.messages.insert(message.clone()) { - if self.voted.insert(address.clone()) { + if let Some(previous) = self.voted.insert(address.clone(), message.clone()) { + // Bad validator sent a different message. + return Some(DoubleVote { + author: address, + vote_one: previous, + vote_two: message + }); + } else { self .block_votes .entry(message.block_hash()) .or_insert_with(HashMap::new) .insert(message.signature(), address.clone()); - } else { - // Bad validator sent a different message. - return Some(address); } } None @@ -101,7 +120,7 @@ impl Default for VoteCollector { impl VoteCollector { /// Insert vote if it is newer than the oldest one. - pub fn vote<'a>(&self, message: M, voter: &'a Address) -> Option<&'a Address> { + pub fn vote<'a>(&self, message: M, voter: &'a Address) -> Option> { self .votes .write() @@ -220,11 +239,11 @@ mod tests { } fn random_vote(collector: &VoteCollector, signature: H520, step: TestStep, block_hash: Option) -> bool { - full_vote(collector, signature, step, block_hash, &H160::random()).is_none() + full_vote(collector, signature, step, block_hash, &H160::random()) } - fn full_vote<'a>(collector: &VoteCollector, signature: H520, step: TestStep, block_hash: Option, address: &'a Address) -> Option<&'a Address> { - collector.vote(TestMessage { signature: signature, step: step, block_hash: block_hash }, address) + fn full_vote<'a>(collector: &VoteCollector, signature: H520, step: TestStep, block_hash: Option, address: &'a Address) -> bool { + collector.vote(TestMessage { signature: signature, step: step, block_hash: block_hash }, address).is_none() } #[test] @@ -319,9 +338,9 @@ mod tests { let collector = VoteCollector::default(); let round = 3; // Vote is inserted fine. - assert!(full_vote(&collector, H520::random(), round, Some("0".sha3()), &Address::default()).is_none()); + assert!(full_vote(&collector, H520::random(), round, Some("0".sha3()), &Address::default())); // Returns the double voting address. - full_vote(&collector, H520::random(), round, Some("1".sha3()), &Address::default()).unwrap(); + assert!(!full_vote(&collector, H520::random(), round, Some("1".sha3()), &Address::default())); assert_eq!(collector.count_round_votes(&round), 1); } } diff --git a/ethcore/src/ethereum/mod.rs b/ethcore/src/ethereum/mod.rs index b54401572..af0582a36 100644 --- a/ethcore/src/ethereum/mod.rs +++ b/ethcore/src/ethereum/mod.rs @@ -95,6 +95,7 @@ mod tests { let engine = &spec.engine; let genesis_header = spec.genesis_header(); let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); + let s = State::from_existing(db, genesis_header.state_root().clone(), engine.account_start_nonce(), Default::default()).unwrap(); assert_eq!(s.balance(&"0000000000000000000000000000000000000001".into()).unwrap(), 1u64.into()); assert_eq!(s.balance(&"0000000000000000000000000000000000000002".into()).unwrap(), 1u64.into()); diff --git a/ethcore/src/json_tests/chain.rs b/ethcore/src/json_tests/chain.rs index 45a4f1d52..ccdd7d499 100644 --- a/ethcore/src/json_tests/chain.rs +++ b/ethcore/src/json_tests/chain.rs @@ -51,7 +51,7 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec { ChainEra::_Eip161 => ethereum::new_eip161_test(), ChainEra::TransitionTest => ethereum::new_transition_test(), }; - spec.set_genesis_state(state); + spec.set_genesis_state(state).expect("Failed to overwrite genesis state"); spec.overwrite_genesis_params(genesis); assert!(spec.is_state_root_valid()); spec diff --git a/ethcore/src/pod_account.rs b/ethcore/src/pod_account.rs index 4b5dc8921..984f37bd8 100644 --- a/ethcore/src/pod_account.rs +++ b/ethcore/src/pod_account.rs @@ -16,7 +16,6 @@ use util::*; use state::Account; -use account_db::AccountDBMut; use ethjson; use types::account_diff::*; use rlp::{self, RlpStream}; @@ -64,7 +63,7 @@ impl PodAccount { } /// Place additional data into given hash DB. - pub fn insert_additional(&self, db: &mut AccountDBMut, factory: &TrieFactory) { + pub fn insert_additional(&self, db: &mut HashDB, factory: &TrieFactory) { match self.code { Some(ref c) if !c.is_empty() => { db.insert(c); } _ => {} diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index c258d89cb..efbfc435d 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -16,25 +16,27 @@ //! Parameters for a block chain. -use util::*; -use builtin::Builtin; -use engines::{Engine, NullEngine, InstantSeal, BasicAuthority, AuthorityRound, Tendermint}; -use factory::Factories; -use executive::Executive; -use trace::{NoopTracer, NoopVMTracer}; -use action_params::{ActionValue, ActionParams}; -use types::executed::CallType; -use state::{Backend, State, Substate}; -use env_info::EnvInfo; -use pod_state::*; -use account_db::*; -use header::{BlockNumber, Header}; -use state_db::StateDB; use super::genesis::Genesis; use super::seal::Generic as GenericSeal; + +use action_params::{ActionValue, ActionParams}; +use builtin::Builtin; +use engines::{Engine, NullEngine, InstantSeal, BasicAuthority, AuthorityRound, Tendermint}; +use env_info::EnvInfo; +use error::Error; use ethereum; use ethjson; +use executive::Executive; +use factory::Factories; +use header::{BlockNumber, Header}; +use pod_state::*; use rlp::{Rlp, RlpStream}; +use state_db::StateDB; +use state::{Backend, State, Substate}; +use state::backend::Basic as BasicBackend; +use trace::{NoopTracer, NoopVMTracer}; +use types::executed::CallType; +use util::*; /// Parameters common to all engines. #[derive(Debug, PartialEq, Clone, Default)] @@ -119,39 +121,46 @@ pub struct Spec { constructors: Vec<(Address, Bytes)>, /// May be prepopulated if we know this in advance. - state_root_memo: RwLock>, + state_root_memo: RwLock, /// Genesis state as plain old data. genesis_state: PodState, } -impl From for Spec { - fn from(s: ethjson::spec::Spec) -> Self { - let builtins = s.accounts.builtins().into_iter().map(|p| (p.0.into(), From::from(p.1))).collect(); - let g = Genesis::from(s.genesis); - let GenericSeal(seal_rlp) = g.seal.into(); - let params = CommonParams::from(s.params); - Spec { - name: s.name.clone().into(), - params: params.clone(), - engine: Spec::engine(s.engine, params, builtins), - data_dir: s.data_dir.unwrap_or(s.name).into(), - nodes: s.nodes.unwrap_or_else(Vec::new), - parent_hash: g.parent_hash, - transactions_root: g.transactions_root, - receipts_root: g.receipts_root, - author: g.author, - difficulty: g.difficulty, - gas_limit: g.gas_limit, - gas_used: g.gas_used, - timestamp: g.timestamp, - extra_data: g.extra_data, - seal_rlp: seal_rlp, - constructors: s.accounts.constructors().into_iter().map(|(a, c)| (a.into(), c.into())).collect(), - state_root_memo: RwLock::new(g.state_root), - genesis_state: From::from(s.accounts), - } +fn load_from(s: ethjson::spec::Spec) -> Result { + let builtins = s.accounts.builtins().into_iter().map(|p| (p.0.into(), From::from(p.1))).collect(); + let g = Genesis::from(s.genesis); + let GenericSeal(seal_rlp) = g.seal.into(); + let params = CommonParams::from(s.params); + + let mut s = Spec { + name: s.name.clone().into(), + params: params.clone(), + engine: Spec::engine(s.engine, params, builtins), + data_dir: s.data_dir.unwrap_or(s.name).into(), + nodes: s.nodes.unwrap_or_else(Vec::new), + parent_hash: g.parent_hash, + transactions_root: g.transactions_root, + receipts_root: g.receipts_root, + author: g.author, + difficulty: g.difficulty, + gas_limit: g.gas_limit, + gas_used: g.gas_used, + timestamp: g.timestamp, + extra_data: g.extra_data, + seal_rlp: seal_rlp, + constructors: s.accounts.constructors().into_iter().map(|(a, c)| (a.into(), c.into())).collect(), + state_root_memo: RwLock::new(Default::default()), // will be overwritten right after. + genesis_state: s.accounts.into(), + }; + + // use memoized state root if provided. + match g.state_root { + Some(root) => *s.state_root_memo.get_mut() = root, + None => { let _ = s.run_constructors(&Default::default(), BasicBackend(MemoryDB::new()))?; }, } + + Ok(s) } macro_rules! load_bundled { @@ -174,13 +183,93 @@ impl Spec { } } + // given a pre-constructor state, run all the given constructors and produce a new state and state root. + fn run_constructors(&self, factories: &Factories, mut db: T) -> Result { + let mut root = SHA3_NULL_RLP; + + // basic accounts in spec. + { + let mut t = factories.trie.create(db.as_hashdb_mut(), &mut root); + + for (address, account) in self.genesis_state.get().iter() { + t.insert(&**address, &account.rlp())?; + } + } + + for (address, account) in self.genesis_state.get().iter() { + db.note_non_null_account(address); + account.insert_additional( + &mut *factories.accountdb.create(db.as_hashdb_mut(), address.sha3()), + &factories.trie + ); + } + + let start_nonce = self.engine.account_start_nonce(); + + let (root, db) = { + let mut state = State::from_existing( + db, + root, + start_nonce, + factories.clone(), + )?; + + // Execute contract constructors. + let env_info = EnvInfo { + number: 0, + author: self.author, + timestamp: self.timestamp, + difficulty: self.difficulty, + last_hashes: Default::default(), + gas_used: U256::zero(), + gas_limit: U256::max_value(), + }; + + let from = Address::default(); + for &(ref address, ref constructor) in self.constructors.iter() { + trace!(target: "spec", "run_constructors: Creating a contract at {}.", address); + trace!(target: "spec", " .. root before = {}", state.root()); + let params = ActionParams { + code_address: address.clone(), + code_hash: constructor.sha3(), + address: address.clone(), + sender: from.clone(), + origin: from.clone(), + gas: U256::max_value(), + gas_price: Default::default(), + value: ActionValue::Transfer(Default::default()), + code: Some(Arc::new(constructor.clone())), + data: None, + call_type: CallType::None, + }; + + let mut substate = Substate::new(); + state.kill_account(&address); + + { + let mut exec = Executive::new(&mut state, &env_info, self.engine.as_ref(), &factories.vm); + if let Err(e) = exec.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) { + warn!(target: "spec", "Genesis constructor execution at {} failed: {}.", address, e); + } + } + + if let Err(e) = state.commit() { + warn!(target: "spec", "Genesis constructor trie commit at {} failed: {}.", address, e); + } + + trace!(target: "spec", " .. root after = {}", state.root()); + } + + state.drop() + }; + + *self.state_root_memo.write() = root; + Ok(db) + } + /// Return the state root for the genesis state, memoising accordingly. pub fn state_root(&self) -> H256 { - if self.state_root_memo.read().is_none() { - *self.state_root_memo.write() = Some(self.genesis_state.root()); - } - self.state_root_memo.read().as_ref().cloned() - .expect("state root memo ensured to be set at this point; qed") + self.state_root_memo.read().clone() } /// Get the known knodes of the network in enode format. @@ -243,95 +332,46 @@ impl Spec { self.timestamp = g.timestamp; self.extra_data = g.extra_data; self.seal_rlp = seal_rlp; - self.state_root_memo = RwLock::new(g.state_root); } /// Alter the value of the genesis state. - pub fn set_genesis_state(&mut self, s: PodState) { + pub fn set_genesis_state(&mut self, s: PodState) -> Result<(), Error> { self.genesis_state = s; - *self.state_root_memo.write() = None; + let _ = self.run_constructors(&Default::default(), BasicBackend(MemoryDB::new()))?; + + Ok(()) } /// Returns `false` if the memoized state root is invalid. `true` otherwise. pub fn is_state_root_valid(&self) -> bool { - self.state_root_memo.read().clone().map_or(true, |sr| sr == self.genesis_state.root()) + // TODO: get rid of this function and ensure state root always is valid. + // we're mostly there, but `self.genesis_state.root()` doesn't encompass + // post-constructor state. + *self.state_root_memo.read() == self.genesis_state.root() } /// Ensure that the given state DB has the trie nodes in for the genesis state. - pub fn ensure_db_good(&self, mut db: StateDB, factories: &Factories) -> Result> { + pub fn ensure_db_good(&self, db: StateDB, factories: &Factories) -> Result { if db.as_hashdb().contains(&self.state_root()) { return Ok(db) } - trace!(target: "spec", "ensure_db_good: Fresh database? Cannot find state root {}", self.state_root()); - let mut root = H256::new(); - { - let mut t = factories.trie.create(db.as_hashdb_mut(), &mut root); - for (address, account) in self.genesis_state.get().iter() { - t.insert(&**address, &account.rlp())?; - } - } + // TODO: could optimize so we don't re-run, but `ensure_db_good` is barely ever + // called anyway. + let db = self.run_constructors(factories, db)?; - trace!(target: "spec", "ensure_db_good: Populated sec trie; root is {}", root); - for (address, account) in self.genesis_state.get().iter() { - db.note_non_null_account(address); - account.insert_additional(&mut AccountDBMut::new(db.as_hashdb_mut(), address), &factories.trie); - } - - // Execute contract constructors. - let env_info = EnvInfo { - number: 0, - author: self.author, - timestamp: self.timestamp, - difficulty: self.difficulty, - last_hashes: Default::default(), - gas_used: U256::zero(), - gas_limit: U256::max_value(), - }; - let from = Address::default(); - let start_nonce = self.engine.account_start_nonce(); - - let mut state = State::from_existing(db, root, start_nonce, factories.clone())?; - // Mutate the state with each constructor. - for &(ref address, ref constructor) in self.constructors.iter() { - trace!(target: "spec", "ensure_db_good: Creating a contract at {}.", address); - let params = ActionParams { - code_address: address.clone(), - code_hash: constructor.sha3(), - address: address.clone(), - sender: from.clone(), - origin: from.clone(), - gas: U256::max_value(), - gas_price: Default::default(), - value: ActionValue::Transfer(Default::default()), - code: Some(Arc::new(constructor.clone())), - data: None, - call_type: CallType::None, - }; - let mut substate = Substate::new(); - state.kill_account(address); - { - let mut exec = Executive::new(&mut state, &env_info, self.engine.as_ref(), &factories.vm); - if let Err(e) = exec.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) { - warn!(target: "spec", "Genesis constructor execution at {} failed: {}.", address, e); - } - } - if let Err(e) = state.commit() { - warn!(target: "spec", "Genesis constructor trie commit at {} failed: {}.", address, e); - } - } - let (root, db) = state.drop(); - - *self.state_root_memo.write() = Some(root); Ok(db) } - /// Loads spec from json file. + /// Loads spec from json file. Provide factories for executing contracts and ensuring + /// storage goes to the right place. pub fn load(reader: R) -> Result where R: Read { - match ethjson::spec::Spec::load(reader) { - Ok(spec) => Ok(spec.into()), - Err(e) => Err(format!("Spec json is invalid: {}", e)), + fn fmt(f: F) -> String { + format!("Spec json is invalid: {}", f) } + + ethjson::spec::Spec::load(reader).map_err(fmt) + .and_then(|x| load_from(x).map_err(fmt)) } /// Create a new Spec which conforms to the Frontier-era Morden chain except that it's a NullEngine consensus. diff --git a/ethcore/src/state/backend.rs b/ethcore/src/state/backend.rs index 5ab620b0e..ea172b5af 100644 --- a/ethcore/src/state/backend.rs +++ b/ethcore/src/state/backend.rs @@ -206,7 +206,8 @@ impl Proving { } } - /// Consume the backend, extracting the gathered proof. + /// Consume the backend, extracting the gathered proof in lexicographical order + /// by value. pub fn extract_proof(self) -> Vec { self.proof.into_inner().into_iter().collect() } @@ -221,3 +222,33 @@ impl Clone for Proving { } } } + +/// A basic backend. Just wraps the given database, directly inserting into and deleting from +/// it. Doesn't cache anything. +pub struct Basic(pub H); + +impl Backend for Basic { + fn as_hashdb(&self) -> &HashDB { + self.0.as_hashdb() + } + + fn as_hashdb_mut(&mut self) -> &mut HashDB { + self.0.as_hashdb_mut() + } + + fn add_to_account_cache(&mut self, _: Address, _: Option, _: bool) { } + + fn cache_code(&self, _: H256, _: Arc>) { } + + fn get_cached_account(&self, _: &Address) -> Option> { None } + + fn get_cached(&self, _: &Address, _: F) -> Option + where F: FnOnce(Option<&mut Account>) -> U + { + None + } + + fn get_cached_code(&self, _: &H256) -> Option>> { None } + fn note_non_null_account(&self, _: &Address) { } + fn is_known_null(&self, _: &Address) -> bool { false } +} diff --git a/ethcore/src/state_db.rs b/ethcore/src/state_db.rs index 6bf8bbd8e..a5823f50b 100644 --- a/ethcore/src/state_db.rs +++ b/ethcore/src/state_db.rs @@ -448,7 +448,8 @@ impl state::Backend for StateDB { fn is_known_null(&self, address: &Address) -> bool { trace!(target: "account_bloom", "Check account bloom: {:?}", address); let bloom = self.account_bloom.lock(); - !bloom.check(&*address.sha3()) + let is_null = !bloom.check(&*address.sha3()); + is_null } } diff --git a/js/package.json b/js/package.json index d5f283541..d8657737b 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "1.7.63", + "version": "1.7.64", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", diff --git a/js/src/abi/decoder/decoder.js b/js/src/abi/decoder/decoder.js index 9cb07e3d7..6922c91e8 100644 --- a/js/src/abi/decoder/decoder.js +++ b/js/src/abi/decoder/decoder.js @@ -113,7 +113,15 @@ export default class Decoder { const str = taken.bytes.map((code) => String.fromCharCode(code)).join(''); - return new DecodeResult(new Token(param.type, utf8.decode(str)), offset + 1); + let decoded; + + try { + decoded = utf8.decode(str); + } catch (error) { + decoded = str; + } + + return new DecodeResult(new Token(param.type, decoded), offset + 1); case 'array': lengthOffset = asU32(Decoder.peek(slices, offset)).div(32).toNumber(); diff --git a/js/src/abi/decoder/decoder.spec.js b/js/src/abi/decoder/decoder.spec.js index 1f27a3e65..5065be3a7 100644 --- a/js/src/abi/decoder/decoder.spec.js +++ b/js/src/abi/decoder/decoder.spec.js @@ -38,6 +38,7 @@ describe('abi/decoder/Decoder', () => { const int1 = '0111111111111111111111111111111111111111111111111111111111111111'; const intn = 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85'; const string1 = '6761766f66796f726b0000000000000000000000000000000000000000000000'; + const string2 = '4665726ee16e64657a0000000000000000000000000000000000000000000000'; const tokenAddress1 = new Token('address', `0x${address1.slice(-40)}`); const tokenAddress2 = new Token('address', `0x${address2.slice(-40)}`); const tokenAddress3 = new Token('address', `0x${address3.slice(-40)}`); @@ -53,6 +54,7 @@ describe('abi/decoder/Decoder', () => { const tokenUint1 = new Token('uint', new BigNumber(int1, 16)); const tokenUintn = new Token('uint', new BigNumber(intn, 16)); const tokenString1 = new Token('string', 'gavofyork'); + const tokenString2 = new Token('string', 'Fernández'); const slices = [ address1, address2, address3, address4 ]; describe('peek', () => { @@ -160,6 +162,12 @@ describe('abi/decoder/Decoder', () => { ).to.deep.equal(tokenString1); }); + it('decodes utf8-invalid string', () => { + expect( + Decoder.decodeParam(new ParamType('string'), [padU32(0x20), padU32(9), string2], 0).token + ).to.deep.equal(tokenString2); + }); + it('decodes string (indexed)', () => { expect( Decoder.decodeParam(new ParamType('string', null, 0, true), [bytes1], 0) diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index e0df518f7..c2093a51b 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -83,7 +83,7 @@ fn make_spec(chain: &BlockChain) -> Spec { let genesis = Genesis::from(chain.genesis()); let mut spec = ethereum::new_frontier_test(); let state = chain.pre_state.clone().into(); - spec.set_genesis_state(state); + spec.set_genesis_state(state).expect("unable to set genesis state"); spec.overwrite_genesis_params(genesis); assert!(spec.is_state_root_valid()); spec