Skip to content

Commit

Permalink
Unify smart contract management in tests (0xPolygonHermez#499)
Browse files Browse the repository at this point in the history
* reset consolidated batches (0xPolygonHermez#497)

* reset consolidated batches

* linter

* error handlers without fatal

* synchronizer context

* Unify smart contract management in tests

* renamed utils -> testutils

* keep emitLog and update test to use it

* no need to install solc

* emitLog -> emitLogs

Co-authored-by: Alonso Rodriguez <[email protected]>
  • Loading branch information
fgimenez and ARR552 authored Mar 18, 2022
1 parent b00f345 commit 7d828df
Show file tree
Hide file tree
Showing 24 changed files with 601 additions and 97 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/updatedeps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ jobs:
GOARCH: "amd64"
- name: Install Protoc
uses: arduino/setup-protoc@v1
- name: Install protoc-gen-go
run: |
go install github.com/golang/protobuf/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
- name: Update deps
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@
/test/vectors/src/**/*sol
/test/vectors/src/**/*sh
/test/vectors/src/package.json

/test/contracts/**/*.abi
/test/contracts/**/*.bin
12 changes: 8 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ build-docker: ## Builds a docker image with the core binary
docker build -t hezcore -f ./Dockerfile .

.PHONY: test
test: ## Runs only short tests without checking race conditions
test: compile-smart-contracts ## Runs only short tests without checking race conditions
$(STOPDB) || true
$(RUNDB); sleep 5
trap '$(STOPDB)' EXIT; go test -short -p 1 ./...

.PHONY: test-full
test-full: build-docker ## Runs all tests checking race conditions
test-full: build-docker compile-smart-contracts ## Runs all tests checking race conditions
$(STOPDB) || true
$(RUNDB); sleep 5
trap '$(STOPDB)' EXIT; MallocNanoZone=0 go test -race -p 1 -timeout 600s ./...
Expand Down Expand Up @@ -116,7 +116,7 @@ stop-explorer-db: ## Stops the explorer database
$(STOPEXPLORERDB)

.PHONY: run
run: ## Runs all the services
run: compile-smart-contracts ## Runs all the services
$(RUNDB)
$(RUNEXPLORERDB)
$(RUNNETWORK)
Expand Down Expand Up @@ -161,12 +161,16 @@ generate-code-from-proto: ## Generates code from proto files

.PHONY: update-external-dependencies
update-external-dependencies: ## Updates external dependencies like images, test vectors or proto files
go run ./cmd/... updatedeps
go run ./scripts/cmd/... updatedeps

.PHONY: run-benchmarks
run-benchmarks: run-db ## Runs benchmars
go test -bench=. ./state/tree

.PHONY: compile-smart-contracts
compile-smart-contracts: ## Compiles smart contracts used in tests and local deployments
go run ./scripts/cmd/... compilesc --in ./test/contracts

## Help display.
## Pulls comments from beside commands and prints a nicely formatted
## display with the commands and their usage information.
Expand Down
7 changes: 0 additions & 7 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,6 @@ func main() {
Action: encryptKey,
Flags: encryptKeyFlags,
},
{
Name: "updatedeps",
Aliases: []string{},
Usage: "Updates external dependencies like images, test vectors or proto files",
Action: updateDeps,
Flags: flags,
},
}

err := app.Run(os.Args)
Expand Down
13 changes: 13 additions & 0 deletions scripts/cmd/compilesc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package main

import (
"github.com/hermeznetwork/hermez-core/scripts/cmd/compilesc"
"github.com/spf13/afero"
"github.com/urfave/cli/v2"
)

func compileSC(ctx *cli.Context) error {
aferoFs := afero.NewOsFs()

return compilesc.NewManager(aferoFs).Run(ctx.String(flagInput))
}
179 changes: 179 additions & 0 deletions scripts/cmd/compilesc/manager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
package compilesc

import (
"bytes"
"encoding/json"
"fmt"
"io"
"os"
"os/exec"
"path"
"path/filepath"
"runtime"
"strings"
"unicode"

"github.com/hermeznetwork/hermez-core/log"
"github.com/spf13/afero"
)

const (
solcImage = "ethereum/solc:0.8.12-alpine"
)

// Contracts is a map from contract name to its compilation results, as obtained
// from solc combined json output.
type Contracts map[string]Result

// Output is an equivalent of solc's combined json output.
type Output struct {
Contracts Contracts
}

// Result contains the actual compilation results of a smart contract.
type Result struct {
Bin string
}

// Manager handles smart contract compilation.
type Manager struct {
aferoFs afero.Fs
}

// NewManager is the Manager constructor.
func NewManager(aferoFs afero.Fs) *Manager {
return &Manager{
aferoFs: aferoFs,
}
}

// Run executes the compilation of the smart contracts. The given input can be
// a directory or a single sol file. In case of a directory, for each .sol
// file found in it or any of its subdirectories it calls the compile command
// and stores a .bin and .abi file with the results of the compilation.
func (cm *Manager) Run(input string) error {
file, err := cm.aferoFs.Open(input)
if err != nil {
return err
}
defer func() {
if err := file.Close(); err != nil {
log.Errorf("Could not close file %q, %v", file.Name(), err)
}
}()

fileInfo, err := file.Stat()
if err != nil {
return err
}

if !fileInfo.IsDir() {
output, err := cm.Compile(file.Name())
if err != nil {
return err
}

base := filepath.Dir(file.Name())
return cm.writeContracts(output.Contracts, base)
}

return afero.Walk(cm.aferoFs, file.Name(), func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info == nil || info.IsDir() {
return nil
}
if filepath.Ext(path) != ".sol" {
return nil
}
output, err := cm.Compile(path)
if err != nil {
return err
}
base := filepath.Dir(path)
return cm.writeContracts(output.Contracts, base)
})
}

