Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Add a hintrunner for the Vm #32

Merged
merged 10 commits into from
Sep 3, 2023
56 changes: 56 additions & 0 deletions pkg/hintrunner/error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package hintrunner

import "fmt"

// HintRunnerError represents error ocurring during the hint runner
type HintRunnerError struct {
err error
}

func NewHintRunnerError(err error) *HintRunnerError {
return &HintRunnerError{err}
}

func (e *HintRunnerError) Error() string {
return fmt.Sprintf("error in hint runner: %s", e.err.Error())
}

func (e *HintRunnerError) Unwrap() error {
return e.err
}

// HintError stores information about the hint being executed as well as its cause
type HintError struct {
hintName string
err error
}

func NewHintError(hintName string, err error) *HintError {
return &HintError{hintName, err}
}

func (e *HintError) Error() string {
return fmt.Sprintf("error executing hint %s: %s", e.hintName, e.err.Error())
}

func (e *HintError) Unwrap() error {
return e.err
}

// OperandError is returned when the error is detected during an operand get/resolve execution
type OperandError struct {
operandName string
err error
}

func NewOperandError(operandName string, err error) *OperandError {
return &OperandError{operandName, err}
}

func (e *OperandError) Error() string {
return fmt.Sprintf("failed to get/resolve operand %s: %s", e.operandName, e.err.Error())
}

func (e *OperandError) Unwrap() error {
return e.err
}
81 changes: 81 additions & 0 deletions pkg/hintrunner/hint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package hintrunner

import (
VM "github.com/NethermindEth/cairo-vm-go/pkg/vm"
"github.com/NethermindEth/cairo-vm-go/pkg/vm/memory"
f "github.com/consensys/gnark-crypto/ecc/stark-curve/fp"
)

type Hinter interface {
Execute(vm *VM.VirtualMachine) *HintError
}

const allocSegmentName = "AllocSegment"

type AllocSegment struct {
dst CellRefer
}

func (hint AllocSegment) Execute(vm *VM.VirtualMachine) *HintError {
segmentIndex := vm.MemoryManager.Memory.AllocateEmptySegment()
memAddress := memory.MemoryValueFromSegmentAndOffset(segmentIndex, 0)

cell, err := hint.dst.Get(vm)
if err != nil {
return NewHintError(allocSegmentName, err)
}

err = cell.Write(memAddress)
if err != nil {
return NewHintError(allocSegmentName, err)
}

return nil
}

const testLessThanName = "TestLessThan"

type TestLessThan struct {
dst CellRefer
lhs ResOperander
rhs ResOperander
}

func (hint TestLessThan) Execute(vm *VM.VirtualMachine) *HintError {
lhsVal, err := hint.lhs.Resolve(vm)
if err != nil {
return NewHintError(testLessThanName, err)
}

rhsVal, err := hint.rhs.Resolve(vm)
if err != nil {
return NewHintError(testLessThanName, err)
}

lhsFelt, err := lhsVal.ToFieldElement()
if err != nil {
return NewHintError(testLessThanName, err)
}

rhsFelt, err := rhsVal.ToFieldElement()
if err != nil {
return NewHintError(testLessThanName, err)
}

resFelt := f.Element{}
if lhsFelt.Cmp(rhsFelt) <= 0 {
resFelt.SetOne()
}

dstCell, err := hint.dst.Get(vm)
if err != nil {
return NewHintError(testLessThanName, err)
}

err = dstCell.Write(memory.MemoryValueFromFieldElement(&resFelt))
if err != nil {
return NewHintError(testLessThanName, err)
}

return nil
}
98 changes: 98 additions & 0 deletions pkg/hintrunner/hint_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package hintrunner

import (
"math/big"
"testing"

VM "github.com/NethermindEth/cairo-vm-go/pkg/vm"
"github.com/NethermindEth/cairo-vm-go/pkg/vm/memory"
"github.com/stretchr/testify/require"
)

