diff --git a/rust/catalyst-voting/Cargo.toml b/rust/catalyst-voting/Cargo.toml index ee48ab65c..2c23a9165 100644 --- a/rust/catalyst-voting/Cargo.toml +++ b/rust/catalyst-voting/Cargo.toml @@ -24,10 +24,10 @@ curve25519-dalek = { version = "4.1.3", features = ["digest", "rand_core"] } ed25519-dalek = { version = "2.1.1", features = ["rand_core"] } blake2b_simd = "1.0.2" rayon = "1.10.0" -proptest = { version = "1.5.0" } [dev-dependencies] criterion = "0.5.1" +proptest = { version = "1.5.0" } # Potentially it could be replaced with using `proptest::property_test` attribute macro, # after this PR will be merged https://github.com/proptest-rs/proptest/pull/523 test-strategy = "0.4.0" diff --git a/rust/catalyst-voting/src/crypto/ed25519/mod.rs b/rust/catalyst-voting/src/crypto/ed25519/mod.rs index ed16c6df3..a2066ad4d 100644 --- a/rust/catalyst-voting/src/crypto/ed25519/mod.rs +++ b/rust/catalyst-voting/src/crypto/ed25519/mod.rs @@ -6,6 +6,7 @@ use ed25519_dalek::{ ed25519::signature::Signer, Signature as Ed25519Signature, SigningKey, VerifyingKey, }; +use super::rng::default_rng; use crate::crypto::rng::rand_core::CryptoRngCore; /// `Ed25519` private key struct. @@ -19,6 +20,11 @@ impl PrivateKey { Self(SigningKey::generate(rng)) } + /// Randomly generate the `ElectionSecretKey` with the `crypto::default_rng`. + pub fn random_with_default_rng() -> Self { + Self::random(&mut default_rng()) + } + /// Get associated `Ed25519` public key. pub fn public_key(&self) -> PublicKey { PublicKey(self.0.verifying_key()) @@ -46,7 +52,7 @@ pub fn verify_signature(pk: &PublicKey, msg: &[u8], sig: &Signature) -> bool { pk.0.verify_strict(msg, &sig.0).is_ok() } -#[allow(missing_docs, clippy::missing_docs_in_private_items)] +#[cfg(test)] mod arbitrary_impl { use proptest::prelude::{any, Arbitrary, BoxedStrategy, Strategy}; diff --git a/rust/catalyst-voting/src/crypto/elgamal/mod.rs b/rust/catalyst-voting/src/crypto/elgamal/mod.rs index 4913476e6..946adedc6 100644 --- a/rust/catalyst-voting/src/crypto/elgamal/mod.rs +++ b/rust/catalyst-voting/src/crypto/elgamal/mod.rs @@ -66,7 +66,7 @@ impl Add<&Ciphertext> for &Ciphertext { } } -#[allow(missing_docs, clippy::missing_docs_in_private_items)] +#[cfg(test)] mod arbitrary_impl { use proptest::{ arbitrary::any, diff --git a/rust/catalyst-voting/src/crypto/group/ristretto255/mod.rs b/rust/catalyst-voting/src/crypto/group/ristretto255/mod.rs index e8963224a..93ffe6ac6 100644 --- a/rust/catalyst-voting/src/crypto/group/ristretto255/mod.rs +++ b/rust/catalyst-voting/src/crypto/group/ristretto255/mod.rs @@ -159,7 +159,7 @@ impl Sub<&GroupElement> for &GroupElement { } } -#[allow(missing_docs, clippy::missing_docs_in_private_items)] +#[cfg(test)] mod arbitrary_impl { use proptest::{ arbitrary::any, diff --git a/rust/catalyst-voting/src/crypto/zk_unit_vector/mod.rs b/rust/catalyst-voting/src/crypto/zk_unit_vector/mod.rs index b9775b45c..b45fd95c2 100644 --- a/rust/catalyst-voting/src/crypto/zk_unit_vector/mod.rs +++ b/rust/catalyst-voting/src/crypto/zk_unit_vector/mod.rs @@ -235,7 +235,7 @@ fn check_2( &right_1 + &right_2 == left } -#[allow(missing_docs, clippy::missing_docs_in_private_items)] +#[cfg(test)] mod arbitrary_impl { use proptest::{ prelude::{any_with, Arbitrary, BoxedStrategy, Strategy}, diff --git a/rust/catalyst-voting/src/crypto/zk_unit_vector/randomness_announcements.rs b/rust/catalyst-voting/src/crypto/zk_unit_vector/randomness_announcements.rs index 19e0d14ed..615b8b48c 100644 --- a/rust/catalyst-voting/src/crypto/zk_unit_vector/randomness_announcements.rs +++ b/rust/catalyst-voting/src/crypto/zk_unit_vector/randomness_announcements.rs @@ -79,7 +79,7 @@ impl ResponseRandomness { } } -#[allow(missing_docs, clippy::missing_docs_in_private_items)] +#[cfg(test)] mod arbitrary_impl { use proptest::{ arbitrary::any, diff --git a/rust/catalyst-voting/src/vote_protocol/committee/mod.rs b/rust/catalyst-voting/src/vote_protocol/committee/mod.rs index 9a53f7d75..5f26aa481 100644 --- a/rust/catalyst-voting/src/vote_protocol/committee/mod.rs +++ b/rust/catalyst-voting/src/vote_protocol/committee/mod.rs @@ -36,7 +36,7 @@ impl ElectionSecretKey { #[derive(Debug, Clone, PartialEq, Eq)] pub struct ElectionPublicKey(pub(crate) GroupElement); -#[allow(missing_docs, clippy::missing_docs_in_private_items)] +#[cfg(test)] mod arbitrary_impl { use proptest::prelude::{any, Arbitrary, BoxedStrategy, Strategy}; diff --git a/rust/catalyst-voting/src/vote_protocol/voter/mod.rs b/rust/catalyst-voting/src/vote_protocol/voter/mod.rs index f16708461..6277dbd36 100644 --- a/rust/catalyst-voting/src/vote_protocol/voter/mod.rs +++ b/rust/catalyst-voting/src/vote_protocol/voter/mod.rs @@ -141,7 +141,7 @@ pub fn decrypt_vote(vote: &EncryptedVote, secret_key: &ElectionSecretKey) -> any bail!("Invalid encrypted vote, not a valid unit vector.") } -#[allow(missing_docs, clippy::missing_docs_in_private_items)] +#[cfg(test)] mod arbitrary_impl { use proptest::{ prelude::{any_with, Arbitrary, BoxedStrategy, Strategy}, diff --git a/rust/catalyst-voting/src/vote_protocol/voter/proof.rs b/rust/catalyst-voting/src/vote_protocol/voter/proof.rs index a244dcc87..2c68a7f3f 100644 --- a/rust/catalyst-voting/src/vote_protocol/voter/proof.rs +++ b/rust/catalyst-voting/src/vote_protocol/voter/proof.rs @@ -107,7 +107,7 @@ pub fn verify_voter_proof( verify_unit_vector_proof(&proof.0, encrypted_vote.0, &public_key.0, &commitment.0) } -#[allow(missing_docs, clippy::missing_docs_in_private_items)] +#[cfg(test)] mod arbitrary_impl { use proptest::prelude::{any_with, Arbitrary, BoxedStrategy, Strategy}; diff --git a/rust/vote-tx-v1/Cargo.toml b/rust/vote-tx-v1/Cargo.toml index e99a5acab..d442dd89e 100644 --- a/rust/vote-tx-v1/Cargo.toml +++ b/rust/vote-tx-v1/Cargo.toml @@ -13,9 +13,9 @@ workspace = true [dependencies] catalyst-voting = { version = "0.0.1", path = "../catalyst-voting" } anyhow = "1.0.89" -proptest = { version = "1.5.0" } [dev-dependencies] +proptest = { version = "1.5.0" } # Potentially it could be replaced with using `proptest::property_test` attribute macro, # after this PR will be merged https://github.com/proptest-rs/proptest/pull/523 test-strategy = "0.4.0" diff --git a/rust/vote-tx-v1/src/decoding.rs b/rust/vote-tx-v1/src/decoding.rs index 3eb78bdae..3e45e66f4 100644 --- a/rust/vote-tx-v1/src/decoding.rs +++ b/rust/vote-tx-v1/src/decoding.rs @@ -216,12 +216,31 @@ impl Tx { #[cfg(test)] mod tests { + use catalyst_voting::{ + crypto::{ed25519::PrivateKey, rng::rand_core::OsRng}, + vote_protocol::committee::ElectionSecretKey, + }; use test_strategy::proptest; use super::*; #[proptest] - fn tx_to_bytes_from_bytes_test(t1: Tx) { + fn tx_public_to_bytes_from_bytes_test( + vote_plan_id: [u8; 32], proposal_index: u8, #[strategy(1u8..5)] voting_options: u8, + #[strategy(0..#voting_options)] choice: u8, + ) { + let mut rng = OsRng; + let users_private_key = PrivateKey::random(&mut rng); + + let t1 = Tx::new_public( + vote_plan_id, + proposal_index, + voting_options, + choice, + &users_private_key, + ) + .unwrap(); + let bytes = t1.to_bytes(); let mut reader = bytes.as_slice(); @@ -242,21 +261,89 @@ mod tests { assert_eq!(proposal_index, t1.proposal_index); let vote_tag = read_be_u8(&mut reader).unwrap(); - assert!(vote_tag == PUBLIC_VOTE_TAG || vote_tag == PRIVATE_VOTE_TAG); - match vote_tag { - PUBLIC_VOTE_TAG => { - let vote = read_be_u8(&mut reader).unwrap(); - assert_eq!(VotePayload::Public(vote), t1.vote); - }, - PRIVATE_VOTE_TAG => { - let size = read_be_u8(&mut reader).unwrap(); - let vote = EncryptedVote::from_bytes(&mut reader, size.into()).unwrap(); - let size = read_be_u8(&mut reader).unwrap(); - let proof = VoterProof::from_bytes(&mut reader, size.into()).unwrap(); - assert_eq!(VotePayload::Private(vote, proof), t1.vote); - }, - _ => {}, - } + assert!(vote_tag == PUBLIC_VOTE_TAG); + + let vote = read_be_u8(&mut reader).unwrap(); + assert_eq!(VotePayload::Public(vote), t1.vote); + + let block_date = read_be_u64(&mut reader).unwrap(); + assert_eq!(block_date, 0); + + let inputs_amount = read_be_u8(&mut reader).unwrap(); + assert_eq!(inputs_amount, NUMBER_OF_INPUTS); + + let outputs_amount = read_be_u8(&mut reader).unwrap(); + assert_eq!(outputs_amount, NUMBER_OF_OUTPUTS); + + let input_tag = read_be_u8(&mut reader).unwrap(); + assert_eq!(input_tag, INPUT_TAG); + + let value = read_be_u64(&mut reader).unwrap(); + assert_eq!(value, 0); + + let public_key = read_array(&mut reader).unwrap(); + assert_eq!(PublicKey::from_bytes(&public_key).unwrap(), t1.public_key); + + let witness_tag = read_be_u8(&mut reader).unwrap(); + assert_eq!(witness_tag, WITNESS_TAG); + + let nonce = read_be_u32(&mut reader).unwrap(); + assert_eq!(nonce, 0); + + let signature = read_array(&mut reader).unwrap(); + assert_eq!(Signature::from_bytes(&signature), t1.signature); + + let t2 = Tx::from_bytes(&mut bytes.as_slice()).unwrap(); + assert_eq!(t1, t2); + } + + #[proptest] + fn tx_private_to_bytes_from_bytes_test( + vote_plan_id: [u8; 32], proposal_index: u8, #[strategy(1u8..5)] voting_options: u8, + #[strategy(0..#voting_options)] choice: u8, + ) { + let mut rng = OsRng; + let users_private_key = PrivateKey::random(&mut rng); + let election_secret_key = ElectionSecretKey::random(&mut rng); + let election_public_key = election_secret_key.public_key(); + + let t1 = Tx::new_private( + vote_plan_id, + proposal_index, + voting_options, + choice, + &election_public_key, + &users_private_key, + &mut rng, + ) + .unwrap(); + + let bytes = t1.to_bytes(); + + let mut reader = bytes.as_slice(); + + let size = read_be_u32(&mut reader).unwrap(); + assert_eq!(size as usize, bytes.len() - 4); + + let padding_tag = read_be_u8(&mut reader).unwrap(); + assert_eq!(padding_tag, PADDING_TAG); + + let fragment_tag = read_be_u8(&mut reader).unwrap(); + assert_eq!(fragment_tag, FRAGMENT_TAG); + + let vote_plan_id = read_array(&mut reader).unwrap(); + assert_eq!(vote_plan_id, t1.vote_plan_id); + + let proposal_index = read_be_u8(&mut reader).unwrap(); + assert_eq!(proposal_index, t1.proposal_index); + + let vote_tag = read_be_u8(&mut reader).unwrap(); + assert!(vote_tag == PRIVATE_VOTE_TAG); + let size = read_be_u8(&mut reader).unwrap(); + let vote = EncryptedVote::from_bytes(&mut reader, size.into()).unwrap(); + let size = read_be_u8(&mut reader).unwrap(); + let proof = VoterProof::from_bytes(&mut reader, size.into()).unwrap(); + assert_eq!(VotePayload::Private(vote, proof), t1.vote); let block_date = read_be_u64(&mut reader).unwrap(); assert_eq!(block_date, 0); diff --git a/rust/vote-tx-v1/src/lib.rs b/rust/vote-tx-v1/src/lib.rs index c7f46ac67..55c4bdce6 100644 --- a/rust/vote-tx-v1/src/lib.rs +++ b/rust/vote-tx-v1/src/lib.rs @@ -295,63 +295,6 @@ impl VotePayload { } } -#[allow(missing_docs, clippy::missing_docs_in_private_items)] -mod arbitrary_impl { - use catalyst_voting::crypto::ed25519::PrivateKey; - use proptest::prelude::{any, any_with, Arbitrary, BoxedStrategy, Strategy}; - - use super::{EncryptedVote, Signature, Tx, VotePayload, VoterProof}; - - impl Arbitrary for Tx { - type Parameters = (); - type Strategy = BoxedStrategy; - - fn arbitrary_with((): Self::Parameters) -> Self::Strategy { - any::<( - [u8; 32], - u8, - VotePayload, - PrivateKey, - [u8; Signature::BYTES_SIZE], - )>() - .prop_map( - |(vote_plan_id, proposal_index, vote, sk, signature_bytes)| { - Tx { - vote_plan_id, - proposal_index, - vote, - public_key: sk.public_key(), - signature: Signature::from_bytes(&signature_bytes), - } - }, - ) - .boxed() - } - } - - impl Arbitrary for VotePayload { - type Parameters = (); - type Strategy = BoxedStrategy; - - fn arbitrary_with((): Self::Parameters) -> Self::Strategy { - any::() - .prop_flat_map(|b| { - if b { - any::().prop_map(VotePayload::Public).boxed() - } else { - any::<(u8, u8)>() - .prop_flat_map(|(s1, s2)| { - any_with::<(EncryptedVote, VoterProof)>((s1.into(), s2.into())) - .prop_map(|(v, p)| VotePayload::Private(v, p)) - }) - .boxed() - } - }) - .boxed() - } - } -} - #[cfg(test)] mod tests { use catalyst_voting::{ @@ -364,9 +307,10 @@ mod tests { #[proptest] fn tx_test( vote_plan_id: [u8; 32], proposal_index: u8, #[strategy(1u8..5)] voting_options: u8, - #[strategy(0..#voting_options)] choice: u8, users_private_key: PrivateKey, - election_secret_key: ElectionSecretKey, + #[strategy(0..#voting_options)] choice: u8, ) { + let users_private_key = PrivateKey::random_with_default_rng(); + let election_secret_key = ElectionSecretKey::random_with_default_rng(); let election_public_key = election_secret_key.public_key(); let tx = Tx::new_public( diff --git a/rust/vote-tx-v2/Cargo.toml b/rust/vote-tx-v2/Cargo.toml index badce38a1..663d2aa7c 100644 --- a/rust/vote-tx-v2/Cargo.toml +++ b/rust/vote-tx-v2/Cargo.toml @@ -15,10 +15,10 @@ workspace = true [dependencies] anyhow = "1.0.89" -proptest = { version = "1.5.0" } minicbor = { version = "0.25.1", features = ["alloc"] } [dev-dependencies] +proptest = { version = "1.5.0" } # Potentially it could be replaced with using `proptest::property_test` attribute macro, # after this PR will be merged https://github.com/proptest-rs/proptest/pull/523 test-strategy = "0.4.0" diff --git a/rust/vote-tx-v2/src/decoding.rs b/rust/vote-tx-v2/src/decoding.rs index 775dd5db6..65e4bbb14 100644 --- a/rust/vote-tx-v2/src/decoding.rs +++ b/rust/vote-tx-v2/src/decoding.rs @@ -240,29 +240,49 @@ impl Encode<()> for PropId { #[cfg(test)] mod tests { + use proptest::{prelude::any_with, sample::size_range}; use test_strategy::proptest; use super::*; use crate::Cbor; + type PropChoice = Vec; + type PropVote = (Vec, Vec, Vec); + #[proptest] - fn generalized_tx_from_bytes_to_bytes_test(generalized_tx: GeneralizedTx) { + fn generalized_tx_from_bytes_to_bytes_test( + vote_type: Vec, + // generates a votes in range from 1 to 10, and choices in range from 1 to 10 + #[strategy(any_with::>(( + size_range(1..10usize), + ( + (size_range(1..10usize), Default::default()), + Default::default(), + Default::default(), + ), + )))] + votes: Vec, + voter_data: Vec, + ) { + let generalized_tx = GeneralizedTx { + tx_body: TxBody { + vote_type: Uuid(vote_type), + votes: votes + .into_iter() + .map(|(choices, proof, prop_id)| { + Vote { + choices: choices.into_iter().map(Choice).collect(), + proof: Proof(proof), + prop_id: PropId(prop_id), + } + }) + .collect(), + voter_data: VoterData(voter_data), + }, + }; + let bytes = generalized_tx.to_bytes().unwrap(); let decoded = GeneralizedTx::from_bytes(&bytes).unwrap(); assert_eq!(generalized_tx, decoded); } - - #[proptest] - fn tx_body_from_bytes_to_bytes_test(tx_body: TxBody) { - let bytes = tx_body.to_bytes().unwrap(); - let decoded = TxBody::from_bytes(&bytes).unwrap(); - assert_eq!(tx_body, decoded); - } - - #[proptest] - fn vote_from_bytes_to_bytes_test(vote: Vote) { - let bytes = vote.to_bytes().unwrap(); - let decoded = Vote::from_bytes(&bytes).unwrap(); - assert_eq!(vote, decoded); - } } diff --git a/rust/vote-tx-v2/src/lib.rs b/rust/vote-tx-v2/src/lib.rs index a169a3296..3c3a57546 100644 --- a/rust/vote-tx-v2/src/lib.rs +++ b/rust/vote-tx-v2/src/lib.rs @@ -82,60 +82,3 @@ pub trait Cbor<'a>: Encode<()> + Decode<'a, ()> { } impl<'a, T> Cbor<'a> for T where T: Encode<()> + Decode<'a, ()> {} - -#[allow(missing_docs, clippy::missing_docs_in_private_items)] -mod arbitrary_impl { - use proptest::{ - prelude::{any, any_with, Arbitrary, BoxedStrategy, Strategy}, - sample::size_range, - }; - - use super::{Choice, GeneralizedTx, Proof, PropId, TxBody, Uuid, Vote, VoterData}; - - impl Arbitrary for GeneralizedTx { - type Parameters = (); - type Strategy = BoxedStrategy; - - fn arbitrary_with((): Self::Parameters) -> Self::Strategy { - any::().prop_map(|tx_body| Self { tx_body }).boxed() - } - } - - impl Arbitrary for TxBody { - type Parameters = (); - type Strategy = BoxedStrategy; - - fn arbitrary_with((): Self::Parameters) -> Self::Strategy { - any::<(Vec, Vec, Vec)>() - .prop_map(|(vote_type, votes, voters_data)| { - Self { - vote_type: Uuid(vote_type), - votes, - voter_data: VoterData(voters_data), - } - }) - .boxed() - } - } - - impl Arbitrary for Vote { - type Parameters = (); - type Strategy = BoxedStrategy; - - fn arbitrary_with((): Self::Parameters) -> Self::Strategy { - any_with::<(Vec>, Vec, Vec)>(( - (size_range(1..10usize), Default::default()), - Default::default(), - Default::default(), - )) - .prop_map(|(choices, proof, prop_id)| { - Self { - choices: choices.into_iter().map(Choice).collect(), - proof: Proof(proof), - prop_id: PropId(prop_id), - } - }) - .boxed() - } - } -}