diff --git a/cairo_programs/uint256.cairo b/cairo_programs/uint256.cairo new file mode 100644 index 00000000..f3e4daf1 --- /dev/null +++ b/cairo_programs/uint256.cairo @@ -0,0 +1,115 @@ +%builtins range_check + +from starkware.cairo.common.uint256 import ( + Uint256, + uint256_add, + split_64, + uint256_sqrt, + uint256_signed_nn, + uint256_unsigned_div_rem, + uint256_mul, + uint256_mul_div_mod +) +from starkware.cairo.common.alloc import alloc + +func fill_array{range_check_ptr: felt}( + array: Uint256*, base: Uint256, step: Uint256, array_length: felt, iterator: felt +) { + if (iterator == array_length) { + return (); + } + let (res, carry_high) = uint256_add(step, base); + let (sqrt) = uint256_sqrt(res); + + assert array[iterator] = sqrt; + return fill_array(array, base, array[iterator], array_length, iterator + 1); +} + +func main{range_check_ptr: felt}() { + let x: Uint256 = Uint256(5, 2); + let y = Uint256(3, 7); + let (res, carry_high) = uint256_add(x, y); + assert res.low = 8; + assert res.high = 9; + assert carry_high = 0; + + let (low, high) = split_64(850981239023189021389081239089023); + assert low = 7249717543555297151; + assert high = 46131785404667; + + let (root) = uint256_sqrt(Uint256(17, 7)); + assert root = Uint256(48805497317890012913, 0); + + let (signed_nn) = uint256_signed_nn(Uint256(5, 2)); + assert signed_nn = 1; + let (p) = uint256_signed_nn(Uint256(1, 170141183460469231731687303715884105728)); + assert p = 0; + let (q) = uint256_signed_nn(Uint256(1, 170141183460469231731687303715884105727)); + assert q = 1; + + let (a_quotient, a_remainder) = uint256_unsigned_div_rem(Uint256(89, 72), Uint256(3, 7)); + assert a_quotient = Uint256(10, 0); + assert a_remainder = Uint256(59, 2); + + let (b_quotient, b_remainder) = uint256_unsigned_div_rem( + Uint256(-3618502788666131213697322783095070105282824848410658236509717448704103809099, 2), + Uint256(5, 2), + ); + assert b_quotient = Uint256(1, 0); + assert b_remainder = Uint256(340282366920938463463374607431768211377, 0); + + let (c_quotient, c_remainder) = uint256_unsigned_div_rem( + Uint256(340282366920938463463374607431768211455, 340282366920938463463374607431768211455), + Uint256(1, 0), + ); + + assert c_quotient = Uint256(340282366920938463463374607431768211455, 340282366920938463463374607431768211455); + assert c_remainder = Uint256(0, 0); + + let (a_quotient_low, a_quotient_high, a_remainder) = uint256_mul_div_mod( + Uint256(89, 72), + Uint256(3, 7), + Uint256(107, 114), + ); + assert a_quotient_low = Uint256(143276786071974089879315624181797141668, 4); + assert a_quotient_high = Uint256(0, 0); + assert a_remainder = Uint256(322372768661941702228460154409043568767, 101); + + let (b_quotient_low, b_quotient_high, b_remainder) = uint256_mul_div_mod( + Uint256(-3618502788666131213697322783095070105282824848410658236509717448704103809099, 2), + Uint256(1, 1), + Uint256(5, 2), + ); + assert b_quotient_low = Uint256(170141183460469231731687303715884105688, 1); + assert b_quotient_high = Uint256(0, 0); + assert b_remainder = Uint256(170141183460469231731687303715884105854, 1); + + let (c_quotient_low, c_quotient_high, c_remainder) = uint256_mul_div_mod( + Uint256(340281070833283907490476236129005105807, 340282366920938463463374607431768211455), + Uint256(2447157533618445569039502, 0), + Uint256(0, 1), + ); + + assert c_quotient_low = Uint256(340282366920938463454053728725133866491, 2447157533618445569039501); + assert c_quotient_high = Uint256(0, 0); + assert c_remainder = Uint256(326588112914912836985603897252688572242, 0); + + let (mult_low_a, mult_high_a) = uint256_mul(Uint256(59, 2), Uint256(10, 0)); + assert mult_low_a = Uint256(590, 20); + assert mult_high_a = Uint256(0, 0); + + let (mult_low_b: Uint256, mult_high_b: Uint256) = uint256_mul( + Uint256(271442546951262198976322048597925888860, 0), + Uint256(271442546951262198976322048597925888860, 0), + ); + assert mult_low_b = Uint256( + 42047520920204780886066537579778623760, 216529163594619381764978757921136443390 + ); + assert mult_high_b = Uint256(0, 0); + + let array_length = 100; + let (sum_array: Uint256*) = alloc(); + fill_array(sum_array, Uint256(57, 8), Uint256(17, 7), array_length, 0); + + return (); +} diff --git a/cairo_programs/uint256_integration_tests.cairo b/cairo_programs/uint256_integration_tests.cairo new file mode 100644 index 00000000..70599483 --- /dev/null +++ b/cairo_programs/uint256_integration_tests.cairo @@ -0,0 +1,151 @@ +%builtins range_check bitwise + +from starkware.cairo.common.uint256 import ( + Uint256, + uint256_add, + split_64, + uint256_sqrt, + uint256_signed_nn, + uint256_unsigned_div_rem, + uint256_mul, + uint256_or, + uint256_reverse_endian, +) +from starkware.cairo.common.alloc import alloc +from starkware.cairo.common.cairo_builtins import BitwiseBuiltin + +func fill_array(array_start: felt*, base: felt, step: felt, iter: felt, last: felt) -> () { + if (iter == last) { + return (); + } + assert array_start[iter] = base + step; + return fill_array(array_start, base + step, step, iter + 1, last); +} + +func fill_uint256_array{range_check_ptr: felt}( + array: Uint256*, base: Uint256, step: Uint256, array_len: felt, iterator: felt +) { + if (iterator == array_len) { + return (); + } + let (res: Uint256, carry_high: felt) = uint256_add(step, base); + + assert array[iterator] = res; + return fill_uint256_array(array, base, array[iterator], array_len, iterator + 1); +} + +func test_sqrt{range_check_ptr}( + base_array: Uint256*, new_array: Uint256*, iter: felt, last: felt +) -> () { + alloc_locals; + + if (iter == last) { + return (); + } + + let res: Uint256 = uint256_sqrt(base_array[iter]); + assert new_array[iter] = res; + + return test_sqrt(base_array, new_array, iter + 1, last); +} + +func test_signed_nn{range_check_ptr}( + base_array: Uint256*, new_array: felt*, iter: felt, last: felt +) -> () { + alloc_locals; + + if (iter == last) { + return (); + } + + let res: felt = uint256_signed_nn(base_array[iter]); + assert res = 1; + assert new_array[iter] = res; + + return test_signed_nn(base_array, new_array, iter + 1, last); +} + +func test_unsigned_div_rem{range_check_ptr}( + base_array: Uint256*, new_array: Uint256*, iter: felt, last: felt +) -> () { + alloc_locals; + + if (iter == last) { + return (); + } + + let (quotient: Uint256, remainder: Uint256) = uint256_unsigned_div_rem( + base_array[iter], Uint256(7, 8) + ); + assert new_array[(iter * 2)] = quotient; + assert new_array[(iter * 2) + 1] = remainder; + + return test_unsigned_div_rem(base_array, new_array, iter + 1, last); +} + +func test_split_64{range_check_ptr}( + base_array: felt*, new_array: felt*, iter: felt, last: felt +) -> () { + alloc_locals; + + if (iter == last) { + return (); + } + + let (low: felt, high: felt) = split_64(base_array[iter]); + assert new_array[(iter * 2)] = low; + assert new_array[(iter * 2) + 1] = high; + return test_split_64(base_array, new_array, iter + 1, last); +} + +func test_integration{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}( + base_array: Uint256*, new_array: Uint256*, iter: felt, last: felt +) -> () { + alloc_locals; + + if (iter == last) { + return (); + } + + let (add: Uint256, carry_high: felt) = uint256_add(base_array[iter], base_array[iter + 1]); + let (quotient: Uint256, remainder: Uint256) = uint256_unsigned_div_rem(add, Uint256(5, 3)); + let (low: Uint256, high: Uint256) = uint256_mul(quotient, remainder); + let (bitwise_or: Uint256) = uint256_or(low, high); + let (reverse_endian: Uint256) = uint256_reverse_endian(bitwise_or); + let (result: Uint256) = uint256_sqrt(reverse_endian); + + assert new_array[iter] = result; + return test_integration(base_array, new_array, iter + 1, last); +} + +func run_tests{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}(array_len: felt) -> () { + alloc_locals; + let (uint256_array: Uint256*) = alloc(); + fill_uint256_array(uint256_array, Uint256(57, 8), Uint256(57, 101), array_len, 0); + + let (array_sqrt: Uint256*) = alloc(); + test_sqrt(uint256_array, array_sqrt, 0, array_len); + + let (array_signed_nn: felt*) = alloc(); + test_signed_nn(uint256_array, array_signed_nn, 0, array_len); + + let (array_unsigned_div_rem: Uint256*) = alloc(); + test_unsigned_div_rem(uint256_array, array_unsigned_div_rem, 0, array_len); + + let (felt_array: felt*) = alloc(); + fill_array(felt_array, 0, 3, 0, array_len); + + let (array_split_64: felt*) = alloc(); + test_split_64(felt_array, array_split_64, 0, array_len); + + let (array_test_integration: Uint256*) = alloc(); + test_integration(uint256_array, array_test_integration, 0, array_len - 1); + + return (); +} + +func main{range_check_ptr: felt, bitwise_ptr: BitwiseBuiltin*}() { + run_tests(10); + + return (); +} diff --git a/cairo_programs/uint256_root.cairo b/cairo_programs/uint256_root.cairo new file mode 100644 index 00000000..98b6b543 --- /dev/null +++ b/cairo_programs/uint256_root.cairo @@ -0,0 +1,12 @@ +%builtins range_check bitwise +from starkware.cairo.common.uint256 import ( + Uint256, + uint256_sqrt, +) +from starkware.cairo.common.cairo_builtins import BitwiseBuiltin + +func main{range_check_ptr: felt, bitwise_ptr: BitwiseBuiltin*}() { + let n = Uint256(0, 157560248172239344387757911110183813120); + let res = uint256_sqrt(n); + return (); +} diff --git a/pkg/hints/hint_codes/uint256_hint_codes.go b/pkg/hints/hint_codes/uint256_hint_codes.go new file mode 100644 index 00000000..1881c125 --- /dev/null +++ b/pkg/hints/hint_codes/uint256_hint_codes.go @@ -0,0 +1,28 @@ +package hint_codes + +const UINT256_ADD = "sum_low = ids.a.low + ids.b.low\nids.carry_low = 1 if sum_low >= ids.SHIFT else 0\nsum_high = ids.a.high + ids.b.high + ids.carry_low\nids.carry_high = 1 if sum_high >= ids.SHIFT else 0" +const UINT256_ADD_LOW = "sum_low = ids.a.low + ids.b.low\nids.carry_low = 1 if sum_low >= ids.SHIFT else 0" +const SPLIT_64 = "ids.low = ids.a & ((1<<64) - 1)\nids.high = ids.a >> 64" +const UINT256_SQRT = "from starkware.python.math_utils import isqrt\nn = (ids.n.high << 128) + ids.n.low\nroot = isqrt(n)\nassert 0 <= root < 2 ** 128\nids.root.low = root\nids.root.high = 0" +const UINT256_SQRT_FELT = "from starkware.python.math_utils import isqrt\nn = (ids.n.high << 128) + ids.n.low\nroot = isqrt(n)\nassert 0 <= root < 2 ** 128\nids.root = root;" +const UINT256_SIGNED_NN = "memory[ap] = 1 if 0 <= (ids.a.high % PRIME) < 2 ** 127 else 0" +const UINT256_UNSIGNED_DIV_REM = "a = (ids.a.high << 128) + ids.a.low\ndiv = (ids.div.high << 128) + ids.div.low\nquotient, remainder = divmod(a, div)\n\nids.quotient.low = quotient & ((1 << 128) - 1)\nids.quotient.high = quotient >> 128\nids.remainder.low = remainder & ((1 << 128) - 1)\nids.remainder.high = remainder >> 128" +const UINT256_EXPANDED_UNSIGNED_DIV_REM = "a = (ids.a.high << 128) + ids.a.low\ndiv = (ids.div.b23 << 128) + ids.div.b01\nquotient, remainder = divmod(a, div)\n\nids.quotient.low = quotient & ((1 << 128) - 1)\nids.quotient.high = quotient >> 128\nids.remainder.low = remainder & ((1 << 128) - 1)\nids.remainder.high = remainder >> 128" +const UINT256_MUL_DIV_MOD = "a = (ids.a.high << 128) + ids.a.low\nb = (ids.b.high << 128) + ids.b.low\ndiv = (ids.div.high << 128) + ids.div.low\nquotient, remainder = divmod(a * b, div)\n\nids.quotient_low.low = quotient & ((1 << 128) - 1)\nids.quotient_low.high = (quotient >> 128) & ((1 << 128) - 1)\nids.quotient_high.low = (quotient >> 256) & ((1 << 128) - 1)\nids.quotient_high.high = quotient >> 384\nids.remainder.low = remainder & ((1 << 128) - 1)\nids.remainder.high = remainder >> 128" +const UINT256_SUB = `def split(num: int, num_bits_shift: int = 128, length: int = 2): + a = [] + for _ in range(length): + a.append( num & ((1 << num_bits_shift) - 1) ) + num = num >> num_bits_shift + return tuple(a) + +def pack(z, num_bits_shift: int = 128) -> int: + limbs = (z.low, z.high) + return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) + +a = pack(ids.a) +b = pack(ids.b) +res = (a - b)%2**256 +res_split = split(res) +ids.res.low = res_split[0] +ids.res.high = res_split[1]` diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index 9685de6e..669cc1af 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -188,6 +188,26 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, return splitInt(data.Ids, vm) case SPLIT_INT_ASSERT_RANGE: return splitIntAssertRange(data.Ids, vm) + case UINT256_ADD: + return uint256Add(data.Ids, vm, false) + case UINT256_ADD_LOW: + return uint256Add(data.Ids, vm, true) + case UINT256_SUB: + return uint256Sub(data.Ids, vm) + case SPLIT_64: + return split64(data.Ids, vm) + case UINT256_SQRT: + return uint256Sqrt(data.Ids, vm, false) + case UINT256_SQRT_FELT: + return uint256Sqrt(data.Ids, vm, true) + case UINT256_SIGNED_NN: + return uint256SignedNN(data.Ids, vm) + case UINT256_UNSIGNED_DIV_REM: + return uint256UnsignedDivRem(data.Ids, vm) + case UINT256_EXPANDED_UNSIGNED_DIV_REM: + return uint256ExpandedUnsignedDivRem(data.Ids, vm) + case UINT256_MUL_DIV_MOD: + return uint256MulDivMod(data.Ids, vm) case DIV_MOD_N_PACKED_DIVMOD_V1: return divModNPackedDivMod(data.Ids, vm, execScopes) case DIV_MOD_N_PACKED_DIVMOD_EXTERNAL_N: diff --git a/pkg/hints/hint_utils/ids_manager.go b/pkg/hints/hint_utils/ids_manager.go index ef2550b5..2394f8b1 100644 --- a/pkg/hints/hint_utils/ids_manager.go +++ b/pkg/hints/hint_utils/ids_manager.go @@ -76,6 +76,22 @@ func (ids *IdsManager) GetFelt(name string, vm *VirtualMachine) (lambdaworks.Fel return felt, nil } +func (ids *IdsManager) GetUint256(name string, vm *VirtualMachine) (Uint256, error) { + lowAddr, err := ids.GetAddr(name, vm) + if err != nil { + return Uint256{}, err + } + low, err := vm.Segments.Memory.GetFelt(lowAddr) + if err != nil { + return Uint256{}, err + } + high, err := vm.Segments.Memory.GetFelt(lowAddr.AddUint(1)) + if err != nil { + return Uint256{}, err + } + return Uint256{Low: low, High: high}, nil +} + // Returns the value of an identifier as a Relocatable func (ids *IdsManager) GetRelocatable(name string, vm *VirtualMachine) (Relocatable, error) { val, err := ids.Get(name, vm) @@ -217,6 +233,19 @@ func (ids *IdsManager) InsertStructField(name string, field_off uint, value *May return vm.Segments.Memory.Insert(addr.AddUint(field_off), value) } +// Inserts Uint256 value into an ids field (given the identifier is a Uint256) +func (ids *IdsManager) InsertUint256(name string, val Uint256, vm *VirtualMachine) error { + baseAddr, err := ids.GetAddr(name, vm) + if err != nil { + return err + } + err = vm.Segments.Memory.Insert(baseAddr, NewMaybeRelocatableFelt(val.Low)) + if err != nil { + return err + } + return vm.Segments.Memory.Insert(baseAddr.AddUint(1), NewMaybeRelocatableFelt(val.High)) +} + // Inserts value into the address of the given identifier func insertIdsFromReference(value *MaybeRelocatable, reference *HintReference, apTracking parser.ApTrackingData, vm *VirtualMachine) error { addr, ok := getAddressFromReference(reference, apTracking, vm) diff --git a/pkg/hints/hint_utils/uint256_utils.go b/pkg/hints/hint_utils/uint256_utils.go new file mode 100644 index 00000000..be26da96 --- /dev/null +++ b/pkg/hints/hint_utils/uint256_utils.go @@ -0,0 +1,42 @@ +package hint_utils + +import ( + "math/big" + + . "github.com/lambdaclass/cairo-vm.go/pkg/lambdaworks" +) + +type Uint256 struct { + Low Felt + High Felt +} + +func (u *Uint256) ToString() string { + return "Uint256 { low: " + u.Low.ToSignedFeltString() + ", high: " + u.High.ToSignedFeltString() + " }" +} + +/* +Returns a Uint256 as a big.Int + + res = high << 128 + low +*/ +func (u *Uint256) ToBigInt() *big.Int { + high := new(big.Int).Lsh(u.High.ToBigInt(), 128) + low := u.Low.ToBigInt() + res := new(big.Int).Add(high, low) + return res +} + +/* +Returns a big.Int as Uint256 +*/ +func ToUint256(a *big.Int) Uint256 { + maxU128, _ := new(big.Int).SetString("340282366920938463463374607431768211455", 10) + low := new(big.Int).And(a, maxU128) + high := new(big.Int).Rsh(a, 128) + return Uint256{Low: FeltFromBigInt(low), High: FeltFromBigInt(high)} +} + +func (u *Uint256) IsEqual(other Uint256) bool { + return u.Low.Cmp(other.Low) == 0 && u.High.Cmp(other.High) == 0 +} diff --git a/pkg/hints/math_cmp_hints.go b/pkg/hints/math_cmp_hints.go index 5e4e064b..2e22e667 100644 --- a/pkg/hints/math_cmp_hints.go +++ b/pkg/hints/math_cmp_hints.go @@ -26,7 +26,9 @@ func isNNOutOfRange(ids IdsManager, vm *VirtualMachine) error { if err != nil { return err } - if (FeltZero().Sub(a).Sub(FeltOne())).Bits() < builtins.RANGE_CHECK_N_PARTS*builtins.INNER_RC_BOUND_SHIFT { + op := FeltZero().Sub(a).Sub(FeltOne()) + bound := FeltOne().Shl(16 * builtins.RANGE_CHECK_N_PARTS) + if op.Cmp(bound) == -1 { return vm.Segments.Memory.Insert(vm.RunContext.Ap, NewMaybeRelocatableFelt(FeltZero())) } return vm.Segments.Memory.Insert(vm.RunContext.Ap, NewMaybeRelocatableFelt(FeltOne())) diff --git a/pkg/hints/uint256_hints.go b/pkg/hints/uint256_hints.go new file mode 100644 index 00000000..8e7f413c --- /dev/null +++ b/pkg/hints/uint256_hints.go @@ -0,0 +1,316 @@ +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/vm" + . "github.com/lambdaclass/cairo-vm.go/pkg/vm/memory" + "github.com/pkg/errors" +) + +func ErrRootOOR(root *big.Int) error { + return errors.Errorf("assert 0 <= %d < 2**128", root) +} + +/* +Implements hints: +%{ + sum_low = ids.a.low + ids.b.low + ids.carry_low = 1 if sum_low >= ids.SHIFT else 0 + sum_high = ids.a.high + ids.b.high + ids.carry_low + ids.carry_high = 1 if sum_high >= ids.SHIFT else 0 +%} +%{ + sum_low = ids.a.low + ids.b.low + ids.carry_low = 1 if sum_low >= ids.SHIFT else 0 +%} +*/ + +func uint256Add(ids IdsManager, vm *VirtualMachine, lowOnly bool) error { + shift := FeltOne().Shl(128) + a, err := ids.GetUint256("a", vm) + if err != nil { + return err + } + b, err := ids.GetUint256("b", vm) + if err != nil { + return err + } + + sumLow := a.Low.Add(b.Low) + carryLow := FeltZero() + if sumLow.Cmp(shift) != -1 { + carryLow = FeltOne() + } + + if !lowOnly { + sumHigh := a.High.Add(b.High.Add(carryLow)) + carryHigh := FeltZero() + if sumHigh.Cmp(shift) != -1 { + carryHigh = FeltOne() + } + err := ids.Insert("carry_high", NewMaybeRelocatableFelt(carryHigh), vm) + if err != nil { + return err + } + } + return ids.Insert("carry_low", NewMaybeRelocatableFelt(carryLow), vm) + +} + +/* +Implements hint: +%{ + def split(num: int, num_bits_shift: int = 128, length: int = 2): + a = [] + for _ in range(length): + a.append( num & ((1 << num_bits_shift) - 1) ) + num = num >> num_bits_shift + return tuple(a) + + def pack(z, num_bits_shift: int = 128) -> int: + limbs = (z.low, z.high) + return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) + + a = pack(ids.a) + b = pack(ids.b) + res = (a - b)%2**256 + res_split = split(res) + ids.res.low = res_split[0] + ids.res.high = res_split[1] +%} +*/ + +func uint256Sub(ids IdsManager, vm *VirtualMachine) error { + a, err := ids.GetUint256("a", vm) + if err != nil { + return err + } + b, err := ids.GetUint256("b", vm) + if err != nil { + return err + } + var resBigInt *big.Int + if a.ToBigInt().Cmp(b.ToBigInt()) != -1 { + resBigInt = new(big.Int).Sub(a.ToBigInt(), b.ToBigInt()) + } else { + mod256 := new(big.Int).Lsh(new(big.Int).SetUint64(1), 256) + if mod256.Cmp(b.ToBigInt()) != -1 { + resBigInt = new(big.Int).Sub(mod256, b.ToBigInt()) + resBigInt = new(big.Int).Add(resBigInt, a.ToBigInt()) + } else { + loweredB := new(big.Int).Mod(b.ToBigInt(), mod256) + if a.ToBigInt().Cmp(loweredB) != -1 { + resBigInt = new(big.Int).Sub(a.ToBigInt(), loweredB) + } else { + resBigInt = new(big.Int).Sub(mod256, loweredB) + resBigInt = new(big.Int).Add(resBigInt, a.ToBigInt()) + } + } + } + + res := ToUint256(resBigInt) + return ids.InsertUint256("res", res, vm) +} + +/* +Implements hint: + + %{ + ids.low = ids.a & ((1<<64) - 1) + ids.high = ids.a >> 64 + +%} +*/ +func split64(ids IdsManager, vm *VirtualMachine) error { + a, err := ids.GetFelt("a", vm) + if err != nil { + return err + } + flag := (FeltOne().Shl(64)).Sub(FeltOne()) // (1 << 64) - 1 + low := a.And(flag) + high := a.Shr(64) // a >> 64 + err = ids.Insert("low", NewMaybeRelocatableFelt(low), vm) + if err != nil { + return err + } + err = ids.Insert("high", NewMaybeRelocatableFelt(high), vm) + if err != nil { + return err + } + return nil + +} + +/* +Implements hint: + + %{ + from starkware.python.math_utils import isqrt + n = (ids.n.high << 128) + ids.n.low + root = isqrt(n) + assert 0 <= root < 2 ** 128 + ids.root.low = root + ids.root.high = 0 + +%} +*/ +func uint256Sqrt(ids IdsManager, vm *VirtualMachine, onlyLow bool) error { + uintN, err := ids.GetUint256("n", vm) + if err != nil { + return err + } + n := uintN.ToBigInt() + root := new(big.Int).Sqrt(n) + if root.BitLen() > 128 { + return ErrRootOOR(root) + } + + feltRoot := FeltFromBigInt(root) + if onlyLow { + return ids.Insert("root", NewMaybeRelocatableFelt(feltRoot), vm) + } else { + return ids.InsertUint256("root", Uint256{Low: feltRoot, High: FeltZero()}, vm) + } +} + +/* +Implements hint: +%{ memory[ap] = 1 if 0 <= (ids.a.high % PRIME) < 2 ** 127 else 0 %} +*/ +func uint256SignedNN(ids IdsManager, vm *VirtualMachine) error { + a, err := ids.GetUint256("a", vm) + if err != nil { + return err + } + i128Max := FeltFromDecString("170141183460469231731687303715884105727") + if a.High.Cmp(FeltZero()) != -1 && a.High.Cmp(i128Max) != 1 { + return vm.Segments.Memory.Insert(vm.RunContext.Ap, NewMaybeRelocatableFelt(FeltOne())) + } else { + return vm.Segments.Memory.Insert(vm.RunContext.Ap, NewMaybeRelocatableFelt(FeltZero())) + } +} + +/* +Implements hint: + + %{ + a = (ids.a.high << 128) + ids.a.low + div = (ids.div.high << 128) + ids.div.low + quotient, remainder = divmod(a, div) + + ids.quotient.low = quotient & ((1 << 128) - 1) + ids.quotient.high = quotient >> 128 + ids.remainder.low = remainder & ((1 << 128) - 1) + ids.remainder.high = remainder >> 128 + +%} +*/ +func uint256UnsignedDivRem(ids IdsManager, vm *VirtualMachine) error { + return uint256OfssetedUnisgnedDivRem(ids, vm, 0, 1) + +} + +/* +Implements hint: + + %{ + a = (ids.a.high << 128) + ids.a.low + div = (ids.div.b23 << 128) + ids.div.b01 + quotient, remainder = divmod(a, div) + + ids.quotient.low = quotient & ((1 << 128) - 1) + ids.quotient.high = quotient >> 128 + ids.remainder.low = remainder & ((1 << 128) - 1) + ids.remainder.high = remainder >> 128 + +%} +*/ +func uint256ExpandedUnsignedDivRem(ids IdsManager, vm *VirtualMachine) error { + return uint256OfssetedUnisgnedDivRem(ids, vm, 1, 3) +} + +func uint256OfssetedUnisgnedDivRem(ids IdsManager, vm *VirtualMachine, divOffsetLow uint, divOffsetHigh uint) error { + a, err := ids.GetUint256("a", vm) + if err != nil { + return err + } + divLow, err := ids.GetStructFieldFelt("div", divOffsetLow, vm) + if err != nil { + return err + } + divHigh, err := ids.GetStructFieldFelt("div", divOffsetHigh, vm) + if err != nil { + return err + } + div := Uint256{Low: divLow, High: divHigh} + q, r := new(big.Int).DivMod(a.ToBigInt(), div.ToBigInt(), new(big.Int)) + + err = ids.InsertUint256("quotient", ToUint256(q), vm) + if err != nil { + return err + } + return ids.InsertUint256("remainder", ToUint256(r), vm) + +} + +/* +Implements hint: + + %{ + a = (ids.a.high << 128) + ids.a.low + div = (ids.div.b23 << 128) + ids.div.b01 + quotient, remainder = divmod(a, div) + + ids.quotient.low = quotient & ((1 << 128) - 1) + ids.quotient.high = quotient >> 128 + ids.remainder.low = remainder & ((1 << 128) - 1) + ids.remainder.high = remainder >> 128 + +%} +*/ +func uint256MulDivMod(ids IdsManager, vm *VirtualMachine) error { + a, err := ids.GetUint256("a", vm) + if err != nil { + return err + } + b, err := ids.GetUint256("b", vm) + if err != nil { + return err + } + div, err := ids.GetUint256("div", vm) + if err != nil { + return err + } + + if div.ToBigInt().Cmp(big.NewInt(0)) == 0 { + return errors.Errorf("Attempted to divide by zero") + } + + mul := new(big.Int).Mul(a.ToBigInt(), b.ToBigInt()) + quotient, rem := new(big.Int).DivMod(mul, div.ToBigInt(), new(big.Int)) + + maxU128, _ := new(big.Int).SetString("340282366920938463463374607431768211455", 10) + + var quotientLow Uint256 + var quotientHigh Uint256 + var remainder Uint256 + quotientLow.Low = FeltFromBigInt(new(big.Int).And(quotient, maxU128)) // q & maxU128 + quotientLow.High = FeltFromBigInt(new(big.Int).And(new(big.Int).Rsh(quotient, 128), maxU128)) // q >> 128 & maxU128 + quotientHigh.Low = FeltFromBigInt(new(big.Int).And(new(big.Int).Rsh(quotient, 256), maxU128)) // q >> 256 & maxU128 + quotientHigh.High = FeltFromBigInt(new(big.Int).Rsh(quotient, 384)) // q >> 384 + remainder.Low = FeltFromBigInt(new(big.Int).And(rem, maxU128)) // rem & maxU128 + remainder.High = FeltFromBigInt(new(big.Int).And(new(big.Int).Rsh(rem, 128), maxU128)) // rem >> 128 & maxU128 + + err = ids.InsertUint256("quotient_low", quotientLow, vm) + if err != nil { + return err + } + err = ids.InsertUint256("quotient_high", quotientHigh, vm) + if err != nil { + return err + } + return ids.InsertUint256("remainder", remainder, vm) +} diff --git a/pkg/hints/uint256_hints_test.go b/pkg/hints/uint256_hints_test.go new file mode 100644 index 00000000..89f350f0 --- /dev/null +++ b/pkg/hints/uint256_hints_test.go @@ -0,0 +1,758 @@ +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/parser" + . "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" +) + +func TestUint256AddOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "a": { + NewMaybeRelocatableFeltFromUint64(2), + NewMaybeRelocatableFeltFromUint64(3), + }, + "b": { + NewMaybeRelocatableFeltFromUint64(4), + NewMaybeRelocatableFelt(FeltFromDecString("340282366920938463463374607431768211455")), + }, + "carry_low": {nil}, + "carry_high": {nil}, + }, + vm, + ) + hintData := any(HintData{ + Ids: idsManager, + Code: UINT256_ADD, + }) + scopes := NewExecutionScopes() + hintProcessor := CairoVmHintProcessor{} + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("failed with error: %s", err) + } + + carry_low, err := idsManager.GetFelt("carry_low", vm) + if err != nil { + t.Errorf("failed with error: %s", err) + } + if carry_low.Cmp(FeltZero()) != 0 { + t.Errorf("expected carry_low: 0, got: %s", carry_low.ToSignedFeltString()) + } + carry_high, err := idsManager.GetFelt("carry_high", vm) + if err != nil { + t.Errorf("failed with error: %s", err) + } + if carry_high.Cmp(FeltOne()) != 0 { + t.Errorf("expected carry_high: 0, got: %s", carry_high.ToSignedFeltString()) + } +} + +func TestUint256AddLowOnlyOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "a": { + NewMaybeRelocatableFeltFromUint64(2), + NewMaybeRelocatableFeltFromUint64(3), + }, + "b": { + NewMaybeRelocatableFeltFromUint64(4), + NewMaybeRelocatableFelt(FeltFromDecString("340282366920938463463374607431768211455")), + }, + "carry_low": {nil}, + }, + vm, + ) + hintData := any(HintData{ + Ids: idsManager, + Code: UINT256_ADD_LOW, + }) + scopes := NewExecutionScopes() + hintProcessor := CairoVmHintProcessor{} + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("failed with error: %s", err) + } + + carry_low, err := idsManager.GetFelt("carry_low", vm) + if err != nil { + t.Errorf("failed with error: %s", err) + } + if carry_low.Cmp(FeltZero()) != 0 { + t.Errorf("expected carry_low: 0, got: %s", carry_low.ToSignedFeltString()) + } +} + +func TestUint256AddFailInsert(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "a": { + NewMaybeRelocatableFeltFromUint64(2), + NewMaybeRelocatableFeltFromUint64(3), + }, + "b": { + NewMaybeRelocatableFeltFromUint64(4), + NewMaybeRelocatableFeltFromUint64(2), + }, + "carry_low": {NewMaybeRelocatableFeltFromUint64(2)}, + }, + vm, + ) + hintData := any(HintData{ + Ids: idsManager, + Code: UINT256_ADD_LOW, + }) + scopes := NewExecutionScopes() + hintProcessor := CairoVmHintProcessor{} + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Errorf("should fail with error: ErrMemoryWriteOnce") + } + +} + +func TestSplit64Ok(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "a": { + NewMaybeRelocatableFelt(FeltFromDecString("-3")), + }, + "low": {nil}, + "high": {nil}, + }, + vm, + ) + + hintData := any(HintData{ + Ids: idsManager, + Code: SPLIT_64, + }) + scopes := NewExecutionScopes() + hintProcessor := CairoVmHintProcessor{} + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("failed with error: %s", err) + } + + low, err := idsManager.GetFelt("low", vm) + if err != nil { + t.Errorf("failed with error: %s", err) + } + expected_low := FeltFromDecString("-3").And(FeltOne().Shl(64).Sub(FeltOne())) + if low != expected_low { + t.Errorf("expected low: %d, got: %d", expected_low, low) + } + + high, err := idsManager.GetFelt("high", vm) + if err != nil { + t.Errorf("failed with error: %s", err) + } + expected_high := FeltFromDecString("-3").Shr(64) + if high != expected_high { + t.Errorf("expected high: %d, got: %d", expected_high, high) + } + +} + +func TestSplit64BigA(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "a": { + NewMaybeRelocatableFelt(FeltFromDecString("400066369019890261321163226850167045262")), + }, + "low": {nil}, + "high": {nil}, + }, + vm, + ) + + hintData := any(HintData{ + Ids: idsManager, + Code: SPLIT_64, + }) + scopes := NewExecutionScopes() + hintProcessor := CairoVmHintProcessor{} + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("failed with error: %s", err) + } + + low, err := idsManager.GetFelt("low", vm) + if err != nil { + t.Errorf("failed with error: %s", err) + } + + expected_low := FeltFromUint64(2279400676465785998) + if low.Cmp(expected_low) != 0 { + t.Errorf("expected low: %d, got: %d", expected_low, low) + } + high, err := idsManager.GetFelt("high", vm) + if err != nil { + t.Errorf("failed with error: %s", err) + } + expected_high := FeltFromDecString("21687641321487626429") + if high.Cmp(expected_high) != 0 { + t.Errorf("expected high: %d, got: %d", expected_high, high) + } + +} + +func TestUint256SqrtOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "n": { + NewMaybeRelocatableFelt(FeltFromUint64(17)), + NewMaybeRelocatableFelt(FeltFromUint64(7)), + }, + "root": {nil, nil}, + }, + vm, + ) + hintData := any(HintData{ + Ids: idsManager, + Code: UINT256_SQRT, + }) + scopes := NewExecutionScopes() + hintProcessor := CairoVmHintProcessor{} + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("failed with error: %s", err) + } + + expected_root, _ := new(big.Int).SetString("48805497317890012913", 10) + root, err := idsManager.GetFelt("root", vm) + if err != nil { + t.Errorf("failed with error: %s", err) + } + if root != FeltFromBigInt(expected_root) { + t.Errorf("failed, expected root: %d, got: %d", FeltFromBigInt(expected_root), root) + } +} + +func TestUint256SqrtKo(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + + idsManager := SetupIdsForTest(map[string][]*MaybeRelocatable{ + "n": { + NewMaybeRelocatableFelt(FeltZero()), + NewMaybeRelocatableFelt(FeltFromDecString("340282366920938463463374607431768211458")), + }, + "root": {nil}, + }, vm) + + hintData := any(HintData{Ids: idsManager, Code: UINT256_SQRT}) + hintProcessor := CairoVmHintProcessor{} + err := hintProcessor.ExecuteHint(vm, &hintData, nil, NewExecutionScopes()) + expectedRoot := FeltFromDecString("340282366920938463463374607431768211456") + if err.Error() != ErrRootOOR(expectedRoot.ToBigInt()).Error() { + t.Errorf("failed with error: %s", err) + } +} + +func TestUint256SqrtFeltOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "n": { + NewMaybeRelocatableFelt(FeltFromUint64(879232)), + NewMaybeRelocatableFelt(FeltFromUint64(135906)), + }, + "root": {nil}, + }, + vm, + ) + hintData := any(HintData{ + Ids: idsManager, + Code: UINT256_SQRT_FELT, + }) + scopes := NewExecutionScopes() + hintProcessor := CairoVmHintProcessor{} + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("failed with error: %s", err) + } + expected_root, _ := new(big.Int).SetString("6800471701195223914689", 10) + expectedResult := FeltFromBigInt(expected_root) + root, err := idsManager.GetFelt("root", vm) + if err != nil { + t.Errorf("failed with error: %s", err) + } + if root != expectedResult { + t.Errorf("failed, expected root: %d, got: %d", expectedResult, root) + } +} + +func TestUint256SignedNNOkResultOne(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments = AddNSegments(vm.Segments, 5) + ids := map[string][]*MaybeRelocatable{ + "a": { + NewMaybeRelocatableFelt(FeltFromUint64(1)), + NewMaybeRelocatableFelt(FeltFromUint64(1)), + }, + } + idsManager := SetupIdsForTest(ids, vm) + hintData := any(HintData{ + Ids: idsManager, + Code: UINT256_SIGNED_NN, + }) + hintProcessor := CairoVmHintProcessor{} + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) + if err != nil { + t.Errorf("failed with error: %s", err) + } + + result, err := vm.Segments.Memory.GetFelt(vm.RunContext.Ap) + if err != nil { + t.Errorf("failed with error: %s", err) + } + + if result != FeltOne() { + t.Errorf("failed, expected result: %d, got: %d", FeltOne(), result) + } +} + +func TestUint256SignedNNOkResultZero(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.RunContext.Ap = NewRelocatable(0, 5) + ids := map[string][]*MaybeRelocatable{ + "a": { + NewMaybeRelocatableFelt(FeltFromUint64(1)), + NewMaybeRelocatableFelt(FeltFromDecString("-4")), + }, + } + idsManager := SetupIdsForTest(ids, vm) + hintData := any(HintData{ + Ids: idsManager, + Code: UINT256_SIGNED_NN, + }) + hintProcessor := CairoVmHintProcessor{} + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) + if err != nil { + t.Errorf("failed with error: %s", err) + } + + result, err := vm.Segments.Memory.GetFelt(vm.RunContext.Ap) + if err != nil { + t.Errorf("failed with error: %s", err) + } + + if result != FeltZero() { + t.Errorf("failed, expected result: %d, got: %d", FeltZero(), result) + } +} + +func TestUint256SignedNNInvalidMemoryInser(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.RunContext.Ap = NewRelocatable(0, 5) + vm.Segments.Memory.Insert(vm.RunContext.Ap, NewMaybeRelocatableFeltFromUint64(10)) + ids := map[string][]*MaybeRelocatable{ + "a": { + NewMaybeRelocatableFelt(FeltFromUint64(1)), + NewMaybeRelocatableFelt(FeltFromUint64(1)), + }, + } + idsManager := SetupIdsForTest(ids, vm) + idsManager.HintApTracking = parser.ApTrackingData{Group: 4, Offset: 5} + hintData := any(HintData{ + Ids: idsManager, + Code: UINT256_SIGNED_NN, + }) + hintProcessor := CairoVmHintProcessor{} + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) + + expectedErr := ErrMemoryWriteOnce(NewRelocatable(0, 5), *NewMaybeRelocatableFeltFromUint64(10), *NewMaybeRelocatableFelt(FeltOne())) + if err.Error() != expectedErr.Error() { + t.Errorf("should fail with error: %s", err) + } +} + +func TestUint256UnsignedDivRemOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + + ids := map[string][]*MaybeRelocatable{ + "a": { + NewMaybeRelocatableFeltFromUint64(89), + NewMaybeRelocatableFeltFromUint64(72), + }, + "div": { + NewMaybeRelocatableFeltFromUint64(3), + NewMaybeRelocatableFeltFromUint64(7), + }, + "quotient": {nil, nil}, + "remainder": {nil, nil}, + } + idsManager := SetupIdsForTest(ids, vm) + hintData := any(HintData{ + Ids: idsManager, + Code: UINT256_UNSIGNED_DIV_REM, + }) + hintProcessor := CairoVmHintProcessor{} + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) + if err != nil { + t.Errorf("failed with error: %s", err) + } + + quotient, err := idsManager.GetUint256("quotient", vm) + if err != nil { + t.Errorf("failed with error: %s", err) + } + + expectedQuotient := Uint256{Low: FeltFromUint(10), High: FeltFromUint(0)} + if quotient != expectedQuotient { + t.Errorf("expected quotient: %s, got: %s", expectedQuotient.ToString(), quotient.ToString()) + } + + remainder, err := idsManager.GetUint256("remainder", vm) + if err != nil { + t.Errorf("failed with error: %s", err) + } + + expectedRemainder := Uint256{Low: FeltFromUint(59), High: FeltFromUint(2)} + if remainder != expectedRemainder { + t.Errorf("expected remainder: %s, got: %s", expectedRemainder.ToString(), remainder.ToString()) + } + +} + +func TestUint256UnsignedDivRemInvalidMemoryInsert(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + + ids := map[string][]*MaybeRelocatable{ + "a": { + NewMaybeRelocatableFeltFromUint64(89), + NewMaybeRelocatableFeltFromUint64(72), + }, + "div": { + NewMaybeRelocatableFeltFromUint64(3), + NewMaybeRelocatableFeltFromUint64(7), + }, + "quotient": {NewMaybeRelocatableFeltFromUint64(2), NewMaybeRelocatableFelt(FeltZero())}, + "remainder": {nil, nil}, + } + idsManager := SetupIdsForTest(ids, vm) + hintData := any(HintData{ + Ids: idsManager, + Code: UINT256_UNSIGNED_DIV_REM, + }) + hintProcessor := CairoVmHintProcessor{} + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) + if err == nil { + t.Errorf("this test should fail") + } +} + +func TestUint256ExpandedUnsignedDivRemOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + + ids := map[string][]*MaybeRelocatable{ + "a": { + NewMaybeRelocatableFeltFromUint64(89), + NewMaybeRelocatableFeltFromUint64(72), + }, + + "div": { + NewMaybeRelocatableFelt(FeltFromDecString("55340232221128654848")), + NewMaybeRelocatableFeltFromUint64(3), + NewMaybeRelocatableFelt(FeltFromDecString("129127208515966861312")), + NewMaybeRelocatableFeltFromUint64(7), + }, + "quotient": {nil, nil}, + "remainder": {nil, nil}, + } + idsManager := SetupIdsForTest(ids, vm) + hintData := any(HintData{ + Ids: idsManager, + Code: UINT256_EXPANDED_UNSIGNED_DIV_REM, + }) + hintProcessor := CairoVmHintProcessor{} + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) + if err != nil { + t.Errorf("failed with error: %s", err) + } + + quotient, err := idsManager.GetUint256("quotient", vm) + if err != nil { + t.Errorf("failed with error: %s", err) + } + + expectedQuotient := Uint256{Low: FeltFromUint(10), High: FeltFromUint(0)} + if quotient != expectedQuotient { + t.Errorf("expected quotient: %s, got: %s", expectedQuotient.ToString(), quotient.ToString()) + } + + remainder, err := idsManager.GetUint256("remainder", vm) + if err != nil { + t.Errorf("failed with error: %s", err) + } + + expectedRemainder := Uint256{Low: FeltFromUint(59), High: FeltFromUint(2)} + if remainder != expectedRemainder { + t.Errorf("expected remainder: %s, got: %s", expectedRemainder.ToString(), remainder.ToString()) + } + +} + +func TestUint256MulDivOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + + ids := map[string][]*MaybeRelocatable{ + "a": { + NewMaybeRelocatableFeltFromUint64(89), + NewMaybeRelocatableFeltFromUint64(72), + }, + "b": { + NewMaybeRelocatableFeltFromUint64(3), + NewMaybeRelocatableFeltFromUint64(7), + }, + "div": { + NewMaybeRelocatableFeltFromUint64(107), + NewMaybeRelocatableFeltFromUint64(114), + }, + "quotient_low": {nil, nil}, + "quotient_high": {nil, nil}, + "remainder": {nil, nil}, + } + idsManager := SetupIdsForTest(ids, vm) + hintData := any(HintData{ + Ids: idsManager, + Code: UINT256_MUL_DIV_MOD, + }) + hintProcessor := CairoVmHintProcessor{} + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) + if err != nil { + t.Errorf("failed with error: %s", err) + } + + quotientLow, err := idsManager.GetUint256("quotient_low", vm) + if err != nil { + t.Errorf("failed with error: %s", err) + } + expectedQuotientLow := Uint256{Low: FeltFromDecString("143276786071974089879315624181797141668"), High: FeltFromUint(4)} + if !quotientLow.IsEqual(expectedQuotientLow) { + t.Errorf("expected quotient_low: %s, got: %s", expectedQuotientLow.ToString(), quotientLow.ToString()) + } + + quotientHigh, err := idsManager.GetUint256("quotient_high", vm) + if err != nil { + t.Errorf("failed with error: %s", err) + } + expectedQuotientHigh := Uint256{Low: FeltFromUint(0), High: FeltFromUint(0)} + if !quotientHigh.IsEqual(expectedQuotientHigh) { + t.Errorf("expected quotient_high: %s, got: %s", expectedQuotientHigh.ToString(), quotientHigh.ToString()) + } + + remainder, err := idsManager.GetUint256("remainder", vm) + if err != nil { + t.Errorf("failed with error: %s", err) + } + expectedRemainder := Uint256{Low: FeltFromDecString("322372768661941702228460154409043568767"), High: FeltFromUint(101)} + if !remainder.IsEqual(expectedRemainder) { + t.Errorf("expected remainder: %s, got: %s", expectedRemainder.ToString(), remainder.ToString()) + } +} + +func TestUint256SubNonNegativeOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + + ids := map[string][]*MaybeRelocatable{ + "a": { + NewMaybeRelocatableFeltFromUint64(12179), + NewMaybeRelocatableFeltFromUint64(13044), + }, + "b": { + NewMaybeRelocatableFeltFromUint64(1001), + NewMaybeRelocatableFeltFromUint64(6687), + }, + "res": {nil, nil}, + } + idsManager := SetupIdsForTest(ids, vm) + hintData := any(HintData{ + Ids: idsManager, + Code: UINT256_SUB, + }) + hintProcessor := CairoVmHintProcessor{} + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) + if err != nil { + t.Errorf("failed with error: %s", err) + } + + result, err := idsManager.GetUint256("res", vm) + if err != nil { + t.Errorf("failed with error: %s", err) + } + expectedResult := Uint256{Low: FeltFromUint(11178), High: FeltFromUint(6357)} + if !result.IsEqual(expectedResult) { + t.Errorf("expected result: %s, got: %s", expectedResult.ToString(), result.ToString()) + } +} + +func TestUint256SubNegativeOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + + ids := map[string][]*MaybeRelocatable{ + "a": { + NewMaybeRelocatableFeltFromUint64(1001), + NewMaybeRelocatableFeltFromUint64(6687), + }, + "b": { + NewMaybeRelocatableFeltFromUint64(12179), + NewMaybeRelocatableFeltFromUint64(13044), + }, + "res": {nil, nil}, + } + idsManager := SetupIdsForTest(ids, vm) + hintData := any(HintData{ + Ids: idsManager, + Code: UINT256_SUB, + }) + hintProcessor := CairoVmHintProcessor{} + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) + if err != nil { + t.Errorf("failed with error: %s", err) + } + + result, err := idsManager.GetUint256("res", vm) + if err != nil { + t.Errorf("failed with error: %s", err) + } + expectedResult := Uint256{Low: FeltFromDecString("340282366920938463463374607431768200278"), High: FeltFromDecString("340282366920938463463374607431768205098")} + if !result.IsEqual(expectedResult) { + t.Errorf("expected result: %s, got: %s", expectedResult.ToString(), result.ToString()) + } +} + +func TestUint256SubBHighGt256LteA(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + + ids := map[string][]*MaybeRelocatable{ + "a": { + NewMaybeRelocatableFelt(FeltFromDecString("340282366920938463463374607431768211456")), + NewMaybeRelocatableFelt(FeltZero()), + }, + "b": { + NewMaybeRelocatableFelt(FeltZero()), + NewMaybeRelocatableFelt(FeltFromDecString("340282366920938463463374607431768211457")), + }, + "res": {nil, nil}, + } + idsManager := SetupIdsForTest(ids, vm) + hintData := any(HintData{ + Ids: idsManager, + Code: UINT256_SUB, + }) + hintProcessor := CairoVmHintProcessor{} + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) + if err != nil { + t.Errorf("failed with error: %s", err) + } + + result, err := idsManager.GetUint256("res", vm) + if err != nil { + t.Errorf("failed with error: %s", err) + } + expectedResult := Uint256{Low: FeltZero(), High: FeltZero()} + if !result.IsEqual(expectedResult) { + t.Errorf("expected result: %s, got: %s", expectedResult.ToString(), result.ToString()) + } +} + +func TestUint256SubBHighGt256GtA(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + + ids := map[string][]*MaybeRelocatable{ + "a": { + NewMaybeRelocatableFelt(FeltOne()), + NewMaybeRelocatableFelt(FeltZero()), + }, + "b": { + NewMaybeRelocatableFelt(FeltZero()), + NewMaybeRelocatableFelt(FeltFromDecString("340282366920938463463374607431768211457")), + }, + "res": {nil, nil}, + } + idsManager := SetupIdsForTest(ids, vm) + hintData := any(HintData{ + Ids: idsManager, + Code: UINT256_SUB, + }) + hintProcessor := CairoVmHintProcessor{} + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) + if err != nil { + t.Errorf("failed with error: %s", err) + } + + result, err := idsManager.GetUint256("res", vm) + if err != nil { + t.Errorf("failed with error: %s", err) + } + expectedResult := Uint256{Low: FeltOne(), High: FeltFromDecString("340282366920938463463374607431768211455")} + if !result.IsEqual(expectedResult) { + t.Errorf("expected result: %s, got: %s", expectedResult.ToString(), result.ToString()) + } +} + +func TestUint256SubMissingNumber(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + + ids := map[string][]*MaybeRelocatable{ + "a": { + NewMaybeRelocatableFelt(FeltOne()), + nil, + }, + } + idsManager := SetupIdsForTest(ids, vm) + hintData := any(HintData{ + Ids: idsManager, + Code: UINT256_SUB, + }) + hintProcessor := CairoVmHintProcessor{} + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) + if err == nil { + t.Errorf("should fail with error: Memory Get: Value not found") + } +} diff --git a/pkg/lambdaworks/lambdaworks.go b/pkg/lambdaworks/lambdaworks.go index 3cd29d21..fb175502 100644 --- a/pkg/lambdaworks/lambdaworks.go +++ b/pkg/lambdaworks/lambdaworks.go @@ -353,7 +353,7 @@ func (a Felt) DivFloor(b Felt) Felt { } /* -Compares x and y and returns: +Compares a and b and returns: -1 if a < b 0 if a == b diff --git a/pkg/vm/cairo_run/cairo_run_test.go b/pkg/vm/cairo_run/cairo_run_test.go index f0685afb..4aa6c0f3 100644 --- a/pkg/vm/cairo_run/cairo_run_test.go +++ b/pkg/vm/cairo_run/cairo_run_test.go @@ -352,3 +352,15 @@ func TestCairoKeccak(t *testing.T) { func TestKeccakAddUint256(t *testing.T) { testProgram("keccak_add_uint256", t) } + +func TestUint256Integration(t *testing.T) { + testProgram("uint256_integration_tests", t) +} + +func TestUint256(t *testing.T) { + testProgram("uint256", t) +} + +func TestUint256Root(t *testing.T) { + testProgram("uint256_root", t) +} diff --git a/pkg/vm/memory/memory.go b/pkg/vm/memory/memory.go index 4939a8a0..86aa49cf 100644 --- a/pkg/vm/memory/memory.go +++ b/pkg/vm/memory/memory.go @@ -117,7 +117,7 @@ func (m *Memory) Get(addr Relocatable) (*MaybeRelocatable, error) { value, ok := m.Data[addr] if !ok { - return nil, errors.New("Memory Get: Value not found") + return nil, errors.Errorf("Memory Get: Value not found in addr: %s", addr.ToString()) } return &value, nil