1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
use std::any::type_name;
use std::convert::TryFrom;
use std::mem;

use num_traits::Zero;

use super::*;
use crate::algebra::{Hashable, Pack, PackSelected};
use crate::crypto::hash::PackedHasher;
use crate::generator::ShareGen;
use crate::proof::{OpenOnline, OpenPreprocessing};

pub struct ProverTranscript<D: Domain, I: Iterator<Item = D::Recon>> {
    // original un-expanded seeds
    // (used to extract proof at the end)
    seeds: [[u8; KEY_SIZE]; PACKED],

    //
    witness: I,

    // used to generate fresh shares
    share_gen: Box<ShareGen<D>>,

    // transcript hashes
    hash_online: PackedHasher,
    hash_preprocess: PackedHasher,

    // recorded corrections/reconstructions/masked inputs
    reconstructions: Vec<D::Share>,
    corrections: Vec<D::Recon>,
    inputs: Vec<D::Recon>,
}

impl<D: Domain, I: Iterator<Item = D::Recon>> ProverTranscript<D, I> {
    pub fn new(
        witness: I,           // iterator over
        seeds: [Key; PACKED], // seeds for each packed repetition
    ) -> Self {
        Self {
            seeds,
            share_gen: share_gen_from_rep_seeds(&seeds),
            witness,
            hash_online: PackedHasher::new(),
            hash_preprocess: PackedHasher::new(),
            reconstructions: vec![],
            corrections: vec![],
            inputs: vec![],
        }
    }

    /// Extracts proofs from transcript
    ///
    /// # Arguments
    ///
    /// - 'players': The players to omit from the online execution.
    ///              If players[i] == PLAYERS, the preprocessing is opened instead.
    pub(crate) fn extract(
        self,                     // consumes the transcript
        players: [usize; PACKED], // the online players to omit.
    ) -> (Vec<OpenOnline>, Vec<OpenPreprocessing>) {
        let mut dst_recon: [Vec<u8>; PACKED] = [
            vec![],
            vec![],
            vec![],
            vec![],
            vec![],
            vec![],
            vec![],
            vec![],
        ];

        let mut dst_corr: [Vec<u8>; PACKED] = [
            vec![],
            vec![],
            vec![],
            vec![],
            vec![],
            vec![],
            vec![],
            vec![],
        ];

        let mut dst_input: [Vec<u8>; PACKED] = [
            vec![],
            vec![],
            vec![],
            vec![],
            vec![],
            vec![],
            vec![],
            vec![],
        ];

        let selected: Vec<bool> = players.iter().copied().map(|i| i < PLAYERS).collect();

        // pack reconstruction shares of omitted player
        D::Share::pack_selected(&mut dst_recon, &self.reconstructions[..], players);

        // pack corrections
        D::Recon::pack(
            &mut dst_corr,
            &self.corrections[..],
            <&[bool; PACKED]>::try_from(&selected[..]).unwrap(),
        );

        // pack masked inputs
        D::Recon::pack(
            &mut dst_input,
            &self.inputs[..],
            <&[bool; PACKED]>::try_from(&selected[..]).unwrap(),
        );

        // open
        let mut open_preprocessing: Vec<OpenPreprocessing> = vec![];
        let mut open_online: Vec<OpenOnline> = vec![];
        for rep in 0..PACKED {
            let omit = players[rep];
            debug_assert!(omit <= PLAYERS);
            if omit < PLAYERS {
                // fetch the packed corrections
                let corrs = mem::take(&mut dst_corr[rep]);
                let recons = mem::take(&mut dst_recon[rep]);
                let inputs = mem::take(&mut dst_input[rep]);

                // remove seed of unopened player
                let mut seeds = expand_seed(self.seeds[rep]);
                seeds[omit] = [0u8; KEY_SIZE];

                // append to opening
                open_online.push(OpenOnline {
                    omit: omit as u8,
                    recons,
                    corrs,
                    inputs,
                    seeds,
                })
            } else {
                debug_assert_eq!(omit, PLAYERS);
                debug_assert_eq!(
                    dst_corr[rep].len(),
                    0,
                    "rep = {}, players = {:?}, {:?}",
                    rep,
                    &players,
                    &dst_corr
                );

                debug_assert_eq!(
                    dst_recon[rep].len(),
                    0,
                    "rep = {}, players = {:?}, domain = {}, {:?}",
                    rep,
                    &players,
                    type_name::<D>(),
                    &dst_recon
                );

                debug_assert_eq!(
                    dst_input[rep].len(),
                    0,
                    "rep = {}, players = {:?}, {:?}",
                    rep,
                    &players,
                    &dst_input
                );

                open_preprocessing.push(OpenPreprocessing {
                    comm_online: self.hash_online[rep].finalize().into(),
                    seed: self.seeds[rep],
                })
            }
        }

        (open_online, open_preprocessing)
    }
}

impl<D: Domain, I: Iterator<Item = D::Recon>> Transcript<D> for ProverTranscript<D, I> {
    const IS_PROVER: bool = true;

    fn input(&mut self) -> Wire<D> {
        // generate fresh share
        let mask = self.share_gen.next();

        // reconstruct mask
        let lambda = D::reconstruct(&mask);

        // fetch next input and compute "correction"
        // st. mask + corr = input, i.e. (mask || corr) is a sharing of input.
        let input = self.witness.next().expect("witness is too short");
        let corr = input - lambda;

        // commit to input
        corr.hash(&mut self.hash_online);

        // save masked input for proof extraction
        self.inputs.push(corr);
        Wire { mask, corr }
    }

    fn online_hash(&self) -> [Hash; PACKED] {
        self.hash_online.finalize()
    }

    fn preprocess_hash(&self) -> [Hash; PACKED] {
        self.hash_preprocess.finalize()
    }

    fn reconstruct(&mut self, mask: D::Share) -> D::Recon {
        mask.hash(&mut self.hash_online);
        self.reconstructions.push(mask);
        D::reconstruct(&mask)
    }

    fn correction(&mut self, corr: D::Recon) -> D::Recon {
        corr.hash(&mut self.hash_preprocess);
        self.corrections.push(corr);
        corr
    }

    fn zero_check(&mut self, recon: D::Recon) {
        // TODO: create a useful trace for the user
        assert!(
            recon.is_zero(),
            "witness is invalid!. Wire has value {:?}, expected zero.",
            recon
        );
    }

    fn new_mask(&mut self) -> D::Share {
        self.share_gen.next()
    }
}