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

Implement ASSERT_LE_FELT+ hints #240

Merged
merged 29 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
561d5c4
Add IdsManager.GetConst
fmoletta Sep 18, 2023
e25c3f4
Integrate into logic
fmoletta Sep 18, 2023
8ad2c3a
Add utils to help with fetching constants
fmoletta Sep 18, 2023
53e6b76
Add SetupConstantsForTest
fmoletta Sep 18, 2023
34cc455
Add comments
fmoletta Sep 18, 2023
164820a
Guard error case
fmoletta Sep 18, 2023
e529897
Fix typo
fmoletta Sep 18, 2023
4b93d07
Fix util
fmoletta Sep 18, 2023
6b7d886
Merge branch 'add-constants-to-ids' of github.com:lambdaclass/cairo-v…
fmoletta Sep 18, 2023
37c674a
Add hint code
fmoletta Sep 18, 2023
9ddaeac
Implement FeltFromBigInt
fmoletta Sep 18, 2023
73703bc
Implement ASSERT_LE_FELT hint
fmoletta Sep 18, 2023
6f7fe68
Add comment
fmoletta Sep 18, 2023
a1c0625
Add test
fmoletta Sep 18, 2023
a712b47
Add the 3 assert_le_felt_exclued hints
fmoletta Sep 18, 2023
ad0cd1e
Add tests
fmoletta Sep 18, 2023
df0ef46
Fix identifier
fmoletta Sep 18, 2023
1eabf94
Add integration test
fmoletta Sep 18, 2023
64b7164
Fix type const in tests
fmoletta Sep 18, 2023
b8877e8
Merge branch 'main' of github.com:lambdaclass/cairo-vm.go into assert…
fmoletta Sep 18, 2023
a85c2c9
Fix typo
fmoletta Sep 18, 2023
f4a0cf2
Fix test
fmoletta Sep 18, 2023
03142ac
Fix bug
fmoletta Sep 18, 2023
742d51e
Remove debug print
fmoletta Sep 18, 2023
8707c24
Fix test name
fmoletta Sep 18, 2023
a920761
Merge branch 'main' of github.com:lambdaclass/cairo-vm.go into assert…
fmoletta Sep 19, 2023
863069b
Merge branch 'main' of github.com:lambdaclass/cairo-vm.go into assert…
fmoletta Sep 19, 2023
48f9911
Merge branch 'main' into assert-le-felt
fmoletta Sep 19, 2023
878927c
Format files
entropidelic Sep 19, 2023
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
10 changes: 10 additions & 0 deletions cairo_programs/assert_le_felt.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

%builtins range_check
from starkware.cairo.common.math import assert_le_felt

func main{range_check_ptr: felt}() {
assert_le_felt(1, 2);
assert_le_felt(-2, -1);
assert_le_felt(2, -1);
return ();
}
8 changes: 8 additions & 0 deletions pkg/hints/hint_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any,
return memcpy_enter_scope(data.Ids, vm, execScopes)
case VM_ENTER_SCOPE:
return vm_enter_scope(execScopes)
case ASSERT_LE_FELT:
return assertLeFelt(data.Ids, vm, execScopes, constants)
case ASSERT_LE_FELT_EXCLUDED_0:
return assertLeFeltExcluded0(vm, execScopes)
case ASSERT_LE_FELT_EXCLUDED_1:
return assertLeFeltExcluded1(vm, execScopes)
case ASSERT_LE_FELT_EXCLUDED_2:
return assertLeFeltExcluded2(vm, execScopes)
default:
return errors.Errorf("Unknown Hint: %s", data.Code)
}
Expand Down
8 changes: 8 additions & 0 deletions pkg/hints/math_hint_codes.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,11 @@ else:
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)"

const ASSERT_LE_FELT = "import itertools\n\nfrom starkware.cairo.common.math_utils import assert_integer\nassert_integer(ids.a)\nassert_integer(ids.b)\na = ids.a % PRIME\nb = ids.b % PRIME\nassert a <= b, f'a = {a} is not less than or equal to b = {b}.'\n\n# Find an arc less than PRIME / 3, and another less than PRIME / 2.\nlengths_and_indices = [(a, 0), (b - a, 1), (PRIME - 1 - b, 2)]\nlengths_and_indices.sort()\nassert lengths_and_indices[0][0] <= PRIME // 3 and lengths_and_indices[1][0] <= PRIME // 2\nexcluded = lengths_and_indices[2][1]\n\nmemory[ids.range_check_ptr + 1], memory[ids.range_check_ptr + 0] = (\n divmod(lengths_and_indices[0][0], ids.PRIME_OVER_3_HIGH))\nmemory[ids.range_check_ptr + 3], memory[ids.range_check_ptr + 2] = (\n divmod(lengths_and_indices[1][0], ids.PRIME_OVER_2_HIGH))"

const ASSERT_LE_FELT_EXCLUDED_0 = "memory[ap] = 1 if excluded != 0 else 0"

const ASSERT_LE_FELT_EXCLUDED_1 = "memory[ap] = 1 if excluded != 1 else 0"

const ASSERT_LE_FELT_EXCLUDED_2 = "assert excluded == 2"
122 changes: 122 additions & 0 deletions pkg/hints/math_hints.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package hints

