Skip to content

Commit c335590

Browse files
Create the catalyst-contest crate
1 parent 44776e3 commit c335590

File tree

8 files changed

+541
-0
lines changed

8 files changed

+541
-0
lines changed

rust/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ members = [
99
"cbork-abnf-parser",
1010
"cbork-cddl-parser",
1111
"cbork-utils",
12+
"catalyst-contest",
1213
"catalyst-voting",
1314
"catalyst-types",
1415
"immutable-ledger",

rust/catalyst-contest/Cargo.toml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[package]
2+
name = "catalyst-contest"
3+
description = "Catalyst voting (contest)"
4+
keywords = ["cardano", "catalyst", "voting", "contest"]
5+
version = "0.0.1"
6+
edition.workspace = true
7+
authors.workspace = true
8+
homepage.workspace = true
9+
repository.workspace = true
10+
license.workspace = true
11+
12+
[lints]
13+
workspace = true
14+
15+
[dependencies]
16+
minicbor = { version = "0.25.1", features = ["alloc", "derive", "half"] }
17+
18+
cbork-utils = { version = "0.0.2", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "cbork-utils-v0.0.2" }
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
//! Voters Choices.
2+
3+
use minicbor::{Decode, Decoder, Encode, Encoder, encode::Write};
4+
5+
/// Voters Choices.
6+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
7+
pub enum Choices {
8+
/// A universal unencrypted set of choices.
9+
Clear(Vec<i64>),
10+
/// ElGamal/Ristretto255 encrypted choices.
11+
ElgamalRistretto255 {
12+
/// ElGamal/Ristretto255 encrypted choices.
13+
choices: Vec<ElgamalRistretto255Choice>,
14+
/// A universal encrypted row proof.
15+
row_proof: Option<RowProof>,
16+
},
17+
}
18+
19+
/// An elgamal encrypted ciphertext `(c1, c2)`.
20+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
21+
pub struct ElgamalRistretto255Choice {
22+
/// An individual Elgamal group element that composes the elgamal cipher text.
23+
pub c1: [u8; 32],
24+
/// An individual Elgamal group element that composes the elgamal cipher text.
25+
pub c2: [u8; 32],
26+
}
27+
28+
/// A universal encrypted row proof.
29+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
30+
pub struct RowProof {
31+
// TODO: FIXME:
32+
}
33+
34+
impl Decode<'_, ()> for Choices {
35+
fn decode(
36+
d: &mut Decoder<'_>,
37+
ctx: &mut (),
38+
) -> Result<Self, minicbor::decode::Error> {
39+
// TODO: FIXME:
40+
todo!()
41+
}
42+
}
43+
44+
impl Encode<()> for Choices {
45+
fn encode<W: Write>(
46+
&self,
47+
e: &mut Encoder<W>,
48+
ctx: &mut (),
49+
) -> Result<(), minicbor::encode::Error<W::Error>> {
50+
match self {
51+
Choices::Clear(choices) => {
52+
e.array(choices.len() as u64 + 1)?;
53+
0.encode(e, ctx)?;
54+
for choice in choices {
55+
choice.encode(e, ctx)?;
56+
}
57+
},
58+
Choices::ElgamalRistretto255 { choices, row_proof } => {
59+
e.array(2)?;
60+
1.encode(e, ctx)?;
61+
e.array(choices.len() as u64 + row_proof.is_some() as u64)?;
62+
choices.encode(e, ctx)?;
63+
if let Some(row_proof) = row_proof {
64+
row_proof.encode(e, ctx)?;
65+
}
66+
},
67+
}
68+
Ok(())
69+
}
70+
}
71+
72+
impl Decode<'_, ()> for ElgamalRistretto255Choice {
73+
fn decode(
74+
d: &mut Decoder<'_>,
75+
ctx: &mut (),
76+
) -> Result<Self, minicbor::decode::Error> {
77+
// TODO: FIXME:
78+
todo!()
79+
}
80+
}
81+
82+
impl Encode<()> for ElgamalRistretto255Choice {
83+
fn encode<W: Write>(
84+
&self,
85+
e: &mut Encoder<W>,
86+
ctx: &mut (),
87+
) -> Result<(), minicbor::encode::Error<W::Error>> {
88+
// TODO: FIXME:
89+
todo!()
90+
}
91+
}
92+
93+
impl Decode<'_, ()> for RowProof {
94+
fn decode(
95+
d: &mut Decoder<'_>,
96+
ctx: &mut (),
97+
) -> Result<Self, minicbor::decode::Error> {
98+
// TODO: FIXME:
99+
todo!()
100+
}
101+
}
102+
103+
impl Encode<()> for RowProof {
104+
fn encode<W: Write>(
105+
&self,
106+
e: &mut Encoder<W>,
107+
ctx: &mut (),
108+
) -> Result<(), minicbor::encode::Error<W::Error>> {
109+
// TODO: FIXME:
110+
todo!()
111+
}
112+
}
113+
114+
#[cfg(test)]
115+
mod tests {
116+
use super::*;
117+
118+
#[test]
119+
fn clear_choices_roundtrip() {
120+
let original = Choices::Clear(vec![1, 2, 3]);
121+
let mut buffer = Vec::new();
122+
original
123+
.encode(&mut Encoder::new(&mut buffer), &mut ())
124+
.unwrap();
125+
let decoded = Choices::decode(&mut Decoder::new(&buffer), &mut ()).unwrap();
126+
assert_eq!(original, decoded);
127+
}
128+
129+
#[test]
130+
fn elgamal_ristretto255_choices_roundtrip() {
131+
let original = Choices::ElgamalRistretto255 {
132+
choices: vec![],
133+
row_proof: Some(RowProof {}),
134+
};
135+
let mut buffer = Vec::new();
136+
original
137+
.encode(&mut Encoder::new(&mut buffer), &mut ())
138+
.unwrap();
139+
let decoded = Choices::decode(&mut Decoder::new(&buffer), &mut ()).unwrap();
140+
assert_eq!(original, decoded);
141+
}
142+
143+
#[test]
144+
fn elgamal_ristretto255_choice_roundtrip() {
145+
let bytes = [
146+
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
147+
25, 26, 27, 28, 29, 30, 31, 32,
148+
];
149+
let original = ElgamalRistretto255Choice {
150+
c1: bytes,
151+
c2: bytes,
152+
};
153+
let mut buffer = Vec::new();
154+
original
155+
.encode(&mut Encoder::new(&mut buffer), &mut ())
156+
.unwrap();
157+
let decoded =
158+
ElgamalRistretto255Choice::decode(&mut Decoder::new(&buffer), &mut ()).unwrap();
159+
assert_eq!(original, decoded);
160+
}
161+
162+
#[test]
163+
fn row_proof_roundtrip() {
164+
let original = RowProof {};
165+
let mut buffer = Vec::new();
166+
original
167+
.encode(&mut Encoder::new(&mut buffer), &mut ())
168+
.unwrap();
169+
let decoded = RowProof::decode(&mut Decoder::new(&buffer), &mut ()).unwrap();
170+
assert_eq!(original, decoded);
171+
}
172+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//! A universal encrypted column proof.
2+
3+
use cbork_utils::decode_helper::decode_array_len;
4+
use minicbor::{Decode, Decoder, Encode, Encoder, encode::Write};
5+
6+
/// A length of the underlying CBOR array.
7+
const ARRAY_LEN: u64 = 2;
8+
9+
/// A universal encrypted column proof.
10+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
11+
pub struct ColumnProof(pub u64);
12+
13+
impl Decode<'_, ()> for ColumnProof {
14+
fn decode(
15+
d: &mut Decoder<'_>,
16+
ctx: &mut (),
17+
) -> Result<Self, minicbor::decode::Error> {
18+
let len = decode_array_len(d, "column proof")?;
19+
if len != ARRAY_LEN {
20+
return Err(minicbor::decode::Error::message(format!(
21+
"Unexpected column proof array length {len}, expected {ARRAY_LEN}"
22+
)));
23+
}
24+
let val = u64::decode(d, ctx)?;
25+
26+
let len = decode_array_len(d, "column proof undefined part")?;
27+
for _ in 0..len {
28+
d.undefined()?;
29+
}
30+
31+
Ok(ColumnProof(val))
32+
}
33+
}
34+
35+
impl Encode<()> for ColumnProof {
36+
fn encode<W: Write>(
37+
&self,
38+
e: &mut Encoder<W>,
39+
ctx: &mut (),
40+
) -> Result<(), minicbor::encode::Error<W::Error>> {
41+
e.array(ARRAY_LEN)?;
42+
self.0.encode(e, ctx)?;
43+
e.array(1)?;
44+
e.undefined()?;
45+
Ok(())
46+
}
47+
}
48+
49+
#[cfg(test)]
50+
mod tests {
51+
use super::*;
52+
53+
#[test]
54+
fn roundtrip() {
55+
let original = ColumnProof(1);
56+
let mut buffer = Vec::new();
57+
original
58+
.encode(&mut Encoder::new(&mut buffer), &mut ())
59+
.unwrap();
60+
let decoded = ColumnProof::decode(&mut Decoder::new(&buffer), &mut ()).unwrap();
61+
assert_eq!(original, decoded);
62+
}
63+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
//! An individual Ballot cast in a Contest by a registered user.
2+
3+
use std::collections::BTreeMap;
4+
5+
use minicbor::{Decode, Decoder, Encode, Encoder, encode::Write};
6+
7+
use crate::{Choices, ColumnProof, EncryptedChoices, MatrixProof};
8+
9+
/// An individual Ballot cast in a Contest by a registered user.
10+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
11+
pub struct ContentBallot {
12+
/// A map of voters choices.
13+
pub choices: BTreeMap<u64, Choices>,
14+
/// A universal encrypted column proof.
15+
pub column_proof: Option<ColumnProof>,
16+
/// A universal encrypted matrix proof.
17+
pub matrix_proof: Option<MatrixProof>,
18+
/// An encrypted voter choice payload.
19+
pub voter_choices: Option<EncryptedChoices>,
20+
}
21+
22+
impl Decode<'_, ()> for ContentBallot {
23+
fn decode(
24+
d: &mut Decoder<'_>,
25+
ctx: &mut (),
26+
) -> Result<Self, minicbor::decode::Error> {
27+
d.map()?;
28+
// TODO: FIXME:
29+
todo!()
30+
}
31+
}
32+
33+
impl Encode<()> for ContentBallot {
34+
fn encode<W: Write>(
35+
&self,
36+
e: &mut Encoder<W>,
37+
ctx: &mut (),
38+
) -> Result<(), minicbor::encode::Error<W::Error>> {
39+
e.begin_map()?;
40+
41+
for (&key, val) in self.choices.iter() {
42+
e.u64(key)?.encode(val)?;
43+
}
44+
if let Some(column_proof) = self.column_proof.as_ref() {
45+
e.str("column-proof")?.encode(column_proof)?;
46+
}
47+
if let Some(matrix_proof) = self.matrix_proof.as_ref() {
48+
e.str("matrix-proof")?.encode(matrix_proof)?;
49+
}
50+
if let Some(voter_choices) = self.voter_choices.as_ref() {
51+
e.str("voter-choices")?.encode(voter_choices)?;
52+
}
53+
54+
e.end()?;
55+
Ok(())
56+
}
57+
}
58+
59+
#[cfg(test)]
60+
mod tests {
61+
use super::*;
62+
use crate::{ElgamalRistretto255Choice, EncryptedBlock, RowProof};
63+
64+
#[test]
65+
fn roundtrip() {
66+
let bytes = [
67+
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
68+
25, 26, 27, 28, 29, 30, 31, 32,
69+
];
70+
let original = ContentBallot {
71+
choices: [
72+
(1, Choices::Clear(vec![1, 2, 3, -4, -5].into())),
73+
(2, Choices::ElgamalRistretto255 {
74+
choices: vec![ElgamalRistretto255Choice {
75+
c1: bytes,
76+
c2: bytes,
77+
}],
78+
row_proof: None,
79+
}),
80+
(3, Choices::ElgamalRistretto255 {
81+
choices: vec![ElgamalRistretto255Choice {
82+
c1: bytes,
83+
c2: bytes,
84+
}],
85+
row_proof: Some(RowProof {}),
86+
}),
87+
]
88+
.into(),
89+
column_proof: Some(ColumnProof(1)),
90+
matrix_proof: Some(MatrixProof(2)),
91+
voter_choices: Some(EncryptedChoices(vec![
92+
EncryptedBlock([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]),
93+
EncryptedBlock([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]),
94+
])),
95+
};
96+
let mut buffer = Vec::new();
97+
original
98+
.encode(&mut Encoder::new(&mut buffer), &mut ())
99+
.unwrap();
100+
let decoded = ContentBallot::decode(&mut Decoder::new(&buffer), &mut ()).unwrap();
101+
assert_eq!(original, decoded);
102+
}
103+
}

0 commit comments

Comments
 (0)