From 7754dc766c3c26a8694cf5284a84b58714f00fab Mon Sep 17 00:00:00 2001 From: crStiv Date: Fri, 28 Mar 2025 13:59:36 +0100 Subject: [PATCH 1/6] Create main.go --- ops/cmd/check_addresses/main.go | 156 ++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 ops/cmd/check_addresses/main.go diff --git a/ops/cmd/check_addresses/main.go b/ops/cmd/check_addresses/main.go new file mode 100644 index 0000000000..025307a054 --- /dev/null +++ b/ops/cmd/check_addresses/main.go @@ -0,0 +1,156 @@ +package main + +import ( + "flag" + "fmt" + "log" + "os" + "path/filepath" + "regexp" + "strings" + + "github.com/ethereum/go-ethereum/common" +) + +// addressRegex matches Ethereum addresses in the format 0x... +var addressRegex = regexp.MustCompile(`0x[0-9a-fA-F]{40}`) + +// ContractData represents the structure of contract entries in the TOML file +type ContractData struct { + Version string `toml:"version"` + Address string `toml:"address,omitempty"` + ImplementationAddress string `toml:"implementation_address,omitempty"` +} + +// ReleaseData maps contract names to their ContractData +type ReleaseData map[string]ContractData + +// VersionsData maps release versions to their respective ReleaseData +type VersionsData map[string]ReleaseData + +func main() { + // Parse command line arguments + var ( + fixFlag = flag.Bool("fix", false, "Fix incorrect addresses") + allFlag = flag.Bool("all", false, "Check all TOML files in validation directory") + fileFlag = flag.String("file", "", "Check specific file") + ) + flag.Parse() + + // Get the root directory of the repository + rootDir, err := findRepoRoot() + if err != nil { + log.Fatalf("Error finding repository root: %v", err) + } + + // Determine which files to check + var filesToCheck []string + if *allFlag { + err := filepath.Walk(filepath.Join(rootDir, "validation"), func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() && strings.HasSuffix(path, ".toml") { + filesToCheck = append(filesToCheck, path) + } + return nil + }) + if err != nil { + log.Fatalf("Error walking validation directory: %v", err) + } + } else if *fileFlag != "" { + filesToCheck = append(filesToCheck, *fileFlag) + } else { + // Default file + filesToCheck = append(filesToCheck, filepath.Join(rootDir, "validation", "standard", "standard-versions-sepolia.toml")) + } + + // Check each file + totalIncorrect := 0 + for _, file := range filesToCheck { + incorrectCount := checkFile(file, *fixFlag) + totalIncorrect += incorrectCount + } + + // Exit with error code if incorrect addresses were found and not fixed + if totalIncorrect > 0 && !*fixFlag { + os.Exit(1) + } +} + +// checkFile checks a single file for incorrectly checksummed addresses +func checkFile(tomlFile string, fix bool) int { + // Read the TOML file content + content, err := os.ReadFile(tomlFile) + if err != nil { + log.Fatalf("Error reading TOML file %s: %v", tomlFile, err) + } + + // Original content as string + originalContent := string(content) + + // Use regex to find all addresses + addresses := addressRegex.FindAllString(originalContent, -1) + + // Map to keep track of incorrect addresses and their checksummed versions + incorrectAddresses := make(map[string]string) + + // Check each address for correct checksum + for _, addr := range addresses { + checksumAddr := common.HexToAddress(addr).Hex() + if addr != checksumAddr { + incorrectAddresses[addr] = checksumAddr + fmt.Printf("File %s: Incorrect address: %s should be %s\n", tomlFile, addr, checksumAddr) + } + } + + // If no incorrect addresses found, we're done + if len(incorrectAddresses) == 0 { + fmt.Printf("File %s: All addresses are correctly checksummed!\n", tomlFile) + return 0 + } + + fmt.Printf("File %s: Found %d incorrectly checksummed addresses\n", tomlFile, len(incorrectAddresses)) + + // Fix incorrect addresses if requested + if fix { + // Fix incorrect addresses in the content + correctedContent := originalContent + for incorrect, correct := range incorrectAddresses { + correctedContent = strings.ReplaceAll(correctedContent, incorrect, correct) + } + + // Write corrected content back to file + err = os.WriteFile(tomlFile, []byte(correctedContent), 0644) + if err != nil { + log.Fatalf("Error writing corrected content to %s: %v", tomlFile, err) + } + + fmt.Printf("File %s: Fixed %d addresses\n", tomlFile, len(incorrectAddresses)) + } + + return len(incorrectAddresses) +} + +// findRepoRoot tries to find the repository root by looking for .repo-root file +func findRepoRoot() (string, error) { + // Start from the current directory + dir, err := os.Getwd() + if err != nil { + return "", err + } + + // First try: check if we're already in the root (has .repo-root file) + if _, err := os.Stat(filepath.Join(dir, ".repo-root")); err == nil { + return dir, nil + } + + // Second try: go up one level (we might be in the ops directory) + parentDir := filepath.Dir(dir) + if _, err := os.Stat(filepath.Join(parentDir, ".repo-root")); err == nil { + return parentDir, nil + } + + // If both fail, return the best guess (parent directory) + return parentDir, nil +} From f2ef633aec161b1d79f77d2faa0a137297d14209 Mon Sep 17 00:00:00 2001 From: crStiv Date: Fri, 28 Mar 2025 14:00:00 +0100 Subject: [PATCH 2/6] Create README.md --- ops/cmd/check_addresses/README.md | 49 +++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 ops/cmd/check_addresses/README.md diff --git a/ops/cmd/check_addresses/README.md b/ops/cmd/check_addresses/README.md new file mode 100644 index 0000000000..84651a8b91 --- /dev/null +++ b/ops/cmd/check_addresses/README.md @@ -0,0 +1,49 @@ +# Address Checksum Validator + +This tool helps ensure that all Ethereum addresses in TOML files are properly checksummed. + +## What's an Ethereum Checksum Address? + +Ethereum addresses use a mixed-case checksum format (EIP-55) to help detect input errors. The correct format has both uppercase and lowercase letters, and the specific case pattern serves as a checksum. For example: +- `0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed` (correct) +- `0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed` (incorrect, all lowercase) + +Using checksummed addresses helps prevent errors when importing them from Solidity or other systems. + +## Usage + +``` +go run ./cmd/check_addresses/main.go [options] +``` + +### Options + +- `-all`: Check all TOML files in the validation directory (default: only checks standard-versions-sepolia.toml) +- `-fix`: Fix incorrect addresses (default: only reports incorrect addresses) +- `-file=`: Check a specific file (default: checks standard-versions-sepolia.toml) + +### Examples + +Check the default file: +``` +go run ./cmd/check_addresses/main.go +``` + +Check all TOML files: +``` +go run ./cmd/check_addresses/main.go -all +``` + +Check a specific file: +``` +go run ./cmd/check_addresses/main.go -file=path/to/file.toml +``` + +Fix addresses in all TOML files: +``` +go run ./cmd/check_addresses/main.go -all -fix +``` + +## CI Integration + +This tool is integrated into the CI pipeline and will fail builds if it detects incorrectly checksummed addresses. From f85359367ead7963770df9f14e8cdf3ace1b5363 Mon Sep 17 00:00:00 2001 From: crStiv Date: Fri, 28 Mar 2025 14:00:08 +0100 Subject: [PATCH 3/6] Update config.yml --- .circleci/config.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 238d824dc2..2d5a176316 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -153,6 +153,11 @@ workflows: - run-tool: name: check-chainlist tool: check_chainlist + - run-tool: + name: check-addresses + tool: check_addresses + args: "-all" + check_diff: true - run-staging-report: name: run-staging-report From 9a4696d1b55fc6b7206323442972841f05242540 Mon Sep 17 00:00:00 2001 From: crStiv Date: Tue, 15 Apr 2025 16:52:16 +0200 Subject: [PATCH 4/6] Update main.go --- ops/cmd/check_addresses/main.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ops/cmd/check_addresses/main.go b/ops/cmd/check_addresses/main.go index 025307a054..3b68221c89 100644 --- a/ops/cmd/check_addresses/main.go +++ b/ops/cmd/check_addresses/main.go @@ -34,9 +34,16 @@ func main() { fixFlag = flag.Bool("fix", false, "Fix incorrect addresses") allFlag = flag.Bool("all", false, "Check all TOML files in validation directory") fileFlag = flag.String("file", "", "Check specific file") + ciMode = os.Getenv("CI") != "" // Detect if running in CI environment ) flag.Parse() + // In CI mode with check_diff, we should fix files + if ciMode && !*fixFlag { + fmt.Println("Running in CI mode with check_diff, enabling auto-fix") + *fixFlag = true + } + // Get the root directory of the repository rootDir, err := findRepoRoot() if err != nil { From d42ae665f8a6cd23a546ca82276978afda78e085 Mon Sep 17 00:00:00 2001 From: crStiv Date: Tue, 15 Apr 2025 17:00:15 +0200 Subject: [PATCH 5/6] Create test_addresses.toml --- .../testdata/test_addresses.toml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 ops/cmd/check_addresses/testdata/test_addresses.toml diff --git a/ops/cmd/check_addresses/testdata/test_addresses.toml b/ops/cmd/check_addresses/testdata/test_addresses.toml new file mode 100644 index 0000000000..7599912eb1 --- /dev/null +++ b/ops/cmd/check_addresses/testdata/test_addresses.toml @@ -0,0 +1,19 @@ +# Test file for address validation + +# Incorrect addresses (all lowercase) +[version1.contracts.contract1] +version = "1.0.0" +address = "0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed" + +[version1.contracts.contract2] +version = "1.0.0" +implementation_address = "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + +# Correct addresses (checksummed) +[version2.contracts.contract1] +version = "2.0.0" +address = "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed" + +[version2.contracts.contract2] +version = "2.0.0" +implementation_address = "0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa" From 83b2659596e654061cb83b394bc29e4979b24a19 Mon Sep 17 00:00:00 2001 From: crStiv Date: Tue, 15 Apr 2025 17:00:32 +0200 Subject: [PATCH 6/6] Create main_test.go --- ops/cmd/check_addresses/main_test.go | 145 +++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 ops/cmd/check_addresses/main_test.go diff --git a/ops/cmd/check_addresses/main_test.go b/ops/cmd/check_addresses/main_test.go new file mode 100644 index 0000000000..3d978f1131 --- /dev/null +++ b/ops/cmd/check_addresses/main_test.go @@ -0,0 +1,145 @@ +package main + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestAddressRegex(t *testing.T) { + // Test that the regex matches Ethereum addresses correctly + validAddresses := []string{ + "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", + "0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed", + "0x0000000000000000000000000000000000000000", + "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + } + + for _, addr := range validAddresses { + match := addressRegex.MatchString(addr) + assert.True(t, match, "Address should match: %s", addr) + } + + invalidAddresses := []string{ + "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeA", // Too short + "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAedFF", // Too long + "5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", // Missing 0x prefix + "0xGAAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", // Invalid character + } + + for _, addr := range invalidAddresses { + match := addressRegex.MatchString(addr) + assert.False(t, match, "Address should not match: %s", addr) + } +} + +func TestFindRepoRoot(t *testing.T) { + // Create a temporary test directory structure + tmpDir, err := os.MkdirTemp("", "check_addresses_test") + assert.NoError(t, err) + defer os.RemoveAll(tmpDir) + + // Create a .repo-root file in the temp directory + repoRootFile := filepath.Join(tmpDir, ".repo-root") + err = os.WriteFile(repoRootFile, []byte("test repo root"), 0644) + assert.NoError(t, err) + + // Create a subdirectory + subDir := filepath.Join(tmpDir, "ops") + err = os.Mkdir(subDir, 0755) + assert.NoError(t, err) + + // Test findRepoRoot from the tmpDir + oldWd, err := os.Getwd() + assert.NoError(t, err) + defer os.Chdir(oldWd) + + err = os.Chdir(tmpDir) + assert.NoError(t, err) + + root, err := findRepoRoot() + assert.NoError(t, err) + assert.Equal(t, tmpDir, root) + + // Test findRepoRoot from the subDir + err = os.Chdir(subDir) + assert.NoError(t, err) + + root, err = findRepoRoot() + assert.NoError(t, err) + assert.Equal(t, tmpDir, root) +} + +func TestCheckFile(t *testing.T) { + // Create a temporary copy of our test file + tmpDir, err := os.MkdirTemp("", "check_addresses_test") + assert.NoError(t, err) + defer os.RemoveAll(tmpDir) + + // Ensure testdata directory exists + testdataDir := filepath.Join("testdata") + if _, err := os.Stat(testdataDir); os.IsNotExist(err) { + err = os.Mkdir(testdataDir, 0755) + assert.NoError(t, err) + } + + // Define paths + srcFilePath := filepath.Join("testdata", "test_addresses.toml") + tmpFilePath := filepath.Join(tmpDir, "test_addresses.toml") + + // Create the test file if it doesn't exist + if _, err := os.Stat(srcFilePath); os.IsNotExist(err) { + testContent := `# Test file for address validation + +# Incorrect addresses (all lowercase) +[version1.contracts.contract1] +version = "1.0.0" +address = "0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed" + +[version1.contracts.contract2] +version = "1.0.0" +implementation_address = "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + +# Correct addresses (checksummed) +[version2.contracts.contract1] +version = "2.0.0" +address = "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed" + +[version2.contracts.contract2] +version = "2.0.0" +implementation_address = "0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa"` + err = os.WriteFile(srcFilePath, []byte(testContent), 0644) + assert.NoError(t, err) + } + + // Copy test file to temp directory + content, err := os.ReadFile(srcFilePath) + assert.NoError(t, err) + err = os.WriteFile(tmpFilePath, content, 0644) + assert.NoError(t, err) + + // Test checking without fixing + incorrectCount := checkFile(tmpFilePath, false) + assert.Equal(t, 2, incorrectCount, "Should find two incorrect addresses") + + // Test checking with fixing + incorrectCount = checkFile(tmpFilePath, true) + assert.Equal(t, 2, incorrectCount, "Should find and fix two incorrect addresses") + + // Verify fix was applied + incorrectCount = checkFile(tmpFilePath, false) + assert.Equal(t, 0, incorrectCount, "Should find no incorrect addresses after fixing") + + // Read the fixed file and verify the content + fixedContent, err := os.ReadFile(tmpFilePath) + assert.NoError(t, err) + fixedContentStr := string(fixedContent) + + // Check if the specific addresses were fixed + assert.Contains(t, fixedContentStr, "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed") + assert.NotContains(t, fixedContentStr, "0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed") + assert.Contains(t, fixedContentStr, "0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa") + assert.NotContains(t, fixedContentStr, "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") +}