diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 930e2fc19c..94ed9ac812 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -7,7 +7,7 @@ members = [ "cbork", "cbork-abnf-parser", "cbork-cddl-parser", - "catalyst-voting", + "catalyst-voting", "jormungandr-vote-tx", ] [workspace.package] diff --git a/rust/Earthfile b/rust/Earthfile index 0339348a2c..714d9df718 100644 --- a/rust/Earthfile +++ b/rust/Earthfile @@ -10,7 +10,7 @@ COPY_SRC: .cargo .config \ c509-certificate \ cardano-chain-follower \ - catalyst-voting \ + catalyst-voting jormungandr-vote-tx \ cbork cbork-abnf-parser cbork-cddl-parser \ hermes-ipfs \ . @@ -53,7 +53,7 @@ build: --cmd="/scripts/std_build.py" \ --args1="--libs=c509-certificate --libs=cardano-chain-follower --libs=hermes-ipfs" \ --args2="--libs=cbork-cddl-parser --libs=cbork-abnf-parser" \ - --args3="--libs=catalyst-voting" \ + --args3="--libs=catalyst-voting --libs=jormungandr-vote-tx" \ --args4="--bins=cbork/cbork" \ --args5="--cov_report=$HOME/build/coverage-report.info" \ --output="release/[^\./]+" \ diff --git a/rust/catalyst-voting/Cargo.toml b/rust/catalyst-voting/Cargo.toml index 2c23a9165d..ee48ab65c3 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/benches/vote_protocol.rs b/rust/catalyst-voting/benches/vote_protocol.rs index 55f9227a94..4c2c40f5bc 100644 --- a/rust/catalyst-voting/benches/vote_protocol.rs +++ b/rust/catalyst-voting/benches/vote_protocol.rs @@ -12,7 +12,7 @@ )] use catalyst_voting::{ - crypto::default_rng, + crypto::rng::default_rng, vote_protocol::{ committee::{ElectionPublicKey, ElectionSecretKey}, tally::{ diff --git a/rust/catalyst-voting/src/crypto/ed25519/mod.rs b/rust/catalyst-voting/src/crypto/ed25519/mod.rs index a9cc4488e5..ed16c6df39 100644 --- a/rust/catalyst-voting/src/crypto/ed25519/mod.rs +++ b/rust/catalyst-voting/src/crypto/ed25519/mod.rs @@ -5,7 +5,8 @@ mod decoding; use ed25519_dalek::{ ed25519::signature::Signer, Signature as Ed25519Signature, SigningKey, VerifyingKey, }; -use rand_core::CryptoRngCore; + +use crate::crypto::rng::rand_core::CryptoRngCore; /// `Ed25519` private key struct. #[must_use] @@ -45,12 +46,11 @@ pub fn verify_signature(pk: &PublicKey, msg: &[u8], sig: &Signature) -> bool { pk.0.verify_strict(msg, &sig.0).is_ok() } -#[cfg(test)] -mod tests { +#[allow(missing_docs, clippy::missing_docs_in_private_items)] +mod arbitrary_impl { use proptest::prelude::{any, Arbitrary, BoxedStrategy, Strategy}; - use test_strategy::proptest; - use super::*; + use super::{PrivateKey, SigningKey}; impl Arbitrary for PrivateKey { type Parameters = (); @@ -62,6 +62,13 @@ mod tests { .boxed() } } +} + +#[cfg(test)] +mod tests { + use test_strategy::proptest; + + use super::*; #[proptest] fn sign_test(private_key: PrivateKey, msg: Vec) { diff --git a/rust/catalyst-voting/src/crypto/elgamal/mod.rs b/rust/catalyst-voting/src/crypto/elgamal/mod.rs index 25b1920284..4913476e6a 100644 --- a/rust/catalyst-voting/src/crypto/elgamal/mod.rs +++ b/rust/catalyst-voting/src/crypto/elgamal/mod.rs @@ -66,15 +66,14 @@ impl Add<&Ciphertext> for &Ciphertext { } } -#[cfg(test)] -mod tests { +#[allow(missing_docs, clippy::missing_docs_in_private_items)] +mod arbitrary_impl { use proptest::{ arbitrary::any, prelude::{Arbitrary, BoxedStrategy, Strategy}, }; - use test_strategy::proptest; - use super::*; + use super::{Ciphertext, GroupElement}; impl Arbitrary for Ciphertext { type Parameters = (); @@ -86,6 +85,13 @@ mod tests { .boxed() } } +} + +#[cfg(test)] +mod tests { + use test_strategy::proptest; + + use super::*; #[proptest] fn ciphertext_add_test(e1: Scalar, e2: Scalar, e3: Scalar, e4: Scalar) { diff --git a/rust/catalyst-voting/src/crypto/group/ristretto255/mod.rs b/rust/catalyst-voting/src/crypto/group/ristretto255/mod.rs index d291da4182..e8963224a7 100644 --- a/rust/catalyst-voting/src/crypto/group/ristretto255/mod.rs +++ b/rust/catalyst-voting/src/crypto/group/ristretto255/mod.rs @@ -15,9 +15,11 @@ use curve25519_dalek::{ traits::Identity, RistrettoPoint, }; -use rand_core::CryptoRngCore; -use crate::crypto::hash::digest::{consts::U64, Digest}; +use crate::crypto::{ + hash::digest::{consts::U64, Digest}, + rng::rand_core::CryptoRngCore, +}; /// Ristretto group scalar. #[derive(Debug, Clone, PartialEq, Eq)] @@ -157,15 +159,14 @@ impl Sub<&GroupElement> for &GroupElement { } } -#[cfg(test)] -mod tests { +#[allow(missing_docs, clippy::missing_docs_in_private_items)] +mod arbitrary_impl { use proptest::{ arbitrary::any, prelude::{Arbitrary, BoxedStrategy, Strategy}, }; - use test_strategy::proptest; - use super::*; + use super::{GroupElement, Mul, Scalar}; impl Arbitrary for Scalar { type Parameters = (); @@ -186,6 +187,13 @@ mod tests { .boxed() } } +} + +#[cfg(test)] +mod tests { + use test_strategy::proptest; + + use super::*; #[proptest] fn scalar_arithmetic_tests(e1: Scalar, e2: Scalar, e3: Scalar) { diff --git a/rust/catalyst-voting/src/crypto/mod.rs b/rust/catalyst-voting/src/crypto/mod.rs index 0be3852f7b..626100a456 100644 --- a/rust/catalyst-voting/src/crypto/mod.rs +++ b/rust/catalyst-voting/src/crypto/mod.rs @@ -1,20 +1,10 @@ //! Crypto primitives which are used by voting protocol. -// cspell: words Seedable - -use rand_chacha::ChaCha8Rng; -use rand_core::{CryptoRngCore, SeedableRng}; - pub mod babystep_giantstep; pub mod ed25519; pub mod elgamal; pub mod group; pub mod hash; +pub mod rng; pub mod zk_dl_equality; pub mod zk_unit_vector; - -/// Default random number generator `rand_chacha::ChaCha8Rng`. -#[must_use] -pub fn default_rng() -> impl CryptoRngCore { - ChaCha8Rng::from_entropy() -} diff --git a/rust/catalyst-voting/src/crypto/rng.rs b/rust/catalyst-voting/src/crypto/rng.rs new file mode 100644 index 0000000000..de7c2c4280 --- /dev/null +++ b/rust/catalyst-voting/src/crypto/rng.rs @@ -0,0 +1,14 @@ +//! Random number generator objects. + +// cspell: words Seedable + +use rand_chacha::ChaCha8Rng; +pub use rand_core; +use rand_core::{CryptoRngCore, SeedableRng}; + +/// Default random number generator `rand_chacha::ChaCha8Rng`. +#[must_use] +#[allow(clippy::module_name_repetitions)] +pub fn default_rng() -> impl CryptoRngCore { + ChaCha8Rng::from_entropy() +} 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 4d0bc6a243..b9775b45c2 100644 --- a/rust/catalyst-voting/src/crypto/zk_unit_vector/mod.rs +++ b/rust/catalyst-voting/src/crypto/zk_unit_vector/mod.rs @@ -16,7 +16,6 @@ use std::ops::Mul; use challenges::{calculate_first_challenge_hash, calculate_second_challenge_hash}; use polynomial::{calculate_polynomial_val, generate_polynomial, Polynomial}; -use rand_core::CryptoRngCore; use randomness_announcements::{Announcement, BlindingRandomness, ResponseRandomness}; use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; use utils::get_bit; @@ -24,6 +23,7 @@ use utils::get_bit; use crate::crypto::{ elgamal::{encrypt, Ciphertext}, group::{GroupElement, Scalar}, + rng::rand_core::CryptoRngCore, }; /// Unit vector proof struct @@ -235,16 +235,14 @@ fn check_2( &right_1 + &right_2 == left } -#[cfg(test)] -mod tests { +#[allow(missing_docs, clippy::missing_docs_in_private_items)] +mod arbitrary_impl { use proptest::{ prelude::{any_with, Arbitrary, BoxedStrategy, Strategy}, sample::size_range, }; - use rand_core::OsRng; - use test_strategy::proptest; - use super::{super::elgamal::generate_public_key, *}; + use super::{Announcement, Ciphertext, ResponseRandomness, Scalar, UnitVectorProof}; impl Arbitrary for UnitVectorProof { type Parameters = usize; @@ -263,6 +261,15 @@ mod tests { .boxed() } } +} + +#[cfg(test)] +mod tests { + use proptest::sample::size_range; + use rand_core::OsRng; + use test_strategy::proptest; + + use super::{super::elgamal::generate_public_key, *}; fn is_unit_vector(vector: &[Scalar]) -> bool { let ones = vector.iter().filter(|s| s == &&Scalar::one()).count(); 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 4771a04a4a..19e0d14ed3 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 @@ -4,9 +4,10 @@ use std::ops::Mul; -use rand_core::CryptoRngCore; - -use crate::crypto::group::{GroupElement, Scalar}; +use crate::crypto::{ + group::{GroupElement, Scalar}, + rng::rand_core::CryptoRngCore, +}; /// Randomness generated in the proof, used for the hiding property. #[derive(Clone, Debug, Eq, PartialEq)] @@ -78,14 +79,14 @@ impl ResponseRandomness { } } -#[cfg(test)] -mod tests { +#[allow(missing_docs, clippy::missing_docs_in_private_items)] +mod arbitrary_impl { use proptest::{ arbitrary::any, prelude::{Arbitrary, BoxedStrategy, Strategy}, }; - use super::*; + use super::{Announcement, BlindingRandomness, GroupElement, ResponseRandomness, Scalar}; impl Arbitrary for BlindingRandomness { type Parameters = (); diff --git a/rust/catalyst-voting/src/lib.rs b/rust/catalyst-voting/src/lib.rs index e0d911e718..b165d78f1c 100644 --- a/rust/catalyst-voting/src/lib.rs +++ b/rust/catalyst-voting/src/lib.rs @@ -1,6 +1,5 @@ //! Voting primitives which are used among Catalyst ecosystem. pub mod crypto; -pub mod txs; mod utils; pub mod vote_protocol; diff --git a/rust/catalyst-voting/src/txs/mod.rs b/rust/catalyst-voting/src/txs/mod.rs deleted file mode 100644 index 71e3abe603..0000000000 --- a/rust/catalyst-voting/src/txs/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -//! A catalyst transaction objects implementation - -pub mod v1; diff --git a/rust/catalyst-voting/src/utils.rs b/rust/catalyst-voting/src/utils.rs index 393c00fb71..95198847ad 100644 --- a/rust/catalyst-voting/src/utils.rs +++ b/rust/catalyst-voting/src/utils.rs @@ -2,30 +2,6 @@ use std::io::Read; -/// Read a single byte from the reader. -#[inline] -pub(crate) fn read_be_u8(reader: &mut R) -> anyhow::Result { - let mut buf = [0u8; 1]; - reader.read_exact(&mut buf)?; - Ok(u8::from_be_bytes(buf)) -} - -/// Read a big-endian u32 from the reader. -#[inline] -pub(crate) fn read_be_u32(reader: &mut R) -> anyhow::Result { - let mut buf = [0u8; 4]; - reader.read_exact(&mut buf)?; - Ok(u32::from_be_bytes(buf)) -} - -/// Read a big-endian u64 from the reader. -#[inline] -pub(crate) fn read_be_u64(reader: &mut R) -> anyhow::Result { - let mut buf = [0u8; 8]; - reader.read_exact(&mut buf)?; - Ok(u64::from_be_bytes(buf)) -} - /// Read a N-byte array from the reader. #[inline] pub(crate) fn read_array(reader: &mut R) -> anyhow::Result<[u8; N]> { diff --git a/rust/catalyst-voting/src/vote_protocol/committee/mod.rs b/rust/catalyst-voting/src/vote_protocol/committee/mod.rs index 0f11ea0bfd..9a53f7d75d 100644 --- a/rust/catalyst-voting/src/vote_protocol/committee/mod.rs +++ b/rust/catalyst-voting/src/vote_protocol/committee/mod.rs @@ -2,12 +2,10 @@ mod decoding; -use rand_core::CryptoRngCore; - use crate::crypto::{ - default_rng, elgamal::generate_public_key, group::{GroupElement, Scalar}, + rng::{default_rng, rand_core::CryptoRngCore}, }; /// Election secret key. @@ -38,11 +36,11 @@ impl ElectionSecretKey { #[derive(Debug, Clone, PartialEq, Eq)] pub struct ElectionPublicKey(pub(crate) GroupElement); -#[cfg(test)] -mod tests { +#[allow(missing_docs, clippy::missing_docs_in_private_items)] +mod arbitrary_impl { use proptest::prelude::{any, Arbitrary, BoxedStrategy, Strategy}; - use super::*; + use super::{ElectionSecretKey, Scalar}; impl Arbitrary for ElectionSecretKey { type Parameters = (); diff --git a/rust/catalyst-voting/src/vote_protocol/tally/proof.rs b/rust/catalyst-voting/src/vote_protocol/tally/proof.rs index 46d89844b1..fbc8a16f30 100644 --- a/rust/catalyst-voting/src/vote_protocol/tally/proof.rs +++ b/rust/catalyst-voting/src/vote_protocol/tally/proof.rs @@ -3,13 +3,11 @@ use std::ops::Mul; -use rand_core::CryptoRngCore; - use super::EncryptedTally; use crate::{ crypto::{ - default_rng, group::{GroupElement, Scalar}, + rng::{default_rng, rand_core::CryptoRngCore}, zk_dl_equality::{generate_dleq_proof, verify_dleq_proof, DleqProof}, }, vote_protocol::committee::{ElectionPublicKey, ElectionSecretKey}, diff --git a/rust/catalyst-voting/src/vote_protocol/voter/decoding.rs b/rust/catalyst-voting/src/vote_protocol/voter/decoding.rs index f4f08cc866..8924d52513 100644 --- a/rust/catalyst-voting/src/vote_protocol/voter/decoding.rs +++ b/rust/catalyst-voting/src/vote_protocol/voter/decoding.rs @@ -12,7 +12,8 @@ use crate::{ impl EncryptedVote { /// Get an underlying vector length. - pub(crate) fn size(&self) -> usize { + #[must_use] + pub fn size(&self) -> usize { self.0.len() } diff --git a/rust/catalyst-voting/src/vote_protocol/voter/mod.rs b/rust/catalyst-voting/src/vote_protocol/voter/mod.rs index 7937faa497..f167084613 100644 --- a/rust/catalyst-voting/src/vote_protocol/voter/mod.rs +++ b/rust/catalyst-voting/src/vote_protocol/voter/mod.rs @@ -4,15 +4,14 @@ mod decoding; pub mod proof; use anyhow::{anyhow, bail, ensure}; -use rand_core::CryptoRngCore; use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; use super::committee::{ElectionPublicKey, ElectionSecretKey}; use crate::crypto::{ babystep_giantstep::BabyStepGiantStep, - default_rng, elgamal::{decrypt, encrypt, Ciphertext}, group::Scalar, + rng::{default_rng, rand_core::CryptoRngCore}, }; /// A representation of the voter's voting choice. @@ -142,14 +141,14 @@ pub fn decrypt_vote(vote: &EncryptedVote, secret_key: &ElectionSecretKey) -> any bail!("Invalid encrypted vote, not a valid unit vector.") } -#[cfg(test)] -mod tests { +#[allow(missing_docs, clippy::missing_docs_in_private_items)] +mod arbitrary_impl { use proptest::{ prelude::{any_with, Arbitrary, BoxedStrategy, Strategy}, sample::size_range, }; - use super::*; + use super::{Ciphertext, EncryptedVote}; impl Arbitrary for EncryptedVote { type Parameters = usize; @@ -161,6 +160,11 @@ mod tests { .boxed() } } +} + +#[cfg(test)] +mod tests { + use super::*; #[test] fn vote_test() { diff --git a/rust/catalyst-voting/src/vote_protocol/voter/proof.rs b/rust/catalyst-voting/src/vote_protocol/voter/proof.rs index 2f74a4ac01..a244dcc870 100644 --- a/rust/catalyst-voting/src/vote_protocol/voter/proof.rs +++ b/rust/catalyst-voting/src/vote_protocol/voter/proof.rs @@ -4,14 +4,13 @@ use std::ops::Mul; use anyhow::ensure; -use rand_core::CryptoRngCore; use super::{EncryptedVote, EncryptionRandomness, Vote}; use crate::{ crypto::{ - default_rng, group::{GroupElement, Scalar}, hash::digest::{consts::U64, Digest}, + rng::{default_rng, rand_core::CryptoRngCore}, zk_unit_vector::{generate_unit_vector_proof, verify_unit_vector_proof, UnitVectorProof}, }, vote_protocol::committee::ElectionPublicKey, @@ -108,11 +107,11 @@ pub fn verify_voter_proof( verify_unit_vector_proof(&proof.0, encrypted_vote.0, &public_key.0, &commitment.0) } -#[cfg(test)] -mod tests { +#[allow(missing_docs, clippy::missing_docs_in_private_items)] +mod arbitrary_impl { use proptest::prelude::{any_with, Arbitrary, BoxedStrategy, Strategy}; - use super::*; + use super::{UnitVectorProof, VoterProof}; impl Arbitrary for VoterProof { type Parameters = usize; diff --git a/rust/jormungandr-vote-tx/Cargo.toml b/rust/jormungandr-vote-tx/Cargo.toml new file mode 100644 index 0000000000..42a2088acb --- /dev/null +++ b/rust/jormungandr-vote-tx/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "jormungandr-vote-tx" +version = "0.0.1" +edition.workspace = true +authors.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true + +[lints] +workspace = true + +[dependencies] +catalyst-voting = { version = "0.0.1", path = "../catalyst-voting" } +anyhow = "1.0.89" +proptest = { version = "1.5.0" } + +[dev-dependencies] +# 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/txs/v1/decoding.rs b/rust/jormungandr-vote-tx/src/decoding.rs similarity index 84% rename from rust/catalyst-voting/src/txs/v1/decoding.rs rename to rust/jormungandr-vote-tx/src/decoding.rs index 58e56c517a..3eb78bdae9 100644 --- a/rust/catalyst-voting/src/txs/v1/decoding.rs +++ b/rust/jormungandr-vote-tx/src/decoding.rs @@ -3,11 +3,11 @@ use std::io::Read; use anyhow::{anyhow, bail, ensure}; +use catalyst_voting::crypto::ed25519::{PublicKey, Signature}; -use super::{EncryptedVote, Tx, VotePayload, VoterProof}; use crate::{ - crypto::ed25519::{PublicKey, Signature}, utils::{read_array, read_be_u32, read_be_u64, read_be_u8}, + EncryptedVote, Tx, VotePayload, VoterProof, }; /// Jörmungandr tx fragment tag. @@ -216,60 +216,9 @@ impl Tx { #[cfg(test)] mod tests { - use proptest::prelude::{any, any_with, Arbitrary, BoxedStrategy, Strategy}; use test_strategy::proptest; use super::*; - use crate::crypto::ed25519::PrivateKey; - - 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() - } - } #[proptest] fn tx_to_bytes_from_bytes_test(t1: Tx) { diff --git a/rust/catalyst-voting/src/txs/v1/mod.rs b/rust/jormungandr-vote-tx/src/lib.rs similarity index 78% rename from rust/catalyst-voting/src/txs/v1/mod.rs rename to rust/jormungandr-vote-tx/src/lib.rs index c98bfe2835..c8859180aa 100644 --- a/rust/catalyst-voting/src/txs/v1/mod.rs +++ b/rust/jormungandr-vote-tx/src/lib.rs @@ -1,11 +1,12 @@ -//! A Jörmungandr transaction object structured following this [spec](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/catalyst_voting/transaction/#v1-jormungandr) +//! A Jörmungandr transaction object structured following this +//! [spec](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/catalyst_voting/jorm/) //! //! ```rust //! use catalyst_voting::{ -//! crypto::{default_rng, ed25519::PrivateKey}, -//! txs::v1::Tx, +//! crypto::{ed25519::PrivateKey, rng::default_rng}, //! vote_protocol::committee::ElectionSecretKey, //! }; +//! use jormungandr_vote_tx::Tx; //! //! let vote_plan_id = [0u8; 32]; //! let proposal_index = 0u8; @@ -14,7 +15,8 @@ //! let choice = 1; //! //! let users_private_key = PrivateKey::random(&mut default_rng()); -//! let election_public_key = ElectionSecretKey::random_with_default_rng().public_key(); +//! let election_secret_key = ElectionSecretKey::random_with_default_rng(); +//! let election_public_key = election_secret_key.public_key(); //! //! let public_tx = Tx::new_public( //! vote_plan_id, @@ -25,6 +27,8 @@ //! ) //! .unwrap(); //! public_tx.verify_signature().unwrap(); +//! let tx_choice = public_tx.public_choice().unwrap(); +//! assert_eq!(tx_choice, choice); //! //! let private_tx = Tx::new_private_with_default_rng( //! vote_plan_id, @@ -37,18 +41,19 @@ //! .unwrap(); //! private_tx.verify_signature().unwrap(); //! private_tx.verify_proof(&election_public_key).unwrap(); +//! let tx_choice = private_tx.private_choice(&election_secret_key).unwrap(); +//! assert_eq!(tx_choice, choice); //! ``` mod decoding; +mod utils; use anyhow::ensure; -use rand_core::CryptoRngCore; - -use crate::{ +use catalyst_voting::{ crypto::{ - default_rng, ed25519::{sign, verify_signature, PrivateKey, PublicKey, Signature}, hash::{digest::Digest, Blake2b256Hasher, Blake2b512Hasher}, + rng::{default_rng, rand_core::CryptoRngCore}, }, vote_protocol::{ committee::{ElectionPublicKey, ElectionSecretKey}, @@ -290,12 +295,71 @@ 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::{ + crypto::ed25519::PrivateKey, vote_protocol::committee::ElectionSecretKey, + }; use test_strategy::proptest; use super::*; - use crate::{crypto::ed25519::PrivateKey, vote_protocol::committee::ElectionSecretKey}; #[proptest] fn tx_test( diff --git a/rust/jormungandr-vote-tx/src/utils.rs b/rust/jormungandr-vote-tx/src/utils.rs new file mode 100644 index 0000000000..77a7795059 --- /dev/null +++ b/rust/jormungandr-vote-tx/src/utils.rs @@ -0,0 +1,35 @@ +//! Utility functions. + +use std::io::Read; + +/// Read a single byte from the reader. +#[inline] +pub(crate) fn read_be_u8(reader: &mut R) -> anyhow::Result { + let mut buf = [0u8; 1]; + reader.read_exact(&mut buf)?; + Ok(u8::from_be_bytes(buf)) +} + +/// Read a big-endian u32 from the reader. +#[inline] +pub(crate) fn read_be_u32(reader: &mut R) -> anyhow::Result { + let mut buf = [0u8; 4]; + reader.read_exact(&mut buf)?; + Ok(u32::from_be_bytes(buf)) +} + +/// Read a big-endian u64 from the reader. +#[inline] +pub fn read_be_u64(reader: &mut R) -> anyhow::Result { + let mut buf = [0u8; 8]; + reader.read_exact(&mut buf)?; + Ok(u64::from_be_bytes(buf)) +} + +/// Read a N-byte array from the reader. +#[inline] +pub(crate) fn read_array(reader: &mut R) -> anyhow::Result<[u8; N]> { + let mut buf = [0u8; N]; + reader.read_exact(&mut buf)?; + Ok(buf) +}