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 {
Hashes map[string]string `json:"hashes"`
}
func (m *Main) localPrestateOption(dir string) tmpl.TemplateContextOptions {
prestateBuilder := build.NewPrestateBuilder(
build.WithPrestateBaseDir(m.cfg.baseDir),
build.WithPrestateDryRun(m.cfg.dryRun),
)
type localPrestateHolder struct {
info *PrestateInfo
baseDir string
buildDir string
dryRun bool
builder *build.PrestateBuilder
}
return tmpl.WithFunction("localPrestate", func() (*PrestateInfo, error) {
prestatePath := []string{"proofs", "op-program", "cannon"}
prestateURL := fileserverURL(prestatePath...)
func newLocalPrestateHolder(baseDir string, buildDir string, dryRun bool) *localPrestateHolder {
return &localPrestateHolder{
baseDir: baseDir,
buildDir: buildDir,
dryRun: dryRun,
builder: build.NewPrestateBuilder(
build.WithPrestateBaseDir(baseDir),
build.WithPrestateDryRun(dryRun),
),
}
}
// Create build directory with the final path structure
buildDir := filepath.Join(append([]string{dir}, prestatePath...)...)
if err := os.MkdirAll(buildDir, 0755); err != nil {
return nil, fmt.Errorf("failed to create prestate build directory: %w", err)
}
func (h *localPrestateHolder) GetPrestateInfo() (*PrestateInfo, error) {
if h.info != nil {
return h.info, nil
}
info := &PrestateInfo{
URL: prestateURL,
Hashes: make(map[string]string),
}
prestatePath := []string{"proofs", "op-program", "cannon"}
prestateURL := fileserverURL(prestatePath...)
if m.cfg.dryRun {
return info, nil
}
// Create build directory with the final path structure
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
fileToKey := map[string]string{
"prestate-proof.json": "prestate",
"prestate-proof-mt64.json": "prestate-mt64",
"prestate-proof-mt.json": "prestate-mt",
// Find and process all prestate files
matches, err := filepath.Glob(filepath.Join(buildDir, "prestate-proof*.json"))
if err != nil {
return nil, fmt.Errorf("failed to find prestate files: %w", err)
}
// 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
if err := prestateBuilder.Build(buildDir); err != nil {
return nil, fmt.Errorf("failed to build prestates: %w", 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)
}
// Find and process all prestate files
matches, err := filepath.Glob(filepath.Join(buildDir, "prestate-proof*.json"))
if err != nil {
return nil, fmt.Errorf("failed to find prestate files: %w", err)
// Store hash with its corresponding key
if key, exists := fileToKey[filepath.Base(filePath)]; exists {
info.Hashes[key] = data.Pre
}
// 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)
}
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)
// 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)
}
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 (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
"gopkg.in/yaml.v3"
)
type mockDeployer struct {
......@@ -290,8 +291,17 @@ _prestate-build target:
// Create template context with just the prestate function
tmplCtx := tmpl.NewTemplateContext(m.localPrestateOption(tmpDir))
// Test template
template := `prestate_url: {{(localPrestate).URL}}`
// Test template with multiple calls to localPrestate
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)
err = tmplCtx.InstantiateTemplate(bytes.NewBufferString(template), buf)
......@@ -305,7 +315,25 @@ _prestate-build target:
output := buf.String()
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")
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