From fae330d2872fdae9deb044c482bfe89d5c5df9d0 Mon Sep 17 00:00:00 2001
From: MaksymMalicki <81577596+MaksymMalicki@users.noreply.github.com>
Date: Tue, 14 Jan 2025 09:16:07 -0500
Subject: [PATCH] Missing dict functionalities (#680)

* Fixes for the generation of entry code, fixes of hints parsing

* Add modifications to the runner

* Add fixes for the entrycode generation

* Refactor main CLI, offset the hints indexes by entry code size, load arguments and initial gas to the memory

* Add available gas and user args (#677)

* Add parsing logic for input user args

* Add flags for available gas, input user args, writing args to memory

* Fix unit tests for user arguments parsing

* Lint the PR

* Add user args to hint context

* Refactor the code

* Fix unconditional append of ExternalWriteArgsToMemory, bug fixes in integration tests

* Add fixes of the call size calculation and include ExternalWriteArgsToMemory hint when gas present

* Add layouts for integration tests

* Add error handling

* Fixes in entry code generation

* Address changes mentioned in a discussion

* Add comment regarding writing to memory in a hint for the future reference in the integration tests with args

* Changes in calculations of the initial PC offset, CALL opcode offset incremented by mainFuncOffset, writing user args to the AP in the hint

* Turn back VM config to private field

* Add error handling on assign of `userArgs` to the initial scope

* Lint project

* Bump go version from 1.20 -> 1.21 (#678)

* Bump go version from 1.20 -> 1.21

* Update golangci-lint

* Simplify the Makefile

* Correction in the makefile

* Fix the integration tests

* Fixes in the runner

* Fixes in the runner

* Fix the unit tests, uncomment pythonVm execution in integration tests, code cleanups

* Add writing tokens gas cost to memory

* Proper builtins initialization for cairo mode

* Address comments in the PR

* Fix bugs regarding dicts

* Remove prints

* Fixes of the last tests for the dicts

* Add dict_non_squashed dir to the integration tests

* Address all the comments
---
 integration_tests/cairo_vm_test.go |  1 +
 pkg/hintrunner/core/hint.go        | 25 ++++++++++++------
 pkg/runner/gas.go                  |  4 +--
 pkg/runner/runner.go               | 42 ++++++++++++++++++++++++++----
 4 files changed, 57 insertions(+), 15 deletions(-)

diff --git a/integration_tests/cairo_vm_test.go b/integration_tests/cairo_vm_test.go
index 1ae4e157..d6822065 100644
--- a/integration_tests/cairo_vm_test.go
+++ b/integration_tests/cairo_vm_test.go
@@ -180,6 +180,7 @@ func TestCairoFiles(t *testing.T) {
 		{"./cairo_zero_file_tests/", true},
 		{"./builtin_tests/", true},
 		// {"./cairo_1_programs/", false},
+		// {"./cairo_1_programs/dict_non_squashed", false},
 	}
 
 	// filter is for debugging purposes
diff --git a/pkg/hintrunner/core/hint.go b/pkg/hintrunner/core/hint.go
index d1540ae0..6667335b 100644
--- a/pkg/hintrunner/core/hint.go
+++ b/pkg/hintrunner/core/hint.go
@@ -1143,7 +1143,8 @@ func (hint *Felt252DictEntryInit) Execute(vm *VM.VirtualMachine, ctx *hinter.Hin
 
 	prevValue, err := ctx.DictionaryManager.At(dictPtr, key)
 	if err != nil {
-		return fmt.Errorf("get dictionary entry: %w", err)
+		mv := mem.MemoryValueFromFieldElement(&utils.FeltZero)
+		prevValue = &mv
 	}
 	if prevValue == nil {
 		mv := mem.EmptyMemoryValueAsFelt()
@@ -1234,10 +1235,10 @@ func (hint *InitSquashData) String() string {
 }
 
 func (hint *InitSquashData) Execute(vm *VM.VirtualMachine, ctx *hinter.HintRunnerContext) error {
-	// todo(rodro): Don't know if it could be called multiple times, or
 	err := hinter.InitializeSquashedDictionaryManager(ctx)
 	if err != nil {
-		return err
+		ctx.SquashedDictionaryManager = hinter.SquashedDictionaryManager{}
+		_ = hinter.InitializeSquashedDictionaryManager(ctx)
 	}
 
 	dictAccessPtr, err := hinter.ResolveAsAddress(vm, hint.DictAccesses)
@@ -1272,7 +1273,7 @@ func (hint *InitSquashData) Execute(vm *VM.VirtualMachine, ctx *hinter.HintRunne
 
 	// sort the keys in descending order
 	sort.Slice(ctx.SquashedDictionaryManager.Keys, func(i, j int) bool {
-		return ctx.SquashedDictionaryManager.Keys[i].Cmp(&ctx.SquashedDictionaryManager.Keys[j]) < 0
+		return ctx.SquashedDictionaryManager.Keys[i].Cmp(&ctx.SquashedDictionaryManager.Keys[j]) > 0
 	})
 
 	// if the first key is bigger than 2^128, signal it
@@ -1401,11 +1402,15 @@ func (hint *ShouldContinueSquashLoop) Execute(vm *VM.VirtualMachine, ctx *hinter
 	}
 
 	var shouldContinueLoop f.Element
-	if lastIndices, err := ctx.SquashedDictionaryManager.LastIndices(); err == nil && len(lastIndices) <= 1 {
-		shouldContinueLoop.SetOne()
-	} else if err != nil {
+	lastIndices, err := ctx.SquashedDictionaryManager.LastIndices()
+	if err != nil {
 		return fmt.Errorf("get last indices: %w", err)
 	}
+	if len(lastIndices) > 1 {
+		shouldContinueLoop.SetOne()
+	} else {
+		shouldContinueLoop.SetZero()
+	}
 
 	mv := mem.MemoryValueFromFieldElement(&shouldContinueLoop)
 	return vm.Memory.WriteToAddress(&shouldContinuePtr, &mv)
@@ -1425,11 +1430,15 @@ func (hint *GetNextDictKey) Execute(vm *VM.VirtualMachine, ctx *hinter.HintRunne
 		return fmt.Errorf("get next key address: %w", err)
 	}
 
-	nextKey, err := ctx.SquashedDictionaryManager.PopKey()
+	_, err = ctx.SquashedDictionaryManager.PopKey()
 	if err != nil {
 		return fmt.Errorf("pop key: %w", err)
 	}
 
+	nextKey, err := ctx.SquashedDictionaryManager.LastKey()
+	if err != nil {
+		return fmt.Errorf("get last key: %w", err)
+	}
 	mv := mem.MemoryValueFromFieldElement(&nextKey)
 	return vm.Memory.WriteToAddress(&nextKeyAddr, &mv)
 }
diff --git a/pkg/runner/gas.go b/pkg/runner/gas.go
index 57446b8a..f825cd7a 100644
--- a/pkg/runner/gas.go
+++ b/pkg/runner/gas.go
@@ -50,8 +50,8 @@ func gasInitialization(memory *mem.Memory) error {
 	if err != nil {
 		return err
 	}
-
-	preCostTokenTypes := []TokenGasCost{PedersenToken, BitwiseToken, EcOpToken, PoseidonToken, AddModToken, MulModToken}
+	// The order of the tokens is relevant, source: https://github.com/starkware-libs/cairo/blob/f6aaaa306804257bfc15d65b5ab6b90e141b54ec/crates/cairo-lang-sierra/src/extensions/modules/gas.rs#L194
+	preCostTokenTypes := []TokenGasCost{PedersenToken, PoseidonToken, BitwiseToken, EcOpToken, AddModToken, MulModToken}
 
 	for _, token := range preCostTokenTypes {
 		cost, err := getTokenGasCost(token)
diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go
index b5cfa851..225f9266 100644
--- a/pkg/runner/runner.go
+++ b/pkg/runner/runner.go
@@ -541,13 +541,47 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo
 	}
 
 	ctx := &InlineCasmContext{}
+
+	gotSegmentArena := false
+	for _, builtin := range function.Builtins {
+		if builtin == builtins.SegmentArenaType {
+			gotSegmentArena = true
+		}
+	}
+
+	hints := make(map[uint64][]hinter.Hinter)
+
+	if gotSegmentArena {
+		hints[uint64(ctx.currentCodeOffset)] = []hinter.Hinter{
+			&core.AllocSegment{
+				Dst: hinter.ApCellRef(0),
+			},
+			&core.AllocSegment{
+				Dst: hinter.ApCellRef(1),
+			},
+		}
+		ctx.AddInlineCASM(
+			"[ap+2] = 0, ap++;",
+		)
+		ctx.AddInlineCASM(
+			"[ap] = [[ap-1]], ap++;",
+		)
+		ctx.AddInlineCASM(
+			`
+			[ap] = [[ap-2]+1], ap++;
+			[ap-1] = [[ap-3]+2];
+			`,
+		)
+		apOffset += 3
+	}
+
 	paramsSize := 0
 	for _, param := range paramTypes {
 		paramsSize += param.Size
 	}
 	apOffset += paramsSize
 	usedArgs := 0
-	var hints map[uint64][]hinter.Hinter
+
 	for _, builtin := range function.Builtins {
 		if offset, isBuiltin := builtinsOffsetsMap[builtin]; isBuiltin {
 			ctx.AddInlineCASM(
@@ -561,10 +595,8 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo
 			)
 			apOffset += 1
 		} else if builtin == builtins.GasBuiltinType {
-			hints = map[uint64][]hinter.Hinter{
-				uint64(ctx.currentCodeOffset): {
-					&core.ExternalWriteArgsToMemory{},
-				},
+			hints[uint64(ctx.currentCodeOffset)] = []hinter.Hinter{
+				&core.ExternalWriteArgsToMemory{},
 			}
 			ctx.AddInlineCASM("ap += 1;")
 			apOffset += 1