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/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); } }