use ::serde::{Deserialize, Serialize};
use jormungandr_lib::{
crypto::account::{Identifier, SigningKey},
interfaces::Address,
interfaces::Value,
};
pub type VotingGroup = String;
fn is_false(b: &bool) -> bool {
!(*b)
}
#[derive(Serialize, Deserialize, Debug, Clone )]
pub struct VoterHIR {
#[serde(with = "serde")]
pub voting_key: Identifier,
pub address: Address,
pub voting_group: VotingGroup,
pub voting_power: Value,
#[serde(default, skip_serializing_if = "is_false")]
pub underthreshold: bool,
#[serde(default, skip_serializing_if = "is_false")]
pub overlimit: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub private_key: Option<String>,
}
impl VoterHIR {
#[must_use]
pub fn to_loadtest_snapshot(&self) -> Self {
let loadtest_key = SigningKey::generate(rand::thread_rng());
let loadtest_voting_key = loadtest_key.identifier();
let discriminator = self.address.1.discrimination();
let loadtest_address = loadtest_voting_key.to_address(discriminator);
let private_key = format!("0x{}", loadtest_key.to_hex());
Self {
voting_key: loadtest_voting_key,
address: loadtest_address.into(),
voting_group: self.voting_group.clone(),
voting_power: self.voting_power,
underthreshold: self.underthreshold,
overlimit: self.overlimit,
private_key: Some(private_key),
}
}
#[must_use]
pub fn cap_voting_power(&self, cap: u64) -> Self {
let mut voting_power = self.voting_power.as_u64();
let mut overlimit = self.overlimit;
if voting_power > cap {
voting_power = cap;
overlimit = true;
};
VoterHIR {
voting_key: self.voting_key.clone(),
address: self.address.clone(),
voting_group: self.voting_group.clone(),
voting_power: voting_power.into(),
underthreshold: self.underthreshold,
overlimit, private_key: self.private_key.clone(),
}
}
}
mod serde {
use super::*;
use ::serde::{de::Error, Deserializer, Serializer};
pub fn serialize<S>(voting_key: &Identifier, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&format!("0x{}", voting_key.to_hex()))
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Identifier, D::Error>
where
D: Deserializer<'de>,
{
let hex = String::deserialize(deserializer)?;
Identifier::from_hex(hex.trim_start_matches("0x"))
.map_err(|e| D::Error::custom(format!("invalid public key: {}", e)))
}
}
#[cfg(any(test, feature = "proptest"))]
pub mod tests {
use super::*;
use ::proptest::{prelude::*, strategy::BoxedStrategy};
use jormungandr_lib::crypto::account::Identifier;
use std::ops::Range;
impl Arbitrary for VoterHIR {
type Parameters = (String, VpRange);
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
(any::<[u8; 32]>(), args.1 .0)
.prop_map(move |(key, voting_power)| VoterHIR {
voting_key: Identifier::from_hex(&hex::encode(key)).unwrap(),
address: chain_addr::Address(
chain_addr::Discrimination::Production,
chain_addr::Kind::Account(
Identifier::from_hex(&hex::encode(key))
.unwrap()
.to_inner()
.into(),
),
)
.into(),
voting_power: voting_power.into(),
voting_group: args.0.clone(),
underthreshold: false,
overlimit: false,
private_key: None,
})
.boxed()
}
}
pub struct VpRange(Range<u64>);
impl VpRange {
pub const fn ada_distribution() -> Self {
Self(1..45_000_000_000)
}
}
impl Default for VpRange {
fn default() -> Self {
Self(0..u64::MAX)
}
}
impl From<Range<u64>> for VpRange {
fn from(range: Range<u64>) -> Self {
Self(range)
}
}
}