Skip to content

Commit

Permalink
Merge pull request AleoNet#2576 from eqlabs/feat/event-proptest
Browse files Browse the repository at this point in the history
[Narwhal] Proptests for BFT events
  • Loading branch information
vvp authored Jul 27, 2023
2 parents 3b73f0e + 8e23697 commit e7baa61
Show file tree
Hide file tree
Showing 14 changed files with 512 additions and 5 deletions.
3 changes: 2 additions & 1 deletion node/narwhal/committee/src/prop_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ impl Arbitrary for ValidatorSet {
type Strategy = BoxedStrategy<ValidatorSet>;

fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
validator_set(any_valid_validator(), size_range(4..=MAX_COMMITTEE_SIZE as usize)).boxed()
// use minimal validator set to speed up tests that require signing from the committee members
validator_set(any_valid_validator(), size_range(4..=4usize)).boxed()
}
}

Expand Down
29 changes: 29 additions & 0 deletions node/narwhal/src/event/batch_certified.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,32 @@ impl<N: Network> EventTrait for BatchCertified<N> {
Ok(Self { certificate })
}
}

#[cfg(test)]
pub mod prop_tests {
use crate::{
event::{certificate_response::prop_tests::any_batch_certificate, EventTrait},
BatchCertified,
};
use bytes::{BufMut, BytesMut};
use proptest::prelude::{BoxedStrategy, Strategy};
use test_strategy::proptest;

type CurrentNetwork = snarkvm::prelude::Testnet3;

pub fn any_batch_certified() -> BoxedStrategy<BatchCertified<CurrentNetwork>> {
any_batch_certificate().prop_map(BatchCertified::from).boxed()
}

#[proptest]
fn serialize_deserialize(#[strategy(any_batch_certified())] original: BatchCertified<CurrentNetwork>) {
let mut buf = BytesMut::default().writer();
BatchCertified::serialize(&original, &mut buf).unwrap();

let deserialized: BatchCertified<CurrentNetwork> = BatchCertified::deserialize(buf.get_ref().clone()).unwrap();
assert_eq!(
original.certificate.deserialize_blocking().unwrap(),
deserialized.certificate.deserialize_blocking().unwrap()
);
}
}
36 changes: 36 additions & 0 deletions node/narwhal/src/event/batch_propose.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,39 @@ impl<N: Network> EventTrait for BatchPropose<N> {
Ok(Self { round, batch_header })
}
}

#[cfg(test)]
pub mod prop_tests {
use crate::{
event::{certificate_response::prop_tests::any_batch_header, EventTrait},
BatchPropose,
};
use bytes::{BufMut, BytesMut};
use proptest::prelude::{any, BoxedStrategy, Strategy};
use snarkos_node_narwhal_committee::prop_tests::CommitteeContext;
use snarkvm::prelude::narwhal::Data;
use test_strategy::proptest;

type CurrentNetwork = snarkvm::prelude::Testnet3;

pub fn any_batch_propose() -> BoxedStrategy<BatchPropose<CurrentNetwork>> {
any::<CommitteeContext>()
.prop_flat_map(|committee| (any::<u64>(), any_batch_header(&committee)))
.prop_map(|(round, batch_header)| BatchPropose::new(round, Data::Object(batch_header)))
.boxed()
}

#[proptest]
fn serialize_deserialize(#[strategy(any_batch_propose())] original: BatchPropose<CurrentNetwork>) {
let mut buf = BytesMut::default().writer();
BatchPropose::serialize(&original, &mut buf).unwrap();

let deserialized: BatchPropose<CurrentNetwork> = BatchPropose::deserialize(buf.get_ref().clone()).unwrap();
// because of the Data enum, we cannot compare the structs directly even though it derives PartialEq
assert_eq!(original.round, deserialized.round);
assert_eq!(
original.batch_header.deserialize_blocking().unwrap(),
deserialized.batch_header.deserialize_blocking().unwrap()
);
}
}
35 changes: 35 additions & 0 deletions node/narwhal/src/event/batch_signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,38 @@ impl<N: Network> EventTrait for BatchSignature<N> {
})
}
}

#[cfg(test)]
pub mod prop_tests {
use crate::{
event::{
certificate_request::prop_tests::any_field,
challenge_response::prop_tests::any_signature,
EventTrait,
},
helpers::now,
BatchSignature,
};
use bytes::{BufMut, BytesMut};
use proptest::prelude::{BoxedStrategy, Just, Strategy};
use test_strategy::proptest;

type CurrentNetwork = snarkvm::prelude::Testnet3;

pub fn any_batch_signature() -> BoxedStrategy<BatchSignature<CurrentNetwork>> {
(any_field(), any_signature(), Just(now()), -10..10i64)
.prop_map(|(certificate_id, signature, timestamp, drift)| {
BatchSignature::new(certificate_id, signature, timestamp + drift)
})
.boxed()
}

#[proptest]
fn serialize_deserialize(#[strategy(any_batch_signature())] original: BatchSignature<CurrentNetwork>) {
let mut buf = BytesMut::default().writer();
BatchSignature::serialize(&original, &mut buf).unwrap();

let deserialized: BatchSignature<CurrentNetwork> = BatchSignature::deserialize(buf.get_ref().clone()).unwrap();
assert_eq!(original, deserialized);
}
}
29 changes: 29 additions & 0 deletions node/narwhal/src/event/certificate_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,32 @@ impl<N: Network> EventTrait for CertificateRequest<N> {
Ok(Self { certificate_id })
}
}

