Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement GET_POINT_FROM_X hint #298

Merged
merged 12 commits into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
CAIRO_VM_CLI:=cairo-vm/target/release/cairo-vm-cli

$(CAIRO_VM_CLI):
git clone --depth 1 -b v0.8.5 https://github.com/lambdaclass/cairo-vm
git clone --depth 1 -b v0.8.7 https://github.com/lambdaclass/cairo-vm
cd cairo-vm; cargo b --release --bin cairo-vm-cli

# Create proof mode programs.
Expand Down
24 changes: 24 additions & 0 deletions cairo_programs/signature.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
%builtins range_check

from starkware.cairo.common.cairo_secp.signature import div_mod_n, get_point_from_x
from starkware.cairo.common.cairo_secp.bigint import BigInt3

func main{range_check_ptr: felt}() {
let a: BigInt3 = BigInt3(100, 99, 98);
let b: BigInt3 = BigInt3(10, 9, 8);
let (res) = div_mod_n(a, b);
assert res.d0 = 3413472211745629263979533;
assert res.d1 = 17305268010345238170172332;
assert res.d2 = 11991751872105858217578135;

let x: BigInt3 = BigInt3(100, 99, 98);
let v: felt = 10;
let (point) = get_point_from_x(x, v);
assert point.x.d0 = 100;
assert point.x.d1 = 99;
assert point.x.d2 = 98;
assert point.y.d0 = 50471654703173585387369794;
assert point.y.d1 = 68898944762041070370364387;
assert point.y.d2 = 16932612780945290933872774;
return ();
}
12 changes: 12 additions & 0 deletions pkg/hints/hint_codes/signature_hint_codes.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,15 @@ const DIV_MOD_N_SAFE_DIV = "value = k = safe_div(res * b - a, N)"
const DIV_MOD_N_SAFE_DIV_PLUS_ONE = "value = k_plus_one = safe_div(res * b - a, N) + 1"

const XS_SAFE_DIV = "value = k = safe_div(res * s - x, N)"

const GET_POINT_FROM_X = `from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack

x_cube_int = pack(ids.x_cube, PRIME) % SECP_P
y_square_int = (x_cube_int + ids.BETA) % SECP_P
y = pow(y_square_int, (SECP_P + 1) // 4, SECP_P)

# We need to decide whether to take y or SECP_P - y.
if ids.v % 2 == y % 2:
value = y
else:
value = (-y) % SECP_P`
2 changes: 2 additions & 0 deletions pkg/hints/hint_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any,
return divModNSafeDiv(data.Ids, execScopes, "a", "b", false)
case DIV_MOD_N_SAFE_DIV_PLUS_ONE:
return divModNSafeDiv(data.Ids, execScopes, "a", "b", true)
case GET_POINT_FROM_X:
return getPointFromX(data.Ids, vm, execScopes, constants)
case VERIFY_ZERO_EXTERNAL_SECP:
return verifyZeroWithExternalConst(*vm, *execScopes, data.Ids)
case FAST_EC_ADD_ASSIGN_NEW_X:
Expand Down
31 changes: 31 additions & 0 deletions pkg/hints/signature_hints.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"math/big"

. "github.com/lambdaclass/cairo-vm.go/pkg/hints/hint_utils"
. "github.com/lambdaclass/cairo-vm.go/pkg/lambdaworks"
. "github.com/lambdaclass/cairo-vm.go/pkg/types"
"github.com/lambdaclass/cairo-vm.go/pkg/utils"
. "github.com/lambdaclass/cairo-vm.go/pkg/vm"
Expand Down Expand Up @@ -82,3 +83,33 @@ func divModNSafeDiv(ids IdsManager, scopes *ExecutionScopes, aAlias string, bAli
scopes.AssignOrUpdateVariable("value", *value)
return nil
}

func getPointFromX(ids IdsManager, vm *VirtualMachine, scopes *ExecutionScopes, constants *map[string]Felt) error {
// Handle scope & ids variables
secpP := SECP_P()
scopes.AssignOrUpdateVariable("SECP_P", secpP)
betaFelt, err := ids.GetConst("BETA", constants)
if err != nil {
return err
}
beta := new(big.Int).Mod(betaFelt.ToBigInt(), &secpP)
xCubeIntUnpacked, err := Uint384FromVarName("x_cube", ids, vm)
if err != nil {
return err
}
xCube := xCubeIntUnpacked.Pack86()
vFelt, err := ids.GetFelt("v", vm)
v := vFelt.ToBigInt()
if err != nil {
return err
}
// Hint logic
yCube := new(big.Int).Mod(new(big.Int).Add(&xCube, beta), &secpP)
// y = (yCube ** ((SECP_P + 1) << 2)) % SECP_P
y := new(big.Int).Exp(yCube, new(big.Int).Rsh(new(big.Int).Add(&secpP, big.NewInt(1)), 2), &secpP)
if utils.IsEven(v) != utils.IsEven(y) {
y = new(big.Int).Sub(&secpP, y)
}
scopes.AssignOrUpdateVariable("value", *y)
return nil
}
76 changes: 76 additions & 0 deletions pkg/hints/signature_hints_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,79 @@ func TestDivModSafeDivPlusOneOk(t *testing.T) {
t.Error("Wrong/No scope value val")
}
}

