Skip to content
Open
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
11 changes: 9 additions & 2 deletions scripts/autogen/generate-semver-lock/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,17 +88,24 @@ func processFile(file string) (*SemverLockResult, []error) {
return nil, []error{fmt.Errorf("failed to read source file: %w", err)}
}

trimmedSourceCode := bytes.TrimSuffix(sourceCode, []byte("\n"))
normalizedSourceCode := normalizeSourceCode(sourceCode)

return &SemverLockResult{
ContractKey: contractKey,
SemverLockOutput: SemverLockOutput{
InitCodeHash: crypto.Keccak256Hash(initCodeBytes).Hex(),
SourceCodeHash: crypto.Keccak256Hash(trimmedSourceCode).Hex(),
SourceCodeHash: crypto.Keccak256Hash(normalizedSourceCode).Hex(),
},
}, nil
}

// normalizeSourceCode makes source hashes independent of the checkout's line-ending
// configuration while preserving the existing behavior of ignoring one trailing newline.
func normalizeSourceCode(sourceCode []byte) []byte {
sourceCode = bytes.ReplaceAll(sourceCode, []byte("\r\n"), []byte("\n"))
return bytes.TrimSuffix(sourceCode, []byte("\n"))
}

func hasSemverVersion(artifact *solc.ForgeArtifact, contractName string) bool {
for _, node := range artifact.Ast.Nodes {
if node.NodeType != "ContractDefinition" || node.Name != contractName {
Expand Down
81 changes: 81 additions & 0 deletions scripts/autogen/generate-semver-lock/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package main

import (
"os"
"testing"

"github.com/stretchr/testify/require"
)

func TestNormalizeSourceCode(t *testing.T) {
tests := []struct {
name string
source string
want string
}{
{
name: "Unix line endings",
source: "contract Test {\n function version() public pure returns (string memory) {}\n}\n",
want: "contract Test {\n function version() public pure returns (string memory) {}\n}",
},
{
name: "Windows line endings",
source: "contract Test {\r\n function version() public pure returns (string memory) {}\r\n}\r\n",
want: "contract Test {\n function version() public pure returns (string memory) {}\n}",
},
{
name: "No trailing newline",
source: "contract Test {}",
want: "contract Test {}",
},
{
name: "Only one trailing newline is ignored",
source: "contract Test {}\r\n\r\n",
want: "contract Test {}\n",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
require.Equal(t, tt.want, string(normalizeSourceCode([]byte(tt.source))))
})
}
}

func TestProcessFile_LineEndingsProduceSameSourceHash(t *testing.T) {
t.Chdir(t.TempDir())
require.NoError(t, os.Mkdir("src", 0755))

artifact := `{
"ast": {
"nodes": [{
"nodeType": "ContractDefinition",
"name": "Test",
"nodes": [{
"nodeType": "FunctionDefinition",
"name": "version",
"documentation": {"text": "@custom:semver 1.0.0"}
}]
}]
},
"bytecode": {"object": "0x00"},
"metadata": {
"settings": {
"compilationTarget": {"src/Test.sol": "Test"}
}
}
}`
require.NoError(t, os.WriteFile("Test.json", []byte(artifact), 0644))

require.NoError(t, os.WriteFile("src/Test.sol", []byte("contract Test {\n}\n"), 0644))
lf, errs := processFile("Test.json")
require.Empty(t, errs)
require.NotNil(t, lf)

require.NoError(t, os.WriteFile("src/Test.sol", []byte("contract Test {\r\n}\r\n"), 0644))
crlf, errs := processFile("Test.json")
require.Empty(t, errs)
require.NotNil(t, crlf)

require.Equal(t, lf.SemverLockOutput.SourceCodeHash, crlf.SemverLockOutput.SourceCodeHash)
}