#[cfg(test)]
pub mod prop_tests {
use crate::{event::EventTrait, helpers::storage::prop_tests::CryptoTestRng, CertificateRequest};
use bytes::{BufMut, BytesMut};
use proptest::prelude::{any, BoxedStrategy, Strategy};
use snarkvm::prelude::{Field, Uniform};
use test_strategy::proptest;

type CurrentNetwork = snarkvm::prelude::Testnet3;

pub fn any_field() -> BoxedStrategy<Field<CurrentNetwork>> {
any::<CryptoTestRng>().prop_map(|mut rng| Field::rand(&mut rng)).boxed()
}

pub fn any_certificate_request() -> BoxedStrategy<CertificateRequest<CurrentNetwork>> {
any_field().prop_map(CertificateRequest::new).boxed()
}

#[proptest]
fn serialize_deserialize(#[strategy(any_certificate_request())] original: CertificateRequest<CurrentNetwork>) {
let mut buf = BytesMut::default().writer();
CertificateRequest::serialize(&original, &mut buf).unwrap();

let deserialized: CertificateRequest<CurrentNetwork> =
CertificateRequest::deserialize(buf.get_ref().clone()).unwrap();
assert_eq!(original, deserialized);
}
}
61 changes: 61 additions & 0 deletions node/narwhal/src/event/certificate_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,64 @@ impl<N: Network> EventTrait for CertificateResponse<N> {
Ok(Self { certificate })
}
}

#[cfg(test)]
pub mod prop_tests {
use crate::{
event::{transmission_response::prop_tests::any_transmission, EventTrait},
helpers::{
now,
storage::prop_tests::{sign_batch_header, CryptoTestRng},
},
CertificateResponse,
};
use bytes::{BufMut, BytesMut};
use proptest::{
collection::vec,
prelude::{any, BoxedStrategy, Just, Strategy},
sample::Selector,
};
use snarkos_node_narwhal_committee::prop_tests::{CommitteeContext, ValidatorSet};
use snarkvm::ledger::narwhal::{BatchCertificate, BatchHeader};
use test_strategy::proptest;

type CurrentNetwork = snarkvm::prelude::Testnet3;

pub fn any_batch_header(committee: &CommitteeContext) -> BoxedStrategy<BatchHeader<CurrentNetwork>> {
(Just(committee.clone()), any::<Selector>(), any::<CryptoTestRng>(), vec(any_transmission(), 0..16))
.prop_map(|(committee, selector, mut rng, transmissions)| {
let CommitteeContext(_, ValidatorSet(validators)) = committee;
let signer = selector.select(validators);
let transmission_ids = transmissions.into_iter().map(|(id, _)| id).collect();

BatchHeader::new(signer.account.private_key(), 0, now(), transmission_ids, Default::default(), &mut rng)
.unwrap()
})
.boxed()
}

pub fn any_batch_certificate() -> BoxedStrategy<BatchCertificate<CurrentNetwork>> {
any::<CommitteeContext>()
.prop_flat_map(|committee| (Just(committee.clone()), any_batch_header(&committee), any::<CryptoTestRng>()))
.prop_map(|(committee, batch_header, mut rng)| {
let CommitteeContext(_, validator_set) = committee;
BatchCertificate::new(batch_header.clone(), sign_batch_header(&validator_set, &batch_header, &mut rng))
.unwrap()
})
.boxed()
}

pub fn any_certificate_response() -> BoxedStrategy<CertificateResponse<CurrentNetwork>> {
any_batch_certificate().prop_map(CertificateResponse::new).boxed()
}

#[proptest]
fn serialize_deserialize(#[strategy(any_certificate_response())] original: CertificateResponse<CurrentNetwork>) {
let mut buf = BytesMut::default().writer();
CertificateResponse::serialize(&original, &mut buf).unwrap();

let deserialized: CertificateResponse<CurrentNetwork> =
CertificateResponse::deserialize(buf.get_ref().clone()).unwrap();
assert_eq!(original, deserialized);
}
}
32 changes: 32 additions & 0 deletions node/narwhal/src/event/challenge_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,35 @@ impl<N: Network> EventTrait for ChallengeRequest<N> {
Ok(Self { version, listener_port, address, nonce })
}
}