func TestAllocSegment(t *testing.T) {
vm := defaultVirtualMachine()
vm.Context.Ap = 3
vm.Context.Fp = 0

var ap ApCellRef = 5
var fp FpCellRef = 9

alloc1 := AllocSegment{ap}
alloc2 := AllocSegment{fp}

err := alloc1.Execute(vm)
t.Log(err)
require.Nil(t, err)
require.Equal(t, 3, len(vm.MemoryManager.Memory.Segments))
require.Equal(
t,
memory.MemoryValueFromSegmentAndOffset(2, 0),
readFrom(vm, VM.ExecutionSegment, vm.Context.Ap+5),
)

err = alloc2.Execute(vm)
require.Nil(t, err)
require.Equal(t, 4, len(vm.MemoryManager.Memory.Segments))
require.Equal(
t,
memory.MemoryValueFromSegmentAndOffset(3, 0),
readFrom(vm, VM.ExecutionSegment, vm.Context.Fp+9),
)

}

func TestTestLessThanFalse(t *testing.T) {
vm := defaultVirtualMachine()
vm.Context.Ap = 0
vm.Context.Fp = 0
writeTo(vm, VM.ExecutionSegment, 0, memory.MemoryValueFromInt(17))

var dst ApCellRef = 1

lhs := Immediate(*big.NewInt(32))

var rhsRef FpCellRef = 0
rhs := Deref{rhsRef}

hint := TestLessThan{
dst: dst,
lhs: lhs,
rhs: rhs,
}

err := hint.Execute(vm)
require.Nil(t, err)
require.Equal(
t,
memory.EmptyMemoryValueAsFelt(),
readFrom(vm, VM.ExecutionSegment, 1),
)
}

func TestTestLessThanTrue(t *testing.T) {
vm := defaultVirtualMachine()
vm.Context.Ap = 0
vm.Context.Fp = 0
writeTo(vm, VM.ExecutionSegment, 0, memory.MemoryValueFromInt(23))

var dst ApCellRef = 1

lhs := Immediate(*big.NewInt(13))

var rhsRef FpCellRef = 0
rhs := Deref{rhsRef}

hint := TestLessThan{
dst: dst,
lhs: lhs,
rhs: rhs,
}

err := hint.Execute(vm)
require.Nil(t, err)
require.Equal(
t,
memory.MemoryValueFromInt(1),
readFrom(vm, VM.ExecutionSegment, 1),
)
}
28 changes: 28 additions & 0 deletions pkg/hintrunner/hintrunner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package hintrunner

import (
VM "github.com/NethermindEth/cairo-vm-go/pkg/vm"
)

// todo: Can two or more hints be assigned to a specific PC?
type HintRunner struct {
// A mapping from program counter to hint implementation
hints map[uint64]Hinter
}

func CreateHintRunner(hints map[uint64]Hinter) HintRunner {
return HintRunner{hints}
}

func (hr HintRunner) RunHint(vm *VM.VirtualMachine) *HintRunnerError {
hint := hr.hints[vm.Context.Pc]
if hint == nil {
return nil
}

err := hint.Execute(vm)
if err != nil {
return NewHintRunnerError(err)
}
return nil
}
47 changes: 47 additions & 0 deletions pkg/hintrunner/hintrunner_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package hintrunner

import (
"testing"

VM "github.com/NethermindEth/cairo-vm-go/pkg/vm"
"github.com/NethermindEth/cairo-vm-go/pkg/vm/memory"
"github.com/stretchr/testify/require"
)

func TestExistingHint(t *testing.T) {
vm := defaultVirtualMachine()
vm.Context.Ap = 3

var ap ApCellRef = 5
allocHint := AllocSegment{ap}

hr := CreateHintRunner(map[uint64]Hinter{
10: allocHint,
})

vm.Context.Pc = 10
err := hr.RunHint(vm)
require.Nil(t, err)
require.Equal(
t,
memory.MemoryValueFromSegmentAndOffset(2, 0),
readFrom(vm, VM.ExecutionSegment, vm.Context.Ap+5),
)
}

func TestNoHint(t *testing.T) {
vm := defaultVirtualMachine()
vm.Context.Ap = 3

var ap ApCellRef = 5
allocHint := AllocSegment{ap}

hr := CreateHintRunner(map[uint64]Hinter{
10: allocHint,
})

vm.Context.Pc = 100
err := hr.RunHint(vm)
require.Nil(t, err)
require.Equal(t, 2, len(vm.MemoryManager.Memory.Segments))
}
Loading