Validator/authority contract (#3937)

* dir

* simple validator list

* stub validator contract

* make the engine hold Weak<Client> instead of IoChannel

* validator set factory

* register weak client with ValidatorContract

* check chain security

* add address array to generator

* register provider contract

* update validator set on notify

* add validator contract spec

* simple list test

* split update and contract test

* contract change

* use client in tendermint

* fix deadlock

* step duration in params

* adapt tendermint tests

* add storage fields to test spec

* constructor spec

* execute under wrong address

* create under correct address

* revert

* validator contract constructor

* move genesis block lookup

* add removal ability to contract

* validator contract adding validators

* fix basic authority

* validator changing test

* more docs

* update sync tests

* remove env_logger

* another env_logger

* cameltoe

* hold EngineClient instead of Client

* add a comment about lock scope
This commit is contained in:
keorn
2017-01-10 12:23:59 +01:00
committed by Arkadiy Paronyan
parent 5c5244911e
commit be30c44179
29 changed files with 749 additions and 314 deletions

View File

@@ -16,6 +16,8 @@
//! A blockchain engine that supports a basic, non-BFT proof-of-authority.
use std::sync::Weak;
use util::*;
use ethkey::{recover, public_to_address};
use account_provider::AccountProvider;
use block::*;
@@ -28,26 +30,23 @@ use evm::Schedule;
use ethjson;
use header::Header;
use transaction::SignedTransaction;
use util::*;
use client::Client;
use super::validator_set::{ValidatorSet, new_validator_set};
/// `BasicAuthority` params.
#[derive(Debug, PartialEq)]
pub struct BasicAuthorityParams {
/// Gas limit divisor.
pub gas_limit_bound_divisor: U256,
/// Block duration.
pub duration_limit: u64,
/// Valid signatories.
pub authorities: HashSet<Address>,
pub validators: ethjson::spec::ValidatorSet,
}
impl From<ethjson::spec::BasicAuthorityParams> for BasicAuthorityParams {
fn from(p: ethjson::spec::BasicAuthorityParams) -> Self {
BasicAuthorityParams {
gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(),
duration_limit: p.duration_limit.into(),
authorities: p.authorities.into_iter().map(Into::into).collect::<HashSet<_>>(),
validators: p.validators,
}
}
}
@@ -56,10 +55,11 @@ impl From<ethjson::spec::BasicAuthorityParams> for BasicAuthorityParams {
/// mainnet chains in the Olympic, Frontier and Homestead eras.
pub struct BasicAuthority {
params: CommonParams,
our_params: BasicAuthorityParams,
gas_limit_bound_divisor: U256,
builtins: BTreeMap<Address, Builtin>,
account_provider: Mutex<Option<Arc<AccountProvider>>>,
password: RwLock<Option<String>>,
validators: Box<ValidatorSet + Send + Sync>,
}
impl BasicAuthority {
@@ -67,8 +67,9 @@ impl BasicAuthority {
pub fn new(params: CommonParams, our_params: BasicAuthorityParams, builtins: BTreeMap<Address, Builtin>) -> Self {
BasicAuthority {
params: params,
our_params: our_params,
gas_limit_bound_divisor: our_params.gas_limit_bound_divisor,
builtins: builtins,
validators: new_validator_set(our_params.validators),
account_provider: Mutex::new(None),
password: RwLock::new(None),
}
@@ -95,7 +96,7 @@ impl Engine for BasicAuthority {
header.set_difficulty(parent.difficulty().clone());
header.set_gas_limit({
let gas_limit = parent.gas_limit().clone();
let bound_divisor = self.our_params.gas_limit_bound_divisor;
let bound_divisor = self.gas_limit_bound_divisor;
if gas_limit < gas_floor_target {
min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into())
} else {
@@ -105,22 +106,22 @@ impl Engine for BasicAuthority {
}
fn is_sealer(&self, author: &Address) -> Option<bool> {
Some(self.our_params.authorities.contains(author))
Some(self.validators.contains(author))
}
/// Attempt to seal the block internally.
///
/// This operation is synchronous and may (quite reasonably) not be available, in which `false` will
/// be returned.
fn generate_seal(&self, block: &ExecutedBlock) -> Seal {
if let Some(ref ap) = *self.account_provider.lock() {
let header = block.header();
let message = header.bare_hash();
// account should be pernamently unlocked, otherwise sealing will fail
if let Ok(signature) = ap.sign(*block.header().author(), self.password.read().clone(), message) {
return Seal::Regular(vec![::rlp::encode(&(&*signature as &[u8])).to_vec()]);
} else {
trace!(target: "basicauthority", "generate_seal: FAIL: accounts secret key unavailable");
let author = header.author();
if self.validators.contains(author) {
let message = header.bare_hash();
// account should be pernamently unlocked, otherwise sealing will fail
if let Ok(signature) = ap.sign(*author, self.password.read().clone(), message) {
return Seal::Regular(vec![::rlp::encode(&(&*signature as &[u8])).to_vec()]);
} else {
trace!(target: "basicauthority", "generate_seal: FAIL: accounts secret key unavailable");
}
}
} else {
trace!(target: "basicauthority", "generate_seal: FAIL: accounts not provided");
@@ -145,7 +146,7 @@ impl Engine for BasicAuthority {
// check the signature is legit.
let sig = UntrustedRlp::new(&header.seal()[0]).as_val::<H520>()?;
let signer = public_to_address(&recover(&sig.into(), &header.bare_hash())?);
if !self.our_params.authorities.contains(&signer) {
if !self.validators.contains(&signer) {
return Err(BlockError::InvalidSeal)?;
}
Ok(())
@@ -161,7 +162,7 @@ impl Engine for BasicAuthority {
if header.difficulty() != parent.difficulty() {
return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: *parent.difficulty(), found: *header.difficulty() })))
}
let gas_limit_divisor = self.our_params.gas_limit_bound_divisor;
let gas_limit_divisor = self.gas_limit_bound_divisor;
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 {
@@ -179,6 +180,10 @@ impl Engine for BasicAuthority {
t.sender().map(|_|()) // Perform EC recovery and cache sender
}
fn register_client(&self, client: Weak<Client>) {
self.validators.register_call_contract(client);
}
fn set_signer(&self, _address: Address, password: String) {
*self.password.write() = Some(password);
}