#[cfg(test)]
pub mod prop_tests {
use crate::{event::EventTrait, ChallengeRequest};
use bytes::{BufMut, BytesMut};
use proptest::prelude::{any, BoxedStrategy, Strategy};
use snarkos_node_narwhal_committee::prop_tests::any_valid_account;
use test_strategy::proptest;

type CurrentNetwork = snarkvm::prelude::Testnet3;

pub fn any_challenge_request() -> BoxedStrategy<ChallengeRequest<CurrentNetwork>> {
(any_valid_account(), any::<u64>(), any::<u32>(), any::<u16>())
.prop_map(|(account, nonce, version, listener_port)| ChallengeRequest {
address: account.address(),
nonce,
version,
listener_port,
})
.boxed()
}

#[proptest]
fn serialize_deserialize(#[strategy(any_challenge_request())] original: ChallengeRequest<CurrentNetwork>) {
let mut buf = BytesMut::default().writer();
ChallengeRequest::serialize(&original, &mut buf).unwrap();

let deserialized: ChallengeRequest<CurrentNetwork> =
ChallengeRequest::deserialize(buf.get_ref().clone()).unwrap();
assert_eq!(original, deserialized);
}
}
42 changes: 42 additions & 0 deletions node/narwhal/src/event/challenge_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,45 @@ impl<N: Network> EventTrait for ChallengeResponse<N> {
Ok(Self { signature: Data::Buffer(reader.into_inner().freeze()) })
}
}

#[cfg(test)]
pub mod prop_tests {
use crate::{event::EventTrait, helpers::storage::prop_tests::CryptoTestRng, ChallengeResponse};
use bytes::{BufMut, BytesMut};
use proptest::prelude::{any, BoxedStrategy, Strategy};
use snarkvm::{
ledger::narwhal::Data,
prelude::{PrivateKey, Signature},
utilities::rand::Uniform,
};
use test_strategy::proptest;

type CurrentNetwork = snarkvm::prelude::Testnet3;

pub fn any_signature() -> BoxedStrategy<Signature<CurrentNetwork>> {
(any::<CryptoTestRng>(), 0..64)
.prop_map(|(mut rng, message_size)| {
let message: Vec<_> = (0..message_size).map(|_| Uniform::rand(&mut rng)).collect();
let private_key = PrivateKey::new(&mut rng).unwrap();
Signature::sign(&private_key, &message, &mut rng).unwrap()
})
.boxed()
}

pub fn any_challenge_response() -> BoxedStrategy<ChallengeResponse<CurrentNetwork>> {
any_signature().prop_map(|sig| ChallengeResponse { signature: Data::Object(sig) }).boxed()
}

#[proptest]
fn serialize_deserialize(#[strategy(any_challenge_response())] original: ChallengeResponse<CurrentNetwork>) {
let mut buf = BytesMut::default().writer();
ChallengeResponse::serialize(&original, &mut buf).unwrap();

let deserialized: ChallengeResponse<CurrentNetwork> =
ChallengeResponse::deserialize(buf.get_ref().clone()).unwrap();
assert_eq!(
original.signature.deserialize_blocking().unwrap(),
deserialized.signature.deserialize_blocking().unwrap()
);
}
}
41 changes: 41 additions & 0 deletions node/narwhal/src/event/disconnect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,44 @@ impl EventTrait for Disconnect {
}
}
}

#[cfg(test)]
mod tests {
use crate::{event::EventTrait, Disconnect, DisconnectReason};
use bytes::{BufMut, BytesMut};

#[test]
fn serialize_deserialize() {
// TODO switch to an iteration method that doesn't require manually updating this vec if enums are added
let all_reasons = vec![
DisconnectReason::ProtocolViolation,
DisconnectReason::NoReasonGiven,
DisconnectReason::InvalidChallengeResponse,
DisconnectReason::OutdatedClientVersion,
];

for reason in all_reasons.iter() {
let disconnect = Disconnect::from(*reason);
let mut buf = BytesMut::default().writer();
Disconnect::serialize(&disconnect, &mut buf).unwrap();

let disconnect = Disconnect::deserialize(buf.get_ref().clone()).unwrap();
assert_eq!(reason, &disconnect.reason);
}
}

#[test]
fn deserializing_empty_defaults_no_reason() {
let buf = BytesMut::default().writer();
let disconnect = Disconnect::deserialize(buf.get_ref().clone()).unwrap();
assert_eq!(disconnect.reason, DisconnectReason::NoReasonGiven);
}

#[test]
#[should_panic(expected = "Invalid 'Disconnect' event")]
fn deserializing_invalid_data_panics() {
let mut buf = BytesMut::default().writer();
bincode::serialize_into(&mut buf, "not a DisconnectReason-value").unwrap();
let _disconnect = Disconnect::deserialize(buf.get_ref().clone()).unwrap();
}
}
Loading

0 comments on commit e7baa61

Please sign in to comment.