Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

I132 invalid public keys #133

Merged
merged 8 commits into from
Oct 31, 2024
7 changes: 7 additions & 0 deletions crates/ethcore/src/engines/hbbft/contracts/keygen_history.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use client::traits::EngineClient;
use crypto::{
self,
publickey::{ec_math_utils::public_add, Public},

Check warning on line 4 in crates/ethcore/src/engines/hbbft/contracts/keygen_history.rs

View workflow job for this annotation

GitHub Actions / Test and Build (ubuntu-latest, 1.72)

unused import: `ec_math_utils::public_add`

Check warning on line 4 in crates/ethcore/src/engines/hbbft/contracts/keygen_history.rs

View workflow job for this annotation

GitHub Actions / Test and Build (ubuntu-latest, 1.72)

unused import: `ec_math_utils::public_add`

Check warning on line 4 in crates/ethcore/src/engines/hbbft/contracts/keygen_history.rs

View workflow job for this annotation

GitHub Actions / Check

unused import: `ec_math_utils::public_add`

Check warning on line 4 in crates/ethcore/src/engines/hbbft/contracts/keygen_history.rs

View workflow job for this annotation

GitHub Actions / Check

unused import: `ec_math_utils::public_add`

Check warning on line 4 in crates/ethcore/src/engines/hbbft/contracts/keygen_history.rs

View workflow job for this annotation

GitHub Actions / Check

unused import: `ec_math_utils::public_add`
};
use engines::{
hbbft::{
Expand Down Expand Up @@ -179,6 +179,13 @@
pub inner: Arc<RwLock<Option<Box<dyn EngineSigner>>>>,
}

impl PublicWrapper {
/// Check if the public key is valid.
pub fn is_valid(&self) -> bool {
self.encrypt(b"a", &mut rand::thread_rng()).is_ok()
}
}

impl<'a> PublicKey for PublicWrapper {
type Error = crypto::publickey::Error;
type SecretKey = KeyPairWrapper;
Expand Down
1 change: 1 addition & 0 deletions crates/ethcore/src/engines/hbbft/hbbft_engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1009,6 +1009,7 @@
Some(client_arc) => {
if self.is_syncing(&client_arc) {
// we are syncing - do not do anything.
trace!(target: "engine", "do_validator_engine_actions: skipping because we are syncing.");
return Ok(());
}

Expand Down Expand Up @@ -1177,7 +1178,7 @@
Err(e) => {
error!(target: "consensus", "Error initializing synckeygen: {:?}", e);
}
Err(_) => {

Check warning on line 1181 in crates/ethcore/src/engines/hbbft/hbbft_engine.rs

View workflow job for this annotation

GitHub Actions / Test and Build (ubuntu-latest, 1.72)

unreachable pattern

Check warning on line 1181 in crates/ethcore/src/engines/hbbft/hbbft_engine.rs

View workflow job for this annotation

GitHub Actions / Test and Build (ubuntu-latest, 1.72)

unreachable pattern

Check warning on line 1181 in crates/ethcore/src/engines/hbbft/hbbft_engine.rs

View workflow job for this annotation

GitHub Actions / Check

unreachable pattern

Check warning on line 1181 in crates/ethcore/src/engines/hbbft/hbbft_engine.rs

View workflow job for this annotation

GitHub Actions / Check

unreachable pattern

Check warning on line 1181 in crates/ethcore/src/engines/hbbft/hbbft_engine.rs

View workflow job for this annotation

GitHub Actions / Check

unreachable pattern
error!(target: "consensus", "Error initializing synckeygen: unknown Error");
}
}
Expand Down
163 changes: 123 additions & 40 deletions crates/ethcore/src/engines/hbbft/keygen_transactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@
},
signer::EngineSigner,
};
use ethereum_types::{Address, U256};
use ethereum_types::{Address, Public, U256};

Check warning on line 19 in crates/ethcore/src/engines/hbbft/keygen_transactions.rs

View workflow job for this annotation

GitHub Actions / Test and Build (ubuntu-latest, 1.72)

unused import: `Public`

Check warning on line 19 in crates/ethcore/src/engines/hbbft/keygen_transactions.rs

View workflow job for this annotation

GitHub Actions / Test and Build (ubuntu-latest, 1.72)

unused import: `Public`

Check warning on line 19 in crates/ethcore/src/engines/hbbft/keygen_transactions.rs

View workflow job for this annotation

GitHub Actions / Check

unused import: `Public`

Check warning on line 19 in crates/ethcore/src/engines/hbbft/keygen_transactions.rs

View workflow job for this annotation

GitHub Actions / Check

unused import: `Public`

Check warning on line 19 in crates/ethcore/src/engines/hbbft/keygen_transactions.rs

View workflow job for this annotation

GitHub Actions / Check

unused import: `Public`
use itertools::Itertools;
use parking_lot::RwLock;
use std::{collections::BTreeMap, sync::Arc};
use types::ids::BlockId;

use crate::client::BlockChainClient;

pub struct KeygenTransactionSender {
last_keygen_mode: KeyGenMode,
keygen_mode_counter: u64,
Expand All @@ -36,6 +38,21 @@
Yes,
}

