diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 8f2cb4c9d..653615891 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -57,3 +57,8 @@ current development landscape. - We follow the [Effective Go](https://golang.org/doc/effective_go) guidelines. Please make sure your code is idiomatic and follows the guidelines. + +### Code Linting + +- We employ `golangci-lint` for code linting in our development process. It ensures that code adheres to established standards, and any changes that do not pass the linting checks will trigger an error during the Continuous Integration (CI) process. +- You can run it locally by installing the `golangci-lint` binary and running `make lint` in the root directory of the repository. diff --git a/.github/workflows/lint-go.yaml b/.github/workflows/lint-go.yaml new file mode 100644 index 000000000..30eb70d7b --- /dev/null +++ b/.github/workflows/lint-go.yaml @@ -0,0 +1,31 @@ +name: Lint + +on: + push: + branches: + - master + pull_request: + +permissions: + contents: read + +jobs: + golangci: + name: golangci-lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v4 + with: + go-version: '1.21' + cache: false + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + version: v1.54 + # use the default if on main branch, otherwise use the pull request config + args: --timeout=30m --config=${{ github.event_name == 'pull_request' && '.golangci.pull-request.yml' || '.golangci.yml' }} + only-new-issues: true + skip-cache: true + skip-pkg-cache: true + skip-build-cache: true diff --git a/.golangci.pull-request.yml b/.golangci.pull-request.yml new file mode 100644 index 000000000..6fcbb0ba2 --- /dev/null +++ b/.golangci.pull-request.yml @@ -0,0 +1,20 @@ +# Please refer to the official golangci-lint config documentation for more details: +# https://golangci-lint.run/usage/configuration/ +# https://github.com/golangci/golangci-lint/blob/master/.golangci.reference.yml + +linters: + enable-all: true + +run: + timeout: 10m + tests: false + +issues: + max-issues-per-linter: 1000 + exclude-rules: + - path: vm/contracts.go + text: 'SA1019: "golang.org/x/crypto/ripemd160" is deprecated: RIPEMD-160 is a legacy hash and should not be used for new applications.' + - path: vm/bn256/cloudflare/optate.go + linters: + - deadcode + - staticcheck diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 000000000..f1aa4ecfb --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,22 @@ +# Please refer to the official golangci-lint config documentation for more details: +# https://golangci-lint.run/usage/configuration/ +# https://github.com/golangci/golangci-lint/blob/master/.golangci.reference.yml + +linters: + disable: + - unused + - errcheck + +run: + timeout: 10m + tests: false + +issues: + max-issues-per-linter: 1000 + exclude-rules: + - path: vm/contracts.go + text: 'SA1019: "golang.org/x/crypto/ripemd160" is deprecated: RIPEMD-160 is a legacy hash and should not be used for new applications.' + - path: vm/bn256/cloudflare/optate.go + linters: + - deadcode + - staticcheck diff --git a/Makefile b/Makefile index 60e01cee8..91b76c140 100644 --- a/Makefile +++ b/Makefile @@ -13,12 +13,15 @@ export GO111MODULE=on .PHONY: thor disco all clean test -thor:| go_version_check +help: + @egrep -h '\s#@\s' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?#@ "}; {printf "\033[36m %-30s\033[0m %s\n", $$1, $$2}' + +thor:| go_version_check #@ Build the `thor` executable @echo "building $@..." @go build -v -o $(CURDIR)/bin/$@ -ldflags "-X main.version=$(THOR_VERSION) -X main.gitCommit=$(GIT_COMMIT) -X main.gitTag=$(GIT_TAG)" ./cmd/thor @echo "done. executable created at 'bin/$@'" -disco:| go_version_check +disco:| go_version_check #@ Build the `disco` executable @echo "building $@..." @go build -v -o $(CURDIR)/bin/$@ -ldflags "-X main.version=$(DISCO_VERSION) -X main.gitCommit=$(GIT_COMMIT) -X main.gitTag=$(GIT_TAG)" ./cmd/disco @echo "done. executable created at 'bin/$@'" @@ -37,16 +40,25 @@ go_version_check: fi \ fi -all: thor disco +all: thor disco #@ Build the `thor` and `disco` executables -clean: +clean: #@ Clean the build artifacts -rm -rf \ $(CURDIR)/bin/thor \ -$(CURDIR)/bin/disco +$(CURDIR)/bin/disco -test:| go_version_check +test:| go_version_check #@ Run the tests @go test -cover $(PACKAGES) -test-coverage:| go_version_check +test-coverage:| go_version_check #@ Run the tests with coverage @go test -race -coverprofile=coverage.out -covermode=atomic $(PACKAGES) @go tool cover -html=coverage.out + +lint_command_check: + @command -v golangci-lint || (echo "golangci-lint not found, please install it from https://golangci-lint.run/usage/install/" && exit 1) + +lint: | go_version_check lint_command_check #@ Run 'golangci-lint' on new code changes + @golangci-lint run --new --config .golangci.pull-request.yml + +lint-all: | go_version_check lint_command_check #@ Run 'golangci-lint' on the entire codebase + @golangci-lint run --config .golangci.yml diff --git a/README.md b/README.md index 00d0f523f..04f118bc3 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,8 @@ git clone https://github.com/vechain/thor.git cd thor ``` +To see a list of all available commands, run `make help` + ### Building To build the main app `thor`, just run diff --git a/api/accounts/types.go b/api/accounts/types.go index d54b9f462..d220232bb 100644 --- a/api/accounts/types.go +++ b/api/accounts/types.go @@ -59,9 +59,7 @@ func convertCallResultWithInputGas(vo *runtime.Output, inputGas uint64) *CallRes Data: hexutil.Encode(txEvent.Data), } event.Topics = make([]thor.Bytes32, len(txEvent.Topics)) - for k, topic := range txEvent.Topics { - event.Topics[k] = topic - } + copy(event.Topics, txEvent.Topics) events[j] = event } for j, txTransfer := range vo.Transfers { diff --git a/api/transactions/types.go b/api/transactions/types.go index 34644f942..cb2380694 100644 --- a/api/transactions/types.go +++ b/api/transactions/types.go @@ -205,9 +205,7 @@ func convertReceipt(txReceipt *tx.Receipt, header *block.Header, tx *tx.Transact Data: hexutil.Encode(txEvent.Data), } event.Topics = make([]thor.Bytes32, len(txEvent.Topics)) - for k, topic := range txEvent.Topics { - event.Topics[k] = topic - } + copy(event.Topics, txEvent.Topics) otp.Events[j] = event } diff --git a/cmd/thor/main.go b/cmd/thor/main.go index f18150fc9..66118c9e7 100644 --- a/cmd/thor/main.go +++ b/cmd/thor/main.go @@ -8,7 +8,7 @@ package main import ( "encoding/json" "fmt" - "io/ioutil" + "io" "os" "path/filepath" "time" @@ -16,7 +16,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/crypto" "github.com/inconshreveable/log15" - isatty "github.com/mattn/go-isatty" + "github.com/mattn/go-isatty" "github.com/pborman/uuid" "github.com/pkg/errors" "github.com/vechain/thor/v2/api" @@ -30,7 +30,7 @@ import ( "github.com/vechain/thor/v2/state" "github.com/vechain/thor/v2/thor" "github.com/vechain/thor/v2/txpool" - cli "gopkg.in/urfave/cli.v1" + "gopkg.in/urfave/cli.v1" // Force-load the tracer engines to trigger registration _ "github.com/vechain/thor/v2/tracers/js" @@ -355,7 +355,7 @@ func masterKeyAction(ctx *cli.Context) error { if isatty.IsTerminal(os.Stdin.Fd()) { fmt.Println("Input JSON keystore (end with ^d):") } - keyjson, err := ioutil.ReadAll(os.Stdin) + keyjson, err := io.ReadAll(os.Stdin) if err != nil { return err } diff --git a/cmd/thor/utils.go b/cmd/thor/utils.go index 9765705c5..be6a62b45 100644 --- a/cmd/thor/utils.go +++ b/cmd/thor/utils.go @@ -11,7 +11,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "math" "net" "net/http" @@ -34,7 +33,7 @@ import ( "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/rlp" "github.com/inconshreveable/log15" - tty "github.com/mattn/go-tty" + "github.com/mattn/go-tty" "github.com/pkg/errors" "github.com/vechain/thor/v2/api/doc" "github.com/vechain/thor/v2/chain" @@ -49,7 +48,7 @@ import ( "github.com/vechain/thor/v2/thor" "github.com/vechain/thor/v2/tx" "github.com/vechain/thor/v2/txpool" - cli "gopkg.in/urfave/cli.v1" + "gopkg.in/urfave/cli.v1" ) func initLogger(ctx *cli.Context) { @@ -146,7 +145,7 @@ func handleXGenesisID(h http.Handler, genesisID thor.Bytes32) http.Handler { } w.Header().Set(headerKey, expectedID) if actualID != "" && actualID != expectedID { - io.Copy(ioutil.Discard, r.Body) + io.Copy(io.Discard, r.Body) http.Error(w, "genesis id mismatch", http.StatusForbidden) return } @@ -445,7 +444,7 @@ func newP2PComm(ctx *cli.Context, repo *chain.Repository, txPool *txpool.TxPool, peersCachePath := filepath.Join(instanceDir, "peers.cache") - if data, err := ioutil.ReadFile(peersCachePath); err != nil { + if data, err := os.ReadFile(peersCachePath); err != nil { if !os.IsNotExist(err) { log.Warn("failed to load peers cache", "err", err) } @@ -500,7 +499,7 @@ func (p *p2pComm) Stop() { log.Warn("failed to encode cached peers", "err", err) return } - if err := ioutil.WriteFile(p.peersCachePath, data, 0600); err != nil { + if err := os.WriteFile(p.peersCachePath, data, 0600); err != nil { log.Warn("failed to write peers cache", "err", err) } } diff --git a/p2psrv/bootstrap_nodes.go b/p2psrv/bootstrap_nodes.go index 209e1bb9b..a52391b74 100644 --- a/p2psrv/bootstrap_nodes.go +++ b/p2psrv/bootstrap_nodes.go @@ -5,7 +5,6 @@ import ( "context" "fmt" "io" - "io/ioutil" "net/http" "github.com/ethereum/go-ethereum/p2p/discv5" @@ -22,7 +21,7 @@ func fetchRemoteBootstrapNodes(ctx context.Context, remoteURL string) ([]*discv5 return nil, err } defer resp.Body.Close() - defer io.Copy(ioutil.Discard, resp.Body) + defer io.Copy(io.Discard, resp.Body) if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("http fetch failed: statusCode=%d", resp.StatusCode) diff --git a/txpool/blocklist.go b/txpool/blocklist.go index b87238410..0f9f6b5c1 100644 --- a/txpool/blocklist.go +++ b/txpool/blocklist.go @@ -10,7 +10,6 @@ import ( "context" "fmt" "io" - "io/ioutil" "net/http" "os" "strings" @@ -83,7 +82,7 @@ func (bl *blocklist) Fetch(ctx context.Context, url string, eTag *string) error return err } defer resp.Body.Close() - defer io.Copy(ioutil.Discard, resp.Body) + defer io.Copy(io.Discard, resp.Body) if resp.StatusCode == http.StatusNotModified { return nil diff --git a/vm/contracts.go b/vm/contracts.go index 36fa3b0f3..964acf9d3 100644 --- a/vm/contracts.go +++ b/vm/contracts.go @@ -473,7 +473,7 @@ func (c *blake2F) Run(input []byte) ([]byte, error) { // Parse the input into the Blake2b call parameters var ( rounds = binary.BigEndian.Uint32(input[0:4]) - final = (input[212] == blake2FFinalBlockBytes) + final = input[212] == blake2FFinalBlockBytes h [8]uint64 m [16]uint64