func TestGetPointFromXOk(t *testing.T) {
vm := NewVirtualMachine()
vm.Segments.AddSegment()
idsManager := SetupIdsForTest(
map[string][]*MaybeRelocatable{
"v": {
NewMaybeRelocatableFelt(FeltFromUint(18)),
},
"x_cube": {
NewMaybeRelocatableFelt(FeltFromUint64(2147483647)),
NewMaybeRelocatableFelt(FeltFromUint64(2147483647)),
NewMaybeRelocatableFelt(FeltFromUint64(2147483647)),
},
},
vm,
)
constants := SetupConstantsForTest(map[string]Felt{
"BETA": FeltFromUint(7),
}, &idsManager)
hintProcessor := CairoVmHintProcessor{}
hintData := any(HintData{
Ids: idsManager,
Code: GET_POINT_FROM_X,
})
scopes := NewExecutionScopes()
err := hintProcessor.ExecuteHint(vm, &hintData, &constants, scopes)
if err != nil {
t.Errorf("GET_POINT_FROM_X hint test failed with error %s", err)
}
// Check result in scope
expectedValue, _ := new(big.Int).SetString("21517397797248348844406833268402983856262903417026833897388175962266357959124", 10)

value, err := FetchScopeVar[big.Int]("value", scopes)
if err != nil || value.Cmp(expectedValue) != 0 {
t.Errorf("Wrong/No scope var value.\n Expected %v, got: %v", expectedValue, &value)
}
}

func TestGetPointFromXNegativeY(t *testing.T) {
vm := NewVirtualMachine()
vm.Segments.AddSegment()
idsManager := SetupIdsForTest(
map[string][]*MaybeRelocatable{
"v": {
NewMaybeRelocatableFelt(FeltOne()),
},
"x_cube": {
NewMaybeRelocatableFelt(FeltFromUint64(2147483647)),
NewMaybeRelocatableFelt(FeltFromUint64(2147483647)),
NewMaybeRelocatableFelt(FeltFromUint64(2147483647)),
},
},
vm,
)
constants := SetupConstantsForTest(map[string]Felt{
"BETA": FeltFromUint(7),
}, &idsManager)
hintProcessor := CairoVmHintProcessor{}
hintData := any(HintData{
Ids: idsManager,
Code: GET_POINT_FROM_X,
})
scopes := NewExecutionScopes()
err := hintProcessor.ExecuteHint(vm, &hintData, &constants, scopes)
if err != nil {
t.Errorf("GET_POINT_FROM_X hint test failed with error %s", err)
}
// Check result in scope
expectedValue, _ := new(big.Int).SetString("94274691440067846579164151740284923997007081248613730142069408045642476712539", 10)

value, err := FetchScopeVar[big.Int]("value", scopes)
if err != nil || value.Cmp(expectedValue) != 0 {
t.Errorf("Wrong/No scope var value.\n Expected %v, got: %v", expectedValue, &value)
}
}
5 changes: 5 additions & 0 deletions pkg/utils/math_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ func Igcdex(a *big.Int, b *big.Int) (*big.Int, *big.Int, *big.Int) {
}
}

func IsEven(n *big.Int) bool {
res := new(big.Int).And(n, big.NewInt(1))
return res.Cmp(big.NewInt(0)) != 0
}

func ISqrt(x *big.Int) (*big.Int, error) {
if x.Sign() == -1 {
return nil, errors.Errorf("Expected x: %s to be non-negative", x)
Expand Down
4 changes: 4 additions & 0 deletions pkg/vm/cairo_run/cairo_run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,10 @@ func TestDivModN(t *testing.T) {
testProgram("div_mod_n", t)
}

func TestSignature(t *testing.T) {
testProgram("signature", t)
}

func TestEcDoubleAssign(t *testing.T) {
testProgram("ec_double_assign", t)
}
Expand Down