• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

input-output-hk / catalyst-libs / 20908186781

12 Jan 2026 04:34AM UTC coverage: 69.556% (+2.4%) from 67.194%
20908186781

push

github

web-flow
feat(rust/signed-doc): `Contest Delegation` validation (#750)

* wip

* add separate `rep_nomination_ref_check` function

* wip

* wip

* wip

* wip

* add contest_delegation test for `catalyst-contest`

* wip

* fix test

* add test case

* fix fmt, fix spelling

* fix comment

* wip

* fix clippy

* cleanup

148 of 179 new or added lines in 6 files covered. (82.68%)

204 existing lines in 26 files now uncovered.

16007 of 23013 relevant lines covered (69.56%)

2651.03 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.38
/rust/catalyst-voting/src/crypto/zk_unit_vector/decoding.rs
1
//! ZK Unit Vector objects decoding implementation
2

3
use cbork_utils::decode_helper::decode_array_len;
4
use minicbor::{Decode, Decoder, Encode, Encoder, encode::Write};
5

6
use super::{ResponseRandomness, UnitVectorProof};
7
use crate::crypto::{
8
    elgamal::Ciphertext,
9
    group::{GroupElement, Scalar},
10
    zk_unit_vector::randomness_announcements::Announcement,
11
};
12

13
/// A CBOR version of the `UnitVectorProof`.
14
const UNIT_VECTOR_PROOF_VERSION: u64 = 0;
15

16
/// A number of elements in the
17
/// `zkproof-elgamal-ristretto255-unit-vector-with-single-selection-item` data type.
18
///
19
/// The CBOR CDDL schema:
20
/// ```cddl
21
/// zkproof-elgamal-ristretto255-unit-vector-with-single-selection-item = ( zkproof-elgamal-announcement, ~elgamal-ristretto255-encrypted-choice, zkproof-ed25519-r-response )
22
///
23
/// zkproof-elgamal-announcement = ( zkproof-elgamal-group-element, zkproof-elgamal-group-element, zkproof-elgamal-group-element )
24
///
25
/// zkproof-elgamal-group-element = bytes .size 32
26
///
27
/// elgamal-ristretto255-encrypted-choice = [
28
///     c1: elgamal-ristretto255-group-element
29
///     c2: elgamal-ristretto255-group-element
30
/// ]
31
///
32
/// zkproof-ed25519-r-response = ( zkproof-ed25519-scalar, zkproof-ed25519-scalar, zkproof-ed25519-scalar )
33
///
34
/// zkproof-ed25519-scalar = bytes .size 32
35
/// ```
36
///
37
/// Therefore, the total number consists of the following:
38
/// - 8 (zkproof-elgamal-ristretto255-unit-vector-with-single-selection-item)
39
///      - 3 (zkproof-elgamal-announcement = x3 zkproof-elgamal-group-element)
40
///      - 2 (elgamal-ristretto255-encrypted-choice = x2
41
///        elgamal-ristretto255-group-element)
42
///      - 3 (zkproof-ed25519-r-response = x3 zkproof-ed25519-scalar)
43
const ITEM_ELEMENTS_LEN: u64 = 8;
44

45
/// A minimal length of the underlying CBOR array of the `UnitVectorProof` data type.
46
///
47
/// The CBOR CDDL schema:
48
/// ```cddl
49
/// zkproof-elgamal-ristretto255-unit-vector-with-single-selection = [ +zkproof-elgamal-ristretto255-unit-vector-with-single-selection-item, zkproof-ed25519-scalar ]
50
/// ```
51
const MIN_PROOF_CBOR_ARRAY_LEN: u64 = ITEM_ELEMENTS_LEN + 1;
52

53
impl Encode<()> for Announcement {
54
    fn encode<W: Write>(
512✔
55
        &self,
512✔
56
        e: &mut Encoder<W>,
512✔
57
        ctx: &mut (),
512✔
58
    ) -> Result<(), minicbor::encode::Error<W::Error>> {
512✔
59
        self.i.encode(e, ctx)?;
512✔
60
        self.b.encode(e, ctx)?;
512✔
61
        self.a.encode(e, ctx)
512✔
62
    }
512✔
63
}
64

65
impl Decode<'_, ()> for Announcement {
66
    fn decode(
512✔
67
        d: &mut Decoder<'_>,
512✔
68
        ctx: &mut (),
512✔
69
    ) -> Result<Self, minicbor::decode::Error> {
512✔
70
        let i = GroupElement::decode(d, ctx)?;
512✔
71
        let b = GroupElement::decode(d, ctx)?;
512✔
72
        let a = GroupElement::decode(d, ctx)?;
512✔
73
        Ok(Self { i, b, a })
512✔
74
    }
512✔
75
}
76

77
impl Encode<()> for ResponseRandomness {
78
    fn encode<W: Write>(
512✔
79
        &self,
512✔
80
        e: &mut Encoder<W>,
512✔
81
        ctx: &mut (),
512✔
82
    ) -> Result<(), minicbor::encode::Error<W::Error>> {
512✔
83
        self.z.encode(e, ctx)?;
512✔
84
        self.w.encode(e, ctx)?;
512✔
85
        self.v.encode(e, ctx)
512✔
86
    }
512✔
87
}
88

89
impl Decode<'_, ()> for ResponseRandomness {
90
    fn decode(
512✔
91
        d: &mut Decoder<'_>,
512✔
92
        ctx: &mut (),
512✔
93
    ) -> Result<Self, minicbor::decode::Error> {
512✔
94
        let z = Scalar::decode(d, ctx)?;
512✔
95
        let w = Scalar::decode(d, ctx)?;
512✔
96
        let v = Scalar::decode(d, ctx)?;
512✔
97
        Ok(Self { z, w, v })
512✔
98
    }
