forked from lambdaclass/cairo-vm
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
279 additions
and
6 deletions.
There are no files selected for viewing
276 changes: 276 additions & 0 deletions
276
vm/src/hint_processor/cairo_1_hint_processor/circuit.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,276 @@ | ||
use std::array; | ||
use std::ops::{Deref, Shl}; | ||
|
||
use num_bigint::{BigInt, BigUint, ToBigInt}; | ||
use num_integer::{ExtendedGcd, Integer}; | ||
use num_traits::{One, Signed, Zero}; | ||
use starknet_types_core::felt::Felt as Felt252; | ||
|
||
use crate::types::relocatable::{MaybeRelocatable, Relocatable}; | ||
use crate::vm::errors::hint_errors::HintError; | ||
use crate::vm::vm_core::VirtualMachine; | ||
|
||
|
||
// These constants are copied from `cairo-lang-sierra` crate | ||
/// The number of limbs used to represent a single value in the circuit. | ||
pub const VALUE_SIZE: usize = 4; | ||
/// The size of the AddMod and MulMod builtin instances. | ||
pub const MOD_BUILTIN_INSTANCE_SIZE: usize = 7; | ||
/// A gate is defined by 3 offsets, the first two are the inputs and the third is the output. | ||
pub const OFFSETS_PER_GATE: usize = 3; | ||
|
||
struct CircuitInstance<'a> { | ||
vm: &'a mut VirtualMachine, | ||
values_ptr: Relocatable, | ||
add_mod_offsets: Relocatable, | ||
mul_mod_offsets: Relocatable, | ||
modulus: BigUint, | ||
} | ||
|
||
impl CircuitInstance<'_> { | ||
/// Given an address of an offset, gives the corresponding address in the values buffer. | ||
fn get_value_ptr(&self, offset_addr: Relocatable) -> Relocatable { | ||
(self.values_ptr + self.vm.get_integer(offset_addr).unwrap().as_ref()).unwrap() | ||
} | ||
|
||
/// Reads a value from the values buffer. | ||
fn read_circuit_value(&mut self, offset_addr: Relocatable) -> Option<BigUint> { | ||
let addr = self.get_value_ptr(offset_addr); | ||
read_circuit_value(self.vm, addr) | ||
} | ||
|
||
/// Writes a value to the values buffer. | ||
fn write_circuit_value(&mut self, offset_addr: Relocatable, value: BigUint) { | ||
let addr = self.get_value_ptr(offset_addr); | ||
write_circuit_value(self.vm, addr, value); | ||
} | ||
|
||
/// Reads a values from the location specified by `index` in the `add_mod_offsets` buffer. | ||
fn read_addmod_value(&mut self, index: usize) -> Option<BigUint> { | ||
self.read_circuit_value((self.add_mod_offsets + index).unwrap()) | ||
} | ||
|
||
/// Writes `value` to the location specified by `index` in the `add_mod_offsets` buffer. | ||
fn write_addmod_value(&mut self, index: usize, value: BigUint) { | ||
self.write_circuit_value((self.add_mod_offsets + index).unwrap(), value) | ||
} | ||
|
||
/// Reads a values from the location specified by `index` in the `mul_mod_offsets` buffer. | ||
fn get_mulmod_value(&mut self, index: usize) -> Option<BigUint> { | ||
self.read_circuit_value((self.mul_mod_offsets + index).unwrap()) | ||
} | ||
|
||
/// Writes `value` to the location specified by `index` in the `mul_mod_offsets` buffer. | ||
fn write_mulmod_value(&mut self, index: usize, value: BigUint) { | ||
self.write_circuit_value((self.mul_mod_offsets + index).unwrap(), value) | ||
} | ||
|
||
/// Fills the values for an add mod gate. | ||
/// Returns true if all the inputs are ready and the values were filled successfully, false | ||
/// otherwise. | ||
fn fill_add_gate(&mut self, index: usize) -> bool { | ||
let lhs = self.read_addmod_value(index); | ||
let rhs = self.read_addmod_value(index + 1); | ||
|
||
match (lhs, rhs) { | ||
(Some(lhs), Some(rhs)) => { | ||
let res = (lhs + rhs).mod_floor(&self.modulus); | ||
self.write_addmod_value(index + 2, res); | ||
true | ||
} | ||
|
||
(None, Some(rhs)) => { | ||
// lhs + rhs = res => lhs = res - rhs | ||
if let Some(res) = self.read_addmod_value(index + 2) { | ||
self.write_addmod_value( | ||
index, | ||
(res + &self.modulus - rhs).mod_floor(&self.modulus), | ||
); | ||
true | ||
} else { | ||
false | ||
} | ||
} | ||
|
||
_ => false, | ||
} | ||
} | ||
|
||
/// Fills the values for a mul mod gate. | ||
/// Assumes all the inputs are ready and the modulus is not zero or one. | ||
/// Returns true if the values were filled successfully, returns false if its an inverse | ||
/// operation and input is not invertible. | ||
fn fill_mul_gate(&mut self, index: usize) -> bool { | ||
let lhs = self.get_mulmod_value(index); | ||
let rhs = self.get_mulmod_value(index + 1); | ||
|
||
match (lhs, rhs) { | ||
(Some(lhs), Some(rhs)) => { | ||
let res = (lhs * rhs).mod_floor(&self.modulus); | ||
self.write_mulmod_value(index + 2, res); | ||
true | ||
} | ||
(None, Some(rhs)) => { | ||
let (success, res) = invert_or_nullify(rhs, &self.modulus); | ||
self.write_mulmod_value(index, res); | ||
success | ||
} | ||
_ => panic!("Unexpected None value in fill_mul_gate"), | ||
} | ||
} | ||
} | ||
|
||
/// Reads a circuit value from the memory. | ||
fn read_circuit_value(vm: &mut VirtualMachine, addr: Relocatable) -> Option<BigUint> { | ||
let mut res = BigUint::zero(); | ||
for i in (0..VALUE_SIZE).rev() { | ||
let addr_i = (addr + i).unwrap(); | ||
match vm.get_maybe(&addr_i) { | ||
Some(MaybeRelocatable::Int(limb)) => res = res.shl(96) + limb.to_biguint(), | ||
_ => return None, | ||
} | ||
} | ||
Some(res) | ||
} | ||
|
||
/// Writes a circuit value from the memory. | ||
fn write_circuit_value(vm: &mut VirtualMachine, addr: Relocatable, mut value: BigUint) { | ||
for i in 0..VALUE_SIZE { | ||
let (new_value, rem) = value.div_rem(&BigUint::from(1_u32).shl(96)); | ||
let addr_i = (addr + i).unwrap(); | ||
vm.insert_value(addr_i, Felt252::from(rem)).unwrap(); | ||
value = new_value; | ||
} | ||
} | ||
|
||
/// Fills the values for a circuit. | ||
/// | ||
/// Returns the first mul gate index that failed to fill its values or `n_mul_mods` if all gates | ||
/// were filled successfully. | ||
pub fn fill_values( | ||
vm: &mut VirtualMachine, | ||
values_ptr: Relocatable, | ||
add_mod_offsets: Relocatable, | ||
n_add_mods: usize, | ||
mul_mod_offsets: Relocatable, | ||
n_mul_mods: usize, | ||
modulus_ptr: Relocatable, | ||
) -> usize { | ||
let modulus = read_circuit_value(vm, modulus_ptr).unwrap(); | ||
let mut c = CircuitInstance { vm, values_ptr, add_mod_offsets, mul_mod_offsets, modulus }; | ||
|
||
let mut addmod_idx = 0; | ||
let mut first_failure_idx = n_mul_mods; | ||
|
||
let mut mulmod_idx = 0; | ||
loop { | ||
while addmod_idx < n_add_mods { | ||
if !c.fill_add_gate(3 * addmod_idx) { | ||
break; | ||
} | ||
addmod_idx += 1; | ||
} | ||
|
||
if mulmod_idx == n_mul_mods { | ||
break; | ||
} | ||
|
||
let success = c.fill_mul_gate(3 * mulmod_idx); | ||
if !success && first_failure_idx == n_mul_mods { | ||
first_failure_idx = mulmod_idx; | ||
} | ||
mulmod_idx += 1; | ||
} | ||
|
||
first_failure_idx | ||
} | ||
|
||
/// Returns (value % modulus). | ||
fn positive_modulus(value: &BigInt, modulus: &BigUint) -> BigUint { | ||
let value_mod = value.magnitude().mod_floor(modulus); | ||
if value.is_negative() { modulus - value_mod } else { value_mod } | ||
} | ||
|
||
/// Given a value and a modulus, either finds its inverse or finds a non-zero value that nullifies | ||
/// the value. | ||
/// | ||
/// If the value is invertible, returns (true, inverse), otherwise returns (false, nullifier). | ||
fn invert_or_nullify(value: BigUint, modulus: &BigUint) -> (bool, BigUint) { | ||
let ExtendedGcd::<_> { gcd, x, y: _ } = | ||
value.to_bigint().unwrap().extended_gcd(&modulus.to_bigint().unwrap()); | ||
|
||
let gcd = gcd.to_biguint().unwrap(); | ||
if gcd.is_one() { | ||
return (true, positive_modulus(&x, modulus)); | ||
} | ||
let nullifier = modulus / gcd; | ||
// Note that gcd divides the value, so value * nullifier = value * (modulus / gcd) = | ||
// (value // gcd) * modulus = 0 (mod modulus) | ||
(false, nullifier) | ||
} | ||
|
||
/// Fills the instances of a mod builtin. | ||
pub fn fill_instances( | ||
vm: &mut VirtualMachine, | ||
builtin_ptr: Relocatable, | ||
n_instances: usize, | ||
modulus: [Felt252; VALUE_SIZE], | ||
values_ptr: Relocatable, | ||
mut offsets_ptr: Relocatable, | ||
) -> Result<(), HintError> { | ||
for i in 0..n_instances { | ||
let instance_ptr = (builtin_ptr + i * MOD_BUILTIN_INSTANCE_SIZE)?; | ||
|
||
for (idx, value) in modulus.iter().enumerate() { | ||
vm.insert_value((instance_ptr + idx)?, *value)?; | ||
} | ||
|
||
vm.insert_value((instance_ptr + 4)?, values_ptr)?; | ||
vm.insert_value((instance_ptr + 5)?, offsets_ptr)?; | ||
offsets_ptr += OFFSETS_PER_GATE; | ||
vm.insert_value((instance_ptr + 6)?, n_instances - i)?; | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
/// Evaluates a circuit and fills the builtin instances and the values buffer. | ||
/// | ||
/// Returns the first mul gate index that failed to fill its values or `n_mul_mods` if all gates | ||
/// were filled successfully. | ||
pub fn eval_circuit( | ||
vm: &mut VirtualMachine, | ||
add_mod_builtin: Relocatable, | ||
n_add_mods: usize, | ||
mul_mod_builtin: Relocatable, | ||
n_mul_mods: usize, | ||
) -> Result<(), HintError> { | ||
let modulus_ptr = mul_mod_builtin; | ||
// The offset of the values pointer inside an instance of the builtins. | ||
let values_offset = 4; | ||
// The offset of the offsets pointer inside an instance of the builtins. | ||
let offsets_offset = 5; | ||
let values_ptr = vm.get_relocatable((mul_mod_builtin + values_offset)?)?; | ||
|
||
let mul_mod_offsets = vm.get_relocatable((mul_mod_builtin + offsets_offset)?)?; | ||
let add_mod_offsets = if n_add_mods == 0 { | ||
mul_mod_offsets | ||
} else { | ||
vm.get_relocatable((add_mod_builtin + offsets_offset)?)? | ||
}; | ||
let n_computed_gates = fill_values( | ||
vm, | ||
values_ptr, | ||
add_mod_offsets, | ||
n_add_mods, | ||
mul_mod_offsets, | ||
n_mul_mods, | ||
modulus_ptr, | ||
); | ||
|
||
let modulus: [Felt252; 4] = | ||
array::from_fn(|i| *vm.get_integer((modulus_ptr + i).unwrap()).unwrap().deref()); | ||
fill_instances(vm, add_mod_builtin, n_add_mods, modulus, values_ptr, add_mod_offsets)?; | ||
fill_instances(vm, mul_mod_builtin, n_computed_gates, modulus, values_ptr, mul_mod_offsets)?; | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
pub mod dict_manager; | ||
pub mod hint_processor; | ||
pub mod hint_processor_utils; | ||
pub mod circuit; |