2017-01-10 12:23:59 +01:00
// Copyright 2015, 2016 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 <http://www.gnu.org/licenses/>.
2017-01-24 10:03:58 +01:00
/// Validator set maintained in a contract, updated using `getValidators` method.
/// It can also report validators for misbehaviour with two levels: `reportMalicious` and `reportBenign`.
2017-01-10 12:23:59 +01:00
use std ::sync ::Weak ;
use util ::* ;
use client ::{ Client , BlockChainClient } ;
use super ::ValidatorSet ;
2017-01-24 10:03:58 +01:00
use super ::safe_contract ::ValidatorSafeContract ;
2017-01-10 12:23:59 +01:00
/// The validator contract should have the following interface:
/// [{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"}]
pub struct ValidatorContract {
2017-01-24 10:03:58 +01:00
validators : Arc < ValidatorSafeContract > ,
2017-01-10 12:23:59 +01:00
provider : RwLock < Option < provider ::Contract > > ,
}
impl ValidatorContract {
pub fn new ( contract_address : Address ) -> Self {
ValidatorContract {
2017-01-24 10:03:58 +01:00
validators : Arc ::new ( ValidatorSafeContract ::new ( contract_address ) ) ,
2017-01-10 12:23:59 +01:00
provider : RwLock ::new ( None ) ,
}
}
}
impl ValidatorSet for Arc < ValidatorContract > {
fn contains ( & self , address : & Address ) -> bool {
2017-01-24 10:03:58 +01:00
self . validators . contains ( address )
2017-01-10 12:23:59 +01:00
}
fn get ( & self , nonce : usize ) -> Address {
2017-01-24 10:03:58 +01:00
self . validators . get ( nonce )
2017-01-10 12:23:59 +01:00
}
fn count ( & self ) -> usize {
2017-01-24 10:03:58 +01:00
self . validators . count ( )
2017-01-10 12:23:59 +01:00
}
2017-01-24 10:03:58 +01:00
fn report_malicious ( & self , address : & Address ) {
if let Some ( ref provider ) = * self . provider . read ( ) {
match provider . report_malicious ( address ) {
Ok ( _ ) = > warn! ( target : " engine " , " Reported malicious validator {} " , address ) ,
Err ( s ) = > warn! ( target : " engine " , " Validator {} could not be reported {} " , address , s ) ,
}
} else {
warn! ( target : " engine " , " Malicious behaviour could not be reported: no provider contract. " )
2017-01-10 12:23:59 +01:00
}
2017-01-24 10:03:58 +01:00
}
fn report_benign ( & self , address : & Address ) {
if let Some ( ref provider ) = * self . provider . read ( ) {
match provider . report_benign ( address ) {
Ok ( _ ) = > warn! ( target : " engine " , " Reported benign validator misbehaviour {} " , address ) ,
Err ( s ) = > warn! ( target : " engine " , " Validator {} could not be reported {} " , address , s ) ,
}
} else {
warn! ( target : " engine " , " Benign misbehaviour could not be reported: no provider contract. " )
2017-01-10 12:23:59 +01:00
}
2017-01-24 10:03:58 +01:00
}
fn register_contract ( & self , client : Weak < Client > ) {
self . validators . register_contract ( client . clone ( ) ) ;
let transact = move | a , d | client
. upgrade ( )
. ok_or ( " No client! " . into ( ) )
. and_then ( | c | c . transact_contract ( a , d ) . map_err ( | e | format! ( " Transaction import error: {} " , e ) ) )
. map ( | _ | Default ::default ( ) ) ;
* self . provider . write ( ) = Some ( provider ::Contract ::new ( self . validators . address , transact ) ) ;
2017-01-10 12:23:59 +01:00
}
}
mod provider {
// Autogenerated from JSON contract definition using Rust contract convertor.
#![ allow(unused_imports) ]
use std ::string ::String ;
use std ::result ::Result ;
use std ::fmt ;
use { util , ethabi } ;
use util ::{ FixedHash , Uint } ;
pub struct Contract {
contract : ethabi ::Contract ,
address : util ::Address ,
do_call : Box < Fn ( util ::Address , Vec < u8 > ) -> Result < Vec < u8 > , String > + Send + Sync + 'static > ,
}
impl Contract {
pub fn new < F > ( address : util ::Address , do_call : F ) -> Self where F : Fn ( util ::Address , Vec < u8 > ) -> Result < Vec < u8 > , String > + Send + Sync + 'static {
Contract {
2017-01-24 10:03:58 +01:00
contract : ethabi ::Contract ::new ( ethabi ::Interface ::load ( b " [{ \" 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 \" }] " ) . expect ( " JSON is autogenerated; qed " ) ) ,
2017-01-10 12:23:59 +01:00
address : address ,
do_call : Box ::new ( do_call ) ,
}
}
fn as_string < T : fmt ::Debug > ( e : T ) -> String { format! ( " {:?} " , e ) }
2017-01-24 10:03:58 +01:00
/// Auto-generated from: `{"constant":false,"inputs":[{"name":"validator","type":"address"}],"name":"reportMalicious","outputs":[],"payable":false,"type":"function"}`
#[ allow(dead_code) ]
pub fn report_malicious ( & self , validator : & util ::Address ) -> Result < ( ) , String > {
let call = self . contract . function ( " reportMalicious " . into ( ) ) . map_err ( Self ::as_string ) ? ;
let data = call . encode_call (
vec! [ ethabi ::Token ::Address ( validator . clone ( ) . 0 ) ]
) . map_err ( Self ::as_string ) ? ;
call . decode_output ( ( self . do_call ) ( self . address . clone ( ) , data ) ? ) . map_err ( Self ::as_string ) ? ;
Ok ( ( ) )
}
2017-01-11 12:16:47 +01:00
2017-01-24 10:03:58 +01:00
/// Auto-generated from: `{"constant":false,"inputs":[{"name":"validator","type":"address"}],"name":"reportBenign","outputs":[],"payable":false,"type":"function"}`
2017-01-10 12:23:59 +01:00
#[ allow(dead_code) ]
2017-01-24 10:03:58 +01:00
pub fn report_benign ( & self , validator : & util ::Address ) -> Result < ( ) , String > {
let call = self . contract . function ( " reportBenign " . into ( ) ) . map_err ( Self ::as_string ) ? ;
2017-01-10 12:23:59 +01:00
let data = call . encode_call (
2017-01-24 10:03:58 +01:00
vec! [ ethabi ::Token ::Address ( validator . clone ( ) . 0 ) ]
2017-01-10 12:23:59 +01:00
) . map_err ( Self ::as_string ) ? ;
2017-01-24 10:03:58 +01:00
call . decode_output ( ( self . do_call ) ( self . address . clone ( ) , data ) ? ) . map_err ( Self ::as_string ) ? ;
Ok ( ( ) )
2017-01-10 12:23:59 +01:00
}
}
}
#[ cfg(test) ]
mod tests {
use util ::* ;
2017-01-24 10:03:58 +01:00
use rlp ::encode ;
use ethkey ::Secret ;
2017-01-10 12:23:59 +01:00
use spec ::Spec ;
2017-01-24 10:03:58 +01:00
use header ::Header ;
2017-01-10 12:23:59 +01:00
use account_provider ::AccountProvider ;
use miner ::MinerService ;
2017-01-24 10:03:58 +01:00
use client ::BlockChainClient ;
2017-01-18 18:49:50 +01:00
use tests ::helpers ::generate_dummy_client_with_spec_and_accounts ;
2017-01-10 12:23:59 +01:00
use super ::super ::ValidatorSet ;
use super ::ValidatorContract ;
#[ test ]
fn fetches_validators ( ) {
2017-01-18 18:49:50 +01:00
let client = generate_dummy_client_with_spec_and_accounts ( Spec ::new_validator_contract , None ) ;
2017-01-10 12:23:59 +01:00
let vc = Arc ::new ( ValidatorContract ::new ( Address ::from_str ( " 0000000000000000000000000000000000000005 " ) . unwrap ( ) ) ) ;
2017-01-24 10:03:58 +01:00
vc . register_contract ( Arc ::downgrade ( & client ) ) ;
2017-01-10 12:23:59 +01:00
assert! ( vc . contains ( & Address ::from_str ( " 7d577a597b2742b498cb5cf0c26cdcd726d39e6e " ) . unwrap ( ) ) ) ;
assert! ( vc . contains ( & Address ::from_str ( " 82a978b3f5962a5b0957d9ee9eef472ee55b42f1 " ) . unwrap ( ) ) ) ;
}
2017-01-24 10:03:58 +01:00
2017-01-10 12:23:59 +01:00
#[ test ]
2017-01-24 10:03:58 +01:00
fn reports_validators ( ) {
2017-01-10 12:23:59 +01:00
let tap = Arc ::new ( AccountProvider ::transient_provider ( ) ) ;
2017-01-24 10:03:58 +01:00
let v1 = tap . insert_account ( Secret ::from_slice ( & " 1 " . sha3 ( ) ) . unwrap ( ) , " " ) . unwrap ( ) ;
let client = generate_dummy_client_with_spec_and_accounts ( Spec ::new_validator_contract , Some ( tap . clone ( ) ) ) ;
2017-01-10 12:23:59 +01:00
client . engine ( ) . register_client ( Arc ::downgrade ( & client ) ) ;
let validator_contract = Address ::from_str ( " 0000000000000000000000000000000000000005 " ) . unwrap ( ) ;
2017-01-24 10:03:58 +01:00
// Make sure reporting can be done.
client . miner ( ) . set_gas_floor_target ( 1_000_000. into ( ) ) ;
2017-01-10 12:23:59 +01:00
client . miner ( ) . set_engine_signer ( v1 , " " . into ( ) ) . unwrap ( ) ;
2017-01-24 10:03:58 +01:00
let mut header = Header ::default ( ) ;
let seal = encode ( & vec! ( 5 u8 ) ) . to_vec ( ) ;
header . set_seal ( vec! ( seal ) ) ;
header . set_author ( v1 ) ;
header . set_number ( 1 ) ;
// `reportBenign` when the designated proposer releases block from the future (bad clock).
assert! ( client . engine ( ) . verify_block_unordered ( & header , None ) . is_err ( ) ) ;
// Seal a block.
client . engine ( ) . step ( ) ;
2017-01-10 12:23:59 +01:00
assert_eq! ( client . chain_info ( ) . best_block_number , 1 ) ;
2017-01-24 10:03:58 +01:00
// Check if the unresponsive validator is `disliked`.
assert_eq! ( client . call_contract ( validator_contract , " d8f2e0bf " . from_hex ( ) . unwrap ( ) ) . unwrap ( ) . to_hex ( ) , " 0000000000000000000000007d577a597b2742b498cb5cf0c26cdcd726d39e6e " ) ;
// Simulate a misbehaving validator by handling a double proposal.
assert! ( client . engine ( ) . verify_block_family ( & header , & header , None ) . is_err ( ) ) ;
// Seal a block.
client . engine ( ) . step ( ) ;
client . engine ( ) . step ( ) ;
assert_eq! ( client . chain_info ( ) . best_block_number , 2 ) ;
2017-01-10 12:23:59 +01:00
2017-01-24 10:03:58 +01:00
// Check if misbehaving validator was removed.
client . transact_contract ( Default ::default ( ) , Default ::default ( ) ) . unwrap ( ) ;
client . engine ( ) . step ( ) ;
client . engine ( ) . step ( ) ;
2017-01-10 12:23:59 +01:00
assert_eq! ( client . chain_info ( ) . best_block_number , 2 ) ;
}
}