Skip to content

Commit 2b6904a

Browse files
authored
feat: support versioned lt file format (#733)
* support versioned lt file format This updates the LT file format to a versioned format which supports embedded metadata. The original legacy format is still supported. * support metadata for trace file generation This adds support for reading and writing embedded metadata (and other versioning information) to the trace file.
1 parent 530a5b9 commit 2b6904a

File tree

12 files changed

+436
-56
lines changed

12 files changed

+436
-56
lines changed

pkg/binfile/binfile.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ type BinaryFile struct {
4040

4141
// NewBinaryFile constructs a new binary file with the default header for the
4242
// currently supported version.
43-
func NewBinaryFile(headerdata []byte, attributes []Attribute, schema *hir.Schema) *BinaryFile {
43+
func NewBinaryFile(metadata []byte, attributes []Attribute, schema *hir.Schema) *BinaryFile {
4444
return &BinaryFile{
45-
Header{ZKBINARY, BINFILE_MAJOR_VERSION, BINFILE_MINOR_VERSION, headerdata},
45+
Header{ZKBINARY, BINFILE_MAJOR_VERSION, BINFILE_MINOR_VERSION, metadata},
4646
attributes,
4747
*schema,
4848
}

pkg/cmd/check.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,8 @@ var checkCmd = &cobra.Command{
102102
traces = ReadBatchedTraceFile(args[0])
103103
} else {
104104
// unbatched (i.e. normal) mode
105-
columns := ReadTraceFile(args[0])
106-
traces = [][]trace.RawColumn{columns}
105+
tracefile := ReadTraceFile(args[0])
106+
traces = [][]trace.RawColumn{tracefile.Columns}
107107
}
108108
//
109109
stats.Log("Reading trace file")

pkg/cmd/debug.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ var debugCmd = &cobra.Command{
6767
binfile := ReadConstraintFiles(stdlib, debug, legacy, args)
6868
// Print meta-data (if requested)
6969
if metadata {
70-
printMetadata(&binfile.Header)
70+
printBinaryFileMetadata(&binfile.Header)
7171
}
7272
// Print stats (if requested)
7373
if stats {
@@ -166,7 +166,7 @@ func printStats(hirSchema *hir.Schema, hir bool, mir bool, air bool, optConfig m
166166
tbl.Print()
167167
}
168168

169-
func printMetadata(header *binfile.Header) {
169+
func printBinaryFileMetadata(header *binfile.Header) {
170170
fmt.Printf("Format: %d.%d\n", header.MajorVersion, header.MinorVersion)
171171
// Attempt to parse metadata
172172
metadata, err := header.GetMetaData()

pkg/cmd/generate.go

Lines changed: 105 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,19 @@ func generateJavaIntegration(filename string, pkgname string, srcmap *corset.Sou
7878
}
7979
// Strip suffix to determine classname
8080
classname := strings.TrimSuffix(basename, ".java")
81+
metadata, err := binfile.Header.GetMetaData()
82+
// Error check
83+
if err != nil {
84+
return "", err
85+
}
8186
// begin generation
82-
generateJavaHeader(pkgname, &builder)
83-
generateJavaModule(classname, srcmap.Root, &binfile.Schema, indentBuilder{0, &builder})
87+
generateJavaHeader(pkgname, metadata, &builder)
88+
generateJavaModule(classname, srcmap.Root, metadata, &binfile.Schema, indentBuilder{0, &builder})
8489
//
8590
return builder.String(), nil
8691
}
8792

88-
func generateJavaHeader(pkgname string, builder *strings.Builder) {
93+
func generateJavaHeader(pkgname string, metadata map[string]string, builder *strings.Builder) {
8994
builder.WriteString(license)
9095
// Write package line
9196
if pkgname != "" {
@@ -94,9 +99,29 @@ func generateJavaHeader(pkgname string, builder *strings.Builder) {
9499
//
95100
builder.WriteString(javaImports)
96101
builder.WriteString(javaWarning)
102+
//
103+
if len(metadata) > 0 {
104+
// Write embedded metadata for the record.
105+
builder.WriteString(" * <p>Embedded Metata</p>\n")
106+
builder.WriteString(" * <ul>\n")
107+
//
108+
for k, v := range metadata {
109+
builder.WriteString(" * <li>")
110+
builder.WriteString(k)
111+
builder.WriteString(": ")
112+
builder.WriteString(v)
113+
builder.WriteString("</li>\n")
114+
}
115+
//
116+
builder.WriteString(" * </ul>\n")
117+
}
118+
//
119+
builder.WriteString(" */\n")
97120
}
98121

99-
func generateJavaModule(className string, mod corset.SourceModule, schema *hir.Schema, builder indentBuilder) {
122+
func generateJavaModule(className string, mod corset.SourceModule, metadata map[string]string, schema *hir.Schema,
123+
builder indentBuilder) {
124+
//
100125
var nFields uint
101126
// Attempt to find module
102127
mid, ok := schema.Modules().Find(func(m sc.Module) bool { return m.Name == mod.Name })
@@ -108,6 +133,11 @@ func generateJavaModule(className string, mod corset.SourceModule, schema *hir.S
108133
generateJavaClassHeader(mod.Name == "", className, builder)
109134
generateJavaModuleConstants(mod.Constants, builder.Indent())
110135
generateJavaModuleSubmoduleFields(mod.Submodules, builder.Indent())
136+
//
137+
if mod.Name == "" {
138+
generateJavaModuleMetadata(metadata, builder.Indent())
139+
}
140+
//
111141
generateJavaModuleHeaders(mid, mod, schema, builder.Indent())
112142
//
113143
if nFields = generateJavaModuleRegisterFields(mid, schema, builder.Indent()); nFields > 0 {
@@ -125,7 +155,7 @@ func generateJavaModule(className string, mod corset.SourceModule, schema *hir.S
125155
// Generate any submodules
126156
for _, submod := range mod.Submodules {
127157
if !submod.Virtual {
128-
generateJavaModule(toPascalCase(submod.Name), submod, schema, builder.Indent())
158+
generateJavaModule(toPascalCase(submod.Name), submod, metadata, schema, builder.Indent())
129159
} else {
130160
generateJavaModuleColumnSetters(className, submod, schema, builder.Indent())
131161
}
@@ -323,6 +353,23 @@ func generateJavaModuleSubmoduleFields(submodules []corset.SourceModule, builder
323353
}
324354
}
325355

356+
func generateJavaModuleMetadata(metadata map[string]string, builder indentBuilder) {
357+
// Write field declaration
358+
builder.WriteIndentedString("public static Map<String,String> metadata() {\n")
359+
// Initialise map using Java static initialiser
360+
if len(metadata) > 0 {
361+
i1Builder := builder.Indent()
362+
i1Builder.WriteIndentedString("Map<String,String> metadata = new HashMap<>();\n")
363+
364+
for k, v := range metadata {
365+
i1Builder.WriteIndentedString("metadata.put(\"", k, "\",\"", v, "\");\n")
366+
}
367+
//
368+
i1Builder.WriteIndentedString("return metadata;\n")
369+
builder.WriteIndentedString("}\n\n")
370+
}
371+
}
372+
326373
func generateJavaModuleConstructor(classname string, mid uint, mod corset.SourceModule,
327374
schema *hir.Schema, builder indentBuilder) {
328375
//
@@ -686,24 +733,28 @@ const javaWarning string = `
686733
* WARNING: This code is generated automatically.
687734
*
688735
* <p>Any modifications to this code may be overwritten and could lead to unexpected behavior.
689-
* Please DO NOT ATTEMPT TO MODIFY this code directly.
690-
*/
736+
* Please DO NOT ATTEMPT TO MODIFY this code directly</p>.
737+
*
691738
`
692739

693740
const javaImports string = `
694741
import java.io.IOException;
695742
import java.io.RandomAccessFile;
696743
import java.math.BigInteger;
744+
import java.nio.ByteBuffer;
697745
import java.nio.MappedByteBuffer;
698746
import java.nio.channels.FileChannel;
699747
import java.util.ArrayList;
700748
import java.util.BitSet;
749+
import java.util.HashMap;
701750
import java.util.List;
751+
import java.util.Map;
702752
703753
import net.consensys.linea.zktracer.types.UnsignedByte;
704754
import org.apache.tuweni.bytes.Bytes;
705755
`
706756

757+
// nolint
707758
const javaTraceOf string = `
708759
/**
709760
* Construct a new trace which will be written to a given file.
@@ -713,21 +764,46 @@ const javaTraceOf string = `
713764
*
714765
* @throws IOException If an I/O error occurs.
715766
*/
716-
public static Trace of(RandomAccessFile file, List<ColumnHeader> rawHeaders) throws IOException {
717-
// First align headers according to register indices.
718-
ColumnHeader[] headers = alignHeaders(rawHeaders);
719-
// Second determine file size
720-
long headerSize = determineHeaderSize(headers);
721-
long dataSize = determineHeaderSize(headers);
767+
public static Trace of(RandomAccessFile file, List<ColumnHeader> rawHeaders, byte[] metadata) throws IOException {
768+
// Construct trace file header bytes
769+
byte[] header = constructTraceFileHeader(metadata);
770+
// Align headers according to register indices.
771+
ColumnHeader[] columnHeaders = alignHeaders(rawHeaders);
772+
// Determine file size
773+
long headerSize = determineColumnHeadersSize(columnHeaders) + header.length;
774+
long dataSize = determineColumnDataSize(columnHeaders);
722775
file.setLength(headerSize + dataSize);
723-
// Write header
724-
writeHeader(file,headers,headerSize);
776+
// Write headers
777+
writeHeaders(file,header,columnHeaders,headerSize);
725778
// Initialise buffers
726-
MappedByteBuffer[] buffers = initialiseByteBuffers(file,headers,headerSize);
779+
MappedByteBuffer[] buffers = initialiseByteBuffers(file,columnHeaders,headerSize);
727780
// Done
728781
return new Trace(buffers);
729782
}
730783
784+
/**
785+
* Construct trace file header containing the given metadata bytes.
786+
*
787+
* @param metadata Metadata bytes to be embedded in the trace file.
788+
*
789+
* @return bytes making up the header.
790+
*/
791+
private static byte[] constructTraceFileHeader(byte[] metadata) {
792+
ByteBuffer buffer = ByteBuffer.allocate(16 + metadata.length);
793+
// File identifier
794+
buffer.put(new byte[]{'z','k','t','r','a','c','e','r'});
795+
// Major version
796+
buffer.putShort((short) 1);
797+
// Minor version
798+
buffer.putShort((short) 0);
799+
// Metadata length
800+
buffer.putInt(metadata.length);
801+
// Metadata
802+
buffer.put(metadata);
803+
// Done
804+
return buffer.array();
805+
}
806+
731807
/**
732808
* Align headers ensures that the order in which columns are seen matches the order found in the trace schema.
733809
*
@@ -750,7 +826,7 @@ const javaTraceOf string = `
750826
* @param headers Set of headers for the columns being written.
751827
* @return Number of bytes requires for the trace file header.
752828
*/
753-
private static long determineHeaderSize(ColumnHeader[] headers) {
829+
private static long determineColumnHeadersSize(ColumnHeader[] headers) {
754830
long nBytes = 4; // column count
755831
756832
for (ColumnHeader header : headers) {
@@ -769,7 +845,7 @@ const javaTraceOf string = `
769845
* @param headers Set of headers for the columns being written.
770846
* @return Number of bytes required for storing all column data, excluding the header.
771847
*/
772-
private static long determineDataSize(ColumnHeader[] headers) {
848+
private static long determineColumnDataSize(ColumnHeader[] headers) {
773849
long nBytes = 0;
774850
775851
for (ColumnHeader header : headers) {
@@ -781,20 +857,24 @@ const javaTraceOf string = `
781857
782858
/**
783859
* Write header information for the trace file.
860+
*
784861
* @param file Trace file being written.
862+
* @param header Trace file header
785863
* @param headers Column headers.
786864
* @param size Overall size of the header.
787865
*/
788-
private static void writeHeader(RandomAccessFile file, ColumnHeader[] headers, long size) throws IOException {
789-
final var header = file.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, size);
866+
private static void writeHeaders(RandomAccessFile file, byte[] header, ColumnHeader[] headers, long size) throws IOException {
867+
final var buffer = file.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, size);
868+
// Write trace file header
869+
buffer.put(header);
790870
// Write column count as uint32
791-
header.putInt(headers.length);
871+
buffer.putInt(headers.length);
792872
// Write column headers one-by-one
793873
for(ColumnHeader h : headers) {
794-
header.putShort((short) h.name.length());
795-
header.put(h.name.getBytes());
796-
header.put((byte) h.bytesPerElement);
797-
header.putInt((int) h.length);
874+
buffer.putShort((short) h.name.length());
875+
buffer.put(h.name.getBytes());
876+
buffer.put((byte) h.bytesPerElement);
877+
buffer.putInt((int) h.length);
798878
}
799879
}
800880

pkg/cmd/inspect.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,13 @@ var inspectCmd = &cobra.Command{
5050
//
5151
stats.Log("Reading constraints file")
5252
// Parse trace file
53-
columns := ReadTraceFile(args[0])
53+
tracefile := ReadTraceFile(args[0])
5454
//
5555
stats.Log("Reading trace file")
5656
//
5757
builder := sc.NewTraceBuilder(&binf.Schema).Expand(true).Defensive(defensive).Parallel(true)
5858
//
59-
trace, errors := builder.Build(columns)
59+
trace, errors := builder.Build(tracefile.Columns)
6060
//
6161
if len(errors) == 0 {
6262
// Run the inspector.

pkg/cmd/trace.go

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"github.com/consensys/go-corset/pkg/mir"
2424
sc "github.com/consensys/go-corset/pkg/schema"
2525
"github.com/consensys/go-corset/pkg/trace"
26+
"github.com/consensys/go-corset/pkg/trace/lt"
2627
"github.com/consensys/go-corset/pkg/util"
2728
"github.com/consensys/go-corset/pkg/util/collection/hash"
2829
"github.com/consensys/go-corset/pkg/util/collection/set"
@@ -71,14 +72,19 @@ var traceCmd = &cobra.Command{
7172
mir := GetFlag(cmd, "mir")
7273
hir := GetFlag(cmd, "hir")
7374
batched := GetFlag(cmd, "batched")
75+
metadata := GetFlag(cmd, "metadata")
7476
// Parse trace file(s)
7577
if batched {
7678
// batched mode
7779
traces = ReadBatchedTraceFile(args[0])
7880
} else {
7981
// unbatched (i.e. normal) mode
80-
columns := ReadTraceFile(args[0])
81-
traces = [][]trace.RawColumn{columns}
82+
tracefile := ReadTraceFile(args[0])
83+
traces = [][]trace.RawColumn{tracefile.Columns}
84+
// Print meta-data (if requested)
85+
if metadata {
86+
printTraceFileMetadata(&tracefile.Header)
87+
}
8288
}
8389
//
8490
if expand && !air && !mir && !hir {
@@ -144,6 +150,7 @@ func init() {
144150
traceCmd.Flags().Bool("air", false, "expand to AIR level")
145151
traceCmd.Flags().Bool("batched", false,
146152
"specify trace file is batched (i.e. contains multiple traces, one for each line)")
153+
traceCmd.Flags().Bool("metadata", false, "Print embedded metadata")
147154
}
148155

149156
const air_LEVEL = 0
@@ -254,6 +261,23 @@ func sliceColumns(cols []trace.RawColumn, start uint, end uint) {
254261
}
255262
}
256263

264+
func printTraceFileMetadata(header *lt.Header) {
265+
fmt.Printf("Format: %d.%d\n", header.MajorVersion, header.MinorVersion)
266+
// Attempt to parse metadata
267+
metadata, err := header.GetMetaData()
268+
//
269+
if err != nil {
270+
fmt.Println(err.Error())
271+
os.Exit(1)
272+
} else if metadata != nil {
273+
fmt.Println("Metadata:")
274+
//
275+
for k, v := range metadata {
276+
fmt.Printf("\t%s: %s\n", k, v)
277+
}
278+
}
279+
}
280+
257281
func printTrace(start uint, max_width uint, cols []trace.RawColumn) {
258282
n := uint(len(cols))
259283
height := maxHeightColumns(cols)

pkg/cmd/tracediff.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@ var traceDiffCmd = &cobra.Command{
2626
filename1 := args[0]
2727
filename2 := args[1]
2828
// Read trace files
29-
columns1 := ReadTraceFile(filename1)
30-
columns2 := ReadTraceFile(filename2)
29+
tracefile1 := ReadTraceFile(filename1)
30+
tracefile2 := ReadTraceFile(filename2)
3131
// Sanity check
32-
if len(columns1) != len(columns2) {
33-
fmt.Printf("differing number of columns (%d v %d)", len(columns1), len(columns2))
32+
if len(tracefile1.Columns) != len(tracefile2.Columns) {
33+
fmt.Printf("differing number of columns (%d v %d)", len(tracefile1.Columns), len(tracefile2.Columns))
3434
os.Exit(2)
3535
}
3636
//
37-
errors := parallelDiff(columns1, columns2)
37+
errors := parallelDiff(tracefile1.Columns, tracefile2.Columns)
3838
// report any differences
3939
for _, err := range errors {
4040
fmt.Println(err)

0 commit comments

Comments
 (0)