Skip to content

Commit

Permalink
Implement signing.
Browse files Browse the repository at this point in the history
  • Loading branch information
afck committed Jan 8, 2019
1 parent 36e08bc commit 4e7e838
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 12 deletions.
35 changes: 33 additions & 2 deletions ethcore/src/engines/authority_round/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use std::time::{UNIX_EPOCH, SystemTime, Duration};

use account_provider::AccountProvider;
use block::*;
use client::EngineClient;
use client::{BlockId, EngineClient};
use engines::{Engine, Seal, EngineError, ConstructedVerifier};
use engines::block_reward;
use engines::block_reward::{BlockRewardContract, RewardKind};
Expand Down Expand Up @@ -436,6 +436,11 @@ pub struct AuthorityRound {
maximum_empty_steps: usize,
consensus_kind: ConsensusKind,
machine: EthereumMachine,
/// The stored secret contribution to randomness.
// TODO: Only used in PoS. Maybe make part of `ConsensusKind`? Or tie together with `randomness_contract`?
rand_secret: RwLock<Option<randomness::Secret>>,
/// If set, enables random number contract integration.
randomness_contract: Option<Address>,
}

// header-chain validator.
Expand Down Expand Up @@ -690,6 +695,8 @@ impl AuthorityRound {
strict_empty_steps_transition: our_params.strict_empty_steps_transition,
consensus_kind: our_params.consensus_kind,
machine: machine,
rand_secret: Default::default(),
randomness_contract: our_params.randomness_contract,
});

