winter_prover/constraints/commitment/
default.rs

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
// Copyright (c) Facebook, Inc. and its affiliates.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.

use alloc::vec::Vec;
use core::marker::PhantomData;

use air::{proof::Queries, PartitionOptions};
use crypto::{ElementHasher, VectorCommitment};
use math::FieldElement;
use tracing::info_span;

use super::{ConstraintCommitment, RowMatrix};
use crate::{CompositionPoly, CompositionPolyTrace, StarkDomain, DEFAULT_SEGMENT_WIDTH};

// CONSTRAINT COMMITMENT
// ================================================================================================

/// Constraint evaluation commitment.
///
/// The commitment consists of two components:
/// * Evaluations of composition polynomial columns over the LDE domain.
/// * Vector commitment where each vector element corresponds to the digest of a row in the
///   composition polynomial evaluation matrix.
pub struct DefaultConstraintCommitment<
    E: FieldElement,
    H: ElementHasher<BaseField = E::BaseField>,
    V: VectorCommitment<H>,
> {
    evaluations: RowMatrix<E>,
    vector_commitment: V,
    _h: PhantomData<H>,
}

impl<E, H, V> DefaultConstraintCommitment<E, H, V>
where
    E: FieldElement,
    H: ElementHasher<BaseField = E::BaseField>,
    V: VectorCommitment<H>,
{
    /// Creates a new constraint evaluation commitment from the provided composition polynomial
    /// evaluations and the corresponding vector commitment.
    pub fn new(
        composition_poly_trace: CompositionPolyTrace<E>,
        num_constraint_composition_columns: usize,
        domain: &StarkDomain<E::BaseField>,
        partition_options: PartitionOptions,
    ) -> (Self, CompositionPoly<E>) {
        // extend the main execution trace and build a commitment to the extended trace
        let (evaluations, commitment, composition_poly) = build_constraint_commitment::<E, H, V>(
            composition_poly_trace,
            num_constraint_composition_columns,
            domain,
            partition_options,
        );

        assert_eq!(
            evaluations.num_rows(),
            commitment.domain_len(),
            "number of rows in constraint evaluation matrix must be the same as the size \
            of the vector commitment domain"
        );

        let commitment = Self {
            evaluations,
            vector_commitment: commitment,
            _h: PhantomData,
        };

        (commitment, composition_poly)
    }
}

impl<E, H, V> ConstraintCommitment<E> for DefaultConstraintCommitment<E, H, V>
where
    E: FieldElement,
    H: ElementHasher<BaseField = E::BaseField> + core::marker::Sync,
    V: VectorCommitment<H> + core::marker::Sync,
{
    type HashFn = H;
    type VC = V;

    /// Returns the commitment.
    fn commitment(&self) -> H::Digest {
        self.vector_commitment.commitment()
    }

    /// Returns constraint evaluations at the specified positions along with a batch opening proof
    /// against the vector commitment.
    fn query(self, positions: &[usize]) -> Queries {
        // build batch opening proof to the leaves specified by positions
        let opening_proof = self
            .vector_commitment
            .open_many(positions)
            .expect("failed to generate a batch opening proof for constraint queries");

        // determine a set of evaluations corresponding to each position
        let mut evaluations = Vec::new();
        for &position in positions {
            let row = self.evaluations.row(position).to_vec();
            evaluations.push(row);
        }

        Queries::new::<H, E, V>(opening_proof.1, evaluations)
    }
}

fn build_constraint_commitment<E, H, V>(
    composition_poly_trace: CompositionPolyTrace<E>,
    num_constraint_composition_columns: usize,
    domain: &StarkDomain<E::BaseField>,
    partition_options: PartitionOptions,
) -> (RowMatrix<E>, V, CompositionPoly<E>)
where
    E: FieldElement,
    H: ElementHasher<BaseField = E::BaseField>,
    V: VectorCommitment<H>,
{
    // first, build constraint composition polynomial from its trace as follows:
    // - interpolate the trace into a polynomial in coefficient form
    // - "break" the polynomial into a set of column polynomials each of degree equal to
    //   trace_length - 1
    let composition_poly = info_span!(
        "build_composition_poly_columns",
        num_columns = num_constraint_composition_columns
    )
    .in_scope(|| {
        CompositionPoly::new(composition_poly_trace, domain, num_constraint_composition_columns)
    });
    assert_eq!(composition_poly.num_columns(), num_constraint_composition_columns);
    assert_eq!(composition_poly.column_degree(), domain.trace_length() - 1);

    // then, evaluate composition polynomial columns over the LDE domain
    let domain_size = domain.lde_domain_size();
    let composed_evaluations = info_span!("evaluate_composition_poly_columns").in_scope(|| {
        RowMatrix::evaluate_polys_over::<DEFAULT_SEGMENT_WIDTH>(composition_poly.data(), domain)
    });
    assert_eq!(composed_evaluations.num_cols(), num_constraint_composition_columns);
    assert_eq!(composed_evaluations.num_rows(), domain_size);

    // finally, build constraint evaluation commitment
    let commitment = info_span!(
        "compute_constraint_evaluation_commitment",
        log_domain_size = domain_size.ilog2()
    )
    .in_scope(|| composed_evaluations.commit_to_rows::<H, V>(partition_options));

    (composed_evaluations, commitment, composition_poly)
}