openethereum/ethcore/src/engines/tendermint/mod.rs

1127 lines
37 KiB
Rust
Raw Normal View History

// Copyright 2015-2017 Parity Technologies (UK) Ltd.
2016-08-23 12:58:40 +02:00
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
2016-12-08 20:09:30 +01:00
/// Tendermint BFT consensus engine with round robin proof-of-authority.
/// At each blockchain `Height` there can be multiple `View`s of voting.
/// Signatures always sign `Height`, `View`, `Step` and `BlockHash` which is a block hash without seal.
2016-12-08 20:09:30 +01:00
/// First a block with `Seal::Proposal` is issued by the designated proposer.
/// Next the `View` proceeds through `Prevote` and `Precommit` `Step`s.
/// Block is issued when there is enough `Precommit` votes collected on a particular block at the end of a `View`.
2016-12-08 20:09:30 +01:00
/// Once enough votes have been gathered the proposer issues that block in the `Commit` step.
2016-08-23 12:58:40 +02:00
mod message;
2016-09-29 16:32:49 +02:00
mod params;
2017-07-29 21:56:42 +02:00
use std::sync::{Weak, Arc};
2016-08-25 19:22:10 +02:00
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
use std::collections::HashSet;
use hash::keccak;
use ethereum_types::{H256, H520, U128, U256, Address};
use parking_lot::RwLock;
use unexpected::{OutOfBounds, Mismatch};
use client::EngineClient;
use bytes::Bytes;
use error::{Error, BlockError};
use header::{Header, BlockNumber};
use rlp::Rlp;
New Transaction Queue implementation (#8074) * Implementation of Verifier, Scoring and Ready. * Queue in progress. * TransactionPool. * Prepare for txpool release. * Miner refactor [WiP] * WiP reworking miner. * Make it compile. * Add some docs. * Split blockchain access to a separate file. * Work on miner API. * Fix ethcore tests. * Refactor miner interface for sealing/work packages. * Implement next nonce. * RPC compiles. * Implement couple of missing methdods for RPC. * Add transaction queue listeners. * Compiles! * Clean-up and parallelize. * Get rid of RefCell in header. * Revert "Get rid of RefCell in header." This reverts commit 0f2424c9b7319a786e1565ea2a8a6d801a21b4fb. * Override Sync requirement. * Fix status display. * Unify logging. * Extract some cheap checks. * Measurements and optimizations. * Fix scoring bug, heap size of bug and add cache * Disable tx queueing and parallel verification. * Make ethcore and ethcore-miner compile again. * Make RPC compile again. * Bunch of txpool tests. * Migrate transaction queue tests. * Nonce Cap * Nonce cap cache and tests. * Remove stale future transactions from the queue. * Optimize scoring and write some tests. * Simple penalization. * Clean up and support for different scoring algorithms. * Add CLI parameters for the new queue. * Remove banning queue. * Disable debug build. * Change per_sender limit to be 1% instead of 5% * Avoid cloning when propagating transactions. * Remove old todo. * Post-review fixes. * Fix miner options default. * Implement back ready transactions for light client. * Get rid of from_pending_block * Pass rejection reason. * Add more details to drop. * Rollback heap size of. * Avoid cloning hashes when propagating and include more details on rejection. * Fix tests. * Introduce nonces cache. * Remove uneccessary hashes allocation. * Lower the mem limit. * Re-enable parallel verification. * Add miner log. Don't check the type if not below min_gas_price. * Add more traces, fix disabling miner. * Fix creating pending blocks twice on AuRa authorities. * Fix tests. * re-use pending blocks in AuRa * Use reseal_min_period to prevent too frequent update_sealing. * Fix log to contain hash not sender. * Optimize local transactions. * Fix aura tests. * Update locks comments. * Get rid of unsafe Sync impl. * Review fixes. * Remove excessive matches. * Fix compilation errors. * Use new pool in private transactions. * Fix private-tx test. * Fix secret store tests. * Actually use gas_floor_target * Fix config tests. * Fix pool tests. * Address grumbles.
2018-04-13 17:34:27 +02:00
use ethkey::{self, Message, Signature};
2016-08-23 12:58:40 +02:00
use account_provider::AccountProvider;
use block::*;
use engines::{Engine, Seal, EngineError, ConstructedVerifier};
use io::IoService;
use super::signer::EngineSigner;
use super::validator_set::{ValidatorSet, SimpleList};
use super::transition::TransitionHandler;
use super::vote_collector::VoteCollector;
2016-11-21 17:02:26 +01:00
use self::message::*;
2016-09-29 16:32:49 +02:00
use self::params::TendermintParams;
use machine::{AuxiliaryData, EthereumMachine};
2016-08-31 18:18:02 +02:00
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
2016-11-15 11:21:18 +01:00
pub enum Step {
2016-08-23 12:58:40 +02:00
Propose,
2016-11-15 11:21:18 +01:00
Prevote,
Precommit,
Commit
2016-08-23 12:58:40 +02:00
}
2016-12-07 16:42:58 +01:00
impl Step {
pub fn is_pre(self) -> bool {
match self {
Step::Prevote | Step::Precommit => true,
_ => false,
}
}
}
pub type Height = usize;
pub type View = usize;
pub type BlockHash = H256;
2016-08-23 12:58:40 +02:00
/// Engine using `Tendermint` consensus algorithm, suitable for EVM chain.
pub struct Tendermint {
2016-11-16 19:01:09 +01:00
step_service: IoService<Step>,
client: RwLock<Option<Weak<EngineClient>>>,
2016-11-15 11:21:18 +01:00
/// Blockchain height.
height: AtomicUsize,
/// Consensus view.
view: AtomicUsize,
/// Consensus step.
2016-11-15 11:21:18 +01:00
step: RwLock<Step>,
/// Vote accumulator.
votes: VoteCollector<ConsensusMessage>,
2016-11-18 00:36:24 +01:00
/// Used to sign messages and proposals.
signer: RwLock<EngineSigner>,
2016-11-17 18:12:37 +01:00
/// Message for the last PoLC.
2016-11-18 13:27:00 +01:00
lock_change: RwLock<Option<ConsensusMessage>>,
/// Last lock view.
2016-11-18 13:27:00 +01:00
last_lock: AtomicUsize,
2016-11-17 18:12:37 +01:00
/// Bare hash of the proposed block, used for seal submission.
2016-12-09 10:53:38 +01:00
proposal: RwLock<Option<H256>>,
/// Hash of the proposal parent block.
proposal_parent: RwLock<H256>,
/// Last block proposed by this validator.
last_proposed: RwLock<H256>,
/// Set used to determine the current validators.
validators: Box<ValidatorSet>,
/// Reward per block, in base units.
block_reward: U256,
/// ethereum machine descriptor
machine: EthereumMachine,
}
struct EpochVerifier<F>
where F: Fn(&Signature, &Message) -> Result<Address, Error> + Send + Sync
{
subchain_validators: SimpleList,
recover: F
}
impl <F> super::EpochVerifier<EthereumMachine> for EpochVerifier<F>
where F: Fn(&Signature, &Message) -> Result<Address, Error> + Send + Sync
{
fn verify_light(&self, header: &Header) -> Result<(), Error> {
let message = header.bare_hash();
let mut addresses = HashSet::new();
let ref header_signatures_field = header.seal().get(2).ok_or(BlockError::InvalidSeal)?;
for rlp in Rlp::new(header_signatures_field).iter() {
let signature: H520 = rlp.as_val()?;
let address = (self.recover)(&signature.into(), &message)?;
if !self.subchain_validators.contains(header.parent_hash(), &address) {
return Err(EngineError::NotAuthorized(address.to_owned()).into());
}
addresses.insert(address);
}
let n = addresses.len();
let threshold = self.subchain_validators.len() * 2/3;
if n > threshold {
Ok(())
} else {
Err(EngineError::BadSealFieldSize(OutOfBounds {
min: Some(threshold),
max: None,
found: n
}).into())
}
}
fn check_finality_proof(&self, proof: &[u8]) -> Option<Vec<H256>> {
let header: Header = ::rlp::decode(proof);
self.verify_light(&header).ok().map(|_| vec![header.hash()])
}
}
fn combine_proofs(signal_number: BlockNumber, set_proof: &[u8], finality_proof: &[u8]) -> Vec<u8> {
let mut stream = ::rlp::RlpStream::new_list(3);
stream.append(&signal_number).append(&set_proof).append(&finality_proof);
stream.out()
}
fn destructure_proofs(combined: &[u8]) -> Result<(BlockNumber, &[u8], &[u8]), Error> {
let rlp = Rlp::new(combined);
Ok((
rlp.at(0)?.as_val()?,
rlp.at(1)?.data()?,
rlp.at(2)?.data()?,
))
}
2016-08-23 12:58:40 +02:00
impl Tendermint {
/// Create a new instance of Tendermint engine
pub fn new(our_params: TendermintParams, machine: EthereumMachine) -> Result<Arc<Self>, Error> {
let engine = Arc::new(
Tendermint {
client: RwLock::new(None),
step_service: IoService::<Step>::start()?,
2016-11-28 10:42:50 +01:00
height: AtomicUsize::new(1),
view: AtomicUsize::new(0),
2016-11-15 11:21:18 +01:00
step: RwLock::new(Step::Propose),
votes: Default::default(),
signer: Default::default(),
2016-11-18 13:27:00 +01:00
lock_change: RwLock::new(None),
last_lock: AtomicUsize::new(0),
2016-12-09 10:53:38 +01:00
proposal: RwLock::new(None),
proposal_parent: Default::default(),
last_proposed: Default::default(),
validators: our_params.validators,
block_reward: our_params.block_reward,
machine: machine,
});
let handler = TransitionHandler::new(Arc::downgrade(&engine) as Weak<Engine<_>>, Box::new(our_params.timeouts));
engine.step_service.register_handler(Arc::new(handler))?;
2016-11-15 11:21:18 +01:00
Ok(engine)
2016-10-05 15:33:07 +02:00
}
2016-11-16 13:43:21 +01:00
fn update_sealing(&self) {
if let Some(ref weak) = *self.client.read() {
if let Some(c) = weak.upgrade() {
c.update_sealing();
2016-11-16 13:43:21 +01:00
}
}
}
2016-11-16 16:56:16 +01:00
fn submit_seal(&self, block_hash: H256, seal: Vec<Bytes>) {
if let Some(ref weak) = *self.client.read() {
if let Some(c) = weak.upgrade() {
c.submit_seal(block_hash, seal);
2016-11-16 16:56:16 +01:00
}
}
}
2016-11-28 16:24:22 +01:00
fn broadcast_message(&self, message: Bytes) {
if let Some(ref weak) = *self.client.read() {
if let Some(c) = weak.upgrade() {
c.broadcast_consensus_message(message);
2016-11-17 13:18:20 +01:00
}
}
}
2016-11-18 00:36:24 +01:00
fn generate_message(&self, block_hash: Option<BlockHash>) -> Option<Bytes> {
let h = self.height.load(AtomicOrdering::SeqCst);
let r = self.view.load(AtomicOrdering::SeqCst);
let s = *self.step.read();
let vote_info = message_info_rlp(&VoteStep::new(h, r, s), block_hash);
match (self.signer.read().address(), self.sign(keccak(&vote_info)).map(Into::into)) {
(Some(validator), Ok(signature)) => {
let message_rlp = message_full_rlp(&signature, &vote_info);
let message = ConsensusMessage::new(signature, h, r, s, block_hash);
self.votes.vote(message.clone(), validator);
debug!(target: "engine", "Generated {:?} as {}.", message, validator);
self.handle_valid_message(&message);
Some(message_rlp)
},
(None, _) => {
trace!(target: "engine", "No message, since there is no engine signer.");
None
},
(Some(v), Err(e)) => {
trace!(target: "engine", "{} could not sign the message {}", v, e);
None
},
2016-11-18 00:36:24 +01:00
}
2016-11-17 18:12:37 +01:00
}
2016-11-28 16:24:22 +01:00
fn generate_and_broadcast_message(&self, block_hash: Option<BlockHash>) {
if let Some(message) = self.generate_message(block_hash) {
self.broadcast_message(message);
}
}
2016-12-09 14:52:08 +01:00
/// Broadcast all messages since last issued block to get the peers up to speed.
2016-12-02 14:30:43 +01:00
fn broadcast_old_messages(&self) {
for m in self.votes.get_up_to(&VoteStep::new(self.height.load(AtomicOrdering::SeqCst), self.view.load(AtomicOrdering::SeqCst), Step::Precommit)).into_iter() {
self.broadcast_message(m);
2016-12-02 14:30:43 +01:00
}
}
2016-12-08 20:09:30 +01:00
fn to_next_height(&self, height: Height) {
let new_height = height + 1;
debug!(target: "engine", "Received a Commit, transitioning to height {}.", new_height);
self.last_lock.store(0, AtomicOrdering::SeqCst);
2016-12-08 20:09:30 +01:00
self.height.store(new_height, AtomicOrdering::SeqCst);
self.view.store(0, AtomicOrdering::SeqCst);
*self.lock_change.write() = None;
*self.proposal.write() = None;
}
/// Use via step_service to transition steps.
2016-11-16 19:01:09 +01:00
fn to_step(&self, step: Step) {
2016-12-10 16:50:23 +01:00
if let Err(io_err) = self.step_service.send_message(step) {
warn!(target: "engine", "Could not proceed to step {}.", io_err)
2016-12-10 16:50:23 +01:00
}
2016-11-16 19:01:09 +01:00
*self.step.write() = step;
match step {
2016-11-17 14:26:57 +01:00
Step::Propose => {
self.update_sealing()
},
2016-11-17 13:18:20 +01:00
Step::Prevote => {
2016-11-18 13:27:00 +01:00
let block_hash = match *self.lock_change.read() {
Some(ref m) if !self.should_unlock(m.vote_step.view) => m.block_hash,
2016-12-02 21:04:12 +01:00
_ => self.proposal.read().clone(),
2016-11-18 13:27:00 +01:00
};
2016-11-30 17:02:05 +01:00
self.generate_and_broadcast_message(block_hash);
2016-11-17 13:18:20 +01:00
},
Step::Precommit => {
trace!(target: "engine", "to_step: Precommit.");
2016-11-18 13:27:00 +01:00
let block_hash = match *self.lock_change.read() {
Some(ref m) if self.is_view(m) && m.block_hash.is_some() => {
trace!(target: "engine", "Setting last lock: {}", m.vote_step.view);
self.last_lock.store(m.vote_step.view, AtomicOrdering::SeqCst);
2016-11-18 13:27:00 +01:00
m.block_hash
},
_ => None,
2016-11-18 00:36:24 +01:00
};
2016-11-28 16:24:22 +01:00
self.generate_and_broadcast_message(block_hash);
2016-11-17 13:18:20 +01:00
},
Step::Commit => {
trace!(target: "engine", "to_step: Commit.");
2016-11-17 13:18:20 +01:00
},
2016-11-16 19:01:09 +01:00
}
}
2016-10-05 15:33:07 +02:00
fn is_authority(&self, address: &Address) -> bool {
self.validators.contains(&*self.proposal_parent.read(), address)
2016-08-26 19:27:50 +02:00
}
fn check_above_threshold(&self, n: usize) -> Result<(), EngineError> {
let threshold = self.validators.count(&*self.proposal_parent.read()) * 2/3;
if n > threshold {
Ok(())
} else {
Err(EngineError::BadSealFieldSize(OutOfBounds {
min: Some(threshold),
max: None,
found: n
}))
}
2016-08-24 11:58:49 +02:00
}
/// Find the designated for the given view.
fn view_proposer(&self, bh: &H256, height: Height, view: View) -> Address {
let proposer_nonce = height + view;
trace!(target: "engine", "Proposer nonce: {}", proposer_nonce);
self.validators.get(bh, proposer_nonce)
}
/// Check if address is a proposer for given view.
fn check_view_proposer(&self, bh: &H256, height: Height, view: View, address: &Address) -> Result<(), EngineError> {
let proposer = self.view_proposer(bh, height, view);
if proposer == *address {
2016-11-24 14:57:54 +01:00
Ok(())
} else {
Err(EngineError::NotProposer(Mismatch { expected: proposer, found: address.clone() }))
2016-11-24 14:57:54 +01:00
}
2016-11-15 11:21:18 +01:00
}
/// Check if current signer is the current proposer.
fn is_signer_proposer(&self, bh: &H256) -> bool {
let proposer = self.view_proposer(bh, self.height.load(AtomicOrdering::SeqCst), self.view.load(AtomicOrdering::SeqCst));
self.signer.read().is_address(&proposer)
}
2016-11-18 14:38:04 +01:00
fn is_height(&self, message: &ConsensusMessage) -> bool {
message.vote_step.is_height(self.height.load(AtomicOrdering::SeqCst))
}
fn is_view(&self, message: &ConsensusMessage) -> bool {
message.vote_step.is_view(self.height.load(AtomicOrdering::SeqCst), self.view.load(AtomicOrdering::SeqCst))
2016-11-18 13:27:00 +01:00
}
fn increment_view(&self, n: View) {
trace!(target: "engine", "increment_view: New view.");
self.view.fetch_add(n, AtomicOrdering::SeqCst);
2016-11-18 14:38:04 +01:00
}
fn should_unlock(&self, lock_change_view: View) -> bool {
self.last_lock.load(AtomicOrdering::SeqCst) < lock_change_view
&& lock_change_view < self.view.load(AtomicOrdering::SeqCst)
2016-11-18 14:38:04 +01:00
}
fn has_enough_any_votes(&self) -> bool {
let step_votes = self.votes.count_round_votes(&VoteStep::new(self.height.load(AtomicOrdering::SeqCst), self.view.load(AtomicOrdering::SeqCst), *self.step.read()));
self.check_above_threshold(step_votes).is_ok()
2016-11-16 19:01:09 +01:00
}
fn has_enough_future_step_votes(&self, vote_step: &VoteStep) -> bool {
if vote_step.view > self.view.load(AtomicOrdering::SeqCst) {
let step_votes = self.votes.count_round_votes(vote_step);
self.check_above_threshold(step_votes).is_ok()
} else {
false
}
2016-11-16 19:01:09 +01:00
}
fn has_enough_aligned_votes(&self, message: &ConsensusMessage) -> bool {
2016-11-28 15:08:38 +01:00
let aligned_count = self.votes.count_aligned_votes(&message);
self.check_above_threshold(aligned_count).is_ok()
2016-11-15 11:21:18 +01:00
}
2016-12-02 21:04:12 +01:00
fn handle_valid_message(&self, message: &ConsensusMessage) {
let ref vote_step = message.vote_step;
2016-12-02 21:04:12 +01:00
let is_newer_than_lock = match *self.lock_change.read() {
Some(ref lock) => vote_step > &lock.vote_step,
2016-12-02 21:04:12 +01:00
None => true,
};
2016-12-12 17:20:20 +01:00
let lock_change = is_newer_than_lock
&& vote_step.step == Step::Prevote
2016-12-02 21:04:12 +01:00
&& message.block_hash.is_some()
2016-12-12 17:20:20 +01:00
&& self.has_enough_aligned_votes(message);
if lock_change {
trace!(target: "engine", "handle_valid_message: Lock change.");
*self.lock_change.write() = Some(message.clone());
2016-12-02 21:04:12 +01:00
}
// Check if it can affect the step transition.
if self.is_height(message) {
let next_step = match *self.step.read() {
Step::Precommit if message.block_hash.is_none() && self.has_enough_aligned_votes(message) => {
self.increment_view(1);
Some(Step::Propose)
},
2016-12-02 21:04:12 +01:00
Step::Precommit if self.has_enough_aligned_votes(message) => {
let bh = message.block_hash.expect("previous guard ensures is_some; qed");
if *self.last_proposed.read() == bh {
// Commit the block using a complete signature set.
// Generate seal and remove old votes.
let precommits = self.votes.round_signatures(vote_step, &bh);
trace!(target: "engine", "Collected seal: {:?}", precommits);
let seal = vec![
::rlp::encode(&vote_step.view).into_vec(),
::rlp::NULL_RLP.to_vec(),
::rlp::encode_list(&precommits).into_vec()
];
self.submit_seal(bh, seal);
self.votes.throw_out_old(&vote_step);
2016-12-02 21:04:12 +01:00
}
self.to_next_height(self.height.load(AtomicOrdering::SeqCst));
Some(Step::Commit)
2016-12-02 21:04:12 +01:00
},
Step::Precommit if self.has_enough_future_step_votes(&vote_step) => {
self.increment_view(vote_step.view - self.view.load(AtomicOrdering::SeqCst));
2016-12-02 21:04:12 +01:00
Some(Step::Precommit)
},
// Avoid counting votes twice.
2016-12-12 17:20:20 +01:00
Step::Prevote if lock_change => Some(Step::Precommit),
2016-12-02 21:04:12 +01:00
Step::Prevote if self.has_enough_aligned_votes(message) => Some(Step::Precommit),
Step::Prevote if self.has_enough_future_step_votes(&vote_step) => {
self.increment_view(vote_step.view - self.view.load(AtomicOrdering::SeqCst));
2016-12-02 21:04:12 +01:00
Some(Step::Prevote)
},
_ => None,
};
if let Some(step) = next_step {
trace!(target: "engine", "Transition to {:?} triggered.", step);
2016-12-10 16:50:23 +01:00
self.to_step(step);
2016-12-02 21:04:12 +01:00
}
}
}
2016-11-15 11:21:18 +01:00
}
impl Engine<EthereumMachine> for Tendermint {
2016-08-23 12:58:40 +02:00
fn name(&self) -> &str { "Tendermint" }
/// (consensus view, proposal signature, authority signatures)
Aura: Broadcast empty step messages instead of creating empty blocks (#7605) * aura: broadcast empty step message instead of sealing empty block * aura: add empty_step messages to seal * aura: include parent_hash in empty step message * aura: verify received empty step messages * aura: verify empty step messages in block * aura: fix dead lock on empty_steps * aura: fix EmptyStep Encodable * aura: take number of empty steps into account in chain score * aura: use empty step signers for finality * aura: add empty "empty step" messages to seal when reading from spec * aura: fix EmptyStep rlp encoding * aura: use Vec<u8> instead of Bytes * aura: fix block empty step verification * Update .gitlab-ci.yml fix lint * aura: fix accumulation of empty step signatures for finality * aura: include empty steps in seal signature * aura: configurable max number of empty steps * engine: pass block header to seal_fields method This is necessary to make the number of seal fields dynamic, e.g. activating a transition on a certain block number that changes the seal. * aura: add transition to enable empty step messages * aura: clear old empty step messages on verify_block_external * aura: ignore empty step messages from the future * aura: report skipped primaries when empty steps are not enabled * aura: fix tests * aura: report misbehavior * aura: add tests for rolling finality with multiple signatures * engine: fix validator set test In this test the block validation wasn't failing because the block was in the future (expected failure) but was instead failing because the author of the block isn't the expected authority. Since we added reporting of blocks produced by the wrong authority this test started failing. * aura: reward all the authors of empty step messages * aura: fix reward attribution for new blocks * aura: add tests for empty steps broadcasting and inclusion in blocks * aura: reduce size of empty step messages in seal * aura: add test for empty step inclusion in blocks * aura: add test for rewarding of empty steps * aura: add test for empty steps validation * aura: fix rlp encoding of sealed empty step * aura: fix grumbles
2018-02-15 01:39:29 +01:00
fn seal_fields(&self, _header: &Header) -> usize { 3 }
2016-08-23 12:58:40 +02:00
fn machine(&self) -> &EthereumMachine { &self.machine }
2016-08-23 12:58:40 +02:00
fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 0 }
2016-12-07 09:32:36 +01:00
fn maximum_uncle_age(&self) -> usize { 0 }
fn populate_from_parent(&self, header: &mut Header, parent: &Header) {
// Chain scoring: total weight is sqrt(U256::max_value())*height - view
let new_difficulty = U256::from(U128::max_value())
+ consensus_view(parent).expect("Header has been verified; qed").into()
- self.view.load(AtomicOrdering::SeqCst).into();
header.set_difficulty(new_difficulty);
2016-08-23 12:58:40 +02:00
}
2016-12-01 18:21:51 +01:00
/// Should this node participate.
2017-02-20 16:35:53 +01:00
fn seals_internally(&self) -> Option<bool> {
Some(self.signer.read().is_some())
2016-10-11 19:37:31 +02:00
}
2016-08-23 12:58:40 +02:00
2016-12-08 12:03:34 +01:00
/// Attempt to seal generate a proposal seal.
///
/// This operation is synchronous and may (quite reasonably) not be available, in which case
/// `Seal::None` will be returned.
fn generate_seal(&self, block: &ExecutedBlock, _parent: &Header) -> Seal {
let header = block.header();
let author = header.author();
// Only proposer can generate seal if None was generated.
if !self.is_signer_proposer(header.parent_hash()) || self.proposal.read().is_some() {
return Seal::None;
}
2016-12-01 22:56:38 +01:00
let height = header.number() as Height;
let view = self.view.load(AtomicOrdering::SeqCst);
let bh = Some(header.bare_hash());
let vote_info = message_info_rlp(&VoteStep::new(height, view, Step::Propose), bh.clone());
if let Ok(signature) = self.sign(keccak(&vote_info)).map(Into::into) {
// Insert Propose vote.
debug!(target: "engine", "Submitting proposal {} at height {} view {}.", header.bare_hash(), height, view);
self.votes.vote(ConsensusMessage::new(signature, height, view, Step::Propose, bh), *author);
// Remember the owned block.
*self.last_proposed.write() = header.bare_hash();
// Remember proposal for later seal submission.
*self.proposal.write() = bh;
*self.proposal_parent.write() = header.parent_hash().clone();
Seal::Proposal(vec![
::rlp::encode(&view).into_vec(),
::rlp::encode(&signature).into_vec(),
::rlp::EMPTY_LIST_RLP.to_vec()
])
2016-10-05 15:33:07 +02:00
} else {
warn!(target: "engine", "generate_seal: FAIL: accounts secret key unavailable");
2016-12-08 12:03:34 +01:00
Seal::None
2016-10-05 15:33:07 +02:00
}
2016-08-23 12:58:40 +02:00
}
fn handle_message(&self, rlp: &[u8]) -> Result<(), EngineError> {
fn fmt_err<T: ::std::fmt::Debug>(x: T) -> EngineError {
EngineError::MalformedMessage(format!("{:?}", x))
}
let rlp = Rlp::new(rlp);
let message: ConsensusMessage = rlp.as_val().map_err(fmt_err)?;
if !self.votes.is_old_or_known(&message) {
let msg_hash = keccak(rlp.at(1).map_err(fmt_err)?.as_raw());
New Transaction Queue implementation (#8074) * Implementation of Verifier, Scoring and Ready. * Queue in progress. * TransactionPool. * Prepare for txpool release. * Miner refactor [WiP] * WiP reworking miner. * Make it compile. * Add some docs. * Split blockchain access to a separate file. * Work on miner API. * Fix ethcore tests. * Refactor miner interface for sealing/work packages. * Implement next nonce. * RPC compiles. * Implement couple of missing methdods for RPC. * Add transaction queue listeners. * Compiles! * Clean-up and parallelize. * Get rid of RefCell in header. * Revert "Get rid of RefCell in header." This reverts commit 0f2424c9b7319a786e1565ea2a8a6d801a21b4fb. * Override Sync requirement. * Fix status display. * Unify logging. * Extract some cheap checks. * Measurements and optimizations. * Fix scoring bug, heap size of bug and add cache * Disable tx queueing and parallel verification. * Make ethcore and ethcore-miner compile again. * Make RPC compile again. * Bunch of txpool tests. * Migrate transaction queue tests. * Nonce Cap * Nonce cap cache and tests. * Remove stale future transactions from the queue. * Optimize scoring and write some tests. * Simple penalization. * Clean up and support for different scoring algorithms. * Add CLI parameters for the new queue. * Remove banning queue. * Disable debug build. * Change per_sender limit to be 1% instead of 5% * Avoid cloning when propagating transactions. * Remove old todo. * Post-review fixes. * Fix miner options default. * Implement back ready transactions for light client. * Get rid of from_pending_block * Pass rejection reason. * Add more details to drop. * Rollback heap size of. * Avoid cloning hashes when propagating and include more details on rejection. * Fix tests. * Introduce nonces cache. * Remove uneccessary hashes allocation. * Lower the mem limit. * Re-enable parallel verification. * Add miner log. Don't check the type if not below min_gas_price. * Add more traces, fix disabling miner. * Fix creating pending blocks twice on AuRa authorities. * Fix tests. * re-use pending blocks in AuRa * Use reseal_min_period to prevent too frequent update_sealing. * Fix log to contain hash not sender. * Optimize local transactions. * Fix aura tests. * Update locks comments. * Get rid of unsafe Sync impl. * Review fixes. * Remove excessive matches. * Fix compilation errors. * Use new pool in private transactions. * Fix private-tx test. * Fix secret store tests. * Actually use gas_floor_target * Fix config tests. * Fix pool tests. * Address grumbles.
2018-04-13 17:34:27 +02:00
let sender = ethkey::public_to_address(
&ethkey::recover(&message.signature.into(), &msg_hash).map_err(fmt_err)?
);
if !self.is_authority(&sender) {
return Err(EngineError::NotAuthorized(sender));
}
self.broadcast_message(rlp.as_raw().to_vec());
if let Some(double) = self.votes.vote(message.clone(), sender) {
let height = message.vote_step.height as BlockNumber;
self.validators.report_malicious(&sender, height, height, ::rlp::encode(&double).into_vec());
return Err(EngineError::DoubleVote(sender));
}
trace!(target: "engine", "Handling a valid {:?} from {}.", message, sender);
2016-12-02 21:04:12 +01:00
self.handle_valid_message(&message);
2016-11-15 11:21:18 +01:00
}
2016-11-17 13:18:20 +01:00
Ok(())
2016-08-23 12:58:40 +02:00
}
fn on_new_block(&self, block: &mut ExecutedBlock, epoch_begin: bool) -> Result<(), Error> {
if !epoch_begin { return Ok(()) }
// genesis is never a new block, but might as well check.
let header = block.header().clone();
let first = header.number() == 0;
let mut call = |to, data| {
let result = self.machine.execute_as_system(
block,
to,
U256::max_value(), // unbounded gas? maybe make configurable.
Some(data),
);
result.map_err(|e| format!("{}", e))
};
self.validators.on_epoch_begin(first, &header, &mut call)
}
/// Apply the block reward on finalisation of the block.
fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error>{
use parity_machine::WithBalances;
Aura: Broadcast empty step messages instead of creating empty blocks (#7605) * aura: broadcast empty step message instead of sealing empty block * aura: add empty_step messages to seal * aura: include parent_hash in empty step message * aura: verify received empty step messages * aura: verify empty step messages in block * aura: fix dead lock on empty_steps * aura: fix EmptyStep Encodable * aura: take number of empty steps into account in chain score * aura: use empty step signers for finality * aura: add empty "empty step" messages to seal when reading from spec * aura: fix EmptyStep rlp encoding * aura: use Vec<u8> instead of Bytes * aura: fix block empty step verification * Update .gitlab-ci.yml fix lint * aura: fix accumulation of empty step signatures for finality * aura: include empty steps in seal signature * aura: configurable max number of empty steps * engine: pass block header to seal_fields method This is necessary to make the number of seal fields dynamic, e.g. activating a transition on a certain block number that changes the seal. * aura: add transition to enable empty step messages * aura: clear old empty step messages on verify_block_external * aura: ignore empty step messages from the future * aura: report skipped primaries when empty steps are not enabled * aura: fix tests * aura: report misbehavior * aura: add tests for rolling finality with multiple signatures * engine: fix validator set test In this test the block validation wasn't failing because the block was in the future (expected failure) but was instead failing because the author of the block isn't the expected authority. Since we added reporting of blocks produced by the wrong authority this test started failing. * aura: reward all the authors of empty step messages * aura: fix reward attribution for new blocks * aura: add tests for empty steps broadcasting and inclusion in blocks * aura: reduce size of empty step messages in seal * aura: add test for empty step inclusion in blocks * aura: add test for rewarding of empty steps * aura: add test for empty steps validation * aura: fix rlp encoding of sealed empty step * aura: fix grumbles
2018-02-15 01:39:29 +01:00
let author = *block.header().author();
self.machine.add_balance(block, &author, &self.block_reward)?;
self.machine.note_rewards(block, &[(author, self.block_reward)], &[])
}
fn verify_local_seal(&self, _header: &Header) -> Result<(), Error> {
Ok(())
}
fn verify_block_basic(&self, header: &Header) -> Result<(), Error> {
2016-09-30 13:22:46 +02:00
let seal_length = header.seal().len();
Aura: Broadcast empty step messages instead of creating empty blocks (#7605) * aura: broadcast empty step message instead of sealing empty block * aura: add empty_step messages to seal * aura: include parent_hash in empty step message * aura: verify received empty step messages * aura: verify empty step messages in block * aura: fix dead lock on empty_steps * aura: fix EmptyStep Encodable * aura: take number of empty steps into account in chain score * aura: use empty step signers for finality * aura: add empty "empty step" messages to seal when reading from spec * aura: fix EmptyStep rlp encoding * aura: use Vec<u8> instead of Bytes * aura: fix block empty step verification * Update .gitlab-ci.yml fix lint * aura: fix accumulation of empty step signatures for finality * aura: include empty steps in seal signature * aura: configurable max number of empty steps * engine: pass block header to seal_fields method This is necessary to make the number of seal fields dynamic, e.g. activating a transition on a certain block number that changes the seal. * aura: add transition to enable empty step messages * aura: clear old empty step messages on verify_block_external * aura: ignore empty step messages from the future * aura: report skipped primaries when empty steps are not enabled * aura: fix tests * aura: report misbehavior * aura: add tests for rolling finality with multiple signatures * engine: fix validator set test In this test the block validation wasn't failing because the block was in the future (expected failure) but was instead failing because the author of the block isn't the expected authority. Since we added reporting of blocks produced by the wrong authority this test started failing. * aura: reward all the authors of empty step messages * aura: fix reward attribution for new blocks * aura: add tests for empty steps broadcasting and inclusion in blocks * aura: reduce size of empty step messages in seal * aura: add test for empty step inclusion in blocks * aura: add test for rewarding of empty steps * aura: add test for empty steps validation * aura: fix rlp encoding of sealed empty step * aura: fix grumbles
2018-02-15 01:39:29 +01:00
let expected_seal_fields = self.seal_fields(header);
if seal_length == expected_seal_fields {
// Either proposal or commit.
2017-06-28 09:36:42 +02:00
if (header.seal()[1] == ::rlp::NULL_RLP)
!= (header.seal()[2] == ::rlp::EMPTY_LIST_RLP) {
2016-12-02 14:30:43 +01:00
Ok(())
} else {
warn!(target: "engine", "verify_block_basic: Block is neither a Commit nor Proposal.");
Err(BlockError::InvalidSeal.into())
2016-12-02 14:30:43 +01:00
}
2016-09-30 13:22:46 +02:00
} else {
Err(BlockError::InvalidSealArity(
Aura: Broadcast empty step messages instead of creating empty blocks (#7605) * aura: broadcast empty step message instead of sealing empty block * aura: add empty_step messages to seal * aura: include parent_hash in empty step message * aura: verify received empty step messages * aura: verify empty step messages in block * aura: fix dead lock on empty_steps * aura: fix EmptyStep Encodable * aura: take number of empty steps into account in chain score * aura: use empty step signers for finality * aura: add empty "empty step" messages to seal when reading from spec * aura: fix EmptyStep rlp encoding * aura: use Vec<u8> instead of Bytes * aura: fix block empty step verification * Update .gitlab-ci.yml fix lint * aura: fix accumulation of empty step signatures for finality * aura: include empty steps in seal signature * aura: configurable max number of empty steps * engine: pass block header to seal_fields method This is necessary to make the number of seal fields dynamic, e.g. activating a transition on a certain block number that changes the seal. * aura: add transition to enable empty step messages * aura: clear old empty step messages on verify_block_external * aura: ignore empty step messages from the future * aura: report skipped primaries when empty steps are not enabled * aura: fix tests * aura: report misbehavior * aura: add tests for rolling finality with multiple signatures * engine: fix validator set test In this test the block validation wasn't failing because the block was in the future (expected failure) but was instead failing because the author of the block isn't the expected authority. Since we added reporting of blocks produced by the wrong authority this test started failing. * aura: reward all the authors of empty step messages * aura: fix reward attribution for new blocks * aura: add tests for empty steps broadcasting and inclusion in blocks * aura: reduce size of empty step messages in seal * aura: add test for empty step inclusion in blocks * aura: add test for rewarding of empty steps * aura: add test for empty steps validation * aura: fix rlp encoding of sealed empty step * aura: fix grumbles
2018-02-15 01:39:29 +01:00
Mismatch { expected: expected_seal_fields, found: seal_length }
).into())
2016-08-23 12:58:40 +02:00
}
}
fn verify_block_external(&self, header: &Header) -> Result<(), Error> {
if let Ok(proposal) = ConsensusMessage::new_proposal(header) {
let proposer = proposal.verify()?;
if !self.is_authority(&proposer) {
return Err(EngineError::NotAuthorized(proposer).into());
2016-12-01 22:56:38 +01:00
}
self.check_view_proposer(
header.parent_hash(),
proposal.vote_step.height,
proposal.vote_step.view,
&proposer
).map_err(Into::into)
} else {
let vote_step = VoteStep::new(header.number() as usize, consensus_view(header)?, Step::Precommit);
let precommit_hash = message_hash(vote_step.clone(), header.bare_hash());
let ref signatures_field = header.seal().get(2).expect("block went through verify_block_basic; block has .seal_fields() fields; qed");
let mut origins = HashSet::new();
for rlp in Rlp::new(signatures_field).iter() {
let precommit = ConsensusMessage {
signature: rlp.as_val()?,
block_hash: Some(header.bare_hash()),
vote_step: vote_step.clone(),
};
let address = match self.votes.get(&precommit) {
Some(a) => a,
New Transaction Queue implementation (#8074) * Implementation of Verifier, Scoring and Ready. * Queue in progress. * TransactionPool. * Prepare for txpool release. * Miner refactor [WiP] * WiP reworking miner. * Make it compile. * Add some docs. * Split blockchain access to a separate file. * Work on miner API. * Fix ethcore tests. * Refactor miner interface for sealing/work packages. * Implement next nonce. * RPC compiles. * Implement couple of missing methdods for RPC. * Add transaction queue listeners. * Compiles! * Clean-up and parallelize. * Get rid of RefCell in header. * Revert "Get rid of RefCell in header." This reverts commit 0f2424c9b7319a786e1565ea2a8a6d801a21b4fb. * Override Sync requirement. * Fix status display. * Unify logging. * Extract some cheap checks. * Measurements and optimizations. * Fix scoring bug, heap size of bug and add cache * Disable tx queueing and parallel verification. * Make ethcore and ethcore-miner compile again. * Make RPC compile again. * Bunch of txpool tests. * Migrate transaction queue tests. * Nonce Cap * Nonce cap cache and tests. * Remove stale future transactions from the queue. * Optimize scoring and write some tests. * Simple penalization. * Clean up and support for different scoring algorithms. * Add CLI parameters for the new queue. * Remove banning queue. * Disable debug build. * Change per_sender limit to be 1% instead of 5% * Avoid cloning when propagating transactions. * Remove old todo. * Post-review fixes. * Fix miner options default. * Implement back ready transactions for light client. * Get rid of from_pending_block * Pass rejection reason. * Add more details to drop. * Rollback heap size of. * Avoid cloning hashes when propagating and include more details on rejection. * Fix tests. * Introduce nonces cache. * Remove uneccessary hashes allocation. * Lower the mem limit. * Re-enable parallel verification. * Add miner log. Don't check the type if not below min_gas_price. * Add more traces, fix disabling miner. * Fix creating pending blocks twice on AuRa authorities. * Fix tests. * re-use pending blocks in AuRa * Use reseal_min_period to prevent too frequent update_sealing. * Fix log to contain hash not sender. * Optimize local transactions. * Fix aura tests. * Update locks comments. * Get rid of unsafe Sync impl. * Review fixes. * Remove excessive matches. * Fix compilation errors. * Use new pool in private transactions. * Fix private-tx test. * Fix secret store tests. * Actually use gas_floor_target * Fix config tests. * Fix pool tests. * Address grumbles.
2018-04-13 17:34:27 +02:00
None => ethkey::public_to_address(&ethkey::recover(&precommit.signature.into(), &precommit_hash)?),
};
if !self.validators.contains(header.parent_hash(), &address) {
return Err(EngineError::NotAuthorized(address.to_owned()).into());
}
if !origins.insert(address) {
warn!(target: "engine", "verify_block_unordered: Duplicate signature from {} on the seal.", address);
return Err(BlockError::InvalidSeal.into());
}
2016-12-02 14:30:43 +01:00
}
2016-08-23 12:58:40 +02:00
self.check_above_threshold(origins.len()).map_err(Into::into)
2016-08-23 12:58:40 +02:00
}
}
fn signals_epoch_end(&self, header: &Header, aux: AuxiliaryData)
-> super::EpochChange<EthereumMachine>
{
let first = header.number() == 0;
self.validators.signals_epoch_end(first, header, aux)
}
fn is_epoch_end(
&self,
chain_head: &Header,
_chain: &super::Headers<Header>,
transition_store: &super::PendingTransitionStore,
) -> Option<Vec<u8>> {
let first = chain_head.number() == 0;
if let Some(change) = self.validators.is_epoch_end(first, chain_head) {
2017-08-04 21:43:31 +02:00
let change = combine_proofs(chain_head.number(), &change, &[]);
return Some(change)
} else if let Some(pending) = transition_store(chain_head.hash()) {
let signal_number = chain_head.number();
let finality_proof = ::rlp::encode(chain_head);
return Some(combine_proofs(signal_number, &pending.proof, &finality_proof))
}
None
}
fn epoch_verifier<'a>(&self, _header: &Header, proof: &'a [u8]) -> ConstructedVerifier<'a, EthereumMachine> {
let (signal_number, set_proof, finality_proof) = match destructure_proofs(proof) {
Ok(x) => x,
Err(e) => return ConstructedVerifier::Err(e),
};
let first = signal_number == 0;
match self.validators.epoch_set(first, &self.machine, signal_number, set_proof) {
Ok((list, finalize)) => {
let verifier = Box::new(EpochVerifier {
subchain_validators: list,
recover: |signature: &Signature, message: &Message| {
New Transaction Queue implementation (#8074) * Implementation of Verifier, Scoring and Ready. * Queue in progress. * TransactionPool. * Prepare for txpool release. * Miner refactor [WiP] * WiP reworking miner. * Make it compile. * Add some docs. * Split blockchain access to a separate file. * Work on miner API. * Fix ethcore tests. * Refactor miner interface for sealing/work packages. * Implement next nonce. * RPC compiles. * Implement couple of missing methdods for RPC. * Add transaction queue listeners. * Compiles! * Clean-up and parallelize. * Get rid of RefCell in header. * Revert "Get rid of RefCell in header." This reverts commit 0f2424c9b7319a786e1565ea2a8a6d801a21b4fb. * Override Sync requirement. * Fix status display. * Unify logging. * Extract some cheap checks. * Measurements and optimizations. * Fix scoring bug, heap size of bug and add cache * Disable tx queueing and parallel verification. * Make ethcore and ethcore-miner compile again. * Make RPC compile again. * Bunch of txpool tests. * Migrate transaction queue tests. * Nonce Cap * Nonce cap cache and tests. * Remove stale future transactions from the queue. * Optimize scoring and write some tests. * Simple penalization. * Clean up and support for different scoring algorithms. * Add CLI parameters for the new queue. * Remove banning queue. * Disable debug build. * Change per_sender limit to be 1% instead of 5% * Avoid cloning when propagating transactions. * Remove old todo. * Post-review fixes. * Fix miner options default. * Implement back ready transactions for light client. * Get rid of from_pending_block * Pass rejection reason. * Add more details to drop. * Rollback heap size of. * Avoid cloning hashes when propagating and include more details on rejection. * Fix tests. * Introduce nonces cache. * Remove uneccessary hashes allocation. * Lower the mem limit. * Re-enable parallel verification. * Add miner log. Don't check the type if not below min_gas_price. * Add more traces, fix disabling miner. * Fix creating pending blocks twice on AuRa authorities. * Fix tests. * re-use pending blocks in AuRa * Use reseal_min_period to prevent too frequent update_sealing. * Fix log to contain hash not sender. * Optimize local transactions. * Fix aura tests. * Update locks comments. * Get rid of unsafe Sync impl. * Review fixes. * Remove excessive matches. * Fix compilation errors. * Use new pool in private transactions. * Fix private-tx test. * Fix secret store tests. * Actually use gas_floor_target * Fix config tests. * Fix pool tests. * Address grumbles.
2018-04-13 17:34:27 +02:00
Ok(ethkey::public_to_address(&ethkey::recover(&signature, &message)?))
},
});
match finalize {
Some(finalize) => ConstructedVerifier::Unconfirmed(verifier, finality_proof, finalize),
None => ConstructedVerifier::Trusted(verifier),
}
}
Err(e) => ConstructedVerifier::Err(e),
}
}
fn set_signer(&self, ap: Arc<AccountProvider>, address: Address, password: String) {
{
self.signer.write().set(ap, address, password);
}
2016-12-10 16:50:23 +01:00
self.to_step(Step::Propose);
2016-11-30 13:59:33 +01:00
}
fn sign(&self, hash: H256) -> Result<Signature, Error> {
New Transaction Queue implementation (#8074) * Implementation of Verifier, Scoring and Ready. * Queue in progress. * TransactionPool. * Prepare for txpool release. * Miner refactor [WiP] * WiP reworking miner. * Make it compile. * Add some docs. * Split blockchain access to a separate file. * Work on miner API. * Fix ethcore tests. * Refactor miner interface for sealing/work packages. * Implement next nonce. * RPC compiles. * Implement couple of missing methdods for RPC. * Add transaction queue listeners. * Compiles! * Clean-up and parallelize. * Get rid of RefCell in header. * Revert "Get rid of RefCell in header." This reverts commit 0f2424c9b7319a786e1565ea2a8a6d801a21b4fb. * Override Sync requirement. * Fix status display. * Unify logging. * Extract some cheap checks. * Measurements and optimizations. * Fix scoring bug, heap size of bug and add cache * Disable tx queueing and parallel verification. * Make ethcore and ethcore-miner compile again. * Make RPC compile again. * Bunch of txpool tests. * Migrate transaction queue tests. * Nonce Cap * Nonce cap cache and tests. * Remove stale future transactions from the queue. * Optimize scoring and write some tests. * Simple penalization. * Clean up and support for different scoring algorithms. * Add CLI parameters for the new queue. * Remove banning queue. * Disable debug build. * Change per_sender limit to be 1% instead of 5% * Avoid cloning when propagating transactions. * Remove old todo. * Post-review fixes. * Fix miner options default. * Implement back ready transactions for light client. * Get rid of from_pending_block * Pass rejection reason. * Add more details to drop. * Rollback heap size of. * Avoid cloning hashes when propagating and include more details on rejection. * Fix tests. * Introduce nonces cache. * Remove uneccessary hashes allocation. * Lower the mem limit. * Re-enable parallel verification. * Add miner log. Don't check the type if not below min_gas_price. * Add more traces, fix disabling miner. * Fix creating pending blocks twice on AuRa authorities. * Fix tests. * re-use pending blocks in AuRa * Use reseal_min_period to prevent too frequent update_sealing. * Fix log to contain hash not sender. * Optimize local transactions. * Fix aura tests. * Update locks comments. * Get rid of unsafe Sync impl. * Review fixes. * Remove excessive matches. * Fix compilation errors. * Use new pool in private transactions. * Fix private-tx test. * Fix secret store tests. * Actually use gas_floor_target * Fix config tests. * Fix pool tests. * Address grumbles.
2018-04-13 17:34:27 +02:00
Ok(self.signer.read().sign(hash)?)
}
fn snapshot_components(&self) -> Option<Box<::snapshot::SnapshotComponents>> {
Some(Box::new(::snapshot::PoaSnapshot))
}
2016-12-09 14:52:08 +01:00
fn stop(&self) {
self.step_service.stop()
}
2016-12-08 12:03:34 +01:00
fn is_proposal(&self, header: &Header) -> bool {
let signatures_len = header.seal()[2].len();
// Signatures have to be an empty list rlp.
if signatures_len != 1 {
2016-12-08 20:09:30 +01:00
// New Commit received, skip to next height.
trace!(target: "engine", "Received a commit: {:?}.", header.number());
self.to_next_height(header.number() as usize);
self.to_step(Step::Commit);
2016-12-08 12:03:34 +01:00
return false;
}
let proposal = ConsensusMessage::new_proposal(header).expect("block went through full verification; this Engine verifies new_proposal creation; qed");
2016-12-08 12:03:34 +01:00
let proposer = proposal.verify().expect("block went through full verification; this Engine tries verify; qed");
debug!(target: "engine", "Received a new proposal {:?} from {}.", proposal.vote_step, proposer);
if self.is_view(&proposal) {
2016-12-08 12:03:34 +01:00
*self.proposal.write() = proposal.block_hash.clone();
*self.proposal_parent.write() = header.parent_hash().clone();
2016-12-08 12:03:34 +01:00
}
self.votes.vote(proposal, proposer);
2016-12-08 12:03:34 +01:00
true
}
2016-12-10 16:50:23 +01:00
/// Equivalent to a timeout: to be used for tests.
fn step(&self) {
let next_step = match *self.step.read() {
Step::Propose => {
trace!(target: "engine", "Propose timeout.");
if self.proposal.read().is_none() {
// Report the proposer if no proposal was received.
let height = self.height.load(AtomicOrdering::SeqCst);
let current_proposer = self.view_proposer(&*self.proposal_parent.read(), height, self.view.load(AtomicOrdering::SeqCst));
self.validators.report_benign(&current_proposer, height as BlockNumber, height as BlockNumber);
}
2016-12-10 16:50:23 +01:00
Step::Prevote
},
Step::Prevote if self.has_enough_any_votes() => {
trace!(target: "engine", "Prevote timeout.");
2016-12-10 16:50:23 +01:00
Step::Precommit
},
Step::Prevote => {
trace!(target: "engine", "Prevote timeout without enough votes.");
2016-12-10 16:50:23 +01:00
self.broadcast_old_messages();
Step::Prevote
},
Step::Precommit if self.has_enough_any_votes() => {
trace!(target: "engine", "Precommit timeout.");
self.increment_view(1);
2016-12-10 16:50:23 +01:00
Step::Propose
},
Step::Precommit => {
trace!(target: "engine", "Precommit timeout without enough votes.");
2016-12-10 16:50:23 +01:00
self.broadcast_old_messages();
Step::Precommit
},
Step::Commit => {
trace!(target: "engine", "Commit timeout.");
2016-12-10 16:50:23 +01:00
Step::Propose
},
};
self.to_step(next_step);
}
fn register_client(&self, client: Weak<EngineClient>) {
if let Some(c) = client.upgrade() {
self.height.store(c.chain_info().best_block_number as usize + 1, AtomicOrdering::SeqCst);
}
*self.client.write() = Some(client.clone());
self.validators.register_client(client);
2016-11-18 00:36:24 +01:00
}
2016-08-23 12:58:40 +02:00
}
#[cfg(test)]
mod tests {
2017-07-29 17:12:07 +02:00
use std::str::FromStr;
use rustc_hex::FromHex;
use ethereum_types::Address;
use bytes::Bytes;
2016-08-23 12:58:40 +02:00
use block::*;
use error::{Error, BlockError};
use header::Header;
`Client` refactoring (#7038) * Improves `BestBlock` comment * Improves `TraceDB` comment * Improves `journaldb::Algorithm` comment. Probably the whole enum should be renamed to `Strategy` or something alike. * Comments some of the `Client`'s fields * Deglobs client imports * Fixes comments * Extracts `import_lock` to `Importer` struct * Extracts `verifier` to `Importer` struct * Extracts `block_queue` to `Importer` struct * Extracts `miner` to `Importer` struct * Extracts `ancient_verifier` to `Importer` struct * Extracts `rng` to `Importer` struct * Extracts `import_old_block` to `Importer` struct * Adds `Nonce` trait * Adds `Balance` trait * Adds `ChainInfo` trait * Fixes imports for tests using `chain_info` method * Adds `BlockInfo` trait * Adds more `ChainInfo` imports * Adds `BlockInfo` imports * Adds `ReopenBlock` trait * Adds `PrepareOpenBlock` trait * Fixes import in tests * Adds `CallContract` trait * Fixes imports in tests using `call_contract` method * Adds `TransactionInfo` trait * Adds `RegistryInfo` trait * Fixes imports in tests using `registry_address` method * Adds `ScheduleInfo` trait * Adds `ImportSealedBlock` trait * Fixes imports in test using `import_sealed_block` method * Adds `BroadcastProposalBlock` trait * Migrates `Miner` to static dispatch * Fixes tests * Moves `calculate_enacted_retracted` to `Importer` * Moves import-related methods to `Importer` * Removes redundant `import_old_block` wrapper * Extracts `import_block*` into separate trait * Fixes tests * Handles `Pending` in `LightFetch` * Handles `Pending` in filters * Handles `Pending` in `ParityClient` * Handles `Pending` in `EthClient` * Removes `BlockId::Pending`, partly refactors dependent code * Adds `StateInfo` trait * Exports `StateOrBlock` and `BlockChain` types from `client` module * Refactors `balance` RPC using generic API * Refactors `storage_at` RPC using generic API * Makes `MinerService::pending_state`'s return type dynamic * Adds `StateOrBlock` and `BlockChain` types * Adds impl of `client::BlockChain` for `Client` * Exports `StateInfo` trait from `client` module * Missing `self` use To be fixed up to "Adds impl of `client::BlockChain` for `Client`" * Adds `number_to_id` and refactors dependent RPC methods * Refactors `code_at` using generic API * Adds `StateClient` trait * Refactors RPC to use `StateClient` trait * Reverts `client::BlockChain` trait stuff, refactors methods to accept `StateOrBlock` * Refactors TestClient * Adds helper function `block_number_to_id` * Uses `block_number_to_id` instead of local function * Handles `Pending` in `list_accounts` and `list_storage_keys` * Attempt to use associated types for state instead of trait objects * Simplifies `state_at_beginning` * Extracts `call` and `call_many` into separate trait * Refactors `build_last_hashes` to accept reference * Exports `Call` type from the module * Refactors `call` and `call_many` to accept state and header * Exports `state_at` in `StateClient` * Exports `pending_block_header` from `MinerService` * Refactors RPC `call` method using new API * Adds missing parentheses * Refactors `parity::call` to use new call API * Update .gitlab-ci.yml fix gitlab lint * Fixes error handling * Refactors `traces::call` and `call_many` to use new call API * Refactors `call_contract` * Refactors `block_header` * Refactors internal RPC method `block` * Moves `estimate_gas` to `Call` trait, refactors parameters * Refactors `estimate_gas` in RPC * Refactors `uncle` * Refactors RPC `transaction` * Covers missing branches * Makes it all compile, fixes compiler grumbles * Adds casts in `blockchain` module * Fixes `PendingBlock` tests, work on `MinerService` * Adds test stubs for StateClient and EngineInfo * Makes `state_db` public * Adds missing impls for `TestBlockChainClient` * Adds trait documentation * Adds missing docs to the `state_db` module * Fixes trivial compilation errors * Moves `code_hash` method to a `BlockInfo` trait * Refactors `Verifier` to be generic over client * Refactors `TransactionFilter` to be generic over client * Refactors `Miner` and `Client` to reflect changes in verifier and txfilter API * Moves `ServiceTransactionChecker` back to `ethcore` * Fixes trait bounds in `Miner` API * Fixes `Client` * Fixes lifetime bound in `FullFamilyParams` * Adds comments to `FullFamilyParams` * Fixes imports in `ethcore` * Fixes BlockNumber handling in `code_at` and `replay_block_transactions` * fix compile issues * First step to redundant trait merge * Fixes compilation error in RPC tests * Adds mock `State` as a stub for `TestClient` * Handles `StateOrBlock::State` in `TestBlockChainClient::balance` * Fixes `transaction_count` RPC * Fixes `transaction_count` * Moves `service_transaction.json` to the `contracts` subfolder * Fixes compilation errors in tests * Refactors client to use `AccountData` * Refactors client to use `BlockChain` * Refactors miner to use aggregate traits * Adds `SealedBlockImporter` trait * Refactors miner to use `SealedBlockImporter` trait * Removes unused imports * Simplifies `RegistryInfo::registry_address` * Fixes indentation * Removes commented out trait bound
2018-03-03 18:42:13 +01:00
use client::ChainInfo;
use miner::MinerService;
Private transactions integration pr (#6422) * Private transaction message added * Empty line removed * Private transactions logic removed from client into the separate module * Fixed compilation after merge with head * Signed private transaction message added as well * Comments after the review fixed * Private tx execution * Test update * Renamed some methods * Fixed some tests * Reverted submodules * Fixed build * Private transaction message added * Empty line removed * Private transactions logic removed from client into the separate module * Fixed compilation after merge with head * Signed private transaction message added as well * Comments after the review fixed * Encrypted private transaction message and signed reply added * Private tx execution * Test update * Main scenario completed * Merged with the latest head * Private transactions API * Comments after review fixed * Parameters for private transactions added to parity arguments * New files added * New API methods added * Do not process packets from unconfirmed peers * Merge with ptm_ss branch * Encryption and permissioning with key server added * Fixed compilation after merge * Version of Parity protocol incremented in order to support private transactions * Doc strings for constants added * Proper format for doc string added * fixed some encryptor.rs grumbles * Private transactions functionality moved to the separate crate * Refactoring in order to remove late initialisation * Tests fixed after moving to the separate crate * Fetch method removed * Sync test helpers refactored * Interaction with encryptor refactored * Contract address retrieving via substate removed * Sensible gas limit for private transactions implemented * New private contract with nonces added * Parsing of the response from key server fixed * Build fixed after the merge, native contracts removed * Crate renamed * Tests moved to the separate directory * Handling of errors reworked in order to use error chain * Encodable macro added, new constructor replaced with default * Native ethabi usage removed * Couple conversions optimized * Interactions with client reworked * Errors omitting removed * Fix after merge * Fix after the merge * private transactions improvements in progress * private_transactions -> ethcore/private-tx * making private transactions more idiomatic * private-tx encryptor uses shared FetchClient and is more idiomatic * removed redundant tests, moved integration tests to tests/ dir * fixed failing service test * reenable add_notify on private tx provider * removed private_tx tests from sync module * removed commented out code * Use plain password instead of unlocking account manager * remove dead code * Link to the contract changed * Transaction signature chain replay protection module created * Redundant type conversion removed * Contract address returned by private provider * Test fixed * Addressing grumbles in PrivateTransactions (#8249) * Tiny fixes part 1. * A bunch of additional comments and todos. * Fix ethsync tests. * resolved merge conflicts * final private tx pr (#8318) * added cli option that enables private transactions * fixed failing test * fixed failing test * fixed failing test * fixed failing test
2018-04-09 16:14:33 +02:00
use test_helpers::{
2018-03-12 18:05:52 +01:00
TestNotify, get_temp_state_db, generate_dummy_client,
generate_dummy_client_with_spec_and_accounts
};
2016-08-23 12:58:40 +02:00
use account_provider::AccountProvider;
use spec::Spec;
use engines::{EthEngine, EngineError, Seal};
use engines::epoch::EpochVerifier;
use super::*;
2016-08-23 12:58:40 +02:00
/// Accounts inserted with "0" and "1" are validators. First proposer is "0".
fn setup() -> (Spec, Arc<AccountProvider>) {
let tap = Arc::new(AccountProvider::transient_provider());
let spec = Spec::new_test_tendermint();
(spec, tap)
}
fn propose_default(spec: &Spec, proposer: Address) -> (ClosedBlock, Vec<Bytes>) {
2017-04-06 19:26:17 +02:00
let db = get_temp_state_db();
let db = spec.ensure_db_good(db, &Default::default()).unwrap();
2016-11-24 20:59:08 +01:00
let genesis_header = spec.genesis_header();
let last_hashes = Arc::new(vec![genesis_header.hash()]);
let b = OpenBlock::new(spec.engine.as_ref(), Default::default(), false, db.boxed_clone(), &genesis_header, last_hashes, proposer, (3141562.into(), 31415620.into()), vec![], false).unwrap();
let b = b.close();
if let Seal::Proposal(seal) = spec.engine.generate_seal(b.block(), &genesis_header) {
2016-12-08 12:03:34 +01:00
(b, seal)
} else {
panic!()
}
2016-08-26 19:27:50 +02:00
}
fn vote<F>(engine: &EthEngine, signer: F, height: usize, view: usize, step: Step, block_hash: Option<H256>) -> Bytes where F: FnOnce(H256) -> Result<H520, ::account_provider::SignError> {
let mi = message_info_rlp(&VoteStep::new(height, view, step), block_hash);
let m = message_full_rlp(&signer(keccak(&mi)).unwrap().into(), &mi);
engine.handle_message(&m).unwrap();
2016-12-09 20:48:05 +01:00
m
2016-09-07 16:25:42 +02:00
}
fn proposal_seal(tap: &Arc<AccountProvider>, header: &Header, view: View) -> Vec<Bytes> {
2016-11-21 19:54:16 +01:00
let author = header.author();
let vote_info = message_info_rlp(&VoteStep::new(header.number() as Height, view, Step::Propose), Some(header.bare_hash()));
let signature = tap.sign(*author, None, keccak(vote_info)).unwrap();
2016-11-21 19:54:16 +01:00
vec![
::rlp::encode(&view).into_vec(),
::rlp::encode(&H520::from(signature)).into_vec(),
2017-06-28 16:41:08 +02:00
::rlp::EMPTY_LIST_RLP.to_vec()
2016-11-21 19:54:16 +01:00
]
2016-09-07 16:25:42 +02:00
}
fn insert_and_unlock(tap: &Arc<AccountProvider>, acc: &str) -> Address {
let addr = tap.insert_account(keccak(acc).into(), acc).unwrap();
tap.unlock_account_permanently(addr, acc.into()).unwrap();
addr
}
fn insert_and_register(tap: &Arc<AccountProvider>, engine: &EthEngine, acc: &str) -> Address {
2016-12-05 14:24:22 +01:00
let addr = insert_and_unlock(tap, acc);
engine.set_signer(tap.clone(), addr.clone(), acc.into());
2016-11-30 15:30:21 +01:00
addr
}
2016-08-23 12:58:40 +02:00
#[test]
fn has_valid_metadata() {
2016-09-29 17:57:52 +02:00
let engine = Spec::new_test_tendermint().engine;
2016-08-23 12:58:40 +02:00
assert!(!engine.name().is_empty());
}
#[test]
fn can_return_schedule() {
2016-09-29 17:57:52 +02:00
let engine = Spec::new_test_tendermint().engine;
let schedule = engine.schedule(10000000);
2016-08-23 12:58:40 +02:00
assert!(schedule.stack_limit > 0);
}
#[test]
2016-09-07 16:25:42 +02:00
fn verification_fails_on_short_seal() {
2016-09-29 17:57:52 +02:00
let engine = Spec::new_test_tendermint().engine;
2016-11-24 14:57:54 +01:00
let header = Header::default();
2016-08-23 12:58:40 +02:00
let verify_result = engine.verify_block_basic(&header);
2016-08-23 12:58:40 +02:00
match verify_result {
Err(Error::Block(BlockError::InvalidSealArity(_))) => {},
Err(_) => { panic!("should be block seal-arity mismatch error (got {:?})", verify_result); },
_ => { panic!("Should be error, got Ok"); },
}
}
#[test]
fn allows_correct_proposer() {
let (spec, tap) = setup();
let engine = spec.engine;
let mut parent_header: Header = Header::default();
parent_header.set_gas_limit(U256::from_str("222222").unwrap());
let mut header = Header::default();
header.set_number(1);
header.set_gas_limit(U256::from_str("222222").unwrap());
let validator = insert_and_unlock(&tap, "1");
header.set_author(validator);
let seal = proposal_seal(&tap, &header, 0);
header.set_seal(seal);
// Good proposer.
assert!(engine.verify_block_external(&header).is_ok());
let validator = insert_and_unlock(&tap, "0");
2016-12-02 14:59:54 +01:00
header.set_author(validator);
let seal = proposal_seal(&tap, &header, 0);
header.set_seal(seal);
// Bad proposer.
match engine.verify_block_external(&header) {
2016-11-29 13:51:27 +01:00
Err(Error::Engine(EngineError::NotProposer(_))) => {},
2016-11-24 14:57:54 +01:00
_ => panic!(),
}
2016-12-02 14:59:54 +01:00
let random = insert_and_unlock(&tap, "101");
header.set_author(random);
let seal = proposal_seal(&tap, &header, 0);
header.set_seal(seal);
// Not authority.
match engine.verify_block_external(&header) {
2016-12-02 14:59:54 +01:00
Err(Error::Engine(EngineError::NotAuthorized(_))) => {},
_ => panic!(),
2016-12-09 14:52:08 +01:00
};
engine.stop();
}
2016-08-23 12:58:40 +02:00
#[test]
2016-11-24 14:57:54 +01:00
fn seal_signatures_checking() {
let (spec, tap) = setup();
let engine = spec.engine;
2016-09-07 16:25:42 +02:00
let mut parent_header: Header = Header::default();
parent_header.set_gas_limit(U256::from_str("222222").unwrap());
2016-11-24 14:57:54 +01:00
let mut header = Header::default();
header.set_number(2);
header.set_gas_limit(U256::from_str("222222").unwrap());
2016-11-30 17:40:16 +01:00
let proposer = insert_and_unlock(&tap, "1");
2016-11-24 14:57:54 +01:00
header.set_author(proposer);
let mut seal = proposal_seal(&tap, &header, 0);
2016-09-07 16:25:42 +02:00
let vote_info = message_info_rlp(&VoteStep::new(2, 0, Step::Precommit), Some(header.bare_hash()));
let signature1 = tap.sign(proposer, None, keccak(&vote_info)).unwrap();
2016-12-02 14:30:43 +01:00
2017-06-28 16:41:08 +02:00
seal[1] = ::rlp::NULL_RLP.to_vec();
seal[2] = ::rlp::encode_list(&vec![H520::from(signature1.clone())]).into_vec();
2016-12-02 14:30:43 +01:00
header.set_seal(seal.clone());
2016-09-07 16:25:42 +02:00
2016-12-02 14:30:43 +01:00
// One good signature is not enough.
match engine.verify_block_external(&header) {
2016-12-02 14:30:43 +01:00
Err(Error::Engine(EngineError::BadSealFieldSize(_))) => {},
_ => panic!(),
}
2016-09-07 16:25:42 +02:00
2016-12-02 14:30:43 +01:00
let voter = insert_and_unlock(&tap, "0");
let signature0 = tap.sign(voter, None, keccak(&vote_info)).unwrap();
2016-12-02 14:30:43 +01:00
seal[2] = ::rlp::encode_list(&vec![H520::from(signature1.clone()), H520::from(signature0.clone())]).into_vec();
2016-11-24 14:57:54 +01:00
header.set_seal(seal.clone());
2016-09-07 16:25:42 +02:00
assert!(engine.verify_block_external(&header).is_ok());
2016-09-07 16:25:42 +02:00
2016-11-24 14:57:54 +01:00
let bad_voter = insert_and_unlock(&tap, "101");
let bad_signature = tap.sign(bad_voter, None, keccak(vote_info)).unwrap();
2016-09-07 16:25:42 +02:00
seal[2] = ::rlp::encode_list(&vec![H520::from(signature1), H520::from(bad_signature)]).into_vec();
2016-09-07 16:25:42 +02:00
header.set_seal(seal);
2016-11-24 14:57:54 +01:00
// One good and one bad signature.
match engine.verify_block_external(&header) {
2016-11-29 13:51:27 +01:00
Err(Error::Engine(EngineError::NotAuthorized(_))) => {},
2016-11-24 14:57:54 +01:00
_ => panic!(),
2016-12-09 14:52:08 +01:00
};
engine.stop();
2016-09-07 16:25:42 +02:00
}
#[test]
fn can_generate_seal() {
2016-11-24 14:57:54 +01:00
let (spec, tap) = setup();
2016-11-30 15:30:21 +01:00
let proposer = insert_and_register(&tap, spec.engine.as_ref(), "1");
2016-09-07 16:25:42 +02:00
2016-11-24 20:59:08 +01:00
let (b, seal) = propose_default(&spec, proposer);
assert!(b.lock().try_seal(spec.engine.as_ref(), seal).is_ok());
2016-08-24 15:55:47 +02:00
}
2016-12-09 20:48:05 +01:00
#[test]
fn can_recognize_proposal() {
let (spec, tap) = setup();
let proposer = insert_and_register(&tap, spec.engine.as_ref(), "1");
2016-12-09 20:48:05 +01:00
let (b, seal) = propose_default(&spec, proposer);
let sealed = b.lock().seal(spec.engine.as_ref(), seal).unwrap();
2016-12-09 20:48:05 +01:00
assert!(spec.engine.is_proposal(sealed.header()));
}
#[test]
fn relays_messages() {
let (spec, tap) = setup();
let engine = spec.engine.clone();
let v0 = insert_and_unlock(&tap, "0");
let v1 = insert_and_register(&tap, engine.as_ref(), "1");
2016-12-09 20:48:05 +01:00
let h = 1;
2016-12-09 20:48:05 +01:00
let r = 0;
// Propose
let (b, _) = propose_default(&spec, v1.clone());
let proposal = Some(b.header().bare_hash());
let client = generate_dummy_client(0);
let notify = Arc::new(TestNotify::default());
client.add_notify(notify.clone());
engine.register_client(Arc::downgrade(&client) as _);
2016-12-09 20:48:05 +01:00
let prevote_current = vote(engine.as_ref(), |mh| tap.sign(v0, None, mh).map(H520::from), h, r, Step::Prevote, proposal);
2016-12-09 20:48:05 +01:00
let precommit_current = vote(engine.as_ref(), |mh| tap.sign(v0, None, mh).map(H520::from), h, r, Step::Precommit, proposal);
2016-12-09 20:48:05 +01:00
let prevote_future = vote(engine.as_ref(), |mh| tap.sign(v0, None, mh).map(H520::from), h + 1, r, Step::Prevote, proposal);
2016-12-09 20:48:05 +01:00
// Relays all valid present and future messages.
assert!(notify.messages.read().contains(&prevote_current));
assert!(notify.messages.read().contains(&precommit_current));
assert!(notify.messages.read().contains(&prevote_future));
2016-12-09 20:48:05 +01:00
}
2016-08-23 12:58:40 +02:00
#[test]
2016-12-09 20:48:05 +01:00
fn seal_submission() {
use ethkey::{Generator, Random};
use transaction::{Transaction, Action};
2016-09-07 16:25:42 +02:00
let tap = Arc::new(AccountProvider::transient_provider());
// Accounts for signing votes.
let v0 = insert_and_unlock(&tap, "0");
let v1 = insert_and_unlock(&tap, "1");
let client = generate_dummy_client_with_spec_and_accounts(Spec::new_test_tendermint, Some(tap.clone()));
let engine = client.engine();
New Transaction Queue implementation (#8074) * Implementation of Verifier, Scoring and Ready. * Queue in progress. * TransactionPool. * Prepare for txpool release. * Miner refactor [WiP] * WiP reworking miner. * Make it compile. * Add some docs. * Split blockchain access to a separate file. * Work on miner API. * Fix ethcore tests. * Refactor miner interface for sealing/work packages. * Implement next nonce. * RPC compiles. * Implement couple of missing methdods for RPC. * Add transaction queue listeners. * Compiles! * Clean-up and parallelize. * Get rid of RefCell in header. * Revert "Get rid of RefCell in header." This reverts commit 0f2424c9b7319a786e1565ea2a8a6d801a21b4fb. * Override Sync requirement. * Fix status display. * Unify logging. * Extract some cheap checks. * Measurements and optimizations. * Fix scoring bug, heap size of bug and add cache * Disable tx queueing and parallel verification. * Make ethcore and ethcore-miner compile again. * Make RPC compile again. * Bunch of txpool tests. * Migrate transaction queue tests. * Nonce Cap * Nonce cap cache and tests. * Remove stale future transactions from the queue. * Optimize scoring and write some tests. * Simple penalization. * Clean up and support for different scoring algorithms. * Add CLI parameters for the new queue. * Remove banning queue. * Disable debug build. * Change per_sender limit to be 1% instead of 5% * Avoid cloning when propagating transactions. * Remove old todo. * Post-review fixes. * Fix miner options default. * Implement back ready transactions for light client. * Get rid of from_pending_block * Pass rejection reason. * Add more details to drop. * Rollback heap size of. * Avoid cloning hashes when propagating and include more details on rejection. * Fix tests. * Introduce nonces cache. * Remove uneccessary hashes allocation. * Lower the mem limit. * Re-enable parallel verification. * Add miner log. Don't check the type if not below min_gas_price. * Add more traces, fix disabling miner. * Fix creating pending blocks twice on AuRa authorities. * Fix tests. * re-use pending blocks in AuRa * Use reseal_min_period to prevent too frequent update_sealing. * Fix log to contain hash not sender. * Optimize local transactions. * Fix aura tests. * Update locks comments. * Get rid of unsafe Sync impl. * Review fixes. * Remove excessive matches. * Fix compilation errors. * Use new pool in private transactions. * Fix private-tx test. * Fix secret store tests. * Actually use gas_floor_target * Fix config tests. * Fix pool tests. * Address grumbles.
2018-04-13 17:34:27 +02:00
client.miner().set_author(v1.clone(), Some("1".into())).unwrap();
let notify = Arc::new(TestNotify::default());
client.add_notify(notify.clone());
engine.register_client(Arc::downgrade(&client) as _);
2016-11-28 10:42:50 +01:00
let keypair = Random.generate().unwrap();
let transaction = Transaction {
action: Action::Create,
value: U256::zero(),
data: "3331600055".from_hex().unwrap(),
gas: U256::from(100_000),
gas_price: U256::zero(),
nonce: U256::zero(),
}.sign(keypair.secret(), None);
client.miner().import_own_transaction(client.as_ref(), transaction.into()).unwrap();
2016-12-12 17:20:20 +01:00
// Propose
let proposal = Some(client.miner().pending_block(0).unwrap().header.bare_hash());
// Propose timeout
2016-12-12 17:20:20 +01:00
engine.step();
let h = 1;
let r = 0;
2016-12-09 20:48:05 +01:00
// Prevote.
vote(engine, |mh| tap.sign(v1, None, mh).map(H520::from), h, r, Step::Prevote, proposal);
vote(engine, |mh| tap.sign(v0, None, mh).map(H520::from), h, r, Step::Prevote, proposal);
vote(engine, |mh| tap.sign(v1, None, mh).map(H520::from), h, r, Step::Precommit, proposal);
2016-12-09 20:48:05 +01:00
assert_eq!(client.chain_info().best_block_number, 0);
// Last precommit.
vote(engine, |mh| tap.sign(v0, None, mh).map(H520::from), h, r, Step::Precommit, proposal);
assert_eq!(client.chain_info().best_block_number, 1);
2016-08-23 12:58:40 +02:00
}
#[test]
fn epoch_verifier_verify_light() {
use ethkey::Error as EthkeyError;
let (spec, tap) = setup();
let engine = spec.engine;
let mut parent_header: Header = Header::default();
parent_header.set_gas_limit(U256::from_str("222222").unwrap());
let mut header = Header::default();
header.set_number(2);
header.set_gas_limit(U256::from_str("222222").unwrap());
let proposer = insert_and_unlock(&tap, "1");
header.set_author(proposer);
let mut seal = proposal_seal(&tap, &header, 0);
let vote_info = message_info_rlp(&VoteStep::new(2, 0, Step::Precommit), Some(header.bare_hash()));
let signature1 = tap.sign(proposer, None, keccak(&vote_info)).unwrap();
let voter = insert_and_unlock(&tap, "0");
let signature0 = tap.sign(voter, None, keccak(&vote_info)).unwrap();
seal[1] = ::rlp::NULL_RLP.to_vec();
seal[2] = ::rlp::encode_list(&vec![H520::from(signature1.clone())]).into_vec();
header.set_seal(seal.clone());
let epoch_verifier = super::EpochVerifier {
subchain_validators: SimpleList::new(vec![proposer.clone(), voter.clone()]),
recover: {
let signature1 = signature1.clone();
let signature0 = signature0.clone();
let proposer = proposer.clone();
let voter = voter.clone();
move |s: &Signature, _: &Message| {
if *s == signature1 {
Ok(proposer)
} else if *s == signature0 {
Ok(voter)
} else {
Err(Error::Ethkey(EthkeyError::InvalidSignature))
}
}
},
};
// One good signature is not enough.
match epoch_verifier.verify_light(&header) {
Err(Error::Engine(EngineError::BadSealFieldSize(_))) => {},
_ => panic!(),
}
seal[2] = ::rlp::encode_list(&vec![H520::from(signature1.clone()), H520::from(signature0.clone())]).into_vec();
header.set_seal(seal.clone());
assert!(epoch_verifier.verify_light(&header).is_ok());
let bad_voter = insert_and_unlock(&tap, "101");
let bad_signature = tap.sign(bad_voter, None, keccak(&vote_info)).unwrap();
seal[2] = ::rlp::encode_list(&vec![H520::from(signature1), H520::from(bad_signature)]).into_vec();
header.set_seal(seal);
// One good and one bad signature.
match epoch_verifier.verify_light(&header) {
Err(Error::Ethkey(EthkeyError::InvalidSignature)) => {},
_ => panic!(),
};
engine.stop();
}
2016-08-23 12:58:40 +02:00
}