Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor RandomCoin trait #214

Merged
merged 2 commits into from
Sep 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 9 additions & 57 deletions crypto/src/random/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,64 +117,9 @@ impl<B: StarkField, H: ElementHasher<BaseField = B>> RandomCoin for DefaultRando
self.counter = 0;
}

/// Reseeds the coin with the specified value by setting the new seed to hash(`seed` ||
/// `value`).
///
/// # Examples
/// ```
/// # use winter_crypto::{RandomCoin, DefaultRandomCoin, Hasher, hashers::Blake3_256};
/// # use math::fields::f128::BaseElement;
/// // initial elements for seeding the random coin
/// let seed = &[BaseElement::new(1), BaseElement::new(2), BaseElement::new(3), BaseElement::new(4)];
///
/// let mut coin1 = DefaultRandomCoin::<Blake3_256<BaseElement>>::new(seed);
/// let mut coin2 = DefaultRandomCoin::<Blake3_256<BaseElement>>::new(seed);
///
/// // should draw the same element form both coins
/// let e1 = coin1.draw::<BaseElement>().unwrap();;
/// let e2 = coin2.draw::<BaseElement>().unwrap();;
/// assert_eq!(e1, e2);
///
/// // after reseeding should draw different elements
/// coin2.reseed_with_int(42);
/// let e1 = coin1.draw::<BaseElement>().unwrap();;
/// let e2 = coin2.draw::<BaseElement>().unwrap();;
/// assert_ne!(e1, e2);
/// ```
fn reseed_with_int(&mut self, value: u64) {
self.seed = H::merge_with_int(self.seed, value);
self.counter = 0;
}

// PUBLIC ACCESSORS
// --------------------------------------------------------------------------------------------

/// Returns the number of leading zeros in the seed if it is interpreted as an integer in
/// big-endian byte order.
///
/// # Examples
/// ```
/// # use winter_crypto::{RandomCoin, DefaultRandomCoin, Hasher, hashers::Blake3_256};
/// # use math::fields::f128::BaseElement;
/// // initial elements for seeding the random coin
/// let seed = &[BaseElement::new(1), BaseElement::new(2), BaseElement::new(3), BaseElement::new(4)];
///
/// let mut coin = DefaultRandomCoin::<Blake3_256<BaseElement>>::new(seed);
///
/// let mut value = 0;
/// while coin.check_leading_zeros(value) < 2 {
/// value += 1;
/// }
///
/// coin.reseed_with_int(value);
/// assert!(coin.leading_zeros() >= 2);
/// ```
fn leading_zeros(&self) -> u32 {
let bytes = self.seed.as_bytes();
let seed_head = u64::from_le_bytes(bytes[..8].try_into().unwrap());
seed_head.trailing_zeros()
}

/// Computes hash(`seed` || `value`) and returns the number of leading zeros in the resulting
/// value if it is interpreted as an integer in big-endian byte order.
fn check_leading_zeros(&self, value: u64) -> u32 {
Expand Down Expand Up @@ -208,7 +153,8 @@ impl<B: StarkField, H: ElementHasher<BaseField = B>> RandomCoin for DefaultRando
Err(RandomCoinError::FailedToDrawFieldElement(1000))
}

