- Bump rust version to `nightly-2025-07-02` because some of the nightly features we were using have been stabilized. - Introduce feature `disk_cache` which enables caching to disk. Each time an artifact is retrieved from the cache it will be read and deserialized. On a cache miss the artifact will be created, serialized and stored to disk. - Introduce feature `mem_cache` which enables caching to memory. All cached artifacts are kept in memory after they are created. The mem cache implementation avoids cloning of artifacts by extending their lifetime to `'static`. This is `unsafe` code, but I argue that this usage is safe. - Add a `build.rs` - When the feature `disk_cache` is enabled, the `build.rs` will inject env variables to the process with the git commit information, which is used to index the cached artifacts - Replace all previous cached artifacts from `LazyStatic` methods that call the cache API - Derive `Serialize, Deserialize` for all `*Target` types so that they can be serialized for caching to disk - Add finer level of caching: now we cache the `CircuitData` and `VerifierData` independently. The reason for this is that `CircuitData` is a very big artifact which is not needed for verification. So by only accessing `VerifierData` in verification we don't pay a big overhead for reading from disk and deserializing - Add missing artifacts to the cache: like the `CircuitData` for the `MainPod` indexed by `Params` - Add helper types to serialize and deserialize `CircuitData`, `CommonData` and `VerifierData` with the set of gates and generators used in the recursive MainPod circuit - Tweak the ids of our custom gates so that they remain unique when their generic parameters change - Bugfix: several tests were using the standard `vd_set` but were using MainPod circuits with non-default parameters. This was working before because there was a bug: the MainPod circuit was reporting that the used verifier data was the standard one instead of picking the one corresponding to it's own Params. Summary of breaking changes: - One and only one of the features `mem_cache` or `disk_cache` need to be enabled. By default it's `mem_cache` - To enable the `disk_cache` you need to disable the default features like this: `--no-default-features --features=backend_plonky2,zk,disk_cache` - Removed `DEFAULT_PARAMS`, instead use `Params::default()` - Removed `STANDARD_REC_MAIN_POD_CIRCUIT_DATA`, instead use `cache_get_standard_rec_main_pod_common_circuit_data` - The library is now using `nightly-2025-07-02`. Some rust language features are unstable in previous versions.
97 lines
4 KiB
Rust
97 lines
4 KiB
Rust
use std::array;
|
|
|
|
use plonky2::field::goldilocks_field::GoldilocksField;
|
|
|
|
use crate::backends::plonky2::primitives::ec::{
|
|
curve::{add_homog_offset, ECFieldExt},
|
|
gates::{field::QuinticTensor, generic::SimpleGate},
|
|
};
|
|
|
|
/// Gate computing the addition of two elliptic curve points in
|
|
/// homogeneous coordinates *minus* an offset in the `z` and `t`
|
|
/// coordinates, viz. the extension field generator times `Point::B1`,
|
|
/// cf. CircuitBuilderElliptic::add_point.
|
|
///
|
|
/// In plonky2 one Gate can do multiple operations and the gate will register one
|
|
/// generator per operation. When a gate operation is used, the `CircuitBuilder` tracks the
|
|
/// allocation of operations to gates via the `current_slots` field. Once the circuit is fully
|
|
/// defined, during the build the circuit the generators
|
|
/// associated to unused operations (free slots) are removed:
|
|
/// https://github.com/0xPolygonZero/plonky2/blob/82791c4809d6275682c34b926390ecdbdc2a5297/plonky2/src/plonk/circuit_builder.rs#L1210
|
|
/// Since the generator for the unused operations are removed, no witness value will be calculated
|
|
/// for them, and the free slots gate witness wires will be filled with the default value which is zero:
|
|
/// https://github.com/0xPolygonZero/plonky2/blob/82791c4809d6275682c34b926390ecdbdc2a5297/plonky2/src/iop/witness.rs#L377
|
|
/// This means that a gate with multiple operations need to pass the constraints for a single
|
|
/// operation when all its witness wire values are zero (so that when the gate is partially used,
|
|
/// the unused slots still pass the constraints). This is the reason why this gate doesn't add the
|
|
/// final offset: if it did, the constraints wouldn't pass on the zero witness values.
|
|
#[derive(Debug, Default, Clone)]
|
|
pub struct ECAddHomogOffset;
|
|
|
|
impl SimpleGate for ECAddHomogOffset {
|
|
type F = GoldilocksField;
|
|
const INPUTS_PER_OP: usize = 20;
|
|
const OUTPUTS_PER_OP: usize = 20;
|
|
const DEGREE: usize = 4;
|
|
const ID: &'static str = "ECAddHomog";
|
|
fn eval<const D: usize>(
|
|
wires: &[<Self::F as plonky2::field::extension::Extendable<D>>::Extension],
|
|
) -> Vec<<Self::F as plonky2::field::extension::Extendable<D>>::Extension>
|
|
where
|
|
Self::F: plonky2::field::extension::Extendable<D>,
|
|
{
|
|
let mut ans = Vec::with_capacity(20);
|
|
let [x1, u1, x2, u2] =
|
|
array::from_fn(|j| QuinticTensor::from_base(array::from_fn(|i| wires[5 * j + i])));
|
|
let out = add_homog_offset(x1, u1, x2, u2);
|
|
for v in out {
|
|
ans.extend(v.to_base());
|
|
}
|
|
ans
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use plonky2::{
|
|
gates::gate_testing::{test_eval_fns, test_low_degree},
|
|
plonk::{circuit_data::CircuitConfig, config::PoseidonGoldilocksConfig},
|
|
};
|
|
|
|
use crate::backends::plonky2::primitives::ec::gates::{
|
|
curve::ECAddHomogOffset, generic::GateAdapter,
|
|
};
|
|
|
|
#[test]
|
|
fn test_recursion() -> Result<(), anyhow::Error> {
|
|
let config = CircuitConfig::standard_recursion_config();
|
|
let gate = GateAdapter::<ECAddHomogOffset>::new_from_config(&config);
|
|
|
|
test_eval_fns::<_, PoseidonGoldilocksConfig, _, 2>(gate)
|
|
}
|
|
|
|
#[test]
|
|
fn test_low_degree_orig() -> Result<(), anyhow::Error> {
|
|
let config = CircuitConfig::standard_recursion_config();
|
|
let gate = GateAdapter::<ECAddHomogOffset>::new_from_config(&config);
|
|
|
|
test_low_degree::<_, _, 2>(gate);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_low_degree_recursive() -> Result<(), anyhow::Error> {
|
|
let config = CircuitConfig::standard_recursion_config();
|
|
let orig_gate = GateAdapter::<ECAddHomogOffset>::new_from_config(&config);
|
|
|
|
test_low_degree::<_, _, 2>(orig_gate.recursive_gate());
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_double_recursion() -> Result<(), anyhow::Error> {
|
|
let config = CircuitConfig::standard_recursion_config();
|
|
let orig_gate = GateAdapter::<ECAddHomogOffset>::new_from_config(&config);
|
|
test_eval_fns::<_, PoseidonGoldilocksConfig, _, 2>(orig_gate.recursive_gate())
|
|
}
|
|
}
|