From 45da4d01b00f0e76795262ea78a8ef85e604fb76 Mon Sep 17 00:00:00 2001 From: Tristan <122918260+TAdev0@users.noreply.github.com> Date: Fri, 28 Jun 2024 08:09:36 +0200 Subject: [PATCH] `NondetElementsOverTwo` + `NondetElementsOverTen`( poseidon builtin related hints) (#493) * NondetElementsOverTwo + NondetElementsOverTen * fix hintcode * fix hintcodes and bugs * add unit tests * Update poseidon.starknet_with_keccak.cairo --------- Co-authored-by: Shourya Goel --- .../poseidon.starknet_with_keccak.cairo | 30 +++++++ pkg/hintrunner/zero/hintcode.go | 2 + pkg/hintrunner/zero/zerohint.go | 4 + pkg/hintrunner/zero/zerohint_others.go | 84 +++++++++++++++++++ pkg/hintrunner/zero/zerohint_others_test.go | 48 +++++++++++ 5 files changed, 168 insertions(+) create mode 100644 integration_tests/cairo_zero_hint_tests_in_progress/poseidon.starknet_with_keccak.cairo diff --git a/integration_tests/cairo_zero_hint_tests_in_progress/poseidon.starknet_with_keccak.cairo b/integration_tests/cairo_zero_hint_tests_in_progress/poseidon.starknet_with_keccak.cairo new file mode 100644 index 000000000..39cfa4987 --- /dev/null +++ b/integration_tests/cairo_zero_hint_tests_in_progress/poseidon.starknet_with_keccak.cairo @@ -0,0 +1,30 @@ +%builtins poseidon +from starkware.cairo.common.cairo_builtins import PoseidonBuiltin +from starkware.cairo.common.poseidon_state import PoseidonBuiltinState +from starkware.cairo.common.builtin_poseidon.poseidon import ( + poseidon_hash, + poseidon_hash_single, + poseidon_hash_many, +) +from starkware.cairo.common.alloc import alloc + +func main{poseidon_ptr: PoseidonBuiltin*}() { + // Hash one + let (x) = poseidon_hash_single( + 218676008889449692916464780911713710628115973574242889792891157041292792362 + ); + assert x = 2835120893146788752888137145656423078969524407843035783270702964188823073934; + // Hash two + let (y) = poseidon_hash(1253795, 18540013156130945068); + assert y = 37282360750367388068593128053386029947772104009544220786084510532118246655; + // Hash five + let felts: felt* = alloc(); + assert felts[0] = 84175983715088675913672849362079546; + assert felts[1] = 9384720329467203286234076408512594689579283578028960384690; + assert felts[2] = 291883989128409324823849293040390493094093; + assert felts[3] = 5849589438543859348593485948598349584395839402940940290490324; + assert felts[4] = 1836254780028456372728992049476335424263474849; + let (z) = poseidon_hash_many(5, felts); + assert z = 47102513329160951064697157194713013753695317629154835326726810042406974264; + return (); +} diff --git a/pkg/hintrunner/zero/hintcode.go b/pkg/hintrunner/zero/hintcode.go index 5e58d7550..3df68b47d 100644 --- a/pkg/hintrunner/zero/hintcode.go +++ b/pkg/hintrunner/zero/hintcode.go @@ -153,6 +153,8 @@ ids.multiplicities = segments.gen_arg([len(positions_dict[k]) for k in output])` vmEnterScopeCode string = "vm_enter_scope()" vmExitScopeCode string = "vm_exit_scope()" findElementCode string = "array_ptr = ids.array_ptr\nelm_size = ids.elm_size\nassert isinstance(elm_size, int) and elm_size > 0, \\\n f'Invalid value for elm_size. Got: {elm_size}.'\nkey = ids.key\n\nif '__find_element_index' in globals():\n ids.index = __find_element_index\n found_key = memory[array_ptr + elm_size * __find_element_index]\n assert found_key == key, \\\n f'Invalid index found in __find_element_index. index: {__find_element_index}, ' \\\n f'expected key {key}, found key: {found_key}.'\n # Delete __find_element_index to make sure it's not used for the next calls.\n del __find_element_index\nelse:\n n_elms = ids.n_elms\n assert isinstance(n_elms, int) and n_elms >= 0, \\\n f'Invalid value for n_elms. Got: {n_elms}.'\n if '__find_element_max_size' in globals():\n assert n_elms <= __find_element_max_size, \\\n f'find_element() can only be used with n_elms<={__find_element_max_size}. ' \\\n f'Got: n_elms={n_elms}.'\n\n for i in range(n_elms):\n if memory[array_ptr + elm_size * i] == key:\n ids.index = i\n break\n else:\n raise ValueError(f'Key {key} was not found.')" + nondetElementsOverTWoCode string = "memory[ap] = to_felt_or_relocatable(ids.n >= 2)" + nondetElementsOverTenCode string = "memory[ap] = to_felt_or_relocatable(ids.n >= 10)" setAddCode string = "assert ids.elm_size > 0\nassert ids.set_ptr <= ids.set_end_ptr\nelm_list = memory.get_range(ids.elm_ptr, ids.elm_size)\nfor i in range(0, ids.set_end_ptr - ids.set_ptr, ids.elm_size):\n if memory.get_range(ids.set_ptr + i, ids.elm_size) == elm_list:\n ids.index = i // ids.elm_size\n ids.is_elm_in_set = 1\n break\nelse:\n ids.is_elm_in_set = 0" searchSortedLowerCode string = `array_ptr = ids.array_ptr elm_size = ids.elm_size diff --git a/pkg/hintrunner/zero/zerohint.go b/pkg/hintrunner/zero/zerohint.go index ee4b4933a..34b84e8bb 100644 --- a/pkg/hintrunner/zero/zerohint.go +++ b/pkg/hintrunner/zero/zerohint.go @@ -236,6 +236,10 @@ func GetHintFromCode(program *zero.ZeroProgram, rawHint zero.Hint, hintPC uint64 return createTestAssignHinter(resolver) case findElementCode: return createFindElementHinter(resolver) + case nondetElementsOverTWoCode: + return createNondetElementsOverTWoHinter(resolver) + case nondetElementsOverTenCode: + return createNondetElementsOverTenHinter(resolver) default: return nil, fmt.Errorf("not identified hint") } diff --git a/pkg/hintrunner/zero/zerohint_others.go b/pkg/hintrunner/zero/zerohint_others.go index 74f335c3d..848a752ee 100644 --- a/pkg/hintrunner/zero/zerohint_others.go +++ b/pkg/hintrunner/zero/zerohint_others.go @@ -608,3 +608,87 @@ func createSearchSortedLowerHinter(resolver hintReferenceResolver) (hinter.Hinte return newSearchSortedLowerHint(arrayPtr, elmSize, nElms, key, index), nil } + +// NondetElementsOverTWo hint compares the offset difference between two memory address and +// writes 1 or 0 at `ap` memory address, depending on whether the difference is greater or +// equal to 2 or not +// +// `newNondetElementsOverTWoHint` takes 2 operanders as arguments +// - `elementsEnd` represents the address in memory right after the last element of the array +// - `elements` represents the address in memory of the first element of the array +func newNondetElementsOverTWoHint(n hinter.ResOperander) hinter.Hinter { + return &GenericZeroHinter{ + Name: "NondetElementsOverTWo", + Op: func(vm *VM.VirtualMachine, ctx *hinter.HintRunnerContext) error { + //> python hint in cairo file: "ids.elements_end - ids.elements >= 2" + //> python hint in whitelist: "memory[ap] = to_felt_or_relocatable(ids.elements_end - ids.elements >= 2)" + //> compiled file hint: "memory[ap] = to_felt_or_relocatable(ids.n >= 2)" + + n, err := hinter.ResolveAsUint64(vm, n) + if err != nil { + return err + } + + apAddr := vm.Context.AddressAp() + var resultMv memory.MemoryValue + if n >= uint64(2) { + resultMv = memory.MemoryValueFromFieldElement(&utils.FeltOne) + } else { + resultMv = memory.MemoryValueFromFieldElement(&utils.FeltZero) + } + + return vm.Memory.WriteToAddress(&apAddr, &resultMv) + }, + } +} + +func createNondetElementsOverTWoHinter(resolver hintReferenceResolver) (hinter.Hinter, error) { + n, err := resolver.GetResOperander("n") + if err != nil { + return nil, err + } + + return newNondetElementsOverTWoHint(n), nil +} + +// NondetElementsOverTen hint compares the offset difference between two memory address and +// writes 1 or 0 at `ap` memory address, depending on whether the difference is greater or +// esual to 10 or not +// +// `newNondetElementsOverTenHint` takes 2 operanders as arguments +// - `elementsEnd` represents the address in memory right after the last element of the array +// - `elements` represents the address in memory of the first element of the array +func newNondetElementsOverTenHint(n hinter.ResOperander) hinter.Hinter { + return &GenericZeroHinter{ + Name: "NondetElementsOverTen", + Op: func(vm *VM.VirtualMachine, ctx *hinter.HintRunnerContext) error { + //> python hint in cairo file: "ids.elements_end - ids.elements >= 10" + //> python hint in whitelist: "memory[ap] = to_felt_or_relocatable(ids.elements_end - ids.elements >= 10)" + //> compiled file hint: "memory[ap] = to_felt_or_relocatable(ids.n >= 10)" + + n, err := hinter.ResolveAsUint64(vm, n) + if err != nil { + return err + } + + apAddr := vm.Context.AddressAp() + var resultMv memory.MemoryValue + if n >= uint64(10) { + resultMv = memory.MemoryValueFromFieldElement(&utils.FeltOne) + } else { + resultMv = memory.MemoryValueFromFieldElement(&utils.FeltZero) + } + + return vm.Memory.WriteToAddress(&apAddr, &resultMv) + }, + } +} + +func createNondetElementsOverTenHinter(resolver hintReferenceResolver) (hinter.Hinter, error) { + n, err := resolver.GetResOperander("n") + if err != nil { + return nil, err + } + + return newNondetElementsOverTenHint(n), nil +} diff --git a/pkg/hintrunner/zero/zerohint_others_test.go b/pkg/hintrunner/zero/zerohint_others_test.go index 4ccc5164c..0a162bb6c 100644 --- a/pkg/hintrunner/zero/zerohint_others_test.go +++ b/pkg/hintrunner/zero/zerohint_others_test.go @@ -531,5 +531,53 @@ func TestZeroHintOthers(t *testing.T) { }), }, }, + "NondetElementsOverTwo": { + { + operanders: []*hintOperander{ + {Name: "n", Kind: apRelative, Value: feltUint64(1)}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newNondetElementsOverTWoHint( + ctx.operanders["n"], + ) + }, + check: apValueEquals(feltUint64(0)), + }, + { + operanders: []*hintOperander{ + {Name: "n", Kind: apRelative, Value: feltUint64(2)}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newNondetElementsOverTWoHint( + ctx.operanders["n"], + ) + }, + check: apValueEquals(feltUint64(1)), + }, + }, + "NondetElementsOverTen": { + { + operanders: []*hintOperander{ + {Name: "n", Kind: apRelative, Value: feltUint64(9)}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newNondetElementsOverTenHint( + ctx.operanders["n"], + ) + }, + check: apValueEquals(feltUint64(0)), + }, + { + operanders: []*hintOperander{ + {Name: "n", Kind: apRelative, Value: feltUint64(10)}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newNondetElementsOverTenHint( + ctx.operanders["n"], + ) + }, + check: apValueEquals(feltUint64(1)), + }, + }, }) }