binius_ip_prover/channel.rs
1// Copyright 2026 The Binius Developers
2
3//! Channel abstraction for public-coin interactive protocol provers.
4//!
5//! In a public-coin interactive protocol, the prover sends deterministic messages while the
6//! verifier's messages consist entirely of random challenges. This module provides the
7//! [`IPProverChannel`] trait that models the prover's view of such an interaction.
8//!
9//! The trait abstracts over:
10//! - Sending prover messages (field elements)
11//! - Sampling random challenges (which must match what the verifier samples)
12//!
13//! This abstraction allows protocol implementations to be generic over the underlying
14//! communication mechanism, whether it's an actual interactive channel or a non-interactive
15//! transcript using the Fiat-Shamir heuristic.
16
17use std::ops::Shr;
18
19use binius_core::word::Word;
20use binius_field::Field;
21use binius_transcript::{
22 ProverTranscript,
23 fiat_shamir::{CanSample, CanSampleBits, Challenger},
24};
25
26/// Channel for sending prover messages and sampling challenges in a public-coin interactive
27/// protocol.
28///
29/// In a public-coin protocol, the prover's role is to:
30/// 1. Send field elements to the verifier via `send_*` methods
31/// 2. Sample the same random challenges as the verifier via `sample`
32///
33/// When used with a Fiat-Shamir transcript, the challenges are derived deterministically from
34/// the transcript state, ensuring prover and verifier derive identical challenges.
35pub trait IPProverChannel<F: Field> {
36 /// Sends a single field element to the verifier.
37 fn send_one(&mut self, elem: F);
38
39 /// Sends multiple field elements to the verifier.
40 fn send_many(&mut self, elems: &[F]) {
41 for &elem in elems {
42 self.send_one(elem);
43 }
44 }
45
46 /// Sends a value the verifier could compute for itself, as advice.
47 ///
48 /// The verifier's counterpart is
49 /// [`recv_public_claim`](binius_ip::channel::IPVerifierChannel::recv_public_claim), which
50 /// documents what a claim is. A claim depends on public-channel-derived values alone, so a
51 /// channel that masks the prover's messages sends this one in the clear. The default is the
52 /// plain send, for a channel that draws no such distinction.
53 fn send_public_claim(&mut self, elem: F) {
54 self.send_one(elem);
55 }
56
57 /// Observes a single field element, feeding it into the Fiat-Shamir state.
58 fn observe_one(&mut self, val: F);
59
60 /// Observes multiple field elements, feeding them into the Fiat-Shamir state.
61 fn observe_many(&mut self, vals: &[F]) {
62 for &val in vals {
63 self.observe_one(val);
64 }
65 }
66
67 /// Samples a random challenge.
68 ///
69 /// In a Fiat-Shamir transcript, this derives the challenge deterministically from
70 /// the current transcript state, matching what the verifier will sample.
71 fn sample(&mut self) -> F;
72
73 /// Samples `n` random challenges.
74 fn sample_many(&mut self, n: usize) -> Vec<F> {
75 std::iter::repeat_with(|| self.sample()).take(n).collect()
76 }
77
78 /// Samples a fixed-size array of random challenges.
79 fn sample_array<const N: usize>(&mut self) -> [F; N] {
80 std::array::from_fn(|_| self.sample())
81 }
82}
83
84/// A prover channel whose protocol carries 64-bit words alongside field elements.
85///
86/// The prover-side counterpart of
87/// [`WordIPVerifierChannel`](binius_ip::channel::WordIPVerifierChannel). It carries only the
88/// operations both parties perform — lifting constants, observing, shifting and sampling — since
89/// the arithmetic over a word's bits is the verifier's alone.
90pub trait WordIPProverChannel<F: Field>: IPProverChannel<F> {
91 /// The word type this channel carries.
92 ///
93 /// Mirrors [`WordIPVerifierChannel::Word`](binius_ip::channel::WordIPVerifierChannel::Word),
94 /// including the [`From<Word>`](From) and [`Shr`] bounds that keep lifting and index
95 /// arithmetic plain operations rather than channel methods.
96 type Word: Clone + From<Word> + Shr<u32, Output = Self::Word>;
97
98 /// Feeds words into the Fiat-Shamir state, each as eight little-endian bytes.
99 fn observe_words(&mut self, words: &[Self::Word]);
100
101 /// Samples a uniform word of the given bit width, matching what the verifier samples.
102 ///
103 /// The result is masked to `bits` bits.
104 fn sample_bits(&mut self, bits: usize) -> Self::Word;
105}
106
107impl<F, Challenger_> IPProverChannel<F> for ProverTranscript<Challenger_>
108where
109 F: Field,
110 Challenger_: Challenger,
111{
112 fn send_one(&mut self, elem: F) {
113 self.message().write_scalar(elem);
114 }
115
116 fn send_many(&mut self, elems: &[F]) {
117 self.message().write_scalar_slice(elems);
118 }
119
120 fn observe_one(&mut self, val: F) {
121 self.observe().write_scalar(val);
122 }
123
124 fn observe_many(&mut self, vals: &[F]) {
125 self.observe().write_scalar_slice(vals);
126 }
127
128 fn sample(&mut self) -> F {
129 CanSample::sample(self)
130 }
131}
132
133impl<F, Challenger_> WordIPProverChannel<F> for ProverTranscript<Challenger_>
134where
135 F: Field,
136 Challenger_: Challenger,
137{
138 type Word = Word;
139
140 fn observe_words(&mut self, words: &[Word]) {
141 self.observe().write_slice(words);
142 }
143
144 fn sample_bits(&mut self, bits: usize) -> Word {
145 Word::from_u64(CanSampleBits::<u32>::sample_bits(self, bits) as u64)
146 }
147}