diff --git a/pkg/hintrunner/hinter/dict.go b/pkg/hintrunner/hinter/dict.go index 7a03819be..c3e4ee006 100644 --- a/pkg/hintrunner/hinter/dict.go +++ b/pkg/hintrunner/hinter/dict.go @@ -16,6 +16,8 @@ type Dictionary struct { idx uint64 // Default value for key not present in the dictionary defaultValue *mem.MemoryValue + // first free offset in memory segment of dictionary + freeOffset uint64 } // Gets the memory value at certain key @@ -39,6 +41,11 @@ func (d *Dictionary) InitNumber() uint64 { return d.idx } +// Given a incrementBy value, it increments the freeOffset field of dictionary by it +func (d *Dictionary) IncrementFreeOffset(freeOffset uint64) { + d.freeOffset += freeOffset +} + // Used to manage dictionaries creation type DictionaryManager struct { // a map that links a segment index to a dictionary @@ -60,6 +67,7 @@ func (dm *DictionaryManager) NewDictionary(vm *VM.VirtualMachine) mem.MemoryAddr data: make(map[f.Element]*mem.MemoryValue), idx: uint64(len(dm.dictionaries)), defaultValue: nil, + freeOffset: 0, } return newDictAddr } @@ -74,6 +82,7 @@ func (dm *DictionaryManager) NewDefaultDictionary(vm *VM.VirtualMachine, default data: make(map[f.Element]*mem.MemoryValue), idx: uint64(len(dm.dictionaries)), defaultValue: defaultValue, + freeOffset: 0, } return newDefaultDictAddr } diff --git a/pkg/hintrunner/zero/hintcode.go b/pkg/hintrunner/zero/hintcode.go index 61fa64a6a..592287948 100644 --- a/pkg/hintrunner/zero/hintcode.go +++ b/pkg/hintrunner/zero/hintcode.go @@ -80,6 +80,7 @@ const ( // ------ Dictionaries hints related code ------ defaultDictNewCode string = "if '__dict_manager' not in globals():\n from starkware.cairo.common.dict import DictManager\n __dict_manager = DictManager()\n\nmemory[ap] = __dict_manager.new_default_dict(segments, ids.default_value)" + dictReadCode string = "dict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ndict_tracker.current_ptr += ids.DictAccess.SIZE\nids.value = dict_tracker.data[ids.key]" // ------ Other hints related code ------ allocSegmentCode string = "memory[ap] = segments.add()" diff --git a/pkg/hintrunner/zero/zerohint.go b/pkg/hintrunner/zero/zerohint.go index ebb7c50f9..9ac9beded 100644 --- a/pkg/hintrunner/zero/zerohint.go +++ b/pkg/hintrunner/zero/zerohint.go @@ -140,6 +140,8 @@ func GetHintFromCode(program *zero.ZeroProgram, rawHint zero.Hint, hintPC uint64 // Dictionary hints case defaultDictNewCode: return createDefaultDictNewHinter(resolver) + case dictReadCode: + return createDictReadHinter(resolver) // Other hints case allocSegmentCode: return createAllocSegmentHinter(resolver) diff --git a/pkg/hintrunner/zero/zerohint_dictionaries.go b/pkg/hintrunner/zero/zerohint_dictionaries.go index e1a246a77..e34be0f00 100644 --- a/pkg/hintrunner/zero/zerohint_dictionaries.go +++ b/pkg/hintrunner/zero/zerohint_dictionaries.go @@ -1,6 +1,8 @@ package zero import ( + "fmt" + "github.com/NethermindEth/cairo-vm-go/pkg/hintrunner/hinter" VM "github.com/NethermindEth/cairo-vm-go/pkg/vm" mem "github.com/NethermindEth/cairo-vm-go/pkg/vm/memory" @@ -50,3 +52,62 @@ func createDefaultDictNewHinter(resolver hintReferenceResolver) (hinter.Hinter, } return newDefaultDictNewHint(defaultValue), nil } + +func newDictReadHint(dictPtr, key, value hinter.ResOperander) hinter.Hinter { + return &GenericZeroHinter{ + Name: "DictRead", + Op: func(vm *VM.VirtualMachine, ctx *hinter.HintRunnerContext) error { + //> dict_tracker = __dict_manager.get_tracker(ids.dict_ptr) + //> dict_tracker.current_ptr += ids.DictAccess.SIZE + //> ids.value = dict_tracker.data[ids.key] + + //> dict_tracker = __dict_manager.get_tracker(ids.dict_ptr) + dictPtr, err := hinter.ResolveAsAddress(vm, dictPtr) + if err != nil { + return err + } + dictionaryManager, ok := ctx.ScopeManager.GetDictionaryManager() + if !ok { + return fmt.Errorf("__dict_manager not in scope") + } + dictionary, err := dictionaryManager.GetDictionary(dictPtr) + if err != nil { + return err + } + + //> dict_tracker.current_ptr += ids.DictAccess.SIZE + dictionary.IncrementFreeOffset(3) + + //> ids.value = dict_tracker.data[ids.key] + key, err := hinter.ResolveAsFelt(vm, key) + if err != nil { + return err + } + keyValue, err := dictionary.At(key) + if err != nil { + return err + } + valueAddr, err := value.GetAddress(vm) + if err != nil { + return err + } + return vm.Memory.WriteToAddress(&valueAddr, keyValue) + }, + } +} + +func createDictReadHinter(resolver hintReferenceResolver) (hinter.Hinter, error) { + dictPtr, err := resolver.GetResOperander("dict_ptr") + if err != nil { + return nil, err + } + key, err := resolver.GetResOperander("key") + if err != nil { + return nil, err + } + value, err := resolver.GetResOperander("value") + if err != nil { + return nil, err + } + return newDictReadHint(dictPtr, key, value), nil +} diff --git a/pkg/hintrunner/zero/zerohint_dictionaries_test.go b/pkg/hintrunner/zero/zerohint_dictionaries_test.go index 67ddd29b1..4f6140156 100644 --- a/pkg/hintrunner/zero/zerohint_dictionaries_test.go +++ b/pkg/hintrunner/zero/zerohint_dictionaries_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/NethermindEth/cairo-vm-go/pkg/hintrunner/hinter" + "github.com/NethermindEth/cairo-vm-go/pkg/vm/memory" "github.com/consensys/gnark-crypto/ecc/stark-curve/fp" ) @@ -50,5 +51,26 @@ func TestZeroHintDictionaries(t *testing.T) { }, }, }, + "DictRead": { + { + operanders: []*hintOperander{ + {Name: "dict_ptr", Kind: apRelative, Value: addrWithSegment(2, 0)}, + {Name: "key", Kind: apRelative, Value: feltUint64(100)}, + {Name: "value", Kind: uninitialized}, + }, + ctxInit: func(ctx *hinter.HintRunnerContext) { + hinter.InitializeDictionaryManager(ctx) + hinter.InitializeScopeManager(ctx, map[string]any{ + "__dict_manager": ctx.DictionaryManager, + }) + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + defaultValueMv := memory.MemoryValueFromInt(12345) + ctx.runnerContext.DictionaryManager.NewDefaultDictionary(ctx.vm, &defaultValueMv) + return newDictReadHint(ctx.operanders["dict_ptr"], ctx.operanders["key"], ctx.operanders["value"]) + }, + check: varValueEquals("value", feltUint64(12345)), + }, + }, }) }