Skip to content

Commit

Permalink
Merge branch 'main' of github.com:lambdaclass/cairo-vm.go into HEAD
Browse files Browse the repository at this point in the history
  • Loading branch information
toni-calvin committed Oct 25, 2023
2 parents 4c1b132 + 432dd89 commit 02464df
Show file tree
Hide file tree
Showing 29 changed files with 1,019 additions and 23 deletions.
20 changes: 20 additions & 0 deletions cairo_programs/blake2s_hello_world_hash.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
%builtins range_check bitwise

from starkware.cairo.common.alloc import alloc
from starkware.cairo.common.cairo_blake2s.blake2s import blake2s
from starkware.cairo.common.cairo_builtins import BitwiseBuiltin

// Computes the hash of "Hello World"
func main{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() {
alloc_locals;
let inputs: felt* = alloc();
assert inputs[0] = 'Hell';
assert inputs[1] = 'o Wo';
assert inputs[2] = 'rld';
let (local blake2s_ptr_start) = alloc();
let blake2s_ptr = blake2s_ptr_start;
let (output) = blake2s{range_check_ptr=range_check_ptr, blake2s_ptr=blake2s_ptr}(inputs, 9);
assert output.low = 219917655069954262743903159041439073909;
assert output.high = 296157033687865319468534978667166017272;
return ();
}
14 changes: 13 additions & 1 deletion cmd/cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,14 @@ func handleCommands(ctx *cli.Context) error {
layout = "plain"
}

cairoRunConfig := cairo_run.CairoRunConfig{DisableTracePadding: false, ProofMode: ctx.Bool("proof_mode"), Layout: layout}
proofMode := ctx.Bool("proof_mode")

secureRun := !proofMode
if ctx.Bool("secure_run") {
secureRun = true
}

cairoRunConfig := cairo_run.CairoRunConfig{DisableTracePadding: false, ProofMode: proofMode, Layout: layout, SecureRun: secureRun}

cairoRunner, err := cairo_run.CairoRun(programPath, cairoRunConfig)
if err != nil {
Expand Down Expand Up @@ -51,6 +58,11 @@ func main() {
Aliases: []string{"p"},
Usage: "Run in proof mode",
},
&cli.BoolFlag{
Name: "secure_run",
Aliases: []string{"s"},
Usage: "Run security checks. Default: true unless proof_mode is true",
},
&cli.StringFlag{
Name: "layout",
Aliases: []string{"l"},
Expand Down
11 changes: 11 additions & 0 deletions pkg/builtins/bitwise.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ func (b *BitwiseBuiltinRunner) CellsPerInstance() uint {
return BITWISE_CELLS_PER_INSTANCE
}

func (b *BitwiseBuiltinRunner) InputCellsPerInstance() uint {
return BIWISE_INPUT_CELLS_PER_INSTANCE
}

func (b *BitwiseBuiltinRunner) GetAllocatedMemoryUnits(segments *memory.MemorySegmentManager, currentStep uint) (uint, error) {
// This condition corresponds to an uninitialized ratio for the builtin, which should only
// happen when layout is `dynamic`
Expand Down Expand Up @@ -253,3 +257,10 @@ func (r *BitwiseBuiltinRunner) GetUsedInstances(segments *memory.MemorySegmentMa

return utils.DivCeil(usedCells, r.CellsPerInstance()), nil
}

func (b *BitwiseBuiltinRunner) GetMemorySegmentAddresses() (memory.Relocatable, memory.Relocatable, error) {
if b.StopPtr == nil {
return memory.Relocatable{}, memory.Relocatable{}, NewErrNoStopPointer(b.Name())
}
return b.base, memory.NewRelocatable(b.base.SegmentIndex, *b.StopPtr), nil
}
99 changes: 87 additions & 12 deletions pkg/builtins/builtin_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package builtins

import (
"fmt"
"sort"

"github.com/lambdaclass/cairo-vm.go/pkg/vm/memory"
"github.com/pkg/errors"
Expand All @@ -28,6 +29,10 @@ type BuiltinRunner interface {
Base() memory.Relocatable
// Returns the name of the builtin
Name() string
// Cells per builtin instance
CellsPerInstance() uint
// Input cells per builtin instance
InputCellsPerInstance() uint
// Creates a memory segment for the builtin and initializes its base
InitializeSegments(*memory.MemorySegmentManager)
// Returns the builtin's initial stack
Expand All @@ -40,27 +45,97 @@ type BuiltinRunner interface {
AddValidationRule(*memory.Memory)
// Sets the inclusion of the Builtin Runner in the Cairo Runner
Include(bool)
// TODO: Later additions -> Some of them could depend on a Default Implementation
// // Most of them depend on Layouts being implemented
// // Use cases:
// // I. PROOF_MODE
// Returns the builtin's ratio, is zero if the layout is dynamic
Ratio() uint
// Returns the builtin's allocated memory units
GetAllocatedMemoryUnits(segments *memory.MemorySegmentManager, currentStep uint) (uint, error)
// // Returns the list of memory addresses used by the builtin
// Returns the list of memory addresses used by the builtin
GetMemoryAccesses(*memory.MemorySegmentManager) ([]memory.Relocatable, error)
GetRangeCheckUsage(*memory.Memory) (*uint, *uint)
GetUsedPermRangeCheckLimits(segments *memory.MemorySegmentManager, currentStep uint) (uint, error)
GetUsedDilutedCheckUnits(dilutedSpacing uint, dilutedNBits uint) uint
GetUsedCellsAndAllocatedSizes(segments *memory.MemorySegmentManager, currentStep uint) (uint, uint, error)
FinalStack(segments *memory.MemorySegmentManager, pointer memory.Relocatable) (memory.Relocatable, error)
// // II. SECURITY (secure-run flag cairo-run || verify-secure flag run_from_entrypoint)
// RunSecurityChecks(*vm.VirtualMachine) error // verify_secure_runner logic
// // Returns the base & stop_ptr, stop_ptr can be nil
// GetMemorySegmentAddresses() (memory.Relocatable, *memory.Relocatable) //verify_secure_runner logic
// // III. STARKNET-SPECIFIC
// Returns the base & stop_ptr
GetMemorySegmentAddresses() (memory.Relocatable, memory.Relocatable, error)
// Amount of builtin instances used
GetUsedInstances(*memory.MemorySegmentManager) (uint, error)
// // IV. GENERAL CASE (but not critical)
// FinalStack(*memory.MemorySegmentManager, memory.Relocatable) (memory.Relocatable, error) // read_return_values
}

func RunSecurityChecksForBuiltin(builtin BuiltinRunner, segments *memory.MemorySegmentManager) error {
if builtin.Name() == OUTPUT_BUILTIN_NAME {
return nil
}

cellsPerInstance := builtin.CellsPerInstance()
nInputCells := builtin.InputCellsPerInstance()
builtinSegmentIndex := builtin.Base().SegmentIndex

offsets := make([]int, 0)
// Collect the builtin segment's address' offsets
for addr := range segments.Memory.Data {
if addr.SegmentIndex == builtinSegmentIndex {
offsets = append(offsets, int(addr.Offset))
}
}

if len(offsets) == 0 {
// No checks to run for empty segment
return nil
}
// Sort offsets for easier comparison
sort.Ints(offsets)
// Obtain max offset
maxOffset := offsets[len(offsets)-1]

n := (maxOffset / int(cellsPerInstance)) + 1
//Verify that n is not too large to make sure the expectedOffsets list that is constructed below is not too large.
if n > len(offsets)/int(nInputCells) {
return errors.Errorf("Missing memory cells for %s", builtin.Name())
}

// Check that the two inputs (x and y) of each instance are set.
expectedOffsets := make([]int, 0)
for i := 0; i < n; i++ {
for j := 0; j < int(nInputCells); j++ {
expectedOffsets = append(expectedOffsets, int(cellsPerInstance)*i+j)
}
}
// Find the missing offsets (offsets in expectedOffsets but not in offsets)
missingOffsets := make([]int, 0)
j := 0
i := 0
for i < len(expectedOffsets) && j < len(offsets) {
if expectedOffsets[i] < offsets[j] {
missingOffsets = append(missingOffsets, expectedOffsets[i])
} else {
j++
}
i++
}
for i < len(expectedOffsets) {
missingOffsets = append(missingOffsets, expectedOffsets[i])
i++
}
if len(missingOffsets) != 0 {
return errors.Errorf("Missing memory cells for builtin: %s: %v", builtin.Name(), missingOffsets)
}

// Verify auto deduction rules for the unassigned output cells.
// Assigned output cells are checked as part of the call to VerifyAutoDeductions().
for i := uint(0); i < uint(n); i++ {
for j := uint(nInputCells); j < cellsPerInstance; j++ {
addr := memory.NewRelocatable(builtinSegmentIndex, cellsPerInstance*i+j)
_, err := segments.Memory.Get(addr)
// Output cell not in memory
if err != nil {
_, err = builtin.DeduceMemoryCell(addr, &segments.Memory)
if err != nil {
return err
}
}
}
}

return nil
}
105 changes: 105 additions & 0 deletions pkg/builtins/builtin_runner_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package builtins_test

import (
"testing"

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

func TestRunSecurityChecksEmptyMemory(t *testing.T) {
builtin := builtins.NewBitwiseBuiltinRunner(256)
segments := memory.NewMemorySegmentManager()
err := builtins.RunSecurityChecksForBuiltin(builtin, &segments)
if err != nil {
t.Errorf("RunSecurityChecks failed with error: %s", err.Error())
}
}

func TestRunSecurityChecksMissingMemoryCells(t *testing.T) {
builtin := builtins.NewBitwiseBuiltinRunner(256)
segments := memory.NewMemorySegmentManager()

builtin.InitializeSegments(&segments)
builtinBase := builtin.Base()
// A bitwise cell consists of 5 elements: 2 input cells & 3 output cells
// In this test we insert cells 4-5 and leave the first input cell empty
// This will fail the security checks, as the memory cell with offset 0 will be missing
builtinSegment := []memory.MaybeRelocatable{
*memory.NewMaybeRelocatableFelt(lambdaworks.FeltFromUint(1)),
*memory.NewMaybeRelocatableFelt(lambdaworks.FeltFromUint(2)),
*memory.NewMaybeRelocatableFelt(lambdaworks.FeltFromUint(3)),
*memory.NewMaybeRelocatableFelt(lambdaworks.FeltFromUint(4)),
}
segments.LoadData(builtinBase.AddUint(1), &builtinSegment)

err := builtins.RunSecurityChecksForBuiltin(builtin, &segments)
if err == nil {
t.Errorf("RunSecurityChecks should have failed")
}
}

func TestRunSecurityChecksMissingMemoryCellsNCheck(t *testing.T) {
builtin := builtins.NewBitwiseBuiltinRunner(256)
segments := memory.NewMemorySegmentManager()

builtin.InitializeSegments(&segments)
builtinBase := builtin.Base()
// n = max(offsets) // cellsPerInstance + 1
// n = max[(0]) // 5 + 1 = 0 // 5 + 1 = 1
// len(offsets) // inputCells = 1 // 2
// This will fail the security checks, as n > len(offsets) // inputCells
builtinSegment := []memory.MaybeRelocatable{
*memory.NewMaybeRelocatableFelt(lambdaworks.FeltFromUint(1)),
}
segments.LoadData(builtinBase, &builtinSegment)

err := builtins.RunSecurityChecksForBuiltin(builtin, &segments)
if err == nil {
t.Errorf("RunSecurityChecks should have failed")
}
}

func TestRunSecurityChecksValidateOutputCellsNotDeducedOk(t *testing.T) {
builtin := builtins.NewBitwiseBuiltinRunner(256)
segments := memory.NewMemorySegmentManager()

builtin.InitializeSegments(&segments)
builtinBase := builtin.Base()
// A bitwise cell consists of 5 elements: 2 input cells & 3 output cells
// In this test we insert the input cells (1-2), but not the output cells (3-5)
// This will cause the security checks to run the auto-deductions for those output cells
builtinSegment := []memory.MaybeRelocatable{
*memory.NewMaybeRelocatableFelt(lambdaworks.FeltFromUint(1)),
*memory.NewMaybeRelocatableFelt(lambdaworks.FeltFromUint(2)),
}
segments.LoadData(builtinBase, &builtinSegment)

err := builtins.RunSecurityChecksForBuiltin(builtin, &segments)
if err != nil {
t.Errorf("RunSecurityChecks failed with error: %s", err.Error())
}
}

func TestRunSecurityChecksValidateOutputCellsNotDeducedErr(t *testing.T) {
builtin := builtins.NewBitwiseBuiltinRunner(256)
segments := memory.NewMemorySegmentManager()

builtin.InitializeSegments(&segments)
builtinBase := builtin.Base()
// A bitwise cell consists of 5 elements: 2 input cells & 3 output cells
// In this test we insert the input cells (1-2), but not the output cells (3-5)
// This will cause the security checks to run the auto-deductions for those output cells
// As we inserted an invalid value (PRIME -1) on the first input cell, this deduction will fail
builtinSegment := []memory.MaybeRelocatable{
*memory.NewMaybeRelocatableFelt(lambdaworks.FeltFromDecString("-1")),
*memory.NewMaybeRelocatableFelt(lambdaworks.FeltFromUint(2)),
}
segments.LoadData(builtinBase, &builtinSegment)

err := builtins.RunSecurityChecksForBuiltin(builtin, &segments)
if err == nil {
t.Errorf("RunSecurityChecks should have failed")
}
}
11 changes: 11 additions & 0 deletions pkg/builtins/ec_op.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ func (r *EcOpBuiltinRunner) CellsPerInstance() uint {
return CELLS_PER_EC_OP
}

func (b *EcOpBuiltinRunner) InputCellsPerInstance() uint {
return INPUT_CELLS_PER_EC_OP
}

func (ec *EcOpBuiltinRunner) AddValidationRule(*memory.Memory) {}

func (ec *EcOpBuiltinRunner) Base() memory.Relocatable {
Expand Down Expand Up @@ -405,3 +409,10 @@ func (r *EcOpBuiltinRunner) GetUsedInstances(segments *memory.MemorySegmentManag

return utils.DivCeil(usedCells, r.CellsPerInstance()), nil
}

func (b *EcOpBuiltinRunner) GetMemorySegmentAddresses() (memory.Relocatable, memory.Relocatable, error) {
if b.StopPtr == nil {
return memory.Relocatable{}, memory.Relocatable{}, NewErrNoStopPointer(b.Name())
}
return b.base, memory.NewRelocatable(b.base.SegmentIndex, *b.StopPtr), nil
}
11 changes: 11 additions & 0 deletions pkg/builtins/keccak.go
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,10 @@ func (k *KeccakBuiltinRunner) CellsPerInstance() uint {
return KECCAK_CELLS_PER_INSTANCE
}

func (b *KeccakBuiltinRunner) InputCellsPerInstance() uint {
return KECCAK_INPUT_CELLS_PER_INSTANCE
}

func (k *KeccakBuiltinRunner) GetAllocatedMemoryUnits(segments *memory.MemorySegmentManager, currentStep uint) (uint, error) {
// This condition corresponds to an uninitialized ratio for the builtin, which should only
// happen when layout is `dynamic`
Expand Down Expand Up @@ -676,3 +680,10 @@ func (r *KeccakBuiltinRunner) GetUsedInstances(segments *memory.MemorySegmentMan

return utils.DivCeil(usedCells, r.CellsPerInstance()), nil
}

func (b *KeccakBuiltinRunner) GetMemorySegmentAddresses() (memory.Relocatable, memory.Relocatable, error) {
if b.StopPtr == nil {
return memory.Relocatable{}, memory.Relocatable{}, NewErrNoStopPointer(b.Name())
}
return b.base, memory.NewRelocatable(b.base.SegmentIndex, *b.StopPtr), nil
}
16 changes: 16 additions & 0 deletions pkg/builtins/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
)

const OUTPUT_BUILTIN_NAME = "output"
const OUTPUT_CELLS_PER_INSTANCE = 1

type OutputBuiltinRunner struct {
base memory.Relocatable
Expand Down Expand Up @@ -133,3 +134,18 @@ func (r *OutputBuiltinRunner) GetUsedInstances(segments *memory.MemorySegmentMan

return usedCells, nil
}

func (b *OutputBuiltinRunner) GetMemorySegmentAddresses() (memory.Relocatable, memory.Relocatable, error) {
if b.StopPtr == nil {
return memory.Relocatable{}, memory.Relocatable{}, NewErrNoStopPointer(b.Name())
}
return b.base, memory.NewRelocatable(b.base.SegmentIndex, *b.StopPtr), nil
}

func (r *OutputBuiltinRunner) CellsPerInstance() uint {
return OUTPUT_CELLS_PER_INSTANCE
}

func (b *OutputBuiltinRunner) InputCellsPerInstance() uint {
return OUTPUT_CELLS_PER_INSTANCE
}
11 changes: 11 additions & 0 deletions pkg/builtins/pedersen.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ func (p *PedersenBuiltinRunner) CellsPerInstance() uint {
return PEDERSEN_CELLS_PER_INSTANCE
}

func (p *PedersenBuiltinRunner) InputCellsPerInstance() uint {
return PEDERSEN_INPUT_CELLS_PER_INSTANCE
}

func (p *PedersenBuiltinRunner) GetAllocatedMemoryUnits(segments *memory.MemorySegmentManager, currentStep uint) (uint, error) {
// This condition corresponds to an uninitialized ratio for the builtin, which should only
// happen when layout is `dynamic`
Expand Down Expand Up @@ -228,3 +232,10 @@ func (r *PedersenBuiltinRunner) GetUsedInstances(segments *memory.MemorySegmentM

return utils.DivCeil(usedCells, r.CellsPerInstance()), nil
}

func (b *PedersenBuiltinRunner) GetMemorySegmentAddresses() (memory.Relocatable, memory.Relocatable, error) {
if b.StopPtr == nil {
return memory.Relocatable{}, memory.Relocatable{}, NewErrNoStopPointer(b.Name())
}
return b.base, memory.NewRelocatable(b.base.SegmentIndex, *b.StopPtr), nil
}
Loading

0 comments on commit 02464df

Please sign in to comment.