From 27817ff6ccf8f8fef2b26be3b72ece39fb0cef21 Mon Sep 17 00:00:00 2001 From: Shourya Goel Date: Wed, 3 Jul 2024 18:09:49 +0530 Subject: [PATCH 1/8] Implemented `SplitOutputMidLowHigh` (#511) Implemented SplitOutputMidLowHigh --- pkg/hintrunner/zero/hintcode.go | 1 + pkg/hintrunner/zero/zerohint.go | 2 + pkg/hintrunner/zero/zerohint_keccak.go | 102 ++++++++++++++++++++ pkg/hintrunner/zero/zerohint_keccak_test.go | 98 +++++++++++++++++++ 4 files changed, 203 insertions(+) diff --git a/pkg/hintrunner/zero/hintcode.go b/pkg/hintrunner/zero/hintcode.go index c0c27b89..e697e2b1 100644 --- a/pkg/hintrunner/zero/hintcode.go +++ b/pkg/hintrunner/zero/hintcode.go @@ -127,6 +127,7 @@ ids.multiplicities = segments.gen_arg([len(positions_dict[k]) for k in output])` blockPermutationCode string = "from starkware.cairo.common.keccak_utils.keccak_utils import keccak_func\n_keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS)\nassert 0 <= _keccak_state_size_felts < 100\noutput_values = keccak_func(memory.get_range(\nids.keccak_ptr - _keccak_state_size_felts, _keccak_state_size_felts))\nsegments.write_arg(ids.keccak_ptr, output_values)" compareBytesInWordCode string = "memory[ap] = to_felt_or_relocatable(ids.n_bytes < ids.BYTES_IN_WORD)" compareKeccakFullRateInBytesCode string = "memory[ap] = to_felt_or_relocatable(ids.n_bytes >= ids.KECCAK_FULL_RATE_IN_BYTES)" + splitOutputMidLowHighCode string = "tmp, ids.output1_low = divmod(ids.output1, 256 ** 7)\nids.output1_high, ids.output1_mid = divmod(tmp, 2 ** 128)" // ------ Dictionaries hints related code ------ dictNewCode 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_dict(segments, initial_dict)\ndel initial_dict" diff --git a/pkg/hintrunner/zero/zerohint.go b/pkg/hintrunner/zero/zerohint.go index 4ec0f0b6..26ffd6ba 100644 --- a/pkg/hintrunner/zero/zerohint.go +++ b/pkg/hintrunner/zero/zerohint.go @@ -173,6 +173,8 @@ func GetHintFromCode(program *zero.ZeroProgram, rawHint zero.Hint, hintPC uint64 return createBlockPermutationHinter(resolver) case compareBytesInWordCode: return createCompareBytesInWordNondetHinter(resolver) + case splitOutputMidLowHighCode: + return createSplitOutputMidLowHighHinter(resolver) // Usort hints case usortEnterScopeCode: return createUsortEnterScopeHinter() diff --git a/pkg/hintrunner/zero/zerohint_keccak.go b/pkg/hintrunner/zero/zerohint_keccak.go index c5c099d8..4c27c642 100644 --- a/pkg/hintrunner/zero/zerohint_keccak.go +++ b/pkg/hintrunner/zero/zerohint_keccak.go @@ -3,6 +3,7 @@ package zero import ( "fmt" "math" + "math/big" "github.com/NethermindEth/cairo-vm-go/pkg/hintrunner/hinter" "github.com/NethermindEth/cairo-vm-go/pkg/utils" @@ -572,3 +573,104 @@ func createCompareBytesInWordNondetHinter(resolver hintReferenceResolver) (hinte return newCompareBytesInWordHint(nBytes), nil } + +// SplitOutputMidLowHigh hint assigns to `ids.output1_low` the remainder of the division +// of `ids.output1` variable by 256 ** 7 and uses its quotient as a variable which is +// divided by 2 ** 128, the quotient and remainder of which are then assigned to `ids.output1_high` +// and `ids.output1_mid` respectively. +// +// `newSplitOutputMidLowHighHint` takes 4 operanders as arguments +// - `output1Low` is the variable that will store the remainder of the first division +// - `output1Mid` is the variable that will store the remainder of the second division +// - `output1High` is the variable that will store the quotient of the second division +// - `output1` is the variable that will be divided in the first division +func newSplitOutputMidLowHighHint(output1, output1Low, output1Mid, output1High hinter.ResOperander) hinter.Hinter { + return &GenericZeroHinter{ + Name: "SplitOutputMidLowHigh", + Op: func(vm *VM.VirtualMachine, _ *hinter.HintRunnerContext) error { + //> tmp, ids.output1_low = divmod(ids.output1, 256 ** 7) + //> ids.output1_high, ids.output1_mid = divmod(tmp, 2 ** 128) + + output1LowAddr, err := output1Low.GetAddress(vm) + if err != nil { + return err + } + + output1MidAddr, err := output1Mid.GetAddress(vm) + if err != nil { + return err + } + + output1HighAddr, err := output1High.GetAddress(vm) + if err != nil { + return err + } + + output1Felt, err := hinter.ResolveAsFelt(vm, output1) + if err != nil { + return err + } + + output1BigInt := new(big.Int) + output1Felt.BigInt(output1BigInt) + + tmpBigInt := new(big.Int) + output1LowBigInt := new(big.Int) + output1MidBigInt := new(big.Int) + output1HighBigInt := new(big.Int) + + divisorOne := new(big.Int).Exp(big.NewInt(256), big.NewInt(7), nil) + divisorTwo := new(big.Int).Exp(big.NewInt(2), big.NewInt(128), nil) + + tmpBigInt.DivMod(output1BigInt, divisorOne, output1LowBigInt) + output1HighBigInt.DivMod(tmpBigInt, divisorTwo, output1MidBigInt) + + var output1LowFelt fp.Element + output1LowFelt.SetBigInt(output1LowBigInt) + output1LowMv := memory.MemoryValueFromFieldElement(&output1LowFelt) + + var output1MidFelt fp.Element + output1MidFelt.SetBigInt(output1MidBigInt) + output1MidMv := memory.MemoryValueFromFieldElement(&output1MidFelt) + + var output1HighFelt fp.Element + output1HighFelt.SetBigInt(output1HighBigInt) + output1HighMv := memory.MemoryValueFromFieldElement(&output1HighFelt) + + err = vm.Memory.WriteToAddress(&output1LowAddr, &output1LowMv) + if err != nil { + return err + } + + err = vm.Memory.WriteToAddress(&output1MidAddr, &output1MidMv) + if err != nil { + return err + } + return vm.Memory.WriteToAddress(&output1HighAddr, &output1HighMv) + }, + } +} + +func createSplitOutputMidLowHighHinter(resolver hintReferenceResolver) (hinter.Hinter, error) { + output1, err := resolver.GetResOperander("output1") + if err != nil { + return nil, err + } + + output1Low, err := resolver.GetResOperander("output1_low") + if err != nil { + return nil, err + } + + output1Mid, err := resolver.GetResOperander("output1_mid") + if err != nil { + return nil, err + } + + output1High, err := resolver.GetResOperander("output1_high") + if err != nil { + return nil, err + } + + return newSplitOutputMidLowHighHint(output1, output1Low, output1Mid, output1High), nil +} diff --git a/pkg/hintrunner/zero/zerohint_keccak_test.go b/pkg/hintrunner/zero/zerohint_keccak_test.go index 8617d0cb..03629ea3 100644 --- a/pkg/hintrunner/zero/zerohint_keccak_test.go +++ b/pkg/hintrunner/zero/zerohint_keccak_test.go @@ -605,5 +605,103 @@ func TestZeroHintKeccak(t *testing.T) { check: apValueEquals(feltUint64(0)), }, }, + "SplitOutputMidLowHigh": { + { + operanders: []*hintOperander{ + {Name: "output1", Kind: apRelative, Value: feltUint64(0)}, + {Name: "output1_low", Kind: uninitialized}, + {Name: "output1_mid", Kind: uninitialized}, + {Name: "output1_high", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitOutputMidLowHighHint(ctx.operanders["output1"], ctx.operanders["output1_low"], ctx.operanders["output1_mid"], ctx.operanders["output1_high"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"output1_low": feltUint64(0), "output1_mid": feltUint64(0), "output1_high": feltUint64(0)}), + }, + { + operanders: []*hintOperander{ + {Name: "output1", Kind: apRelative, Value: feltUint64(72365738)}, + {Name: "output1_low", Kind: uninitialized}, + {Name: "output1_mid", Kind: uninitialized}, + {Name: "output1_high", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitOutputMidLowHighHint(ctx.operanders["output1"], ctx.operanders["output1_low"], ctx.operanders["output1_mid"], ctx.operanders["output1_high"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"output1_low": feltUint64(72365738), "output1_mid": feltUint64(0), "output1_high": feltUint64(0)}), + }, + { + operanders: []*hintOperander{ + {Name: "output1", Kind: apRelative, Value: feltUint64(72057594037927936)}, + {Name: "output1_low", Kind: uninitialized}, + {Name: "output1_mid", Kind: uninitialized}, + {Name: "output1_high", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitOutputMidLowHighHint(ctx.operanders["output1"], ctx.operanders["output1_low"], ctx.operanders["output1_mid"], ctx.operanders["output1_high"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"output1_low": feltUint64(0), "output1_mid": feltUint64(1), "output1_high": feltUint64(0)}), + }, + { + operanders: []*hintOperander{ + {Name: "output1", Kind: apRelative, Value: feltString("24519928653854221733733552434404946937899825954937634816")}, + {Name: "output1_low", Kind: uninitialized}, + {Name: "output1_mid", Kind: uninitialized}, + {Name: "output1_high", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitOutputMidLowHighHint(ctx.operanders["output1"], ctx.operanders["output1_low"], ctx.operanders["output1_mid"], ctx.operanders["output1_high"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"output1_low": feltUint64(0), "output1_mid": feltUint64(0), "output1_high": feltUint64(1)}), + }, + { + operanders: []*hintOperander{ + {Name: "output1", Kind: apRelative, Value: feltString("24519928653854221733733552434404946940926244904530608128")}, + {Name: "output1_low", Kind: uninitialized}, + {Name: "output1_mid", Kind: uninitialized}, + {Name: "output1_high", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitOutputMidLowHighHint(ctx.operanders["output1"], ctx.operanders["output1_low"], ctx.operanders["output1_mid"], ctx.operanders["output1_high"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"output1_low": feltUint64(0), "output1_mid": feltUint64(42), "output1_high": feltUint64(1)}), + }, + { + operanders: []*hintOperander{ + {Name: "output1", Kind: apRelative, Value: feltString("894386062958165334425")}, + {Name: "output1_low", Kind: uninitialized}, + {Name: "output1_mid", Kind: uninitialized}, + {Name: "output1_high", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitOutputMidLowHighHint(ctx.operanders["output1"], ctx.operanders["output1_low"], ctx.operanders["output1_mid"], ctx.operanders["output1_high"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"output1_low": feltUint64(7205759403792793), "output1_mid": feltUint64(12412), "output1_high": feltUint64(0)}), + }, + { + operanders: []*hintOperander{ + {Name: "output1", Kind: apRelative, Value: feltString("24519928653854221733733552434404946937899825956147353057")}, + {Name: "output1_low", Kind: uninitialized}, + {Name: "output1_mid", Kind: uninitialized}, + {Name: "output1_high", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitOutputMidLowHighHint(ctx.operanders["output1"], ctx.operanders["output1_low"], ctx.operanders["output1_mid"], ctx.operanders["output1_high"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"output1_low": feltUint64(1209718241), "output1_mid": feltUint64(0), "output1_high": feltUint64(1)}), + }, + { + operanders: []*hintOperander{ + {Name: "output1", Kind: apRelative, Value: feltString("24519928653854221733733552434404946940926244943286781240")}, + {Name: "output1_low", Kind: uninitialized}, + {Name: "output1_mid", Kind: uninitialized}, + {Name: "output1_high", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitOutputMidLowHighHint(ctx.operanders["output1"], ctx.operanders["output1_low"], ctx.operanders["output1_mid"], ctx.operanders["output1_high"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"output1_low": feltUint64(38756173112), "output1_mid": feltUint64(42), "output1_high": feltUint64(1)}), + }, + }, }) } From 00f020586995ca72acc7da4b10ce27eb419d5a23 Mon Sep 17 00:00:00 2001 From: Shourya Goel Date: Wed, 3 Jul 2024 18:22:45 +0530 Subject: [PATCH 2/8] `SplitNBytesCode` hint (#510) * Implemented hint * Bug Fix * Added some more tests * nit * Resolved merge conflict --- pkg/hintrunner/zero/hintcode.go | 1 + pkg/hintrunner/zero/zerohint.go | 2 + pkg/hintrunner/zero/zerohint_keccak.go | 83 ++++++++++++++++++++- pkg/hintrunner/zero/zerohint_keccak_test.go | 57 ++++++++++++++ 4 files changed, 139 insertions(+), 4 deletions(-) diff --git a/pkg/hintrunner/zero/hintcode.go b/pkg/hintrunner/zero/hintcode.go index e697e2b1..34808f0c 100644 --- a/pkg/hintrunner/zero/hintcode.go +++ b/pkg/hintrunner/zero/hintcode.go @@ -128,6 +128,7 @@ ids.multiplicities = segments.gen_arg([len(positions_dict[k]) for k in output])` compareBytesInWordCode string = "memory[ap] = to_felt_or_relocatable(ids.n_bytes < ids.BYTES_IN_WORD)" compareKeccakFullRateInBytesCode string = "memory[ap] = to_felt_or_relocatable(ids.n_bytes >= ids.KECCAK_FULL_RATE_IN_BYTES)" splitOutputMidLowHighCode string = "tmp, ids.output1_low = divmod(ids.output1, 256 ** 7)\nids.output1_high, ids.output1_mid = divmod(tmp, 2 ** 128)" + SplitNBytesCode string = "ids.n_words_to_copy, ids.n_bytes_left = divmod(ids.n_bytes, ids.BYTES_IN_WORD)" // ------ Dictionaries hints related code ------ dictNewCode 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_dict(segments, initial_dict)\ndel initial_dict" diff --git a/pkg/hintrunner/zero/zerohint.go b/pkg/hintrunner/zero/zerohint.go index 26ffd6ba..f4fdc214 100644 --- a/pkg/hintrunner/zero/zerohint.go +++ b/pkg/hintrunner/zero/zerohint.go @@ -175,6 +175,8 @@ func GetHintFromCode(program *zero.ZeroProgram, rawHint zero.Hint, hintPC uint64 return createCompareBytesInWordNondetHinter(resolver) case splitOutputMidLowHighCode: return createSplitOutputMidLowHighHinter(resolver) + case SplitNBytesCode: + return createSplitNBytesHinter(resolver) // Usort hints case usortEnterScopeCode: return createUsortEnterScopeHinter() diff --git a/pkg/hintrunner/zero/zerohint_keccak.go b/pkg/hintrunner/zero/zerohint_keccak.go index 4c27c642..782adc8e 100644 --- a/pkg/hintrunner/zero/zerohint_keccak.go +++ b/pkg/hintrunner/zero/zerohint_keccak.go @@ -317,9 +317,8 @@ func createUnsafeKeccakFinalizeHinter(resolver hintReferenceResolver) (hinter.Hi // - `low` is the low part of the `uint256` argument for the Keccac function // - `high` is the high part of the `uint256` argument for the Keccac function func newKeccakWriteArgsHint(inputs, low, high hinter.ResOperander) hinter.Hinter { - name := "KeccakWriteArgs" return &GenericZeroHinter{ - Name: name, + Name: "KeccakWriteArgs", Op: func(vm *VM.VirtualMachine, _ *hinter.HintRunnerContext) error { //> segments.write_arg(ids.inputs, [ids.low % 2 ** 64, ids.low // 2 ** 64]) //> segments.write_arg(ids.inputs + 2, [ids.high % 2 ** 64, ids.high // 2 ** 64]) @@ -460,9 +459,8 @@ func createCompareKeccakFullRateInBytesNondetHinter(resolver hintReferenceResolv // `newBlockPermutationHint` reads 25 memory cells starting from `keccakPtr - 25`, and writes // the result of the Keccak block permutation in the next 25 memory cells, starting from `keccakPtr` func newBlockPermutationHint(keccakPtr hinter.ResOperander) hinter.Hinter { - name := "BlockPermutation" return &GenericZeroHinter{ - Name: name, + Name: "BlockPermutation", Op: func(vm *VM.VirtualMachine, _ *hinter.HintRunnerContext) error { //> from starkware.cairo.common.keccak_utils.keccak_utils import keccak_func //> _keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS) @@ -674,3 +672,80 @@ func createSplitOutputMidLowHighHinter(resolver hintReferenceResolver) (hinter.H return newSplitOutputMidLowHighHint(output1, output1Low, output1Mid, output1High), nil } + +// SplitNBytes hint assigns to `ids.n_words_to_copy` and `ids.n_bytes_left` variables +// the quotient and remainder of the division of `ids.n_bytes` variable by the +// variable `ids.BYTES_IN_WORD` +// +// `newSplitNBytesHint` takes 3 operanders as arguments +// - `nWordsToCopy` is the variable that will store the quotient of the division +// - `nBytesLeft` is the variable that will store the remainder of the division +// - `nBytes` is the variable that will be divided +func newSplitNBytesHint(nBytes, nWordsToCopy, nBytesLeft hinter.ResOperander) hinter.Hinter { + name := "SplitNBytes" + return &GenericZeroHinter{ + Name: name, + Op: func(vm *VM.VirtualMachine, _ *hinter.HintRunnerContext) error { + //> ids.n_words_to_copy, ids.n_bytes_left = divmod(ids.n_bytes, ids.BYTES_IN_WORD) + + nWordsToCopyAddr, err := nWordsToCopy.GetAddress(vm) + if err != nil { + return err + } + + nBytesLeftAddr, err := nBytesLeft.GetAddress(vm) + if err != nil { + return err + } + + nBytesFelt, err := hinter.ResolveAsFelt(vm, nBytes) + if err != nil { + return err + } + + nBytesBigInt := new(big.Int) + nBytesFelt.BigInt(nBytesBigInt) + + bytesInWord := big.NewInt(8) + + nWordsToCopyBigInt := new(big.Int) + nBytesLeftBigInt := new(big.Int) + + nWordsToCopyBigInt.DivMod(nBytesBigInt, bytesInWord, nBytesLeftBigInt) + + var nWordsToCopyFelt fp.Element + nWordsToCopyFelt.SetBigInt(nWordsToCopyBigInt) + nWordsToCopyMv := memory.MemoryValueFromFieldElement(&nWordsToCopyFelt) + + var nBytesLeftFelt fp.Element + nBytesLeftFelt.SetBigInt(nBytesLeftBigInt) + nBytesLeftMv := memory.MemoryValueFromFieldElement(&nBytesLeftFelt) + + err = vm.Memory.WriteToAddress(&nBytesLeftAddr, &nBytesLeftMv) + if err != nil { + return err + } + + return vm.Memory.WriteToAddress(&nWordsToCopyAddr, &nWordsToCopyMv) + }, + } +} + +func createSplitNBytesHinter(resolver hintReferenceResolver) (hinter.Hinter, error) { + nBytes, err := resolver.GetResOperander("n_bytes") + if err != nil { + return nil, err + } + + nWordsToCopy, err := resolver.GetResOperander("n_words_to_copy") + if err != nil { + return nil, err + } + + nBytesLeft, err := resolver.GetResOperander("n_bytes_left") + if err != nil { + return nil, err + } + + return newSplitNBytesHint(nBytes, nWordsToCopy, nBytesLeft), nil +} diff --git a/pkg/hintrunner/zero/zerohint_keccak_test.go b/pkg/hintrunner/zero/zerohint_keccak_test.go index 03629ea3..0db02368 100644 --- a/pkg/hintrunner/zero/zerohint_keccak_test.go +++ b/pkg/hintrunner/zero/zerohint_keccak_test.go @@ -703,5 +703,62 @@ func TestZeroHintKeccak(t *testing.T) { check: allVarValueEquals(map[string]*fp.Element{"output1_low": feltUint64(38756173112), "output1_mid": feltUint64(42), "output1_high": feltUint64(1)}), }, }, + "SplitNBytes": { + { + operanders: []*hintOperander{ + {Name: "n_bytes", Kind: apRelative, Value: feltUint64(0)}, + {Name: "n_words_to_copy", Kind: uninitialized}, + {Name: "n_bytes_left", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitNBytesHint(ctx.operanders["n_bytes"], ctx.operanders["n_words_to_copy"], ctx.operanders["n_bytes_left"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"n_words_to_copy": feltUint64(0), "n_bytes_left": feltUint64(0)}), + }, + { + operanders: []*hintOperander{ + {Name: "n_bytes", Kind: apRelative, Value: feltUint64(45)}, + {Name: "n_words_to_copy", Kind: uninitialized}, + {Name: "n_bytes_left", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitNBytesHint(ctx.operanders["n_bytes"], ctx.operanders["n_words_to_copy"], ctx.operanders["n_bytes_left"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"n_words_to_copy": feltUint64(5), "n_bytes_left": feltUint64(5)}), + }, + { + operanders: []*hintOperander{ + {Name: "n_bytes", Kind: apRelative, Value: feltUint64(80)}, + {Name: "n_words_to_copy", Kind: uninitialized}, + {Name: "n_bytes_left", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitNBytesHint(ctx.operanders["n_bytes"], ctx.operanders["n_words_to_copy"], ctx.operanders["n_bytes_left"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"n_words_to_copy": feltUint64(10), "n_bytes_left": feltUint64(0)}), + }, + { + operanders: []*hintOperander{ + {Name: "n_bytes", Kind: apRelative, Value: feltUint64(7)}, + {Name: "n_words_to_copy", Kind: uninitialized}, + {Name: "n_bytes_left", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitNBytesHint(ctx.operanders["n_bytes"], ctx.operanders["n_words_to_copy"], ctx.operanders["n_bytes_left"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"n_words_to_copy": feltUint64(0), "n_bytes_left": feltUint64(7)}), + }, + { + operanders: []*hintOperander{ + {Name: "n_bytes", Kind: apRelative, Value: feltUint64(7523672657695691)}, + {Name: "n_words_to_copy", Kind: uninitialized}, + {Name: "n_bytes_left", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitNBytesHint(ctx.operanders["n_bytes"], ctx.operanders["n_words_to_copy"], ctx.operanders["n_bytes_left"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"n_words_to_copy": feltUint64(940459082211961), "n_bytes_left": feltUint64(3)}), + }, + }, }) } From 60deaa164344e5ec9a66ecfad38dfee9d993da02 Mon Sep 17 00:00:00 2001 From: Tristan <122918260+TAdev0@users.noreply.github.com> Date: Wed, 3 Jul 2024 14:59:29 +0200 Subject: [PATCH 3/8] typo comment (#512) correct curve name for ReduceV1 Co-authored-by: Shourya Goel --- pkg/hintrunner/zero/zerohint_ec.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/hintrunner/zero/zerohint_ec.go b/pkg/hintrunner/zero/zerohint_ec.go index 73469efe..3424ccd3 100644 --- a/pkg/hintrunner/zero/zerohint_ec.go +++ b/pkg/hintrunner/zero/zerohint_ec.go @@ -381,7 +381,7 @@ func createEcDoubleSlopeV1Hinter(resolver hintReferenceResolver) (hinter.Hinter, return newEcDoubleSlopeV1Hint(point), nil } -// ReduceV1 hint reduces a packed value modulo the SECP256R1 prime +// ReduceV1 hint reduces a packed value modulo the SECP256K1 prime // // `newReduceV1Hint` takes 1 operander as argument // - `x` is the packed value to be reduced From a86cb4bafa028db736461ab83129fbedb8e0ca65 Mon Sep 17 00:00:00 2001 From: Shourya Goel Date: Wed, 3 Jul 2024 19:54:56 +0530 Subject: [PATCH 4/8] Implement `RecoverY` hint (#506) * bug fix * Implemented the hint * Added test for recoverY * Added more tests * Fix * Update hintcode.go * Update zerohint.go * nit * Refactored code * nit * Cleaned the code * Modified IsQuadResidue to use helper function * Update math_utils.go --- pkg/hintrunner/utils/math_utils.go | 39 +++++++++++- pkg/hintrunner/zero/hintcode.go | 1 + pkg/hintrunner/zero/zerohint.go | 2 + pkg/hintrunner/zero/zerohint_ec.go | 83 +++++++++++++++++++++++++ pkg/hintrunner/zero/zerohint_ec_test.go | 80 ++++++++++++++++++++++++ pkg/hintrunner/zero/zerohint_math.go | 42 +++++-------- pkg/vm/builtins/ecdsa.go | 2 +- 7 files changed, 218 insertions(+), 31 deletions(-) diff --git a/pkg/hintrunner/utils/math_utils.go b/pkg/hintrunner/utils/math_utils.go index 6a112587..9b789b6f 100644 --- a/pkg/hintrunner/utils/math_utils.go +++ b/pkg/hintrunner/utils/math_utils.go @@ -129,10 +129,45 @@ func sign(n *big.Int) (int, big.Int) { func SafeDiv(x, y *big.Int) (big.Int, error) { if y.Cmp(big.NewInt(0)) == 0 { - return *big.NewInt(0), fmt.Errorf("Division by zero.") + return *big.NewInt(0), fmt.Errorf("division by zero") } if new(big.Int).Mod(x, y).Cmp(big.NewInt(0)) != 0 { - return *big.NewInt(0), fmt.Errorf("%v is not divisible by %v.", x, y) + return *big.NewInt(0), fmt.Errorf("%v is not divisible by %v", x, y) } return *new(big.Int).Div(x, y), nil } + +func IsQuadResidue(x *fp.Element) bool { + // Implementation adapted from sympy implementation which can be found here : + // https://github.com/sympy/sympy/blob/d91b8ad6d36a59a879cc70e5f4b379da5fdd46ce/sympy/ntheory/residue_ntheory.py#L689 + // We have omitted the prime as it will be CAIRO_PRIME + + return x.IsZero() || x.IsOne() || x.Legendre() == 1 +} + +func YSquaredFromX(x, beta, fieldPrime *big.Int) *big.Int { + // Computes y^2 using the curve equation: + // y^2 = x^3 + alpha * x + beta (mod field_prime) + // We ignore alpha as it is a constant with a value of 1 + + ySquaredBigInt := new(big.Int).Set(x) + ySquaredBigInt.Mul(ySquaredBigInt, x).Mod(ySquaredBigInt, fieldPrime) + ySquaredBigInt.Mul(ySquaredBigInt, x).Mod(ySquaredBigInt, fieldPrime) + ySquaredBigInt.Add(ySquaredBigInt, x).Mod(ySquaredBigInt, fieldPrime) + ySquaredBigInt.Add(ySquaredBigInt, beta).Mod(ySquaredBigInt, fieldPrime) + + return ySquaredBigInt +} + +func Sqrt(x, p *big.Int) *big.Int { + // Finds the minimum non-negative integer m such that (m*m) % p == x. + + halfPrimeBigInt := new(big.Int).Rsh(p, 1) + m := new(big.Int).ModSqrt(x, p) + + if m.Cmp(halfPrimeBigInt) > 0 { + m.Sub(p, m) + } + + return m +} diff --git a/pkg/hintrunner/zero/hintcode.go b/pkg/hintrunner/zero/hintcode.go index 34808f0c..c3d7e2c0 100644 --- a/pkg/hintrunner/zero/hintcode.go +++ b/pkg/hintrunner/zero/hintcode.go @@ -97,6 +97,7 @@ ids.multiplicities = segments.gen_arg([len(positions_dict[k]) for k in output])` isZeroNondetCode string = "memory[ap] = to_felt_or_relocatable(x == 0)" isZeroPackCode string = "from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack\n\nx = pack(ids.x, PRIME) % SECP_P" isZeroDivModCode string = "from starkware.cairo.common.cairo_secp.secp_utils import SECP_P\nfrom starkware.python.math_utils import div_mod\n\nvalue = x_inv = div_mod(1, x, SECP_P)" + recoverYCode string = "from starkware.crypto.signature.signature import ALPHA, BETA, FIELD_PRIME\nfrom starkware.python.math_utils import recover_y\nids.p.x = ids.x\n# This raises an exception if `x` is not on the curve.\nids.p.y = recover_y(ids.x, ALPHA, BETA, FIELD_PRIME)" // ------ Signature hints related code ------ verifyECDSASignatureCode string = "ecdsa_builtin.add_signature(ids.ecdsa_ptr.address_, (ids.signature_r, ids.signature_s))" diff --git a/pkg/hintrunner/zero/zerohint.go b/pkg/hintrunner/zero/zerohint.go index f4fdc214..51c08dd8 100644 --- a/pkg/hintrunner/zero/zerohint.go +++ b/pkg/hintrunner/zero/zerohint.go @@ -149,6 +149,8 @@ func GetHintFromCode(program *zero.ZeroProgram, rawHint zero.Hint, hintPC uint64 return createIsZeroPackHinter(resolver) case isZeroDivModCode: return createIsZeroDivModHinter() + case recoverYCode: + return createRecoverYHinter(resolver) // Blake hints case blake2sAddUint256BigendCode: return createBlake2sAddUint256Hinter(resolver, true) diff --git a/pkg/hintrunner/zero/zerohint_ec.go b/pkg/hintrunner/zero/zerohint_ec.go index 3424ccd3..02838a13 100644 --- a/pkg/hintrunner/zero/zerohint_ec.go +++ b/pkg/hintrunner/zero/zerohint_ec.go @@ -862,3 +862,86 @@ func newIsZeroDivModHint() hinter.Hinter { func createIsZeroDivModHinter() (hinter.Hinter, error) { return newIsZeroDivModHint(), nil } + +// RecoverY hint Recovers the y coordinate of a point on the elliptic curve +// y^2 = x^3 + alpha * x + beta (mod field_prime) of a given x coordinate. +// +// `newRecoverYHint` takes 2 operanders as arguments +// - `x` is the x coordinate of an elliptic curve point +// - `p` is one of the two EC points with the given x coordinate (x, y) +func newRecoverYHint(x, p hinter.ResOperander) hinter.Hinter { + return &GenericZeroHinter{ + Name: "RecoverY", + Op: func(vm *VM.VirtualMachine, ctx *hinter.HintRunnerContext) error { + //> from starkware.crypto.signature.signature import ALPHA, BETA, FIELD_PRIME + //> from starkware.python.math_utils import recover_y + //> ids.p.x = ids.x + //> # This raises an exception if `x` is not on the curve. + //> ids.p.y = recover_y(ids.x, ALPHA, BETA, FIELD_PRIME) + + pXAddr, err := p.GetAddress(vm) + if err != nil { + return err + } + + pYAddr, err := pXAddr.AddOffset(1) + if err != nil { + return err + } + + xFelt, err := hinter.ResolveAsFelt(vm, x) + if err != nil { + return err + } + + valueX := mem.MemoryValueFromFieldElement(xFelt) + + err = vm.Memory.WriteToAddress(&pXAddr, &valueX) + if err != nil { + return err + } + + const betaString = "3141592653589793238462643383279502884197169399375105820974944592307816406665" + betaBigInt, ok := new(big.Int).SetString(betaString, 10) + if !ok { + panic("failed to convert BETA string to big.Int") + } + + const fieldPrimeString = "3618502788666131213697322783095070105623107215331596699973092056135872020481" + fieldPrimeBigInt, ok := new(big.Int).SetString(fieldPrimeString, 10) + if !ok { + panic("failed to convert FIELD_PRIME string to big.Int") + } + + xBigInt := new(big.Int) + xFelt.BigInt(xBigInt) + + // y^2 = x^3 + alpha * x + beta (mod field_prime) + ySquaredBigInt := secp_utils.YSquaredFromX(xBigInt, betaBigInt, fieldPrimeBigInt) + ySquaredFelt := new(fp.Element).SetBigInt(ySquaredBigInt) + + if secp_utils.IsQuadResidue(ySquaredFelt) { + result := new(fp.Element).SetBigInt(secp_utils.Sqrt(ySquaredBigInt, fieldPrimeBigInt)) + value := mem.MemoryValueFromFieldElement(result) + return vm.Memory.WriteToAddress(&pYAddr, &value) + } else { + ySquaredString := ySquaredBigInt.String() + return fmt.Errorf("%s does not represent the x coordinate of a point on the curve", ySquaredString) + } + }, + } +} + +func createRecoverYHinter(resolver hintReferenceResolver) (hinter.Hinter, error) { + x, err := resolver.GetResOperander("x") + if err != nil { + return nil, err + } + + p, err := resolver.GetResOperander("p") + if err != nil { + return nil, err + } + + return newRecoverYHint(x, p), nil +} diff --git a/pkg/hintrunner/zero/zerohint_ec_test.go b/pkg/hintrunner/zero/zerohint_ec_test.go index 5e5abb76..07f366c8 100644 --- a/pkg/hintrunner/zero/zerohint_ec_test.go +++ b/pkg/hintrunner/zero/zerohint_ec_test.go @@ -974,6 +974,86 @@ func TestZeroHintEc(t *testing.T) { check: varValueInScopeEquals("value", bigIntString("4", 10)), }, }, + "RecoverY": { + { + operanders: []*hintOperander{ + {Name: "x", Kind: apRelative, Value: feltString("2497468900767850684421727063357792717599762502387246235265616708902555305129")}, + {Name: "p.x", Kind: uninitialized}, + {Name: "p.y", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newRecoverYHint(ctx.operanders["x"], ctx.operanders["p.x"]) + }, + check: allVarValueEquals(map[string]*fp.Element{ + "p.x": feltString("2497468900767850684421727063357792717599762502387246235265616708902555305129"), + "p.y": feltString("205857351767627712295703269674687767888261140702556021834663354704341414042"), + }), + }, + { + operanders: []*hintOperander{ + {Name: "x", Kind: apRelative, Value: feltString("205857351767627712295703269674687767888261140702556021834663354704341414042")}, + {Name: "p.x", Kind: uninitialized}, + {Name: "p.y", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newRecoverYHint(ctx.operanders["x"], ctx.operanders["p.x"]) + }, + errCheck: errorTextContains("does not represent the x coordinate of a point on the curve"), + }, + { + operanders: []*hintOperander{ + {Name: "x", Kind: apRelative, Value: feltString("3004956058830981475544150447242655232275382685012344776588097793621230049020")}, + {Name: "p.x", Kind: uninitialized}, + {Name: "p.y", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newRecoverYHint(ctx.operanders["x"], ctx.operanders["p.x"]) + }, + check: allVarValueEquals(map[string]*fp.Element{ + "p.x": feltString("3004956058830981475544150447242655232275382685012344776588097793621230049020"), + "p.y": feltString("386236054595386575795345623791920124827519018828430310912260655089307618738"), + }), + }, + { + operanders: []*hintOperander{ + {Name: "x", Kind: apRelative, Value: feltString("138597138396302485058562442936200017709939129389766076747102238692717075504")}, + {Name: "p.x", Kind: uninitialized}, + {Name: "p.y", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newRecoverYHint(ctx.operanders["x"], ctx.operanders["p.x"]) + }, + check: allVarValueEquals(map[string]*fp.Element{ + "p.x": feltString("138597138396302485058562442936200017709939129389766076747102238692717075504"), + "p.y": feltString("1116947097676727397390632683964789044871379304271794004325353078455954290524"), + }), + }, + { + operanders: []*hintOperander{ + {Name: "x", Kind: apRelative, Value: feltString("71635783675677659163985681365816684268526846280467284682674852685628658265882465826464572245")}, + {Name: "p.x", Kind: uninitialized}, + {Name: "p.y", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newRecoverYHint(ctx.operanders["x"], ctx.operanders["p.x"]) + }, + check: allVarValueEquals(map[string]*fp.Element{ + "p.x": feltString("71635783675677659163985681365816684268526846280467284682674852685628658265882465826464572245"), + "p.y": feltString("903372048565605391120071143811887302063650776015287438589675702929494830362"), + }), + }, + { + operanders: []*hintOperander{ + {Name: "x", Kind: apRelative, Value: feltString("42424242424242424242")}, + {Name: "p.x", Kind: uninitialized}, + {Name: "p.y", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newRecoverYHint(ctx.operanders["x"], ctx.operanders["p.x"]) + }, + errCheck: errorTextContains("does not represent the x coordinate of a point on the curve"), + }, + }, }, ) } diff --git a/pkg/hintrunner/zero/zerohint_math.go b/pkg/hintrunner/zero/zerohint_math.go index 058edcff..3817411b 100644 --- a/pkg/hintrunner/zero/zerohint_math.go +++ b/pkg/hintrunner/zero/zerohint_math.go @@ -1152,41 +1152,27 @@ func newIsQuadResidueHint(x, y hinter.ResOperander) hinter.Hinter { xBigInt := math_utils.AsInt(x) var value = memory.MemoryValue{} + var result *fp.Element = new(fp.Element) - if x.IsZero() || x.IsOne() { - value = memory.MemoryValueFromFieldElement(x) + const primeString = "3618502788666131213697322783095070105623107215331596699973092056135872020481" + primeBigInt, ok := new(big.Int).SetString(primeString, 10) + if !ok { + panic("failed to convert prime string to big.Int") + } + if math_utils.IsQuadResidue(x) { + result.SetBigInt(math_utils.Sqrt(&xBigInt, primeBigInt)) } else { - var result *fp.Element = new(fp.Element) - - if x.Legendre() == 1 { - // result = x.Sqrt(x) - - const primeString = "3618502788666131213697322783095070105623107215331596699973092056135872020481" - primeBigInt, ok := new(big.Int).SetString(primeString, 10) - if !ok { - panic("failed to convert prime string to big.Int") - } - - // divide primeBigInt by 2 - halfPrimeBigInt := new(big.Int).Rsh(primeBigInt, 1) - - tempResult := new(big.Int).ModSqrt(&xBigInt, primeBigInt) - - // ensures that tempResult is the smaller of the two possible square roots in the prime field. - if tempResult.Cmp(halfPrimeBigInt) > 0 { - tempResult.Sub(primeBigInt, tempResult) - } - - result.SetBigInt(tempResult) - - } else { - result = x.Sqrt(new(fp.Element).Div(x, new(fp.Element).SetUint64(3))) + y, err := math_utils.Divmod(&xBigInt, big.NewInt(3), primeBigInt) + if err != nil { + return err } - value = memory.MemoryValueFromFieldElement(result) + result.SetBigInt(math_utils.Sqrt(&y, primeBigInt)) } + value = memory.MemoryValueFromFieldElement(result) + return vm.Memory.WriteToAddress(&yAddr, &value) }, } diff --git a/pkg/vm/builtins/ecdsa.go b/pkg/vm/builtins/ecdsa.go index 8a1f6686..9b11e6f2 100644 --- a/pkg/vm/builtins/ecdsa.go +++ b/pkg/vm/builtins/ecdsa.go @@ -60,7 +60,7 @@ func (e *ECDSA) CheckWrite(segment *memory.Segment, offset uint64, value *memory pubKey := &ecdsa.PublicKey{A: key} sig, ok := e.signatures[pubOffset] if !ok { - return fmt.Errorf("signature is missing form ECDA builtin") + return fmt.Errorf("signature is missing from ECDSA builtin") } msgBytes := msgField.Bytes() From 9c163339a7863d78c18b4d96d471ef601dff4650 Mon Sep 17 00:00:00 2001 From: Tristan <122918260+TAdev0@users.noreply.github.com> Date: Thu, 4 Jul 2024 09:38:04 +0200 Subject: [PATCH 5/8] `SplitInput3` + `SplitInput6` + `SplitInput9` hints (#507) * SplitInput3 * SplitInt6 * SplitInt9 * fix * add unit tests * complete unit tests * fmt * fmt * fmt --- pkg/hintrunner/zero/hintcode.go | 3 + pkg/hintrunner/zero/zerohint.go | 6 + pkg/hintrunner/zero/zerohint_keccak.go | 260 ++++++++++++++++++++ pkg/hintrunner/zero/zerohint_keccak_test.go | 231 +++++++++++++++++ 4 files changed, 500 insertions(+) diff --git a/pkg/hintrunner/zero/hintcode.go b/pkg/hintrunner/zero/hintcode.go index c3d7e2c0..d08fd4c8 100644 --- a/pkg/hintrunner/zero/hintcode.go +++ b/pkg/hintrunner/zero/hintcode.go @@ -128,6 +128,9 @@ ids.multiplicities = segments.gen_arg([len(positions_dict[k]) for k in output])` blockPermutationCode string = "from starkware.cairo.common.keccak_utils.keccak_utils import keccak_func\n_keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS)\nassert 0 <= _keccak_state_size_felts < 100\noutput_values = keccak_func(memory.get_range(\nids.keccak_ptr - _keccak_state_size_felts, _keccak_state_size_felts))\nsegments.write_arg(ids.keccak_ptr, output_values)" compareBytesInWordCode string = "memory[ap] = to_felt_or_relocatable(ids.n_bytes < ids.BYTES_IN_WORD)" compareKeccakFullRateInBytesCode string = "memory[ap] = to_felt_or_relocatable(ids.n_bytes >= ids.KECCAK_FULL_RATE_IN_BYTES)" + splintInput3Code string = "ids.high3, ids.low3 = divmod(memory[ids.inputs + 3], 256)" + splintInput6Code string = "ids.high6, ids.low6 = divmod(memory[ids.inputs + 6], 256 ** 2)" + splintInput9Code string = "ids.high9, ids.low9 = divmod(memory[ids.inputs + 9], 256 ** 3)" splitOutputMidLowHighCode string = "tmp, ids.output1_low = divmod(ids.output1, 256 ** 7)\nids.output1_high, ids.output1_mid = divmod(tmp, 2 ** 128)" SplitNBytesCode string = "ids.n_words_to_copy, ids.n_bytes_left = divmod(ids.n_bytes, ids.BYTES_IN_WORD)" diff --git a/pkg/hintrunner/zero/zerohint.go b/pkg/hintrunner/zero/zerohint.go index 51c08dd8..92554c5b 100644 --- a/pkg/hintrunner/zero/zerohint.go +++ b/pkg/hintrunner/zero/zerohint.go @@ -175,6 +175,12 @@ func GetHintFromCode(program *zero.ZeroProgram, rawHint zero.Hint, hintPC uint64 return createBlockPermutationHinter(resolver) case compareBytesInWordCode: return createCompareBytesInWordNondetHinter(resolver) + case splintInput3Code: + return createSplitInput3Hinter(resolver) + case splintInput6Code: + return createSplitInput6Hinter(resolver) + case splintInput9Code: + return createSplitInput9Hinter(resolver) case splitOutputMidLowHighCode: return createSplitOutputMidLowHighHinter(resolver) case SplitNBytesCode: diff --git a/pkg/hintrunner/zero/zerohint_keccak.go b/pkg/hintrunner/zero/zerohint_keccak.go index 782adc8e..f72dc956 100644 --- a/pkg/hintrunner/zero/zerohint_keccak.go +++ b/pkg/hintrunner/zero/zerohint_keccak.go @@ -749,3 +749,263 @@ func createSplitNBytesHinter(resolver hintReferenceResolver) (hinter.Hinter, err return newSplitNBytesHint(nBytes, nWordsToCopy, nBytesLeft), nil } + +// SplitInput3 hint assigns to `ids.high3` and `ids.low3` variables +// the quotient and remainder of the division of the value at memory address +// `ids.inputs + 3` by 256 +// +// `newSplitInput3Hint` takes 3 operanders as arguments +// - `high3` is the variable that will store the quotient of the division +// - `low3` is the variable that will store the remainder of the division +// - `inputs` is the address in memory to which we add an offset of 3 and read that value +func newSplitInput3Hint(high3, low3, inputs hinter.ResOperander) hinter.Hinter { + return &GenericZeroHinter{ + Name: "SplitInput3", + Op: func(vm *VM.VirtualMachine, _ *hinter.HintRunnerContext) error { + //> ids.high3, ids.low3 = divmod(memory[ids.inputs + 3], 256) + + high3Addr, err := high3.GetAddress(vm) + if err != nil { + return err + } + + low3Addr, err := low3.GetAddress(vm) + if err != nil { + return err + } + + inputsAddr, err := hinter.ResolveAsAddress(vm, inputs) + if err != nil { + return err + } + + *inputsAddr, err = inputsAddr.AddOffset(3) + if err != nil { + return err + } + + inputValue, err := vm.Memory.ReadFromAddress(inputsAddr) + if err != nil { + return err + } + + var inputBigInt big.Int + inputValue.Felt.BigInt(&inputBigInt) + + divisor := big.NewInt(256) + + high3BigInt := new(big.Int) + low3BigInt := new(big.Int) + + high3BigInt.DivMod(&inputBigInt, divisor, low3BigInt) + + var high3Felt fp.Element + high3Felt.SetBigInt(high3BigInt) + high3Mv := memory.MemoryValueFromFieldElement(&high3Felt) + + var low3Felt fp.Element + low3Felt.SetBigInt(low3BigInt) + low3Mv := memory.MemoryValueFromFieldElement(&low3Felt) + + err = vm.Memory.WriteToAddress(&low3Addr, &low3Mv) + if err != nil { + return err + } + + return vm.Memory.WriteToAddress(&high3Addr, &high3Mv) + }, + } +} + +func createSplitInput3Hinter(resolver hintReferenceResolver) (hinter.Hinter, error) { + high3, err := resolver.GetResOperander("high3") + if err != nil { + return nil, err + } + + low3, err := resolver.GetResOperander("low3") + if err != nil { + return nil, err + } + + inputs, err := resolver.GetResOperander("inputs") + if err != nil { + return nil, err + } + + return newSplitInput3Hint(high3, low3, inputs), nil +} + +// SplitInput6 hint assigns to `ids.high6` and `ids.low6` variables +// the quotient and remainder of the division of the value at memory address +// `ids.inputs + 6` by 256 ** 2 +// +// `newSplitInput6Hint` takes 3 operanders as arguments +// - `high6` is the variable that will store the quotient of the division +// - `low6` is the variable that will store the remainder of the division +// - `inputs` is the address in memory to which we add an offset of 6 and read that value +func newSplitInput6Hint(high6, low6, inputs hinter.ResOperander) hinter.Hinter { + return &GenericZeroHinter{ + Name: "SplitInput6", + Op: func(vm *VM.VirtualMachine, _ *hinter.HintRunnerContext) error { + //> ids.high6, ids.low6 = divmod(memory[ids.inputs + 6], 256 ** 2) + + high6Addr, err := high6.GetAddress(vm) + if err != nil { + return err + } + + low6Addr, err := low6.GetAddress(vm) + if err != nil { + return err + } + + inputsAddr, err := hinter.ResolveAsAddress(vm, inputs) + if err != nil { + return err + } + + *inputsAddr, err = inputsAddr.AddOffset(6) + if err != nil { + return err + } + + inputValue, err := vm.Memory.ReadFromAddress(inputsAddr) + if err != nil { + return err + } + + var inputBigInt big.Int + inputValue.Felt.BigInt(&inputBigInt) + + // 256 ** 2 + divisor := big.NewInt(65536) + + high6BigInt := new(big.Int) + low6BigInt := new(big.Int) + + high6BigInt.DivMod(&inputBigInt, divisor, low6BigInt) + + var high6Felt fp.Element + high6Felt.SetBigInt(high6BigInt) + high6Mv := memory.MemoryValueFromFieldElement(&high6Felt) + + var low6Felt fp.Element + low6Felt.SetBigInt(low6BigInt) + low6Mv := memory.MemoryValueFromFieldElement(&low6Felt) + + err = vm.Memory.WriteToAddress(&low6Addr, &low6Mv) + if err != nil { + return err + } + + return vm.Memory.WriteToAddress(&high6Addr, &high6Mv) + }, + } +} + +func createSplitInput6Hinter(resolver hintReferenceResolver) (hinter.Hinter, error) { + high6, err := resolver.GetResOperander("high6") + if err != nil { + return nil, err + } + + low6, err := resolver.GetResOperander("low6") + if err != nil { + return nil, err + } + + inputs, err := resolver.GetResOperander("inputs") + if err != nil { + return nil, err + } + + return newSplitInput6Hint(high6, low6, inputs), nil +} + +// SplitInput9 hint assigns to `ids.high9` and `ids.low9` variables +// the quotient and remainder of the division of the value at memory address +// `ids.inputs + 9` by 256 ** 3 +// +// `newSplitInput9Hint` takes 3 operanders as arguments +// - `high9` is the variable that will store the quotient of the division +// - `low9` is the variable that will store the remainder of the division +// - `inputs` is the address in memory to which we add an offset of 9 and read that value +func newSplitInput9Hint(high9, low9, inputs hinter.ResOperander) hinter.Hinter { + return &GenericZeroHinter{ + Name: "SplitInput9", + Op: func(vm *VM.VirtualMachine, _ *hinter.HintRunnerContext) error { + //> ids.high9, ids.low9 = divmod(memory[ids.inputs + 9], 256 ** 3) + + high9Addr, err := high9.GetAddress(vm) + if err != nil { + return err + } + + low9Addr, err := low9.GetAddress(vm) + if err != nil { + return err + } + + inputsAddr, err := hinter.ResolveAsAddress(vm, inputs) + if err != nil { + return err + } + + *inputsAddr, err = inputsAddr.AddOffset(9) + if err != nil { + return err + } + + inputValue, err := vm.Memory.ReadFromAddress(inputsAddr) + if err != nil { + return err + } + + var inputBigInt big.Int + inputValue.Felt.BigInt(&inputBigInt) + + // 256 ** 3 + divisor := big.NewInt(16777216) + + high9BigInt := new(big.Int) + low9BigInt := new(big.Int) + + high9BigInt.DivMod(&inputBigInt, divisor, low9BigInt) + + var high9Felt fp.Element + high9Felt.SetBigInt(high9BigInt) + high9Mv := memory.MemoryValueFromFieldElement(&high9Felt) + + var low9Felt fp.Element + low9Felt.SetBigInt(low9BigInt) + low9Mv := memory.MemoryValueFromFieldElement(&low9Felt) + + err = vm.Memory.WriteToAddress(&low9Addr, &low9Mv) + if err != nil { + return err + } + + return vm.Memory.WriteToAddress(&high9Addr, &high9Mv) + }, + } +} + +func createSplitInput9Hinter(resolver hintReferenceResolver) (hinter.Hinter, error) { + high9, err := resolver.GetResOperander("high9") + if err != nil { + return nil, err + } + + low9, err := resolver.GetResOperander("low9") + if err != nil { + return nil, err + } + + inputs, err := resolver.GetResOperander("inputs") + if err != nil { + return nil, err + } + + return newSplitInput9Hint(high9, low9, inputs), nil +} diff --git a/pkg/hintrunner/zero/zerohint_keccak_test.go b/pkg/hintrunner/zero/zerohint_keccak_test.go index 0db02368..b03d5ed3 100644 --- a/pkg/hintrunner/zero/zerohint_keccak_test.go +++ b/pkg/hintrunner/zero/zerohint_keccak_test.go @@ -760,5 +760,236 @@ func TestZeroHintKeccak(t *testing.T) { check: allVarValueEquals(map[string]*fp.Element{"n_words_to_copy": feltUint64(940459082211961), "n_bytes_left": feltUint64(3)}), }, }, + "SplitInput3": { + { + operanders: []*hintOperander{ + {Name: "inputs", Kind: fpRelative, Value: addr(2)}, + {Name: "real_input", Kind: apRelative, Value: feltUint64(0)}, + {Name: "high3", Kind: uninitialized}, + {Name: "low3", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitInput3Hint(ctx.operanders["high3"], ctx.operanders["low3"], ctx.operanders["inputs"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"high3": feltUint64(0), "low3": feltUint64(0)}), + }, + { + operanders: []*hintOperander{ + {Name: "inputs", Kind: fpRelative, Value: addr(2)}, + {Name: "real_input", Kind: apRelative, Value: feltUint64(100)}, + {Name: "high3", Kind: uninitialized}, + {Name: "low3", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitInput3Hint(ctx.operanders["high3"], ctx.operanders["low3"], ctx.operanders["inputs"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"high3": feltUint64(0), "low3": feltUint64(100)}), + }, + { + operanders: []*hintOperander{ + {Name: "inputs", Kind: fpRelative, Value: addr(2)}, + {Name: "real_input", Kind: apRelative, Value: feltUint64(256)}, + {Name: "high3", Kind: uninitialized}, + {Name: "low3", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitInput3Hint(ctx.operanders["high3"], ctx.operanders["low3"], ctx.operanders["inputs"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"high3": feltUint64(1), "low3": feltUint64(0)}), + }, + { + operanders: []*hintOperander{ + {Name: "inputs", Kind: fpRelative, Value: addr(2)}, + {Name: "real_input", Kind: apRelative, Value: feltUint64(257)}, + {Name: "high3", Kind: uninitialized}, + {Name: "low3", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitInput3Hint(ctx.operanders["high3"], ctx.operanders["low3"], ctx.operanders["inputs"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"high3": feltUint64(1), "low3": feltUint64(1)}), + }, + { + operanders: []*hintOperander{ + {Name: "inputs", Kind: fpRelative, Value: addr(2)}, + {Name: "real_input", Kind: apRelative, Value: feltUint64(2561)}, + {Name: "high3", Kind: uninitialized}, + {Name: "low3", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitInput3Hint(ctx.operanders["high3"], ctx.operanders["low3"], ctx.operanders["inputs"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"high3": feltUint64(10), "low3": feltUint64(1)}), + }, + }, + "SplitInput6": { + { + operanders: []*hintOperander{ + {Name: "inputs", Kind: fpRelative, Value: addr(2)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "real_input", Kind: apRelative, Value: feltUint64(0)}, + {Name: "high6", Kind: uninitialized}, + {Name: "low6", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitInput6Hint(ctx.operanders["high6"], ctx.operanders["low6"], ctx.operanders["inputs"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"high6": feltUint64(0), "low6": feltUint64(0)}), + }, + { + operanders: []*hintOperander{ + {Name: "inputs", Kind: fpRelative, Value: addr(2)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "real_input", Kind: apRelative, Value: feltUint64(100)}, + {Name: "high6", Kind: uninitialized}, + {Name: "low6", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitInput6Hint(ctx.operanders["high6"], ctx.operanders["low6"], ctx.operanders["inputs"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"high6": feltUint64(0), "low6": feltUint64(100)}), + }, + { + operanders: []*hintOperander{ + {Name: "inputs", Kind: fpRelative, Value: addr(2)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "real_input", Kind: apRelative, Value: feltUint64(65536)}, + {Name: "high6", Kind: uninitialized}, + {Name: "low6", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitInput6Hint(ctx.operanders["high6"], ctx.operanders["low6"], ctx.operanders["inputs"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"high6": feltUint64(1), "low6": feltUint64(0)}), + }, + { + operanders: []*hintOperander{ + {Name: "inputs", Kind: fpRelative, Value: addr(2)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "real_input", Kind: apRelative, Value: feltUint64(65537)}, + {Name: "high6", Kind: uninitialized}, + {Name: "low6", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitInput6Hint(ctx.operanders["high6"], ctx.operanders["low6"], ctx.operanders["inputs"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"high6": feltUint64(1), "low6": feltUint64(1)}), + }, + { + operanders: []*hintOperander{ + {Name: "inputs", Kind: fpRelative, Value: addr(2)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "real_input", Kind: apRelative, Value: feltUint64(655361)}, + {Name: "high6", Kind: uninitialized}, + {Name: "low6", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitInput6Hint(ctx.operanders["high6"], ctx.operanders["low6"], ctx.operanders["inputs"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"high6": feltUint64(10), "low6": feltUint64(1)}), + }, + }, + "SplitInput9": { + { + operanders: []*hintOperander{ + {Name: "inputs", Kind: fpRelative, Value: addr(2)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "real_input", Kind: apRelative, Value: feltUint64(0)}, + {Name: "high9", Kind: uninitialized}, + {Name: "low9", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitInput9Hint(ctx.operanders["high9"], ctx.operanders["low9"], ctx.operanders["inputs"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"high9": feltUint64(0), "low9": feltUint64(0)}), + }, + { + operanders: []*hintOperander{ + {Name: "inputs", Kind: fpRelative, Value: addr(2)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "real_input", Kind: apRelative, Value: feltUint64(100)}, + {Name: "high9", Kind: uninitialized}, + {Name: "low9", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitInput9Hint(ctx.operanders["high9"], ctx.operanders["low9"], ctx.operanders["inputs"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"high9": feltUint64(0), "low9": feltUint64(100)}), + }, + { + operanders: []*hintOperander{ + {Name: "inputs", Kind: fpRelative, Value: addr(2)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "real_input", Kind: apRelative, Value: feltUint64(16777216)}, + {Name: "high9", Kind: uninitialized}, + {Name: "low9", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitInput9Hint(ctx.operanders["high9"], ctx.operanders["low9"], ctx.operanders["inputs"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"high9": feltUint64(1), "low9": feltUint64(0)}), + }, + { + operanders: []*hintOperander{ + {Name: "inputs", Kind: fpRelative, Value: addr(2)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "real_input", Kind: apRelative, Value: feltUint64(16777217)}, + {Name: "high9", Kind: uninitialized}, + {Name: "low9", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitInput9Hint(ctx.operanders["high9"], ctx.operanders["low9"], ctx.operanders["inputs"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"high9": feltUint64(1), "low9": feltUint64(1)}), + }, + { + operanders: []*hintOperander{ + {Name: "inputs", Kind: fpRelative, Value: addr(2)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "real_input", Kind: apRelative, Value: feltUint64(167772161)}, + {Name: "high9", Kind: uninitialized}, + {Name: "low9", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitInput9Hint(ctx.operanders["high9"], ctx.operanders["low9"], ctx.operanders["inputs"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"high9": feltUint64(10), "low9": feltUint64(1)}), + }, + }, }) } From 6cab0efa705272f018797cfadd6e3c2eca2f5670 Mon Sep 17 00:00:00 2001 From: Tristan <122918260+TAdev0@users.noreply.github.com> Date: Thu, 4 Jul 2024 10:40:23 +0200 Subject: [PATCH 6/8] `SplitInput12` + `SplitInput15` hints (#508) * SplitInput12 + SplitInput15 * fmt * fmt * fmt --- pkg/hintrunner/zero/hintcode.go | 9 +- pkg/hintrunner/zero/zerohint.go | 10 +- pkg/hintrunner/zero/zerohint_keccak.go | 174 +++++++++++++++ pkg/hintrunner/zero/zerohint_keccak_test.go | 229 ++++++++++++++++++++ 4 files changed, 416 insertions(+), 6 deletions(-) diff --git a/pkg/hintrunner/zero/hintcode.go b/pkg/hintrunner/zero/hintcode.go index d08fd4c8..9169660f 100644 --- a/pkg/hintrunner/zero/hintcode.go +++ b/pkg/hintrunner/zero/hintcode.go @@ -50,6 +50,7 @@ const ( // is_quad_residue() hint isQuadResidueCode string = "from starkware.crypto.signature.signature import FIELD_PRIME\nfrom starkware.python.math_utils import div_mod, is_quad_residue, sqrt\n\nx = ids.x\nif is_quad_residue(x, FIELD_PRIME):\n ids.y = sqrt(x, FIELD_PRIME)\nelse:\n ids.y = sqrt(div_mod(x, 3, FIELD_PRIME), FIELD_PRIME)" + // ------ Uint256 hints related code ------ uint256AddCode string = "sum_low = ids.a.low + ids.b.low\nids.carry_low = 1 if sum_low >= ids.SHIFT else 0\nsum_high = ids.a.high + ids.b.high + ids.carry_low\nids.carry_high = 1 if sum_high >= ids.SHIFT else 0" split64Code string = "ids.low = ids.a & ((1<<64) - 1)\nids.high = ids.a >> 64" @@ -128,9 +129,11 @@ ids.multiplicities = segments.gen_arg([len(positions_dict[k]) for k in output])` blockPermutationCode string = "from starkware.cairo.common.keccak_utils.keccak_utils import keccak_func\n_keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS)\nassert 0 <= _keccak_state_size_felts < 100\noutput_values = keccak_func(memory.get_range(\nids.keccak_ptr - _keccak_state_size_felts, _keccak_state_size_felts))\nsegments.write_arg(ids.keccak_ptr, output_values)" compareBytesInWordCode string = "memory[ap] = to_felt_or_relocatable(ids.n_bytes < ids.BYTES_IN_WORD)" compareKeccakFullRateInBytesCode string = "memory[ap] = to_felt_or_relocatable(ids.n_bytes >= ids.KECCAK_FULL_RATE_IN_BYTES)" - splintInput3Code string = "ids.high3, ids.low3 = divmod(memory[ids.inputs + 3], 256)" - splintInput6Code string = "ids.high6, ids.low6 = divmod(memory[ids.inputs + 6], 256 ** 2)" - splintInput9Code string = "ids.high9, ids.low9 = divmod(memory[ids.inputs + 9], 256 ** 3)" + splitInput3Code string = "ids.high3, ids.low3 = divmod(memory[ids.inputs + 3], 256)" + splitInput6Code string = "ids.high6, ids.low6 = divmod(memory[ids.inputs + 6], 256 ** 2)" + splitInput9Code string = "ids.high9, ids.low9 = divmod(memory[ids.inputs + 9], 256 ** 3)" + splitInput12Code string = "ids.high12, ids.low12 = divmod(memory[ids.inputs + 12], 256 ** 4)" + splitInput15Code string = "ids.high15, ids.low15 = divmod(memory[ids.inputs + 15], 256 ** 5)" splitOutputMidLowHighCode string = "tmp, ids.output1_low = divmod(ids.output1, 256 ** 7)\nids.output1_high, ids.output1_mid = divmod(tmp, 2 ** 128)" SplitNBytesCode string = "ids.n_words_to_copy, ids.n_bytes_left = divmod(ids.n_bytes, ids.BYTES_IN_WORD)" diff --git a/pkg/hintrunner/zero/zerohint.go b/pkg/hintrunner/zero/zerohint.go index 92554c5b..89a157a4 100644 --- a/pkg/hintrunner/zero/zerohint.go +++ b/pkg/hintrunner/zero/zerohint.go @@ -175,12 +175,16 @@ func GetHintFromCode(program *zero.ZeroProgram, rawHint zero.Hint, hintPC uint64 return createBlockPermutationHinter(resolver) case compareBytesInWordCode: return createCompareBytesInWordNondetHinter(resolver) - case splintInput3Code: + case splitInput3Code: return createSplitInput3Hinter(resolver) - case splintInput6Code: + case splitInput6Code: return createSplitInput6Hinter(resolver) - case splintInput9Code: + case splitInput9Code: return createSplitInput9Hinter(resolver) + case splitInput12Code: + return createSplitInput12Hinter(resolver) + case splitInput15Code: + return createSplitInput15Hinter(resolver) case splitOutputMidLowHighCode: return createSplitOutputMidLowHighHinter(resolver) case SplitNBytesCode: diff --git a/pkg/hintrunner/zero/zerohint_keccak.go b/pkg/hintrunner/zero/zerohint_keccak.go index f72dc956..36169e43 100644 --- a/pkg/hintrunner/zero/zerohint_keccak.go +++ b/pkg/hintrunner/zero/zerohint_keccak.go @@ -572,6 +572,180 @@ func createCompareBytesInWordNondetHinter(resolver hintReferenceResolver) (hinte return newCompareBytesInWordHint(nBytes), nil } +// SplitInput12 hint assigns to `ids.high12` and `ids.low12` variables +// the quotient and remainder of the division of the value at memory address +// `ids.inputs + 12` by 256 ** 4 +// +// `newSplitInput12Hint` takes 3 operanders as arguments +// - `high12` is the variable that will store the quotient of the division +// - `low12` is the variable that will store the remainder of the division +// - `inputs` is the address in memory to which we add an offset of 12 and read that value +func newSplitInput12Hint(high12, low12, inputs hinter.ResOperander) hinter.Hinter { + return &GenericZeroHinter{ + Name: "SplitInput12", + Op: func(vm *VM.VirtualMachine, _ *hinter.HintRunnerContext) error { + //> ids.high12, ids.low12 = divmod(memory[ids.inputs + 12], 256 ** 4) + + high12Addr, err := high12.GetAddress(vm) + if err != nil { + return err + } + + low12Addr, err := low12.GetAddress(vm) + if err != nil { + return err + } + + inputsAddr, err := hinter.ResolveAsAddress(vm, inputs) + if err != nil { + return err + } + + *inputsAddr, err = inputsAddr.AddOffset(12) + if err != nil { + return err + } + + inputValue, err := vm.Memory.ReadFromAddress(inputsAddr) + if err != nil { + return err + } + + var inputBigInt big.Int + inputValue.Felt.BigInt(&inputBigInt) + + // 256 ** 4 + divisor := big.NewInt(4294967296) + + high12BigInt := new(big.Int) + low12BigInt := new(big.Int) + + high12BigInt.DivMod(&inputBigInt, divisor, low12BigInt) + + var high12Felt fp.Element + high12Felt.SetBigInt(high12BigInt) + high12Mv := memory.MemoryValueFromFieldElement(&high12Felt) + + var low12Felt fp.Element + low12Felt.SetBigInt(low12BigInt) + low12Mv := memory.MemoryValueFromFieldElement(&low12Felt) + + err = vm.Memory.WriteToAddress(&low12Addr, &low12Mv) + if err != nil { + return err + } + + return vm.Memory.WriteToAddress(&high12Addr, &high12Mv) + }, + } +} + +func createSplitInput12Hinter(resolver hintReferenceResolver) (hinter.Hinter, error) { + high12, err := resolver.GetResOperander("high12") + if err != nil { + return nil, err + } + + low12, err := resolver.GetResOperander("low12") + if err != nil { + return nil, err + } + + inputs, err := resolver.GetResOperander("inputs") + if err != nil { + return nil, err + } + + return newSplitInput12Hint(high12, low12, inputs), nil +} + +// SplitInput15 hint assigns to `ids.high15` and `ids.low15` variables +// the quotient and remainder of the division of the value at memory address +// `ids.inputs + 15` by 256 ** 5 +// +// `newSplitInput9Hint` takes 3 operanders as arguments +// - `high15` is the variable that will store the quotient of the division +// - `low15` is the variable that will store the remainder of the division +// - `inputs` is the address in memory to which we add an offset of 15 and read that value +func newSplitInput15Hint(high15, low15, inputs hinter.ResOperander) hinter.Hinter { + return &GenericZeroHinter{ + Name: "SplitInput15", + Op: func(vm *VM.VirtualMachine, _ *hinter.HintRunnerContext) error { + //> ids.high15, ids.low15 = divmod(memory[ids.inputs + 15], 256 ** 5) + + high15Addr, err := high15.GetAddress(vm) + if err != nil { + return err + } + + low15Addr, err := low15.GetAddress(vm) + if err != nil { + return err + } + + inputsAddr, err := hinter.ResolveAsAddress(vm, inputs) + if err != nil { + return err + } + + *inputsAddr, err = inputsAddr.AddOffset(15) + if err != nil { + return err + } + + inputValue, err := vm.Memory.ReadFromAddress(inputsAddr) + if err != nil { + return err + } + + var inputBigInt big.Int + inputValue.Felt.BigInt(&inputBigInt) + + // 256 ** 5 + divisor := big.NewInt(1099511627776) + + high15BigInt := new(big.Int) + low15BigInt := new(big.Int) + + high15BigInt.DivMod(&inputBigInt, divisor, low15BigInt) + + var high15Felt fp.Element + high15Felt.SetBigInt(high15BigInt) + high15Mv := memory.MemoryValueFromFieldElement(&high15Felt) + + var low15Felt fp.Element + low15Felt.SetBigInt(low15BigInt) + low15Mv := memory.MemoryValueFromFieldElement(&low15Felt) + + err = vm.Memory.WriteToAddress(&low15Addr, &low15Mv) + if err != nil { + return err + } + + return vm.Memory.WriteToAddress(&high15Addr, &high15Mv) + }, + } +} + +func createSplitInput15Hinter(resolver hintReferenceResolver) (hinter.Hinter, error) { + high15, err := resolver.GetResOperander("high15") + if err != nil { + return nil, err + } + + low15, err := resolver.GetResOperander("low15") + if err != nil { + return nil, err + } + + inputs, err := resolver.GetResOperander("inputs") + if err != nil { + return nil, err + } + + return newSplitInput15Hint(high15, low15, inputs), nil +} + // SplitOutputMidLowHigh hint assigns to `ids.output1_low` the remainder of the division // of `ids.output1` variable by 256 ** 7 and uses its quotient as a variable which is // divided by 2 ** 128, the quotient and remainder of which are then assigned to `ids.output1_high` diff --git a/pkg/hintrunner/zero/zerohint_keccak_test.go b/pkg/hintrunner/zero/zerohint_keccak_test.go index b03d5ed3..247a299d 100644 --- a/pkg/hintrunner/zero/zerohint_keccak_test.go +++ b/pkg/hintrunner/zero/zerohint_keccak_test.go @@ -605,6 +605,235 @@ func TestZeroHintKeccak(t *testing.T) { check: apValueEquals(feltUint64(0)), }, }, + "SplitInput12": { + { + operanders: []*hintOperander{ + {Name: "inputs", Kind: fpRelative, Value: addr(2)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "real_input", Kind: apRelative, Value: feltUint64(0)}, + {Name: "high12", Kind: uninitialized}, + {Name: "low12", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitInput12Hint(ctx.operanders["high12"], ctx.operanders["low12"], ctx.operanders["inputs"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"high12": feltUint64(0), "low12": feltUint64(0)}), + }, + { + operanders: []*hintOperander{ + {Name: "inputs", Kind: fpRelative, Value: addr(2)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "real_input", Kind: apRelative, Value: feltUint64(100)}, + {Name: "high12", Kind: uninitialized}, + {Name: "low12", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitInput12Hint(ctx.operanders["high12"], ctx.operanders["low12"], ctx.operanders["inputs"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"high12": feltUint64(0), "low12": feltUint64(100)}), + }, + { + operanders: []*hintOperander{ + {Name: "inputs", Kind: fpRelative, Value: addr(2)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "real_input", Kind: apRelative, Value: feltUint64(4294967296)}, + {Name: "high12", Kind: uninitialized}, + {Name: "low12", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitInput12Hint(ctx.operanders["high12"], ctx.operanders["low12"], ctx.operanders["inputs"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"high12": feltUint64(1), "low12": feltUint64(0)}), + }, + { + operanders: []*hintOperander{ + {Name: "inputs", Kind: fpRelative, Value: addr(2)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "real_input", Kind: apRelative, Value: feltUint64(4294967297)}, + {Name: "high12", Kind: uninitialized}, + {Name: "low12", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitInput12Hint(ctx.operanders["high12"], ctx.operanders["low12"], ctx.operanders["inputs"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"high12": feltUint64(1), "low12": feltUint64(1)}), + }, + { + operanders: []*hintOperander{ + {Name: "inputs", Kind: fpRelative, Value: addr(2)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "real_input", Kind: apRelative, Value: feltUint64(42949672961)}, + {Name: "high12", Kind: uninitialized}, + {Name: "low12", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitInput12Hint(ctx.operanders["high12"], ctx.operanders["low12"], ctx.operanders["inputs"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"high12": feltUint64(10), "low12": feltUint64(1)}), + }, + }, + "SplitInput15": { + { + operanders: []*hintOperander{ + {Name: "inputs", Kind: fpRelative, Value: addr(2)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "real_input", Kind: apRelative, Value: feltUint64(0)}, + {Name: "high15", Kind: uninitialized}, + {Name: "low15", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitInput15Hint(ctx.operanders["high15"], ctx.operanders["low15"], ctx.operanders["inputs"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"high15": feltUint64(0), "low15": feltUint64(0)}), + }, + { + operanders: []*hintOperander{ + {Name: "inputs", Kind: fpRelative, Value: addr(2)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "real_input", Kind: apRelative, Value: feltUint64(100)}, + {Name: "high15", Kind: uninitialized}, + {Name: "low15", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitInput15Hint(ctx.operanders["high15"], ctx.operanders["low15"], ctx.operanders["inputs"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"high15": feltUint64(0), "low15": feltUint64(100)}), + }, + { + operanders: []*hintOperander{ + {Name: "inputs", Kind: fpRelative, Value: addr(2)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "real_input", Kind: apRelative, Value: feltUint64(1099511627776)}, + {Name: "high15", Kind: uninitialized}, + {Name: "low15", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitInput15Hint(ctx.operanders["high15"], ctx.operanders["low15"], ctx.operanders["inputs"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"high15": feltUint64(1), "low15": feltUint64(0)}), + }, + { + operanders: []*hintOperander{ + {Name: "inputs", Kind: fpRelative, Value: addr(2)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "real_input", Kind: apRelative, Value: feltUint64(1099511627777)}, + {Name: "high15", Kind: uninitialized}, + {Name: "low15", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitInput15Hint(ctx.operanders["high15"], ctx.operanders["low15"], ctx.operanders["inputs"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"high15": feltUint64(1), "low15": feltUint64(1)}), + }, + { + operanders: []*hintOperander{ + {Name: "inputs", Kind: fpRelative, Value: addr(2)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "random_value", Kind: apRelative, Value: feltUint64(0)}, + {Name: "real_input", Kind: apRelative, Value: feltUint64(10995116277761)}, + {Name: "high15", Kind: uninitialized}, + {Name: "low15", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitInput15Hint(ctx.operanders["high15"], ctx.operanders["low15"], ctx.operanders["inputs"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"high15": feltUint64(10), "low15": feltUint64(1)}), + }, + }, "SplitOutputMidLowHigh": { { operanders: []*hintOperander{ From 312d061783e6bea41acc9264a5b205a3cde7ccbc Mon Sep 17 00:00:00 2001 From: Tristan <122918260+TAdev0@users.noreply.github.com> Date: Thu, 4 Jul 2024 11:11:58 +0200 Subject: [PATCH 7/8] `keccak` integration tests (#497) * unsafe keccak + unsafe keccak finalize * solve many issues * fmt * fmt --------- Co-authored-by: Shourya Goel --- .../unsafe_keccak.small.cairo | 21 +++++ .../unsafe_keccak_finalize.small.cairo | 21 +++++ .../block_permutation.small.cairo | 84 ------------------- ...keccak_finalize.starknet_with_keccak.cairo | 29 +++++++ .../keccak.starknet_with_keccak.cairo | 39 +++++++++ ...cak_add_uint256.starknet_with_keccak.cairo | 20 +++++ ...keccak_uint256s.starknet_with_keccak.cairo | 17 ++++ pkg/hintrunner/zero/hintcode.go | 15 +--- 8 files changed, 151 insertions(+), 95 deletions(-) create mode 100644 integration_tests/cairo_zero_hint_tests/unsafe_keccak.small.cairo create mode 100644 integration_tests/cairo_zero_hint_tests/unsafe_keccak_finalize.small.cairo delete mode 100644 integration_tests/cairo_zero_hint_tests_in_progress/block_permutation.small.cairo create mode 100644 integration_tests/cairo_zero_hint_tests_in_progress/cairo_keccak_finalize.starknet_with_keccak.cairo create mode 100644 integration_tests/cairo_zero_hint_tests_in_progress/keccak.starknet_with_keccak.cairo create mode 100644 integration_tests/cairo_zero_hint_tests_in_progress/keccak_add_uint256.starknet_with_keccak.cairo create mode 100644 integration_tests/cairo_zero_hint_tests_in_progress/keccak_uint256s.starknet_with_keccak.cairo diff --git a/integration_tests/cairo_zero_hint_tests/unsafe_keccak.small.cairo b/integration_tests/cairo_zero_hint_tests/unsafe_keccak.small.cairo new file mode 100644 index 00000000..37d72214 --- /dev/null +++ b/integration_tests/cairo_zero_hint_tests/unsafe_keccak.small.cairo @@ -0,0 +1,21 @@ +%builtins output + +from starkware.cairo.common.alloc import alloc +from starkware.cairo.common.keccak import unsafe_keccak + +func main{output_ptr: felt*}() { + alloc_locals; + + let (data: felt*) = alloc(); + + assert data[0] = 500; + assert data[1] = 2; + assert data[2] = 3; + assert data[3] = 6; + assert data[4] = 1; + assert data[5] = 4444; + + let (low: felt, high: felt) = unsafe_keccak(data, 6); + + return (); +} \ No newline at end of file diff --git a/integration_tests/cairo_zero_hint_tests/unsafe_keccak_finalize.small.cairo b/integration_tests/cairo_zero_hint_tests/unsafe_keccak_finalize.small.cairo new file mode 100644 index 00000000..563e5cad --- /dev/null +++ b/integration_tests/cairo_zero_hint_tests/unsafe_keccak_finalize.small.cairo @@ -0,0 +1,21 @@ +%builtins output + +from starkware.cairo.common.alloc import alloc +from starkware.cairo.common.keccak import unsafe_keccak_finalize, KeccakState +from starkware.cairo.common.uint256 import Uint256 + +func main{output_ptr: felt*}() { + alloc_locals; + + let (data: felt*) = alloc(); + + assert data[0] = 0; + assert data[1] = 1; + assert data[2] = 2; + + let keccak_state = KeccakState(start_ptr=data, end_ptr=data + 2); + + let res: Uint256 = unsafe_keccak_finalize(keccak_state); + + return (); +} \ No newline at end of file diff --git a/integration_tests/cairo_zero_hint_tests_in_progress/block_permutation.small.cairo b/integration_tests/cairo_zero_hint_tests_in_progress/block_permutation.small.cairo deleted file mode 100644 index 5fea2bd3..00000000 --- a/integration_tests/cairo_zero_hint_tests_in_progress/block_permutation.small.cairo +++ /dev/null @@ -1,84 +0,0 @@ -// The content of this file has been partially borrowed from LambdaClass Cairo VM in Rust -// See https://github.com/lambdaclass/cairo-vm/blob/aecbb3f01dacb6d3f90256c808466c2c37606252/cairo_programs/keccak_alternative_hint.cairo#L20 - -%builtins output range_check bitwise - -from starkware.cairo.common.cairo_keccak.keccak import ( - _prepare_block, - KECCAK_FULL_RATE_IN_BYTES, - KECCAK_FULL_RATE_IN_WORDS, - KECCAK_STATE_SIZE_FELTS, -) -from starkware.cairo.common.math import assert_nn_le -from starkware.cairo.common.cairo_builtins import BitwiseBuiltin -from starkware.cairo.common.alloc import alloc -from starkware.cairo.common.serialize import serialize_word - -func _block_permutation_cairo_keccak{output_ptr: felt*, keccak_ptr: felt*}() { - alloc_locals; - let output = output_ptr; - let keccak_ptr_start = keccak_ptr - KECCAK_STATE_SIZE_FELTS; - %{ - from starkware.cairo.common.cairo_keccak.keccak_utils import keccak_func - _keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS) - assert 0 <= _keccak_state_size_felts < 100 - output_values = keccak_func(memory.get_range( - ids.keccak_ptr_start, _keccak_state_size_felts)) - segments.write_arg(ids.output, output_values) - %} - let keccak_ptr = keccak_ptr + KECCAK_STATE_SIZE_FELTS; - - return (); -} - -func run_cairo_keccak{output_ptr: felt*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() { - alloc_locals; - - let (output: felt*) = alloc(); - let keccak_output = output; - - let (inputs: felt*) = alloc(); - let inputs_start = inputs; - fill_array(inputs, 9, 3, 0); - - let (state: felt*) = alloc(); - let state_start = state; - fill_array(state, 5, 25, 0); - - let n_bytes = 24; - - _prepare_block{keccak_ptr=output_ptr}(inputs=inputs, n_bytes=n_bytes, state=state); - _block_permutation_cairo_keccak{keccak_ptr=output_ptr}(); - - local full_word: felt; - %{ ids.full_word = int(ids.n_bytes >= 8) %} - assert full_word = 1; - - let n_bytes = 8; - local full_word: felt; - %{ ids.full_word = int(ids.n_bytes >= 8) %} - assert full_word = 1; - - let n_bytes = 7; - local full_word: felt; - %{ ids.full_word = int(ids.n_bytes >= 8) %} - assert full_word = 0; - - return (); -} - -func fill_array(array: felt*, base: felt, array_length: felt, iterator: felt) { - if (iterator == array_length) { - return (); - } - - assert array[iterator] = base; - - return fill_array(array, base, array_length, iterator + 1); -} - -func main{output_ptr: felt*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() { - run_cairo_keccak(); - - return (); -} diff --git a/integration_tests/cairo_zero_hint_tests_in_progress/cairo_keccak_finalize.starknet_with_keccak.cairo b/integration_tests/cairo_zero_hint_tests_in_progress/cairo_keccak_finalize.starknet_with_keccak.cairo new file mode 100644 index 00000000..bdded716 --- /dev/null +++ b/integration_tests/cairo_zero_hint_tests_in_progress/cairo_keccak_finalize.starknet_with_keccak.cairo @@ -0,0 +1,29 @@ +%builtins range_check bitwise + +from starkware.cairo.common.cairo_keccak.keccak import cairo_keccak, finalize_keccak +from starkware.cairo.common.uint256 import Uint256 +from starkware.cairo.common.cairo_builtins import BitwiseBuiltin +from starkware.cairo.common.alloc import alloc + +func main{range_check_ptr: felt, bitwise_ptr: BitwiseBuiltin*}() { + alloc_locals; + + let (keccak_ptr: felt*) = alloc(); + let keccak_ptr_start = keccak_ptr; + + let (inputs: felt*) = alloc(); + + assert inputs[0] = 8031924123371070792; + assert inputs[1] = 560229490; + + let n_bytes = 16; + + let (res: Uint256) = cairo_keccak{keccak_ptr=keccak_ptr}(inputs=inputs, n_bytes=n_bytes); + + assert res.low = 293431514620200399776069983710520819074; + assert res.high = 317109767021952548743448767588473366791; + + finalize_keccak(keccak_ptr_start=keccak_ptr_start, keccak_ptr_end=keccak_ptr); + + return (); +} \ No newline at end of file diff --git a/integration_tests/cairo_zero_hint_tests_in_progress/keccak.starknet_with_keccak.cairo b/integration_tests/cairo_zero_hint_tests_in_progress/keccak.starknet_with_keccak.cairo new file mode 100644 index 00000000..b5f1b363 --- /dev/null +++ b/integration_tests/cairo_zero_hint_tests_in_progress/keccak.starknet_with_keccak.cairo @@ -0,0 +1,39 @@ +%builtins output range_check bitwise + +from starkware.cairo.common.cairo_keccak.keccak import _keccak +from starkware.cairo.common.cairo_builtins import BitwiseBuiltin +from starkware.cairo.common.alloc import alloc +from starkware.cairo.common.serialize import serialize_word + +func fill_array(array: felt*, base: felt, array_length: felt, iterator: felt) { + if (iterator == array_length) { + return (); + } + + assert array[iterator] = base; + + return fill_array(array, base, array_length, iterator + 1); +} + +func main{output_ptr: felt*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() { + alloc_locals; + + let (output: felt*) = alloc(); + let keccak_output = output; + + let (inputs: felt*) = alloc(); + let inputs_start = inputs; + fill_array(inputs, 9, 3, 0); + + let (state: felt*) = alloc(); + let state_start = state; + fill_array(state, 5, 25, 0); + + let n_bytes = 24; + + let (res: felt*) = _keccak{keccak_ptr=keccak_output}( + inputs=inputs_start, n_bytes=n_bytes, state=state_start + ); + + return (); +} \ No newline at end of file diff --git a/integration_tests/cairo_zero_hint_tests_in_progress/keccak_add_uint256.starknet_with_keccak.cairo b/integration_tests/cairo_zero_hint_tests_in_progress/keccak_add_uint256.starknet_with_keccak.cairo new file mode 100644 index 00000000..fd047d97 --- /dev/null +++ b/integration_tests/cairo_zero_hint_tests_in_progress/keccak_add_uint256.starknet_with_keccak.cairo @@ -0,0 +1,20 @@ +%builtins output range_check bitwise + +from starkware.cairo.common.keccak_utils.keccak_utils import keccak_add_uint256 +from starkware.cairo.common.uint256 import Uint256 +from starkware.cairo.common.cairo_builtins import BitwiseBuiltin +from starkware.cairo.common.alloc import alloc +from starkware.cairo.common.serialize import serialize_word + +func main{output_ptr: felt*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() { + alloc_locals; + + let (inputs) = alloc(); + let inputs_start = inputs; + + let num = Uint256(34623634663146736, 598249824422424658356); + + keccak_add_uint256{inputs=inputs_start}(num=num, bigend=0); + + return (); +} \ No newline at end of file diff --git a/integration_tests/cairo_zero_hint_tests_in_progress/keccak_uint256s.starknet_with_keccak.cairo b/integration_tests/cairo_zero_hint_tests_in_progress/keccak_uint256s.starknet_with_keccak.cairo new file mode 100644 index 00000000..e6e69066 --- /dev/null +++ b/integration_tests/cairo_zero_hint_tests_in_progress/keccak_uint256s.starknet_with_keccak.cairo @@ -0,0 +1,17 @@ +%builtins range_check bitwise keccak +from starkware.cairo.common.cairo_builtins import KeccakBuiltin, BitwiseBuiltin +from starkware.cairo.common.builtin_keccak.keccak import keccak_uint256s +from starkware.cairo.common.alloc import alloc +from starkware.cairo.common.uint256 import Uint256 + +func main{range_check_ptr, bitwise_ptr: BitwiseBuiltin*, keccak_ptr: KeccakBuiltin*}() { + let elements: Uint256* = alloc(); + assert elements[0] = Uint256(713458135386519, 18359173571); + assert elements[1] = Uint256(1536741637546373185, 84357893467438914); + assert elements[2] = Uint256(2842949328439284983294, 39248298942938492384); + assert elements[3] = Uint256(27518568234293478923754395731931, 981587843715983274); + assert elements[4] = Uint256(326848123647324823482, 93453458349589345); + let (res) = keccak_uint256s(5, elements); + + return (); +} \ No newline at end of file diff --git a/pkg/hintrunner/zero/hintcode.go b/pkg/hintrunner/zero/hintcode.go index 9169660f..8cd34c4f 100644 --- a/pkg/hintrunner/zero/hintcode.go +++ b/pkg/hintrunner/zero/hintcode.go @@ -115,18 +115,11 @@ ids.multiplicities = segments.gen_arg([len(positions_dict[k]) for k in output])` blake2sComputeCode string = "from starkware.cairo.common.cairo_blake2s.blake2s_utils import compute_blake2s_func\ncompute_blake2s_func(segments=segments, output_ptr=ids.output)" // ------ Keccak hints related code ------ - unsafeKeccakFinalizeCode string = "from eth_hash.auto import keccak\nkeccak_input = bytearray()\nn_elms = ids.keccak_state.end_ptr - ids.keccak_state.start_ptr\nfor word in memory.get_range(ids.keccak_state.start_ptr, n_elms):\n keccak_input += word.to_bytes(16, 'big')\nhashed = keccak(keccak_input)\nids.high = int.from_bytes(hashed[:16], 'big')\nids.low = int.from_bytes(hashed[16:32], 'big')" - unsafeKeccakCode string = "from eth_hash.auto import keccak\n\ndata, length = ids.data, ids.length\n\nif '__keccak_max_size' in globals():\n assert length <= __keccak_max_size, \\\n f'unsafe_keccak() can only be used with length<={__keccak_max_size}. ' \\\n f'Got: length={length}.'\n\nkeccak_input = bytearray()\nfor word_i, byte_i in enumerate(range(0, length, 16)):\n word = memory[data + word_i]\n n_bytes = min(16, length - byte_i)\n assert 0 <= word < 2 ** (8 * n_bytes)\n keccak_input += word.to_bytes(n_bytes, 'big')\n\nhashed = keccak(keccak_input)\nids.high = int.from_bytes(hashed[:16], 'big')\nids.low = int.from_bytes(hashed[16:32], 'big')" - cairoKeccakFinalizeCode string = `# Add dummy pairs of input and output. - _keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS) - _block_size = int(ids.BLOCK_SIZE) - assert 0 <= _keccak_state_size_felts < 100 - assert 0 <= _block_size < 10 - inp = [0] * _keccak_state_size_felts - padding = (inp + keccak_func(inp)) * _block_size - segments.write_arg(ids.keccak_ptr_end, padding)` + unsafeKeccakFinalizeCode string = "from eth_hash.auto import keccak\nkeccak_input = bytearray()\nn_elms = ids.keccak_state.end_ptr - ids.keccak_state.start_ptr\nfor word in memory.get_range(ids.keccak_state.start_ptr, n_elms):\n keccak_input += word.to_bytes(16, 'big')\nhashed = keccak(keccak_input)\nids.high = int.from_bytes(hashed[:16], 'big')\nids.low = int.from_bytes(hashed[16:32], 'big')" + unsafeKeccakCode string = "from eth_hash.auto import keccak\n\ndata, length = ids.data, ids.length\n\nif '__keccak_max_size' in globals():\n assert length <= __keccak_max_size, \\\n f'unsafe_keccak() can only be used with length<={__keccak_max_size}. ' \\\n f'Got: length={length}.'\n\nkeccak_input = bytearray()\nfor word_i, byte_i in enumerate(range(0, length, 16)):\n word = memory[data + word_i]\n n_bytes = min(16, length - byte_i)\n assert 0 <= word < 2 ** (8 * n_bytes)\n keccak_input += word.to_bytes(n_bytes, 'big')\n\nhashed = keccak(keccak_input)\nids.high = int.from_bytes(hashed[:16], 'big')\nids.low = int.from_bytes(hashed[16:32], 'big')" + cairoKeccakFinalizeCode string = "# Add dummy pairs of input and output.\n_keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS)\n_block_size = int(ids.BLOCK_SIZE)\nassert 0 <= _keccak_state_size_felts < 100\nassert 0 <= _block_size < 10\ninp = [0] * _keccak_state_size_felts\npadding = (inp + keccak_func(inp)) * _block_size\nsegments.write_arg(ids.keccak_ptr_end, padding)" keccakWriteArgsCode string = "segments.write_arg(ids.inputs, [ids.low % 2 ** 64, ids.low // 2 ** 64])\nsegments.write_arg(ids.inputs + 2, [ids.high % 2 ** 64, ids.high // 2 ** 64])" - blockPermutationCode string = "from starkware.cairo.common.keccak_utils.keccak_utils import keccak_func\n_keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS)\nassert 0 <= _keccak_state_size_felts < 100\noutput_values = keccak_func(memory.get_range(\nids.keccak_ptr - _keccak_state_size_felts, _keccak_state_size_felts))\nsegments.write_arg(ids.keccak_ptr, output_values)" + blockPermutationCode string = "from starkware.cairo.common.keccak_utils.keccak_utils import keccak_func\n_keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS)\nassert 0 <= _keccak_state_size_felts < 100\n\noutput_values = keccak_func(memory.get_range(\n ids.keccak_ptr - _keccak_state_size_felts, _keccak_state_size_felts))\nsegments.write_arg(ids.keccak_ptr, output_values)" compareBytesInWordCode string = "memory[ap] = to_felt_or_relocatable(ids.n_bytes < ids.BYTES_IN_WORD)" compareKeccakFullRateInBytesCode string = "memory[ap] = to_felt_or_relocatable(ids.n_bytes >= ids.KECCAK_FULL_RATE_IN_BYTES)" splitInput3Code string = "ids.high3, ids.low3 = divmod(memory[ids.inputs + 3], 256)" From 420549c6aecd6d2fc8e575afd7df8145d924779a Mon Sep 17 00:00:00 2001 From: Tristan <122918260+TAdev0@users.noreply.github.com> Date: Thu, 4 Jul 2024 11:20:58 +0200 Subject: [PATCH 8/8] `SplitOutput0` hint (#509) * SplitOutput0 * fmt * fmt * add tests --------- Co-authored-by: Shourya Goel --- pkg/hintrunner/zero/hintcode.go | 1 + pkg/hintrunner/zero/zerohint.go | 2 + pkg/hintrunner/zero/zerohint_keccak.go | 75 +++++++++++++++++++++ pkg/hintrunner/zero/zerohint_keccak_test.go | 46 +++++++++++++ 4 files changed, 124 insertions(+) diff --git a/pkg/hintrunner/zero/hintcode.go b/pkg/hintrunner/zero/hintcode.go index 8cd34c4f..38056fb0 100644 --- a/pkg/hintrunner/zero/hintcode.go +++ b/pkg/hintrunner/zero/hintcode.go @@ -128,6 +128,7 @@ ids.multiplicities = segments.gen_arg([len(positions_dict[k]) for k in output])` splitInput12Code string = "ids.high12, ids.low12 = divmod(memory[ids.inputs + 12], 256 ** 4)" splitInput15Code string = "ids.high15, ids.low15 = divmod(memory[ids.inputs + 15], 256 ** 5)" splitOutputMidLowHighCode string = "tmp, ids.output1_low = divmod(ids.output1, 256 ** 7)\nids.output1_high, ids.output1_mid = divmod(tmp, 2 ** 128)" + splitOutput0Code string = "ids.output0_low = ids.output0 & ((1 << 128) - 1)\nids.output0_high = ids.output0 >> 128" SplitNBytesCode string = "ids.n_words_to_copy, ids.n_bytes_left = divmod(ids.n_bytes, ids.BYTES_IN_WORD)" // ------ Dictionaries hints related code ------ diff --git a/pkg/hintrunner/zero/zerohint.go b/pkg/hintrunner/zero/zerohint.go index 89a157a4..1e0c1a1f 100644 --- a/pkg/hintrunner/zero/zerohint.go +++ b/pkg/hintrunner/zero/zerohint.go @@ -187,6 +187,8 @@ func GetHintFromCode(program *zero.ZeroProgram, rawHint zero.Hint, hintPC uint64 return createSplitInput15Hinter(resolver) case splitOutputMidLowHighCode: return createSplitOutputMidLowHighHinter(resolver) + case splitOutput0Code: + return createSplitOutput0Hinter(resolver) case SplitNBytesCode: return createSplitNBytesHinter(resolver) // Usort hints diff --git a/pkg/hintrunner/zero/zerohint_keccak.go b/pkg/hintrunner/zero/zerohint_keccak.go index 36169e43..e9021b37 100644 --- a/pkg/hintrunner/zero/zerohint_keccak.go +++ b/pkg/hintrunner/zero/zerohint_keccak.go @@ -847,6 +847,81 @@ func createSplitOutputMidLowHighHinter(resolver hintReferenceResolver) (hinter.H return newSplitOutputMidLowHighHint(output1, output1Low, output1Mid, output1High), nil } +// SplitOutput0 hint splits `output0` into `output0_low` (16 bytes) and `output0_high` (9 bytes) +// +// `newSplitOutput0Hint` takes 3 operanders as arguments +// - `output0_low` is the variable that will store the low part of `output0` +// - `output0_high` is the variable that will store the high part of `output0` +// - `output0` is the value to split +func newSplitOutput0Hint(output0Low, output0High, output0 hinter.ResOperander) hinter.Hinter { + return &GenericZeroHinter{ + Name: "SplitOutput0", + Op: func(vm *VM.VirtualMachine, _ *hinter.HintRunnerContext) error { + //> ids.output0_low = ids.output0 & ((1 << 128) - 1) + //> ids.output0_high = ids.output0 >> 128 + + output0LowAddr, err := output0Low.GetAddress(vm) + if err != nil { + return err + } + + output0HighAddr, err := output0High.GetAddress(vm) + if err != nil { + return err + } + + output0, err := hinter.ResolveAsFelt(vm, output0) + if err != nil { + return err + } + + output0Uint := uint256.Int(output0.Bits()) + + var output0Low uint256.Int + mask := new(uint256.Int).Lsh(uint256.NewInt(1), 128) + mask.Sub(mask, uint256.NewInt(1)) + output0Low.And(&output0Uint, mask) + output0LowBytes := output0Low.Bytes() + output0LowFelt := fp.Element{} + output0LowFelt.SetBytes(output0LowBytes) + output0LowMv := memory.MemoryValueFromFieldElement(&output0LowFelt) + + var output0High uint256.Int + output0High.Rsh(&output0Uint, 128) + output0HighBytes := output0High.Bytes() + output0HighFelt := fp.Element{} + output0HighFelt.SetBytes(output0HighBytes) + output0HighMv := memory.MemoryValueFromFieldElement(&output0HighFelt) + + err = vm.Memory.WriteToAddress(&output0LowAddr, &output0LowMv) + if err != nil { + return err + } + + return vm.Memory.WriteToAddress(&output0HighAddr, &output0HighMv) + }, + } +} + +func createSplitOutput0Hinter(resolver hintReferenceResolver) (hinter.Hinter, error) { + output0Low, err := resolver.GetResOperander("output0_low") + if err != nil { + return nil, err + } + + output0High, err := resolver.GetResOperander("output0_high") + if err != nil { + return nil, err + } + + output0, err := resolver.GetResOperander("output0") + if err != nil { + return nil, err + } + + return newSplitOutput0Hint(output0Low, output0High, output0), nil +} + // SplitNBytes hint assigns to `ids.n_words_to_copy` and `ids.n_bytes_left` variables // the quotient and remainder of the division of `ids.n_bytes` variable by the // variable `ids.BYTES_IN_WORD` diff --git a/pkg/hintrunner/zero/zerohint_keccak_test.go b/pkg/hintrunner/zero/zerohint_keccak_test.go index 247a299d..beffb71d 100644 --- a/pkg/hintrunner/zero/zerohint_keccak_test.go +++ b/pkg/hintrunner/zero/zerohint_keccak_test.go @@ -1220,5 +1220,51 @@ func TestZeroHintKeccak(t *testing.T) { check: allVarValueEquals(map[string]*fp.Element{"high9": feltUint64(10), "low9": feltUint64(1)}), }, }, + "SplitOutput0": { + { + operanders: []*hintOperander{ + {Name: "output0", Kind: apRelative, Value: feltUint64(0)}, + {Name: "output0_low", Kind: uninitialized}, + {Name: "output0_high", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitOutput0Hint(ctx.operanders["output0_low"], ctx.operanders["output0_high"], ctx.operanders["output0"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"output0_low": feltUint64(0), "output0_high": feltUint64(0)}), + }, + { + operanders: []*hintOperander{ + {Name: "output0", Kind: apRelative, Value: feltUint64(1)}, + {Name: "output0_low", Kind: uninitialized}, + {Name: "output0_high", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitOutput0Hint(ctx.operanders["output0_low"], ctx.operanders["output0_high"], ctx.operanders["output0"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"output0_low": feltUint64(1), "output0_high": feltUint64(0)}), + }, + { + operanders: []*hintOperander{ + {Name: "output0", Kind: apRelative, Value: feltString("340282366920938463463374607431768211455")}, + {Name: "output0_low", Kind: uninitialized}, + {Name: "output0_high", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitOutput0Hint(ctx.operanders["output0_low"], ctx.operanders["output0_high"], ctx.operanders["output0"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"output0_low": feltString("340282366920938463463374607431768211455"), "output0_high": feltString("0")}), + }, + { + operanders: []*hintOperander{ + {Name: "output0", Kind: apRelative, Value: feltString("340282366920938463463374607431768211456")}, + {Name: "output0_low", Kind: uninitialized}, + {Name: "output0_high", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitOutput0Hint(ctx.operanders["output0_low"], ctx.operanders["output0_high"], ctx.operanders["output0"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"output0_low": feltString("0"), "output0_high": feltString("1")}), + }, + }, }) }