Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions kurtosis-devnet/op-program-svc/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ARG BASE_VERSION=latest
ARG BASE_IMAGE=op-program-base:latest

FROM golang:1.22.7-alpine3.20 AS builder

Expand All @@ -9,7 +9,7 @@ RUN go mod init op-program-svc
RUN go build -o op-program-svc .


FROM op-program-base:${BASE_VERSION} AS svc
FROM ${BASE_IMAGE} AS svc

ARG GIT_COMMIT
ARG GIT_DATE
Expand Down
73 changes: 70 additions & 3 deletions kurtosis-devnet/op-program-svc/fs.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"bytes"
"encoding/json"
"errors"
"fmt"
Expand Down Expand Up @@ -29,6 +30,53 @@ type proofFile struct {
file File
}

// infoFile implements http.File for the virtual info.json file
type infoFile struct {
*bytes.Reader
content []byte
}

func newInfoFile(proofFiles map[string]string) *infoFile {
// Create inverted map
invertedMap := make(map[string]string)
for hash, variablePart := range proofFiles {
// Replace dashes with underscores in the key
key := fmt.Sprintf("prestate%s", variablePart)
key = strings.ReplaceAll(key, "-", "_")
invertedMap[key] = hash
}

// Convert to JSON
content, err := json.MarshalIndent(invertedMap, "", " ")
if err != nil {
// Fallback to empty JSON object if marshaling fails
content = []byte("{}")
}

return &infoFile{
Reader: bytes.NewReader(content),
content: content,
}
}

func (f *infoFile) Close() error {
return nil
}

func (f *infoFile) Readdir(count int) ([]fs.FileInfo, error) {
return nil, fmt.Errorf("not a directory")
}

func (f *infoFile) Stat() (fs.FileInfo, error) {
return virtualFileInfo{
name: "info.json",
size: int64(len(f.content)),
mode: 0644,
modTime: time.Now(),
isDir: false,
}, nil
}

func (f *proofFile) Close() error {
return f.file.Close()
}
Expand Down Expand Up @@ -73,7 +121,7 @@ func (d *proofDir) Readdir(count int) ([]fs.FileInfo, error) {
defer d.proofMutex.RUnlock()

// If we've already read all entries
if d.pos >= len(d.proofFiles)*2 {
if d.pos >= len(d.proofFiles)*2+1 {
if count <= 0 {
return nil, nil
}
Expand All @@ -89,11 +137,23 @@ func (d *proofDir) Readdir(count int) ([]fs.FileInfo, error) {

start := d.pos
end := start + count
if count <= 0 || end > len(d.proofFiles)*2 {
end = len(d.proofFiles) * 2
if count <= 0 || end > len(d.proofFiles)*2+1 {
end = len(d.proofFiles)*2 + 1
}

for i := start; i < end; i++ {
// Special case for info.json (last entry)
if i == len(d.proofFiles)*2 {
entries = append(entries, virtualFileInfo{
name: "info.json",
size: 0, // Size will be determined when actually opening the file
mode: 0644,
modTime: time.Now(),
isDir: false,
})
continue
}

hash := hashes[i/2]
isJSON := i%2 == 0

Expand Down Expand Up @@ -167,6 +227,13 @@ func (fs *proofFileSystem) Open(name string) (http.File, error) {
// Clean the path and remove leading slash
name = strings.TrimPrefix(filepath.Clean(name), "/")

// Special case for info.json
if name == "info.json" {
fs.proofMutex.RLock()
defer fs.proofMutex.RUnlock()
return newInfoFile(fs.proofFiles), nil
}

fs.proofMutex.RLock()
defer fs.proofMutex.RUnlock()

Expand Down
44 changes: 42 additions & 2 deletions kurtosis-devnet/op-program-svc/fs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,33 @@ func TestProofFileSystem(t *testing.T) {
}
})

t.Run("OpenInfoJSONFile", func(t *testing.T) {
file, err := pfs.Open("/info.json")
if err != nil {
t.Errorf("Failed to open info.json file: %v", err)
}
defer file.Close()

// Read contents
contents, err := io.ReadAll(file)
if err != nil {
t.Errorf("Failed to read file contents: %v", err)
}

// Verify the contents contain the inverted map
var infoData map[string]string
err = json.Unmarshal(contents, &infoData)
if err != nil {
t.Errorf("Failed to parse info.json contents: %v", err)
}

// Check that the key has dashes replaced with underscores
expectedKey := "prestate_test"
if hash, ok := infoData[expectedKey]; !ok || hash != "hash123" {
t.Errorf("Expected info.json to contain mapping from %s to hash123, got %v", expectedKey, hash)
}
})

t.Run("OpenNonExistentFile", func(t *testing.T) {
_, err := pfs.Open("/nonexistent.json")
if err == nil {
Expand All @@ -97,8 +124,21 @@ func TestProofFileSystem(t *testing.T) {
t.Errorf("Failed to read directory: %v", err)
}

if len(files) != 2 { // We expect both .json and .bin.gz files
t.Errorf("Expected 2 files, got %d", len(files))
// We expect both .json and .bin.gz files for hash123, plus info.json
if len(files) != 3 {
t.Errorf("Expected 3 files, got %d", len(files))
}

// Verify info.json is included in the directory listing
foundInfoJson := false
for _, file := range files {
if file.Name() == "info.json" {
foundInfoJson = true
break
}
}
if !foundInfoJson {
t.Error("info.json not found in directory listing")
}
})
}
13 changes: 11 additions & 2 deletions kurtosis-devnet/op-program-svc/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,18 @@ func main() {

srv := createServer()

handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
http.FileServer(srv.proofFS).ServeHTTP(w, r)
case http.MethodPost:
srv.handleUpload(w, r)
default:
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
})
// Set up routes
http.HandleFunc("/", srv.handleUpload)
http.Handle("/proofs/", http.StripPrefix("/proofs/", http.FileServer(srv.proofFS)))
http.HandleFunc("/", handler)

log.Printf("Starting server on :%d with:", srv.port)
log.Printf(" app-root: %s", srv.appRoot)
Expand Down