diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 5fcc252e3..2e419a4a3 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -220,7 +220,7 @@ func main() { if err != nil { return fmt.Errorf("cannot parse args: %w", err) } - program, hints, userArgs, err := runner.AssembleProgram(cairoProgram, userArgs, availableGas) + program, hints, userArgs, err := runner.AssembleProgram(cairoProgram, userArgs, availableGas, proofmode) if err != nil { return fmt.Errorf("cannot assemble program: %w", err) } @@ -258,7 +258,7 @@ func runVM( availableGas uint64, ) error { fmt.Println("Running....") - runner, err := runner.NewRunner(&program, hints, runnerMode, collectTrace, maxsteps, layoutName, userArgs, availableGas) + cairoRunner, err := runner.NewRunner(&program, hints, runnerMode, collectTrace, maxsteps, layoutName, userArgs, availableGas) if err != nil { return fmt.Errorf("cannot create runner: %w", err) } @@ -268,25 +268,34 @@ func runVM( // but these functions are implemented differently in both this and cairo-rs VMs // and the difference is quite subtle. if entrypointOffset == 0 { - if err := runner.Run(); err != nil { + if err := cairoRunner.Run(); err != nil { return fmt.Errorf("runtime error: %w", err) } } else { - if err := runner.RunEntryPoint(entrypointOffset); err != nil { + if err := cairoRunner.RunEntryPoint(entrypointOffset); err != nil { return fmt.Errorf("runtime error (entrypoint=%d): %w", entrypointOffset, err) } } + + if airPublicInputLocation != "" { + if runnerMode == runner.ProofModeCairo { + fmt.Println("finalizing builtins for cairo") + } else if runnerMode == runner.ExecutionModeCairo { + fmt.Println("finalizing builtins for cairo") + } + + } if proofmode { - if err := runner.EndRun(); err != nil { + if err := cairoRunner.EndRun(); err != nil { return fmt.Errorf("cannot end run: %w", err) } - if err := runner.FinalizeSegments(); err != nil { + if err := cairoRunner.FinalizeSegments(); err != nil { return fmt.Errorf("cannot finalize segments: %w", err) } } if proofmode || collectTrace { - trace, err := runner.BuildTrace() + trace, err := cairoRunner.BuildTrace() if err != nil { return fmt.Errorf("cannot build trace: %w", err) } @@ -299,7 +308,7 @@ func runVM( } if proofmode || buildMemory { - memory, err := runner.BuildMemory() + memory, err := cairoRunner.BuildMemory() if err != nil { return fmt.Errorf("cannot build memory: %w", err) } @@ -313,7 +322,7 @@ func runVM( if proofmode { if airPublicInputLocation != "" { - airPublicInput, err := runner.GetAirPublicInput() + airPublicInput, err := cairoRunner.GetAirPublicInput() if err != nil { return err } @@ -336,7 +345,7 @@ func runVM( if err != nil { return err } - airPrivateInput, err := runner.GetAirPrivateInput(tracePath, memoryPath) + airPrivateInput, err := cairoRunner.GetAirPrivateInput(tracePath, memoryPath) if err != nil { return err } @@ -352,7 +361,7 @@ func runVM( } fmt.Println("Success!") - output := runner.Output() + output := cairoRunner.Output() if len(output) > 0 { fmt.Println("Program output:") for _, val := range output { diff --git a/integration_tests/cairo_1_programs/with_input/proofmode__small.cairo b/integration_tests/cairo_1_programs/with_input/proofmode__small.cairo new file mode 100644 index 000000000..0d299e431 --- /dev/null +++ b/integration_tests/cairo_1_programs/with_input/proofmode__small.cairo @@ -0,0 +1,22 @@ +fn arr_sum(mut vals: Array) -> felt252 { + let mut sum: felt252 = 0; + + loop { + match vals.pop_front() { + Option::Some(v) => { + sum += v; + }, + Option::None(_) => { + break sum; + } + }; + } +} + +fn main(vals: Array) -> Array { + let sum1 = arr_sum(vals); + let mut res: Array = ArrayTrait::new(); + res.append(sum1); + res.append(sum1); + res +} \ No newline at end of file diff --git a/integration_tests/cairo_1_programs/with_input/proofmode_segment_arena__small.cairo b/integration_tests/cairo_1_programs/with_input/proofmode_segment_arena__small.cairo new file mode 100644 index 000000000..6c7ea1e2b --- /dev/null +++ b/integration_tests/cairo_1_programs/with_input/proofmode_segment_arena__small.cairo @@ -0,0 +1,45 @@ +use array::ArrayTrait; +use array::SpanTrait; +use dict::Felt252DictTrait; +use pedersen::PedersenTrait; + +fn main(mut vals: Array) -> Array { + // Create a dictionary using segment arena + let mut dict = felt252_dict_new::(); + + // Store each value in the dictionary with its index + let mut index: felt252 = 0; + loop { + match vals.pop_front() { + Option::Some(v) => { + dict.insert(index, v); + index += 1; + }, + Option::None(_) => { + break; + } + }; + }; + + // Create result array + let mut res: Array = ArrayTrait::new(); + + // Read values back from dictionary and hash them + let mut i: felt252 = 0; + loop { + let i_u128: u128 = i.try_into().unwrap(); + let index_u128: u128 = index.try_into().unwrap(); + + if i_u128 >= index_u128 { + break; + } + + let value = dict.get(i); + let hash = pedersen::pedersen(value, i); + res.append(hash); + + i += 1; + }; + + res +} \ No newline at end of file diff --git a/integration_tests/cairo_1_programs/with_input/proofmode_with_builtins__small.cairo b/integration_tests/cairo_1_programs/with_input/proofmode_with_builtins__small.cairo new file mode 100644 index 000000000..f61d484ff --- /dev/null +++ b/integration_tests/cairo_1_programs/with_input/proofmode_with_builtins__small.cairo @@ -0,0 +1,47 @@ +use array::ArrayTrait; +use array::SpanTrait; +use pedersen::PedersenTrait; +use core::num::traits::Bounded; + +fn check_and_sum(mut vals: Array) -> felt252 { + let mut sum: felt252 = 0; + let upper_bound: u128 = 1000000; // Example bound + + loop { + match vals.pop_front() { + Option::Some(v) => { + // Convert felt252 to u128 for range check + let v_u128: u128 = v.try_into().unwrap(); + // This will ensure v is non-negative and within u128 bounds + assert(v_u128 <= upper_bound, 'Value exceeds bound'); + + sum += v; + }, + Option::None(_) => { + break sum; + } + }; + } +} + +fn main(mut vals: Array) -> Array { + // Calculate sum with range checks + let sum = check_and_sum(vals); + + // Hash the sum using Pedersen + let hash = pedersen::pedersen(sum, 0); + + // Create result array + let mut res: Array = ArrayTrait::new(); + + // Add original sum + res.append(sum); + // Add hash of sum + res.append(hash); + + // Add a u128 conversion to demonstrate more range checks + let sum_u128: u128 = sum.try_into().unwrap(); + res.append(sum_u128.into()); + + res +} \ No newline at end of file diff --git a/pkg/hintrunner/core/hint.go b/pkg/hintrunner/core/hint.go index 6ef697617..584c8f504 100644 --- a/pkg/hintrunner/core/hint.go +++ b/pkg/hintrunner/core/hint.go @@ -1075,7 +1075,11 @@ func (hint *AllocFelt252Dict) String() string { return "AllocFelt252Dict" } func (hint *AllocFelt252Dict) Execute(vm *VM.VirtualMachine, ctx *hinter.HintRunnerContext) error { - hinter.InitializeDictionaryManager(ctx) + useTemporarySegments, err := hinter.GetVariableAs[bool](&ctx.ScopeManager, "useTemporarySegments") + if err != nil { + return fmt.Errorf("get useTemporarySegments: %w", err) + } + hinter.InitializeDictionaryManager(ctx, useTemporarySegments) arenaPtr, err := hinter.ResolveAsAddress(vm, hint.SegmentArenaPtr) if err != nil { @@ -2009,3 +2013,17 @@ func (hint *ExternalWriteGasToMemory) Execute(vm *VM.VirtualMachine, ctx *hinter } return nil } + +type RelocateAllDictionaries struct{} + +func (hint *RelocateAllDictionaries) String() string { + return "RelocateAllDictionaries" +} + +func (hint *RelocateAllDictionaries) Execute(vm *VM.VirtualMachine, ctx *hinter.HintRunnerContext) error { + err := ctx.DictionaryManager.RelocateAllDictionaries(vm) + if err != nil { + return fmt.Errorf("relocate all dictionaries: %w", err) + } + return nil +} diff --git a/pkg/hintrunner/hinter/dict.go b/pkg/hintrunner/hinter/dict.go index 88f3569c1..4390b6f5f 100644 --- a/pkg/hintrunner/hinter/dict.go +++ b/pkg/hintrunner/hinter/dict.go @@ -38,19 +38,27 @@ func (d *Dictionary) InitNumber() uint64 { type DictionaryManager struct { // a map that links a segment index to a dictionary dictionaries map[uint64]Dictionary + // useTemporarySegments is a flag that indicates if the dictionaries should located in temporary segments, and later relocated to memory segments + useTemporarySegments bool } -func InitializeDictionaryManager(ctx *HintRunnerContext) { +func InitializeDictionaryManager(ctx *HintRunnerContext, useTemporarySegments bool) { if ctx.DictionaryManager.dictionaries == nil { ctx.DictionaryManager.dictionaries = make(map[uint64]Dictionary) } + ctx.DictionaryManager.useTemporarySegments = useTemporarySegments } // It creates a new segment which will hold dictionary values. It links this // segment with the current dictionary and returns the address that points // to the start of this segment func (dm *DictionaryManager) NewDictionary(vm *VM.VirtualMachine) mem.MemoryAddress { - newDictAddr := vm.Memory.AllocateEmptySegment() + var newDictAddr mem.MemoryAddress + if dm.useTemporarySegments { + newDictAddr = vm.Memory.AllocateEmptyTemporarySegment() + } else { + newDictAddr = vm.Memory.AllocateEmptySegment() + } dm.dictionaries[newDictAddr.SegmentIndex] = Dictionary{ data: make(map[f.Element]*mem.MemoryValue), idx: uint64(len(dm.dictionaries)), @@ -86,6 +94,35 @@ func (dm *DictionaryManager) Set(dictAddr *mem.MemoryAddress, key *f.Element, va return fmt.Errorf("no dictionary at address %s", dictAddr) } +// Relocates all dictionaries into a single segment if proofmode is enabled +// In LambdaClass VM there is add_relocation_rule() used, which is used only to relocate dictionaries / in specific hint. Thus we relocate dictionaries right away. +func (dm *DictionaryManager) RelocateAllDictionaries(vm *VM.VirtualMachine) error { + segmentAddr := vm.Memory.AllocateEmptySegment() + for key, dict := range dm.dictionaries { + for _, value := range dict.data { + if value.IsAddress() { + addr, err := value.MemoryAddress() + if err != nil { + return err + } + element := -key + addr.Offset + mv := mem.MemoryValueFromUint(element) + err = vm.Memory.WriteToAddress(&segmentAddr, &mv) + if err != nil { + return err + } + } else { + err := vm.Memory.WriteToAddress(&segmentAddr, value) + if err != nil { + return err + } + } + segmentAddr.Offset++ + } + } + return nil +} + // Used to keep track of squashed dictionaries type SquashedDictionaryManager struct { // A map from each key to a list of indices where the key is present diff --git a/pkg/parsers/starknet/starknet.go b/pkg/parsers/starknet/starknet.go index 0bbc615d5..9c3f49c85 100644 --- a/pkg/parsers/starknet/starknet.go +++ b/pkg/parsers/starknet/starknet.go @@ -28,10 +28,10 @@ type Arg struct { } type EntryPointByFunction struct { - Offset int `json:"offset"` - Builtins []builtins.BuiltinType `json:"builtins"` - InputArgs []Arg `json:"input_args"` - ReturnArg []Arg `json:"return_arg"` + Offset int `json:"offset"` + Builtins []builtins.BuiltinType `json:"builtins"` + InputArgs []Arg `json:"input_args"` + ReturnArgs []Arg `json:"return_arg"` } type Hints struct { diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index bef7c782e..0d0a4bd6b 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "slices" + "strings" "github.com/NethermindEth/cairo-vm-go/pkg/assembler" "github.com/NethermindEth/cairo-vm-go/pkg/hintrunner" @@ -48,7 +49,7 @@ func NewRunner(program *Program, hints map[uint64][]hinter.Hinter, runnerMode Ru if err != nil { return Runner{}, err } - newHintRunnerContext := getNewHintRunnerContext(program, userArgs, availableGas) + newHintRunnerContext := getNewHintRunnerContext(program, userArgs, availableGas, runnerMode == ProofModeCairo || runnerMode == ProofModeZero) hintrunner := hintrunner.NewHintRunner(hints, &newHintRunnerContext) return Runner{ program: program, @@ -60,7 +61,7 @@ func NewRunner(program *Program, hints map[uint64][]hinter.Hinter, runnerMode Ru }, nil } -func getNewHintRunnerContext(program *Program, userArgs []starknet.CairoFuncArgs, availableGas uint64) hinter.HintRunnerContext { +func getNewHintRunnerContext(program *Program, userArgs []starknet.CairoFuncArgs, availableGas uint64, proofmode bool) hinter.HintRunnerContext { // The writeApOffset is the offset where the user arguments will be written. It is added to the current AP in the ExternalWriteArgsToMemory hint. // The writeApOffset is significant for Cairo programs, because of the prepended Entry Code instructions. // In the entry code instructions the builtins bases (excluding gas, segment arena and output) are written to the memory, @@ -77,12 +78,16 @@ func getNewHintRunnerContext(program *Program, userArgs []starknet.CairoFuncArgs writeApOffset += 3 } } + if proofmode { + writeApOffset += uint64(len(program.Builtins)) - 1 + } newHintrunnerContext := *hinter.InitializeDefaultContext() err := newHintrunnerContext.ScopeManager.AssignVariables(map[string]any{ - "userArgs": userArgs, - "apOffset": writeApOffset, - "gas": availableGas, + "userArgs": userArgs, + "apOffset": writeApOffset, + "gas": availableGas, + "useTemporarySegments": proofmode, }) // Error handling: this condition should never be true, since the context was initialized above if err != nil { @@ -91,11 +96,19 @@ func getNewHintRunnerContext(program *Program, userArgs []starknet.CairoFuncArgs return newHintrunnerContext } -func AssembleProgram(cairoProgram *starknet.StarknetProgram, userArgs []starknet.CairoFuncArgs, availableGas uint64) (Program, map[uint64][]hinter.Hinter, []starknet.CairoFuncArgs, error) { +func AssembleProgram(cairoProgram *starknet.StarknetProgram, userArgs []starknet.CairoFuncArgs, availableGas uint64, proofmode bool) (Program, map[uint64][]hinter.Hinter, []starknet.CairoFuncArgs, error) { mainFunc, ok := cairoProgram.EntryPointsByFunction["main"] if !ok { return Program{}, nil, nil, fmt.Errorf("cannot find main function") } + + if proofmode { + err := CheckOnlyArrayFeltInputAndReturntValue(mainFunc) + if err != nil { + return Program{}, nil, nil, err + } + } + expectedArgsSize, actualArgsSize := 0, 0 for _, arg := range mainFunc.InputArgs { expectedArgsSize += arg.Size @@ -119,7 +132,8 @@ func AssembleProgram(cairoProgram *starknet.StarknetProgram, userArgs []starknet return Program{}, nil, nil, fmt.Errorf("cannot get hints: %w", err) } - entryCodeInstructions, entryCodeHints, err := GetEntryCodeInstructions(mainFunc) + entryCodeInstructions, entryCodeHints, err := GetEntryCodeInstructions(mainFunc, proofmode) + if err != nil { return Program{}, nil, nil, fmt.Errorf("cannot load entry code instructions: %w", err) } @@ -564,12 +578,17 @@ func (ctx *InlineCasmContext) AddInlineCASM(code string) { ctx.currentCodeOffset += int(total_size) } -func GetEntryCodeInstructions(function starknet.EntryPointByFunction) ([]*fp.Element, map[uint64][]hinter.Hinter, error) { +// Function derived from the cairo-lang-runner crate. +// https://github.com/starkware-libs/cairo/blob/40a7b60687682238f7f71ef7c59c986cc5733915/crates/cairo-lang-runner/src/lib.rs#L703 +// / Returns the instructions to add to the beginning of the code to successfully call the main +// / function, as well as the builtins required to execute the program. +func GetEntryCodeInstructions(function starknet.EntryPointByFunction, proofmode bool) ([]*fp.Element, map[uint64][]hinter.Hinter, error) { paramTypes := function.InputArgs apOffset := 0 builtinOffset := 3 codeOffset := uint64(function.Offset) builtinsOffsetsMap := map[builtins.BuiltinType]int{} + ctx := &InlineCasmContext{} for _, builtin := range []builtins.BuiltinType{ builtins.MulModType, @@ -587,7 +606,11 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction) ([]*fp.Ele } } - ctx := &InlineCasmContext{} + if proofmode { + builtinsOffsetsMap[builtins.OutputType] = 0 + // Increment the length of the builtins by 1 to account for the output builtin + ctx.AddInlineCASM(fmt.Sprintf("ap += %d;", len(function.Builtins)+1)) + } gotSegmentArena := false for _, builtin := range function.Builtins { @@ -627,6 +650,7 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction) ([]*fp.Ele paramsSize += param.Size } apOffset += paramsSize + gotGasBuiltin := false for _, builtin := range function.Builtins { if offset, isBuiltin := builtinsOffsetsMap[builtin]; isBuiltin { @@ -644,6 +668,7 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction) ([]*fp.Ele hints[uint64(ctx.currentCodeOffset)] = append(hints[uint64(ctx.currentCodeOffset)], &core.ExternalWriteGasToMemory{}) ctx.AddInlineCASM("ap += 1;") apOffset += 1 + gotGasBuiltin = true } } @@ -667,6 +692,122 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction) ([]*fp.Ele } totalSize := uint64(endInstructionsSize) + uint64(codeOffset) ctx.AddInlineCASM(fmt.Sprintf("call rel %d; ret;", int(totalSize))) + outputPtr := fmt.Sprintf("[fp-%d]", len(function.Builtins)+2) + if proofmode { + for i, b := range function.Builtins { + // assert [fp + i] == [fp - builtin_offset] + offset := builtinsOffsetsMap[b] + // increment the offset by 1 to account for the skipped output builtin + ctx.AddInlineCASM(fmt.Sprintf("[fp+%d] = [ap-%d];", i+1, offset)) + } + outputs := []string{} + + lastReturnArg := function.ReturnArgs[len(function.ReturnArgs)-1] + for i := function.ReturnArgs[len(function.ReturnArgs)-1].Size + 1; i >= 1; i-- { + outputs = append(outputs, fmt.Sprintf("[ap-%d]", i)) + } + + arrayStartPtr, arrayEndPtr := outputs[0], outputs[1] + if strings.HasPrefix(lastReturnArg.DebugName, "core::panics::PanicResult") { + // assert panic_flag = *(output_ptr++); + panicFlag := outputs[0] + ctx.AddInlineCASM(fmt.Sprintf("%s = [%s];", panicFlag, outputPtr)) + arrayStartPtr, arrayEndPtr = outputs[1], outputs[2] + } + + ctx.AddInlineCASM( + fmt.Sprintf(` + %s = [ap] + %s, ap++; + [ap-1] = [%s+1]; + [ap] = [ap-1], ap++; + [ap] = %s, ap++; + [ap] = %s + 1, ap++; + jmp rel 4 if [ap-3] != 0; + jmp rel 12; + + [ap] = [[ap-2]], ap++; + [ap-1] = [[ap-2]]; + [ap-4] = [ap] + 1, ap++; + [ap] = [ap-4] + 1, ap++; + [ap] = [ap-4] + 1, ap++; + jmp rel -8 if [ap-3] != 0; + `, arrayEndPtr, arrayStartPtr, outputPtr, arrayStartPtr, outputPtr), + ) + if paramsSize != 0 { + offset := 2 * len(function.Builtins) + if gotSegmentArena { + offset += 4 + } + if gotGasBuiltin { + offset += 1 + } + arrayStartPtr := fmt.Sprintf("[fp+%d]", offset) + arrayEndPtr := fmt.Sprintf("[fp+%d]", offset+1) + + ctx.AddInlineCASM( + fmt.Sprintf(` + %s = [ap] + %s, ap++; + [ap-1] = [[ap-2]]; + [ap] = [ap-1], ap++; + [ap] = %s, ap++; + [ap] = [ap-4]+1, ap++; + jmp rel 4 if [ap-3] != 0; + jmp rel 12; + + [ap] = [[ap-2]], ap++; + [ap-1] = [[ap-2]]; + [ap-4] = [ap]+1, ap++; + [ap] = [ap-4]+1, ap++; + [ap] = [ap-4]+1; + jmp rel -8 if [ap-3] != 0; + `, arrayEndPtr, arrayStartPtr, arrayStartPtr), + ) + } + // After we are done writing into the output segment, we can write the final output_ptr into locals: + // The last instruction will write the final output ptr so we can find it in [ap - 1] + ctx.AddInlineCASM("[fp] = [[ap - 1]];") + + if gotSegmentArena { + offset := 2 + len(function.Builtins) + segmentArenaPtr := fmt.Sprintf("[fp + %d]", offset) + hints[uint64(ctx.currentCodeOffset)] = append(hints[uint64(ctx.currentCodeOffset)], &core.RelocateAllDictionaries{}) + ctx.AddInlineCASM(fmt.Sprintf(` + [ap]=[%s-2], ap++; + [ap]=[%s-1], ap++; + [ap-1]=[ap-2]; + jmp rel 4 if [ap-2] != 0; + jpm rel 19; + [ap]=[[%s-3], ap++; + [ap-3] = [ap]+1; + jmp rel 4 if [ap-1] != 0; + jmp rel 12; + [ap]=[[ap-2]+1], ap++; + [ap] = [[ap-3]+3], ap++; + [ap-1] = [ap-2] + 1; + [ap] = [ap-4] + 3, ap++; + [ap-4] = [ap] + 1, ap++; + jmp rel -12; + `, segmentArenaPtr, segmentArenaPtr, segmentArenaPtr, + )) + } + + // Copying the final builtins from locals into the top of the stack. + for i := range function.Builtins { + ctx.AddInlineCASM(fmt.Sprintf("[ap] = [fp + %d], ap++;", i)) + } + } else { + // Writing the final builtins into the top of the stack. + for _, b := range function.Builtins { + offset := builtinsOffsetsMap[b] + ctx.AddInlineCASM(fmt.Sprintf("[ap] = [fp - %d], ap++;", offset)) + } + + } + if proofmode { + ctx.AddInlineCASM("jmp rel 0;") + } else { + ctx.AddInlineCASM("ret;") + } return ctx.instructions, hints, nil } @@ -675,3 +816,19 @@ func GetFooterInstructions() []*fp.Element { // and `pc` registers. return []*fp.Element{new(fp.Element).SetUint64(2345108766317314046)} } + +func CheckOnlyArrayFeltInputAndReturntValue(mainFunc starknet.EntryPointByFunction) error { + if len(mainFunc.InputArgs) != 1 { + return fmt.Errorf("main function in proofmode should have felt252 array as input argument") + } + if len(mainFunc.ReturnArgs) != 1 { + return fmt.Errorf("main function in proofmode should have an felt252 array as return argument") + } + if mainFunc.InputArgs[0].Size != 2 || mainFunc.InputArgs[0].DebugName != "Array" { + return fmt.Errorf("main function input argument should be Felt Array") + } + if mainFunc.ReturnArgs[0].Size != 3 || mainFunc.ReturnArgs[0].DebugName != "core::panics::PanicResult::<(core::array::Array::)>" { + return fmt.Errorf("main function return argument should be Felt Array") + } + return nil +} diff --git a/pkg/vm/memory/memory.go b/pkg/vm/memory/memory.go index 944c5eec3..28e3833c4 100644 --- a/pkg/vm/memory/memory.go +++ b/pkg/vm/memory/memory.go @@ -191,6 +191,8 @@ func (segment *Segment) String() string { // Represents the whole VM memory divided into segments type Memory struct { Segments []*Segment + // TemporarySegments is a map of temporary segments, key is the segment index, value is the segment + TemporarySegments map[uint64]*Segment } // todo(rodro): can the amount of segments be known before hand? @@ -227,6 +229,15 @@ func (memory *Memory) AllocateEmptySegment() MemoryAddress { } } +// Allocates an empty temporary segment and returns its index +func (memory *Memory) AllocateEmptyTemporarySegment() MemoryAddress { + memory.TemporarySegments[uint64(len(memory.TemporarySegments))] = EmptySegment() + return MemoryAddress{ + SegmentIndex: -uint64(len(memory.TemporarySegments)), + Offset: 0, + } +} + // Allocate a Builtin segment func (memory *Memory) AllocateBuiltinSegment(builtinRunner BuiltinRunner) MemoryAddress { builtinSegment := EmptySegment().WithBuiltinRunner(builtinRunner)