Skip to content

Commit

Permalink
Merge branch 'main' of github.com:lambdaclass/cairo-vm.go into memcpy…
Browse files Browse the repository at this point in the history
…-hints
  • Loading branch information
toni-calvin committed Sep 19, 2023
2 parents 5d9c089 + 60d7b72 commit 853ee4c
Show file tree
Hide file tree
Showing 15 changed files with 449 additions and 8 deletions.
43 changes: 43 additions & 0 deletions cairo_programs/is_quad_residue.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
%builtins output
from starkware.cairo.common.serialize import serialize_word
from starkware.cairo.common.math import is_quad_residue
from starkware.cairo.common.alloc import alloc

func fill_array(array_start: felt*, iter: felt) -> () {
if (iter == 8) {
return ();
}
assert array_start[iter] = iter;
return fill_array(array_start, iter + 1);
}

func check_quad_res{output_ptr: felt*}(inputs: felt*, expected: felt*, iter: felt) {
if (iter == 8) {
return ();
}
serialize_word(inputs[iter]);
serialize_word(expected[iter]);

assert is_quad_residue(inputs[iter]) = expected[iter];
return check_quad_res(inputs, expected, iter + 1);
}

func main{output_ptr: felt*}() {
alloc_locals;
let (inputs: felt*) = alloc();
fill_array(inputs, 0);

let (expected: felt*) = alloc();
assert expected[0] = 1;
assert expected[1] = 1;
assert expected[2] = 1;
assert expected[3] = 0;
assert expected[4] = 1;
assert expected[5] = 1;
assert expected[6] = 0;
assert expected[7] = 1;

check_quad_res(inputs, expected, 0);

return ();
}
43 changes: 43 additions & 0 deletions cairo_programs/proof_programs/is_quad_residue.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
%builtins output
from starkware.cairo.common.serialize import serialize_word
from starkware.cairo.common.math import is_quad_residue
from starkware.cairo.common.alloc import alloc

func fill_array(array_start: felt*, iter: felt) -> () {
if (iter == 8) {
return ();
}
assert array_start[iter] = iter;
return fill_array(array_start, iter + 1);
}

func check_quad_res{output_ptr: felt*}(inputs: felt*, expected: felt*, iter: felt) {
if (iter == 8) {
return ();
}
serialize_word(inputs[iter]);
serialize_word(expected[iter]);

assert is_quad_residue(inputs[iter]) = expected[iter];
return check_quad_res(inputs, expected, iter + 1);
}

func main{output_ptr: felt*}() {
alloc_locals;
let (inputs: felt*) = alloc();
fill_array(inputs, 0);

let (expected: felt*) = alloc();
assert expected[0] = 1;
assert expected[1] = 1;
assert expected[2] = 1;
assert expected[3] = 0;
assert expected[4] = 1;
assert expected[5] = 1;
assert expected[6] = 0;
assert expected[7] = 1;

check_quad_res(inputs, expected, 0);

return ();
}
4 changes: 3 additions & 1 deletion pkg/hints/hint_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func (p *CairoVmHintProcessor) CompileHint(hintParams *parser.HintParams, refere
name = split[len(split)-1]
references[name] = ParseHintReference(referenceManager.References[n])
}
ids := NewIdsManager(references, hintParams.FlowTrackingData.APTracking)
ids := NewIdsManager(references, hintParams.FlowTrackingData.APTracking, hintParams.AccessibleScopes)
return HintData{Ids: ids, Code: hintParams.Code}, nil
}

