Commit 37c44291 authored by Yann Hodique's avatar Yann Hodique Committed by GitHub

fix(kurtosis-devnet): ensure localPrestate is idempotent (#13725)

parent 86632e48
...@@ -110,87 +110,114 @@ type PrestateInfo struct { ...@@ -110,87 +110,114 @@ type PrestateInfo struct {
Hashes map[string]string `json:"hashes"` Hashes map[string]string `json:"hashes"`
} }
func (m *Main) localPrestateOption(dir string) tmpl.TemplateContextOptions { type localPrestateHolder struct {
prestateBuilder := build.NewPrestateBuilder( info *PrestateInfo
build.WithPrestateBaseDir(m.cfg.baseDir), baseDir string
build.WithPrestateDryRun(m.cfg.dryRun), buildDir string
) dryRun bool
builder *build.PrestateBuilder
}
return tmpl.WithFunction("localPrestate", func() (*PrestateInfo, error) { func newLocalPrestateHolder(baseDir string, buildDir string, dryRun bool) *localPrestateHolder {
prestatePath := []string{"proofs", "op-program", "cannon"} return &localPrestateHolder{
prestateURL := fileserverURL(prestatePath...) baseDir: baseDir,
buildDir: buildDir,
dryRun: dryRun,
builder: build.NewPrestateBuilder(
build.WithPrestateBaseDir(baseDir),
build.WithPrestateDryRun(dryRun),
),
}
}
// Create build directory with the final path structure func (h *localPrestateHolder) GetPrestateInfo() (*PrestateInfo, error) {
buildDir := filepath.Join(append([]string{dir}, prestatePath...)...) if h.info != nil {
if err := os.MkdirAll(buildDir, 0755); err != nil { return h.info, nil
return nil, fmt.Errorf("failed to create prestate build directory: %w", err) }
}
info := &PrestateInfo{ prestatePath := []string{"proofs", "op-program", "cannon"}
URL: prestateURL, prestateURL := fileserverURL(prestatePath...)
Hashes: make(map[string]string),
}
if m.cfg.dryRun { // Create build directory with the final path structure
return info, nil buildDir := filepath.Join(append([]string{h.buildDir}, prestatePath...)...)
} if err := os.MkdirAll(buildDir, 0755); err != nil {
return nil, fmt.Errorf("failed to create prestate build directory: %w", err)
}
info := &PrestateInfo{
URL: prestateURL,
Hashes: make(map[string]string),
}
if h.dryRun {
h.info = info
return info, nil
}
// Map of known file prefixes to their keys
fileToKey := map[string]string{
"prestate-proof.json": "prestate",
"prestate-proof-mt64.json": "prestate-mt64",
"prestate-proof-mt.json": "prestate-mt",
}
// Build all prestate files directly in the target directory
if err := h.builder.Build(buildDir); err != nil {
return nil, fmt.Errorf("failed to build prestates: %w", err)
}
// Map of known file prefixes to their keys // Find and process all prestate files
fileToKey := map[string]string{ matches, err := filepath.Glob(filepath.Join(buildDir, "prestate-proof*.json"))
"prestate-proof.json": "prestate", if err != nil {
"prestate-proof-mt64.json": "prestate-mt64", return nil, fmt.Errorf("failed to find prestate files: %w", err)
"prestate-proof-mt.json": "prestate-mt", }
// Process each file to rename it to its hash
for _, filePath := range matches {
content, err := os.ReadFile(filePath)
if err != nil {
return nil, fmt.Errorf("failed to read prestate %s: %w", filepath.Base(filePath), err)
} }
// Build all prestate files directly in the target directory var data struct {
if err := prestateBuilder.Build(buildDir); err != nil { Pre string `json:"pre"`
return nil, fmt.Errorf("failed to build prestates: %w", err) }
if err := json.Unmarshal(content, &data); err != nil {
return nil, fmt.Errorf("failed to parse prestate %s: %w", filepath.Base(filePath), err)
} }
// Find and process all prestate files // Store hash with its corresponding key
matches, err := filepath.Glob(filepath.Join(buildDir, "prestate-proof*.json")) if key, exists := fileToKey[filepath.Base(filePath)]; exists {
if err != nil { info.Hashes[key] = data.Pre
return nil, fmt.Errorf("failed to find prestate files: %w", err)
} }
// Process each file to rename it to its hash // Rename files to hash-based names
for _, filePath := range matches { newFileName := data.Pre + ".json"
content, err := os.ReadFile(filePath) hashedPath := filepath.Join(buildDir, newFileName)
if err != nil { if err := os.Rename(filePath, hashedPath); err != nil {
return nil, fmt.Errorf("failed to read prestate %s: %w", filepath.Base(filePath), err) return nil, fmt.Errorf("failed to rename prestate %s: %w", filepath.Base(filePath), err)
}
var data struct {
Pre string `json:"pre"`
}
if err := json.Unmarshal(content, &data); err != nil {
return nil, fmt.Errorf("failed to parse prestate %s: %w", filepath.Base(filePath), err)
}
// Store hash with its corresponding key
if key, exists := fileToKey[filepath.Base(filePath)]; exists {
info.Hashes[key] = data.Pre
}
// Rename files to hash-based names
newFileName := data.Pre + ".json"
hashedPath := filepath.Join(buildDir, newFileName)
if err := os.Rename(filePath, hashedPath); err != nil {
return nil, fmt.Errorf("failed to rename prestate %s: %w", filepath.Base(filePath), err)
}
log.Printf("%s available at: %s/%s\n", filepath.Base(filePath), prestateURL, newFileName)
// Rename the corresponding binary file
binFilePath := strings.Replace(strings.TrimSuffix(filePath, ".json"), "-proof", "", 1) + ".bin.gz"
newBinFileName := data.Pre + ".bin.gz"
binHashedPath := filepath.Join(buildDir, newBinFileName)
if err := os.Rename(binFilePath, binHashedPath); err != nil {
return nil, fmt.Errorf("failed to rename prestate %s: %w", filepath.Base(binFilePath), err)
}
log.Printf("%s available at: %s/%s\n", filepath.Base(binFilePath), prestateURL, newBinFileName)
} }
log.Printf("%s available at: %s/%s\n", filepath.Base(filePath), prestateURL, newFileName)
// Rename the corresponding binary file
binFilePath := strings.Replace(strings.TrimSuffix(filePath, ".json"), "-proof", "", 1) + ".bin.gz"
newBinFileName := data.Pre + ".bin.gz"
binHashedPath := filepath.Join(buildDir, newBinFileName)
if err := os.Rename(binFilePath, binHashedPath); err != nil {
return nil, fmt.Errorf("failed to rename prestate %s: %w", filepath.Base(binFilePath), err)
}
log.Printf("%s available at: %s/%s\n", filepath.Base(binFilePath), prestateURL, newBinFileName)
}
return info, nil h.info = info
return info, nil
}
func (m *Main) localPrestateOption(dir string) tmpl.TemplateContextOptions {
holder := newLocalPrestateHolder(m.cfg.baseDir, dir, m.cfg.dryRun)
return tmpl.WithFunction("localPrestate", func() (*PrestateInfo, error) {
return holder.GetPrestateInfo()
}) })
} }
......
...@@ -15,6 +15,7 @@ import ( ...@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"gopkg.in/yaml.v3"
) )
type mockDeployer struct { type mockDeployer struct {
...@@ -290,8 +291,17 @@ _prestate-build target: ...@@ -290,8 +291,17 @@ _prestate-build target:
// Create template context with just the prestate function // Create template context with just the prestate function
tmplCtx := tmpl.NewTemplateContext(m.localPrestateOption(tmpDir)) tmplCtx := tmpl.NewTemplateContext(m.localPrestateOption(tmpDir))
// Test template // Test template with multiple calls to localPrestate
template := `prestate_url: {{(localPrestate).URL}}` template := `first:
url: {{(localPrestate).URL}}
hashes:
game: {{index (localPrestate).Hashes "game"}}
proof: {{index (localPrestate).Hashes "proof"}}
second:
url: {{(localPrestate).URL}}
hashes:
game: {{index (localPrestate).Hashes "game"}}
proof: {{index (localPrestate).Hashes "proof"}}`
buf := bytes.NewBuffer(nil) buf := bytes.NewBuffer(nil)
err = tmplCtx.InstantiateTemplate(bytes.NewBufferString(template), buf) err = tmplCtx.InstantiateTemplate(bytes.NewBufferString(template), buf)
...@@ -305,7 +315,25 @@ _prestate-build target: ...@@ -305,7 +315,25 @@ _prestate-build target:
output := buf.String() output := buf.String()
assert.Contains(t, output, "url: http://fileserver/proofs/op-program/cannon") assert.Contains(t, output, "url: http://fileserver/proofs/op-program/cannon")
// Verify the directory was created // Verify both calls return the same values
var result struct {
First struct {
URL string `yaml:"url"`
Hashes map[string]string `yaml:"hashes"`
} `yaml:"first"`
Second struct {
URL string `yaml:"url"`
Hashes map[string]string `yaml:"hashes"`
} `yaml:"second"`
}
err = yaml.Unmarshal(buf.Bytes(), &result)
require.NoError(t, err)
// Check that both calls returned identical results
assert.Equal(t, result.First.URL, result.Second.URL, "URLs should match")
assert.Equal(t, result.First.Hashes, result.Second.Hashes, "Hashes should match")
// Verify the directory was created only once
prestateDir := filepath.Join(tmpDir, "proofs", "op-program", "cannon") prestateDir := filepath.Join(tmpDir, "proofs", "op-program", "cannon")
assert.DirExists(t, prestateDir) assert.DirExists(t, prestateDir)
}) })
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment