Skip to content

Commit

Permalink
Opcode assertions (#32)
Browse files Browse the repository at this point in the history
* Add roadmap to README (#17)

* Add roadmap to README

* Small typo

* Add felt documentation (#16)

* Add Basic Types + Memory walkthrough to Write your own Cairo VM section  (#8)

* Add non c-specific project guidelines

* Copy the rest of the file

* Copy + adapt introduction

* Copy doc in C repo PR

* Replace relocatable structure with go structure

* Make Relocatable fields public

* Update MaybeRelocatable section with go version

* Remove leftover comment

* Update Memory structure

* Add Insert code (minus temp segments comment)

* Add Get code (minus temp segments comment)

* Add MemorySegmentManager section

* Add maybeRelocatable convenience methods

* Add missing language keyword

* Fix typo

* fmt

* Replace hashmap for map

* Add `RunContext` & `VirtualMachine` structres to Write your own VM section (#12)

* Add RunContext

* Add VM structure

* First draft OpcodeAssertions

* First draft OpcodeAssertions

* Testing

* TestOpcodeAssertionsResUnconstrained

* Testing

* Mini improvment in README (#34)

Co-authored-by: Pablo Deymonnaz <[email protected]>

* Review PR #1
- Removes useless methods
- Formats variables name
- Removes useless pointers

* Review PR #1
- Removes useless methods
- Formats variables name
- Removes useless pointers
- Deletes again mock function
- Rename function names

* Removed error difference.

---------

Co-authored-by: Javier Rodríguez Chatruc <[email protected]>
Co-authored-by: fmoletta <[email protected]>
Co-authored-by: Pablo Deymonnaz <[email protected]>
Co-authored-by: Pablo Deymonnaz <[email protected]>
  • Loading branch information
5 people committed Aug 3, 2023
1 parent 665bca2 commit 143dd6e
Show file tree
Hide file tree
Showing 11 changed files with 265 additions and 70 deletions.
5 changes: 0 additions & 5 deletions pkg/lambdaworks/lambdaworks.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 0 additions & 2 deletions pkg/lambdaworks/lib/lambdaworks.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
4 changes: 0 additions & 4 deletions pkg/lambdaworks/lib/lambdaworks/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
2 changes: 1 addition & 1 deletion pkg/salutes/salutes.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
2 changes: 1 addition & 1 deletion pkg/salutes/salutes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
7 changes: 7 additions & 0 deletions pkg/vm/instruction.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
36 changes: 23 additions & 13 deletions pkg/vm/memory/relocatable.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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
}
}
4 changes: 2 additions & 2 deletions pkg/vm/memory/segments.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
42 changes: 14 additions & 28 deletions pkg/vm/run_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import (
"math"

"github.com/lambdaclass/cairo-vm.go/pkg/vm/memory"

"errors"
)

// RunContext containts the register states of the
Expand All @@ -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))
}

}
Expand All @@ -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))
}
}

Expand All @@ -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))
}
}
74 changes: 60 additions & 14 deletions pkg/vm/vm_core.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}

// ------------------------
Expand All @@ -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
}
Expand Down Expand Up @@ -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
}
Expand Down
Loading

0 comments on commit 143dd6e

Please sign in to comment.