Skip to content

Commit

Permalink
fix(staking): implement serde manually to preserve Rc properties
Browse files Browse the repository at this point in the history
  • Loading branch information
aesedepece committed Jun 27, 2024
1 parent 45ff41f commit c1b696a
Show file tree
Hide file tree
Showing 5 changed files with 313 additions and 75 deletions.
2 changes: 2 additions & 0 deletions data_structures/src/capabilities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ pub enum Capability {
Witnessing = 1,
}

pub const ALL_CAPABILITIES: [Capability; 2] = [Capability::Mining, Capability::Witnessing];

#[derive(Copy, Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
pub struct CapabilityMap<T>
where
Expand Down
46 changes: 23 additions & 23 deletions data_structures/src/data_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -794,7 +794,7 @@ mod tests {
);
assert!(p.to_be_stored.is_empty());

assert!(p.update_data_request_stages(None).is_empty());
assert!(p.update_data_request_stages(None, None).is_empty());

(epoch, fake_block_hash, p, dr_transaction.hash())
}
Expand Down Expand Up @@ -829,7 +829,7 @@ mod tests {
);
assert!(p.to_be_stored.is_empty());

assert!(p.update_data_request_stages(None).is_empty());
assert!(p.update_data_request_stages(None, None).is_empty());

(epoch, fake_block_hash, p, dr_transaction.hash())
}
Expand Down Expand Up @@ -864,7 +864,7 @@ mod tests {
);
assert!(p.to_be_stored.is_empty());

assert!(p.update_data_request_stages(None).is_empty());
assert!(p.update_data_request_stages(None, None).is_empty());