512✔
99
}
100

101
impl Encode<()> for UnitVectorProof {
102
    fn encode<W: Write>(
256✔
103
        &self,
256✔
104
        e: &mut Encoder<W>,
256✔
105
        ctx: &mut (),
256✔
106
    ) -> Result<(), minicbor::encode::Error<W::Error>> {
256✔
107
        if self.0.len() != self.1.len() || self.0.len() != self.2.len() {
256✔
UNCOV
108
            return Err(minicbor::encode::Error::message(format!(
×
UNCOV
109
                "All UnitVectorProof parts must have the same length: announcements = {}, choice = {}, responses = {}",
×
UNCOV
110
                self.0.len(),
×
UNCOV
111
                self.1.len(),
×
UNCOV
112
                self.2.len()
×
UNCOV
113
            )));
×
114
        }
256✔
115

116
        e.array(2)?;
256✔
117
        UNIT_VECTOR_PROOF_VERSION.encode(e, ctx)?;
256✔
118

119
        e.array(self.0.len() as u64 * ITEM_ELEMENTS_LEN + 1)?;
256✔
120
        for ((announcement, choice), response) in
256✔
121
            self.0.iter().zip(self.1.iter()).zip(self.2.iter())
256✔
122
        {
123
            announcement.encode(e, ctx)?;
256✔
124
            choice.first().encode(e, ctx)?;
256✔
125
            choice.second().encode(e, ctx)?;
256✔
126
            response.encode(e, ctx)?;
256✔
127
        }
128
        self.3.encode(e, ctx)
256✔
129
    }
256✔
130
}
131

132
impl Decode<'_, ()> for UnitVectorProof {
133
    fn decode(
256✔
134
        d: &mut Decoder<'_>,
256✔
135
        ctx: &mut (),
256✔
136
    ) -> Result<Self, minicbor::decode::Error> {
256✔
137
        let len = decode_array_len(d, "UnitVectorProof")?;
256✔
138
        if len != 2 {
256✔
UNCOV
139
            return Err(minicbor::decode::Error::message(format!(
×
UNCOV
140
                "Unexpected UnitVectorProof array length {len}, expected 2"
×
UNCOV
141
            )));
×
142
        }
256✔
143

144
        let version = u64::decode(d, ctx)?;
256✔
145
        if version != UNIT_VECTOR_PROOF_VERSION {
256✔
UNCOV
146
            return Err(minicbor::decode::Error::message(format!(
×
UNCOV
147
                "Unexpected UnitVectorProof version value: {version}, expected {UNIT_VECTOR_PROOF_VERSION}"
×
UNCOV
148
            )));
×
149
        }
256✔
150

151
        let len = decode_array_len(d, "UnitVectorProof inner array")?;
256✔
152
        if len < MIN_PROOF_CBOR_ARRAY_LEN
256✔
153
            || !len.saturating_sub(1).is_multiple_of(ITEM_ELEMENTS_LEN)
256✔
154
        {
UNCOV
155
            return Err(minicbor::decode::Error::message(format!(
×
UNCOV
156
                "Unexpected rUnitVectorProof inner array length {len}, expected multiplier of {MIN_PROOF_CBOR_ARRAY_LEN}"
×
UNCOV
157
            )));
×
158
        }
256✔
159

160
        let elements = len.saturating_sub(1) / ITEM_ELEMENTS_LEN;
256✔
161
        let mut announcements = Vec::with_capacity(elements as usize);
256✔
162
        let mut choices = Vec::with_capacity(elements as usize);
256✔
163
        let mut responses = Vec::with_capacity(elements as usize);
256✔
164

165
        for _ in 0..elements {
256✔
166
            announcements.push(Announcement::decode(d, ctx)?);
256✔
167
            let first = GroupElement::decode(d, ctx)?;
256✔
168
            let second = GroupElement::decode(d, ctx)?;
256✔
169
            choices.push(Ciphertext::from_elements(first, second));
256✔
170
            responses.push(ResponseRandomness::decode(d, ctx)?);
256✔
171
        }
172
        let scalar = Scalar::decode(d, ctx)?;
256✔
173

174
        Ok(Self(announcements, choices, responses, scalar))
256✔
175
    }
256✔
176
}
177

178
#[cfg(test)]
179
#[allow(clippy::explicit_deref_methods)]
180
mod tests {
181
    use proptest::property_test;
182

183
    use super::*;
184

185
    #[property_test]
186
    fn response_randomness_cbor_roundtrip(original: ResponseRandomness) {
187
        let mut buffer = Vec::new();
188
        original
189
            .encode(&mut Encoder::new(&mut buffer), &mut ())
190
            .unwrap();
191
        let decoded = ResponseRandomness::decode(&mut Decoder::new(&buffer), &mut ()).unwrap();
192
        assert_eq!(original, decoded);
193
    }
194

195
    #[property_test]
196
    fn announcement_cbor_roundtrip(original: Announcement) {
197
        let mut buffer = Vec::new();
198
        original
199
            .encode(&mut Encoder::new(&mut buffer), &mut ())
200
            .unwrap();
201
        let decoded = Announcement::decode(&mut Decoder::new(&buffer), &mut ()).unwrap();
202
        assert_eq!(original, decoded);
203
    }
204

205
    #[property_test]
206
    fn unit_vector_proof_cbor_roundtrip(original: UnitVectorProof) {
207
        let mut buffer = Vec::new();
208
        original
209
            .encode(&mut Encoder::new(&mut buffer), &mut ())
210
            .unwrap();
211
        let decoded = UnitVectorProof::decode(&mut Decoder::new(&buffer), &mut ()).unwrap();
212
        assert_eq!(original, decoded);
213
    }
214
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc