diff --git a/cairo_programs/dict_squash.cairo b/cairo_programs/dict_squash.cairo new file mode 100644 index 00000000..05816c63 --- /dev/null +++ b/cairo_programs/dict_squash.cairo @@ -0,0 +1,29 @@ +%builtins range_check + +from starkware.cairo.common.dict_access import DictAccess +from starkware.cairo.common.dict import dict_write, dict_update, dict_squash +from starkware.cairo.common.default_dict import default_dict_new + +func main{range_check_ptr}() -> () { + let (dict_start) = default_dict_new(17); + let dict_end = dict_start; + dict_write{dict_ptr=dict_end}(0, 1); + dict_write{dict_ptr=dict_end}(1, 10); + dict_write{dict_ptr=dict_end}(2, -2); + dict_update{dict_ptr=dict_end}(0, 1, 2); + dict_update{dict_ptr=dict_end}(0, 2, 3); + dict_update{dict_ptr=dict_end}(0, 3, 4); + dict_update{dict_ptr=dict_end}(1, 10, 15); + dict_update{dict_ptr=dict_end}(1, 15, 20); + dict_update{dict_ptr=dict_end}(1, 20, 25); + dict_update{dict_ptr=dict_end}(2, -2, -4); + dict_update{dict_ptr=dict_end}(2, -4, -8); + dict_update{dict_ptr=dict_end}(2, -8, -16); + let (squashed_dict_start, squashed_dict_end) = dict_squash{range_check_ptr=range_check_ptr}( + dict_start, dict_end + ); + assert squashed_dict_end[0] = DictAccess(key=0, prev_value=1, new_value=4); + assert squashed_dict_end[1] = DictAccess(key=1, prev_value=10, new_value=25); + assert squashed_dict_end[2] = DictAccess(key=2, prev_value=-2, new_value=-16); + return (); +} diff --git a/cairo_programs/squash_dict.cairo b/cairo_programs/squash_dict.cairo new file mode 100644 index 00000000..7af92702 --- /dev/null +++ b/cairo_programs/squash_dict.cairo @@ -0,0 +1,33 @@ +%builtins range_check + +from starkware.cairo.common.squash_dict import squash_dict +from starkware.cairo.common.alloc import alloc +from starkware.cairo.common.dict_access import DictAccess + +func main{range_check_ptr}() -> () { + alloc_locals; + let (dict_start: DictAccess*) = alloc(); + assert dict_start[0] = DictAccess(key=0, prev_value=100, new_value=100); + assert dict_start[1] = DictAccess(key=1, prev_value=50, new_value=50); + assert dict_start[2] = DictAccess(key=0, prev_value=100, new_value=200); + assert dict_start[3] = DictAccess(key=1, prev_value=50, new_value=100); + assert dict_start[4] = DictAccess(key=0, prev_value=200, new_value=300); + assert dict_start[5] = DictAccess(key=1, prev_value=100, new_value=150); + + let dict_end = dict_start + 6 * DictAccess.SIZE; + // (dict_start, dict_end) now represents the dictionary + // {0: 100, 1: 50, 0: 200, 1: 100, 0: 300, 1: 150}. + + // Squash the dictionary from an array of 6 DictAccess structs + // to an array of 2, with a single DictAccess entry per key. + let (local squashed_dict_start: DictAccess*) = alloc(); + let (squashed_dict_end) = squash_dict{range_check_ptr=range_check_ptr}( + dict_start, dict_end, squashed_dict_start + ); + + // Check the values of the squashed_dict + // should be: {0: (100, 300), 1: (50, 150)} + assert squashed_dict_start[0] = DictAccess(key=0, prev_value=100, new_value=300); + assert squashed_dict_start[1] = DictAccess(key=1, prev_value=50, new_value=150); + return (); +} diff --git a/go.mod b/go.mod index 463c710c..5d10e288 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.20 require ( github.com/pkg/errors v0.9.1 golang.org/x/crypto v0.12.0 + golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc ) require ( diff --git a/go.sum b/go.sum index 0a509fa9..2dd7019d 100644 --- a/go.sum +++ b/go.sum @@ -11,4 +11,9 @@ github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6S github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU= +golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/pkg/hints/dict_hints.go b/pkg/hints/dict_hints.go index 82159553..f6750355 100644 --- a/pkg/hints/dict_hints.go +++ b/pkg/hints/dict_hints.go @@ -141,3 +141,70 @@ func dictUpdate(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) err tracker.CurrentPtr.Offset += DICT_ACCESS_SIZE return nil } + +func dictSquashCopyDict(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error { + // Extract Variables + dictManager, ok := FetchDictManager(scopes) + if !ok { + return errors.New("Variable __dict_manager not present in current execution scope") + } + dictAccessEnd, err := ids.GetRelocatable("dict_accesses_end", vm) + if err != nil { + return err + } + // Hint logic + tracker, err := dictManager.GetTracker(dictAccessEnd) + if err != nil { + return err + } + initialDict := tracker.CopyDictionary() + scopes.EnterScope(map[string]interface{}{ + "__dict_manager": dictManager, + "initial_dict": initialDict, + }) + return nil +} + +func dictSquashUpdatePtr(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error { + // Extract Variables + dictManager, ok := FetchDictManager(scopes) + if !ok { + return errors.New("Variable __dict_manager not present in current execution scope") + } + squashedDictStart, err := ids.GetRelocatable("squashed_dict_start", vm) + if err != nil { + return err + } + squashedDictEnd, err := ids.GetRelocatable("squashed_dict_end", vm) + if err != nil { + return err + } + // Hint logic + tracker, err := dictManager.GetTracker(squashedDictStart) + if err != nil { + return err + } + tracker.CurrentPtr = squashedDictEnd + return nil +} + +func dictNew(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error { + // Fetch scope variables + initialDictAny, err := scopes.Get("initial_dict") + if err != nil { + return err + } + initialDict, ok := initialDictAny.(map[memory.MaybeRelocatable]memory.MaybeRelocatable) + if !ok { + return errors.New("initial_dict not in scope") + } + // Hint Logic + dictManager, ok := FetchDictManager(scopes) + if !ok { + newDictManager := NewDictManager() + dictManager = &newDictManager + scopes.AssignOrUpdateVariable("__dict_manager", dictManager) + } + dict_ptr := dictManager.NewDictionary(&initialDict, vm) + return vm.Segments.Memory.Insert(vm.RunContext.Ap, memory.NewMaybeRelocatableRelocatable(dict_ptr)) +} diff --git a/pkg/hints/dict_hints_test.go b/pkg/hints/dict_hints_test.go index b70b47dc..dbc5d028 100644 --- a/pkg/hints/dict_hints_test.go +++ b/pkg/hints/dict_hints_test.go @@ -1,6 +1,7 @@ package hints_test import ( + "reflect" "testing" . "github.com/lambdaclass/cairo-vm.go/pkg/hints" @@ -71,7 +72,7 @@ func TestDefaultDictNewHasManager(t *testing.T) { if err != nil { t.Errorf("DEFAULT_DICT_NEW hint test failed with error %s", err) } - // Check that the manager wan't replaced by a new one + // Check that the manager wasn't replaced by a new one dictManagerPtr, ok := FetchDictManager(scopes) if !ok || dictManagerPtr != dictManagerRef { t.Error("DEFAULT_DICT_NEW DictManager replaced") @@ -414,3 +415,237 @@ func TestDictUpdateErr(t *testing.T) { t.Error("DICT_UPDATE hint test should have failed") } } + +func TestDictSqushCopyDictOkEmptyDict(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + + // Create dictManager & add it to scope + dictManager := dict_manager.NewDictManager() + dictManagerRef := &dictManager + initialDict := map[MaybeRelocatable]MaybeRelocatable{} + dict_ptr := dictManager.NewDictionary(&initialDict, vm) + scopes.AssignOrUpdateVariable("__dict_manager", dictManagerRef) + + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "dict_accesses_end": {NewMaybeRelocatableRelocatable(dict_ptr)}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: DICT_SQUASH_COPY_DICT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("DICT_SQUASH_COPY_DICT hint test failed with error %s", err) + } + // Check new scope + new_scope, _ := scopes.GetLocalVariables() + if !reflect.DeepEqual(new_scope, map[string]interface{}{ + "__dict_manager": dictManagerRef, + "initial_dict": initialDict, + }) { + t.Errorf("DICT_SQUASH_COPY_DICT hint test wrong new sope created") + } +} + +func TestDictSqushCopyDictOkNonEmptyDict(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + + // Create dictManager & add it to scope + dictManager := dict_manager.NewDictManager() + dictManagerRef := &dictManager + initialDict := map[MaybeRelocatable]MaybeRelocatable{ + *NewMaybeRelocatableFelt(FeltZero()): *NewMaybeRelocatableFelt(FeltOne()), + *NewMaybeRelocatableFelt(FeltOne()): *NewMaybeRelocatableFelt(FeltOne()), + } + dict_ptr := dictManager.NewDictionary(&initialDict, vm) + scopes.AssignOrUpdateVariable("__dict_manager", dictManagerRef) + + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "dict_accesses_end": {NewMaybeRelocatableRelocatable(dict_ptr)}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: DICT_SQUASH_COPY_DICT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("DICT_SQUASH_COPY_DICT hint test failed with error %s", err) + } + // Check new scope + new_scope, _ := scopes.GetLocalVariables() + if !reflect.DeepEqual(new_scope, map[string]interface{}{ + "__dict_manager": dictManagerRef, + "initial_dict": initialDict, + }) { + t.Errorf("DICT_SQUASH_COPY_DICT hint test wrong new sope created") + } +} + +func TestDictSquashUpdatePtrOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + initialDict := make(map[MaybeRelocatable]MaybeRelocatable) + // Create dictManager & add it to scope + dictManager := dict_manager.NewDictManager() + dict_ptr := dictManager.NewDictionary(&initialDict, vm) + // Keep a reference to the tracker to check that it was updated after the hint + tracker, _ := dictManager.GetTracker(dict_ptr) + scopes.AssignOrUpdateVariable("__dict_manager", &dictManager) + + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "squashed_dict_start": {NewMaybeRelocatableRelocatable(dict_ptr)}, + "squashed_dict_end": {NewMaybeRelocatableRelocatable(dict_ptr.AddUint(5))}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: DICT_SQUASH_UPDATE_PTR, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("DICT_SQUASH_UPDATE_PTR hint test failed with error %s", err) + } + // Check updated ptr + if tracker.CurrentPtr != dict_ptr.AddUint(5) { + t.Error("DICT_SQUASH_UPDATE_PTR hint test failed: Wrong updated tracker.CurrentPtr") + } +} + +func TestDictSquashUpdatePtrMismatchedPtr(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + initialDict := make(map[MaybeRelocatable]MaybeRelocatable) + // Create dictManager & add it to scope + dictManager := dict_manager.NewDictManager() + dict_ptr := dictManager.NewDictionary(&initialDict, vm) + scopes.AssignOrUpdateVariable("__dict_manager", &dictManager) + + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "squashed_dict_start": {NewMaybeRelocatableRelocatable(dict_ptr.AddUint(3))}, + "squashed_dict_end": {NewMaybeRelocatableRelocatable(dict_ptr.AddUint(5))}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: DICT_SQUASH_UPDATE_PTR, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Errorf("DICT_SQUASH_UPDATE_PTR hint test should have failed") + } +} + +func TestDictNewCreateManager(t *testing.T) { + vm := NewVirtualMachine() + scopes := types.NewExecutionScopes() + initialDict := make(map[MaybeRelocatable]MaybeRelocatable) + scopes.AssignOrUpdateVariable("initial_dict", initialDict) + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{}, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: DICT_NEW, + }) + // Advance AP so that values don't clash with FP-based ids + vm.RunContext.Ap = NewRelocatable(0, 5) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("DICT_NEW hint test failed with error %s", err) + } + // Check that a manager was created in the scope + _, ok := FetchDictManager(scopes) + if !ok { + t.Error("DICT_NEW No DictManager created") + } + // Check that the correct base was inserted into ap + val, _ := vm.Segments.Memory.Get(vm.RunContext.Ap) + if val == nil || *val != *NewMaybeRelocatableRelocatable(NewRelocatable(1, 0)) { + t.Error("DICT_NEW Wrong/No base inserted into ap") + } +} + +func TestDictNewHasManager(t *testing.T) { + vm := NewVirtualMachine() + scopes := types.NewExecutionScopes() + // Create initialDict & dictManager & add them to scope + initialDict := make(map[MaybeRelocatable]MaybeRelocatable) + scopes.AssignOrUpdateVariable("initial_dict", initialDict) + dictManager := dict_manager.NewDictManager() + dictManagerRef := &dictManager + scopes.AssignOrUpdateVariable("__dict_manager", dictManagerRef) + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{}, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: DICT_NEW, + }) + // Advance AP so that values don't clash with FP-based ids + vm.RunContext.Ap = NewRelocatable(0, 5) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("DICT_NEW hint test failed with error %s", err) + } + // Check that the manager wasn't replaced by a new one + dictManagerPtr, ok := FetchDictManager(scopes) + if !ok || dictManagerPtr != dictManagerRef { + t.Error("DICT_NEW DictManager replaced") + } + // Check that the correct base was inserted into ap + val, _ := vm.Segments.Memory.Get(vm.RunContext.Ap) + if val == nil || *val != *NewMaybeRelocatableRelocatable(NewRelocatable(1, 0)) { + t.Error("DICT_NEW Wrong/No base inserted into ap") + } +} + +func TestDictNewHasManagerNoInitialDict(t *testing.T) { + vm := NewVirtualMachine() + scopes := types.NewExecutionScopes() + // Create dictManager & add it to scope + dictManager := dict_manager.NewDictManager() + dictManagerRef := &dictManager + scopes.AssignOrUpdateVariable("__dict_manager", dictManagerRef) + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{}, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: DICT_NEW, + }) + // Advance AP so that values don't clash with FP-based ids + vm.RunContext.Ap = NewRelocatable(0, 5) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Errorf("DICT_NEW hint test should have failed") + } +} diff --git a/pkg/hints/ec_hint_test.go b/pkg/hints/ec_hint_test.go index bed5fbe3..8cd486aa 100644 --- a/pkg/hints/ec_hint_test.go +++ b/pkg/hints/ec_hint_test.go @@ -1,7 +1,6 @@ package hints_test import ( - "fmt" "math/big" "testing" @@ -51,9 +50,6 @@ func TestRunEcNegateOk(t *testing.T) { }, vm, ) - - point, _ := idsManager.Get("point", vm) - fmt.Println("Ids manager: ", point) hintProcessor := CairoVmHintProcessor{} hintData := any(HintData{ Ids: idsManager, diff --git a/pkg/hints/hint_codes/dict_hint_codes.go b/pkg/hints/hint_codes/dict_hint_codes.go index b8942a2f..8c302a99 100644 --- a/pkg/hints/hint_codes/dict_hint_codes.go +++ b/pkg/hints/hint_codes/dict_hint_codes.go @@ -7,3 +7,27 @@ const DICT_READ = "dict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ndict const DICT_WRITE = "dict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ndict_tracker.current_ptr += ids.DictAccess.SIZE\nids.dict_ptr.prev_value = dict_tracker.data[ids.key]\ndict_tracker.data[ids.key] = ids.new_value" const DICT_UPDATE = "# Verify dict pointer and prev value.\ndict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ncurrent_value = dict_tracker.data[ids.key]\nassert current_value == ids.prev_value, \\\n f'Wrong previous value in dict. Got {ids.prev_value}, expected {current_value}.'\n\n# Update value.\ndict_tracker.data[ids.key] = ids.new_value\ndict_tracker.current_ptr += ids.DictAccess.SIZE" + +const SQUASH_DICT = "dict_access_size = ids.DictAccess.SIZE\naddress = ids.dict_accesses.address_\nassert ids.ptr_diff % dict_access_size == 0, \\\n 'Accesses array size must be divisible by DictAccess.SIZE'\nn_accesses = ids.n_accesses\nif '__squash_dict_max_size' in globals():\n assert n_accesses <= __squash_dict_max_size, \\\n f'squash_dict() can only be used with n_accesses<={__squash_dict_max_size}. ' \\\n f'Got: n_accesses={n_accesses}.'\n# A map from key to the list of indices accessing it.\naccess_indices = {}\nfor i in range(n_accesses):\n key = memory[address + dict_access_size * i]\n access_indices.setdefault(key, []).append(i)\n# Descending list of keys.\nkeys = sorted(access_indices.keys(), reverse=True)\n# Are the keys used bigger than range_check bound.\nids.big_keys = 1 if keys[0] >= range_check_builtin.bound else 0\nids.first_key = key = keys.pop()" + +const SQUASH_DICT_INNER_SKIP_LOOP = "ids.should_skip_loop = 0 if current_access_indices else 1" + +const SQUASH_DICT_INNER_FIRST_ITERATION = "current_access_indices = sorted(access_indices[key])[::-1]\ncurrent_access_index = current_access_indices.pop()\nmemory[ids.range_check_ptr] = current_access_index" + +const SQUASH_DICT_INNER_CHECK_ACCESS_INDEX = "new_access_index = current_access_indices.pop()\nids.loop_temps.index_delta_minus1 = new_access_index - current_access_index - 1\ncurrent_access_index = new_access_index" + +const SQUASH_DICT_INNER_CONTINUE_LOOP = "ids.loop_temps.should_continue = 1 if current_access_indices else 0" + +const SQUASH_DICT_INNER_ASSERT_LEN_KEYS = "assert len(keys) == 0" + +const SQUASH_DICT_INNER_LEN_ASSERT = "assert len(current_access_indices) == 0" + +const SQUASH_DICT_INNER_USED_ACCESSES_ASSERT = "assert ids.n_used_accesses == len(access_indices[key])" + +const SQUASH_DICT_INNER_NEXT_KEY = "assert len(keys) > 0, 'No keys left but remaining_accesses > 0.'\nids.next_key = key = keys.pop()" + +const DICT_SQUASH_COPY_DICT = "# Prepare arguments for dict_new. In particular, the same dictionary values should be copied\n# to the new (squashed) dictionary.\nvm_enter_scope({\n # Make __dict_manager accessible.\n '__dict_manager': __dict_manager,\n # Create a copy of the dict, in case it changes in the future.\n 'initial_dict': dict(__dict_manager.get_dict(ids.dict_accesses_end)),\n})" + +const DICT_SQUASH_UPDATE_PTR = "# Update the DictTracker's current_ptr to point to the end of the squashed dict.\n__dict_manager.get_tracker(ids.squashed_dict_start).current_ptr = \\\n ids.squashed_dict_end.address_" + +const DICT_NEW = "if '__dict_manager' not in globals():\n from starkware.cairo.common.dict import DictManager\n __dict_manager = DictManager()\n\nmemory[ap] = __dict_manager.new_dict(segments, initial_dict)\ndel initial_dict" diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index d6ebcc08..7917d066 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -60,6 +60,30 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, return dictWrite(data.Ids, execScopes, vm) case DICT_UPDATE: return dictUpdate(data.Ids, execScopes, vm) + case SQUASH_DICT: + return squashDict(data.Ids, execScopes, vm) + case SQUASH_DICT_INNER_SKIP_LOOP: + return squashDictInnerSkipLoop(data.Ids, execScopes, vm) + case SQUASH_DICT_INNER_FIRST_ITERATION: + return squashDictInnerFirstIteration(data.Ids, execScopes, vm) + case SQUASH_DICT_INNER_CHECK_ACCESS_INDEX: + return squashDictInnerCheckAccessIndex(data.Ids, execScopes, vm) + case SQUASH_DICT_INNER_CONTINUE_LOOP: + return squashDictInnerContinueLoop(data.Ids, execScopes, vm) + case SQUASH_DICT_INNER_ASSERT_LEN_KEYS: + return squashDictInnerAssertLenKeys(execScopes) + case SQUASH_DICT_INNER_LEN_ASSERT: + return squashDictInnerLenAssert(execScopes) + case SQUASH_DICT_INNER_USED_ACCESSES_ASSERT: + return squashDictInnerUsedAccessesAssert(data.Ids, execScopes, vm) + case SQUASH_DICT_INNER_NEXT_KEY: + return squashDictInnerNextKey(data.Ids, execScopes, vm) + case DICT_SQUASH_COPY_DICT: + return dictSquashCopyDict(data.Ids, execScopes, vm) + case DICT_SQUASH_UPDATE_PTR: + return dictSquashUpdatePtr(data.Ids, execScopes, vm) + case DICT_NEW: + return dictNew(data.Ids, execScopes, vm) case VM_EXIT_SCOPE: return vm_exit_scope(execScopes) case ASSERT_NOT_EQUAL: diff --git a/pkg/hints/squash_dict_hints.go b/pkg/hints/squash_dict_hints.go new file mode 100644 index 00000000..87767908 --- /dev/null +++ b/pkg/hints/squash_dict_hints.go @@ -0,0 +1,309 @@ +package hints + +import ( + "sort" + + "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/types" + . "github.com/lambdaclass/cairo-vm.go/pkg/vm" + . "github.com/lambdaclass/cairo-vm.go/pkg/vm/memory" + "github.com/pkg/errors" + "golang.org/x/exp/maps" +) + +// SortMaybeRelocatables implements sort.Interface for []MaybeRelocatables +type SortMaybeRelocatables []MaybeRelocatable + +func (s SortMaybeRelocatables) Len() int { return len(s) } +func (s SortMaybeRelocatables) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s SortMaybeRelocatables) Less(i, j int) bool { + isLess := false + //Integers are considered smaller than all relocatable values. + a, b := s[i], s[j] + aFelt, aIsFelt := a.GetFelt() + bFelt, bIsFelt := b.GetFelt() + + switch true { + // Both felts + case aIsFelt && bIsFelt: + if aFelt.Cmp(bFelt) == -1 { + isLess = true + } + // a Felt, b Relocatable + case aIsFelt && !bIsFelt: + // a Relocatable, b Felt + case !aIsFelt && bIsFelt: + isLess = true + // Both Relocatables + case !aIsFelt && !bIsFelt: + aRel, _ := a.GetRelocatable() + bRel, _ := a.GetRelocatable() + if aRel.SegmentIndex == bRel.SegmentIndex { + isLess = aRel.Offset < bRel.Offset + } else { + isLess = aRel.SegmentIndex < bRel.SegmentIndex + } + } + + return isLess +} + +func squashDict(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error { + address, err := ids.GetRelocatable("dict_accesses", vm) + if err != nil { + return err + } + ptrDiff, err := ids.GetFelt("ptr_diff", vm) + if err != nil { + return err + } + if !ptrDiff.ModFloor(FeltFromUint64(DICT_ACCESS_SIZE)).IsZero() { + return errors.New("Accesses array size must be divisible by DictAccess.SIZE") + } + nAccessesFelt, err := ids.GetFelt("n_accesses", vm) + if err != nil { + return err + } + nAccesses, err := nAccessesFelt.ToU64() + if err != nil { + return err + } + squashDictMaxSize, err := scopes.Get("__squash_dict_max_size") + if err == nil { + maxSize, ok := squashDictMaxSize.(uint64) + if ok { + if nAccesses > maxSize { + return errors.Errorf("squash_dict() can only be used with n_accesses<=%d.\nGot: n_accesses=%d.", maxSize, nAccesses) + } + } + } + // A map from key to the list of indices accessing it. + accessIndices := make(map[MaybeRelocatable][]int) + for i := 0; i < int(nAccesses); i++ { + key, err := vm.Segments.Memory.Get(address.AddUint(uint(i) * DICT_ACCESS_SIZE)) + if err != nil { + return err + } + _, hasKey := accessIndices[*key] + if !hasKey { + accessIndices[*key] = make([]int, 0) + } + accessIndices[*key] = append(accessIndices[*key], i) + } + //Descending list of keys. + keys := maps.Keys(accessIndices) + if len(keys) == 0 { + return errors.New("keys is empty") + } + sort.Sort(sort.Reverse(SortMaybeRelocatables(keys))) + //Are the keys used bigger than the range_check bound. + bigKeys := FeltZero() + highKeyFelt, isFelt := keys[0].GetFelt() + if isFelt && highKeyFelt.Bits() >= builtins.RANGE_CHECK_N_PARTS*builtins.INNER_RC_BOUND_SHIFT { + bigKeys = FeltOne() + } + lowKey := keys[len(keys)-1] + // Insert new scope variables + scopes.AssignOrUpdateVariable("access_indices", accessIndices) + scopes.AssignOrUpdateVariable("keys", keys[:len(keys)-1]) + scopes.AssignOrUpdateVariable("key", lowKey) + // Insert ids variables + err = ids.Insert("big_keys", NewMaybeRelocatableFelt(bigKeys), vm) + if err != nil { + return err + } + return ids.Insert("first_key", &lowKey, vm) +} + +func squashDictInnerSkipLoop(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error { + currentAccessIndicesAny, err := scopes.Get("current_access_indices") + if err != nil { + return err + } + currentAccessIndices, ok := currentAccessIndicesAny.([]int) + if !ok { + return errors.New("current_access_indices not in scope") + } + // Hint Logic + if len(currentAccessIndices) != 0 { + return ids.Insert("should_skip_loop", NewMaybeRelocatableFelt(FeltZero()), vm) + } + return ids.Insert("should_skip_loop", NewMaybeRelocatableFelt(FeltOne()), vm) +} + +func squashDictInnerFirstIteration(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error { + // Fetch scope variables + accessIndicesAny, err := scopes.Get("access_indices") + if err != nil { + return err + } + accessIndices, ok := accessIndicesAny.(map[MaybeRelocatable][]int) + if !ok { + return errors.New("access_indices not in scope") + } + + keyAny, err := scopes.Get("key") + if err != nil { + return err + } + key, ok := keyAny.(MaybeRelocatable) + if !ok { + return errors.New("key not in scope") + } + // Fetch ids variables + rangeCheckPtr, err := ids.GetRelocatable("range_check_ptr", vm) + if err != nil { + return err + } + // Hint Logic + currentAccessIndices := accessIndices[key] + sort.Sort(sort.Reverse(sort.IntSlice(currentAccessIndices))) + if len(currentAccessIndices) == 0 { + return errors.New("current_access_indices is empty") + } + currentAccessIndex := currentAccessIndices[len(currentAccessIndices)-1] + currentAccessIndices = currentAccessIndices[:len(currentAccessIndices)-1] + // Add variables to scope + scopes.AssignOrUpdateVariable("current_access_indices", currentAccessIndices) + scopes.AssignOrUpdateVariable("current_access_index", currentAccessIndex) + //Insert current_accesss_index into range_check_ptr + return vm.Segments.Memory.Insert(rangeCheckPtr, NewMaybeRelocatableFelt(FeltFromUint64(uint64(currentAccessIndex)))) +} + +func squashDictInnerCheckAccessIndex(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error { + // Fetch scope variables + currentAccessIndicesAny, err := scopes.Get("current_access_indices") + if err != nil { + return err + } + currentAccessIndices, ok := currentAccessIndicesAny.([]int) + if !ok { + return errors.New("current_access_indices not in scope") + } + currentAccessIndexAny, err := scopes.Get("current_access_index") + if err != nil { + return err + } + currentAccessIndex, ok := currentAccessIndexAny.(int) + if !ok { + return errors.New("current_access_index not in scope") + } + // Hint Logic + if len(currentAccessIndices) == 0 { + return errors.New("current_access_indices is empty") + } + newAccessIndex := currentAccessIndices[len(currentAccessIndices)-1] + currentAccessIndices = currentAccessIndices[:len(currentAccessIndices)-1] + deltaMinusOne := newAccessIndex - currentAccessIndex - 1 + // Update scope variables + scopes.AssignOrUpdateVariable("current_access_indices", currentAccessIndices) + scopes.AssignOrUpdateVariable("current_access_index", newAccessIndex) + scopes.AssignOrUpdateVariable("new_access_index", newAccessIndex) + // Update ids variables + return ids.Insert("loop_temps", NewMaybeRelocatableFelt(FeltFromUint64(uint64(deltaMinusOne))), vm) +} + +func squashDictInnerContinueLoop(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error { + currentAccessIndicesAny, err := scopes.Get("current_access_indices") + if err != nil { + return err + } + currentAccessIndices, ok := currentAccessIndicesAny.([]int) + if !ok { + return errors.New("current_access_indices not in scope") + } + // Hint Logic + if len(currentAccessIndices) == 0 { + return ids.InsertStructField("loop_temps", 3, NewMaybeRelocatableFelt(FeltZero()), vm) + } + return ids.InsertStructField("loop_temps", 3, NewMaybeRelocatableFelt(FeltOne()), vm) +} + +func squashDictInnerAssertLenKeys(scopes *ExecutionScopes) error { + // Fetch scope variables + keysAny, err := scopes.Get("keys") + if err != nil { + return err + } + keys, ok := keysAny.([]MaybeRelocatable) + if !ok { + return errors.New("keys not in scope") + } + // Hint logic + if len(keys) != 0 { + return errors.New("Assertion failed: len(keys) == 0") + } + return nil +} + +func squashDictInnerLenAssert(scopes *ExecutionScopes) error { + // Fetch scope variables + currentAccessIndicesAny, err := scopes.Get("current_access_indices") + if err != nil { + return err + } + currentAccessIndices, ok := currentAccessIndicesAny.([]int) + if !ok { + return errors.New("current_access_indices not in scope") + } + // Hint logic + if len(currentAccessIndices) != 0 { + return errors.New("Assertion failed: len(current_access_indices) == 0") + } + return nil +} + +func squashDictInnerUsedAccessesAssert(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error { + // Fetch scope variables + accessIndicesAny, err := scopes.Get("access_indices") + if err != nil { + return err + } + accessIndices, ok := accessIndicesAny.(map[MaybeRelocatable][]int) + if !ok { + return errors.New("access_indices not in scope") + } + keyAny, err := scopes.Get("key") + if err != nil { + return err + } + key, ok := keyAny.(MaybeRelocatable) + if !ok { + return errors.New("key not in scope") + } + // Fetch ids variable + nUsedAccesses, err := ids.GetFelt("n_used_accesses", vm) + if err != nil { + return err + } + // Hint logic + if FeltFromUint64(uint64(len(accessIndices[key]))) != nUsedAccesses { + return errors.New("Assertion failed: ids.n_used_accesses == len(access_indices[key])") + } + return nil +} + +func squashDictInnerNextKey(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error { + // Fetch scope variables + keysAny, err := scopes.Get("keys") + if err != nil { + return err + } + keys, ok := keysAny.([]MaybeRelocatable) + if !ok { + return errors.New("keys not in scope") + } + // Hint logic + if len(keys) <= 0 { + return errors.New("Assertion failed: len(keys) > 0.\nNo keys left but remaining_accesses > 0.)") + } + key := keys[len(keys)-1] + keys = keys[:len(keys)-1] + ids.Insert("next_key", &key, vm) + // Update scope variables + scopes.AssignOrUpdateVariable("keys", keys) + scopes.AssignOrUpdateVariable("key", key) + return nil +} diff --git a/pkg/hints/squash_dict_hints_test.go b/pkg/hints/squash_dict_hints_test.go new file mode 100644 index 00000000..d3003474 --- /dev/null +++ b/pkg/hints/squash_dict_hints_test.go @@ -0,0 +1,788 @@ +package hints_test + +import ( + "reflect" + "testing" + + . "github.com/lambdaclass/cairo-vm.go/pkg/hints" + . "github.com/lambdaclass/cairo-vm.go/pkg/hints/hint_codes" + . "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" +) + +func TestSquashDictValidOneKeyDictNoMaxSizeBigKeys(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "dict_accesses": {NewMaybeRelocatableRelocatable(NewRelocatable(2, 0))}, + "big_keys": {nil}, + "first_key": {nil}, + "ptr_diff": {NewMaybeRelocatableFelt(FeltFromUint64(6))}, + "n_accesses": {NewMaybeRelocatableFelt(FeltFromUint64(2))}, + }, + vm, + ) + // Insert dict into memory + // Dict = {(prime - 1): (1,1), (prime - 1): (1,2)} + vm.Segments.Memory.Insert(NewRelocatable(2, 0), NewMaybeRelocatableFelt(FeltFromDecString("-1"))) + vm.Segments.Memory.Insert(NewRelocatable(2, 1), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 2), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 3), NewMaybeRelocatableFelt(FeltFromDecString("-1"))) + vm.Segments.Memory.Insert(NewRelocatable(2, 4), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 5), NewMaybeRelocatableFelt(FeltFromUint64(2))) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("SQUASH_DICT hint failed with error: %s", err) + } + // Check scope + accessIndicesAny, err := scopes.Get("access_indices") + accessIndices := accessIndicesAny.(map[MaybeRelocatable][]int) + // expect access_indices = {prime -1: [0, 1]} + expectedAccessIndices := map[MaybeRelocatable][]int{ + *NewMaybeRelocatableFelt(FeltFromDecString("-1")): {0, 1}, + } + if !reflect.DeepEqual(accessIndices, expectedAccessIndices) { + t.Errorf("SQUASH_DICT wrong access_indices.\n Expected %v, got: %v", expectedAccessIndices, accessIndices) + } + keysAny, err := scopes.Get("keys") + keys := keysAny.([]MaybeRelocatable) + expectedKeys := []MaybeRelocatable{} + if !reflect.DeepEqual(keys, expectedKeys) { + t.Errorf("SQUASH_DICT wrong keys.\n Expected %v, got: %v", expectedKeys, keys) + } + keyAny, err := scopes.Get("key") + key := keyAny.(MaybeRelocatable) + expectedKey := *NewMaybeRelocatableFelt(FeltFromDecString("-1")) + if !reflect.DeepEqual(key, expectedKey) { + t.Errorf("SQUASH_DICT wrong key.\n Expected %v, got: %v", expectedKey, keys) + } +} + +func TestSquashDictValidOneKeyDictWithMaxSize(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("__squash_dict_max_size", uint64(12)) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "dict_accesses": {NewMaybeRelocatableRelocatable(NewRelocatable(2, 0))}, + "big_keys": {nil}, + "first_key": {nil}, + "ptr_diff": {NewMaybeRelocatableFelt(FeltFromUint64(6))}, + "n_accesses": {NewMaybeRelocatableFelt(FeltFromUint64(2))}, + }, + vm, + ) + // Insert dict into memory + // Dict = {(prime - 1): (1,1), (prime - 1): (1,2)} + vm.Segments.Memory.Insert(NewRelocatable(2, 0), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 1), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 2), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 3), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 4), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 5), NewMaybeRelocatableFelt(FeltFromUint64(2))) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("SQUASH_DICT hint failed with error: %s", err) + } + // Check scope + accessIndicesAny, err := scopes.Get("access_indices") + accessIndices := accessIndicesAny.(map[MaybeRelocatable][]int) + // expect access_indices = {prime -1: [0, 1]} + expectedAccessIndices := map[MaybeRelocatable][]int{ + *NewMaybeRelocatableFelt(FeltOne()): {0, 1}, + } + if !reflect.DeepEqual(accessIndices, expectedAccessIndices) { + t.Errorf("SQUASH_DICT wrong access_indices.\n Expected %v, got: %v", expectedAccessIndices, accessIndices) + } + keysAny, err := scopes.Get("keys") + keys := keysAny.([]MaybeRelocatable) + expectedKeys := []MaybeRelocatable{} + if !reflect.DeepEqual(keys, expectedKeys) { + t.Errorf("SQUASH_DICT wrong keys.\n Expected %v, got: %v", expectedKeys, keys) + } + keyAny, err := scopes.Get("key") + key := keyAny.(MaybeRelocatable) + expectedKey := *NewMaybeRelocatableFelt(FeltOne()) + if !reflect.DeepEqual(key, expectedKey) { + t.Errorf("SQUASH_DICT wrong key.\n Expected %v, got: %v", expectedKey, keys) + } +} + +func TestSquashDictValidTwoKeyDictNoMaxSizeBigKeys(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "dict_accesses": {NewMaybeRelocatableRelocatable(NewRelocatable(2, 0))}, + "big_keys": {nil}, + "first_key": {nil}, + "ptr_diff": {NewMaybeRelocatableFelt(FeltFromUint64(6))}, + "n_accesses": {NewMaybeRelocatableFelt(FeltFromUint64(4))}, + }, + vm, + ) + // Insert dict into memory + //Dict = {1: (1,1), 1: (1,2), 2: (10,10), 2: (10,20)} + vm.Segments.Memory.Insert(NewRelocatable(2, 0), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 1), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 2), NewMaybeRelocatableFelt(FeltOne())) + + vm.Segments.Memory.Insert(NewRelocatable(2, 3), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 4), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 5), NewMaybeRelocatableFelt(FeltFromUint64(2))) + + vm.Segments.Memory.Insert(NewRelocatable(2, 6), NewMaybeRelocatableFelt(FeltFromUint64(2))) + vm.Segments.Memory.Insert(NewRelocatable(2, 7), NewMaybeRelocatableFelt(FeltFromUint64(10))) + vm.Segments.Memory.Insert(NewRelocatable(2, 8), NewMaybeRelocatableFelt(FeltFromUint64(10))) + + vm.Segments.Memory.Insert(NewRelocatable(2, 9), NewMaybeRelocatableFelt(FeltFromUint64(2))) + vm.Segments.Memory.Insert(NewRelocatable(2, 10), NewMaybeRelocatableFelt(FeltFromUint64(10))) + vm.Segments.Memory.Insert(NewRelocatable(2, 11), NewMaybeRelocatableFelt(FeltFromUint64(20))) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("SQUASH_DICT hint failed with error: %s", err) + } + // Check scope + accessIndicesAny, err := scopes.Get("access_indices") + accessIndices := accessIndicesAny.(map[MaybeRelocatable][]int) + // expect access_indices = {prime -1: [0, 1]} + expectedAccessIndices := map[MaybeRelocatable][]int{ + *NewMaybeRelocatableFelt(FeltOne()): {0, 1}, + *NewMaybeRelocatableFelt(FeltFromUint64(2)): {2, 3}, + } + if !reflect.DeepEqual(accessIndices, expectedAccessIndices) { + t.Errorf("SQUASH_DICT wrong access_indices.\n Expected %v, got: %v", expectedAccessIndices, accessIndices) + } + keysAny, err := scopes.Get("keys") + keys := keysAny.([]MaybeRelocatable) + expectedKeys := []MaybeRelocatable{ + *NewMaybeRelocatableFelt(FeltFromUint64(2)), + } + if !reflect.DeepEqual(keys, expectedKeys) { + t.Errorf("SQUASH_DICT wrong keys.\n Expected %v, got: %v", expectedKeys, keys) + } + keyAny, err := scopes.Get("key") + key := keyAny.(MaybeRelocatable) + expectedKey := *NewMaybeRelocatableFelt(FeltOne()) + if !reflect.DeepEqual(key, expectedKey) { + t.Errorf("SQUASH_DICT wrong key.\n Expected %v, got: %v", expectedKey, keys) + } +} + +func TestSquashDictInvalidOneKeyDictWithMaxSizeExceeded(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("__squash_dict_max_size", uint64(1)) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "dict_accesses": {NewMaybeRelocatableRelocatable(NewRelocatable(2, 0))}, + "big_keys": {nil}, + "first_key": {nil}, + "ptr_diff": {NewMaybeRelocatableFelt(FeltFromUint64(6))}, + "n_accesses": {NewMaybeRelocatableFelt(FeltFromUint64(2))}, + }, + vm, + ) + // Insert dict into memory + // Dict = {(prime - 1): (1,1), (prime - 1): (1,2)} + vm.Segments.Memory.Insert(NewRelocatable(2, 0), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 1), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 2), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 3), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 4), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 5), NewMaybeRelocatableFelt(FeltFromUint64(2))) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Errorf("SQUASH_DICT hint should have failed") + } +} + +func TestSquashDictInvalidOneKeyDictBadPtrDiff(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "dict_accesses": {NewMaybeRelocatableRelocatable(NewRelocatable(2, 0))}, + "big_keys": {nil}, + "first_key": {nil}, + "ptr_diff": {NewMaybeRelocatableFelt(FeltFromUint64(7))}, + "n_accesses": {NewMaybeRelocatableFelt(FeltFromUint64(2))}, + }, + vm, + ) + // Insert dict into memory + // Dict = {(prime - 1): (1,1), (prime - 1): (1,2)} + vm.Segments.Memory.Insert(NewRelocatable(2, 0), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 1), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 2), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 3), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 4), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 5), NewMaybeRelocatableFelt(FeltFromUint64(2))) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Errorf("SQUASH_DICT hint should have failed") + } +} + +func TestSquashDictInvalidOneKeyDictNAccessesTooBig(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "dict_accesses": {NewMaybeRelocatableRelocatable(NewRelocatable(2, 0))}, + "big_keys": {nil}, + "first_key": {nil}, + "ptr_diff": {NewMaybeRelocatableFelt(FeltFromUint64(6))}, + "n_accesses": {NewMaybeRelocatableFelt(FeltFromDecString("-1"))}, + }, + vm, + ) + // Insert dict into memory + // Dict = {(prime - 1): (1,1), (prime - 1): (1,2)} + vm.Segments.Memory.Insert(NewRelocatable(2, 0), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 1), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 2), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 3), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 4), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 5), NewMaybeRelocatableFelt(FeltFromUint64(2))) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Errorf("SQUASH_DICT hint should have failed") + } +} + +func TestSquashDictSkipLoopTrue(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("current_access_indices", []int{}) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "should_skip_loop": {nil}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_SKIP_LOOP, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("SQUASH_DICT_INNER_SKIP_LOOP hint failed with error: %s", err) + } + // Check ids.skip_loop + skipLoop, err := idsManager.GetFelt("should_skip_loop", vm) + if err != nil || skipLoop != FeltOne() { + t.Errorf("SQUASH_DICT_INNER_SKIP_LOOP hint failed. Wrong/No ids.skip_loop") + } +} + +func TestSquashDictSkipLoopFalse(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("current_access_indices", []int{1, 2}) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "should_skip_loop": {nil}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_SKIP_LOOP, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("SQUASH_DICT_INNER_SKIP_LOOP hint failed with error: %s", err) + } + // Check ids.skip_loop + skipLoop, err := idsManager.GetFelt("should_skip_loop", vm) + if err != nil || skipLoop != FeltZero() { + t.Errorf("SQUASH_DICT_INNER_SKIP_LOOP hint failed. Wrong/No ids.skip_loop") + } +} + +func TestSquashDictInnerFirstIterationOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + range_check_ptr := vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("access_indices", map[MaybeRelocatable][]int{ + *NewMaybeRelocatableFelt(FeltFromUint64(5)): { + 9, 3, 10, 7, + }, + }) + scopes.AssignOrUpdateVariable("key", *NewMaybeRelocatableFelt(FeltFromUint64(5))) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "range_check_ptr": {NewMaybeRelocatableRelocatable(range_check_ptr)}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_FIRST_ITERATION, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("SQUASH_DICT_INNER_FIRST_ITERATION hint failed with error: %s", err) + } + // Check scope values + currentAccessIndicesAny, err := scopes.Get("current_access_indices") + currentAccessIndices := currentAccessIndicesAny.([]int) + expectedCurrentAccessIndices := []int{10, 9, 7} + if !reflect.DeepEqual(currentAccessIndices, expectedCurrentAccessIndices) { + t.Errorf("Wrong current_access_indices.\n Expected %v, got: %v", expectedCurrentAccessIndices, currentAccessIndices) + } + + currentAccessIndexAny, err := scopes.Get("current_access_index") + currentAccessIndex := currentAccessIndexAny.(int) + expectedCurrentAccessIndex := int(3) + if !reflect.DeepEqual(currentAccessIndex, expectedCurrentAccessIndex) { + t.Errorf("Wrong current_access_index.\n Expected %v, got: %v", expectedCurrentAccessIndex, currentAccessIndex) + } + + // Check memory[ids.range_check_ptr] + val, err := vm.Segments.Memory.Get(range_check_ptr) + if err != nil || *val != *NewMaybeRelocatableFelt(FeltFromUint64(3)) { + t.Errorf("Wrong/No value inserted into memory[ids.range_check_ptr]") + } +} + +func TestSquashDictInnerFirstIterationEmpty(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + range_check_ptr := vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("access_indices", map[MaybeRelocatable][]int{}) + scopes.AssignOrUpdateVariable("key", *NewMaybeRelocatableFelt(FeltFromUint64(5))) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "range_check_ptr": {NewMaybeRelocatableRelocatable(range_check_ptr)}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_FIRST_ITERATION, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Error("SQUASH_DICT_INNER_FIRST_ITERATION hint should have failed") + } +} + +func TestSquashDictInnerCheckAccessIndexOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("current_access_indices", []int{ + 10, 9, 7, 5, + }, + ) + scopes.AssignOrUpdateVariable("current_access_index", int(1)) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "loop_temps": {nil}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_CHECK_ACCESS_INDEX, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("SQUASH_DICT_INNER_CHECK_ACCESS_INDEX hint failed with error: %s", err) + } + // Check scope values + currentAccessIndicesAny, err := scopes.Get("current_access_indices") + currentAccessIndices := currentAccessIndicesAny.([]int) + expectedCurrentAccessIndices := []int{10, 9, 7} + if !reflect.DeepEqual(currentAccessIndices, expectedCurrentAccessIndices) { + t.Errorf("Wrong current_access_indices.\n Expected %v, got: %v", expectedCurrentAccessIndices, currentAccessIndices) + } + + currentAccessIndexAny, err := scopes.Get("current_access_index") + currentAccessIndex := currentAccessIndexAny.(int) + expectedCurrentAccessIndex := int(5) + if !reflect.DeepEqual(currentAccessIndex, expectedCurrentAccessIndex) { + t.Errorf("Wrong current_access_index.\n Expected %v, got: %v", expectedCurrentAccessIndex, currentAccessIndex) + } + + newAccessIndexAny, err := scopes.Get("new_access_index") + newAccessIndex := newAccessIndexAny.(int) + expectedNewAccessIndex := int(5) + if !reflect.DeepEqual(newAccessIndex, expectedNewAccessIndex) { + t.Errorf("Wrong new_access_index.\n Expected %v, got: %v", expectedNewAccessIndex, newAccessIndex) + } + + //Check loop_temps.index_delta_minus_1 + val, err := idsManager.GetFelt("loop_temps", vm) + if err != nil || val != FeltFromUint64(3) { + t.Errorf("Wrong/No value inserted into memory[ids.range_check_ptr]") + } +} + +func TestSquashDictInnerCheckAccessIndexEmpty(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("current_access_indices", []int{}) + scopes.AssignOrUpdateVariable("current_access_index", int(1)) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "loop_temps": {nil}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_CHECK_ACCESS_INDEX, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Errorf("SQUASH_DICT_INNER_CHECK_ACCESS_INDEX hint should have failed") + } +} + +func TestSquashDictContinueLoopTrue(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("current_access_indices", []int{1, 3}) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "loop_temps": {nil}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_CONTINUE_LOOP, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("SQUASH_DICT_INNER_CONTINUE_LOOP hint failed with error: %s", err) + } + // Check ids.loop_temps.should_continue + skipLoop, err := idsManager.GetStructFieldFelt("loop_temps", 3, vm) + if err != nil || skipLoop != FeltOne() { + t.Errorf("SQUASH_DICT_INNER_CONTINUE_LOOP hint failed. Wrong/No ids.loop_temps.should_continue") + } +} + +func TestSquashDictContinueLoopFalse(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("current_access_indices", []int{}) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "loop_temps": {nil}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_CONTINUE_LOOP, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("SQUASH_DICT_INNER_CONTINUE_LOOP hint failed with error: %s", err) + } + // Check ids.loop_temps.should_continue + continueLoop, err := idsManager.GetStructFieldFelt("loop_temps", 3, vm) + if err != nil || continueLoop != FeltZero() { + t.Errorf("SQUASH_DICT_INNER_CONTINUE_LOOP hint failed. Wrong/No ids.loop_temps.should_continue") + } +} + +func TestSquashDictInnerAssertLenKeysNotEmpty(t *testing.T) { + vm := NewVirtualMachine() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("keys", []MaybeRelocatable{*NewMaybeRelocatableFelt(FeltZero())}) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{}, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_ASSERT_LEN_KEYS, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Errorf("SQUASH_DICT_INNER_ASSERT_LEN_KEYS hint should have failed") + } +} + +func TestSquashDictInnerAssertLenKeysEmpty(t *testing.T) { + vm := NewVirtualMachine() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("keys", []MaybeRelocatable{}) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{}, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_ASSERT_LEN_KEYS, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("SQUASH_DICT_INNER_ASSERT_LEN_KEYS hint failed with error: %s", err) + } +} + +func TestSquashDictInnerLenAssertKeysNotEmpty(t *testing.T) { + vm := NewVirtualMachine() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("current_access_indices", []int{2, 3}) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{}, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_LEN_ASSERT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Errorf("SQUASH_DICT_INNER_LEN_ASSERT hint should have failed") + } +} + +func TestSquashDictInnerLenAssertEmpty(t *testing.T) { + vm := NewVirtualMachine() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("current_access_indices", []int{}) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{}, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_LEN_ASSERT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("SQUASH_DICT_INNER_LEN_ASSERT hint failed with error: %s", err) + } +} + +func TestSquashDictInnerUsedAccessesAssertOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("access_indices", map[MaybeRelocatable][]int{ + *NewMaybeRelocatableFelt(FeltZero()): {3}, + }) + scopes.AssignOrUpdateVariable("key", *NewMaybeRelocatableFelt(FeltZero())) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "n_used_accesses": {NewMaybeRelocatableFelt(FeltOne())}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_USED_ACCESSES_ASSERT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("SQUASH_DICT_INNER_USED_ACCESSES_ASSERT hint failed with error: %s", err) + } +} + +func TestSquashDictInnerUsedAccessesAssertBadLen(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("access_indices", map[MaybeRelocatable][]int{ + *NewMaybeRelocatableFelt(FeltZero()): {3, 5}, + }) + scopes.AssignOrUpdateVariable("key", *NewMaybeRelocatableFelt(FeltZero())) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "n_used_accesses": {NewMaybeRelocatableFelt(FeltOne())}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_USED_ACCESSES_ASSERT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Errorf("SQUASH_DICT_INNER_USED_ACCESSES_ASSERT hint should have failed") + } +} + +func TestSquashDictInnerUsedAccessesAssertBadKey(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("access_indices", map[MaybeRelocatable][]int{ + *NewMaybeRelocatableFelt(FeltZero()): {3}, + }) + scopes.AssignOrUpdateVariable("key", *NewMaybeRelocatableFelt(FeltOne())) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "n_used_accesses": {NewMaybeRelocatableFelt(FeltOne())}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_USED_ACCESSES_ASSERT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Errorf("SQUASH_DICT_INNER_USED_ACCESSES_ASSERT hint should have failed") + } +} + +func TestSquashDictInnerNextKeyOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("keys", []MaybeRelocatable{ + *NewMaybeRelocatableFelt(FeltFromUint64(5)), + *NewMaybeRelocatableFelt(FeltFromUint64(4)), + }) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "next_key": {nil}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_NEXT_KEY, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("SQUASH_DICT_INNER_NEXT_KEY hint failed with error: %s", err) + } + // Check scope values + keysAny, err := scopes.Get("keys") + keys := keysAny.([]MaybeRelocatable) + expectedkeys := []MaybeRelocatable{ + *NewMaybeRelocatableFelt(FeltFromUint64(5)), + } + if !reflect.DeepEqual(keys, expectedkeys) { + t.Errorf("Wrong keys.\n Expected %v, got: %v", expectedkeys, keys) + } + + keyAny, err := scopes.Get("key") + key := keyAny.(MaybeRelocatable) + expectedKey := *NewMaybeRelocatableFelt(FeltFromUint64(4)) + if !reflect.DeepEqual(key, expectedKey) { + t.Errorf("Wrong current_access_index.\n Expected %v, got: %v", expectedKey, key) + } + + // Check ids.next_key + val, err := idsManager.GetFelt("next_key", vm) + if err != nil || val != FeltFromUint64(4) { + t.Errorf("Wrong/No value inserted into ids.next_key") + } +} + +func TestSquashDictInnerNextKeyErr(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("keys", []MaybeRelocatable{}) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "next_key": {nil}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_NEXT_KEY, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Errorf("SQUASH_DICT_INNER_NEXT_KEY hint should have failed") + } +} diff --git a/pkg/lambdaworks/lib/lambdaworks.h b/pkg/lambdaworks/lib/lambdaworks.h index 573a8825..850c9809 100644 --- a/pkg/lambdaworks/lib/lambdaworks.h +++ b/pkg/lambdaworks/lib/lambdaworks.h @@ -94,9 +94,8 @@ void div_rem(felt_t a, felt_t b, felt_t div, felt_t rem); /* Compares x and y and returns: - - -1 if a < b - 0 if a == b - +1 if a > b + -1 if a < b + 0 if a == b + +1 if a > b */ int cmp(felt_t a, felt_t b); diff --git a/pkg/vm/cairo_run/cairo_run_test.go b/pkg/vm/cairo_run/cairo_run_test.go index 02e81d24..893a958f 100644 --- a/pkg/vm/cairo_run/cairo_run_test.go +++ b/pkg/vm/cairo_run/cairo_run_test.go @@ -210,6 +210,14 @@ func TestSqrtHint(t *testing.T) { } } +func TestSquashDict(t *testing.T) { + cairoRunConfig := cairo_run.CairoRunConfig{DisableTracePadding: false, Layout: "small", ProofMode: false} + _, err := cairo_run.CairoRun("../../../cairo_programs/squash_dict.json", cairoRunConfig) + if err != nil { + t.Errorf("Program execution failed with error: %s", err) + } +} + func TestAssert250BitHint(t *testing.T) { cairoRunConfig := cairo_run.CairoRunConfig{DisableTracePadding: false, Layout: "all_cairo", ProofMode: false} _, err := cairo_run.CairoRun("../../../cairo_programs/assert_250_bit_element_array.json", cairoRunConfig) @@ -218,6 +226,14 @@ func TestAssert250BitHint(t *testing.T) { } } +func TestDictSquash(t *testing.T) { + cairoRunConfig := cairo_run.CairoRunConfig{DisableTracePadding: false, Layout: "small", ProofMode: false} + _, err := cairo_run.CairoRun("../../../cairo_programs/dict_squash.json", cairoRunConfig) + if err != nil { + t.Errorf("Program execution failed with error: %s", err) + } +} + func TestSplitFeltHint(t *testing.T) { cairoRunConfig := cairo_run.CairoRunConfig{DisableTracePadding: false, Layout: "all_cairo", ProofMode: false} _, err := cairo_run.CairoRun("../../../cairo_programs/split_felt.json", cairoRunConfig)