From f1b2f6f143dd5b05702aed90ef5df5091b2b1ded Mon Sep 17 00:00:00 2001 From: Mariano Nicolini Date: Mon, 18 Sep 2023 11:53:49 -0300 Subject: [PATCH 01/55] Add hint codes --- pkg/hints/divrem_hint_codes.go | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 pkg/hints/divrem_hint_codes.go diff --git a/pkg/hints/divrem_hint_codes.go b/pkg/hints/divrem_hint_codes.go new file mode 100644 index 00000000..6e96995b --- /dev/null +++ b/pkg/hints/divrem_hint_codes.go @@ -0,0 +1,5 @@ +package hints + +const UNSIGNED_DIV_REM = "from starkware.cairo.common.math_utils import assert_integer\nassert_integer(ids.div)\nassert 0 < ids.div <= PRIME // range_check_builtin.bound, \\\n f'div={hex(ids.div)} is out of the valid range.'\nids.q, ids.r = divmod(ids.value, ids.div)" + +const SIGNED_DIV_REM = "from starkware.cairo.common.math_utils import as_int, assert_integer\n\nassert_integer(ids.div)\nassert 0 < ids.div <= PRIME // range_check_builtin.bound, \\\n f'div={hex(ids.div)} is out of the valid range.'\n\nassert_integer(ids.bound)\nassert ids.bound <= range_check_builtin.bound // 2, \\\n f'bound={hex(ids.bound)} is out of the valid range.'\n\nint_value = as_int(ids.value, PRIME)\nq, ids.r = divmod(int_value, ids.div)\n\nassert -ids.bound <= q < ids.bound, \\\n f'{int_value} / {ids.div} = {q} is out of the range [{-ids.bound}, {ids.bound}).'\n\nids.biased_q = q + ids.bound" From d60bf791cff1b042ab1a28987f48023cae9a9065 Mon Sep 17 00:00:00 2001 From: Mariano Nicolini Date: Mon, 18 Sep 2023 12:03:42 -0300 Subject: [PATCH 02/55] Move divrem hint codes to math hint codes --- pkg/hints/divrem_hint_codes.go | 5 ----- pkg/hints/math_hint_codes.go | 4 ++++ 2 files changed, 4 insertions(+), 5 deletions(-) delete mode 100644 pkg/hints/divrem_hint_codes.go diff --git a/pkg/hints/divrem_hint_codes.go b/pkg/hints/divrem_hint_codes.go deleted file mode 100644 index 6e96995b..00000000 --- a/pkg/hints/divrem_hint_codes.go +++ /dev/null @@ -1,5 +0,0 @@ -package hints - -const UNSIGNED_DIV_REM = "from starkware.cairo.common.math_utils import assert_integer\nassert_integer(ids.div)\nassert 0 < ids.div <= PRIME // range_check_builtin.bound, \\\n f'div={hex(ids.div)} is out of the valid range.'\nids.q, ids.r = divmod(ids.value, ids.div)" - -const SIGNED_DIV_REM = "from starkware.cairo.common.math_utils import as_int, assert_integer\n\nassert_integer(ids.div)\nassert 0 < ids.div <= PRIME // range_check_builtin.bound, \\\n f'div={hex(ids.div)} is out of the valid range.'\n\nassert_integer(ids.bound)\nassert ids.bound <= range_check_builtin.bound // 2, \\\n f'bound={hex(ids.bound)} is out of the valid range.'\n\nint_value = as_int(ids.value, PRIME)\nq, ids.r = divmod(int_value, ids.div)\n\nassert -ids.bound <= q < ids.bound, \\\n f'{int_value} / {ids.div} = {q} is out of the range [{-ids.bound}, {ids.bound}).'\n\nids.biased_q = q + ids.bound" diff --git a/pkg/hints/math_hint_codes.go b/pkg/hints/math_hint_codes.go index fcec4449..0d7b90ec 100644 --- a/pkg/hints/math_hint_codes.go +++ b/pkg/hints/math_hint_codes.go @@ -9,3 +9,7 @@ const ASSERT_NOT_ZERO = "from starkware.cairo.common.math_utils import assert_in const ASSERT_NOT_EQUAL = "from starkware.cairo.lang.vm.relocatable import RelocatableValue\nboth_ints = isinstance(ids.a, int) and isinstance(ids.b, int)\nboth_relocatable = (\n isinstance(ids.a, RelocatableValue) and isinstance(ids.b, RelocatableValue) and\n ids.a.segment_index == ids.b.segment_index)\nassert both_ints or both_relocatable, \\\n f'assert_not_equal failed: non-comparable values: {ids.a}, {ids.b}.'\nassert (ids.a - ids.b) % PRIME != 0, f'assert_not_equal failed: {ids.a} = {ids.b}.'" const SQRT = "from starkware.python.math_utils import isqrt\nvalue = ids.value % PRIME\nassert value < 2 ** 250, f\"value={value} is outside of the range [0, 2**250).\"\nassert 2 ** 250 < PRIME\nids.root = isqrt(value)" + +const UNSIGNED_DIV_REM = "from starkware.cairo.common.math_utils import assert_integer\nassert_integer(ids.div)\nassert 0 < ids.div <= PRIME // range_check_builtin.bound, \\\n f'div={hex(ids.div)} is out of the valid range.'\nids.q, ids.r = divmod(ids.value, ids.div)" + +const SIGNED_DIV_REM = "from starkware.cairo.common.math_utils import as_int, assert_integer\n\nassert_integer(ids.div)\nassert 0 < ids.div <= PRIME // range_check_builtin.bound, \\\n f'div={hex(ids.div)} is out of the valid range.'\n\nassert_integer(ids.bound)\nassert ids.bound <= range_check_builtin.bound // 2, \\\n f'bound={hex(ids.bound)} is out of the valid range.'\n\nint_value = as_int(ids.value, PRIME)\nq, ids.r = divmod(int_value, ids.div)\n\nassert -ids.bound <= q < ids.bound, \\\n f'{int_value} / {ids.div} = {q} is out of the range [{-ids.bound}, {ids.bound}).'\n\nids.biased_q = q + ids.bound" From 3691ea7bff8162761f833264908ed58412266b1b Mon Sep 17 00:00:00 2001 From: Mariano Nicolini Date: Mon, 18 Sep 2023 13:56:01 -0300 Subject: [PATCH 03/55] Finish implementation of unsignedDivRem hint --- pkg/hints/hint_processor.go | 2 ++ pkg/hints/math_hints.go | 43 ++++++++++++++++++++++++++++++++++ pkg/lambdaworks/lambdaworks.go | 5 ++++ 3 files changed, 50 insertions(+) diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index 8cf116f1..e51cb0c2 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -67,6 +67,8 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, return memcpy_enter_scope(data.Ids, vm, execScopes) case VM_ENTER_SCOPE: return vm_enter_scope(execScopes) + case UNSIGNED_DIV_REM: + return unsignedDivRem(data.Ids, vm) default: return errors.Errorf("Unknown Hint: %s", data.Code) } diff --git a/pkg/hints/math_hints.go b/pkg/hints/math_hints.go index b303d95c..f0a539a9 100644 --- a/pkg/hints/math_hints.go +++ b/pkg/hints/math_hints.go @@ -1,8 +1,11 @@ package hints import ( + "math/big" + "github.com/lambdaclass/cairo-vm.go/pkg/builtins" . "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/lambdaworks" . "github.com/lambdaclass/cairo-vm.go/pkg/math_utils" . "github.com/lambdaclass/cairo-vm.go/pkg/vm" @@ -118,3 +121,43 @@ func sqrt(ids IdsManager, vm *VirtualMachine) error { ids.Insert("root", NewMaybeRelocatableFelt(root_felt), vm) return nil } + +/* +Implements hint: + + %{ + from starkware.cairo.common.math_utils import assert_integer + assert_integer(ids.div) + assert 0 < ids.div <= PRIME // range_check_builtin.bound, \ + f'div={hex(ids.div)} is out of the valid range.' + ids.q, ids.r = divmod(ids.value, ids.div) + %} +*/ +func unsignedDivRem(ids IdsManager, vm *VirtualMachine) error { + div, err := ids.GetFelt("div", vm) + if err != nil { + return err + } + + value, err := ids.GetFelt("value", vm) + if err != nil { + return err + } + + // It is safe to cast INNER_RC_BOUND into int64 since the constant is set to 65536 + rcBound := big.NewInt(int64(builtins.INNER_RC_BOUND)) + limit := new(big.Int).Div(lambdaworks.Prime(), rcBound) + + // Check if `div` is greater than `limit` + cmp := div.ToBigInt().Cmp(limit) == 1 + + if div.IsZero() || cmp { + return errors.Errorf("Div out of range: 0 < %d <= %d", div, rcBound) + } + + q, r := value.DivRem(div) + ids.Insert("q", NewMaybeRelocatableFelt(q), vm) + ids.Insert("r", NewMaybeRelocatableFelt(r), vm) + + return nil +} diff --git a/pkg/lambdaworks/lambdaworks.go b/pkg/lambdaworks/lambdaworks.go index e5c36bd3..6b9c32e8 100644 --- a/pkg/lambdaworks/lambdaworks.go +++ b/pkg/lambdaworks/lambdaworks.go @@ -258,6 +258,11 @@ func (f Felt) ToBigInt() *big.Int { const CAIRO_PRIME_HEX = "0x800000000000011000000000000000000000000000000000000000000000001" const SIGNED_FELT_MAX_HEX = "0x400000000000008800000000000000000000000000000000000000000000000" +func Prime() *big.Int { + cairoPrime, _ := new(big.Int).SetString(CAIRO_PRIME_HEX, 0) + return cairoPrime +} + // Implements `as_int` behaviour func (f Felt) ToSigned() *big.Int { n := f.ToBigInt() From 29938c7799aba167c5d8797002cd51a69cf900b2 Mon Sep 17 00:00:00 2001 From: Mariano Nicolini Date: Mon, 18 Sep 2023 17:40:58 -0300 Subject: [PATCH 04/55] Add some fields to range check builtin runner --- pkg/builtins/range_check.go | 10 ++- pkg/builtins/range_check_test.go | 8 +-- pkg/hints/math_hints.go | 111 ++++++++++++++++++++++++++++++- pkg/runners/cairo_runner_test.go | 2 +- 4 files changed, 122 insertions(+), 9 deletions(-) diff --git a/pkg/builtins/range_check.go b/pkg/builtins/range_check.go index 49aaa4c6..fd832042 100644 --- a/pkg/builtins/range_check.go +++ b/pkg/builtins/range_check.go @@ -36,14 +36,18 @@ type RangeCheckBuiltinRunner struct { ratio uint instancesPerComponent uint StopPtr *uint + nParts uint + Bound lambdaworks.Felt } -func NewRangeCheckBuiltinRunner(ratio uint) *RangeCheckBuiltinRunner { - return &RangeCheckBuiltinRunner{ratio: ratio, instancesPerComponent: 1} +func NewRangeCheckBuiltinRunner(ratio uint, nParts uint) *RangeCheckBuiltinRunner { + bound := lambdaworks.FeltOne().Shl(16 * uint64(nParts)) + + return &RangeCheckBuiltinRunner{ratio: ratio, instancesPerComponent: 1, nParts: nParts, Bound: bound} } func DefaultRangeCheckBuiltinRunner() *RangeCheckBuiltinRunner { - return NewRangeCheckBuiltinRunner(8) + return NewRangeCheckBuiltinRunner(8, 8) } func (r *RangeCheckBuiltinRunner) Base() memory.Relocatable { diff --git a/pkg/builtins/range_check_test.go b/pkg/builtins/range_check_test.go index 8563a988..17db35db 100644 --- a/pkg/builtins/range_check_test.go +++ b/pkg/builtins/range_check_test.go @@ -58,7 +58,7 @@ func TestGetAllocatedMemoryUnitsRangeCheck(t *testing.T) { } func TestGetRangeCheckUsageSuccessfulA(t *testing.T) { - var builtin = builtins.NewRangeCheckBuiltinRunner(8) + var builtin = builtins.DefaultRangeCheckBuiltinRunner() builtin.Include(true) builtin.SetBase(memory.NewRelocatable(0, 0)) @@ -82,7 +82,7 @@ func TestGetRangeCheckUsageSuccessfulA(t *testing.T) { } func TestGetRangeCheckUsageSuccessfulB(t *testing.T) { - var builtin = builtins.NewRangeCheckBuiltinRunner(8) + var builtin = builtins.DefaultRangeCheckBuiltinRunner() builtin.Include(true) builtin.SetBase(memory.NewRelocatable(0, 0)) @@ -106,7 +106,7 @@ func TestGetRangeCheckUsageSuccessfulB(t *testing.T) { } func TestGetRangeCheckUsageSuccessfulC(t *testing.T) { - var builtin = builtins.NewRangeCheckBuiltinRunner(8) + var builtin = builtins.DefaultRangeCheckBuiltinRunner() builtin.Include(true) builtin.SetBase(memory.NewRelocatable(0, 0)) @@ -132,7 +132,7 @@ func TestGetRangeCheckUsageSuccessfulC(t *testing.T) { } func TestGetRangeCheckUsageEmptyMemory(t *testing.T) { - var builtin = builtins.NewRangeCheckBuiltinRunner(8) + var builtin = builtins.DefaultRangeCheckBuiltinRunner() builtin.Include(true) builtin.SetBase(memory.NewRelocatable(0, 0)) diff --git a/pkg/hints/math_hints.go b/pkg/hints/math_hints.go index f0a539a9..c8fc48b8 100644 --- a/pkg/hints/math_hints.go +++ b/pkg/hints/math_hints.go @@ -138,7 +138,6 @@ func unsignedDivRem(ids IdsManager, vm *VirtualMachine) error { if err != nil { return err } - value, err := ids.GetFelt("value", vm) if err != nil { return err @@ -161,3 +160,113 @@ func unsignedDivRem(ids IdsManager, vm *VirtualMachine) error { return nil } + +/* +Implements hint: + + %{ + from starkware.cairo.common.math_utils import as_int, assert_integer + + assert_integer(ids.div) + assert 0 < ids.div <= PRIME // range_check_builtin.bound, \ + f'div={hex(ids.div)} is out of the valid range.' + + assert_integer(ids.bound) + assert ids.bound <= range_check_builtin.bound // 2, \ + f'bound={hex(ids.bound)} is out of the valid range.' + + int_value = as_int(ids.value, PRIME) + q, ids.r = divmod(int_value, ids.div) + + assert -ids.bound <= q < ids.bound, \ + f'{int_value} / {ids.div} = {q} is out of the range [{-ids.bound}, {ids.bound}).' + + ids.biased_q = q + ids.bound + %} +*/ + +// func signedDivRem(ids IdsManager, vm *VirtualMachine) error { +// div, err := ids.GetFelt("div", vm) +// if err != nil { +// return err +// } +// value, err := ids.GetFelt("value", vm) +// if err != nil { +// return err +// } +// bound, err := ids.GetFelt("bound", vm) +// if err != nil { +// return err +// } + +// // It is safe to cast INNER_RC_BOUND into int64 since the constant is set to 65536 +// rcBound := big.NewInt(int64(builtins.INNER_RC_BOUND)) +// limit := new(big.Int).Div(lambdaworks.Prime(), rcBound) + +// // Check if `div` is greater than `limit` +// cmp := div.ToBigInt().Cmp(limit) == 1 + +// if div.IsZero() || cmp { +// return errors.Errorf("Div out of range: 0 < %d <= %d", div, rcBound) +// } + +// return nil +// } + +// pub fn signed_div_rem( +// vm: &mut VirtualMachine, +// ids_data: &HashMap, +// ap_tracking: &ApTracking, +// ) -> Result<(), HintError> { +// let div = get_integer_from_var_name("div", vm, ids_data, ap_tracking)?; +// let value = get_integer_from_var_name("value", vm, ids_data, ap_tracking)?; +// let value = value.as_ref(); +// let bound = get_integer_from_var_name("bound", vm, ids_data, ap_tracking)?; +// let builtin = vm.get_range_check_builtin()?; + +// match &builtin._bound { +// Some(builtin_bound) +// if div.is_zero() || div.as_ref() > &div_prime_by_bound(builtin_bound.clone())? => +// { +// return Err(HintError::OutOfValidRange(Box::new(( +// div.into_owned(), +// builtin_bound.clone(), +// )))); +// } +// Some(builtin_bound) if bound.as_ref() > &(builtin_bound >> 1_u32) => { +// return Err(HintError::OutOfValidRange(Box::new(( +// bound.into_owned(), +// builtin_bound >> 1_u32, +// )))); +// } +// None if div.is_zero() => { +// return Err(HintError::OutOfValidRange(Box::new(( +// div.into_owned(), +// Felt252::zero() - Felt252::one(), +// )))); +// } +// _ => {} +// } + +// let int_value = value.to_signed_felt(); +// let int_div = div.to_bigint(); +// let int_bound = bound.to_bigint(); +// let (q, r) = int_value.div_mod_floor(&int_div); + +// if int_bound.abs() < q.abs() { +// return Err(HintError::OutOfValidRange(Box::new(( +// Felt252::new(q), +// bound.into_owned(), +// )))); +// } + +// let biased_q = q + int_bound; +// insert_value_from_var_name("r", Felt252::new(r), vm, ids_data, ap_tracking)?; +// insert_value_from_var_name( +// "biased_q", +// Felt252::new(biased_q), +// vm, +// ids_data, +// ap_tracking, +// ) +// } diff --git a/pkg/runners/cairo_runner_test.go b/pkg/runners/cairo_runner_test.go index 7005a8c6..e19367d1 100644 --- a/pkg/runners/cairo_runner_test.go +++ b/pkg/runners/cairo_runner_test.go @@ -609,7 +609,7 @@ func TestCheckRangeUsageInsufficientAllocatedCells(t *testing.T) { t.Error("Could not initialize Cairo Runner") } runner.Vm.Trace = make([]vm.TraceEntry, 0) - builtin := builtins.NewRangeCheckBuiltinRunner(8) + builtin := builtins.DefaultRangeCheckBuiltinRunner() runner.Vm.BuiltinRunners = append(runner.Vm.BuiltinRunners, builtin) runner.Vm.Segments.AddSegment() runner.Vm.Segments.Memory.Insert( From f769433b453d8a1512243129ceb31dfcfd02c974 Mon Sep 17 00:00:00 2001 From: Mariano Nicolini Date: Mon, 18 Sep 2023 20:15:02 -0300 Subject: [PATCH 05/55] WIP implementation of signedDivRem hint --- pkg/hints/math_hints.go | 80 +++++++++++++++++++++++++---------------- pkg/vm/vm_core.go | 14 ++++++++ 2 files changed, 64 insertions(+), 30 deletions(-) diff --git a/pkg/hints/math_hints.go b/pkg/hints/math_hints.go index c8fc48b8..7cfe2448 100644 --- a/pkg/hints/math_hints.go +++ b/pkg/hints/math_hints.go @@ -143,9 +143,12 @@ func unsignedDivRem(ids IdsManager, vm *VirtualMachine) error { return err } - // It is safe to cast INNER_RC_BOUND into int64 since the constant is set to 65536 - rcBound := big.NewInt(int64(builtins.INNER_RC_BOUND)) - limit := new(big.Int).Div(lambdaworks.Prime(), rcBound) + rcBound, err := vm.GetRangeCheckBound() + if err != nil { + return err + } + + limit := new(big.Int).Div(lambdaworks.Prime(), rcBound.ToBigInt()) // Check if `div` is greater than `limit` cmp := div.ToBigInt().Cmp(limit) == 1 @@ -185,33 +188,50 @@ Implements hint: %} */ -// func signedDivRem(ids IdsManager, vm *VirtualMachine) error { -// div, err := ids.GetFelt("div", vm) -// if err != nil { -// return err -// } -// value, err := ids.GetFelt("value", vm) -// if err != nil { -// return err -// } -// bound, err := ids.GetFelt("bound", vm) -// if err != nil { -// return err -// } - -// // It is safe to cast INNER_RC_BOUND into int64 since the constant is set to 65536 -// rcBound := big.NewInt(int64(builtins.INNER_RC_BOUND)) -// limit := new(big.Int).Div(lambdaworks.Prime(), rcBound) - -// // Check if `div` is greater than `limit` -// cmp := div.ToBigInt().Cmp(limit) == 1 - -// if div.IsZero() || cmp { -// return errors.Errorf("Div out of range: 0 < %d <= %d", div, rcBound) -// } - -// return nil -// } +func signedDivRem(ids IdsManager, vm *VirtualMachine) error { + div, err := ids.GetFelt("div", vm) + if err != nil { + return err + } + value, err := ids.GetFelt("value", vm) + if err != nil { + return err + } + bound, err := ids.GetFelt("bound", vm) + if err != nil { + return err + } + + // It is safe to cast INNER_RC_BOUND into int64 since the constant is set to 65536 + rcBound, err := vm.GetRangeCheckBound() + if err != nil { + return err + } + + if rcBound.Cmp(lambdaworks.FeltZero()) == 0 { + return errors.New("range check bound cannot be zero") + } + primeBoundDivision := new(big.Int).Div(lambdaworks.Prime(), rcBound.ToBigInt()) + + // Check if `div` is greater than `limit` and make assertions + divGreater := div.ToBigInt().Cmp(primeBoundDivision) == 1 + if div.IsZero() || divGreater { + return errors.Errorf("div=%d is out of the valid range", div) + } + + if bound.Cmp(rcBound.Shr(1)) == 1 { + return errors.Errorf("bound=%d is out of the valid range") + } + + sgnValue := value.ToSigned() + sgnBound := bound.ToBigInt() + intDiv := div.ToBigInt() + + q := new(big.Int).Div(sgnValue, intDiv) + r := new(big.Int).Rem(sgnValue, intDiv) + + return nil +} // pub fn signed_div_rem( // vm: &mut VirtualMachine, diff --git a/pkg/vm/vm_core.go b/pkg/vm/vm_core.go index c5f1b121..e3522cd5 100644 --- a/pkg/vm/vm_core.go +++ b/pkg/vm/vm_core.go @@ -626,3 +626,17 @@ func (vm *VirtualMachine) GetBuiltinRunner(builtinName string) (*builtins.Builti } return nil, &VirtualMachineError{"BuiltinNotFound"} } + +func (vm *VirtualMachine) GetRangeCheckBound() (lambdaworks.Felt, error) { + builtin, err := vm.GetBuiltinRunner("range_check") + if err != nil { + return lambdaworks.FeltZero(), err + } + + rcBuiltin, ok := (*builtin).(*builtins.RangeCheckBuiltinRunner) + if !ok { + return lambdaworks.FeltZero(), errors.New("could not cast to RangeCheckBuiltinRunner") + } + + return rcBuiltin.Bound, nil +} From 6b54344a75a920653361606a54874281160fa493 Mon Sep 17 00:00:00 2001 From: Mariano Nicolini Date: Tue, 19 Sep 2023 10:24:52 -0300 Subject: [PATCH 06/55] Dummy commit --- pkg/hints/math_hints.go | 88 ++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/pkg/hints/math_hints.go b/pkg/hints/math_hints.go index 7cfe2448..1ed6198b 100644 --- a/pkg/hints/math_hints.go +++ b/pkg/hints/math_hints.go @@ -188,50 +188,50 @@ Implements hint: %} */ -func signedDivRem(ids IdsManager, vm *VirtualMachine) error { - div, err := ids.GetFelt("div", vm) - if err != nil { - return err - } - value, err := ids.GetFelt("value", vm) - if err != nil { - return err - } - bound, err := ids.GetFelt("bound", vm) - if err != nil { - return err - } - - // It is safe to cast INNER_RC_BOUND into int64 since the constant is set to 65536 - rcBound, err := vm.GetRangeCheckBound() - if err != nil { - return err - } - - if rcBound.Cmp(lambdaworks.FeltZero()) == 0 { - return errors.New("range check bound cannot be zero") - } - primeBoundDivision := new(big.Int).Div(lambdaworks.Prime(), rcBound.ToBigInt()) - - // Check if `div` is greater than `limit` and make assertions - divGreater := div.ToBigInt().Cmp(primeBoundDivision) == 1 - if div.IsZero() || divGreater { - return errors.Errorf("div=%d is out of the valid range", div) - } - - if bound.Cmp(rcBound.Shr(1)) == 1 { - return errors.Errorf("bound=%d is out of the valid range") - } - - sgnValue := value.ToSigned() - sgnBound := bound.ToBigInt() - intDiv := div.ToBigInt() - - q := new(big.Int).Div(sgnValue, intDiv) - r := new(big.Int).Rem(sgnValue, intDiv) - - return nil -} +// func signedDivRem(ids IdsManager, vm *VirtualMachine) error { +// div, err := ids.GetFelt("div", vm) +// if err != nil { +// return err +// } +// value, err := ids.GetFelt("value", vm) +// if err != nil { +// return err +// } +// bound, err := ids.GetFelt("bound", vm) +// if err != nil { +// return err +// } + +// // It is safe to cast INNER_RC_BOUND into int64 since the constant is set to 65536 +// rcBound, err := vm.GetRangeCheckBound() +// if err != nil { +// return err +// } + +// if rcBound.Cmp(lambdaworks.FeltZero()) == 0 { +// return errors.New("range check bound cannot be zero") +// } +// primeBoundDivision := new(big.Int).Div(lambdaworks.Prime(), rcBound.ToBigInt()) + +// // Check if `div` is greater than `limit` and make assertions +// divGreater := div.ToBigInt().Cmp(primeBoundDivision) == 1 +// if div.IsZero() || divGreater { +// return errors.Errorf("div=%d is out of the valid range", div) +// } + +// if bound.Cmp(rcBound.Shr(1)) == 1 { +// return errors.Errorf("bound=%d is out of the valid range") +// } + +// sgnValue := value.ToSigned() +// sgnBound := bound.ToBigInt() +// intDiv := div.ToBigInt() + +// q := new(big.Int).Div(sgnValue, intDiv) +// r := new(big.Int).Rem(sgnValue, intDiv) + +// return nil +// } // pub fn signed_div_rem( // vm: &mut VirtualMachine, From 91f1436a506fd624933535d0a35cd6e8eab5cd80 Mon Sep 17 00:00:00 2001 From: Mariano Nicolini Date: Tue, 19 Sep 2023 11:16:36 -0300 Subject: [PATCH 07/55] Save work in progress --- cairo_programs/signed_div_rem.cairo | 50 ++++++++++ cairo_programs/unsigned_div_rem.cairo | 27 ++++++ pkg/hints/math_hints.go | 131 ++++++++++---------------- pkg/vm/cairo_run/cairo_run_test.go | 8 ++ 4 files changed, 137 insertions(+), 79 deletions(-) create mode 100644 cairo_programs/signed_div_rem.cairo create mode 100644 cairo_programs/unsigned_div_rem.cairo diff --git a/cairo_programs/signed_div_rem.cairo b/cairo_programs/signed_div_rem.cairo new file mode 100644 index 00000000..70586943 --- /dev/null +++ b/cairo_programs/signed_div_rem.cairo @@ -0,0 +1,50 @@ +%builtins output range_check +from starkware.cairo.common.math import signed_div_rem, assert_le +from starkware.cairo.common.serialize import serialize_word + +func signed_div_rem_man{range_check_ptr}(value, div, bound) -> (q: felt, r: felt) { + let r = [range_check_ptr]; + let biased_q = [range_check_ptr + 1]; // == q + bound. + let range_check_ptr = range_check_ptr + 2; + %{ + from starkware.cairo.common.math_utils import as_int, assert_integer + + assert_integer(ids.div) + assert 0 < ids.div <= PRIME // range_check_builtin.bound, \ + f'div={hex(ids.div)} is out of the valid range.' + + assert_integer(ids.bound) + assert ids.bound <= range_check_builtin.bound // 2, \ + f'bound={hex(ids.bound)} is out of the valid range.' + + int_value = as_int(ids.value, PRIME) + q, ids.r = divmod(int_value, ids.div) + + assert -ids.bound <= q < ids.bound, \ + f'{int_value} / {ids.div} = {q} is out of the range [{-ids.bound}, {ids.bound}).' + + ids.biased_q = q + ids.bound + %} + let q = biased_q - bound; + assert value = q * div + r; + assert_le(r, div - 1); + assert_le(biased_q, 2 * bound - 1); + return (q, r); +} + +func main{output_ptr: felt*, range_check_ptr: felt}() { + let (q_negative_expected, r_negative_expected) = signed_div_rem(-10, 3, 29); + let (q_negative, r_negative) = signed_div_rem_man(-10, 3, 29); + assert q_negative_expected = q_negative; + assert r_negative_expected = r_negative; + serialize_word(q_negative_expected); + serialize_word(q_negative); + serialize_word(r_negative_expected); + serialize_word(r_negative); + + let (q_expected, r_expected) = signed_div_rem(10, 3, 29); + let (q, r) = signed_div_rem_man(10, 3, 29); + assert q_expected = q; + assert r_expected = r; + return (); +} diff --git a/cairo_programs/unsigned_div_rem.cairo b/cairo_programs/unsigned_div_rem.cairo new file mode 100644 index 00000000..2f5e7ba2 --- /dev/null +++ b/cairo_programs/unsigned_div_rem.cairo @@ -0,0 +1,27 @@ +%builtins range_check +from starkware.cairo.common.math import unsigned_div_rem + +func unsigned_div_rem_man{range_check_ptr}(value, div) -> (q: felt, r: felt) { + let r = [range_check_ptr]; + let q = [range_check_ptr + 1]; + let range_check_ptr = range_check_ptr + 2; + %{ + from starkware.cairo.common.math_utils import assert_integer + assert_integer(ids.div) + assert 0 < ids.div <= PRIME // range_check_builtin.bound, \ + f'div={hex(ids.div)} is out of the valid range.' + ids.q, ids.r = divmod(ids.value, ids.div) + %} + + return (q, r); +} + +func main{range_check_ptr: felt}() { + let (q, r) = unsigned_div_rem_man(10, 3); + let (expected_q, expected_r) = unsigned_div_rem(10, 3); + assert q = expected_q; + assert r = expected_r; + assert q = 3; + assert r = 1; + return (); +} diff --git a/pkg/hints/math_hints.go b/pkg/hints/math_hints.go index ce948e7f..0843173e 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" @@ -68,38 +69,6 @@ func assert_not_zero(ids IdsManager, vm *VirtualMachine) error { return nil } -// Implements hint:from starkware.cairo.common.math.cairo -// -// %{ -// from starkware.crypto.signature.signature import FIELD_PRIME -// from starkware.python.math_utils import div_mod, is_quad_residue, sqrt -// -// x = ids.x -// if is_quad_residue(x, FIELD_PRIME): -// ids.y = sqrt(x, FIELD_PRIME) -// else: -// ids.y = sqrt(div_mod(x, 3, FIELD_PRIME), FIELD_PRIME) -// -// %} -func is_quad_residue(ids IdsManager, vm *VirtualMachine) error { - x, err := ids.GetFelt("x", vm) - if err != nil { - return err - } - if x.IsZero() || x.IsOne() { - ids.Insert("y", NewMaybeRelocatableFelt(x), vm) - - } else if x.Pow(SignedFeltMaxValue()) == FeltOne() { - num := x.Sqrt() - ids.Insert("y", NewMaybeRelocatableFelt(num), vm) - - } else { - num := (x.Div(lambdaworks.FeltFromUint64(3))).Sqrt() - ids.Insert("y", NewMaybeRelocatableFelt(num), vm) - } - return nil -} - func assert_not_equal(ids IdsManager, vm *VirtualMachine) error { // Extract Ids Variables a, err := ids.Get("a", vm) @@ -180,12 +149,15 @@ func unsignedDivRem(ids IdsManager, vm *VirtualMachine) error { return err } - limit := new(big.Int).Div(lambdaworks.Prime(), rcBound.ToBigInt()) + if rcBound.Cmp(lambdaworks.FeltZero()) == 0 { + return errors.New("range check bound cannot be zero") + } + primeBoundDivision := new(big.Int).Div(lambdaworks.Prime(), rcBound.ToBigInt()) // Check if `div` is greater than `limit` - cmp := div.ToBigInt().Cmp(limit) == 1 + divGreater := div.ToBigInt().Cmp(primeBoundDivision) == 1 - if div.IsZero() || cmp { + if div.IsZero() || divGreater { return errors.Errorf("Div out of range: 0 < %d <= %d", div, rcBound) } @@ -220,50 +192,51 @@ Implements hint: %} */ -// func signedDivRem(ids IdsManager, vm *VirtualMachine) error { -// div, err := ids.GetFelt("div", vm) -// if err != nil { -// return err -// } -// value, err := ids.GetFelt("value", vm) -// if err != nil { -// return err -// } -// bound, err := ids.GetFelt("bound", vm) -// if err != nil { -// return err -// } - -// // It is safe to cast INNER_RC_BOUND into int64 since the constant is set to 65536 -// rcBound, err := vm.GetRangeCheckBound() -// if err != nil { -// return err -// } - -// if rcBound.Cmp(lambdaworks.FeltZero()) == 0 { -// return errors.New("range check bound cannot be zero") -// } -// primeBoundDivision := new(big.Int).Div(lambdaworks.Prime(), rcBound.ToBigInt()) - -// // Check if `div` is greater than `limit` and make assertions -// divGreater := div.ToBigInt().Cmp(primeBoundDivision) == 1 -// if div.IsZero() || divGreater { -// return errors.Errorf("div=%d is out of the valid range", div) -// } - -// if bound.Cmp(rcBound.Shr(1)) == 1 { -// return errors.Errorf("bound=%d is out of the valid range") -// } - -// sgnValue := value.ToSigned() -// sgnBound := bound.ToBigInt() -// intDiv := div.ToBigInt() - -// q := new(big.Int).Div(sgnValue, intDiv) -// r := new(big.Int).Rem(sgnValue, intDiv) - -// return nil -// } +func signedDivRem(ids IdsManager, vm *VirtualMachine) error { + div, err := ids.GetFelt("div", vm) + if err != nil { + return err + } + value, err := ids.GetFelt("value", vm) + if err != nil { + return err + } + bound, err := ids.GetFelt("bound", vm) + if err != nil { + return err + } + + rcBound, err := vm.GetRangeCheckBound() + if err != nil { + return err + } + + if rcBound.Cmp(lambdaworks.FeltZero()) == 0 { + return errors.New("range check bound cannot be zero") + } + primeBoundDivision := new(big.Int).Div(lambdaworks.Prime(), rcBound.ToBigInt()) + + // Check if `div` is greater than `limit` and make assertions + divGreater := div.ToBigInt().Cmp(primeBoundDivision) == 1 + if div.IsZero() || divGreater { + return errors.Errorf("div=%d is out of the valid range", div) + } + + if bound.Cmp(rcBound.Shr(1)) == 1 { + return errors.Errorf("bound=%d is out of the valid range") + } + + sgnValue := value.ToSigned() + sgnBound := bound.ToBigInt() + intDiv := div.ToBigInt() + + q := new(big.Int).Div(sgnValue, intDiv) + r := new(big.Int).Rem(sgnValue, intDiv) + + fmt.Println(sgnBound, q, r) + + return nil +} // pub fn signed_div_rem( // vm: &mut VirtualMachine, diff --git a/pkg/vm/cairo_run/cairo_run_test.go b/pkg/vm/cairo_run/cairo_run_test.go index 3182e252..3da1ce5b 100644 --- a/pkg/vm/cairo_run/cairo_run_test.go +++ b/pkg/vm/cairo_run/cairo_run_test.go @@ -202,3 +202,11 @@ func TestSqrtHint(t *testing.T) { t.Errorf("Program execution failed with error: %s", err) } } + +func TestUnsignedDivRemHint(t *testing.T) { + cairoRunConfig := cairo_run.CairoRunConfig{DisableTracePadding: false, Layout: "all_cairo", ProofMode: false} + _, err := cairo_run.CairoRun("../../../cairo_programs/sqrt.json", cairoRunConfig) + if err != nil { + t.Errorf("Program execution failed with error: %s", err) + } +} From be0f705ca44d1a3eb91bd294862a8184072a7216 Mon Sep 17 00:00:00 2001 From: Mariano Nicolini Date: Tue, 19 Sep 2023 12:13:52 -0300 Subject: [PATCH 08/55] Add FeltFromBigInt function --- pkg/hints/math_hints.go | 34 ++++++++++++++++++++++++++++- pkg/lambdaworks/lambdaworks.go | 6 +++++ pkg/lambdaworks/lambdaworks_test.go | 11 ++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/pkg/hints/math_hints.go b/pkg/hints/math_hints.go index 0843173e..5283e46a 100644 --- a/pkg/hints/math_hints.go +++ b/pkg/hints/math_hints.go @@ -69,6 +69,38 @@ func assert_not_zero(ids IdsManager, vm *VirtualMachine) error { return nil } +// Implements hint:from starkware.cairo.common.math.cairo +// +// %{ +// from starkware.crypto.signature.signature import FIELD_PRIME +// from starkware.python.math_utils import div_mod, is_quad_residue, sqrt +// +// x = ids.x +// if is_quad_residue(x, FIELD_PRIME): +// ids.y = sqrt(x, FIELD_PRIME) +// else: +// ids.y = sqrt(div_mod(x, 3, FIELD_PRIME), FIELD_PRIME) +// +// %} +func is_quad_residue(ids IdsManager, vm *VirtualMachine) error { + x, err := ids.GetFelt("x", vm) + if err != nil { + return err + } + if x.IsZero() || x.IsOne() { + ids.Insert("y", NewMaybeRelocatableFelt(x), vm) + + } else if x.Pow(SignedFeltMaxValue()) == FeltOne() { + num := x.Sqrt() + ids.Insert("y", NewMaybeRelocatableFelt(num), vm) + + } else { + num := (x.Div(lambdaworks.FeltFromUint64(3))).Sqrt() + ids.Insert("y", NewMaybeRelocatableFelt(num), vm) + } + return nil +} + func assert_not_equal(ids IdsManager, vm *VirtualMachine) error { // Extract Ids Variables a, err := ids.Get("a", vm) @@ -223,7 +255,7 @@ func signedDivRem(ids IdsManager, vm *VirtualMachine) error { } if bound.Cmp(rcBound.Shr(1)) == 1 { - return errors.Errorf("bound=%d is out of the valid range") + return errors.Errorf("bound=%d is out of the valid range", bound) } sgnValue := value.ToSigned() diff --git a/pkg/lambdaworks/lambdaworks.go b/pkg/lambdaworks/lambdaworks.go index a9cde7c1..d77f3a85 100644 --- a/pkg/lambdaworks/lambdaworks.go +++ b/pkg/lambdaworks/lambdaworks.go @@ -58,6 +58,12 @@ func FeltFromUint64(value uint64) Felt { return fromC(result) } +func FeltFromBigInt(value big.Int) Felt { + buff := make([]byte, 32) + bytes := value.FillBytes(buff) + return FeltFromBeBytes((*[32]byte)(bytes)) +} + func FeltFromHex(value string) Felt { cs := C.CString(value) defer C.free(unsafe.Pointer(cs)) diff --git a/pkg/lambdaworks/lambdaworks_test.go b/pkg/lambdaworks/lambdaworks_test.go index 8116378d..7418aea7 100644 --- a/pkg/lambdaworks/lambdaworks_test.go +++ b/pkg/lambdaworks/lambdaworks_test.go @@ -180,6 +180,17 @@ func TestFromBeBytes(t *testing.T) { } } +func TestFromBigInt(t *testing.T) { + big := big.NewInt(15) + + expectedFelt := lambdaworks.FeltFromDecString("15") + resFelt := lambdaworks.FeltFromBigInt(*big) + + if resFelt.Cmp(expectedFelt) != 0 { + t.Errorf("TestFromBigInt failed. Expected 15, got: %v", resFelt) + } +} + func TestFeltSub(t *testing.T) { f_one := lambdaworks.FeltOne() expected := lambdaworks.FeltZero() From c17e91e41d91d08ec019258d53cc549b719eb8ee Mon Sep 17 00:00:00 2001 From: Mariano Nicolini Date: Tue, 19 Sep 2023 12:16:34 -0300 Subject: [PATCH 09/55] Add unsigned div rem integration test --- pkg/vm/cairo_run/cairo_run_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/vm/cairo_run/cairo_run_test.go b/pkg/vm/cairo_run/cairo_run_test.go index 3da1ce5b..30b63411 100644 --- a/pkg/vm/cairo_run/cairo_run_test.go +++ b/pkg/vm/cairo_run/cairo_run_test.go @@ -205,7 +205,7 @@ func TestSqrtHint(t *testing.T) { func TestUnsignedDivRemHint(t *testing.T) { cairoRunConfig := cairo_run.CairoRunConfig{DisableTracePadding: false, Layout: "all_cairo", ProofMode: false} - _, err := cairo_run.CairoRun("../../../cairo_programs/sqrt.json", cairoRunConfig) + _, err := cairo_run.CairoRun("../../../cairo_programs/unsigned_div_rem.json", cairoRunConfig) if err != nil { t.Errorf("Program execution failed with error: %s", err) } From d7a7693205e931310c4c573abc93e5820914fbf7 Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 19 Sep 2023 12:37:01 -0300 Subject: [PATCH 10/55] Implement UnsafeKeccak --- pkg/hints/keccak_hint_codes.go | 3 ++ pkg/hints/keccak_hints.go | 69 ++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 pkg/hints/keccak_hint_codes.go create mode 100644 pkg/hints/keccak_hints.go diff --git a/pkg/hints/keccak_hint_codes.go b/pkg/hints/keccak_hint_codes.go new file mode 100644 index 00000000..f05875b8 --- /dev/null +++ b/pkg/hints/keccak_hint_codes.go @@ -0,0 +1,3 @@ +package hints + +const UNSAFE_KECCAK = "from eth_hash.auto import keccak\n\ndata, length = ids.data, ids.length\n\nif '__keccak_max_size' in globals():\n assert length <= __keccak_max_size, \\\n f'unsafe_keccak() can only be used with length<={__keccak_max_size}. ' \\\n f'Got: length={length}.'\n\nkeccak_input = bytearray()\nfor word_i, byte_i in enumerate(range(0, length, 16)):\n word = memory[data + word_i]\n n_bytes = min(16, length - byte_i)\n assert 0 <= word < 2 ** (8 * n_bytes)\n keccak_input += word.to_bytes(n_bytes, 'big')\n\nhashed = keccak(keccak_input)\nids.high = int.from_bytes(hashed[:16], 'big')\nids.low = int.from_bytes(hashed[16:32], 'big')" diff --git a/pkg/hints/keccak_hints.go b/pkg/hints/keccak_hints.go new file mode 100644 index 00000000..b9bb4527 --- /dev/null +++ b/pkg/hints/keccak_hints.go @@ -0,0 +1,69 @@ +package hints + +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/types" + . "github.com/lambdaclass/cairo-vm.go/pkg/vm" + . "github.com/lambdaclass/cairo-vm.go/pkg/vm/memory" + "github.com/pkg/errors" + "golang.org/x/crypto/sha3" +) + +func unsafeKeccak(ids IdsManager, vm *VirtualMachine, scopes ExecutionScopes) error { + // Fetch ids variable + lengthFelt, err := ids.GetFelt("length", vm) + if err != nil { + return err + } + length, err := lengthFelt.ToU64() + if err != nil { + return err + } + data, err := ids.GetRelocatable("data", vm) + if err != nil { + return err + } + // Check __keccak_max_size if available + keccakMaxSizeAny, err := scopes.Get("__keccak_max_size") + if err == nil { + keccakMaxSize, ok := keccakMaxSizeAny.(uint64) + if ok { + if length > keccakMaxSize { + return errors.Errorf("unsafe_keccak() can only be used with length<=%d. Got: length=%d", keccakMaxSize, length) + } + } + } + keccakInput := make([]byte, 0) + for byteIdx, wordIdx := 0, 0; byteIdx < int(length); byteIdx, wordIdx = byteIdx+16, wordIdx+1 { + wordAddr := data.AddUint(uint(wordIdx)) + word, err := vm.Segments.Memory.GetFelt(wordAddr) + if err != nil { + return err + } + nBytes := int(length) - byteIdx + if nBytes > 16 { + nBytes = 16 + } + + if int(word.Bits()) > 8*nBytes { + return errors.Errorf("Invalid word size: %s", word.ToHexString()) + } + + start := 32 - nBytes + keccakInput = append(keccakInput, word.ToBeBytes()[start:]...) + + } + + hasher := sha3.New256() + resBytes := hasher.Sum(keccakInput) + + high := FeltFromBeBytes((*[32]byte)(resBytes[:16])) + low := FeltFromBeBytes((*[32]byte)(resBytes[16:32])) + + err = ids.Insert("high", NewMaybeRelocatableFelt(high), vm) + if err != nil { + return err + } + return ids.Insert("low", NewMaybeRelocatableFelt(low), vm) +} From 4ee4a97fddbf059cac31796ec218eeb9769b63a2 Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 19 Sep 2023 12:37:48 -0300 Subject: [PATCH 11/55] Update dependencies --- go.sum | 2 ++ 1 file changed, 2 insertions(+) diff --git a/go.sum b/go.sum index 0a509fa9..6fea8f82 100644 --- a/go.sum +++ b/go.sum @@ -10,5 +10,7 @@ github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= From 73acd4746f2eda0fe5b82fb94ac351d7fba719d9 Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 19 Sep 2023 13:20:04 -0300 Subject: [PATCH 12/55] Add unit test --- pkg/hints/hint_processor.go | 2 ++ pkg/hints/keccak_hints.go | 7 +++-- pkg/hints/keccak_hints_test.go | 57 ++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 pkg/hints/keccak_hints_test.go diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index cf8181a6..cdc33536 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -69,6 +69,8 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, return memcpy_enter_scope(data.Ids, vm, execScopes) case VM_ENTER_SCOPE: return vm_enter_scope(execScopes) + case UNSAFE_KECCAK: + return unsafeKeccak(data.Ids, vm, *execScopes) default: return errors.Errorf("Unknown Hint: %s", data.Code) } diff --git a/pkg/hints/keccak_hints.go b/pkg/hints/keccak_hints.go index b9bb4527..22e32edd 100644 --- a/pkg/hints/keccak_hints.go +++ b/pkg/hints/keccak_hints.go @@ -58,8 +58,11 @@ func unsafeKeccak(ids IdsManager, vm *VirtualMachine, scopes ExecutionScopes) er hasher := sha3.New256() resBytes := hasher.Sum(keccakInput) - high := FeltFromBeBytes((*[32]byte)(resBytes[:16])) - low := FeltFromBeBytes((*[32]byte)(resBytes[16:32])) + highBytes := append([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, resBytes[:16]...) + lowBytes := append([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, resBytes[16:32]...) + + high := FeltFromBeBytes((*[32]byte)(highBytes)) + low := FeltFromBeBytes((*[32]byte)(lowBytes)) err = ids.Insert("high", NewMaybeRelocatableFelt(high), vm) if err != nil { diff --git a/pkg/hints/keccak_hints_test.go b/pkg/hints/keccak_hints_test.go new file mode 100644 index 00000000..1ebd4ae1 --- /dev/null +++ b/pkg/hints/keccak_hints_test.go @@ -0,0 +1,57 @@ +package hints_test + +import ( + "testing" + + . "github.com/lambdaclass/cairo-vm.go/pkg/hints" + . "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 TestUnsafeKeccakOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + data_ptr := vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "length": {NewMaybeRelocatableFelt(FeltFromUint64(3))}, + "data": {NewMaybeRelocatableRelocatable(data_ptr)}, + "high": {nil}, + "low": {nil}, + }, + vm, + ) + // Insert data into memory + data := []MaybeRelocatable{ + *NewMaybeRelocatableFelt(FeltOne()), + *NewMaybeRelocatableFelt(FeltOne()), + *NewMaybeRelocatableFelt(FeltOne()), + } + vm.Segments.LoadData(data_ptr, &data) + // Add __keccak_max_size + scopes := NewExecutionScopes() + scopes.AssignOrUpdateVariable("__keccak_max_size", uint16(500)) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: UNSAFE_KECCAK, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("UNSAFE_KECCAK hint test failed with error %s", err) + } + // Check ids values + high, err := idsManager.GetFelt("high", vm) + expectedHigh := FeltFromDecString("199195598804046335037364682505062700553") + if err != nil || high != expectedHigh { + t.Errorf("Wrong/No ids.high.\n Expected %s, got %s.", expectedHigh.ToHexString(), high.ToHexString()) + } + low, err := idsManager.GetFelt("low", vm) + expectedLow := FeltFromDecString("259413678945892999811634722593932702747") + if err != nil || low != expectedLow { + t.Errorf("Wrong/No ids.low\n Expected %s, got %s.", expectedLow.ToHexString(), low.ToHexString()) + } +} From 967d07266fc6708b113e01f740d2afb31ded7371 Mon Sep 17 00:00:00 2001 From: Mariano Nicolini Date: Tue, 19 Sep 2023 13:43:53 -0300 Subject: [PATCH 13/55] Fix division bug and make integration test pass --- cairo_programs/signed_div_rem.cairo | 1 + pkg/hints/hint_processor.go | 2 + pkg/hints/math_hints.go | 76 +++++------------------------ pkg/lambdaworks/lambdaworks.go | 2 +- pkg/lambdaworks/lambdaworks_test.go | 2 +- pkg/vm/cairo_run/cairo_run_test.go | 8 +++ 6 files changed, 25 insertions(+), 66 deletions(-) diff --git a/cairo_programs/signed_div_rem.cairo b/cairo_programs/signed_div_rem.cairo index 70586943..18c255cc 100644 --- a/cairo_programs/signed_div_rem.cairo +++ b/cairo_programs/signed_div_rem.cairo @@ -5,6 +5,7 @@ from starkware.cairo.common.serialize import serialize_word func signed_div_rem_man{range_check_ptr}(value, div, bound) -> (q: felt, r: felt) { let r = [range_check_ptr]; let biased_q = [range_check_ptr + 1]; // == q + bound. + let range_check_ptr = range_check_ptr + 2; %{ from starkware.cairo.common.math_utils import as_int, assert_integer diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index 76df26a9..32e09d15 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -71,6 +71,8 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, return vm_enter_scope(execScopes) case UNSIGNED_DIV_REM: return unsignedDivRem(data.Ids, vm) + case SIGNED_DIV_REM: + return signedDivRem(data.Ids, vm) default: return errors.Errorf("Unknown Hint: %s", data.Code) } diff --git a/pkg/hints/math_hints.go b/pkg/hints/math_hints.go index 5283e46a..5b9b2073 100644 --- a/pkg/hints/math_hints.go +++ b/pkg/hints/math_hints.go @@ -1,7 +1,6 @@ package hints import ( - "fmt" "math/big" "github.com/lambdaclass/cairo-vm.go/pkg/builtins" @@ -253,77 +252,26 @@ func signedDivRem(ids IdsManager, vm *VirtualMachine) error { if div.IsZero() || divGreater { return errors.Errorf("div=%d is out of the valid range", div) } - if bound.Cmp(rcBound.Shr(1)) == 1 { return errors.Errorf("bound=%d is out of the valid range", bound) } sgnValue := value.ToSigned() - sgnBound := bound.ToBigInt() + intBound := bound.ToBigInt() intDiv := div.ToBigInt() - q := new(big.Int).Div(sgnValue, intDiv) - r := new(big.Int).Rem(sgnValue, intDiv) + q, r := new(big.Int).DivMod(sgnValue, intDiv, new(big.Int)) + + if new(big.Int).Abs(intBound).Cmp(new(big.Int).Abs(q)) == -1 { + return errors.Errorf("%d / %d = %d is out of the range [-%d, %d]", sgnValue, div, q, bound, bound) + } - fmt.Println(sgnBound, q, r) + biasedQ := new(big.Int).Add(q, intBound) + biasedQFelt := lambdaworks.FeltFromBigInt(biasedQ) + rFelt := lambdaworks.FeltFromBigInt(r) + + ids.Insert("r", NewMaybeRelocatableFelt(rFelt), vm) + ids.Insert("biased_q", NewMaybeRelocatableFelt(biasedQFelt), vm) return nil } - -// pub fn signed_div_rem( -// vm: &mut VirtualMachine, -// ids_data: &HashMap, -// ap_tracking: &ApTracking, -// ) -> Result<(), HintError> { -// let div = get_integer_from_var_name("div", vm, ids_data, ap_tracking)?; -// let value = get_integer_from_var_name("value", vm, ids_data, ap_tracking)?; -// let value = value.as_ref(); -// let bound = get_integer_from_var_name("bound", vm, ids_data, ap_tracking)?; -// let builtin = vm.get_range_check_builtin()?; - -// match &builtin._bound { -// Some(builtin_bound) -// if div.is_zero() || div.as_ref() > &div_prime_by_bound(builtin_bound.clone())? => -// { -// return Err(HintError::OutOfValidRange(Box::new(( -// div.into_owned(), -// builtin_bound.clone(), -// )))); -// } -// Some(builtin_bound) if bound.as_ref() > &(builtin_bound >> 1_u32) => { -// return Err(HintError::OutOfValidRange(Box::new(( -// bound.into_owned(), -// builtin_bound >> 1_u32, -// )))); -// } -// None if div.is_zero() => { -// return Err(HintError::OutOfValidRange(Box::new(( -// div.into_owned(), -// Felt252::zero() - Felt252::one(), -// )))); -// } -// _ => {} -// } - -// let int_value = value.to_signed_felt(); -// let int_div = div.to_bigint(); -// let int_bound = bound.to_bigint(); -// let (q, r) = int_value.div_mod_floor(&int_div); - -// if int_bound.abs() < q.abs() { -// return Err(HintError::OutOfValidRange(Box::new(( -// Felt252::new(q), -// bound.into_owned(), -// )))); -// } - -// let biased_q = q + int_bound; -// insert_value_from_var_name("r", Felt252::new(r), vm, ids_data, ap_tracking)?; -// insert_value_from_var_name( -// "biased_q", -// Felt252::new(biased_q), -// vm, -// ids_data, -// ap_tracking, -// ) -// } diff --git a/pkg/lambdaworks/lambdaworks.go b/pkg/lambdaworks/lambdaworks.go index d77f3a85..56c3a649 100644 --- a/pkg/lambdaworks/lambdaworks.go +++ b/pkg/lambdaworks/lambdaworks.go @@ -58,7 +58,7 @@ func FeltFromUint64(value uint64) Felt { return fromC(result) } -func FeltFromBigInt(value big.Int) Felt { +func FeltFromBigInt(value *big.Int) Felt { buff := make([]byte, 32) bytes := value.FillBytes(buff) return FeltFromBeBytes((*[32]byte)(bytes)) diff --git a/pkg/lambdaworks/lambdaworks_test.go b/pkg/lambdaworks/lambdaworks_test.go index 7418aea7..28209dd6 100644 --- a/pkg/lambdaworks/lambdaworks_test.go +++ b/pkg/lambdaworks/lambdaworks_test.go @@ -184,7 +184,7 @@ func TestFromBigInt(t *testing.T) { big := big.NewInt(15) expectedFelt := lambdaworks.FeltFromDecString("15") - resFelt := lambdaworks.FeltFromBigInt(*big) + resFelt := lambdaworks.FeltFromBigInt(big) if resFelt.Cmp(expectedFelt) != 0 { t.Errorf("TestFromBigInt failed. Expected 15, got: %v", resFelt) diff --git a/pkg/vm/cairo_run/cairo_run_test.go b/pkg/vm/cairo_run/cairo_run_test.go index 30b63411..09530e8d 100644 --- a/pkg/vm/cairo_run/cairo_run_test.go +++ b/pkg/vm/cairo_run/cairo_run_test.go @@ -210,3 +210,11 @@ func TestUnsignedDivRemHint(t *testing.T) { t.Errorf("Program execution failed with error: %s", err) } } + +func TestSignedDivRemHint(t *testing.T) { + cairoRunConfig := cairo_run.CairoRunConfig{DisableTracePadding: false, Layout: "all_cairo", ProofMode: false} + _, err := cairo_run.CairoRun("../../../cairo_programs/signed_div_rem.json", cairoRunConfig) + if err != nil { + t.Errorf("Program execution failed with error: %s", err) + } +} From 79ad5d4837bd2893f2cec8aaf1db762492601bd1 Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 19 Sep 2023 15:55:36 -0300 Subject: [PATCH 14/55] Fix hash --- go.mod | 5 +++-- go.sum | 9 +++++++-- pkg/hints/keccak_hints.go | 7 ++++--- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 463c710c..919dbd1b 100644 --- a/go.mod +++ b/go.mod @@ -4,15 +4,16 @@ go 1.20 require ( github.com/pkg/errors v0.9.1 - golang.org/x/crypto v0.12.0 + golang.org/x/crypto v0.13.0 ) require ( github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/ebfe/keccak v0.0.0-20150115210727-5cc570678d1b // indirect github.com/ethereum/go-ethereum v1.12.1 // indirect github.com/miguelmota/go-solidity-sha3 v0.1.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/urfave/cli/v2 v2.25.7 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect - golang.org/x/sys v0.11.0 // indirect + golang.org/x/sys v0.12.0 // indirect ) diff --git a/go.sum b/go.sum index 6fea8f82..03f416b0 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/ebfe/keccak v0.0.0-20150115210727-5cc570678d1b h1:BMyjwV6Fal/Ffphi4dJfulSxMeDl0xFS2vs5QLr6rsI= +github.com/ebfe/keccak v0.0.0-20150115210727-5cc570678d1b/go.mod h1:fnviDXB7GJWiSUI9thIXmk9QKM8Rhj1JV/LcMRzkiVA= github.com/ethereum/go-ethereum v1.12.1/go.mod h1:zKetLweqBR8ZS+1O9iJWI8DvmmD2NzD19apjEWDCsnw= github.com/miguelmota/go-solidity-sha3 v0.1.1/go.mod h1:sax1FvQF+f71j8W1uUHMZn8NxKyl5rYLks2nqj8RFEw= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -10,7 +12,10 @@ github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= -golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/pkg/hints/keccak_hints.go b/pkg/hints/keccak_hints.go index 22e32edd..615e5d06 100644 --- a/pkg/hints/keccak_hints.go +++ b/pkg/hints/keccak_hints.go @@ -1,13 +1,13 @@ package hints import ( + "github.com/ebfe/keccak" . "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" - "golang.org/x/crypto/sha3" ) func unsafeKeccak(ids IdsManager, vm *VirtualMachine, scopes ExecutionScopes) error { @@ -55,8 +55,9 @@ func unsafeKeccak(ids IdsManager, vm *VirtualMachine, scopes ExecutionScopes) er } - hasher := sha3.New256() - resBytes := hasher.Sum(keccakInput) + hasher := keccak.New256() + hasher.Write(keccakInput) + resBytes := hasher.Sum(nil) highBytes := append([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, resBytes[:16]...) lowBytes := append([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, resBytes[16:32]...) From 8dcf49c567fecbc94414712cadf7d67d1079f7f4 Mon Sep 17 00:00:00 2001 From: Mariano Nicolini Date: Tue, 19 Sep 2023 16:32:26 -0300 Subject: [PATCH 15/55] Save work in progress --- pkg/hints/hint_utils/testing_utils.go | 7 ++++ pkg/hints/math_hints.go | 11 +++++-- pkg/hints/math_hints_test.go | 46 +++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 2 deletions(-) diff --git a/pkg/hints/hint_utils/testing_utils.go b/pkg/hints/hint_utils/testing_utils.go index 8c7f1f5c..91904483 100644 --- a/pkg/hints/hint_utils/testing_utils.go +++ b/pkg/hints/hint_utils/testing_utils.go @@ -32,7 +32,14 @@ func SetupIdsForTest(ids map[string][]*memory.MaybeRelocatable, vm *VirtualMachi }, } // Update current_offset + // var offset_update int + // if len(elems) != 0 { + // offset_update = len(elems) + // } else { + // offset_update = 1 + // } current_offset += len(elems) + // current_offset += offset_update // Insert ids variables (if present) for n, elem := range elems { if elem != nil { diff --git a/pkg/hints/math_hints.go b/pkg/hints/math_hints.go index 5b9b2073..71b30fa6 100644 --- a/pkg/hints/math_hints.go +++ b/pkg/hints/math_hints.go @@ -193,8 +193,15 @@ func unsignedDivRem(ids IdsManager, vm *VirtualMachine) error { } q, r := value.DivRem(div) - ids.Insert("q", NewMaybeRelocatableFelt(q), vm) - ids.Insert("r", NewMaybeRelocatableFelt(r), vm) + + err = ids.Insert("q", NewMaybeRelocatableFelt(q), vm) + if err != nil { + return err + } + err = ids.Insert("r", NewMaybeRelocatableFelt(r), vm) + if err != nil { + return err + } return nil } diff --git a/pkg/hints/math_hints_test.go b/pkg/hints/math_hints_test.go index 54672499..0fe0e16e 100644 --- a/pkg/hints/math_hints_test.go +++ b/pkg/hints/math_hints_test.go @@ -3,6 +3,7 @@ package hints_test import ( "testing" + "github.com/lambdaclass/cairo-vm.go/pkg/builtins" . "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" @@ -317,3 +318,48 @@ func TestSqrtOk(t *testing.T) { t.Errorf("Expected sqrt(9) == 3. Got: %v", root) } } + +func TestUnsignedDivRemHintSuccess(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "div": {NewMaybeRelocatableFelt(FeltFromDecString("3"))}, + "value": {NewMaybeRelocatableFelt(FeltFromDecString("10"))}, + "r": nil, + "q": nil, + }, + vm, + ) + rcBuiltin := builtins.DefaultRangeCheckBuiltinRunner() + vm.BuiltinRunners = []builtins.BuiltinRunner{rcBuiltin} + + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: UNSIGNED_DIV_REM, + }) + + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) + if err != nil { + t.Errorf("UNSIGNED_DIV_REM hint failed with error: %s", err) + } + + q, err := idsManager.GetFelt("q", vm) + if err != nil { + t.Errorf("failed to get `q`: %s", err) + } + + r, err := idsManager.GetFelt("r", vm) + if err != nil { + t.Errorf("failed to get `r`: %s", err) + } + + if q != FeltFromUint64(3) { + t.Errorf("Expected q=3, got: %v", q) + } + + if r != FeltFromUint64(1) { + t.Errorf("Expected r=1, got: %v", r) + } +} From e969f48fc98b27b9374705d2e10b15e2d5054d8b Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 19 Sep 2023 16:51:10 -0300 Subject: [PATCH 16/55] Add unit tests --- pkg/hints/keccak_hints_test.go | 69 +++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/pkg/hints/keccak_hints_test.go b/pkg/hints/keccak_hints_test.go index 1ebd4ae1..18c02b8a 100644 --- a/pkg/hints/keccak_hints_test.go +++ b/pkg/hints/keccak_hints_test.go @@ -33,7 +33,7 @@ func TestUnsafeKeccakOk(t *testing.T) { vm.Segments.LoadData(data_ptr, &data) // Add __keccak_max_size scopes := NewExecutionScopes() - scopes.AssignOrUpdateVariable("__keccak_max_size", uint16(500)) + scopes.AssignOrUpdateVariable("__keccak_max_size", uint64(500)) hintProcessor := CairoVmHintProcessor{} hintData := any(HintData{ Ids: idsManager, @@ -55,3 +55,70 @@ func TestUnsafeKeccakOk(t *testing.T) { t.Errorf("Wrong/No ids.low\n Expected %s, got %s.", expectedLow.ToHexString(), low.ToHexString()) } } + +func TestUnsafeKeccakMaxSizeExceeded(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + data_ptr := vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "length": {NewMaybeRelocatableFelt(FeltFromUint64(3))}, + "data": {NewMaybeRelocatableRelocatable(data_ptr)}, + "high": {nil}, + "low": {nil}, + }, + vm, + ) + // Insert data into memory + data := []MaybeRelocatable{ + *NewMaybeRelocatableFelt(FeltOne()), + *NewMaybeRelocatableFelt(FeltOne()), + *NewMaybeRelocatableFelt(FeltOne()), + } + vm.Segments.LoadData(data_ptr, &data) + // Add __keccak_max_size + scopes := NewExecutionScopes() + scopes.AssignOrUpdateVariable("__keccak_max_size", uint64(2)) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: UNSAFE_KECCAK, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Errorf("UNSAFE_KECCAK hint test should have failed") + } +} + +func TestUnsafeKeccakInvalidWordSize(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + data_ptr := vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "length": {NewMaybeRelocatableFelt(FeltFromUint64(3))}, + "data": {NewMaybeRelocatableRelocatable(data_ptr)}, + "high": {nil}, + "low": {nil}, + }, + vm, + ) + // Insert data into memory + data := []MaybeRelocatable{ + *NewMaybeRelocatableFelt(FeltFromDecString("-1")), + *NewMaybeRelocatableFelt(FeltOne()), + *NewMaybeRelocatableFelt(FeltOne()), + } + vm.Segments.LoadData(data_ptr, &data) + // Add __keccak_max_size + scopes := NewExecutionScopes() + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: UNSAFE_KECCAK, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Errorf("UNSAFE_KECCAK hint test should have failed") + } +} From 7f5dbfbb557894f66aee8346d258f9244bc9eaef Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 19 Sep 2023 16:57:55 -0300 Subject: [PATCH 17/55] Add integration test --- cairo_programs/unsafe_keccak.cairo | 28 ++++++++++++++++++++++++++++ pkg/vm/cairo_run/cairo_run_test.go | 8 ++++++++ 2 files changed, 36 insertions(+) create mode 100644 cairo_programs/unsafe_keccak.cairo diff --git a/cairo_programs/unsafe_keccak.cairo b/cairo_programs/unsafe_keccak.cairo new file mode 100644 index 00000000..9f80a750 --- /dev/null +++ b/cairo_programs/unsafe_keccak.cairo @@ -0,0 +1,28 @@ +%builtins output + +from starkware.cairo.common.alloc import alloc +from starkware.cairo.common.serialize import serialize_word +from starkware.cairo.common.keccak import unsafe_keccak + +func main{output_ptr: felt*}() { + alloc_locals; + + let (data: felt*) = alloc(); + + assert data[0] = 500; + assert data[1] = 2; + assert data[2] = 3; + assert data[3] = 6; + assert data[4] = 1; + assert data[5] = 4444; + + let (low: felt, high: felt) = unsafe_keccak(data, 6); + + assert low = 182565855334575837944615807286777833262; + assert high = 90044356407795786957420814893241941221; + + serialize_word(low); + serialize_word(high); + + return (); +} diff --git a/pkg/vm/cairo_run/cairo_run_test.go b/pkg/vm/cairo_run/cairo_run_test.go index 3182e252..c6cf2dc0 100644 --- a/pkg/vm/cairo_run/cairo_run_test.go +++ b/pkg/vm/cairo_run/cairo_run_test.go @@ -202,3 +202,11 @@ func TestSqrtHint(t *testing.T) { t.Errorf("Program execution failed with error: %s", err) } } + +func TestUnsafeKeccak(t *testing.T) { + cairoRunConfig := cairo_run.CairoRunConfig{DisableTracePadding: false, Layout: "all_cairo", ProofMode: false} + _, err := cairo_run.CairoRun("../../../cairo_programs/unsafe_keccak.json", cairoRunConfig) + if err != nil { + t.Errorf("Program execution failed with error: %s", err) + } +} From e568e66a87b2a7748673fd7a8daaeca961dc86b7 Mon Sep 17 00:00:00 2001 From: Mariano Nicolini Date: Tue, 19 Sep 2023 17:05:22 -0300 Subject: [PATCH 18/55] Finished unit test for divrem hints --- pkg/hints/math_hints.go | 2 +- pkg/hints/math_hints_test.go | 196 ++++++++++++++++++++++++++++++++++- 2 files changed, 192 insertions(+), 6 deletions(-) diff --git a/pkg/hints/math_hints.go b/pkg/hints/math_hints.go index 71b30fa6..b26d6c5a 100644 --- a/pkg/hints/math_hints.go +++ b/pkg/hints/math_hints.go @@ -189,7 +189,7 @@ func unsignedDivRem(ids IdsManager, vm *VirtualMachine) error { divGreater := div.ToBigInt().Cmp(primeBoundDivision) == 1 if div.IsZero() || divGreater { - return errors.Errorf("Div out of range: 0 < %d <= %d", div, rcBound) + return errors.Errorf("Div out of range: 0 < %d <= %d", div.ToBigInt(), rcBound.ToBigInt()) } q, r := value.DivRem(div) diff --git a/pkg/hints/math_hints_test.go b/pkg/hints/math_hints_test.go index 0fe0e16e..9d1f1914 100644 --- a/pkg/hints/math_hints_test.go +++ b/pkg/hints/math_hints_test.go @@ -324,10 +324,10 @@ func TestUnsignedDivRemHintSuccess(t *testing.T) { vm.Segments.AddSegment() idsManager := SetupIdsForTest( map[string][]*MaybeRelocatable{ - "div": {NewMaybeRelocatableFelt(FeltFromDecString("3"))}, - "value": {NewMaybeRelocatableFelt(FeltFromDecString("10"))}, - "r": nil, - "q": nil, + "div": {NewMaybeRelocatableFelt(FeltFromDecString("7"))}, + "value": {NewMaybeRelocatableFelt(FeltFromDecString("15"))}, + "r": {nil}, + "q": {nil}, }, vm, ) @@ -355,7 +355,7 @@ func TestUnsignedDivRemHintSuccess(t *testing.T) { t.Errorf("failed to get `r`: %s", err) } - if q != FeltFromUint64(3) { + if q != FeltFromUint64(2) { t.Errorf("Expected q=3, got: %v", q) } @@ -363,3 +363,189 @@ func TestUnsignedDivRemHintSuccess(t *testing.T) { t.Errorf("Expected r=1, got: %v", r) } } + +func TestUnsignedDivRemHintDivZeroError(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + // This is the condition that should make the hint execution error. + "div": {NewMaybeRelocatableFelt(FeltFromDecString("0"))}, + "value": {NewMaybeRelocatableFelt(FeltFromDecString("15"))}, + "r": {nil}, + "q": {nil}, + }, + vm, + ) + rcBuiltin := builtins.DefaultRangeCheckBuiltinRunner() + vm.BuiltinRunners = []builtins.BuiltinRunner{rcBuiltin} + + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: UNSIGNED_DIV_REM, + }) + + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) + if err == nil { + t.Error("UNSIGNED_DIV_REM should have failed") + } +} + +func TestUnsignedDivRemHintOutOfBoundsError(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + // This is the condition that should make the hint execution error. + "div": {NewMaybeRelocatableFelt(FeltFromDecString("10633823966279327296825105735305134081"))}, + "value": {NewMaybeRelocatableFelt(FeltFromDecString("15"))}, + "r": {nil}, + "q": {nil}, + }, + vm, + ) + rcBuiltin := builtins.DefaultRangeCheckBuiltinRunner() + vm.BuiltinRunners = []builtins.BuiltinRunner{rcBuiltin} + + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: UNSIGNED_DIV_REM, + }) + + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) + if err == nil { + t.Errorf("UNSIGNED_DIV_REM should have failed") + } +} + +func TestSignedDivRemHintSuccess(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "div": {NewMaybeRelocatableFelt(FeltFromDecString("3"))}, + "value": {NewMaybeRelocatableFelt(FeltFromDecString("10"))}, + "bound": {NewMaybeRelocatableFelt(FeltFromDecString("29"))}, + "r": {nil}, + "biased_q": {nil}, + }, + vm, + ) + rcBuiltin := builtins.DefaultRangeCheckBuiltinRunner() + vm.BuiltinRunners = []builtins.BuiltinRunner{rcBuiltin} + + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SIGNED_DIV_REM, + }) + + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) + if err != nil { + t.Errorf("UNSIGNED_DIV_REM hint failed with error: %s", err) + } + + biasedQ, err := idsManager.GetFelt("biased_q", vm) + if err != nil { + t.Errorf("failed to get `biased_q`: %s", err) + } + + r, err := idsManager.GetFelt("r", vm) + if err != nil { + t.Errorf("failed to get `r`: %s", err) + } + + if biasedQ != FeltFromUint64(32) { + t.Errorf("Expected biased_q=32, got: %v", biasedQ) + } + + if r != FeltFromUint64(1) { + t.Errorf("Expected r=1, got: %v", r) + } +} + +func TestSignedDivRemHintDivZeroError(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "div": {NewMaybeRelocatableFelt(FeltFromDecString("0"))}, + "value": {NewMaybeRelocatableFelt(FeltFromDecString("10"))}, + "bound": {NewMaybeRelocatableFelt(FeltFromDecString("29"))}, + "r": {nil}, + "biased_q": {nil}, + }, + vm, + ) + rcBuiltin := builtins.DefaultRangeCheckBuiltinRunner() + vm.BuiltinRunners = []builtins.BuiltinRunner{rcBuiltin} + + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SIGNED_DIV_REM, + }) + + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) + if err == nil { + t.Errorf("UNSIGNED_DIV_REM hint should have failed") + } +} + +func TestSignedDivRemHintOutOfRcBoundsError(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "div": {NewMaybeRelocatableFelt(FeltFromDecString("10633823966279327296825105735305134081"))}, + "value": {NewMaybeRelocatableFelt(FeltFromDecString("10"))}, + "bound": {NewMaybeRelocatableFelt(FeltFromDecString("29"))}, + "r": {nil}, + "biased_q": {nil}, + }, + vm, + ) + rcBuiltin := builtins.DefaultRangeCheckBuiltinRunner() + vm.BuiltinRunners = []builtins.BuiltinRunner{rcBuiltin} + + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SIGNED_DIV_REM, + }) + + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) + if err == nil { + t.Errorf("UNSIGNED_DIV_REM hint should have failed") + } +} + +func TestSignedDivRemHintOutOfBoundsError(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "div": {NewMaybeRelocatableFelt(FeltFromDecString("4"))}, + "value": {NewMaybeRelocatableFelt(FeltFromDecString("16"))}, + "bound": {NewMaybeRelocatableFelt(FeltFromDecString("2"))}, + "r": {nil}, + "biased_q": {nil}, + }, + vm, + ) + rcBuiltin := builtins.DefaultRangeCheckBuiltinRunner() + vm.BuiltinRunners = []builtins.BuiltinRunner{rcBuiltin} + + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SIGNED_DIV_REM, + }) + + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) + if err == nil { + t.Errorf("UNSIGNED_DIV_REM hint should have failed") + } +} From 3683f8345c51d819806951a71dd221b502725ca6 Mon Sep 17 00:00:00 2001 From: Mariano Nicolini Date: Tue, 19 Sep 2023 17:30:58 -0300 Subject: [PATCH 19/55] Remove unused commented code --- pkg/hints/hint_utils/testing_utils.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/pkg/hints/hint_utils/testing_utils.go b/pkg/hints/hint_utils/testing_utils.go index 91904483..46403f07 100644 --- a/pkg/hints/hint_utils/testing_utils.go +++ b/pkg/hints/hint_utils/testing_utils.go @@ -32,14 +32,8 @@ func SetupIdsForTest(ids map[string][]*memory.MaybeRelocatable, vm *VirtualMachi }, } // Update current_offset - // var offset_update int - // if len(elems) != 0 { - // offset_update = len(elems) - // } else { - // offset_update = 1 - // } current_offset += len(elems) - // current_offset += offset_update + // Insert ids variables (if present) for n, elem := range elems { if elem != nil { From 1d569c89a34e3d2ebe7f4b92ead36dd8e0590c11 Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 19 Sep 2023 17:35:06 -0300 Subject: [PATCH 20/55] Add missing file --- pkg/hints/hint_codes/keccak_hint_codes.go | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 pkg/hints/hint_codes/keccak_hint_codes.go diff --git a/pkg/hints/hint_codes/keccak_hint_codes.go b/pkg/hints/hint_codes/keccak_hint_codes.go new file mode 100644 index 00000000..2db053ca --- /dev/null +++ b/pkg/hints/hint_codes/keccak_hint_codes.go @@ -0,0 +1,3 @@ +package hint_codes + +const UNSAFE_KECCAK = "from eth_hash.auto import keccak\n\ndata, length = ids.data, ids.length\n\nif '__keccak_max_size' in globals():\n assert length <= __keccak_max_size, \\\n f'unsafe_keccak() can only be used with length<={__keccak_max_size}. ' \\\n f'Got: length={length}.'\n\nkeccak_input = bytearray()\nfor word_i, byte_i in enumerate(range(0, length, 16)):\n word = memory[data + word_i]\n n_bytes = min(16, length - byte_i)\n assert 0 <= word < 2 ** (8 * n_bytes)\n keccak_input += word.to_bytes(n_bytes, 'big')\n\nhashed = keccak(keccak_input)\nids.high = int.from_bytes(hashed[:16], 'big')\nids.low = int.from_bytes(hashed[16:32], 'big')" From 2da342e2bdb324308a4f34e0007d4c172862a4a6 Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 19 Sep 2023 17:48:04 -0300 Subject: [PATCH 21/55] Add constant + GetStructFieldRelocatable + start hint --- pkg/hints/hint_codes/keccak_hint_codes.go | 2 ++ pkg/hints/hint_utils/ids_manager.go | 29 +++++++++++++++++++++++ pkg/hints/keccak_hints.go | 16 +++++++++++++ 3 files changed, 47 insertions(+) diff --git a/pkg/hints/hint_codes/keccak_hint_codes.go b/pkg/hints/hint_codes/keccak_hint_codes.go index 2db053ca..c9cc1ea1 100644 --- a/pkg/hints/hint_codes/keccak_hint_codes.go +++ b/pkg/hints/hint_codes/keccak_hint_codes.go @@ -1,3 +1,5 @@ package hint_codes const UNSAFE_KECCAK = "from eth_hash.auto import keccak\n\ndata, length = ids.data, ids.length\n\nif '__keccak_max_size' in globals():\n assert length <= __keccak_max_size, \\\n f'unsafe_keccak() can only be used with length<={__keccak_max_size}. ' \\\n f'Got: length={length}.'\n\nkeccak_input = bytearray()\nfor word_i, byte_i in enumerate(range(0, length, 16)):\n word = memory[data + word_i]\n n_bytes = min(16, length - byte_i)\n assert 0 <= word < 2 ** (8 * n_bytes)\n keccak_input += word.to_bytes(n_bytes, 'big')\n\nhashed = keccak(keccak_input)\nids.high = int.from_bytes(hashed[:16], 'big')\nids.low = int.from_bytes(hashed[16:32], 'big')" + +const UNSAFE_KECCAK_FINALIZE = "from eth_hash.auto import keccak\nkeccak_input = bytearray()\nn_elms = ids.keccak_state.end_ptr - ids.keccak_state.start_ptr\nfor word in memory.get_range(ids.keccak_state.start_ptr, n_elms):\n keccak_input += word.to_bytes(16, 'big')\nhashed = keccak(keccak_input)\nids.high = int.from_bytes(hashed[:16], 'big')\nids.low = int.from_bytes(hashed[16:32], 'big')" diff --git a/pkg/hints/hint_utils/ids_manager.go b/pkg/hints/hint_utils/ids_manager.go index f883ac5c..fadacfeb 100644 --- a/pkg/hints/hint_utils/ids_manager.go +++ b/pkg/hints/hint_utils/ids_manager.go @@ -162,6 +162,35 @@ func (ids *IdsManager) GetStructFieldFelt(name string, field_off uint, vm *Virtu return lambdaworks.Felt{}, ErrUnknownIdentifier(name) } +/* + Returns the value of an ids' field (given that the identifier is a sruct) as a Relocatable + For example: + + struct shelter { + cats cat* + dogs dog* + } + + to access each struct field, cats will be field 0 and dogs will be field 1, so to access them we can use: + ids_cats := ids.GetStructFieldFelt("shelter", 0, vm) or ids_cats := ids.Get("shelter", vm) + ids_dogs := ids.GetStructFieldFelt("shelter", 1, vm) +*/ +func (ids *IdsManager) GetStructFieldRelocatable(name string, field_off uint, vm *VirtualMachine) (Relocatable, error) { + reference, ok := ids.References[name] + if ok { + val, ok := getStructFieldFromReference(&reference, field_off, ids.HintApTracking, vm) + if ok { + rel, is_rel := val.GetRelocatable() + if !is_rel { + return Relocatable{}, errors.Errorf("Identifier %s is not a Relocatable", name) + } + return rel, nil + } + } + + return Relocatable{}, ErrUnknownIdentifier(name) +} + /* Inserts value into an ids' field (given that the identifier is a sruct) For example: diff --git a/pkg/hints/keccak_hints.go b/pkg/hints/keccak_hints.go index 615e5d06..1ec95b77 100644 --- a/pkg/hints/keccak_hints.go +++ b/pkg/hints/keccak_hints.go @@ -71,3 +71,19 @@ func unsafeKeccak(ids IdsManager, vm *VirtualMachine, scopes ExecutionScopes) er } return ids.Insert("low", NewMaybeRelocatableFelt(low), vm) } + +func unsafeKeccakFinalize(ids IdsManager, vm *VirtualMachine, scopes ExecutionScopes) error { + // Fetch ids variables + startPtr, err := ids.GetStructFieldRelocatable("keccak_state", 0, vm) + if err != nil { + return err + } + endPtr, err := ids.GetStructFieldRelocatable("keccak_state", 1, vm) + if err != nil { + return err + } + n_elems, err := endPtr.Sub(startPtr) + if err != nil { + return err + } +} From 96137fb021953c14793c7898d1f4c40d74fa1933 Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 19 Sep 2023 17:48:30 -0300 Subject: [PATCH 22/55] Add test file --- cairo_programs/unsafe_keccak_finalize.cairo | 28 +++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 cairo_programs/unsafe_keccak_finalize.cairo diff --git a/cairo_programs/unsafe_keccak_finalize.cairo b/cairo_programs/unsafe_keccak_finalize.cairo new file mode 100644 index 00000000..6cf89f88 --- /dev/null +++ b/cairo_programs/unsafe_keccak_finalize.cairo @@ -0,0 +1,28 @@ +%builtins output + +from starkware.cairo.common.alloc import alloc +from starkware.cairo.common.serialize import serialize_word +from starkware.cairo.common.keccak import unsafe_keccak_finalize, KeccakState +from starkware.cairo.common.uint256 import Uint256 + +func main{output_ptr: felt*}() { + alloc_locals; + + let (data: felt*) = alloc(); + + assert data[0] = 0; + assert data[1] = 1; + assert data[2] = 2; + + let keccak_state = KeccakState(start_ptr=data, end_ptr=data + 2); + + let res: Uint256 = unsafe_keccak_finalize(keccak_state); + + assert res.low = 17219183504112405672555532996650339574; + assert res.high = 235346966651632113557018504892503714354; + + serialize_word(res.low); + serialize_word(res.high); + + return (); +} From bdbe7b458096709cac287647972a186ea662c26e Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 19 Sep 2023 17:55:55 -0300 Subject: [PATCH 23/55] Add MemorySegmentManager.GetFeltRange --- pkg/vm/memory/segments.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pkg/vm/memory/segments.go b/pkg/vm/memory/segments.go index d22d62f7..659304f8 100644 --- a/pkg/vm/memory/segments.go +++ b/pkg/vm/memory/segments.go @@ -172,3 +172,15 @@ func (m *MemorySegmentManager) Finalize(size *uint, segmentIndex uint, publicMem m.PublicMemoryOffsets[segmentIndex] = emptyList } } + +func (m *MemorySegmentManager) GetFeltRange(start Relocatable, size uint) ([]lambdaworks.Felt, error) { + feltRange := make([]lambdaworks.Felt, 0, size) + for i := uint(0); i < size; i++ { + val, err := m.Memory.GetFelt(start.AddUint(i)) + if err != nil { + return nil, err + } + feltRange = append(feltRange, val) + } + return feltRange, nil +} From 7cb6ecaca17e7f929478e80fad50a03916c997ca Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 19 Sep 2023 17:56:16 -0300 Subject: [PATCH 24/55] Progress --- pkg/hints/keccak_hints.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pkg/hints/keccak_hints.go b/pkg/hints/keccak_hints.go index 1ec95b77..a79ffbff 100644 --- a/pkg/hints/keccak_hints.go +++ b/pkg/hints/keccak_hints.go @@ -82,8 +82,15 @@ func unsafeKeccakFinalize(ids IdsManager, vm *VirtualMachine, scopes ExecutionSc if err != nil { return err } - n_elems, err := endPtr.Sub(startPtr) + + // Hint Logic + nElemsFelt, err := endPtr.Sub(startPtr) if err != nil { return err } + nElems, err := nElemsFelt.ToU64() + if err != nil { + return err + } + } From ce7c084bdf7748a58559eff05220025d5ae3be40 Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 19 Sep 2023 18:14:23 -0300 Subject: [PATCH 25/55] Finish hint --- pkg/hints/hint_processor.go | 2 ++ pkg/hints/keccak_hints.go | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index f9b1174b..9a25fb77 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -102,6 +102,8 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, return vm_enter_scope(execScopes) case UNSAFE_KECCAK: return unsafeKeccak(data.Ids, vm, *execScopes) + case UNSAFE_KECCAK_FINALIZE: + return unsafeKeccakFinalize(data.Ids, vm, *execScopes) case IS_NN: return isNN(data.Ids, vm) case IS_NN_OUT_OF_RANGE: diff --git a/pkg/hints/keccak_hints.go b/pkg/hints/keccak_hints.go index a79ffbff..6f639991 100644 --- a/pkg/hints/keccak_hints.go +++ b/pkg/hints/keccak_hints.go @@ -92,5 +92,28 @@ func unsafeKeccakFinalize(ids IdsManager, vm *VirtualMachine, scopes ExecutionSc if err != nil { return err } + inputFelts, err := vm.Segments.GetFeltRange(startPtr, uint(nElems)) + if err != nil { + return err + } + inputBytes := make([]byte, 0, 16*nElems) + for i := 0; i < int(nElems); i++ { + inputBytes = append(inputBytes, inputFelts[i].ToBeBytes()[16:]...) + } + + hasher := keccak.New256() + hasher.Write(inputBytes) + resBytes := hasher.Sum(nil) + + highBytes := append([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, resBytes[:16]...) + lowBytes := append([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, resBytes[16:32]...) + high := FeltFromBeBytes((*[32]byte)(highBytes)) + low := FeltFromBeBytes((*[32]byte)(lowBytes)) + + err = ids.Insert("high", NewMaybeRelocatableFelt(high), vm) + if err != nil { + return err + } + return ids.Insert("low", NewMaybeRelocatableFelt(low), vm) } From 85ebe5064fe01357529069ce5d5976f07a31b253 Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 19 Sep 2023 18:23:25 -0300 Subject: [PATCH 26/55] Add unit test --- pkg/hints/hint_processor.go | 2 +- pkg/hints/keccak_hints.go | 2 +- pkg/hints/keccak_hints_test.go | 43 ++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index 9a25fb77..55ce7a89 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -103,7 +103,7 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, case UNSAFE_KECCAK: return unsafeKeccak(data.Ids, vm, *execScopes) case UNSAFE_KECCAK_FINALIZE: - return unsafeKeccakFinalize(data.Ids, vm, *execScopes) + return unsafeKeccakFinalize(data.Ids, vm) case IS_NN: return isNN(data.Ids, vm) case IS_NN_OUT_OF_RANGE: diff --git a/pkg/hints/keccak_hints.go b/pkg/hints/keccak_hints.go index 6f639991..969ccdba 100644 --- a/pkg/hints/keccak_hints.go +++ b/pkg/hints/keccak_hints.go @@ -72,7 +72,7 @@ func unsafeKeccak(ids IdsManager, vm *VirtualMachine, scopes ExecutionScopes) er return ids.Insert("low", NewMaybeRelocatableFelt(low), vm) } -func unsafeKeccakFinalize(ids IdsManager, vm *VirtualMachine, scopes ExecutionScopes) error { +func unsafeKeccakFinalize(ids IdsManager, vm *VirtualMachine) error { // Fetch ids variables startPtr, err := ids.GetStructFieldRelocatable("keccak_state", 0, vm) if err != nil { diff --git a/pkg/hints/keccak_hints_test.go b/pkg/hints/keccak_hints_test.go index 020412b9..6f20d8e4 100644 --- a/pkg/hints/keccak_hints_test.go +++ b/pkg/hints/keccak_hints_test.go @@ -123,3 +123,46 @@ func TestUnsafeKeccakInvalidWordSize(t *testing.T) { t.Errorf("UNSAFE_KECCAK hint test should have failed") } } + +func TestUnsafeKeccakFinalizeOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + inputStart := vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "keccak_state": { + NewMaybeRelocatableRelocatable(inputStart), + NewMaybeRelocatableRelocatable(inputStart.AddUint(2)), + }, + "high": {nil}, + "low": {nil}, + }, + vm, + ) + // Insert keccak input into memory + input := []MaybeRelocatable{ + *NewMaybeRelocatableFelt(FeltZero()), + *NewMaybeRelocatableFelt(FeltOne()), + } + vm.Segments.LoadData(inputStart, &input) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: UNSAFE_KECCAK_FINALIZE, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) + if err != nil { + t.Errorf("UNSAFE_KECCAK_FINALIZE hint test failed with error %s", err) + } + // Check ids values + high, err := idsManager.GetFelt("high", vm) + expectedHigh := FeltFromDecString("235346966651632113557018504892503714354") + if err != nil || high != expectedHigh { + t.Errorf("Wrong/No ids.high.\n Expected %s, got %s.", expectedHigh.ToHexString(), high.ToHexString()) + } + low, err := idsManager.GetFelt("low", vm) + expectedLow := FeltFromDecString("17219183504112405672555532996650339574") + if err != nil || low != expectedLow { + t.Errorf("Wrong/No ids.low\n Expected %s, got %s.", expectedLow.ToHexString(), low.ToHexString()) + } +} From 18d1137d7791a7547fc23ec1500606a9e6e94f7f Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 19 Sep 2023 18:24:28 -0300 Subject: [PATCH 27/55] Add integration test --- pkg/vm/cairo_run/cairo_run_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkg/vm/cairo_run/cairo_run_test.go b/pkg/vm/cairo_run/cairo_run_test.go index ea6d889f..f3587222 100644 --- a/pkg/vm/cairo_run/cairo_run_test.go +++ b/pkg/vm/cairo_run/cairo_run_test.go @@ -218,6 +218,14 @@ func TestUnsafeKeccak(t *testing.T) { } } +func TestUnsafeKeccakFinalize(t *testing.T) { + cairoRunConfig := cairo_run.CairoRunConfig{DisableTracePadding: false, Layout: "all_cairo", ProofMode: false} + _, err := cairo_run.CairoRun("../../../cairo_programs/unsafe_keccak_finalize.json", cairoRunConfig) + if err != nil { + t.Errorf("Program execution failed with error: %s", err) + } +} + func TestMathCmp(t *testing.T) { cairoRunConfig := cairo_run.CairoRunConfig{DisableTracePadding: false, Layout: "all_cairo", ProofMode: false} _, err := cairo_run.CairoRun("../../../cairo_programs/math_cmp.json", cairoRunConfig) From 590f61fa6606028563293bc2d7a71b5e7ef4c825 Mon Sep 17 00:00:00 2001 From: Mariano Nicolini Date: Wed, 20 Sep 2023 11:24:37 -0300 Subject: [PATCH 28/55] Remove nParts and bound from range check runner fields, add method to calculate bound from constant and add test to assert bound is never zero --- pkg/builtins/range_check.go | 10 ++++++---- pkg/builtins/range_check_test.go | 12 ++++++++++++ pkg/hints/math_hints.go | 8 +++----- pkg/vm/vm_core.go | 2 +- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/pkg/builtins/range_check.go b/pkg/builtins/range_check.go index fd832042..2f42886b 100644 --- a/pkg/builtins/range_check.go +++ b/pkg/builtins/range_check.go @@ -36,14 +36,11 @@ type RangeCheckBuiltinRunner struct { ratio uint instancesPerComponent uint StopPtr *uint - nParts uint - Bound lambdaworks.Felt } func NewRangeCheckBuiltinRunner(ratio uint, nParts uint) *RangeCheckBuiltinRunner { - bound := lambdaworks.FeltOne().Shl(16 * uint64(nParts)) - return &RangeCheckBuiltinRunner{ratio: ratio, instancesPerComponent: 1, nParts: nParts, Bound: bound} + return &RangeCheckBuiltinRunner{ratio: ratio, instancesPerComponent: 1} } func DefaultRangeCheckBuiltinRunner() *RangeCheckBuiltinRunner { @@ -58,6 +55,11 @@ func (r *RangeCheckBuiltinRunner) Name() string { return RANGE_CHECK_BUILTIN_NAME } +func (r *RangeCheckBuiltinRunner) Bound() lambdaworks.Felt { + bound := lambdaworks.FeltOne().Shl(16 * uint64(RANGE_CHECK_N_PARTS)) + return bound +} + func (r *RangeCheckBuiltinRunner) SetBase(value memory.Relocatable) { r.base = value } diff --git a/pkg/builtins/range_check_test.go b/pkg/builtins/range_check_test.go index 17db35db..d515af3e 100644 --- a/pkg/builtins/range_check_test.go +++ b/pkg/builtins/range_check_test.go @@ -148,3 +148,15 @@ func TestGetRangeCheckUsageEmptyMemory(t *testing.T) { t.Errorf("rcMax should return nil, got %d", *resultMax) } } + +// Range check bound is calculated via the constant RANGE_CHECK_N_PARTS. +// If something changes and the bound is set to zero, there could be unexpected errors. +func TestBoundIsNotZero(t *testing.T) { + rangeCheck := builtins.DefaultRangeCheckBuiltinRunner() + + bound := rangeCheck.Bound() + + if bound.IsZero() { + t.Error("range check bound should never be zero") + } +} diff --git a/pkg/hints/math_hints.go b/pkg/hints/math_hints.go index c498a653..b43b5e1b 100644 --- a/pkg/hints/math_hints.go +++ b/pkg/hints/math_hints.go @@ -209,9 +209,10 @@ func unsignedDivRem(ids IdsManager, vm *VirtualMachine) error { } rcBound, err := vm.GetRangeCheckBound() - if rcBound.Cmp(lambdaworks.FeltZero()) == 0 { - return errors.New("range check bound cannot be zero") + if err != nil { + return err } + primeBoundDivision := new(big.Int).Div(lambdaworks.Prime(), rcBound.ToBigInt()) // Check if `div` is greater than `limit` @@ -443,9 +444,6 @@ func signedDivRem(ids IdsManager, vm *VirtualMachine) error { return err } - if rcBound.Cmp(lambdaworks.FeltZero()) == 0 { - return errors.New("range check bound cannot be zero") - } primeBoundDivision := new(big.Int).Div(lambdaworks.Prime(), rcBound.ToBigInt()) // Check if `div` is greater than `limit` and make assertions diff --git a/pkg/vm/vm_core.go b/pkg/vm/vm_core.go index e3522cd5..1c3804de 100644 --- a/pkg/vm/vm_core.go +++ b/pkg/vm/vm_core.go @@ -638,5 +638,5 @@ func (vm *VirtualMachine) GetRangeCheckBound() (lambdaworks.Felt, error) { return lambdaworks.FeltZero(), errors.New("could not cast to RangeCheckBuiltinRunner") } - return rcBuiltin.Bound, nil + return rcBuiltin.Bound(), nil } From 43dfe76c4f8c794398ac3f73571bd1cf2554c358 Mon Sep 17 00:00:00 2001 From: Mariano Nicolini Date: Wed, 20 Sep 2023 11:30:07 -0300 Subject: [PATCH 29/55] Remove unused input parameter to NewRangeCheckBuiltinRunner --- pkg/builtins/range_check.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/builtins/range_check.go b/pkg/builtins/range_check.go index 2f42886b..3780ef3f 100644 --- a/pkg/builtins/range_check.go +++ b/pkg/builtins/range_check.go @@ -38,13 +38,13 @@ type RangeCheckBuiltinRunner struct { StopPtr *uint } -func NewRangeCheckBuiltinRunner(ratio uint, nParts uint) *RangeCheckBuiltinRunner { +func NewRangeCheckBuiltinRunner(ratio uint) *RangeCheckBuiltinRunner { return &RangeCheckBuiltinRunner{ratio: ratio, instancesPerComponent: 1} } func DefaultRangeCheckBuiltinRunner() *RangeCheckBuiltinRunner { - return NewRangeCheckBuiltinRunner(8, 8) + return NewRangeCheckBuiltinRunner(8) } func (r *RangeCheckBuiltinRunner) Base() memory.Relocatable { From 6e56be7789400b5cdfa53c3c012051ddf921bbe3 Mon Sep 17 00:00:00 2001 From: Mariano Nicolini Date: Wed, 20 Sep 2023 11:33:45 -0300 Subject: [PATCH 30/55] Change number to constant in Bound impl --- pkg/builtins/range_check.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/builtins/range_check.go b/pkg/builtins/range_check.go index 3780ef3f..8ee6ae65 100644 --- a/pkg/builtins/range_check.go +++ b/pkg/builtins/range_check.go @@ -56,7 +56,7 @@ func (r *RangeCheckBuiltinRunner) Name() string { } func (r *RangeCheckBuiltinRunner) Bound() lambdaworks.Felt { - bound := lambdaworks.FeltOne().Shl(16 * uint64(RANGE_CHECK_N_PARTS)) + bound := lambdaworks.FeltOne().Shl(INNER_RC_BOUND_SHIFT * RANGE_CHECK_N_PARTS) return bound } From 09473b9e1eb12ef3e30052183eb5b6978523107c Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 20 Sep 2023 11:41:24 -0300 Subject: [PATCH 31/55] Add hint + test --- pkg/hints/hint_codes/keccak_hint_codes.go | 2 + pkg/hints/hint_processor.go | 2 + pkg/hints/keccak_hints.go | 12 +++ pkg/hints/keccak_hints_test.go | 96 +++++++++++++++++++++++ 4 files changed, 112 insertions(+) diff --git a/pkg/hints/hint_codes/keccak_hint_codes.go b/pkg/hints/hint_codes/keccak_hint_codes.go index c9cc1ea1..dc88ff5f 100644 --- a/pkg/hints/hint_codes/keccak_hint_codes.go +++ b/pkg/hints/hint_codes/keccak_hint_codes.go @@ -3,3 +3,5 @@ package hint_codes const UNSAFE_KECCAK = "from eth_hash.auto import keccak\n\ndata, length = ids.data, ids.length\n\nif '__keccak_max_size' in globals():\n assert length <= __keccak_max_size, \\\n f'unsafe_keccak() can only be used with length<={__keccak_max_size}. ' \\\n f'Got: length={length}.'\n\nkeccak_input = bytearray()\nfor word_i, byte_i in enumerate(range(0, length, 16)):\n word = memory[data + word_i]\n n_bytes = min(16, length - byte_i)\n assert 0 <= word < 2 ** (8 * n_bytes)\n keccak_input += word.to_bytes(n_bytes, 'big')\n\nhashed = keccak(keccak_input)\nids.high = int.from_bytes(hashed[:16], 'big')\nids.low = int.from_bytes(hashed[16:32], 'big')" const UNSAFE_KECCAK_FINALIZE = "from eth_hash.auto import keccak\nkeccak_input = bytearray()\nn_elms = ids.keccak_state.end_ptr - ids.keccak_state.start_ptr\nfor word in memory.get_range(ids.keccak_state.start_ptr, n_elms):\n keccak_input += word.to_bytes(16, 'big')\nhashed = keccak(keccak_input)\nids.high = int.from_bytes(hashed[:16], 'big')\nids.low = int.from_bytes(hashed[16:32], 'big')" + +const COMPARE_BYTES_IN_WORD_NONDET = "memory[ap] = to_felt_or_relocatable(ids.n_bytes < ids.BYTES_IN_WORD)" diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index 55ce7a89..54c144f0 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -104,6 +104,8 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, return unsafeKeccak(data.Ids, vm, *execScopes) case UNSAFE_KECCAK_FINALIZE: return unsafeKeccakFinalize(data.Ids, vm) + case COMPARE_BYTES_IN_WORD_NONDET: + return compareBytesInWordNondet(data.Ids, vm, constants) case IS_NN: return isNN(data.Ids, vm) case IS_NN_OUT_OF_RANGE: diff --git a/pkg/hints/keccak_hints.go b/pkg/hints/keccak_hints.go index 969ccdba..8f834740 100644 --- a/pkg/hints/keccak_hints.go +++ b/pkg/hints/keccak_hints.go @@ -117,3 +117,15 @@ func unsafeKeccakFinalize(ids IdsManager, vm *VirtualMachine) error { } return ids.Insert("low", NewMaybeRelocatableFelt(low), vm) } + +func compareBytesInWordNondet(ids IdsManager, vm *VirtualMachine, constants *map[string]Felt) error { + nBytes, err := ids.GetFelt("n_bytes", vm) + if err != nil { + return err + } + bytesInWord, err := ids.GetConst("BYTES_IN_WORD", constants) + if nBytes.Cmp(bytesInWord) == -1 { + return vm.Segments.Memory.Insert(vm.RunContext.Ap, NewMaybeRelocatableFelt(FeltOne())) + } + return vm.Segments.Memory.Insert(vm.RunContext.Ap, NewMaybeRelocatableFelt(FeltZero())) +} diff --git a/pkg/hints/keccak_hints_test.go b/pkg/hints/keccak_hints_test.go index 6f20d8e4..31fe6177 100644 --- a/pkg/hints/keccak_hints_test.go +++ b/pkg/hints/keccak_hints_test.go @@ -166,3 +166,99 @@ func TestUnsafeKeccakFinalizeOk(t *testing.T) { t.Errorf("Wrong/No ids.low\n Expected %s, got %s.", expectedLow.ToHexString(), low.ToHexString()) } } + +func TestCompareBytesInWordHintEq(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + // Advance fp to avoid clashes with values inserted into ap + vm.RunContext.Fp.Offset += 1 + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "n_bytes": {NewMaybeRelocatableFelt(FeltFromUint64(17))}, + }, + vm, + ) + constants := SetupConstantsForTest( + map[string]Felt{ + "BYTES_IN_WORD": FeltFromUint64(17), + }, + &idsManager) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: COMPARE_BYTES_IN_WORD_NONDET, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, &constants, nil) + if err != nil { + t.Errorf("COMPARE_BYTES_IN_WORD_NONDET hint test failed with error %s", err) + } + // Check the value of memory[ap] + val, err := vm.Segments.Memory.GetFelt(vm.RunContext.Ap) + if err != nil || !val.IsZero() { + t.Error("Wrong/No value inserted into ap") + } +} + +func TestCompareBytesInWordHintGt(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + // Advance fp to avoid clashes with values inserted into ap + vm.RunContext.Fp.Offset += 1 + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "n_bytes": {NewMaybeRelocatableFelt(FeltFromUint64(18))}, + }, + vm, + ) + constants := SetupConstantsForTest( + map[string]Felt{ + "BYTES_IN_WORD": FeltFromUint64(17), + }, + &idsManager) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: COMPARE_BYTES_IN_WORD_NONDET, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, &constants, nil) + if err != nil { + t.Errorf("COMPARE_BYTES_IN_WORD_NONDET hint test failed with error %s", err) + } + // Check the value of memory[ap] + val, err := vm.Segments.Memory.GetFelt(vm.RunContext.Ap) + if err != nil || !val.IsZero() { + t.Error("Wrong/No value inserted into ap") + } +} + +func TestCompareBytesInWordHintLt(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + // Advance fp to avoid clashes with values inserted into ap + vm.RunContext.Fp.Offset += 1 + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "n_bytes": {NewMaybeRelocatableFelt(FeltFromUint64(16))}, + }, + vm, + ) + constants := SetupConstantsForTest( + map[string]Felt{ + "BYTES_IN_WORD": FeltFromUint64(17), + }, + &idsManager) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: COMPARE_BYTES_IN_WORD_NONDET, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, &constants, nil) + if err != nil { + t.Errorf("COMPARE_BYTES_IN_WORD_NONDET hint test failed with error %s", err) + } + // Check the value of memory[ap] + val, err := vm.Segments.Memory.GetFelt(vm.RunContext.Ap) + if err != nil || val != FeltOne() { + t.Error("Wrong/No value inserted into ap") + } +} From 511c00327a9a99f4f2d26d22cd3831d7b03542c6 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 20 Sep 2023 11:48:20 -0300 Subject: [PATCH 32/55] Add integration tests --- pkg/vm/cairo_run/cairo_run_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkg/vm/cairo_run/cairo_run_test.go b/pkg/vm/cairo_run/cairo_run_test.go index f3587222..35b67d11 100644 --- a/pkg/vm/cairo_run/cairo_run_test.go +++ b/pkg/vm/cairo_run/cairo_run_test.go @@ -265,3 +265,11 @@ func TestSplitFeltHint(t *testing.T) { t.Errorf("Program execution failed with error: %s", err) } } + +func TestKeccakIntegrationTests(t *testing.T) { + cairoRunConfig := cairo_run.CairoRunConfig{DisableTracePadding: false, Layout: "all_cairo", ProofMode: false} + _, err := cairo_run.CairoRun("../../../cairo_programs/keccak_integration_tests.json", cairoRunConfig) + if err != nil { + t.Errorf("Program execution failed with error: %s", err) + } +} From 8dee382325dfdfb511d147bb79d6158afd396708 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 20 Sep 2023 11:58:46 -0300 Subject: [PATCH 33/55] Add hint + tests --- pkg/hints/hint_codes/keccak_hint_codes.go | 2 + pkg/hints/hint_processor.go | 2 + pkg/hints/keccak_hints.go | 12 +++ pkg/hints/keccak_hints_test.go | 96 +++++++++++++++++++++++ 4 files changed, 112 insertions(+) diff --git a/pkg/hints/hint_codes/keccak_hint_codes.go b/pkg/hints/hint_codes/keccak_hint_codes.go index dc88ff5f..9a0efa25 100644 --- a/pkg/hints/hint_codes/keccak_hint_codes.go +++ b/pkg/hints/hint_codes/keccak_hint_codes.go @@ -5,3 +5,5 @@ const UNSAFE_KECCAK = "from eth_hash.auto import keccak\n\ndata, length = ids.da const UNSAFE_KECCAK_FINALIZE = "from eth_hash.auto import keccak\nkeccak_input = bytearray()\nn_elms = ids.keccak_state.end_ptr - ids.keccak_state.start_ptr\nfor word in memory.get_range(ids.keccak_state.start_ptr, n_elms):\n keccak_input += word.to_bytes(16, 'big')\nhashed = keccak(keccak_input)\nids.high = int.from_bytes(hashed[:16], 'big')\nids.low = int.from_bytes(hashed[16:32], 'big')" const COMPARE_BYTES_IN_WORD_NONDET = "memory[ap] = to_felt_or_relocatable(ids.n_bytes < ids.BYTES_IN_WORD)" + +const COMPARE_KECCAK_FULL_RATE_IN_BYTES_NONDET = "memory[ap] = to_felt_or_relocatable(ids.n_bytes >= ids.KECCAK_FULL_RATE_IN_BYTES)" diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index eaf8f4ce..df716960 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -112,6 +112,8 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, return unsafeKeccakFinalize(data.Ids, vm) case COMPARE_BYTES_IN_WORD_NONDET: return compareBytesInWordNondet(data.Ids, vm, constants) + case COMPARE_KECCAK_FULL_RATE_IN_BYTES_NONDET: + return compareKeccakFullRateInBytesNondet(data.Ids, vm, constants) case UNSIGNED_DIV_REM: return unsignedDivRem(data.Ids, vm) case SIGNED_DIV_REM: diff --git a/pkg/hints/keccak_hints.go b/pkg/hints/keccak_hints.go index 8f834740..cad2846f 100644 --- a/pkg/hints/keccak_hints.go +++ b/pkg/hints/keccak_hints.go @@ -129,3 +129,15 @@ func compareBytesInWordNondet(ids IdsManager, vm *VirtualMachine, constants *map } return vm.Segments.Memory.Insert(vm.RunContext.Ap, NewMaybeRelocatableFelt(FeltZero())) } + +func compareKeccakFullRateInBytesNondet(ids IdsManager, vm *VirtualMachine, constants *map[string]Felt) error { + nBytes, err := ids.GetFelt("n_bytes", vm) + if err != nil { + return err + } + bytesInWord, err := ids.GetConst("KECCAK_FULL_RATE_IN_BYTES", constants) + if !(nBytes.Cmp(bytesInWord) == -1) { + return vm.Segments.Memory.Insert(vm.RunContext.Ap, NewMaybeRelocatableFelt(FeltOne())) + } + return vm.Segments.Memory.Insert(vm.RunContext.Ap, NewMaybeRelocatableFelt(FeltZero())) +} diff --git a/pkg/hints/keccak_hints_test.go b/pkg/hints/keccak_hints_test.go index 31fe6177..bccdeecd 100644 --- a/pkg/hints/keccak_hints_test.go +++ b/pkg/hints/keccak_hints_test.go @@ -262,3 +262,99 @@ func TestCompareBytesInWordHintLt(t *testing.T) { t.Error("Wrong/No value inserted into ap") } } + +func TestCompareKeccakFullRateInBytesHintEq(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + // Advance fp to avoid clashes with values inserted into ap + vm.RunContext.Fp.Offset += 1 + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "n_bytes": {NewMaybeRelocatableFelt(FeltFromUint64(17))}, + }, + vm, + ) + constants := SetupConstantsForTest( + map[string]Felt{ + "KECCAK_FULL_RATE_IN_BYTES": FeltFromUint64(17), + }, + &idsManager) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: COMPARE_KECCAK_FULL_RATE_IN_BYTES_NONDET, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, &constants, nil) + if err != nil { + t.Errorf("COMPARE_KECCAK_FULL_RATE_IN_BYTES_NONDET hint test failed with error %s", err) + } + // Check the value of memory[ap] + val, err := vm.Segments.Memory.GetFelt(vm.RunContext.Ap) + if err != nil || val != FeltOne() { + t.Error("Wrong/No value inserted into ap") + } +} + +func TestCompareKeccakFullRateInBytesHintGt(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + // Advance fp to avoid clashes with values inserted into ap + vm.RunContext.Fp.Offset += 1 + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "n_bytes": {NewMaybeRelocatableFelt(FeltFromUint64(18))}, + }, + vm, + ) + constants := SetupConstantsForTest( + map[string]Felt{ + "KECCAK_FULL_RATE_IN_BYTES": FeltFromUint64(17), + }, + &idsManager) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: COMPARE_KECCAK_FULL_RATE_IN_BYTES_NONDET, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, &constants, nil) + if err != nil { + t.Errorf("COMPARE_KECCAK_FULL_RATE_IN_BYTES_NONDET hint test failed with error %s", err) + } + // Check the value of memory[ap] + val, err := vm.Segments.Memory.GetFelt(vm.RunContext.Ap) + if err != nil || val != FeltOne() { + t.Error("Wrong/No value inserted into ap") + } +} + +func TestCompareKeccakFullRateInBytesHintLt(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + // Advance fp to avoid clashes with values inserted into ap + vm.RunContext.Fp.Offset += 1 + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "n_bytes": {NewMaybeRelocatableFelt(FeltFromUint64(16))}, + }, + vm, + ) + constants := SetupConstantsForTest( + map[string]Felt{ + "KECCAK_FULL_RATE_IN_BYTES": FeltFromUint64(17), + }, + &idsManager) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: COMPARE_KECCAK_FULL_RATE_IN_BYTES_NONDET, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, &constants, nil) + if err != nil { + t.Errorf("COMPARE_KECCAK_FULL_RATE_IN_BYTES_NONDET hint test failed with error %s", err) + } + // Check the value of memory[ap] + val, err := vm.Segments.Memory.GetFelt(vm.RunContext.Ap) + if err != nil || !val.IsZero() { + t.Error("Wrong/No value inserted into ap") + } +} From e0f17398a59c14f6271381efd6f21ca4e553f2f7 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 20 Sep 2023 12:40:15 -0300 Subject: [PATCH 34/55] Add hint --- pkg/builtins/keccak.go | 4 +- pkg/hints/hint_codes/keccak_hint_codes.go | 8 ++++ pkg/hints/hint_processor.go | 2 + pkg/hints/keccak_hints.go | 46 ++++++++++++++++++++++- 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/pkg/builtins/keccak.go b/pkg/builtins/keccak.go index 46b53809..a5f9638e 100644 --- a/pkg/builtins/keccak.go +++ b/pkg/builtins/keccak.go @@ -101,7 +101,7 @@ func (k *KeccakBuiltinRunner) DeduceMemoryCell(address Relocatable, mem *Memory) for i := 0; i < 25; i++ { output_message_u64[i] = binary.LittleEndian.Uint64(output_message_bytes[8*i : 8*i+8]) } - keccakF1600(&output_message_u64) + KeccakF1600(&output_message_u64) // Convert back to bytes output_message := make([]byte, 0, 200) @@ -152,7 +152,7 @@ var rc = [24]uint64{ // keccakF1600 applies the Keccak permutation to a 1600b-wide // state represented as a slice of 25 uint64s. -func keccakF1600(a *[25]uint64) { +func KeccakF1600(a *[25]uint64) { // Implementation translated from Keccak-inplace.c // in the keccak reference code. var t, bc0, bc1, bc2, bc3, bc4, d0, d1, d2, d3, d4 uint64 diff --git a/pkg/hints/hint_codes/keccak_hint_codes.go b/pkg/hints/hint_codes/keccak_hint_codes.go index 9a0efa25..86d5ca07 100644 --- a/pkg/hints/hint_codes/keccak_hint_codes.go +++ b/pkg/hints/hint_codes/keccak_hint_codes.go @@ -7,3 +7,11 @@ const UNSAFE_KECCAK_FINALIZE = "from eth_hash.auto import keccak\nkeccak_input = const COMPARE_BYTES_IN_WORD_NONDET = "memory[ap] = to_felt_or_relocatable(ids.n_bytes < ids.BYTES_IN_WORD)" const COMPARE_KECCAK_FULL_RATE_IN_BYTES_NONDET = "memory[ap] = to_felt_or_relocatable(ids.n_bytes >= ids.KECCAK_FULL_RATE_IN_BYTES)" + +const BLOCK_PERMUTATIONr = `from starkware.cairo.common.keccak_utils.keccak_utils import keccak_func +_keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS) +assert 0 <= _keccak_state_size_felts < 100 + +output_values = keccak_func(memory.get_range( + ids.keccak_ptr - _keccak_state_size_felts, _keccak_state_size_felts)) +segments.write_arg(ids.keccak_ptr, output_values)` diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index df716960..9b03249c 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -114,6 +114,8 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, return compareBytesInWordNondet(data.Ids, vm, constants) case COMPARE_KECCAK_FULL_RATE_IN_BYTES_NONDET: return compareKeccakFullRateInBytesNondet(data.Ids, vm, constants) + case BLOCK_PERMUTATIONr: + return blockPermutation(data.Ids, vm, constants) case UNSIGNED_DIV_REM: return unsignedDivRem(data.Ids, vm) case SIGNED_DIV_REM: diff --git a/pkg/hints/keccak_hints.go b/pkg/hints/keccak_hints.go index cad2846f..58afd186 100644 --- a/pkg/hints/keccak_hints.go +++ b/pkg/hints/keccak_hints.go @@ -2,6 +2,7 @@ package hints import ( "github.com/ebfe/keccak" + "github.com/lambdaclass/cairo-vm.go/pkg/builtins" . "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" @@ -136,8 +137,51 @@ func compareKeccakFullRateInBytesNondet(ids IdsManager, vm *VirtualMachine, cons return err } bytesInWord, err := ids.GetConst("KECCAK_FULL_RATE_IN_BYTES", constants) - if !(nBytes.Cmp(bytesInWord) == -1) { + if nBytes.Cmp(bytesInWord) != -1 { return vm.Segments.Memory.Insert(vm.RunContext.Ap, NewMaybeRelocatableFelt(FeltOne())) } return vm.Segments.Memory.Insert(vm.RunContext.Ap, NewMaybeRelocatableFelt(FeltZero())) } + +func blockPermutation(ids IdsManager, vm *VirtualMachine, constants *map[string]Felt) error { + const KECCAK_SIZE = 25 + keccakStateSizeFeltsFelt, err := ids.GetConst("KECCAK_STATE_SIZE_FELTS", constants) + if err != nil { + return err + } + if keccakStateSizeFeltsFelt.Cmp(FeltFromUint64(KECCAK_SIZE)) != 0 { + return errors.New("Assertion failed: _keccak_state_size_felts == 25") + } + + keccakPtr, err := ids.GetRelocatable("keccak_ptr", vm) + if err != nil { + return err + } + startPtr, err := keccakPtr.SubUint(KECCAK_SIZE) + if err != nil { + return err + } + inputFelt, err := vm.Segments.GetFeltRange(startPtr, KECCAK_SIZE) + if err != nil { + return err + } + + var inputU64 [KECCAK_SIZE]uint64 + for i := 0; i < KECCAK_SIZE; i++ { + val, err := inputFelt[i].ToU64() + if err != nil { + return err + } + inputU64[i] = val + } + + builtins.KeccakF1600(&inputU64) + + output := make([]MaybeRelocatable, 0, KECCAK_SIZE) + for i := 0; i < KECCAK_SIZE; i++ { + output = append(output, *NewMaybeRelocatableFelt(FeltFromUint64(inputU64[i]))) + } + + _, err = vm.Segments.LoadData(keccakPtr, &output) + return err +} From 81fc8be8d36175e20b3eca9d81872d5f020dc68c Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 20 Sep 2023 12:47:13 -0300 Subject: [PATCH 35/55] Fix test --- pkg/hints/hint_codes/keccak_hint_codes.go | 2 +- pkg/hints/hint_processor.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/hints/hint_codes/keccak_hint_codes.go b/pkg/hints/hint_codes/keccak_hint_codes.go index 86d5ca07..60f39928 100644 --- a/pkg/hints/hint_codes/keccak_hint_codes.go +++ b/pkg/hints/hint_codes/keccak_hint_codes.go @@ -8,7 +8,7 @@ const COMPARE_BYTES_IN_WORD_NONDET = "memory[ap] = to_felt_or_relocatable(ids.n_ const COMPARE_KECCAK_FULL_RATE_IN_BYTES_NONDET = "memory[ap] = to_felt_or_relocatable(ids.n_bytes >= ids.KECCAK_FULL_RATE_IN_BYTES)" -const BLOCK_PERMUTATIONr = `from starkware.cairo.common.keccak_utils.keccak_utils import keccak_func +const BLOCK_PERMUTATION = `from starkware.cairo.common.keccak_utils.keccak_utils import keccak_func _keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS) assert 0 <= _keccak_state_size_felts < 100 diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index 9b03249c..c8e80c52 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -114,7 +114,7 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, return compareBytesInWordNondet(data.Ids, vm, constants) case COMPARE_KECCAK_FULL_RATE_IN_BYTES_NONDET: return compareKeccakFullRateInBytesNondet(data.Ids, vm, constants) - case BLOCK_PERMUTATIONr: + case BLOCK_PERMUTATION: return blockPermutation(data.Ids, vm, constants) case UNSIGNED_DIV_REM: return unsignedDivRem(data.Ids, vm) From e402b4033adf66706e979b5d10c883b2aa658aa3 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 20 Sep 2023 12:51:32 -0300 Subject: [PATCH 36/55] Add test --- pkg/hints/keccak_hints_test.go | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/pkg/hints/keccak_hints_test.go b/pkg/hints/keccak_hints_test.go index bccdeecd..3fe61996 100644 --- a/pkg/hints/keccak_hints_test.go +++ b/pkg/hints/keccak_hints_test.go @@ -358,3 +358,34 @@ func TestCompareKeccakFullRateInBytesHintLt(t *testing.T) { t.Error("Wrong/No value inserted into ap") } } + +func TestBlockPermutationOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + keccak_ptr := vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "keccak_ptr": {NewMaybeRelocatableRelocatable(keccak_ptr.AddUint(25))}, + }, + vm, + ) + data := make([]MaybeRelocatable, 0, 25) + for i := 0; i < 25; i++ { + data = append(data, *NewMaybeRelocatableFelt(FeltZero())) + } + vm.Segments.LoadData(keccak_ptr, &data) + hintProcessor := CairoVmHintProcessor{} + constants := SetupConstantsForTest( + map[string]Felt{ + "KECCAK_STATE_SIZE_FELTS": FeltFromUint64(25), + }, + &idsManager) + hintData := any(HintData{ + Ids: idsManager, + Code: BLOCK_PERMUTATION, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, &constants, nil) + if err != nil { + t.Errorf("BLOCK_PERMUTATION hint test failed with error %s", err) + } +} From 3e4904ab683bd7607286efc27b7d6fa2633441ce Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 20 Sep 2023 14:51:46 -0300 Subject: [PATCH 37/55] Implement CAIRO_KECCAK_FINALIZE --- pkg/hints/hint_codes/keccak_hint_codes.go | 18 +++++++++++ pkg/hints/hint_processor.go | 4 +++ pkg/hints/keccak_hints.go | 38 +++++++++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/pkg/hints/hint_codes/keccak_hint_codes.go b/pkg/hints/hint_codes/keccak_hint_codes.go index 60f39928..5aa48b57 100644 --- a/pkg/hints/hint_codes/keccak_hint_codes.go +++ b/pkg/hints/hint_codes/keccak_hint_codes.go @@ -15,3 +15,21 @@ assert 0 <= _keccak_state_size_felts < 100 output_values = keccak_func(memory.get_range( ids.keccak_ptr - _keccak_state_size_felts, _keccak_state_size_felts)) segments.write_arg(ids.keccak_ptr, output_values)` + +const CAIRO_KECCAK_FINALIZE_V1 = `# Add dummy pairs of input and output. +_keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS) +_block_size = int(ids.BLOCK_SIZE) +assert 0 <= _keccak_state_size_felts < 100 +assert 0 <= _block_size < 10 +inp = [0] * _keccak_state_size_felts +padding = (inp + keccak_func(inp)) * _block_size +segments.write_arg(ids.keccak_ptr_end, padding)` + +const CAIRO_KECCAK_FINALIZE_V2 = `# Add dummy pairs of input and output. +_keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS) +_block_size = int(ids.BLOCK_SIZE) +assert 0 <= _keccak_state_size_felts < 100 +assert 0 <= _block_size < 1000 +inp = [0] * _keccak_state_size_felts +padding = (inp + keccak_func(inp)) * _block_size +segments.write_arg(ids.keccak_ptr_end, padding)` diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index c8e80c52..54771c81 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -116,6 +116,10 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, return compareKeccakFullRateInBytesNondet(data.Ids, vm, constants) case BLOCK_PERMUTATION: return blockPermutation(data.Ids, vm, constants) + case CAIRO_KECCAK_FINALIZE_V1: + return cairoKeccakFinalize(data.Ids, vm, constants, 100) + case CAIRO_KECCAK_FINALIZE_V2: + return cairoKeccakFinalize(data.Ids, vm, constants, 1000) case UNSIGNED_DIV_REM: return unsignedDivRem(data.Ids, vm) case SIGNED_DIV_REM: diff --git a/pkg/hints/keccak_hints.go b/pkg/hints/keccak_hints.go index 58afd186..5f889300 100644 --- a/pkg/hints/keccak_hints.go +++ b/pkg/hints/keccak_hints.go @@ -185,3 +185,41 @@ func blockPermutation(ids IdsManager, vm *VirtualMachine, constants *map[string] _, err = vm.Segments.LoadData(keccakPtr, &output) return err } + +func cairoKeccakFinalize(ids IdsManager, vm *VirtualMachine, constants *map[string]Felt, blockSizeLimit uint64) error { + const KECCAK_SIZE = 25 + keccakStateSizeFeltsFelt, err := ids.GetConst("KECCAK_STATE_SIZE_FELTS", constants) + if err != nil { + return err + } + if keccakStateSizeFeltsFelt.Cmp(FeltFromUint64(KECCAK_SIZE)) != 0 { + return errors.New("Assertion failed: _keccak_state_size_felts == 25") + } + + blockSizeFelt, err := ids.GetConst("BLOCK_SIZE", constants) + if err != nil { + return err + } + if blockSizeFelt.Cmp(FeltFromUint64(blockSizeLimit)) != -1 { + return errors.Errorf("assert 0 <= _block_size < %d", blockSizeLimit) + } + blockSize, _ := blockSizeFelt.ToU64() + var input [KECCAK_SIZE]uint64 + builtins.KeccakF1600(&input) + padding := make([]MaybeRelocatable, 0, KECCAK_SIZE*2) + for i := 0; i < KECCAK_SIZE; i++ { + padding[i] = *NewMaybeRelocatableFelt(FeltZero()) + } + for i := 0; i < KECCAK_SIZE; i++ { + padding[i+KECCAK_SIZE] = *NewMaybeRelocatableFelt(FeltFromUint64(input[i])) + } + for i := 0; i < int(blockSize); i++ { + padding = append(padding, padding[:25]...) + } + keccakPtrEnd, err := ids.GetRelocatable("keccak_end_ptr", vm) + if err != nil { + return err + } + _, err = vm.Segments.LoadData(keccakPtrEnd, &padding) + return err +} From 2ff101d7b63d40a9f53a0d99b9bf6306dfc10757 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 20 Sep 2023 15:17:23 -0300 Subject: [PATCH 38/55] Extract aliased constants --- pkg/parser/parser.go | 17 +++++++++-------- pkg/vm/program.go | 40 +++++++++++++++++++++++++++++++--------- pkg/vm/program_test.go | 29 +++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 17 deletions(-) diff --git a/pkg/parser/parser.go b/pkg/parser/parser.go index 46f9d415..2f3c2f6a 100644 --- a/pkg/parser/parser.go +++ b/pkg/parser/parser.go @@ -40,14 +40,15 @@ type DebugInfo struct { } type Identifier struct { - FullName string `json:"full_name"` - Members map[string]any `json:"members"` - Size int `json:"size"` - Decorators []string `json:"decorators"` - PC int `json:"pc"` - Type string `json:"type"` - CairoType string `json:"cairo_type"` - Value big.Int `json:"value"` + FullName string `json:"full_name"` + Members map[string]any `json:"members"` + Size int `json:"size"` + Decorators []string `json:"decorators"` + PC int `json:"pc"` + Type string `json:"type"` + CairoType string `json:"cairo_type"` + Value big.Int `json:"value"` + Destination string `json:"destination"` } type ApTrackingData struct { diff --git a/pkg/vm/program.go b/pkg/vm/program.go index b72707ee..d126d68c 100644 --- a/pkg/vm/program.go +++ b/pkg/vm/program.go @@ -7,14 +7,15 @@ import ( ) type Identifier struct { - FullName string - Members map[string]any - Size int - Decorators []string - PC int - Type string - CairoType string - Value lambdaworks.Felt + FullName string + Members map[string]any + Size int + Decorators []string + PC int + Type string + CairoType string + Value lambdaworks.Felt + Destination string } type Program struct { @@ -53,6 +54,7 @@ func DeserializeProgramJson(compiledProgram parser.CompiledJson) Program { programIdentifier.Type = identifier.Type programIdentifier.CairoType = identifier.CairoType programIdentifier.Value = lambdaworks.FeltFromDecString(identifier.Value.String()) + programIdentifier.Destination = identifier.Destination program.Identifiers[key] = programIdentifier } program.Hints = compiledProgram.Hints @@ -64,9 +66,29 @@ func DeserializeProgramJson(compiledProgram parser.CompiledJson) Program { func (p *Program) ExtractConstants() map[string]lambdaworks.Felt { constants := make(map[string]lambdaworks.Felt) for name, identifier := range p.Identifiers { - if identifier.Type == "const" { + switch identifier.Type { + case "const": constants[name] = identifier.Value + case "alias": + val, ok := searchConstFromAlias(identifier.Destination, &p.Identifiers) + if ok { + constants[name] = val + } } } + return constants } + +func searchConstFromAlias(destination string, identifiers *map[string]Identifier) (lambdaworks.Felt, bool) { + identifier, ok := (*identifiers)[destination] + if ok { + switch identifier.Type { + case "const": + return identifier.Value, true + case "alias": + return searchConstFromAlias(identifier.Destination, identifiers) + } + } + return lambdaworks.Felt{}, false +} diff --git a/pkg/vm/program_test.go b/pkg/vm/program_test.go index bf8d4fff..d361fe97 100644 --- a/pkg/vm/program_test.go +++ b/pkg/vm/program_test.go @@ -49,5 +49,34 @@ func TestExtractConstants(t *testing.T) { if !reflect.DeepEqual(program.ExtractConstants(), expectedConstants) { t.Errorf("Wrong Constants, expected %v, got %v", expectedConstants, program.ExtractConstants()) } +} +func TestExtractConstantsWithAliasedConstants(t *testing.T) { + program := vm.Program{ + Identifiers: map[string]vm.Identifier{ + "path.A": { + Value: lambdaworks.FeltFromUint64(7), + Type: "const", + }, + "path.b": { + Value: lambdaworks.FeltFromUint64(17), + Type: "label", + }, + "other_path.A": { + Destination: "path.A", + Type: "alias", + }, + "other_path.b": { + Destination: "path.b", + Type: "alias", + }, + }, + } + expectedConstants := map[string]lambdaworks.Felt{ + "path.A": lambdaworks.FeltFromUint64(7), + "other_path.A": lambdaworks.FeltFromUint64(7), + } + if !reflect.DeepEqual(program.ExtractConstants(), expectedConstants) { + t.Errorf("Wrong Constants, expected %v, got %v", expectedConstants, program.ExtractConstants()) + } } From 38b6f79134b4392ba93815b20bda72ded4fa38e9 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 20 Sep 2023 15:39:06 -0300 Subject: [PATCH 39/55] Fix index out of range --- pkg/hints/keccak_hints.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/hints/keccak_hints.go b/pkg/hints/keccak_hints.go index 5f889300..11f98312 100644 --- a/pkg/hints/keccak_hints.go +++ b/pkg/hints/keccak_hints.go @@ -206,12 +206,12 @@ func cairoKeccakFinalize(ids IdsManager, vm *VirtualMachine, constants *map[stri blockSize, _ := blockSizeFelt.ToU64() var input [KECCAK_SIZE]uint64 builtins.KeccakF1600(&input) - padding := make([]MaybeRelocatable, 0, KECCAK_SIZE*2) + padding := make([]MaybeRelocatable, 0, KECCAK_SIZE*2*blockSize) for i := 0; i < KECCAK_SIZE; i++ { - padding[i] = *NewMaybeRelocatableFelt(FeltZero()) + padding = append(padding, *NewMaybeRelocatableFelt(FeltZero())) } for i := 0; i < KECCAK_SIZE; i++ { - padding[i+KECCAK_SIZE] = *NewMaybeRelocatableFelt(FeltFromUint64(input[i])) + padding = append(padding, *NewMaybeRelocatableFelt(FeltFromUint64(input[i]))) } for i := 0; i < int(blockSize); i++ { padding = append(padding, padding[:25]...) From 9655a7e8939dd6985022a706a98397e35db4ea91 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 20 Sep 2023 15:40:43 -0300 Subject: [PATCH 40/55] Fix var name --- pkg/hints/keccak_hints.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/hints/keccak_hints.go b/pkg/hints/keccak_hints.go index 11f98312..d17e2a8c 100644 --- a/pkg/hints/keccak_hints.go +++ b/pkg/hints/keccak_hints.go @@ -216,10 +216,10 @@ func cairoKeccakFinalize(ids IdsManager, vm *VirtualMachine, constants *map[stri for i := 0; i < int(blockSize); i++ { padding = append(padding, padding[:25]...) } - keccakPtrEnd, err := ids.GetRelocatable("keccak_end_ptr", vm) + keccakEndPtr, err := ids.GetRelocatable("keccak_ptr_end", vm) if err != nil { return err } - _, err = vm.Segments.LoadData(keccakPtrEnd, &padding) + _, err = vm.Segments.LoadData(keccakEndPtr, &padding) return err } From 63df855873a64f52bc0ba3b0329ffb6efc6f71ca Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 20 Sep 2023 17:26:30 -0300 Subject: [PATCH 41/55] Make error more expressive --- pkg/vm/vm_core.go | 2 +- pkg/vm/vm_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/vm/vm_core.go b/pkg/vm/vm_core.go index 1c3804de..9b4a78b3 100644 --- a/pkg/vm/vm_core.go +++ b/pkg/vm/vm_core.go @@ -247,7 +247,7 @@ func (vm *VirtualMachine) OpcodeAssertions(instruction Instruction, operands Ope return &VirtualMachineError{"UnconstrainedResAssertEq"} } if !operands.Res.IsEqual(&operands.Dst) { - return &VirtualMachineError{"DiffAssertValues"} + return &VirtualMachineError{fmt.Sprintf("An ASSERT_EQ instruction failed: %s != %s.", operands.Res.ToString(), operands.Dst.ToString())} } case Call: new_rel := vm.RunContext.Pc.AddUint(instruction.Size()) diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 25ade31b..6351ae6b 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -645,7 +645,7 @@ func TestOpcodeAssertionsInstructionFailedRelocatables(t *testing.T) { testVm := vm.NewVirtualMachine() err := testVm.OpcodeAssertions(instruction, operands) - if err.Error() != "DiffAssertValues" { + if err == nil { t.Error("Assertion should error out with DiffAssertValues") } } From 41284f6e6fb223c8d1d62341b54cbd732617b31f Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 20 Sep 2023 17:45:43 -0300 Subject: [PATCH 42/55] Fix hint --- pkg/hints/keccak_hints.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/hints/keccak_hints.go b/pkg/hints/keccak_hints.go index d17e2a8c..c32605ef 100644 --- a/pkg/hints/keccak_hints.go +++ b/pkg/hints/keccak_hints.go @@ -214,7 +214,7 @@ func cairoKeccakFinalize(ids IdsManager, vm *VirtualMachine, constants *map[stri padding = append(padding, *NewMaybeRelocatableFelt(FeltFromUint64(input[i]))) } for i := 0; i < int(blockSize); i++ { - padding = append(padding, padding[:25]...) + padding = append(padding, padding[:50]...) } keccakEndPtr, err := ids.GetRelocatable("keccak_ptr_end", vm) if err != nil { From cb02d8aa0875a60f2d9a37bdd57179e104f810bc Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 20 Sep 2023 17:45:55 -0300 Subject: [PATCH 43/55] Add test, fix test --- pkg/vm/cairo_run/cairo_run_test.go | 8 ++++++++ pkg/vm/vm_test.go | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/pkg/vm/cairo_run/cairo_run_test.go b/pkg/vm/cairo_run/cairo_run_test.go index 1971a453..222b6153 100644 --- a/pkg/vm/cairo_run/cairo_run_test.go +++ b/pkg/vm/cairo_run/cairo_run_test.go @@ -311,3 +311,11 @@ func TestKeccakIntegrationTests(t *testing.T) { t.Errorf("Program execution failed with error: %s", err) } } + +func TestCairoKeccak(t *testing.T) { + cairoRunConfig := cairo_run.CairoRunConfig{DisableTracePadding: false, Layout: "all_cairo", ProofMode: false} + _, err := cairo_run.CairoRun("../../../cairo_programs/cairo_keccak.json", cairoRunConfig) + if err != nil { + t.Errorf("Program execution failed with error: %s", err) + } +} diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 6351ae6b..c2b5b8f1 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -615,7 +615,7 @@ func TestOpcodeAssertionsInstructionFailed(t *testing.T) { testVm := vm.NewVirtualMachine() err := testVm.OpcodeAssertions(instruction, operands) - if err.Error() != "DiffAssertValues" { + if err == nil { t.Error("Assertion should error out with DiffAssertValues") } From a6d59df7efa2872fa13f2ae66b16605e8badb668 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 20 Sep 2023 17:46:12 -0300 Subject: [PATCH 44/55] Add test files --- cairo_programs/cairo_keccak.cairo | 29 +++++ cairo_programs/keccak_integration_tests.cairo | 107 ++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 cairo_programs/cairo_keccak.cairo create mode 100644 cairo_programs/keccak_integration_tests.cairo diff --git a/cairo_programs/cairo_keccak.cairo b/cairo_programs/cairo_keccak.cairo new file mode 100644 index 00000000..8adcd515 --- /dev/null +++ b/cairo_programs/cairo_keccak.cairo @@ -0,0 +1,29 @@ +%builtins range_check bitwise + +from starkware.cairo.common.cairo_keccak.keccak import cairo_keccak, finalize_keccak +from starkware.cairo.common.uint256 import Uint256 +from starkware.cairo.common.cairo_builtins import BitwiseBuiltin +from starkware.cairo.common.alloc import alloc + +func main{range_check_ptr: felt, bitwise_ptr: BitwiseBuiltin*}() { + alloc_locals; + + let (keccak_ptr: felt*) = alloc(); + let keccak_ptr_start = keccak_ptr; + + let (inputs: felt*) = alloc(); + + assert inputs[0] = 8031924123371070792; + assert inputs[1] = 560229490; + + let n_bytes = 16; + + let (res: Uint256) = cairo_keccak{keccak_ptr=keccak_ptr}(inputs=inputs, n_bytes=n_bytes); + + assert res.low = 293431514620200399776069983710520819074; + assert res.high = 317109767021952548743448767588473366791; + + finalize_keccak(keccak_ptr_start=keccak_ptr_start, keccak_ptr_end=keccak_ptr); + + return (); +} diff --git a/cairo_programs/keccak_integration_tests.cairo b/cairo_programs/keccak_integration_tests.cairo new file mode 100644 index 00000000..7f476245 --- /dev/null +++ b/cairo_programs/keccak_integration_tests.cairo @@ -0,0 +1,107 @@ +%builtins range_check bitwise + +from starkware.cairo.common.keccak import unsafe_keccak, unsafe_keccak_finalize, KeccakState +from starkware.cairo.common.cairo_keccak.keccak import cairo_keccak, finalize_keccak +from starkware.cairo.common.keccak_utils.keccak_utils import keccak_add_uint256 +from starkware.cairo.common.alloc import alloc +from starkware.cairo.common.uint256 import Uint256 +from starkware.cairo.common.cairo_builtins import BitwiseBuiltin +from starkware.cairo.common.math import unsigned_div_rem + +func fill_array(array: felt*, base: felt, step: felt, array_length: felt, iter: felt) { + if (iter == array_length) { + return (); + } + assert array[iter] = base + step * iter; + return fill_array(array, base, step, array_length, iter + 1); +} + +func test_integration{range_check_ptr: felt, bitwise_ptr: BitwiseBuiltin*}(iter: felt, last: felt) { + alloc_locals; + if (iter == last) { + return (); + } + + let (data_1: felt*) = alloc(); + let data_len: felt = 15; + let chunk_len: felt = 5; + + fill_array(data_1, iter, iter + 1, data_len, 0); + + let (low_1: felt, high_1: felt) = unsafe_keccak(data_1, chunk_len); + let (low_2: felt, high_2: felt) = unsafe_keccak(data_1 + chunk_len, chunk_len); + let (low_3: felt, high_3: felt) = unsafe_keccak(data_1 + 2 * chunk_len, chunk_len); + + // With the results of unsafe_keccak, create an array to pass to unsafe_keccak_finalize + // through a KeccakState + let (data_2: felt*) = alloc(); + assert data_2[0] = low_1; + assert data_2[1] = high_1; + assert data_2[2] = low_2; + assert data_2[3] = high_2; + assert data_2[4] = low_3; + assert data_2[5] = high_3; + + let keccak_state: KeccakState = KeccakState(start_ptr=data_2, end_ptr=data_2 + 6); + let res_1: Uint256 = unsafe_keccak_finalize(keccak_state); + + let (data_3: felt*) = alloc(); + + // This is done to make sure that the numbers inserted in data_3 + // fit in a u64 + let (q, r) = unsigned_div_rem(res_1.low, 18446744073709551615); + assert data_3[0] = q; + let (q, r) = unsigned_div_rem(res_1.high, 18446744073709551615); + assert data_3[1] = q; + + let (keccak_ptr: felt*) = alloc(); + let keccak_ptr_start = keccak_ptr; + + let res_2: Uint256 = cairo_keccak{keccak_ptr=keccak_ptr}(data_3, 16); + + finalize_keccak(keccak_ptr_start=keccak_ptr_start, keccak_ptr_end=keccak_ptr); + + let (inputs) = alloc(); + let inputs_start = inputs; + keccak_add_uint256{inputs=inputs_start}(num=res_2, bigend=0); + + // These values are hardcoded for last = 10 + // Since we are dealing with hash functions and using the output of one of them + // as the input of the other, asserting only the last results of the iteration + // should be enough + if (iter == last - 1 and last == 10) { + assert res_2.low = 3896836249413878817054429671793519200; + assert res_2.high = 253424239110447628170109510737834198489; + + assert inputs[0] = 16681956707691293280; + assert inputs[1] = 211247916371739620; + assert inputs[2] = 6796127878994642393; + assert inputs[3] = 13738155530201662906; + } + + // These values are hardcoded for last = 100 + // This should be used for benchmarking. + if (iter == last - 1 and last == 100) { + assert res_2.low = 52798800345724801884797411011515944813; + assert res_2.high = 159010026777930121161844734347918361509; + + assert inputs[0] = 14656556134934286189; + assert inputs[1] = 2862228701973161639; + assert inputs[2] = 206697371206337445; + assert inputs[3] = 8619950823980503604; + } + + return test_integration{range_check_ptr=range_check_ptr, bitwise_ptr=bitwise_ptr}( + iter + 1, last + ); +} + +func run_test{range_check_ptr: felt, bitwise_ptr: BitwiseBuiltin*}(last: felt) { + test_integration(0, last); + return (); +} + +func main{range_check_ptr: felt, bitwise_ptr: BitwiseBuiltin*}() { + run_test(10); + return (); +} From e5b1356d7eeff8bfea7cebded98644ac98c3eda4 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 20 Sep 2023 18:14:17 -0300 Subject: [PATCH 45/55] Implement keccakWriteArgs --- pkg/hints/hint_codes/keccak_hint_codes.go | 3 +++ pkg/hints/hint_processor.go | 4 ++- pkg/hints/keccak_hints.go | 32 +++++++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/pkg/hints/hint_codes/keccak_hint_codes.go b/pkg/hints/hint_codes/keccak_hint_codes.go index 5aa48b57..9927b27b 100644 --- a/pkg/hints/hint_codes/keccak_hint_codes.go +++ b/pkg/hints/hint_codes/keccak_hint_codes.go @@ -33,3 +33,6 @@ assert 0 <= _block_size < 1000 inp = [0] * _keccak_state_size_felts padding = (inp + keccak_func(inp)) * _block_size segments.write_arg(ids.keccak_ptr_end, padding)` + +const KECCAK_WRITE_ARGS = `segments.write_arg(ids.inputs, [ids.low % 2 ** 64, ids.low // 2 ** 64]) +segments.write_arg(ids.inputs + 2, [ids.high % 2 ** 64, ids.high // 2 ** 64])` diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index 54771c81..a603c1f1 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -117,9 +117,11 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, case BLOCK_PERMUTATION: return blockPermutation(data.Ids, vm, constants) case CAIRO_KECCAK_FINALIZE_V1: - return cairoKeccakFinalize(data.Ids, vm, constants, 100) + return cairoKeccakFinalize(data.Ids, vm, constants, 10) case CAIRO_KECCAK_FINALIZE_V2: return cairoKeccakFinalize(data.Ids, vm, constants, 1000) + case KECCAK_WRITE_ARGS: + return keccakWriteArgs(data.Ids, vm) case UNSIGNED_DIV_REM: return unsignedDivRem(data.Ids, vm) case SIGNED_DIV_REM: diff --git a/pkg/hints/keccak_hints.go b/pkg/hints/keccak_hints.go index c32605ef..c2c6b6bf 100644 --- a/pkg/hints/keccak_hints.go +++ b/pkg/hints/keccak_hints.go @@ -1,6 +1,8 @@ package hints import ( + "math" + "github.com/ebfe/keccak" "github.com/lambdaclass/cairo-vm.go/pkg/builtins" . "github.com/lambdaclass/cairo-vm.go/pkg/hints/hint_utils" @@ -223,3 +225,33 @@ func cairoKeccakFinalize(ids IdsManager, vm *VirtualMachine, constants *map[stri _, err = vm.Segments.LoadData(keccakEndPtr, &padding) return err } + +func keccakWriteArgs(ids IdsManager, vm *VirtualMachine) error { + inputs, err := ids.GetRelocatable("inputs", vm) + if err != nil { + return err + } + low, err := ids.GetFelt("low", vm) + if err != nil { + return err + } + high, err := ids.GetFelt("high", vm) + if err != nil { + return err + } + low_args := []MaybeRelocatable{ + *NewMaybeRelocatableFelt(low.And(FeltFromUint64(math.MaxUint64))), + *NewMaybeRelocatableFelt(low.Shr(64)), + } + high_args := []MaybeRelocatable{ + *NewMaybeRelocatableFelt(high.And(FeltFromUint64(math.MaxUint64))), + *NewMaybeRelocatableFelt(high.Shr(64)), + } + + inputs, err = vm.Segments.LoadData(inputs, &low_args) + if err != nil { + return err + } + _, err = vm.Segments.LoadData(inputs, &high_args) + return err +} From c462e668cc071cfb4e5ef9ed73e7e2c8f98c1886 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 20 Sep 2023 18:53:53 -0300 Subject: [PATCH 46/55] Extend hint parsing --- pkg/hints/hint_utils/hint_reference.go | 12 ++++++++++++ pkg/hints/hint_utils/hint_reference_test.go | 14 ++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/pkg/hints/hint_utils/hint_reference.go b/pkg/hints/hint_utils/hint_reference.go index 27149cd6..4954cb91 100644 --- a/pkg/hints/hint_utils/hint_reference.go +++ b/pkg/hints/hint_utils/hint_reference.go @@ -204,6 +204,18 @@ func ParseHintReference(reference parser.Reference) HintReference { ValueType: valueType, } } + // Reference no dereference 2 offsets - + : cast(reg - off1 + off2, type) + _, err = fmt.Sscanf(valueString, "cast%c%c - %d + %d, %s", &off1Reg0, &off1Reg1, &off1, &off2, &valueType) + if err == nil { + off1Reg := getRegister(off1Reg0, off1Reg1) + return HintReference{ + ApTrackingData: reference.ApTrackingData, + Offset1: OffsetValue{ValueType: Reference, Register: off1Reg, Value: -off1}, + Offset2: OffsetValue{Value: off2}, + Dereference: dereference, + ValueType: valueType, + } + } // No matches (aka wrong format) return HintReference{ApTrackingData: reference.ApTrackingData} } diff --git a/pkg/hints/hint_utils/hint_reference_test.go b/pkg/hints/hint_utils/hint_reference_test.go index 868274dd..98a5b983 100644 --- a/pkg/hints/hint_utils/hint_reference_test.go +++ b/pkg/hints/hint_utils/hint_reference_test.go @@ -326,3 +326,17 @@ func TestParseHintDereferenceReferenceDoubleDerefBothOffOmitted(t *testing.T) { t.Errorf("Wrong parsed reference, %+v", ParseHintReference(reference)) } } + +func TestParseHintDereferenceValueMinusValPlusVal(t *testing.T) { + reference := parser.Reference{Value: "[cast(ap - 0 + (-1), felt*)]"} + expected := HintReference{ + Offset1: OffsetValue{ValueType: Reference, Value: 0, Dereference: false}, + Offset2: OffsetValue{ValueType: Value, Value: -1, Dereference: false}, + ValueType: "felt*", + Dereference: true, + } + + if ParseHintReference(reference) != expected { + t.Errorf("Wrong parsed reference, %+v", ParseHintReference(reference)) + } +} From 27abebe692a75ef111a2d7cc2e6a17577c8cf975 Mon Sep 17 00:00:00 2001 From: fmoletta <99273364+fmoletta@users.noreply.github.com> Date: Thu, 21 Sep 2023 11:38:13 -0300 Subject: [PATCH 47/55] Update program.go --- pkg/vm/program.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/vm/program.go b/pkg/vm/program.go index d126d68c..667fa3f6 100644 --- a/pkg/vm/program.go +++ b/pkg/vm/program.go @@ -76,7 +76,6 @@ func (p *Program) ExtractConstants() map[string]lambdaworks.Felt { } } } - return constants } From cacb6608d469e0a4f93bd0a60ae92aab58ff8504 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 21 Sep 2023 15:37:13 -0300 Subject: [PATCH 48/55] Fix DivCeil --- pkg/utils/math_utils.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/utils/math_utils.go b/pkg/utils/math_utils.go index 18adc42b..0950ec22 100644 --- a/pkg/utils/math_utils.go +++ b/pkg/utils/math_utils.go @@ -44,5 +44,9 @@ func MaxInt(x int, y int) int { } func DivCeil(x uint, y uint) uint { - return 1 + (x-1)/y + q := x / y + if q*y < x { + q++ + } + return q } From 81c86a0cdb893d0f723d69f7d7fd44fd276378ca Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 21 Sep 2023 15:37:43 -0300 Subject: [PATCH 49/55] Add integration test --- cairo_programs/keccak_add_uint256.cairo | 30 +++++++++++++++++++++++++ pkg/vm/cairo_run/cairo_run_test.go | 4 ++++ 2 files changed, 34 insertions(+) create mode 100644 cairo_programs/keccak_add_uint256.cairo diff --git a/cairo_programs/keccak_add_uint256.cairo b/cairo_programs/keccak_add_uint256.cairo new file mode 100644 index 00000000..3014c9a1 --- /dev/null +++ b/cairo_programs/keccak_add_uint256.cairo @@ -0,0 +1,30 @@ +%builtins output range_check bitwise + +from starkware.cairo.common.keccak_utils.keccak_utils import keccak_add_uint256 +from starkware.cairo.common.uint256 import Uint256 +from starkware.cairo.common.cairo_builtins import BitwiseBuiltin +from starkware.cairo.common.alloc import alloc +from starkware.cairo.common.serialize import serialize_word + +func main{output_ptr: felt*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() { + alloc_locals; + + let (inputs) = alloc(); + let inputs_start = inputs; + + let num = Uint256(34623634663146736, 598249824422424658356); + + keccak_add_uint256{inputs=inputs_start}(num=num, bigend=0); + + assert inputs[0] = 34623634663146736; + assert inputs[1] = 0; + assert inputs[2] = 7954014063719006644; + assert inputs[3] = 32; + + serialize_word(inputs[0]); + serialize_word(inputs[1]); + serialize_word(inputs[2]); + serialize_word(inputs[3]); + + return (); +} \ No newline at end of file diff --git a/pkg/vm/cairo_run/cairo_run_test.go b/pkg/vm/cairo_run/cairo_run_test.go index a8a74a9f..1d5f99b8 100644 --- a/pkg/vm/cairo_run/cairo_run_test.go +++ b/pkg/vm/cairo_run/cairo_run_test.go @@ -213,3 +213,7 @@ func TestCairoKeccak(t *testing.T) { t.Errorf("Program execution failed with error: %s", err) } } + +func TestKeccakAddUint256(t *testing.T) { + testProgram("keccak_add_uint256", t) +} From 54f867d4a47b23dbeb115cd461bd1a8c28ba5767 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 21 Sep 2023 15:45:27 -0300 Subject: [PATCH 50/55] Fix DivCeil --- pkg/utils/math_utils.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/utils/math_utils.go b/pkg/utils/math_utils.go index 18adc42b..0950ec22 100644 --- a/pkg/utils/math_utils.go +++ b/pkg/utils/math_utils.go @@ -44,5 +44,9 @@ func MaxInt(x int, y int) int { } func DivCeil(x uint, y uint) uint { - return 1 + (x-1)/y + q := x / y + if q*y < x { + q++ + } + return q } From b4312d71fd48901b79b8b14c489dfea049068a5b Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 21 Sep 2023 15:47:49 -0300 Subject: [PATCH 51/55] v2 --- pkg/utils/math_utils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/utils/math_utils.go b/pkg/utils/math_utils.go index 0950ec22..54000f2d 100644 --- a/pkg/utils/math_utils.go +++ b/pkg/utils/math_utils.go @@ -45,7 +45,7 @@ func MaxInt(x int, y int) int { func DivCeil(x uint, y uint) uint { q := x / y - if q*y < x { + if x%y != 0 { q++ } return q From 5c2cd638a95cc917f174466ca1740fff62acc818 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 21 Sep 2023 16:50:24 -0300 Subject: [PATCH 52/55] Fix bug --- pkg/hints/keccak_hints.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/hints/keccak_hints.go b/pkg/hints/keccak_hints.go index c2c6b6bf..d6a775ce 100644 --- a/pkg/hints/keccak_hints.go +++ b/pkg/hints/keccak_hints.go @@ -215,7 +215,7 @@ func cairoKeccakFinalize(ids IdsManager, vm *VirtualMachine, constants *map[stri for i := 0; i < KECCAK_SIZE; i++ { padding = append(padding, *NewMaybeRelocatableFelt(FeltFromUint64(input[i]))) } - for i := 0; i < int(blockSize); i++ { + for i := 0; i < int(blockSize-1); i++ { padding = append(padding, padding[:50]...) } keccakEndPtr, err := ids.GetRelocatable("keccak_ptr_end", vm) From d1ed22c8403fb795e8f9ba6b1695287b548e7791 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 21 Sep 2023 16:50:43 -0300 Subject: [PATCH 53/55] Fix bug but better --- pkg/hints/keccak_hints.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/hints/keccak_hints.go b/pkg/hints/keccak_hints.go index d6a775ce..e3b4382b 100644 --- a/pkg/hints/keccak_hints.go +++ b/pkg/hints/keccak_hints.go @@ -215,7 +215,7 @@ func cairoKeccakFinalize(ids IdsManager, vm *VirtualMachine, constants *map[stri for i := 0; i < KECCAK_SIZE; i++ { padding = append(padding, *NewMaybeRelocatableFelt(FeltFromUint64(input[i]))) } - for i := 0; i < int(blockSize-1); i++ { + for i := 1; i < int(blockSize); i++ { padding = append(padding, padding[:50]...) } keccakEndPtr, err := ids.GetRelocatable("keccak_ptr_end", vm) From 81b280afbb5a6ede4be90c0eb85ff0264bc73caf Mon Sep 17 00:00:00 2001 From: fmoletta <99273364+fmoletta@users.noreply.github.com> Date: Thu, 21 Sep 2023 16:52:18 -0300 Subject: [PATCH 54/55] Update keccak_add_uint256.cairo --- cairo_programs/keccak_add_uint256.cairo | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cairo_programs/keccak_add_uint256.cairo b/cairo_programs/keccak_add_uint256.cairo index 3014c9a1..34ef99a9 100644 --- a/cairo_programs/keccak_add_uint256.cairo +++ b/cairo_programs/keccak_add_uint256.cairo @@ -27,4 +27,5 @@ func main{output_ptr: felt*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() { serialize_word(inputs[3]); return (); -} \ No newline at end of file +} + From f263abd81936ba87bc70e70d50cbf4302201fc33 Mon Sep 17 00:00:00 2001 From: fmoletta <99273364+fmoletta@users.noreply.github.com> Date: Thu, 21 Sep 2023 16:53:46 -0300 Subject: [PATCH 55/55] Update cairo_run_test.go --- pkg/vm/cairo_run/cairo_run_test.go | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/pkg/vm/cairo_run/cairo_run_test.go b/pkg/vm/cairo_run/cairo_run_test.go index 1d5f99b8..749b1433 100644 --- a/pkg/vm/cairo_run/cairo_run_test.go +++ b/pkg/vm/cairo_run/cairo_run_test.go @@ -199,19 +199,11 @@ func TestSplitIntHint(t *testing.T) { } func TestKeccakIntegrationTests(t *testing.T) { - cairoRunConfig := cairo_run.CairoRunConfig{DisableTracePadding: false, Layout: "all_cairo", ProofMode: false} - _, err := cairo_run.CairoRun("../../../cairo_programs/keccak_integration_tests.json", cairoRunConfig) - if err != nil { - t.Errorf("Program execution failed with error: %s", err) - } + testProgram("keccak_integration_tests", t) } func TestCairoKeccak(t *testing.T) { - cairoRunConfig := cairo_run.CairoRunConfig{DisableTracePadding: false, Layout: "all_cairo", ProofMode: false} - _, err := cairo_run.CairoRun("../../../cairo_programs/cairo_keccak.json", cairoRunConfig) - if err != nil { - t.Errorf("Program execution failed with error: %s", err) - } + testProgram("cairo_keccak", t) } func TestKeccakAddUint256(t *testing.T) {