use std::{
collections::{BTreeMap, HashMap},
marker::PhantomData,
};
use anyhow::Result;
use bitvec::{bitvec, vec::BitVec};
use committable::{Commitment, Committable};
use either::Either;
use ethereum_types::U256;
use tracing::error;
use crate::{
message::UpgradeLock,
simple_certificate::Threshold,
simple_vote::{VersionedVoteData, Voteable},
traits::{
election::Membership,
node_implementation::{NodeType, Versions},
signature_key::{SignatureKey, StakeTableEntryType},
},
};
pub trait Vote<TYPES: NodeType>: HasViewNumber<TYPES> {
type Commitment: Voteable;
fn signature(&self) -> <TYPES::SignatureKey as SignatureKey>::PureAssembledSignatureType;
fn date(&self) -> &Self::Commitment;
fn date_commitment(&self) -> Commitment<Self::Commitment>;
fn signing_key(&self) -> TYPES::SignatureKey;
}
pub trait HasViewNumber<TYPES: NodeType> {
fn view_number(&self) -> TYPES::Time;
}
pub trait Certificate<TYPES: NodeType>: HasViewNumber<TYPES> {
type Voteable: Voteable;
type Threshold: Threshold<TYPES>;
fn create_signed_certificate<V: Versions>(
vote_commitment: Commitment<VersionedVoteData<TYPES, Self::Voteable, V>>,
data: Self::Voteable,
sig: <TYPES::SignatureKey as SignatureKey>::QcType,
view: TYPES::Time,
) -> Self;
fn is_valid_cert<MEMBERSHIP: Membership<TYPES>, V: Versions>(
&self,
membership: &MEMBERSHIP,
upgrade_lock: &UpgradeLock<TYPES, V>,
) -> impl std::future::Future<Output = bool>;
fn threshold<MEMBERSHIP: Membership<TYPES>>(membership: &MEMBERSHIP) -> u64;
fn date(&self) -> &Self::Voteable;
fn date_commitment<V: Versions>(
&self,
upgrade_lock: &UpgradeLock<TYPES, V>,
) -> impl std::future::Future<Output = Result<Commitment<VersionedVoteData<TYPES, Self::Voteable, V>>>>;
}
type SignersMap<COMMITMENT, KEY> = HashMap<
COMMITMENT,
(
BitVec,
Vec<<KEY as SignatureKey>::PureAssembledSignatureType>,
),
>;
#[allow(clippy::type_complexity)]
pub struct VoteAccumulator<
TYPES: NodeType,
VOTE: Vote<TYPES>,
CERT: Certificate<TYPES, Voteable = VOTE::Commitment>,
V: Versions,
> {
pub vote_outcomes: VoteMap2<
Commitment<VersionedVoteData<TYPES, <VOTE as Vote<TYPES>>::Commitment, V>>,
TYPES::SignatureKey,
<TYPES::SignatureKey as SignatureKey>::PureAssembledSignatureType,
>,
pub signers: SignersMap<
Commitment<VersionedVoteData<TYPES, <VOTE as Vote<TYPES>>::Commitment, V>>,
TYPES::SignatureKey,
>,
pub phantom: PhantomData<(TYPES, VOTE, CERT)>,
pub upgrade_lock: UpgradeLock<TYPES, V>,
}
impl<
TYPES: NodeType,
VOTE: Vote<TYPES>,
CERT: Certificate<TYPES, Voteable = VOTE::Commitment>,
V: Versions,
> VoteAccumulator<TYPES, VOTE, CERT, V>
{
pub async fn accumulate(
&mut self,
vote: &VOTE,
membership: &TYPES::Membership,
) -> Either<(), CERT> {
let key = vote.signing_key();
let vote_commitment = match VersionedVoteData::new(
vote.date().clone(),
vote.view_number(),
&self.upgrade_lock,
)
.await
{
Ok(data) => data.commit(),
Err(e) => {
tracing::warn!("Failed to generate versioned vote data: {e}");
return Either::Left(());
}
};
if !key.validate(&vote.signature(), vote_commitment.as_ref()) {
error!("Invalid vote! Vote Data {:?}", vote.date());
return Either::Left(());
}
let Some(stake_table_entry) = membership.stake(&key) else {
return Either::Left(());
};
let stake_table = membership.stake_table();
let Some(vote_node_id) = stake_table
.iter()
.position(|x| *x == stake_table_entry.clone())
else {
return Either::Left(());
};
let original_signature: <TYPES::SignatureKey as SignatureKey>::PureAssembledSignatureType =
vote.signature();
let (total_stake_casted, total_vote_map) = self
.vote_outcomes
.entry(vote_commitment)
.or_insert_with(|| (U256::from(0), BTreeMap::new()));
if total_vote_map.contains_key(&key) {
return Either::Left(());
}
let (signers, sig_list) = self
.signers
.entry(vote_commitment)
.or_insert((bitvec![0; membership.total_nodes()], Vec::new()));
if signers.get(vote_node_id).as_deref() == Some(&true) {
error!("Node id is already in signers list");
return Either::Left(());
}
signers.set(vote_node_id, true);
sig_list.push(original_signature);
*total_stake_casted += stake_table_entry.stake();
total_vote_map.insert(key, (vote.signature(), vote_commitment));
if *total_stake_casted >= CERT::threshold(membership).into() {
let real_qc_pp: <<TYPES as NodeType>::SignatureKey as SignatureKey>::QcParams =
<TYPES::SignatureKey as SignatureKey>::public_parameter(
stake_table,
U256::from(CERT::threshold(membership)),
);
let real_qc_sig = <TYPES::SignatureKey as SignatureKey>::assemble(
&real_qc_pp,
signers.as_bitslice(),
&sig_list[..],
);
let cert = CERT::create_signed_certificate::<V>(
vote_commitment,
vote.date().clone(),
real_qc_sig,
vote.view_number(),
);
return Either::Right(cert);
}
Either::Left(())
}
}
type VoteMap2<COMMITMENT, PK, SIG> = HashMap<COMMITMENT, (U256, BTreeMap<PK, (SIG, COMMITMENT)>)>;