From 9f13cdc1247ee0eb863472c1662a0bb01f4d6215 Mon Sep 17 00:00:00 2001 From: DavePearce Date: Thu, 27 Jun 2024 10:37:44 +1200 Subject: [PATCH 1/2] Support conversion from `json` to `lt` This allows `FieldColumn` to be written out as bytes, where each element of the column occupies 32 bytes. --- pkg/cmd/trace.go | 45 ++++++++++++++++++++++++++++++++++++++- pkg/trace/bytes_column.go | 34 +++++++++++++++++++++++++---- pkg/trace/field_column.go | 14 ++++++++++-- pkg/trace/lt/reader.go | 5 ++++- pkg/util/table.go | 9 +++++++- 5 files changed, 98 insertions(+), 9 deletions(-) diff --git a/pkg/cmd/trace.go b/pkg/cmd/trace.go index 1d9eeff..43dac72 100644 --- a/pkg/cmd/trace.go +++ b/pkg/cmd/trace.go @@ -2,6 +2,7 @@ package cmd import ( "fmt" + "math" "os" "strings" @@ -25,12 +26,20 @@ var traceCmd = &cobra.Command{ // Parse trace trace := readTraceFile(args[0]) list := getFlag(cmd, "list") + print := getFlag(cmd, "print") + padding := getUint(cmd, "pad") + start := getUint(cmd, "start") + end := getUint(cmd, "end") + max_width := getUint(cmd, "max-width") filter := getString(cmd, "filter") output := getString(cmd, "out") // if filter != "" { trace = filterColumns(trace, filter) } + if padding != 0 { + trace.Pad(padding) + } if list { listColumns(trace) } @@ -38,6 +47,10 @@ var traceCmd = &cobra.Command{ if output != "" { writeTraceFile(output, trace) } + + if print { + printTrace(start, end, max_width, trace) + } }, } @@ -71,9 +84,39 @@ func listColumns(tr trace.Trace) { tbl.Print() } +func printTrace(start uint, end uint, max_width uint, tr trace.Trace) { + height := min(tr.Height(), end) - start + tbl := util.NewTablePrinter(1+height, 1+tr.Width()) + + for j := uint(0); j < height; j++ { + tbl.Set(j+1, 0, fmt.Sprintf("#%d", j+start)) + } + + for i := uint(0); i < tr.Width(); i++ { + ith := tr.ColumnByIndex(i) + tbl.Set(0, i+1, ith.Name()) + + if start < ith.Height() { + ith_height := min(ith.Height(), end) - start + for j := uint(0); j < ith_height; j++ { + tbl.Set(j+1, i+1, ith.Get(int(j+start)).String()) + } + } + } + + // + tbl.SetMaxWidth(max_width) + tbl.Print() +} + func init() { rootCmd.AddCommand(traceCmd) - traceCmd.Flags().BoolP("list", "l", false, "detail the columns in the trace file") + traceCmd.Flags().BoolP("list", "l", false, "list only the columns in the trace file") + traceCmd.Flags().BoolP("print", "p", false, "print entire trace file") + traceCmd.Flags().Uint("pad", 0, "add a given number of padding rows (to each module)") + traceCmd.Flags().UintP("start", "s", 0, "filter out rows below this") + traceCmd.Flags().UintP("end", "e", math.MaxUint, "filter out this and all following rows") + traceCmd.Flags().Uint("max-width", 32, "specify maximum display width for a column") traceCmd.Flags().StringP("out", "o", "", "Specify output file to write trace") traceCmd.Flags().StringP("filter", "f", "", "Filter columns beginning with prefix") } diff --git a/pkg/trace/bytes_column.go b/pkg/trace/bytes_column.go index 9f5b0df..72140a6 100644 --- a/pkg/trace/bytes_column.go +++ b/pkg/trace/bytes_column.go @@ -20,11 +20,13 @@ type BytesColumn struct { length uint // The data stored in this column (as bytes). bytes []byte + // Value to be used when padding this column + padding *fr.Element } // NewBytesColumn constructs a new BytesColumn from its constituent parts. -func NewBytesColumn(name string, width uint8, length uint, bytes []byte) *BytesColumn { - return &BytesColumn{name, width, length, bytes} +func NewBytesColumn(name string, width uint8, length uint, bytes []byte, padding *fr.Element) *BytesColumn { + return &BytesColumn{name, width, length, bytes, padding} } // Name returns the name of this column @@ -44,7 +46,7 @@ func (p *BytesColumn) Height() uint { // Padding returns the value which will be used for padding this column. func (p *BytesColumn) Padding() *fr.Element { - panic("todo") + return p.padding } // Get the ith row of this column as a field element. @@ -64,6 +66,7 @@ func (p *BytesColumn) Clone() Column { clone.name = p.name clone.length = p.length clone.width = p.width + clone.padding = p.padding // NOTE: the following is as we never actually mutate the underlying bytes // array. clone.bytes = p.bytes @@ -98,7 +101,30 @@ func (p *BytesColumn) Data() []*fr.Element { // Pad this column with n copies of the column's padding value. func (p *BytesColumn) Pad(n uint) { - panic("TODO") + // Computing padding length (in bytes) + padding_len := n * uint(p.width) + // Access bytes to use for padding + padding_bytes := p.padding.Bytes() + padded_bytes := make([]byte, padding_len+uint(len(p.bytes))) + // Append padding + offset := 0 + + for i := uint(0); i < n; i++ { + // Calculate starting position within the 32byte array, remembering that + // padding_bytes is stored in _big endian_ format meaning + // padding_bytes[0] is the _most significant_ byte. + start := 32 - p.width + // Copy over least significant bytes + for j := start; j < 32; j++ { + padded_bytes[offset] = padding_bytes[j] + offset++ + } + } + // Copy over original data + copy(padded_bytes[padding_len:], p.bytes) + // Done + p.bytes = padded_bytes + p.length += n } // Write the raw bytes of this column to a given writer, returning an error diff --git a/pkg/trace/field_column.go b/pkg/trace/field_column.go index aced90a..ab639e8 100644 --- a/pkg/trace/field_column.go +++ b/pkg/trace/field_column.go @@ -86,7 +86,17 @@ func (p *FieldColumn) Pad(n uint) { } // Write the raw bytes of this column to a given writer, returning an error -// if this failed (for some reason). +// if this failed (for some reason). Observe that this always writes data in +// 32byte chunks. func (p *FieldColumn) Write(w io.Writer) error { - panic("TODO") + for _, e := range p.data { + // Read exactly 32 bytes + bytes := e.Bytes() + // Write them out + if _, err := w.Write(bytes[:]); err != nil { + return err + } + } + // + return nil } diff --git a/pkg/trace/lt/reader.go b/pkg/trace/lt/reader.go index 64e466d..355df1b 100644 --- a/pkg/trace/lt/reader.go +++ b/pkg/trace/lt/reader.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/binary" + "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" "github.com/consensys/go-corset/pkg/trace" ) @@ -69,6 +70,8 @@ func readColumnHeader(buf *bytes.Reader) (*trace.BytesColumn, error) { if err := binary.Read(buf, binary.BigEndian, &length); err != nil { return nil, err } + // Default padding + zero := fr.NewElement(0) // Done - return trace.NewBytesColumn(string(name), bytesPerElement, uint(length), nil), nil + return trace.NewBytesColumn(string(name), bytesPerElement, uint(length), nil, &zero), nil } diff --git a/pkg/util/table.go b/pkg/util/table.go index 89789c8..7a3354f 100644 --- a/pkg/util/table.go +++ b/pkg/util/table.go @@ -51,7 +51,14 @@ func (p *TablePrinter) Print() { for i := 0; i < len(p.rows); i++ { row := p.rows[i] for j, col := range row { - fmt.Printf(" %*s |", p.widths[j], col) + jth := col + jth_width := p.widths[j] + + if uint(len(col)) > jth_width { + jth = col[0:jth_width] + } + + fmt.Printf(" %*s |", jth_width, jth) } fmt.Println() From 55888727c7bdd645eb5e1e501d94db6238ed1be6 Mon Sep 17 00:00:00 2001 From: DavePearce Date: Thu, 27 Jun 2024 10:52:37 +1200 Subject: [PATCH 2/2] Refactoring This puts through a number of minor refactorings which are now possible, and removes some code which is no longer used. --- pkg/air/eval.go | 2 +- pkg/cmd/trace.go | 6 +-- pkg/cmd/util.go | 2 +- pkg/hir/eval.go | 2 +- pkg/mir/eval.go | 2 +- pkg/schema/alignment.go | 4 +- pkg/schema/assignment/byte_decomposition.go | 10 ++--- pkg/schema/assignment/computed.go | 6 +-- pkg/schema/assignment/lexicographic_sort.go | 8 ++-- pkg/schema/assignment/sorted_permutation.go | 7 ++-- pkg/schema/constraint/permutation.go | 2 +- pkg/schema/constraint/range.go | 2 +- pkg/schema/constraint/type.go | 2 +- pkg/schema/schemas.go | 25 ------------- pkg/test/ir_test.go | 3 +- pkg/trace/array_trace.go | 30 ++------------- pkg/trace/field_column.go | 5 +++ pkg/trace/json/reader.go | 35 ++++++++++++++++++ pkg/trace/json/writer.go | 2 +- pkg/trace/lt/writer.go | 4 +- pkg/trace/printer.go | 4 +- pkg/trace/trace.go | 41 ++------------------- 22 files changed, 81 insertions(+), 123 deletions(-) create mode 100644 pkg/trace/json/reader.go diff --git a/pkg/air/eval.go b/pkg/air/eval.go index b274767..c1f143a 100644 --- a/pkg/air/eval.go +++ b/pkg/air/eval.go @@ -9,7 +9,7 @@ import ( // value at that row of the column in question or nil is that row is // out-of-bounds. func (e *ColumnAccess) EvalAt(k int, tr trace.Trace) *fr.Element { - val := tr.ColumnByIndex(e.Column).Get(k + e.Shift) + val := tr.Column(e.Column).Get(k + e.Shift) var clone fr.Element // Clone original value diff --git a/pkg/cmd/trace.go b/pkg/cmd/trace.go index 43dac72..69adb0a 100644 --- a/pkg/cmd/trace.go +++ b/pkg/cmd/trace.go @@ -60,7 +60,7 @@ func filterColumns(tr trace.Trace, prefix string) trace.Trace { ntr := trace.EmptyArrayTrace() // for i := uint(0); i < tr.Width(); i++ { - ith := tr.ColumnByIndex(i) + ith := tr.Column(i) if strings.HasPrefix(ith.Name(), prefix) { ntr.Add(ith) } @@ -73,7 +73,7 @@ func listColumns(tr trace.Trace) { tbl := util.NewTablePrinter(3, tr.Width()) for i := uint(0); i < tr.Width(); i++ { - ith := tr.ColumnByIndex(i) + ith := tr.Column(i) elems := fmt.Sprintf("%d rows", ith.Height()) bytes := fmt.Sprintf("%d bytes", ith.Width()*ith.Height()) tbl.SetRow(i, ith.Name(), elems, bytes) @@ -93,7 +93,7 @@ func printTrace(start uint, end uint, max_width uint, tr trace.Trace) { } for i := uint(0); i < tr.Width(); i++ { - ith := tr.ColumnByIndex(i) + ith := tr.Column(i) tbl.Set(0, i+1, ith.Name()) if start < ith.Height() { diff --git a/pkg/cmd/util.go b/pkg/cmd/util.go index 52c5b57..f81671f 100644 --- a/pkg/cmd/util.go +++ b/pkg/cmd/util.go @@ -102,7 +102,7 @@ func readTraceFile(filename string) trace.Trace { // switch ext { case ".json": - tr, err = trace.ParseJsonTrace(bytes) + tr, err = json.FromBytes(bytes) if err == nil { return tr } diff --git a/pkg/hir/eval.go b/pkg/hir/eval.go index d4fcf6e..a2912d1 100644 --- a/pkg/hir/eval.go +++ b/pkg/hir/eval.go @@ -9,7 +9,7 @@ import ( // value at that row of the column in question or nil is that row is // out-of-bounds. func (e *ColumnAccess) EvalAllAt(k int, tr trace.Trace) []*fr.Element { - val := tr.ColumnByIndex(e.Column).Get(k + e.Shift) + val := tr.Column(e.Column).Get(k + e.Shift) var clone fr.Element // Clone original value diff --git a/pkg/mir/eval.go b/pkg/mir/eval.go index 10e602c..f865c19 100644 --- a/pkg/mir/eval.go +++ b/pkg/mir/eval.go @@ -9,7 +9,7 @@ import ( // value at that row of the column in question or nil is that row is // out-of-bounds. func (e *ColumnAccess) EvalAt(k int, tr trace.Trace) *fr.Element { - val := tr.ColumnByIndex(e.Column).Get(k + e.Shift) + val := tr.Column(e.Column).Get(k + e.Shift) var clone fr.Element // Clone original value diff --git a/pkg/schema/alignment.go b/pkg/schema/alignment.go index 72ceb50..e31e04c 100644 --- a/pkg/schema/alignment.go +++ b/pkg/schema/alignment.go @@ -47,7 +47,7 @@ func alignWith(expand bool, p tr.Trace, schema Schema) error { return fmt.Errorf("trace missing column %s", schemaName) } - traceName := p.ColumnByIndex(index).Name() + traceName := p.Column(index).Name() // Check alignment if traceName != schemaName { // Not aligned --- so fix @@ -74,7 +74,7 @@ func alignWith(expand bool, p tr.Trace, schema Schema) error { unknowns := make([]string, n) // Determine names of unknown columns. for i := index; i < ncols; i++ { - unknowns[i-index] = p.ColumnByIndex(i).Name() + unknowns[i-index] = p.Column(i).Name() } // return fmt.Errorf("trace contains unknown columns: %v", unknowns) diff --git a/pkg/schema/assignment/byte_decomposition.go b/pkg/schema/assignment/byte_decomposition.go index bf2dc9c..bf592c1 100644 --- a/pkg/schema/assignment/byte_decomposition.go +++ b/pkg/schema/assignment/byte_decomposition.go @@ -5,7 +5,7 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" "github.com/consensys/go-corset/pkg/schema" - tr "github.com/consensys/go-corset/pkg/trace" + "github.com/consensys/go-corset/pkg/trace" "github.com/consensys/go-corset/pkg/util" ) @@ -62,13 +62,13 @@ func (p *ByteDecomposition) IsComputed() bool { // ExpandTrace expands a given trace to include the columns specified by a given // ByteDecomposition. This requires computing the value of each byte column in // the decomposition. -func (p *ByteDecomposition) ExpandTrace(tr tr.Trace) error { +func (p *ByteDecomposition) ExpandTrace(tr trace.Trace) error { // Calculate how many bytes required. n := len(p.targets) // Identify target column - target := tr.ColumnByIndex(p.source) + target := tr.Column(p.source) // Extract column data to decompose - data := tr.ColumnByIndex(p.source).Data() + data := tr.Column(p.source).Data() // Construct byte column data cols := make([][]*fr.Element, n) // Initialise columns @@ -86,7 +86,7 @@ func (p *ByteDecomposition) ExpandTrace(tr tr.Trace) error { padding := decomposeIntoBytes(target.Padding(), n) // Finally, add byte columns to trace for i := 0; i < n; i++ { - tr.AddColumn(p.targets[i].Name(), cols[i], padding[i]) + tr.Add(trace.NewFieldColumn(p.targets[i].Name(), cols[i], padding[i])) } // Done return nil diff --git a/pkg/schema/assignment/computed.go b/pkg/schema/assignment/computed.go index 26dbc0c..e780c19 100644 --- a/pkg/schema/assignment/computed.go +++ b/pkg/schema/assignment/computed.go @@ -6,7 +6,7 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" "github.com/consensys/go-corset/pkg/schema" sc "github.com/consensys/go-corset/pkg/schema" - tr "github.com/consensys/go-corset/pkg/trace" + "github.com/consensys/go-corset/pkg/trace" "github.com/consensys/go-corset/pkg/util" ) @@ -75,7 +75,7 @@ func (p *ComputedColumn[E]) RequiredSpillage() uint { // ExpandTrace attempts to a new column to the trace which contains the result // of evaluating a given expression on each row. If the column already exists, // then an error is flagged. -func (p *ComputedColumn[E]) ExpandTrace(tr tr.Trace) error { +func (p *ComputedColumn[E]) ExpandTrace(tr trace.Trace) error { if tr.HasColumn(p.name) { return fmt.Errorf("Computed column already exists ({%s})", p.name) } @@ -96,7 +96,7 @@ func (p *ComputedColumn[E]) ExpandTrace(tr tr.Trace) error { // the padding value for *this* column. padding := p.expr.EvalAt(-1, tr) // Colunm needs to be expanded. - tr.AddColumn(p.name, data, padding) + tr.Add(trace.NewFieldColumn(p.name, data, padding)) // Done return nil } diff --git a/pkg/schema/assignment/lexicographic_sort.go b/pkg/schema/assignment/lexicographic_sort.go index 218df22..c05f039 100644 --- a/pkg/schema/assignment/lexicographic_sort.go +++ b/pkg/schema/assignment/lexicographic_sort.go @@ -85,8 +85,8 @@ func (p *LexicographicSort) ExpandTrace(tr trace.Trace) error { delta[i] = &zero // Decide which row is the winner (if any) for j := 0; j < ncols; j++ { - prev := tr.ColumnByIndex(p.sources[j]).Get(i - 1) - curr := tr.ColumnByIndex(p.sources[j]).Get(i) + prev := tr.Column(p.sources[j]).Get(i - 1) + curr := tr.Column(p.sources[j]).Get(i) if !set && prev != nil && prev.Cmp(curr) != 0 { var diff fr.Element @@ -108,11 +108,11 @@ func (p *LexicographicSort) ExpandTrace(tr trace.Trace) error { } } // Add delta column data - tr.AddColumn(p.targets[0].Name(), delta, &zero) + tr.Add(trace.NewFieldColumn(p.targets[0].Name(), delta, &zero)) // Add bit column data for i := 0; i < ncols; i++ { bitName := p.targets[1+i].Name() - tr.AddColumn(bitName, bit[i], &zero) + tr.Add(trace.NewFieldColumn(bitName, bit[i], &zero)) } // Done. return nil diff --git a/pkg/schema/assignment/sorted_permutation.go b/pkg/schema/assignment/sorted_permutation.go index 1761690..e7a2836 100644 --- a/pkg/schema/assignment/sorted_permutation.go +++ b/pkg/schema/assignment/sorted_permutation.go @@ -5,6 +5,7 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" "github.com/consensys/go-corset/pkg/schema" + "github.com/consensys/go-corset/pkg/trace" tr "github.com/consensys/go-corset/pkg/trace" "github.com/consensys/go-corset/pkg/util" ) @@ -119,7 +120,7 @@ func (p *SortedPermutation) ExpandTrace(tr tr.Trace) error { for i := 0; i < len(p.sources); i++ { src := p.sources[i] // Read column data to initialise permutation. - data := tr.ColumnByIndex(src).Data() + data := tr.Column(src).Data() // Copy column data to initialise permutation. cols[i] = make([]*fr.Element, len(data)) copy(cols[i], data) @@ -131,8 +132,8 @@ func (p *SortedPermutation) ExpandTrace(tr tr.Trace) error { for i := p.Columns(); i.HasNext(); { dstColName := i.Next().Name() - srcCol := tr.ColumnByIndex(p.sources[index]) - tr.AddColumn(dstColName, cols[index], srcCol.Padding()) + srcCol := tr.Column(p.sources[index]) + tr.Add(trace.NewFieldColumn(dstColName, cols[index], srcCol.Padding())) index++ } diff --git a/pkg/schema/constraint/permutation.go b/pkg/schema/constraint/permutation.go index aa2d274..980b591 100644 --- a/pkg/schema/constraint/permutation.go +++ b/pkg/schema/constraint/permutation.go @@ -77,7 +77,7 @@ func sliceColumns(columns []uint, tr trace.Trace) [][]*fr.Element { cols := make([][]*fr.Element, len(columns)) // Slice out the data for i, n := range columns { - nth := tr.ColumnByIndex(n) + nth := tr.Column(n) cols[i] = nth.Data() } // Done diff --git a/pkg/schema/constraint/range.go b/pkg/schema/constraint/range.go index 78a69b7..b78636a 100644 --- a/pkg/schema/constraint/range.go +++ b/pkg/schema/constraint/range.go @@ -37,7 +37,7 @@ func NewRangeConstraint(column uint, bound *fr.Element) *RangeConstraint { // Accepts checks whether a range constraint evaluates to zero on // every row of a table. If so, return nil otherwise return an error. func (p *RangeConstraint) Accepts(tr trace.Trace) error { - column := tr.ColumnByIndex(p.column) + column := tr.Column(p.column) for k := 0; k < int(tr.Height()); k++ { // Get the value on the kth row kth := column.Get(k) diff --git a/pkg/schema/constraint/type.go b/pkg/schema/constraint/type.go index d009ece..4d8bc63 100644 --- a/pkg/schema/constraint/type.go +++ b/pkg/schema/constraint/type.go @@ -39,7 +39,7 @@ func (p *TypeConstraint) Type() schema.Type { // Accepts checks whether a range constraint evaluates to zero on // every row of a table. If so, return nil otherwise return an error. func (p *TypeConstraint) Accepts(tr trace.Trace) error { - column := tr.ColumnByIndex(p.column) + column := tr.Column(p.column) for k := 0; k < int(tr.Height()); k++ { // Get the value on the kth row kth := column.Get(k) diff --git a/pkg/schema/schemas.go b/pkg/schema/schemas.go index 8c3c95c..67289ba 100644 --- a/pkg/schema/schemas.go +++ b/pkg/schema/schemas.go @@ -1,8 +1,6 @@ package schema import ( - "fmt" - tr "github.com/consensys/go-corset/pkg/trace" ) @@ -69,26 +67,3 @@ func ColumnIndexOf(schema Schema, name string) (uint, bool) { return c.Name() == name }) } - -// ColumnByName returns the column with the matching name, or panics if no such -// column exists. -func ColumnByName(schema Schema, name string) Column { - var col Column - // Attempt to determine the index of this column - _, ok := schema.Columns().Find(func(c Column) bool { - col = c - return c.Name() == name - }) - // If we found it, then done. - if ok { - return col - } - // Otherwise panic. - panic(fmt.Sprintf("unknown column %s", name)) -} - -// HasColumn checks whether a column of the given name is declared within the schema. -func HasColumn(schema Schema, name string) bool { - _, ok := ColumnIndexOf(schema, name) - return ok -} diff --git a/pkg/test/ir_test.go b/pkg/test/ir_test.go index cd01504..fc08601 100644 --- a/pkg/test/ir_test.go +++ b/pkg/test/ir_test.go @@ -12,6 +12,7 @@ import ( "github.com/consensys/go-corset/pkg/schema" sc "github.com/consensys/go-corset/pkg/schema" "github.com/consensys/go-corset/pkg/trace" + "github.com/consensys/go-corset/pkg/trace/json" ) // Determines the (relative) location of the test directory. That is @@ -469,7 +470,7 @@ func ReadTracesFile(name string, ext string) []*trace.ArrayTrace { for i, line := range lines { // Parse input line as JSON if line != "" && !strings.HasPrefix(line, ";;") { - tr, err := trace.ParseJsonTrace([]byte(line)) + tr, err := json.FromBytes([]byte(line)) if err != nil { msg := fmt.Sprintf("%s.%s:%d: %s", name, ext, i+1, err) panic(msg) diff --git a/pkg/trace/array_trace.go b/pkg/trace/array_trace.go index 3fd3c31..f86ba43 100644 --- a/pkg/trace/array_trace.go +++ b/pkg/trace/array_trace.go @@ -67,24 +67,11 @@ func (p *ArrayTrace) Columns() []Column { return p.columns } -// ColumnByIndex looks up a column based on its index. -func (p *ArrayTrace) ColumnByIndex(index uint) Column { +// Column looks up a column based on its index. +func (p *ArrayTrace) Column(index uint) Column { return p.columns[index] } -// ColumnByName looks up a column based on its name. If the column doesn't -// exist, then nil is returned. -func (p *ArrayTrace) ColumnByName(name string) Column { - for _, c := range p.columns { - if name == c.Name() { - // Matched column - return c - } - } - - return nil -} - // HasColumn checks whether the trace has a given named column (or not). func (p *ArrayTrace) HasColumn(name string) bool { _, ok := p.ColumnIndex(name) @@ -120,18 +107,7 @@ func (p *ArrayTrace) Add(column Column) { // AddColumn adds a new column of data to this trace. func (p *ArrayTrace) AddColumn(name string, data []*fr.Element, padding *fr.Element) { - // Sanity check the column does not already exist. - if p.HasColumn(name) { - panic("column already exists") - } - // Construct new column - column := FieldColumn{name, data, padding} - // Append it - p.columns = append(p.columns, &column) - // Update maximum height - if uint(len(data)) > p.height { - p.height = uint(len(data)) - } + p.Add(&FieldColumn{name, data, padding}) } // Height determines the maximum height of any column within this trace. diff --git a/pkg/trace/field_column.go b/pkg/trace/field_column.go index ab639e8..54ad7f0 100644 --- a/pkg/trace/field_column.go +++ b/pkg/trace/field_column.go @@ -21,6 +21,11 @@ type FieldColumn struct { padding *fr.Element } +// NewFieldColumn constructs a FieldColumn with the give name, data and padding. +func NewFieldColumn(name string, data []*fr.Element, padding *fr.Element) *FieldColumn { + return &FieldColumn{name, data, padding} +} + // Name returns the name of the given column. func (p *FieldColumn) Name() string { return p.name diff --git a/pkg/trace/json/reader.go b/pkg/trace/json/reader.go new file mode 100644 index 0000000..b5236c6 --- /dev/null +++ b/pkg/trace/json/reader.go @@ -0,0 +1,35 @@ +package json + +import ( + "encoding/json" + "math/big" + + "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" + "github.com/consensys/go-corset/pkg/trace" + "github.com/consensys/go-corset/pkg/util" +) + +// FromBytes parses a trace expressed in JSON notation. For example, {"X": +// [0], "Y": [1]} is a trace containing one row of data each for two columns "X" +// and "Y". +func FromBytes(bytes []byte) (*trace.ArrayTrace, error) { + var zero fr.Element = fr.NewElement((0)) + + var rawData map[string][]*big.Int + // Unmarshall + jsonErr := json.Unmarshal(bytes, &rawData) + if jsonErr != nil { + return nil, jsonErr + } + + trace := trace.EmptyArrayTrace() + + for name, rawInts := range rawData { + // Translate raw bigints into raw field elements + rawElements := util.ToFieldElements(rawInts) + // Add new column to the trace + trace.AddColumn(name, rawElements, &zero) + } + // Done. + return trace, nil +} diff --git a/pkg/trace/json/writer.go b/pkg/trace/json/writer.go index 9bf23b8..8f94cc7 100644 --- a/pkg/trace/json/writer.go +++ b/pkg/trace/json/writer.go @@ -17,7 +17,7 @@ func ToJsonString(tr trace.Trace) string { builder.WriteString(", ") } - ith := tr.ColumnByIndex(i) + ith := tr.Column(i) builder.WriteString("\"") builder.WriteString(ith.Name()) diff --git a/pkg/trace/lt/writer.go b/pkg/trace/lt/writer.go index be99e27..9774a97 100644 --- a/pkg/trace/lt/writer.go +++ b/pkg/trace/lt/writer.go @@ -38,7 +38,7 @@ func WriteBytes(tr trace.Trace, buf io.Writer) error { } // Write header information for i := uint(0); i < ncols; i++ { - col := tr.ColumnByIndex(i) + col := tr.Column(i) // Write name length nameBytes := []byte(col.Name()) nameLen := uint16(len(nameBytes)) @@ -62,7 +62,7 @@ func WriteBytes(tr trace.Trace, buf io.Writer) error { } // Write column data information for i := uint(0); i < ncols; i++ { - col := tr.ColumnByIndex(i) + col := tr.Column(i) if err := col.Write(buf); err != nil { return err } diff --git a/pkg/trace/printer.go b/pkg/trace/printer.go index d3a09df..dde516a 100644 --- a/pkg/trace/printer.go +++ b/pkg/trace/printer.go @@ -28,10 +28,10 @@ func traceColumnData(tr Trace, col uint) []string { n := tr.Height() data := make([]string, n+2) data[0] = fmt.Sprintf("#%d", col) - data[1] = tr.ColumnByIndex(col).Name() + data[1] = tr.Column(col).Name() for row := 0; row < int(n); row++ { - data[row+2] = tr.ColumnByIndex(col).Get(row).String() + data[row+2] = tr.Column(col).Get(row).String() } return data diff --git a/pkg/trace/trace.go b/pkg/trace/trace.go index 2d12cb1..1573155 100644 --- a/pkg/trace/trace.go +++ b/pkg/trace/trace.go @@ -1,12 +1,9 @@ package trace import ( - "encoding/json" "io" - "math/big" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" - "github.com/consensys/go-corset/pkg/util" ) // Column describes an individual column of data within a trace table. @@ -38,14 +35,11 @@ type Column interface { // same height and can be either "data" columns or "computed" columns. type Trace interface { // Add a new column of data - AddColumn(name string, data []*fr.Element, padding *fr.Element) + Add(Column) // Clone creates an identical clone of this trace. Clone() Trace - // ColumnByIndex returns the ith column in this trace. - ColumnByIndex(uint) Column - // ColumnByName returns the data of a given column in order that it can be - // inspected. If the given column does not exist, then nil is returned. - ColumnByName(name string) Column + // Column returns the ith column in this trace. + Column(uint) Column // Determine the index of a particular column in this trace, or return false // if no such column exists. ColumnIndex(name string) (uint, bool) @@ -62,32 +56,3 @@ type Trace interface { // Get the number of columns in this trace. Width() uint } - -// =================================================================== -// JSON Parser -// =================================================================== - -// ParseJsonTrace parses a trace expressed in JSON notation. For example, {"X": -// [0], "Y": [1]} is a trace containing one row of data each for two columns "X" -// and "Y". -func ParseJsonTrace(bytes []byte) (*ArrayTrace, error) { - var zero fr.Element = fr.NewElement((0)) - - var rawData map[string][]*big.Int - // Unmarshall - jsonErr := json.Unmarshal(bytes, &rawData) - if jsonErr != nil { - return nil, jsonErr - } - - trace := EmptyArrayTrace() - - for name, rawInts := range rawData { - // Translate raw bigints into raw field elements - rawElements := util.ToFieldElements(rawInts) - // Add new column to the trace - trace.AddColumn(name, rawElements, &zero) - } - // Done. - return trace, nil -}