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:
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user