@@ -19,7 +19,10 @@ import (
1919
2020 "github.com/consensys/go-corset/pkg/asm/io"
2121 "github.com/consensys/go-corset/pkg/ir"
22+ "github.com/consensys/go-corset/pkg/ir/mir"
2223 "github.com/consensys/go-corset/pkg/schema"
24+ sc "github.com/consensys/go-corset/pkg/schema"
25+ "github.com/consensys/go-corset/pkg/trace"
2326 "github.com/consensys/go-corset/pkg/trace/lt"
2427 "github.com/consensys/go-corset/pkg/util/collection/array"
2528 "github.com/consensys/go-corset/pkg/util/field"
@@ -78,7 +81,7 @@ func Propagate[F field.Element[F], T io.Instruction[T]](p MixedProgram[F, T], tr
7881 // Construct suitable executior for the given program
7982 var (
8083 errors []error
81- n = len (p .program .Functions ())
84+ n = uint ( len (p .program .Functions () ))
8285 //
8386 executor = io .NewExecutor (p .program )
8487 // Clone heap in trace file, since will mutate this.
@@ -91,7 +94,7 @@ func Propagate[F field.Element[F], T io.Instruction[T]](p MixedProgram[F, T], tr
9194 return lt.TraceFile {}, errors
9295 }
9396 // Write seed instances
94- errors = writeInstances (p . program , trace .Modules [: n ] , executor )
97+ errors = writeInstances (p , n , trace .Modules , executor )
9598 // Read out generated instances
9699 modules := readInstances (& heap , p .program , executor )
97100 // Append external modules (which are unaffected by propagation).
@@ -103,15 +106,24 @@ func Propagate[F field.Element[F], T io.Instruction[T]](p MixedProgram[F, T], tr
103106// WriteInstances writes all of the instances defined in the given trace columns
104107// into the executor which, in turn, forces it to execute the relevant
105108// functions, and functions they call, etc.
106- func writeInstances [T io.Instruction [T ]](p io. Program [ T ], trace []lt. Module [word. BigEndian ] ,
107- executor * io.Executor [T ]) []error {
109+ func writeInstances [F field. Element [ F ], T io.Instruction [T ]](p MixedProgram [ F , T ], n uint ,
110+ trace []lt. Module [word. BigEndian ], executor * io.Executor [T ]) []error {
108111 //
109112 var errors []error
110113 //
111- for i , m := range trace {
112- errs := writeFunctionInstances (uint (i ), p , m , executor )
114+ for i , m := range trace [: n ] {
115+ errs := writeFunctionInstances (uint (i ), p . program , m , executor )
113116 errors = append (errors , errs ... )
114117 }
118+ // Write all from non-assembly modules
119+ for i , m := range trace [n :] {
120+ var extern = p .externs [i ]
121+ // Write instances from any external calls
122+ for _ , call := range extractExternalCalls (extern ) {
123+ errs := writeExternCall (call , p .program , m , executor )
124+ errors = append (errors , errs ... )
125+ }
126+ }
115127 //
116128 return errors
117129}
@@ -142,6 +154,68 @@ func writeFunctionInstances[T io.Instruction[T]](fid uint, p io.Program[T], mod
142154 return errors
143155}
144156
157+ // Extract any external function calls found within the given module, returning
158+ // them as an array.
159+ func extractExternalCalls [F field.Element [F ], M sc.Module [F ]](extern M ) []mir.FunctionCall [F ] {
160+ var calls []mir.FunctionCall [F ]
161+ //
162+ for iter := extern .Constraints (); iter .HasNext (); {
163+ c := iter .Next ()
164+ // This should always hold
165+ if hc , ok := c .(mir.Constraint [F ]); ok {
166+ // Check whether its a call or not
167+ if call , ok := hc .Unwrap ().(mir.FunctionCall [F ]); ok {
168+ // Yes, so record it
169+ calls = append (calls , call )
170+ }
171+ }
172+ }
173+ //
174+ return calls
175+ }
176+
177+ // Write any function instances arising from the given call.
178+ func writeExternCall [F field.Element [F ], T io.Instruction [T ]](call mir.FunctionCall [F ], p io.Program [T ], mod RawModule ,
179+ executor * io.Executor [T ]) []error {
180+ //
181+ var (
182+ trMod = & ltModuleAdaptor [F ]{mod }
183+ height = mod .Height ()
184+ fn = p .Function (call .Callee )
185+ inputs = make ([]big.Int , fn .NumInputs ())
186+ outputs = make ([]big.Int , fn .NumOutputs ())
187+ errors []error
188+ )
189+ //
190+ if call .Selector .HasValue () {
191+ var selector = call .Selector .Unwrap ()
192+ // Invoke each user-defined instance in turn
193+ for i := range height {
194+ // execute if selector enabled
195+ if enabled , _ , err := selector .TestAt (int (i ), trMod , nil ); enabled {
196+ // Extract external columns
197+ extractExternColumns (int (i ), call , trMod , inputs , outputs )
198+ // Execute function call to produce outputs
199+ errs := executeAndCheck (call .Callee , fn .Name (), inputs , outputs , executor )
200+ errors = append (errors , errs ... )
201+ } else if err != nil {
202+ errors = append (errors , err )
203+ }
204+ }
205+ } else {
206+ // Invoke each user-defined instance in turn
207+ for i := range height {
208+ // Extract external columns
209+ extractExternColumns (int (i ), call , trMod , inputs , outputs )
210+ // Execute function call to produce outputs
211+ errs := executeAndCheck (call .Callee , fn .Name (), inputs , outputs , executor )
212+ errors = append (errors , errs ... )
213+ }
214+ }
215+ //
216+ return errors
217+ }
218+
145219func executeAndCheck [T io.Instruction [T ]](fid uint , name string , inputs , outputs []big.Int ,
146220 executor * io.Executor [T ]) []error {
147221 var (
@@ -195,6 +269,34 @@ func extractFunctionColumns(row uint, mod RawModule, inputs, outputs []big.Int)
195269 }
196270}
197271
272+ func extractExternColumns [F field.Element [F ]](row int , call mir.FunctionCall [F ], mod trace.Module [F ],
273+ inputs , outputs []big.Int ) []error {
274+ //
275+ // Extract function arguments
276+ errs1 := extractExternTerms (row , call .Arguments , mod , inputs )
277+ // Extract function returns
278+ errs2 := extractExternTerms (row , call .Returns , mod , outputs )
279+ //
280+ return append (errs1 , errs2 ... )
281+ }
282+
283+ func extractExternTerms [F field.Element [F ]](row int , terms []mir.Term [F ], mod trace.Module [F ], values []big.Int ,
284+ ) []error {
285+ var errors []error
286+ //
287+ for i , arg := range terms {
288+ var (
289+ ith big.Int
290+ val , err = arg .EvalAt (row , mod , nil )
291+ )
292+ ith .SetBytes (val .Bytes ())
293+ values [i ] = ith
294+ //
295+ errors = append (errors , err )
296+ }
297+ //
298+ return errors
299+ }
198300func extractFunctionPadding (registers []schema.Register , inputs , outputs []big.Int ) {
199301 var numInputs = len (inputs )
200302 //
@@ -279,3 +381,55 @@ func toArgumentString(args []big.Int) string {
279381 //
280382 return builder .String ()
281383}
384+
385+ // The purpose of the lt adaptor is to make an lt.TraceFile look like a Trace.
386+ // In general, this is not safe. However, we use this once we already know that
387+ // the trace has been aligned. Also, it is only used in a specific context.
388+ type ltModuleAdaptor [F field.Element [F ]] struct {
389+ module lt.Module [word.BigEndian ]
390+ }
391+
392+ func (p * ltModuleAdaptor [F ]) Name () string {
393+ return p .module .Name
394+ }
395+
396+ func (p * ltModuleAdaptor [F ]) Width () uint {
397+ return uint (len (p .module .Columns ))
398+ }
399+
400+ func (p * ltModuleAdaptor [F ]) Height () uint {
401+ return p .module .Height ()
402+ }
403+
404+ func (p * ltModuleAdaptor [F ]) Column (cid uint ) trace.Column [F ] {
405+ return & ltColumnAdaptor [F ]{p .module .Columns [cid ]}
406+ }
407+
408+ func (p * ltModuleAdaptor [F ]) ColumnOf (col string ) trace.Column [F ] {
409+ panic ("unsupported operation" )
410+ }
411+
412+ type ltColumnAdaptor [F field.Element [F ]] struct {
413+ column lt.Column [word.BigEndian ]
414+ }
415+
416+ func (p * ltColumnAdaptor [F ]) Name () string {
417+ return p .column .Name
418+ }
419+
420+ func (p * ltColumnAdaptor [F ]) Get (row int ) F {
421+ var (
422+ v = p .column .Data .Get (uint (row ))
423+ w F
424+ )
425+ // Convert
426+ return w .SetBytes (v .Bytes ())
427+ }
428+
429+ func (p * ltColumnAdaptor [F ]) Data () array.Array [F ] {
430+ panic ("unsupported operation" )
431+ }
432+
433+ func (p * ltColumnAdaptor [F ]) Padding () F {
434+ panic ("unsupported operation" )
435+ }
0 commit comments