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

Add VerifySecureRunner + SecureRun config flag #303

Merged
merged 37 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
ad62601
Add ExecutionResources struct
fmoletta Sep 27, 2023
467621f
Fix: Remove vm argument from CairoRunner methods
fmoletta Sep 27, 2023
8e2597a
Finish func + remove todos
fmoletta Sep 27, 2023
4538465
Add unit test
fmoletta Sep 27, 2023
18b6255
Implement GenArg
fmoletta Sep 27, 2023
a82ec66
Remove recursive processing
fmoletta Sep 27, 2023
13fa7c2
Add unit tests
fmoletta Sep 27, 2023
623f8dc
Fix test values
fmoletta Sep 27, 2023
8fdb11f
Start fn
fmoletta Sep 27, 2023
59417f9
Merge branch 'GenWriteArg' into runFromEntrypoint
fmoletta Sep 27, 2023
d07aeb1
Add RunFromEntryPoint
fmoletta Sep 27, 2023
206ea73
Add test for RunFromEntryPoint
fmoletta Sep 27, 2023
1230ee1
Add unit tests
fmoletta Sep 27, 2023
b7684cd
Add comments
fmoletta Sep 27, 2023
18d9553
Add skeleton
fmoletta Sep 27, 2023
20d65ef
Implement GetMemoryAccesses for BuiltinRunner
fmoletta Sep 27, 2023
2de8b40
Start implementing RunSecurityChecks
fmoletta Sep 27, 2023
85227d6
Implement VerifyAutoDeductionsForAddr
fmoletta Sep 28, 2023
3771c74
Finish func + remove import cycle
fmoletta Sep 28, 2023
7ee92b3
Add fix + unit test
fmoletta Sep 28, 2023
fbfc94a
Fix logic, add test
fmoletta Sep 28, 2023
1f090a5
Fix logic
fmoletta Sep 28, 2023
c28f686
Fix logic
fmoletta Sep 28, 2023
cd017a5
Fix logic + add test
fmoletta Sep 28, 2023
6a53c17
Add unit tests
fmoletta Sep 28, 2023
eb863d9
Move RunSecurityChecks to external func in builtins package
fmoletta Sep 28, 2023
76e4c67
Remove todos from BuiltinRunner interface
fmoletta Sep 28, 2023
324fc1d
Finish verifySecureRunner
fmoletta Sep 28, 2023
22cfcfb
Add verifySecure to RunFromEntryPoint
fmoletta Sep 28, 2023
01ecde9
Add unit tests
fmoletta Sep 28, 2023
e1dd296
Add secure_run flag
fmoletta Sep 28, 2023
1cb8df4
Fix typo
fmoletta Sep 28, 2023
fa27b2e
Merge branch 'main' of github.com:lambdaclass/cairo-vm.go into secure…
fmoletta Oct 2, 2023
67924a2
Fix alias for new flag
fmoletta Oct 2, 2023
da1b2c5
Set secure_run to true in testProgram
fmoletta Oct 3, 2023
5a37197
Merge branch 'main' into secure-run
pefontana Oct 5, 2023
614cd99
Merge branch 'main' into secure-run
pefontana Oct 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}
11 changes: 11 additions & 0 deletions pkg/builtins/poseidon.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ func (p *PoseidonBuiltinRunner) CellsPerInstance() uint {
return POSEIDON_CELLS_PER_INSTANCE
}

func (p *PoseidonBuiltinRunner) InputCellsPerInstance() uint {
return POSEIDON_INPUT_CELLS_PER_INSTANCE
}

func (p *PoseidonBuiltinRunner) 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 @@ -217,3 +221,10 @@ func (r *PoseidonBuiltinRunner) GetUsedInstances(segments *memory.MemorySegmentM

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

func (b *PoseidonBuiltinRunner) 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
Loading