Expand All @@ -47,6 +47,8 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any,
return is_positive(data.Ids, vm)
case ASSERT_NOT_ZERO:
return assert_not_zero(data.Ids, vm)
case IS_QUAD_RESIDUE:
return is_quad_residue(data.Ids, vm)
case DEFAULT_DICT_NEW:
return defaultDictNew(data.Ids, execScopes, vm)
case DICT_READ:
Expand Down
28 changes: 23 additions & 5 deletions pkg/hints/hint_utils/ids_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ import (
// Identifier Manager
// Provides methods that allow hints to interact with cairo variables given their identifier name
type IdsManager struct {
References map[string]HintReference
HintApTracking parser.ApTrackingData
References map[string]HintReference
HintApTracking parser.ApTrackingData
AccessibleScopes []string
}

func ErrIdsManager(err error) error {
Expand All @@ -28,13 +29,30 @@ func ErrIdentifierNotFelt(name string) error {
return ErrIdsManager(errors.Errorf("Identifier %s is not a Felt", name))
}

func NewIdsManager(references map[string]HintReference, hintApTracking parser.ApTrackingData) IdsManager {
func NewIdsManager(references map[string]HintReference, hintApTracking parser.ApTrackingData, accessibleScopes []string) IdsManager {
return IdsManager{
References: references,
HintApTracking: hintApTracking,
References: references,
HintApTracking: hintApTracking,
AccessibleScopes: accessibleScopes,
}
}

// Fetches a constant used by the hint
// Searches inner modules first for name-matching constants
func (ids *IdsManager) GetConst(name string, constants *map[string]lambdaworks.Felt) (lambdaworks.Felt, error) {
// Hints should always have accessible scopes
if len(ids.AccessibleScopes) != 0 {
// Accessible scopes are listed from outer to inner
for i := len(ids.AccessibleScopes) - 1; i >= 0; i-- {
constant, ok := (*constants)[ids.AccessibleScopes[i]+"."+name]
if ok {
return constant, nil
}
}
}
return lambdaworks.FeltZero(), errors.Errorf("Missing constant %s", name)
}

// Inserts value into memory given its identifier name
func (ids *IdsManager) Insert(name string, value *MaybeRelocatable, vm *VirtualMachine) error {

Expand Down
52 changes: 52 additions & 0 deletions pkg/hints/hint_utils/ids_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,3 +285,55 @@ func TestIdsManagerGetStructFieldTest(t *testing.T) {
t.Errorf("IdsManager.GetStructFieldFelt returned wrong values")
}
}

func TestIdsManagerGetConst(t *testing.T) {
ids := IdsManager{
AccessibleScopes: []string{
"starkware.cairo.common.math",
"starkware.cairo.common.math.assert_250_bit",
},
}
upperBound := lambdaworks.FeltFromUint64(250)
constants := map[string]lambdaworks.Felt{
"starkware.cairo.common.math.assert_250_bit.UPPER_BOUND": upperBound,
}
constant, err := ids.GetConst("UPPER_BOUND", &constants)
if err != nil || constant != upperBound {
t.Errorf("IdsManager.GetConst returned wrong/no constant")
}
}

func TestIdsManagerGetConstPrioritizeInnerModule(t *testing.T) {
ids := IdsManager{
AccessibleScopes: []string{
"starkware.cairo.common.math",
"starkware.cairo.common.math.assert_250_bit",
},
}
upperBound := lambdaworks.FeltFromUint64(250)
constants := map[string]lambdaworks.Felt{
"starkware.cairo.common.math.assert_250_bit.UPPER_BOUND": upperBound,
"starkware.cairo.common.math.UPPER_BOUND": lambdaworks.FeltZero(),
}
constant, err := ids.GetConst("UPPER_BOUND", &constants)
if err != nil || constant != upperBound {
t.Errorf("IdsManager.GetConst returned wrong/no constant")
}
}

func TestIdsManagerGetConstNoMConst(t *testing.T) {
ids := IdsManager{
AccessibleScopes: []string{
"starkware.cairo.common.math",
"starkware.cairo.common.math.assert_250_bit",
},
}
lowerBound := lambdaworks.FeltFromUint64(250)
constants := map[string]lambdaworks.Felt{
"starkware.cairo.common.math.assert_250_bit.LOWER_BOUND": lowerBound,
}
_, err := ids.GetConst("UPPER_BOUND", &constants)
if err == nil {
t.Errorf("IdsManager.GetConst should have failed")
}
}
14 changes: 13 additions & 1 deletion pkg/hints/hint_utils/testing_utils.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package hint_utils

import (
"github.com/lambdaclass/cairo-vm.go/pkg/lambdaworks"
"github.com/lambdaclass/cairo-vm.go/pkg/parser"
. "github.com/lambdaclass/cairo-vm.go/pkg/vm"
"github.com/lambdaclass/cairo-vm.go/pkg/vm/memory"
Expand All @@ -17,7 +18,7 @@ import (
// All references will be FP-based, so please don't update the value of FP after calling this function,
// and make sure that the memory at fp's segment is clear from its current offset onwards
func SetupIdsForTest(ids map[string][]*memory.MaybeRelocatable, vm *VirtualMachine) IdsManager {
manager := NewIdsManager(make(map[string]HintReference), parser.ApTrackingData{})
manager := NewIdsManager(make(map[string]HintReference), parser.ApTrackingData{}, []string{})
base_addr := vm.RunContext.Fp
current_offset := 0
for name, elems := range ids {
Expand All @@ -43,3 +44,14 @@ func SetupIdsForTest(ids map[string][]*memory.MaybeRelocatable, vm *VirtualMachi
}
return manager
}

// Returns a constants map accoring to the new_constants map received
// Adds a path to each constant and a matching path to the hint's accessible scopes
func SetupConstantsForTest(new_constants map[string]lambdaworks.Felt, ids *IdsManager) map[string]lambdaworks.Felt {
constants := make(map[string]lambdaworks.Felt)
ids.AccessibleScopes = append(ids.AccessibleScopes, "path")
for name, constant := range new_constants {
constants["path."+name] = constant
}
return constants
}
21 changes: 21 additions & 0 deletions pkg/hints/hint_utils/testing_utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,24 @@ func TestSetupIdsForTestStructWithGap(t *testing.T) {
t.Error("Failed to insert ids")
}
}

func TestSetupConstantsForTest(t *testing.T) {
constA := FeltOne()
constB := FeltZero()
IdsManager := IdsManager{}
constants := SetupConstantsForTest(map[string]Felt{
"A": constA,
"B": constB,
},
&IdsManager,
)
// Check that we can fetch the constants
a, err := IdsManager.GetConst("A", &constants)
if err != nil || a != constA {
t.Error("SetupConstantsForTest wrong/no A")
}
b, err := IdsManager.GetConst("B", &constants)
if err != nil || b != constB {
t.Error("SetupConstantsForTest wrong/no B")
}
}
9 changes: 9 additions & 0 deletions pkg/hints/math_hint_codes.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ const IS_POSITIVE = "from starkware.cairo.common.math_utils import is_positive\n

const ASSERT_NOT_ZERO = "from starkware.cairo.common.math_utils import assert_integer\nassert_integer(ids.value)\nassert ids.value % PRIME != 0, f'assert_not_zero failed: {ids.value} = 0.'"

const IS_QUAD_RESIDUE = `from starkware.crypto.signature.signature import FIELD_PRIME
from starkware.python.math_utils import div_mod, is_quad_residue, sqrt
x = ids.x
if is_quad_residue(x, FIELD_PRIME):
ids.y = sqrt(x, FIELD_PRIME)
else:
ids.y = sqrt(div_mod(x, 3, FIELD_PRIME), FIELD_PRIME)`

const ASSERT_NOT_EQUAL = "from starkware.cairo.lang.vm.relocatable import RelocatableValue\nboth_ints = isinstance(ids.a, int) and isinstance(ids.b, int)\nboth_relocatable = (\n isinstance(ids.a, RelocatableValue) and isinstance(ids.b, RelocatableValue) and\n ids.a.segment_index == ids.b.segment_index)\nassert both_ints or both_relocatable, \\\n f'assert_not_equal failed: non-comparable values: {ids.a}, {ids.b}.'\nassert (ids.a - ids.b) % PRIME != 0, f'assert_not_equal failed: {ids.a} = {ids.b}.'"

const SQRT = "from starkware.python.math_utils import isqrt\nvalue = ids.value % PRIME\nassert value < 2 ** 250, f\"value={value} is outside of the range [0, 2**250).\"\nassert 2 ** 250 < PRIME\nids.root = isqrt(value)"
33 changes: 33 additions & 0 deletions pkg/hints/math_hints.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package hints
import (
"github.com/lambdaclass/cairo-vm.go/pkg/builtins"
. "github.com/lambdaclass/cairo-vm.go/pkg/hints/hint_utils"
"github.com/lambdaclass/cairo-vm.go/pkg/lambdaworks"
. "github.com/lambdaclass/cairo-vm.go/pkg/lambdaworks"
. "github.com/lambdaclass/cairo-vm.go/pkg/math_utils"
. "github.com/lambdaclass/cairo-vm.go/pkg/vm"
Expand Down Expand Up @@ -65,6 +66,38 @@ func assert_not_zero(ids IdsManager, vm *VirtualMachine) error {
return nil
}

// Implements hint:from starkware.cairo.common.math.cairo
//
// %{
// from starkware.crypto.signature.signature import FIELD_PRIME
// from starkware.python.math_utils import div_mod, is_quad_residue, sqrt
//
// x = ids.x
// if is_quad_residue(x, FIELD_PRIME):
// ids.y = sqrt(x, FIELD_PRIME)
// else:
// ids.y = sqrt(div_mod(x, 3, FIELD_PRIME), FIELD_PRIME)
//
// %}
func is_quad_residue(ids IdsManager, vm *VirtualMachine) error {
x, err := ids.GetFelt("x", vm)
if err != nil {
return err
}
if x.IsZero() || x.IsOne() {
ids.Insert("y", NewMaybeRelocatableFelt(x), vm)

} else if x.Pow(SignedFeltMaxValue()) == FeltOne() {
num := x.Sqrt()
ids.Insert("y", NewMaybeRelocatableFelt(num), vm)

} else {
num := (x.Div(lambdaworks.FeltFromUint64(3))).Sqrt()
ids.Insert("y", NewMaybeRelocatableFelt(num), vm)
}
return nil
}

func assert_not_equal(ids IdsManager, vm *VirtualMachine) error {
// Extract Ids Variables
a, err := ids.Get("a", vm)
Expand Down
1 change: 1 addition & 0 deletions pkg/hints/memcpy_hints_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
. "github.com/lambdaclass/cairo-vm.go/pkg/hints/hint_utils"
. "github.com/lambdaclass/cairo-vm.go/pkg/lambdaworks"
. "github.com/lambdaclass/cairo-vm.go/pkg/types"
. "github.com/lambdaclass/cairo-vm.go/pkg/utils"
. "github.com/lambdaclass/cairo-vm.go/pkg/vm"
. "github.com/lambdaclass/cairo-vm.go/pkg/vm/memory"
)
Expand Down
28 changes: 28 additions & 0 deletions pkg/lambdaworks/lambdaworks.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,13 @@ func FeltOne() Felt {
return fromC(result)
}

// Gets the Signed Felt max value: 0x400000000000008800000000000000000000000000000000000000000000000
func SignedFeltMaxValue() Felt {
var result C.felt_t
C.signed_felt_max_value(&result[0])
return fromC(result)
}

func (f Felt) IsZero() bool {
return f == FeltZero()
}
Expand All @@ -152,6 +159,10 @@ func (f Felt) IsPositive() bool {
return !f.IsZero()
}

func (f Felt) IsOne() bool {
return f == FeltOne()
}

// Writes the result variable with the sum of a and b felts.
func (a Felt) Add(b Felt) Felt {
var result C.felt_t
Expand Down Expand Up @@ -246,6 +257,23 @@ func (a Felt) PowUint(p uint32) Felt {
return fromC(result)
}

func (a Felt) Pow(p Felt) Felt {
var result C.felt_t
var a_c C.felt_t = a.toC()
var p_c C.felt_t = p.toC()

C.felt_pow(&a_c[0], &p_c[0], &result[0])
return fromC(result)
}

func (a Felt) Sqrt() Felt {
var result C.felt_t
var a_c C.felt_t = a.toC()

C.felt_sqrt(&a_c[0], &result[0])
return fromC(result)
}

func (a Felt) Shr(b uint) Felt {
var result C.felt_t
var a_c C.felt_t = a.toC()
Expand Down
Loading

0 comments on commit 853ee4c

Please sign in to comment.