diff --git a/pkg/lambdaworks/lambdaworks.go b/pkg/lambdaworks/lambdaworks.go index 38ca1c18..10684b2a 100644 --- a/pkg/lambdaworks/lambdaworks.go +++ b/pkg/lambdaworks/lambdaworks.go @@ -90,11 +90,6 @@ func Div(a, b Felt) Felt { return fromC(result) } -// Returns the result of the number function. -func Number() int { - return int(C.number()) -} - // turns a felt to usize func (felt Felt) ToU64() (uint64, error) { if felt.limbs[0] == 0 && felt.limbs[1] == 0 && felt.limbs[2] == 0 { diff --git a/pkg/lambdaworks/lib/lambdaworks.h b/pkg/lambdaworks/lib/lambdaworks.h index 9561b898..8ab2504a 100644 --- a/pkg/lambdaworks/lib/lambdaworks.h +++ b/pkg/lambdaworks/lib/lambdaworks.h @@ -25,5 +25,3 @@ void mul(felt_t a, felt_t b, felt_t result); /* Writes the result variable with a / b. */ void div(felt_t a, felt_t b, felt_t result); - -int number(); diff --git a/pkg/lambdaworks/lib/lambdaworks/src/lib.rs b/pkg/lambdaworks/lib/lambdaworks/src/lib.rs index 95e52c22..6edea65f 100644 --- a/pkg/lambdaworks/lib/lambdaworks/src/lib.rs +++ b/pkg/lambdaworks/lib/lambdaworks/src/lib.rs @@ -72,7 +72,3 @@ pub extern "C" fn div(a: Limbs, b: Limbs, result: Limbs) { felt_to_limbs(limbs_to_felt(a) / limbs_to_felt(b), result) } -#[no_mangle] -pub extern "C" fn number() -> i32 { - return 42; -} diff --git a/pkg/salutes/salutes.go b/pkg/salutes/salutes.go index 4528308c..5bd53b96 100644 --- a/pkg/salutes/salutes.go +++ b/pkg/salutes/salutes.go @@ -7,5 +7,5 @@ import ( ) func Hello() string { - return fmt.Sprintf("Hello, world! Here's your number: %d", lambdaworks.Number()) + return fmt.Sprintf("Hello, world! Here's your felt: %d", lambdaworks.From(42)) } diff --git a/pkg/salutes/salutes_test.go b/pkg/salutes/salutes_test.go index 5a1e474b..319cf624 100644 --- a/pkg/salutes/salutes_test.go +++ b/pkg/salutes/salutes_test.go @@ -8,7 +8,7 @@ import ( func TestHello(t *testing.T) { got := salutes.Hello() - expected := "Hello, world! Here's your number: 42" + expected := "Hello, world! Here's your felt: {[0 0 0 42]}" if got != expected { t.Errorf("We should have '%s' as the salute, got '%s'", expected, got) } diff --git a/pkg/vm/instruction.go b/pkg/vm/instruction.go index f89af725..65fc1338 100644 --- a/pkg/vm/instruction.go +++ b/pkg/vm/instruction.go @@ -36,6 +36,13 @@ type Instruction struct { Opcode Opcode } +func (i *Instruction) size() uint { + if i.Op1Addr == Op1SrcImm { + return 2 + } + return 1 +} + // x-----------------------------x // x----- Instruction flags -----x // x-----------------------------x diff --git a/pkg/vm/memory/relocatable.go b/pkg/vm/memory/relocatable.go index 35723b3c..29f648e1 100644 --- a/pkg/vm/memory/relocatable.go +++ b/pkg/vm/memory/relocatable.go @@ -15,13 +15,17 @@ type Relocatable struct { Offset uint } +func (r *Relocatable) IsEqual(r1 *Relocatable) bool { + return (r.SegmentIndex == r1.SegmentIndex && r.Offset == r1.Offset) +} + // Creates a new Relocatable struct with the specified segment index // and offset. func NewRelocatable(segment_idx int, offset uint) Relocatable { return Relocatable{segment_idx, offset} } -func (relocatable *Relocatable) SubRelocatable(other uint) (Relocatable, error) { +func (relocatable *Relocatable) SubUint(other uint) (Relocatable, error) { if relocatable.Offset < other { return NewRelocatable(0, 0), &SubReloctableError{Msg: "RelocatableSubUsizeNegOffset"} } else { @@ -30,23 +34,12 @@ func (relocatable *Relocatable) SubRelocatable(other uint) (Relocatable, error) } } -func (relocatable *Relocatable) AddRelocatable(other uint) (Relocatable, error) { +func (relocatable *Relocatable) AddUint(other uint) (Relocatable, error) { new_offset := relocatable.Offset + other return NewRelocatable(relocatable.SegmentIndex, new_offset), nil } -// Get the the indexes of the Relocatable struct. -// Returns a tuple with both values (segment_index, offset) -func (r *Relocatable) into_indexes() (uint, uint) { - if r.SegmentIndex < 0 { - corrected_segment_idx := uint(-(r.SegmentIndex + 1)) - return corrected_segment_idx, r.Offset - } - - return uint(r.SegmentIndex), r.Offset -} - // Int in the Cairo VM represents a value in memory that // is not an address. type Int struct { @@ -118,3 +111,20 @@ func (m *MaybeRelocatable) GetRelocatable() (Relocatable, bool) { rel, is_type := m.inner.(Relocatable) return rel, is_type } + +// If m and m1 are equal, returns true, otherwise returns false +func (m *MaybeRelocatable) IsEqual(m1 *MaybeRelocatable) bool { + a, a_type := m.GetInt() + b, b_type := m1.GetInt() + if a_type == b_type { + if a_type { + return a == b + } else { + a, _ := m.GetRelocatable() + b, _ := m1.GetRelocatable() + return a.IsEqual(&b) + } + } else { + return false + } +} diff --git a/pkg/vm/memory/segments.go b/pkg/vm/memory/segments.go index 95fb1dda..f565e1f9 100644 --- a/pkg/vm/memory/segments.go +++ b/pkg/vm/memory/segments.go @@ -8,9 +8,9 @@ type MemorySegmentManager struct { Memory Memory } -func NewMemorySegmentManager() *MemorySegmentManager { +func NewMemorySegmentManager() MemorySegmentManager { memory := NewMemory() - return &MemorySegmentManager{make(map[uint]uint), *memory} + return MemorySegmentManager{make(map[uint]uint), *memory} } // Adds a memory segment and returns the first address of the new segment diff --git a/pkg/vm/run_context.go b/pkg/vm/run_context.go index 819cfbf8..0e9fb5eb 100644 --- a/pkg/vm/run_context.go +++ b/pkg/vm/run_context.go @@ -4,8 +4,6 @@ import ( "math" "github.com/lambdaclass/cairo-vm.go/pkg/vm/memory" - - "errors" ) // RunContext containts the register states of the @@ -16,31 +14,19 @@ type RunContext struct { Fp memory.Relocatable } -func (run_context RunContext) GetAp() memory.Relocatable { - return run_context.Ap -} - -func (run_context RunContext) GetFP() memory.Relocatable { - return run_context.Fp -} - -func (run_context RunContext) get_pc() memory.Relocatable { - return run_context.Pc -} - func (run_context RunContext) ComputeDstAddr(instruction Instruction) (memory.Relocatable, error) { var base_addr memory.Relocatable switch instruction.DstReg { case AP: - base_addr = run_context.GetAp() + base_addr = run_context.Ap case FP: - base_addr = run_context.GetFP() + base_addr = run_context.Fp } if instruction.OffOp0 < 0 { - return base_addr.SubRelocatable(uint(math.Abs(float64(instruction.OffDst)))) + return base_addr.SubUint(uint(math.Abs(float64(instruction.OffDst)))) } else { - return base_addr.AddRelocatable(uint(instruction.OffDst)) + return base_addr.AddUint(uint(instruction.OffDst)) } } @@ -49,15 +35,15 @@ func (run_context RunContext) ComputeOp0Addr(instruction Instruction) (memory.Re var base_addr memory.Relocatable switch instruction.Op0Reg { case AP: - base_addr = run_context.GetAp() + base_addr = run_context.Ap case FP: - base_addr = run_context.GetFP() + base_addr = run_context.Fp } if instruction.OffOp1 < 0 { - return base_addr.SubRelocatable(uint(math.Abs(float64(instruction.OffOp0)))) + return base_addr.SubUint(uint(math.Abs(float64(instruction.OffOp0)))) } else { - return base_addr.AddRelocatable(uint(instruction.OffOp0)) + return base_addr.AddUint(uint(instruction.OffOp0)) } } @@ -66,21 +52,21 @@ func (run_context RunContext) ComputeOp1Addr(instruction Instruction, op0 memory switch instruction.Op1Addr { case Op1SrcFP: - base_addr = run_context.GetFP() + base_addr = run_context.Fp case Op1SrcAP: - base_addr = run_context.GetAp() + base_addr = run_context.Ap case Op1SrcImm: if instruction.OffOp1 == 1 { - base_addr = run_context.get_pc() + base_addr = run_context.Pc } else { base_addr = memory.NewRelocatable(-1, 0) - return base_addr, errors.New("UnknownOp0") + return base_addr, &VirtualMachineError{Msg: "UnknownOp0"} } // Todo:check case op0 } if instruction.OffOp1 < 0 { - return base_addr.SubRelocatable(uint(math.Abs(float64(instruction.OffOp1)))) + return base_addr.SubUint(uint(math.Abs(float64(instruction.OffOp1)))) } else { - return base_addr.AddRelocatable(uint(instruction.OffOp1)) + return base_addr.AddUint(uint(instruction.OffOp1)) } } diff --git a/pkg/vm/vm_core.go b/pkg/vm/vm_core.go index 09794c41..168c313b 100644 --- a/pkg/vm/vm_core.go +++ b/pkg/vm/vm_core.go @@ -8,6 +8,14 @@ import ( "github.com/lambdaclass/cairo-vm.go/pkg/vm/memory" ) +type VirtualMachineError struct { + Msg string +} + +func (e *VirtualMachineError) Error() string { + return fmt.Sprintf(e.Msg) +} + // VirtualMachine represents the Cairo VM. // Runs Cairo assembly and produces an execution trace. // TODO: write proper methods to obtain the fields instead of making them public @@ -17,17 +25,25 @@ type VirtualMachine struct { Segments memory.MemorySegmentManager } +func NewVirtualMachine() *VirtualMachine { + return &VirtualMachine{ + runContext: RunContext{}, + currentStep: 0, + segments: memory.NewMemorySegmentManager(), + } +} + type Operands struct { - dst memory.MaybeRelocatable - res memory.MaybeRelocatable - op0 memory.MaybeRelocatable - op1 memory.MaybeRelocatable + Dst memory.MaybeRelocatable + Res *memory.MaybeRelocatable + Op0 memory.MaybeRelocatable + Op1 memory.MaybeRelocatable } type OperandsAddresses struct { - dst_addr memory.Relocatable - op0_addr memory.Relocatable - op1_addr memory.Relocatable + DstAddr memory.Relocatable + Op0Addr memory.Relocatable + Op1Addr memory.Relocatable } // ------------------------ @@ -38,6 +54,36 @@ type DeducedOperands struct { operands uint8 } +func (vm *VirtualMachine) OpcodeAssertions(instruction Instruction, operands Operands) error { + switch instruction.Opcode { + case AssertEq: + if operands.Res == nil { + return &VirtualMachineError{"UnconstrainedResAssertEq"} + } + if !operands.Res.IsEqual(&operands.Dst) { + return &VirtualMachineError{"DiffAssertValues"} + } + case Call: + new_rel, err := vm.runContext.Pc.AddUint(instruction.size()) + if err != nil { + return err + } + returnPC := memory.NewMaybeRelocatableRelocatable(new_rel) + + if !operands.Op0.IsEqual(returnPC) { + return &VirtualMachineError{"CantWriteReturnPc"} + } + + returnFP := vm.runContext.Fp + dstRelocatable, _ := operands.Dst.GetRelocatable() + if !returnFP.IsEqual(&dstRelocatable) { + return &VirtualMachineError{"CantWriteReturnFp"} + } + } + + return nil +} + func (deduced *DeducedOperands) set_dst(value uint8) { deduced.operands = deduced.operands | value } @@ -114,16 +160,16 @@ func (vm *VirtualMachine) ComputeOperands(instruction Instruction) (Operands, Op res, err := vm.ComputeRes(instruction, *op0_op, *op1_op) accesed_addresses := OperandsAddresses{ - dst_addr: dst_addr, - op0_addr: op0_addr, - op1_addr: op1_addr, + DstAddr: dst_addr, + Op0Addr: op0_addr, + Op1Addr: op1_addr, } operands := Operands{ - dst: *dst_op, - op0: *op0_op, - op1: *op1_op, - res: res, + Dst: *dst_op, + Op0: *op0_op, + Op1: *op1_op, + Res: &res, } return operands, accesed_addresses, deduced_operands, nil } diff --git a/pkg/vm/vm_core_test.go b/pkg/vm/vm_core_test.go new file mode 100644 index 00000000..eed47440 --- /dev/null +++ b/pkg/vm/vm_core_test.go @@ -0,0 +1,157 @@ +package vm + +import ( + "testing" + + "github.com/lambdaclass/cairo-vm.go/pkg/lambdaworks" + "github.com/lambdaclass/cairo-vm.go/pkg/vm/memory" +) + +func TestOpcodeAssertionsResUnconstrained(t *testing.T) { + instruction := Instruction{ + OffOp0: 1, + OffOp1: 2, + OffDst: 3, + DstReg: FP, + Op0Reg: AP, + Op1Addr: Op1SrcAP, + ResLogic: ResAdd, + PcUpdate: PcUpdateRegular, + ApUpdate: ApUpdateRegular, + FpUpdate: FpUpdateAPPlus2, + Opcode: AssertEq, + } + + operands := Operands{ + Dst: *memory.NewMaybeRelocatableInt(lambdaworks.From(8)), + Res: nil, + Op0: *memory.NewMaybeRelocatableInt(lambdaworks.From(9)), + Op1: *memory.NewMaybeRelocatableInt(lambdaworks.From(10)), + } + + testVm := NewVirtualMachine() + + err := testVm.OpcodeAssertions(instruction, operands) + if err.Error() != "UnconstrainedResAssertEq" { + t.Error("Assertion should error out with UnconstrainedResAssertEq") + } +} + +func TestOpcodeAssertionsInstructionFailed(t *testing.T) { + instruction := Instruction{ + OffOp0: 1, + OffOp1: 2, + OffDst: 3, + DstReg: FP, + Op0Reg: AP, + Op1Addr: Op1SrcAP, + ResLogic: ResAdd, + PcUpdate: PcUpdateRegular, + ApUpdate: ApUpdateRegular, + FpUpdate: FpUpdateAPPlus2, + Opcode: AssertEq, + } + + operands := Operands{ + Dst: *memory.NewMaybeRelocatableInt(lambdaworks.From(9)), + Res: memory.NewMaybeRelocatableInt(lambdaworks.From(8)), + Op0: *memory.NewMaybeRelocatableInt(lambdaworks.From(9)), + Op1: *memory.NewMaybeRelocatableInt(lambdaworks.From(10)), + } + + testVm := NewVirtualMachine() + err := testVm.OpcodeAssertions(instruction, operands) + if err.Error() != "DiffAssertValues" { + t.Error("Assertion should error out with DiffAssertValues") + } + +} + +func TestOpcodeAssertionsInstructionFailedRelocatables(t *testing.T) { + instruction := Instruction{ + OffOp0: 1, + OffOp1: 2, + OffDst: 3, + DstReg: FP, + Op0Reg: AP, + Op1Addr: Op1SrcAP, + ResLogic: ResAdd, + PcUpdate: PcUpdateRegular, + ApUpdate: ApUpdateRegular, + FpUpdate: FpUpdateAPPlus2, + Opcode: AssertEq, + } + + operands := Operands{ + Dst: *memory.NewMaybeRelocatableRelocatable(memory.NewRelocatable(1, 1)), + Res: memory.NewMaybeRelocatableRelocatable(memory.NewRelocatable(1, 2)), + Op0: *memory.NewMaybeRelocatableInt(lambdaworks.From(9)), + Op1: *memory.NewMaybeRelocatableInt(lambdaworks.From(10)), + } + + testVm := NewVirtualMachine() + err := testVm.OpcodeAssertions(instruction, operands) + if err.Error() != "DiffAssertValues" { + t.Error("Assertion should error out with DiffAssertValues") + } +} + +func TestOpcodeAssertionsInconsistentOp0(t *testing.T) { + instruction := Instruction{ + OffOp0: 1, + OffOp1: 2, + OffDst: 3, + DstReg: FP, + Op0Reg: AP, + Op1Addr: Op1SrcAP, + ResLogic: ResAdd, + PcUpdate: PcUpdateRegular, + ApUpdate: ApUpdateRegular, + FpUpdate: FpUpdateAPPlus2, + Opcode: Call, + } + + operands := Operands{ + Dst: *memory.NewMaybeRelocatableRelocatable(memory.NewRelocatable(0, 8)), + Res: memory.NewMaybeRelocatableInt(lambdaworks.From(8)), + Op0: *memory.NewMaybeRelocatableInt(lambdaworks.From(9)), + Op1: *memory.NewMaybeRelocatableInt(lambdaworks.From(10)), + } + + testVm := NewVirtualMachine() + testVm.runContext.Pc = memory.NewRelocatable(0, 4) + err := testVm.OpcodeAssertions(instruction, operands) + if err.Error() != "CantWriteReturnPc" { + t.Error("Assertion should error out with CantWriteReturnPc") + } +} + +func TestOpcodeAssertionsInconsistentDst(t *testing.T) { + instruction := Instruction{ + OffOp0: 1, + OffOp1: 2, + OffDst: 3, + DstReg: FP, + Op0Reg: AP, + Op1Addr: Op1SrcAP, + ResLogic: ResAdd, + PcUpdate: PcUpdateRegular, + ApUpdate: ApUpdateRegular, + FpUpdate: FpUpdateAPPlus2, + Opcode: Call, + } + + operands := Operands{ + Dst: *memory.NewMaybeRelocatableInt(lambdaworks.From(8)), + Res: memory.NewMaybeRelocatableInt(lambdaworks.From(8)), + Op0: *memory.NewMaybeRelocatableRelocatable(memory.NewRelocatable(0, 1)), + Op1: *memory.NewMaybeRelocatableInt(lambdaworks.From(10)), + } + + testVm := NewVirtualMachine() + testVm.runContext.Fp = memory.NewRelocatable(1, 6) + err := testVm.OpcodeAssertions(instruction, operands) + if err.Error() != "CantWriteReturnFp" { + t.Error("Assertion should error out with CantWriteReturnFp") + } +}