Skip to main content

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}