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

View File

@ -22,6 +22,7 @@ mod contract;
mod multi; mod multi;
use std::sync::Weak; use std::sync::Weak;
use ids::BlockId;
use util::{Address, H256}; use util::{Address, H256};
use ethjson::spec::ValidatorSet as ValidatorSpec; use ethjson::spec::ValidatorSet as ValidatorSpec;
use client::Client; use client::Client;
@ -31,6 +32,8 @@ use self::contract::ValidatorContract;
use self::safe_contract::ValidatorSafeContract; use self::safe_contract::ValidatorSafeContract;
use self::multi::Multi; use self::multi::Multi;
use super::Call;
/// Creates a validator set from spec. /// Creates a validator set from spec.
pub fn new_validator_set(spec: ValidatorSpec) -> Box<ValidatorSet> { pub fn new_validator_set(spec: ValidatorSpec) -> Box<ValidatorSet> {
match spec { match spec {
@ -44,13 +47,39 @@ pub fn new_validator_set(spec: ValidatorSpec) -> Box<ValidatorSet> {
} }
/// A validator set. /// A validator set.
// TODO [keorn]: remove internal callers.
pub trait ValidatorSet: Send + Sync { pub trait ValidatorSet: Send + Sync {
/// Checks if a given address is a validator. /// Get the default "Call" helper, for use in general operation.
fn contains(&self, parent_block_hash: &H256, address: &Address) -> bool; 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. /// 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. /// 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. /// Notifies about malicious behaviour.
fn report_malicious(&self, _validator: &Address) {} fn report_malicious(&self, _validator: &Address) {}
/// Notifies about benign misbehaviour. /// Notifies about benign misbehaviour.

View File

@ -18,13 +18,14 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::sync::Weak; use std::sync::Weak;
use engines::Call;
use util::{H256, Address, RwLock}; use util::{H256, Address, RwLock};
use ids::BlockId; use ids::BlockId;
use header::BlockNumber; use header::BlockNumber;
use client::{Client, BlockChainClient}; use client::{Client, BlockChainClient};
use super::ValidatorSet; 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 { pub struct Multi {
sets: BTreeMap<BlockNumber, Box<ValidatorSet>>, 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 match self
.block_number .block_number
.read()(bh) .read()(id)
.map(|parent_block| self .map(|parent_block| self
.sets .sets
.iter() .iter()
@ -66,16 +67,24 @@ impl Multi {
} }
impl ValidatorSet for Multi { impl ValidatorSet for Multi {
fn contains(&self, bh: &H256, address: &Address) -> bool { fn default_caller(&self, block_id: BlockId) -> Box<Call> {
self.correct_set(bh).map_or(false, |set| set.contains(bh, address)) 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 { fn contains_with_caller(&self, bh: &H256, address: &Address, caller: &Call) -> bool {
self.correct_set(bh).map_or_else(Default::default, |set| set.get(bh, nonce)) self.correct_set(BlockId::Hash(*bh))
.map_or(false, |set| set.contains_with_caller(bh, address, caller))
} }
fn count(&self, bh: &H256) -> usize { fn get_with_caller(&self, bh: &H256, nonce: usize, caller: &Call) -> Address {
self.correct_set(bh).map_or_else(usize::max_value, |set| set.count(bh)) 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) { fn report_malicious(&self, validator: &Address) {
@ -94,10 +103,10 @@ impl ValidatorSet for Multi {
for set in self.sets.values() { for set in self.sets.values() {
set.register_contract(client.clone()); set.register_contract(client.clone());
} }
*self.block_number.write() = Box::new(move |hash| client *self.block_number.write() = Box::new(move |id| client
.upgrade() .upgrade()
.ok_or("No client!".into()) .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::*;
use util::cache::MemoryLruCache; use util::cache::MemoryLruCache;
use engines::Call;
use types::ids::BlockId; use types::ids::BlockId;
use client::{Client, BlockChainClient}; 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. /// Queries the state and gets the set of validators.
fn get_list(&self, block_hash: H256) -> Option<SimpleList> { fn get_list(&self, caller: &Call) -> Option<SimpleList> {
match self.provider.get_validators(&*self.do_call(BlockId::Hash(block_hash))).wait() { match self.provider.get_validators(caller).wait() {
Ok(new) => { Ok(new) => {
debug!(target: "engine", "Set of validators obtained: {:?}", new); debug!(target: "engine", "Set of validators obtained: {:?}", new);
Some(SimpleList::new(new)) Some(SimpleList::new(new))
@ -73,14 +66,22 @@ impl ValidatorSafeContract {
} }
impl ValidatorSet for 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 mut guard = self.validators.write();
let maybe_existing = guard let maybe_existing = guard
.get_mut(block_hash) .get_mut(block_hash)
.map(|list| list.contains(block_hash, address)); .map(|list| list.contains(block_hash, address));
maybe_existing maybe_existing
.unwrap_or_else(|| self .unwrap_or_else(|| self
.get_list(block_hash.clone()) .get_list(caller)
.map_or(false, |list| { .map_or(false, |list| {
let contains = list.contains(block_hash, address); let contains = list.contains(block_hash, address);
guard.insert(block_hash.clone(), list); 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 mut guard = self.validators.write();
let maybe_existing = guard let maybe_existing = guard
.get_mut(block_hash) .get_mut(block_hash)
.map(|list| list.get(block_hash, nonce)); .map(|list| list.get(block_hash, nonce));
maybe_existing maybe_existing
.unwrap_or_else(|| self .unwrap_or_else(|| self
.get_list(block_hash.clone()) .get_list(caller)
.map_or_else(Default::default, |list| { .map_or_else(Default::default, |list| {
let address = list.get(block_hash, nonce); let address = list.get(block_hash, nonce);
guard.insert(block_hash.clone(), list); 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 mut guard = self.validators.write();
let maybe_existing = guard let maybe_existing = guard
.get_mut(block_hash) .get_mut(block_hash)
.map(|list| list.count(block_hash)); .map(|list| list.count(block_hash));
maybe_existing maybe_existing
.unwrap_or_else(|| self .unwrap_or_else(|| self
.get_list(block_hash.clone()) .get_list(caller)
.map_or_else(usize::max_value, |list| { .map_or_else(usize::max_value, |list| {
let address = list.count(block_hash); let address = list.count(block_hash);
guard.insert(block_hash.clone(), list); guard.insert(block_hash.clone(), list);

View File

@ -17,6 +17,8 @@
/// Preconfigured validator list. /// Preconfigured validator list.
use util::{H256, Address, HeapSizeOf}; use util::{H256, Address, HeapSizeOf};
use engines::Call;
use super::ValidatorSet; use super::ValidatorSet;
#[derive(Debug, PartialEq, Eq, Default)] #[derive(Debug, PartialEq, Eq, Default)]
@ -39,16 +41,20 @@ impl HeapSizeOf for SimpleList {
} }
impl ValidatorSet 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) 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(); 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() 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() self.validators.len()
} }
} }