diff --git a/pkg/hints/bigint_hint.go b/pkg/hints/bigint_hint.go index a13fc098..d29a5817 100644 --- a/pkg/hints/bigint_hint.go +++ b/pkg/hints/bigint_hint.go @@ -6,7 +6,9 @@ import ( . "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/math_utils" . "github.com/lambdaclass/cairo-vm.go/pkg/types" + . "github.com/lambdaclass/cairo-vm.go/pkg/utils" . "github.com/lambdaclass/cairo-vm.go/pkg/vm" "github.com/lambdaclass/cairo-vm.go/pkg/vm/memory" ) @@ -20,8 +22,8 @@ Implements hint: %} */ -func NondetBigInt3(virtual_machine VirtualMachine, execScopes ExecutionScopes, idsData IdsManager) error { - resRelloc, err := idsData.GetAddr("res", &virtual_machine) +func NondetBigInt3(vm VirtualMachine, execScopes ExecutionScopes, idsData IdsManager) error { + resRelloc, err := idsData.GetAddr("res", &vm) if err != nil { return err } @@ -47,10 +49,142 @@ func NondetBigInt3(virtual_machine VirtualMachine, execScopes ExecutionScopes, i arg = append(arg, *m) } - _, loadErr := virtual_machine.Segments.LoadData(resRelloc, &arg) + _, loadErr := vm.Segments.LoadData(resRelloc, &arg) if loadErr != nil { return loadErr } return nil } + +/// Implements hint: +/// ```python +/// k = safe_div(res * y - x, p) +/// value = k if k > 0 else 0 - k +/// ids.flag = 1 if k > 0 else 0 +/// ``` + +func SafeDivBigint(vm *VirtualMachine, execScopes *ExecutionScopes, idsData IdsManager) error { + resUncast, err := execScopes.Get("res") + if err != nil { + return err + } + res, ok := resUncast.(big.Int) + if !ok { + return errors.New("Could not cast res value in SafeDivBigint") + } + + yUncast, err := execScopes.Get("y") + if err != nil { + return err + } + y, ok := yUncast.(big.Int) + if !ok { + return errors.New("Could not cast y value in SafeDivBigint") + } + + xUncast, err := execScopes.Get("x") + if err != nil { + return err + } + x, ok := xUncast.(big.Int) + if !ok { + return errors.New("Could not cast x value in SafeDivBigint") + } + + pUncast, err := execScopes.Get("p") + if err != nil { + return err + } + p, ok := pUncast.(big.Int) + if !ok { + return errors.New("Could not cast p value in SafeDivBigint") + } + + param_x := new(big.Int).Mul(&res, &y) + param_x.Sub(param_x, &x) + + k, err := SafeDivBig(param_x, &p) + if err != nil { + return err + } + + var value big.Int + var flag lambdaworks.Felt + + // check if k is positive + if k.Cmp(big.NewInt(0)) == 1 { + value = *k + flag = lambdaworks.FeltFromUint(1) + } else { + value = *new(big.Int).Neg(k) + flag = lambdaworks.FeltFromUint(0) + } + + execScopes.AssignOrUpdateVariable("k", *k) + execScopes.AssignOrUpdateVariable("value", value) + + val := memory.NewMaybeRelocatableFelt(flag) + idsData.Insert("flag", val, vm) + + return nil +} + +func calculateX(vm *VirtualMachine, idsData IdsManager) (big.Int, error) { + x_bigint5, err := LimbsFromVarName(5, "x", idsData, vm) + if err != nil { + return big.Int{}, err + } + // pack only takes the first three limbs + x0 := x_bigint5[0] + x1 := x_bigint5[1] + x2 := x_bigint5[2] + + var limbs = []lambdaworks.Felt{x0, x1, x2} + + x_lower := BigInt3{Limbs: limbs} + x_lower_bigint := x_lower.Pack86() + + d3 := x_bigint5[3].ToSigned() + d4 := x_bigint5[4].ToSigned() + + base := BASE() + d3.Mul(d3, base.Exp(&base, big.NewInt(3), nil)) + d4.Mul(d4, base.Exp(&base, big.NewInt(4), nil)) + + x_lower_bigint.Add(&x_lower_bigint, d3) + x_lower_bigint.Add(&x_lower_bigint, d4) + + return x_lower_bigint, nil +} + +func bigintPackDivMod(vm *VirtualMachine, execScopes *ExecutionScopes, idsData IdsManager) error { + + pUnpack, err := BigInt3FromVarName("P", idsData, vm) + if err != nil { + return err + } + p := pUnpack.Pack86() + + x, err := calculateX(vm, idsData) + if err != nil { + return err + } + x1 := x + + yUnpacked, err := BigInt3FromVarName("y", idsData, vm) + if err != nil { + return err + } + y := yUnpacked.Pack86() + + res, _ := math_utils.DivMod(&x1, &y, &p) + + execScopes.AssignOrUpdateVariable("res", *res) + execScopes.AssignOrUpdateVariable("value", *res) + execScopes.AssignOrUpdateVariable("x", x) + execScopes.AssignOrUpdateVariable("y", y) + execScopes.AssignOrUpdateVariable("p", p) + + return nil +} diff --git a/pkg/hints/bigint_hint_test.go b/pkg/hints/bigint_hint_test.go index 27bc55b4..00be7132 100644 --- a/pkg/hints/bigint_hint_test.go +++ b/pkg/hints/bigint_hint_test.go @@ -10,6 +10,7 @@ import ( "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/lambdaclass/cairo-vm.go/pkg/vm/memory" ) @@ -73,3 +74,180 @@ func TestNonDetBigInt3Ok(t *testing.T) { } } } + +func TestSafeDivBigInt(t *testing.T) { + vm := NewVirtualMachine() + + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + + execScopes := NewExecutionScopes() + + res, _ := new(big.Int).SetString("109567829260688255124154626727441144629993228404337546799996747905569082729709", 10) + x, _ := new(big.Int).SetString("91414600319290532004473480113251693728834511388719905794310982800988866814583", 10) + y, _ := new(big.Int).SetString("38047400353360331012910998489219098987968251547384484838080352663220422975266", 10) + p, _ := new(big.Int).SetString("115792089237316195423570985008687907852837564279074904382605163141518161494337", 10) + + execScopes.AssignOrUpdateVariable("res", *res) + execScopes.AssignOrUpdateVariable("x", *x) + execScopes.AssignOrUpdateVariable("y", *y) + execScopes.AssignOrUpdateVariable("p", *p) + + vm.RunContext.Fp = NewRelocatable(1, 0) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "flag": {nil}, + }, + vm, + ) + + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: BIGINT_SAFE_DIV, + }) + + err := hintProcessor.ExecuteHint(vm, &hintData, nil, execScopes) + + if err != nil { + t.Errorf("Safe Big int div hint test failed with error: %s", err) + } else { + expectedK, _ := new(big.Int).SetString("36002209591245282109880156842267569109802494162594623391338581162816748840003", 10) + expectedVal, _ := new(big.Int).SetString("36002209591245282109880156842267569109802494162594623391338581162816748840003", 10) + + kUncast, err := execScopes.Get("k") + if err != nil { + t.Errorf("%s", err) + } + k, _ := kUncast.(big.Int) + + valUncast, err := execScopes.Get("value") + if err != nil { + t.Errorf("%s", err) + } + value, _ := valUncast.(big.Int) + + if expectedK.Cmp(&k) != 0 { + t.Errorf("incorrect K value expected: %s, got: %s", expectedK.Text(10), k.Text(10)) + } + + if expectedVal.Cmp(&value) != 0 { + t.Errorf("incorrect value expected: %s, got: %s", expectedVal.Text(10), value.Text(10)) + } + + // check memory + addr := memory.NewRelocatable(1, 0) + val, _ := vm.Segments.Memory.GetFelt(addr) + + if val != lambdaworks.FeltFromUint(1) { + t.Errorf("incorrect value fetched from memory") + } + } +} + +func TestBigintPackDivModHint(t *testing.T) { + vm := NewVirtualMachine() + + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + + vm.RunContext.Fp = NewRelocatable(1, 0) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "x": { + NewMaybeRelocatableFelt(lambdaworks.FeltFromHex("0x38a23ca66202c8c2a72277")), + NewMaybeRelocatableFelt(lambdaworks.FeltFromHex("0x6730e765376ff17ea8385")), + NewMaybeRelocatableFelt(lambdaworks.FeltFromHex("0xca1ad489ab60ea581e6c1")), + NewMaybeRelocatableFelt(lambdaworks.FeltFromUint(0)), + NewMaybeRelocatableFelt(lambdaworks.FeltFromUint(0)), + }, + "y": { + NewMaybeRelocatableFelt(lambdaworks.FeltFromHex("0x20a4b46d3c5e24cda81f22")), + NewMaybeRelocatableFelt(lambdaworks.FeltFromHex("0x967bf895824330d4273d0")), + NewMaybeRelocatableFelt(lambdaworks.FeltFromHex("0x541e10c21560da25ada4c")), + }, + "P": { + NewMaybeRelocatableFelt(lambdaworks.FeltFromHex("0x8a03bbfd25e8cd0364141")), + NewMaybeRelocatableFelt(lambdaworks.FeltFromHex("0x3ffffffffffaeabb739abd")), + NewMaybeRelocatableFelt(lambdaworks.FeltFromHex("0xfffffffffffffffffffff")), + }, + }, + vm, + ) + + execScopes := NewExecutionScopes() + + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: BIGINT_PACK_DIV_MOD, + }) + + err := hintProcessor.ExecuteHint(vm, &hintData, nil, execScopes) + + if err != nil { + t.Errorf("BIGINT_PACK_DIV_MOD test failed with error: %s", err) + } else { + expected, _ := new(big.Int).SetString("109567829260688255124154626727441144629993228404337546799996747905569082729709", 10) + yExpected, _ := new(big.Int).SetString("38047400353360331012910998489219098987968251547384484838080352663220422975266", 10) + xExpected, _ := new(big.Int).SetString("91414600319290532004473480113251693728834511388719905794310982800988866814583", 10) + pExpected, _ := new(big.Int).SetString("115792089237316195423570985008687907852837564279074904382605163141518161494337", 10) + + // fetch values + resu, err := execScopes.Get("res") + if err != nil { + t.Errorf("BIGINT_PACK_DIV_MOD test failed with error: %s", err) + } + res := resu.(big.Int) + + valu, err := execScopes.Get("value") + if err != nil { + t.Errorf("BIGINT_PACK_DIV_MOD test failed with error: %s", err) + } + value := valu.(big.Int) + + yu, err := execScopes.Get("y") + if err != nil { + t.Errorf("BIGINT_PACK_DIV_MOD test failed with error: %s", err) + } + y := yu.(big.Int) + + xu, err := execScopes.Get("x") + if err != nil { + t.Errorf("BIGINT_PACK_DIV_MOD test failed with error: %s", err) + } + x := xu.(big.Int) + + pu, err := execScopes.Get("p") + if err != nil { + t.Errorf("BIGINT_PACK_DIV_MOD test failed with error: %s", err) + } + p := pu.(big.Int) + + if res.Cmp(expected) != 0 { + t.Errorf("incorrect res expected: %s, got: %s", expected.Text(10), res.Text(10)) + } + if value.Cmp(expected) != 0 { + t.Errorf("incorrect value expected: %s, got: %s", expected.Text(10), value.Text(10)) + } + if y.Cmp(yExpected) != 0 { + t.Errorf("incorrect y expected: %s, got: %s", yExpected.Text(10), y.Text(10)) + } + if x.Cmp(xExpected) != 0 { + t.Errorf("incorrect x expected: %s, got: %s", xExpected.Text(10), x.Text(10)) + } + if p.Cmp(pExpected) != 0 { + t.Errorf("incorrect p expected: %s, got: %s", pExpected.Text(10), p.Text(10)) + } + } +} diff --git a/pkg/hints/ec_hint_test.go b/pkg/hints/ec_hint_test.go index 974576b4..9d4c336c 100644 --- a/pkg/hints/ec_hint_test.go +++ b/pkg/hints/ec_hint_test.go @@ -315,7 +315,6 @@ func TestRunComputeSlopeV2Ok(t *testing.T) { idsManager := SetupIdsForTest( map[string][]*MaybeRelocatable{ "point0": { - NewMaybeRelocatableFelt(FeltFromUint64(512)), NewMaybeRelocatableFelt(FeltFromUint64(2412)), NewMaybeRelocatableFelt(FeltFromUint64(133)), diff --git a/pkg/hints/hint_codes/ec_op_hints.go b/pkg/hints/hint_codes/ec_op_hints.go index e8ab665c..9e06ddb7 100644 --- a/pkg/hints/hint_codes/ec_op_hints.go +++ b/pkg/hints/hint_codes/ec_op_hints.go @@ -59,3 +59,6 @@ y0 = pack(ids.pt0.y, PRIME) value = new_x = (pow(slope, 2, SECP_P) - x0 - x1) % SECP_P"` const FAST_EC_ADD_ASSIGN_NEW_Y = "value = new_y = (slope * (x0 - new_x) - y0) % SECP_P" + +const BIGINT_SAFE_DIV = "k = safe_div(res * y - x, p)\nvalue = k if k > 0 else 0 - k\nids.flag = 1 if k > 0 else 0" +const BIGINT_PACK_DIV_MOD = "from starkware.cairo.common.cairo_secp.secp_utils import pack\nfrom starkware.cairo.common.math_utils import as_int\nfrom starkware.python.math_utils import div_mod, safe_div\n\np = pack(ids.P, PRIME)\nx = pack(ids.x, PRIME) + as_int(ids.x.d3, PRIME) * ids.BASE ** 3 + as_int(ids.x.d4, PRIME) * ids.BASE ** 4\ny = pack(ids.y, PRIME)\n\nvalue = res = div_mod(x, y, p)" diff --git a/pkg/hints/hint_codes/math_hint_codes.go b/pkg/hints/hint_codes/math_hint_codes.go index de6083ff..7c517534 100644 --- a/pkg/hints/hint_codes/math_hint_codes.go +++ b/pkg/hints/hint_codes/math_hint_codes.go @@ -42,3 +42,7 @@ const SPLIT_FELT = "from starkware.cairo.common.math_utils import assert_integer const SPLIT_INT = "memory[ids.output] = res = (int(ids.value) % PRIME) % ids.base\nassert res < ids.bound, f'split_int(): Limb {res} is out of range.'" const SPLIT_INT_ASSERT_RANGE = "assert ids.value == 0, 'split_int(): value is out of range.'" + +const ASSERT_LE_FELT_V_0_6 = "from starkware.cairo.common.math_utils import assert_integer\nassert_integer(ids.a)\nassert_integer(ids.b)\nassert (ids.a % PRIME) <= (ids.b % PRIME), \\\n f'a = {ids.a % PRIME} is not less than or equal to b = {ids.b % PRIME}.'" + +const ASSERT_LE_FELT_V_0_8 = "from starkware.cairo.common.math_utils import assert_integer\nassert_integer(ids.a)\nassert_integer(ids.b)\na = ids.a % PRIME\nb = ids.b % PRIME\nassert a <= b, f'a = {a} is not less than or equal to b = {b}.'\n\nids.small_inputs = int(\n a < range_check_builtin.bound and (b - a) < range_check_builtin.bound)" diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index ccac7bd2..fadf858d 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -198,6 +198,14 @@ 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 BIGINT_SAFE_DIV: + return SafeDivBigint(vm, execScopes, data.Ids) + case BIGINT_PACK_DIV_MOD: + return bigintPackDivMod(vm, execScopes, data.Ids) + case ASSERT_LE_FELT_V_0_6: + return assertLeFeltV06(vm, data.Ids) + case ASSERT_LE_FELT_V_0_8: + return assertLeFeltV08(vm, data.Ids) default: return errors.Errorf("Unknown Hint: %s", data.Code) } diff --git a/pkg/hints/hint_utils/bigint_utils.go b/pkg/hints/hint_utils/bigint_utils.go index 9a900443..916f0181 100644 --- a/pkg/hints/hint_utils/bigint_utils.go +++ b/pkg/hints/hint_utils/bigint_utils.go @@ -14,7 +14,7 @@ import ( // This file contains an implementation of each behaviour at the limbs level, and the wrappers for each specific type // Generic methods for all types -func limbsFromVarName(nLimbs int, name string, ids IdsManager, vm *VirtualMachine) ([]Felt, error) { +func LimbsFromVarName(nLimbs int, name string, ids IdsManager, vm *VirtualMachine) ([]Felt, error) { baseAddr, err := ids.GetAddr(name, vm) if err != nil { return nil, err diff --git a/pkg/hints/hint_utils/secp_utils.go b/pkg/hints/hint_utils/secp_utils.go index a125823a..291b0907 100644 --- a/pkg/hints/hint_utils/secp_utils.go +++ b/pkg/hints/hint_utils/secp_utils.go @@ -5,6 +5,11 @@ import ( "math/big" ) +func BASE() big.Int { + base, _ := new(big.Int).SetString("77371252455336267181195264", 10) + return *base +} + func SECP_P() big.Int { secpP, _ := new(big.Int).SetString("115792089237316195423570985008687907853269984665640564039457584007908834671663", 10) return *secpP diff --git a/pkg/hints/math_hints.go b/pkg/hints/math_hints.go index 03b239c3..c0ed9577 100644 --- a/pkg/hints/math_hints.go +++ b/pkg/hints/math_hints.go @@ -1,6 +1,7 @@ package hints import ( + "fmt" "math/big" "github.com/lambdaclass/cairo-vm.go/pkg/builtins" @@ -10,6 +11,7 @@ import ( . "github.com/lambdaclass/cairo-vm.go/pkg/math_utils" . "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/lambdaclass/cairo-vm.go/pkg/vm/memory" "github.com/pkg/errors" ) @@ -605,3 +607,52 @@ func splitIntAssertRange(ids IdsManager, vm *VirtualMachine) error { return nil } + +func assertLeFeltV06(vm *VirtualMachine, ids IdsManager) error { + a, err := ids.GetFelt("a", vm) + if err != nil { + return err + } + + b, err := ids.GetFelt("b", vm) + if err != nil { + return err + } + if a.Cmp(b) == 1 { + err := fmt.Sprintf("Assertion failed, %s, is not less or equal to %s", a.ToHexString(), b.ToHexString()) + return errors.New(err) + } + + return nil +} + +func assertLeFeltV08(vm *VirtualMachine, ids IdsManager) error { + a, err := ids.GetFelt("a", vm) + if err != nil { + return err + } + + b, err := ids.GetFelt("b", vm) + if err != nil { + return err + } + if a.Cmp(b) == 1 { + err := fmt.Sprintf("Assertion failed, %s, is not less or equal to %s", a.ToHexString(), b.ToHexString()) + return errors.New(err) + } + + bound, err := vm.GetRangeCheckBound() + if err != nil { + return err + } + + acmp := a.Cmp(bound) + bcmp := b.Sub(a).Cmp(bound) + + // Todo: check if using only & is enough (rust uses && between bools) + small_inputs := lambdaworks.FeltFromUint(uint(acmp & bcmp)) + + ids.Insert("small_inputs", memory.NewMaybeRelocatableFelt(small_inputs), vm) + + return nil +} diff --git a/pkg/hints/math_hints_test.go b/pkg/hints/math_hints_test.go index 296dfb8e..97706d16 100644 --- a/pkg/hints/math_hints_test.go +++ b/pkg/hints/math_hints_test.go @@ -1138,3 +1138,57 @@ func TestSplitIntAssertRangeHintOutOfRangeError(t *testing.T) { t.Errorf("SPLIT_INT_ASSERT_RANGE hint should have failed") } } + +func TestAssertLeFeltV06AssertionFail(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + + vm.RunContext.Fp = memory.NewRelocatable(1, 2) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "a": {NewMaybeRelocatableFelt(FeltFromDecString("17"))}, + "b": {NewMaybeRelocatableFelt(FeltFromDecString("7"))}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: ASSERT_LE_FELT_V_0_6, + }) + + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) + + if err == nil { + t.Errorf("ASSERT_LE_FELT_V_0_6 hint should have failed") + } + +} + +func TestAssertLeFeltV08AssertionFail(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + + vm.RunContext.Fp = memory.NewRelocatable(1, 2) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "a": {NewMaybeRelocatableFelt(FeltFromDecString("17"))}, + "b": {NewMaybeRelocatableFelt(FeltFromDecString("7"))}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: ASSERT_LE_FELT_V_0_8, + }) + + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) + + if err == nil { + t.Errorf("ASSERT_LE_FELT_V_0_6 hint should have failed") + } + +}