Add POSDAO transition and malice report queue. (#11245)

* Add POSDAO transition; call emitInitiateChange.

* Retry failed malice reports.

* Make malice reports with zero gas price.

* Address review comments.

* Extract ReportQueue from ValidatorSafeContract.

* Add shouldValidatorReport to validator set contract.

* Rename queue_report to enqueue_report

* Increment nonce between randomness and POSDAO transactions.

* Refactor the test ValidatorSet contract

* Address review comments, docs

* Update ethcore/res/validator_contract.sol

Co-Authored-By: David <dvdplm@gmail.com>

* Update ethcore/res/validator_contract.sol

Co-Authored-By: David <dvdplm@gmail.com>

Co-authored-by: varasev <33550681+varasev@users.noreply.github.com>
Co-authored-by: David <dvdplm@gmail.com>
This commit is contained in:
Andreas Fackler
2020-01-29 15:08:03 +01:00
committed by GitHub
parent ceda9d95de
commit bf44f024c6
13 changed files with 587 additions and 56 deletions

View File

@@ -21,8 +21,8 @@ use std::sync::Weak;
use parity_bytes::Bytes;
use ethabi_contract::use_contract;
use ethereum_types::{H256, Address};
use log::{warn, trace};
use ethereum_types::{H256, U256, Address};
use log::{info, warn, trace};
use machine::Machine;
use parking_lot::RwLock;
use common_types::{
@@ -31,6 +31,7 @@ use common_types::{
header::Header,
errors::EthcoreError,
engines::machine::{Call, AuxiliaryData},
transaction,
};
use client_traits::{EngineClient, TransactionRequest};
@@ -48,31 +49,63 @@ pub struct ValidatorContract {
contract_address: Address,
validators: ValidatorSafeContract,
client: RwLock<Option<Weak<dyn EngineClient>>>, // TODO [keorn]: remove
posdao_transition: Option<BlockNumber>,
}
impl ValidatorContract {
pub fn new(contract_address: Address) -> Self {
pub fn new(contract_address: Address, posdao_transition: Option<BlockNumber>) -> Self {
ValidatorContract {
contract_address,
validators: ValidatorSafeContract::new(contract_address),
validators: ValidatorSafeContract::new(contract_address, posdao_transition),
client: RwLock::new(None),
posdao_transition,
}
}
}
impl ValidatorContract {
fn transact(&self, data: Bytes) -> Result<(), String> {
let client = self.client.read().as_ref()
.and_then(Weak::upgrade)
.ok_or_else(|| "No client!")?;
fn transact(&self, data: Bytes, gas_price: Option<U256>, client: &dyn EngineClient) -> Result<(), String> {
let full_client = client.as_full_client().ok_or("No full client!")?;
let tx_request = TransactionRequest::call(self.contract_address, data).gas_price(gas_price);
match full_client.transact(tx_request) {
Ok(()) | Err(transaction::Error::AlreadyImported) => Ok(()),
Err(e) => Err(e.to_string())?,
}
}
match client.as_full_client() {
Some(c) => {
c.transact(TransactionRequest::call(self.contract_address, data))
.map_err(|e| format!("Transaction import error: {}", e))?;
Ok(())
},
None => Err("No full client!".into()),
fn do_report_malicious(&self, address: &Address, block: BlockNumber, proof: Bytes) -> Result<(), EthcoreError> {
let client = self.client.read().as_ref().and_then(Weak::upgrade).ok_or("No client!")?;
let latest = client.block_header(BlockId::Latest).ok_or("No latest block!")?;
if !self.contains(&latest.parent_hash(), address) {
warn!(target: "engine", "Not reporting {} on block {}: Not a validator", address, block);
return Ok(());
}
let data = validator_report::functions::report_malicious::encode_input(*address, block, proof);
self.validators.enqueue_report(*address, block, data.clone());
let gas_price = self.report_gas_price(latest.number());
self.transact(data, gas_price, &*client)?;
warn!(target: "engine", "Reported malicious validator {} at block {}", address, block);
Ok(())
}
fn do_report_benign(&self, address: &Address, block: BlockNumber) -> Result<(), EthcoreError> {
let client = self.client.read().as_ref().and_then(Weak::upgrade).ok_or("No client!")?;
let latest = client.block_header(BlockId::Latest).ok_or("No latest block!")?;
let data = validator_report::functions::report_benign::encode_input(*address, block);
let gas_price = self.report_gas_price(latest.number());
self.transact(data, gas_price, &*client)?;
warn!(target: "engine", "Benign report for validator {} at block {}", address, block);
Ok(())
}
/// Returns the gas price for report transactions.
///
/// After `posdaoTransition`, this is zero. Otherwise it is the default (`None`).
fn report_gas_price(&self, block: BlockNumber) -> Option<U256> {
if self.posdao_transition? <= block {
Some(0.into())
} else {
None
}
}
}
@@ -82,6 +115,16 @@ impl ValidatorSet for ValidatorContract {
self.validators.default_caller(id)
}
fn generate_engine_transactions(&self, first: bool, header: &Header, call: &mut SystemCall)
-> Result<Vec<(Address, Bytes)>, EthcoreError>
{
self.validators.generate_engine_transactions(first, header, call)
}
fn on_close_block(&self, header: &Header, address: &Address) -> Result<(), EthcoreError> {
self.validators.on_close_block(header, address)
}
fn on_epoch_begin(&self, first: bool, header: &Header, call: &mut SystemCall) -> Result<(), EthcoreError> {
self.validators.on_epoch_begin(first, header, call)
}
@@ -120,19 +163,15 @@ impl ValidatorSet for ValidatorContract {
}
fn report_malicious(&self, address: &Address, _set_block: BlockNumber, block: BlockNumber, proof: Bytes) {
let data = validator_report::functions::report_malicious::encode_input(*address, block, proof);
match self.transact(data) {
Ok(_) => warn!(target: "engine", "Reported malicious validator {}", address),
Err(s) => warn!(target: "engine", "Validator {} could not be reported {}", address, s),
if let Err(s) = self.do_report_malicious(address, block, proof) {
warn!(target: "engine", "Validator {} could not be reported ({}) on block {}", address, s, block);
}
}
fn report_benign(&self, address: &Address, _set_block: BlockNumber, block: BlockNumber) {
trace!(target: "engine", "validator set recording benign misbehaviour at block #{} by {:#x}", block, address);
let data = validator_report::functions::report_benign::encode_input(*address, block);
match self.transact(data) {
Ok(_) => warn!(target: "engine", "Reported benign validator misbehaviour {}", address),
Err(s) => warn!(target: "engine", "Validator {} could not be reported {}", address, s),
if let Err(s) = self.do_report_benign(address, block) {
warn!(target: "engine", "Validator {} could not be reported ({}) on block {}", address, s, block);
}
}
@@ -150,6 +189,7 @@ mod tests {
use call_contract::CallContract;
use common_types::{header::Header, ids::BlockId};
use client_traits::{BlockChainClient, ChainInfo, BlockInfo, TransactionRequest};
use ethabi::FunctionOutputDecoder;
use ethcore::{
miner::{self, MinerService},
test_helpers::generate_dummy_client_with_spec,
@@ -167,7 +207,8 @@ mod tests {
#[test]
fn fetches_validators() {
let client = generate_dummy_client_with_spec(spec::new_validator_contract);
let vc = Arc::new(ValidatorContract::new("0000000000000000000000000000000000000005".parse::<Address>().unwrap()));
let addr: Address = "0000000000000000000000000000000000000005".parse().unwrap();
let vc = Arc::new(ValidatorContract::new(addr, None));
vc.register_client(Arc::downgrade(&client) as _);
let last_hash = client.best_block_header().hash();
assert!(vc.contains(&last_hash, &"7d577a597b2742b498cb5cf0c26cdcd726d39e6e".parse::<Address>().unwrap()));
@@ -198,6 +239,8 @@ mod tests {
assert!(client.engine().verify_block_external(&header).is_err());
client.engine().step();
assert_eq!(client.chain_info().best_block_number, 0);
// `reportBenign` when the designated proposer releases block from the future (bad clock).
assert!(client.engine().verify_block_basic(&header).is_err());
// Now create one that is more in future. That one should be rejected and validator should be reported.
let mut header = Header::default();
@@ -211,7 +254,7 @@ mod tests {
// Seal a block.
client.engine().step();
assert_eq!(client.chain_info().best_block_number, 1);
// Check if the unresponsive validator is `disliked`.
// Check if the unresponsive validator is `disliked`. "d8f2e0bf" accesses the field `disliked`..
assert_eq!(
client.call_contract(BlockId::Latest, validator_contract, "d8f2e0bf".from_hex().unwrap()).unwrap().to_hex(),
"0000000000000000000000007d577a597b2742b498cb5cf0c26cdcd726d39e6e"
@@ -223,6 +266,9 @@ mod tests {
client.engine().step();
client.engine().step();
assert_eq!(client.chain_info().best_block_number, 2);
let (data, decoder) = super::validator_report::functions::malice_reported_for_block::call(v1, 1);
let reported_enc = client.call_contract(BlockId::Latest, validator_contract, data).expect("call failed");
assert_ne!(Vec::<Address>::new(), decoder.decode(&reported_enc).expect("decoding failed"));
// Check if misbehaving validator was removed.
client.transact(TransactionRequest::call(Default::default(), Default::default())).unwrap();

View File

@@ -49,18 +49,35 @@ use self::contract::ValidatorContract;
use self::safe_contract::ValidatorSafeContract;
use self::multi::Multi;
/// Creates a validator set from spec.
pub fn new_validator_set(spec: ValidatorSpec) -> Box<dyn ValidatorSet> {
/// Creates a validator set from the given spec and initializes a transition to POSDAO AuRa consensus.
pub fn new_validator_set_posdao(
spec: ValidatorSpec,
posdao_transition: Option<BlockNumber>
) -> Box<dyn ValidatorSet> {
match spec {
ValidatorSpec::List(list) => Box::new(SimpleList::new(list.into_iter().map(Into::into).collect())),
ValidatorSpec::SafeContract(address) => Box::new(ValidatorSafeContract::new(address.into())),
ValidatorSpec::Contract(address) => Box::new(ValidatorContract::new(address.into())),
ValidatorSpec::Multi(sequence) => Box::new(
Multi::new(sequence.into_iter().map(|(block, set)| (block.into(), new_validator_set(set))).collect())
),
ValidatorSpec::List(list) =>
Box::new(SimpleList::new(list.into_iter().map(Into::into).collect())),
ValidatorSpec::SafeContract(address) =>
Box::new(ValidatorSafeContract::new(address.into(), posdao_transition)),
ValidatorSpec::Contract(address) =>
Box::new(ValidatorContract::new(address.into(), posdao_transition)),
ValidatorSpec::Multi(sequence) => Box::new(Multi::new(
sequence
.into_iter()
.map(|(block, set)| (
block.into(),
new_validator_set_posdao(set, posdao_transition)
))
.collect()
)),
}
}
/// Creates a validator set from the given spec.
pub fn new_validator_set(spec: ValidatorSpec) -> Box<dyn ValidatorSet> {
new_validator_set_posdao(spec, None)
}
/// A validator set.
pub trait ValidatorSet: Send + Sync + 'static {
/// Get the default "Call" helper, for use in general operation.
@@ -68,6 +85,17 @@ pub trait ValidatorSet: Send + Sync + 'static {
// a strict dependency on state always being available.
fn default_caller(&self, block_id: BlockId) -> Box<Call>;
/// Called for each new block this node is creating. If this block is
/// the first block of an epoch, this is called *after* `on_epoch_begin()`,
/// but with the same parameters.
///
/// Returns a list of contract calls to be pushed onto the new block.
fn generate_engine_transactions(&self, _first: bool, _header: &Header, _call: &mut SystemCall)
-> Result<Vec<(Address, Bytes)>, EthcoreError>;
/// Called on the close of every block.
fn on_close_block(&self, _header: &Header, _address: &Address) -> Result<(), EthcoreError>;
/// Checks if a given address is a validator,
/// using underlying, default call mechanism.
fn contains(&self, parent: &H256, address: &Address) -> bool {

View File

@@ -51,6 +51,14 @@ impl Multi {
}
}
fn map_children<T, F>(&self, header: &Header, mut func: F) -> Result<T, EthcoreError>
where F: FnMut(&dyn ValidatorSet, bool) -> Result<T, EthcoreError>
{
let (set_block, set) = self.correct_set_by_number(header.number());
let first = set_block == header.number();
func(set, first)
}
fn correct_set(&self, id: BlockId) -> Option<&dyn ValidatorSet> {
match self.block_number.read()(id).map(|parent_block| self.correct_set_by_number(parent_block)) {
Ok((_, set)) => Some(set),
@@ -82,11 +90,20 @@ impl ValidatorSet for Multi {
.unwrap_or_else(|| Box::new(|_, _| Err("No validator set for given ID.".into())))
}
fn on_epoch_begin(&self, _first: bool, header: &Header, call: &mut SystemCall) -> Result<(), EthcoreError> {
let (set_block, set) = self.correct_set_by_number(header.number());
let first = set_block == header.number();
fn generate_engine_transactions(&self, _first: bool, header: &Header, call: &mut SystemCall)
-> Result<Vec<(Address, Bytes)>, EthcoreError>
{
self.map_children(header, &mut |set: &dyn ValidatorSet, first| {
set.generate_engine_transactions(first, header, call)
})
}
set.on_epoch_begin(first, header, call)
fn on_close_block(&self, header: &Header, address: &Address) -> Result<(), EthcoreError> {
self.map_children(header, &mut |set: &dyn ValidatorSet, _first| set.on_close_block(header, address))
}
fn on_epoch_begin(&self, _first: bool, header: &Header, call: &mut SystemCall) -> Result<(), EthcoreError> {
self.map_children(header, &mut |set: &dyn ValidatorSet, first| set.on_epoch_begin(first, header, call))
}
fn genesis_epoch_data(&self, header: &Header, call: &Call) -> Result<Vec<u8>, String> {

View File

@@ -16,9 +16,10 @@
/// Validator set maintained in a contract, updated using `getValidators` method.
use std::sync::{Weak, Arc};
use std::collections::VecDeque;
use std::sync::{Arc, Weak};
use client_traits::EngineClient;
use client_traits::{BlockChainClient, EngineClient, TransactionRequest};
use common_types::{
BlockNumber,
header::Header,
@@ -27,6 +28,7 @@ use common_types::{
log_entry::LogEntry,
engines::machine::{Call, AuxiliaryData, AuxiliaryRequest},
receipt::Receipt,
transaction::{self, Action, Transaction},
};
use ethabi::FunctionOutputDecoder;
use ethabi_contract::use_contract;
@@ -34,11 +36,11 @@ use ethereum_types::{H256, U256, Address, Bloom};
use keccak_hash::keccak;
use kvdb::DBValue;
use lazy_static::lazy_static;
use log::{debug, info, trace};
use log::{debug, info, trace, warn};
use machine::Machine;
use memory_cache::MemoryLruCache;
use parity_bytes::Bytes;
use parking_lot::RwLock;
use parking_lot::{Mutex, RwLock};
use rlp::{Rlp, RlpStream};
use unexpected::Mismatch;
@@ -47,6 +49,13 @@ use super::simple_list::SimpleList;
use_contract!(validator_set, "res/validator_set.json");
/// The maximum number of reports to keep queued.
const MAX_QUEUED_REPORTS: usize = 10;
/// The maximum number of malice reports to include when creating a new block.
const MAX_REPORTS_PER_BLOCK: usize = 10;
/// Don't re-send malice reports every block. Skip this many before retrying.
const REPORTS_SKIP_BLOCKS: u64 = 1;
const MEMOIZE_CAPACITY: usize = 500;
// TODO: ethabi should be able to generate this.
@@ -86,6 +95,12 @@ pub struct ValidatorSafeContract {
/// is the validator set valid for the blocks following that hash.
validators: RwLock<MemoryLruCache<H256, SimpleList>>,
client: RwLock<Option<Weak<dyn EngineClient>>>, // TODO [keorn]: remove
report_queue: Mutex<ReportQueue>,
/// The block number where we resent the queued reports last time.
resent_reports_in_block: Mutex<BlockNumber>,
/// If set, this is the block number at which the consensus engine switches from AuRa to AuRa
/// with POSDAO modifications.
posdao_transition: Option<BlockNumber>,
}
// first proof is just a state proof call of `getValidators` at header's state.
@@ -103,8 +118,6 @@ fn encode_first_proof(header: &Header, state_items: &[Vec<u8>]) -> Bytes {
fn check_first_proof(machine: &Machine, contract_address: Address, old_header: Header, state_items: &[DBValue])
-> Result<Vec<Address>, String>
{
use common_types::transaction::{Action, Transaction};
// TODO: match client contract_call_tx more cleanly without duplication.
const PROVIDED_GAS: u64 = 50_000_000;
@@ -200,14 +213,44 @@ fn prove_initial(contract_address: Address, header: &Header, caller: &Call) -> R
}
impl ValidatorSafeContract {
pub fn new(contract_address: Address) -> Self {
pub fn new(contract_address: Address, posdao_transition: Option<BlockNumber>) -> Self {
ValidatorSafeContract {
contract_address,
validators: RwLock::new(MemoryLruCache::new(MEMOIZE_CAPACITY)),
client: RwLock::new(None),
report_queue: Mutex::new(ReportQueue::default()),
resent_reports_in_block: Mutex::new(0),
posdao_transition,
}
}
fn transact(&self, data: Bytes, nonce: U256) -> Result<(), EthcoreError> {
let client = self.client.read().as_ref().and_then(Weak::upgrade).ok_or("No client!")?;
let full_client = client.as_full_client().ok_or("No full client!")?;
let tx_request = TransactionRequest::call(self.contract_address, data).gas_price(U256::zero()).nonce(nonce);
match full_client.transact(tx_request) {
Ok(()) | Err(transaction::Error::AlreadyImported) => Ok(()),
Err(e) => Err(e)?,
}
}
/// Puts a malice report into the queue for later resending.
///
/// # Arguments
///
/// * `addr` - The address of the misbehaving validator.
/// * `block` - The block number at which the misbehavior occurred.
/// * `data` - The call data for the `reportMalicious` contract call.
pub(crate) fn enqueue_report(&self, addr: Address, block: BlockNumber, data: Vec<u8>) {
// Skip the rest of the function unless there has been a transition to POSDAO AuRa.
if self.posdao_transition.map_or(true, |block_num| block < block_num) {
trace!(target: "engine", "Skipping queueing a malicious behavior report");
return;
}
self.report_queue.lock().push(addr, block, data)
}
/// Queries the state and gets the set of validators.
fn get_list(&self, caller: &Call) -> Option<SimpleList> {
let contract_address = self.contract_address;
@@ -300,6 +343,85 @@ impl ValidatorSet for ValidatorSafeContract {
.map(|out| (out, Vec::new()))) // generate no proofs in general
}
fn generate_engine_transactions(&self, _first: bool, header: &Header, caller: &mut SystemCall)
-> Result<Vec<(Address, Bytes)>, EthcoreError>
{
// Skip the rest of the function unless there has been a transition to POSDAO AuRa.
if self.posdao_transition.map_or(true, |block_num| header.number() < block_num) {
trace!(target: "engine", "Skipping a call to emitInitiateChange");
return Ok(Vec::new());
}
let mut transactions = Vec::new();
// Create the `InitiateChange` event if necessary.
let (data, decoder) = validator_set::functions::emit_initiate_change_callable::call();
let emit_initiate_change_callable = caller(self.contract_address, data)
.and_then(|x| decoder.decode(&x)
.map_err(|x| format!("chain spec bug: could not decode: {:?}", x)))
.map_err(EngineError::FailedSystemCall)?;
if !emit_initiate_change_callable {
trace!(target: "engine", "New block #{} issued ― no need to call emitInitiateChange()", header.number());
} else {
trace!(target: "engine", "New block issued #{} ― calling emitInitiateChange()", header.number());
let (data, _decoder) = validator_set::functions::emit_initiate_change::call();
transactions.push((self.contract_address, data));
}
let client = self.client.read().as_ref().and_then(Weak::upgrade).ok_or("No client!")?;
let client = client.as_full_client().ok_or("No full client!")?;
// Retry all pending reports.
let mut report_queue = self.report_queue.lock();
report_queue.filter(client, header.author(), self.contract_address);
for (_address, _block, data) in report_queue.iter().take(MAX_REPORTS_PER_BLOCK) {
transactions.push((self.contract_address, data.clone()))
}
Ok(transactions)
}
fn on_close_block(&self, header: &Header, our_address: &Address) -> Result<(), EthcoreError> {
// Skip the rest of the function unless there has been a transition to POSDAO AuRa.
if self.posdao_transition.map_or(true, |block_num| header.number() < block_num) {
trace!(target: "engine", "Skipping resending of queued malicious behavior reports");
return Ok(());
}
let client = self.client.read().as_ref().and_then(Weak::upgrade).ok_or("No client!")?;
let client = client.as_full_client().ok_or("No full client!")?;
let mut report_queue = self.report_queue.lock();
report_queue.filter(client, our_address, self.contract_address);
report_queue.truncate();
let mut resent_reports_in_block = self.resent_reports_in_block.lock();
// Skip at least one block after sending malicious reports last time.
if header.number() > *resent_reports_in_block + REPORTS_SKIP_BLOCKS {
*resent_reports_in_block = header.number();
let mut nonce = client.latest_nonce(our_address);
for (address, block, data) in report_queue.iter() {
debug!(target: "engine", "Retrying to report validator {} for misbehavior on block {} with nonce {}.",
address, block, nonce);
while match self.transact(data.clone(), nonce) {
Ok(()) => false,
Err(EthcoreError::Transaction(transaction::Error::Old)) => true,
Err(err) => {
warn!(target: "engine", "Cannot report validator {} for misbehavior on block {}: {}",
address, block, err);
false
}
} {
warn!(target: "engine", "Nonce {} already used. Incrementing.", nonce);
nonce += U256::from(1);
}
nonce += U256::from(1);
}
}
Ok(())
}
fn on_epoch_begin(&self, _first: bool, _header: &Header, caller: &mut SystemCall) -> Result<(), EthcoreError> {
let data = validator_set::functions::finalize_change::encode_input();
caller(self.contract_address, data)
@@ -450,6 +572,68 @@ impl ValidatorSet for ValidatorSafeContract {
}
}
/// A queue containing pending reports of malicious validators.
#[derive(Debug, Default)]
struct ReportQueue(VecDeque<(Address, BlockNumber, Vec<u8>)>);
impl ReportQueue {
/// Pushes a report to the end of the queue.
fn push(&mut self, addr: Address, block: BlockNumber, data: Vec<u8>) {
self.0.push_back((addr, block, data));
}
/// Filters reports of validators that have already been reported or are banned.
fn filter(
&mut self,
client: &dyn BlockChainClient,
our_address: &Address,
contract_address: Address,
) {
self.0.retain(|&(malicious_validator_address, block, ref _data)| {
trace!(
target: "engine",
"Checking if report of malicious validator {} at block {} should be removed from cache",
malicious_validator_address,
block
);
// Check if the validator should be reported.
let (data, decoder) = validator_set::functions::should_validator_report::call(
*our_address, malicious_validator_address, block
);
match client.call_contract(BlockId::Latest, contract_address, data)
.and_then(|result| decoder.decode(&result[..]).map_err(|e| e.to_string()))
{
Ok(false) => {
trace!(target: "engine", "Successfully removed report from report cache");
false
}
Ok(true) => true,
Err(err) => {
warn!(target: "engine", "Failed to query report status {:?}, dropping pending report.", err);
false
}
}
});
}
/// Returns an iterator over all transactions in the queue.
fn iter(&self) -> impl Iterator<Item = &(Address, BlockNumber, Vec<u8>)> {
self.0.iter()
}
/// Removes reports from the queue if it contains more than `MAX_QUEUED_REPORTS` entries.
fn truncate(&mut self) {
if self.0.len() > MAX_QUEUED_REPORTS {
warn!(
target: "engine",
"Removing {} reports from report cache, even though it has not been finalized",
self.0.len() - MAX_QUEUED_REPORTS
);
self.0.truncate(MAX_QUEUED_REPORTS);
}
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
@@ -481,7 +665,8 @@ mod tests {
#[test]
fn fetches_validators() {
let client = generate_dummy_client_with_spec(spec::new_validator_safe_contract);
let vc = Arc::new(ValidatorSafeContract::new("0000000000000000000000000000000000000005".parse::<Address>().unwrap()));
let addr: Address = "0000000000000000000000000000000000000005".parse().unwrap();
let vc = Arc::new(ValidatorSafeContract::new(addr, None));
vc.register_client(Arc::downgrade(&client) as _);
let last_hash = client.best_block_header().hash();
assert!(vc.contains(&last_hash, &"7d577a597b2742b498cb5cf0c26cdcd726d39e6e".parse::<Address>().unwrap()));

View File

@@ -26,9 +26,10 @@ use common_types::{
use ethereum_types::{H256, Address};
use log::warn;
use machine::Machine;
use parity_bytes::Bytes;
use parity_util_mem::MallocSizeOf;
use super::ValidatorSet;
use super::{SystemCall, ValidatorSet};
/// Validator set containing a known set of addresses.
#[derive(Clone, Debug, PartialEq, Eq, Default, MallocSizeOf)]
@@ -71,6 +72,16 @@ impl ValidatorSet for SimpleList {
Box::new(|_, _| Err("Simple list doesn't require calls.".into()))
}
fn generate_engine_transactions(&self, _first: bool, _header: &Header, _call: &mut SystemCall)
-> Result<Vec<(Address, Bytes)>, EthcoreError>
{
Ok(Vec::new())
}
fn on_close_block(&self, _header: &Header, _address: &Address) -> Result<(), EthcoreError> {
Ok(())
}
fn is_epoch_end(&self, first: bool, _chain_head: &Header) -> Option<Vec<u8>> {
match first {
true => Some(Vec::new()), // allow transition to fixed list, and instantly

View File

@@ -33,7 +33,7 @@ use ethereum_types::{H256, Address};
use machine::Machine;
use parity_bytes::Bytes;
use super::{ValidatorSet, SimpleList};
use super::{SystemCall, ValidatorSet, SimpleList};
/// Set used for testing with a single validator.
#[derive(Clone, MallocSizeOf, Debug)]
@@ -82,6 +82,16 @@ impl ValidatorSet for TestSet {
Box::new(|_, _| Err("Test set doesn't require calls.".into()))
}
fn generate_engine_transactions(&self, _first: bool, _header: &Header, _call: &mut SystemCall)
-> Result<Vec<(Address, Bytes)>, EthcoreError>
{
Ok(Vec::new())
}
fn on_close_block(&self, _header: &Header, _address: &Address) -> Result<(), EthcoreError> {
Ok(())
}
fn is_epoch_end(&self, _first: bool, _chain_head: &Header) -> Option<Vec<u8>> { None }
fn signals_epoch_end(&self, _: bool, _: &Header, _: AuxiliaryData)