(epoch, fake_block_hash, p, dr_transaction.hash())
}
Expand Down Expand Up @@ -906,7 +906,7 @@ mod tests {
assert!(p.data_requests_by_epoch[&epoch].contains(&dr_pointer));

// Update stages
assert!(p.update_data_request_stages(None).is_empty());
assert!(p.update_data_request_stages(None, None).is_empty());

// Now in reveal stage
assert_eq!(
Expand Down Expand Up @@ -953,7 +953,7 @@ mod tests {
);

// Update stages
assert!(p.update_data_request_stages(None).is_empty());
assert!(p.update_data_request_stages(None, None).is_empty());

// Now in tally stage
assert_eq!(
Expand All @@ -978,7 +978,7 @@ mod tests {
assert_eq!(p.data_request_state(&dr_pointer), None);

// Update stages
assert!(p.update_data_request_stages(None).is_empty());
assert!(p.update_data_request_stages(None, None).is_empty());

assert_eq!(p.to_be_stored.len(), 1);
assert_eq!(
Expand Down Expand Up @@ -1048,7 +1048,7 @@ mod tests {
.unwrap();

// Update stages
assert!(p.update_data_request_stages(None).is_empty());
assert!(p.update_data_request_stages(None, None).is_empty());

// Now in reveal stage 1
assert_eq!(
Expand All @@ -1069,7 +1069,7 @@ mod tests {
.unwrap();

// Update stages
assert!(p.update_data_request_stages(None).is_empty());
assert!(p.update_data_request_stages(None, None).is_empty());
// Now in reveal stage 2
assert_eq!(
p.data_request_pool[&dr_pointer].stage,
Expand All @@ -1081,7 +1081,7 @@ mod tests {
);

// Update stages
assert!(p.update_data_request_stages(None).is_empty());
assert!(p.update_data_request_stages(None, None).is_empty());
// Now in reveal stage 3
assert_eq!(
p.data_request_pool[&dr_pointer].stage,
Expand All @@ -1093,7 +1093,7 @@ mod tests {
);

// Update stages
assert!(p.update_data_request_stages(None).is_empty());
assert!(p.update_data_request_stages(None, None).is_empty());
// Now in tally stage
assert_eq!(
p.data_request_pool[&dr_pointer].stage,
Expand Down Expand Up @@ -1139,7 +1139,7 @@ mod tests {
.unwrap();

// Update stages
assert!(p.update_data_request_stages(None).is_empty());
assert!(p.update_data_request_stages(None, None).is_empty());

// Now in reveal stage 1
assert_eq!(
Expand All @@ -1160,7 +1160,7 @@ mod tests {
.unwrap();

// Update stages
assert!(p.update_data_request_stages(None).is_empty());
assert!(p.update_data_request_stages(None, None).is_empty());
// Now in reveal stage 2
assert_eq!(
p.data_request_pool[&dr_pointer].stage,
Expand All @@ -1182,7 +1182,7 @@ mod tests {
.unwrap();

// Update stages
assert!(p.update_data_request_stages(None).is_empty());
assert!(p.update_data_request_stages(None, None).is_empty());
// Now in tally stage
assert_eq!(
p.data_request_pool[&dr_pointer].stage,
Expand Down Expand Up @@ -1225,7 +1225,7 @@ mod tests {
.unwrap();

// Update stages
assert!(p.update_data_request_stages(None).is_empty());
assert!(p.update_data_request_stages(None, None).is_empty());
// Now in reveal stage 1
assert_eq!(
p.data_request_pool[&dr_pointer].stage,
Expand All @@ -1237,7 +1237,7 @@ mod tests {
);

// Update stages
assert!(p.update_data_request_stages(None).is_empty());
assert!(p.update_data_request_stages(None, None).is_empty());
// Now in reveal stage 2
assert_eq!(
p.data_request_pool[&dr_pointer].stage,
Expand All @@ -1249,7 +1249,7 @@ mod tests {
);

// Update stages
assert!(p.update_data_request_stages(None).is_empty());
assert!(p.update_data_request_stages(None, None).is_empty());
// Now in reveal stage 3
assert_eq!(
p.data_request_pool[&dr_pointer].stage,
Expand All @@ -1261,7 +1261,7 @@ mod tests {
);

// Update stages
assert!(p.update_data_request_stages(None).is_empty());
assert!(p.update_data_request_stages(None, None).is_empty());
// Now in tally stage, after 3 reveal stages with no reveals
assert_eq!(
p.data_request_pool[&dr_pointer].stage,
Expand Down Expand Up @@ -1317,7 +1317,7 @@ mod tests {
);

// Update stages. This will return our reveal transaction
let my_reveals = p.update_data_request_stages(None);
let my_reveals = p.update_data_request_stages(None, None);
assert_eq!(my_reveals.len(), 1);
let my_reveal = &my_reveals[0];
assert_eq!(my_reveal, &reveal_transaction);
Expand Down Expand Up @@ -1358,7 +1358,7 @@ mod tests {

// Since extra_commit_rounds = 0, updating again in commit stage will
// move the data request to tally stage
assert!(p.update_data_request_stages(None).is_empty());
assert!(p.update_data_request_stages(None, None).is_empty());

// Now in tally stage
assert_eq!(
Expand All @@ -1385,7 +1385,7 @@ mod tests {
assert_eq!(p.data_request_pool[&dr_pointer].backup_witnesses(), 1);

// Second commitment round
assert!(p.update_data_request_stages(None).is_empty());
assert!(p.update_data_request_stages(None, None).is_empty());
assert_eq!(
p.data_request_pool[&dr_pointer].stage,
DataRequestStage::COMMIT
Expand All @@ -1397,7 +1397,7 @@ mod tests {
assert_eq!(p.data_request_pool[&dr_pointer].backup_witnesses(), 2);

// Third commitment round
assert!(p.update_data_request_stages(None).is_empty());
assert!(p.update_data_request_stages(None, None).is_empty());
assert_eq!(
p.data_request_pool[&dr_pointer].stage,
DataRequestStage::COMMIT
Expand All @@ -1410,7 +1410,7 @@ mod tests {

// Since extra_commit_rounds = 1, updating again in commit stage will
// move the data request to tally stage
assert!(p.update_data_request_stages(None).is_empty());
assert!(p.update_data_request_stages(None, None).is_empty());

// Now in tally stage
assert_eq!(
Expand All @@ -1436,7 +1436,7 @@ mod tests {
);
assert_eq!(p.data_request_pool[&dr_pointer].backup_witnesses(), 1);

assert!(p.update_data_request_stages(None).is_empty());
assert!(p.update_data_request_stages(None, None).is_empty());

// Add commit and update stage
let (_fake_block_hash, p, dr_pointer) =
Expand Down
157 changes: 151 additions & 6 deletions data_structures/src/staking/helpers.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
use std::fmt::{Debug, Display, Formatter};
use std::{rc::Rc, str::FromStr, sync::RwLock};
use std::{
collections::BTreeMap,
fmt::{Debug, Display, Formatter},
iter::Sum,
marker::PhantomData,
ops::{Add, Div, Mul, Sub},
rc::Rc,
str::FromStr,
sync::RwLock,
};

use failure::Error;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use num_traits::{Saturating, Zero};
use serde::{
de::{DeserializeOwned, MapAccess, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};

use crate::{chain::PublicKeyHash, proto::ProtobufConvert};

use crate::staking::prelude::*;
use crate::{chain::PublicKeyHash, proto::ProtobufConvert, staking::prelude::*};

/// Just a type alias for consistency of using the same data type to represent power.
pub type Power = u64;
Expand Down Expand Up @@ -188,3 +198,138 @@ pub enum CensusStrategy {
/// power.
Evenly(usize) = 3,
}

impl<Address, Coins, Epoch, Power> Serialize for Stakes<Address, Coins, Epoch, Power>
where
Address: Default + Ord,
Coins: Ord,
Epoch: Default,
StakeKey<Address>: Serialize,
SyncStake<Address, Coins, Epoch, Power>: Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.by_key.serialize(serializer)
}
}

impl<'de, Address, Coins, Epoch, Power> Deserialize<'de> for Stakes<Address, Coins, Epoch, Power>
where
Address: Clone + Debug + Default + DeserializeOwned + Display + Ord + Send + Sync,
Coins: Copy
+ Debug
+ Default
+ Display
+ DeserializeOwned
+ From<u64>
+ Mul<Output = Coins>
+ Mul<Epoch, Output = Power>
+ Ord
+ Send
+ Sub<Output = Coins>
+ Sum
+ Sync
+ Zero,
Epoch: Copy
+ Debug
+ Default
+ DeserializeOwned
+ Display
+ From<u32>
+ Saturating
+ Send
+ Sub<Output = Epoch>
+ Sync,
Power:
Add<Output = Power> + Copy + Default + DeserializeOwned + Div<Output = Power> + Ord + Sum,
u64: From<Coins> + From<Power>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_map(StakesVisitor::<Address, Coins, Epoch, Power>::default())
}
}

#[derive(Default)]
struct StakesVisitor<Address, Coins, Epoch, Power> {
phantom_address: PhantomData<Address>,
phantom_coins: PhantomData<Coins>,
phantom_epoch: PhantomData<Epoch>,
phantom_power: PhantomData<Power>,
}

impl<'de, Address, Coins, Epoch, Power> Visitor<'de> for StakesVisitor<Address, Coins, Epoch, Power>
where
Address: Clone + Debug + Default + Deserialize<'de> + Display + Ord + Send + Sync,
Coins: Copy
+ Debug
+ Default
+ Deserialize<'de>
+ Display
+ From<u64>
+ Mul<Output = Coins>
+ Mul<Epoch, Output = Power>
+ Ord
+ Send
+ Sub<Output = Coins>
+ Sum
+ Sync
+ Zero,
Epoch: Copy
+ Debug
+ Default
+ Deserialize<'de>
+ Display
+ From<u32>
+ Send
+ Saturating
+ Sub<Output = Epoch>
+ Sync,
Power: Add<Output = Power> + Copy + Default + Div<Output = Power> + Ord + Sum,
u64: From<Coins> + From<Power>,
{
type Value = Stakes<Address, Coins, Epoch, Power>;

fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
formatter.write_str("Stakes<Address, Coins, Epoch, Power>")
}

fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut entries =
<BTreeMap<StakeKey<Address>, SyncStake<Address, Coins, Epoch, Power>>>::new();

while let Some((key, value)) = map.next_entry()? {
entries.insert(key, value);
}

let stakes = Stakes::with_entries(entries);

Ok(stakes)
}
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_cloning_assumptions() {
let a =
SyncStake::<String, u64, u64, u64>::from(Stake::from_parts(123, Default::default()));
let b = a.clone();

{
let mut value = b.value.write().unwrap();
value.coins = 456;
}

assert_eq!(a.value.read().unwrap().coins, 456);
}
}
Loading

0 comments on commit c1b696a

Please sign in to comment.