Skip to content

Commit

Permalink
Cairo0 div mod n safe div hint (#344)
Browse files Browse the repository at this point in the history
* Implement VerifyECDSASignature hint

* Fixed retrieving memory with signature via pointer

* Scooped out the deref from double deref in test cases

* Implement GetPointFromXHint

* Modify zerohint.go

* Add testcases to the hint

* Add final testcases

* Implement DivModNSafeDivHint

* Add checking errors

* Add error checks

* Changes from main merge

* Remove faulty integration tests

* Implements comments from discussion

* Fixes from merge

* Changes adressed by PR discussion

* Fixes after merge
  • Loading branch information
MaksymMalicki committed Apr 19, 2024
1 parent d50bca6 commit 7d292fa
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 16 deletions.
21 changes: 15 additions & 6 deletions pkg/hintrunner/utils/math_utils.go
Original file line number Diff line number Diff line change
@@ -1,25 +1,34 @@
package utils

import (
"fmt"
"math/big"

"github.com/consensys/gnark-crypto/ecc/stark-curve/fp"
)

func AsInt(valueFelt *fp.Element) *big.Int {
// https://github.com/starkware-libs/cairo-lang/blob/efa9648f57568aad8f8a13fbf027d2de7c63c2c0/src/starkware/cairo/common/math_utils.py#L8

func AsInt(valueFelt *fp.Element) big.Int {
var valueBig big.Int
valueFelt.BigInt(&valueBig)
return AsIntBig(&valueBig)
}

func AsIntBig(value *big.Int) *big.Int {
func AsIntBig(value *big.Int) big.Int {
boundBig := new(big.Int).Div(fp.Modulus(), big.NewInt(2))

// val if val < prime // 2 else val - prime
if value.Cmp(boundBig) == -1 {
return value
return *value
}
return *new(big.Int).Sub(value, fp.Modulus())
}

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.")
}
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 new(big.Int).Sub(value, fp.Modulus())
return *new(big.Int).Div(x, y), nil
}
19 changes: 14 additions & 5 deletions pkg/hintrunner/utils/secp_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package utils

import (
"fmt"

"math/big"

"github.com/consensys/gnark-crypto/ecc/stark-curve/fp"
Expand Down Expand Up @@ -29,15 +30,22 @@ func SecPPacked(limbs [3]*fp.Element) (*big.Int, error) {
for idx, limb := range limbs {
limbBig := AsInt(limb)
valueToAddBig := new(big.Int).Exp(baseBig, big.NewInt(int64(idx)), nil)
valueToAddBig.Mul(valueToAddBig, limbBig)
valueToAddBig.Mul(valueToAddBig, &limbBig)
packedBig.Add(packedBig, valueToAddBig)
}

return packedBig, nil
}

func GetBetaBig() *big.Int {
return big.NewInt(7)
func GetBetaBig() big.Int {
return *big.NewInt(7)
}

func GetNBig() big.Int {
// https://github.com/starkware-libs/cairo-lang/blob/efa9648f57568aad8f8a13fbf027d2de7c63c2c0/src/starkware/cairo/common/cairo_secp/secp_utils.py#L9

NBig, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16)
return *NBig
}

func SecPSplit(num *big.Int) ([]*big.Int, error) {
Expand All @@ -63,7 +71,8 @@ func SecPSplit(num *big.Int) ([]*big.Int, error) {
return split, nil
}

func GetSecp256R1_P() (*big.Int, bool) {
func GetSecp256R1_P() (big.Int, bool) {
// 2**256 - 2**224 + 2**192 + 2**96 - 1
return new(big.Int).SetString("115792089210356248762697446949407573530086143415290314195533631308867097853951", 10)
secp256r1_p, ok := new(big.Int).SetString("115792089210356248762697446949407573530086143415290314195533631308867097853951", 10)
return *secp256r1_p, ok
}
1 change: 1 addition & 0 deletions pkg/hintrunner/zero/hintcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ const (
// ------ Signature hints related code ------
verifyECDSASignatureCode string = "ecdsa_builtin.add_signature(ids.ecdsa_ptr.address_, (ids.signature_r, ids.signature_s))"
getPointFromXCode string = "from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack\nx_cube_int = pack(ids.x_cube, PRIME) % SECP_P\ny_square_int = (x_cube_int + ids.BETA) % SECP_P\ny = pow(y_square_int, (SECP_P + 1) // 4, SECP_P)\nif ids.v % 2 == y % 2:\nvalue = y\nelse:\nvalue = (-y) % SECP_P"
divModNSafeDivCode string = "value = k = safe_div(res * b - a, N)"
importSecp256R1PCode string = "from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P as SECP_P"
verifyZeroCode string = "from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack\n\nq, r = divmod(pack(ids.val, PRIME), SECP_P)\nassert r == 0, f\"verify_zero: Invalid input {ids.val.d0, ids.val.d1, ids.val.d2}.\"\nids.q = q % PRIME"

Expand Down
2 changes: 2 additions & 0 deletions pkg/hintrunner/zero/zerohint.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ func GetHintFromCode(program *zero.ZeroProgram, rawHint zero.Hint, hintPC uint64
return createVerifyECDSASignatureHinter(resolver)
case getPointFromXCode:
return createGetPointFromXHinter(resolver)
case divModNSafeDivCode:
return createDivModSafeDivHinter()
case importSecp256R1PCode:
return createImportSecp256R1PHinter()
case verifyZeroCode:
Expand Down
2 changes: 1 addition & 1 deletion pkg/hintrunner/zero/zerohint_math.go
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,7 @@ func newSignedDivRemHint(value, div, bound, r, biased_q hinter.ResOperander) hin
var divBig, boundBig big.Int
divFelt.BigInt(&divBig)
boundFelt.BigInt(&boundBig)
qBig, rBig := new(big.Int).DivMod(intValueBig, &divBig, new(big.Int))
qBig, rBig := new(big.Int).DivMod(&intValueBig, &divBig, new(big.Int))
rFelt := new(fp.Element).SetBigInt(rBig)
rAddr, err := r.GetAddress(vm)
if err != nil {
Expand Down
46 changes: 44 additions & 2 deletions pkg/hintrunner/zero/zerohint_signatures.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ func newGetPointFromXHinter(xCube, v hinter.ResOperander) hinter.Hinter {
xCubeIntBig.Mod(xCubeIntBig, secpBig)

//> y_square_int = (x_cube_int + ids.BETA) % SECP_P
ySquareIntBig := new(big.Int).Add(xCubeIntBig, secp_utils.GetBetaBig())
betaBig := secp_utils.GetBetaBig()
ySquareIntBig := new(big.Int).Add(xCubeIntBig, &betaBig)
ySquareIntBig.Mod(ySquareIntBig, secpBig)

//> y = pow(y_square_int, (SECP_P + 1) // 4, SECP_P)
Expand Down Expand Up @@ -133,6 +134,47 @@ func createGetPointFromXHinter(resolver hintReferenceResolver) (hinter.Hinter, e
return newGetPointFromXHinter(xCube, v), nil
}

func newDivModSafeDivHinter() hinter.Hinter {
return &GenericZeroHinter{
Name: "DivModSafeDivHinter",
Op: func(vm *VM.VirtualMachine, ctx *hinter.HintRunnerContext) error {
//> value = k = safe_div(res * b - a, N)

res, err := ctx.ScopeManager.GetVariableValueAsBigInt("res")
if err != nil {
return err
}
a, err := ctx.ScopeManager.GetVariableValueAsBigInt("a")
if err != nil {
return err
}
b, err := ctx.ScopeManager.GetVariableValueAsBigInt("b")
if err != nil {
return err
}
N, err := ctx.ScopeManager.GetVariableValueAsBigInt("N")
if err != nil {
return err
}
divisor := new(big.Int).Sub(new(big.Int).Mul(res, b), a)
value, err := secp_utils.SafeDiv(divisor, N)
if err != nil {
return err
}
k := new(big.Int).Set(&value)
err = ctx.ScopeManager.AssignVariable("k", k)
if err != nil {
return err
}
return ctx.ScopeManager.AssignVariable("value", &value)
},
}
}

func createDivModSafeDivHinter() (hinter.Hinter, error) {
return newDivModSafeDivHinter(), nil
}

func newImportSecp256R1PHinter() hinter.Hinter {
return &GenericZeroHinter{
Name: "Secp256R1",
Expand All @@ -142,7 +184,7 @@ func newImportSecp256R1PHinter() hinter.Hinter {
if !ok {
return fmt.Errorf("SECP256R1_P failed.")
}
return ctx.ScopeManager.AssignVariable("SECP_P", SECP256R1_PBig)
return ctx.ScopeManager.AssignVariable("SECP_P", &SECP256R1_PBig)
},
}
}
Expand Down
76 changes: 74 additions & 2 deletions pkg/hintrunner/zero/zerohint_signatures_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/NethermindEth/cairo-vm-go/pkg/hintrunner/hinter"
"github.com/NethermindEth/cairo-vm-go/pkg/parsers/starknet"
"github.com/NethermindEth/cairo-vm-go/pkg/utils"

"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -44,8 +45,8 @@ func TestSignatures(t *testing.T) {
},
check: varValueInScopeEquals("value", bigIntString("64828261740814840065360381756190772627110652128289340260788836867053167272156", 10)),
},
// if v % 2 != y % 2:
{
// if v % 2 != y % 2:
operanders: []*hintOperander{
{Name: "xCube.d0", Kind: apRelative, Value: &utils.FeltOne},
{Name: "xCube.d1", Kind: apRelative, Value: &utils.FeltOne},
Expand All @@ -60,8 +61,8 @@ func TestSignatures(t *testing.T) {
},
check: varValueInScopeEquals("value", bigIntString("3754707778961574900176639079436749683878498834289427635045629810524611907876", 10)),
},
// values are 2**86 BASE
{
// values are 2**86 BASE
operanders: []*hintOperander{
{Name: "xCube.d0", Kind: apRelative, Value: feltString("77371252455336267181195264")},
{Name: "xCube.d1", Kind: apRelative, Value: feltString("77371252455336267181195264")},
Expand All @@ -77,6 +78,77 @@ func TestSignatures(t *testing.T) {
check: varValueInScopeEquals("value", bigIntString("64330220386510520462271671435567806262107470356169873352512014089172394266548", 10)),
},
},
"DivModNSafeDivHint": {
{
// zero quotient
operanders: []*hintOperander{},
ctxInit: func(ctx *hinter.HintRunnerContext) {
ctx.ScopeManager.EnterScope(map[string]any{})
err := ctx.ScopeManager.AssignVariables(map[string]any{
"res": bigIntString("0", 10),
"a": bigIntString("0", 10),
"b": bigIntString("0", 10),
"N": bigIntString("1", 10),
})
if err != nil {
t.Fatal(err)
}
},
makeHinter: func(ctx *hintTestContext) hinter.Hinter {
return newDivModSafeDivHinter()
},
check: varListInScopeEquals(map[string]any{
"value": bigIntString("0", 10),
"k": bigIntString("0", 10),
}),
},
{
// negative quotient
operanders: []*hintOperander{},
ctxInit: func(ctx *hinter.HintRunnerContext) {
ctx.ScopeManager.EnterScope(map[string]any{})
err := ctx.ScopeManager.AssignVariables(map[string]any{
"res": bigIntString("1", 10),
"a": bigIntString("2", 10),
"b": bigIntString("1", 10),
"N": bigIntString("1", 10),
})
if err != nil {
t.Fatal(err)
}
},
makeHinter: func(ctx *hintTestContext) hinter.Hinter {
return newDivModSafeDivHinter()
},
check: varListInScopeEquals(map[string]any{
"value": bigIntString("-1", 10),
"k": bigIntString("-1", 10),
}),
},
{
// positive quotient
operanders: []*hintOperander{},
ctxInit: func(ctx *hinter.HintRunnerContext) {
ctx.ScopeManager.EnterScope(map[string]any{})
err := ctx.ScopeManager.AssignVariables(map[string]any{
"res": bigIntString("10", 10),
"a": bigIntString("20", 10),
"b": bigIntString("30", 10),
"N": bigIntString("2", 10),
})
if err != nil {
t.Fatal(err)
}
},
makeHinter: func(ctx *hintTestContext) hinter.Hinter {
return newDivModSafeDivHinter()
},
check: varListInScopeEquals(map[string]any{
"value": bigIntString("140", 10),
"k": bigIntString("140", 10),
}),
},
},
"ImportSecp256R1P": {
{
operanders: []*hintOperander{},
Expand Down
8 changes: 8 additions & 0 deletions pkg/hintrunner/zero/zerohint_utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,3 +203,11 @@ func errorIsNil(t *testing.T, ctx *hintTestContext, err error) {
t.Fatalf("expected a nil error, got: %v", err)
}
}

func varListInScopeEquals(expectedValues map[string]any) func(t *testing.T, ctx *hintTestContext) {
return func(t *testing.T, ctx *hintTestContext) {
for varName, expected := range expectedValues {
varValueInScopeEquals(varName, expected)(t, ctx)
}
}
}

0 comments on commit 7d292fa

Please sign in to comment.