/// Returns a vector of unique integers selected from the range [0, domain_size).
/// Returns a vector of unique integers selected from the range [0, domain_size) after reseeding
/// the PRNG with the specified `nonce` by setting the new seed to hash(`seed` || `nonce`).
///
/// # Errors
/// Returns an error if the specified number of unique integers could not be generated
Expand All @@ -231,7 +177,8 @@ impl<B: StarkField, H: ElementHasher<BaseField = B>> RandomCoin for DefaultRando
///
/// let num_values = 20;
/// let domain_size = 64;
/// let values = coin.draw_integers(num_values, domain_size).unwrap();
/// let nonce = 0;
/// let values = coin.draw_integers(num_values, domain_size, nonce).unwrap();
///
/// assert_eq!(num_values, values.len());
///
Expand All @@ -245,6 +192,7 @@ impl<B: StarkField, H: ElementHasher<BaseField = B>> RandomCoin for DefaultRando
&mut self,
num_values: usize,
domain_size: usize,
nonce: u64,
) -> Result<Vec<usize>, RandomCoinError> {
assert!(
domain_size.is_power_of_two(),
Expand All @@ -255,6 +203,10 @@ impl<B: StarkField, H: ElementHasher<BaseField = B>> RandomCoin for DefaultRando
"number of values must be smaller than domain size"
);

// reseed with nonce
self.seed = H::merge_with_int(self.seed, nonce);
self.counter = 0;

// determine how many bits are needed to represent valid values in the domain
let v_mask = (domain_size - 1) as u64;

Expand Down
12 changes: 3 additions & 9 deletions crypto/src/random/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,6 @@ pub trait RandomCoin: Sync {
/// Reseeds the coin with the specified data by setting the new seed to hash(`seed` || `data`).
fn reseed(&mut self, data: <Self::Hasher as Hasher>::Digest);

/// Reseeds the coin with the specified value by setting the new seed to hash(`seed` ||
/// `value`).
fn reseed_with_int(&mut self, value: u64);

/// Returns the number of leading zeros in the seed if it is interpreted as an integer in
/// big-endian byte order.
fn leading_zeros(&self) -> u32;

/// Computes hash(`seed` || `value`) and returns the number of leading zeros in the resulting
/// value if it is interpreted as an integer in big-endian byte order.
fn check_leading_zeros(&self, value: u64) -> u32;
Expand All @@ -55,7 +47,8 @@ pub trait RandomCoin: Sync {
/// PRNG.
fn draw<E: FieldElement<BaseField = Self::BaseField>>(&mut self) -> Result<E, RandomCoinError>;

/// Returns a vector of unique integers selected from the range [0, domain_size).
/// Returns a vector of unique integers selected from the range [0, domain_size) after it reseeds
/// the coin with a nonce.
///
/// # Errors
/// Returns an error if the specified number of unique integers could not be generated
Expand All @@ -69,5 +62,6 @@ pub trait RandomCoin: Sync {
&mut self,
num_values: usize,
domain_size: usize,
nonce: u64,
) -> Result<Vec<usize>, RandomCoinError>;
}
6 changes: 3 additions & 3 deletions fri/src/prover/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,15 @@ where
/// layer should be queried.
///
/// The positions are pseudo-randomly generated based on the values the prover has written
/// into this channel.
/// into this channel and a PoW nonce.
///
/// # Panics
/// Panics if the specified number of unique positions could not be drawn from the specified
/// domain. Both number of queried positions and domain size are specified during
/// construction of the channel.
pub fn draw_query_positions(&mut self) -> Vec<usize> {
pub fn draw_query_positions(&mut self, nonce: u64) -> Vec<usize> {
self.public_coin
.draw_integers(self.num_queries, self.domain_size)
.draw_integers(self.num_queries, self.domain_size, nonce)
.expect("failed to draw query position")
}

Expand Down
2 changes: 1 addition & 1 deletion fri/src/prover/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ fn fri_prove_verify(
// instantiate the prover and generate the proof
let mut prover = FriProver::new(options.clone());
prover.build_layers(&mut channel, evaluations.clone());
let positions = channel.draw_query_positions();
let positions = channel.draw_query_positions(0);
let proof = prover.build_proof(&positions);

// make sure the proof can be verified
Expand Down
3 changes: 1 addition & 2 deletions prover/src/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ where
let num_queries = self.context.options().num_queries();
let lde_domain_size = self.context.lde_domain_size();
self.public_coin
.draw_integers(num_queries, lde_domain_size)
.draw_integers(num_queries, lde_domain_size, self.pow_nonce)
.expect("failed to draw query position")
}

Expand All @@ -163,7 +163,6 @@ where
.expect("nonce not found");

self.pow_nonce = nonce;
self.public_coin.reseed_with_int(nonce);
}

// PROOF BUILDER
Expand Down
11 changes: 7 additions & 4 deletions verifier/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,12 +247,11 @@ where
// TODO: make sure air.lde_domain_size() == fri_verifier.domain_size()

// 5 ----- trace and constraint queries -------------------------------------------------------
// read proof-of-work nonce sent by the prover and update the public coin with it
// read proof-of-work nonce sent by the prover
let pow_nonce = channel.read_pow_nonce();
public_coin.reseed_with_int(pow_nonce);

// make sure the proof-of-work specified by the grinding factor is satisfied
if public_coin.leading_zeros() < air.options().grinding_factor() {
if public_coin.check_leading_zeros(pow_nonce) < air.options().grinding_factor() {
return Err(VerifierError::QuerySeedProofOfWorkVerificationFailed);
}

Expand All @@ -261,7 +260,11 @@ where
// and the prover responds with decommitments against these positions for trace and constraint
// composition polynomial evaluations.
let query_positions = public_coin
.draw_integers(air.options().num_queries(), air.lde_domain_size())
.draw_integers(
air.options().num_queries(),
air.lde_domain_size(),
pow_nonce,
)
.map_err(|_| VerifierError::RandomCoinError)?;

// read evaluations of trace and constraint composition polynomials at the queried positions;
Expand Down
Loading