use snarkvm::{
console::prelude::*,
ledger::{
block::Transaction,
coinbase::{ProverSolution, PuzzleCommitment},
narwhal::{Data, Transmission, TransmissionID},
},
};
use indexmap::{IndexMap, IndexSet};
use parking_lot::RwLock;
use std::sync::Arc;
#[derive(Clone, Debug)]
pub struct Ready<N: Network> {
transmissions: Arc<RwLock<IndexMap<TransmissionID<N>, Transmission<N>>>>,
}
impl<N: Network> Default for Ready<N> {
fn default() -> Self {
Self::new()
}
}
impl<N: Network> Ready<N> {
pub fn new() -> Self {
Self { transmissions: Default::default() }
}
pub fn is_empty(&self) -> bool {
self.transmissions.read().is_empty()
}
pub fn num_transmissions(&self) -> usize {
self.transmissions.read().len()
}
pub fn num_ratifications(&self) -> usize {
self.transmissions.read().keys().filter(|id| matches!(id, TransmissionID::Ratification)).count()
}
pub fn num_solutions(&self) -> usize {
self.transmissions.read().keys().filter(|id| matches!(id, TransmissionID::Solution(..))).count()
}
pub fn num_transactions(&self) -> usize {
self.transmissions.read().keys().filter(|id| matches!(id, TransmissionID::Transaction(..))).count()
}
pub fn transmission_ids(&self) -> IndexSet<TransmissionID<N>> {
self.transmissions.read().keys().copied().collect()
}
pub fn transmissions(&self) -> IndexMap<TransmissionID<N>, Transmission<N>> {
self.transmissions.read().clone()
}
pub fn solutions(&self) -> impl '_ + Iterator<Item = (PuzzleCommitment<N>, Data<ProverSolution<N>>)> {
self.transmissions.read().clone().into_iter().filter_map(|(id, transmission)| match (id, transmission) {
(TransmissionID::Solution(id), Transmission::Solution(solution)) => Some((id, solution)),
_ => None,
})
}
pub fn transactions(&self) -> impl '_ + Iterator<Item = (N::TransactionID, Data<Transaction<N>>)> {
self.transmissions.read().clone().into_iter().filter_map(|(id, transmission)| match (id, transmission) {
(TransmissionID::Transaction(id), Transmission::Transaction(tx)) => Some((id, tx)),
_ => None,
})
}
}
impl<N: Network> Ready<N> {
pub fn contains(&self, transmission_id: impl Into<TransmissionID<N>>) -> bool {
self.transmissions.read().contains_key(&transmission_id.into())
}
pub fn get(&self, transmission_id: impl Into<TransmissionID<N>>) -> Option<Transmission<N>> {
self.transmissions.read().get(&transmission_id.into()).cloned()
}
pub fn insert(&self, transmission_id: impl Into<TransmissionID<N>>, transmission: Transmission<N>) -> bool {
let transmission_id = transmission_id.into();
let is_new = self.transmissions.write().insert(transmission_id, transmission).is_none();
is_new
}
pub fn drain(&self, num_transmissions: usize) -> IndexMap<TransmissionID<N>, Transmission<N>> {
let mut transmissions = self.transmissions.write();
let range = 0..transmissions.len().min(num_transmissions);
transmissions.drain(range).collect::<IndexMap<_, _>>()
}
}
#[cfg(test)]
mod tests {
use super::*;
use snarkvm::ledger::{coinbase::PuzzleCommitment, narwhal::Data};
use ::bytes::Bytes;
type CurrentNetwork = snarkvm::prelude::Testnet3;
#[test]
fn test_ready() {
let rng = &mut TestRng::default();
let data = |rng: &mut TestRng| Data::Buffer(Bytes::from((0..512).map(|_| rng.gen::<u8>()).collect::<Vec<_>>()));
let ready = Ready::<CurrentNetwork>::new();
let commitment_1 = TransmissionID::Solution(PuzzleCommitment::from_g1_affine(rng.gen()));
let commitment_2 = TransmissionID::Solution(PuzzleCommitment::from_g1_affine(rng.gen()));
let commitment_3 = TransmissionID::Solution(PuzzleCommitment::from_g1_affine(rng.gen()));
let solution_1 = Transmission::Solution(data(rng));
let solution_2 = Transmission::Solution(data(rng));
let solution_3 = Transmission::Solution(data(rng));
assert!(ready.insert(commitment_1, solution_1.clone()));
assert!(ready.insert(commitment_2, solution_2.clone()));
assert!(ready.insert(commitment_3, solution_3.clone()));
assert_eq!(ready.num_transmissions(), 3);
let transmission_ids = vec![commitment_1, commitment_2, commitment_3].into_iter().collect::<IndexSet<_>>();
assert_eq!(ready.transmission_ids(), transmission_ids);
transmission_ids.iter().for_each(|id| assert!(ready.contains(*id)));
let commitment_unknown = TransmissionID::Solution(PuzzleCommitment::from_g1_affine(rng.gen()));
assert!(!ready.contains(commitment_unknown));
assert_eq!(ready.get(commitment_1), Some(solution_1.clone()));
assert_eq!(ready.get(commitment_2), Some(solution_2.clone()));
assert_eq!(ready.get(commitment_3), Some(solution_3.clone()));
assert_eq!(ready.get(commitment_unknown), None);
let transmissions = ready.drain(3);
assert!(ready.is_empty());
assert_eq!(ready.transmission_ids(), IndexSet::new());
assert_eq!(
transmissions,
vec![(commitment_1, solution_1), (commitment_2, solution_2), (commitment_3, solution_3)]
.into_iter()
.collect::<IndexMap<_, _>>()
);
}
#[test]
fn test_ready_duplicate() {
use rand::RngCore;
let rng = &mut TestRng::default();
let mut vec = vec![0u8; 512];
rng.fill_bytes(&mut vec);
let data = Data::Buffer(Bytes::from(vec));
let ready = Ready::<CurrentNetwork>::new();
let commitment = TransmissionID::Solution(PuzzleCommitment::from_g1_affine(rng.gen()));
let solution = Transmission::Solution(data);
assert!(ready.insert(commitment, solution.clone()));
assert!(!ready.insert(commitment, solution));
assert_eq!(ready.num_transmissions(), 1);
}
}