import (
"math/big"

"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/types"
. "github.com/lambdaclass/cairo-vm.go/pkg/vm"
. "github.com/lambdaclass/cairo-vm.go/pkg/vm/memory"
"github.com/pkg/errors"
Expand Down Expand Up @@ -151,3 +154,122 @@ func sqrt(ids IdsManager, vm *VirtualMachine) error {
ids.Insert("root", NewMaybeRelocatableFelt(root_felt), vm)
return nil
}

func assertLeFelt(ids IdsManager, vm *VirtualMachine, scopes *ExecutionScopes, constants *map[string]Felt) error {
// Fetch constants
primeOver3HighFelt, err := ids.GetConst("PRIME_OVER_3_HIGH", constants)
if err != nil {
return err
}
primeOver3High := primeOver3HighFelt.ToBigInt()
primeOver2HighFelt, err := ids.GetConst("PRIME_OVER_2_HIGH", constants)
if err != nil {
return err
}
primeOver2High := primeOver2HighFelt.ToBigInt()
// Fetch ids variables
aFelt, err := ids.GetFelt("a", vm)
if err != nil {
return err
}
a := aFelt.ToBigInt()
bFelt, err := ids.GetFelt("b", vm)
if err != nil {
return err
}
b := bFelt.ToBigInt()
rangeCheckPtr, err := ids.GetRelocatable("range_check_ptr", vm)
if err != nil {
return err
}
// Hint Logic
cairoPrime, _ := new(big.Int).SetString(CAIRO_PRIME_HEX, 0)
halfPrime := new(big.Int).Div(cairoPrime, new(big.Int).SetUint64(2))
thirdOfPrime := new(big.Int).Div(cairoPrime, new(big.Int).SetUint64(3))
if a.Cmp(b) == 1 {
return errors.Errorf("Assertion failed, %v, is not less or equal to %v", a, b)
}
arc1 := new(big.Int).Sub(b, a)
arc2 := new(big.Int).Sub(new(big.Int).Sub(cairoPrime, (big.NewInt(1))), b)

// Split lengthsAndIndices array into lenght & idxs array and mantain the same order between them
lengths := []*big.Int{a, arc1, arc2}
idxs := []int{0, 1, 2}
// Sort lengths & idxs by lengths
for i := 0; i < 3; i++ {
for j := i; j > 0 && lengths[j-1].Cmp(lengths[j]) == 1; j-- {
lengths[j], lengths[j-1] = lengths[j-1], lengths[j]
idxs[j], idxs[j-1] = idxs[j-1], idxs[j]
}
}

if lengths[0].Cmp(thirdOfPrime) == 1 || lengths[1].Cmp(halfPrime) == 1 {
return errors.Errorf("Arc too big, %v must be <= %v and %v <= %v", lengths[0], thirdOfPrime, lengths[1], halfPrime)
}
excluded := idxs[2]
scopes.AssignOrUpdateVariable("excluded", excluded)
q_0, r_0 := new(big.Int).DivMod(lengths[0], primeOver3High, primeOver3High)
q_1, r_1 := new(big.Int).DivMod(lengths[1], primeOver2High, primeOver2High)

// Insert values into range_check_ptr
data := []MaybeRelocatable{
*NewMaybeRelocatableFelt(FeltFromBigInt(r_0)),
*NewMaybeRelocatableFelt(FeltFromBigInt(q_0)),
*NewMaybeRelocatableFelt(FeltFromBigInt(r_1)),
*NewMaybeRelocatableFelt(FeltFromBigInt(q_1)),
}
_, err = vm.Segments.LoadData(rangeCheckPtr, &data)

return err
}

// "memory[ap] = 1 if excluded != 0 else 0"
func assertLeFeltExcluded0(vm *VirtualMachine, scopes *ExecutionScopes) error {
// Fetch scope var
excludedAny, err := scopes.Get("excluded")
if err != nil {
return err
}
excluded, ok := excludedAny.(int)
if !ok {
return errors.New("excluded not in scope")
}
if excluded == 0 {
return vm.Segments.Memory.Insert(vm.RunContext.Ap, NewMaybeRelocatableFelt(FeltZero()))
}
return vm.Segments.Memory.Insert(vm.RunContext.Ap, NewMaybeRelocatableFelt(FeltOne()))
}

// "memory[ap] = 1 if excluded != 1 else 0"
func assertLeFeltExcluded1(vm *VirtualMachine, scopes *ExecutionScopes) error {
// Fetch scope var
excludedAny, err := scopes.Get("excluded")
if err != nil {
return err
}
excluded, ok := excludedAny.(int)
if !ok {
return errors.New("excluded not in scope")
}
if excluded == 1 {
return vm.Segments.Memory.Insert(vm.RunContext.Ap, NewMaybeRelocatableFelt(FeltZero()))
}
return vm.Segments.Memory.Insert(vm.RunContext.Ap, NewMaybeRelocatableFelt(FeltOne()))
}

// "assert excluded == 2"
func assertLeFeltExcluded2(vm *VirtualMachine, scopes *ExecutionScopes) error {
// Fetch scope var
excludedAny, err := scopes.Get("excluded")
if err != nil {
return err
}
excluded, ok := excludedAny.(int)
if !ok {
return errors.New("excluded not in scope")
}
if excluded != 2 {
return errors.New("Assertion Failed: excluded == 2")
}
return nil
}
141 changes: 141 additions & 0 deletions pkg/hints/math_hints_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
. "github.com/lambdaclass/cairo-vm.go/pkg/hints"
. "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/vm"
. "github.com/lambdaclass/cairo-vm.go/pkg/vm/memory"
)
Expand Down Expand Up @@ -316,3 +317,143 @@ func TestSqrtOk(t *testing.T) {
t.Errorf("Expected sqrt(9) == 3. Got: %v", root)
}
}

func TestAssertLeFeltOk(t *testing.T) {
vm := NewVirtualMachine()
vm.Segments.AddSegment()
vm.Segments.AddSegment()
scopes := NewExecutionScopes()
idsManager := SetupIdsForTest(
map[string][]*MaybeRelocatable{
"a": {NewMaybeRelocatableFelt(FeltOne())},
"b": {NewMaybeRelocatableFelt(FeltFromUint64(2))},
"range_check_ptr": {NewMaybeRelocatableRelocatable(NewRelocatable(1, 0))},
},
vm,
)
constants := SetupConstantsForTest(map[string]Felt{
"PRIME_OVER_3_HIGH": FeltFromHex("4000000000000088000000000000001"),
"PRIME_OVER_2_HIGH": FeltFromHex("2AAAAAAAAAAAAB05555555555555556"),
},
&idsManager,
)
hintProcessor := CairoVmHintProcessor{}
hintData := any(HintData{
Ids: idsManager,
Code: ASSERT_LE_FELT,
})
err := hintProcessor.ExecuteHint(vm, &hintData, &constants, scopes)
if err != nil {
t.Errorf("ASSERT_LE_FELT hint failed with error: %s", err)
}
}

func TestAssertLeFeltExcluded0Zero(t *testing.T) {
vm := NewVirtualMachine()
vm.Segments.AddSegment()
scopes := NewExecutionScopes()
scopes.AssignOrUpdateVariable("excluded", int(0))
hintProcessor := CairoVmHintProcessor{}
hintData := any(HintData{
Code: ASSERT_LE_FELT_EXCLUDED_0,
})
err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes)
if err != nil {
t.Errorf("ASSERT_LE_FELT_EXCLUDED_0 hint test failed with error %s", err)
}
// Check the value of memory[ap]
val, err := vm.Segments.Memory.GetFelt(vm.RunContext.Ap)
if err != nil || !val.IsZero() {
t.Error("Wrong/No value inserted into ap")
}
}

func TestAssertLeFeltExcluded0One(t *testing.T) {
vm := NewVirtualMachine()
vm.Segments.AddSegment()
scopes := NewExecutionScopes()
scopes.AssignOrUpdateVariable("excluded", int(1))
hintProcessor := CairoVmHintProcessor{}
hintData := any(HintData{
Code: ASSERT_LE_FELT_EXCLUDED_0,
})
err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes)
if err != nil {
t.Errorf("ASSERT_LE_FELT_EXCLUDED_0 hint test failed with error %s", err)
}
// Check the value of memory[ap]
val, err := vm.Segments.Memory.GetFelt(vm.RunContext.Ap)
if err != nil || val != FeltOne() {
t.Error("Wrong/No value inserted into ap")
}
}

func TestAssertLeFeltExcluded1Zero(t *testing.T) {
vm := NewVirtualMachine()
vm.Segments.AddSegment()
scopes := NewExecutionScopes()
scopes.AssignOrUpdateVariable("excluded", int(1))
hintProcessor := CairoVmHintProcessor{}
hintData := any(HintData{
Code: ASSERT_LE_FELT_EXCLUDED_1,
})
err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes)
if err != nil {
t.Errorf("ASSERT_LE_FELT_EXCLUDED_1 hint test failed with error %s", err)
}
// Check the value of memory[ap]
val, err := vm.Segments.Memory.GetFelt(vm.RunContext.Ap)
if err != nil || !val.IsZero() {
t.Error("Wrong/No value inserted into ap")
}
}

func TestAssertLeFeltExcluded1One(t *testing.T) {
vm := NewVirtualMachine()
vm.Segments.AddSegment()
scopes := NewExecutionScopes()
scopes.AssignOrUpdateVariable("excluded", int(0))
hintProcessor := CairoVmHintProcessor{}
hintData := any(HintData{
Code: ASSERT_LE_FELT_EXCLUDED_1,
})
err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes)
if err != nil {
t.Errorf("ASSERT_LE_FELT_EXCLUDED_1 hint test failed with error %s", err)
}
// Check the value of memory[ap]
val, err := vm.Segments.Memory.GetFelt(vm.RunContext.Ap)
if err != nil || val != FeltOne() {
t.Error("Wrong/No value inserted into ap")
}
}

func TestAssertLeFeltExcluded2Ok(t *testing.T) {
vm := NewVirtualMachine()
vm.Segments.AddSegment()
scopes := NewExecutionScopes()
scopes.AssignOrUpdateVariable("excluded", int(2))
hintProcessor := CairoVmHintProcessor{}
hintData := any(HintData{
Code: ASSERT_LE_FELT_EXCLUDED_2,
})
err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes)
if err != nil {
t.Errorf("ASSERT_LE_FELT_EXCLUDED_2 hint test failed with error %s", err)
}
}

func TestAssertLeFeltExcluded2Err(t *testing.T) {
vm := NewVirtualMachine()
vm.Segments.AddSegment()
scopes := NewExecutionScopes()
scopes.AssignOrUpdateVariable("excluded", int(0))
hintProcessor := CairoVmHintProcessor{}
hintData := any(HintData{
Code: ASSERT_LE_FELT_EXCLUDED_2,
})
err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes)
if err == nil {
t.Errorf("ASSERT_LE_FELT_EXCLUDED_2 hint test should have failed")
}
}
12 changes: 12 additions & 0 deletions pkg/lambdaworks/lambdaworks.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,18 @@ func (f Felt) ToBigInt() *big.Int {
return new(big.Int).SetBytes(f.ToBeBytes()[:32])
}

func FeltFromBigInt(n *big.Int) Felt {
// Perform modulo prime
prime, _ := new(big.Int).SetString(CAIRO_PRIME_HEX, 0)
if n.Cmp(prime) != -1 {
n = new(big.Int).Mod(n, prime)
}
bytes := n.Bytes()
var bytes32 [32]byte
copy(bytes32[:], bytes)
return FeltFromLeBytes(&bytes32)
}

const CAIRO_PRIME_HEX = "0x800000000000011000000000000000000000000000000000000000000000001"
const SIGNED_FELT_MAX_HEX = "0x400000000000008800000000000000000000000000000000000000000000000"

Expand Down
16 changes: 16 additions & 0 deletions pkg/lambdaworks/lambdaworks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,22 @@ func TestToBigInt(t *testing.T) {
}
}

func TestFromBigInt(t *testing.T) {
expectedFelt := lambdaworks.FeltFromUint64(26)
bigInt := new(big.Int).SetUint64(26)
if !reflect.DeepEqual(lambdaworks.FeltFromBigInt(bigInt), expectedFelt) {
t.Errorf("TestToBigInt failed. Expected: %v, Got: %v", 26, lambdaworks.FeltFromBigInt(bigInt))
}
}

func TestFromBigIntPrime(t *testing.T) {
expectedFelt := lambdaworks.FeltFromDecString("0")
bigInt, _ := new(big.Int).SetString(lambdaworks.CAIRO_PRIME_HEX, 0)
if !reflect.DeepEqual(lambdaworks.FeltFromBigInt(bigInt), expectedFelt) {
t.Errorf("TestToBigInt failed. Expected: PRIME, Got: %v", lambdaworks.FeltFromBigInt(bigInt))
}
}

func TestToSignedNegative(t *testing.T) {
felt := lambdaworks.FeltFromDecString("-1")
bigInt := felt.ToSigned()
Expand Down
8 changes: 8 additions & 0 deletions pkg/vm/cairo_run/cairo_run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,11 @@ func TestSqrtHint(t *testing.T) {
t.Errorf("Program execution failed with error: %s", err)
}
}

func TestAssertLeFelt(t *testing.T) {
cairoRunConfig := cairo_run.CairoRunConfig{DisableTracePadding: false, Layout: "all_cairo", ProofMode: false}
_, err := cairo_run.CairoRun("../../../cairo_programs/assert_le_felt.json", cairoRunConfig)
if err != nil {
t.Errorf("Program execution failed with error: %s", err)
}
}
2 changes: 1 addition & 1 deletion pkg/vm/program.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func DeserializeProgramJson(compiledProgram parser.CompiledJson) Program {
func (p *Program) ExtractConstants() map[string]lambdaworks.Felt {
constants := make(map[string]lambdaworks.Felt)
for name, identifier := range p.Identifiers {
if identifier.Type == "constant" {
if identifier.Type == "const" {
constants[name] = identifier.Value
}
}
Expand Down
Loading