supply optional call context to validator sets

This commit is contained in:
Robert Habermeier 2017-04-12 16:42:19 +02:00
parent 0f80c57dca
commit ec922ee5e4
5 changed files with 94 additions and 43 deletions

View File

@ -18,12 +18,14 @@
/// It can also report validators for misbehaviour with two levels: `reportMalicious` and `reportBenign`.
use std::sync::Weak;
use futures::Future;
use util::*;
use client::{Client, BlockChainClient};
use futures::Future;
use native_contracts::ValidatorReport as Provider;
use client::{Client, BlockChainClient};
use engines::Call;
use super::ValidatorSet;
use super::safe_contract::ValidatorSafeContract;
@ -48,7 +50,7 @@ impl ValidatorContract {
// could be `impl Trait`.
// note: dispatches transactions to network as well as execute.
// TODO [keorn]: Make more general.
fn transact(&self) -> Box<Fn(Address, Vec<u8>) -> Result<Vec<u8>, String>> {
fn transact(&self) -> Box<Call> {
let client = self.client.read().clone();
Box::new(move |a, d| client.as_ref()
.and_then(Weak::upgrade)
@ -59,16 +61,20 @@ impl ValidatorContract {
}
impl ValidatorSet for ValidatorContract {
fn contains(&self, bh: &H256, address: &Address) -> bool {
self.validators.contains(bh, address)
fn default_caller(&self, id: ::ids::BlockId) -> Box<Call> {
self.validators.default_caller(id)
}
fn get(&self, bh: &H256, nonce: usize) -> Address {
self.validators.get(bh, nonce)
fn contains_with_caller(&self, bh: &H256, address: &Address, caller: &Call) -> bool {
self.validators.contains_with_caller(bh, address, caller)
}
fn count(&self, bh: &H256) -> usize {
self.validators.count(bh)
fn get_with_caller(&self, bh: &H256, nonce: usize, caller: &Call) -> Address {
self.validators.get_with_caller(bh, nonce, caller)
}
fn count_with_caller(&self, bh: &H256, caller: &Call) -> usize {
self.validators.count_with_caller(bh, caller)
}
fn report_malicious(&self, address: &Address) {

View File

@ -22,6 +22,7 @@ mod contract;
mod multi;
use std::sync::Weak;
use ids::BlockId;
use util::{Address, H256};
use ethjson::spec::ValidatorSet as ValidatorSpec;
use client::Client;
@ -31,6 +32,8 @@ use self::contract::ValidatorContract;
use self::safe_contract::ValidatorSafeContract;
use self::multi::Multi;
use super::Call;
/// Creates a validator set from spec.
pub fn new_validator_set(spec: ValidatorSpec) -> Box<ValidatorSet> {
match spec {
@ -44,13 +47,39 @@ pub fn new_validator_set(spec: ValidatorSpec) -> Box<ValidatorSet> {
}
/// A validator set.
// TODO [keorn]: remove internal callers.
pub trait ValidatorSet: Send + Sync {
/// Checks if a given address is a validator.
fn contains(&self, parent_block_hash: &H256, address: &Address) -> bool;
/// Get the default "Call" helper, for use in general operation.
fn default_caller(&self, block_id: BlockId) -> Box<Call>;
/// Checks if a given address is a validator,
/// using underlying, default call mechanism.
fn contains(&self, parent: &H256, address: &Address) -> bool {
let default = self.default_caller(BlockId::Hash(*parent));
self.contains_with_caller(parent, address, &*default)
}
/// Draws an validator nonce modulo number of validators.
fn get(&self, parent_block_hash: &H256, nonce: usize) -> Address;
fn get(&self, parent: &H256, nonce: usize) -> Address {
let default = self.default_caller(BlockId::Hash(*parent));
self.get_with_caller(parent, nonce, &*default)
}
/// Returns the current number of validators.
fn count(&self, parent_block_hash: &H256) -> usize;
fn count(&self, parent: &H256) -> usize {
let default = self.default_caller(BlockId::Hash(*parent));
self.count_with_caller(parent, &*default)
}
/// Checks if a given address is a validator, with the given function
/// for executing synchronous calls to contracts.
fn contains_with_caller(&self, parent_block_hash: &H256, address: &Address, caller: &Call) -> bool;
/// Draws an validator nonce modulo number of validators.
///
fn get_with_caller(&self, parent_block_hash: &H256, nonce: usize, caller: &Call) -> Address;
/// Returns the current number of validators.
fn count_with_caller(&self, parent_block_hash: &H256, caller: &Call) -> usize;
/// Notifies about malicious behaviour.
fn report_malicious(&self, _validator: &Address) {}
/// Notifies about benign misbehaviour.

View File

@ -18,13 +18,14 @@
use std::collections::BTreeMap;
use std::sync::Weak;
use engines::Call;
use util::{H256, Address, RwLock};
use ids::BlockId;
use header::BlockNumber;
use client::{Client, BlockChainClient};
use super::ValidatorSet;
type BlockNumberLookup = Box<Fn(&H256) -> Result<BlockNumber, String> + Send + Sync + 'static>;
type BlockNumberLookup = Box<Fn(BlockId) -> Result<BlockNumber, String> + Send + Sync + 'static>;
pub struct Multi {
sets: BTreeMap<BlockNumber, Box<ValidatorSet>>,
@ -40,10 +41,10 @@ impl Multi {
}
}
fn correct_set(&self, bh: &H256) -> Option<&ValidatorSet> {
fn correct_set(&self, id: BlockId) -> Option<&ValidatorSet> {
match self
.block_number
.read()(bh)
.read()(id)
.map(|parent_block| self
.sets
.iter()
@ -66,16 +67,24 @@ impl Multi {
}
impl ValidatorSet for Multi {
fn contains(&self, bh: &H256, address: &Address) -> bool {
self.correct_set(bh).map_or(false, |set| set.contains(bh, address))
fn default_caller(&self, block_id: BlockId) -> Box<Call> {
self.correct_set(block_id).map(|set| set.default_caller(block_id))
.unwrap_or(Box::new(|_, _| Err("No validator set for given ID.".into())))
}
fn get(&self, bh: &H256, nonce: usize) -> Address {
self.correct_set(bh).map_or_else(Default::default, |set| set.get(bh, nonce))
fn contains_with_caller(&self, bh: &H256, address: &Address, caller: &Call) -> bool {
self.correct_set(BlockId::Hash(*bh))
.map_or(false, |set| set.contains_with_caller(bh, address, caller))
}
fn count(&self, bh: &H256) -> usize {
self.correct_set(bh).map_or_else(usize::max_value, |set| set.count(bh))
fn get_with_caller(&self, bh: &H256, nonce: usize, caller: &Call) -> Address {
self.correct_set(BlockId::Hash(*bh))
.map_or_else(Default::default, |set| set.get_with_caller(bh, nonce, caller))
}
fn count_with_caller(&self, bh: &H256, caller: &Call) -> usize {
self.correct_set(BlockId::Hash(*bh))
.map_or_else(usize::max_value, |set| set.count_with_caller(bh, caller))
}
fn report_malicious(&self, validator: &Address) {
@ -94,10 +103,10 @@ impl ValidatorSet for Multi {
for set in self.sets.values() {
set.register_contract(client.clone());
}
*self.block_number.write() = Box::new(move |hash| client
*self.block_number.write() = Box::new(move |id| client
.upgrade()
.ok_or("No client!".into())
.and_then(|c| c.block_number(BlockId::Hash(*hash)).ok_or("Unknown block".into())));
.and_then(|c| c.block_number(id).ok_or("Unknown block".into())));
}
}

View File

@ -23,6 +23,7 @@ use native_contracts::ValidatorSet as Provider;
use util::*;
use util::cache::MemoryLruCache;
use engines::Call;
use types::ids::BlockId;
use client::{Client, BlockChainClient};
@ -49,17 +50,9 @@ impl ValidatorSafeContract {
}
}
fn do_call(&self, id: BlockId) -> Box<Fn(Address, Vec<u8>) -> Result<Vec<u8>, String>> {
let client = self.client.read().clone();
Box::new(move |addr, data| client.as_ref()
.and_then(Weak::upgrade)
.ok_or("No client!".into())
.and_then(|c| c.call_contract(id, addr, data)))
}
/// Queries the state and gets the set of validators.
fn get_list(&self, block_hash: H256) -> Option<SimpleList> {
match self.provider.get_validators(&*self.do_call(BlockId::Hash(block_hash))).wait() {
fn get_list(&self, caller: &Call) -> Option<SimpleList> {
match self.provider.get_validators(caller).wait() {
Ok(new) => {
debug!(target: "engine", "Set of validators obtained: {:?}", new);
Some(SimpleList::new(new))
@ -73,14 +66,22 @@ impl ValidatorSafeContract {
}
impl ValidatorSet for ValidatorSafeContract {
fn contains(&self, block_hash: &H256, address: &Address) -> bool {
fn default_caller(&self, id: BlockId) -> Box<Call> {
let client = self.client.read().clone();
Box::new(move |addr, data| client.as_ref()
.and_then(Weak::upgrade)
.ok_or("No client!".into())
.and_then(|c| c.call_contract(id, addr, data)))
}
fn contains_with_caller(&self, block_hash: &H256, address: &Address, caller: &Call) -> bool {
let mut guard = self.validators.write();
let maybe_existing = guard
.get_mut(block_hash)
.map(|list| list.contains(block_hash, address));
maybe_existing
.unwrap_or_else(|| self
.get_list(block_hash.clone())
.get_list(caller)
.map_or(false, |list| {
let contains = list.contains(block_hash, address);
guard.insert(block_hash.clone(), list);
@ -88,14 +89,14 @@ impl ValidatorSet for ValidatorSafeContract {
}))
}
fn get(&self, block_hash: &H256, nonce: usize) -> Address {
fn get_with_caller(&self, block_hash: &H256, nonce: usize, caller: &Call) -> Address {
let mut guard = self.validators.write();
let maybe_existing = guard
.get_mut(block_hash)
.map(|list| list.get(block_hash, nonce));
maybe_existing
.unwrap_or_else(|| self
.get_list(block_hash.clone())
.get_list(caller)
.map_or_else(Default::default, |list| {
let address = list.get(block_hash, nonce);
guard.insert(block_hash.clone(), list);
@ -103,14 +104,14 @@ impl ValidatorSet for ValidatorSafeContract {
}))
}
fn count(&self, block_hash: &H256) -> usize {
fn count_with_caller(&self, block_hash: &H256, caller: &Call) -> usize {
let mut guard = self.validators.write();
let maybe_existing = guard
.get_mut(block_hash)
.map(|list| list.count(block_hash));
maybe_existing
.unwrap_or_else(|| self
.get_list(block_hash.clone())
.get_list(caller)
.map_or_else(usize::max_value, |list| {
let address = list.count(block_hash);
guard.insert(block_hash.clone(), list);

View File

@ -17,6 +17,8 @@
/// Preconfigured validator list.
use util::{H256, Address, HeapSizeOf};
use engines::Call;
use super::ValidatorSet;
#[derive(Debug, PartialEq, Eq, Default)]
@ -39,16 +41,20 @@ impl HeapSizeOf for SimpleList {
}
impl ValidatorSet for SimpleList {
fn contains(&self, _bh: &H256, address: &Address) -> bool {
fn default_caller(&self, _block_id: ::ids::BlockId) -> Box<Call> {
Box::new(|_, _| Err("Simple list doesn't require calls.".into()))
}
fn contains_with_caller(&self, _bh: &H256, address: &Address, _: &Call) -> bool {
self.validators.contains(address)
}
fn get(&self, _bh: &H256, nonce: usize) -> Address {
fn get_with_caller(&self, _bh: &H256, nonce: usize, _: &Call) -> Address {
let validator_n = self.validators.len();
self.validators.get(nonce % validator_n).expect("There are validator_n authorities; taking number modulo validator_n gives number in validator_n range; qed").clone()
}
fn count(&self, _bh: &H256) -> usize {
fn count_with_caller(&self, _bh: &H256, _: &Call) -> usize {
self.validators.len()
}
}