From 05e796e5addbb7f793c918f2ace4af922e4d6f19 Mon Sep 17 00:00:00 2001 From: toni-calvin Date: Fri, 8 Sep 2023 15:20:57 +0200 Subject: [PATCH 01/74] First commit --- pkg/types/exec_scope_test.go | 0 pkg/types/exec_scopes.go | 54 ++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 pkg/types/exec_scope_test.go create mode 100644 pkg/types/exec_scopes.go diff --git a/pkg/types/exec_scope_test.go b/pkg/types/exec_scope_test.go new file mode 100644 index 00000000..e69de29b diff --git a/pkg/types/exec_scopes.go b/pkg/types/exec_scopes.go new file mode 100644 index 00000000..8585e9c4 --- /dev/null +++ b/pkg/types/exec_scopes.go @@ -0,0 +1,54 @@ +package types + +type ExecutionScopes struct { + data []map[string]interface{} +} + +func NewExecutionScopes() *ExecutionScopes { + return &ExecutionScopes{ + data: make([]map[string]interface{}, 0), + } +} + +func (es *ExecutionScopes) enterScope(newScopeLocals map[string]interface{}) { + es.data = append(es.data, newScopeLocals) +} + +func (es *ExecutionScopes) exitScope() error { + if len(es.data) == 1 { + return ExecutionScopesError("Cannot exit main scope.") + } + es.data = es.data[:len(es.data)-1] + return nil +} + +func (es *ExecutionScopes) getLocalVariablesMut() (*map[string]interface{}, error) { + if len(es.data) > 0 { + return &es.data[len(es.data)-1], nil + } + return nil, ExecutionScopesError("Every enter_scope() requires a corresponding exit_scope().") +} + +func (es *ExecutionScopes) getLocalVariables() (map[string]interface{}, error) { + if len(es.data) > 0 { + return es.data[len(es.data)-1], nil + } + return nil, ExecutionScopesError("Every enter_scope() requires a corresponding exit_scope().") +} + +func (es *ExecutionScopes) deleteVariable(varName string) { + locals, err := es.getLocalVariablesMut() + if err != nil { + return + } + delete(*locals, varName) + +} + +func (es *ExecutionScopes) assignOrUpdateVariable(varName string, varValue interface{}) { + locals, err := es.getLocalVariablesMut() + if err != nil { + return + } + (*locals)[varName] = varValue +} From 1ae3ab0ff277cad5576395a0eed0dc05e5aaf48a Mon Sep 17 00:00:00 2001 From: toni-calvin Date: Tue, 12 Sep 2023 11:24:22 +0200 Subject: [PATCH 02/74] Add testing --- pkg/types/exec_scope_test.go | 35 ++++++++++++++++++++++++ pkg/types/exec_scopes.go | 53 ++++++++++++++++++++++++++++++------ 2 files changed, 80 insertions(+), 8 deletions(-) diff --git a/pkg/types/exec_scope_test.go b/pkg/types/exec_scope_test.go index e69de29b..5a616e87 100644 --- a/pkg/types/exec_scope_test.go +++ b/pkg/types/exec_scope_test.go @@ -0,0 +1,35 @@ +package types_test + +import ( + "testing" + + "github.com/lambdaclass/cairo-vm.go/pkg/lambdaworks" + "github.com/lambdaclass/cairo-vm.go/pkg/types" +) + +func TestInitializeExecutionScopes(t *testing.T) { + scopes := types.NewExecutionScopes() + if len(scopes.Data()) != 1 { + t.Errorf("TestInitializeExecutionScopes failed, expected length: %d, got: %d", 1, len((scopes.Data()))) + } +} + +func TestGetLocalVariables(t *testing.T) { + scope := make(map[string]interface{}) + scope["k"] = lambdaworks.FeltOne() + + scopes := types.NewExecutionScopes() + scopes.EnterScope(scope) + + result, err := scopes.Get("key") + if err != nil { + t.Errorf("TestGetLocalVariables failed with error: %s", err) + + } + f_res := result.(lambdaworks.Felt) + expected := lambdaworks.FeltOne() + if expected != f_res { + t.Errorf("TestGetLocalVariables failed, expected: %s, got: %s", expected.ToSignedFeltString(), f_res.ToSignedFeltString()) + } + +} diff --git a/pkg/types/exec_scopes.go b/pkg/types/exec_scopes.go index 8585e9c4..a2093b53 100644 --- a/pkg/types/exec_scopes.go +++ b/pkg/types/exec_scopes.go @@ -1,22 +1,35 @@ package types +import ( + "github.com/pkg/errors" +) + type ExecutionScopes struct { data []map[string]interface{} } +func ExecutionScopesError(err error) error { + return errors.Wrapf(err, "Execution scopes error") +} + func NewExecutionScopes() *ExecutionScopes { return &ExecutionScopes{ - data: make([]map[string]interface{}, 0), + data: make([]map[string]interface{}, 1), } } -func (es *ExecutionScopes) enterScope(newScopeLocals map[string]interface{}) { +func (es *ExecutionScopes) Data() []map[string]interface{} { + return es.data +} + +func (es *ExecutionScopes) EnterScope(newScopeLocals map[string]interface{}) { es.data = append(es.data, newScopeLocals) + } -func (es *ExecutionScopes) exitScope() error { +func (es *ExecutionScopes) ExitScope() error { if len(es.data) == 1 { - return ExecutionScopesError("Cannot exit main scope.") + return ExecutionScopesError(errors.Errorf("Cannot exit main scope.")) } es.data = es.data[:len(es.data)-1] return nil @@ -26,17 +39,17 @@ func (es *ExecutionScopes) getLocalVariablesMut() (*map[string]interface{}, erro if len(es.data) > 0 { return &es.data[len(es.data)-1], nil } - return nil, ExecutionScopesError("Every enter_scope() requires a corresponding exit_scope().") + return nil, ExecutionScopesError(errors.Errorf("Every enter_scope() requires a corresponding exit_scope().")) } func (es *ExecutionScopes) getLocalVariables() (map[string]interface{}, error) { if len(es.data) > 0 { return es.data[len(es.data)-1], nil } - return nil, ExecutionScopesError("Every enter_scope() requires a corresponding exit_scope().") + return nil, ExecutionScopesError(errors.Errorf("Every enter_scope() requires a corresponding exit_scope().")) } -func (es *ExecutionScopes) deleteVariable(varName string) { +func (es *ExecutionScopes) DeleteVariable(varName string) { locals, err := es.getLocalVariablesMut() if err != nil { return @@ -45,10 +58,34 @@ func (es *ExecutionScopes) deleteVariable(varName string) { } -func (es *ExecutionScopes) assignOrUpdateVariable(varName string, varValue interface{}) { +func (es *ExecutionScopes) AssignOrUpdateVariable(varName string, varValue interface{}) { locals, err := es.getLocalVariablesMut() if err != nil { return } (*locals)[varName] = varValue } + +func (es *ExecutionScopes) Get(varName string) (interface{}, error) { + locals, err := es.getLocalVariables() + if err != nil { + return nil, err + } + val, prs := locals[varName] + if !prs { + return nil, ExecutionScopesError(errors.Errorf("Variable %s not in scope", varName)) + } + return val, nil +} + +func (es *ExecutionScopes) GetRef(varName string) (*interface{}, error) { + locals, err := es.getLocalVariables() + if err != nil { + return nil, err + } + val, prs := locals[varName] + if !prs { + return nil, ExecutionScopesError(errors.Errorf("Variable %s not in scope", varName)) + } + return &val, nil +} From 548a74187ae93cfe54ab7c299945ccf0dd284144 Mon Sep 17 00:00:00 2001 From: toni-calvin Date: Tue, 12 Sep 2023 11:29:05 +0200 Subject: [PATCH 03/74] Add test --- pkg/types/exec_scope_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/types/exec_scope_test.go b/pkg/types/exec_scope_test.go index 5a616e87..47d00ca8 100644 --- a/pkg/types/exec_scope_test.go +++ b/pkg/types/exec_scope_test.go @@ -21,7 +21,7 @@ func TestGetLocalVariables(t *testing.T) { scopes := types.NewExecutionScopes() scopes.EnterScope(scope) - result, err := scopes.Get("key") + result, err := scopes.Get("k") if err != nil { t.Errorf("TestGetLocalVariables failed with error: %s", err) @@ -31,5 +31,4 @@ func TestGetLocalVariables(t *testing.T) { if expected != f_res { t.Errorf("TestGetLocalVariables failed, expected: %s, got: %s", expected.ToSignedFeltString(), f_res.ToSignedFeltString()) } - } From d4195c921a9cdc4ffa251d52fde1b5a19bd89dae Mon Sep 17 00:00:00 2001 From: toni-calvin Date: Tue, 12 Sep 2023 11:46:37 +0200 Subject: [PATCH 04/74] Add tests --- pkg/types/exec_scope_test.go | 65 ++++++++++++++++++++++++++++++++++++ pkg/types/exec_scopes.go | 12 ++++--- 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/pkg/types/exec_scope_test.go b/pkg/types/exec_scope_test.go index 47d00ca8..c6e51da7 100644 --- a/pkg/types/exec_scope_test.go +++ b/pkg/types/exec_scope_test.go @@ -32,3 +32,68 @@ func TestGetLocalVariables(t *testing.T) { t.Errorf("TestGetLocalVariables failed, expected: %s, got: %s", expected.ToSignedFeltString(), f_res.ToSignedFeltString()) } } + +func TestEnterNewScope(t *testing.T) { + scope := make(map[string]interface{}) + scope["a"] = lambdaworks.FeltOne() + + scopes := types.NewExecutionScopes() + scopes.EnterScope(scope) + + locals, err := scopes.GetLocalVariables() + if err != nil { + t.Errorf("TestEnterNewScope failed with error: %s", err) + } + + if len(locals) != 1 { + t.Errorf("TestEnterNewScope failed, expected length: %d, got: %d", 1, len(locals)) + } + + result, err := scopes.Get("a") + if err != nil { + t.Errorf("TestEnterNewScope failed with error: %s", err) + + } + + f_res := result.(lambdaworks.Felt) + expected := lambdaworks.FeltOne() + if expected != f_res { + t.Errorf("TestEnterNewScope failed, expected: %s, got: %s", expected.ToSignedFeltString(), f_res.ToSignedFeltString()) + } + + snd_scope := make(map[string]interface{}) + snd_scope["b"] = lambdaworks.FeltZero() + scopes.EnterScope(snd_scope) + + locals, err = scopes.GetLocalVariables() + if err != nil { + t.Errorf("TestEnterNewScope failed with error: %s", err) + } + + if len(locals) != 1 { + t.Errorf("TestEnterNewScope failed, expected length: %d, got: %d", 1, len(locals)) + } + + // check variable a can't be accessed now + _, err = scopes.Get("a") + if err.Error() != types.ErrVariableNotInScope("a").Error() { + t.Errorf("TestEnterNewScope should fail with error: %s", types.ErrVariableNotInScope("a").Error()) + + } + + result, err = scopes.Get("b") + if err != nil { + t.Errorf("TestEnterNewScope failed with error: %s", err) + } + + f_res = result.(lambdaworks.Felt) + expected = lambdaworks.FeltZero() + if expected != f_res { + t.Errorf("TestEnterNewScope failed, expected: %s, got: %s", expected.ToSignedFeltString(), f_res.ToSignedFeltString()) + } + +} + +func TestExitScopeTest(t *testing.T) { + +} diff --git a/pkg/types/exec_scopes.go b/pkg/types/exec_scopes.go index a2093b53..2acb0c78 100644 --- a/pkg/types/exec_scopes.go +++ b/pkg/types/exec_scopes.go @@ -12,6 +12,10 @@ func ExecutionScopesError(err error) error { return errors.Wrapf(err, "Execution scopes error") } +func ErrVariableNotInScope(varName string) error { + return ExecutionScopesError(errors.Errorf("Variable %s not in scope", varName)) +} + func NewExecutionScopes() *ExecutionScopes { return &ExecutionScopes{ data: make([]map[string]interface{}, 1), @@ -42,7 +46,7 @@ func (es *ExecutionScopes) getLocalVariablesMut() (*map[string]interface{}, erro return nil, ExecutionScopesError(errors.Errorf("Every enter_scope() requires a corresponding exit_scope().")) } -func (es *ExecutionScopes) getLocalVariables() (map[string]interface{}, error) { +func (es *ExecutionScopes) GetLocalVariables() (map[string]interface{}, error) { if len(es.data) > 0 { return es.data[len(es.data)-1], nil } @@ -67,19 +71,19 @@ func (es *ExecutionScopes) AssignOrUpdateVariable(varName string, varValue inter } func (es *ExecutionScopes) Get(varName string) (interface{}, error) { - locals, err := es.getLocalVariables() + locals, err := es.GetLocalVariables() if err != nil { return nil, err } val, prs := locals[varName] if !prs { - return nil, ExecutionScopesError(errors.Errorf("Variable %s not in scope", varName)) + return nil, ErrVariableNotInScope(varName) } return val, nil } func (es *ExecutionScopes) GetRef(varName string) (*interface{}, error) { - locals, err := es.getLocalVariables() + locals, err := es.GetLocalVariables() if err != nil { return nil, err } From 2cfe447c43a78976704e79b22582a11cd1b3e9c9 Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 12 Sep 2023 18:41:13 -0300 Subject: [PATCH 05/74] Implement `ASSERT_NOT_EQUAL` hint --- pkg/hints/math_hint_codes.go | 2 ++ pkg/hints/math_hints.go | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/pkg/hints/math_hint_codes.go b/pkg/hints/math_hint_codes.go index 42f8c6de..51159030 100644 --- a/pkg/hints/math_hint_codes.go +++ b/pkg/hints/math_hint_codes.go @@ -3,3 +3,5 @@ package hints const ASSERT_NN = "from starkware.cairo.common.math_utils import assert_integer\nassert_integer(ids.a)\nassert 0 <= ids.a % PRIME < range_check_builtin.bound, f'a = {ids.a} is out of range.'" const ASSERT_NOT_ZERO = "from starkware.cairo.common.math_utils import assert_integer\nassert_integer(ids.value)\nassert ids.value % PRIME != 0, f'assert_not_zero failed: {ids.value} = 0.'" + +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}.'" diff --git a/pkg/hints/math_hints.go b/pkg/hints/math_hints.go index b58a1278..4d7a3212 100644 --- a/pkg/hints/math_hints.go +++ b/pkg/hints/math_hints.go @@ -44,3 +44,29 @@ func assert_not_zero(ids IdsManager, vm *VirtualMachine) error { } return nil } + +func assert_not_equal(ids *IdsManager, vm *VirtualMachine) error { + // Extract Ids Variables + a, err := ids.Get("a", vm) + if err != nil { + return err + } + b, err := ids.Get("b", vm) + if err != nil { + return err + } + // Hint Logic + a_rel, a_is_rel := a.GetRelocatable() + b_rel, b_is_rel := b.GetRelocatable() + if !((a_is_rel && b_is_rel && a_rel.SegmentIndex == b_rel.SegmentIndex) || (!a_is_rel && !b_is_rel)) { + return errors.Errorf("assert_not_equal failed: non-comparable values: %v, %v.", a, b) + } + diff, err := a.Sub(*b) + if err != nil { + return err + } + if diff.IsZero() { + return errors.Errorf("assert_not_equal failed: %v = %v.", a, b) + } + return nil +} From 8a25e3dfc9fb3414048372e6ea6eea2df7681484 Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 12 Sep 2023 18:53:26 -0300 Subject: [PATCH 06/74] Add unit tests --- pkg/hints/hint_processor.go | 2 + pkg/hints/math_hints.go | 2 +- pkg/hints/math_hints_test.go | 126 +++++++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 1 deletion(-) diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index 6a91f535..4e2e7a2c 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -42,6 +42,8 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, return assert_nn(data.Ids, vm) case ASSERT_NOT_ZERO: return assert_not_zero(data.Ids, vm) + case ASSERT_NOT_EQUAL: + return assert_not_equal(data.Ids, vm) default: return errors.New("Unknown Hint") } diff --git a/pkg/hints/math_hints.go b/pkg/hints/math_hints.go index 4d7a3212..1254ebcc 100644 --- a/pkg/hints/math_hints.go +++ b/pkg/hints/math_hints.go @@ -45,7 +45,7 @@ func assert_not_zero(ids IdsManager, vm *VirtualMachine) error { return nil } -func assert_not_equal(ids *IdsManager, vm *VirtualMachine) error { +func assert_not_equal(ids IdsManager, vm *VirtualMachine) error { // Extract Ids Variables a, err := ids.Get("a", vm) if err != nil { diff --git a/pkg/hints/math_hints_test.go b/pkg/hints/math_hints_test.go index 4574bfa3..3dc387a0 100644 --- a/pkg/hints/math_hints_test.go +++ b/pkg/hints/math_hints_test.go @@ -89,3 +89,129 @@ func TestAssertNotZeroHintFail(t *testing.T) { t.Errorf("ASSERT_NOT_ZERO hint should have failed") } } + +func TestAssertNotEqualHintNonComparableDiffType(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "a": {NewMaybeRelocatableFelt(FeltFromUint64(0))}, + "b": {NewMaybeRelocatableRelocatable(NewRelocatable(0, 0))}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: ASSERT_NOT_EQUAL, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil) + if err == nil { + t.Errorf("ASSERT_NOT_EQUAL hint should have failed") + } +} + +func TestAssertNotEqualHintNonComparableDiffIndex(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "a": {NewMaybeRelocatableRelocatable(NewRelocatable(1, 0))}, + "b": {NewMaybeRelocatableRelocatable(NewRelocatable(0, 0))}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: ASSERT_NOT_EQUAL, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil) + if err == nil { + t.Errorf("ASSERT_NOT_EQUAL hint should have failed") + } +} + +func TestAssertNotEqualHintEqualRelocatables(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "a": {NewMaybeRelocatableRelocatable(NewRelocatable(0, 0))}, + "b": {NewMaybeRelocatableRelocatable(NewRelocatable(0, 0))}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: ASSERT_NOT_EQUAL, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil) + if err == nil { + t.Errorf("ASSERT_NOT_EQUAL hint should have failed") + } +} + +func TestAssertNotEqualHintEqualFelts(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "a": {NewMaybeRelocatableFelt(FeltFromUint64(9))}, + "b": {NewMaybeRelocatableFelt(FeltFromUint64(9))}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: ASSERT_NOT_EQUAL, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil) + if err == nil { + t.Errorf("ASSERT_NOT_EQUAL hint should have failed") + } +} + +func TestAssertNotEqualHintOkFelts(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "a": {NewMaybeRelocatableFelt(FeltFromUint64(9))}, + "b": {NewMaybeRelocatableFelt(FeltFromUint64(7))}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: ASSERT_NOT_EQUAL, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil) + if err != nil { + t.Errorf("ASSERT_NOT_EQUAL hint failed with error: %s", err) + } +} + +func TestAssertNotEqualHintOkRelocatables(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "a": {NewMaybeRelocatableRelocatable(NewRelocatable(1, 9))}, + "b": {NewMaybeRelocatableRelocatable(NewRelocatable(1, 7))}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: ASSERT_NOT_EQUAL, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil) + if err != nil { + t.Errorf("ASSERT_NOT_EQUAL hint failed with error: %s", err) + } +} From d0c30245791a5d2dbdf14a8c482822fac68a388b Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 12 Sep 2023 19:05:49 -0300 Subject: [PATCH 07/74] Add integration test --- cairo_programs/assert_not_equal.cairo | 10 ++++++++++ pkg/hints/math_hint_codes.go | 2 +- pkg/vm/cairo_run/cairo_run_test.go | 7 +++++++ 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 cairo_programs/assert_not_equal.cairo diff --git a/cairo_programs/assert_not_equal.cairo b/cairo_programs/assert_not_equal.cairo new file mode 100644 index 00000000..916bc002 --- /dev/null +++ b/cairo_programs/assert_not_equal.cairo @@ -0,0 +1,10 @@ +%builtins output + +from starkware.cairo.common.math import assert_not_equal + +func main{output_ptr: felt*}() { + assert_not_equal(17, 7); + assert_not_equal(cast(output_ptr, felt), cast(output_ptr + 1, felt)); + assert_not_equal(-1, 1); + return (); +} \ No newline at end of file diff --git a/pkg/hints/math_hint_codes.go b/pkg/hints/math_hint_codes.go index 51159030..e5f3fc61 100644 --- a/pkg/hints/math_hint_codes.go +++ b/pkg/hints/math_hint_codes.go @@ -4,4 +4,4 @@ const ASSERT_NN = "from starkware.cairo.common.math_utils import assert_integer\ const ASSERT_NOT_ZERO = "from starkware.cairo.common.math_utils import assert_integer\nassert_integer(ids.value)\nassert ids.value % PRIME != 0, f'assert_not_zero failed: {ids.value} = 0.'" -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 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}.'" diff --git a/pkg/vm/cairo_run/cairo_run_test.go b/pkg/vm/cairo_run/cairo_run_test.go index 1e8f0258..84feca41 100644 --- a/pkg/vm/cairo_run/cairo_run_test.go +++ b/pkg/vm/cairo_run/cairo_run_test.go @@ -93,3 +93,10 @@ func TestAssertNotZeroHint(t *testing.T) { t.Errorf("Program execution failed with error: %s", err) } } + +func TestAssertNotEqualHint(t *testing.T) { + _, err := cairo_run.CairoRun("../../../cairo_programs/assert_not_equal.json", "small", false) + if err != nil { + t.Errorf("Program execution failed with error: %s", err) + } +} From 02d6a1d3e9f7f89a89bbb2f2155d3edf8434658f Mon Sep 17 00:00:00 2001 From: toni-calvin Date: Wed, 13 Sep 2023 06:40:22 +0200 Subject: [PATCH 08/74] Add tests --- pkg/types/exec_scope_test.go | 173 ++++++++++++++++++++++++++++++++++- pkg/types/exec_scopes.go | 10 +- 2 files changed, 178 insertions(+), 5 deletions(-) diff --git a/pkg/types/exec_scope_test.go b/pkg/types/exec_scope_test.go index c6e51da7..de8182b7 100644 --- a/pkg/types/exec_scope_test.go +++ b/pkg/types/exec_scope_test.go @@ -1,6 +1,7 @@ package types_test import ( + "errors" "testing" "github.com/lambdaclass/cairo-vm.go/pkg/lambdaworks" @@ -76,7 +77,8 @@ func TestEnterNewScope(t *testing.T) { // check variable a can't be accessed now _, err = scopes.Get("a") - if err.Error() != types.ErrVariableNotInScope("a").Error() { + expected_err := types.ErrVariableNotInScope("a") + if errors.Is(err, expected_err) { t.Errorf("TestEnterNewScope should fail with error: %s", types.ErrVariableNotInScope("a").Error()) } @@ -94,6 +96,173 @@ func TestEnterNewScope(t *testing.T) { } -func TestExitScopeTest(t *testing.T) { +func TestExitScope(t *testing.T) { + scope := make(map[string]interface{}) + scope["a"] = lambdaworks.FeltOne() + + scopes := types.NewExecutionScopes() + scopes.EnterScope(scope) + + locals, err := scopes.GetLocalVariables() + if err != nil { + t.Errorf("TestExitScopeTest failed with error: %s", err) + } + + if len(locals) != 1 { + t.Errorf("TestExitScopeTest failed, expected length: %d, got: %d", 1, len(locals)) + } + + result, err := scopes.Get("a") + if err != nil { + t.Errorf("TestExitScopeTest failed with error: %s", err) + + } + + f_res := result.(lambdaworks.Felt) + expected := lambdaworks.FeltOne() + if expected != f_res { + t.Errorf("TestExitScopeTest failed, expected: %s, got: %s", expected.ToSignedFeltString(), f_res.ToSignedFeltString()) + } + + err = scopes.ExitScope() + if err != nil { + t.Errorf("TestExitScopeTest failed with error: %s", err) + } + + locals, err = scopes.GetLocalVariables() + if err != nil { + t.Errorf("TestExitScopeTest failed with error: %s", err) + } + + if len(locals) != 0 { + t.Errorf("TestExitScopeTest failed, expected length: %d, got: %d", 0, len(locals)) + } + +} + +func TestAssignLocalVariable(t *testing.T) { + scope := make(map[string]interface{}) + + scopes := types.NewExecutionScopes() + scopes.EnterScope(scope) + + scopes.AssignOrUpdateVariable("a", uint64(45)) + + locals, err := scopes.GetLocalVariables() + if err != nil { + t.Errorf("TestAssignLocalVariable failed with error: %s", err) + } + + if len(locals) != 1 { + t.Errorf("TestAssignLocalVariable failed, expected length: %d, got: %d", 1, len(locals)) + } + + result, err := scopes.Get("a") + if err != nil { + t.Errorf("TestAssignLocalVariable failed with error: %s", err) + + } + + f_res := result.(uint64) + expected := uint64(45) + if expected != f_res { + t.Errorf("TestAssignLocalVariable failed, expected: uint64(%d), got: %d", expected, f_res) + } + +} + +func TestReAssignLocalVariable(t *testing.T) { + scope := make(map[string]interface{}) + + scopes := types.NewExecutionScopes() + scopes.EnterScope(scope) + + scopes.AssignOrUpdateVariable("a", uint64(45)) + + locals, err := scopes.GetLocalVariables() + if err != nil { + t.Errorf("TestReAssignLocalVariable failed with error: %s", err) + } + + if len(locals) != 1 { + t.Errorf("TestReAssignLocalVariable failed, expected length: %d, got: %d", 1, len(locals)) + } + + result, err := scopes.Get("a") + if err != nil { + t.Errorf("TestReAssignLocalVariable failed with error: %s", err) + + } + + res := result.(uint64) + expected := uint64(45) + if expected != res { + t.Errorf("TestReAssignLocalVariable failed, expected: uint64(%d), got: %d", expected, res) + } + + scopes.AssignOrUpdateVariable("a", lambdaworks.FeltOne()) + + locals, err = scopes.GetLocalVariables() + if err != nil { + t.Errorf("TestReAssignLocalVariable failed with error: %s", err) + } + + if len(locals) != 1 { + t.Errorf("TestReAssignLocalVariable failed, expected length: %d, got: %d", 1, len(locals)) + } + + result, err = scopes.Get("a") + if err != nil { + t.Errorf("TestReAssignLocalVariable failed with error: %s", err) + + } + + f_res := result.(lambdaworks.Felt) + f_expected := lambdaworks.FeltOne() + if f_expected != f_res { + t.Errorf("TestReAssignLocalVariable failed, expected: %s, got: %s", f_expected.ToSignedFeltString(), f_res.ToSignedFeltString()) + } + +} + +func TestDeleteLocalVariable(t *testing.T) { + scope := make(map[string]interface{}) + + scopes := types.NewExecutionScopes() + scopes.EnterScope(scope) + + scopes.AssignOrUpdateVariable("a", "val") + + locals, err := scopes.GetLocalVariables() + if err != nil { + t.Errorf("DeleteLocalVariable failed with error: %s", err) + } + + if len(locals) != 1 { + t.Errorf("DeleteLocalVariable failed, expected length: %d, got: %d", 1, len(locals)) + } + + _, err = scopes.Get("a") + if err != nil { + t.Errorf("DeleteLocalVariable failed with error: %s", err) + } + + scopes.DeleteVariable("a") + + // check variable a can't be accessed now + _, err = scopes.Get("a") + expected := types.ErrVariableNotInScope("a") + if errors.Is(err, expected) { + t.Errorf("TestDeleteLocalVariable should fail with error: %s", types.ErrVariableNotInScope("a").Error()) + } + +} +func TestErrExitMainScope(t *testing.T) { + scopes := types.NewExecutionScopes() + + err := scopes.ExitScope() + if err != types.ErrCannotExitMainScop { + t.Errorf("TestDeleteLocalVariable should fail with error: %s and fails with: %s", types.ErrCannotExitMainScop, err) + } } diff --git a/pkg/types/exec_scopes.go b/pkg/types/exec_scopes.go index 2acb0c78..588745b4 100644 --- a/pkg/types/exec_scopes.go +++ b/pkg/types/exec_scopes.go @@ -8,6 +8,8 @@ type ExecutionScopes struct { data []map[string]interface{} } +var ErrCannotExitMainScop error = ExecutionScopesError(errors.Errorf("Cannot exit main scope.")) + func ExecutionScopesError(err error) error { return errors.Wrapf(err, "Execution scopes error") } @@ -32,10 +34,12 @@ func (es *ExecutionScopes) EnterScope(newScopeLocals map[string]interface{}) { } func (es *ExecutionScopes) ExitScope() error { - if len(es.data) == 1 { - return ExecutionScopesError(errors.Errorf("Cannot exit main scope.")) + if len(es.Data()) < 2 { + return ErrCannotExitMainScop } - es.data = es.data[:len(es.data)-1] + i := len(es.Data()) - 1 + es.data = append(es.Data()[:i], es.Data()[i+1:]...) + return nil } From 30cb98d297f548cd37ca0051f32cb25c95756c22 Mon Sep 17 00:00:00 2001 From: toni-calvin Date: Wed, 13 Sep 2023 10:11:16 +0200 Subject: [PATCH 09/74] Add tests --- pkg/types/exec_scope_test.go | 51 +++++++++++++++++++++++++++++++++--- pkg/types/exec_scopes.go | 38 ++++++++++++++++++++++++--- 2 files changed, 81 insertions(+), 8 deletions(-) diff --git a/pkg/types/exec_scope_test.go b/pkg/types/exec_scope_test.go index de8182b7..e5909fdb 100644 --- a/pkg/types/exec_scope_test.go +++ b/pkg/types/exec_scope_test.go @@ -1,7 +1,7 @@ package types_test import ( - "errors" + "reflect" "testing" "github.com/lambdaclass/cairo-vm.go/pkg/lambdaworks" @@ -78,7 +78,7 @@ func TestEnterNewScope(t *testing.T) { // check variable a can't be accessed now _, err = scopes.Get("a") expected_err := types.ErrVariableNotInScope("a") - if errors.Is(err, expected_err) { + if err.Error() != expected_err.Error() { t.Errorf("TestEnterNewScope should fail with error: %s", types.ErrVariableNotInScope("a").Error()) } @@ -253,7 +253,7 @@ func TestDeleteLocalVariable(t *testing.T) { // check variable a can't be accessed now _, err = scopes.Get("a") expected := types.ErrVariableNotInScope("a") - if errors.Is(err, expected) { + if err.Error() != expected.Error() { t.Errorf("TestDeleteLocalVariable should fail with error: %s", types.ErrVariableNotInScope("a").Error()) } @@ -263,6 +263,49 @@ func TestErrExitMainScope(t *testing.T) { err := scopes.ExitScope() if err != types.ErrCannotExitMainScop { - t.Errorf("TestDeleteLocalVariable should fail with error: %s and fails with: %s", types.ErrCannotExitMainScop, err) + t.Errorf("TestErrExitMainScope should fail with error: %s and fails with: %s", types.ErrCannotExitMainScop, err) } } + +func TestGetListU64(t *testing.T) { + u64_list := []uint64{1, 2, 3, 4, 5} + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("u64_list", u64_list) + + result, err := scopes.GetList(reflect.TypeOf([]uint64{}), "u64_list") + if err != nil { + t.Errorf("TestGetListU64 failed with error: %s", err) + } + + if reflect.TypeOf(result) != reflect.TypeOf(u64_list) { + t.Errorf("TestGetListU64 failed with error: Expected list has type %s, got: %s", reflect.TypeOf(u64_list).String(), reflect.TypeOf(result).String()) + + } + + if !reflect.DeepEqual(u64_list, result) { + t.Errorf("TestGetListU64 failed with error: Expected list %v, got: %v", u64_list, result) + + } + +} + +func TestErrGetList(t *testing.T) { + u64_list := []uint64{1, 2, 3, 4, 5} + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("u64_list", u64_list) + + // Correct code + _, err := scopes.GetList(reflect.TypeOf([]int{}), "u64_list") + expected := types.ErrVariableNotInScope("u64_list") + if err.Error() != expected.Error() { + t.Errorf("TestErrGetList should fail with error: %s\n, got: %s\n", expected, err) + } + + // Extra code - can be deleated in production + // If uncommented, comment 'correct code' here and in method GetList + // _, err := scopes.GetList(reflect.TypeOf([]int{}), "u64_list") + // expected := types.ErrListTypeNotEqual("u64_list", reflect.TypeOf([]int{}).String(), reflect.TypeOf(u64_list).String()) + // if err.Error() != expected.Error() { + // t.Errorf("TestErrGetList should fail with error: %s\n, got: %s\n", expected, err) + // } +} diff --git a/pkg/types/exec_scopes.go b/pkg/types/exec_scopes.go index 588745b4..e22663dc 100644 --- a/pkg/types/exec_scopes.go +++ b/pkg/types/exec_scopes.go @@ -1,6 +1,8 @@ package types import ( + "reflect" + "github.com/pkg/errors" ) @@ -19,9 +21,9 @@ func ErrVariableNotInScope(varName string) error { } func NewExecutionScopes() *ExecutionScopes { - return &ExecutionScopes{ - data: make([]map[string]interface{}, 1), - } + data := make([]map[string]interface{}, 1) + data[0] = make(map[string]interface{}) + return &ExecutionScopes{data} } func (es *ExecutionScopes) Data() []map[string]interface{} { @@ -93,7 +95,35 @@ func (es *ExecutionScopes) GetRef(varName string) (*interface{}, error) { } val, prs := locals[varName] if !prs { - return nil, ExecutionScopesError(errors.Errorf("Variable %s not in scope", varName)) + return nil, ErrVariableNotInScope(varName) } return &val, nil } + +// This error has been used for testing purposes +// We should not give information about exists a list with different types on the scope +// On production should be removed +func ErrListTypeNotEqual(varName string, expectedType string, resultType string) error { + return ExecutionScopesError(errors.Errorf("List %s types does not match, expected type: %s, result type: %s", varName, expectedType, resultType)) +} + +func (es *ExecutionScopes) GetList(T reflect.Type, varName string) (interface{}, error) { + maybeList, err := es.Get(varName) + if err != nil { + return nil, ErrVariableNotInScope(varName) + } + + // Correct code + if reflect.TypeOf(maybeList) != T { + return nil, ErrVariableNotInScope(varName) + } + + // If uncommented, should comment correct code + // Extra code + // if reflect.TypeOf(maybeList) != T { + // return nil, ErrListTypeNotEqual(varName, T.String(), reflect.TypeOf(maybeList).String()) + // } + + return maybeList, nil + +} From 6c8d34680ee50eec2ae4c24adc8349e6a1c0f033 Mon Sep 17 00:00:00 2001 From: toni-calvin Date: Wed, 13 Sep 2023 10:26:35 +0200 Subject: [PATCH 10/74] Add test --- pkg/types/exec_scope_test.go | 20 ++++++++++++++++++++ pkg/types/exec_scopes.go | 6 +----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/pkg/types/exec_scope_test.go b/pkg/types/exec_scope_test.go index e5909fdb..6844e37a 100644 --- a/pkg/types/exec_scope_test.go +++ b/pkg/types/exec_scope_test.go @@ -34,6 +34,26 @@ func TestGetLocalVariables(t *testing.T) { } } +func TestGetRefLocalVariables(t *testing.T) { + scope := make(map[string]interface{}) + scope["k"] = lambdaworks.FeltOne() + + scopes := types.NewExecutionScopes() + scopes.EnterScope(scope) + + result, err := scopes.GetRef("k") + if err != nil { + t.Errorf("TestGetRefLocalVariables failed with error: %s", err) + + } + f_one := lambdaworks.FeltOne() + f_res := (*result).(lambdaworks.Felt) + expected := &f_one + if *expected != f_res { + t.Errorf("TestGetRefLocalVariables failed, expected: %d, got: %d", expected, &f_res) + } +} + func TestEnterNewScope(t *testing.T) { scope := make(map[string]interface{}) scope["a"] = lambdaworks.FeltOne() diff --git a/pkg/types/exec_scopes.go b/pkg/types/exec_scopes.go index e22663dc..2131019e 100644 --- a/pkg/types/exec_scopes.go +++ b/pkg/types/exec_scopes.go @@ -89,14 +89,10 @@ func (es *ExecutionScopes) Get(varName string) (interface{}, error) { } func (es *ExecutionScopes) GetRef(varName string) (*interface{}, error) { - locals, err := es.GetLocalVariables() + val, err := es.Get(varName) if err != nil { return nil, err } - val, prs := locals[varName] - if !prs { - return nil, ErrVariableNotInScope(varName) - } return &val, nil } From 7fb733df9943e7055c2e17f17c1524faa000ed86 Mon Sep 17 00:00:00 2001 From: toni-calvin Date: Wed, 13 Sep 2023 10:29:36 +0200 Subject: [PATCH 11/74] Add minor changes --- pkg/types/exec_scope_test.go | 2 +- pkg/types/exec_scopes.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/types/exec_scope_test.go b/pkg/types/exec_scope_test.go index 6844e37a..18a3484c 100644 --- a/pkg/types/exec_scope_test.go +++ b/pkg/types/exec_scope_test.go @@ -321,7 +321,7 @@ func TestErrGetList(t *testing.T) { t.Errorf("TestErrGetList should fail with error: %s\n, got: %s\n", expected, err) } - // Extra code - can be deleated in production + // Extra code - can be removed in prod // If uncommented, comment 'correct code' here and in method GetList // _, err := scopes.GetList(reflect.TypeOf([]int{}), "u64_list") // expected := types.ErrListTypeNotEqual("u64_list", reflect.TypeOf([]int{}).String(), reflect.TypeOf(u64_list).String()) diff --git a/pkg/types/exec_scopes.go b/pkg/types/exec_scopes.go index 2131019e..d416de64 100644 --- a/pkg/types/exec_scopes.go +++ b/pkg/types/exec_scopes.go @@ -97,8 +97,8 @@ func (es *ExecutionScopes) GetRef(varName string) (*interface{}, error) { } // This error has been used for testing purposes -// We should not give information about exists a list with different types on the scope -// On production should be removed +// We should not give information about an existing list with different types on the scope +// On production can be removed func ErrListTypeNotEqual(varName string, expectedType string, resultType string) error { return ExecutionScopesError(errors.Errorf("List %s types does not match, expected type: %s, result type: %s", varName, expectedType, resultType)) } From ada05b69abeb00cb7afc58891b707085c594b58d Mon Sep 17 00:00:00 2001 From: toni-calvin Date: Wed, 13 Sep 2023 16:27:46 +0200 Subject: [PATCH 12/74] Implement vm_enter_scope hint and test --- pkg/hints/hint_processor.go | 5 +++- pkg/hints/hint_processor_test.go | 4 +-- pkg/hints/math_hint_codes.go | 2 ++ pkg/hints/math_hints_test.go | 8 +++--- pkg/hints/mem_hints.go | 13 ++++++++++ pkg/hints/mem_hints_test.go | 39 ++++++++++++++++++++++++++++++ pkg/vm/hint_processor_interface.go | 3 ++- pkg/vm/vm_core.go | 2 +- 8 files changed, 67 insertions(+), 9 deletions(-) create mode 100644 pkg/hints/mem_hints.go create mode 100644 pkg/hints/mem_hints_test.go diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index 6a91f535..3ced47e5 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -7,6 +7,7 @@ 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/parser" + "github.com/lambdaclass/cairo-vm.go/pkg/types" "github.com/lambdaclass/cairo-vm.go/pkg/vm" ) @@ -32,7 +33,7 @@ func (p *CairoVmHintProcessor) CompileHint(hintParams *parser.HintParams, refere return HintData{Ids: ids, Code: hintParams.Code}, nil } -func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, constants *map[string]Felt) error { +func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, constants *map[string]Felt, executionScopes *types.ExecutionScopes) error { data, ok := (*hintData).(HintData) if !ok { return errors.New("Wrong Hint Data") @@ -42,6 +43,8 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, return assert_nn(data.Ids, vm) case ASSERT_NOT_ZERO: return assert_not_zero(data.Ids, vm) + case VM_EXIT_SCOPE: + return vm_exit_scope(executionScopes) default: return errors.New("Unknown Hint") } diff --git a/pkg/hints/hint_processor_test.go b/pkg/hints/hint_processor_test.go index e7135961..cad19c5c 100644 --- a/pkg/hints/hint_processor_test.go +++ b/pkg/hints/hint_processor_test.go @@ -91,7 +91,7 @@ func TestExecuteHintWrongHintData(t *testing.T) { hintProcessor := &CairoVmHintProcessor{} hintData := any("Mistake") vm := vm.NewVirtualMachine() - err := hintProcessor.ExecuteHint(vm, &hintData, nil) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) if err == nil { t.Errorf("Should have failed") } @@ -101,7 +101,7 @@ func TestExecuteHintUnknownHint(t *testing.T) { hintProcessor := &CairoVmHintProcessor{} hintData := any(HintData{Code: "print(Hello World)"}) vm := vm.NewVirtualMachine() - err := hintProcessor.ExecuteHint(vm, &hintData, nil) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) if err == nil { t.Errorf("Should have failed") } diff --git a/pkg/hints/math_hint_codes.go b/pkg/hints/math_hint_codes.go index 42f8c6de..6bfa9dc8 100644 --- a/pkg/hints/math_hint_codes.go +++ b/pkg/hints/math_hint_codes.go @@ -3,3 +3,5 @@ package hints const ASSERT_NN = "from starkware.cairo.common.math_utils import assert_integer\nassert_integer(ids.a)\nassert 0 <= ids.a % PRIME < range_check_builtin.bound, f'a = {ids.a} is out of range.'" const ASSERT_NOT_ZERO = "from starkware.cairo.common.math_utils import assert_integer\nassert_integer(ids.value)\nassert ids.value % PRIME != 0, f'assert_not_zero failed: {ids.value} = 0.'" + +const VM_EXIT_SCOPE = "vm_exit_scope()" diff --git a/pkg/hints/math_hints_test.go b/pkg/hints/math_hints_test.go index 4574bfa3..2f8b3aca 100644 --- a/pkg/hints/math_hints_test.go +++ b/pkg/hints/math_hints_test.go @@ -24,7 +24,7 @@ func TestIsNNHintOk(t *testing.T) { Ids: idsManager, Code: ASSERT_NN, }) - err := hintProcessor.ExecuteHint(vm, &hintData, nil) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) if err != nil { t.Errorf("ASSERT_NN hint test failed with error %s", err) } @@ -44,7 +44,7 @@ func TestIsNNHintFail(t *testing.T) { Ids: idsManager, Code: ASSERT_NN, }) - err := hintProcessor.ExecuteHint(vm, &hintData, nil) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) if err == nil { t.Errorf("ASSERT_NN hint should have failed") } @@ -64,7 +64,7 @@ func TestAssertNotZeroHintOk(t *testing.T) { Ids: idsManager, Code: ASSERT_NOT_ZERO, }) - err := hintProcessor.ExecuteHint(vm, &hintData, nil) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) if err != nil { t.Errorf("ASSERT_NOT_ZERO hint test failed with error %s", err) } @@ -84,7 +84,7 @@ func TestAssertNotZeroHintFail(t *testing.T) { Ids: idsManager, Code: ASSERT_NOT_ZERO, }) - err := hintProcessor.ExecuteHint(vm, &hintData, nil) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) if err == nil { t.Errorf("ASSERT_NOT_ZERO hint should have failed") } diff --git a/pkg/hints/mem_hints.go b/pkg/hints/mem_hints.go new file mode 100644 index 00000000..0f1a6573 --- /dev/null +++ b/pkg/hints/mem_hints.go @@ -0,0 +1,13 @@ +package hints + +import "github.com/lambdaclass/cairo-vm.go/pkg/types" + +// Implements hint: +// %{ vm_exit_scope() %} +func vm_exit_scope(executionScopes *types.ExecutionScopes) error { + err := executionScopes.ExitScope() + if err != nil { + return err + } + return nil +} diff --git a/pkg/hints/mem_hints_test.go b/pkg/hints/mem_hints_test.go new file mode 100644 index 00000000..b752fb02 --- /dev/null +++ b/pkg/hints/mem_hints_test.go @@ -0,0 +1,39 @@ +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 TestExitScopeValid(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "a": {NewMaybeRelocatableFelt(FeltFromUint64(17))}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: VM_EXIT_SCOPE, + }) + + executionScopes := NewExecutionScopes() + scope := make(map[string]interface{}) + scope["a"] = FeltOne() + executionScopes.EnterScope(scope) + + err := hintProcessor.ExecuteHint(vm, &hintData, nil, executionScopes) + if err != nil { + t.Errorf("VM_EXIT_SCOPE hint test failed with error %s", err) + } + +} diff --git a/pkg/vm/hint_processor_interface.go b/pkg/vm/hint_processor_interface.go index fe18f663..660977f2 100644 --- a/pkg/vm/hint_processor_interface.go +++ b/pkg/vm/hint_processor_interface.go @@ -3,6 +3,7 @@ package vm import ( "github.com/lambdaclass/cairo-vm.go/pkg/lambdaworks" "github.com/lambdaclass/cairo-vm.go/pkg/parser" + "github.com/lambdaclass/cairo-vm.go/pkg/types" ) type HintProcessor interface { @@ -10,5 +11,5 @@ type HintProcessor interface { CompileHint(hintParams *parser.HintParams, referenceManager *parser.ReferenceManager) (any, error) // Executes the hint which's data is provided by a dynamic structure previously created by CompileHint // TODO: add * ExecScopes arg when ready - ExecuteHint(vm *VirtualMachine, hintData *any, constants *map[string]lambdaworks.Felt) error + ExecuteHint(vm *VirtualMachine, hintData *any, constants *map[string]lambdaworks.Felt, execScopes *types.ExecutionScopes) error } diff --git a/pkg/vm/vm_core.go b/pkg/vm/vm_core.go index 016066a5..1f521c02 100644 --- a/pkg/vm/vm_core.go +++ b/pkg/vm/vm_core.go @@ -43,7 +43,7 @@ func (v *VirtualMachine) Step(hintProcessor HintProcessor, hintDataMap *map[uint hintDatas, ok := (*hintDataMap)[v.RunContext.Pc.Offset] if ok { for i := 0; i < len(hintDatas); i++ { - err := hintProcessor.ExecuteHint(v, &hintDatas[i], constants) + err := hintProcessor.ExecuteHint(v, &hintDatas[i], constants, nil) if err != nil { return err } From 2fa4299296ef042f128a27da48401a8daf929b8b Mon Sep 17 00:00:00 2001 From: toni-calvin Date: Wed, 13 Sep 2023 17:20:01 +0200 Subject: [PATCH 13/74] Changes attributes visibility and removes unused methods. --- pkg/types/exec_scope_test.go | 51 ------------------------------------ pkg/types/exec_scopes.go | 40 +++------------------------- 2 files changed, 3 insertions(+), 88 deletions(-) diff --git a/pkg/types/exec_scope_test.go b/pkg/types/exec_scope_test.go index 18a3484c..1ee54fd9 100644 --- a/pkg/types/exec_scope_test.go +++ b/pkg/types/exec_scope_test.go @@ -1,20 +1,12 @@ package types_test import ( - "reflect" "testing" "github.com/lambdaclass/cairo-vm.go/pkg/lambdaworks" "github.com/lambdaclass/cairo-vm.go/pkg/types" ) -func TestInitializeExecutionScopes(t *testing.T) { - scopes := types.NewExecutionScopes() - if len(scopes.Data()) != 1 { - t.Errorf("TestInitializeExecutionScopes failed, expected length: %d, got: %d", 1, len((scopes.Data()))) - } -} - func TestGetLocalVariables(t *testing.T) { scope := make(map[string]interface{}) scope["k"] = lambdaworks.FeltOne() @@ -286,46 +278,3 @@ func TestErrExitMainScope(t *testing.T) { t.Errorf("TestErrExitMainScope should fail with error: %s and fails with: %s", types.ErrCannotExitMainScop, err) } } - -func TestGetListU64(t *testing.T) { - u64_list := []uint64{1, 2, 3, 4, 5} - scopes := types.NewExecutionScopes() - scopes.AssignOrUpdateVariable("u64_list", u64_list) - - result, err := scopes.GetList(reflect.TypeOf([]uint64{}), "u64_list") - if err != nil { - t.Errorf("TestGetListU64 failed with error: %s", err) - } - - if reflect.TypeOf(result) != reflect.TypeOf(u64_list) { - t.Errorf("TestGetListU64 failed with error: Expected list has type %s, got: %s", reflect.TypeOf(u64_list).String(), reflect.TypeOf(result).String()) - - } - - if !reflect.DeepEqual(u64_list, result) { - t.Errorf("TestGetListU64 failed with error: Expected list %v, got: %v", u64_list, result) - - } - -} - -func TestErrGetList(t *testing.T) { - u64_list := []uint64{1, 2, 3, 4, 5} - scopes := types.NewExecutionScopes() - scopes.AssignOrUpdateVariable("u64_list", u64_list) - - // Correct code - _, err := scopes.GetList(reflect.TypeOf([]int{}), "u64_list") - expected := types.ErrVariableNotInScope("u64_list") - if err.Error() != expected.Error() { - t.Errorf("TestErrGetList should fail with error: %s\n, got: %s\n", expected, err) - } - - // Extra code - can be removed in prod - // If uncommented, comment 'correct code' here and in method GetList - // _, err := scopes.GetList(reflect.TypeOf([]int{}), "u64_list") - // expected := types.ErrListTypeNotEqual("u64_list", reflect.TypeOf([]int{}).String(), reflect.TypeOf(u64_list).String()) - // if err.Error() != expected.Error() { - // t.Errorf("TestErrGetList should fail with error: %s\n, got: %s\n", expected, err) - // } -} diff --git a/pkg/types/exec_scopes.go b/pkg/types/exec_scopes.go index d416de64..f6d1fc35 100644 --- a/pkg/types/exec_scopes.go +++ b/pkg/types/exec_scopes.go @@ -1,8 +1,6 @@ package types import ( - "reflect" - "github.com/pkg/errors" ) @@ -26,21 +24,17 @@ func NewExecutionScopes() *ExecutionScopes { return &ExecutionScopes{data} } -func (es *ExecutionScopes) Data() []map[string]interface{} { - return es.data -} - func (es *ExecutionScopes) EnterScope(newScopeLocals map[string]interface{}) { es.data = append(es.data, newScopeLocals) } func (es *ExecutionScopes) ExitScope() error { - if len(es.Data()) < 2 { + if len(es.data) < 2 { return ErrCannotExitMainScop } - i := len(es.Data()) - 1 - es.data = append(es.Data()[:i], es.Data()[i+1:]...) + i := len(es.data) - 1 + es.data = es.data[:i] return nil } @@ -95,31 +89,3 @@ func (es *ExecutionScopes) GetRef(varName string) (*interface{}, error) { } return &val, nil } - -// This error has been used for testing purposes -// We should not give information about an existing list with different types on the scope -// On production can be removed -func ErrListTypeNotEqual(varName string, expectedType string, resultType string) error { - return ExecutionScopesError(errors.Errorf("List %s types does not match, expected type: %s, result type: %s", varName, expectedType, resultType)) -} - -func (es *ExecutionScopes) GetList(T reflect.Type, varName string) (interface{}, error) { - maybeList, err := es.Get(varName) - if err != nil { - return nil, ErrVariableNotInScope(varName) - } - - // Correct code - if reflect.TypeOf(maybeList) != T { - return nil, ErrVariableNotInScope(varName) - } - - // If uncommented, should comment correct code - // Extra code - // if reflect.TypeOf(maybeList) != T { - // return nil, ErrListTypeNotEqual(varName, T.String(), reflect.TypeOf(maybeList).String()) - // } - - return maybeList, nil - -} From e6a46b9e11649b9bbe8b2db33b4e53c65a037c70 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 13 Sep 2023 12:25:06 -0300 Subject: [PATCH 14/74] Fix string --- pkg/hints/math_hint_codes.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/hints/math_hint_codes.go b/pkg/hints/math_hint_codes.go index e5f3fc61..1c2291c4 100644 --- a/pkg/hints/math_hint_codes.go +++ b/pkg/hints/math_hint_codes.go @@ -4,4 +4,4 @@ const ASSERT_NN = "from starkware.cairo.common.math_utils import assert_integer\ const ASSERT_NOT_ZERO = "from starkware.cairo.common.math_utils import assert_integer\nassert_integer(ids.value)\nassert ids.value % PRIME != 0, f'assert_not_zero failed: {ids.value} = 0.'" -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 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}.'" From 107310333c7a35f633bcd248075806113a201451 Mon Sep 17 00:00:00 2001 From: fmoletta <99273364+fmoletta@users.noreply.github.com> Date: Wed, 13 Sep 2023 12:28:05 -0300 Subject: [PATCH 15/74] Add newline at eof --- cairo_programs/assert_not_equal.cairo | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cairo_programs/assert_not_equal.cairo b/cairo_programs/assert_not_equal.cairo index 916bc002..fe7e6ad6 100644 --- a/cairo_programs/assert_not_equal.cairo +++ b/cairo_programs/assert_not_equal.cairo @@ -7,4 +7,5 @@ func main{output_ptr: felt*}() { assert_not_equal(cast(output_ptr, felt), cast(output_ptr + 1, felt)); assert_not_equal(-1, 1); return (); -} \ No newline at end of file +} + From 8bef8befe399bf096e3b508c92ae04f4ac94a583 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 13 Sep 2023 12:28:50 -0300 Subject: [PATCH 16/74] Remove extra line --- cairo_programs/assert_not_equal.cairo | 1 - 1 file changed, 1 deletion(-) diff --git a/cairo_programs/assert_not_equal.cairo b/cairo_programs/assert_not_equal.cairo index fe7e6ad6..a5810e4d 100644 --- a/cairo_programs/assert_not_equal.cairo +++ b/cairo_programs/assert_not_equal.cairo @@ -8,4 +8,3 @@ func main{output_ptr: felt*}() { assert_not_equal(-1, 1); return (); } - From 51f26d8b91716101e0f0b1fbf8dabf9475e25f26 Mon Sep 17 00:00:00 2001 From: toni-calvin Date: Wed, 13 Sep 2023 17:58:29 +0200 Subject: [PATCH 17/74] deletes duplicate code --- pkg/types/exec_scopes.go | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/pkg/types/exec_scopes.go b/pkg/types/exec_scopes.go index f6d1fc35..24e7b2ef 100644 --- a/pkg/types/exec_scopes.go +++ b/pkg/types/exec_scopes.go @@ -40,10 +40,8 @@ func (es *ExecutionScopes) ExitScope() error { } func (es *ExecutionScopes) getLocalVariablesMut() (*map[string]interface{}, error) { - if len(es.data) > 0 { - return &es.data[len(es.data)-1], nil - } - return nil, ExecutionScopesError(errors.Errorf("Every enter_scope() requires a corresponding exit_scope().")) + locals, err := es.GetLocalVariables() + return &locals, err } func (es *ExecutionScopes) GetLocalVariables() (map[string]interface{}, error) { @@ -84,8 +82,5 @@ func (es *ExecutionScopes) Get(varName string) (interface{}, error) { func (es *ExecutionScopes) GetRef(varName string) (*interface{}, error) { val, err := es.Get(varName) - if err != nil { - return nil, err - } - return &val, nil + return &val, err } From 07b4d1dfbe9174dbad998149e728daee291c42ca Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 13 Sep 2023 13:07:32 -0300 Subject: [PATCH 18/74] Remove initial_dict from default-dict_new --- pkg/hints/dict_manager/dict_manager.go | 12 ++++----- pkg/hints/dict_manager/dict_manager_test.go | 27 ++++----------------- 2 files changed, 11 insertions(+), 28 deletions(-) diff --git a/pkg/hints/dict_manager/dict_manager.go b/pkg/hints/dict_manager/dict_manager.go index db37adc8..6ceb36dc 100644 --- a/pkg/hints/dict_manager/dict_manager.go +++ b/pkg/hints/dict_manager/dict_manager.go @@ -24,9 +24,9 @@ func (d *DictManager) NewDictionary(dict *map[MaybeRelocatable]MaybeRelocatable, return base } -func (d *DictManager) NewDefaultDictionary(defaultValue *MaybeRelocatable, dict *map[MaybeRelocatable]MaybeRelocatable, vm *VirtualMachine) Relocatable { +func (d *DictManager) NewDefaultDictionary(defaultValue *MaybeRelocatable, vm *VirtualMachine) Relocatable { base := vm.Segments.AddSegment() - d.trackers[base.SegmentIndex] = NewDictTrackerForDefaultDictionary(base, defaultValue, dict) + d.trackers[base.SegmentIndex] = NewDictTrackerForDefaultDictionary(base, defaultValue) return base } @@ -55,9 +55,9 @@ func NewDictTrackerForDictionary(base Relocatable, dict *map[MaybeRelocatable]Ma } } -func NewDictTrackerForDefaultDictionary(base Relocatable, defaultValue *MaybeRelocatable, dict *map[MaybeRelocatable]MaybeRelocatable) DictTracker { +func NewDictTrackerForDefaultDictionary(base Relocatable, defaultValue *MaybeRelocatable) DictTracker { return DictTracker{ - data: NewDefaultDictionary(defaultValue, dict), + data: NewDefaultDictionary(defaultValue), currentPtr: base, } } @@ -83,9 +83,9 @@ type Dictionary struct { defaultValue *MaybeRelocatable } -func NewDefaultDictionary(defaultValue *MaybeRelocatable, dict *map[MaybeRelocatable]MaybeRelocatable) Dictionary { +func NewDefaultDictionary(defaultValue *MaybeRelocatable) Dictionary { return Dictionary{ - dict: *dict, + dict: make(map[MaybeRelocatable]MaybeRelocatable), defaultValue: defaultValue, } } diff --git a/pkg/hints/dict_manager/dict_manager_test.go b/pkg/hints/dict_manager/dict_manager_test.go index 31c37a50..ad8ddaaf 100644 --- a/pkg/hints/dict_manager/dict_manager_test.go +++ b/pkg/hints/dict_manager/dict_manager_test.go @@ -28,9 +28,8 @@ func TestDictManagerNewDictionaryGetTracker(t *testing.T) { func TestDictManagerNewDefaultDictionaryGetTracker(t *testing.T) { dictManager := NewDictManager() - initialDict := &map[MaybeRelocatable]MaybeRelocatable{} vm := vm.NewVirtualMachine() - base := dictManager.NewDefaultDictionary(nil, initialDict, vm) + base := dictManager.NewDefaultDictionary(nil, vm) if base.SegmentIndex != int(vm.Segments.Memory.NumSegments())-1 { t.Errorf("Segment not created for DictTracker") } @@ -61,13 +60,10 @@ func TestDictManagerNewDictionaryGetTrackerNoTracker(t *testing.T) { // DictTracker func TestDictTrackerDefaultDictCopyDict(t *testing.T) { - initialDict := &map[MaybeRelocatable]MaybeRelocatable{ - *NewMaybeRelocatableFelt(FeltFromUint64(1)): *NewMaybeRelocatableFelt(FeltFromUint64(2)), - } + initialDict := &map[MaybeRelocatable]MaybeRelocatable{} dictTracker := NewDictTrackerForDefaultDictionary( NewRelocatable(0, 0), NewMaybeRelocatableFelt(FeltFromUint64(17)), - &map[MaybeRelocatable]MaybeRelocatable{}, ) // Check CopyDict if reflect.DeepEqual(dictTracker.CopyDictionary(), *initialDict) { @@ -76,14 +72,11 @@ func TestDictTrackerDefaultDictCopyDict(t *testing.T) { } func TestDictTrackerDefaultGetValuePresent(t *testing.T) { - initialDict := &map[MaybeRelocatable]MaybeRelocatable{ - *NewMaybeRelocatableFelt(FeltFromUint64(1)): *NewMaybeRelocatableFelt(FeltFromUint64(2)), - } dictTracker := NewDictTrackerForDefaultDictionary( NewRelocatable(0, 0), NewMaybeRelocatableFelt(FeltFromUint64(17)), - initialDict, ) + dictTracker.InsertValue(NewMaybeRelocatableFelt(FeltFromUint64(1)), NewMaybeRelocatableFelt(FeltFromUint64(2))) // Check GetValue val, err := dictTracker.GetValue(NewMaybeRelocatableFelt(FeltFromUint64(1))) if err != nil { @@ -96,14 +89,11 @@ func TestDictTrackerDefaultGetValuePresent(t *testing.T) { } func TestDictTrackerDefaultGetValueNotPresent(t *testing.T) { - initialDict := &map[MaybeRelocatable]MaybeRelocatable{ - *NewMaybeRelocatableFelt(FeltFromUint64(1)): *NewMaybeRelocatableFelt(FeltFromUint64(2)), - } dictTracker := NewDictTrackerForDefaultDictionary( NewRelocatable(0, 0), NewMaybeRelocatableFelt(FeltFromUint64(17)), - initialDict, ) + dictTracker.InsertValue(NewMaybeRelocatableFelt(FeltFromUint64(1)), NewMaybeRelocatableFelt(FeltFromUint64(2))) // Check that the default value is returned val, err := dictTracker.GetValue(NewMaybeRelocatableFelt(FeltFromUint64(2))) if err != nil { @@ -207,14 +197,7 @@ func TestDictionary(t *testing.T) { } func TestDefaultDictionary(t *testing.T) { - initialDict := &map[MaybeRelocatable]MaybeRelocatable{ - *NewMaybeRelocatableFelt(FeltFromUint64(1)): *NewMaybeRelocatableFelt(FeltFromUint64(2)), - } - dict := NewDefaultDictionary(NewMaybeRelocatableFelt(FeltFromUint64(17)), initialDict) - // Check Get - if *dict.Get(NewMaybeRelocatableFelt(FeltFromUint64(1))) != *NewMaybeRelocatableFelt(FeltFromUint64(2)) { - t.Error("Wrong value returned by Get") - } + dict := NewDefaultDictionary(NewMaybeRelocatableFelt(FeltFromUint64(17))) // InsertValue dict.Insert(NewMaybeRelocatableFelt(FeltFromUint64(7)), NewMaybeRelocatableFelt(FeltFromUint64(8))) // Check Get From d12937ba5fcc6f385ec42c1c2a8bd04e87f564fc Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 13 Sep 2023 13:15:19 -0300 Subject: [PATCH 19/74] fix test --- pkg/hints/dict_manager/dict_manager_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/hints/dict_manager/dict_manager_test.go b/pkg/hints/dict_manager/dict_manager_test.go index ad8ddaaf..2756cae6 100644 --- a/pkg/hints/dict_manager/dict_manager_test.go +++ b/pkg/hints/dict_manager/dict_manager_test.go @@ -66,7 +66,7 @@ func TestDictTrackerDefaultDictCopyDict(t *testing.T) { NewMaybeRelocatableFelt(FeltFromUint64(17)), ) // Check CopyDict - if reflect.DeepEqual(dictTracker.CopyDictionary(), *initialDict) { + if !reflect.DeepEqual(dictTracker.CopyDictionary(), *initialDict) { t.Error("Wrong dict returned by CopyDictionary") } } From ceeafc0062db598038a2ca8923611b162da4922a Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 13 Sep 2023 13:17:20 -0300 Subject: [PATCH 20/74] Implement defaultDictNew --- pkg/hints/dict_hint_codes.go | 3 +++ pkg/hints/dict_hints.go | 41 ++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 pkg/hints/dict_hint_codes.go create mode 100644 pkg/hints/dict_hints.go diff --git a/pkg/hints/dict_hint_codes.go b/pkg/hints/dict_hint_codes.go new file mode 100644 index 00000000..f3c4a86b --- /dev/null +++ b/pkg/hints/dict_hint_codes.go @@ -0,0 +1,3 @@ +package hints + +const DEFAULT_DICT_NEW = "if '__dict_manager' not in globals():\n from starkware.cairo.common.dict import DictManager\n __dict_manager = DictManager()\n\nmemory[ap] = __dict_manager.new_default_dict(segments, ids.default_value)" diff --git a/pkg/hints/dict_hints.go b/pkg/hints/dict_hints.go new file mode 100644 index 00000000..7b2533d2 --- /dev/null +++ b/pkg/hints/dict_hints.go @@ -0,0 +1,41 @@ +package hints + +import ( + . "github.com/lambdaclass/cairo-vm.go/pkg/hints/dict_manager" + . "github.com/lambdaclass/cairo-vm.go/pkg/hints/hint_utils" + . "github.com/lambdaclass/cairo-vm.go/pkg/types" + . "github.com/lambdaclass/cairo-vm.go/pkg/vm" + "github.com/lambdaclass/cairo-vm.go/pkg/vm/memory" +) + +// func copyInitialDict(scopes ExecutionScopes) (*map[MaybeRelocatable]MaybeRelocatable, error) { +// initial_dict_any, err := scopes.Get("initial_dict") +// if err != nil { +// return nil, err +// } +// initial_dict, ok := initial_dict_any.(map[MaybeRelocatable]MaybeRelocatable) +// } + +func fetchDictManager(scopes *ExecutionScopes) (*DictManager, bool) { + dictManager, err := scopes.Get("__dict_manager") + if err != nil { + return nil, false + } + val, ok := dictManager.(*DictManager) + return val, ok +} + +func defaultDictNew(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error { + defaultValue, err := ids.Get("default_value", vm) + if err != nil { + return err + } + dictManager, ok := fetchDictManager(scopes) + if !ok { + newDictManager := NewDictManager() + dictManager = &newDictManager + scopes.AssignOrUpdateVariable("__dict_manager", dictManager) + } + base := dictManager.NewDefaultDictionary(defaultValue, vm) + return vm.Segments.Memory.Insert(vm.RunContext.Ap, memory.NewMaybeRelocatableRelocatable(base)) +} From a5a61b768966123da229ae12d0f78440cd092537 Mon Sep 17 00:00:00 2001 From: toni-calvin Date: Wed, 13 Sep 2023 18:17:52 +0200 Subject: [PATCH 21/74] merge files --- pkg/hints/math_hint_codes.go | 2 -- pkg/hints/mem_hints.go | 13 ------------ pkg/hints/mem_hints_test.go | 39 ---------------------------------- pkg/hints/memcpy_hint_codes.go | 1 + pkg/hints/memcpy_hints.go | 11 ++++++++++ pkg/hints/memcpy_hints_test.go | 31 +++++++++++++++++++++++++++ 6 files changed, 43 insertions(+), 54 deletions(-) delete mode 100644 pkg/hints/mem_hints.go delete mode 100644 pkg/hints/mem_hints_test.go diff --git a/pkg/hints/math_hint_codes.go b/pkg/hints/math_hint_codes.go index 824757aa..737a8d43 100644 --- a/pkg/hints/math_hint_codes.go +++ b/pkg/hints/math_hint_codes.go @@ -5,5 +5,3 @@ const ASSERT_NN = "from starkware.cairo.common.math_utils import assert_integer\ const IS_POSITIVE = "from starkware.cairo.common.math_utils import is_positive\nids.is_positive = 1 if is_positive(\n value=ids.value, prime=PRIME, rc_bound=range_check_builtin.bound) else 0" const ASSERT_NOT_ZERO = "from starkware.cairo.common.math_utils import assert_integer\nassert_integer(ids.value)\nassert ids.value % PRIME != 0, f'assert_not_zero failed: {ids.value} = 0.'" - -const VM_EXIT_SCOPE = "vm_exit_scope()" diff --git a/pkg/hints/mem_hints.go b/pkg/hints/mem_hints.go deleted file mode 100644 index 0f1a6573..00000000 --- a/pkg/hints/mem_hints.go +++ /dev/null @@ -1,13 +0,0 @@ -package hints - -import "github.com/lambdaclass/cairo-vm.go/pkg/types" - -// Implements hint: -// %{ vm_exit_scope() %} -func vm_exit_scope(executionScopes *types.ExecutionScopes) error { - err := executionScopes.ExitScope() - if err != nil { - return err - } - return nil -} diff --git a/pkg/hints/mem_hints_test.go b/pkg/hints/mem_hints_test.go deleted file mode 100644 index b752fb02..00000000 --- a/pkg/hints/mem_hints_test.go +++ /dev/null @@ -1,39 +0,0 @@ -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 TestExitScopeValid(t *testing.T) { - vm := NewVirtualMachine() - vm.Segments.AddSegment() - idsManager := SetupIdsForTest( - map[string][]*MaybeRelocatable{ - "a": {NewMaybeRelocatableFelt(FeltFromUint64(17))}, - }, - vm, - ) - hintProcessor := CairoVmHintProcessor{} - hintData := any(HintData{ - Ids: idsManager, - Code: VM_EXIT_SCOPE, - }) - - executionScopes := NewExecutionScopes() - scope := make(map[string]interface{}) - scope["a"] = FeltOne() - executionScopes.EnterScope(scope) - - err := hintProcessor.ExecuteHint(vm, &hintData, nil, executionScopes) - if err != nil { - t.Errorf("VM_EXIT_SCOPE hint test failed with error %s", err) - } - -} diff --git a/pkg/hints/memcpy_hint_codes.go b/pkg/hints/memcpy_hint_codes.go index 9394cf52..71e356c5 100644 --- a/pkg/hints/memcpy_hint_codes.go +++ b/pkg/hints/memcpy_hint_codes.go @@ -1,3 +1,4 @@ package hints const ADD_SEGMENT = "memory[ap] = segments.add()" +const VM_EXIT_SCOPE = "vm_exit_scope()" diff --git a/pkg/hints/memcpy_hints.go b/pkg/hints/memcpy_hints.go index 76559ee3..20be2141 100644 --- a/pkg/hints/memcpy_hints.go +++ b/pkg/hints/memcpy_hints.go @@ -1,6 +1,7 @@ package hints import ( + "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" ) @@ -10,3 +11,13 @@ func add_segment(vm *VirtualMachine) error { new_segment_base := vm.Segments.AddSegment() return vm.Segments.Memory.Insert(vm.RunContext.Ap, NewMaybeRelocatableRelocatable(new_segment_base)) } + +// Implements hint: +// %{ vm_exit_scope() %} +func vm_exit_scope(executionScopes *types.ExecutionScopes) error { + err := executionScopes.ExitScope() + if err != nil { + return err + } + return nil +} diff --git a/pkg/hints/memcpy_hints_test.go b/pkg/hints/memcpy_hints_test.go index 38a1c268..285d4ff2 100644 --- a/pkg/hints/memcpy_hints_test.go +++ b/pkg/hints/memcpy_hints_test.go @@ -4,7 +4,11 @@ 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 AddSegmentHintOk(t *testing.T) { @@ -22,3 +26,30 @@ func AddSegmentHintOk(t *testing.T) { t.Errorf("ADD_SEGMENT fail expected: %d segments, got: %d", initial_segments+1, vm.Segments.Memory.NumSegments()) } } + +func TestExitScopeValid(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "a": {NewMaybeRelocatableFelt(FeltFromUint64(17))}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: VM_EXIT_SCOPE, + }) + + executionScopes := NewExecutionScopes() + scope := make(map[string]interface{}) + scope["a"] = FeltOne() + executionScopes.EnterScope(scope) + + err := hintProcessor.ExecuteHint(vm, &hintData, nil, executionScopes) + if err != nil { + t.Errorf("VM_EXIT_SCOPE hint test failed with error %s", err) + } + +} From 603bd0dabaf941b1ec96331537248778abd5c70a Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 13 Sep 2023 13:26:53 -0300 Subject: [PATCH 22/74] Integrate ExecutionScopes into hintProcessor logic --- pkg/hints/hint_processor.go | 3 ++- pkg/hints/hint_processor_test.go | 4 ++-- pkg/hints/math_hints_test.go | 26 +++++++++++++------------- pkg/hints/memcpy_hints_test.go | 2 +- pkg/runners/cairo_runner.go | 3 +++ pkg/vm/hint_processor_interface.go | 4 ++-- pkg/vm/vm_core.go | 5 +++-- 7 files changed, 26 insertions(+), 21 deletions(-) diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index 3e1bf5b2..3a35e379 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -7,6 +7,7 @@ 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/parser" + "github.com/lambdaclass/cairo-vm.go/pkg/types" "github.com/lambdaclass/cairo-vm.go/pkg/vm" ) @@ -32,7 +33,7 @@ func (p *CairoVmHintProcessor) CompileHint(hintParams *parser.HintParams, refere return HintData{Ids: ids, Code: hintParams.Code}, nil } -func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, constants *map[string]Felt) error { +func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, constants *map[string]Felt, execScopes *types.ExecutionScopes) error { data, ok := (*hintData).(HintData) if !ok { return errors.New("Wrong Hint Data") diff --git a/pkg/hints/hint_processor_test.go b/pkg/hints/hint_processor_test.go index e7135961..cad19c5c 100644 --- a/pkg/hints/hint_processor_test.go +++ b/pkg/hints/hint_processor_test.go @@ -91,7 +91,7 @@ func TestExecuteHintWrongHintData(t *testing.T) { hintProcessor := &CairoVmHintProcessor{} hintData := any("Mistake") vm := vm.NewVirtualMachine() - err := hintProcessor.ExecuteHint(vm, &hintData, nil) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) if err == nil { t.Errorf("Should have failed") } @@ -101,7 +101,7 @@ func TestExecuteHintUnknownHint(t *testing.T) { hintProcessor := &CairoVmHintProcessor{} hintData := any(HintData{Code: "print(Hello World)"}) vm := vm.NewVirtualMachine() - err := hintProcessor.ExecuteHint(vm, &hintData, nil) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) if err == nil { t.Errorf("Should have failed") } diff --git a/pkg/hints/math_hints_test.go b/pkg/hints/math_hints_test.go index d301ad45..efd08f43 100644 --- a/pkg/hints/math_hints_test.go +++ b/pkg/hints/math_hints_test.go @@ -24,7 +24,7 @@ func TestIsNNHintOk(t *testing.T) { Ids: idsManager, Code: ASSERT_NN, }) - err := hintProcessor.ExecuteHint(vm, &hintData, nil) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) if err != nil { t.Errorf("ASSERT_NN hint test failed with error %s", err) } @@ -44,7 +44,7 @@ func TestIsNNHintFail(t *testing.T) { Ids: idsManager, Code: ASSERT_NN, }) - err := hintProcessor.ExecuteHint(vm, &hintData, nil) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) if err == nil { t.Errorf("ASSERT_NN hint should have failed") } @@ -65,7 +65,7 @@ func TestIsPositiveOkPositive(t *testing.T) { Ids: idsManager, Code: IS_POSITIVE, }) - err := hintProcessor.ExecuteHint(vm, &hintData, nil) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) if err != nil { t.Errorf("IS_POSITIVE hint test failed with error %s", err) } @@ -91,7 +91,7 @@ func TestIsPositiveOkNegative(t *testing.T) { Ids: idsManager, Code: IS_POSITIVE, }) - err := hintProcessor.ExecuteHint(vm, &hintData, nil) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) if err != nil { t.Errorf("IS_POSITIVE hint test failed with error %s", err) } @@ -117,7 +117,7 @@ func TestIsPositiveOutOfRange(t *testing.T) { Ids: idsManager, Code: IS_POSITIVE, }) - err := hintProcessor.ExecuteHint(vm, &hintData, nil) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) if err == nil { t.Errorf("IS_POSITIVE hint test should have failed") } @@ -136,7 +136,7 @@ func TestAssertNotZeroHintOk(t *testing.T) { Ids: idsManager, Code: ASSERT_NOT_ZERO, }) - err := hintProcessor.ExecuteHint(vm, &hintData, nil) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) if err != nil { t.Errorf("ASSERT_NOT_ZERO hint test failed with error %s", err) } @@ -156,7 +156,7 @@ func TestAssertNotZeroHintFail(t *testing.T) { Ids: idsManager, Code: ASSERT_NOT_ZERO, }) - err := hintProcessor.ExecuteHint(vm, &hintData, nil) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) if err == nil { t.Errorf("ASSERT_NOT_ZERO hint should have failed") } @@ -177,7 +177,7 @@ func TestAssertNotEqualHintNonComparableDiffType(t *testing.T) { Ids: idsManager, Code: ASSERT_NOT_EQUAL, }) - err := hintProcessor.ExecuteHint(vm, &hintData, nil) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) if err == nil { t.Errorf("ASSERT_NOT_EQUAL hint should have failed") } @@ -198,7 +198,7 @@ func TestAssertNotEqualHintNonComparableDiffIndex(t *testing.T) { Ids: idsManager, Code: ASSERT_NOT_EQUAL, }) - err := hintProcessor.ExecuteHint(vm, &hintData, nil) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) if err == nil { t.Errorf("ASSERT_NOT_EQUAL hint should have failed") } @@ -219,7 +219,7 @@ func TestAssertNotEqualHintEqualRelocatables(t *testing.T) { Ids: idsManager, Code: ASSERT_NOT_EQUAL, }) - err := hintProcessor.ExecuteHint(vm, &hintData, nil) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) if err == nil { t.Errorf("ASSERT_NOT_EQUAL hint should have failed") } @@ -240,7 +240,7 @@ func TestAssertNotEqualHintEqualFelts(t *testing.T) { Ids: idsManager, Code: ASSERT_NOT_EQUAL, }) - err := hintProcessor.ExecuteHint(vm, &hintData, nil) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) if err == nil { t.Errorf("ASSERT_NOT_EQUAL hint should have failed") } @@ -261,7 +261,7 @@ func TestAssertNotEqualHintOkFelts(t *testing.T) { Ids: idsManager, Code: ASSERT_NOT_EQUAL, }) - err := hintProcessor.ExecuteHint(vm, &hintData, nil) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) if err != nil { t.Errorf("ASSERT_NOT_EQUAL hint failed with error: %s", err) } @@ -282,7 +282,7 @@ func TestAssertNotEqualHintOkRelocatables(t *testing.T) { Ids: idsManager, Code: ASSERT_NOT_EQUAL, }) - err := hintProcessor.ExecuteHint(vm, &hintData, nil) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) if err != nil { t.Errorf("ASSERT_NOT_EQUAL hint failed with error: %s", err) } diff --git a/pkg/hints/memcpy_hints_test.go b/pkg/hints/memcpy_hints_test.go index 2ca358a8..38a1c268 100644 --- a/pkg/hints/memcpy_hints_test.go +++ b/pkg/hints/memcpy_hints_test.go @@ -14,7 +14,7 @@ func AddSegmentHintOk(t *testing.T) { hintData := any(HintData{ Code: ADD_SEGMENT, }) - err := hintProcessor.ExecuteHint(vm, &hintData, nil) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) if err != nil { t.Errorf("ADD_SEGMENT hint test failed with error %s", err) } diff --git a/pkg/runners/cairo_runner.go b/pkg/runners/cairo_runner.go index dc257d29..53ee0b6d 100644 --- a/pkg/runners/cairo_runner.go +++ b/pkg/runners/cairo_runner.go @@ -3,6 +3,7 @@ package runners import ( "github.com/lambdaclass/cairo-vm.go/pkg/builtins" "github.com/lambdaclass/cairo-vm.go/pkg/layouts" + "github.com/lambdaclass/cairo-vm.go/pkg/types" "github.com/lambdaclass/cairo-vm.go/pkg/utils" "github.com/lambdaclass/cairo-vm.go/pkg/vm" "github.com/lambdaclass/cairo-vm.go/pkg/vm/memory" @@ -21,6 +22,7 @@ type CairoRunner struct { mainOffset uint layout layouts.CairoLayout proofMode bool + execScopes types.ExecutionScopes } func NewCairoRunner(program vm.Program, layoutName string, proofMode bool) (*CairoRunner, error) { @@ -46,6 +48,7 @@ func NewCairoRunner(program vm.Program, layoutName string, proofMode bool) (*Cai mainOffset: main_offset, proofMode: proofMode, layout: layout, + execScopes: *types.NewExecutionScopes(), } return &runner, nil } diff --git a/pkg/vm/hint_processor_interface.go b/pkg/vm/hint_processor_interface.go index fe18f663..3aba4c2e 100644 --- a/pkg/vm/hint_processor_interface.go +++ b/pkg/vm/hint_processor_interface.go @@ -3,12 +3,12 @@ package vm import ( "github.com/lambdaclass/cairo-vm.go/pkg/lambdaworks" "github.com/lambdaclass/cairo-vm.go/pkg/parser" + "github.com/lambdaclass/cairo-vm.go/pkg/types" ) type HintProcessor interface { // Transforms hint data outputed by the VM into whichever format will be later used by ExecuteHint CompileHint(hintParams *parser.HintParams, referenceManager *parser.ReferenceManager) (any, error) // Executes the hint which's data is provided by a dynamic structure previously created by CompileHint - // TODO: add * ExecScopes arg when ready - ExecuteHint(vm *VirtualMachine, hintData *any, constants *map[string]lambdaworks.Felt) error + ExecuteHint(vm *VirtualMachine, hintData *any, constants *map[string]lambdaworks.Felt, execScopes *types.ExecutionScopes) error } diff --git a/pkg/vm/vm_core.go b/pkg/vm/vm_core.go index 1a8c9935..9cd1d1de 100644 --- a/pkg/vm/vm_core.go +++ b/pkg/vm/vm_core.go @@ -7,6 +7,7 @@ import ( "github.com/lambdaclass/cairo-vm.go/pkg/builtins" "github.com/lambdaclass/cairo-vm.go/pkg/lambdaworks" + "github.com/lambdaclass/cairo-vm.go/pkg/types" "github.com/lambdaclass/cairo-vm.go/pkg/vm/memory" ) @@ -38,12 +39,12 @@ func NewVirtualMachine() *VirtualMachine { return &VirtualMachine{Segments: segments, BuiltinRunners: builtin_runners, Trace: trace, RelocatedTrace: relocatedTrace} } -func (v *VirtualMachine) Step(hintProcessor HintProcessor, hintDataMap *map[uint][]any, constants *map[string]lambdaworks.Felt) error { +func (v *VirtualMachine) Step(hintProcessor HintProcessor, hintDataMap *map[uint][]any, constants *map[string]lambdaworks.Felt, execScopes *types.ExecutionScopes) error { // Run Hint hintDatas, ok := (*hintDataMap)[v.RunContext.Pc.Offset] if ok { for i := 0; i < len(hintDatas); i++ { - err := hintProcessor.ExecuteHint(v, &hintDatas[i], constants) + err := hintProcessor.ExecuteHint(v, &hintDatas[i], constants, execScopes) if err != nil { return err } From a0866d017db819a1844ff2f70a30b96704ec488c Mon Sep 17 00:00:00 2001 From: toni-calvin Date: Wed, 13 Sep 2023 18:47:31 +0200 Subject: [PATCH 23/74] add test --- pkg/hints/memcpy_hints_test.go | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/pkg/hints/memcpy_hints_test.go b/pkg/hints/memcpy_hints_test.go index 285d4ff2..758b0717 100644 --- a/pkg/hints/memcpy_hints_test.go +++ b/pkg/hints/memcpy_hints_test.go @@ -49,7 +49,33 @@ func TestExitScopeValid(t *testing.T) { err := hintProcessor.ExecuteHint(vm, &hintData, nil, executionScopes) if err != nil { - t.Errorf("VM_EXIT_SCOPE hint test failed with error %s", err) + t.Errorf("TestExitScopeValid failed with error %s", err) + } + +} + +func TestExitScopeInvalid(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "a": {NewMaybeRelocatableFelt(FeltFromUint64(17))}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: VM_EXIT_SCOPE, + }) + + executionScopes := NewExecutionScopes() + scope := make(map[string]interface{}) + scope["a"] = FeltOne() + + err := hintProcessor.ExecuteHint(vm, &hintData, nil, executionScopes) + if err.Error() != ErrCannotExitMainScop.Error() { + t.Errorf("TestExitScopeInvalid should fail with error %s", ErrCannotExitMainScop) } } From c7c8b49275785ef93b443e8fdd567d5307408a37 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 13 Sep 2023 14:01:04 -0300 Subject: [PATCH 24/74] Fixes + add test --- pkg/hints/dict_hints.go | 4 +-- pkg/hints/dict_hints_test.go | 45 +++++++++++++++++++++++++++ pkg/hints/hint_processor.go | 2 ++ pkg/hints/hint_utils/testing_utils.go | 5 +-- pkg/runners/cairo_runner.go | 2 +- 5 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 pkg/hints/dict_hints_test.go diff --git a/pkg/hints/dict_hints.go b/pkg/hints/dict_hints.go index 7b2533d2..5a281ed6 100644 --- a/pkg/hints/dict_hints.go +++ b/pkg/hints/dict_hints.go @@ -16,7 +16,7 @@ import ( // initial_dict, ok := initial_dict_any.(map[MaybeRelocatable]MaybeRelocatable) // } -func fetchDictManager(scopes *ExecutionScopes) (*DictManager, bool) { +func FetchDictManager(scopes *ExecutionScopes) (*DictManager, bool) { dictManager, err := scopes.Get("__dict_manager") if err != nil { return nil, false @@ -30,7 +30,7 @@ func defaultDictNew(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) if err != nil { return err } - dictManager, ok := fetchDictManager(scopes) + dictManager, ok := FetchDictManager(scopes) if !ok { newDictManager := NewDictManager() dictManager = &newDictManager diff --git a/pkg/hints/dict_hints_test.go b/pkg/hints/dict_hints_test.go new file mode 100644 index 00000000..e05d099b --- /dev/null +++ b/pkg/hints/dict_hints_test.go @@ -0,0 +1,45 @@ +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 TestDefaultDictNewCreateManager(t *testing.T) { + vm := NewVirtualMachine() + scopes := types.NewExecutionScopes() + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "default_value": {NewMaybeRelocatableFelt(FeltFromUint64(17))}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: DEFAULT_DICT_NEW, + }) + // Advance AP so that values don't clash with FP-based ids + vm.RunContext.Ap = NewRelocatable(0, 5) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("DEFAULT_DICT_NEW hint test failed with error %s", err) + } + // Check that a manager was created in the scope + _, ok := FetchDictManager(scopes) + if !ok { + t.Error("DEFAULT_DICT_NEW No DictManager created") + } + // Check that the correct base was inserted into ap + val, _ := vm.Segments.Memory.Get(vm.RunContext.Ap) + if val == nil || *val != *NewMaybeRelocatableRelocatable(NewRelocatable(1, 0)) { + t.Error("DEFAULT_DICT_NEW Wrong/No base inserted into ap") + } +} diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index 3a35e379..67b85310 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -49,6 +49,8 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, return assert_not_zero(data.Ids, vm) case ASSERT_NOT_EQUAL: return assert_not_equal(data.Ids, vm) + case DEFAULT_DICT_NEW: + return defaultDictNew(data.Ids, execScopes, vm) default: return errors.New("Unknown Hint") } diff --git a/pkg/hints/hint_utils/testing_utils.go b/pkg/hints/hint_utils/testing_utils.go index d380bdd9..ab2470cd 100644 --- a/pkg/hints/hint_utils/testing_utils.go +++ b/pkg/hints/hint_utils/testing_utils.go @@ -2,7 +2,7 @@ package hint_utils import ( "github.com/lambdaclass/cairo-vm.go/pkg/parser" - "github.com/lambdaclass/cairo-vm.go/pkg/vm" + . "github.com/lambdaclass/cairo-vm.go/pkg/vm" "github.com/lambdaclass/cairo-vm.go/pkg/vm/memory" ) @@ -16,7 +16,7 @@ import ( // Considerations: // All references will be FP-based, so please don't update the value of FP after calling this function, // and make sure that the memory at fp's segment is clear from its current offset onwards -func SetupIdsForTest(ids map[string][]*memory.MaybeRelocatable, vm *vm.VirtualMachine) IdsManager { +func SetupIdsForTest(ids map[string][]*memory.MaybeRelocatable, vm *VirtualMachine) IdsManager { manager := NewIdsManager(make(map[string]HintReference), parser.ApTrackingData{}) base_addr := vm.RunContext.Fp current_offset := 0 @@ -27,6 +27,7 @@ func SetupIdsForTest(ids map[string][]*memory.MaybeRelocatable, vm *vm.VirtualMa Offset1: OffsetValue{ ValueType: Reference, Value: current_offset, + Register: FP, }, } // Update current_offset diff --git a/pkg/runners/cairo_runner.go b/pkg/runners/cairo_runner.go index 53ee0b6d..8bf69ac6 100644 --- a/pkg/runners/cairo_runner.go +++ b/pkg/runners/cairo_runner.go @@ -186,7 +186,7 @@ func (r *CairoRunner) RunUntilPC(end memory.Relocatable, hintProcessor vm.HintPr } constants := r.Program.ExtractConstants() for r.Vm.RunContext.Pc != end { - err := r.Vm.Step(hintProcessor, &hintDataMap, &constants) + err := r.Vm.Step(hintProcessor, &hintDataMap, &constants, &r.execScopes) if err != nil { return err } From ba629309a8ae6874b18cb99c45a4b6c53f1ea637 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 13 Sep 2023 14:05:09 -0300 Subject: [PATCH 25/74] Add test --- pkg/hints/dict_hints_test.go | 38 ++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/pkg/hints/dict_hints_test.go b/pkg/hints/dict_hints_test.go index e05d099b..54ba9c85 100644 --- a/pkg/hints/dict_hints_test.go +++ b/pkg/hints/dict_hints_test.go @@ -4,6 +4,7 @@ import ( "testing" . "github.com/lambdaclass/cairo-vm.go/pkg/hints" + "github.com/lambdaclass/cairo-vm.go/pkg/hints/dict_manager" . "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" @@ -43,3 +44,40 @@ func TestDefaultDictNewCreateManager(t *testing.T) { t.Error("DEFAULT_DICT_NEW Wrong/No base inserted into ap") } } + +func TestDefaultDictNewHasManager(t *testing.T) { + vm := NewVirtualMachine() + scopes := types.NewExecutionScopes() + // Create dictManager & add it to scope + dictManager := dict_manager.NewDictManager() + dictManagerRef := &dictManager + scopes.AssignOrUpdateVariable("__dict_manager", dictManagerRef) + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "default_value": {NewMaybeRelocatableFelt(FeltFromUint64(17))}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: DEFAULT_DICT_NEW, + }) + // Advance AP so that values don't clash with FP-based ids + vm.RunContext.Ap = NewRelocatable(0, 5) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("DEFAULT_DICT_NEW hint test failed with error %s", err) + } + // Check that the manager wan't replaced by a new one + dictManagerPtr, ok := FetchDictManager(scopes) + if !ok || dictManagerPtr != dictManagerRef { + t.Error("DEFAULT_DICT_NEW DictManager replaced") + } + // Check that the correct base was inserted into ap + val, _ := vm.Segments.Memory.Get(vm.RunContext.Ap) + if val == nil || *val != *NewMaybeRelocatableRelocatable(NewRelocatable(1, 0)) { + t.Error("DEFAULT_DICT_NEW Wrong/No base inserted into ap") + } +} From 6b4f740edb5d3fb9b0a863252cf0784be0dd9e65 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 13 Sep 2023 14:06:05 -0300 Subject: [PATCH 26/74] Remove commented fn --- pkg/hints/dict_hints.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pkg/hints/dict_hints.go b/pkg/hints/dict_hints.go index 5a281ed6..7c4ef1b6 100644 --- a/pkg/hints/dict_hints.go +++ b/pkg/hints/dict_hints.go @@ -8,14 +8,6 @@ import ( "github.com/lambdaclass/cairo-vm.go/pkg/vm/memory" ) -// func copyInitialDict(scopes ExecutionScopes) (*map[MaybeRelocatable]MaybeRelocatable, error) { -// initial_dict_any, err := scopes.Get("initial_dict") -// if err != nil { -// return nil, err -// } -// initial_dict, ok := initial_dict_any.(map[MaybeRelocatable]MaybeRelocatable) -// } - func FetchDictManager(scopes *ExecutionScopes) (*DictManager, bool) { dictManager, err := scopes.Get("__dict_manager") if err != nil { From 8c80b5aa8a4c24e78477001835c3b793ca6ca4f2 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 13 Sep 2023 15:04:18 -0300 Subject: [PATCH 27/74] Implement dictRead --- pkg/hints/dict_hint_codes.go | 2 ++ pkg/hints/dict_hints.go | 32 ++++++++++++++++++++++++++ pkg/hints/dict_manager/dict_manager.go | 10 ++++---- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/pkg/hints/dict_hint_codes.go b/pkg/hints/dict_hint_codes.go index f3c4a86b..89de7550 100644 --- a/pkg/hints/dict_hint_codes.go +++ b/pkg/hints/dict_hint_codes.go @@ -1,3 +1,5 @@ package hints const DEFAULT_DICT_NEW = "if '__dict_manager' not in globals():\n from starkware.cairo.common.dict import DictManager\n __dict_manager = DictManager()\n\nmemory[ap] = __dict_manager.new_default_dict(segments, ids.default_value)" + +const DICT_READ = "dict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ndict_tracker.current_ptr += ids.DictAccess.SIZE\nids.value = dict_tracker.data[ids.key]" diff --git a/pkg/hints/dict_hints.go b/pkg/hints/dict_hints.go index 7c4ef1b6..a9fe45f4 100644 --- a/pkg/hints/dict_hints.go +++ b/pkg/hints/dict_hints.go @@ -1,6 +1,8 @@ package hints import ( + "errors" + . "github.com/lambdaclass/cairo-vm.go/pkg/hints/dict_manager" . "github.com/lambdaclass/cairo-vm.go/pkg/hints/hint_utils" . "github.com/lambdaclass/cairo-vm.go/pkg/types" @@ -8,6 +10,8 @@ import ( "github.com/lambdaclass/cairo-vm.go/pkg/vm/memory" ) +const DICT_ACCESS_SIZE = 3 + func FetchDictManager(scopes *ExecutionScopes) (*DictManager, bool) { dictManager, err := scopes.Get("__dict_manager") if err != nil { @@ -31,3 +35,31 @@ func defaultDictNew(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) base := dictManager.NewDefaultDictionary(defaultValue, vm) return vm.Segments.Memory.Insert(vm.RunContext.Ap, memory.NewMaybeRelocatableRelocatable(base)) } + +func dictRead(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error { + // Extract Variables + dictManager, ok := FetchDictManager(scopes) + if !ok { + return errors.New("Variable __dict_manager not present in current execution scope") + } + dict_ptr, err := ids.GetRelocatable("dict_ptr", vm) + if err != nil { + return err + } + key, err := ids.Get("key", vm) + if err != nil { + return err + } + // Hint Logic + tracker, err := dictManager.GetTracker(dict_ptr) + if err != nil { + return err + } + tracker.CurrentPtr.Offset += DICT_ACCESS_SIZE + val, err := tracker.GetValue(key) + if err != nil { + return err + } + ids.Insert("value", val, vm) + return nil +} diff --git a/pkg/hints/dict_manager/dict_manager.go b/pkg/hints/dict_manager/dict_manager.go index 6ceb36dc..dd5e809b 100644 --- a/pkg/hints/dict_manager/dict_manager.go +++ b/pkg/hints/dict_manager/dict_manager.go @@ -35,8 +35,8 @@ func (d *DictManager) GetTracker(dict_ptr Relocatable) (*DictTracker, error) { if !ok { return nil, errors.Errorf("Dict Error: No dict tracker found for segment %d", dict_ptr.SegmentIndex) } - if tracker.currentPtr != dict_ptr { - return nil, errors.Errorf("Dict Error: Wrong dict pointer supplied. Got %v, expected %v", dict_ptr, tracker.currentPtr) + if tracker.CurrentPtr != dict_ptr { + return nil, errors.Errorf("Dict Error: Wrong dict pointer supplied. Got %v, expected %v", dict_ptr, tracker.CurrentPtr) } return &tracker, nil } @@ -45,20 +45,20 @@ func (d *DictManager) GetTracker(dict_ptr Relocatable) (*DictTracker, error) { type DictTracker struct { data Dictionary // Pointer to the first unused position in the dict segment. - currentPtr Relocatable + CurrentPtr Relocatable } func NewDictTrackerForDictionary(base Relocatable, dict *map[MaybeRelocatable]MaybeRelocatable) DictTracker { return DictTracker{ data: NewDictionary(dict), - currentPtr: base, + CurrentPtr: base, } } func NewDictTrackerForDefaultDictionary(base Relocatable, defaultValue *MaybeRelocatable) DictTracker { return DictTracker{ data: NewDefaultDictionary(defaultValue), - currentPtr: base, + CurrentPtr: base, } } From 1f7d9a8e17a31dadeb147b7ed3f740aa3b041afa Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 13 Sep 2023 17:02:47 -0300 Subject: [PATCH 28/74] Add tests --- pkg/hints/dict_hints_test.go | 102 +++++++++++++++++++++++++++++++++++ pkg/hints/hint_processor.go | 2 + 2 files changed, 104 insertions(+) diff --git a/pkg/hints/dict_hints_test.go b/pkg/hints/dict_hints_test.go index 54ba9c85..19c610af 100644 --- a/pkg/hints/dict_hints_test.go +++ b/pkg/hints/dict_hints_test.go @@ -81,3 +81,105 @@ func TestDefaultDictNewHasManager(t *testing.T) { t.Error("DEFAULT_DICT_NEW Wrong/No base inserted into ap") } } + +func TestDictReadDefaultValue(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + + // Create dictManager with a default dictionary & add it to scope + dictManager := dict_manager.NewDictManager() + defaultValue := NewMaybeRelocatableFelt(FeltFromUint64(17)) + dict_ptr := dictManager.NewDefaultDictionary(defaultValue, vm) + scopes.AssignOrUpdateVariable("__dict_manager", &dictManager) + + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "key": {NewMaybeRelocatableFelt(FeltOne())}, + "dict_ptr": {NewMaybeRelocatableRelocatable(dict_ptr)}, + "value": {nil}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: DICT_READ, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("DICT_READ hint test failed with error %s", err) + } + // Check ids.value + val, err := idsManager.GetFelt("value", vm) + if err != nil || val != FeltFromUint64(17) { + t.Error("DEFAULT_DICT_NEW Wrong/No ids.value") + } +} + +func TestDictReadOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + + // Create dictManager with a default dictionary & add it to scope + dictManager := dict_manager.NewDictManager() + initialDict := map[MaybeRelocatable]MaybeRelocatable{ + *NewMaybeRelocatableFelt(FeltOne()): *NewMaybeRelocatableFelt(FeltFromUint64(7)), + } + dict_ptr := dictManager.NewDictionary(&initialDict, vm) + scopes.AssignOrUpdateVariable("__dict_manager", &dictManager) + + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "key": {NewMaybeRelocatableFelt(FeltOne())}, + "dict_ptr": {NewMaybeRelocatableRelocatable(dict_ptr)}, + "value": {nil}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: DICT_READ, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("DICT_READ hint test failed with error %s", err) + } + // Check ids.value + val, err := idsManager.GetFelt("value", vm) + if err != nil || val != FeltFromUint64(7) { + t.Error("DEFAULT_DICT_NEW Wrong/No ids.value") + } +} + +func TestDictReadNoVal(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + + // Create dictManager with a default dictionary & add it to scope + dictManager := dict_manager.NewDictManager() + initialDict := map[MaybeRelocatable]MaybeRelocatable{} + dict_ptr := dictManager.NewDictionary(&initialDict, vm) + scopes.AssignOrUpdateVariable("__dict_manager", &dictManager) + + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "key": {NewMaybeRelocatableFelt(FeltOne())}, + "dict_ptr": {NewMaybeRelocatableRelocatable(dict_ptr)}, + "value": {nil}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: DICT_READ, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Errorf("DICT_READ hint test should have failed") + } +} diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index 67b85310..a18f4460 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -51,6 +51,8 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, return assert_not_equal(data.Ids, vm) case DEFAULT_DICT_NEW: return defaultDictNew(data.Ids, execScopes, vm) + case DICT_READ: + return dictRead(data.Ids, execScopes, vm) default: return errors.New("Unknown Hint") } From 4e35bf19e4f33b47c13133d55b56a0edb464bb09 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 13 Sep 2023 17:15:43 -0300 Subject: [PATCH 29/74] Add test --- pkg/hints/dict_hint_codes.go | 2 + pkg/hints/dict_hints.go | 33 +++++++++++ pkg/hints/dict_hints_test.go | 105 +++++++++++++++++++++++++++++++++++ pkg/hints/hint_processor.go | 2 + 4 files changed, 142 insertions(+) diff --git a/pkg/hints/dict_hint_codes.go b/pkg/hints/dict_hint_codes.go index 89de7550..95354dfa 100644 --- a/pkg/hints/dict_hint_codes.go +++ b/pkg/hints/dict_hint_codes.go @@ -3,3 +3,5 @@ package hints const DEFAULT_DICT_NEW = "if '__dict_manager' not in globals():\n from starkware.cairo.common.dict import DictManager\n __dict_manager = DictManager()\n\nmemory[ap] = __dict_manager.new_default_dict(segments, ids.default_value)" const DICT_READ = "dict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ndict_tracker.current_ptr += ids.DictAccess.SIZE\nids.value = dict_tracker.data[ids.key]" + +const DICT_WRITE = "dict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ndict_tracker.current_ptr += ids.DictAccess.SIZE\nids.dict_ptr.prev_value = dict_tracker.data[ids.key]\ndict_tracker.data[ids.key] = ids.new_value" diff --git a/pkg/hints/dict_hints.go b/pkg/hints/dict_hints.go index a9fe45f4..2a19c369 100644 --- a/pkg/hints/dict_hints.go +++ b/pkg/hints/dict_hints.go @@ -63,3 +63,36 @@ func dictRead(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error ids.Insert("value", val, vm) return nil } + +func dictWrite(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error { + // Extract Variables + dictManager, ok := FetchDictManager(scopes) + if !ok { + return errors.New("Variable __dict_manager not present in current execution scope") + } + dict_ptr, err := ids.GetRelocatable("dict_ptr", vm) + if err != nil { + return err + } + key, err := ids.Get("key", vm) + if err != nil { + return err + } + new_value, err := ids.Get("new_value", vm) + if err != nil { + return err + } + // Hint Logic + tracker, err := dictManager.GetTracker(dict_ptr) + if err != nil { + return err + } + tracker.CurrentPtr.Offset += DICT_ACCESS_SIZE + prev_val, err := tracker.GetValue(key) + if err != nil { + return err + } + ids.Insert("prev_value", prev_val, vm) + tracker.InsertValue(key, new_value) + return nil +} diff --git a/pkg/hints/dict_hints_test.go b/pkg/hints/dict_hints_test.go index 19c610af..c31afc8b 100644 --- a/pkg/hints/dict_hints_test.go +++ b/pkg/hints/dict_hints_test.go @@ -183,3 +183,108 @@ func TestDictReadNoVal(t *testing.T) { t.Errorf("DICT_READ hint test should have failed") } } + +func TestDictWriteOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + + // Create dictManager with a default dictionary & add it to scope + dictManager := dict_manager.NewDictManager() + initialDict := map[MaybeRelocatable]MaybeRelocatable{ + *NewMaybeRelocatableFelt(FeltOne()): *NewMaybeRelocatableFelt(FeltFromUint64(7)), + } + dict_ptr := dictManager.NewDictionary(&initialDict, vm) + scopes.AssignOrUpdateVariable("__dict_manager", &dictManager) + + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "key": {NewMaybeRelocatableFelt(FeltOne())}, + "dict_ptr": {NewMaybeRelocatableRelocatable(dict_ptr)}, + "prev_value": {nil}, + "new_value": {NewMaybeRelocatableFelt(FeltFromUint64(17))}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: DICT_WRITE, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("DICT_WRITE hint test failed with error %s", err) + } + // Check ids.prev_value + val, err := idsManager.GetFelt("prev_value", vm) + if err != nil || val != FeltFromUint64(7) { + t.Error("DICT_WRITE Wrong/No ids.value") + } +} + +func TestDictWriteNoPrevValue(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + + // Create dictManager with a default dictionary & add it to scope + dictManager := dict_manager.NewDictManager() + initialDict := map[MaybeRelocatable]MaybeRelocatable{} + dict_ptr := dictManager.NewDictionary(&initialDict, vm) + scopes.AssignOrUpdateVariable("__dict_manager", &dictManager) + + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "key": {NewMaybeRelocatableFelt(FeltOne())}, + "dict_ptr": {NewMaybeRelocatableRelocatable(dict_ptr)}, + "prev_value": {nil}, + "new_value": {NewMaybeRelocatableFelt(FeltFromUint64(17))}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: DICT_WRITE, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Error("DICT_WRITE hint test should have failed") + } +} + +func TestDictWriteNewWriteDefault(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + + // Create dictManager with a default dictionary & add it to scope + dictManager := dict_manager.NewDictManager() + defaultValue := FeltFromUint64(17) + dict_ptr := dictManager.NewDefaultDictionary(NewMaybeRelocatableFelt(defaultValue), vm) + scopes.AssignOrUpdateVariable("__dict_manager", &dictManager) + + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "key": {NewMaybeRelocatableFelt(FeltOne())}, + "dict_ptr": {NewMaybeRelocatableRelocatable(dict_ptr)}, + "prev_value": {nil}, + "new_value": {NewMaybeRelocatableFelt(FeltFromUint64(17))}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: DICT_WRITE, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("DICT_WRITE hint test failed with error %s", err) + } + // Check ids.prev_value + val, err := idsManager.GetFelt("prev_value", vm) + if err != nil || val != defaultValue { + t.Error("DICT_WRITE Wrong/No ids.value") + } +} diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index a18f4460..b2d8b4a3 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -53,6 +53,8 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, return defaultDictNew(data.Ids, execScopes, vm) case DICT_READ: return dictRead(data.Ids, execScopes, vm) + case DICT_WRITE: + return dictWrite(data.Ids, execScopes, vm) default: return errors.New("Unknown Hint") } From 199313e7218352a259a1c5dc14e7efbb10be431d Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 13 Sep 2023 17:25:04 -0300 Subject: [PATCH 30/74] Store DictTrackers by reference to allow modifications --- pkg/hints/dict_manager/dict_manager.go | 12 +++++++----- pkg/vm/cairo_run/cairo_run_test.go | 7 +++++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/pkg/hints/dict_manager/dict_manager.go b/pkg/hints/dict_manager/dict_manager.go index dd5e809b..5957e392 100644 --- a/pkg/hints/dict_manager/dict_manager.go +++ b/pkg/hints/dict_manager/dict_manager.go @@ -9,24 +9,26 @@ import ( // Manages dictionaries in a Cairo program. // Uses the segment index to associate the corresponding go dict with the Cairo dict. type DictManager struct { - trackers map[int]DictTracker + trackers map[int]*DictTracker } func NewDictManager() DictManager { return DictManager{ - trackers: make(map[int]DictTracker), + trackers: make(map[int]*DictTracker), } } func (d *DictManager) NewDictionary(dict *map[MaybeRelocatable]MaybeRelocatable, vm *VirtualMachine) Relocatable { base := vm.Segments.AddSegment() - d.trackers[base.SegmentIndex] = NewDictTrackerForDictionary(base, dict) + newTracker := NewDictTrackerForDictionary(base, dict) + d.trackers[base.SegmentIndex] = &newTracker return base } func (d *DictManager) NewDefaultDictionary(defaultValue *MaybeRelocatable, vm *VirtualMachine) Relocatable { base := vm.Segments.AddSegment() - d.trackers[base.SegmentIndex] = NewDictTrackerForDefaultDictionary(base, defaultValue) + newTracker := NewDictTrackerForDefaultDictionary(base, defaultValue) + d.trackers[base.SegmentIndex] = &newTracker return base } @@ -38,7 +40,7 @@ func (d *DictManager) GetTracker(dict_ptr Relocatable) (*DictTracker, error) { if tracker.CurrentPtr != dict_ptr { return nil, errors.Errorf("Dict Error: Wrong dict pointer supplied. Got %v, expected %v", dict_ptr, tracker.CurrentPtr) } - return &tracker, nil + return tracker, nil } // Tracks the go dict associated with a Cairo dict. diff --git a/pkg/vm/cairo_run/cairo_run_test.go b/pkg/vm/cairo_run/cairo_run_test.go index 7b72c5e4..e14c7005 100644 --- a/pkg/vm/cairo_run/cairo_run_test.go +++ b/pkg/vm/cairo_run/cairo_run_test.go @@ -113,3 +113,10 @@ func TestAssertNotEqualHint(t *testing.T) { t.Errorf("Program execution failed with error: %s", err) } } + +func TestDict(t *testing.T) { + _, err := cairo_run.CairoRun("../../../cairo_programs/dict.json", "small", false) + if err != nil { + t.Errorf("Program execution failed with error: %s", err) + } +} From fe57bc2b878c04bee47dcd56df823bbe1a9e66c5 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 13 Sep 2023 17:25:30 -0300 Subject: [PATCH 31/74] Add test file --- cairo_programs/dict.cairo | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 cairo_programs/dict.cairo diff --git a/cairo_programs/dict.cairo b/cairo_programs/dict.cairo new file mode 100644 index 00000000..6a9fc065 --- /dev/null +++ b/cairo_programs/dict.cairo @@ -0,0 +1,27 @@ +from starkware.cairo.common.dict import dict_read, dict_write +from starkware.cairo.common.default_dict import default_dict_new +from starkware.cairo.common.dict_access import DictAccess + +func main() { + alloc_locals; + let (local my_dict: DictAccess*) = default_dict_new(17); + dict_write{dict_ptr=my_dict}(key=12, new_value=34); + let (local val1: felt) = dict_read{dict_ptr=my_dict}(key=12); + assert val1 = 34; + let (local val2: felt) = dict_read{dict_ptr=my_dict}(key=11); + assert val2 = 17; + let (local val3: felt) = dict_read{dict_ptr=my_dict}(key=12); + assert val3 = 34; + let (local val4: felt) = dict_read{dict_ptr=my_dict}(key=11); + assert val4 = 17; + dict_write{dict_ptr=my_dict}(key=11, new_value=35); + let (local val5: felt) = dict_read{dict_ptr=my_dict}(key=11); + assert val5 = 35; + dict_write{dict_ptr=my_dict}(key=12, new_value=35); + let (local val6: felt) = dict_read{dict_ptr=my_dict}(key=11); + assert val6 = 35; + dict_write{dict_ptr=my_dict}(key=20, new_value=-5); + let (local val6: felt) = dict_read{dict_ptr=my_dict}(key=20); + assert val6 = -5; + return (); +} \ No newline at end of file From 9c5d72e441647c022bb08500d6c3c4e6e4ac4178 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 13 Sep 2023 17:29:59 -0300 Subject: [PATCH 32/74] Revert "Implement `ASSERT_NOT_EQUAL` hint" This reverts commit 2cfe447c43a78976704e79b22582a11cd1b3e9c9. --- pkg/hints/hint_processor.go | 2 -- pkg/hints/math_hint_codes.go | 3 +++ pkg/hints/math_hints.go | 26 -------------------------- 3 files changed, 3 insertions(+), 28 deletions(-) diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index e0cf0ef9..c97e67de 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -47,8 +47,6 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, return is_positive(data.Ids, vm) case ASSERT_NOT_ZERO: return assert_not_zero(data.Ids, vm) - case ASSERT_NOT_EQUAL: - return assert_not_equal(data.Ids, vm) case DEFAULT_DICT_NEW: return defaultDictNew(data.Ids, execScopes, vm) case DICT_READ: diff --git a/pkg/hints/math_hint_codes.go b/pkg/hints/math_hint_codes.go index 4eead668..91327197 100644 --- a/pkg/hints/math_hint_codes.go +++ b/pkg/hints/math_hint_codes.go @@ -5,5 +5,8 @@ const ASSERT_NN = "from starkware.cairo.common.math_utils import assert_integer\ const IS_POSITIVE = "from starkware.cairo.common.math_utils import is_positive\nids.is_positive = 1 if is_positive(\n value=ids.value, prime=PRIME, rc_bound=range_check_builtin.bound) else 0" const ASSERT_NOT_ZERO = "from starkware.cairo.common.math_utils import assert_integer\nassert_integer(ids.value)\nassert ids.value % PRIME != 0, f'assert_not_zero failed: {ids.value} = 0.'" +<<<<<<< HEAD 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}.'" +======= +>>>>>>> parent of 2cfe447 (Implement `ASSERT_NOT_EQUAL` hint) diff --git a/pkg/hints/math_hints.go b/pkg/hints/math_hints.go index bfac989e..b050f146 100644 --- a/pkg/hints/math_hints.go +++ b/pkg/hints/math_hints.go @@ -63,29 +63,3 @@ func assert_not_zero(ids IdsManager, vm *VirtualMachine) error { } return nil } - -func assert_not_equal(ids IdsManager, vm *VirtualMachine) error { - // Extract Ids Variables - a, err := ids.Get("a", vm) - if err != nil { - return err - } - b, err := ids.Get("b", vm) - if err != nil { - return err - } - // Hint Logic - a_rel, a_is_rel := a.GetRelocatable() - b_rel, b_is_rel := b.GetRelocatable() - if !((a_is_rel && b_is_rel && a_rel.SegmentIndex == b_rel.SegmentIndex) || (!a_is_rel && !b_is_rel)) { - return errors.Errorf("assert_not_equal failed: non-comparable values: %v, %v.", a, b) - } - diff, err := a.Sub(*b) - if err != nil { - return err - } - if diff.IsZero() { - return errors.Errorf("assert_not_equal failed: %v = %v.", a, b) - } - return nil -} From ba6e3a0aaed1d4af5be0356a5c15be46cbfa185a Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 13 Sep 2023 17:30:55 -0300 Subject: [PATCH 33/74] Revert "Add unit tests" This reverts commit 8a25e3dfc9fb3414048372e6ea6eea2df7681484. --- pkg/hints/math_hints.go | 29 +++++++++++++++++++++++++++++ pkg/hints/math_hints_test.go | 3 +++ 2 files changed, 32 insertions(+) diff --git a/pkg/hints/math_hints.go b/pkg/hints/math_hints.go index b050f146..48590061 100644 --- a/pkg/hints/math_hints.go +++ b/pkg/hints/math_hints.go @@ -63,3 +63,32 @@ func assert_not_zero(ids IdsManager, vm *VirtualMachine) error { } return nil } +<<<<<<< HEAD +======= + +func assert_not_equal(ids *IdsManager, vm *VirtualMachine) error { + // Extract Ids Variables + a, err := ids.Get("a", vm) + if err != nil { + return err + } + b, err := ids.Get("b", vm) + if err != nil { + return err + } + // Hint Logic + a_rel, a_is_rel := a.GetRelocatable() + b_rel, b_is_rel := b.GetRelocatable() + if !((a_is_rel && b_is_rel && a_rel.SegmentIndex == b_rel.SegmentIndex) || (!a_is_rel && !b_is_rel)) { + return errors.Errorf("assert_not_equal failed: non-comparable values: %v, %v.", a, b) + } + diff, err := a.Sub(*b) + if err != nil { + return err + } + if diff.IsZero() { + return errors.Errorf("assert_not_equal failed: %v = %v.", a, b) + } + return nil +} +>>>>>>> parent of 8a25e3d (Add unit tests) diff --git a/pkg/hints/math_hints_test.go b/pkg/hints/math_hints_test.go index efd08f43..9ffa4996 100644 --- a/pkg/hints/math_hints_test.go +++ b/pkg/hints/math_hints_test.go @@ -161,6 +161,7 @@ func TestAssertNotZeroHintFail(t *testing.T) { t.Errorf("ASSERT_NOT_ZERO hint should have failed") } } +<<<<<<< HEAD func TestAssertNotEqualHintNonComparableDiffType(t *testing.T) { vm := NewVirtualMachine() @@ -287,3 +288,5 @@ func TestAssertNotEqualHintOkRelocatables(t *testing.T) { t.Errorf("ASSERT_NOT_EQUAL hint failed with error: %s", err) } } +======= +>>>>>>> parent of 8a25e3d (Add unit tests) From dea259c5e99f7938f87bb33f997da9db7f5c3088 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 13 Sep 2023 17:32:39 -0300 Subject: [PATCH 34/74] Remove leftovers from base branch --- pkg/hints/math_hint_codes.go | 5 -- pkg/hints/math_hints.go | 29 ------- pkg/hints/math_hints_test.go | 129 ----------------------------- pkg/vm/cairo_run/cairo_run_test.go | 7 -- 4 files changed, 170 deletions(-) diff --git a/pkg/hints/math_hint_codes.go b/pkg/hints/math_hint_codes.go index 91327197..737a8d43 100644 --- a/pkg/hints/math_hint_codes.go +++ b/pkg/hints/math_hint_codes.go @@ -5,8 +5,3 @@ const ASSERT_NN = "from starkware.cairo.common.math_utils import assert_integer\ const IS_POSITIVE = "from starkware.cairo.common.math_utils import is_positive\nids.is_positive = 1 if is_positive(\n value=ids.value, prime=PRIME, rc_bound=range_check_builtin.bound) else 0" const ASSERT_NOT_ZERO = "from starkware.cairo.common.math_utils import assert_integer\nassert_integer(ids.value)\nassert ids.value % PRIME != 0, f'assert_not_zero failed: {ids.value} = 0.'" -<<<<<<< HEAD - -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}.'" -======= ->>>>>>> parent of 2cfe447 (Implement `ASSERT_NOT_EQUAL` hint) diff --git a/pkg/hints/math_hints.go b/pkg/hints/math_hints.go index 48590061..b050f146 100644 --- a/pkg/hints/math_hints.go +++ b/pkg/hints/math_hints.go @@ -63,32 +63,3 @@ func assert_not_zero(ids IdsManager, vm *VirtualMachine) error { } return nil } -<<<<<<< HEAD -======= - -func assert_not_equal(ids *IdsManager, vm *VirtualMachine) error { - // Extract Ids Variables - a, err := ids.Get("a", vm) - if err != nil { - return err - } - b, err := ids.Get("b", vm) - if err != nil { - return err - } - // Hint Logic - a_rel, a_is_rel := a.GetRelocatable() - b_rel, b_is_rel := b.GetRelocatable() - if !((a_is_rel && b_is_rel && a_rel.SegmentIndex == b_rel.SegmentIndex) || (!a_is_rel && !b_is_rel)) { - return errors.Errorf("assert_not_equal failed: non-comparable values: %v, %v.", a, b) - } - diff, err := a.Sub(*b) - if err != nil { - return err - } - if diff.IsZero() { - return errors.Errorf("assert_not_equal failed: %v = %v.", a, b) - } - return nil -} ->>>>>>> parent of 8a25e3d (Add unit tests) diff --git a/pkg/hints/math_hints_test.go b/pkg/hints/math_hints_test.go index 9ffa4996..3844244a 100644 --- a/pkg/hints/math_hints_test.go +++ b/pkg/hints/math_hints_test.go @@ -161,132 +161,3 @@ func TestAssertNotZeroHintFail(t *testing.T) { t.Errorf("ASSERT_NOT_ZERO hint should have failed") } } -<<<<<<< HEAD - -func TestAssertNotEqualHintNonComparableDiffType(t *testing.T) { - vm := NewVirtualMachine() - vm.Segments.AddSegment() - idsManager := SetupIdsForTest( - map[string][]*MaybeRelocatable{ - "a": {NewMaybeRelocatableFelt(FeltFromUint64(0))}, - "b": {NewMaybeRelocatableRelocatable(NewRelocatable(0, 0))}, - }, - vm, - ) - hintProcessor := CairoVmHintProcessor{} - hintData := any(HintData{ - Ids: idsManager, - Code: ASSERT_NOT_EQUAL, - }) - err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) - if err == nil { - t.Errorf("ASSERT_NOT_EQUAL hint should have failed") - } -} - -func TestAssertNotEqualHintNonComparableDiffIndex(t *testing.T) { - vm := NewVirtualMachine() - vm.Segments.AddSegment() - idsManager := SetupIdsForTest( - map[string][]*MaybeRelocatable{ - "a": {NewMaybeRelocatableRelocatable(NewRelocatable(1, 0))}, - "b": {NewMaybeRelocatableRelocatable(NewRelocatable(0, 0))}, - }, - vm, - ) - hintProcessor := CairoVmHintProcessor{} - hintData := any(HintData{ - Ids: idsManager, - Code: ASSERT_NOT_EQUAL, - }) - err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) - if err == nil { - t.Errorf("ASSERT_NOT_EQUAL hint should have failed") - } -} - -func TestAssertNotEqualHintEqualRelocatables(t *testing.T) { - vm := NewVirtualMachine() - vm.Segments.AddSegment() - idsManager := SetupIdsForTest( - map[string][]*MaybeRelocatable{ - "a": {NewMaybeRelocatableRelocatable(NewRelocatable(0, 0))}, - "b": {NewMaybeRelocatableRelocatable(NewRelocatable(0, 0))}, - }, - vm, - ) - hintProcessor := CairoVmHintProcessor{} - hintData := any(HintData{ - Ids: idsManager, - Code: ASSERT_NOT_EQUAL, - }) - err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) - if err == nil { - t.Errorf("ASSERT_NOT_EQUAL hint should have failed") - } -} - -func TestAssertNotEqualHintEqualFelts(t *testing.T) { - vm := NewVirtualMachine() - vm.Segments.AddSegment() - idsManager := SetupIdsForTest( - map[string][]*MaybeRelocatable{ - "a": {NewMaybeRelocatableFelt(FeltFromUint64(9))}, - "b": {NewMaybeRelocatableFelt(FeltFromUint64(9))}, - }, - vm, - ) - hintProcessor := CairoVmHintProcessor{} - hintData := any(HintData{ - Ids: idsManager, - Code: ASSERT_NOT_EQUAL, - }) - err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) - if err == nil { - t.Errorf("ASSERT_NOT_EQUAL hint should have failed") - } -} - -func TestAssertNotEqualHintOkFelts(t *testing.T) { - vm := NewVirtualMachine() - vm.Segments.AddSegment() - idsManager := SetupIdsForTest( - map[string][]*MaybeRelocatable{ - "a": {NewMaybeRelocatableFelt(FeltFromUint64(9))}, - "b": {NewMaybeRelocatableFelt(FeltFromUint64(7))}, - }, - vm, - ) - hintProcessor := CairoVmHintProcessor{} - hintData := any(HintData{ - Ids: idsManager, - Code: ASSERT_NOT_EQUAL, - }) - err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) - if err != nil { - t.Errorf("ASSERT_NOT_EQUAL hint failed with error: %s", err) - } -} - -func TestAssertNotEqualHintOkRelocatables(t *testing.T) { - vm := NewVirtualMachine() - vm.Segments.AddSegment() - idsManager := SetupIdsForTest( - map[string][]*MaybeRelocatable{ - "a": {NewMaybeRelocatableRelocatable(NewRelocatable(1, 9))}, - "b": {NewMaybeRelocatableRelocatable(NewRelocatable(1, 7))}, - }, - vm, - ) - hintProcessor := CairoVmHintProcessor{} - hintData := any(HintData{ - Ids: idsManager, - Code: ASSERT_NOT_EQUAL, - }) - err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) - if err != nil { - t.Errorf("ASSERT_NOT_EQUAL hint failed with error: %s", err) - } -} -======= ->>>>>>> parent of 8a25e3d (Add unit tests) diff --git a/pkg/vm/cairo_run/cairo_run_test.go b/pkg/vm/cairo_run/cairo_run_test.go index e14c7005..9e0916e3 100644 --- a/pkg/vm/cairo_run/cairo_run_test.go +++ b/pkg/vm/cairo_run/cairo_run_test.go @@ -107,13 +107,6 @@ func TestAssertNotZeroHint(t *testing.T) { } } -func TestAssertNotEqualHint(t *testing.T) { - _, err := cairo_run.CairoRun("../../../cairo_programs/assert_not_equal.json", "small", false) - if err != nil { - t.Errorf("Program execution failed with error: %s", err) - } -} - func TestDict(t *testing.T) { _, err := cairo_run.CairoRun("../../../cairo_programs/dict.json", "small", false) if err != nil { From 705efbfe0cc05ea2937ff00c0ed957cb455f184f Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 13 Sep 2023 17:33:20 -0300 Subject: [PATCH 35/74] Remove leftovers from base branch --- cairo_programs/assert_not_equal.cairo | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 cairo_programs/assert_not_equal.cairo diff --git a/cairo_programs/assert_not_equal.cairo b/cairo_programs/assert_not_equal.cairo deleted file mode 100644 index a5810e4d..00000000 --- a/cairo_programs/assert_not_equal.cairo +++ /dev/null @@ -1,10 +0,0 @@ -%builtins output - -from starkware.cairo.common.math import assert_not_equal - -func main{output_ptr: felt*}() { - assert_not_equal(17, 7); - assert_not_equal(cast(output_ptr, felt), cast(output_ptr + 1, felt)); - assert_not_equal(-1, 1); - return (); -} From 1f12ebb04dc90500d49376d3201efc0665f2fa6a Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 13 Sep 2023 17:33:55 -0300 Subject: [PATCH 36/74] Fix eof --- cairo_programs/dict.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cairo_programs/dict.cairo b/cairo_programs/dict.cairo index 6a9fc065..87e616a0 100644 --- a/cairo_programs/dict.cairo +++ b/cairo_programs/dict.cairo @@ -24,4 +24,4 @@ func main() { let (local val6: felt) = dict_read{dict_ptr=my_dict}(key=20); assert val6 = -5; return (); -} \ No newline at end of file +} From 613fc4151c6b24388b601809fbae767ad10403be Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 13 Sep 2023 17:47:49 -0300 Subject: [PATCH 37/74] Implement DictUpdate --- pkg/hints/dict_hint_codes.go | 2 ++ pkg/hints/dict_hints.go | 42 ++++++++++++++++++++++++++++++++++-- pkg/hints/hint_processor.go | 2 ++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/pkg/hints/dict_hint_codes.go b/pkg/hints/dict_hint_codes.go index 95354dfa..88445b9a 100644 --- a/pkg/hints/dict_hint_codes.go +++ b/pkg/hints/dict_hint_codes.go @@ -5,3 +5,5 @@ const DEFAULT_DICT_NEW = "if '__dict_manager' not in globals():\n from starkw const DICT_READ = "dict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ndict_tracker.current_ptr += ids.DictAccess.SIZE\nids.value = dict_tracker.data[ids.key]" const DICT_WRITE = "dict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ndict_tracker.current_ptr += ids.DictAccess.SIZE\nids.dict_ptr.prev_value = dict_tracker.data[ids.key]\ndict_tracker.data[ids.key] = ids.new_value" + +const DICT_UPDATE = "# Verify dict pointer and prev value.\ndict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ncurrent_value = dict_tracker.data[ids.key]\nassert current_value == ids.prev_value, \\\n f'Wrong previous value in dict. Got {ids.prev_value}, expected {current_value}.'\n\n# Update value.\ndict_tracker.data[ids.key] = ids.new_value\ndict_tracker.current_ptr += ids.DictAccess.SIZE" diff --git a/pkg/hints/dict_hints.go b/pkg/hints/dict_hints.go index 2a19c369..6348b115 100644 --- a/pkg/hints/dict_hints.go +++ b/pkg/hints/dict_hints.go @@ -1,13 +1,12 @@ package hints import ( - "errors" - . "github.com/lambdaclass/cairo-vm.go/pkg/hints/dict_manager" . "github.com/lambdaclass/cairo-vm.go/pkg/hints/hint_utils" . "github.com/lambdaclass/cairo-vm.go/pkg/types" . "github.com/lambdaclass/cairo-vm.go/pkg/vm" "github.com/lambdaclass/cairo-vm.go/pkg/vm/memory" + "github.com/pkg/errors" ) const DICT_ACCESS_SIZE = 3 @@ -96,3 +95,42 @@ func dictWrite(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) erro tracker.InsertValue(key, new_value) return nil } + +func dictUpdate(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error { + // Extract Variables + dictManager, ok := FetchDictManager(scopes) + if !ok { + return errors.New("Variable __dict_manager not present in current execution scope") + } + dict_ptr, err := ids.GetRelocatable("dict_ptr", vm) + if err != nil { + return err + } + key, err := ids.Get("key", vm) + if err != nil { + return err + } + new_value, err := ids.Get("new_value", vm) + if err != nil { + return err + } + prev_value, err := ids.Get("prev_value", vm) + if err != nil { + return err + } + // Hint Logic + tracker, err := dictManager.GetTracker(dict_ptr) + if err != nil { + return err + } + current_value, err := tracker.GetValue(key) + if err != nil { + return err + } + if *prev_value != *current_value { + return errors.Errorf("Wrong previous value in dict. Got %v, expected %v.", *current_value, *prev_value) + } + tracker.InsertValue(key, new_value) + tracker.CurrentPtr.Offset += DICT_ACCESS_SIZE + return nil +} diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index c97e67de..42c25d82 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -53,6 +53,8 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, return dictRead(data.Ids, execScopes, vm) case DICT_WRITE: return dictWrite(data.Ids, execScopes, vm) + case DICT_UPDATE: + return dictUpdate(data.Ids, execScopes, vm) default: return errors.Errorf("Unknown Hint: %s", data.Code) } From f27926e617e1ff76f1a7349b11fec15427a1f347 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 13 Sep 2023 17:55:52 -0300 Subject: [PATCH 38/74] Add unit tests --- pkg/hints/dict_hints_test.go | 128 +++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/pkg/hints/dict_hints_test.go b/pkg/hints/dict_hints_test.go index c31afc8b..6fd5e33b 100644 --- a/pkg/hints/dict_hints_test.go +++ b/pkg/hints/dict_hints_test.go @@ -288,3 +288,131 @@ func TestDictWriteNewWriteDefault(t *testing.T) { t.Error("DICT_WRITE Wrong/No ids.value") } } + +func TestDictUpdateDefaultValueOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + + // Create dictManager with a default dictionary & add it to scope + dictManager := dict_manager.NewDictManager() + defaultValue := FeltFromUint64(17) + dict_ptr := dictManager.NewDefaultDictionary(NewMaybeRelocatableFelt(defaultValue), vm) + scopes.AssignOrUpdateVariable("__dict_manager", &dictManager) + + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "key": {NewMaybeRelocatableFelt(FeltOne())}, + "dict_ptr": {NewMaybeRelocatableRelocatable(dict_ptr)}, + "prev_value": {NewMaybeRelocatableFelt(defaultValue)}, + "new_value": {NewMaybeRelocatableFelt(FeltFromUint64(17))}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: DICT_UPDATE, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("DICT_UPDATE hint test failed with error %s", err) + } +} + +func TestDictUpdateDefaultValueErr(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + + // Create dictManager with a default dictionary & add it to scope + dictManager := dict_manager.NewDictManager() + defaultValue := FeltFromUint64(17) + dict_ptr := dictManager.NewDefaultDictionary(NewMaybeRelocatableFelt(defaultValue), vm) + scopes.AssignOrUpdateVariable("__dict_manager", &dictManager) + + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "key": {NewMaybeRelocatableFelt(FeltOne())}, + "dict_ptr": {NewMaybeRelocatableRelocatable(dict_ptr)}, + "prev_value": {NewMaybeRelocatableFelt(defaultValue.Add(FeltOne()))}, + "new_value": {NewMaybeRelocatableFelt(FeltFromUint64(17))}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: DICT_UPDATE, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Error("DICT_UPDATE hint test should have failed") + } +} + +func TestDictUpdateOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + + // Create dictManager & add it to scope + dictManager := dict_manager.NewDictManager() + initialDict := map[MaybeRelocatable]MaybeRelocatable{ + *NewMaybeRelocatableFelt(FeltZero()): *NewMaybeRelocatableFelt(FeltOne()), + } + dict_ptr := dictManager.NewDictionary(&initialDict, vm) + scopes.AssignOrUpdateVariable("__dict_manager", &dictManager) + + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "key": {NewMaybeRelocatableFelt(FeltZero())}, + "dict_ptr": {NewMaybeRelocatableRelocatable(dict_ptr)}, + "prev_value": {NewMaybeRelocatableFelt(FeltOne())}, + "new_value": {NewMaybeRelocatableFelt(FeltFromUint64(17))}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: DICT_UPDATE, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("DICT_UPDATE hint test failed with error %s", err) + } +} + +func TestDictUpdateErr(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + + // Create dictManager & add it to scope + dictManager := dict_manager.NewDictManager() + initialDict := map[MaybeRelocatable]MaybeRelocatable{ + *NewMaybeRelocatableFelt(FeltZero()): *NewMaybeRelocatableFelt(FeltOne()), + } + dict_ptr := dictManager.NewDictionary(&initialDict, vm) + scopes.AssignOrUpdateVariable("__dict_manager", &dictManager) + + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "key": {NewMaybeRelocatableFelt(FeltZero())}, + "dict_ptr": {NewMaybeRelocatableRelocatable(dict_ptr)}, + "prev_value": {NewMaybeRelocatableFelt(FeltZero())}, + "new_value": {NewMaybeRelocatableFelt(FeltFromUint64(17))}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: DICT_UPDATE, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Error("DICT_UPDATE hint test should have failed") + } +} From effc2076aa34f8ff33b725383536ce1de03f7d67 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 13 Sep 2023 18:10:59 -0300 Subject: [PATCH 39/74] Add integration test --- cairo_programs/dict_update.cairo | 25 +++++++++++++++++++++++++ pkg/vm/cairo_run/cairo_run_test.go | 7 +++++++ 2 files changed, 32 insertions(+) create mode 100644 cairo_programs/dict_update.cairo diff --git a/cairo_programs/dict_update.cairo b/cairo_programs/dict_update.cairo new file mode 100644 index 00000000..03641a0f --- /dev/null +++ b/cairo_programs/dict_update.cairo @@ -0,0 +1,25 @@ +from starkware.cairo.common.dict import dict_read, dict_write, dict_update +from starkware.cairo.common.default_dict import default_dict_new +from starkware.cairo.common.dict_access import DictAccess + +func main() { + alloc_locals; + // Create default dict + let (local my_dict: DictAccess*) = default_dict_new(17); + // Write value + dict_write{dict_ptr=my_dict}(key=12, new_value=34); + let (local val1: felt) = dict_read{dict_ptr=my_dict}(key=12); + assert val1 = 34; + // Update written value + dict_update{dict_ptr=my_dict}(key=12, prev_value=34, new_value=49); + let (local val2: felt) = dict_read{dict_ptr=my_dict}(key=12); + assert val2 = 49; + // Update value that doesnt exixt yet, using default value as prev_value + dict_update{dict_ptr=my_dict}(key=10, prev_value=17, new_value=22); + let (local val3: felt) = dict_read{dict_ptr=my_dict}(key=10); + assert val3 = 22; + dict_update{dict_ptr=my_dict}(key=10, prev_value=22, new_value=-8); + let (local val4: felt) = dict_read{dict_ptr=my_dict}(key=10); + assert val4 = -8; + return (); +} diff --git a/pkg/vm/cairo_run/cairo_run_test.go b/pkg/vm/cairo_run/cairo_run_test.go index 9e0916e3..edc40125 100644 --- a/pkg/vm/cairo_run/cairo_run_test.go +++ b/pkg/vm/cairo_run/cairo_run_test.go @@ -113,3 +113,10 @@ func TestDict(t *testing.T) { t.Errorf("Program execution failed with error: %s", err) } } + +func TestDictUpdate(t *testing.T) { + _, err := cairo_run.CairoRun("../../../cairo_programs/dict_update.json", "small", false) + if err != nil { + t.Errorf("Program execution failed with error: %s", err) + } +} From 7ba2fbc9b23438f788512d8d0bd0112f13c247cf Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 13 Sep 2023 18:34:36 -0300 Subject: [PATCH 40/74] Dont ignore errors --- pkg/hints/dict_hints.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pkg/hints/dict_hints.go b/pkg/hints/dict_hints.go index 2a19c369..157e7464 100644 --- a/pkg/hints/dict_hints.go +++ b/pkg/hints/dict_hints.go @@ -60,8 +60,7 @@ func dictRead(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error if err != nil { return err } - ids.Insert("value", val, vm) - return nil + return ids.Insert("value", val, vm) } func dictWrite(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error { @@ -92,7 +91,6 @@ func dictWrite(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) erro if err != nil { return err } - ids.Insert("prev_value", prev_val, vm) tracker.InsertValue(key, new_value) - return nil + return ids.Insert("prev_value", prev_val, vm) } From d1f7ed46e3a0de507ec74793785339cd38636a47 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 13 Sep 2023 18:49:40 -0300 Subject: [PATCH 41/74] Fix DictWrite hint --- pkg/hints/dict_hints.go | 11 ++++++++++- pkg/hints/dict_hints_test.go | 25 +++++++++++-------------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/pkg/hints/dict_hints.go b/pkg/hints/dict_hints.go index 157e7464..2302ec01 100644 --- a/pkg/hints/dict_hints.go +++ b/pkg/hints/dict_hints.go @@ -81,6 +81,15 @@ func dictWrite(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) erro if err != nil { return err } + /* dict_ptr has type *DictAccess + struct DictAccess { + key: felt, + prev_value: felt, + new_value: felt, + } + so ids.dict_ptr.prev_value = [dict_ptr + 1] + */ + prev_val_addr := dict_ptr.AddUint(1) // Hint Logic tracker, err := dictManager.GetTracker(dict_ptr) if err != nil { @@ -92,5 +101,5 @@ func dictWrite(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) erro return err } tracker.InsertValue(key, new_value) - return ids.Insert("prev_value", prev_val, vm) + return vm.Segments.Memory.Insert(prev_val_addr, prev_val) } diff --git a/pkg/hints/dict_hints_test.go b/pkg/hints/dict_hints_test.go index c31afc8b..b8b29330 100644 --- a/pkg/hints/dict_hints_test.go +++ b/pkg/hints/dict_hints_test.go @@ -199,10 +199,9 @@ func TestDictWriteOk(t *testing.T) { idsManager := SetupIdsForTest( map[string][]*MaybeRelocatable{ - "key": {NewMaybeRelocatableFelt(FeltOne())}, - "dict_ptr": {NewMaybeRelocatableRelocatable(dict_ptr)}, - "prev_value": {nil}, - "new_value": {NewMaybeRelocatableFelt(FeltFromUint64(17))}, + "key": {NewMaybeRelocatableFelt(FeltOne())}, + "dict_ptr": {NewMaybeRelocatableRelocatable(dict_ptr)}, + "new_value": {NewMaybeRelocatableFelt(FeltFromUint64(17))}, }, vm, ) @@ -216,7 +215,7 @@ func TestDictWriteOk(t *testing.T) { t.Errorf("DICT_WRITE hint test failed with error %s", err) } // Check ids.prev_value - val, err := idsManager.GetFelt("prev_value", vm) + val, err := vm.Segments.Memory.GetFelt(dict_ptr.AddUint(1)) if err != nil || val != FeltFromUint64(7) { t.Error("DICT_WRITE Wrong/No ids.value") } @@ -235,10 +234,9 @@ func TestDictWriteNoPrevValue(t *testing.T) { idsManager := SetupIdsForTest( map[string][]*MaybeRelocatable{ - "key": {NewMaybeRelocatableFelt(FeltOne())}, - "dict_ptr": {NewMaybeRelocatableRelocatable(dict_ptr)}, - "prev_value": {nil}, - "new_value": {NewMaybeRelocatableFelt(FeltFromUint64(17))}, + "key": {NewMaybeRelocatableFelt(FeltOne())}, + "dict_ptr": {NewMaybeRelocatableRelocatable(dict_ptr)}, + "new_value": {NewMaybeRelocatableFelt(FeltFromUint64(17))}, }, vm, ) @@ -266,10 +264,9 @@ func TestDictWriteNewWriteDefault(t *testing.T) { idsManager := SetupIdsForTest( map[string][]*MaybeRelocatable{ - "key": {NewMaybeRelocatableFelt(FeltOne())}, - "dict_ptr": {NewMaybeRelocatableRelocatable(dict_ptr)}, - "prev_value": {nil}, - "new_value": {NewMaybeRelocatableFelt(FeltFromUint64(17))}, + "key": {NewMaybeRelocatableFelt(FeltOne())}, + "dict_ptr": {NewMaybeRelocatableRelocatable(dict_ptr)}, + "new_value": {NewMaybeRelocatableFelt(FeltFromUint64(17))}, }, vm, ) @@ -283,7 +280,7 @@ func TestDictWriteNewWriteDefault(t *testing.T) { t.Errorf("DICT_WRITE hint test failed with error %s", err) } // Check ids.prev_value - val, err := idsManager.GetFelt("prev_value", vm) + val, err := vm.Segments.Memory.GetFelt(dict_ptr.AddUint(1)) if err != nil || val != defaultValue { t.Error("DICT_WRITE Wrong/No ids.value") } From 3765aed670e996115268c25a80906c64095a68db Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 13 Sep 2023 18:55:09 -0300 Subject: [PATCH 42/74] Add hint codes --- pkg/hints/dict_hint_codes.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pkg/hints/dict_hint_codes.go b/pkg/hints/dict_hint_codes.go index 88445b9a..a16273cd 100644 --- a/pkg/hints/dict_hint_codes.go +++ b/pkg/hints/dict_hint_codes.go @@ -7,3 +7,21 @@ const DICT_READ = "dict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ndict const DICT_WRITE = "dict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ndict_tracker.current_ptr += ids.DictAccess.SIZE\nids.dict_ptr.prev_value = dict_tracker.data[ids.key]\ndict_tracker.data[ids.key] = ids.new_value" const DICT_UPDATE = "# Verify dict pointer and prev value.\ndict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ncurrent_value = dict_tracker.data[ids.key]\nassert current_value == ids.prev_value, \\\n f'Wrong previous value in dict. Got {ids.prev_value}, expected {current_value}.'\n\n# Update value.\ndict_tracker.data[ids.key] = ids.new_value\ndict_tracker.current_ptr += ids.DictAccess.SIZE" + +const SQUASH_DICT = "dict_access_size = ids.DictAccess.SIZE\naddress = ids.dict_accesses.address_\nassert ids.ptr_diff % dict_access_size == 0, \\\n 'Accesses array size must be divisible by DictAccess.SIZE'\nn_accesses = ids.n_accesses\nif '__squash_dict_max_size' in globals():\n assert n_accesses <= __squash_dict_max_size, \\\n f'squash_dict() can only be used with n_accesses<={__squash_dict_max_size}. ' \\n f'Got: n_accesses={n_accesses}.'\n# A map from key to the list of indices accessing it.\naccess_indices = {}\nfor i in range(n_accesses):\n key = memory[address + dict_access_size * i]\n access_indices.setdefault(key, []).append(i)\n# Descending list of keys.\nkeys = sorted(access_indices.keys(), reverse=True)\n# Are the keys used bigger than range_check bound.\nids.big_keys = 1 if keys[0] >= range_check_builtin.bound else 0\nids.first_key = key = keys.pop()" + +const SQUASH_DICT_INNER_SKIP_LOOP = "ids.should_skip_loop = 0 if current_access_indices else 1" + +const SQUASH_DICT_INNER_FIRST_ITERATION = "current_access_indices = sorted(access_indices[key])[::-1]\ncurrent_access_index = current_access_indices.pop()\nmemory[ids.range_check_ptr] = current_access_index" + +const SQUASH_DICT_INNER_CHECK_ACCESS_INDEX = "new_access_index = current_access_indices.pop()\nids.loop_temps.index_delta_minus1 = new_access_index - current_access_index - 1\ncurrent_access_index = new_access_index" + +const SQUASH_DICT_INNER_CONTINUE_LOOP = "ids.loop_temps.should_continue = 1 if current_access_indices else 0" + +const SQUASH_DICT_INNER_ASSERT_LEN_KEYS = "assert len(keys) == 0" + +const SQUASH_DICT_INNER_LEN_ASSERT = "assert len(current_access_indices) == 0" + +const SQUASH_DICT_INNER_USED_ACCESSES_ASSERT = "assert ids.n_used_accesses == len(access_indices[key])" + +const SQUASH_DICT_INNER_NEXT_KEY = "assert len(keys) > 0, 'No keys left but remaining_accesses > 0.'\nids.next_key = key = keys.pop()" From 3208fac130564eb60666617f52facec3c7a9aa95 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 14 Sep 2023 11:10:43 -0300 Subject: [PATCH 43/74] Implement DivRem, ModFloor, DivFloor --- pkg/lambdaworks/lambdaworks.go | 19 ++++++++++++++ pkg/lambdaworks/lambdaworks_test.go | 30 ++++++++++++++++++++++ pkg/lambdaworks/lib/lambdaworks.h | 4 +++ pkg/lambdaworks/lib/lambdaworks/src/lib.rs | 10 ++++++++ 4 files changed, 63 insertions(+) diff --git a/pkg/lambdaworks/lambdaworks.go b/pkg/lambdaworks/lambdaworks.go index cb5394ef..9a68f649 100644 --- a/pkg/lambdaworks/lambdaworks.go +++ b/pkg/lambdaworks/lambdaworks.go @@ -252,3 +252,22 @@ func (f Felt) ToSigned() *big.Int { } return n } + +func (a Felt) DivRem(b Felt) (Felt, Felt) { + var div C.felt_t + var rem C.felt_t + var a_c C.felt_t = a.toC() + var b_c C.felt_t = b.toC() + C.div_rem(&a_c[0], &b_c[0], &div[0], &rem[0]) + return fromC(div), fromC(rem) +} + +func (a Felt) ModFloor(b Felt) Felt { + _, rem := a.DivRem(b) + return rem +} + +func (a Felt) DivFloor(b Felt) Felt { + div, _ := a.DivRem(b) + return div +} diff --git a/pkg/lambdaworks/lambdaworks_test.go b/pkg/lambdaworks/lambdaworks_test.go index 82690333..42bc3eed 100644 --- a/pkg/lambdaworks/lambdaworks_test.go +++ b/pkg/lambdaworks/lambdaworks_test.go @@ -9,6 +9,36 @@ import ( "github.com/lambdaclass/cairo-vm.go/pkg/vm/memory" ) +func TestFeltDivFloor(t *testing.T) { + a := lambdaworks.FeltFromUint64(13) + b := lambdaworks.FeltFromUint64(3) + expected := lambdaworks.FeltFromUint64(4) + r := a.DivFloor(b) + if r != expected { + t.Errorf("TestFeltDivFloor failed. Expected: %v, Got: %v", expected, r) + } +} + +func TestFeltModFloor(t *testing.T) { + a := lambdaworks.FeltFromUint64(13) + b := lambdaworks.FeltFromUint64(3) + expected := lambdaworks.FeltFromUint64(1) + r := a.ModFloor(b) + if r != expected { + t.Errorf("TestFeltModFloor failed. Expected: %v, Got: %v", expected, r) + } +} +func TestFeltDivRem(t *testing.T) { + a := lambdaworks.FeltFromUint64(8) + b := lambdaworks.FeltFromUint64(3) + expected_div := lambdaworks.FeltFromUint64(2) + expected_rem := lambdaworks.FeltFromUint64(2) + div, rem := a.DivRem(b) + if div != expected_div || rem != expected_rem { + t.Errorf("TestFeltDivRem failed. Expected: (%v, %v), Got: (%v, %v)", expected_div, expected_rem, div, rem) + } +} + func TestToBigInt(t *testing.T) { felt := lambdaworks.FeltFromUint64(26) bigInt := felt.ToBigInt() diff --git a/pkg/lambdaworks/lib/lambdaworks.h b/pkg/lambdaworks/lib/lambdaworks.h index 6f697cdd..45637767 100644 --- a/pkg/lambdaworks/lib/lambdaworks.h +++ b/pkg/lambdaworks/lib/lambdaworks.h @@ -69,4 +69,8 @@ char* to_signed_felt(felt_t value); /* frees a pointer to a string */ void free_string(char* ptr); + void felt_shr(felt_t a, size_t b, felt_t result); + +/* Writes the div & rem variables with a.div_rem(b). */ +void div_rem(felt_t a, felt_t b, felt_t div, felt_t rem); diff --git a/pkg/lambdaworks/lib/lambdaworks/src/lib.rs b/pkg/lambdaworks/lib/lambdaworks/src/lib.rs index c79b7da8..7cafc5c1 100644 --- a/pkg/lambdaworks/lib/lambdaworks/src/lib.rs +++ b/pkg/lambdaworks/lib/lambdaworks/src/lib.rs @@ -223,3 +223,13 @@ pub extern "C" fn felt_shr(a: Limbs, b: usize, result: Limbs) { felt_to_limbs(Felt::from(&res), result) } +#[no_mangle] +pub extern "C" fn div_rem(a: Limbs, b: Limbs, div: Limbs, rem: Limbs) { + let felt_a = limbs_to_felt(a).representative(); + let felt_b = limbs_to_felt(b).representative(); + + let (felt_div, felt_rem) = felt_a.div_rem(&felt_b); + + felt_to_limbs(Felt::from(&felt_div), div); + felt_to_limbs(Felt::from(&felt_rem), rem) +} From 011e2963d8e52990dd2898d3bcd0d959075229e4 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 14 Sep 2023 11:10:59 -0300 Subject: [PATCH 44/74] solve conflicts --- pkg/hints/hint_processor.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index d11b45e9..be2f95f2 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -53,13 +53,10 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, return dictRead(data.Ids, execScopes, vm) case DICT_WRITE: return dictWrite(data.Ids, execScopes, vm) -<<<<<<< HEAD case DICT_UPDATE: return dictUpdate(data.Ids, execScopes, vm) -======= case ASSERT_NOT_EQUAL: return assert_not_equal(data.Ids, vm) ->>>>>>> dict-hint-1 default: return errors.Errorf("Unknown Hint: %s", data.Code) } From 40a475e6d7b59df0eea2dec9f30ef00ddf2ce5e2 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 14 Sep 2023 11:43:19 -0300 Subject: [PATCH 45/74] Add Felt.Cmp --- pkg/lambdaworks/lambdaworks.go | 13 ++++++++++++ pkg/lambdaworks/lambdaworks_test.go | 24 ++++++++++++++++++++++ pkg/lambdaworks/lib/lambdaworks.h | 9 ++++++++ pkg/lambdaworks/lib/lambdaworks/src/lib.rs | 12 +++++++++++ 4 files changed, 58 insertions(+) diff --git a/pkg/lambdaworks/lambdaworks.go b/pkg/lambdaworks/lambdaworks.go index 9a68f649..7c9ba1bd 100644 --- a/pkg/lambdaworks/lambdaworks.go +++ b/pkg/lambdaworks/lambdaworks.go @@ -271,3 +271,16 @@ func (a Felt) DivFloor(b Felt) Felt { div, _ := a.DivRem(b) return div } + +/* +Compares x and y and returns: + + -1 if a < b + 0 if a == b + +1 if a > b +*/ +func (a Felt) Cmp(b Felt) int { + var a_c C.felt_t = a.toC() + var b_c C.felt_t = b.toC() + return int(C.cmp(&a_c[0], &b_c[0])) +} diff --git a/pkg/lambdaworks/lambdaworks_test.go b/pkg/lambdaworks/lambdaworks_test.go index 42bc3eed..3c9c962b 100644 --- a/pkg/lambdaworks/lambdaworks_test.go +++ b/pkg/lambdaworks/lambdaworks_test.go @@ -9,6 +9,30 @@ import ( "github.com/lambdaclass/cairo-vm.go/pkg/vm/memory" ) +func TestCmpHigher(t *testing.T) { + a := lambdaworks.FeltFromUint64(13) + b := lambdaworks.FeltFromUint64(3) + if a.Cmp(b) != 1 { + t.Errorf("TestCmpEq failed") + } +} + +func TestCmpLower(t *testing.T) { + a := lambdaworks.FeltFromUint64(3) + b := lambdaworks.FeltFromUint64(13) + if a.Cmp(b) != -1 { + t.Errorf("TestCmpEq failed") + } +} + +func TestCmpEq(t *testing.T) { + a := lambdaworks.FeltFromUint64(13) + b := lambdaworks.FeltFromUint64(13) + if a.Cmp(b) != 0 { + t.Errorf("TestCmpEq failed") + } +} + func TestFeltDivFloor(t *testing.T) { a := lambdaworks.FeltFromUint64(13) b := lambdaworks.FeltFromUint64(3) diff --git a/pkg/lambdaworks/lib/lambdaworks.h b/pkg/lambdaworks/lib/lambdaworks.h index 45637767..d6f81312 100644 --- a/pkg/lambdaworks/lib/lambdaworks.h +++ b/pkg/lambdaworks/lib/lambdaworks.h @@ -74,3 +74,12 @@ void felt_shr(felt_t a, size_t b, felt_t result); /* Writes the div & rem variables with a.div_rem(b). */ void div_rem(felt_t a, felt_t b, felt_t div, felt_t rem); + +/* +Compares x and y and returns: + + -1 if a < b + 0 if a == b + +1 if a > b +*/ +int cmp(felt_t a, felt_t b); diff --git a/pkg/lambdaworks/lib/lambdaworks/src/lib.rs b/pkg/lambdaworks/lib/lambdaworks/src/lib.rs index 7cafc5c1..fde98646 100644 --- a/pkg/lambdaworks/lib/lambdaworks/src/lib.rs +++ b/pkg/lambdaworks/lib/lambdaworks/src/lib.rs @@ -233,3 +233,15 @@ pub extern "C" fn div_rem(a: Limbs, b: Limbs, div: Limbs, rem: Limbs) { felt_to_limbs(Felt::from(&felt_div), div); felt_to_limbs(Felt::from(&felt_rem), rem) } + +#[no_mangle] +pub extern "C" fn cmp(a: Limbs, b: Limbs) -> i32 { + let felt_a = limbs_to_felt(a); + let felt_b = limbs_to_felt(b); + match (felt_a, felt_b) { + (a, b) if a == b => 0, + (a, b) if a > b => 1, + // (a < b) + _ => -1, + } +} From 8a3e0d1baf879a8938dd5666a3419d8d008fbdbe Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 14 Sep 2023 13:30:22 -0300 Subject: [PATCH 46/74] Implmenet squash_dict hint --- go.sum | 3 + pkg/hints/squash_dict_hints.go | 115 +++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 pkg/hints/squash_dict_hints.go diff --git a/go.sum b/go.sum index 7a055313..ed50f359 100644 --- a/go.sum +++ b/go.sum @@ -3,4 +3,7 @@ github.com/miguelmota/go-solidity-sha3 v0.1.1/go.mod h1:sax1FvQF+f71j8W1uUHMZn8N github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +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/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/pkg/hints/squash_dict_hints.go b/pkg/hints/squash_dict_hints.go new file mode 100644 index 00000000..94a20d54 --- /dev/null +++ b/pkg/hints/squash_dict_hints.go @@ -0,0 +1,115 @@ +package hints + +import ( + "sort" + + "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" + . "github.com/lambdaclass/cairo-vm.go/pkg/vm" + . "github.com/lambdaclass/cairo-vm.go/pkg/vm/memory" + "github.com/pkg/errors" + "golang.org/x/exp/maps" +) + +// SortMaybeRelocatables implements sort.Interface for []*MaybeRelocatables +type SortMaybeRelocatables []*MaybeRelocatable + +func (s SortMaybeRelocatables) Len() int { return len(s) } +func (s SortMaybeRelocatables) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s SortMaybeRelocatables) Less(i, j int) bool { + isLess := false + //Integers are considered smaller than all relocatable values. + a, b := s[i], s[j] + aFelt, aIsFelt := a.GetFelt() + bFelt, bIsFelt := b.GetFelt() + + switch true { + // Both felts + case aIsFelt && bIsFelt: + if aFelt.Cmp(bFelt) == -1 { + isLess = true + } + // a Felt, b Relocatable + case aIsFelt && !bIsFelt: + // a Relocatable, b Felt + case !aIsFelt && bIsFelt: + isLess = true + // Both Relocatables + case !aIsFelt && !bIsFelt: + aRel, _ := a.GetRelocatable() + bRel, _ := a.GetRelocatable() + if aRel.SegmentIndex == bRel.SegmentIndex { + isLess = aRel.Offset < bRel.Offset + } else { + isLess = aRel.SegmentIndex < bRel.SegmentIndex + } + } + + return isLess +} + +func squashDict(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error { + address, err := ids.GetAddr("dict_accesses", vm) + if err != nil { + return err + } + ptrDiff, err := ids.GetFelt("ptr_diff", vm) + if err != nil { + return err + } + if !ptrDiff.ModFloor(lambdaworks.FeltFromUint64(DICT_ACCESS_SIZE)).IsZero() { + errors.New("Accesses array size must be divisible by DictAccess.SIZE") + } + nAccessesFelt, err := ids.GetFelt("n_accesses", vm) + if err != nil { + return err + } + nAccesses, err := nAccessesFelt.ToU64() + if err != nil { + return err + } + squashDictMaxSize, err := scopes.Get("__squash_dict_max_size") + if err == nil { + maxSize, ok := squashDictMaxSize.(uint64) + if ok { + if nAccesses > maxSize { + return errors.Errorf("squash_dict() can only be used with n_accesses<=%d.\nGot: n_accesses=%d.", maxSize, nAccesses) + } + } + } + // A map from key to the list of indices accessing it. + accessIndices := make(map[*MaybeRelocatable][]int) + for i := 0; i < int(nAccesses); i++ { + key, err := vm.Segments.Memory.Get(address.AddUint(uint(i) * DICT_ACCESS_SIZE)) + if err != nil { + return err + } + _, hasKey := accessIndices[key] + if !hasKey { + accessIndices[key] = make([]int, 0) + } + accessIndices[key] = append(accessIndices[key], i) + } + //Descending list of keys. + keys := maps.Keys(accessIndices) + sort.Sort(sort.Reverse(SortMaybeRelocatables(keys))) + //Are the keys used bigger than the range_check bound. + bigKeys := lambdaworks.FeltZero() + highKeyFelt, isFelt := keys[0].GetFelt() + if isFelt && highKeyFelt.Bits() >= builtins.RANGE_CHECK_N_PARTS*builtins.INNER_RC_BOUND_SHIFT { + bigKeys = lambdaworks.FeltOne() + } + lowKey := keys[len(keys)-1] + // Insert new scope variables + scopes.AssignOrUpdateVariable("access_indices", accessIndices) + scopes.AssignOrUpdateVariable("keys", keys) + scopes.AssignOrUpdateVariable("key", lowKey) + // Insert ids variables + err = ids.Insert("big_keys", NewMaybeRelocatableFelt(bigKeys), vm) + if err != nil { + return err + } + return ids.Insert("first_key", lowKey, vm) +} From e148bc4445a545ea00d0143e897efa177050d231 Mon Sep 17 00:00:00 2001 From: toni-calvin Date: Thu, 14 Sep 2023 19:34:57 +0200 Subject: [PATCH 47/74] add test --- pkg/hints/hint_processor.go | 2 ++ pkg/hints/memcpy_hint_codes.go | 1 + pkg/hints/memcpy_hints.go | 6 ++++++ pkg/hints/memcpy_hints_test.go | 36 ++++++++++++++++++++++++++++++++++ pkg/types/exec_scopes.go | 4 ++++ 5 files changed, 49 insertions(+) diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index e12c7194..77a98d2c 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -51,6 +51,8 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, return vm_exit_scope(execScopes) case ASSERT_NOT_EQUAL: return assert_not_equal(data.Ids, vm) + case VM_ENTER_SCOPE: + return vm_enter_scope(execScopes) default: return errors.Errorf("Unknown Hint: %s", data.Code) } diff --git a/pkg/hints/memcpy_hint_codes.go b/pkg/hints/memcpy_hint_codes.go index 71e356c5..65b07faa 100644 --- a/pkg/hints/memcpy_hint_codes.go +++ b/pkg/hints/memcpy_hint_codes.go @@ -2,3 +2,4 @@ package hints const ADD_SEGMENT = "memory[ap] = segments.add()" const VM_EXIT_SCOPE = "vm_exit_scope()" +const VM_ENTER_SCOPE = "vm_enter_scope()" diff --git a/pkg/hints/memcpy_hints.go b/pkg/hints/memcpy_hints.go index e3436a8c..3ab5c8db 100644 --- a/pkg/hints/memcpy_hints.go +++ b/pkg/hints/memcpy_hints.go @@ -17,3 +17,9 @@ func add_segment(vm *VirtualMachine) error { func vm_exit_scope(executionScopes *types.ExecutionScopes) error { return executionScopes.ExitScope() } + +// Implements hint: vm_enter_scope() +func vm_enter_scope(executionScopes *types.ExecutionScopes) error { + executionScopes.EnterScope(make(map[string]interface{})) + return nil +} diff --git a/pkg/hints/memcpy_hints_test.go b/pkg/hints/memcpy_hints_test.go index 758b0717..eacbfa57 100644 --- a/pkg/hints/memcpy_hints_test.go +++ b/pkg/hints/memcpy_hints_test.go @@ -79,3 +79,39 @@ func TestExitScopeInvalid(t *testing.T) { } } + +func TestEnterScope(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "a": {NewMaybeRelocatableFelt(FeltFromUint64(17))}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: VM_ENTER_SCOPE, + }) + + executionScopes := NewExecutionScopes() + scope := make(map[string]interface{}) + scope["a"] = FeltOne() + + err := hintProcessor.ExecuteHint(vm, &hintData, nil, executionScopes) + if err != nil { + t.Errorf("TestEnterScope failed with error %s", err) + } + + if len(executionScopes.Data()) != 2 { + t.Errorf("EnterScopeHint failed, expected data length: %d, got: %d", 2, len(executionScopes.Data())) + } + if len(executionScopes.Data()[0]) != 0 { + t.Errorf("EnterScopeHint failed, expected: 0, got: %v", len(executionScopes.Data()[0])) + } + if len(executionScopes.Data()[1]) != 0 { + t.Errorf("EnterScopeHint failed, expected: 0, got: %v", len(executionScopes.Data()[0])) + } + +} diff --git a/pkg/types/exec_scopes.go b/pkg/types/exec_scopes.go index 50f15213..e94fe153 100644 --- a/pkg/types/exec_scopes.go +++ b/pkg/types/exec_scopes.go @@ -14,6 +14,10 @@ func ExecutionScopesError(err error) error { return errors.Wrapf(err, "Execution scopes error") } +func (es *ExecutionScopes) Data() []map[string]interface{} { + return es.data +} + func ErrVariableNotInScope(varName string) error { return ExecutionScopesError(errors.Errorf("Variable %s not in scope", varName)) } From bb37911ff8f680200aefed3b89cd7f6d556ebaf1 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 14 Sep 2023 15:10:19 -0300 Subject: [PATCH 48/74] Start test --- go.mod | 1 + go.sum | 2 + pkg/hints/hint_processor.go | 2 + pkg/hints/squash_dict_hints.go | 14 +++---- pkg/hints/squash_dict_hints_test.go | 59 +++++++++++++++++++++++++++++ 5 files changed, 71 insertions(+), 7 deletions(-) create mode 100644 pkg/hints/squash_dict_hints_test.go diff --git a/go.mod b/go.mod index 69039c21..17b4759b 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.20 require ( github.com/pkg/errors v0.9.1 golang.org/x/crypto v0.12.0 + golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc ) require ( diff --git a/go.sum b/go.sum index ed50f359..1b2f07e0 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,8 @@ github.com/miguelmota/go-solidity-sha3 v0.1.1/go.mod h1:sax1FvQF+f71j8W1uUHMZn8N github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU= +golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= 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= diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index be2f95f2..b38f9b4f 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -57,6 +57,8 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, return dictUpdate(data.Ids, execScopes, vm) case ASSERT_NOT_EQUAL: return assert_not_equal(data.Ids, vm) + case SQUASH_DICT: + return squashDict(data.Ids, execScopes, vm) default: return errors.Errorf("Unknown Hint: %s", data.Code) } diff --git a/pkg/hints/squash_dict_hints.go b/pkg/hints/squash_dict_hints.go index 94a20d54..d6bcee67 100644 --- a/pkg/hints/squash_dict_hints.go +++ b/pkg/hints/squash_dict_hints.go @@ -14,7 +14,7 @@ import ( ) // SortMaybeRelocatables implements sort.Interface for []*MaybeRelocatables -type SortMaybeRelocatables []*MaybeRelocatable +type SortMaybeRelocatables []MaybeRelocatable func (s SortMaybeRelocatables) Len() int { return len(s) } func (s SortMaybeRelocatables) Swap(i, j int) { s[i], s[j] = s[j], s[i] } @@ -51,7 +51,7 @@ func (s SortMaybeRelocatables) Less(i, j int) bool { } func squashDict(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error { - address, err := ids.GetAddr("dict_accesses", vm) + address, err := ids.GetRelocatable("dict_accesses", vm) if err != nil { return err } @@ -80,17 +80,17 @@ func squashDict(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) err } } // A map from key to the list of indices accessing it. - accessIndices := make(map[*MaybeRelocatable][]int) + accessIndices := make(map[MaybeRelocatable][]int) for i := 0; i < int(nAccesses); i++ { key, err := vm.Segments.Memory.Get(address.AddUint(uint(i) * DICT_ACCESS_SIZE)) if err != nil { return err } - _, hasKey := accessIndices[key] + _, hasKey := accessIndices[*key] if !hasKey { - accessIndices[key] = make([]int, 0) + accessIndices[*key] = make([]int, 0) } - accessIndices[key] = append(accessIndices[key], i) + accessIndices[*key] = append(accessIndices[*key], i) } //Descending list of keys. keys := maps.Keys(accessIndices) @@ -111,5 +111,5 @@ func squashDict(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) err if err != nil { return err } - return ids.Insert("first_key", lowKey, vm) + return ids.Insert("first_key", &lowKey, vm) } diff --git a/pkg/hints/squash_dict_hints_test.go b/pkg/hints/squash_dict_hints_test.go new file mode 100644 index 00000000..676bf00e --- /dev/null +++ b/pkg/hints/squash_dict_hints_test.go @@ -0,0 +1,59 @@ +package hints_test + +import ( + "reflect" + "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 TestSquashDictValidOneKeyDictNoMaxSizeBigKeys(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "dict_accesses": {NewMaybeRelocatableRelocatable(NewRelocatable(2, 0))}, + "big_keys": {nil}, + "first_key": {nil}, + "ptr_diff": {NewMaybeRelocatableFelt(FeltFromUint64(6))}, + "n_accesses": {NewMaybeRelocatableFelt(FeltFromUint64(2))}, + }, + vm, + ) + // Insert dict into memory + // Dict = {(prime - 1): (1,1), (prime - 1): (1,2)} + vm.Segments.Memory.Insert(NewRelocatable(2, 0), NewMaybeRelocatableFelt(FeltFromDecString("-1"))) + vm.Segments.Memory.Insert(NewRelocatable(2, 1), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 2), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 3), NewMaybeRelocatableFelt(FeltFromDecString("-1"))) + vm.Segments.Memory.Insert(NewRelocatable(2, 4), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 5), NewMaybeRelocatableFelt(FeltFromUint64(2))) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("SQUASH_DICT hint failed with error: %s", err) + } + // Check scope + accessIndicesAny, err := scopes.Get("access_indices") + accessIndices := accessIndicesAny.(map[MaybeRelocatable][]int) + // expect access_indices = {prime -1: [0, 1]} + expectedAccessIndices := map[MaybeRelocatable][]int{ + *NewMaybeRelocatableFelt(FeltFromDecString("-1")): {0, 1}, + } + if !reflect.DeepEqual(accessIndices, expectedAccessIndices) { + t.Errorf("SQUASH_DICT wrong access_indices.\n Expected %v, got: %v", expectedAccessIndices, accessIndices) + } + +} From 3ccfc6cc427cbcab34ca174935744f51c00fa3d3 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 14 Sep 2023 15:17:32 -0300 Subject: [PATCH 49/74] Complete test --- pkg/hints/squash_dict_hints_test.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/pkg/hints/squash_dict_hints_test.go b/pkg/hints/squash_dict_hints_test.go index 676bf00e..f6721115 100644 --- a/pkg/hints/squash_dict_hints_test.go +++ b/pkg/hints/squash_dict_hints_test.go @@ -55,5 +55,18 @@ func TestSquashDictValidOneKeyDictNoMaxSizeBigKeys(t *testing.T) { if !reflect.DeepEqual(accessIndices, expectedAccessIndices) { t.Errorf("SQUASH_DICT wrong access_indices.\n Expected %v, got: %v", expectedAccessIndices, accessIndices) } - + keysAny, err := scopes.Get("keys") + keys := keysAny.([]MaybeRelocatable) + expectedKeys := []MaybeRelocatable{ + *NewMaybeRelocatableFelt(FeltFromDecString("-1")), + } + if !reflect.DeepEqual(keys, expectedKeys) { + t.Errorf("SQUASH_DICT wrong keys.\n Expected %v, got: %v", expectedKeys, keys) + } + keyAny, err := scopes.Get("key") + key := keyAny.(MaybeRelocatable) + expectedKey := *NewMaybeRelocatableFelt(FeltFromDecString("-1")) + if !reflect.DeepEqual(key, expectedKey) { + t.Errorf("SQUASH_DICT wrong key.\n Expected %v, got: %v", expectedKey, keys) + } } From bb8e8711fffe4ce43cef29f1ba11bc9bbc09f9b0 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 14 Sep 2023 15:34:01 -0300 Subject: [PATCH 50/74] Add fix + test --- pkg/hints/squash_dict_hints.go | 2 +- pkg/hints/squash_dict_hints_test.go | 129 +++++++++++++++++++++++++++- 2 files changed, 128 insertions(+), 3 deletions(-) diff --git a/pkg/hints/squash_dict_hints.go b/pkg/hints/squash_dict_hints.go index d6bcee67..5b631c2d 100644 --- a/pkg/hints/squash_dict_hints.go +++ b/pkg/hints/squash_dict_hints.go @@ -104,7 +104,7 @@ func squashDict(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) err lowKey := keys[len(keys)-1] // Insert new scope variables scopes.AssignOrUpdateVariable("access_indices", accessIndices) - scopes.AssignOrUpdateVariable("keys", keys) + scopes.AssignOrUpdateVariable("keys", keys[:len(keys)-1]) scopes.AssignOrUpdateVariable("key", lowKey) // Insert ids variables err = ids.Insert("big_keys", NewMaybeRelocatableFelt(bigKeys), vm) diff --git a/pkg/hints/squash_dict_hints_test.go b/pkg/hints/squash_dict_hints_test.go index f6721115..63440d34 100644 --- a/pkg/hints/squash_dict_hints_test.go +++ b/pkg/hints/squash_dict_hints_test.go @@ -57,15 +57,140 @@ func TestSquashDictValidOneKeyDictNoMaxSizeBigKeys(t *testing.T) { } keysAny, err := scopes.Get("keys") keys := keysAny.([]MaybeRelocatable) + expectedKeys := []MaybeRelocatable{} + if !reflect.DeepEqual(keys, expectedKeys) { + t.Errorf("SQUASH_DICT wrong keys.\n Expected %v, got: %v", expectedKeys, keys) + } + keyAny, err := scopes.Get("key") + key := keyAny.(MaybeRelocatable) + expectedKey := *NewMaybeRelocatableFelt(FeltFromDecString("-1")) + if !reflect.DeepEqual(key, expectedKey) { + t.Errorf("SQUASH_DICT wrong key.\n Expected %v, got: %v", expectedKey, keys) + } +} + +func TestSquashDictValidOneKeyDictWithMaxSize(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("__squash_dict_max_size", 12) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "dict_accesses": {NewMaybeRelocatableRelocatable(NewRelocatable(2, 0))}, + "big_keys": {nil}, + "first_key": {nil}, + "ptr_diff": {NewMaybeRelocatableFelt(FeltFromUint64(6))}, + "n_accesses": {NewMaybeRelocatableFelt(FeltFromUint64(2))}, + }, + vm, + ) + // Insert dict into memory + // Dict = {(prime - 1): (1,1), (prime - 1): (1,2)} + vm.Segments.Memory.Insert(NewRelocatable(2, 0), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 1), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 2), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 3), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 4), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 5), NewMaybeRelocatableFelt(FeltFromUint64(2))) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("SQUASH_DICT hint failed with error: %s", err) + } + // Check scope + accessIndicesAny, err := scopes.Get("access_indices") + accessIndices := accessIndicesAny.(map[MaybeRelocatable][]int) + // expect access_indices = {prime -1: [0, 1]} + expectedAccessIndices := map[MaybeRelocatable][]int{ + *NewMaybeRelocatableFelt(FeltOne()): {0, 1}, + } + if !reflect.DeepEqual(accessIndices, expectedAccessIndices) { + t.Errorf("SQUASH_DICT wrong access_indices.\n Expected %v, got: %v", expectedAccessIndices, accessIndices) + } + keysAny, err := scopes.Get("keys") + keys := keysAny.([]MaybeRelocatable) + expectedKeys := []MaybeRelocatable{} + if !reflect.DeepEqual(keys, expectedKeys) { + t.Errorf("SQUASH_DICT wrong keys.\n Expected %v, got: %v", expectedKeys, keys) + } + keyAny, err := scopes.Get("key") + key := keyAny.(MaybeRelocatable) + expectedKey := *NewMaybeRelocatableFelt(FeltOne()) + if !reflect.DeepEqual(key, expectedKey) { + t.Errorf("SQUASH_DICT wrong key.\n Expected %v, got: %v", expectedKey, keys) + } +} + +func TestSquashDictValidTwoKeyDictNoMaxSizeBigKeys(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "dict_accesses": {NewMaybeRelocatableRelocatable(NewRelocatable(2, 0))}, + "big_keys": {nil}, + "first_key": {nil}, + "ptr_diff": {NewMaybeRelocatableFelt(FeltFromUint64(6))}, + "n_accesses": {NewMaybeRelocatableFelt(FeltFromUint64(4))}, + }, + vm, + ) + // Insert dict into memory + //Dict = {1: (1,1), 1: (1,2), 2: (10,10), 2: (10,20)} + vm.Segments.Memory.Insert(NewRelocatable(2, 0), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 1), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 2), NewMaybeRelocatableFelt(FeltOne())) + + vm.Segments.Memory.Insert(NewRelocatable(2, 3), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 4), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 5), NewMaybeRelocatableFelt(FeltFromUint64(2))) + + vm.Segments.Memory.Insert(NewRelocatable(2, 6), NewMaybeRelocatableFelt(FeltFromUint64(2))) + vm.Segments.Memory.Insert(NewRelocatable(2, 7), NewMaybeRelocatableFelt(FeltFromUint64(10))) + vm.Segments.Memory.Insert(NewRelocatable(2, 8), NewMaybeRelocatableFelt(FeltFromUint64(10))) + + vm.Segments.Memory.Insert(NewRelocatable(2, 9), NewMaybeRelocatableFelt(FeltFromUint64(2))) + vm.Segments.Memory.Insert(NewRelocatable(2, 10), NewMaybeRelocatableFelt(FeltFromUint64(10))) + vm.Segments.Memory.Insert(NewRelocatable(2, 11), NewMaybeRelocatableFelt(FeltFromUint64(20))) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("SQUASH_DICT hint failed with error: %s", err) + } + // Check scope + accessIndicesAny, err := scopes.Get("access_indices") + accessIndices := accessIndicesAny.(map[MaybeRelocatable][]int) + // expect access_indices = {prime -1: [0, 1]} + expectedAccessIndices := map[MaybeRelocatable][]int{ + *NewMaybeRelocatableFelt(FeltOne()): {0, 1}, + *NewMaybeRelocatableFelt(FeltFromUint64(2)): {2, 3}, + } + if !reflect.DeepEqual(accessIndices, expectedAccessIndices) { + t.Errorf("SQUASH_DICT wrong access_indices.\n Expected %v, got: %v", expectedAccessIndices, accessIndices) + } + keysAny, err := scopes.Get("keys") + keys := keysAny.([]MaybeRelocatable) expectedKeys := []MaybeRelocatable{ - *NewMaybeRelocatableFelt(FeltFromDecString("-1")), + *NewMaybeRelocatableFelt(FeltFromUint64(2)), } if !reflect.DeepEqual(keys, expectedKeys) { t.Errorf("SQUASH_DICT wrong keys.\n Expected %v, got: %v", expectedKeys, keys) } keyAny, err := scopes.Get("key") key := keyAny.(MaybeRelocatable) - expectedKey := *NewMaybeRelocatableFelt(FeltFromDecString("-1")) + expectedKey := *NewMaybeRelocatableFelt(FeltOne()) if !reflect.DeepEqual(key, expectedKey) { t.Errorf("SQUASH_DICT wrong key.\n Expected %v, got: %v", expectedKey, keys) } From f64f7ffc140b6b7d637beb92b1d06e3394e1d273 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 14 Sep 2023 15:44:45 -0300 Subject: [PATCH 51/74] Add Fix + Test --- pkg/hints/squash_dict_hints.go | 2 +- pkg/hints/squash_dict_hints_test.go | 108 +++++++++++++++++++++++++++- 2 files changed, 108 insertions(+), 2 deletions(-) diff --git a/pkg/hints/squash_dict_hints.go b/pkg/hints/squash_dict_hints.go index 5b631c2d..10fed1c5 100644 --- a/pkg/hints/squash_dict_hints.go +++ b/pkg/hints/squash_dict_hints.go @@ -60,7 +60,7 @@ func squashDict(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) err return err } if !ptrDiff.ModFloor(lambdaworks.FeltFromUint64(DICT_ACCESS_SIZE)).IsZero() { - errors.New("Accesses array size must be divisible by DictAccess.SIZE") + return errors.New("Accesses array size must be divisible by DictAccess.SIZE") } nAccessesFelt, err := ids.GetFelt("n_accesses", vm) if err != nil { diff --git a/pkg/hints/squash_dict_hints_test.go b/pkg/hints/squash_dict_hints_test.go index 63440d34..a57b15c1 100644 --- a/pkg/hints/squash_dict_hints_test.go +++ b/pkg/hints/squash_dict_hints_test.go @@ -75,7 +75,7 @@ func TestSquashDictValidOneKeyDictWithMaxSize(t *testing.T) { vm.Segments.AddSegment() vm.Segments.AddSegment() scopes := types.NewExecutionScopes() - scopes.AssignOrUpdateVariable("__squash_dict_max_size", 12) + scopes.AssignOrUpdateVariable("__squash_dict_max_size", uint64(12)) idsManager := SetupIdsForTest( map[string][]*MaybeRelocatable{ "dict_accesses": {NewMaybeRelocatableRelocatable(NewRelocatable(2, 0))}, @@ -195,3 +195,109 @@ func TestSquashDictValidTwoKeyDictNoMaxSizeBigKeys(t *testing.T) { t.Errorf("SQUASH_DICT wrong key.\n Expected %v, got: %v", expectedKey, keys) } } + +func TestSquashDictInvalidOneKeyDictWithMaxSizeExceeded(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("__squash_dict_max_size", uint64(1)) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "dict_accesses": {NewMaybeRelocatableRelocatable(NewRelocatable(2, 0))}, + "big_keys": {nil}, + "first_key": {nil}, + "ptr_diff": {NewMaybeRelocatableFelt(FeltFromUint64(6))}, + "n_accesses": {NewMaybeRelocatableFelt(FeltFromUint64(2))}, + }, + vm, + ) + // Insert dict into memory + // Dict = {(prime - 1): (1,1), (prime - 1): (1,2)} + vm.Segments.Memory.Insert(NewRelocatable(2, 0), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 1), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 2), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 3), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 4), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 5), NewMaybeRelocatableFelt(FeltFromUint64(2))) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Errorf("SQUASH_DICT hint should have failed") + } +} + +func TestSquashDictInvalidOneKeyDictBadPtrDiff(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "dict_accesses": {NewMaybeRelocatableRelocatable(NewRelocatable(2, 0))}, + "big_keys": {nil}, + "first_key": {nil}, + "ptr_diff": {NewMaybeRelocatableFelt(FeltFromUint64(7))}, + "n_accesses": {NewMaybeRelocatableFelt(FeltFromUint64(2))}, + }, + vm, + ) + // Insert dict into memory + // Dict = {(prime - 1): (1,1), (prime - 1): (1,2)} + vm.Segments.Memory.Insert(NewRelocatable(2, 0), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 1), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 2), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 3), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 4), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 5), NewMaybeRelocatableFelt(FeltFromUint64(2))) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Errorf("SQUASH_DICT hint should have failed") + } +} + +func TestSquashDictInvalidOneKeyDictNAccessesTooBig(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "dict_accesses": {NewMaybeRelocatableRelocatable(NewRelocatable(2, 0))}, + "big_keys": {nil}, + "first_key": {nil}, + "ptr_diff": {NewMaybeRelocatableFelt(FeltFromUint64(6))}, + "n_accesses": {NewMaybeRelocatableFelt(FeltFromDecString("-1"))}, + }, + vm, + ) + // Insert dict into memory + // Dict = {(prime - 1): (1,1), (prime - 1): (1,2)} + vm.Segments.Memory.Insert(NewRelocatable(2, 0), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 1), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 2), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 3), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 4), NewMaybeRelocatableFelt(FeltOne())) + vm.Segments.Memory.Insert(NewRelocatable(2, 5), NewMaybeRelocatableFelt(FeltFromUint64(2))) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Errorf("SQUASH_DICT hint should have failed") + } +} From 14fb716ec0631e0ba7e7519e5ede2f829eac170f Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 14 Sep 2023 16:09:20 -0300 Subject: [PATCH 52/74] Add tests --- pkg/hints/hint_processor.go | 2 + pkg/hints/squash_dict_hints.go | 16 ++++++-- pkg/hints/squash_dict_hints_test.go | 57 +++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 4 deletions(-) diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index b38f9b4f..87c1627e 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -59,6 +59,8 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, return assert_not_equal(data.Ids, vm) case SQUASH_DICT: return squashDict(data.Ids, execScopes, vm) + case SQUASH_DICT_INNER_SKIP_LOOP: + return squashDictInnerSkipLoop(data.Ids, execScopes, vm) default: return errors.Errorf("Unknown Hint: %s", data.Code) } diff --git a/pkg/hints/squash_dict_hints.go b/pkg/hints/squash_dict_hints.go index 10fed1c5..339686d8 100644 --- a/pkg/hints/squash_dict_hints.go +++ b/pkg/hints/squash_dict_hints.go @@ -5,7 +5,7 @@ import ( "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/types" . "github.com/lambdaclass/cairo-vm.go/pkg/vm" . "github.com/lambdaclass/cairo-vm.go/pkg/vm/memory" @@ -59,7 +59,7 @@ func squashDict(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) err if err != nil { return err } - if !ptrDiff.ModFloor(lambdaworks.FeltFromUint64(DICT_ACCESS_SIZE)).IsZero() { + if !ptrDiff.ModFloor(FeltFromUint64(DICT_ACCESS_SIZE)).IsZero() { return errors.New("Accesses array size must be divisible by DictAccess.SIZE") } nAccessesFelt, err := ids.GetFelt("n_accesses", vm) @@ -96,10 +96,10 @@ func squashDict(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) err keys := maps.Keys(accessIndices) sort.Sort(sort.Reverse(SortMaybeRelocatables(keys))) //Are the keys used bigger than the range_check bound. - bigKeys := lambdaworks.FeltZero() + bigKeys := FeltZero() highKeyFelt, isFelt := keys[0].GetFelt() if isFelt && highKeyFelt.Bits() >= builtins.RANGE_CHECK_N_PARTS*builtins.INNER_RC_BOUND_SHIFT { - bigKeys = lambdaworks.FeltOne() + bigKeys = FeltOne() } lowKey := keys[len(keys)-1] // Insert new scope variables @@ -113,3 +113,11 @@ func squashDict(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) err } return ids.Insert("first_key", &lowKey, vm) } + +func squashDictInnerSkipLoop(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error { + _, err := scopes.Get("current_access_indices") + if err == nil { + return ids.Insert("should_skip_loop", NewMaybeRelocatableFelt(FeltZero()), vm) + } + return ids.Insert("should_skip_loop", NewMaybeRelocatableFelt(FeltOne()), vm) +} diff --git a/pkg/hints/squash_dict_hints_test.go b/pkg/hints/squash_dict_hints_test.go index a57b15c1..1dc4fc1e 100644 --- a/pkg/hints/squash_dict_hints_test.go +++ b/pkg/hints/squash_dict_hints_test.go @@ -301,3 +301,60 @@ func TestSquashDictInvalidOneKeyDictNAccessesTooBig(t *testing.T) { t.Errorf("SQUASH_DICT hint should have failed") } } + +func TestSquashDictSkipLoopTrue(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "should_skip_loop": {nil}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_SKIP_LOOP, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("SQUASH_DICT_INNER_SKIP_LOOP hint failed with error: %s", err) + } + // Check ids.skip_loop + skipLoop, err := idsManager.GetFelt("should_skip_loop", vm) + if err != nil || skipLoop != FeltOne() { + t.Errorf("SQUASH_DICT_INNER_SKIP_LOOP hint failed. Wrong/No ids.skip_loop") + } +} + +func TestSquashDictSkipLoopFalse(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("current_access_indices", []MaybeRelocatable{}) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "should_skip_loop": {nil}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_SKIP_LOOP, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("SQUASH_DICT_INNER_SKIP_LOOP hint failed with error: %s", err) + } + // Check ids.skip_loop + skipLoop, err := idsManager.GetFelt("should_skip_loop", vm) + if err != nil || skipLoop != FeltZero() { + t.Errorf("SQUASH_DICT_INNER_SKIP_LOOP hint failed. Wrong/No ids.skip_loop") + } +} From 434773d36e85eb00d221112a47782882444bf3fa Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 14 Sep 2023 16:32:26 -0300 Subject: [PATCH 53/74] Implement SQUASH_DICT_INNER_FIRST_ITERATION --- pkg/hints/squash_dict_hints.go | 42 ++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/pkg/hints/squash_dict_hints.go b/pkg/hints/squash_dict_hints.go index 339686d8..bfeab1f9 100644 --- a/pkg/hints/squash_dict_hints.go +++ b/pkg/hints/squash_dict_hints.go @@ -101,6 +101,9 @@ func squashDict(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) err if isFelt && highKeyFelt.Bits() >= builtins.RANGE_CHECK_N_PARTS*builtins.INNER_RC_BOUND_SHIFT { bigKeys = FeltOne() } + if len(keys) == 0 { + return errors.New("keys is empty") + } lowKey := keys[len(keys)-1] // Insert new scope variables scopes.AssignOrUpdateVariable("access_indices", accessIndices) @@ -121,3 +124,42 @@ func squashDictInnerSkipLoop(ids IdsManager, scopes *ExecutionScopes, vm *Virtua } return ids.Insert("should_skip_loop", NewMaybeRelocatableFelt(FeltOne()), vm) } + +func squashDictInnerFirstIteration(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error { + // Fetch scope variables + accessIndicesAny, err := scopes.Get("access_indices") + if err != nil { + return err + } + accessIndices, ok := accessIndicesAny.(map[MaybeRelocatable][]int) + if !ok { + return errors.New("access_indices not in scope") + } + + keyAny, err := scopes.Get("key") + if err != nil { + return err + } + key, ok := keyAny.(MaybeRelocatable) + if !ok { + return errors.New("key not in scope") + } + // Fetch ids variables + rangeCheckPtr, err := ids.GetRelocatable("range_check_ptr", vm) + if err != nil { + return err + } + // Hint Logic + currentAccessIndices := accessIndices[key] + sort.Sort(sort.Reverse(sort.IntSlice(currentAccessIndices))) + if len(currentAccessIndices) == 0 { + return errors.New("access_indices is empty") + } + currentAccessIndex := currentAccessIndices[len(currentAccessIndices)-1] + currentAccessIndices = currentAccessIndices[:len(currentAccessIndices)-1] + // Add variables to scope + scopes.AssignOrUpdateVariable("current_access_indices", currentAccessIndices) + scopes.AssignOrUpdateVariable("current_access_index", currentAccessIndex) + //Insert current_accesss_index into range_check_ptr + return vm.Segments.Memory.Insert(rangeCheckPtr, NewMaybeRelocatableFelt(FeltFromUint64(uint64(currentAccessIndex)))) +} From 19d6beaffadaf9e4935a69d85856e04082ded1e5 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 14 Sep 2023 16:45:31 -0300 Subject: [PATCH 54/74] Add test --- pkg/hints/hint_processor.go | 2 ++ pkg/hints/squash_dict_hints_test.go | 48 +++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index 87c1627e..696d3516 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -61,6 +61,8 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, return squashDict(data.Ids, execScopes, vm) case SQUASH_DICT_INNER_SKIP_LOOP: return squashDictInnerSkipLoop(data.Ids, execScopes, vm) + case SQUASH_DICT_INNER_FIRST_ITERATION: + return squashDictInnerFirstIteration(data.Ids, execScopes, vm) default: return errors.Errorf("Unknown Hint: %s", data.Code) } diff --git a/pkg/hints/squash_dict_hints_test.go b/pkg/hints/squash_dict_hints_test.go index 1dc4fc1e..68980fe1 100644 --- a/pkg/hints/squash_dict_hints_test.go +++ b/pkg/hints/squash_dict_hints_test.go @@ -358,3 +358,51 @@ func TestSquashDictSkipLoopFalse(t *testing.T) { t.Errorf("SQUASH_DICT_INNER_SKIP_LOOP hint failed. Wrong/No ids.skip_loop") } } + +func TestSquashDictInnerFirstIterationOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + range_check_ptr := vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("access_indices", map[MaybeRelocatable][]int{ + *NewMaybeRelocatableFelt(FeltFromUint64(5)): { + 9, 3, 10, 7, + }, + }) + scopes.AssignOrUpdateVariable("key", *NewMaybeRelocatableFelt(FeltFromUint64(5))) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "range_check_ptr": {NewMaybeRelocatableRelocatable(range_check_ptr)}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_FIRST_ITERATION, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("SQUASH_DICT_INNER_FIRST_ITERATION hint failed with error: %s", err) + } + // Check scope values + currentAccessIndicesAny, err := scopes.Get("current_access_indices") + currentAccessIndices := currentAccessIndicesAny.([]int) + expectedCurrentAccessIndices := []int{10, 9, 7} + if !reflect.DeepEqual(currentAccessIndices, expectedCurrentAccessIndices) { + t.Errorf("Wrong current_access_indices.\n Expected %v, got: %v", expectedCurrentAccessIndices, currentAccessIndices) + } + + currentAccessIndexAny, err := scopes.Get("current_access_index") + currentAccessIndex := currentAccessIndexAny.(int) + expectedCurrentAccessIndex := int(3) + if !reflect.DeepEqual(currentAccessIndex, expectedCurrentAccessIndex) { + t.Errorf("Wrong current_access_index.\n Expected %v, got: %v", expectedCurrentAccessIndex, currentAccessIndex) + } + + // Check memory[ids.range_check_ptr] + val, err := vm.Segments.Memory.Get(range_check_ptr) + if err != nil || *val != *NewMaybeRelocatableFelt(FeltFromUint64(3)) { + t.Errorf("Wrong/No value inserted into memory[ids.range_check_ptr]") + } +} From 9fc2bf716ff69ccdcf04d5ca10409310014b56ad Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 14 Sep 2023 16:47:25 -0300 Subject: [PATCH 55/74] Add test --- pkg/hints/squash_dict_hints_test.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/pkg/hints/squash_dict_hints_test.go b/pkg/hints/squash_dict_hints_test.go index 68980fe1..bd9d258d 100644 --- a/pkg/hints/squash_dict_hints_test.go +++ b/pkg/hints/squash_dict_hints_test.go @@ -406,3 +406,27 @@ func TestSquashDictInnerFirstIterationOk(t *testing.T) { t.Errorf("Wrong/No value inserted into memory[ids.range_check_ptr]") } } + +func TestSquashDictInnerFirstIterationEmpty(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + range_check_ptr := vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("access_indices", map[MaybeRelocatable][]int{}) + scopes.AssignOrUpdateVariable("key", *NewMaybeRelocatableFelt(FeltFromUint64(5))) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "range_check_ptr": {NewMaybeRelocatableRelocatable(range_check_ptr)}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_FIRST_ITERATION, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Error("SQUASH_DICT_INNER_FIRST_ITERATION hint should have failed") + } +} From 0ae2320221ece3e349f5b9c7b82030f36144e832 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 14 Sep 2023 17:37:48 -0300 Subject: [PATCH 56/74] Implement hint --- pkg/hints/hint_processor.go | 2 ++ pkg/hints/squash_dict_hints.go | 36 +++++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index 696d3516..eb4a8733 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -63,6 +63,8 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, return squashDictInnerSkipLoop(data.Ids, execScopes, vm) case SQUASH_DICT_INNER_FIRST_ITERATION: return squashDictInnerFirstIteration(data.Ids, execScopes, vm) + case SQUASH_DICT_INNER_CHECK_ACCESS_INDEX: + return squashDictInnerCheckAccessIndex(data.Ids, execScopes, vm) default: return errors.Errorf("Unknown Hint: %s", data.Code) } diff --git a/pkg/hints/squash_dict_hints.go b/pkg/hints/squash_dict_hints.go index bfeab1f9..38fa4bda 100644 --- a/pkg/hints/squash_dict_hints.go +++ b/pkg/hints/squash_dict_hints.go @@ -1,6 +1,7 @@ package hints import ( + "fmt" "sort" "github.com/lambdaclass/cairo-vm.go/pkg/builtins" @@ -153,7 +154,7 @@ func squashDictInnerFirstIteration(ids IdsManager, scopes *ExecutionScopes, vm * currentAccessIndices := accessIndices[key] sort.Sort(sort.Reverse(sort.IntSlice(currentAccessIndices))) if len(currentAccessIndices) == 0 { - return errors.New("access_indices is empty") + return errors.New("current_access_indices is empty") } currentAccessIndex := currentAccessIndices[len(currentAccessIndices)-1] currentAccessIndices = currentAccessIndices[:len(currentAccessIndices)-1] @@ -163,3 +164,36 @@ func squashDictInnerFirstIteration(ids IdsManager, scopes *ExecutionScopes, vm * //Insert current_accesss_index into range_check_ptr return vm.Segments.Memory.Insert(rangeCheckPtr, NewMaybeRelocatableFelt(FeltFromUint64(uint64(currentAccessIndex)))) } + +func squashDictInnerCheckAccessIndex(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error { + // Fetch scope variables + currentAccessIndicesAny, err := scopes.Get("current_access_indices") + if err != nil { + return err + } + currentAccessIndices, ok := currentAccessIndicesAny.([]int) + if !ok { + return errors.New("current_access_indices not in scope") + } + currentAccessIndexAny, err := scopes.Get("current_access_index") + if err != nil { + return err + } + currentAccessIndex, ok := currentAccessIndexAny.(int) + if !ok { + return errors.New("current_access_index not in scope") + } + // Hint Logic + if len(currentAccessIndices) == 0 { + return errors.New("current_access_indices is empty") + } + newAccessIndex := currentAccessIndices[len(currentAccessIndices)-1] + currentAccessIndices = currentAccessIndices[:len(currentAccessIndices)-1] + deltaMinusOne := newAccessIndex - currentAccessIndex - 1 + // Update scope variables + scopes.AssignOrUpdateVariable("current_access_indices", currentAccessIndices) + scopes.AssignOrUpdateVariable("current_access_index", newAccessIndex) + scopes.AssignOrUpdateVariable("new_access_index", newAccessIndex) + // Update ids variables + return ids.Insert("loop_temps", NewMaybeRelocatableFelt(FeltFromDecString(fmt.Sprint(deltaMinusOne))), vm) +} From 11377da264735ec3f614f73147d1adc247c4ae50 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 14 Sep 2023 17:46:20 -0300 Subject: [PATCH 57/74] Add test --- pkg/hints/squash_dict_hints_test.go | 53 +++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/pkg/hints/squash_dict_hints_test.go b/pkg/hints/squash_dict_hints_test.go index bd9d258d..6d2e1863 100644 --- a/pkg/hints/squash_dict_hints_test.go +++ b/pkg/hints/squash_dict_hints_test.go @@ -430,3 +430,56 @@ func TestSquashDictInnerFirstIterationEmpty(t *testing.T) { t.Error("SQUASH_DICT_INNER_FIRST_ITERATION hint should have failed") } } + +func TestSquashDictInnerCheckAccessIndexOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("current_access_indices", []int{ + 10, 9, 7, 5, + }, + ) + scopes.AssignOrUpdateVariable("current_access_index", int(1)) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "loop_temps": {nil}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_CHECK_ACCESS_INDEX, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("SQUASH_DICT_INNER_CHECK_ACCESS_INDEX hint failed with error: %s", err) + } + // Check scope values + currentAccessIndicesAny, err := scopes.Get("current_access_indices") + currentAccessIndices := currentAccessIndicesAny.([]int) + expectedCurrentAccessIndices := []int{10, 9, 7} + if !reflect.DeepEqual(currentAccessIndices, expectedCurrentAccessIndices) { + t.Errorf("Wrong current_access_indices.\n Expected %v, got: %v", expectedCurrentAccessIndices, currentAccessIndices) + } + + currentAccessIndexAny, err := scopes.Get("current_access_index") + currentAccessIndex := currentAccessIndexAny.(int) + expectedCurrentAccessIndex := int(5) + if !reflect.DeepEqual(currentAccessIndex, expectedCurrentAccessIndex) { + t.Errorf("Wrong current_access_index.\n Expected %v, got: %v", expectedCurrentAccessIndex, currentAccessIndex) + } + + newAccessIndexAny, err := scopes.Get("new_access_index") + newAccessIndex := newAccessIndexAny.(int) + expectedNewAccessIndex := int(5) + if !reflect.DeepEqual(newAccessIndex, expectedNewAccessIndex) { + t.Errorf("Wrong new_access_index.\n Expected %v, got: %v", expectedNewAccessIndex, newAccessIndex) + } + + //Check loop_temps.index_delta_minus_1 + val, err := idsManager.GetFelt("loop_temps", vm) + if err != nil || val != FeltFromUint64(3) { + t.Errorf("Wrong/No value inserted into memory[ids.range_check_ptr]") + } +} From 5f2b604bc87e4c41ca282e433605f717adbfc4c9 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 14 Sep 2023 17:48:50 -0300 Subject: [PATCH 58/74] Test --- pkg/hints/squash_dict_hints_test.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/pkg/hints/squash_dict_hints_test.go b/pkg/hints/squash_dict_hints_test.go index 6d2e1863..d4b38476 100644 --- a/pkg/hints/squash_dict_hints_test.go +++ b/pkg/hints/squash_dict_hints_test.go @@ -483,3 +483,26 @@ func TestSquashDictInnerCheckAccessIndexOk(t *testing.T) { t.Errorf("Wrong/No value inserted into memory[ids.range_check_ptr]") } } + +func TestSquashDictInnerCheckAccessIndexEmpty(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("current_access_indices", []int{}) + scopes.AssignOrUpdateVariable("current_access_index", int(1)) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "loop_temps": {nil}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_CHECK_ACCESS_INDEX, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Errorf("SQUASH_DICT_INNER_CHECK_ACCESS_INDEX hint should have failed") + } +} From 2ae857b1bb63bc308a5520ac8860eaafea76945e Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 14 Sep 2023 17:59:33 -0300 Subject: [PATCH 59/74] Add Hint + tests --- pkg/hints/hint_processor.go | 2 + pkg/hints/squash_dict_hints.go | 8 ++++ pkg/hints/squash_dict_hints_test.go | 57 +++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index eb4a8733..425772ca 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -65,6 +65,8 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, return squashDictInnerFirstIteration(data.Ids, execScopes, vm) case SQUASH_DICT_INNER_CHECK_ACCESS_INDEX: return squashDictInnerCheckAccessIndex(data.Ids, execScopes, vm) + case SQUASH_DICT_INNER_CONTINUE_LOOP: + return squashDictInnerContinueLoop(data.Ids, execScopes, vm) default: return errors.Errorf("Unknown Hint: %s", data.Code) } diff --git a/pkg/hints/squash_dict_hints.go b/pkg/hints/squash_dict_hints.go index 38fa4bda..b3f66fb7 100644 --- a/pkg/hints/squash_dict_hints.go +++ b/pkg/hints/squash_dict_hints.go @@ -197,3 +197,11 @@ func squashDictInnerCheckAccessIndex(ids IdsManager, scopes *ExecutionScopes, vm // Update ids variables return ids.Insert("loop_temps", NewMaybeRelocatableFelt(FeltFromDecString(fmt.Sprint(deltaMinusOne))), vm) } + +func squashDictInnerContinueLoop(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error { + _, err := scopes.Get("current_access_indices") + if err != nil { + return ids.InsertStructField("loop_temps", 3, NewMaybeRelocatableFelt(FeltZero()), vm) + } + return ids.InsertStructField("loop_temps", 3, NewMaybeRelocatableFelt(FeltOne()), vm) +} diff --git a/pkg/hints/squash_dict_hints_test.go b/pkg/hints/squash_dict_hints_test.go index d4b38476..0c5cbcec 100644 --- a/pkg/hints/squash_dict_hints_test.go +++ b/pkg/hints/squash_dict_hints_test.go @@ -506,3 +506,60 @@ func TestSquashDictInnerCheckAccessIndexEmpty(t *testing.T) { t.Errorf("SQUASH_DICT_INNER_CHECK_ACCESS_INDEX hint should have failed") } } + +func TestSquashDictContinuepLoopTrue(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("current_access_indices", []MaybeRelocatable{}) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "loop_temps": {nil}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_CONTINUE_LOOP, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("SQUASH_DICT_INNER_CONTINUE_LOOP hint failed with error: %s", err) + } + // Check ids.loop_temps.should_continue + skipLoop, err := idsManager.GetStructFieldFelt("loop_temps", 3, vm) + if err != nil || skipLoop != FeltOne() { + t.Errorf("SQUASH_DICT_INNER_CONTINUE_LOOP hint failed. Wrong/No ids.loop_temps.should_continue") + } +} + +func TestSquashDictContinueLoopFalse(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "loop_temps": {nil}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_CONTINUE_LOOP, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("SQUASH_DICT_INNER_CONTINUE_LOOP hint failed with error: %s", err) + } + // Check ids.loop_temps.should_continue + continueLoop, err := idsManager.GetStructFieldFelt("loop_temps", 3, vm) + if err != nil || continueLoop != FeltZero() { + t.Errorf("SQUASH_DICT_INNER_CONTINUE_LOOP hint failed. Wrong/No ids.loop_temps.should_continue") + } +} From 9e129344581402dd856916d7026ba34c9d04ecdf Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 14 Sep 2023 18:10:47 -0300 Subject: [PATCH 60/74] Add tests --- pkg/hints/hint_processor.go | 2 ++ pkg/hints/squash_dict_hints.go | 17 +++++++++++++ pkg/hints/squash_dict_hints_test.go | 38 +++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index 425772ca..8f1c0b1b 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 squashDictInnerCheckAccessIndex(data.Ids, execScopes, vm) case SQUASH_DICT_INNER_CONTINUE_LOOP: return squashDictInnerContinueLoop(data.Ids, execScopes, vm) + case SQUASH_DICT_INNER_ASSERT_LEN_KEYS: + return squashDictInnerAssertLenKeys(execScopes) default: return errors.Errorf("Unknown Hint: %s", data.Code) } diff --git a/pkg/hints/squash_dict_hints.go b/pkg/hints/squash_dict_hints.go index b3f66fb7..d80fe11a 100644 --- a/pkg/hints/squash_dict_hints.go +++ b/pkg/hints/squash_dict_hints.go @@ -205,3 +205,20 @@ func squashDictInnerContinueLoop(ids IdsManager, scopes *ExecutionScopes, vm *Vi } return ids.InsertStructField("loop_temps", 3, NewMaybeRelocatableFelt(FeltOne()), vm) } + +func squashDictInnerAssertLenKeys(scopes *ExecutionScopes) error { + // Fetch scope variables + keysAny, err := scopes.Get("keys") + if err != nil { + return err + } + keys, ok := keysAny.([]MaybeRelocatable) + if !ok { + return errors.New("keys not in scope") + } + // Hint logic + if len(keys) != 0 { + return errors.New("Assertion failed: len(keys) == 0") + } + return nil +} diff --git a/pkg/hints/squash_dict_hints_test.go b/pkg/hints/squash_dict_hints_test.go index 0c5cbcec..cdbe6138 100644 --- a/pkg/hints/squash_dict_hints_test.go +++ b/pkg/hints/squash_dict_hints_test.go @@ -563,3 +563,41 @@ func TestSquashDictContinueLoopFalse(t *testing.T) { t.Errorf("SQUASH_DICT_INNER_CONTINUE_LOOP hint failed. Wrong/No ids.loop_temps.should_continue") } } + +func TestSquashDictInnerAssertLenKeysNotEmpty(t *testing.T) { + vm := NewVirtualMachine() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("keys", []MaybeRelocatable{*NewMaybeRelocatableFelt(FeltZero())}) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{}, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_ASSERT_LEN_KEYS, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Errorf("SQUASH_DICT_INNER_ASSERT_LEN_KEYS hint should have") + } +} + +func TestSquashDictInnerAssertLenKeysEmpty(t *testing.T) { + vm := NewVirtualMachine() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("keys", []MaybeRelocatable{}) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{}, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_ASSERT_LEN_KEYS, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("SQUASH_DICT_INNER_ASSERT_LEN_KEYS hint failed with error: %s", err) + } +} From 2487454563a1140634797a0dbd1d71b6167a92e4 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 14 Sep 2023 18:20:39 -0300 Subject: [PATCH 61/74] Add hint + test --- pkg/hints/hint_processor.go | 2 ++ pkg/hints/squash_dict_hints.go | 17 ++++++++++++ pkg/hints/squash_dict_hints_test.go | 40 ++++++++++++++++++++++++++++- 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index 8f1c0b1b..2ebbc6c1 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 squashDictInnerContinueLoop(data.Ids, execScopes, vm) case SQUASH_DICT_INNER_ASSERT_LEN_KEYS: return squashDictInnerAssertLenKeys(execScopes) + case SQUASH_DICT_INNER_LEN_ASSERT: + return squashDictInnerLenAssert(execScopes) default: return errors.Errorf("Unknown Hint: %s", data.Code) } diff --git a/pkg/hints/squash_dict_hints.go b/pkg/hints/squash_dict_hints.go index d80fe11a..0a081a11 100644 --- a/pkg/hints/squash_dict_hints.go +++ b/pkg/hints/squash_dict_hints.go @@ -222,3 +222,20 @@ func squashDictInnerAssertLenKeys(scopes *ExecutionScopes) error { } return nil } + +func squashDictInnerLenAssert(scopes *ExecutionScopes) error { + // Fetch scope variables + currentAccessIndicesAny, err := scopes.Get("current_access_indices") + if err != nil { + return err + } + currentAccessIndices, ok := currentAccessIndicesAny.([]int) + if !ok { + return errors.New("current_access_indices not in scope") + } + // Hint logic + if len(currentAccessIndices) != 0 { + return errors.New("Assertion failed: len(current_access_indices) == 0") + } + return nil +} diff --git a/pkg/hints/squash_dict_hints_test.go b/pkg/hints/squash_dict_hints_test.go index cdbe6138..2b0b7267 100644 --- a/pkg/hints/squash_dict_hints_test.go +++ b/pkg/hints/squash_dict_hints_test.go @@ -579,7 +579,7 @@ func TestSquashDictInnerAssertLenKeysNotEmpty(t *testing.T) { }) err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) if err == nil { - t.Errorf("SQUASH_DICT_INNER_ASSERT_LEN_KEYS hint should have") + t.Errorf("SQUASH_DICT_INNER_ASSERT_LEN_KEYS hint should have failed") } } @@ -601,3 +601,41 @@ func TestSquashDictInnerAssertLenKeysEmpty(t *testing.T) { t.Errorf("SQUASH_DICT_INNER_ASSERT_LEN_KEYS hint failed with error: %s", err) } } + +func TestSquashDictInnerLenAssertKeysNotEmpty(t *testing.T) { + vm := NewVirtualMachine() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("current_access_indices", []int{2, 3}) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{}, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_LEN_ASSERT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Errorf("SQUASH_DICT_INNER_LEN_ASSERT hint should have failed") + } +} + +func TestSquashDictInnerLenAssertEmpty(t *testing.T) { + vm := NewVirtualMachine() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("current_access_indices", []int{}) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{}, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_LEN_ASSERT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("SQUASH_DICT_INNER_LEN_ASSERT hint failed with error: %s", err) + } +} From a27db2100c11ced05e999de561d562674e0c39a0 Mon Sep 17 00:00:00 2001 From: toni-calvin Date: Fri, 15 Sep 2023 13:58:27 +0200 Subject: [PATCH 62/74] remove ExecutionScopes.Data() method --- pkg/hints/memcpy_hints_test.go | 25 ++++++------------------- pkg/types/exec_scopes.go | 4 ---- 2 files changed, 6 insertions(+), 23 deletions(-) diff --git a/pkg/hints/memcpy_hints_test.go b/pkg/hints/memcpy_hints_test.go index eacbfa57..3ff7c859 100644 --- a/pkg/hints/memcpy_hints_test.go +++ b/pkg/hints/memcpy_hints_test.go @@ -27,7 +27,7 @@ func AddSegmentHintOk(t *testing.T) { } } -func TestExitScopeValid(t *testing.T) { +func TestExitScopeHintValid(t *testing.T) { vm := NewVirtualMachine() vm.Segments.AddSegment() idsManager := SetupIdsForTest( @@ -49,12 +49,12 @@ func TestExitScopeValid(t *testing.T) { err := hintProcessor.ExecuteHint(vm, &hintData, nil, executionScopes) if err != nil { - t.Errorf("TestExitScopeValid failed with error %s", err) + t.Errorf("TestExitScopeHintValid failed with error %s", err) } } -func TestExitScopeInvalid(t *testing.T) { +func TestExitScopeHintInvalid(t *testing.T) { vm := NewVirtualMachine() vm.Segments.AddSegment() idsManager := SetupIdsForTest( @@ -75,7 +75,7 @@ func TestExitScopeInvalid(t *testing.T) { err := hintProcessor.ExecuteHint(vm, &hintData, nil, executionScopes) if err.Error() != ErrCannotExitMainScop.Error() { - t.Errorf("TestExitScopeInvalid should fail with error %s", ErrCannotExitMainScop) + t.Errorf("TestExitScopeHintInvalid should fail with error %s", ErrCannotExitMainScop) } } @@ -84,9 +84,7 @@ func TestEnterScope(t *testing.T) { vm := NewVirtualMachine() vm.Segments.AddSegment() idsManager := SetupIdsForTest( - map[string][]*MaybeRelocatable{ - "a": {NewMaybeRelocatableFelt(FeltFromUint64(17))}, - }, + map[string][]*MaybeRelocatable{}, vm, ) hintProcessor := CairoVmHintProcessor{} @@ -101,17 +99,6 @@ func TestEnterScope(t *testing.T) { err := hintProcessor.ExecuteHint(vm, &hintData, nil, executionScopes) if err != nil { - t.Errorf("TestEnterScope failed with error %s", err) - } - - if len(executionScopes.Data()) != 2 { - t.Errorf("EnterScopeHint failed, expected data length: %d, got: %d", 2, len(executionScopes.Data())) - } - if len(executionScopes.Data()[0]) != 0 { - t.Errorf("EnterScopeHint failed, expected: 0, got: %v", len(executionScopes.Data()[0])) + t.Errorf("TestEnterScopeHint failed with error %s", err) } - if len(executionScopes.Data()[1]) != 0 { - t.Errorf("EnterScopeHint failed, expected: 0, got: %v", len(executionScopes.Data()[0])) - } - } diff --git a/pkg/types/exec_scopes.go b/pkg/types/exec_scopes.go index e94fe153..50f15213 100644 --- a/pkg/types/exec_scopes.go +++ b/pkg/types/exec_scopes.go @@ -14,10 +14,6 @@ func ExecutionScopesError(err error) error { return errors.Wrapf(err, "Execution scopes error") } -func (es *ExecutionScopes) Data() []map[string]interface{} { - return es.data -} - func ErrVariableNotInScope(varName string) error { return ExecutionScopesError(errors.Errorf("Variable %s not in scope", varName)) } From 259b35a61bf8ba2f479a4397bbd8b67ae256ff58 Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 15 Sep 2023 11:45:39 -0300 Subject: [PATCH 63/74] Add hint + test --- pkg/hints/hint_processor.go | 2 ++ pkg/hints/squash_dict_hints.go | 30 +++++++++++++++++++++++++++++ pkg/hints/squash_dict_hints_test.go | 25 ++++++++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index 2ebbc6c1..4d8f4fc4 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 squashDictInnerAssertLenKeys(execScopes) case SQUASH_DICT_INNER_LEN_ASSERT: return squashDictInnerLenAssert(execScopes) + case SQUASH_DICT_INNER_USED_ACCESSES_ASSERT: + return squashDictInnerUsedAccessesAssert(data.Ids, execScopes, vm) default: return errors.Errorf("Unknown Hint: %s", data.Code) } diff --git a/pkg/hints/squash_dict_hints.go b/pkg/hints/squash_dict_hints.go index 0a081a11..41a8c8b8 100644 --- a/pkg/hints/squash_dict_hints.go +++ b/pkg/hints/squash_dict_hints.go @@ -239,3 +239,33 @@ func squashDictInnerLenAssert(scopes *ExecutionScopes) error { } return nil } + +func squashDictInnerUsedAccessesAssert(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error { + // Fetch scope variables + accessIndicesAny, err := scopes.Get("access_indices") + if err != nil { + return err + } + accessIndices, ok := accessIndicesAny.(map[MaybeRelocatable][]int) + if !ok { + return errors.New("access_indices not in scope") + } + keyAny, err := scopes.Get("key") + if err != nil { + return err + } + key, ok := keyAny.(MaybeRelocatable) + if !ok { + return errors.New("key not in scope") + } + // Fetch ids variable + nUsedAccesses, err := ids.GetFelt("n_used_accesses", vm) + if err != nil { + return err + } + // Hint logic + if FeltFromUint64(uint64(len(accessIndices[key]))) != nUsedAccesses { + return errors.New("Assertion failed: ids.n_used_accesses == len(access_indices[key])") + } + return nil +} diff --git a/pkg/hints/squash_dict_hints_test.go b/pkg/hints/squash_dict_hints_test.go index 2b0b7267..3809f19f 100644 --- a/pkg/hints/squash_dict_hints_test.go +++ b/pkg/hints/squash_dict_hints_test.go @@ -639,3 +639,28 @@ func TestSquashDictInnerLenAssertEmpty(t *testing.T) { t.Errorf("SQUASH_DICT_INNER_LEN_ASSERT hint failed with error: %s", err) } } + +func TestSquashDictInnerUsedAccessesAssertOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("access_indices", map[MaybeRelocatable][]int{ + *NewMaybeRelocatableFelt(FeltZero()): {3}, + }) + scopes.AssignOrUpdateVariable("key", *NewMaybeRelocatableFelt(FeltZero())) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "n_used_accesses": {NewMaybeRelocatableFelt(FeltOne())}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_USED_ACCESSES_ASSERT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("SQUASH_DICT_INNER_USED_ACCESSES_ASSERT hint failed with error: %s", err) + } +} From 7bd668a0a50b09800c1297bc9bf6b80dc1c4d2ff Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 15 Sep 2023 11:48:16 -0300 Subject: [PATCH 64/74] More tests --- pkg/hints/squash_dict_hints_test.go | 50 +++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/pkg/hints/squash_dict_hints_test.go b/pkg/hints/squash_dict_hints_test.go index 3809f19f..9a881261 100644 --- a/pkg/hints/squash_dict_hints_test.go +++ b/pkg/hints/squash_dict_hints_test.go @@ -664,3 +664,53 @@ func TestSquashDictInnerUsedAccessesAssertOk(t *testing.T) { t.Errorf("SQUASH_DICT_INNER_USED_ACCESSES_ASSERT hint failed with error: %s", err) } } + +func TestSquashDictInnerUsedAccessesAssertBadLen(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("access_indices", map[MaybeRelocatable][]int{ + *NewMaybeRelocatableFelt(FeltZero()): {3, 5}, + }) + scopes.AssignOrUpdateVariable("key", *NewMaybeRelocatableFelt(FeltZero())) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "n_used_accesses": {NewMaybeRelocatableFelt(FeltOne())}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_USED_ACCESSES_ASSERT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Errorf("SQUASH_DICT_INNER_USED_ACCESSES_ASSERT hint should have failed") + } +} + +func TestSquashDictInnerUsedAccessesAssertBadKey(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("access_indices", map[MaybeRelocatable][]int{ + *NewMaybeRelocatableFelt(FeltZero()): {3}, + }) + scopes.AssignOrUpdateVariable("key", *NewMaybeRelocatableFelt(FeltOne())) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "n_used_accesses": {NewMaybeRelocatableFelt(FeltOne())}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_USED_ACCESSES_ASSERT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Errorf("SQUASH_DICT_INNER_USED_ACCESSES_ASSERT hint should have failed") + } +} From dd6d55642c80b44d6adce5d0be0c063d21936db5 Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 15 Sep 2023 14:56:48 -0300 Subject: [PATCH 65/74] Add integration test --- cairo_programs/squash_dict.cairo | 33 ++++++++++++++ pkg/hints/hint_processor.go | 2 + pkg/hints/squash_dict_hints.go | 23 ++++++++++ pkg/hints/squash_dict_hints_test.go | 69 +++++++++++++++++++++++++++++ pkg/vm/cairo_run/cairo_run_test.go | 7 +++ 5 files changed, 134 insertions(+) create mode 100644 cairo_programs/squash_dict.cairo diff --git a/cairo_programs/squash_dict.cairo b/cairo_programs/squash_dict.cairo new file mode 100644 index 00000000..67f7d3c5 --- /dev/null +++ b/cairo_programs/squash_dict.cairo @@ -0,0 +1,33 @@ +%builtins range_check + +from starkware.cairo.common.squash_dict import squash_dict +from starkware.cairo.common.alloc import alloc +from starkware.cairo.common.dict_access import DictAccess + +func main{range_check_ptr}() -> () { + alloc_locals; + let (dict_start: DictAccess*) = alloc(); + assert dict_start[0] = DictAccess(key=0, prev_value=100, new_value=100); + assert dict_start[1] = DictAccess(key=1, prev_value=50, new_value=50); + assert dict_start[2] = DictAccess(key=0, prev_value=100, new_value=200); + assert dict_start[3] = DictAccess(key=1, prev_value=50, new_value=100); + assert dict_start[4] = DictAccess(key=0, prev_value=200, new_value=300); + assert dict_start[5] = DictAccess(key=1, prev_value=100, new_value=150); + + let dict_end = dict_start + 6 * DictAccess.SIZE; + // (dict_start, dict_end) now represents the dictionary + // {0: 100, 1: 50, 0: 200, 1: 100, 0: 300, 1: 150}. + + // Squash the dictionary from an array of 6 DictAccess structs + // to an array of 2, with a single DictAccess entry per key. + let (local squashed_dict_start: DictAccess*) = alloc(); + let (squashed_dict_end) = squash_dict{range_check_ptr=range_check_ptr}( + dict_start, dict_end, squashed_dict_start + ); + + // Check the values of the squashed_dict + // should be: {0: (100, 300), 1: (50, 150)} + assert squashed_dict_start[0] = DictAccess(key=0, prev_value=100, new_value=300); + assert squashed_dict_start[1] = DictAccess(key=1, prev_value=50, new_value=150); + return (); +} \ No newline at end of file diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index c13084a0..fdf3708f 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -73,6 +73,8 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, return squashDictInnerLenAssert(execScopes) case SQUASH_DICT_INNER_USED_ACCESSES_ASSERT: return squashDictInnerUsedAccessesAssert(data.Ids, execScopes, vm) + case SQUASH_DICT_INNER_NEXT_KEY: + return squashDictInnerNextKey(data.Ids, execScopes, vm) case VM_EXIT_SCOPE: return vm_exit_scope(execScopes) default: diff --git a/pkg/hints/squash_dict_hints.go b/pkg/hints/squash_dict_hints.go index 41a8c8b8..60096816 100644 --- a/pkg/hints/squash_dict_hints.go +++ b/pkg/hints/squash_dict_hints.go @@ -269,3 +269,26 @@ func squashDictInnerUsedAccessesAssert(ids IdsManager, scopes *ExecutionScopes, } return nil } + +func squashDictInnerNextKey(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error { + // Fetch scope variables + keysAny, err := scopes.Get("keys") + if err != nil { + return err + } + keys, ok := keysAny.([]MaybeRelocatable) + if !ok { + return errors.New("keys not in scope") + } + // Hint logic + if len(keys) <= 0 { + return errors.New("Assertion failed: len(keys) > 0.\nNo keys left but remaining_accesses > 0.)") + } + key := keys[len(keys)-1] + keys = keys[:len(keys)-1] + ids.Insert("next_key", &key, vm) + // Update scope variables + scopes.AssignOrUpdateVariable("keys", keys) + scopes.AssignOrUpdateVariable("key", key) + return nil +} diff --git a/pkg/hints/squash_dict_hints_test.go b/pkg/hints/squash_dict_hints_test.go index 9a881261..40087602 100644 --- a/pkg/hints/squash_dict_hints_test.go +++ b/pkg/hints/squash_dict_hints_test.go @@ -714,3 +714,72 @@ func TestSquashDictInnerUsedAccessesAssertBadKey(t *testing.T) { t.Errorf("SQUASH_DICT_INNER_USED_ACCESSES_ASSERT hint should have failed") } } + +func TestSquashDictInnerNextKeyOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("keys", []MaybeRelocatable{ + *NewMaybeRelocatableFelt(FeltFromUint64(5)), + *NewMaybeRelocatableFelt(FeltFromUint64(4)), + }) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "next_key": {nil}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_NEXT_KEY, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("SQUASH_DICT_INNER_NEXT_KEY hint failed with error: %s", err) + } + // Check scope values + keysAny, err := scopes.Get("keys") + keys := keysAny.([]MaybeRelocatable) + expectedkeys := []MaybeRelocatable{ + *NewMaybeRelocatableFelt(FeltFromUint64(5)), + } + if !reflect.DeepEqual(keys, expectedkeys) { + t.Errorf("Wrong keys.\n Expected %v, got: %v", expectedkeys, keys) + } + + keyAny, err := scopes.Get("key") + key := keyAny.(MaybeRelocatable) + expectedKey := *NewMaybeRelocatableFelt(FeltFromUint64(4)) + if !reflect.DeepEqual(key, expectedKey) { + t.Errorf("Wrong current_access_index.\n Expected %v, got: %v", expectedKey, key) + } + + // Check ids.next_key + val, err := idsManager.GetFelt("next_key", vm) + if err != nil || val != FeltFromUint64(4) { + t.Errorf("Wrong/No value inserted into ids.next_key") + } +} + +func TestSquashDictInnerNextKeyErr(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("keys", []MaybeRelocatable{}) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "next_key": {nil}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SQUASH_DICT_INNER_NEXT_KEY, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("SQUASH_DICT_INNER_NEXT_KEY hint should have failed") + } +} diff --git a/pkg/vm/cairo_run/cairo_run_test.go b/pkg/vm/cairo_run/cairo_run_test.go index 61b759f9..76740fcd 100644 --- a/pkg/vm/cairo_run/cairo_run_test.go +++ b/pkg/vm/cairo_run/cairo_run_test.go @@ -127,3 +127,10 @@ func TestAssertNotEqualHint(t *testing.T) { t.Errorf("Program execution failed with error: %s", err) } } + +func TestSquashDict(t *testing.T) { + _, err := cairo_run.CairoRun("../../../cairo_programs/squash_dict.json", "small", false) + if err != nil { + t.Errorf("Program execution failed with error: %s", err) + } +} From ae00d5ae3f41f8170da526ca36f2971e1c2e12a0 Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 15 Sep 2023 15:01:06 -0300 Subject: [PATCH 66/74] Fix test --- pkg/hints/squash_dict_hints_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/hints/squash_dict_hints_test.go b/pkg/hints/squash_dict_hints_test.go index 40087602..c63f7f76 100644 --- a/pkg/hints/squash_dict_hints_test.go +++ b/pkg/hints/squash_dict_hints_test.go @@ -779,7 +779,7 @@ func TestSquashDictInnerNextKeyErr(t *testing.T) { Code: SQUASH_DICT_INNER_NEXT_KEY, }) err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) - if err != nil { + if err == nil { t.Errorf("SQUASH_DICT_INNER_NEXT_KEY hint should have failed") } } From 8d39fe50e3760d3b17a0cf0f926a7144899c698b Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 15 Sep 2023 15:06:08 -0300 Subject: [PATCH 67/74] Fix hint code --- pkg/hints/dict_hint_codes.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/hints/dict_hint_codes.go b/pkg/hints/dict_hint_codes.go index a16273cd..c588297f 100644 --- a/pkg/hints/dict_hint_codes.go +++ b/pkg/hints/dict_hint_codes.go @@ -8,7 +8,7 @@ const DICT_WRITE = "dict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ndic const DICT_UPDATE = "# Verify dict pointer and prev value.\ndict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ncurrent_value = dict_tracker.data[ids.key]\nassert current_value == ids.prev_value, \\\n f'Wrong previous value in dict. Got {ids.prev_value}, expected {current_value}.'\n\n# Update value.\ndict_tracker.data[ids.key] = ids.new_value\ndict_tracker.current_ptr += ids.DictAccess.SIZE" -const SQUASH_DICT = "dict_access_size = ids.DictAccess.SIZE\naddress = ids.dict_accesses.address_\nassert ids.ptr_diff % dict_access_size == 0, \\\n 'Accesses array size must be divisible by DictAccess.SIZE'\nn_accesses = ids.n_accesses\nif '__squash_dict_max_size' in globals():\n assert n_accesses <= __squash_dict_max_size, \\\n f'squash_dict() can only be used with n_accesses<={__squash_dict_max_size}. ' \\n f'Got: n_accesses={n_accesses}.'\n# A map from key to the list of indices accessing it.\naccess_indices = {}\nfor i in range(n_accesses):\n key = memory[address + dict_access_size * i]\n access_indices.setdefault(key, []).append(i)\n# Descending list of keys.\nkeys = sorted(access_indices.keys(), reverse=True)\n# Are the keys used bigger than range_check bound.\nids.big_keys = 1 if keys[0] >= range_check_builtin.bound else 0\nids.first_key = key = keys.pop()" +const SQUASH_DICT = "dict_access_size = ids.DictAccess.SIZE\naddress = ids.dict_accesses.address_\nassert ids.ptr_diff %% dict_access_size == 0, \\\n 'Accesses array size must be divisible by DictAccess.SIZE'\nn_accesses = ids.n_accesses\nif '__squash_dict_max_size' in globals():\n assert n_accesses <= __squash_dict_max_size, \\\n f'squash_dict() can only be used with n_accesses<={__squash_dict_max_size}. ' \\\n f'Got: n_accesses={n_accesses}.'\n# A map from key to the list of indices accessing it.\naccess_indices = {}\nfor i in range(n_accesses):\n key = memory[address + dict_access_size * i]\n access_indices.setdefault(key, []).append(i)\n# Descending list of keys.\nkeys = sorted(access_indices.keys(), reverse=True)\n# Are the keys used bigger than range_check bound.\nids.big_keys = 1 if keys[0] >= range_check_builtin.bound else 0\nids.first_key = key = keys.pop()" const SQUASH_DICT_INNER_SKIP_LOOP = "ids.should_skip_loop = 0 if current_access_indices else 1" From d166edd28103714480df949ea5419e6af200a3bd Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 15 Sep 2023 15:10:50 -0300 Subject: [PATCH 68/74] Fix string --- pkg/hints/dict_hint_codes.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/hints/dict_hint_codes.go b/pkg/hints/dict_hint_codes.go index c588297f..cf9b3ebe 100644 --- a/pkg/hints/dict_hint_codes.go +++ b/pkg/hints/dict_hint_codes.go @@ -8,7 +8,7 @@ const DICT_WRITE = "dict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ndic const DICT_UPDATE = "# Verify dict pointer and prev value.\ndict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ncurrent_value = dict_tracker.data[ids.key]\nassert current_value == ids.prev_value, \\\n f'Wrong previous value in dict. Got {ids.prev_value}, expected {current_value}.'\n\n# Update value.\ndict_tracker.data[ids.key] = ids.new_value\ndict_tracker.current_ptr += ids.DictAccess.SIZE" -const SQUASH_DICT = "dict_access_size = ids.DictAccess.SIZE\naddress = ids.dict_accesses.address_\nassert ids.ptr_diff %% dict_access_size == 0, \\\n 'Accesses array size must be divisible by DictAccess.SIZE'\nn_accesses = ids.n_accesses\nif '__squash_dict_max_size' in globals():\n assert n_accesses <= __squash_dict_max_size, \\\n f'squash_dict() can only be used with n_accesses<={__squash_dict_max_size}. ' \\\n f'Got: n_accesses={n_accesses}.'\n# A map from key to the list of indices accessing it.\naccess_indices = {}\nfor i in range(n_accesses):\n key = memory[address + dict_access_size * i]\n access_indices.setdefault(key, []).append(i)\n# Descending list of keys.\nkeys = sorted(access_indices.keys(), reverse=True)\n# Are the keys used bigger than range_check bound.\nids.big_keys = 1 if keys[0] >= range_check_builtin.bound else 0\nids.first_key = key = keys.pop()" +const SQUASH_DICT = "dict_access_size = ids.DictAccess.SIZE\naddress = ids.dict_accesses.address_\nassert ids.ptr_diff % dict_access_size == 0, \\\n 'Accesses array size must be divisible by DictAccess.SIZE'\nn_accesses = ids.n_accesses\nif '__squash_dict_max_size' in globals():\n assert n_accesses <= __squash_dict_max_size, \\\n f'squash_dict() can only be used with n_accesses<={__squash_dict_max_size}. ' \\\n f'Got: n_accesses={n_accesses}.'\n# A map from key to the list of indices accessing it.\naccess_indices = {}\nfor i in range(n_accesses):\n key = memory[address + dict_access_size * i]\n access_indices.setdefault(key, []).append(i)\n# Descending list of keys.\nkeys = sorted(access_indices.keys(), reverse=True)\n# Are the keys used bigger than range_check bound.\nids.big_keys = 1 if keys[0] >= range_check_builtin.bound else 0\nids.first_key = key = keys.pop()" const SQUASH_DICT_INNER_SKIP_LOOP = "ids.should_skip_loop = 0 if current_access_indices else 1" From 686491fae187e7198b4381b0570cb65421ed1e3b Mon Sep 17 00:00:00 2001 From: Federica Date: Mon, 18 Sep 2023 10:27:42 -0300 Subject: [PATCH 69/74] Check map is empty instead of map exists --- pkg/hints/squash_dict_hints.go | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/pkg/hints/squash_dict_hints.go b/pkg/hints/squash_dict_hints.go index 60096816..65b926ea 100644 --- a/pkg/hints/squash_dict_hints.go +++ b/pkg/hints/squash_dict_hints.go @@ -1,7 +1,6 @@ package hints import ( - "fmt" "sort" "github.com/lambdaclass/cairo-vm.go/pkg/builtins" @@ -119,8 +118,16 @@ func squashDict(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) err } func squashDictInnerSkipLoop(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error { - _, err := scopes.Get("current_access_indices") - if err == nil { + currentAccessIndicesAny, err := scopes.Get("current_access_indices") + if err != nil { + return err + } + currentAccessIndices, ok := currentAccessIndicesAny.([]int) + if !ok { + return errors.New("current_access_indices not in scope") + } + // Hint Logic + if len(currentAccessIndices) != 0 { return ids.Insert("should_skip_loop", NewMaybeRelocatableFelt(FeltZero()), vm) } return ids.Insert("should_skip_loop", NewMaybeRelocatableFelt(FeltOne()), vm) @@ -195,12 +202,20 @@ func squashDictInnerCheckAccessIndex(ids IdsManager, scopes *ExecutionScopes, vm scopes.AssignOrUpdateVariable("current_access_index", newAccessIndex) scopes.AssignOrUpdateVariable("new_access_index", newAccessIndex) // Update ids variables - return ids.Insert("loop_temps", NewMaybeRelocatableFelt(FeltFromDecString(fmt.Sprint(deltaMinusOne))), vm) + return ids.Insert("loop_temps", NewMaybeRelocatableFelt(FeltFromUint64(uint64(deltaMinusOne))), vm) } func squashDictInnerContinueLoop(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error { - _, err := scopes.Get("current_access_indices") + currentAccessIndicesAny, err := scopes.Get("current_access_indices") if err != nil { + return err + } + currentAccessIndices, ok := currentAccessIndicesAny.([]int) + if !ok { + return errors.New("current_access_indices not in scope") + } + // Hint Logic + if len(currentAccessIndices) == 0 { return ids.InsertStructField("loop_temps", 3, NewMaybeRelocatableFelt(FeltZero()), vm) } return ids.InsertStructField("loop_temps", 3, NewMaybeRelocatableFelt(FeltOne()), vm) From b61778071812863ce49f1a843dc816e78b6d945b Mon Sep 17 00:00:00 2001 From: Federica Date: Mon, 18 Sep 2023 10:34:07 -0300 Subject: [PATCH 70/74] Fix tests --- pkg/hints/squash_dict_hints_test.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pkg/hints/squash_dict_hints_test.go b/pkg/hints/squash_dict_hints_test.go index c63f7f76..186980e2 100644 --- a/pkg/hints/squash_dict_hints_test.go +++ b/pkg/hints/squash_dict_hints_test.go @@ -308,6 +308,7 @@ func TestSquashDictSkipLoopTrue(t *testing.T) { vm.Segments.AddSegment() vm.Segments.AddSegment() scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("current_access_indices", []int{}) idsManager := SetupIdsForTest( map[string][]*MaybeRelocatable{ "should_skip_loop": {nil}, @@ -336,7 +337,7 @@ func TestSquashDictSkipLoopFalse(t *testing.T) { vm.Segments.AddSegment() vm.Segments.AddSegment() scopes := types.NewExecutionScopes() - scopes.AssignOrUpdateVariable("current_access_indices", []MaybeRelocatable{}) + scopes.AssignOrUpdateVariable("current_access_indices", []int{1, 2}) idsManager := SetupIdsForTest( map[string][]*MaybeRelocatable{ "should_skip_loop": {nil}, @@ -507,13 +508,13 @@ func TestSquashDictInnerCheckAccessIndexEmpty(t *testing.T) { } } -func TestSquashDictContinuepLoopTrue(t *testing.T) { +func TestSquashDictContinueLoopTrue(t *testing.T) { vm := NewVirtualMachine() vm.Segments.AddSegment() vm.Segments.AddSegment() vm.Segments.AddSegment() scopes := types.NewExecutionScopes() - scopes.AssignOrUpdateVariable("current_access_indices", []MaybeRelocatable{}) + scopes.AssignOrUpdateVariable("current_access_indices", []int{1, 3}) idsManager := SetupIdsForTest( map[string][]*MaybeRelocatable{ "loop_temps": {nil}, @@ -542,6 +543,7 @@ func TestSquashDictContinueLoopFalse(t *testing.T) { vm.Segments.AddSegment() vm.Segments.AddSegment() scopes := types.NewExecutionScopes() + scopes.AssignOrUpdateVariable("current_access_indices", []int{}) idsManager := SetupIdsForTest( map[string][]*MaybeRelocatable{ "loop_temps": {nil}, From 2b2a447c8adedc151cfeb543a884a4277eecf6b6 Mon Sep 17 00:00:00 2001 From: Federica Date: Mon, 18 Sep 2023 10:48:12 -0300 Subject: [PATCH 71/74] Fix fmt --- cairo_programs/squash_dict.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cairo_programs/squash_dict.cairo b/cairo_programs/squash_dict.cairo index 67f7d3c5..7af92702 100644 --- a/cairo_programs/squash_dict.cairo +++ b/cairo_programs/squash_dict.cairo @@ -30,4 +30,4 @@ func main{range_check_ptr}() -> () { assert squashed_dict_start[0] = DictAccess(key=0, prev_value=100, new_value=300); assert squashed_dict_start[1] = DictAccess(key=1, prev_value=50, new_value=150); return (); -} \ No newline at end of file +} From 27229b0a10bf4c63405d00a8d12457c21b552d2d Mon Sep 17 00:00:00 2001 From: Federica Date: Mon, 18 Sep 2023 10:50:37 -0300 Subject: [PATCH 72/74] Fix comments --- pkg/hints/squash_dict_hints.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/hints/squash_dict_hints.go b/pkg/hints/squash_dict_hints.go index 65b926ea..071fff63 100644 --- a/pkg/hints/squash_dict_hints.go +++ b/pkg/hints/squash_dict_hints.go @@ -13,7 +13,7 @@ import ( "golang.org/x/exp/maps" ) -// SortMaybeRelocatables implements sort.Interface for []*MaybeRelocatables +// SortMaybeRelocatables implements sort.Interface for []MaybeRelocatables type SortMaybeRelocatables []MaybeRelocatable func (s SortMaybeRelocatables) Len() int { return len(s) } From 39c7e3b2952c45f1ccea16d2213356c4e9445c0a Mon Sep 17 00:00:00 2001 From: Federica Date: Mon, 18 Sep 2023 10:54:27 -0300 Subject: [PATCH 73/74] Move up check --- pkg/hints/squash_dict_hints.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/hints/squash_dict_hints.go b/pkg/hints/squash_dict_hints.go index 071fff63..87767908 100644 --- a/pkg/hints/squash_dict_hints.go +++ b/pkg/hints/squash_dict_hints.go @@ -94,6 +94,9 @@ func squashDict(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) err } //Descending list of keys. keys := maps.Keys(accessIndices) + if len(keys) == 0 { + return errors.New("keys is empty") + } sort.Sort(sort.Reverse(SortMaybeRelocatables(keys))) //Are the keys used bigger than the range_check bound. bigKeys := FeltZero() @@ -101,9 +104,6 @@ func squashDict(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) err if isFelt && highKeyFelt.Bits() >= builtins.RANGE_CHECK_N_PARTS*builtins.INNER_RC_BOUND_SHIFT { bigKeys = FeltOne() } - if len(keys) == 0 { - return errors.New("keys is empty") - } lowKey := keys[len(keys)-1] // Insert new scope variables scopes.AssignOrUpdateVariable("access_indices", accessIndices) From 5e3ecb23dfb931c36847fd304d8cbdca0607be8b Mon Sep 17 00:00:00 2001 From: fmoletta <99273364+fmoletta@users.noreply.github.com> Date: Tue, 19 Sep 2023 00:39:57 +0300 Subject: [PATCH 74/74] Implement `DICT_SQUASH_COPY_DICT`, `DICT_SQUASH_UPDATE_PTR` & `DICT_NEW` (#231) * Implement dict squash copy dict hint * Add test * Add test * Add hint * Add test * Add hint * Add test * Add test, fix string * Add test file * Add newline at eof --- cairo_programs/dict_squash.cairo | 29 ++++ pkg/hints/dict_hint_codes.go | 6 + pkg/hints/dict_hints.go | 67 ++++++++ pkg/hints/dict_hints_test.go | 237 ++++++++++++++++++++++++++++- pkg/hints/hint_processor.go | 6 + pkg/vm/cairo_run/cairo_run_test.go | 8 + 6 files changed, 352 insertions(+), 1 deletion(-) create mode 100644 cairo_programs/dict_squash.cairo diff --git a/cairo_programs/dict_squash.cairo b/cairo_programs/dict_squash.cairo new file mode 100644 index 00000000..05816c63 --- /dev/null +++ b/cairo_programs/dict_squash.cairo @@ -0,0 +1,29 @@ +%builtins range_check + +from starkware.cairo.common.dict_access import DictAccess +from starkware.cairo.common.dict import dict_write, dict_update, dict_squash +from starkware.cairo.common.default_dict import default_dict_new + +func main{range_check_ptr}() -> () { + let (dict_start) = default_dict_new(17); + let dict_end = dict_start; + dict_write{dict_ptr=dict_end}(0, 1); + dict_write{dict_ptr=dict_end}(1, 10); + dict_write{dict_ptr=dict_end}(2, -2); + dict_update{dict_ptr=dict_end}(0, 1, 2); + dict_update{dict_ptr=dict_end}(0, 2, 3); + dict_update{dict_ptr=dict_end}(0, 3, 4); + dict_update{dict_ptr=dict_end}(1, 10, 15); + dict_update{dict_ptr=dict_end}(1, 15, 20); + dict_update{dict_ptr=dict_end}(1, 20, 25); + dict_update{dict_ptr=dict_end}(2, -2, -4); + dict_update{dict_ptr=dict_end}(2, -4, -8); + dict_update{dict_ptr=dict_end}(2, -8, -16); + let (squashed_dict_start, squashed_dict_end) = dict_squash{range_check_ptr=range_check_ptr}( + dict_start, dict_end + ); + assert squashed_dict_end[0] = DictAccess(key=0, prev_value=1, new_value=4); + assert squashed_dict_end[1] = DictAccess(key=1, prev_value=10, new_value=25); + assert squashed_dict_end[2] = DictAccess(key=2, prev_value=-2, new_value=-16); + return (); +} diff --git a/pkg/hints/dict_hint_codes.go b/pkg/hints/dict_hint_codes.go index cf9b3ebe..62407b89 100644 --- a/pkg/hints/dict_hint_codes.go +++ b/pkg/hints/dict_hint_codes.go @@ -25,3 +25,9 @@ const SQUASH_DICT_INNER_LEN_ASSERT = "assert len(current_access_indices) == 0" const SQUASH_DICT_INNER_USED_ACCESSES_ASSERT = "assert ids.n_used_accesses == len(access_indices[key])" const SQUASH_DICT_INNER_NEXT_KEY = "assert len(keys) > 0, 'No keys left but remaining_accesses > 0.'\nids.next_key = key = keys.pop()" + +const DICT_SQUASH_COPY_DICT = "# Prepare arguments for dict_new. In particular, the same dictionary values should be copied\n# to the new (squashed) dictionary.\nvm_enter_scope({\n # Make __dict_manager accessible.\n '__dict_manager': __dict_manager,\n # Create a copy of the dict, in case it changes in the future.\n 'initial_dict': dict(__dict_manager.get_dict(ids.dict_accesses_end)),\n})" + +const DICT_SQUASH_UPDATE_PTR = "# Update the DictTracker's current_ptr to point to the end of the squashed dict.\n__dict_manager.get_tracker(ids.squashed_dict_start).current_ptr = \\\n ids.squashed_dict_end.address_" + +const DICT_NEW = "if '__dict_manager' not in globals():\n from starkware.cairo.common.dict import DictManager\n __dict_manager = DictManager()\n\nmemory[ap] = __dict_manager.new_dict(segments, initial_dict)\ndel initial_dict" diff --git a/pkg/hints/dict_hints.go b/pkg/hints/dict_hints.go index 82159553..f6750355 100644 --- a/pkg/hints/dict_hints.go +++ b/pkg/hints/dict_hints.go @@ -141,3 +141,70 @@ func dictUpdate(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) err tracker.CurrentPtr.Offset += DICT_ACCESS_SIZE return nil } + +func dictSquashCopyDict(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error { + // Extract Variables + dictManager, ok := FetchDictManager(scopes) + if !ok { + return errors.New("Variable __dict_manager not present in current execution scope") + } + dictAccessEnd, err := ids.GetRelocatable("dict_accesses_end", vm) + if err != nil { + return err + } + // Hint logic + tracker, err := dictManager.GetTracker(dictAccessEnd) + if err != nil { + return err + } + initialDict := tracker.CopyDictionary() + scopes.EnterScope(map[string]interface{}{ + "__dict_manager": dictManager, + "initial_dict": initialDict, + }) + return nil +} + +func dictSquashUpdatePtr(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error { + // Extract Variables + dictManager, ok := FetchDictManager(scopes) + if !ok { + return errors.New("Variable __dict_manager not present in current execution scope") + } + squashedDictStart, err := ids.GetRelocatable("squashed_dict_start", vm) + if err != nil { + return err + } + squashedDictEnd, err := ids.GetRelocatable("squashed_dict_end", vm) + if err != nil { + return err + } + // Hint logic + tracker, err := dictManager.GetTracker(squashedDictStart) + if err != nil { + return err + } + tracker.CurrentPtr = squashedDictEnd + return nil +} + +func dictNew(ids IdsManager, scopes *ExecutionScopes, vm *VirtualMachine) error { + // Fetch scope variables + initialDictAny, err := scopes.Get("initial_dict") + if err != nil { + return err + } + initialDict, ok := initialDictAny.(map[memory.MaybeRelocatable]memory.MaybeRelocatable) + if !ok { + return errors.New("initial_dict not in scope") + } + // Hint Logic + dictManager, ok := FetchDictManager(scopes) + if !ok { + newDictManager := NewDictManager() + dictManager = &newDictManager + scopes.AssignOrUpdateVariable("__dict_manager", dictManager) + } + dict_ptr := dictManager.NewDictionary(&initialDict, vm) + return vm.Segments.Memory.Insert(vm.RunContext.Ap, memory.NewMaybeRelocatableRelocatable(dict_ptr)) +} diff --git a/pkg/hints/dict_hints_test.go b/pkg/hints/dict_hints_test.go index 76e5de9d..8bfa94b2 100644 --- a/pkg/hints/dict_hints_test.go +++ b/pkg/hints/dict_hints_test.go @@ -1,6 +1,7 @@ package hints_test import ( + "reflect" "testing" . "github.com/lambdaclass/cairo-vm.go/pkg/hints" @@ -70,7 +71,7 @@ func TestDefaultDictNewHasManager(t *testing.T) { if err != nil { t.Errorf("DEFAULT_DICT_NEW hint test failed with error %s", err) } - // Check that the manager wan't replaced by a new one + // Check that the manager wasn't replaced by a new one dictManagerPtr, ok := FetchDictManager(scopes) if !ok || dictManagerPtr != dictManagerRef { t.Error("DEFAULT_DICT_NEW DictManager replaced") @@ -413,3 +414,237 @@ func TestDictUpdateErr(t *testing.T) { t.Error("DICT_UPDATE hint test should have failed") } } + +func TestDictSqushCopyDictOkEmptyDict(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + + // Create dictManager & add it to scope + dictManager := dict_manager.NewDictManager() + dictManagerRef := &dictManager + initialDict := map[MaybeRelocatable]MaybeRelocatable{} + dict_ptr := dictManager.NewDictionary(&initialDict, vm) + scopes.AssignOrUpdateVariable("__dict_manager", dictManagerRef) + + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "dict_accesses_end": {NewMaybeRelocatableRelocatable(dict_ptr)}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: DICT_SQUASH_COPY_DICT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("DICT_SQUASH_COPY_DICT hint test failed with error %s", err) + } + // Check new scope + new_scope, _ := scopes.GetLocalVariables() + if !reflect.DeepEqual(new_scope, map[string]interface{}{ + "__dict_manager": dictManagerRef, + "initial_dict": initialDict, + }) { + t.Errorf("DICT_SQUASH_COPY_DICT hint test wrong new sope created") + } +} + +func TestDictSqushCopyDictOkNonEmptyDict(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + + // Create dictManager & add it to scope + dictManager := dict_manager.NewDictManager() + dictManagerRef := &dictManager + initialDict := map[MaybeRelocatable]MaybeRelocatable{ + *NewMaybeRelocatableFelt(FeltZero()): *NewMaybeRelocatableFelt(FeltOne()), + *NewMaybeRelocatableFelt(FeltOne()): *NewMaybeRelocatableFelt(FeltOne()), + } + dict_ptr := dictManager.NewDictionary(&initialDict, vm) + scopes.AssignOrUpdateVariable("__dict_manager", dictManagerRef) + + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "dict_accesses_end": {NewMaybeRelocatableRelocatable(dict_ptr)}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: DICT_SQUASH_COPY_DICT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("DICT_SQUASH_COPY_DICT hint test failed with error %s", err) + } + // Check new scope + new_scope, _ := scopes.GetLocalVariables() + if !reflect.DeepEqual(new_scope, map[string]interface{}{ + "__dict_manager": dictManagerRef, + "initial_dict": initialDict, + }) { + t.Errorf("DICT_SQUASH_COPY_DICT hint test wrong new sope created") + } +} + +func TestDictSquashUpdatePtrOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + initialDict := make(map[MaybeRelocatable]MaybeRelocatable) + // Create dictManager & add it to scope + dictManager := dict_manager.NewDictManager() + dict_ptr := dictManager.NewDictionary(&initialDict, vm) + // Keep a reference to the tracker to check that it was updated after the hint + tracker, _ := dictManager.GetTracker(dict_ptr) + scopes.AssignOrUpdateVariable("__dict_manager", &dictManager) + + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "squashed_dict_start": {NewMaybeRelocatableRelocatable(dict_ptr)}, + "squashed_dict_end": {NewMaybeRelocatableRelocatable(dict_ptr.AddUint(5))}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: DICT_SQUASH_UPDATE_PTR, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("DICT_SQUASH_UPDATE_PTR hint test failed with error %s", err) + } + // Check updated ptr + if tracker.CurrentPtr != dict_ptr.AddUint(5) { + t.Error("DICT_SQUASH_UPDATE_PTR hint test failed: Wrong updated tracker.CurrentPtr") + } +} + +func TestDictSquashUpdatePtrMismatchedPtr(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + scopes := types.NewExecutionScopes() + initialDict := make(map[MaybeRelocatable]MaybeRelocatable) + // Create dictManager & add it to scope + dictManager := dict_manager.NewDictManager() + dict_ptr := dictManager.NewDictionary(&initialDict, vm) + scopes.AssignOrUpdateVariable("__dict_manager", &dictManager) + + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "squashed_dict_start": {NewMaybeRelocatableRelocatable(dict_ptr.AddUint(3))}, + "squashed_dict_end": {NewMaybeRelocatableRelocatable(dict_ptr.AddUint(5))}, + }, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: DICT_SQUASH_UPDATE_PTR, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Errorf("DICT_SQUASH_UPDATE_PTR hint test should have failed") + } +} + +func TestDictNewCreateManager(t *testing.T) { + vm := NewVirtualMachine() + scopes := types.NewExecutionScopes() + initialDict := make(map[MaybeRelocatable]MaybeRelocatable) + scopes.AssignOrUpdateVariable("initial_dict", initialDict) + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{}, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: DICT_NEW, + }) + // Advance AP so that values don't clash with FP-based ids + vm.RunContext.Ap = NewRelocatable(0, 5) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("DICT_NEW hint test failed with error %s", err) + } + // Check that a manager was created in the scope + _, ok := FetchDictManager(scopes) + if !ok { + t.Error("DICT_NEW No DictManager created") + } + // Check that the correct base was inserted into ap + val, _ := vm.Segments.Memory.Get(vm.RunContext.Ap) + if val == nil || *val != *NewMaybeRelocatableRelocatable(NewRelocatable(1, 0)) { + t.Error("DICT_NEW Wrong/No base inserted into ap") + } +} + +func TestDictNewHasManager(t *testing.T) { + vm := NewVirtualMachine() + scopes := types.NewExecutionScopes() + // Create initialDict & dictManager & add them to scope + initialDict := make(map[MaybeRelocatable]MaybeRelocatable) + scopes.AssignOrUpdateVariable("initial_dict", initialDict) + dictManager := dict_manager.NewDictManager() + dictManagerRef := &dictManager + scopes.AssignOrUpdateVariable("__dict_manager", dictManagerRef) + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{}, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: DICT_NEW, + }) + // Advance AP so that values don't clash with FP-based ids + vm.RunContext.Ap = NewRelocatable(0, 5) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err != nil { + t.Errorf("DICT_NEW hint test failed with error %s", err) + } + // Check that the manager wasn't replaced by a new one + dictManagerPtr, ok := FetchDictManager(scopes) + if !ok || dictManagerPtr != dictManagerRef { + t.Error("DICT_NEW DictManager replaced") + } + // Check that the correct base was inserted into ap + val, _ := vm.Segments.Memory.Get(vm.RunContext.Ap) + if val == nil || *val != *NewMaybeRelocatableRelocatable(NewRelocatable(1, 0)) { + t.Error("DICT_NEW Wrong/No base inserted into ap") + } +} + +func TestDictNewHasManagerNoInitialDict(t *testing.T) { + vm := NewVirtualMachine() + scopes := types.NewExecutionScopes() + // Create dictManager & add it to scope + dictManager := dict_manager.NewDictManager() + dictManagerRef := &dictManager + scopes.AssignOrUpdateVariable("__dict_manager", dictManagerRef) + vm.Segments.AddSegment() + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{}, + vm, + ) + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: DICT_NEW, + }) + // Advance AP so that values don't clash with FP-based ids + vm.RunContext.Ap = NewRelocatable(0, 5) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, scopes) + if err == nil { + t.Errorf("DICT_NEW hint test should have failed") + } +} diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index 776228c8..e789601c 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -73,6 +73,12 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any, return squashDictInnerUsedAccessesAssert(data.Ids, execScopes, vm) case SQUASH_DICT_INNER_NEXT_KEY: return squashDictInnerNextKey(data.Ids, execScopes, vm) + case DICT_SQUASH_COPY_DICT: + return dictSquashCopyDict(data.Ids, execScopes, vm) + case DICT_SQUASH_UPDATE_PTR: + return dictSquashUpdatePtr(data.Ids, execScopes, vm) + case DICT_NEW: + return dictNew(data.Ids, execScopes, vm) case VM_EXIT_SCOPE: return vm_exit_scope(execScopes) case ASSERT_NOT_EQUAL: diff --git a/pkg/vm/cairo_run/cairo_run_test.go b/pkg/vm/cairo_run/cairo_run_test.go index c4606410..c255c761 100644 --- a/pkg/vm/cairo_run/cairo_run_test.go +++ b/pkg/vm/cairo_run/cairo_run_test.go @@ -194,3 +194,11 @@ func TestSquashDict(t *testing.T) { t.Errorf("Program execution failed with error: %s", err) } } + +func TestDictSquash(t *testing.T) { + cairoRunConfig := cairo_run.CairoRunConfig{DisableTracePadding: false, Layout: "small", ProofMode: false} + _, err := cairo_run.CairoRun("../../../cairo_programs/dict_squash.json", cairoRunConfig) + if err != nil { + t.Errorf("Program execution failed with error: %s", err) + } +}