From c58f01e09ca83422e32bcb7e2eded001b2abbc2c Mon Sep 17 00:00:00 2001 From: fmoletta <99273364+fmoletta@users.noreply.github.com> Date: Wed, 25 Oct 2023 14:42:27 +0300 Subject: [PATCH] Implement `REDUCE+` & `VERIFY_ZERO+` hints (#307) * Add ec hints * Implement hints * Add the hints to the processor * Test pack86 function * Test hint * Delete debug info, Test ec negative op * Second hint test * Test embedded hint * Change to Camel case * Implement slope hints * Fix format * Delete github conflict string * Tests hints * Tests hints slopes * Rename misleading name function * Fix function name * Fix error in function call * Delete debug info * Delete unused import * Secp hints * Secpr21 * Add it to the hint processor * Hints secp * bigint3 nondet * Zero verify * Merge main * Add hint to hint processor * Debug info * Prints * Test verify with unit test * Debug unit test * Test verify zero with debug * Non det big 3 test * Modify test to use ids manager * Add hint codes * Implement base hint * Add hints * Add hints to ExecuteHint * debug info * Fix broken test * Move integration test to cairo_run_test.go * Move file from hints_utils and rename * Delete debug * Return error of IdsData.Insert * Change to camel case * Add unit test * Add unit test * Add hint codes * Implement hint * Add SafeDivBig * Add generic way to fetch scope variables * Add generic fetch * Add generic way to fetch scope variables * Use more specific error * Add hints to ExecuteHint * Add extra hint * Fix logic, add unit test * Add unit test * use boolean flag instead or arg * Fix scope var name * Fix scope var name in tests * Make FetchScopeVar work despite references * Revert "Make FetchScopeVar work despite references" This reverts commit 69993be48a9a8fea8d241450463e4dd240091056. * Handle scope variables as big.Int instead of *big.Int * Fix merge cnflicts * Fix tests * Implement Igcdex + add tests * Implement DivMod * Use DivMod instead of Div + Mod * Dont modify the original value in bigint3_split function * Push test file * Remove redundant check * Implement Reduce_V1 hint * Fix file names * Add unit test * Add testing util CheckScopeVar` * Refactor test * Merge math_utils/utils & utils/math_utils * Restore gitignore * Add hint + tests * Add test file * Add REDUCE_V2 hint * Implement verify_zero hints * Add verify_zero hints * Fix fmt * Implement `GET_POINT_FROM_X` hint (#298) * Add hint code * Implement hint * Add unit test * Fix test value * Fix hint logic * Add unit test * Add integration test * fmt * Fix * Bump cairo-vm version --------- Co-authored-by: Milton Co-authored-by: mmsc2 <88055861+mmsc2@users.noreply.github.com> Co-authored-by: Mariano A. Nicolini Co-authored-by: Pedro Fontana Co-authored-by: toni-calvin --- Makefile | 2 +- cairo_programs/reduce.cairo | 127 +++++++++++ cairo_programs/signature.cairo | 24 ++ pkg/hints/hint_codes/secp_hint_codes.go | 39 ++++ pkg/hints/hint_codes/secp_p_hint.go | 6 - pkg/hints/hint_codes/signature_hint_codes.go | 12 + pkg/hints/hint_processor.go | 13 ++ pkg/hints/secp_hints.go | 64 ++++++ pkg/hints/secp_hints_test.go | 217 +++++++++++++++++++ pkg/hints/signature_hints.go | 31 +++ pkg/hints/signature_hints_test.go | 76 +++++++ pkg/utils/math_utils.go | 5 + pkg/vm/cairo_run/cairo_run_test.go | 7 + 13 files changed, 616 insertions(+), 7 deletions(-) create mode 100644 cairo_programs/reduce.cairo create mode 100644 cairo_programs/signature.cairo create mode 100644 pkg/hints/hint_codes/secp_hint_codes.go delete mode 100644 pkg/hints/hint_codes/secp_p_hint.go create mode 100644 pkg/hints/secp_hints.go create mode 100644 pkg/hints/secp_hints_test.go diff --git a/Makefile b/Makefile index 9954a52f..467040f7 100644 --- a/Makefile +++ b/Makefile @@ -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. diff --git a/cairo_programs/reduce.cairo b/cairo_programs/reduce.cairo new file mode 100644 index 00000000..486e8b22 --- /dev/null +++ b/cairo_programs/reduce.cairo @@ -0,0 +1,127 @@ +%builtins range_check + +from starkware.cairo.common.cairo_secp.bigint import BigInt3, UnreducedBigInt3, nondet_bigint3 + +const BASE = 2 ** 86; +const SECP_REM = 19; + +func verify_zero{range_check_ptr}(val: UnreducedBigInt3) { + let q = [ap]; + %{ + from starkware.cairo.common.cairo_secp.secp_utils import pack + SECP_P = 2**255-19 + to_assert = pack(ids.val, PRIME) + q, r = divmod(pack(ids.val, PRIME), SECP_P) + assert r == 0, f"verify_zero: Invalid input {ids.val.d0, ids.val.d1, ids.val.d2}." + ids.q = q % PRIME + %} + let q_biased = [ap + 1]; + q_biased = q + 2 ** 127, ap++; + [range_check_ptr] = q_biased, ap++; + // This implies that q is in the range [-2**127, 2**127). + + tempvar r1 = (val.d0 + q * SECP_REM) / BASE; + assert [range_check_ptr + 1] = r1 + 2 ** 127; + // This implies that r1 is in the range [-2**127, 2**127). + // Therefore, r1 * BASE is in the range [-2**213, 2**213). + // By the soundness assumption, val.d0 is in the range (-2**250, 2**250). + // This implies that r1 * BASE = val.d0 + q * SECP_REM (as integers). + + tempvar r2 = (val.d1 + r1) / BASE; + assert [range_check_ptr + 2] = r2 + 2 ** 127; + // Similarly, this implies that r2 * BASE = val.d1 + r1 (as integers). + // Therefore, r2 * BASE**2 = val.d1 * BASE + r1 * BASE. + + assert val.d2 = q * (BASE / 8) - r2; + // Similarly, this implies that q * BASE / 4 = val.d2 + r2 (as integers). + // Therefore, + // q * BASE**3 / 4 = val.d2 * BASE**2 + r2 * BASE ** 2 = + // val.d2 * BASE**2 + val.d1 * BASE + r1 * BASE = + // val.d2 * BASE**2 + val.d1 * BASE + val.d0 + q * SECP_REM = + // val + q * SECP_REM. + // Hence, val = q * (BASE**3 / 4 - SECP_REM) = q * (2**256 - SECP_REM) = q * secp256k1_prime. + + let range_check_ptr = range_check_ptr + 3; + return (); +} + +// Receives an unreduced number, and returns a number that is equal to the original number mod +// Ed25519 prime and in reduced form (meaning every limb is in the range [0, BASE)). +// +// Completeness assumption: x's limbs are in the range (-2**210.99, 2**210.99). +// Soundness assumption: x's limbs are in the range (-2**249.99, 2**249.99). +func reduce_ed25519{range_check_ptr}(x: UnreducedBigInt3) -> (reduced_x: BigInt3) { + %{ + from starkware.cairo.common.cairo_secp.secp_utils import pack + SECP_P=2**255-19 + + value = pack(ids.x, PRIME) % SECP_P + %} + let (reduced_x: BigInt3) = nondet_bigint3(); + + verify_zero( + UnreducedBigInt3(d0=x.d0 - reduced_x.d0, d1=x.d1 - reduced_x.d1, d2=x.d2 - reduced_x.d2) + ); + return (reduced_x=reduced_x); +} + +func test_reduce_ed25519{range_check_ptr}() { + let x = UnreducedBigInt3(0, 0, 0); + let (res) = reduce_ed25519(x); + assert res = BigInt3(0, 0, 0); + + let x = UnreducedBigInt3( + 1113660525233188137217661511617697775365785011829423399545361443, + 1243997169368861650657124871657865626433458458266748922940703512, + 1484456708474143440067316914074363277495967516029110959982060577, + ); + let (res) = reduce_ed25519(x); + assert res = BigInt3( + 42193159084937489098474581, 19864776835133205750023223, 916662843592479469328893 + ); + + return (); +} + +func reduce_v2{range_check_ptr}(x: UnreducedBigInt3) -> (reduced_x: BigInt3) { + let orig_x = x; + %{ from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P as SECP_P %} + %{ + from starkware.cairo.common.cairo_secp.secp_utils import pack + value = pack(ids.x, PRIME) % SECP_P + %} + let (reduced_x: BigInt3) = nondet_bigint3(); + + verify_zero( + UnreducedBigInt3( + d0=orig_x.d0 - reduced_x.d0, + d1=orig_x.d1 - reduced_x.d1, + d2=orig_x.d2 - reduced_x.d2 + ) + ); + return (reduced_x=reduced_x); +} + +func main{range_check_ptr}() { + test_reduce_ed25519(); + + // reduce_v2 tests + let x = UnreducedBigInt3(0, 0, 0); + let (reduce_v2_a) = reduce_v2(x); + assert reduce_v2_a = BigInt3( + 0, 0, 0 + ); + + let y = UnreducedBigInt3(12354, 745634534, 81298789312879123); + let (reduce_v2_b) = reduce_v2(y); + assert reduce_v2_b = BigInt3( + 12354, 745634534, 81298789312879123 + ); + + let z = UnreducedBigInt3(12354812987893128791212331231233, 7453123123123123312634534, 8129224990312325879); + let (reduce_v2_c) = reduce_v2(z); + assert reduce_v2_c = BigInt3( + 16653320122975184709085185, 7453123123123123312794216, 8129224990312325879 + ); + return (); +} diff --git a/cairo_programs/signature.cairo b/cairo_programs/signature.cairo new file mode 100644 index 00000000..b6f4a0d4 --- /dev/null +++ b/cairo_programs/signature.cairo @@ -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 (); +} diff --git a/pkg/hints/hint_codes/secp_hint_codes.go b/pkg/hints/hint_codes/secp_hint_codes.go new file mode 100644 index 00000000..2698a95a --- /dev/null +++ b/pkg/hints/hint_codes/secp_hint_codes.go @@ -0,0 +1,39 @@ +package hint_codes + +const IMPORT_SECP256R1_ALPHA = "from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_ALPHA as ALPHA" + +const IMPORT_SECP256R1_N = "from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_N as N" + +const IMPORT_SECP256R1_P = "from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P as SECP_P" + +const VERIFY_ZERO_EXTERNAL_SECP = "from starkware.cairo.common.cairo_secp.secp_utils import 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" + +const REDUCE_V1 = `from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack + +value = pack(ids.x, PRIME) % SECP_P` + +const REDUCE_V2 = `from starkware.cairo.common.cairo_secp.secp_utils import pack +value = pack(ids.x, PRIME) % SECP_P` + +const REDUCE_ED25519 = `from starkware.cairo.common.cairo_secp.secp_utils import pack +SECP_P=2**255-19 + +value = pack(ids.x, PRIME) % SECP_P` + +const VERIFY_ZERO_V1 = `from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack + +q, r = divmod(pack(ids.val, PRIME), SECP_P) +assert r == 0, f"verify_zero: Invalid input {ids.val.d0, ids.val.d1, ids.val.d2}." +ids.q = q % PRIME` + +const VERIFY_ZERO_V2 = `from starkware.cairo.common.cairo_secp.secp_utils import SECP_P +q, r = divmod(pack(ids.val, PRIME), SECP_P) +assert r == 0, f"verify_zero: Invalid input {ids.val.d0, ids.val.d1, ids.val.d2}." +ids.q = q % PRIME` + +const VERIFY_ZERO_V3 = `from starkware.cairo.common.cairo_secp.secp_utils import pack +SECP_P = 2**255-19 +to_assert = pack(ids.val, PRIME) +q, r = divmod(pack(ids.val, PRIME), SECP_P) +assert r == 0, f"verify_zero: Invalid input {ids.val.d0, ids.val.d1, ids.val.d2}." +ids.q = q % PRIME` diff --git a/pkg/hints/hint_codes/secp_p_hint.go b/pkg/hints/hint_codes/secp_p_hint.go deleted file mode 100644 index 2d6dac14..00000000 --- a/pkg/hints/hint_codes/secp_p_hint.go +++ /dev/null @@ -1,6 +0,0 @@ -package hint_codes - -const IMPORT_SECP256R1_ALPHA = "from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_ALPHA as ALPHA" -const IMPORT_SECP256R1_N = "from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_N as N" -const IMPORT_SECP256R1_P = "from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P as SECP_P" -const VERIFY_ZERO_EXTERNAL_SECP = "from starkware.cairo.common.cairo_secp.secp_utils import 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" diff --git a/pkg/hints/hint_codes/signature_hint_codes.go b/pkg/hints/hint_codes/signature_hint_codes.go index 37b37051..b1e4e189 100644 --- a/pkg/hints/hint_codes/signature_hint_codes.go +++ b/pkg/hints/hint_codes/signature_hint_codes.go @@ -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` diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index 7abcc972..225f4c50 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -4,6 +4,7 @@ import ( "strings" . "github.com/lambdaclass/cairo-vm.go/pkg/hints/hint_codes" + "github.com/lambdaclass/cairo-vm.go/pkg/hints/hint_utils" . "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/parser" @@ -218,6 +219,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: @@ -228,6 +231,16 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, return fastEcAddAssignNewX(data.Ids, vm, execScopes, "pt0", "pt1", SECP_P()) case FAST_EC_ADD_ASSIGN_NEW_Y: return fastEcAddAssignNewY(execScopes) + case REDUCE_V1: + return reduceV1(data.Ids, vm, execScopes) + case REDUCE_V2: + return reduceV2(data.Ids, vm, execScopes) + case REDUCE_ED25519: + return reduceED25519(data.Ids, vm, execScopes) + case VERIFY_ZERO_V1, VERIFY_ZERO_V2: + return verifyZero(data.Ids, vm, execScopes, hint_utils.SECP_P()) + case VERIFY_ZERO_V3: + return verifyZero(data.Ids, vm, execScopes, hint_utils.SECP_P_V2()) case BLAKE2S_COMPUTE: return blake2sCompute(data.Ids, vm) default: diff --git a/pkg/hints/secp_hints.go b/pkg/hints/secp_hints.go new file mode 100644 index 00000000..7f2fba87 --- /dev/null +++ b/pkg/hints/secp_hints.go @@ -0,0 +1,64 @@ +package hints + +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/vm" + "github.com/lambdaclass/cairo-vm.go/pkg/vm/memory" + "github.com/pkg/errors" +) + +func reduceV1(ids IdsManager, vm *VirtualMachine, scopes *ExecutionScopes) error { + secpP := SECP_P() + scopes.AssignOrUpdateVariable("SECP_P", secpP) + value, err := Uint384FromVarName("x", ids, vm) + if err != nil { + return err + } + packedValue := value.Pack86() + scopes.AssignOrUpdateVariable("value", *new(big.Int).Mod(&packedValue, &secpP)) + return nil +} + +func reduceV2(ids IdsManager, vm *VirtualMachine, scopes *ExecutionScopes) error { + secpP, err := FetchScopeVar[big.Int]("SECP_P", scopes) + if err != nil { + return err + } + value, err := Uint384FromVarName("x", ids, vm) + if err != nil { + return err + } + packedValue := value.Pack86() + scopes.AssignOrUpdateVariable("value", *new(big.Int).Mod(&packedValue, &secpP)) + return nil +} + +func reduceED25519(ids IdsManager, vm *VirtualMachine, scopes *ExecutionScopes) error { + secpP := SECP_P_V2() + scopes.AssignOrUpdateVariable("SECP_P", secpP) + value, err := Uint384FromVarName("x", ids, vm) + if err != nil { + return err + } + packedValue := value.Pack86() + scopes.AssignOrUpdateVariable("value", *new(big.Int).Mod(&packedValue, &secpP)) + return nil +} + +func verifyZero(ids IdsManager, vm *VirtualMachine, scopes *ExecutionScopes, secpP big.Int) error { + scopes.AssignOrUpdateVariable("SECP_P", secpP) + valUnpacked, err := Uint384FromVarName("val", ids, vm) + if err != nil { + return err + } + val := valUnpacked.Pack86() + q, r := new(big.Int).DivMod(&val, &secpP, new(big.Int)) + if r.Cmp(big.NewInt(0)) != 0 { + return errors.Errorf("verify_zero: Invalid input %s", val.Text(10)) + } + return ids.Insert("q", memory.NewMaybeRelocatableFelt(lambdaworks.FeltFromBigInt(q)), vm) +} diff --git a/pkg/hints/secp_hints_test.go b/pkg/hints/secp_hints_test.go new file mode 100644 index 00000000..a1556d04 --- /dev/null +++ b/pkg/hints/secp_hints_test.go @@ -0,0 +1,217 @@ +package hints_test + +import ( + "math/big" + "testing" + + . "github.com/lambdaclass/cairo-vm.go/pkg/hints" + . "github.com/lambdaclass/cairo-vm.go/pkg/hints/hint_codes" + . "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/vm" + . "github.com/lambdaclass/cairo-vm.go/pkg/vm/memory" +) + +func TestReduceV1(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "x": { + NewMaybeRelocatableFelt(FeltFromDecString("6")), + NewMaybeRelocatableFelt(FeltFromDecString("6")), + NewMaybeRelocatableFelt(FeltFromDecString("6")), + }, + }, + vm, + ) + scopes := NewExecutionScopes() + + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: REDUCE_V1, + }) + + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("REDUCE_V1 hint failed with error %s", err) + } + // Checked scope variables + CheckScopeVar[big.Int]("SECP_P", SECP_P(), scopes, t) + + valueUnpacked := Uint384{Limbs: []Felt{FeltFromUint(6), FeltFromUint(6), FeltFromUint(6)}} + CheckScopeVar[big.Int]("value", valueUnpacked.Pack86(), scopes, t) +} + +func TestReduceV2(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "x": { + NewMaybeRelocatableFelt(FeltFromDecString("6")), + NewMaybeRelocatableFelt(FeltFromDecString("6")), + NewMaybeRelocatableFelt(FeltFromDecString("6")), + }, + }, + vm, + ) + scopes := NewExecutionScopes() + scopes.AssignOrUpdateVariable("SECP_P", SECP_P()) + + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: REDUCE_V2, + }) + + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("REDUCE_V2 hint failed with error %s", err) + } + + valueUnpacked := Uint384{Limbs: []Felt{FeltFromUint(6), FeltFromUint(6), FeltFromUint(6)}} + CheckScopeVar[big.Int]("value", valueUnpacked.Pack86(), scopes, t) +} + +func TestReduceED(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "x": { + NewMaybeRelocatableFelt(FeltFromDecString("6")), + NewMaybeRelocatableFelt(FeltFromDecString("6")), + NewMaybeRelocatableFelt(FeltFromDecString("6")), + }, + }, + vm, + ) + scopes := NewExecutionScopes() + + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: REDUCE_ED25519, + }) + + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("REDUCE_ED25519 hint failed with error %s", err) + } + // Checked scope variables + CheckScopeVar[big.Int]("SECP_P", SECP_P_V2(), scopes, t) + + valueUnpacked := Uint384{Limbs: []Felt{FeltFromUint(6), FeltFromUint(6), FeltFromUint(6)}} + CheckScopeVar[big.Int]("value", valueUnpacked.Pack86(), scopes, t) +} + +func TestVerifyZeroV1(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "val": { + NewMaybeRelocatableFelt(FeltFromDecString("0")), + NewMaybeRelocatableFelt(FeltFromDecString("0")), + NewMaybeRelocatableFelt(FeltFromDecString("0")), + }, + "q": {nil, nil, nil}, + }, + vm, + ) + scopes := NewExecutionScopes() + + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: VERIFY_ZERO_V1, + }) + + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("VERIFY_ZERO_V1 hint failed with error %s", err) + } + // Check scope variables + CheckScopeVar[big.Int]("SECP_P", SECP_P(), scopes, t) + // Check ids variables + expectedQ := FeltZero() + idsQ, err := idsManager.GetFelt("q", vm) + if err != nil || expectedQ.Cmp(idsQ) != 0 { + t.Error("Wrong/No ids.q") + } +} + +func TestVerifyZeroV2(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "val": { + NewMaybeRelocatableFelt(FeltFromDecString("0")), + NewMaybeRelocatableFelt(FeltFromDecString("0")), + NewMaybeRelocatableFelt(FeltFromDecString("0")), + }, + "q": {nil, nil, nil}, + }, + vm, + ) + scopes := NewExecutionScopes() + + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: VERIFY_ZERO_V2, + }) + + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("VERIFY_ZERO_V2 hint failed with error %s", err) + } + // Check scope variables + CheckScopeVar[big.Int]("SECP_P", SECP_P(), scopes, t) + // Check ids variables + expectedQ := FeltZero() + idsQ, err := idsManager.GetFelt("q", vm) + if err != nil || expectedQ.Cmp(idsQ) != 0 { + t.Error("Wrong/No ids.q") + } +} + +func TestVerifyZeroV3(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "val": { + NewMaybeRelocatableFelt(FeltFromDecString("0")), + NewMaybeRelocatableFelt(FeltFromDecString("0")), + NewMaybeRelocatableFelt(FeltFromDecString("0")), + }, + "q": {nil, nil, nil}, + }, + vm, + ) + scopes := NewExecutionScopes() + + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: VERIFY_ZERO_V3, + }) + + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("VERIFY_ZERO_V3 hint failed with error %s", err) + } + // Check scope variables + CheckScopeVar[big.Int]("SECP_P", SECP_P_V2(), scopes, t) + // Check ids variables + expectedQ := FeltZero() + idsQ, err := idsManager.GetFelt("q", vm) + if err != nil || expectedQ.Cmp(idsQ) != 0 { + t.Error("Wrong/No ids.q") + } +} diff --git a/pkg/hints/signature_hints.go b/pkg/hints/signature_hints.go index 37b57f7f..60340204 100644 --- a/pkg/hints/signature_hints.go +++ b/pkg/hints/signature_hints.go @@ -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" @@ -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 +} diff --git a/pkg/hints/signature_hints_test.go b/pkg/hints/signature_hints_test.go index 4c3de522..23c648e4 100644 --- a/pkg/hints/signature_hints_test.go +++ b/pkg/hints/signature_hints_test.go @@ -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) + } +} diff --git a/pkg/utils/math_utils.go b/pkg/utils/math_utils.go index 7f011a6c..54ce207e 100644 --- a/pkg/utils/math_utils.go +++ b/pkg/utils/math_utils.go @@ -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) diff --git a/pkg/vm/cairo_run/cairo_run_test.go b/pkg/vm/cairo_run/cairo_run_test.go index 4fc0fc75..9a459b4d 100644 --- a/pkg/vm/cairo_run/cairo_run_test.go +++ b/pkg/vm/cairo_run/cairo_run_test.go @@ -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) } @@ -353,6 +357,9 @@ func TestKeccakAddUint256(t *testing.T) { testProgram("keccak_add_uint256", t) } +func TestReduce(t *testing.T) { + testProgram("reduce", t) +} func TestBlake2sHelloWorldHash(t *testing.T) { testProgram("blake2s_hello_world_hash", t) }