// Do not initialize timeouts for tests.
Expand Down Expand Up @@ -1143,6 +1150,28 @@ impl Engine<EthereumMachine> for AuthorityRound {
epoch_begin: bool,
_ancestry: &mut Iterator<Item=ExtendedHeader>,
) -> Result<(), Error> {
// Random number generation
// TODO: Is this the right place to do this?
if let (Some(contract_addr), Some(our_addr)) = (self.randomness_contract, self.signer.read().address()) {
let client = match self.client.read().as_ref().and_then(|weak| weak.upgrade()) {
Some(client) => client,
None => {
debug!(target: "engine", "Unable to close block: missing client ref.");
return Err(EngineError::RequiresClient.into())
},
};
let block_id = BlockId::Number(block.header.number());
let mut contract = util::BoundContract::bind(&*client, block_id, contract_addr);
// TODO: How should these errors be handled?
let phase = randomness::RandomnessPhase::load(&contract, our_addr)
.map_err(|err| EngineError::FailedSystemCall(format!("Randomness error: {:?}", err)))?;
let secret = *self.rand_secret.read();
let mut rng = ::rand::OsRng::new()?;
// TODO: Add new transaction to the block?
*self.rand_secret.write() = phase.advance(&contract, secret, &*self.signer.read(), &mut rng)
.map_err(|err| EngineError::FailedSystemCall(format!("Randomness error: {:?}", err)))?;
}

// with immediate transitions, we don't use the epoch mechanism anyway.
// the genesis is always considered an epoch, but we ignore it intentionally.
if self.immediate_transitions || !epoch_begin { return Ok(()) }
Expand Down Expand Up @@ -1551,7 +1580,7 @@ mod tests {
use engines::{Seal, Engine, EngineError, EthEngine};
use engines::validator_set::{TestSet, SimpleList};
use error::{Error, ErrorKind};
use super::{AuthorityRoundParams, AuthorityRound, EmptyStep, SealedEmptyStep, calculate_score};
use super::{AuthorityRoundParams, AuthorityRound, EmptyStep, SealedEmptyStep, calculate_score, ConsensusKind};

fn aura<F>(f: F) -> Arc<AuthorityRound> where
F: FnOnce(&mut AuthorityRoundParams),
Expand All @@ -1571,6 +1600,8 @@ mod tests {
block_reward_contract_transition: 0,
block_reward_contract: Default::default(),
strict_empty_steps_transition: 0,
consensus_kind: ConsensusKind::Poa,
randomness_contract: None,
};

// mutate aura params
Expand Down
15 changes: 11 additions & 4 deletions ethcore/src/engines/authority_round/randomness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
//!
//! No additional state is kept inside the `RandomnessPhase`, it must be passed in each time.
use account_provider::SignError;
use engines::signer::EngineSigner;
use ethabi::{Bytes, Hash};
use ethereum_types::{Address, U256};
use hash::keccak;
Expand Down Expand Up @@ -91,6 +93,8 @@ pub enum PhaseError {
TransactionFailed(CallError),
/// When trying to reveal the secret, no secret was found.
LostSecret,
/// Failed to sign our commitment or secret.
Sign(SignError),
/// A secret was stored, but it did not match the committed hash.
StaleSecret,
}
Expand Down Expand Up @@ -173,11 +177,12 @@ impl RandomnessPhase {
self,
contract: &BoundContract,
stored_secret: Option<Secret>,
signer: &EngineSigner,
rng: &mut R,
) -> Result<Option<Secret>, PhaseError> {
match self {
RandomnessPhase::Waiting | RandomnessPhase::Committed => Ok(stored_secret),
RandomnessPhase::BeforeCommit { our_address } => {
RandomnessPhase::BeforeCommit { .. } => {
// We generate a new secret to submit each time, this function will only be called
// once per round of randomness generation.
let mut buf = [0u8; 32];
Expand All @@ -188,7 +193,8 @@ impl RandomnessPhase {

// Currently the PoS contracts are setup in a way that only the system address can
// commit hashes, so we need to sign "manually".
let signature: Bytes = unimplemented!();
let signature: Bytes =
signer.sign(secret_hash).map_err(PhaseError::Sign)?.as_ref().into();

// Schedule the transaction that commits the hash.
contract
Expand All @@ -210,12 +216,13 @@ impl RandomnessPhase {
.call_const(aura_random::functions::get_commit::call(round, our_address))
.map_err(PhaseError::LoadFailed)?;

if (secret_hash != committed_hash) {
if secret_hash != committed_hash {
return Err(PhaseError::StaleSecret);
}

// We are now sure that we have the correct secret and can reveal it.
let signature: Bytes = unimplemented!();
let signature: Bytes =
signer.sign(secret.into()).map_err(PhaseError::Sign)?.as_ref().into();
contract
.schedule_call_transaction(aura_random::functions::reveal_secret::call(
secret, signature,
Expand Down
10 changes: 6 additions & 4 deletions ethcore/src/engines/authority_round/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::fmt;
use ethabi;
use ethereum_types::Address;

use client::{BlockId, CallContract, Client, EngineClient};
use client::{BlockId, EngineClient};
use transaction;

/// A contract bound to a client and block number.
Expand All @@ -16,7 +16,7 @@ use transaction;
/// These three parts are enough to call a contract's function; return values are automatically
/// decoded.
pub struct BoundContract<'a> {
client: &'a Client,
client: &'a EngineClient,
block_id: BlockId,
contract_addr: Address,
}
Expand All @@ -37,7 +37,7 @@ pub enum CallError {
impl<'a> fmt::Debug for BoundContract<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("BoundContract")
.field("client", &(self.client as *const Client))
.field("client", &(self.client as *const EngineClient))
.field("block_id", &self.block_id)
.field("contract_addr", &self.contract_addr)
.finish()
Expand All @@ -47,7 +47,7 @@ impl<'a> fmt::Debug for BoundContract<'a> {
impl<'a> BoundContract<'a> {
/// Create a new `BoundContract`.
#[inline]
pub fn bind(client: &Client, block_id: BlockId, contract_addr: Address) -> BoundContract {
pub fn bind(client: &EngineClient, block_id: BlockId, contract_addr: Address) -> BoundContract {
BoundContract {
client,
block_id,
Expand All @@ -67,6 +67,8 @@ impl<'a> BoundContract<'a> {

let call_return = self
.client
.as_full_client()
.ok_or(CallError::NotFullClient)?
.call_contract(self.block_id, self.contract_addr, data)
.map_err(CallError::CallFailed)?;

Expand Down
7 changes: 5 additions & 2 deletions ethcore/src/engines/validator_set/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
use heapsize::HeapSizeOf;
use ethereum_types::{H256, Address};
use ethkey::Signature;

use error::Error;
use machine::{AuxiliaryData, Call, EthereumMachine};
use header::{Header, BlockNumber};
use super::{ValidatorSet, SimpleList};
Expand Down Expand Up @@ -84,8 +86,9 @@ impl ValidatorSet for TestSet {
1
}

fn report_malicious(&self, _validator: &Address, _set_block: BlockNumber, block: BlockNumber, signer: &mut dyn FnMut(H256) -> Signature) {
self.last_malicious.store(block as usize, AtomicOrdering::SeqCst)
fn report_malicious(&self, _validator: &Address, _set_block: BlockNumber, block: BlockNumber, _signer: &dyn Fn(H256) -> Result<Signature, Error>) -> Result<(), Error> {
self.last_malicious.store(block as usize, AtomicOrdering::SeqCst);
Ok(Default::default()) // TODO: Return signature?
}

fn report_benign(&self, _validator: &Address, _set_block: BlockNumber, block: BlockNumber) {
Expand Down

0 comments on commit 4e7e838

Please sign in to comment.