// Compile invokes solc on the given sol file and returns the results and a
// potential error.
func (cm *Manager) Compile(file string) (*Output, error) {
log.Infof("Compiling %q...", file)

c := exec.Command("docker", "run", "-i", solcImage, "-", "--combined-json", "bin")
var stdout, stderr bytes.Buffer
c.Stdout = &stdout
c.Stderr = &stderr

stdin, err := c.StdinPipe()
if err != nil {
return nil, err
}
inputFile, err := cm.aferoFs.Open(file)
if err != nil {
return nil, err
}
_, err = io.Copy(stdin, inputFile)
if err != nil {
return nil, err
}
err = stdin.Close()
if err != nil {
return nil, err
}

envPath := os.Getenv("PATH")
c.Env = []string{fmt.Sprintf("PATH=%s", envPath)}

if err := c.Run(); err != nil {
return nil, err
}

o := &Output{}
if err := json.Unmarshal(stdout.Bytes(), o); err != nil {
return nil, err
}

return o, nil
}

func (cm *Manager) writeContracts(contracts Contracts, base string) error {
for name, result := range contracts {
items := strings.Split(name, ":")
r := []rune(items[len(items)-1])
r[0] = unicode.ToLower(r[0])
contractName := string(r)
targetFile := path.Join(base, contractName)
if err := cm.write(targetFile, result); err != nil {
return err
}
}
return nil
}

func (cm *Manager) write(file string, result Result) error {
baseName := strings.TrimSuffix(file, filepath.Ext(file))

binFileName := fmt.Sprintf("%s.bin", baseName)
return afero.WriteFile(cm.aferoFs, binFileName, []byte(result.Bin), 0644)
}

// ReadBytecode reads the bytecode of the given contract.
func ReadBytecode(contractPath string) (string, error) {
const basePath = "../../../test/contracts"

_, currentFilename, _, ok := runtime.Caller(0)
if !ok {
return "", fmt.Errorf("Could not get name of current file")
}
fullBasePath := path.Join(path.Dir(currentFilename), basePath)

binPath := strings.Replace(contractPath, ".sol", ".bin", -1)

content, err := os.ReadFile(path.Join(fullBasePath, binPath))
if err != nil {
return "", err
}
return string(content), nil
}
Loading

0 comments on commit 7d828df

Please sign in to comment.