#[derive(Debug)]
pub enum KeyGenError {
NoSigner,
NoFullClient,
NoPartToWrite,
CallError(CallError),
Unexpected,
}

impl From<CallError> for KeyGenError {
fn from(e: CallError) -> Self {
KeyGenError::CallError(e)
}
}

static KEYGEN_TRANSACTION_SEND_DELAY: u64 = 3;
static KEYGEN_TRANSACTION_RESEND_DELAY: u64 = 10;

Expand Down Expand Up @@ -102,17 +119,17 @@
&mut self,
client: &dyn EngineClient,
signer: &Arc<RwLock<Option<Box<dyn EngineSigner>>>>,
) -> Result<(), CallError> {
) -> Result<(), KeyGenError> {
// If we have no signer there is nothing for us to send.
let address = match signer.read().as_ref() {
Some(signer) => signer.address(),
None => {
warn!(target: "engine", "Could not send keygen transactions, because signer module could not be retrieved");
return Err(CallError::ReturnValueInvalid);
return Err(KeyGenError::NoSigner);
}
};

let full_client = client.as_full_client().ok_or(CallError::NotFullClient)?;
let full_client = client.as_full_client().ok_or(KeyGenError::NoFullClient)?;

// If the chain is still syncing, do not send Parts or Acks.
if full_client.is_major_syncing() {
Expand All @@ -122,30 +139,82 @@

trace!(target:"engine", " get_validator_pubkeys...");

let vmap = get_validator_pubkeys(&*client, BlockId::Latest, ValidatorType::Pending)?;
let vmap = get_validator_pubkeys(&*client, BlockId::Latest, ValidatorType::Pending)
.map_err(|e| KeyGenError::CallError(e))?;
let pub_keys: BTreeMap<_, _> = vmap
.values()
.map(|p| (*p, PublicWrapper { inner: p.clone() }))
.collect();

let pub_keys_arc = Arc::new(pub_keys);
let upcoming_epoch =
get_posdao_epoch(client, BlockId::Latest).map_err(|e| KeyGenError::CallError(e))? + 1;

//let pub_key_len = pub_keys.len();
// if synckeygen creation fails then either signer or validator pub keys are problematic.
// Todo: We should expect up to f clients to write invalid pub keys. Report and re-start pending validator set selection.
let (mut synckeygen, part) = engine_signer_to_synckeygen(signer, Arc::new(pub_keys))
.map_err(|e| {
warn!(target:"engine", "engine_signer_to_synckeygen error {:?}", e);
CallError::ReturnValueInvalid
})?;
let (mut synckeygen, part) = match engine_signer_to_synckeygen(signer, pub_keys_arc.clone())
{
Ok((mut synckeygen_, part_)) => (synckeygen_, part_),

Check warning on line 158 in crates/ethcore/src/engines/hbbft/keygen_transactions.rs

View workflow job for this annotation

GitHub Actions / Test and Build (ubuntu-latest, 1.72)

variable does not need to be mutable

Check warning on line 158 in crates/ethcore/src/engines/hbbft/keygen_transactions.rs

View workflow job for this annotation

GitHub Actions / Test and Build (ubuntu-latest, 1.72)

variable does not need to be mutable

Check warning on line 158 in crates/ethcore/src/engines/hbbft/keygen_transactions.rs

View workflow job for this annotation

GitHub Actions / Check

variable does not need to be mutable

Check warning on line 158 in crates/ethcore/src/engines/hbbft/keygen_transactions.rs

View workflow job for this annotation

GitHub Actions / Check

variable does not need to be mutable

Check warning on line 158 in crates/ethcore/src/engines/hbbft/keygen_transactions.rs

View workflow job for this annotation

GitHub Actions / Check

variable does not need to be mutable
Err(e) => {
warn!(target:"engine", "engine_signer_to_synckeygen pub keys count {:?} error {:?}", pub_keys_arc.len(), e);
//let mut failure_pub_keys: Vec<Public> = Vec::new();
let mut failure_pub_keys: Vec<u8> = Vec::new();
pub_keys_arc.iter().for_each(|(k, v)| {
warn!(target:"engine", "pub key {}", k.as_bytes().iter().join(""));

if !v.is_valid() {
warn!(target:"engine", "INVALID pub key {}", k);

// append the bytes of the public key to the failure_pub_keys.
k.as_bytes().iter().for_each(|b| {
failure_pub_keys.push(*b);
});
}
});

// if we should send our parts, we will send the public keys of the troublemakers instead.

match self
.should_send_part(client, &address)
.map_err(|e| KeyGenError::CallError(e))?
{
ShouldSendKeyAnswer::NoNotThisKeyGenMode => {
return Err(KeyGenError::Unexpected)
}
ShouldSendKeyAnswer::NoWaiting => return Err(KeyGenError::Unexpected),
ShouldSendKeyAnswer::Yes => {
let serialized_part = match bincode::serialize(&failure_pub_keys) {
Ok(part) => part,
Err(e) => {
warn!(target:"engine", "could not serialize part: {:?}", e);
return Err(KeyGenError::Unexpected);
}
};

send_part_transaction(
full_client,
client,
&address,
upcoming_epoch,
serialized_part,
)?;
trace!(target:"engine", "PART Transaction send for moving forward key gen phase");
return Ok(());
}
}
}
};

// If there is no part then we are not part of the pending validator set and there is nothing for us to do.
let part_data = match part {
Some(part) => part,
None => {
warn!(target:"engine", "no part to write.");
return Err(CallError::ReturnValueInvalid);
return Err(KeyGenError::NoPartToWrite);
}
};

let upcoming_epoch = get_posdao_epoch(client, BlockId::Latest)? + 1;
trace!(target:"engine", "preparing to send PARTS for upcoming epoch: {}", upcoming_epoch);

// Check if we already sent our part.
Expand All @@ -155,36 +224,17 @@
Ok(part) => part,
Err(e) => {
warn!(target:"engine", "could not serialize part: {:?}", e);
return Err(CallError::ReturnValueInvalid);
return Err(KeyGenError::Unexpected);
}
};
let serialized_part_len = serialized_part.len();
let current_round = get_current_key_gen_round(client)?;
let write_part_data = key_history_contract::functions::write_part::call(

send_part_transaction(
full_client,
client,
&address,
upcoming_epoch,
current_round,
serialized_part,
);

// the required gas values have been approximated by
// experimenting and it's a very rough estimation.
// it can be further fine tuned to be just above the real consumption.
// ACKs require much more gas,
// and usually run into the gas limit problems.
let gas: usize = serialized_part_len * 800 + 100_000;

let part_transaction =
TransactionRequest::call(*KEYGEN_HISTORY_ADDRESS, write_part_data.0)
.gas(U256::from(gas))
.nonce(full_client.nonce(&address, BlockId::Latest).unwrap())
.gas_price(U256::from(10000000000u64));
full_client
.transact_silently(part_transaction)
.map_err(|e| {
warn!(target:"engine", "could not transact_silently: {:?}", e);
CallError::ReturnValueInvalid
})?;

)?;
trace!(target:"engine", "PART Transaction send.");
return Ok(());
}
Expand Down Expand Up @@ -213,7 +263,7 @@
}
Err(err) => {
error!(target:"engine", "could not retrieve part for {} call failed. Error: {:?}", *v, err);
return Err(err);
return Err(KeyGenError::CallError(err));
}
}
);
Expand All @@ -230,7 +280,7 @@
for ack in acks {
let ack_to_push = match bincode::serialize(&ack) {
Ok(serialized_ack) => serialized_ack,
Err(_) => return Err(CallError::ReturnValueInvalid),
Err(e) => return Err(KeyGenError::Unexpected),

Check warning on line 283 in crates/ethcore/src/engines/hbbft/keygen_transactions.rs

View workflow job for this annotation

GitHub Actions / Test and Build (ubuntu-latest, 1.72)

unused variable: `e`

Check warning on line 283 in crates/ethcore/src/engines/hbbft/keygen_transactions.rs

View workflow job for this annotation

GitHub Actions / Test and Build (ubuntu-latest, 1.72)

unused variable: `e`

Check warning on line 283 in crates/ethcore/src/engines/hbbft/keygen_transactions.rs

View workflow job for this annotation

GitHub Actions / Check

unused variable: `e`

Check warning on line 283 in crates/ethcore/src/engines/hbbft/keygen_transactions.rs

View workflow job for this annotation

GitHub Actions / Check

unused variable: `e`

Check warning on line 283 in crates/ethcore/src/engines/hbbft/keygen_transactions.rs

View workflow job for this annotation

GitHub Actions / Check

unused variable: `e`
};
total_bytes_for_acks += ack_to_push.len();
serialized_acks.push(ack_to_push);
Expand Down Expand Up @@ -263,3 +313,36 @@
Ok(())
}
}

fn send_part_transaction(
full_client: &dyn BlockChainClient,
client: &dyn EngineClient,
mining_address: &Address,
upcoming_epoch: U256,
data: Vec<u8>,
) -> Result<(), KeyGenError> {
// the required gas values have been approximated by
// experimenting and it's a very rough estimation.
// it can be further fine tuned to be just above the real consumption.
// ACKs require much more gas,
// and usually run into the gas limit problems.
let gas: usize = data.len() * 800 + 100_000;

let nonce = full_client.nonce(&mining_address, BlockId::Latest).unwrap();
let current_round = get_current_key_gen_round(client)?;
let write_part_data =
key_history_contract::functions::write_part::call(upcoming_epoch, current_round, data);

let part_transaction = TransactionRequest::call(*KEYGEN_HISTORY_ADDRESS, write_part_data.0)
.gas(U256::from(gas))
.nonce(nonce)
.gas_price(U256::from(10000000000u64));
full_client
.transact_silently(part_transaction)
.map_err(|e| {
warn!(target:"engine", "could not transact_silently: {:?}", e);
CallError::ReturnValueInvalid
})?;

return Ok(());
}
Loading