From 54a1d21a1d5c30dd5bb0255379dee43a8622c313 Mon Sep 17 00:00:00 2001 From: "M. Mahdi Khosravi" Date: Tue, 5 Dec 2023 17:33:59 +0300 Subject: [PATCH] RandomEcPoint Hint Implementation (#177) * added UpdatePc tests and fixed a bug in UpdatePc * Fixed accessing field value without Read() * fixed failing tests * small refactor for TestUpdatePcJump * randomEcPoint done * move comment * move randomizers into utils.go * address PR comments --- pkg/hintrunner/hint.go | 59 +++++++++++++++++++++++++++- pkg/hintrunner/hint_bechmark_test.go | 40 +++++++++---------- pkg/hintrunner/hint_test.go | 28 +++++++++++++ pkg/hintrunner/utils.go | 25 ++++++++++++ 4 files changed, 129 insertions(+), 23 deletions(-) diff --git a/pkg/hintrunner/hint.go b/pkg/hintrunner/hint.go index 588b21f08..349d556c0 100644 --- a/pkg/hintrunner/hint.go +++ b/pkg/hintrunner/hint.go @@ -1230,7 +1230,6 @@ func (hint *AssertLeFindSmallArc) Execute(vm *VM.VirtualMachine, ctx *HintRunner return nil } - type AssertLeIsFirstArcExcluded struct { skipExcludeAFlag CellRefer } @@ -1278,3 +1277,61 @@ func (hint *AssertLeIsSecondArcExcluded) Execute(vm *VM.VirtualMachine, ctx *Hin return vm.Memory.WriteToAddress(&addr, &writeValue) } + +type RandomEcPoint struct { + x CellRefer + y CellRefer +} + +func (hint *RandomEcPoint) String() string { + return "RandomEc" +} + +func (hint *RandomEcPoint) Execute(vm *VM.VirtualMachine) error { + // Keep sampling a random field element `X` until `X^3 + X + beta` is a quadratic residue. + + // Starkware's elliptic curve Beta value https://docs.starkware.co/starkex/crypto/stark-curve.html + betaFelt := f.Element{3863487492851900874, 7432612994240712710, 12360725113329547591, 88155977965380735} + + var randomX, randomYSquared f.Element + rand := defaultRandGenerator() + for { + randomX = randomFeltElement(rand) + randomYSquared = f.Element{} + randomYSquared.Square(&randomX) + randomYSquared.Mul(&randomYSquared, &randomX) + randomYSquared.Add(&randomYSquared, &randomX) + randomYSquared.Add(&randomYSquared, &betaFelt) + // Legendre == 1 -> Quadratic residue + // Legendre == -1 -> Quadratic non-residue + // Legendre == 0 -> Zero + if randomYSquared.Legendre() == 1 { + break + } + } + + xVal := mem.MemoryValueFromFieldElement(&randomX) + yVal := mem.MemoryValueFromFieldElement(randomYSquared.Square(&randomYSquared)) + + xAddr, err := hint.x.Get(vm) + if err != nil { + return fmt.Errorf("get register x %s: %w", hint.x, err) + } + + err = vm.Memory.WriteToAddress(&xAddr, &xVal) + if err != nil { + return fmt.Errorf("write to address x %s: %w", xVal, err) + } + + yAddr, err := hint.y.Get(vm) + if err != nil { + return fmt.Errorf("get register y %s: %w", hint.y, err) + } + + err = vm.Memory.WriteToAddress(&yAddr, &yVal) + if err != nil { + return fmt.Errorf("write to address y %s: %w", yVal, err) + } + + return nil +} diff --git a/pkg/hintrunner/hint_bechmark_test.go b/pkg/hintrunner/hint_bechmark_test.go index dcc2a9db3..82ba24f3b 100644 --- a/pkg/hintrunner/hint_bechmark_test.go +++ b/pkg/hintrunner/hint_bechmark_test.go @@ -1,14 +1,11 @@ package hintrunner import ( - "encoding/binary" - "math/rand" "testing" VM "github.com/NethermindEth/cairo-vm-go/pkg/vm" "github.com/NethermindEth/cairo-vm-go/pkg/vm/builtins" "github.com/NethermindEth/cairo-vm-go/pkg/vm/memory" - f "github.com/consensys/gnark-crypto/ecc/stark-curve/fp" "github.com/stretchr/testify/assert" ) @@ -338,25 +335,24 @@ func BenchmarkAssertLeFindSmallArc(b *testing.B) { } -func randomFeltElement(rand *rand.Rand) f.Element { - b := [32]byte{} - binary.BigEndian.PutUint64(b[24:32], rand.Uint64()) - binary.BigEndian.PutUint64(b[16:24], rand.Uint64()) - binary.BigEndian.PutUint64(b[8:16], rand.Uint64()) - //Limit to 59 bits so at max we have a 251 bit number - binary.BigEndian.PutUint64(b[0:8], rand.Uint64()>>5) - f, _ := f.BigEndian.Element(&b) - return f -} +func BenchmarkRandomEcPoint(b *testing.B) { + vm := defaultVirtualMachine() -func randomFeltElementU128(rand *rand.Rand) f.Element { - b := [32]byte{} - binary.BigEndian.PutUint64(b[24:32], rand.Uint64()) - binary.BigEndian.PutUint64(b[16:24], rand.Uint64()) - f, _ := f.BigEndian.Element(&b) - return f -} + b.ResetTimer() + for i := 0; i < b.N; i++ { + + hint := RandomEcPoint{ + x: ApCellRef(0), + y: ApCellRef(1), + } + + err := hint.Execute(vm) + if err != nil { + b.Error(err) + break + } + + vm.Context.Ap += 2 + } -func defaultRandGenerator() *rand.Rand { - return rand.New(rand.NewSource(0)) } diff --git a/pkg/hintrunner/hint_test.go b/pkg/hintrunner/hint_test.go index 46b1c55f7..8d22275c0 100644 --- a/pkg/hintrunner/hint_test.go +++ b/pkg/hintrunner/hint_test.go @@ -834,3 +834,31 @@ func TestAssertLeIsSecondArcExcluded(t *testing.T) { require.Equal(t, expected, actual) } + +func TestRandomEcPoint(t *testing.T) { + vm := defaultVirtualMachine() + vm.Context.Ap = 0 + vm.Context.Fp = 0 + + hint := RandomEcPoint{ + x: ApCellRef(0), + y: ApCellRef(1), + } + + err := hint.Execute(vm) + + require.NoError(t, err) + + expectedX := mem.MemoryValueFromFieldElement( + &f.Element{12217889558999792019, 3067322962467879919, 3160430244162662030, 474947714424245026}, + ) + expectedY := mem.MemoryValueFromFieldElement( + &f.Element{12193331470568888984, 1737428559173019240, 11500517745011090163, 245183001587853482}, + ) + + actualX := readFrom(vm, VM.ExecutionSegment, 0) + actualY := readFrom(vm, VM.ExecutionSegment, 1) + + require.Equal(t, expectedX, actualX) + require.Equal(t, expectedY, actualY) +} diff --git a/pkg/hintrunner/utils.go b/pkg/hintrunner/utils.go index c75e8571a..633b7adbb 100644 --- a/pkg/hintrunner/utils.go +++ b/pkg/hintrunner/utils.go @@ -1,7 +1,9 @@ package hintrunner import ( + "encoding/binary" "fmt" + "math/rand" VM "github.com/NethermindEth/cairo-vm-go/pkg/vm" mem "github.com/NethermindEth/cairo-vm-go/pkg/vm/memory" @@ -49,3 +51,26 @@ func ResolveAsUint64(vm *VM.VirtualMachine, op ResOperander) (uint64, error) { return uint64Value, nil } + +func randomFeltElement(rand *rand.Rand) f.Element { + b := [32]byte{} + binary.BigEndian.PutUint64(b[24:32], rand.Uint64()) + binary.BigEndian.PutUint64(b[16:24], rand.Uint64()) + binary.BigEndian.PutUint64(b[8:16], rand.Uint64()) + //Limit to 59 bits so at max we have a 251 bit number + binary.BigEndian.PutUint64(b[0:8], rand.Uint64()>>5) + f, _ := f.BigEndian.Element(&b) + return f +} + +func randomFeltElementU128(rand *rand.Rand) f.Element { + b := [32]byte{} + binary.BigEndian.PutUint64(b[24:32], rand.Uint64()) + binary.BigEndian.PutUint64(b[16:24], rand.Uint64()) + f, _ := f.BigEndian.Element(&b) + return f +} + +func defaultRandGenerator() *rand.Rand { + return rand.New(rand.NewSource(0)) +}